objs-core 2.0.2 → 2.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/EXAMPLES.md +59 -58
- package/README.md +51 -27
- package/SKILL.md +53 -4
- package/objs.built.js +154 -37
- package/objs.built.min.js +26 -26
- package/objs.d.ts +3 -2
- package/objs.js +171 -47
- package/package.json +1 -1
package/objs.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @fileoverview Objs-core library
|
|
3
|
-
* @version 2.
|
|
3
|
+
* @version 2.1
|
|
4
4
|
* @author Roman Torshin
|
|
5
5
|
* @license Apache-2.0
|
|
6
6
|
*/
|
|
@@ -14,7 +14,7 @@ const __DEV__ = true;
|
|
|
14
14
|
* @param {any} query - Selector, DOM element to use, an array of elements, inited ID or nothing for creating an element
|
|
15
15
|
* @returns {Object} Objs instance with DOM manipulation methods
|
|
16
16
|
*/
|
|
17
|
-
const o = (query) => {
|
|
17
|
+
const o = (query) => {
|
|
18
18
|
let result = {
|
|
19
19
|
els: [],
|
|
20
20
|
ie: {},
|
|
@@ -22,6 +22,7 @@ const o = (query) => {
|
|
|
22
22
|
parented: {},
|
|
23
23
|
store: {},
|
|
24
24
|
refs: {},
|
|
25
|
+
_refsByIndex: [],
|
|
25
26
|
states: [],
|
|
26
27
|
isDebug: false,
|
|
27
28
|
currentState: "",
|
|
@@ -133,10 +134,49 @@ const o = (query) => {
|
|
|
133
134
|
result.states = [];
|
|
134
135
|
result.ie = {};
|
|
135
136
|
}
|
|
137
|
+
if (Array.isArray(result._refsByIndex)) {
|
|
138
|
+
const currentLen = result._refsByIndex.length;
|
|
139
|
+
if (currentLen > ln) {
|
|
140
|
+
cycleObj(result._refsByIndex, (k) => {
|
|
141
|
+
const idx = +k;
|
|
142
|
+
if (idx >= ln) {
|
|
143
|
+
delete result._refsByIndex[idx];
|
|
144
|
+
}
|
|
145
|
+
});
|
|
146
|
+
result._refsByIndex.length = ln;
|
|
147
|
+
} else if (currentLen < ln) {
|
|
148
|
+
for (let idx = currentLen; idx < ln; idx++) {
|
|
149
|
+
result._refsByIndex[idx] = {};
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
}
|
|
136
153
|
};
|
|
137
154
|
// sets new objects to operate
|
|
138
155
|
result.reset = o;
|
|
139
156
|
|
|
157
|
+
/**
|
|
158
|
+
* Auto-hydrate: after innerHTML is set, bind inited instances (e.g. those
|
|
159
|
+
* created and stored in the parent render) to the DOM nodes that came from
|
|
160
|
+
* the container's HTML, so the parent can control them via store/refs.
|
|
161
|
+
* Scopes by container so the elements from HTML are the Objs instances.
|
|
162
|
+
*/
|
|
163
|
+
const hydrateDataOInitIn = (containerEl) => {
|
|
164
|
+
if (ssr || !containerEl.querySelectorAll) return;
|
|
165
|
+
const nodes = containerEl.querySelectorAll("[data-o-init]");
|
|
166
|
+
const byId = {};
|
|
167
|
+
nodes.forEach((node) => {
|
|
168
|
+
const id = node.getAttribute("data-o-init");
|
|
169
|
+
if (id === null) return;
|
|
170
|
+
if (!byId[id]) byId[id] = [];
|
|
171
|
+
byId[id].push(node);
|
|
172
|
+
});
|
|
173
|
+
cycleObj(byId, (id) => {
|
|
174
|
+
const inst = o.inits[id];
|
|
175
|
+
if (!inst) return;
|
|
176
|
+
inst.getSSR(Number(id), byId[id]);
|
|
177
|
+
});
|
|
178
|
+
};
|
|
179
|
+
|
|
140
180
|
/**
|
|
141
181
|
* Transform DOM elements based on state and props
|
|
142
182
|
* @param {Element} el - DOM element to transform
|
|
@@ -186,7 +226,7 @@ const o = (query) => {
|
|
|
186
226
|
) {
|
|
187
227
|
// insert html
|
|
188
228
|
["html", "innerHTML"].includes(s)
|
|
189
|
-
? (el.innerHTML = value)
|
|
229
|
+
? (el.innerHTML = value, !ssr && hydrateDataOInitIn(el))
|
|
190
230
|
: // className alias
|
|
191
231
|
s === "className"
|
|
192
232
|
? el.setAttribute("class", value)
|
|
@@ -326,23 +366,23 @@ const o = (query) => {
|
|
|
326
366
|
|
|
327
367
|
// creation elements for prop in props
|
|
328
368
|
const newEl = (n, prop = {}) => {
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
newElem.firstElementChild.dataset.oInit = n;
|
|
339
|
-
return newElem.firstElementChild;
|
|
340
|
-
}
|
|
369
|
+
const resolved = type(data) === functionType ? data(prop) : data;
|
|
370
|
+
if (type(resolved) === objectType) {
|
|
371
|
+
return D.createElement(resolved.tag || resolved.tagName || "div");
|
|
372
|
+
}
|
|
373
|
+
const newElem = D.createElement("div");
|
|
374
|
+
newElem.innerHTML = resolved;
|
|
375
|
+
if (newElem.children.length > ONE || !newElem.firstElementChild) {
|
|
376
|
+
newElem.dataset.oInit = n;
|
|
377
|
+
return newElem;
|
|
341
378
|
}
|
|
379
|
+
newElem.firstElementChild.dataset.oInit = n;
|
|
380
|
+
return newElem.firstElementChild;
|
|
342
381
|
};
|
|
343
382
|
|
|
344
383
|
// properties creation
|
|
345
384
|
const rawData = props; // raw argument before array-wrapping
|
|
385
|
+
if (!Array.isArray(props)) props = [props];
|
|
346
386
|
!props.length ? (props = [props]) : props;
|
|
347
387
|
|
|
348
388
|
// creating elements if no one was selected
|
|
@@ -383,19 +423,45 @@ const o = (query) => {
|
|
|
383
423
|
if (creation) {
|
|
384
424
|
buff["data-o-init"] = initN;
|
|
385
425
|
buff["data-o-init-i"] = i;
|
|
426
|
+
if (buff.events) {
|
|
427
|
+
result._hydrateEvents = result._hydrateEvents || [];
|
|
428
|
+
result._hydrateEvents[i] = buff.events;
|
|
429
|
+
}
|
|
386
430
|
}
|
|
387
431
|
transform(el, buff, props[j ? i : 0]);
|
|
388
432
|
}
|
|
389
433
|
});
|
|
390
434
|
if (creation) {
|
|
435
|
+
result._refsByIndex = [];
|
|
391
436
|
result.refs = {};
|
|
392
|
-
result.els.forEach((el) => {
|
|
437
|
+
result.els.forEach((el, idx) => {
|
|
393
438
|
if (!el.querySelectorAll) return;
|
|
439
|
+
const refsForEl = {};
|
|
394
440
|
el.querySelectorAll("[ref]").forEach((refEl) => {
|
|
395
|
-
|
|
396
|
-
refEl
|
|
441
|
+
const refName = refEl.getAttribute("ref");
|
|
442
|
+
const refInstance = o(refEl);
|
|
443
|
+
refsForEl[refName] = refInstance;
|
|
444
|
+
if (idx === 0) result.refs[refName] = refInstance;
|
|
397
445
|
});
|
|
446
|
+
result._refsByIndex[idx] = refsForEl;
|
|
398
447
|
});
|
|
448
|
+
if (!ssr && result._hydrateEvents) {
|
|
449
|
+
result._hydrateEvents.forEach((evts, idx) => {
|
|
450
|
+
if (!evts) return;
|
|
451
|
+
result.select(idx);
|
|
452
|
+
cycleObj(evts, (event) => {
|
|
453
|
+
const spec = evts[event];
|
|
454
|
+
if (type(spec) === objectType && spec.targetRef && type(spec.handler) === functionType) {
|
|
455
|
+
const refsForIdx = result._refsByIndex?.[idx] ?? result.refs;
|
|
456
|
+
const ref = refsForIdx?.[spec.targetRef];
|
|
457
|
+
if (ref) ref.on(event, spec.handler);
|
|
458
|
+
} else if (type(spec) === functionType) {
|
|
459
|
+
result.on(event, spec);
|
|
460
|
+
}
|
|
461
|
+
});
|
|
462
|
+
});
|
|
463
|
+
result.all();
|
|
464
|
+
}
|
|
399
465
|
}
|
|
400
466
|
}
|
|
401
467
|
|
|
@@ -409,24 +475,47 @@ const o = (query) => {
|
|
|
409
475
|
});
|
|
410
476
|
});
|
|
411
477
|
const renderState = states.render || states;
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
renderState.events &&
|
|
416
|
-
renderState.ssr
|
|
417
|
-
) {
|
|
478
|
+
const hasStateEvents = !ssr && type(renderState) === objectType && renderState.events;
|
|
479
|
+
const hasHydrateEvents = !ssr && result._hydrateEvents && result._hydrateEvents.length;
|
|
480
|
+
if (hasStateEvents || hasHydrateEvents) {
|
|
418
481
|
result.initSSRAfterGettingSSR = () => {
|
|
482
|
+
result._refsByIndex = [];
|
|
419
483
|
result.refs = {};
|
|
420
|
-
result.els.forEach((el) => {
|
|
484
|
+
result.els.forEach((el, idx) => {
|
|
421
485
|
if (!el.querySelectorAll) return;
|
|
486
|
+
const refsForEl = {};
|
|
422
487
|
el.querySelectorAll("[ref]").forEach((refEl) => {
|
|
423
|
-
|
|
488
|
+
const refName = refEl.getAttribute("ref");
|
|
489
|
+
const refInstance = o(refEl);
|
|
490
|
+
refsForEl[refName] = refInstance;
|
|
491
|
+
if (idx === 0) result.refs[refName] = refInstance;
|
|
424
492
|
refEl.removeAttribute("ref");
|
|
425
493
|
});
|
|
494
|
+
result._refsByIndex[idx] = refsForEl;
|
|
495
|
+
if (idx === 0) result.refs = refsForEl;
|
|
426
496
|
});
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
497
|
+
if (hasStateEvents) {
|
|
498
|
+
cycleObj(renderState.events, (event) => {
|
|
499
|
+
result.on(event, renderState.events[event]);
|
|
500
|
+
});
|
|
501
|
+
}
|
|
502
|
+
if (result._hydrateEvents) {
|
|
503
|
+
result._hydrateEvents.forEach((evts, idx) => {
|
|
504
|
+
if (!evts) return;
|
|
505
|
+
result.select(idx);
|
|
506
|
+
cycleObj(evts, (event) => {
|
|
507
|
+
const spec = evts[event];
|
|
508
|
+
if (type(spec) === objectType && spec.targetRef && type(spec.handler) === functionType) {
|
|
509
|
+
const refsForIdx = result._refsByIndex?.[idx] ?? result.refs;
|
|
510
|
+
const ref = refsForIdx?.[spec.targetRef];
|
|
511
|
+
if (ref) ref.on(event, spec.handler);
|
|
512
|
+
} else if (type(spec) === functionType) {
|
|
513
|
+
result.on(event, spec);
|
|
514
|
+
}
|
|
515
|
+
});
|
|
516
|
+
});
|
|
517
|
+
result.all();
|
|
518
|
+
}
|
|
430
519
|
};
|
|
431
520
|
}
|
|
432
521
|
}, "init");
|
|
@@ -447,10 +536,13 @@ const o = (query) => {
|
|
|
447
536
|
}, "connect");
|
|
448
537
|
|
|
449
538
|
/**
|
|
450
|
-
* Get SSR elements
|
|
539
|
+
* Get SSR elements: bind this instance to DOM nodes (by initId or from a list).
|
|
540
|
+
* When called from auto-hydration, fromEls are the nodes from the parent's HTML
|
|
541
|
+
* so the inited instance stored in the parent can control those elements.
|
|
451
542
|
* @param {number} initId - Initialization ID
|
|
543
|
+
* @param {Element[]} [fromEls] - Optional list of elements to bind to (e.g. from containerEl)
|
|
452
544
|
*/
|
|
453
|
-
result.getSSR = returner((initId) => {
|
|
545
|
+
result.getSSR = returner((initId, fromEls) => {
|
|
454
546
|
typeVerify([[initId, [numberType, undefinedType]]]);
|
|
455
547
|
const effectiveId = initId !== undefined ? initId : result.initID;
|
|
456
548
|
if (
|
|
@@ -459,17 +551,35 @@ const o = (query) => {
|
|
|
459
551
|
) {
|
|
460
552
|
return;
|
|
461
553
|
}
|
|
462
|
-
const ssrEls =
|
|
554
|
+
const ssrEls =
|
|
555
|
+
fromEls && fromEls.length
|
|
556
|
+
? fromEls
|
|
557
|
+
: o.D.querySelectorAll(`[data-o-init="${effectiveId}"]`);
|
|
463
558
|
|
|
464
|
-
if (ssrEls.length
|
|
559
|
+
if (ssrEls.length) {
|
|
465
560
|
result.els = Array.from(ssrEls);
|
|
466
|
-
|
|
467
|
-
|
|
561
|
+
if (initId !== undefined) {
|
|
562
|
+
result.initID = initId;
|
|
563
|
+
o.inits[initId] = result;
|
|
564
|
+
}
|
|
468
565
|
setResultVals(false);
|
|
469
|
-
|
|
470
566
|
if (type(result.initSSRAfterGettingSSR) === functionType) {
|
|
471
567
|
result.initSSRAfterGettingSSR();
|
|
472
|
-
|
|
568
|
+
} else if (fromEls && fromEls.length) {
|
|
569
|
+
result._refsByIndex = [];
|
|
570
|
+
result.refs = {};
|
|
571
|
+
result.els.forEach((el, idx) => {
|
|
572
|
+
if (!el.querySelectorAll) return;
|
|
573
|
+
const refsForEl = {};
|
|
574
|
+
el.querySelectorAll("[ref]").forEach((refEl) => {
|
|
575
|
+
const refName = refEl.getAttribute("ref");
|
|
576
|
+
refsForEl[refName] = o(refEl);
|
|
577
|
+
if (idx === 0) result.refs[refName] = refsForEl[refName];
|
|
578
|
+
refEl.removeAttribute("ref");
|
|
579
|
+
});
|
|
580
|
+
result._refsByIndex[idx] = refsForEl;
|
|
581
|
+
if (idx === 0) result.refs = refsForEl;
|
|
582
|
+
});
|
|
473
583
|
}
|
|
474
584
|
}
|
|
475
585
|
}, "getSSR");
|
|
@@ -648,18 +758,26 @@ const o = (query) => {
|
|
|
648
758
|
}, "sample");
|
|
649
759
|
|
|
650
760
|
/**
|
|
651
|
-
* Select element to control
|
|
652
|
-
* @param {number} i - Index of element to
|
|
761
|
+
* Select element to control. Accepts index (number) or event: select(e) selects the element in this instance that contains e.target (e.g. the row that had the event).
|
|
762
|
+
* @param {number|Event} i - Index of element, or event object (uses e.target to find containing element)
|
|
653
763
|
*/
|
|
654
764
|
result.select = returner((i) => {
|
|
655
|
-
|
|
656
|
-
if (
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
765
|
+
let idx = i;
|
|
766
|
+
if (idx != null && type(idx) === objectType && idx.target && result.els.length) {
|
|
767
|
+
idx = result.els.findIndex((el) => el === idx.target || el.contains(idx.target));
|
|
768
|
+
if (idx < 0) idx = 0;
|
|
769
|
+
}
|
|
770
|
+
typeVerify([[idx, [numberType, undefinedType]]]);
|
|
771
|
+
if (idx === u) {
|
|
772
|
+
idx = result.length - ONE;
|
|
773
|
+
}
|
|
774
|
+
start = idx;
|
|
775
|
+
finish = idx;
|
|
776
|
+
result.el = result.els[idx];
|
|
662
777
|
select = ONE;
|
|
778
|
+
if (Array.isArray(result._refsByIndex) && result._refsByIndex[idx]) {
|
|
779
|
+
result.refs = result._refsByIndex[idx];
|
|
780
|
+
}
|
|
663
781
|
}, "select");
|
|
664
782
|
|
|
665
783
|
/**
|
|
@@ -670,6 +788,9 @@ const o = (query) => {
|
|
|
670
788
|
finish = 0;
|
|
671
789
|
result.el = result.els[0];
|
|
672
790
|
select = 0;
|
|
791
|
+
if (Array.isArray(result._refsByIndex) && result._refsByIndex.length) {
|
|
792
|
+
result.refs = result._refsByIndex[0] || {};
|
|
793
|
+
}
|
|
673
794
|
}, "all");
|
|
674
795
|
|
|
675
796
|
/**
|
|
@@ -708,7 +829,10 @@ const o = (query) => {
|
|
|
708
829
|
j = finish;
|
|
709
830
|
}
|
|
710
831
|
|
|
711
|
-
result.els.splice(
|
|
832
|
+
result.els.splice(j, ONE);
|
|
833
|
+
if (Array.isArray(result._refsByIndex)) {
|
|
834
|
+
result._refsByIndex.splice(j, ONE);
|
|
835
|
+
}
|
|
712
836
|
setResultVals();
|
|
713
837
|
}, "skip");
|
|
714
838
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "objs-core",
|
|
3
|
-
"version": "2.0
|
|
3
|
+
"version": "2.1.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Lightweight (~6 kB) library for fast, AI-friendly front-end development: samples and state control, built-in store (createStore), routing, caching, and recording → Playwright tests. No build step; split design into samples and give them data and actions. Works standalone or alongside React; SSR and hydrate from server-rendered DOM. v2.0: exportPlaywrightTest(), refs, TypeScript definitions, recording in all builds.",
|
|
6
6
|
"homepage": "https://en.fous.name/objs/",
|