micra.js 1.0.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/README.md +166 -0
- package/dist/core/bus.d.ts +32 -0
- package/dist/core/mount.d.ts +27 -0
- package/dist/core/reactive.d.ts +31 -0
- package/dist/core/registry.d.ts +56 -0
- package/dist/core/start.d.ts +26 -0
- package/dist/dom/directives.d.ts +39 -0
- package/dist/dom/each.d.ts +25 -0
- package/dist/dom/events.d.ts +41 -0
- package/dist/dom/query.d.ts +23 -0
- package/dist/dom/refs.d.ts +23 -0
- package/dist/index.d.ts +29 -0
- package/dist/micra.cjs.js +576 -0
- package/dist/micra.cjs.js.map +7 -0
- package/dist/micra.esm.js +554 -0
- package/dist/micra.esm.js.map +7 -0
- package/dist/micra.js +578 -0
- package/dist/micra.js.map +7 -0
- package/dist/micra.min.js +2 -0
- package/dist/types.d.ts +138 -0
- package/dist/utils/expr.d.ts +27 -0
- package/dist/utils/fetch.d.ts +45 -0
- package/package.json +59 -0
- package/src/core/bus.ts +49 -0
- package/src/core/mount.ts +144 -0
- package/src/core/reactive.ts +50 -0
- package/src/core/registry.ts +100 -0
- package/src/core/start.ts +42 -0
- package/src/dom/directives.ts +207 -0
- package/src/dom/each.ts +167 -0
- package/src/dom/events.ts +145 -0
- package/src/dom/query.ts +36 -0
- package/src/dom/refs.ts +35 -0
- package/src/index.ts +42 -0
- package/src/types.ts +157 -0
- package/src/utils/expr.ts +72 -0
- package/src/utils/fetch.ts +100 -0
package/dist/micra.js
ADDED
|
@@ -0,0 +1,578 @@
|
|
|
1
|
+
/* Micra.js v1.0.0 — https://github.com/micra-js/micra — MIT */
|
|
2
|
+
"use strict";
|
|
3
|
+
var Micra = (() => {
|
|
4
|
+
var __defProp = Object.defineProperty;
|
|
5
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
6
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __export = (target, all) => {
|
|
9
|
+
for (var name in all)
|
|
10
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
|
+
};
|
|
12
|
+
var __copyProps = (to, from, except, desc) => {
|
|
13
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
+
for (let key of __getOwnPropNames(from))
|
|
15
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
16
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
17
|
+
}
|
|
18
|
+
return to;
|
|
19
|
+
};
|
|
20
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
21
|
+
|
|
22
|
+
// src/index.ts
|
|
23
|
+
var index_exports = {};
|
|
24
|
+
__export(index_exports, {
|
|
25
|
+
FetchError: () => FetchError,
|
|
26
|
+
debug: () => debug,
|
|
27
|
+
define: () => define,
|
|
28
|
+
defineComponent: () => defineComponent,
|
|
29
|
+
emit: () => emit,
|
|
30
|
+
instances: () => instances,
|
|
31
|
+
mount: () => mount,
|
|
32
|
+
off: () => off,
|
|
33
|
+
on: () => on,
|
|
34
|
+
registry: () => registry,
|
|
35
|
+
start: () => start
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
// src/utils/fetch.ts
|
|
39
|
+
function getCSRF() {
|
|
40
|
+
var _a, _b;
|
|
41
|
+
return (_b = (_a = document.querySelector('meta[name="csrf-token"]')) == null ? void 0 : _a.getAttribute("content")) != null ? _b : null;
|
|
42
|
+
}
|
|
43
|
+
var FetchError = class extends Error {
|
|
44
|
+
constructor(message, status, response) {
|
|
45
|
+
super(message);
|
|
46
|
+
this.status = status;
|
|
47
|
+
this.response = response;
|
|
48
|
+
this.name = "FetchError";
|
|
49
|
+
}
|
|
50
|
+
};
|
|
51
|
+
async function micraFetch(url, options = {}) {
|
|
52
|
+
var _a, _b;
|
|
53
|
+
const method = ((_a = options.method) != null ? _a : "GET").toUpperCase();
|
|
54
|
+
const headers = {
|
|
55
|
+
Accept: "application/json",
|
|
56
|
+
...options.headers
|
|
57
|
+
};
|
|
58
|
+
const csrf = getCSRF();
|
|
59
|
+
if (csrf) headers["X-CSRF-Token"] = csrf;
|
|
60
|
+
let finalUrl = url;
|
|
61
|
+
let body;
|
|
62
|
+
if (method === "GET" || method === "HEAD") {
|
|
63
|
+
const params = {};
|
|
64
|
+
for (const [k, v] of Object.entries(options)) {
|
|
65
|
+
if (k !== "method" && k !== "headers" && v != null) params[k] = String(v);
|
|
66
|
+
}
|
|
67
|
+
if (Object.keys(params).length)
|
|
68
|
+
finalUrl += (url.includes("?") ? "&" : "?") + new URLSearchParams(params);
|
|
69
|
+
} else {
|
|
70
|
+
headers["Content-Type"] = "application/json";
|
|
71
|
+
body = JSON.stringify(options.body !== void 0 ? options.body : options);
|
|
72
|
+
}
|
|
73
|
+
const res = await fetch(finalUrl, {
|
|
74
|
+
method,
|
|
75
|
+
headers,
|
|
76
|
+
...body !== void 0 ? { body } : {}
|
|
77
|
+
});
|
|
78
|
+
if (!res.ok)
|
|
79
|
+
throw new FetchError(`[Micra] fetch: ${method} ${url} \u2192 ${res.status}`, res.status, res);
|
|
80
|
+
const ct = (_b = res.headers.get("content-type")) != null ? _b : "";
|
|
81
|
+
return ct.includes("application/json") ? res.json() : res.text();
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// src/core/registry.ts
|
|
85
|
+
var _registry = /* @__PURE__ */ new Map();
|
|
86
|
+
var _instances = /* @__PURE__ */ new Map();
|
|
87
|
+
function define(name, definition) {
|
|
88
|
+
_registry.set(name, definition);
|
|
89
|
+
}
|
|
90
|
+
function defineComponent(definition) {
|
|
91
|
+
return definition;
|
|
92
|
+
}
|
|
93
|
+
function instances() {
|
|
94
|
+
return _instances;
|
|
95
|
+
}
|
|
96
|
+
function registry() {
|
|
97
|
+
return _registry;
|
|
98
|
+
}
|
|
99
|
+
function debug() {
|
|
100
|
+
var _a;
|
|
101
|
+
if (_instances.size === 0) {
|
|
102
|
+
console.log("[Micra] No live components.");
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
console.group(`[Micra] ${_instances.size} live component(s)`);
|
|
106
|
+
for (const [el, instance] of _instances) {
|
|
107
|
+
const name = (_a = el.getAttribute("data-component")) != null ? _a : "(unnamed)";
|
|
108
|
+
console.group(`%c${name}`, "font-weight:bold;color:#6366f1");
|
|
109
|
+
console.log("$el ", el);
|
|
110
|
+
console.log("state", { ...instance.state });
|
|
111
|
+
console.groupEnd();
|
|
112
|
+
}
|
|
113
|
+
console.groupEnd();
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// src/utils/expr.ts
|
|
117
|
+
var exprCache = /* @__PURE__ */ new Map();
|
|
118
|
+
var SIMPLE_PATH = /^[a-zA-Z_$][a-zA-Z0-9_$]*(\.[a-zA-Z_$][a-zA-Z0-9_$]*)*$/;
|
|
119
|
+
function evalExpr(expr, state) {
|
|
120
|
+
if (SIMPLE_PATH.test(expr)) {
|
|
121
|
+
return expr.split(".").reduce(
|
|
122
|
+
(obj, key) => obj != null ? obj[key] : void 0,
|
|
123
|
+
state
|
|
124
|
+
);
|
|
125
|
+
}
|
|
126
|
+
if (!exprCache.has(expr)) {
|
|
127
|
+
try {
|
|
128
|
+
exprCache.set(
|
|
129
|
+
expr,
|
|
130
|
+
new Function("$s", `with($s){return (${expr})}`)
|
|
131
|
+
);
|
|
132
|
+
} catch {
|
|
133
|
+
warn(`invalid expression "${expr}"`);
|
|
134
|
+
exprCache.set(expr, () => void 0);
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
try {
|
|
138
|
+
return exprCache.get(expr)(state);
|
|
139
|
+
} catch {
|
|
140
|
+
return void 0;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
function warn(msg) {
|
|
144
|
+
console.warn(`[Micra] ${msg}`);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// src/core/bus.ts
|
|
148
|
+
var _bus = /* @__PURE__ */ new Map();
|
|
149
|
+
function on(event, handler) {
|
|
150
|
+
if (!_bus.has(event)) _bus.set(event, /* @__PURE__ */ new Set());
|
|
151
|
+
_bus.get(event).add(handler);
|
|
152
|
+
return () => off(event, handler);
|
|
153
|
+
}
|
|
154
|
+
function off(event, handler) {
|
|
155
|
+
var _a;
|
|
156
|
+
(_a = _bus.get(event)) == null ? void 0 : _a.delete(handler);
|
|
157
|
+
}
|
|
158
|
+
function emit(event, payload) {
|
|
159
|
+
var _a;
|
|
160
|
+
(_a = _bus.get(event)) == null ? void 0 : _a.forEach((h) => {
|
|
161
|
+
try {
|
|
162
|
+
h(payload);
|
|
163
|
+
} catch (e) {
|
|
164
|
+
console.error(`[Micra] bus error [${event}]:`, e);
|
|
165
|
+
}
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
// src/core/reactive.ts
|
|
170
|
+
function createReactiveState(obj, schedule) {
|
|
171
|
+
return new Proxy(obj, {
|
|
172
|
+
set(target, key, value) {
|
|
173
|
+
;
|
|
174
|
+
target[key] = value;
|
|
175
|
+
schedule();
|
|
176
|
+
return true;
|
|
177
|
+
}
|
|
178
|
+
});
|
|
179
|
+
}
|
|
180
|
+
function createScheduler(render) {
|
|
181
|
+
let pending = false;
|
|
182
|
+
return function schedule() {
|
|
183
|
+
if (pending) return;
|
|
184
|
+
pending = true;
|
|
185
|
+
Promise.resolve().then(() => {
|
|
186
|
+
pending = false;
|
|
187
|
+
render();
|
|
188
|
+
});
|
|
189
|
+
};
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
// src/dom/query.ts
|
|
193
|
+
function queryAll(root, sel) {
|
|
194
|
+
return Array.from(root.querySelectorAll(sel));
|
|
195
|
+
}
|
|
196
|
+
function queryOwn(root, attr) {
|
|
197
|
+
return queryAll(root, `[${attr}]`).filter((el) => {
|
|
198
|
+
let node = el.parentElement;
|
|
199
|
+
while (node && node !== root) {
|
|
200
|
+
if (node.hasAttribute("data-component")) return false;
|
|
201
|
+
node = node.parentElement;
|
|
202
|
+
}
|
|
203
|
+
return true;
|
|
204
|
+
});
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
// src/dom/directives.ts
|
|
208
|
+
function applyText(el, expr, state) {
|
|
209
|
+
var _a;
|
|
210
|
+
const text = String((_a = evalExpr(expr, state)) != null ? _a : "");
|
|
211
|
+
if (el.textContent !== text) el.textContent = text;
|
|
212
|
+
}
|
|
213
|
+
function applyHtml(el, expr, state) {
|
|
214
|
+
var _a;
|
|
215
|
+
el.innerHTML = String((_a = evalExpr(expr, state)) != null ? _a : "");
|
|
216
|
+
}
|
|
217
|
+
function applyIf(el, expr, state) {
|
|
218
|
+
el.style.display = evalExpr(expr, state) ? "" : "none";
|
|
219
|
+
}
|
|
220
|
+
function applyBind(el, expr, state) {
|
|
221
|
+
for (const pair of expr.split(",")) {
|
|
222
|
+
const colonIdx = pair.indexOf(":");
|
|
223
|
+
if (colonIdx === -1) continue;
|
|
224
|
+
const attr = pair.slice(0, colonIdx).trim();
|
|
225
|
+
const valExpr = pair.slice(colonIdx + 1).trim();
|
|
226
|
+
const val = evalExpr(valExpr, state);
|
|
227
|
+
if (attr === "class") {
|
|
228
|
+
el.className = String(val != null ? val : "");
|
|
229
|
+
} else if (attr === "value") {
|
|
230
|
+
if (document.activeElement !== el)
|
|
231
|
+
el.value = String(val != null ? val : "");
|
|
232
|
+
} else if (attr === "style") {
|
|
233
|
+
if (typeof val === "object" && val !== null) {
|
|
234
|
+
Object.assign(el.style, val);
|
|
235
|
+
} else {
|
|
236
|
+
el.setAttribute("style", String(val != null ? val : ""));
|
|
237
|
+
}
|
|
238
|
+
} else if (typeof val === "boolean") {
|
|
239
|
+
val ? el.setAttribute(attr, "") : el.removeAttribute(attr);
|
|
240
|
+
} else {
|
|
241
|
+
val == null ? el.removeAttribute(attr) : el.setAttribute(attr, String(val));
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
function applyClass(el, expr, state) {
|
|
246
|
+
for (const pair of expr.split(",")) {
|
|
247
|
+
const colonIdx = pair.indexOf(":");
|
|
248
|
+
if (colonIdx === -1) continue;
|
|
249
|
+
const cls = pair.slice(0, colonIdx).trim();
|
|
250
|
+
const valExpr = pair.slice(colonIdx + 1).trim();
|
|
251
|
+
if (!cls) continue;
|
|
252
|
+
el.classList.toggle(cls, Boolean(evalExpr(valExpr, state)));
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
function applyModel(el, key, rawState) {
|
|
256
|
+
const html = el;
|
|
257
|
+
if (document.activeElement !== el) {
|
|
258
|
+
html.value = rawState[key] == null ? "" : String(rawState[key]);
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
function buildCache(root) {
|
|
262
|
+
const pick = (attr) => {
|
|
263
|
+
var _a;
|
|
264
|
+
const els = queryOwn(root, attr);
|
|
265
|
+
if ((_a = root.hasAttribute) == null ? void 0 : _a.call(root, attr)) els.unshift(root);
|
|
266
|
+
return els.filter((el) => !el.closest("template")).map((el) => ({ el, expr: el.getAttribute(attr) }));
|
|
267
|
+
};
|
|
268
|
+
return {
|
|
269
|
+
text: pick("data-text"),
|
|
270
|
+
html: pick("data-html"),
|
|
271
|
+
if: pick("data-if"),
|
|
272
|
+
show: pick("data-show"),
|
|
273
|
+
bind: pick("data-bind"),
|
|
274
|
+
model: pick("data-model"),
|
|
275
|
+
class: pick("data-class")
|
|
276
|
+
};
|
|
277
|
+
}
|
|
278
|
+
function applyDirectives(root, state, rawState, _instance) {
|
|
279
|
+
if (root.nodeType === Node.DOCUMENT_FRAGMENT_NODE) {
|
|
280
|
+
applyFromList(buildFragmentList(root), state, rawState);
|
|
281
|
+
return;
|
|
282
|
+
}
|
|
283
|
+
const el = root;
|
|
284
|
+
if (!el.__micraCache) el.__micraCache = buildCache(el);
|
|
285
|
+
applyFromList(el.__micraCache, state, rawState);
|
|
286
|
+
}
|
|
287
|
+
function applyFromList(cache, state, rawState) {
|
|
288
|
+
cache.text.forEach((b) => applyText(b.el, b.expr, state));
|
|
289
|
+
cache.html.forEach((b) => applyHtml(b.el, b.expr, state));
|
|
290
|
+
cache.if.forEach((b) => applyIf(b.el, b.expr, state));
|
|
291
|
+
cache.show.forEach((b) => applyIf(b.el, b.expr, state));
|
|
292
|
+
cache.bind.forEach((b) => applyBind(b.el, b.expr, state));
|
|
293
|
+
cache.model.forEach((b) => applyModel(b.el, b.expr.trim(), rawState));
|
|
294
|
+
cache.class.forEach((b) => applyClass(b.el, b.expr, state));
|
|
295
|
+
}
|
|
296
|
+
function buildFragmentList(frag) {
|
|
297
|
+
const pick = (attr) => queryAll(frag, `[${attr}]`).filter((el) => !el.closest("template")).map((el) => ({ el, expr: el.getAttribute(attr) }));
|
|
298
|
+
return {
|
|
299
|
+
text: pick("data-text"),
|
|
300
|
+
html: pick("data-html"),
|
|
301
|
+
if: pick("data-if"),
|
|
302
|
+
show: pick("data-show"),
|
|
303
|
+
bind: pick("data-bind"),
|
|
304
|
+
model: pick("data-model"),
|
|
305
|
+
class: pick("data-class")
|
|
306
|
+
};
|
|
307
|
+
}
|
|
308
|
+
function validateDirectives(root) {
|
|
309
|
+
queryOwn(root, "data-each").forEach((el) => {
|
|
310
|
+
if (!el.hasAttribute("data-key")) {
|
|
311
|
+
warn(`data-each="${el.getAttribute("data-each")}" has no data-key \u2014 keyed diff disabled. Add data-key="id" for better performance.`);
|
|
312
|
+
}
|
|
313
|
+
});
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
// src/dom/events.ts
|
|
317
|
+
function bindDataOn(root, instance) {
|
|
318
|
+
var _a, _b;
|
|
319
|
+
const isFragment = root.nodeType === 11;
|
|
320
|
+
const els = isFragment ? queryAll(root, "[data-on]") : queryOwn(root, "data-on");
|
|
321
|
+
if (!isFragment && ((_a = root.hasAttribute) == null ? void 0 : _a.call(root, "data-on")) && !els.includes(root))
|
|
322
|
+
els.unshift(root);
|
|
323
|
+
for (const el of els) {
|
|
324
|
+
const mEl = el;
|
|
325
|
+
if (mEl.__micraEvents) continue;
|
|
326
|
+
mEl.__micraEvents = true;
|
|
327
|
+
const spec = (_b = mEl.dataset["on"]) != null ? _b : "";
|
|
328
|
+
for (const part of spec.split(",")) {
|
|
329
|
+
const [evSpec, method] = part.trim().split(":");
|
|
330
|
+
if (!evSpec || !method) continue;
|
|
331
|
+
const [evName, ...mods] = evSpec.split(".");
|
|
332
|
+
el.addEventListener(evName, (e) => {
|
|
333
|
+
if (mods.includes("prevent")) e.preventDefault();
|
|
334
|
+
if (mods.includes("stop")) e.stopPropagation();
|
|
335
|
+
if (mods.includes("self") && e.target !== el) return;
|
|
336
|
+
const fn = instance[method.trim()];
|
|
337
|
+
if (typeof fn === "function") fn.call(instance, e);
|
|
338
|
+
else warn(`method "${method.trim()}" not found`);
|
|
339
|
+
});
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
function bindAtEvents(root, instance) {
|
|
344
|
+
const mRoot = root;
|
|
345
|
+
if (mRoot.__micraAtScanned) return;
|
|
346
|
+
mRoot.__micraAtScanned = true;
|
|
347
|
+
const all = queryAll(root, "*");
|
|
348
|
+
for (const el of all) {
|
|
349
|
+
for (const attr of Array.from(el.attributes)) {
|
|
350
|
+
if (!attr.name.startsWith("@")) continue;
|
|
351
|
+
const [evSpec, ...rest] = attr.name.slice(1).split(".");
|
|
352
|
+
const method = attr.value.trim();
|
|
353
|
+
el.addEventListener(evSpec, (e) => {
|
|
354
|
+
if (rest.includes("prevent")) e.preventDefault();
|
|
355
|
+
if (rest.includes("stop")) e.stopPropagation();
|
|
356
|
+
if (rest.includes("self") && e.target !== el) return;
|
|
357
|
+
const fn = instance[method];
|
|
358
|
+
if (typeof fn === "function") fn.call(instance, e);
|
|
359
|
+
else warn(`method "${method}" not found`);
|
|
360
|
+
});
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
function bindModels(root, instance) {
|
|
365
|
+
var _a;
|
|
366
|
+
const isFragment = root.nodeType === 11;
|
|
367
|
+
const els = isFragment ? queryAll(root, "[data-model]") : queryOwn(root, "data-model");
|
|
368
|
+
for (const el of els) {
|
|
369
|
+
const mEl = el;
|
|
370
|
+
if (mEl.__micraModel) continue;
|
|
371
|
+
mEl.__micraModel = true;
|
|
372
|
+
const key = (_a = el.dataset["model"]) != null ? _a : "";
|
|
373
|
+
const tag = el.tagName;
|
|
374
|
+
const update = () => {
|
|
375
|
+
const val = tag === "INPUT" && el.type === "checkbox" ? el.checked : el.value;
|
|
376
|
+
instance.state[key] = val;
|
|
377
|
+
};
|
|
378
|
+
el.addEventListener(
|
|
379
|
+
tag === "SELECT" || el.type === "radio" ? "change" : "input",
|
|
380
|
+
update
|
|
381
|
+
);
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
// src/dom/each.ts
|
|
386
|
+
function renderList(root, state, rawState, instance) {
|
|
387
|
+
queryOwn(root, "data-each").forEach((tmplEl) => {
|
|
388
|
+
var _a;
|
|
389
|
+
if (tmplEl.tagName !== "TEMPLATE") return;
|
|
390
|
+
const tmpl = tmplEl;
|
|
391
|
+
const itemsExpr = tmpl.getAttribute("data-each");
|
|
392
|
+
const keyAttr = (_a = tmpl.getAttribute("data-key")) != null ? _a : null;
|
|
393
|
+
const items = evalExpr(itemsExpr, state);
|
|
394
|
+
if (!tmpl.__micraMarker) {
|
|
395
|
+
const m = document.createComment(`each:${itemsExpr}`);
|
|
396
|
+
tmpl.after(m);
|
|
397
|
+
tmpl.__micraMarker = m;
|
|
398
|
+
tmpl.__micraNodes = /* @__PURE__ */ new Map();
|
|
399
|
+
tmpl.__micraList = [];
|
|
400
|
+
}
|
|
401
|
+
const marker = tmpl.__micraMarker;
|
|
402
|
+
const keyMap = tmpl.__micraNodes;
|
|
403
|
+
const parent = marker.parentNode;
|
|
404
|
+
if (!Array.isArray(items)) {
|
|
405
|
+
tmpl.__micraList.forEach((n) => n.remove());
|
|
406
|
+
tmpl.__micraList = [];
|
|
407
|
+
keyMap.clear();
|
|
408
|
+
return;
|
|
409
|
+
}
|
|
410
|
+
if (keyAttr) {
|
|
411
|
+
renderKeyed(tmpl, items, keyAttr, marker, keyMap, parent, state, rawState, instance);
|
|
412
|
+
} else {
|
|
413
|
+
renderNoKey(tmpl, items, marker, parent, state, rawState, instance);
|
|
414
|
+
}
|
|
415
|
+
});
|
|
416
|
+
}
|
|
417
|
+
function renderKeyed(tmpl, items, keyAttr, marker, keyMap, parent, state, rawState, instance) {
|
|
418
|
+
const nextKeys = /* @__PURE__ */ new Set();
|
|
419
|
+
const nextNodes = [];
|
|
420
|
+
for (const [index, item] of items.entries()) {
|
|
421
|
+
const key = item[keyAttr];
|
|
422
|
+
if (key == null) warn(`data-key="${keyAttr}" is null/undefined on item at index ${index}`);
|
|
423
|
+
nextKeys.add(key);
|
|
424
|
+
let node = keyMap.get(key);
|
|
425
|
+
if (!node) {
|
|
426
|
+
const frag = tmpl.content.cloneNode(true);
|
|
427
|
+
if (frag.childNodes.length === 1) {
|
|
428
|
+
node = frag.firstElementChild;
|
|
429
|
+
} else {
|
|
430
|
+
node = document.createElement("micra-each-item");
|
|
431
|
+
node.style.display = "contents";
|
|
432
|
+
node.append(frag);
|
|
433
|
+
}
|
|
434
|
+
node.__micraKey = key;
|
|
435
|
+
keyMap.set(key, node);
|
|
436
|
+
bindDataOn(node, instance);
|
|
437
|
+
bindAtEvents(node, instance);
|
|
438
|
+
}
|
|
439
|
+
const itemState = Object.assign(
|
|
440
|
+
Object.create(state),
|
|
441
|
+
{ item, index, $index: index }
|
|
442
|
+
);
|
|
443
|
+
applyDirectives(node, itemState, rawState, instance);
|
|
444
|
+
nextNodes.push(node);
|
|
445
|
+
}
|
|
446
|
+
for (const [key, node] of keyMap) {
|
|
447
|
+
if (!nextKeys.has(key)) {
|
|
448
|
+
node.remove();
|
|
449
|
+
keyMap.delete(key);
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
let cursor = marker;
|
|
453
|
+
for (const node of nextNodes) {
|
|
454
|
+
if (cursor.nextSibling !== node) parent.insertBefore(node, cursor.nextSibling);
|
|
455
|
+
cursor = node;
|
|
456
|
+
}
|
|
457
|
+
tmpl.__micraList = nextNodes;
|
|
458
|
+
}
|
|
459
|
+
function renderNoKey(tmpl, items, marker, parent, state, rawState, instance) {
|
|
460
|
+
tmpl.__micraList.forEach((n) => n.remove());
|
|
461
|
+
tmpl.__micraList = [];
|
|
462
|
+
const frag = document.createDocumentFragment();
|
|
463
|
+
for (const [index, item] of items.entries()) {
|
|
464
|
+
const clone = tmpl.content.cloneNode(true);
|
|
465
|
+
const itemState = Object.assign(
|
|
466
|
+
Object.create(state),
|
|
467
|
+
{ item, index, $index: index }
|
|
468
|
+
);
|
|
469
|
+
applyDirectives(clone, itemState, rawState, instance);
|
|
470
|
+
bindDataOn(clone, instance);
|
|
471
|
+
bindAtEvents(clone, instance);
|
|
472
|
+
const nodes = Array.from(clone.childNodes);
|
|
473
|
+
nodes.forEach((n) => {
|
|
474
|
+
n.__micraEach = true;
|
|
475
|
+
frag.append(n);
|
|
476
|
+
});
|
|
477
|
+
tmpl.__micraList.push(...nodes);
|
|
478
|
+
}
|
|
479
|
+
parent.insertBefore(frag, marker.nextSibling);
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
// src/dom/refs.ts
|
|
483
|
+
function collectRefs(root, instance) {
|
|
484
|
+
instance.refs = {};
|
|
485
|
+
for (const el of queryOwn(root, "data-ref")) {
|
|
486
|
+
const name = el.dataset["ref"];
|
|
487
|
+
if (name) instance.refs[name] = el;
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
// src/core/mount.ts
|
|
492
|
+
function mount(selector, definition) {
|
|
493
|
+
var _a;
|
|
494
|
+
const root = typeof selector === "string" ? document.querySelector(selector) : selector;
|
|
495
|
+
if (!root) {
|
|
496
|
+
warn(`"${selector}" not found`);
|
|
497
|
+
return null;
|
|
498
|
+
}
|
|
499
|
+
if (_instances.has(root)) return _instances.get(root);
|
|
500
|
+
const rawState = { ...(_a = definition.state) != null ? _a : {} };
|
|
501
|
+
const instance = { $el: root, refs: {} };
|
|
502
|
+
for (const [key, val] of Object.entries(definition)) {
|
|
503
|
+
if (key === "state" || key === "onCreate" || key === "onDestroy") continue;
|
|
504
|
+
if (typeof val === "function") instance[key] = val;
|
|
505
|
+
}
|
|
506
|
+
instance.prop = function(name, defaultVal) {
|
|
507
|
+
const val = root.dataset[name];
|
|
508
|
+
if (val === void 0) return defaultVal;
|
|
509
|
+
if (val === "true") return true;
|
|
510
|
+
if (val === "false") return false;
|
|
511
|
+
if (val !== "" && !isNaN(Number(val))) return Number(val);
|
|
512
|
+
return val;
|
|
513
|
+
};
|
|
514
|
+
instance.fetch = micraFetch;
|
|
515
|
+
instance.emit = emit;
|
|
516
|
+
instance.on = (event, handler) => {
|
|
517
|
+
const unsub = on(event, handler);
|
|
518
|
+
if (!instance.__micraSubs) instance.__micraSubs = [];
|
|
519
|
+
instance.__micraSubs.push(unsub);
|
|
520
|
+
return unsub;
|
|
521
|
+
};
|
|
522
|
+
let isRendering = false;
|
|
523
|
+
const schedule = createScheduler(() => instance.render());
|
|
524
|
+
instance.state = createReactiveState(rawState, schedule);
|
|
525
|
+
const exprState = new Proxy(rawState, {
|
|
526
|
+
get(target, key) {
|
|
527
|
+
if (key in target) return target[key];
|
|
528
|
+
if (key in instance) return instance[key];
|
|
529
|
+
return void 0;
|
|
530
|
+
}
|
|
531
|
+
});
|
|
532
|
+
instance.render = function() {
|
|
533
|
+
if (isRendering) return;
|
|
534
|
+
isRendering = true;
|
|
535
|
+
try {
|
|
536
|
+
applyDirectives(root, exprState, rawState, instance);
|
|
537
|
+
renderList(root, exprState, rawState, instance);
|
|
538
|
+
bindDataOn(root, instance);
|
|
539
|
+
bindAtEvents(root, instance);
|
|
540
|
+
bindModels(root, instance);
|
|
541
|
+
collectRefs(root, instance);
|
|
542
|
+
} finally {
|
|
543
|
+
isRendering = false;
|
|
544
|
+
}
|
|
545
|
+
};
|
|
546
|
+
instance.destroy = function() {
|
|
547
|
+
var _a2;
|
|
548
|
+
(_a2 = instance.__micraSubs) == null ? void 0 : _a2.forEach((unsub) => unsub());
|
|
549
|
+
if (typeof definition.onDestroy === "function")
|
|
550
|
+
definition.onDestroy.call(instance);
|
|
551
|
+
_instances.delete(root);
|
|
552
|
+
};
|
|
553
|
+
_instances.set(root, instance);
|
|
554
|
+
instance.render();
|
|
555
|
+
validateDirectives(root);
|
|
556
|
+
if (typeof definition.onCreate === "function")
|
|
557
|
+
Promise.resolve().then(
|
|
558
|
+
() => definition.onCreate.call(instance)
|
|
559
|
+
);
|
|
560
|
+
return instance;
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
// src/core/start.ts
|
|
564
|
+
function start(root = document) {
|
|
565
|
+
root.querySelectorAll("[data-component]").forEach((el) => {
|
|
566
|
+
if (_instances.has(el)) return;
|
|
567
|
+
const name = el.getAttribute("data-component");
|
|
568
|
+
const def = _registry.get(name);
|
|
569
|
+
if (!def) {
|
|
570
|
+
warn(`component "${name}" not defined. Call Micra.define('${name}', {...}) first.`);
|
|
571
|
+
return;
|
|
572
|
+
}
|
|
573
|
+
mount(el, def);
|
|
574
|
+
});
|
|
575
|
+
}
|
|
576
|
+
return __toCommonJS(index_exports);
|
|
577
|
+
})();
|
|
578
|
+
//# sourceMappingURL=micra.js.map
|