micra.js 2.2.0 → 2.3.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/CHANGELOG.md +88 -0
- package/README.md +1 -0
- package/dist/core/bus.d.ts +6 -4
- package/dist/core/reactive.d.ts +1 -1
- package/dist/dom/each.d.ts +11 -7
- package/dist/dom/scan.d.ts +2 -6
- package/dist/index.d.ts +1 -1
- package/dist/micra.cjs.js +172 -86
- package/dist/micra.cjs.js.map +3 -3
- package/dist/micra.esm.js +172 -86
- package/dist/micra.esm.js.map +3 -3
- package/dist/micra.js +172 -86
- package/dist/micra.js.map +3 -3
- package/dist/micra.min.js +2 -2
- package/dist/types.d.ts +50 -5
- package/llms-full.txt +67 -14
- package/llms.txt +1 -1
- package/package.json +2 -2
- package/src/core/bus.ts +15 -6
- package/src/core/mount.ts +17 -7
- package/src/core/reactive.ts +2 -1
- package/src/dom/directives.ts +5 -2
- package/src/dom/each.ts +190 -59
- package/src/dom/refs.ts +1 -0
- package/src/dom/scan.ts +2 -22
- package/src/index.ts +3 -0
- package/src/types.ts +61 -5
- package/src/utils/expr.ts +34 -21
package/dist/micra.esm.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/* Micra.js v2.
|
|
1
|
+
/* Micra.js v2.3.0 — https://github.com/micra-js/micra — MIT */
|
|
2
2
|
|
|
3
3
|
// src/utils/fetch.ts
|
|
4
4
|
function getCSRF() {
|
|
@@ -141,27 +141,32 @@ function safeStateHas(state, key) {
|
|
|
141
141
|
return false;
|
|
142
142
|
}
|
|
143
143
|
function evalExpr(expr, state) {
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
if (
|
|
147
|
-
|
|
144
|
+
let cached = exprCache.get(expr);
|
|
145
|
+
if (!cached) {
|
|
146
|
+
if (SIMPLE_PATH.test(expr)) {
|
|
147
|
+
cached = { kind: "path", parts: expr.split(".") };
|
|
148
|
+
} else {
|
|
149
|
+
try {
|
|
150
|
+
cached = {
|
|
151
|
+
kind: "fn",
|
|
152
|
+
fn: new Function("$s", "$safe", `with($safe){with($s){return (${expr})}}`)
|
|
153
|
+
};
|
|
154
|
+
} catch {
|
|
155
|
+
warn(`invalid expression "${expr}"`);
|
|
156
|
+
cached = { kind: "fn", fn: () => void 0 };
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
exprCache.set(expr, cached);
|
|
160
|
+
}
|
|
161
|
+
if (cached.kind === "path") {
|
|
162
|
+
if (!safeStateHas(state, cached.parts[0])) return void 0;
|
|
163
|
+
return cached.parts.reduce(
|
|
148
164
|
(obj, key) => obj != null ? obj[key] : void 0,
|
|
149
165
|
state
|
|
150
166
|
);
|
|
151
167
|
}
|
|
152
|
-
if (!exprCache.has(expr)) {
|
|
153
|
-
try {
|
|
154
|
-
exprCache.set(
|
|
155
|
-
expr,
|
|
156
|
-
new Function("$s", "$safe", `with($safe){with($s){return (${expr})}}`)
|
|
157
|
-
);
|
|
158
|
-
} catch {
|
|
159
|
-
warn(`invalid expression "${expr}"`);
|
|
160
|
-
exprCache.set(expr, () => void 0);
|
|
161
|
-
}
|
|
162
|
-
}
|
|
163
168
|
try {
|
|
164
|
-
return
|
|
169
|
+
return cached.fn(safeStateWrap(state), SAFE_OUTER);
|
|
165
170
|
} catch (e) {
|
|
166
171
|
if (!warnedRuntime.has(expr)) {
|
|
167
172
|
warnedRuntime.add(expr);
|
|
@@ -187,8 +192,9 @@ function off(event, handler) {
|
|
|
187
192
|
set.delete(handler);
|
|
188
193
|
if (set.size === 0) _bus.delete(event);
|
|
189
194
|
}
|
|
190
|
-
function emit(event,
|
|
195
|
+
function emit(event, ...args) {
|
|
191
196
|
var _a;
|
|
197
|
+
const payload = args[0];
|
|
192
198
|
(_a = _bus.get(event)) == null ? void 0 : _a.forEach((h) => {
|
|
193
199
|
try {
|
|
194
200
|
h(payload);
|
|
@@ -199,11 +205,12 @@ function emit(event, payload) {
|
|
|
199
205
|
}
|
|
200
206
|
|
|
201
207
|
// src/core/reactive.ts
|
|
202
|
-
function createReactiveState(obj, schedule) {
|
|
208
|
+
function createReactiveState(obj, schedule, onKey) {
|
|
203
209
|
return new Proxy(obj, {
|
|
204
210
|
set(target, key, value) {
|
|
205
211
|
;
|
|
206
212
|
target[key] = value;
|
|
213
|
+
onKey == null ? void 0 : onKey(key);
|
|
207
214
|
schedule();
|
|
208
215
|
return true;
|
|
209
216
|
}
|
|
@@ -229,7 +236,8 @@ function applyText(el, expr, state) {
|
|
|
229
236
|
}
|
|
230
237
|
function applyHtml(el, expr, state) {
|
|
231
238
|
var _a;
|
|
232
|
-
|
|
239
|
+
const html = String((_a = evalExpr(expr, state)) != null ? _a : "");
|
|
240
|
+
if (el.innerHTML !== html) el.innerHTML = html;
|
|
233
241
|
}
|
|
234
242
|
function applyIf(binding, state) {
|
|
235
243
|
const el = binding.el;
|
|
@@ -246,7 +254,9 @@ function applyIf(binding, state) {
|
|
|
246
254
|
}
|
|
247
255
|
}
|
|
248
256
|
function applyShow(el, expr, state) {
|
|
249
|
-
|
|
257
|
+
const desired = evalExpr(expr, state) ? "" : "none";
|
|
258
|
+
const htmlEl = el;
|
|
259
|
+
if (htmlEl.style.display !== desired) htmlEl.style.display = desired;
|
|
250
260
|
}
|
|
251
261
|
function applyBind(el, pairs, state) {
|
|
252
262
|
for (const [attr, valExpr] of pairs) {
|
|
@@ -491,23 +501,9 @@ function scanComponent(root) {
|
|
|
491
501
|
}
|
|
492
502
|
return scan;
|
|
493
503
|
}
|
|
494
|
-
function scanFragment(frag) {
|
|
495
|
-
const scan = emptyScan();
|
|
496
|
-
const walker = document.createTreeWalker(
|
|
497
|
-
frag,
|
|
498
|
-
NodeFilter.SHOW_ELEMENT,
|
|
499
|
-
NESTED_COMPONENT_FILTER
|
|
500
|
-
);
|
|
501
|
-
let node = walker.nextNode();
|
|
502
|
-
while (node) {
|
|
503
|
-
classify(node, scan);
|
|
504
|
-
node = walker.nextNode();
|
|
505
|
-
}
|
|
506
|
-
return scan;
|
|
507
|
-
}
|
|
508
504
|
|
|
509
505
|
// src/dom/each.ts
|
|
510
|
-
function renderList(templates, state, rawState, instance) {
|
|
506
|
+
function renderList(templates, state, rawState, instance, triggerKey) {
|
|
511
507
|
var _a;
|
|
512
508
|
for (const tmplEl of templates) {
|
|
513
509
|
if (tmplEl.tagName !== "TEMPLATE") continue;
|
|
@@ -524,22 +520,40 @@ function renderList(templates, state, rawState, instance) {
|
|
|
524
520
|
}
|
|
525
521
|
const marker = tmpl.__micraMarker;
|
|
526
522
|
const keyMap = tmpl.__micraNodes;
|
|
527
|
-
|
|
528
|
-
if (!parent) continue;
|
|
523
|
+
if (!marker.parentNode) continue;
|
|
529
524
|
if (!Array.isArray(items)) {
|
|
530
525
|
tmpl.__micraList.forEach((n) => n.remove());
|
|
531
526
|
tmpl.__micraList = [];
|
|
532
527
|
keyMap.clear();
|
|
533
528
|
continue;
|
|
534
529
|
}
|
|
530
|
+
const canSkipUnchanged = triggerKey !== null && triggerKey !== "MULTIPLE" && triggerKey === itemsExpr;
|
|
535
531
|
if (keyAttr) {
|
|
536
|
-
renderKeyed(tmpl, items, keyAttr, marker, keyMap,
|
|
532
|
+
renderKeyed(tmpl, items, keyAttr, marker, keyMap, state, rawState, instance, canSkipUnchanged);
|
|
537
533
|
} else {
|
|
538
|
-
renderNoKey(tmpl, items, marker,
|
|
534
|
+
renderNoKey(tmpl, items, marker, state, rawState, instance, canSkipUnchanged);
|
|
539
535
|
}
|
|
540
536
|
}
|
|
541
537
|
}
|
|
542
|
-
function
|
|
538
|
+
function createRowNode(tmpl, state, instance) {
|
|
539
|
+
const frag = tmpl.content.cloneNode(true);
|
|
540
|
+
let node;
|
|
541
|
+
if (frag.childNodes.length === 1) {
|
|
542
|
+
node = frag.firstElementChild;
|
|
543
|
+
} else {
|
|
544
|
+
node = document.createElement("micra-each-item");
|
|
545
|
+
node.style.display = "contents";
|
|
546
|
+
node.append(frag);
|
|
547
|
+
}
|
|
548
|
+
const rowScan = scanComponent(node);
|
|
549
|
+
node.__micraScan = rowScan;
|
|
550
|
+
node._itemState = Object.create(state);
|
|
551
|
+
bindDataOn(rowScan.on, instance);
|
|
552
|
+
bindAtEvents(rowScan.atEvents, instance);
|
|
553
|
+
bindModels(rowScan.model, instance);
|
|
554
|
+
return node;
|
|
555
|
+
}
|
|
556
|
+
function renderKeyed(tmpl, items, keyAttr, marker, keyMap, state, rawState, instance, canSkipUnchanged) {
|
|
543
557
|
var _a;
|
|
544
558
|
const nextKeys = /* @__PURE__ */ new Set();
|
|
545
559
|
const nextNodes = [];
|
|
@@ -558,26 +572,19 @@ function renderKeyed(tmpl, items, keyAttr, marker, keyMap, parent, state, rawSta
|
|
|
558
572
|
nextKeys.add(key);
|
|
559
573
|
let node = keyMap.get(key);
|
|
560
574
|
if (!node) {
|
|
561
|
-
|
|
562
|
-
if (frag.childNodes.length === 1) {
|
|
563
|
-
node = frag.firstElementChild;
|
|
564
|
-
} else {
|
|
565
|
-
node = document.createElement("micra-each-item");
|
|
566
|
-
node.style.display = "contents";
|
|
567
|
-
node.append(frag);
|
|
568
|
-
}
|
|
575
|
+
node = createRowNode(tmpl, state, instance);
|
|
569
576
|
node.__micraKey = key;
|
|
570
577
|
keyMap.set(key, node);
|
|
571
|
-
|
|
572
|
-
node
|
|
573
|
-
|
|
574
|
-
bindAtEvents(rowScan2.atEvents, instance);
|
|
575
|
-
bindModels(rowScan2.model, instance);
|
|
578
|
+
} else if (canSkipUnchanged && node.__micraItem === item && node.__micraIndex === index) {
|
|
579
|
+
nextNodes.push(node);
|
|
580
|
+
continue;
|
|
576
581
|
}
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
582
|
+
node.__micraItem = item;
|
|
583
|
+
node.__micraIndex = index;
|
|
584
|
+
const itemState = node._itemState;
|
|
585
|
+
itemState.item = item;
|
|
586
|
+
itemState.index = index;
|
|
587
|
+
itemState.$index = index;
|
|
581
588
|
const rowScan = (_a = node.__micraScan) != null ? _a : node.__micraScan = scanComponent(node);
|
|
582
589
|
applyDirectives(rowScan, itemState, rawState, instance);
|
|
583
590
|
nextNodes.push(node);
|
|
@@ -588,40 +595,113 @@ function renderKeyed(tmpl, items, keyAttr, marker, keyMap, parent, state, rawSta
|
|
|
588
595
|
keyMap.delete(key);
|
|
589
596
|
}
|
|
590
597
|
}
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
if (
|
|
594
|
-
|
|
598
|
+
const prevList = tmpl.__micraList;
|
|
599
|
+
if (prevList.length === 0) {
|
|
600
|
+
if (nextNodes.length) {
|
|
601
|
+
const frag = document.createDocumentFragment();
|
|
602
|
+
for (const node of nextNodes) frag.append(node);
|
|
603
|
+
marker.after(frag);
|
|
604
|
+
}
|
|
605
|
+
} else {
|
|
606
|
+
let orderChanged = nextNodes.length !== prevList.length;
|
|
607
|
+
if (!orderChanged) {
|
|
608
|
+
for (let i = 0; i < nextNodes.length; i++) {
|
|
609
|
+
if (nextNodes[i] !== prevList[i]) {
|
|
610
|
+
orderChanged = true;
|
|
611
|
+
break;
|
|
612
|
+
}
|
|
613
|
+
}
|
|
614
|
+
}
|
|
615
|
+
if (orderChanged) reorderKeyed(nextNodes, prevList, marker);
|
|
595
616
|
}
|
|
596
617
|
tmpl.__micraList = nextNodes;
|
|
597
618
|
}
|
|
598
|
-
function
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
const
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
);
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
frag.append(n);
|
|
617
|
-
});
|
|
618
|
-
tmpl.__micraList.push(...nodes);
|
|
619
|
+
function reorderKeyed(nextNodes, prevList, marker) {
|
|
620
|
+
const prevPos = /* @__PURE__ */ new Map();
|
|
621
|
+
for (let i = 0; i < prevList.length; i++) prevPos.set(prevList[i], i);
|
|
622
|
+
const n = nextNodes.length;
|
|
623
|
+
const tails = [];
|
|
624
|
+
const tailIdx = [];
|
|
625
|
+
const prev = new Array(n).fill(-1);
|
|
626
|
+
for (let i = 0; i < n; i++) {
|
|
627
|
+
const p = prevPos.get(nextNodes[i]);
|
|
628
|
+
if (p === void 0) continue;
|
|
629
|
+
let lo = 0, hi = tails.length;
|
|
630
|
+
while (lo < hi) {
|
|
631
|
+
const m = lo + hi >> 1;
|
|
632
|
+
tails[m] < p ? lo = m + 1 : hi = m;
|
|
633
|
+
}
|
|
634
|
+
if (lo > 0) prev[i] = tailIdx[lo - 1];
|
|
635
|
+
tails[lo] = p;
|
|
636
|
+
tailIdx[lo] = i;
|
|
619
637
|
}
|
|
620
|
-
|
|
638
|
+
const stable = /* @__PURE__ */ new Set();
|
|
639
|
+
let idx = tailIdx[tails.length - 1];
|
|
640
|
+
while (idx >= 0) {
|
|
641
|
+
stable.add(idx);
|
|
642
|
+
idx = prev[idx];
|
|
643
|
+
}
|
|
644
|
+
let anchor = marker;
|
|
645
|
+
for (let i = 0; i < n; i++) {
|
|
646
|
+
const node = nextNodes[i];
|
|
647
|
+
if (stable.has(i)) {
|
|
648
|
+
anchor = node;
|
|
649
|
+
continue;
|
|
650
|
+
}
|
|
651
|
+
anchor.after(node);
|
|
652
|
+
anchor = node;
|
|
653
|
+
}
|
|
654
|
+
}
|
|
655
|
+
function renderNoKey(tmpl, items, marker, state, rawState, instance, canSkipUnchanged) {
|
|
656
|
+
const prevList = tmpl.__micraList;
|
|
657
|
+
const prevLen = prevList.length;
|
|
658
|
+
const nextLen = items.length;
|
|
659
|
+
const reuseLen = nextLen < prevLen ? nextLen : prevLen;
|
|
660
|
+
const nextList = new Array(nextLen);
|
|
661
|
+
for (let i = 0; i < reuseLen; i++) {
|
|
662
|
+
const node = prevList[i];
|
|
663
|
+
const item = items[i];
|
|
664
|
+
if (canSkipUnchanged && node.__micraItem === item && node.__micraIndex === i) {
|
|
665
|
+
nextList[i] = node;
|
|
666
|
+
continue;
|
|
667
|
+
}
|
|
668
|
+
node.__micraItem = item;
|
|
669
|
+
node.__micraIndex = i;
|
|
670
|
+
const itemState = node._itemState;
|
|
671
|
+
itemState.item = item;
|
|
672
|
+
itemState.index = i;
|
|
673
|
+
itemState.$index = i;
|
|
674
|
+
applyDirectives(node.__micraScan, itemState, rawState, instance);
|
|
675
|
+
nextList[i] = node;
|
|
676
|
+
}
|
|
677
|
+
for (let i = nextLen; i < prevLen; i++) {
|
|
678
|
+
prevList[i].remove();
|
|
679
|
+
}
|
|
680
|
+
if (nextLen > prevLen) {
|
|
681
|
+
const frag = document.createDocumentFragment();
|
|
682
|
+
for (let i = prevLen; i < nextLen; i++) {
|
|
683
|
+
const node = createRowNode(tmpl, state, instance);
|
|
684
|
+
const item = items[i];
|
|
685
|
+
const itemState = node._itemState;
|
|
686
|
+
itemState.item = item;
|
|
687
|
+
itemState.index = i;
|
|
688
|
+
itemState.$index = i;
|
|
689
|
+
node.__micraEach = true;
|
|
690
|
+
node.__micraItem = item;
|
|
691
|
+
node.__micraIndex = i;
|
|
692
|
+
applyDirectives(node.__micraScan, itemState, rawState, instance);
|
|
693
|
+
nextList[i] = node;
|
|
694
|
+
frag.append(node);
|
|
695
|
+
}
|
|
696
|
+
const anchor = prevLen > 0 ? nextList[prevLen - 1] : marker;
|
|
697
|
+
anchor.after(frag);
|
|
698
|
+
}
|
|
699
|
+
tmpl.__micraList = nextList;
|
|
621
700
|
}
|
|
622
701
|
|
|
623
702
|
// src/dom/refs.ts
|
|
624
703
|
function collectRefs(els, instance) {
|
|
704
|
+
if (!els.length) return;
|
|
625
705
|
instance.refs = {};
|
|
626
706
|
for (const el of els) {
|
|
627
707
|
const name = el.dataset["ref"];
|
|
@@ -664,8 +744,12 @@ function mount(selector, definition) {
|
|
|
664
744
|
return unsub;
|
|
665
745
|
};
|
|
666
746
|
let isRendering = false;
|
|
747
|
+
let _triggerKey = null;
|
|
667
748
|
const schedule = createScheduler(() => instance.render());
|
|
668
|
-
instance.state = createReactiveState(rawState, schedule)
|
|
749
|
+
instance.state = createReactiveState(rawState, schedule, (key) => {
|
|
750
|
+
if (_triggerKey === null) _triggerKey = key;
|
|
751
|
+
else if (_triggerKey !== key) _triggerKey = "MULTIPLE";
|
|
752
|
+
});
|
|
669
753
|
const boundMethods = /* @__PURE__ */ new Map();
|
|
670
754
|
const exprState = new Proxy(rawState, {
|
|
671
755
|
get(target, key) {
|
|
@@ -689,6 +773,8 @@ function mount(selector, definition) {
|
|
|
689
773
|
instance.render = function() {
|
|
690
774
|
var _a2;
|
|
691
775
|
if (instance.__micraDestroyed) return;
|
|
776
|
+
const triggerKey = _triggerKey;
|
|
777
|
+
_triggerKey = null;
|
|
692
778
|
if (isRendering) {
|
|
693
779
|
if (!warnedReentry) {
|
|
694
780
|
warn(
|
|
@@ -703,7 +789,7 @@ function mount(selector, definition) {
|
|
|
703
789
|
const mRoot2 = root;
|
|
704
790
|
const scan = (_a2 = mRoot2.__micraScan) != null ? _a2 : mRoot2.__micraScan = scanComponent(root);
|
|
705
791
|
applyDirectives(scan, exprState, rawState, instance);
|
|
706
|
-
renderList(scan.each, exprState, rawState, instance);
|
|
792
|
+
renderList(scan.each, exprState, rawState, instance, triggerKey);
|
|
707
793
|
bindDataOn(scan.on, instance);
|
|
708
794
|
bindAtEvents(scan.atEvents, instance);
|
|
709
795
|
bindModels(scan.model, instance);
|