sprae 10.6.3 → 10.8.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/core.js +54 -29
- package/directive/class.js +2 -3
- package/directive/default.js +2 -7
- package/directive/each.js +6 -12
- package/directive/if.js +7 -14
- package/directive/ref.js +1 -2
- package/directive/style.js +2 -3
- package/directive/text.js +7 -2
- package/dist/sprae.js +156 -142
- package/dist/sprae.min.js +1 -1
- package/package.json +1 -1
- package/readme.md +14 -16
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/class.js
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { directive } from "../core.js";
|
|
2
|
-
import { ipol } from './default.js';
|
|
3
2
|
import { effect } from "../signal.js";
|
|
4
3
|
|
|
5
4
|
directive.class = (el, evaluate, state) => {
|
|
@@ -8,8 +7,8 @@ directive.class = (el, evaluate, state) => {
|
|
|
8
7
|
let v = evaluate(state);
|
|
9
8
|
let clsx = new Set;
|
|
10
9
|
if (v) {
|
|
11
|
-
if (typeof v === "string")
|
|
12
|
-
else if (Array.isArray(v)) v.map(v =>
|
|
10
|
+
if (typeof v === "string") v.split(' ').map(cls => clsx.add(cls));
|
|
11
|
+
else if (Array.isArray(v)) v.map(v => v && clsx.add(v));
|
|
13
12
|
else Object.entries(v).map(([k, v]) => v && clsx.add(k));
|
|
14
13
|
}
|
|
15
14
|
for (let cls of cur) if (clsx.has(cls)) clsx.delete(cls); else el.classList.remove(cls);
|
package/directive/default.js
CHANGED
|
@@ -6,8 +6,8 @@ directive.default = (target, evaluate, state, name) => {
|
|
|
6
6
|
// simple prop
|
|
7
7
|
if (!name.startsWith('on')) return effect(() => {
|
|
8
8
|
let value = evaluate(state);
|
|
9
|
-
if (name) attr(target, name,
|
|
10
|
-
else for (let key in value) attr(target, dashcase(key),
|
|
9
|
+
if (name) attr(target, name, value)
|
|
10
|
+
else for (let key in value) attr(target, dashcase(key), value[key]);
|
|
11
11
|
});
|
|
12
12
|
|
|
13
13
|
// bind event to a target
|
|
@@ -155,8 +155,3 @@ const debounce = (fn, wait) => {
|
|
|
155
155
|
export const dashcase = (str) => {
|
|
156
156
|
return str.replace(/[A-Z\u00C0-\u00D6\u00D8-\u00DE]/g, (match) => "-" + match.toLowerCase());
|
|
157
157
|
}
|
|
158
|
-
|
|
159
|
-
// interpolate a$<b> fields from context
|
|
160
|
-
export const ipol = (v, state) => {
|
|
161
|
-
return v?.replace ? v.replace(/\$<([^>]+)>/g, (match, field) => state[field] ?? '') : v
|
|
162
|
-
};
|
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,5 +1,4 @@
|
|
|
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
|
|
@@ -7,24 +6,21 @@ import { effect } from "../signal.js";
|
|
|
7
6
|
// we consider :with={x} :if={x} case insignificant
|
|
8
7
|
const _prevIf = Symbol("if");
|
|
9
8
|
directive.if = (ifEl, evaluate, state) => {
|
|
10
|
-
let
|
|
11
|
-
next = ifEl.nextElementSibling,
|
|
9
|
+
let next = ifEl.nextElementSibling,
|
|
12
10
|
holder = document.createTextNode(''),
|
|
13
11
|
|
|
14
12
|
// actual replaceable els (takes <template>)
|
|
15
|
-
cur, ifs, elses
|
|
13
|
+
none = [], cur = none, ifs, elses;
|
|
16
14
|
|
|
17
|
-
ifEl.
|
|
15
|
+
ifEl.replaceWith(holder)
|
|
18
16
|
|
|
19
|
-
ifEl.
|
|
20
|
-
|
|
21
|
-
ifs = ifEl.content ? [...ifEl.content.childNodes] : [ifEl]
|
|
17
|
+
ifs = ifEl.content ? [frag(ifEl)] : [ifEl]
|
|
22
18
|
|
|
23
19
|
if (next?.hasAttribute(":else")) {
|
|
24
20
|
next.removeAttribute(":else");
|
|
25
21
|
// if next is :else :if - leave it for its own :if handler
|
|
26
22
|
if (next.hasAttribute(":if")) elses = none;
|
|
27
|
-
else next.remove(), elses = next.content ? [
|
|
23
|
+
else next.remove(), elses = next.content ? [frag(next)] : [next];
|
|
28
24
|
} else elses = none;
|
|
29
25
|
|
|
30
26
|
// we mark all els as fake-spraed, because we have to sprae for real on insert
|
|
@@ -34,13 +30,10 @@ directive.if = (ifEl, evaluate, state) => {
|
|
|
34
30
|
const newEls = evaluate(state) ? ifs : ifEl[_prevIf] ? none : elses;
|
|
35
31
|
if (next) next[_prevIf] = newEls === ifs
|
|
36
32
|
if (cur != newEls) {
|
|
37
|
-
// :if :each
|
|
38
|
-
if (cur[0]?.[_each]) cur = [cur[0][_each]]
|
|
39
|
-
|
|
40
33
|
for (let el of cur) el.remove();
|
|
41
34
|
cur = newEls;
|
|
42
35
|
for (let el of cur) {
|
|
43
|
-
|
|
36
|
+
holder.before(el.content || el)
|
|
44
37
|
memo.get(el) === null && memo.delete(el) // remove fake memo to sprae as new
|
|
45
38
|
sprae(el, state)
|
|
46
39
|
}
|
package/directive/ref.js
CHANGED
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
import { directive } from "../core.js";
|
|
2
2
|
import { _change, _signals } from "../store.js";
|
|
3
|
-
import { ipol } from './default.js';
|
|
4
3
|
|
|
5
4
|
// ref must be last within primaries, since that must be skipped by :each, but before secondaries
|
|
6
5
|
directive.ref = (el, expr, state) => {
|
|
7
|
-
state[
|
|
6
|
+
state[expr] = el
|
|
8
7
|
}
|
|
9
8
|
|
|
10
9
|
directive.ref.parse = expr => expr
|
package/directive/style.js
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { directive } from "../core.js";
|
|
2
|
-
import { ipol } from './default.js';
|
|
3
2
|
import { effect } from "../signal.js";
|
|
4
3
|
|
|
5
4
|
directive.style = (el, evaluate, state) => {
|
|
@@ -8,10 +7,10 @@ directive.style = (el, evaluate, state) => {
|
|
|
8
7
|
|
|
9
8
|
return effect(() => {
|
|
10
9
|
let v = evaluate(state);
|
|
11
|
-
if (typeof v === "string") el.setAttribute("style", initStyle +
|
|
10
|
+
if (typeof v === "string") el.setAttribute("style", initStyle + v);
|
|
12
11
|
else {
|
|
13
12
|
el.setAttribute("style", initStyle);
|
|
14
|
-
for (let k in v) el.style.setProperty(k,
|
|
13
|
+
for (let k in v) el.style.setProperty(k, v[k]);
|
|
15
14
|
}
|
|
16
15
|
});
|
|
17
16
|
};
|
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,42 @@ 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 = (ifEl, evaluate, state) => {
|
|
281
|
+
let next = ifEl.nextElementSibling, holder = document.createTextNode(""), none = [], cur = none, ifs, elses;
|
|
282
|
+
ifEl.replaceWith(holder);
|
|
283
|
+
ifs = ifEl.content ? [frag(ifEl)] : [ifEl];
|
|
284
|
+
if (next?.hasAttribute(":else")) {
|
|
285
|
+
next.removeAttribute(":else");
|
|
286
|
+
if (next.hasAttribute(":if"))
|
|
287
|
+
elses = none;
|
|
288
|
+
else
|
|
289
|
+
next.remove(), elses = next.content ? [frag(next)] : [next];
|
|
290
|
+
} else
|
|
291
|
+
elses = none;
|
|
292
|
+
for (let el of [...ifs, ...elses])
|
|
293
|
+
memo.set(el, null);
|
|
294
|
+
return effect(() => {
|
|
295
|
+
const newEls = evaluate(state) ? ifs : ifEl[_prevIf] ? none : elses;
|
|
296
|
+
if (next)
|
|
297
|
+
next[_prevIf] = newEls === ifs;
|
|
298
|
+
if (cur != newEls) {
|
|
299
|
+
for (let el of cur)
|
|
300
|
+
el.remove();
|
|
301
|
+
cur = newEls;
|
|
302
|
+
for (let el of cur) {
|
|
303
|
+
holder.before(el.content || el);
|
|
304
|
+
memo.get(el) === null && memo.delete(el);
|
|
305
|
+
sprae(el, state);
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
});
|
|
309
|
+
};
|
|
310
|
+
|
|
259
311
|
// directive/each.js
|
|
260
|
-
var _each = Symbol(":each");
|
|
261
312
|
directive.each = (tpl, [itemVar, idxVar, evaluate], state) => {
|
|
262
|
-
const holder =
|
|
313
|
+
const holder = document.createTextNode("");
|
|
263
314
|
tpl.replaceWith(holder);
|
|
264
315
|
let cur, keys2, prevl = 0;
|
|
265
316
|
const items = computed(() => {
|
|
@@ -295,13 +346,11 @@ directive.each = (tpl, [itemVar, idxVar, evaluate], state) => {
|
|
|
295
346
|
let idx = i, scope = store({
|
|
296
347
|
[itemVar]: cur[_signals]?.[idx] || cur[idx],
|
|
297
348
|
[idxVar]: keys2 ? keys2[idx] : idx
|
|
298
|
-
}, state), el =
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
holder.before(el);
|
|
302
|
-
sprae(frag, scope);
|
|
349
|
+
}, state), el = tpl.content ? frag(tpl) : tpl.cloneNode(true);
|
|
350
|
+
holder.before(el.content || el);
|
|
351
|
+
sprae(el, scope);
|
|
303
352
|
((cur[_signals] ||= [])[i] ||= {})[Symbol.dispose] = () => {
|
|
304
|
-
|
|
353
|
+
el[Symbol.dispose](), el.remove();
|
|
305
354
|
};
|
|
306
355
|
}
|
|
307
356
|
}
|
|
@@ -324,38 +373,82 @@ directive.each.parse = (expr, parse2) => {
|
|
|
324
373
|
return [itemVar, idxVar, parse2(itemsExpr)];
|
|
325
374
|
};
|
|
326
375
|
|
|
327
|
-
// directive/
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
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);
|
|
376
|
+
// directive/ref.js
|
|
377
|
+
directive.ref = (el, expr, state) => {
|
|
378
|
+
state[expr] = el;
|
|
379
|
+
};
|
|
380
|
+
directive.ref.parse = (expr) => expr;
|
|
381
|
+
|
|
382
|
+
// directive/with.js
|
|
383
|
+
directive.with = (el, evaluate, rootState) => {
|
|
384
|
+
let state;
|
|
344
385
|
return effect(() => {
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
386
|
+
let values = evaluate(rootState);
|
|
387
|
+
sprae(el, state ? values : state = store(values, rootState));
|
|
388
|
+
});
|
|
389
|
+
};
|
|
390
|
+
|
|
391
|
+
// directive/html.js
|
|
392
|
+
directive.html = (el, evaluate, state) => {
|
|
393
|
+
let tpl = evaluate(state);
|
|
394
|
+
if (!tpl)
|
|
395
|
+
return;
|
|
396
|
+
let content = (tpl.content || tpl).cloneNode(true);
|
|
397
|
+
el.replaceChildren(content);
|
|
398
|
+
sprae(el, state);
|
|
399
|
+
};
|
|
400
|
+
|
|
401
|
+
// directive/text.js
|
|
402
|
+
directive.text = (el, evaluate, state) => {
|
|
403
|
+
if (el.content) {
|
|
404
|
+
let tplfrag = frag(el);
|
|
405
|
+
if (el !== tplfrag)
|
|
406
|
+
el.replaceWith(tplfrag.content);
|
|
407
|
+
el = tplfrag.childNodes[0];
|
|
408
|
+
}
|
|
409
|
+
return effect(() => {
|
|
410
|
+
let value = evaluate(state);
|
|
411
|
+
el.textContent = value == null ? "" : value;
|
|
412
|
+
});
|
|
413
|
+
};
|
|
414
|
+
|
|
415
|
+
// directive/class.js
|
|
416
|
+
directive.class = (el, evaluate, state) => {
|
|
417
|
+
let cur = /* @__PURE__ */ new Set();
|
|
418
|
+
return effect(() => {
|
|
419
|
+
let v = evaluate(state);
|
|
420
|
+
let clsx = /* @__PURE__ */ new Set();
|
|
421
|
+
if (v) {
|
|
422
|
+
if (typeof v === "string")
|
|
423
|
+
v.split(" ").map((cls) => clsx.add(cls));
|
|
424
|
+
else if (Array.isArray(v))
|
|
425
|
+
v.map((v2) => v2 && clsx.add(v2));
|
|
426
|
+
else
|
|
427
|
+
Object.entries(v).map(([k, v2]) => v2 && clsx.add(k));
|
|
428
|
+
}
|
|
429
|
+
for (let cls of cur)
|
|
430
|
+
if (clsx.has(cls))
|
|
431
|
+
clsx.delete(cls);
|
|
432
|
+
else
|
|
433
|
+
el.classList.remove(cls);
|
|
434
|
+
for (let cls of cur = clsx)
|
|
435
|
+
el.classList.add(cls);
|
|
436
|
+
});
|
|
437
|
+
};
|
|
438
|
+
|
|
439
|
+
// directive/style.js
|
|
440
|
+
directive.style = (el, evaluate, state) => {
|
|
441
|
+
let initStyle = el.getAttribute("style") || "";
|
|
442
|
+
if (!initStyle.endsWith(";"))
|
|
443
|
+
initStyle += "; ";
|
|
444
|
+
return effect(() => {
|
|
445
|
+
let v = evaluate(state);
|
|
446
|
+
if (typeof v === "string")
|
|
447
|
+
el.setAttribute("style", initStyle + v);
|
|
448
|
+
else {
|
|
449
|
+
el.setAttribute("style", initStyle);
|
|
450
|
+
for (let k in v)
|
|
451
|
+
el.style.setProperty(k, v[k]);
|
|
359
452
|
}
|
|
360
453
|
});
|
|
361
454
|
};
|
|
@@ -366,10 +459,10 @@ directive.default = (target, evaluate, state, name) => {
|
|
|
366
459
|
return effect(() => {
|
|
367
460
|
let value = evaluate(state);
|
|
368
461
|
if (name)
|
|
369
|
-
attr(target, name,
|
|
462
|
+
attr(target, name, value);
|
|
370
463
|
else
|
|
371
464
|
for (let key in value)
|
|
372
|
-
attr(target, dashcase(key),
|
|
465
|
+
attr(target, dashcase(key), value[key]);
|
|
373
466
|
});
|
|
374
467
|
const ctxs = name.split("..").map((e) => {
|
|
375
468
|
let ctx = { evt: "", target, test: () => true };
|
|
@@ -507,85 +600,6 @@ var debounce = (fn, wait) => {
|
|
|
507
600
|
var dashcase = (str) => {
|
|
508
601
|
return str.replace(/[A-Z\u00C0-\u00D6\u00D8-\u00DE]/g, (match) => "-" + match.toLowerCase());
|
|
509
602
|
};
|
|
510
|
-
var ipol = (v, state) => {
|
|
511
|
-
return v?.replace ? v.replace(/\$<([^>]+)>/g, (match, field) => state[field] ?? "") : v;
|
|
512
|
-
};
|
|
513
|
-
|
|
514
|
-
// directive/ref.js
|
|
515
|
-
directive.ref = (el, expr, state) => {
|
|
516
|
-
state[ipol(expr, state)] = el;
|
|
517
|
-
};
|
|
518
|
-
directive.ref.parse = (expr) => expr;
|
|
519
|
-
|
|
520
|
-
// directive/with.js
|
|
521
|
-
directive.with = (el, evaluate, rootState) => {
|
|
522
|
-
let state;
|
|
523
|
-
return effect(() => {
|
|
524
|
-
let values = evaluate(rootState);
|
|
525
|
-
sprae(el, state ? values : state = store(values, rootState));
|
|
526
|
-
});
|
|
527
|
-
};
|
|
528
|
-
|
|
529
|
-
// directive/html.js
|
|
530
|
-
directive.html = (el, evaluate, state) => {
|
|
531
|
-
let tpl = evaluate(state);
|
|
532
|
-
if (!tpl)
|
|
533
|
-
return;
|
|
534
|
-
let content = (tpl.content || tpl).cloneNode(true);
|
|
535
|
-
el.replaceChildren(content);
|
|
536
|
-
sprae(el, state);
|
|
537
|
-
};
|
|
538
|
-
|
|
539
|
-
// directive/text.js
|
|
540
|
-
directive.text = (el, evaluate, state) => {
|
|
541
|
-
if (el.content)
|
|
542
|
-
el.replaceWith(el = document.createTextNode(""));
|
|
543
|
-
return effect(() => {
|
|
544
|
-
let value = evaluate(state);
|
|
545
|
-
el.textContent = value == null ? "" : value;
|
|
546
|
-
});
|
|
547
|
-
};
|
|
548
|
-
|
|
549
|
-
// directive/class.js
|
|
550
|
-
directive.class = (el, evaluate, state) => {
|
|
551
|
-
let cur = /* @__PURE__ */ new Set();
|
|
552
|
-
return effect(() => {
|
|
553
|
-
let v = evaluate(state);
|
|
554
|
-
let clsx = /* @__PURE__ */ new Set();
|
|
555
|
-
if (v) {
|
|
556
|
-
if (typeof v === "string")
|
|
557
|
-
ipol(v, state).split(" ").map((cls) => clsx.add(cls));
|
|
558
|
-
else if (Array.isArray(v))
|
|
559
|
-
v.map((v2) => (v2 = ipol(v2, state)) && clsx.add(v2));
|
|
560
|
-
else
|
|
561
|
-
Object.entries(v).map(([k, v2]) => v2 && clsx.add(k));
|
|
562
|
-
}
|
|
563
|
-
for (let cls of cur)
|
|
564
|
-
if (clsx.has(cls))
|
|
565
|
-
clsx.delete(cls);
|
|
566
|
-
else
|
|
567
|
-
el.classList.remove(cls);
|
|
568
|
-
for (let cls of cur = clsx)
|
|
569
|
-
el.classList.add(cls);
|
|
570
|
-
});
|
|
571
|
-
};
|
|
572
|
-
|
|
573
|
-
// directive/style.js
|
|
574
|
-
directive.style = (el, evaluate, state) => {
|
|
575
|
-
let initStyle = el.getAttribute("style") || "";
|
|
576
|
-
if (!initStyle.endsWith(";"))
|
|
577
|
-
initStyle += "; ";
|
|
578
|
-
return effect(() => {
|
|
579
|
-
let v = evaluate(state);
|
|
580
|
-
if (typeof v === "string")
|
|
581
|
-
el.setAttribute("style", initStyle + ipol(v, state));
|
|
582
|
-
else {
|
|
583
|
-
el.setAttribute("style", initStyle);
|
|
584
|
-
for (let k in v)
|
|
585
|
-
el.style.setProperty(k, ipol(v[k], state));
|
|
586
|
-
}
|
|
587
|
-
});
|
|
588
|
-
};
|
|
589
603
|
|
|
590
604
|
// directive/value.js
|
|
591
605
|
directive.value = (el, evaluate, state) => {
|
package/dist/sprae.min.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
var e,t,r,l,n,
|
|
1
|
+
var e,t,r,l,n,o=Object.defineProperty,s=Symbol("signals"),a=Symbol("length");function i(t,r){if(!t)return t;if(t[s])return t;if(Array.isArray(t))return function(t){let r;if(t[s])return t;let l=e(t.length),n=Array(t.length).fill();const o=new Proxy(n,{get:(o,c)=>"symbol"==typeof c?c===a?l:c===s?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 o[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 o}(t);if(t.constructor!==Object)return t;let l={...r?.[s]},o=e(Object.values(t).length);const p=new Proxy(l,{get:(e,t)=>t===a?o:t===s?l:l[t]?.valueOf(),set:(e,t,r,n)=>(n=l[t],c(l,t,r),n??++o.value,1),deleteProperty:(e,t)=>(l[t]&&(f(l,t),o.value--),1),ownKeys:()=>(o.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,o){let s=t[n];if("_"===n[0])t[n]=o;else if(s)if(o===s.peek());else if(s._set)s._set(o);else if(Array.isArray(o)&&Array.isArray(s.peek())){const e=s.peek();e[a]?r((()=>{l((()=>{let t=0,r=o.length;for(;t<r;t++)e[t]=o[t];e.length=r}))})):s.value=o}else s.value=i(o);else t[n]=s=o?.peek?o:e(i(o))}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 o=t.attributes[e];if(":"===o.name[0]){t.removeAttribute(o.name);let e=o.name.slice(1).split(":");for(let n of e){let e=d[n]||d.default,s=e(t,(e.parse||g)(o.value,g),r,n);s&&l.push(s)}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=o=>{o.signal&&function(o){e=o.signal,t=o.effect,n=o.computed,l=o.batch||(e=>e()),r=o.untracked||l}(o),o.compile&&(m=o.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)o(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,o,s=e.nextElementSibling,a=document.createTextNode(""),i=[],u=i;e.replaceWith(a),n=e.content?[w(e)]:[e],s?.hasAttribute(":else")?(s.removeAttribute(":else"),s.hasAttribute(":if")?o=i:(s.remove(),o=s.content?[w(s)]:[s])):o=i;for(let e of[...n,...o])y.set(e,null);return t((()=>{const t=r(l)?n:e[T]?i:o;if(s&&(s[T]=t===n),u!=t){for(let e of u)e.remove();u=t;for(let e of u)a.before(e.content||e),null===y.get(e)&&y.delete(e),h(e,l)}}))},d.each=(e,[l,o,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[s]||[])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[s]?.[n]||p[n],[o]:d?d[n]:n},c),u=e.content?w(e):e.cloneNode(!0);f.before(u.content||u),h(u,a),((p[s]||=[])[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,o="$"]=r.split(/\s*,\s*/);return[n,o,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),o=new Set;t&&("string"==typeof t?t.split(" ").map((e=>o.add(e))):Array.isArray(t)?t.map((e=>e&&o.add(e))):Object.entries(t).map((([e,t])=>t&&o.add(e))));for(let t of n)o.has(t)?o.delete(t):e.classList.remove(t);for(let t of n=o)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 o=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==o.length)return t((()=>f(r(l),o[0])));let s,a,i,u=0;const c=e=>{i=f((t=>(i(),a=e?.(t),(u=++u%o.length)?c(a):s&&c(s))),o[u])};return t((()=>(s=r(l),!i&&c(s),()=>s=null)));function f(e,{evt:t,target:r,test:l,defer:n,stop:o,prevent:s,immediate:a,...i}){n&&(e=n(e));const u=r=>{try{l(r)&&(o&&(a?r.stopImmediatePropagation():r.stopPropagation()),s&&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=o=>{r=!0,setTimeout((()=>{if(r=!1,l)return l=!1,n(o),e(o)}),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,o,s="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,o=e.selectionEnd,e.setAttribute("value",e.value=null==t?"":t),n&&e.setSelectionRange(n,o)):"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((()=>s(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"`
|
|
@@ -83,9 +80,6 @@ Set class value.
|
|
|
83
80
|
<!-- appends class -->
|
|
84
81
|
<div class="foo" :class="bar"></div>
|
|
85
82
|
|
|
86
|
-
<!-- interpolation -->
|
|
87
|
-
<div :class="'foo $<bar>'"></div>
|
|
88
|
-
|
|
89
83
|
<!-- array/object, a-la clsx -->
|
|
90
84
|
<div :class="[foo && 'foo', {bar: bar}]"></div>
|
|
91
85
|
```
|
|
@@ -98,9 +92,6 @@ Set style value.
|
|
|
98
92
|
<!-- extends style -->
|
|
99
93
|
<div style="foo: bar" :style="'baz: qux'">
|
|
100
94
|
|
|
101
|
-
<!-- interpolation -->
|
|
102
|
-
<div :style="'foo: $<bar>'"></div>
|
|
103
|
-
|
|
104
95
|
<!-- object -->
|
|
105
96
|
<div :style="{foo: 'bar'}"></div>
|
|
106
97
|
|
|
@@ -277,7 +268,8 @@ Trigger when element is connected / disconnected from DOM.
|
|
|
277
268
|
|
|
278
269
|
## Signals
|
|
279
270
|
|
|
280
|
-
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:
|
|
281
273
|
|
|
282
274
|
```js
|
|
283
275
|
import sprae from 'sprae';
|
|
@@ -352,6 +344,7 @@ import * as signals from '@preact/signals'
|
|
|
352
344
|
import compile from 'subscript'
|
|
353
345
|
|
|
354
346
|
// standard directives
|
|
347
|
+
import 'sprae/directive/default.js'
|
|
355
348
|
import 'sprae/directive/if.js'
|
|
356
349
|
import 'sprae/directive/text.js'
|
|
357
350
|
|
|
@@ -367,20 +360,25 @@ sprae.use(signals)
|
|
|
367
360
|
sprae.use({ compile })
|
|
368
361
|
```
|
|
369
362
|
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
To destroy state and detach sprae handlers, call `element[Symbol.dispose]()`. -->
|
|
363
|
+
## Hints
|
|
373
364
|
|
|
365
|
+
* To prevent [FOUC](https://en.wikipedia.org/wiki/Flash_of_unstyled_content) add `<style>[:each],[:if],[:else],[:text] {visibility: hidden}</style>`
|
|
366
|
+
* Attributes order matters, eg. `<li :each="value in values" :text="value.name"></li>` is not the same as `<li :text="value.name" :each="value in values"></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 `ref` as `<input :ref="el" :text="el.value"/>`
|
|
370
|
+
* Async/await is not supported in attributes, it's a strong signal you need to put these methods into state.
|
|
374
371
|
|
|
375
372
|
## Justification
|
|
376
373
|
|
|
377
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).
|
|
378
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.
|
|
379
376
|
|
|
380
|
-
_Sprae_ holds open & minimalistic philosophy:
|
|
377
|
+
_Sprae_ holds open, sweet & minimalistic philosophy:
|
|
381
378
|
* Slim `:` API and _signals_ reactivity.
|
|
382
379
|
* Pluggable directives & configurable internals.
|
|
383
380
|
* Small, safe & performant.
|
|
381
|
+
* Bits of organic sugar.
|
|
384
382
|
* Aims at making developers happy 🫰
|
|
385
383
|
|
|
386
384
|
<!--
|