sprae 10.7.0 → 10.8.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/core.js +54 -29
- package/directive/each.js +6 -12
- package/directive/if.js +17 -30
- package/directive/text.js +7 -2
- package/dist/sprae.js +79 -68
- package/dist/sprae.min.js +1 -1
- package/package.json +1 -1
- package/readme.md +14 -10
package/core.js
CHANGED
|
@@ -12,8 +12,8 @@ export const memo = new WeakMap();
|
|
|
12
12
|
|
|
13
13
|
// sprae element: apply directives
|
|
14
14
|
export default function sprae(el, values) {
|
|
15
|
-
// text nodes, comments etc
|
|
16
|
-
if (!el?.
|
|
15
|
+
// text nodes, comments etc
|
|
16
|
+
if (!el?.childNodes) return
|
|
17
17
|
|
|
18
18
|
// repeated call can be caused by :each with new objects with old keys needs an update
|
|
19
19
|
if (memo.has(el)) {
|
|
@@ -38,35 +38,35 @@ export default function sprae(el, values) {
|
|
|
38
38
|
return state;
|
|
39
39
|
|
|
40
40
|
function init(el, parent = el.parentNode) {
|
|
41
|
-
if (el.
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
}
|
|
41
|
+
if (!el.childNodes) return // ignore text nodes, comments etc
|
|
42
|
+
|
|
43
|
+
// init generic-name attributes second
|
|
44
|
+
for (let i = 0; i < el.attributes?.length;) {
|
|
45
|
+
let attr = el.attributes[i];
|
|
46
|
+
|
|
47
|
+
if (attr.name[0] === ':') {
|
|
48
|
+
el.removeAttribute(attr.name);
|
|
49
|
+
|
|
50
|
+
// multiple attributes like :id:for=""
|
|
51
|
+
let names = attr.name.slice(1).split(':')
|
|
52
|
+
|
|
53
|
+
// NOTE: secondary directives don't stop flow nor extend state, so no need to check
|
|
54
|
+
for (let name of names) {
|
|
55
|
+
let dir = directive[name] || directive.default
|
|
56
|
+
let evaluate = (dir.parse || parse)(attr.value, parse)
|
|
57
|
+
let dispose = dir(el, evaluate, state, name);
|
|
58
|
+
if (dispose) disposes.push(dispose);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// stop if element was spraed by internal directive
|
|
62
|
+
if (memo.has(el)) return el[_dispose] && disposes.push(el[_dispose])
|
|
63
|
+
|
|
64
|
+
// stop if element is skipped (detached) like in case of :if or :each
|
|
65
|
+
if (el.parentNode !== parent) return
|
|
66
|
+
} else i++;
|
|
67
67
|
}
|
|
68
68
|
|
|
69
|
-
for (let child of [...el.
|
|
69
|
+
for (let child of [...el.childNodes]) init(child, el);
|
|
70
70
|
};
|
|
71
71
|
}
|
|
72
72
|
|
|
@@ -97,3 +97,28 @@ sprae.use = s => {
|
|
|
97
97
|
s.signal && use(s);
|
|
98
98
|
s.compile && (compile = s.compile);
|
|
99
99
|
}
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
// instantiated <template> fragment holder, like persisting fragment but with minimal API surface
|
|
103
|
+
export const frag = (tpl) => {
|
|
104
|
+
if (!tpl.nodeType) return tpl // existing tpl
|
|
105
|
+
|
|
106
|
+
// ensure at least one node
|
|
107
|
+
tpl.content.appendChild(document.createTextNode(''))
|
|
108
|
+
|
|
109
|
+
let content = tpl.content.cloneNode(true),
|
|
110
|
+
attributes = [...tpl.attributes],
|
|
111
|
+
childNodes = [...content.childNodes]
|
|
112
|
+
|
|
113
|
+
return {
|
|
114
|
+
childNodes,
|
|
115
|
+
content,
|
|
116
|
+
remove: () => content.append(...childNodes),
|
|
117
|
+
replaceWith(el) {
|
|
118
|
+
childNodes[0].before(el)
|
|
119
|
+
content.append(...childNodes)
|
|
120
|
+
},
|
|
121
|
+
attributes,
|
|
122
|
+
removeAttribute(name) { attributes.splice(attributes.findIndex(a => a.name === name), 1) }
|
|
123
|
+
}
|
|
124
|
+
}
|
package/directive/each.js
CHANGED
|
@@ -1,13 +1,11 @@
|
|
|
1
|
-
import sprae, { directive } from "../core.js";
|
|
1
|
+
import sprae, { directive, frag } from "../core.js";
|
|
2
2
|
import store, { _change, _signals } from "../store.js";
|
|
3
3
|
import { effect, untracked, computed } from '../signal.js';
|
|
4
4
|
|
|
5
5
|
|
|
6
|
-
export const _each = Symbol(":each");
|
|
7
|
-
|
|
8
6
|
directive.each = (tpl, [itemVar, idxVar, evaluate], state) => {
|
|
9
7
|
// we need :if to be able to replace holder instead of tpl for :if :each case
|
|
10
|
-
const holder = (
|
|
8
|
+
const holder = (document.createTextNode(""));
|
|
11
9
|
tpl.replaceWith(holder);
|
|
12
10
|
|
|
13
11
|
// we re-create items any time new items are produced
|
|
@@ -58,18 +56,14 @@ directive.each = (tpl, [itemVar, idxVar, evaluate], state) => {
|
|
|
58
56
|
[itemVar]: cur[_signals]?.[idx] || cur[idx],
|
|
59
57
|
[idxVar]: keys ? keys[idx] : idx
|
|
60
58
|
}, state),
|
|
61
|
-
el =
|
|
62
|
-
frag = tpl.content ?
|
|
63
|
-
// fake fragment to allow sprae multiple elements
|
|
64
|
-
{ children: [...el.children], remove() { this.children.map(el => el.remove()) } } :
|
|
65
|
-
el;
|
|
59
|
+
el = tpl.content ? frag(tpl) : tpl.cloneNode(true);
|
|
66
60
|
|
|
67
|
-
holder.before(el);
|
|
68
|
-
sprae(
|
|
61
|
+
holder.before(el.content || el);
|
|
62
|
+
sprae(el, scope);
|
|
69
63
|
|
|
70
64
|
// signal/holder disposal removes element
|
|
71
65
|
((cur[_signals] ||= [])[i] ||= {})[Symbol.dispose] = () => {
|
|
72
|
-
|
|
66
|
+
el[Symbol.dispose](), el.remove()
|
|
73
67
|
};
|
|
74
68
|
}
|
|
75
69
|
}
|
package/directive/if.js
CHANGED
|
@@ -1,48 +1,35 @@
|
|
|
1
|
-
import sprae, { directive, memo } from "../core.js";
|
|
2
|
-
import { _each } from './each.js';
|
|
1
|
+
import sprae, { directive, memo, frag } from "../core.js";
|
|
3
2
|
import { effect } from "../signal.js";
|
|
4
3
|
|
|
5
4
|
// :if is interchangeable with :each depending on order, :if :each or :each :if have different meanings
|
|
6
5
|
// as for :if :with - :if must init first, since it is lazy, to avoid initializing component ahead of time by :with
|
|
7
6
|
// we consider :with={x} :if={x} case insignificant
|
|
8
7
|
const _prevIf = Symbol("if");
|
|
9
|
-
directive.if = (
|
|
10
|
-
let
|
|
11
|
-
next = ifEl.nextElementSibling,
|
|
8
|
+
directive.if = (el, evaluate, state) => {
|
|
9
|
+
let next = el.nextElementSibling,
|
|
12
10
|
holder = document.createTextNode(''),
|
|
11
|
+
curEl, ifEl, elseEl;
|
|
13
12
|
|
|
14
|
-
|
|
15
|
-
cur, ifs, elses, none = [];
|
|
13
|
+
el.replaceWith(holder)
|
|
16
14
|
|
|
17
|
-
ifEl
|
|
18
|
-
|
|
19
|
-
ifEl.remove(), cur = none
|
|
20
|
-
|
|
21
|
-
ifs = ifEl.content ? [...ifEl.content.childNodes] : [ifEl]
|
|
15
|
+
ifEl = el.content ? frag(el) : el
|
|
16
|
+
memo.set(ifEl, null) // mark all el as fake-spraed, because we have to sprae for real on insert
|
|
22
17
|
|
|
23
18
|
if (next?.hasAttribute(":else")) {
|
|
24
19
|
next.removeAttribute(":else");
|
|
25
20
|
// if next is :else :if - leave it for its own :if handler
|
|
26
|
-
if (next.hasAttribute(":if"))
|
|
27
|
-
|
|
28
|
-
} else elses = none;
|
|
29
|
-
|
|
30
|
-
// we mark all els as fake-spraed, because we have to sprae for real on insert
|
|
31
|
-
for (let el of [...ifs, ...elses]) memo.set(el, null)
|
|
21
|
+
if (!next.hasAttribute(":if")) next.remove(), elseEl = next.content ? frag(next) : next, memo.set(elseEl, null)
|
|
22
|
+
}
|
|
32
23
|
|
|
33
24
|
return effect(() => {
|
|
34
|
-
const
|
|
35
|
-
if (next) next[_prevIf] =
|
|
36
|
-
if (
|
|
37
|
-
|
|
38
|
-
if (
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
for (let el of cur) {
|
|
43
|
-
parent.insertBefore(el, holder)
|
|
44
|
-
memo.get(el) === null && memo.delete(el) // remove fake memo to sprae as new
|
|
45
|
-
sprae(el, state)
|
|
25
|
+
const newEl = evaluate(state) ? ifEl : el[_prevIf] ? null : elseEl;
|
|
26
|
+
if (next) next[_prevIf] = newEl === ifEl
|
|
27
|
+
if (curEl != newEl) {
|
|
28
|
+
curEl?.remove()
|
|
29
|
+
if (curEl = newEl) {
|
|
30
|
+
holder.before(curEl.content || curEl)
|
|
31
|
+
memo.get(curEl) === null && memo.delete(curEl) // remove fake memo to sprae as new
|
|
32
|
+
sprae(curEl, state)
|
|
46
33
|
}
|
|
47
34
|
}
|
|
48
35
|
});
|
package/directive/text.js
CHANGED
|
@@ -1,9 +1,14 @@
|
|
|
1
|
-
import { directive } from "../core.js";
|
|
1
|
+
import { directive, frag } from "../core.js";
|
|
2
2
|
import { effect } from "../signal.js";
|
|
3
3
|
|
|
4
4
|
// set text content
|
|
5
5
|
directive.text = (el, evaluate, state) => {
|
|
6
|
-
|
|
6
|
+
// <template :text="a"/> or previously initialized template
|
|
7
|
+
if (el.content) {
|
|
8
|
+
let tplfrag = frag(el)
|
|
9
|
+
if (el !== tplfrag) el.replaceWith(tplfrag.content);
|
|
10
|
+
el = tplfrag.childNodes[0];
|
|
11
|
+
}
|
|
7
12
|
|
|
8
13
|
return effect(() => {
|
|
9
14
|
let value = evaluate(state);
|
package/dist/sprae.js
CHANGED
|
@@ -126,7 +126,7 @@ var _dispose = Symbol.dispose ||= Symbol("dispose");
|
|
|
126
126
|
var directive = {};
|
|
127
127
|
var memo = /* @__PURE__ */ new WeakMap();
|
|
128
128
|
function sprae(el, values) {
|
|
129
|
-
if (!el?.
|
|
129
|
+
if (!el?.childNodes)
|
|
130
130
|
return;
|
|
131
131
|
if (memo.has(el)) {
|
|
132
132
|
return Object.assign(memo.get(el), values);
|
|
@@ -142,28 +142,28 @@ function sprae(el, values) {
|
|
|
142
142
|
};
|
|
143
143
|
return state;
|
|
144
144
|
function init(el2, parent = el2.parentNode) {
|
|
145
|
-
if (el2.
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
145
|
+
if (!el2.childNodes)
|
|
146
|
+
return;
|
|
147
|
+
for (let i = 0; i < el2.attributes?.length; ) {
|
|
148
|
+
let attr2 = el2.attributes[i];
|
|
149
|
+
if (attr2.name[0] === ":") {
|
|
150
|
+
el2.removeAttribute(attr2.name);
|
|
151
|
+
let names = attr2.name.slice(1).split(":");
|
|
152
|
+
for (let name of names) {
|
|
153
|
+
let dir = directive[name] || directive.default;
|
|
154
|
+
let evaluate = (dir.parse || parse)(attr2.value, parse);
|
|
155
|
+
let dispose = dir(el2, evaluate, state, name);
|
|
156
|
+
if (dispose)
|
|
157
|
+
disposes.push(dispose);
|
|
158
|
+
}
|
|
159
|
+
if (memo.has(el2))
|
|
160
|
+
return el2[_dispose] && disposes.push(el2[_dispose]);
|
|
161
|
+
if (el2.parentNode !== parent)
|
|
162
|
+
return;
|
|
163
|
+
} else
|
|
164
|
+
i++;
|
|
165
165
|
}
|
|
166
|
-
for (let child of [...el2.
|
|
166
|
+
for (let child of [...el2.childNodes])
|
|
167
167
|
init(child, el2);
|
|
168
168
|
}
|
|
169
169
|
;
|
|
@@ -191,6 +191,25 @@ sprae.use = (s) => {
|
|
|
191
191
|
s.signal && use(s);
|
|
192
192
|
s.compile && (compile = s.compile);
|
|
193
193
|
};
|
|
194
|
+
var frag = (tpl) => {
|
|
195
|
+
if (!tpl.nodeType)
|
|
196
|
+
return tpl;
|
|
197
|
+
tpl.content.appendChild(document.createTextNode(""));
|
|
198
|
+
let content = tpl.content.cloneNode(true), attributes = [...tpl.attributes], childNodes = [...content.childNodes];
|
|
199
|
+
return {
|
|
200
|
+
childNodes,
|
|
201
|
+
content,
|
|
202
|
+
remove: () => content.append(...childNodes),
|
|
203
|
+
replaceWith(el) {
|
|
204
|
+
childNodes[0].before(el);
|
|
205
|
+
content.append(...childNodes);
|
|
206
|
+
},
|
|
207
|
+
attributes,
|
|
208
|
+
removeAttribute(name) {
|
|
209
|
+
attributes.splice(attributes.findIndex((a) => a.name === name), 1);
|
|
210
|
+
}
|
|
211
|
+
};
|
|
212
|
+
};
|
|
194
213
|
|
|
195
214
|
// node_modules/ulive/dist/ulive.es.js
|
|
196
215
|
var ulive_es_exports = {};
|
|
@@ -256,10 +275,36 @@ var batch2 = (fn) => {
|
|
|
256
275
|
};
|
|
257
276
|
var untracked2 = (fn, prev, v) => (prev = current, current = null, v = fn(), current = prev, v);
|
|
258
277
|
|
|
278
|
+
// directive/if.js
|
|
279
|
+
var _prevIf = Symbol("if");
|
|
280
|
+
directive.if = (el, evaluate, state) => {
|
|
281
|
+
let next = el.nextElementSibling, holder = document.createTextNode(""), curEl, ifEl, elseEl;
|
|
282
|
+
el.replaceWith(holder);
|
|
283
|
+
ifEl = el.content ? frag(el) : el;
|
|
284
|
+
memo.set(ifEl, null);
|
|
285
|
+
if (next?.hasAttribute(":else")) {
|
|
286
|
+
next.removeAttribute(":else");
|
|
287
|
+
if (!next.hasAttribute(":if"))
|
|
288
|
+
next.remove(), elseEl = next.content ? frag(next) : next, memo.set(elseEl, null);
|
|
289
|
+
}
|
|
290
|
+
return effect(() => {
|
|
291
|
+
const newEl = evaluate(state) ? ifEl : el[_prevIf] ? null : elseEl;
|
|
292
|
+
if (next)
|
|
293
|
+
next[_prevIf] = newEl === ifEl;
|
|
294
|
+
if (curEl != newEl) {
|
|
295
|
+
curEl?.remove();
|
|
296
|
+
if (curEl = newEl) {
|
|
297
|
+
holder.before(curEl.content || curEl);
|
|
298
|
+
memo.get(curEl) === null && memo.delete(curEl);
|
|
299
|
+
sprae(curEl, state);
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
});
|
|
303
|
+
};
|
|
304
|
+
|
|
259
305
|
// directive/each.js
|
|
260
|
-
var _each = Symbol(":each");
|
|
261
306
|
directive.each = (tpl, [itemVar, idxVar, evaluate], state) => {
|
|
262
|
-
const holder =
|
|
307
|
+
const holder = document.createTextNode("");
|
|
263
308
|
tpl.replaceWith(holder);
|
|
264
309
|
let cur, keys2, prevl = 0;
|
|
265
310
|
const items = computed(() => {
|
|
@@ -295,13 +340,11 @@ directive.each = (tpl, [itemVar, idxVar, evaluate], state) => {
|
|
|
295
340
|
let idx = i, scope = store({
|
|
296
341
|
[itemVar]: cur[_signals]?.[idx] || cur[idx],
|
|
297
342
|
[idxVar]: keys2 ? keys2[idx] : idx
|
|
298
|
-
}, state), el =
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
holder.before(el);
|
|
302
|
-
sprae(frag, scope);
|
|
343
|
+
}, state), el = tpl.content ? frag(tpl) : tpl.cloneNode(true);
|
|
344
|
+
holder.before(el.content || el);
|
|
345
|
+
sprae(el, scope);
|
|
303
346
|
((cur[_signals] ||= [])[i] ||= {})[Symbol.dispose] = () => {
|
|
304
|
-
|
|
347
|
+
el[Symbol.dispose](), el.remove();
|
|
305
348
|
};
|
|
306
349
|
}
|
|
307
350
|
}
|
|
@@ -324,42 +367,6 @@ directive.each.parse = (expr, parse2) => {
|
|
|
324
367
|
return [itemVar, idxVar, parse2(itemsExpr)];
|
|
325
368
|
};
|
|
326
369
|
|
|
327
|
-
// directive/if.js
|
|
328
|
-
var _prevIf = Symbol("if");
|
|
329
|
-
directive.if = (ifEl, evaluate, state) => {
|
|
330
|
-
let parent = ifEl.parentNode, next = ifEl.nextElementSibling, holder = document.createTextNode(""), cur, ifs, elses, none = [];
|
|
331
|
-
ifEl.after(holder);
|
|
332
|
-
ifEl.remove(), cur = none;
|
|
333
|
-
ifs = ifEl.content ? [...ifEl.content.childNodes] : [ifEl];
|
|
334
|
-
if (next?.hasAttribute(":else")) {
|
|
335
|
-
next.removeAttribute(":else");
|
|
336
|
-
if (next.hasAttribute(":if"))
|
|
337
|
-
elses = none;
|
|
338
|
-
else
|
|
339
|
-
next.remove(), elses = next.content ? [...next.content.childNodes] : [next];
|
|
340
|
-
} else
|
|
341
|
-
elses = none;
|
|
342
|
-
for (let el of [...ifs, ...elses])
|
|
343
|
-
memo.set(el, null);
|
|
344
|
-
return effect(() => {
|
|
345
|
-
const newEls = evaluate(state) ? ifs : ifEl[_prevIf] ? none : elses;
|
|
346
|
-
if (next)
|
|
347
|
-
next[_prevIf] = newEls === ifs;
|
|
348
|
-
if (cur != newEls) {
|
|
349
|
-
if (cur[0]?.[_each])
|
|
350
|
-
cur = [cur[0][_each]];
|
|
351
|
-
for (let el of cur)
|
|
352
|
-
el.remove();
|
|
353
|
-
cur = newEls;
|
|
354
|
-
for (let el of cur) {
|
|
355
|
-
parent.insertBefore(el, holder);
|
|
356
|
-
memo.get(el) === null && memo.delete(el);
|
|
357
|
-
sprae(el, state);
|
|
358
|
-
}
|
|
359
|
-
}
|
|
360
|
-
});
|
|
361
|
-
};
|
|
362
|
-
|
|
363
370
|
// directive/ref.js
|
|
364
371
|
directive.ref = (el, expr, state) => {
|
|
365
372
|
state[expr] = el;
|
|
@@ -387,8 +394,12 @@ directive.html = (el, evaluate, state) => {
|
|
|
387
394
|
|
|
388
395
|
// directive/text.js
|
|
389
396
|
directive.text = (el, evaluate, state) => {
|
|
390
|
-
if (el.content)
|
|
391
|
-
|
|
397
|
+
if (el.content) {
|
|
398
|
+
let tplfrag = frag(el);
|
|
399
|
+
if (el !== tplfrag)
|
|
400
|
+
el.replaceWith(tplfrag.content);
|
|
401
|
+
el = tplfrag.childNodes[0];
|
|
402
|
+
}
|
|
392
403
|
return effect(() => {
|
|
393
404
|
let value = evaluate(state);
|
|
394
405
|
el.textContent = value == null ? "" : value;
|
package/dist/sprae.min.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
var e,t,r,l,n,s=Object.defineProperty,o=Symbol("signals"),a=Symbol("length");function i(t,r){if(!t)return t;if(t[o])return t;if(Array.isArray(t))return function(t){let r;if(t[o])return t;let l=e(t.length),n=Array(t.length).fill();const s=new Proxy(n,{get:(s,c)=>"symbol"==typeof c?c===a?l:c===o?n:n[c]:"length"===c?u[r]?l.peek():l.value:(r=c,n[c]?n[c].valueOf():c<n.length?(n[c]=e(i(t[c]))).value:void 0),set(e,t,r){if("length"===t){for(let e=r,t=n.length;e<t;e++)delete s[e];return l.value=n.length=r,!0}return c(n,t,r),t>=l.peek()&&(l.value=n.length=Number(t)+1),!0},deleteProperty:(e,t)=>(n[t]&&f(n,t),1)});return s}(t);if(t.constructor!==Object)return t;let l={...r?.[o]},s=e(Object.values(t).length);const p=new Proxy(l,{get:(e,t)=>t===a?s:t===o?l:l[t]?.valueOf(),set:(e,t,r,n)=>(n=l[t],c(l,t,r),n??++s.value,1),deleteProperty:(e,t)=>(l[t]&&(f(l,t),s.value--),1),ownKeys:()=>(s.value,Reflect.ownKeys(l))});for(let e in t){const r=Object.getOwnPropertyDescriptor(t,e);r?.get?(l[e]=n(r.get.bind(p)))._set=r.set?.bind(p):(l[e]=void 0,c(l,e,t[e]))}return p}var u={push:1,pop:1,shift:1,unshift:1,splice:1};function c(t,n,s){let o=t[n];if("_"===n[0])t[n]=s;else if(o)if(s===o.peek());else if(o._set)o._set(s);else if(Array.isArray(s)&&Array.isArray(o.peek())){const e=o.peek();e[a]?r((()=>{l((()=>{let t=0,r=s.length;for(;t<r;t++)e[t]=s[t];e.length=r}))})):o.value=s}else o.value=i(s);else t[n]=o=s?.peek?s:e(i(s))}function f(e,t){const r=e[t],l=r[Symbol.dispose];l&&delete r[Symbol.dispose],delete e[t],l?.()}var p=Symbol.dispose||=Symbol("dispose"),d={},y=new WeakMap;function h(e,t){if(!e?.
|
|
1
|
+
var e,t,r,l,n,s=Object.defineProperty,o=Symbol("signals"),a=Symbol("length");function i(t,r){if(!t)return t;if(t[o])return t;if(Array.isArray(t))return function(t){let r;if(t[o])return t;let l=e(t.length),n=Array(t.length).fill();const s=new Proxy(n,{get:(s,c)=>"symbol"==typeof c?c===a?l:c===o?n:n[c]:"length"===c?u[r]?l.peek():l.value:(r=c,n[c]?n[c].valueOf():c<n.length?(n[c]=e(i(t[c]))).value:void 0),set(e,t,r){if("length"===t){for(let e=r,t=n.length;e<t;e++)delete s[e];return l.value=n.length=r,!0}return c(n,t,r),t>=l.peek()&&(l.value=n.length=Number(t)+1),!0},deleteProperty:(e,t)=>(n[t]&&f(n,t),1)});return s}(t);if(t.constructor!==Object)return t;let l={...r?.[o]},s=e(Object.values(t).length);const p=new Proxy(l,{get:(e,t)=>t===a?s:t===o?l:l[t]?.valueOf(),set:(e,t,r,n)=>(n=l[t],c(l,t,r),n??++s.value,1),deleteProperty:(e,t)=>(l[t]&&(f(l,t),s.value--),1),ownKeys:()=>(s.value,Reflect.ownKeys(l))});for(let e in t){const r=Object.getOwnPropertyDescriptor(t,e);r?.get?(l[e]=n(r.get.bind(p)))._set=r.set?.bind(p):(l[e]=void 0,c(l,e,t[e]))}return p}var u={push:1,pop:1,shift:1,unshift:1,splice:1};function c(t,n,s){let o=t[n];if("_"===n[0])t[n]=s;else if(o)if(s===o.peek());else if(o._set)o._set(s);else if(Array.isArray(s)&&Array.isArray(o.peek())){const e=o.peek();e[a]?r((()=>{l((()=>{let t=0,r=s.length;for(;t<r;t++)e[t]=s[t];e.length=r}))})):o.value=s}else o.value=i(s);else t[n]=o=s?.peek?s:e(i(s))}function f(e,t){const r=e[t],l=r[Symbol.dispose];l&&delete r[Symbol.dispose],delete e[t],l?.()}var p=Symbol.dispose||=Symbol("dispose"),d={},y=new WeakMap;function h(e,t){if(!e?.childNodes)return;if(y.has(e))return Object.assign(y.get(e),t);const r=i(t||{}),l=[];return function e(t,n=t.parentNode){if(t.childNodes){for(let e=0;e<t.attributes?.length;){let s=t.attributes[e];if(":"===s.name[0]){t.removeAttribute(s.name);let e=s.name.slice(1).split(":");for(let n of e){let e=d[n]||d.default,o=e(t,(e.parse||g)(s.value,g),r,n);o&&l.push(o)}if(y.has(t))return t[p]&&l.push(t[p]);if(t.parentNode!==n)return}else e++}for(let r of[...t.childNodes])e(r,t)}}(e),y.has(e)||y.set(e,r),e[p]=()=>{for(;l.length;)l.pop()();y.delete(e)},r}var m,v={},g=(e,t,r)=>{if(r=v[e=e.trim()])return r;try{r=m(e)}catch(r){b(r,t,e)}return v[e]=r},b=(e,t,r="")=>{throw Object.assign(e,{message:`∴ ${e.message}\n\n${t}${r?`="${r}"\n\n`:""}`,expr:r})};h.use=s=>{s.signal&&function(s){e=s.signal,t=s.effect,n=s.computed,l=s.batch||(e=>e()),r=s.untracked||l}(s),s.compile&&(m=s.compile)};var k,A,w=e=>{if(!e.nodeType)return e;e.content.appendChild(document.createTextNode(""));let t=e.content.cloneNode(!0),r=[...e.attributes],l=[...t.childNodes];return{childNodes:l,content:t,remove:()=>t.append(...l),replaceWith(e){l[0].before(e),t.append(...l)},attributes:r,removeAttribute(e){r.splice(r.findIndex((t=>t.name===e)),1)}}},S={};((e,t)=>{for(var r in t)s(e,r,{get:t[r],enumerable:!0})})(S,{batch:()=>W,computed:()=>x,effect:()=>O,signal:()=>N,untracked:()=>j});var N=(e,t,r=new Set)=>((t={get value(){return k?.deps.push(r.add(k)),e},set value(t){if(t!==e){e=t;for(let e of r)A?A.add(e):e()}},peek:()=>e}).toJSON=t.then=t.toString=t.valueOf=()=>t.value,t),O=(e,t,r,l)=>(l=(r=l=>{t?.call?.(),l=k,k=r;try{t=e()}finally{k=l}}).deps=[],r(),e=>{for(t?.call?.();e=l.pop();)e.delete(r)}),x=(e,t=N(),r,l)=>((r={get value(){return l||=O((()=>t.value=e())),t.value},peek:t.peek}).toJSON=r.then=r.toString=r.valueOf=()=>r.value,r),W=e=>{let t=A;t||(A=new Set);try{e()}finally{if(!t){t=A,A=null;for(const e of t)e()}}},j=(e,t,r)=>(t=k,k=null,r=e(),k=t,r),T=Symbol("if");d.if=(e,r,l)=>{let n,s,o,a=e.nextElementSibling,i=document.createTextNode("");return e.replaceWith(i),s=e.content?w(e):e,y.set(s,null),a?.hasAttribute(":else")&&(a.removeAttribute(":else"),a.hasAttribute(":if")||(a.remove(),o=a.content?w(a):a,y.set(o,null))),t((()=>{const t=r(l)?s:e[T]?null:o;a&&(a[T]=t===s),n!=t&&(n?.remove(),(n=t)&&(i.before(n.content||n),null===y.get(n)&&y.delete(n),h(n,l)))}))},d.each=(e,[l,s,u],c)=>{const f=document.createTextNode("");e.replaceWith(f);let p,d,y=0;const m=n((()=>{d=null;let e=u(c);return"number"==typeof e&&(e=Array.from({length:e},((e,t)=>t+1))),e?.constructor===Object&&(d=Object.keys(e),e=Object.values(e)),e||[]})),v=()=>{r((()=>{let t=0,r=m.value,n=r.length;if(p&&!p[a]){for(let e of p[o]||[])e[Symbol.dispose]();p=null,y=0}if(n<y)p.length=n;else{if(p)for(;t<y;t++)p[t]=r[t];else p=r;for(;t<n;t++){p[t]=r[t];let n=t,a=i({[l]:p[o]?.[n]||p[n],[s]:d?d[n]:n},c),u=e.content?w(e):e.cloneNode(!0);f.before(u.content||u),h(u,a),((p[o]||=[])[t]||={})[Symbol.dispose]=()=>{u[Symbol.dispose](),u.remove()}}}y=n}))};let g=0;return t((()=>{m.value[a]?.value,g?g++:(v(),queueMicrotask((()=>(g&&v(),g=0))))}))},d.each.parse=(e,t)=>{let[r,l]=e.split(/\s+in\s+/),[n,s="$"]=r.split(/\s*,\s*/);return[n,s,t(l)]},d.ref=(e,t,r)=>{r[t]=e},d.ref.parse=e=>e,d.with=(e,r,l)=>{let n;return t((()=>{let t=r(l);h(e,n?t:n=i(t,l))}))},d.html=(e,t,r)=>{let l=t(r);if(!l)return;let n=(l.content||l).cloneNode(!0);e.replaceChildren(n),h(e,r)},d.text=(e,r,l)=>{if(e.content){let t=w(e);e!==t&&e.replaceWith(t.content),e=t.childNodes[0]}return t((()=>{let t=r(l);e.textContent=null==t?"":t}))},d.class=(e,r,l)=>{let n=new Set;return t((()=>{let t=r(l),s=new Set;t&&("string"==typeof t?t.split(" ").map((e=>s.add(e))):Array.isArray(t)?t.map((e=>e&&s.add(e))):Object.entries(t).map((([e,t])=>t&&s.add(e))));for(let t of n)s.has(t)?s.delete(t):e.classList.remove(t);for(let t of n=s)e.classList.add(t)}))},d.style=(e,r,l)=>{let n=e.getAttribute("style")||"";return n.endsWith(";")||(n+="; "),t((()=>{let t=r(l);if("string"==typeof t)e.setAttribute("style",n+t);else{e.setAttribute("style",n);for(let r in t)e.style.setProperty(r,t[r])}}))},d.default=(e,r,l,n)=>{if(!n.startsWith("on"))return t((()=>{let t=r(l);if(n)E(e,n,t);else for(let r in t)E(e,D(r),t[r])}));const s=n.split("..").map((t=>{let r={evt:"",target:e,test:()=>!0};return r.evt=(t.startsWith("on")?t.slice(2):t).replace(/\.(\w+)?-?([-\w]+)?/g,((e,t,l="")=>(r.test=$[t]?.(r,...l.split("-"))||r.test,""))),r}));if(1==s.length)return t((()=>f(r(l),s[0])));let o,a,i,u=0;const c=e=>{i=f((t=>(i(),a=e?.(t),(u=++u%s.length)?c(a):o&&c(o))),s[u])};return t((()=>(o=r(l),!i&&c(o),()=>o=null)));function f(e,{evt:t,target:r,test:l,defer:n,stop:s,prevent:o,immediate:a,...i}){n&&(e=n(e));const u=r=>{try{l(r)&&(s&&(a?r.stopImmediatePropagation():r.stopPropagation()),o&&r.preventDefault(),e?.(r))}catch(r){b(r,`:on${t}`,e)}};return r.addEventListener(t,u,i),()=>r.removeEventListener(t,u,i)}};var $={prevent(e){e.prevent=!0},stop(e){e.stop=!0},immediate(e){e.immediate=!0},once(e){e.once=!0},passive(e){e.passive=!0},capture(e){e.capture=!0},window(e){e.target=window},document(e){e.target=document},throttle(e,t){e.defer=e=>P(e,t?Number(t)||0:108)},debounce(e,t){e.defer=e=>_(e,t?Number(t)||0:108)},outside:e=>t=>{let r=e.target;return!(r.contains(t.target)||!1===t.target.isConnected||r.offsetWidth<1&&r.offsetHeight<1)},self:e=>t=>t.target===e.target,ctrl:(e,...t)=>e=>C.ctrl(e)&&t.every((t=>C[t]?C[t](e):e.key===t)),shift:(e,...t)=>e=>C.shift(e)&&t.every((t=>C[t]?C[t](e):e.key===t)),alt:(e,...t)=>e=>C.alt(e)&&t.every((t=>C[t]?C[t](e):e.key===t)),meta:(e,...t)=>e=>C.meta(e)&&t.every((t=>C[t]?C[t](e):e.key===t)),arrow:()=>C.arrow,enter:()=>C.enter,esc:()=>C.esc,tab:()=>C.tab,space:()=>C.space,delete:()=>C.delete,digit:()=>C.digit,letter:()=>C.letter,char:()=>C.char},C={ctrl:e=>e.ctrlKey||"Control"===e.key||"Ctrl"===e.key,shift:e=>e.shiftKey||"Shift"===e.key,alt:e=>e.altKey||"Alt"===e.key,meta:e=>e.metaKey||"Meta"===e.key||"Command"===e.key,arrow:e=>e.key.startsWith("Arrow"),enter:e=>"Enter"===e.key,esc:e=>e.key.startsWith("Esc"),tab:e=>"Tab"===e.key,space:e=>" "===e.key||"Space"===e.key||" "===e.key,delete:e=>"Delete"===e.key||"Backspace"===e.key,digit:e=>/^\d$/.test(e.key),letter:e=>/^\p{L}$/gu.test(e.key),char:e=>/^\S$/.test(e.key)},E=(e,t,r)=>{null==r||!1===r?e.removeAttribute(t):e.setAttribute(t,!0===r?"":"number"==typeof r||"string"==typeof r?r:"")},P=(e,t)=>{let r,l,n=s=>{r=!0,setTimeout((()=>{if(r=!1,l)return l=!1,n(s),e(s)}),t)};return t=>r?l=!0:(n(t),e(t))},_=(e,t)=>{let r;return l=>{clearTimeout(r),r=setTimeout((()=>{r=null,e(l)}),t)}},D=e=>e.replace(/[A-Z\u00C0-\u00D6\u00D8-\u00DE]/g,(e=>"-"+e.toLowerCase()));d.value=(e,r,l)=>{let n,s,o="text"===e.type||""===e.type?t=>e.setAttribute("value",e.value=null==t?"":t):"TEXTAREA"===e.tagName||"text"===e.type||""===e.type?t=>(n=e.selectionStart,s=e.selectionEnd,e.setAttribute("value",e.value=null==t?"":t),n&&e.setSelectionRange(n,s)):"checkbox"===e.type?t=>(e.checked=t,E(e,"checked",t)):"select-one"===e.type?t=>{for(let t in e.options)t.removeAttribute("selected");e.value=t,e.selectedOptions[0]?.setAttribute("selected","")}:t=>e.value=t;return t((()=>o(r(l))))},d.fx=(e,r,l)=>t((()=>r(l))),h.use(S),h.use({compile:e=>h.constructor("__scope",`with (__scope) { return ${e} };`)});var K=h;export{K as default};
|
package/package.json
CHANGED
package/readme.md
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
_Sprae_ is open & minimalistic progressive enhancement framework.<br/>
|
|
6
6
|
Perfect for small-scale websites, static pages, landings, prototypes, or lightweight UI.<br/>
|
|
7
|
-
|
|
7
|
+
A light, fast and sweet alternative to alpine, petit-vue etc.
|
|
8
8
|
|
|
9
9
|
## Usage
|
|
10
10
|
|
|
@@ -24,7 +24,7 @@ Perfect for small-scale websites, static pages, landings, prototypes, or lightwe
|
|
|
24
24
|
</script>
|
|
25
25
|
```
|
|
26
26
|
|
|
27
|
-
Sprae evaluates `:`-directives and evaporates them, returning reactive state.
|
|
27
|
+
Sprae evaluates `:`-directives and evaporates them, returning reactive state for updates.
|
|
28
28
|
|
|
29
29
|
## Directives
|
|
30
30
|
|
|
@@ -59,9 +59,6 @@ Multiply element.
|
|
|
59
59
|
<dt :text="item.term"/>
|
|
60
60
|
<dd :text="item.definition"/>
|
|
61
61
|
</template>
|
|
62
|
-
|
|
63
|
-
<!-- prevent FOUC -->
|
|
64
|
-
<style>[:each] {visibility: hidden}</style>
|
|
65
62
|
```
|
|
66
63
|
|
|
67
64
|
#### `:text="value"`
|
|
@@ -271,7 +268,8 @@ Trigger when element is connected / disconnected from DOM.
|
|
|
271
268
|
|
|
272
269
|
## Signals
|
|
273
270
|
|
|
274
|
-
Sprae
|
|
271
|
+
Sprae uses signals for reactivity and can take signal values as inputs.
|
|
272
|
+
Signals provider can be switched to any preact-flavored implementation:
|
|
275
273
|
|
|
276
274
|
```js
|
|
277
275
|
import sprae from 'sprae';
|
|
@@ -346,6 +344,7 @@ import * as signals from '@preact/signals'
|
|
|
346
344
|
import compile from 'subscript'
|
|
347
345
|
|
|
348
346
|
// standard directives
|
|
347
|
+
import 'sprae/directive/default.js'
|
|
349
348
|
import 'sprae/directive/if.js'
|
|
350
349
|
import 'sprae/directive/text.js'
|
|
351
350
|
|
|
@@ -361,20 +360,25 @@ sprae.use(signals)
|
|
|
361
360
|
sprae.use({ compile })
|
|
362
361
|
```
|
|
363
362
|
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
To destroy state and detach sprae handlers, call `element[Symbol.dispose]()`. -->
|
|
363
|
+
## Hints
|
|
367
364
|
|
|
365
|
+
* To prevent [FOUC](https://en.wikipedia.org/wiki/Flash_of_unstyled_content) add `<style>[:each],[:if],[:else] {visibility: hidden}</style>`
|
|
366
|
+
* Attributes order matters, eg. `<li :each="el in els" :text="el.name"></li>` is not the same as `<li :text="el.name" :each="el in els"></li>`
|
|
367
|
+
* To destroy state and detach sprae handlers, call `element[Symbol.dispose]()`.
|
|
368
|
+
* State getters/setters work as computed effects, eg. `sprae(el, { x:1, get x2(){ return this.x * 2} })`.
|
|
369
|
+
* `this` keyword is not used, to get access to current element use `<input :ref="el" :text="el.value"/>`
|
|
370
|
+
* Async/await is not supported in attributes, it's a strong indicator you need to put these methods into state.
|
|
368
371
|
|
|
369
372
|
## Justification
|
|
370
373
|
|
|
371
374
|
* [Template-parts](https://github.com/dy/template-parts) is stuck with native HTML quirks ([parsing table](https://github.com/github/template-parts/issues/24), [SVG attributes](https://github.com/github/template-parts/issues/25), [liquid syntax](https://shopify.github.io/liquid/tags/template/#raw) conflict etc).
|
|
372
375
|
* [Alpine](https://github.com/alpinejs/alpine) / [petite-vue](https://github.com/vuejs/petite-vue) / [lucia](https://github.com/aidenyabi/lucia) escape native HTML quirks, but have excessive API (`:`, `x-`, `{}`, `@`, `$`), tend to [self-encapsulate](https://github.com/alpinejs/alpine/discussions/3223) and not care about size/performance.
|
|
373
376
|
|
|
374
|
-
_Sprae_ holds open & minimalistic philosophy:
|
|
377
|
+
_Sprae_ holds open, sweet & minimalistic philosophy:
|
|
375
378
|
* Slim `:` API and _signals_ reactivity.
|
|
376
379
|
* Pluggable directives & configurable internals.
|
|
377
380
|
* Small, safe & performant.
|
|
381
|
+
* Bits of organic sugar.
|
|
378
382
|
* Aims at making developers happy 🫰
|
|
379
383
|
|
|
380
384
|
<!--
|