sprae 5.0.4 → 5.2.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/examples/todomvc.html +8 -10
- package/package.json +1 -1
- package/r&d.md +1 -2
- package/readme.md +18 -23
- package/sprae.auto.js +28 -10
- package/sprae.auto.min.js +1 -1
- package/sprae.js +28 -10
- package/sprae.min.js +1 -1
- package/src/directives.js +8 -3
- package/src/state.js +15 -8
- package/test/dom.js +33 -11
- package/todo.md +3 -1
package/examples/todomvc.html
CHANGED
|
@@ -40,22 +40,20 @@
|
|
|
40
40
|
:onchange="e => (item.done = !item.done, save(todos))"
|
|
41
41
|
/>
|
|
42
42
|
<label :text="item.text"></label>
|
|
43
|
-
<button class="destroy" :onclick="e => save(todos = todos.filter(i => i !== item))"></button>
|
|
43
|
+
<button class="destroy" :onclick="e => (save(todos = todos.filter(i => i.text !== item.text)))"></button>
|
|
44
44
|
</div>
|
|
45
45
|
<input class=edit
|
|
46
46
|
:value="item.text"
|
|
47
|
-
:
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
keypress(e) { e.key === 'Enter' ? e.target.blur() : null },
|
|
51
|
-
}"
|
|
47
|
+
:oninput="e => {item.text = e.target.value; save(todos)}"
|
|
48
|
+
:onblur="e => li.classList.remove('editing');"
|
|
49
|
+
:onkeypress="(e) => { e.key === 'Enter' ? e.target.blur() : null }"
|
|
52
50
|
/>
|
|
53
51
|
</li>
|
|
54
52
|
</ul>
|
|
55
53
|
</section>
|
|
56
54
|
<footer class="footer">
|
|
57
55
|
<span class="todo-count">
|
|
58
|
-
<strong :text="count">#</strong> <span :text="plur('item', count)">items</span> left
|
|
56
|
+
<strong :text="count()">#</strong> <span :text="plur('item', count())">items</span> left
|
|
59
57
|
</span>
|
|
60
58
|
<ul class="filters">
|
|
61
59
|
<li :each="label, key in {'#/': 'All', '#/active': 'Active', '#/completed': 'Completed'}">
|
|
@@ -77,19 +75,19 @@
|
|
|
77
75
|
<script src="https://unpkg.com/todomvc-common/base.js"></script>
|
|
78
76
|
<script type="importmap"> {
|
|
79
77
|
"imports": {
|
|
80
|
-
"sprae": "https://cdn.
|
|
78
|
+
"sprae": "https://cdn.jsdelivr.net/npm/sprae/sprae.min.js",
|
|
81
79
|
"plur": "https://cdn.skypack.dev/plur"
|
|
82
80
|
}
|
|
83
81
|
}
|
|
84
82
|
</script>
|
|
85
83
|
<script type="module">
|
|
86
|
-
import sprae from 'sprae'
|
|
84
|
+
import sprae from '../sprae.js'
|
|
87
85
|
import plur from 'plur'
|
|
88
86
|
|
|
89
87
|
let state = sprae(document.body, {
|
|
90
88
|
plur,
|
|
91
89
|
todos: JSON.parse(localStorage.getItem('todomvc.items') || '[]'),
|
|
92
|
-
|
|
90
|
+
count() { return this.todos.filter(item => !item.done).length },
|
|
93
91
|
hash: window.location.hash || '#/',
|
|
94
92
|
save: items => localStorage.setItem('todomvc.items', JSON.stringify(items))
|
|
95
93
|
})
|
package/package.json
CHANGED
package/r&d.md
CHANGED
|
@@ -448,8 +448,6 @@
|
|
|
448
448
|
* onkey.after, onkey.microtask, onkey.defer, onkey.immediate
|
|
449
449
|
* onkey.tick-1?
|
|
450
450
|
|
|
451
|
-
### [ ] onkey.unfocusable
|
|
452
|
-
|
|
453
451
|
## [ ] Prop modifiers
|
|
454
452
|
|
|
455
453
|
- overall seems code complication without much benefit
|
|
@@ -475,6 +473,7 @@
|
|
|
475
473
|
* [ ] x.watch-a-b-c - update by change of any of the deps
|
|
476
474
|
* [ ] :x.always - update by _any_ dep change
|
|
477
475
|
* [ ] :class.active="active"
|
|
476
|
+
* [ ] :x.persist="v"
|
|
478
477
|
|
|
479
478
|
## [x] Writing props on elements (like ones in :each) -> nah, just use `:x="this.x=abc"`
|
|
480
479
|
|
package/readme.md
CHANGED
|
@@ -63,14 +63,12 @@ Control flow of elements.
|
|
|
63
63
|
Multiply element. `index` value starts from 1.
|
|
64
64
|
|
|
65
65
|
```html
|
|
66
|
-
<ul>
|
|
67
|
-
<li :each="item in items" :text="item">Untitled</li>
|
|
68
|
-
</ul>
|
|
66
|
+
<ul><li :each="item in items" :text="item"></ul>
|
|
69
67
|
|
|
70
68
|
<!-- Cases -->
|
|
71
69
|
<li :each="item, idx in list" />
|
|
72
70
|
<li :each="val, key in obj" />
|
|
73
|
-
<li :each="
|
|
71
|
+
<li :each="idx1, idx0 in number" />
|
|
74
72
|
|
|
75
73
|
<!-- Loop by condition -->
|
|
76
74
|
<li :if="items" :each="item in items" :text="item" />
|
|
@@ -78,9 +76,6 @@ Multiply element. `index` value starts from 1.
|
|
|
78
76
|
|
|
79
77
|
<!-- Key items to reuse elements -->
|
|
80
78
|
<li :each="item in items" :key="item.id" :text="item.value" />
|
|
81
|
-
|
|
82
|
-
<!-- To avoid FOUC -->
|
|
83
|
-
<style>[:each]{visibility: hidden}</style>
|
|
84
79
|
```
|
|
85
80
|
|
|
86
81
|
#### `:text="value"`
|
|
@@ -131,7 +126,7 @@ Set value of an input, textarea or select. Takes handle of `checked` and `select
|
|
|
131
126
|
|
|
132
127
|
#### `:<prop>="value?"`
|
|
133
128
|
|
|
134
|
-
Set any attribute value or run effect.
|
|
129
|
+
Set any attribute value or run an effect.
|
|
135
130
|
|
|
136
131
|
```html
|
|
137
132
|
<!-- Single property -->
|
|
@@ -166,10 +161,8 @@ Add event listener or events chain.
|
|
|
166
161
|
<!-- Sequence of events -->
|
|
167
162
|
<button :onfocus..onblur="e => {
|
|
168
163
|
// onfocus
|
|
169
|
-
let id = setInterval(track,200)
|
|
170
164
|
return e => {
|
|
171
165
|
// onblur
|
|
172
|
-
clearInterval(id)
|
|
173
166
|
}
|
|
174
167
|
}">
|
|
175
168
|
|
|
@@ -223,6 +216,7 @@ Expose element to current data scope with the `id`:
|
|
|
223
216
|
```html
|
|
224
217
|
<!-- single item -->
|
|
225
218
|
<textarea :ref="text" placeholder="Enter text..."></textarea>
|
|
219
|
+
<span :text="text.value"></span>
|
|
226
220
|
|
|
227
221
|
<!-- iterable items -->
|
|
228
222
|
<ul>
|
|
@@ -230,14 +224,6 @@ Expose element to current data scope with the `id`:
|
|
|
230
224
|
<input :onfocus..onblur="e => (item.classList.add('editing'), e => item.classList.remove('editing'))"/>
|
|
231
225
|
</li>
|
|
232
226
|
</ul>
|
|
233
|
-
|
|
234
|
-
<script type="module">
|
|
235
|
-
import sprae from 'sprae';
|
|
236
|
-
let state = sprae(document, {items: ['a','b','c']})
|
|
237
|
-
|
|
238
|
-
// element is in the state
|
|
239
|
-
state.text // <textarea></textarea>
|
|
240
|
-
</script>
|
|
241
227
|
```
|
|
242
228
|
|
|
243
229
|
## Sandbox
|
|
@@ -249,13 +235,23 @@ Expressions are sandboxed, ie. have no access to global or window (since sprae c
|
|
|
249
235
|
<!-- window is undefined -->
|
|
250
236
|
```
|
|
251
237
|
|
|
252
|
-
Default sandbox provides: _Array_, _Object_, _Number_, _String_, _Boolean_, _Date_,
|
|
253
|
-
Sandbox can be extended as `Object.assign(sprae.globals, { BigInt
|
|
238
|
+
Default sandbox provides: _window_, _document_, _console_, _history_, _location_, _Array_, _Object_, _Number_, _String_, _Boolean_, _Date_, _Set_, _Map_.<br/>
|
|
239
|
+
Sandbox can be extended as `Object.assign(sprae.globals, { BigInt })`.
|
|
240
|
+
|
|
241
|
+
## FOUC
|
|
242
|
+
|
|
243
|
+
To avoid _flash of unstyled content_, you can hide sprae attribute or add a custom effect, eg. `:hidden` - that will be removed once sprae is initialized:
|
|
244
|
+
|
|
245
|
+
```html
|
|
246
|
+
<div :hidden></div>
|
|
247
|
+
<style>[:each],[:hidden] {visibility: hidden}</style>
|
|
248
|
+
```
|
|
254
249
|
|
|
255
250
|
## Examples
|
|
256
251
|
|
|
257
252
|
* TODO MVC: [demo](https://dy.github.io/sprae/examples/todomvc), [code](https://github.com/dy/sprae/blob/main/examples/todomvc.html)
|
|
258
253
|
* Wavearea: [demo](https://dy.github.io/wavearea?src=//cdn.freesound.org/previews/586/586281_2332564-lq.mp3), [code](https://github.com/dy/wavearea)
|
|
254
|
+
* Prostogreen [demo](http://web-being.org/prostogreen/), [code](https://github.com/web-being/prostogreen/)
|
|
259
255
|
|
|
260
256
|
## Justification
|
|
261
257
|
|
|
@@ -263,15 +259,14 @@ Sandbox can be extended as `Object.assign(sprae.globals, { BigInt, window, docum
|
|
|
263
259
|
* [Alpine](https://github.com/alpinejs/alpine) / [vue](https://github.com/vuejs/petite-vue) / [lit](https://github.com/lit/lit/tree/main/packages/lit-html) escapes native HTML quirks, but the syntax is a bit scattered: `:attr`, `v-*`,`x-*`, `@evt`, `{{}}` can be expressed with single convention. Besides, functionality is too broad and can be reduced to essence: perfection is when there's nothing to take away, not add. Also they tend to [self-encapsulate](https://github.com/alpinejs/alpine/discussions/3223), making interop hard.
|
|
264
260
|
* React/[preact](https://ghub.io/preact) does the job wiring up JS to HTML, but with an extreme of migrating HTML to JSX and enforcing SPA, which is not organic for HTML. Also it doesn't support reactive fields (needs render call).
|
|
265
261
|
|
|
266
|
-
_Sprae_ takes convention of _templize directives_ (_alpine_/_vue_ attrs) and builds upon <del>[_@preact/signals_](https://ghub.io/@preact/signals)</del> simple
|
|
262
|
+
_Sprae_ takes convention of _templize directives_ (_alpine_/_vue_ attrs) and builds upon <del>[_@preact/signals_](https://ghub.io/@preact/signals)</del> simple reactive state.
|
|
267
263
|
|
|
268
|
-
* It doesn't break or modify
|
|
264
|
+
* It doesn't break or modify static html markup.
|
|
269
265
|
* It falls back to element content if uninitialized.
|
|
270
266
|
* It doesn't enforce SPA nor JSX.
|
|
271
267
|
* It enables island hydration.
|
|
272
268
|
* It reserves minimal syntax space as `:` convention (keeping tree neatly decorated, not scattered).
|
|
273
269
|
* Expressions are naturally reactive and incur minimal updates.
|
|
274
|
-
* Input data may contain [signals](https://ghub.io/@preact/signals) or [reactive values](https://ghub.io/sube).
|
|
275
270
|
* Elements / data API is open and enable easy interop.
|
|
276
271
|
|
|
277
272
|
It is reminiscent of [XSLT](https://www.w3schools.com/xml/xsl_intro.asp), considered a [buried treasure](https://github.com/bahrus/be-restated) by web-connoisseurs.
|
package/sprae.auto.js
CHANGED
|
@@ -46,7 +46,11 @@
|
|
|
46
46
|
String,
|
|
47
47
|
Boolean,
|
|
48
48
|
Date,
|
|
49
|
-
console
|
|
49
|
+
console,
|
|
50
|
+
window,
|
|
51
|
+
document,
|
|
52
|
+
history,
|
|
53
|
+
location
|
|
50
54
|
};
|
|
51
55
|
var handler = {
|
|
52
56
|
has() {
|
|
@@ -57,9 +61,7 @@
|
|
|
57
61
|
return target[prop];
|
|
58
62
|
if (!(prop in target))
|
|
59
63
|
return target[_parent]?.[prop];
|
|
60
|
-
if (Array.isArray(target) && prop in Array.prototype)
|
|
61
|
-
return target[prop];
|
|
62
|
-
if (prop in Object.prototype)
|
|
64
|
+
if (prop in Object.prototype || Array.isArray(target) && prop in Array.prototype && prop !== "length")
|
|
63
65
|
return target[prop];
|
|
64
66
|
let value = target[prop];
|
|
65
67
|
if (currentFx) {
|
|
@@ -82,10 +84,7 @@
|
|
|
82
84
|
set(target, prop, value) {
|
|
83
85
|
if (!(prop in target) && (target[_parent] && prop in target[_parent]))
|
|
84
86
|
return target[_parent][prop] = value;
|
|
85
|
-
if (Array.isArray(target) && prop
|
|
86
|
-
return target[prop] = value;
|
|
87
|
-
const prev = target[prop];
|
|
88
|
-
if (Object.is(prev, value))
|
|
87
|
+
if (!Array.isArray(target) && Object.is(target[prop], value))
|
|
89
88
|
return true;
|
|
90
89
|
target[prop] = value;
|
|
91
90
|
let propFxs = targetFxs.get(target)?.[prop];
|
|
@@ -109,6 +108,14 @@
|
|
|
109
108
|
let proxy = new Proxy(obj, handler);
|
|
110
109
|
targetProxy.set(obj, proxy);
|
|
111
110
|
proxyTarget.set(proxy, obj);
|
|
111
|
+
let descriptors = Object.getOwnPropertyDescriptors(obj);
|
|
112
|
+
for (let name in descriptors) {
|
|
113
|
+
let desc = descriptors[name];
|
|
114
|
+
if (desc.get) {
|
|
115
|
+
if (desc.get)
|
|
116
|
+
desc.get = desc.get.bind(proxy), Object.defineProperty(obj, name, desc);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
112
119
|
obj[_parent] = parent ? state(parent) : sandbox;
|
|
113
120
|
return proxy;
|
|
114
121
|
};
|
|
@@ -311,8 +318,19 @@
|
|
|
311
318
|
let initClassName = el.getAttribute("class");
|
|
312
319
|
return (state2) => {
|
|
313
320
|
let v = evaluate(state2);
|
|
314
|
-
let className =
|
|
315
|
-
|
|
321
|
+
let className = [initClassName];
|
|
322
|
+
if (v) {
|
|
323
|
+
if (typeof v === "string")
|
|
324
|
+
className.push(v);
|
|
325
|
+
else if (Array.isArray(v))
|
|
326
|
+
className.push(...v);
|
|
327
|
+
else
|
|
328
|
+
className.push(...Object.entries(v).map(([k, v2]) => v2 ? k : ""));
|
|
329
|
+
}
|
|
330
|
+
if (className = className.filter(Boolean).join(" "))
|
|
331
|
+
el.setAttribute("class", className);
|
|
332
|
+
else
|
|
333
|
+
el.removeAttribute("class");
|
|
316
334
|
};
|
|
317
335
|
};
|
|
318
336
|
secondary["style"] = (el, expr) => {
|
package/sprae.auto.min.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
(()=>{var e,t,r=Promise.prototype.then.bind(Promise.resolve()),n=new WeakMap,l=class extends Map{#e=new FinalizationRegistry((e=>super.delete(e)));get size(){return[...this].length}constructor(e=[]){super();for(const[t,r]of e)this.set(t,r)}get(e){return super.get(e)?.deref()}set(e,t){let r=super.get(e);return r&&this.#e.unregister(r),r=(e=>n.get(e)||(e=>{const t=new WeakRef(e);return n.set(e,t),t})(e))(t),this.#e.register(t,e,r),super.set(e,r)}},s=new Set,i=new WeakMap,o=new WeakMap,a=new WeakMap,u=Symbol("parent"),c={Array:Array,Object:Object,Number:Number,String:String,Boolean:Boolean,Date:Date,console:console},f={has:()=>!0,get(t,r){if("symbol"==typeof r)return t[r];if(!(r in t))return t[u]?.[r];if(Array.isArray(t)&&r in Array.prototype
|
|
1
|
+
(()=>{var e,t,r=Promise.prototype.then.bind(Promise.resolve()),n=new WeakMap,l=class extends Map{#e=new FinalizationRegistry((e=>super.delete(e)));get size(){return[...this].length}constructor(e=[]){super();for(const[t,r]of e)this.set(t,r)}get(e){return super.get(e)?.deref()}set(e,t){let r=super.get(e);return r&&this.#e.unregister(r),r=(e=>n.get(e)||(e=>{const t=new WeakRef(e);return n.set(e,t),t})(e))(t),this.#e.register(t,e,r),super.set(e,r)}},s=new Set,i=new WeakMap,o=new WeakMap,a=new WeakMap,u=Symbol("parent"),c={Array:Array,Object:Object,Number:Number,String:String,Boolean:Boolean,Date:Date,console:console,window:window,document:document,history:history,location:location},f={has:()=>!0,get(t,r){if("symbol"==typeof r)return t[r];if(!(r in t))return t[u]?.[r];if(r in Object.prototype||Array.isArray(t)&&r in Array.prototype&&"length"!==r)return t[r];let n=t[r];if(e){let n=i.get(t);n||i.set(t,n={}),n[r]?n[r].includes(e)||n[r].push(e):n[r]=[e]}if(n&&n.constructor===Object||Array.isArray(n)){let e=o.get(n);return e||o.set(n,e=new Proxy(n,f)),e}return n},set(e,t,r){if(!(t in e)&&e[u]&&t in e[u])return e[u][t]=r;if(!Array.isArray(e)&&Object.is(e[t],r))return!0;e[t]=r;let n=i.get(e)?.[t];if(n)for(let e of n)s.add(e);return h(),!0},deleteProperty:(e,t)=>(e[t]=void 0,delete e[t],!0)},p=(e,t)=>{if(o.has(e))return o.get(e);if(a.has(e))return e;let r=new Proxy(e,f);o.set(e,r),a.set(r,e);let n=Object.getOwnPropertyDescriptors(e);for(let t in n){let l=n[t];l.get&&l.get&&(l.get=l.get.bind(r),Object.defineProperty(e,t,l))}return e[u]=t?p(t):c,r},y=t=>{const r=()=>{let n=e;e=r,t(),e=n};return r(),r},h=()=>{t||(t=!0,r((()=>{for(let e of s)e.call();s.clear(),t=!1})))},g={},d={};g.if=(e,t)=>{let r=document.createTextNode(""),n=[j(e,t,":if")],l=[e],s=e;for(;(s=e.nextElementSibling)&&s.hasAttribute(":else");)s.removeAttribute(":else"),(t=s.getAttribute(":if"))?(s.removeAttribute(":if"),s.remove(),l.push(s),n.push(j(e,t,":else :if"))):(s.remove(),l.push(s),n.push((()=>1)));return e.replaceWith(s=r),e=>{let t=n.findIndex((t=>t(e)));l[t]!=s&&((s[m]||s).replaceWith(s=l[t]||r),O(s,e))}},g.with=(e,t,r)=>{const n=j(e,t,":with")(r);O(e,p(n,r))};var m=Symbol(":each");g.each=(e,t)=>{let r=function(e){let t=/,([^,\}\]]*)(?:,([^,\}\]]*))?$/,r=e.match(/([\s\S]*?)\s+(?:in|of)\s+([\s\S]*)/);if(!r)return;let n=r[2].trim(),l=r[1].replace(/^\s*\(|\)\s*$/g,"").trim(),s=l.match(t);return s?[l.replace(t,"").trim(),s[1].trim(),n]:[l,"",n]}(t);if(!r)return E(new Error,e,t,":each");const n=e[m]=document.createTextNode("");e.replaceWith(n);const s=j(e,r[2],":each"),i=e.getAttribute(":key"),o=i?j(null,i,":each"):null;e.removeAttribute(":key");const a=e.getAttribute(":ref"),u=new l,c=new l;let f=[];return l=>{let i=s(l);i?"number"==typeof i?i=Array.from({length:i},((e,t)=>[t,t+1])):Array.isArray(i)?i=i.map(((e,t)=>[t+1,e])):"object"==typeof i?i=Object.entries(i):E(Error("Bad list value"),e,t,":each"):i=[];let y=[],h=[];for(let[t,n]of i){let s,i,f=o?.({[r[0]]:n,[r[1]]:t});null==f?s=e.cloneNode(!0):(s=c.get(f))||c.set(f,s=e.cloneNode(!0)),y.push(s),null!=f&&(i=u.get(f))?i[r[0]]=n:(i=p({[r[0]]:n,[a||""]:null,[r[1]]:t},l),null!=f&&u.set(f,i)),h.push(i)}!function(e,t,r,n){const l=new Map,s=new Map;let i,o;for(i=0;i<t.length;i++)l.set(t[i],i);for(i=0;i<r.length;i++)s.set(r[i],i);for(i=o=0;i!==t.length||o!==r.length;){var a=t[i],u=r[o];if(null===a)i++;else if(r.length<=o)e.removeChild(t[i]),i++;else if(t.length<=i)e.insertBefore(u,t[i]||n),o++;else if(a===u)i++,o++;else{var c=s.get(a),f=l.get(u);void 0===c?(e.removeChild(t[i]),i++):void 0===f?(e.insertBefore(u,t[i]||n),o++):(e.insertBefore(t[f],t[i]||n),t[f]=null,f>i+1&&i++,o++)}}}(n.parentNode,f,y,n),f=y;for(let e=0;e<y.length;e++)O(y[e],h[e])}},g.ref=(e,t,r)=>{r[t]=e},d.render=(e,t,r)=>{let n=j(e,t,":render")(r);n||E(new Error("Template not found"),e,t,":render");let l=n.content.cloneNode(!0);e.replaceChildren(l),O(e,r)},d.id=(e,t)=>{let r=j(e,t,":id");return t=>{return n=r(t),e.id=n||0===n?n:"";var n}},d.class=(e,t)=>{let r=j(e,t,":class"),n=e.getAttribute("class");return t=>{let l=r(t),s=[n];l&&("string"==typeof l?s.push(l):Array.isArray(l)?s.push(...l):s.push(...Object.entries(l).map((([e,t])=>t?e:"")))),(s=s.filter(Boolean).join(" "))?e.setAttribute("class",s):e.removeAttribute("class")}},d.style=(e,t)=>{let r=j(e,t,":style"),n=e.getAttribute("style")||"";return n.endsWith(";")||(n+="; "),t=>{let l=r(t);if("string"==typeof l)e.setAttribute("style",n+l);else{e.setAttribute("style",n);for(let t in l)e.style.setProperty(t,l[t])}}},d.text=(e,t)=>{let r=j(e,t,":text");return t=>{let n=r(t);e.textContent=null==n?"":n}},d[""]=(e,t)=>{let r=j(e,t,":");if(r)return t=>{let n=r(t);for(let t in n)x(e,t.replace(/[A-Z\u00C0-\u00D6\u00D8-\u00DE]/g,(e=>"-"+e.toLowerCase())),n[t])}},d.value=(e,t)=>{let r,n,l=j(e,t,":value"),s="text"===e.type||""===e.type?t=>e.setAttribute("value",e.value=null==t?"":t):"TEXTAREA"===e.tagName||"text"===e.type||""===e.type?t=>(r=e.selectionStart,n=e.selectionEnd,e.setAttribute("value",e.value=null==t?"":t),r&&e.setSelectionRange(r,n)):"checkbox"===e.type?t=>(e.value=t?"on":"",x(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 e=>s(l(e))},d.on=(e,t)=>{let r=j(e,t,":on");return t=>{let n=r(t),l=[];for(let t in n)l.push(v(e,t,n[t]));return()=>{for(let e of l)e()}}};var b=(e,t,r,n)=>{let l=n.startsWith("on")&&n.slice(2),s=j(e,t,":"+n);if(s)return l?t=>{let r=s(t)||(()=>{});return v(e,l,r)}:t=>x(e,n,s(t))},v=(e,t,r)=>{if(!r)return;let n=t.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,n="")=>(r.test=A[t]?.(r,...n.split("-"))||r.test,""))),r}));if(1==n.length)return i(r,n[0]);const l=(t,r=0)=>{let s;return s=i((i=>{r&&s();let o=t.call(e,i);"function"!=typeof o&&(o=()=>{}),r+1<n.length&&l(o,r?r+1:1)}),n[r])};let s=l(r);return()=>s();function i(e,{evt:t,target:r,test:n,defer:l,stop:s,prevent:i,...o}){l&&(e=l(e));let a=t=>n(t)&&(s&&t.stopPropagation(),i&&t.preventDefault(),e.call(r,t));return r.addEventListener(t,a,o),()=>r.removeEventListener(t,a,o)}},A={prevent(e){e.prevent=!0},stop(e){e.stop=!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},toggle(e){e.defer=(t,r)=>n=>r?(r.call?.(e.target,n),r=null):r=t()},throttle(e,t){e.defer=e=>k(e,t?Number(t)||0:108)},debounce(e,t){e.defer=e=>W(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=>w.ctrl(e)&&t.every((t=>w[t]?w[t](e):e.key===t)),shift:(e,...t)=>e=>w.shift(e)&&t.every((t=>w[t]?w[t](e):e.key===t)),alt:(e,...t)=>e=>w.alt(e)&&t.every((t=>w[t]?w[t](e):e.key===t)),meta:(e,...t)=>e=>w.meta(e)&&t.every((t=>w[t]?w[t](e):e.key===t)),arrow:e=>w.arrow,enter:e=>w.enter,escape:e=>w.escape,tab:e=>w.tab,space:e=>w.space,backspace:e=>w.backspace,delete:e=>w.delete,digit:e=>w.digit,letter:e=>w.letter,character:e=>w.character},w={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,escape:e=>e.key.startsWith("Esc"),tab:e=>"Tab"===e.key,space:e=>" "===e.key||"Space"===e.key||" "===e.key,backspace:e=>"Backspace"===e.key,delete:e=>"Delete"===e.key,digit:e=>/^\d$/.test(e.key),letter:e=>/^[a-zA-Z]$/.test(e.key),character:e=>/^\S$/.test(e.key)},k=(e,t)=>{let r,n,l=s=>{r=!0,setTimeout((()=>{if(r=!1,n)return n=!1,l(s),e(s)}),t)};return t=>r?n=!0:(l(t),e(t))},W=(e,t)=>{let r;return n=>{clearTimeout(r),r=setTimeout((()=>{r=null,e(n)}),t)}},x=(e,t,r)=>{null==r||!1===r?e.removeAttribute(t):e.setAttribute(t,!0===r?"":"number"==typeof r||"string"==typeof r?r:"")},S={};function j(e,t,r){let n=S[t];if(!n){let l=/^[\n\s]*if.*\(.*\)/.test(t)||/\b(let|const)\s/.test(t)&&!r.startsWith(":on")?`(() => {${t}})()`:t;try{n=S[t]=new Function("__scope",`with (__scope) { return ${l.trim()} };`)}catch(n){return E(n,e,t,r)}}return l=>{let s;try{s=n.call(e,l)}catch(n){return E(n,e,t,r)}return s}}function E(e,t,n,l){Object.assign(e,{element:t,expression:n}),console.warn(`∴ ${e.message}\n\n${l}=${n?`"${n}"\n\n`:""}`,t),r((()=>{throw e}),0)}O.globals=c;var N=new WeakMap;function O(e,t){if(!e.children)return;if(N.has(e))return Object.assign(N.get(e),t);const r=p(t||{}),n=[],l=(e,t=e.parentNode)=>{for(let l in g){let s=":"+l;if(e.hasAttribute?.(s)){let i=e.getAttribute(s);if(e.removeAttribute(s),n.push(g[l](e,i,r,l)),N.has(e))return;if(e.parentNode!==t)return!1}}if(e.attributes)for(let t=0;t<e.attributes.length;){let l=e.attributes[t];if(":"!==l.name[0]){t++;continue}e.removeAttribute(l.name);let s=l.value,i=l.name.slice(1).split(":");for(let t of i){let l=d[t]||b;n.push(l(e,s,r,t))}}for(let t,r=0;t=e.children[r];r++)!1===l(t,e)&&r--};l(e);for(let e of n)if(e){let t;y((()=>{"function"==typeof t&&t(),t=e(r)}))}return N.set(e,r),r}document.currentScript&&O(document.documentElement)})();
|
package/sprae.js
CHANGED
|
@@ -45,7 +45,11 @@ var sandbox = {
|
|
|
45
45
|
String,
|
|
46
46
|
Boolean,
|
|
47
47
|
Date,
|
|
48
|
-
console
|
|
48
|
+
console,
|
|
49
|
+
window,
|
|
50
|
+
document,
|
|
51
|
+
history,
|
|
52
|
+
location
|
|
49
53
|
};
|
|
50
54
|
var handler = {
|
|
51
55
|
has() {
|
|
@@ -56,9 +60,7 @@ var handler = {
|
|
|
56
60
|
return target[prop];
|
|
57
61
|
if (!(prop in target))
|
|
58
62
|
return target[_parent]?.[prop];
|
|
59
|
-
if (Array.isArray(target) && prop in Array.prototype)
|
|
60
|
-
return target[prop];
|
|
61
|
-
if (prop in Object.prototype)
|
|
63
|
+
if (prop in Object.prototype || Array.isArray(target) && prop in Array.prototype && prop !== "length")
|
|
62
64
|
return target[prop];
|
|
63
65
|
let value = target[prop];
|
|
64
66
|
if (currentFx) {
|
|
@@ -81,10 +83,7 @@ var handler = {
|
|
|
81
83
|
set(target, prop, value) {
|
|
82
84
|
if (!(prop in target) && (target[_parent] && prop in target[_parent]))
|
|
83
85
|
return target[_parent][prop] = value;
|
|
84
|
-
if (Array.isArray(target) && prop
|
|
85
|
-
return target[prop] = value;
|
|
86
|
-
const prev = target[prop];
|
|
87
|
-
if (Object.is(prev, value))
|
|
86
|
+
if (!Array.isArray(target) && Object.is(target[prop], value))
|
|
88
87
|
return true;
|
|
89
88
|
target[prop] = value;
|
|
90
89
|
let propFxs = targetFxs.get(target)?.[prop];
|
|
@@ -108,6 +107,14 @@ var state = (obj, parent) => {
|
|
|
108
107
|
let proxy = new Proxy(obj, handler);
|
|
109
108
|
targetProxy.set(obj, proxy);
|
|
110
109
|
proxyTarget.set(proxy, obj);
|
|
110
|
+
let descriptors = Object.getOwnPropertyDescriptors(obj);
|
|
111
|
+
for (let name in descriptors) {
|
|
112
|
+
let desc = descriptors[name];
|
|
113
|
+
if (desc.get) {
|
|
114
|
+
if (desc.get)
|
|
115
|
+
desc.get = desc.get.bind(proxy), Object.defineProperty(obj, name, desc);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
111
118
|
obj[_parent] = parent ? state(parent) : sandbox;
|
|
112
119
|
return proxy;
|
|
113
120
|
};
|
|
@@ -310,8 +317,19 @@ secondary["class"] = (el, expr) => {
|
|
|
310
317
|
let initClassName = el.getAttribute("class");
|
|
311
318
|
return (state2) => {
|
|
312
319
|
let v = evaluate(state2);
|
|
313
|
-
let className =
|
|
314
|
-
|
|
320
|
+
let className = [initClassName];
|
|
321
|
+
if (v) {
|
|
322
|
+
if (typeof v === "string")
|
|
323
|
+
className.push(v);
|
|
324
|
+
else if (Array.isArray(v))
|
|
325
|
+
className.push(...v);
|
|
326
|
+
else
|
|
327
|
+
className.push(...Object.entries(v).map(([k, v2]) => v2 ? k : ""));
|
|
328
|
+
}
|
|
329
|
+
if (className = className.filter(Boolean).join(" "))
|
|
330
|
+
el.setAttribute("class", className);
|
|
331
|
+
else
|
|
332
|
+
el.removeAttribute("class");
|
|
315
333
|
};
|
|
316
334
|
};
|
|
317
335
|
secondary["style"] = (el, expr) => {
|
package/sprae.min.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
var e,t,r=Promise.prototype.then.bind(Promise.resolve()),n=new WeakMap,l=class extends Map{#e=new FinalizationRegistry((e=>super.delete(e)));get size(){return[...this].length}constructor(e=[]){super();for(const[t,r]of e)this.set(t,r)}get(e){return super.get(e)?.deref()}set(e,t){let r=super.get(e);return r&&this.#e.unregister(r),r=(e=>n.get(e)||(e=>{const t=new WeakRef(e);return n.set(e,t),t})(e))(t),this.#e.register(t,e,r),super.set(e,r)}},s=new Set,i=new WeakMap,o=new WeakMap,a=new WeakMap,u=Symbol("parent"),c={Array:Array,Object:Object,Number:Number,String:String,Boolean:Boolean,Date:Date,console:console},f={has:()=>!0,get(t,r){if("symbol"==typeof r)return t[r];if(!(r in t))return t[u]?.[r];if(Array.isArray(t)&&r in Array.prototype
|
|
1
|
+
var e,t,r=Promise.prototype.then.bind(Promise.resolve()),n=new WeakMap,l=class extends Map{#e=new FinalizationRegistry((e=>super.delete(e)));get size(){return[...this].length}constructor(e=[]){super();for(const[t,r]of e)this.set(t,r)}get(e){return super.get(e)?.deref()}set(e,t){let r=super.get(e);return r&&this.#e.unregister(r),r=(e=>n.get(e)||(e=>{const t=new WeakRef(e);return n.set(e,t),t})(e))(t),this.#e.register(t,e,r),super.set(e,r)}},s=new Set,i=new WeakMap,o=new WeakMap,a=new WeakMap,u=Symbol("parent"),c={Array:Array,Object:Object,Number:Number,String:String,Boolean:Boolean,Date:Date,console:console,window:window,document:document,history:history,location:location},f={has:()=>!0,get(t,r){if("symbol"==typeof r)return t[r];if(!(r in t))return t[u]?.[r];if(r in Object.prototype||Array.isArray(t)&&r in Array.prototype&&"length"!==r)return t[r];let n=t[r];if(e){let n=i.get(t);n||i.set(t,n={}),n[r]?n[r].includes(e)||n[r].push(e):n[r]=[e]}if(n&&n.constructor===Object||Array.isArray(n)){let e=o.get(n);return e||o.set(n,e=new Proxy(n,f)),e}return n},set(e,t,r){if(!(t in e)&&e[u]&&t in e[u])return e[u][t]=r;if(!Array.isArray(e)&&Object.is(e[t],r))return!0;e[t]=r;let n=i.get(e)?.[t];if(n)for(let e of n)s.add(e);return h(),!0},deleteProperty:(e,t)=>(e[t]=void 0,delete e[t],!0)},p=(e,t)=>{if(o.has(e))return o.get(e);if(a.has(e))return e;let r=new Proxy(e,f);o.set(e,r),a.set(r,e);let n=Object.getOwnPropertyDescriptors(e);for(let t in n){let l=n[t];l.get&&l.get&&(l.get=l.get.bind(r),Object.defineProperty(e,t,l))}return e[u]=t?p(t):c,r},y=t=>{const r=()=>{let n=e;e=r,t(),e=n};return r(),r},h=()=>{t||(t=!0,r((()=>{for(let e of s)e.call();s.clear(),t=!1})))},g={},d={};g.if=(e,t)=>{let r=document.createTextNode(""),n=[j(e,t,":if")],l=[e],s=e;for(;(s=e.nextElementSibling)&&s.hasAttribute(":else");)s.removeAttribute(":else"),(t=s.getAttribute(":if"))?(s.removeAttribute(":if"),s.remove(),l.push(s),n.push(j(e,t,":else :if"))):(s.remove(),l.push(s),n.push((()=>1)));return e.replaceWith(s=r),e=>{let t=n.findIndex((t=>t(e)));l[t]!=s&&((s[m]||s).replaceWith(s=l[t]||r),O(s,e))}},g.with=(e,t,r)=>{const n=j(e,t,":with")(r);O(e,p(n,r))};var m=Symbol(":each");g.each=(e,t)=>{let r=function(e){let t=/,([^,\}\]]*)(?:,([^,\}\]]*))?$/,r=e.match(/([\s\S]*?)\s+(?:in|of)\s+([\s\S]*)/);if(!r)return;let n=r[2].trim(),l=r[1].replace(/^\s*\(|\)\s*$/g,"").trim(),s=l.match(t);return s?[l.replace(t,"").trim(),s[1].trim(),n]:[l,"",n]}(t);if(!r)return E(new Error,e,t,":each");const n=e[m]=document.createTextNode("");e.replaceWith(n);const s=j(e,r[2],":each"),i=e.getAttribute(":key"),o=i?j(null,i,":each"):null;e.removeAttribute(":key");const a=e.getAttribute(":ref"),u=new l,c=new l;let f=[];return l=>{let i=s(l);i?"number"==typeof i?i=Array.from({length:i},((e,t)=>[t,t+1])):Array.isArray(i)?i=i.map(((e,t)=>[t+1,e])):"object"==typeof i?i=Object.entries(i):E(Error("Bad list value"),e,t,":each"):i=[];let y=[],h=[];for(let[t,n]of i){let s,i,f=o?.({[r[0]]:n,[r[1]]:t});null==f?s=e.cloneNode(!0):(s=c.get(f))||c.set(f,s=e.cloneNode(!0)),y.push(s),null!=f&&(i=u.get(f))?i[r[0]]=n:(i=p({[r[0]]:n,[a||""]:null,[r[1]]:t},l),null!=f&&u.set(f,i)),h.push(i)}!function(e,t,r,n){const l=new Map,s=new Map;let i,o;for(i=0;i<t.length;i++)l.set(t[i],i);for(i=0;i<r.length;i++)s.set(r[i],i);for(i=o=0;i!==t.length||o!==r.length;){var a=t[i],u=r[o];if(null===a)i++;else if(r.length<=o)e.removeChild(t[i]),i++;else if(t.length<=i)e.insertBefore(u,t[i]||n),o++;else if(a===u)i++,o++;else{var c=s.get(a),f=l.get(u);void 0===c?(e.removeChild(t[i]),i++):void 0===f?(e.insertBefore(u,t[i]||n),o++):(e.insertBefore(t[f],t[i]||n),t[f]=null,f>i+1&&i++,o++)}}}(n.parentNode,f,y,n),f=y;for(let e=0;e<y.length;e++)O(y[e],h[e])}},g.ref=(e,t,r)=>{r[t]=e},d.render=(e,t,r)=>{let n=j(e,t,":render")(r);n||E(new Error("Template not found"),e,t,":render");let l=n.content.cloneNode(!0);e.replaceChildren(l),O(e,r)},d.id=(e,t)=>{let r=j(e,t,":id");return t=>{return n=r(t),e.id=n||0===n?n:"";var n}},d.class=(e,t)=>{let r=j(e,t,":class"),n=e.getAttribute("class");return t=>{let l=r(t),s=[n];l&&("string"==typeof l?s.push(l):Array.isArray(l)?s.push(...l):s.push(...Object.entries(l).map((([e,t])=>t?e:"")))),(s=s.filter(Boolean).join(" "))?e.setAttribute("class",s):e.removeAttribute("class")}},d.style=(e,t)=>{let r=j(e,t,":style"),n=e.getAttribute("style")||"";return n.endsWith(";")||(n+="; "),t=>{let l=r(t);if("string"==typeof l)e.setAttribute("style",n+l);else{e.setAttribute("style",n);for(let t in l)e.style.setProperty(t,l[t])}}},d.text=(e,t)=>{let r=j(e,t,":text");return t=>{let n=r(t);e.textContent=null==n?"":n}},d[""]=(e,t)=>{let r=j(e,t,":");if(r)return t=>{let n=r(t);for(let t in n)x(e,t.replace(/[A-Z\u00C0-\u00D6\u00D8-\u00DE]/g,(e=>"-"+e.toLowerCase())),n[t])}},d.value=(e,t)=>{let r,n,l=j(e,t,":value"),s="text"===e.type||""===e.type?t=>e.setAttribute("value",e.value=null==t?"":t):"TEXTAREA"===e.tagName||"text"===e.type||""===e.type?t=>(r=e.selectionStart,n=e.selectionEnd,e.setAttribute("value",e.value=null==t?"":t),r&&e.setSelectionRange(r,n)):"checkbox"===e.type?t=>(e.value=t?"on":"",x(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 e=>s(l(e))},d.on=(e,t)=>{let r=j(e,t,":on");return t=>{let n=r(t),l=[];for(let t in n)l.push(v(e,t,n[t]));return()=>{for(let e of l)e()}}};var b=(e,t,r,n)=>{let l=n.startsWith("on")&&n.slice(2),s=j(e,t,":"+n);if(s)return l?t=>{let r=s(t)||(()=>{});return v(e,l,r)}:t=>x(e,n,s(t))},v=(e,t,r)=>{if(!r)return;let n=t.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,n="")=>(r.test=A[t]?.(r,...n.split("-"))||r.test,""))),r}));if(1==n.length)return i(r,n[0]);const l=(t,r=0)=>{let s;return s=i((i=>{r&&s();let o=t.call(e,i);"function"!=typeof o&&(o=()=>{}),r+1<n.length&&l(o,r?r+1:1)}),n[r])};let s=l(r);return()=>s();function i(e,{evt:t,target:r,test:n,defer:l,stop:s,prevent:i,...o}){l&&(e=l(e));let a=t=>n(t)&&(s&&t.stopPropagation(),i&&t.preventDefault(),e.call(r,t));return r.addEventListener(t,a,o),()=>r.removeEventListener(t,a,o)}},A={prevent(e){e.prevent=!0},stop(e){e.stop=!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},toggle(e){e.defer=(t,r)=>n=>r?(r.call?.(e.target,n),r=null):r=t()},throttle(e,t){e.defer=e=>k(e,t?Number(t)||0:108)},debounce(e,t){e.defer=e=>W(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=>w.ctrl(e)&&t.every((t=>w[t]?w[t](e):e.key===t)),shift:(e,...t)=>e=>w.shift(e)&&t.every((t=>w[t]?w[t](e):e.key===t)),alt:(e,...t)=>e=>w.alt(e)&&t.every((t=>w[t]?w[t](e):e.key===t)),meta:(e,...t)=>e=>w.meta(e)&&t.every((t=>w[t]?w[t](e):e.key===t)),arrow:e=>w.arrow,enter:e=>w.enter,escape:e=>w.escape,tab:e=>w.tab,space:e=>w.space,backspace:e=>w.backspace,delete:e=>w.delete,digit:e=>w.digit,letter:e=>w.letter,character:e=>w.character},w={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,escape:e=>e.key.startsWith("Esc"),tab:e=>"Tab"===e.key,space:e=>" "===e.key||"Space"===e.key||" "===e.key,backspace:e=>"Backspace"===e.key,delete:e=>"Delete"===e.key,digit:e=>/^\d$/.test(e.key),letter:e=>/^[a-zA-Z]$/.test(e.key),character:e=>/^\S$/.test(e.key)},k=(e,t)=>{let r,n,l=s=>{r=!0,setTimeout((()=>{if(r=!1,n)return n=!1,l(s),e(s)}),t)};return t=>r?n=!0:(l(t),e(t))},W=(e,t)=>{let r;return n=>{clearTimeout(r),r=setTimeout((()=>{r=null,e(n)}),t)}},x=(e,t,r)=>{null==r||!1===r?e.removeAttribute(t):e.setAttribute(t,!0===r?"":"number"==typeof r||"string"==typeof r?r:"")},S={};function j(e,t,r){let n=S[t];if(!n){let l=/^[\n\s]*if.*\(.*\)/.test(t)||/\b(let|const)\s/.test(t)&&!r.startsWith(":on")?`(() => {${t}})()`:t;try{n=S[t]=new Function("__scope",`with (__scope) { return ${l.trim()} };`)}catch(n){return E(n,e,t,r)}}return l=>{let s;try{s=n.call(e,l)}catch(n){return E(n,e,t,r)}return s}}function E(e,t,n,l){Object.assign(e,{element:t,expression:n}),console.warn(`∴ ${e.message}\n\n${l}=${n?`"${n}"\n\n`:""}`,t),r((()=>{throw e}),0)}O.globals=c;var N=new WeakMap;function O(e,t){if(!e.children)return;if(N.has(e))return Object.assign(N.get(e),t);const r=p(t||{}),n=[],l=(e,t=e.parentNode)=>{for(let l in g){let s=":"+l;if(e.hasAttribute?.(s)){let i=e.getAttribute(s);if(e.removeAttribute(s),n.push(g[l](e,i,r,l)),N.has(e))return;if(e.parentNode!==t)return!1}}if(e.attributes)for(let t=0;t<e.attributes.length;){let l=e.attributes[t];if(":"!==l.name[0]){t++;continue}e.removeAttribute(l.name);let s=l.value,i=l.name.slice(1).split(":");for(let t of i){let l=d[t]||b;n.push(l(e,s,r,t))}}for(let t,r=0;t=e.children[r];r++)!1===l(t,e)&&r--};l(e);for(let e of n)if(e){let t;y((()=>{"function"==typeof t&&t(),t=e(r)}))}return N.set(e,r),r}var $=O;document.currentScript&&O(document.documentElement);export{$ as default};
|
package/src/directives.js
CHANGED
|
@@ -50,7 +50,6 @@ primary['with'] = (el, expr, rootState) => {
|
|
|
50
50
|
let evaluate = parseExpr(el, expr, ':with')
|
|
51
51
|
const localState = evaluate(rootState)
|
|
52
52
|
let state = createState(localState, rootState)
|
|
53
|
-
// console.log(123, state.foo, state.bar)
|
|
54
53
|
sprae(el, state);
|
|
55
54
|
}
|
|
56
55
|
|
|
@@ -173,8 +172,14 @@ secondary['class'] = (el, expr) => {
|
|
|
173
172
|
let initClassName = el.getAttribute('class')
|
|
174
173
|
return (state) => {
|
|
175
174
|
let v = evaluate(state)
|
|
176
|
-
let className =
|
|
177
|
-
|
|
175
|
+
let className = [initClassName]
|
|
176
|
+
if (v) {
|
|
177
|
+
if (typeof v === 'string') className.push(v)
|
|
178
|
+
else if (Array.isArray(v)) className.push(...v)
|
|
179
|
+
else className.push(...Object.entries(v).map(([k,v])=>v?k:''))
|
|
180
|
+
}
|
|
181
|
+
if (className = className.filter(Boolean).join(' ')) el.setAttribute('class', className);
|
|
182
|
+
else el.removeAttribute('class')
|
|
178
183
|
}
|
|
179
184
|
}
|
|
180
185
|
|
package/src/state.js
CHANGED
|
@@ -11,7 +11,7 @@ const _parent = Symbol('parent')
|
|
|
11
11
|
// default root sandbox
|
|
12
12
|
export const sandbox = {
|
|
13
13
|
Array, Object, Number, String, Boolean, Date,
|
|
14
|
-
console
|
|
14
|
+
console, window, document, history, location
|
|
15
15
|
}
|
|
16
16
|
|
|
17
17
|
const handler = {
|
|
@@ -23,9 +23,8 @@ const handler = {
|
|
|
23
23
|
get(target, prop) {
|
|
24
24
|
if (typeof prop === 'symbol') return target[prop]
|
|
25
25
|
if (!(prop in target)) return target[_parent]?.[prop]
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
if (prop in Object.prototype) return target[prop];
|
|
26
|
+
// .constructor, .slice etc
|
|
27
|
+
if (prop in Object.prototype || (Array.isArray(target) && prop in Array.prototype && prop !== 'length')) return target[prop];
|
|
29
28
|
|
|
30
29
|
let value = target[prop]
|
|
31
30
|
if (currentFx) {
|
|
@@ -51,13 +50,11 @@ const handler = {
|
|
|
51
50
|
},
|
|
52
51
|
|
|
53
52
|
set(target, prop, value) {
|
|
53
|
+
// "fake" prototype chain, since regular one doesn't fit
|
|
54
54
|
if (!(prop in target) && (target[_parent] && prop in target[_parent])) return target[_parent][prop] = value
|
|
55
|
-
if (Array.isArray(target) && prop in Array.prototype) return target[prop] = value;
|
|
56
|
-
|
|
57
|
-
const prev = target[prop]
|
|
58
55
|
|
|
59
56
|
// avoid bumping unchanged values
|
|
60
|
-
if (Object.is(
|
|
57
|
+
if (!Array.isArray(target) && Object.is(target[prop], value)) return true
|
|
61
58
|
|
|
62
59
|
target[prop] = value
|
|
63
60
|
|
|
@@ -88,6 +85,16 @@ export const state = (obj, parent) => {
|
|
|
88
85
|
targetProxy.set(obj, proxy)
|
|
89
86
|
proxyTarget.set(proxy, obj)
|
|
90
87
|
|
|
88
|
+
// bind all getters here to proxy
|
|
89
|
+
// FIXME: alternatively we can store getters somewhere
|
|
90
|
+
let descriptors = Object.getOwnPropertyDescriptors(obj)
|
|
91
|
+
for (let name in descriptors) {
|
|
92
|
+
let desc = descriptors[name]
|
|
93
|
+
if (desc.get) {
|
|
94
|
+
if (desc.get) desc.get = desc.get.bind(proxy), Object.defineProperty(obj, name, desc)
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
91
98
|
// inherit from parent state
|
|
92
99
|
obj[_parent] = parent ? state(parent) : sandbox
|
|
93
100
|
|
package/test/dom.js
CHANGED
|
@@ -109,6 +109,12 @@ test('class', async () => {
|
|
|
109
109
|
// is(el.outerHTML, `<x class="base x"></x><y class="y w"></y><z class="b c"></z>`);
|
|
110
110
|
})
|
|
111
111
|
|
|
112
|
+
test('class: undefined value', async () => {
|
|
113
|
+
let el = h`<x :class="a"></x><y :class="[b]"></y><z :class="{c}"></z>`
|
|
114
|
+
sprae(el, {a:undefined, b:undefined, c:undefined})
|
|
115
|
+
is(el.outerHTML, `<x></x><y></y><z></z>`)
|
|
116
|
+
})
|
|
117
|
+
|
|
112
118
|
test('class: old svg fun', async () => {
|
|
113
119
|
// raw html creates svganimatedstring
|
|
114
120
|
let el = document.createElement('div')
|
|
@@ -1015,28 +1021,44 @@ test(':: scope directives must come first', async () => {
|
|
|
1015
1021
|
is(a.outerHTML, `<x>1</x>`)
|
|
1016
1022
|
})
|
|
1017
1023
|
|
|
1018
|
-
test.
|
|
1019
|
-
let
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1024
|
+
test.todo('immediate scope', async () => {
|
|
1025
|
+
let el = h`<x :with="{arr:[], inc(){ arr.push(1) }}" :onx="e=>inc()" :text="arr[0]"></x>`
|
|
1026
|
+
sprae(el)
|
|
1027
|
+
is(el.outerHTML, `<x></x>`)
|
|
1028
|
+
el.dispatchEvent(new CustomEvent('x'))
|
|
1029
|
+
await tick()
|
|
1030
|
+
is(el.outerHTML, `<x>1</x>`)
|
|
1031
|
+
})
|
|
1032
|
+
|
|
1033
|
+
test('getters', async () => {
|
|
1034
|
+
let x = h`<h2 :text="doubledCount >= 1 ? 1 : 0"></h2>`
|
|
1025
1035
|
let state = sprae(x, {
|
|
1026
1036
|
count:0,
|
|
1027
|
-
get doubledCount(){
|
|
1028
|
-
increment(){ this.count++ }
|
|
1037
|
+
get doubledCount(){ return this.count * 2 }
|
|
1029
1038
|
})
|
|
1039
|
+
is(x.outerHTML, `<h2>0</h2>`)
|
|
1040
|
+
state.count++
|
|
1041
|
+
await tick()
|
|
1042
|
+
is(x.outerHTML, `<h2>1</h2>`)
|
|
1030
1043
|
})
|
|
1031
1044
|
|
|
1032
1045
|
test('sandbox', async () => {
|
|
1033
|
-
let el = h`<x :x="log.push(typeof
|
|
1046
|
+
let el = h`<x :x="log.push(typeof self, typeof console, typeof arguments, typeof __scope)"></x>`
|
|
1034
1047
|
let log = []
|
|
1035
1048
|
sprae(el.cloneNode(), {log})
|
|
1036
1049
|
is(log, ['undefined', 'object', 'undefined', 'undefined'])
|
|
1037
1050
|
|
|
1038
1051
|
log.splice(0)
|
|
1039
|
-
Object.assign(sprae.globals, { window })
|
|
1052
|
+
Object.assign(sprae.globals, { self: window })
|
|
1040
1053
|
sprae(el.cloneNode(), {log})
|
|
1041
1054
|
is(log, ['object', 'object', 'undefined', 'undefined'])
|
|
1055
|
+
})
|
|
1056
|
+
|
|
1057
|
+
test('subscribe to array length', async () => {
|
|
1058
|
+
let el = h`<div :with="{likes:[]}"><x :onx="e=>likes.push(1)"></x><y :text="likes.length"></y></div>`
|
|
1059
|
+
sprae(el)
|
|
1060
|
+
is(el.innerHTML, `<x></x><y>0</y>`)
|
|
1061
|
+
el.firstChild.dispatchEvent(new CustomEvent('x'))
|
|
1062
|
+
await tick()
|
|
1063
|
+
is(el.innerHTML, `<x></x><y>1</y>`)
|
|
1042
1064
|
})
|
package/todo.md
CHANGED
|
@@ -48,4 +48,6 @@
|
|
|
48
48
|
* [x] parallel chains
|
|
49
49
|
* [x] Sandbox
|
|
50
50
|
* [x] Autorun
|
|
51
|
-
* [
|
|
51
|
+
* [x] There's some bug with prostogreen not triggering effect. (caused by special array.length case)
|
|
52
|
+
* [x] Getters must become evaluable
|
|
53
|
+
* [ ] `:with="{likes:[], like(){ /* likes should not be undefined here */ }}"`
|