@estjs/template 0.0.16-beta.1 → 0.0.16-beta.2
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/dist/template.cjs.js +2 -2
- package/dist/template.cjs.js.map +1 -1
- package/dist/template.d.cts +14 -11
- package/dist/template.d.ts +14 -11
- package/dist/template.dev.cjs.js +113 -115
- package/dist/template.dev.cjs.js.map +1 -1
- package/dist/template.dev.esm.js +112 -114
- package/dist/template.dev.esm.js.map +1 -1
- package/dist/template.esm.js +2 -2
- package/dist/template.esm.js.map +1 -1
- package/package.json +3 -3
package/dist/template.d.cts
CHANGED
|
@@ -168,11 +168,11 @@ declare function onUpdate(hook: LifecycleHook): void;
|
|
|
168
168
|
declare function onDestroy(hook: LifecycleHook): void;
|
|
169
169
|
|
|
170
170
|
/**
|
|
171
|
-
* Modifiers
|
|
171
|
+
* Modifiers for `bind:*` two-way bindings.
|
|
172
172
|
*
|
|
173
|
-
* - `trim` strip surrounding whitespace
|
|
174
|
-
* - `number` coerce numeric strings to numbers (no-op on
|
|
175
|
-
* - `lazy` commit on `change` instead of `input`
|
|
173
|
+
* - `trim` — strip surrounding whitespace
|
|
174
|
+
* - `number` — coerce numeric strings to numbers (no-op on NaN)
|
|
175
|
+
* - `lazy` — commit on `change` instead of `input`
|
|
176
176
|
*/
|
|
177
177
|
interface BindModifiers {
|
|
178
178
|
trim?: boolean;
|
|
@@ -181,15 +181,18 @@ interface BindModifiers {
|
|
|
181
181
|
[key: string]: boolean | undefined;
|
|
182
182
|
}
|
|
183
183
|
/**
|
|
184
|
-
*
|
|
184
|
+
* Creates a two-way binding between a DOM element property and a reactive model.
|
|
185
185
|
*
|
|
186
|
-
*
|
|
187
|
-
*
|
|
188
|
-
*
|
|
189
|
-
* @param
|
|
190
|
-
* @param
|
|
186
|
+
* - **Model → DOM** — a reactive `effect()` pushes model changes to the element.
|
|
187
|
+
* - **DOM → Model** — an event listener reads user input and calls `setter`.
|
|
188
|
+
*
|
|
189
|
+
* @param node Target element. `null` is tolerated (no-op).
|
|
190
|
+
* @param prop Bound property (`value` / `checked` / `files` / custom).
|
|
191
|
+
* @param getter Reactive getter, or a static initial value.
|
|
192
|
+
* @param setter Called with the (optionally transformed) DOM value on user input.
|
|
193
|
+
* @param modifiers Optional `{ trim, number, lazy }`.
|
|
191
194
|
*/
|
|
192
|
-
declare function bindElement(node: Element | null, prop: 'value' | 'checked' | 'files' | string, getter: (() => unknown) | unknown, setter: (
|
|
195
|
+
declare function bindElement(node: Element | null, prop: 'value' | 'checked' | 'files' | string, getter: (() => unknown) | unknown, setter: (v: unknown) => void, modifiers?: BindModifiers): void;
|
|
193
196
|
|
|
194
197
|
/**
|
|
195
198
|
* Set up event delegation for specified event types.
|
package/dist/template.d.ts
CHANGED
|
@@ -168,11 +168,11 @@ declare function onUpdate(hook: LifecycleHook): void;
|
|
|
168
168
|
declare function onDestroy(hook: LifecycleHook): void;
|
|
169
169
|
|
|
170
170
|
/**
|
|
171
|
-
* Modifiers
|
|
171
|
+
* Modifiers for `bind:*` two-way bindings.
|
|
172
172
|
*
|
|
173
|
-
* - `trim` strip surrounding whitespace
|
|
174
|
-
* - `number` coerce numeric strings to numbers (no-op on
|
|
175
|
-
* - `lazy` commit on `change` instead of `input`
|
|
173
|
+
* - `trim` — strip surrounding whitespace
|
|
174
|
+
* - `number` — coerce numeric strings to numbers (no-op on NaN)
|
|
175
|
+
* - `lazy` — commit on `change` instead of `input`
|
|
176
176
|
*/
|
|
177
177
|
interface BindModifiers {
|
|
178
178
|
trim?: boolean;
|
|
@@ -181,15 +181,18 @@ interface BindModifiers {
|
|
|
181
181
|
[key: string]: boolean | undefined;
|
|
182
182
|
}
|
|
183
183
|
/**
|
|
184
|
-
*
|
|
184
|
+
* Creates a two-way binding between a DOM element property and a reactive model.
|
|
185
185
|
*
|
|
186
|
-
*
|
|
187
|
-
*
|
|
188
|
-
*
|
|
189
|
-
* @param
|
|
190
|
-
* @param
|
|
186
|
+
* - **Model → DOM** — a reactive `effect()` pushes model changes to the element.
|
|
187
|
+
* - **DOM → Model** — an event listener reads user input and calls `setter`.
|
|
188
|
+
*
|
|
189
|
+
* @param node Target element. `null` is tolerated (no-op).
|
|
190
|
+
* @param prop Bound property (`value` / `checked` / `files` / custom).
|
|
191
|
+
* @param getter Reactive getter, or a static initial value.
|
|
192
|
+
* @param setter Called with the (optionally transformed) DOM value on user input.
|
|
193
|
+
* @param modifiers Optional `{ trim, number, lazy }`.
|
|
191
194
|
*/
|
|
192
|
-
declare function bindElement(node: Element | null, prop: 'value' | 'checked' | 'files' | string, getter: (() => unknown) | unknown, setter: (
|
|
195
|
+
declare function bindElement(node: Element | null, prop: 'value' | 'checked' | 'files' | string, getter: (() => unknown) | unknown, setter: (v: unknown) => void, modifiers?: BindModifiers): void;
|
|
193
196
|
|
|
194
197
|
/**
|
|
195
198
|
* Set up event delegation for specified event types.
|
package/dist/template.dev.cjs.js
CHANGED
|
@@ -19,7 +19,7 @@ var __objRest = (source, exclude) => {
|
|
|
19
19
|
return target;
|
|
20
20
|
};
|
|
21
21
|
var __async = (__this, __arguments, generator) => {
|
|
22
|
-
return new Promise((
|
|
22
|
+
return new Promise((resolve2, reject) => {
|
|
23
23
|
var fulfilled = (value) => {
|
|
24
24
|
try {
|
|
25
25
|
step(generator.next(value));
|
|
@@ -34,7 +34,7 @@ var __async = (__this, __arguments, generator) => {
|
|
|
34
34
|
reject(e);
|
|
35
35
|
}
|
|
36
36
|
};
|
|
37
|
-
var step = (x) => x.done ?
|
|
37
|
+
var step = (x) => x.done ? resolve2(x.value) : Promise.resolve(x.value).then(fulfilled, rejected);
|
|
38
38
|
step((generator = generator.apply(__this, __arguments)).next());
|
|
39
39
|
});
|
|
40
40
|
};
|
|
@@ -207,7 +207,7 @@ function patchAttr(el, key, prev, next2) {
|
|
|
207
207
|
const elementIsSVG = (el == null ? void 0 : el.namespaceURI) === SVG_NAMESPACE;
|
|
208
208
|
const isXlink = elementIsSVG && key.startsWith("xlink:");
|
|
209
209
|
const isXmlns = elementIsSVG && key.startsWith("xmlns:");
|
|
210
|
-
const
|
|
210
|
+
const isBoolean = shared.isSpecialBooleanAttr(key) || shared.isBooleanAttr(key);
|
|
211
211
|
if (prev === next2) {
|
|
212
212
|
return;
|
|
213
213
|
}
|
|
@@ -232,7 +232,7 @@ function patchAttr(el, key, prev, next2) {
|
|
|
232
232
|
}
|
|
233
233
|
return;
|
|
234
234
|
}
|
|
235
|
-
if (
|
|
235
|
+
if (isBoolean) {
|
|
236
236
|
if (shared.includeBooleanAttr(next2)) {
|
|
237
237
|
el.setAttribute(key, "");
|
|
238
238
|
} else {
|
|
@@ -676,13 +676,19 @@ function insertNode(parent, child2, before) {
|
|
|
676
676
|
}
|
|
677
677
|
}
|
|
678
678
|
function normalizeNode(node) {
|
|
679
|
-
if (
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
if (
|
|
683
|
-
return document.createTextNode(
|
|
679
|
+
if (node instanceof Node) return node;
|
|
680
|
+
if (isComponent(node)) return node;
|
|
681
|
+
const t = typeof node;
|
|
682
|
+
if (node == null || t === "string" || t === "number" || t === "boolean" || t === "symbol") {
|
|
683
|
+
return document.createTextNode(node === false || node == null ? "" : String(node));
|
|
684
|
+
}
|
|
685
|
+
if (shared.isObject(node)) {
|
|
686
|
+
shared.warn(
|
|
687
|
+
"Rendering a plain object as a node is not recommended. The object will be converted to its string representation.",
|
|
688
|
+
node
|
|
689
|
+
);
|
|
684
690
|
}
|
|
685
|
-
return node;
|
|
691
|
+
return document.createTextNode(String(node));
|
|
686
692
|
}
|
|
687
693
|
function insert(parent, nodeFactory, before) {
|
|
688
694
|
if (!parent) return;
|
|
@@ -691,7 +697,9 @@ function insert(parent, nodeFactory, before) {
|
|
|
691
697
|
let isFirstRun = true;
|
|
692
698
|
const resolveNodes = (raw) => {
|
|
693
699
|
if (raw instanceof Node) return [raw];
|
|
694
|
-
if (
|
|
700
|
+
if (isComponent(raw)) return [raw];
|
|
701
|
+
const t = typeof raw;
|
|
702
|
+
if (raw == null || t === "string" || t === "number" || t === "boolean") {
|
|
695
703
|
return [normalizeNode(raw)];
|
|
696
704
|
}
|
|
697
705
|
return shared.coerceArray(raw).map((item) => shared.isFunction(item) ? item() : item).flatMap((i) => i).map(normalizeNode);
|
|
@@ -1230,154 +1238,144 @@ function addEventListener(element, event, handler, options) {
|
|
|
1230
1238
|
}
|
|
1231
1239
|
|
|
1232
1240
|
// src/binding.ts
|
|
1233
|
-
|
|
1241
|
+
function writeValue(el, v) {
|
|
1242
|
+
const target = el;
|
|
1243
|
+
const next2 = v == null ? "" : String(v);
|
|
1244
|
+
if (target.value !== next2) target.value = next2;
|
|
1245
|
+
}
|
|
1246
|
+
var CHECKBOX = {
|
|
1234
1247
|
event: "change",
|
|
1235
|
-
|
|
1236
|
-
read: (
|
|
1237
|
-
write
|
|
1238
|
-
const
|
|
1248
|
+
forceChange: true,
|
|
1249
|
+
read: (el) => el.checked,
|
|
1250
|
+
write(el, v) {
|
|
1251
|
+
const e = el;
|
|
1239
1252
|
const next2 = Boolean(v);
|
|
1240
|
-
if (
|
|
1253
|
+
if (e.checked !== next2) e.checked = next2;
|
|
1241
1254
|
}
|
|
1242
1255
|
};
|
|
1243
|
-
var
|
|
1256
|
+
var RADIO = {
|
|
1244
1257
|
event: "change",
|
|
1245
|
-
|
|
1246
|
-
read
|
|
1247
|
-
const
|
|
1248
|
-
return
|
|
1258
|
+
forceChange: true,
|
|
1259
|
+
read(el) {
|
|
1260
|
+
const e = el;
|
|
1261
|
+
return e.checked ? e.value : "";
|
|
1249
1262
|
},
|
|
1250
|
-
write
|
|
1251
|
-
const
|
|
1252
|
-
const next2 = String(v) ===
|
|
1253
|
-
if (
|
|
1263
|
+
write(el, v) {
|
|
1264
|
+
const e = el;
|
|
1265
|
+
const next2 = String(v) === e.value;
|
|
1266
|
+
if (e.checked !== next2) e.checked = next2;
|
|
1254
1267
|
}
|
|
1255
1268
|
};
|
|
1256
|
-
var
|
|
1269
|
+
var FILE = {
|
|
1257
1270
|
event: "change",
|
|
1258
|
-
|
|
1259
|
-
read: (
|
|
1260
|
-
|
|
1261
|
-
write: () => {
|
|
1271
|
+
forceChange: true,
|
|
1272
|
+
read: (el) => el.files,
|
|
1273
|
+
write() {
|
|
1262
1274
|
}
|
|
1275
|
+
// browsers forbid programmatic writes to file inputs
|
|
1263
1276
|
};
|
|
1264
|
-
var
|
|
1277
|
+
var TEXT = {
|
|
1265
1278
|
event: "input",
|
|
1266
|
-
|
|
1267
|
-
read: (
|
|
1268
|
-
write:
|
|
1269
|
-
const el = n;
|
|
1270
|
-
const next2 = v == null ? "" : String(v);
|
|
1271
|
-
if (el.value !== next2) el.value = next2;
|
|
1272
|
-
}
|
|
1279
|
+
ime: true,
|
|
1280
|
+
read: (el) => el.value,
|
|
1281
|
+
write: writeValue
|
|
1273
1282
|
};
|
|
1274
|
-
var
|
|
1283
|
+
var TEXTAREA = {
|
|
1284
|
+
event: "input",
|
|
1285
|
+
ime: true,
|
|
1286
|
+
read: (el) => el.value,
|
|
1287
|
+
write: writeValue
|
|
1288
|
+
};
|
|
1289
|
+
var SELECT = {
|
|
1275
1290
|
event: "change",
|
|
1276
|
-
|
|
1277
|
-
read
|
|
1278
|
-
const s =
|
|
1291
|
+
forceChange: true,
|
|
1292
|
+
read(el) {
|
|
1293
|
+
const s = el;
|
|
1279
1294
|
return s.multiple ? Array.from(s.selectedOptions, (o) => o.value) : s.value;
|
|
1280
1295
|
},
|
|
1281
|
-
write
|
|
1282
|
-
const s =
|
|
1296
|
+
write(el, v) {
|
|
1297
|
+
const s = el;
|
|
1283
1298
|
if (s.multiple) {
|
|
1284
|
-
const
|
|
1285
|
-
for (const opt of Array.from(s.options)) opt.selected =
|
|
1299
|
+
const selected = new Set((Array.isArray(v) ? v : []).map(String));
|
|
1300
|
+
for (const opt of Array.from(s.options)) opt.selected = selected.has(opt.value);
|
|
1286
1301
|
} else {
|
|
1287
|
-
|
|
1288
|
-
if (s.value !== next2) s.value = next2;
|
|
1302
|
+
writeValue(el, v);
|
|
1289
1303
|
}
|
|
1290
1304
|
}
|
|
1291
1305
|
};
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1306
|
+
function resolve(node, prop) {
|
|
1307
|
+
switch (node.nodeName) {
|
|
1308
|
+
case "INPUT":
|
|
1309
|
+
if (prop === "checked") return node.type === "radio" ? RADIO : CHECKBOX;
|
|
1310
|
+
if (prop === "files") return FILE;
|
|
1311
|
+
return TEXT;
|
|
1312
|
+
case "SELECT":
|
|
1313
|
+
return SELECT;
|
|
1314
|
+
case "TEXTAREA":
|
|
1315
|
+
return TEXTAREA;
|
|
1316
|
+
default:
|
|
1317
|
+
return {
|
|
1318
|
+
event: "input",
|
|
1319
|
+
read: (el) => el[prop],
|
|
1320
|
+
write(el, v) {
|
|
1321
|
+
el[prop] = v;
|
|
1322
|
+
}
|
|
1323
|
+
};
|
|
1300
1324
|
}
|
|
1301
|
-
};
|
|
1302
|
-
function resolveStrategy(node, prop) {
|
|
1303
|
-
const tag = node.nodeName;
|
|
1304
|
-
if (tag === "INPUT") {
|
|
1305
|
-
const type = node.type;
|
|
1306
|
-
if (prop === "checked") {
|
|
1307
|
-
return type === "radio" ? INPUT_RADIO_CHECKED : INPUT_CHECKBOX_CHECKED;
|
|
1308
|
-
}
|
|
1309
|
-
if (prop === "files") return INPUT_FILE_FILES;
|
|
1310
|
-
return INPUT_VALUE;
|
|
1311
|
-
}
|
|
1312
|
-
if (tag === "SELECT") return SELECT_VALUE;
|
|
1313
|
-
if (tag === "TEXTAREA") return TEXTAREA_VALUE;
|
|
1314
|
-
return {
|
|
1315
|
-
event: "input",
|
|
1316
|
-
read: (n) => n[prop],
|
|
1317
|
-
write: (n, v) => {
|
|
1318
|
-
n[prop] = v;
|
|
1319
|
-
}
|
|
1320
|
-
};
|
|
1321
1325
|
}
|
|
1322
|
-
function
|
|
1323
|
-
if (!shared.isString(
|
|
1324
|
-
|
|
1325
|
-
if (
|
|
1326
|
-
|
|
1327
|
-
|
|
1326
|
+
function applyModifiers(v, trim, toNum) {
|
|
1327
|
+
if (!shared.isString(v)) return v;
|
|
1328
|
+
let s = v;
|
|
1329
|
+
if (trim) s = s.trim();
|
|
1330
|
+
if (toNum && s !== "") {
|
|
1331
|
+
const n = Number(s);
|
|
1332
|
+
if (!Number.isNaN(n)) return n;
|
|
1328
1333
|
}
|
|
1329
|
-
return
|
|
1334
|
+
return s;
|
|
1330
1335
|
}
|
|
1331
|
-
function isFocused(
|
|
1332
|
-
const root =
|
|
1333
|
-
return (root instanceof Document || root instanceof ShadowRoot) && root.activeElement ===
|
|
1336
|
+
function isFocused(el) {
|
|
1337
|
+
const root = el.getRootNode();
|
|
1338
|
+
return (root instanceof Document || root instanceof ShadowRoot) && root.activeElement === el;
|
|
1334
1339
|
}
|
|
1335
1340
|
function bindElement(node, prop, getter, setter, modifiers = {}) {
|
|
1336
1341
|
if (!node) return;
|
|
1337
|
-
const
|
|
1338
|
-
const
|
|
1339
|
-
const
|
|
1340
|
-
const
|
|
1341
|
-
const
|
|
1342
|
-
const
|
|
1342
|
+
const { event, read, write, forceChange, ime } = resolve(node, prop);
|
|
1343
|
+
const trim = modifiers.trim === true;
|
|
1344
|
+
const toNum = modifiers.number === true;
|
|
1345
|
+
const lazy = modifiers.lazy === true;
|
|
1346
|
+
const shouldCast = (trim || toNum) && prop !== "files";
|
|
1347
|
+
const getModel = shared.isFunction(getter) ? getter : () => getter;
|
|
1348
|
+
const cast = shouldCast ? (v) => applyModifiers(v, trim, toNum) : (v) => v;
|
|
1343
1349
|
let composing = false;
|
|
1344
|
-
const
|
|
1350
|
+
const eventName = lazy || forceChange ? "change" : event;
|
|
1351
|
+
const syncToModel = () => {
|
|
1345
1352
|
if (composing) return;
|
|
1346
|
-
const raw =
|
|
1353
|
+
const raw = read(node);
|
|
1347
1354
|
if (raw === void 0) return;
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
return;
|
|
1351
|
-
}
|
|
1352
|
-
const next2 = transform(raw);
|
|
1353
|
-
if (!Object.is(readModel(), next2)) {
|
|
1354
|
-
setter(next2);
|
|
1355
|
-
}
|
|
1355
|
+
const next2 = cast(raw);
|
|
1356
|
+
if (!Object.is(getModel(), next2)) setter(next2);
|
|
1356
1357
|
};
|
|
1357
|
-
addEventListener(node, eventName,
|
|
1358
|
-
if (!lazy &&
|
|
1359
|
-
addEventListener(node, "change", () =>
|
|
1360
|
-
strategy.write(node, transform(strategy.read(node)));
|
|
1361
|
-
});
|
|
1358
|
+
addEventListener(node, eventName, syncToModel);
|
|
1359
|
+
if (!lazy && shouldCast && eventName !== "change") {
|
|
1360
|
+
addEventListener(node, "change", () => write(node, cast(read(node))));
|
|
1362
1361
|
}
|
|
1363
|
-
if (
|
|
1362
|
+
if (ime && !lazy) {
|
|
1364
1363
|
addEventListener(node, "compositionstart", () => {
|
|
1365
1364
|
composing = true;
|
|
1366
1365
|
});
|
|
1367
1366
|
addEventListener(node, "compositionend", () => {
|
|
1368
1367
|
if (!composing) return;
|
|
1369
1368
|
composing = false;
|
|
1370
|
-
|
|
1369
|
+
syncToModel();
|
|
1371
1370
|
});
|
|
1372
1371
|
}
|
|
1373
1372
|
const runner = signals.effect(() => {
|
|
1374
|
-
const value =
|
|
1375
|
-
if (
|
|
1373
|
+
const value = getModel();
|
|
1374
|
+
if (ime && !lazy && isFocused(node)) {
|
|
1376
1375
|
if (composing) return;
|
|
1377
|
-
|
|
1378
|
-
if (Object.is(current, value)) return;
|
|
1376
|
+
if (Object.is(cast(read(node)), value)) return;
|
|
1379
1377
|
}
|
|
1380
|
-
|
|
1378
|
+
write(node, value);
|
|
1381
1379
|
});
|
|
1382
1380
|
if (getActiveScope()) {
|
|
1383
1381
|
onCleanup(() => runner.stop());
|