micra.js 2.1.0 → 2.2.1
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 +244 -0
- package/README.md +39 -14
- package/dist/core/mount.d.ts +8 -2
- package/dist/core/reactive.d.ts +1 -1
- package/dist/core/registry.d.ts +13 -4
- package/dist/dom/directives.d.ts +11 -15
- package/dist/dom/each.d.ts +10 -7
- package/dist/dom/events.d.ts +15 -5
- package/dist/dom/refs.d.ts +5 -5
- package/dist/dom/scan.d.ts +34 -0
- package/dist/index.d.ts +1 -1
- package/dist/micra.cjs.js +302 -177
- package/dist/micra.cjs.js.map +4 -4
- package/dist/micra.esm.js +302 -177
- package/dist/micra.esm.js.map +4 -4
- package/dist/micra.js +302 -177
- package/dist/micra.js.map +4 -4
- package/dist/micra.min.js +2 -2
- package/dist/types.d.ts +67 -22
- package/llms-full.txt +600 -0
- package/llms.txt +148 -0
- package/package.json +11 -3
- package/src/core/mount.ts +136 -99
- package/src/core/reactive.ts +2 -1
- package/src/core/registry.ts +19 -9
- package/src/dom/directives.ts +39 -122
- package/src/dom/each.ts +133 -37
- package/src/dom/events.ts +23 -31
- package/src/dom/refs.ts +7 -7
- package/src/dom/scan.ts +189 -0
- package/src/index.ts +2 -0
- package/src/types.ts +80 -22
- package/src/utils/expr.ts +34 -21
package/dist/micra.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/* Micra.js v2.1
|
|
1
|
+
/* Micra.js v2.2.1 — https://github.com/micra-js/micra — MIT */
|
|
2
2
|
"use strict";
|
|
3
3
|
var Micra = (() => {
|
|
4
4
|
var __defProp = Object.defineProperty;
|
|
@@ -176,27 +176,32 @@ var Micra = (() => {
|
|
|
176
176
|
return false;
|
|
177
177
|
}
|
|
178
178
|
function evalExpr(expr, state) {
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
if (
|
|
182
|
-
|
|
179
|
+
let cached = exprCache.get(expr);
|
|
180
|
+
if (!cached) {
|
|
181
|
+
if (SIMPLE_PATH.test(expr)) {
|
|
182
|
+
cached = { kind: "path", parts: expr.split(".") };
|
|
183
|
+
} else {
|
|
184
|
+
try {
|
|
185
|
+
cached = {
|
|
186
|
+
kind: "fn",
|
|
187
|
+
fn: new Function("$s", "$safe", `with($safe){with($s){return (${expr})}}`)
|
|
188
|
+
};
|
|
189
|
+
} catch {
|
|
190
|
+
warn(`invalid expression "${expr}"`);
|
|
191
|
+
cached = { kind: "fn", fn: () => void 0 };
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
exprCache.set(expr, cached);
|
|
195
|
+
}
|
|
196
|
+
if (cached.kind === "path") {
|
|
197
|
+
if (!safeStateHas(state, cached.parts[0])) return void 0;
|
|
198
|
+
return cached.parts.reduce(
|
|
183
199
|
(obj, key) => obj != null ? obj[key] : void 0,
|
|
184
200
|
state
|
|
185
201
|
);
|
|
186
202
|
}
|
|
187
|
-
if (!exprCache.has(expr)) {
|
|
188
|
-
try {
|
|
189
|
-
exprCache.set(
|
|
190
|
-
expr,
|
|
191
|
-
new Function("$s", "$safe", `with($safe){with($s){return (${expr})}}`)
|
|
192
|
-
);
|
|
193
|
-
} catch {
|
|
194
|
-
warn(`invalid expression "${expr}"`);
|
|
195
|
-
exprCache.set(expr, () => void 0);
|
|
196
|
-
}
|
|
197
|
-
}
|
|
198
203
|
try {
|
|
199
|
-
return
|
|
204
|
+
return cached.fn(safeStateWrap(state), SAFE_OUTER);
|
|
200
205
|
} catch (e) {
|
|
201
206
|
if (!warnedRuntime.has(expr)) {
|
|
202
207
|
warnedRuntime.add(expr);
|
|
@@ -234,11 +239,12 @@ var Micra = (() => {
|
|
|
234
239
|
}
|
|
235
240
|
|
|
236
241
|
// src/core/reactive.ts
|
|
237
|
-
function createReactiveState(obj, schedule) {
|
|
242
|
+
function createReactiveState(obj, schedule, onKey) {
|
|
238
243
|
return new Proxy(obj, {
|
|
239
244
|
set(target, key, value) {
|
|
240
245
|
;
|
|
241
246
|
target[key] = value;
|
|
247
|
+
onKey == null ? void 0 : onKey(key);
|
|
242
248
|
schedule();
|
|
243
249
|
return true;
|
|
244
250
|
}
|
|
@@ -256,27 +262,6 @@ var Micra = (() => {
|
|
|
256
262
|
};
|
|
257
263
|
}
|
|
258
264
|
|
|
259
|
-
// src/dom/query.ts
|
|
260
|
-
function queryAll(root, sel) {
|
|
261
|
-
return Array.from(root.querySelectorAll(sel));
|
|
262
|
-
}
|
|
263
|
-
function queryOwn(root, attr) {
|
|
264
|
-
return filterOwn(root, queryAll(root, `[${attr}]`));
|
|
265
|
-
}
|
|
266
|
-
function queryOwnAll(root, sel) {
|
|
267
|
-
return filterOwn(root, queryAll(root, sel));
|
|
268
|
-
}
|
|
269
|
-
function filterOwn(root, els) {
|
|
270
|
-
return els.filter((el) => {
|
|
271
|
-
let node = el.parentElement;
|
|
272
|
-
while (node && node !== root) {
|
|
273
|
-
if (node.hasAttribute("data-component")) return false;
|
|
274
|
-
node = node.parentElement;
|
|
275
|
-
}
|
|
276
|
-
return true;
|
|
277
|
-
});
|
|
278
|
-
}
|
|
279
|
-
|
|
280
265
|
// src/dom/directives.ts
|
|
281
266
|
function applyText(el, expr, state) {
|
|
282
267
|
var _a;
|
|
@@ -285,7 +270,8 @@ var Micra = (() => {
|
|
|
285
270
|
}
|
|
286
271
|
function applyHtml(el, expr, state) {
|
|
287
272
|
var _a;
|
|
288
|
-
|
|
273
|
+
const html = String((_a = evalExpr(expr, state)) != null ? _a : "");
|
|
274
|
+
if (el.innerHTML !== html) el.innerHTML = html;
|
|
289
275
|
}
|
|
290
276
|
function applyIf(binding, state) {
|
|
291
277
|
const el = binding.el;
|
|
@@ -302,7 +288,9 @@ var Micra = (() => {
|
|
|
302
288
|
}
|
|
303
289
|
}
|
|
304
290
|
function applyShow(el, expr, state) {
|
|
305
|
-
|
|
291
|
+
const desired = evalExpr(expr, state) ? "" : "none";
|
|
292
|
+
const htmlEl = el;
|
|
293
|
+
if (htmlEl.style.display !== desired) htmlEl.style.display = desired;
|
|
306
294
|
}
|
|
307
295
|
function applyBind(el, pairs, state) {
|
|
308
296
|
for (const [attr, valExpr] of pairs) {
|
|
@@ -330,92 +318,37 @@ var Micra = (() => {
|
|
|
330
318
|
el.classList.toggle(cls, Boolean(evalExpr(valExpr, state)));
|
|
331
319
|
}
|
|
332
320
|
}
|
|
333
|
-
function parsePairs(expr) {
|
|
334
|
-
const out = [];
|
|
335
|
-
for (const part of expr.split(",")) {
|
|
336
|
-
const colonIdx = part.indexOf(":");
|
|
337
|
-
if (colonIdx === -1) continue;
|
|
338
|
-
const left = part.slice(0, colonIdx).trim();
|
|
339
|
-
const right = part.slice(colonIdx + 1).trim();
|
|
340
|
-
if (!left) continue;
|
|
341
|
-
out.push([left, right]);
|
|
342
|
-
}
|
|
343
|
-
return out;
|
|
344
|
-
}
|
|
345
321
|
function applyModel(el, key, rawState) {
|
|
346
322
|
const html = el;
|
|
347
323
|
const stateVal = rawState[key];
|
|
348
324
|
const desired = stateVal == null ? "" : String(stateVal);
|
|
349
325
|
if (html.value !== desired) html.value = desired;
|
|
350
326
|
}
|
|
351
|
-
function
|
|
352
|
-
const
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
const
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
if: pick("data-if"),
|
|
363
|
-
show: pick("data-show"),
|
|
364
|
-
bind: pickPairs("data-bind"),
|
|
365
|
-
model: pick("data-model"),
|
|
366
|
-
class: pickPairs("data-class")
|
|
367
|
-
};
|
|
368
|
-
}
|
|
369
|
-
function applyDirectives(root, state, rawState, _instance) {
|
|
370
|
-
if (root.nodeType === Node.DOCUMENT_FRAGMENT_NODE) {
|
|
371
|
-
applyFromList(buildFragmentList(root), state, rawState);
|
|
372
|
-
return;
|
|
373
|
-
}
|
|
374
|
-
const el = root;
|
|
375
|
-
if (!el.__micraCache) el.__micraCache = buildCache(el);
|
|
376
|
-
applyFromList(el.__micraCache, state, rawState);
|
|
377
|
-
}
|
|
378
|
-
function applyFromList(cache, state, rawState) {
|
|
379
|
-
cache.if.forEach((b) => applyIf(b, state));
|
|
380
|
-
cache.text.forEach((b) => applyText(b.el, b.expr, state));
|
|
381
|
-
cache.html.forEach((b) => applyHtml(b.el, b.expr, state));
|
|
382
|
-
cache.show.forEach((b) => applyShow(b.el, b.expr, state));
|
|
383
|
-
cache.bind.forEach((b) => applyBind(b.el, b.pairs, state));
|
|
384
|
-
cache.model.forEach((b) => applyModel(b.el, b.expr.trim(), rawState));
|
|
385
|
-
cache.class.forEach((b) => applyClass(b.el, b.pairs, state));
|
|
386
|
-
}
|
|
387
|
-
function buildFragmentList(frag) {
|
|
388
|
-
const pick = (attr) => queryAll(frag, `[${attr}]`).filter((el) => !el.closest("template")).map((el) => ({ el, expr: el.getAttribute(attr) }));
|
|
389
|
-
const pickPairs = (attr) => pick(attr).map((b) => ({ ...b, pairs: parsePairs(b.expr) }));
|
|
390
|
-
return {
|
|
391
|
-
text: pick("data-text"),
|
|
392
|
-
html: pick("data-html"),
|
|
393
|
-
if: pick("data-if"),
|
|
394
|
-
show: pick("data-show"),
|
|
395
|
-
bind: pickPairs("data-bind"),
|
|
396
|
-
model: pick("data-model"),
|
|
397
|
-
class: pickPairs("data-class")
|
|
398
|
-
};
|
|
399
|
-
}
|
|
400
|
-
function validateDirectives(root) {
|
|
401
|
-
var _a, _b;
|
|
402
|
-
queryOwn(root, "data-each").forEach((el) => {
|
|
327
|
+
function applyDirectives(scan, state, rawState, _instance) {
|
|
328
|
+
for (const b of scan.if) applyIf(b, state);
|
|
329
|
+
for (const b of scan.text) applyText(b.el, b.expr, state);
|
|
330
|
+
for (const b of scan.html) applyHtml(b.el, b.expr, state);
|
|
331
|
+
for (const b of scan.show) applyShow(b.el, b.expr, state);
|
|
332
|
+
for (const b of scan.bind) applyBind(b.el, b.pairs, state);
|
|
333
|
+
for (const b of scan.model) applyModel(b.el, b.expr.trim(), rawState);
|
|
334
|
+
for (const b of scan.class) applyClass(b.el, b.pairs, state);
|
|
335
|
+
}
|
|
336
|
+
function validateDirectives(scan) {
|
|
337
|
+
for (const el of scan.each) {
|
|
403
338
|
const tmpl = el;
|
|
404
339
|
if (!el.hasAttribute("data-key") && !tmpl.__micraNoKeyWarned) {
|
|
405
340
|
tmpl.__micraNoKeyWarned = true;
|
|
406
|
-
warn(
|
|
341
|
+
warn(
|
|
342
|
+
`data-each="${el.getAttribute("data-each")}" has no data-key \u2014 keyed diff disabled. Add data-key="id" for better performance.`
|
|
343
|
+
);
|
|
407
344
|
}
|
|
408
|
-
}
|
|
409
|
-
const
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
return ((_a2 = p.trim().split(":")[0]) == null ? void 0 : _a2.trim()) === "class";
|
|
416
|
-
});
|
|
417
|
-
if (hasClassBind && el.hasAttribute("data-class")) {
|
|
418
|
-
warn(`element has both data-bind="class:..." and data-class \u2014 they fight on every render. Use one.`);
|
|
345
|
+
}
|
|
346
|
+
for (const b of scan.bind) {
|
|
347
|
+
const hasClassBind = b.pairs.some((p) => p[0] === "class");
|
|
348
|
+
if (hasClassBind && b.el.hasAttribute("data-class")) {
|
|
349
|
+
warn(
|
|
350
|
+
`element has both data-bind="class:..." and data-class \u2014 they fight on every render. Use one.`
|
|
351
|
+
);
|
|
419
352
|
}
|
|
420
353
|
}
|
|
421
354
|
}
|
|
@@ -426,17 +359,13 @@ var Micra = (() => {
|
|
|
426
359
|
el.addEventListener(type, fn);
|
|
427
360
|
((_a = instance.__micraListeners) != null ? _a : instance.__micraListeners = []).push({ el, type, fn });
|
|
428
361
|
}
|
|
429
|
-
function bindDataOn(
|
|
430
|
-
var _a
|
|
431
|
-
const isFragment = root.nodeType === 11;
|
|
432
|
-
const els = isFragment ? queryAll(root, "[data-on]") : queryOwn(root, "data-on");
|
|
433
|
-
if (!isFragment && ((_a = root.hasAttribute) == null ? void 0 : _a.call(root, "data-on")) && !els.includes(root))
|
|
434
|
-
els.unshift(root);
|
|
362
|
+
function bindDataOn(els, instance) {
|
|
363
|
+
var _a;
|
|
435
364
|
for (const el of els) {
|
|
436
365
|
const mEl = el;
|
|
437
366
|
if (mEl.__micraEvents) continue;
|
|
438
367
|
mEl.__micraEvents = true;
|
|
439
|
-
const spec = (
|
|
368
|
+
const spec = (_a = mEl.dataset["on"]) != null ? _a : "";
|
|
440
369
|
for (const part of spec.split(",")) {
|
|
441
370
|
const [evSpec, method] = part.trim().split(":");
|
|
442
371
|
if (!evSpec || !method) continue;
|
|
@@ -452,11 +381,8 @@ var Micra = (() => {
|
|
|
452
381
|
}
|
|
453
382
|
}
|
|
454
383
|
}
|
|
455
|
-
function bindAtEvents(
|
|
456
|
-
const
|
|
457
|
-
const all = isFragment ? queryAll(root, "*") : queryOwnAll(root, "*");
|
|
458
|
-
if (!isFragment && !all.includes(root)) all.unshift(root);
|
|
459
|
-
for (const el of all) {
|
|
384
|
+
function bindAtEvents(els, instance) {
|
|
385
|
+
for (const el of els) {
|
|
460
386
|
const mEl = el;
|
|
461
387
|
if (mEl.__micraAtBound) continue;
|
|
462
388
|
let bound = false;
|
|
@@ -477,15 +403,12 @@ var Micra = (() => {
|
|
|
477
403
|
if (bound) mEl.__micraAtBound = true;
|
|
478
404
|
}
|
|
479
405
|
}
|
|
480
|
-
function bindModels(
|
|
481
|
-
|
|
482
|
-
const isFragment = root.nodeType === 11;
|
|
483
|
-
const els = isFragment ? queryAll(root, "[data-model]") : queryOwn(root, "data-model");
|
|
484
|
-
for (const el of els) {
|
|
406
|
+
function bindModels(bindings, instance) {
|
|
407
|
+
for (const { el, expr } of bindings) {
|
|
485
408
|
const mEl = el;
|
|
486
409
|
if (mEl.__micraModel) continue;
|
|
487
410
|
mEl.__micraModel = true;
|
|
488
|
-
const key = (
|
|
411
|
+
const key = expr.trim();
|
|
489
412
|
const tag = el.tagName;
|
|
490
413
|
const inputEl = el;
|
|
491
414
|
const inputType = inputEl.type;
|
|
@@ -506,11 +429,132 @@ var Micra = (() => {
|
|
|
506
429
|
}
|
|
507
430
|
}
|
|
508
431
|
|
|
432
|
+
// src/dom/scan.ts
|
|
433
|
+
function emptyScan() {
|
|
434
|
+
return {
|
|
435
|
+
text: [],
|
|
436
|
+
html: [],
|
|
437
|
+
if: [],
|
|
438
|
+
show: [],
|
|
439
|
+
bind: [],
|
|
440
|
+
model: [],
|
|
441
|
+
class: [],
|
|
442
|
+
each: [],
|
|
443
|
+
on: [],
|
|
444
|
+
atEvents: [],
|
|
445
|
+
refs: []
|
|
446
|
+
};
|
|
447
|
+
}
|
|
448
|
+
function parsePairs(expr) {
|
|
449
|
+
const out = [];
|
|
450
|
+
for (const part of expr.split(",")) {
|
|
451
|
+
const colon = part.indexOf(":");
|
|
452
|
+
if (colon === -1) continue;
|
|
453
|
+
const left = part.slice(0, colon).trim();
|
|
454
|
+
const right = part.slice(colon + 1).trim();
|
|
455
|
+
if (!left) continue;
|
|
456
|
+
out.push([left, right]);
|
|
457
|
+
}
|
|
458
|
+
return out;
|
|
459
|
+
}
|
|
460
|
+
function classify(el, scan) {
|
|
461
|
+
if (el.tagName === "TEMPLATE") {
|
|
462
|
+
if (el.hasAttribute("data-each")) scan.each.push(el);
|
|
463
|
+
return;
|
|
464
|
+
}
|
|
465
|
+
const attrs = el.attributes;
|
|
466
|
+
let atEventSeen = false;
|
|
467
|
+
for (let i = 0; i < attrs.length; i++) {
|
|
468
|
+
const a = attrs[i];
|
|
469
|
+
const name = a.name;
|
|
470
|
+
const first = name.charCodeAt(0);
|
|
471
|
+
if (first === 64) {
|
|
472
|
+
if (!atEventSeen) {
|
|
473
|
+
scan.atEvents.push(el);
|
|
474
|
+
atEventSeen = true;
|
|
475
|
+
}
|
|
476
|
+
continue;
|
|
477
|
+
}
|
|
478
|
+
if (first === 100 && name.length >= 6 && name.charCodeAt(4) === 45) {
|
|
479
|
+
const rest = name.slice(5);
|
|
480
|
+
switch (rest) {
|
|
481
|
+
case "text":
|
|
482
|
+
scan.text.push({ el, expr: a.value });
|
|
483
|
+
break;
|
|
484
|
+
case "html":
|
|
485
|
+
scan.html.push({ el, expr: a.value });
|
|
486
|
+
break;
|
|
487
|
+
case "if":
|
|
488
|
+
scan.if.push({ el, expr: a.value });
|
|
489
|
+
break;
|
|
490
|
+
case "show":
|
|
491
|
+
scan.show.push({ el, expr: a.value });
|
|
492
|
+
break;
|
|
493
|
+
case "bind": {
|
|
494
|
+
const pairs = parsePairs(a.value);
|
|
495
|
+
scan.bind.push({ el, expr: a.value, pairs });
|
|
496
|
+
break;
|
|
497
|
+
}
|
|
498
|
+
case "model":
|
|
499
|
+
scan.model.push({ el, expr: a.value });
|
|
500
|
+
break;
|
|
501
|
+
case "class": {
|
|
502
|
+
const pairs = parsePairs(a.value);
|
|
503
|
+
scan.class.push({ el, expr: a.value, pairs });
|
|
504
|
+
break;
|
|
505
|
+
}
|
|
506
|
+
case "on":
|
|
507
|
+
scan.on.push(el);
|
|
508
|
+
break;
|
|
509
|
+
case "ref":
|
|
510
|
+
scan.refs.push(el);
|
|
511
|
+
break;
|
|
512
|
+
}
|
|
513
|
+
}
|
|
514
|
+
}
|
|
515
|
+
}
|
|
516
|
+
var NESTED_COMPONENT_FILTER = {
|
|
517
|
+
acceptNode(node) {
|
|
518
|
+
if (node.hasAttribute("data-component"))
|
|
519
|
+
return NodeFilter.FILTER_REJECT;
|
|
520
|
+
return NodeFilter.FILTER_ACCEPT;
|
|
521
|
+
}
|
|
522
|
+
};
|
|
523
|
+
function scanComponent(root) {
|
|
524
|
+
const scan = emptyScan();
|
|
525
|
+
classify(root, scan);
|
|
526
|
+
const walker = document.createTreeWalker(
|
|
527
|
+
root,
|
|
528
|
+
NodeFilter.SHOW_ELEMENT,
|
|
529
|
+
NESTED_COMPONENT_FILTER
|
|
530
|
+
);
|
|
531
|
+
let node = walker.nextNode();
|
|
532
|
+
while (node) {
|
|
533
|
+
classify(node, scan);
|
|
534
|
+
node = walker.nextNode();
|
|
535
|
+
}
|
|
536
|
+
return scan;
|
|
537
|
+
}
|
|
538
|
+
function scanFragment(frag) {
|
|
539
|
+
const scan = emptyScan();
|
|
540
|
+
const walker = document.createTreeWalker(
|
|
541
|
+
frag,
|
|
542
|
+
NodeFilter.SHOW_ELEMENT,
|
|
543
|
+
NESTED_COMPONENT_FILTER
|
|
544
|
+
);
|
|
545
|
+
let node = walker.nextNode();
|
|
546
|
+
while (node) {
|
|
547
|
+
classify(node, scan);
|
|
548
|
+
node = walker.nextNode();
|
|
549
|
+
}
|
|
550
|
+
return scan;
|
|
551
|
+
}
|
|
552
|
+
|
|
509
553
|
// src/dom/each.ts
|
|
510
|
-
function renderList(
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
if (tmplEl.tagName !== "TEMPLATE")
|
|
554
|
+
function renderList(templates, state, rawState, instance, triggerKey) {
|
|
555
|
+
var _a;
|
|
556
|
+
for (const tmplEl of templates) {
|
|
557
|
+
if (tmplEl.tagName !== "TEMPLATE") continue;
|
|
514
558
|
const tmpl = tmplEl;
|
|
515
559
|
const itemsExpr = tmpl.getAttribute("data-each");
|
|
516
560
|
const keyAttr = (_a = tmpl.getAttribute("data-key")) != null ? _a : null;
|
|
@@ -524,22 +568,23 @@ var Micra = (() => {
|
|
|
524
568
|
}
|
|
525
569
|
const marker = tmpl.__micraMarker;
|
|
526
570
|
const keyMap = tmpl.__micraNodes;
|
|
527
|
-
|
|
528
|
-
if (!parent) return;
|
|
571
|
+
if (!marker.parentNode) continue;
|
|
529
572
|
if (!Array.isArray(items)) {
|
|
530
573
|
tmpl.__micraList.forEach((n) => n.remove());
|
|
531
574
|
tmpl.__micraList = [];
|
|
532
575
|
keyMap.clear();
|
|
533
|
-
|
|
576
|
+
continue;
|
|
534
577
|
}
|
|
578
|
+
const canSkipUnchanged = triggerKey !== null && triggerKey !== "MULTIPLE" && triggerKey === itemsExpr;
|
|
535
579
|
if (keyAttr) {
|
|
536
|
-
renderKeyed(tmpl, items, keyAttr, marker, keyMap,
|
|
580
|
+
renderKeyed(tmpl, items, keyAttr, marker, keyMap, state, rawState, instance, canSkipUnchanged);
|
|
537
581
|
} else {
|
|
538
|
-
renderNoKey(tmpl, items, marker,
|
|
582
|
+
renderNoKey(tmpl, items, marker, state, rawState, instance);
|
|
539
583
|
}
|
|
540
|
-
}
|
|
584
|
+
}
|
|
541
585
|
}
|
|
542
|
-
function renderKeyed(tmpl, items, keyAttr, marker, keyMap,
|
|
586
|
+
function renderKeyed(tmpl, items, keyAttr, marker, keyMap, state, rawState, instance, canSkipUnchanged) {
|
|
587
|
+
var _a;
|
|
543
588
|
const nextKeys = /* @__PURE__ */ new Set();
|
|
544
589
|
const nextNodes = [];
|
|
545
590
|
let warnedNullKey = false;
|
|
@@ -567,14 +612,24 @@ var Micra = (() => {
|
|
|
567
612
|
}
|
|
568
613
|
node.__micraKey = key;
|
|
569
614
|
keyMap.set(key, node);
|
|
570
|
-
|
|
571
|
-
|
|
615
|
+
const rowScan2 = scanComponent(node);
|
|
616
|
+
node.__micraScan = rowScan2;
|
|
617
|
+
bindDataOn(rowScan2.on, instance);
|
|
618
|
+
bindAtEvents(rowScan2.atEvents, instance);
|
|
619
|
+
bindModels(rowScan2.model, instance);
|
|
620
|
+
node._itemState = Object.create(state);
|
|
621
|
+
} else if (canSkipUnchanged && node.__micraItem === item && node.__micraIndex === index) {
|
|
622
|
+
nextNodes.push(node);
|
|
623
|
+
continue;
|
|
572
624
|
}
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
625
|
+
node.__micraItem = item;
|
|
626
|
+
node.__micraIndex = index;
|
|
627
|
+
const itemState = node._itemState;
|
|
628
|
+
itemState.item = item;
|
|
629
|
+
itemState.index = index;
|
|
630
|
+
itemState.$index = index;
|
|
631
|
+
const rowScan = (_a = node.__micraScan) != null ? _a : node.__micraScan = scanComponent(node);
|
|
632
|
+
applyDirectives(rowScan, itemState, rawState, instance);
|
|
578
633
|
nextNodes.push(node);
|
|
579
634
|
}
|
|
580
635
|
for (const [key, node] of keyMap) {
|
|
@@ -583,14 +638,64 @@ var Micra = (() => {
|
|
|
583
638
|
keyMap.delete(key);
|
|
584
639
|
}
|
|
585
640
|
}
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
if (
|
|
589
|
-
|
|
641
|
+
const prevList = tmpl.__micraList;
|
|
642
|
+
if (prevList.length === 0) {
|
|
643
|
+
if (nextNodes.length) {
|
|
644
|
+
const frag = document.createDocumentFragment();
|
|
645
|
+
for (const node of nextNodes) frag.append(node);
|
|
646
|
+
marker.after(frag);
|
|
647
|
+
}
|
|
648
|
+
} else {
|
|
649
|
+
let orderChanged = nextNodes.length !== prevList.length;
|
|
650
|
+
if (!orderChanged) {
|
|
651
|
+
for (let i = 0; i < nextNodes.length; i++) {
|
|
652
|
+
if (nextNodes[i] !== prevList[i]) {
|
|
653
|
+
orderChanged = true;
|
|
654
|
+
break;
|
|
655
|
+
}
|
|
656
|
+
}
|
|
657
|
+
}
|
|
658
|
+
if (orderChanged) reorderKeyed(nextNodes, prevList, marker);
|
|
590
659
|
}
|
|
591
660
|
tmpl.__micraList = nextNodes;
|
|
592
661
|
}
|
|
593
|
-
function
|
|
662
|
+
function reorderKeyed(nextNodes, prevList, marker) {
|
|
663
|
+
const prevPos = /* @__PURE__ */ new Map();
|
|
664
|
+
for (let i = 0; i < prevList.length; i++) prevPos.set(prevList[i], i);
|
|
665
|
+
const n = nextNodes.length;
|
|
666
|
+
const tails = [];
|
|
667
|
+
const tailIdx = [];
|
|
668
|
+
const prev = new Array(n).fill(-1);
|
|
669
|
+
for (let i = 0; i < n; i++) {
|
|
670
|
+
const p = prevPos.get(nextNodes[i]);
|
|
671
|
+
if (p === void 0) continue;
|
|
672
|
+
let lo = 0, hi = tails.length;
|
|
673
|
+
while (lo < hi) {
|
|
674
|
+
const m = lo + hi >> 1;
|
|
675
|
+
tails[m] < p ? lo = m + 1 : hi = m;
|
|
676
|
+
}
|
|
677
|
+
if (lo > 0) prev[i] = tailIdx[lo - 1];
|
|
678
|
+
tails[lo] = p;
|
|
679
|
+
tailIdx[lo] = i;
|
|
680
|
+
}
|
|
681
|
+
const stable = /* @__PURE__ */ new Set();
|
|
682
|
+
let idx = tailIdx[tails.length - 1];
|
|
683
|
+
while (idx >= 0) {
|
|
684
|
+
stable.add(idx);
|
|
685
|
+
idx = prev[idx];
|
|
686
|
+
}
|
|
687
|
+
let anchor = marker;
|
|
688
|
+
for (let i = 0; i < n; i++) {
|
|
689
|
+
const node = nextNodes[i];
|
|
690
|
+
if (stable.has(i)) {
|
|
691
|
+
anchor = node;
|
|
692
|
+
continue;
|
|
693
|
+
}
|
|
694
|
+
anchor.after(node);
|
|
695
|
+
anchor = node;
|
|
696
|
+
}
|
|
697
|
+
}
|
|
698
|
+
function renderNoKey(tmpl, items, marker, state, rawState, instance) {
|
|
594
699
|
tmpl.__micraList.forEach((n) => n.remove());
|
|
595
700
|
tmpl.__micraList = [];
|
|
596
701
|
const frag = document.createDocumentFragment();
|
|
@@ -600,9 +705,11 @@ var Micra = (() => {
|
|
|
600
705
|
Object.create(state),
|
|
601
706
|
{ item, index, $index: index }
|
|
602
707
|
);
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
708
|
+
const fragScan = scanFragment(clone);
|
|
709
|
+
applyDirectives(fragScan, itemState, rawState, instance);
|
|
710
|
+
bindDataOn(fragScan.on, instance);
|
|
711
|
+
bindAtEvents(fragScan.atEvents, instance);
|
|
712
|
+
bindModels(fragScan.model, instance);
|
|
606
713
|
const nodes = Array.from(clone.childNodes);
|
|
607
714
|
nodes.forEach((n) => {
|
|
608
715
|
n.__micraEach = true;
|
|
@@ -610,13 +717,14 @@ var Micra = (() => {
|
|
|
610
717
|
});
|
|
611
718
|
tmpl.__micraList.push(...nodes);
|
|
612
719
|
}
|
|
613
|
-
|
|
720
|
+
marker.after(frag);
|
|
614
721
|
}
|
|
615
722
|
|
|
616
723
|
// src/dom/refs.ts
|
|
617
|
-
function collectRefs(
|
|
724
|
+
function collectRefs(els, instance) {
|
|
725
|
+
if (!els.length) return;
|
|
618
726
|
instance.refs = {};
|
|
619
|
-
for (const el of
|
|
727
|
+
for (const el of els) {
|
|
620
728
|
const name = el.dataset["ref"];
|
|
621
729
|
if (name) instance.refs[name] = el;
|
|
622
730
|
}
|
|
@@ -630,10 +738,13 @@ var Micra = (() => {
|
|
|
630
738
|
warn(`"${selector}" not found`);
|
|
631
739
|
return null;
|
|
632
740
|
}
|
|
633
|
-
if (_instances.has(root))
|
|
741
|
+
if (_instances.has(root))
|
|
742
|
+
return _instances.get(root);
|
|
634
743
|
const rawState = { ...(_a = definition.state) != null ? _a : {} };
|
|
635
744
|
const instance = { $el: root, refs: {} };
|
|
636
|
-
for (const [key, val] of Object.entries(
|
|
745
|
+
for (const [key, val] of Object.entries(
|
|
746
|
+
definition
|
|
747
|
+
)) {
|
|
637
748
|
if (key === "state" || key === "onCreate" || key === "onDestroy") continue;
|
|
638
749
|
if (typeof val === "function") instance[key] = val;
|
|
639
750
|
}
|
|
@@ -654,8 +765,12 @@ var Micra = (() => {
|
|
|
654
765
|
return unsub;
|
|
655
766
|
};
|
|
656
767
|
let isRendering = false;
|
|
768
|
+
let _triggerKey = null;
|
|
657
769
|
const schedule = createScheduler(() => instance.render());
|
|
658
|
-
instance.state = createReactiveState(rawState, schedule)
|
|
770
|
+
instance.state = createReactiveState(rawState, schedule, (key) => {
|
|
771
|
+
if (_triggerKey === null) _triggerKey = key;
|
|
772
|
+
else if (_triggerKey !== key) _triggerKey = "MULTIPLE";
|
|
773
|
+
});
|
|
659
774
|
const boundMethods = /* @__PURE__ */ new Map();
|
|
660
775
|
const exprState = new Proxy(rawState, {
|
|
661
776
|
get(target, key) {
|
|
@@ -677,22 +792,29 @@ var Micra = (() => {
|
|
|
677
792
|
});
|
|
678
793
|
let warnedReentry = false;
|
|
679
794
|
instance.render = function() {
|
|
795
|
+
var _a2;
|
|
680
796
|
if (instance.__micraDestroyed) return;
|
|
797
|
+
const triggerKey = _triggerKey;
|
|
798
|
+
_triggerKey = null;
|
|
681
799
|
if (isRendering) {
|
|
682
800
|
if (!warnedReentry) {
|
|
683
|
-
warn(
|
|
801
|
+
warn(
|
|
802
|
+
"render() re-entry detected \u2014 mutation inside a directive expression is ignored. Move state writes to a method."
|
|
803
|
+
);
|
|
684
804
|
warnedReentry = true;
|
|
685
805
|
}
|
|
686
806
|
return;
|
|
687
807
|
}
|
|
688
808
|
isRendering = true;
|
|
689
809
|
try {
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
810
|
+
const mRoot2 = root;
|
|
811
|
+
const scan = (_a2 = mRoot2.__micraScan) != null ? _a2 : mRoot2.__micraScan = scanComponent(root);
|
|
812
|
+
applyDirectives(scan, exprState, rawState, instance);
|
|
813
|
+
renderList(scan.each, exprState, rawState, instance, triggerKey);
|
|
814
|
+
bindDataOn(scan.on, instance);
|
|
815
|
+
bindAtEvents(scan.atEvents, instance);
|
|
816
|
+
bindModels(scan.model, instance);
|
|
817
|
+
collectRefs(scan.refs, instance);
|
|
696
818
|
} finally {
|
|
697
819
|
isRendering = false;
|
|
698
820
|
}
|
|
@@ -701,14 +823,16 @@ var Micra = (() => {
|
|
|
701
823
|
var _a2, _b;
|
|
702
824
|
if (instance.__micraDestroyed) return;
|
|
703
825
|
instance.__micraDestroyed = true;
|
|
704
|
-
(_a2 = instance.__micraListeners) == null ? void 0 : _a2.forEach(
|
|
826
|
+
(_a2 = instance.__micraListeners) == null ? void 0 : _a2.forEach(
|
|
827
|
+
({ el, type, fn }) => el.removeEventListener(type, fn)
|
|
828
|
+
);
|
|
705
829
|
instance.__micraListeners = [];
|
|
706
830
|
const clearFlags = (el) => {
|
|
707
831
|
const m = el;
|
|
708
832
|
delete m.__micraEvents;
|
|
709
833
|
delete m.__micraAtBound;
|
|
710
834
|
delete m.__micraModel;
|
|
711
|
-
delete m.
|
|
835
|
+
delete m.__micraScan;
|
|
712
836
|
};
|
|
713
837
|
clearFlags(root);
|
|
714
838
|
root.querySelectorAll("*").forEach(clearFlags);
|
|
@@ -720,7 +844,8 @@ var Micra = (() => {
|
|
|
720
844
|
};
|
|
721
845
|
_instances.set(root, instance);
|
|
722
846
|
instance.render();
|
|
723
|
-
|
|
847
|
+
const mRoot = root;
|
|
848
|
+
if (mRoot.__micraScan) validateDirectives(mRoot.__micraScan);
|
|
724
849
|
if (typeof definition.onCreate === "function")
|
|
725
850
|
Promise.resolve().then(
|
|
726
851
|
() => definition.onCreate.call(instance)
|