sprae 2.3.1 → 2.4.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/package.json +1 -1
- package/plan.md +5 -2
- package/r&d.md +110 -18
- package/readme.md +21 -33
- package/sprae.js +57 -28
- package/sprae.min.js +1 -1
- package/src/core.js +2 -0
- package/src/directives.js +66 -30
- package/test/test.js +76 -19
package/package.json
CHANGED
package/plan.md
CHANGED
|
@@ -29,8 +29,11 @@
|
|
|
29
29
|
* [x] Make sure `false` gets serialized, not removes attr
|
|
30
30
|
* [ ] Sandbox expressions: no global, no "scope" object name, no "arguments"
|
|
31
31
|
* ~~[x] report usignal problem~~ author is not really interested
|
|
32
|
-
* [
|
|
32
|
+
* [x] `this` doesn't refer to element/scope in event handlers
|
|
33
|
+
* [x] :text="" empty values shouldn't throw
|
|
34
|
+
* [ ] implement :with
|
|
33
35
|
* [ ] frameworks benchmark
|
|
34
36
|
* [ ] examples
|
|
35
37
|
* [x] todomvc
|
|
36
|
-
* [ ] wave-edit
|
|
38
|
+
* [ ] wave-edit
|
|
39
|
+
* [ ] `sprae(el, newState)` can update element's state directly (as batch!?) -> must be tested against repeats in directives
|
package/r&d.md
CHANGED
|
@@ -97,17 +97,40 @@
|
|
|
97
97
|
|
|
98
98
|
-> possibly we have to just subscribe via mechanism of signals-like deps, and :with just initializes subtree with extended object
|
|
99
99
|
|
|
100
|
-
## [
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
+
|
|
106
|
-
+ it
|
|
107
|
-
|
|
108
|
-
+ it
|
|
109
|
-
|
|
110
|
-
|
|
100
|
+
## [ ] :with? ->
|
|
101
|
+
|
|
102
|
+
1. Get rid of :with
|
|
103
|
+
+ with is bad JS practice/association
|
|
104
|
+
+? is there a bona-fide use case?
|
|
105
|
+
+ the implementation is heavy/unoptimal: two assign-updates happen: for root, for children
|
|
106
|
+
+ it is exception blocking streamline implementation of refs
|
|
107
|
+
+ it shadows data which creates all sorts of nasty debugging effects / states. Isn't it better to keep data/state transparent?
|
|
108
|
+
+ it even enables transparency of :each scopes, since they inherit root scope
|
|
109
|
+
+ it's easier to look out for data in one single place (state init), rather than in a bunch of markup locations
|
|
110
|
+
+?! can be replaced with sort of `<x :xyz="xyz=...calc"></x>`, no?
|
|
111
|
+
-> would need wrapping noname scope access
|
|
112
|
+
+ `:with` defines too many concerns:
|
|
113
|
+
* binds root updates -> child updates;
|
|
114
|
+
* binds child updates to root updates (writes);
|
|
115
|
+
* defines local variables
|
|
116
|
+
* aliases root variables
|
|
117
|
+
? is there value in all of these concerns? It seems we need only local variables, isn't it? Is there a chance partial extension can be required?
|
|
118
|
+
- `:with` can provide situational variables that are useful for props precalculation (since these variables can be reactive.)
|
|
119
|
+
* eg. `<span class="preloader" :with="{str: ''}" :init="setTimeout(() => str+='.', 500)" :text="str" />`
|
|
120
|
+
* that plays role of watch that doesn't require to be outside of local component state.
|
|
121
|
+
- `:with` allows local component state not cluttering global state. There's really no way to define local state that doesn't affect global state.
|
|
122
|
+
1.1 Slim `:with="{a,b,c}"` - just initializes vars
|
|
123
|
+
- Doesn't enable easy init syntax
|
|
124
|
+
2. Use `:let="x, y=2"`?
|
|
125
|
+
+ Doesn't pollute scope but instead cleanly declares local variables
|
|
126
|
+
+ Indicates only local initializer, not subscription
|
|
127
|
+
+ Liquid has `assign` tag `{% assign myVar = false %}` - it only initializes variable
|
|
128
|
+
+ Django `with` performs only alias / complex calc access https://docs.djangoproject.com/en/4.1/ref/templates/builtins/#with - it doesn't sync up global state.
|
|
129
|
+
? call it `:define="x, y, z"`?
|
|
130
|
+
-> it seems `:with="x=1, y=2"` works well. `:let` has dissonance with js'y let.
|
|
131
|
+
? how to extend state
|
|
132
|
+
3. `:with.x="1", :with.y="2"`
|
|
133
|
+
+ easier to parse, since init code can be _messy_
|
|
111
134
|
|
|
112
135
|
## [x] Should we inherit values from `init` in `sprae(el, init)`, instead of creating a snapshot of reactive values in `init`? -> nah, nice idea but too little use. Better create signals struct.
|
|
113
136
|
|
|
@@ -191,11 +214,24 @@
|
|
|
191
214
|
+ methods provided in `init` may not have access to scope _yet_.
|
|
192
215
|
~- not reliable way to obtain scope via `this.x` - better be explicit as `state.x`
|
|
193
216
|
|
|
194
|
-
## [x] :onconnected/:ondisconnected? ->
|
|
217
|
+
## [x] :onconnected/:ondisconnected? -> nah, just use noname effect or external functionality eg fast-on-load
|
|
195
218
|
|
|
196
|
-
|
|
219
|
+
+ can be useful for :if, :each handlers, eg to start animation when element appears in DOM.
|
|
220
|
+
- it is not connected-disconnected: it has nothing to do with document: it attaches/detaches from parent.
|
|
221
|
+
- connected-disconnected is too long name
|
|
222
|
+
? attach-detach?
|
|
223
|
+
? onmount-onunmount?
|
|
224
|
+
- slows down domdiff
|
|
225
|
+
- can be solved as `<x :if="xxx" :="xxx ? (...) : '">` automatically
|
|
226
|
+
|
|
227
|
+
## [x] :focus="direct code", :evt="direct code" -> nah, too messy.
|
|
228
|
+
|
|
229
|
+
+ makes proper use-case for direct code events
|
|
230
|
+
- doesn't make sense for rective properties inside
|
|
231
|
+
+ better fit for special props like `:mount`
|
|
232
|
+
- tons of new special-meaning namespace props
|
|
197
233
|
|
|
198
|
-
## [x] Chain of events: often useful to have shared context. -> Try `:
|
|
234
|
+
## [x] Chain of events: often useful to have shared context. -> Try `:onstart..onend`
|
|
199
235
|
|
|
200
236
|
* focus/blur, connected/disconnected, mousedown/mouseup, keydown/keyup, touchstart/touchmove/touchend, dragstart/dragover/dragend, animationstart/animationover/animationend, transitionstart/transitionend
|
|
201
237
|
? is there a way to organize handlers for chains of events?
|
|
@@ -208,14 +244,35 @@
|
|
|
208
244
|
- ? why not :on-focus-blur
|
|
209
245
|
- ? why not :onfocus-onblur
|
|
210
246
|
+ can be converted from on="{ focusBlur: event }" via dash notation
|
|
247
|
+
- messy error messages
|
|
211
248
|
+ less :on prefixes
|
|
212
249
|
+ has better "flowy" meaning
|
|
213
250
|
* 2.1 :onfocus-onblur="e => e => {}"
|
|
214
251
|
+ distinctive visually as 1
|
|
215
252
|
+ flowy nature of 2
|
|
253
|
+
- blocks `:onfile-attached` and other dashed events
|
|
254
|
+
- `ona-onb` vs `ona-b-onc` is hard to parse mentally
|
|
216
255
|
3. :onfocus.onblur="e => e => {}"
|
|
217
256
|
- looks like a modifier
|
|
218
257
|
- . can be used as event class onclick.x
|
|
258
|
+
4. `:onfocus--blur="e => e => {}"`
|
|
259
|
+
+ reminds BEM
|
|
260
|
+
- reminds BEM
|
|
261
|
+
5. `:onfocus..blur="e => e => {}"`
|
|
262
|
+
+ reminds range
|
|
263
|
+
+ literally means start..end
|
|
264
|
+
+ all pros of 2.
|
|
265
|
+
+ reminds spray via dots
|
|
266
|
+
- can be confusing if `blur` is event or just property (can that be a property?)
|
|
267
|
+
6. `:onfocus..onblur="e => e => {}"`
|
|
268
|
+
+ all props of 5.
|
|
269
|
+
+ more obvious that blur is event.
|
|
270
|
+
|
|
271
|
+
4. `:onfocus="e => e => {}"` Keep registered pairs of events: just expect focus return blur, etc.
|
|
272
|
+
+ Shorter syntax
|
|
273
|
+
+ Avoids :onfile-attachment-accepted problem
|
|
274
|
+
- Less verbose and explicit
|
|
275
|
+
- No way to customize sequences, eg. custom events
|
|
219
276
|
|
|
220
277
|
## [ ] Plugins
|
|
221
278
|
|
|
@@ -224,9 +281,10 @@
|
|
|
224
281
|
- can be solved naturally, unless there's special meaning
|
|
225
282
|
* @sprae/hcodes: `<x :hcode=""` – provide microformats
|
|
226
283
|
* @sprae/with
|
|
227
|
-
* @sprae/
|
|
284
|
+
* @sprae/onconnected
|
|
285
|
+
* @sprae/onvisible?
|
|
228
286
|
|
|
229
|
-
## [x] Write any-attributes via
|
|
287
|
+
## [x] Write any-attributes via `:<prop>? -> yep`
|
|
230
288
|
|
|
231
289
|
+ Since we support attr walking, maybe instead of :on and :prop just allow any attributes?
|
|
232
290
|
+ that would allow event and attr modifiers...
|
|
@@ -263,8 +321,10 @@
|
|
|
263
321
|
+ Provides precisely controlled sandbox
|
|
264
322
|
- Some limited lang opportunities
|
|
265
323
|
- need to match many syntax quirks, can be tedious
|
|
324
|
+
~ can be fine to limit expressions to meaningful default: why Proxy, generators, awaits, global access etc.
|
|
266
325
|
- Somewhat heavy to bundle
|
|
267
|
-
|
|
326
|
+
~ 1-2kb is not super-heavy, besides signal-struct kicks out
|
|
327
|
+
+ Scope is easier to provide: no need for signal proxy
|
|
268
328
|
+ Can detect access errors in advance
|
|
269
329
|
+ Syntax-level access to signals can be inavoidable: external signals still "leak in" (via arrays or etc.).
|
|
270
330
|
+ Updating simple objects should also rerender the template parts, not just signals.
|
|
@@ -272,6 +332,9 @@
|
|
|
272
332
|
- Screwed up debugging / stacktrace (unless errored)
|
|
273
333
|
+ that "unlimits" returned struct, so that any property can be added/deleted.
|
|
274
334
|
- doesn't really save from `new (()=>{}).constructor` hack: we gotta substitute objects too.
|
|
335
|
+
+ allows easier handle of `:with="a=1,b=2,c=3"` - we just naturally get local variables without messup with global
|
|
336
|
+
+ we can even define locals without `let`...
|
|
337
|
+
- not having "comfy" compatible JS at hand: cognitive load of whole language "layer" in-between
|
|
275
338
|
|
|
276
339
|
2. Use sandboxed proxy
|
|
277
340
|
- tough evaluation
|
|
@@ -297,4 +360,33 @@
|
|
|
297
360
|
## [ ] Better :ref
|
|
298
361
|
+ :ref="`a-${1}`"
|
|
299
362
|
+ :id:ref="xyz"
|
|
300
|
-
? maybe id should have same signature
|
|
363
|
+
? maybe id should have same signature
|
|
364
|
+
? should it be very similar mechanism to `:with="a=1,b=2"`
|
|
365
|
+
|
|
366
|
+
## [ ] Event modifiers :ona.once, `:ona`
|
|
367
|
+
|
|
368
|
+
- .prevent,.stop - not needed since expects a function
|
|
369
|
+
? or should we just trigger it for user?
|
|
370
|
+
? :onclick.outside
|
|
371
|
+
? :onclick.window, :onclick.document
|
|
372
|
+
? :onclick.once
|
|
373
|
+
? :onclick.debounce
|
|
374
|
+
? :onclick.throttle.750ms
|
|
375
|
+
? :onclick.self
|
|
376
|
+
? :onspecial-event.camel, :onx-y.dot
|
|
377
|
+
? :onclick.passive
|
|
378
|
+
? :onkeypress.shift.enter
|
|
379
|
+
.shift Shift
|
|
380
|
+
.enter Enter
|
|
381
|
+
.space Space
|
|
382
|
+
.ctrl Ctrl
|
|
383
|
+
.cmd Cmd
|
|
384
|
+
.meta Cmd on Mac, Windows key on Windows
|
|
385
|
+
.alt Alt
|
|
386
|
+
.up .down .left .right Up/Down/Left/Right arrows
|
|
387
|
+
.escape Escape
|
|
388
|
+
.tab Tab
|
|
389
|
+
.caps-lock Caps Lock
|
|
390
|
+
.equal Equal, =
|
|
391
|
+
.period Period, .
|
|
392
|
+
.slash Foward Slash, /
|
package/readme.md
CHANGED
|
@@ -89,41 +89,44 @@ Welcome, <span :text="user.name">Guest</span>.
|
|
|
89
89
|
|
|
90
90
|
#### `:class="value"`
|
|
91
91
|
|
|
92
|
-
Set class value from either string, array or object.
|
|
92
|
+
Set class value from either string, array or object. Extends direct class, rather than replaces.
|
|
93
93
|
|
|
94
94
|
```html
|
|
95
95
|
<div :class="`foo ${bar}`"></div>
|
|
96
96
|
<div :class="['foo', 'bar']"></div>
|
|
97
97
|
<div :class="{foo: true, bar: false}"></div>
|
|
98
|
+
|
|
99
|
+
<div class="a" :class="['b', 'c']"></div>
|
|
100
|
+
<!--
|
|
101
|
+
<div class="a b c"></div>
|
|
102
|
+
-->
|
|
98
103
|
```
|
|
99
104
|
|
|
100
105
|
#### `:style="value"`
|
|
101
106
|
|
|
102
|
-
Set style value from object or a string.
|
|
107
|
+
Set style value from object or a string. Extends style.
|
|
103
108
|
|
|
104
109
|
```html
|
|
105
110
|
<div :style="foo: bar"></div>
|
|
106
111
|
<div :style="{foo: 'bar'}"></div>
|
|
107
112
|
```
|
|
108
113
|
|
|
109
|
-
<!--
|
|
110
114
|
#### `:value="value"`
|
|
111
115
|
|
|
112
|
-
Set value of an input, textarea or select.
|
|
116
|
+
Set value of an input, textarea or select. Takes handle of `checked` and `selected` attributes.
|
|
113
117
|
|
|
114
118
|
```html
|
|
115
|
-
<input :
|
|
116
|
-
<textarea :
|
|
119
|
+
<input :value="text" />
|
|
120
|
+
<textarea :value="text" />
|
|
117
121
|
|
|
118
|
-
<select :
|
|
122
|
+
<select :value="selected">
|
|
119
123
|
<option :each="i in 5" :value="i" :text="i"></option>
|
|
120
124
|
</select>
|
|
121
125
|
```
|
|
122
|
-
-->
|
|
123
126
|
|
|
124
|
-
#### `:<prop>="value"`, `:="props"`
|
|
127
|
+
#### `:<prop>="value?"`, `:="props?"`
|
|
125
128
|
|
|
126
|
-
Set any prop value.
|
|
129
|
+
Set any prop value or run effect.
|
|
127
130
|
|
|
128
131
|
```html
|
|
129
132
|
<!-- Single property -->
|
|
@@ -134,9 +137,12 @@ Set any prop value.
|
|
|
134
137
|
|
|
135
138
|
<!-- Bulk properties -->
|
|
136
139
|
<input :="{ id: name, name, type:'text', value }" />
|
|
140
|
+
|
|
141
|
+
<!-- Effect (triggers any time foo or bar changes) -->
|
|
142
|
+
<div :="if (foo) bar()" :fx="void bar()" ></div>
|
|
137
143
|
```
|
|
138
144
|
|
|
139
|
-
#### `:on<event>="handler"`, `:on="events"`
|
|
145
|
+
#### `:on<event>="handler"`, `:on="events"`, `:<in>..<out>="handler"`
|
|
140
146
|
|
|
141
147
|
Add event listeners.
|
|
142
148
|
|
|
@@ -148,7 +154,7 @@ Add event listeners.
|
|
|
148
154
|
<input :value="text" :oninput:onchange="e => text=e.target.value">
|
|
149
155
|
|
|
150
156
|
<!-- Sequence of events -->
|
|
151
|
-
<button :onfocus
|
|
157
|
+
<button :onfocus..onblur="e => {
|
|
152
158
|
// onfocus
|
|
153
159
|
let id = setInterval(track,200)
|
|
154
160
|
return e => {
|
|
@@ -161,23 +167,6 @@ Add event listeners.
|
|
|
161
167
|
<button :on="{ click: handler, touch: handler, special: handler }">Submit</button>
|
|
162
168
|
```
|
|
163
169
|
|
|
164
|
-
<!--
|
|
165
|
-
#### `:oninit="fn"`
|
|
166
|
-
|
|
167
|
-
Create initializer handler - called whenever element is initialized by sprae.
|
|
168
|
-
|
|
169
|
-
```html
|
|
170
|
-
<div id="foo" :oninit="function(){ this === foo }"></div>
|
|
171
|
-
```
|
|
172
|
-
|
|
173
|
-
#### `:onconnect="fn"`, `:ondisconnect="fn"`
|
|
174
|
-
|
|
175
|
-
Invoked when element is connected or disconnected from DOM.
|
|
176
|
-
|
|
177
|
-
```html
|
|
178
|
-
```
|
|
179
|
-
-->
|
|
180
|
-
|
|
181
170
|
#### `:data="values"`
|
|
182
171
|
|
|
183
172
|
Set [data-*](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/data-*) attributes. CamelCase is converted to dash-case.
|
|
@@ -217,9 +206,7 @@ Expose element to data scope with the `id`:
|
|
|
217
206
|
<!-- iterable items -->
|
|
218
207
|
<ul>
|
|
219
208
|
<li :each="item in items" :ref="item">
|
|
220
|
-
<input
|
|
221
|
-
:onfocus-onblur="e => (item.classList.add('editing'), e => item.classList.remove('editing'))"
|
|
222
|
-
/>
|
|
209
|
+
<input :onfocus..onblur="e => (item.classList.add('editing'), e => item.classList.remove('editing'))"/>
|
|
223
210
|
</li>
|
|
224
211
|
</ul>
|
|
225
212
|
|
|
@@ -325,8 +312,9 @@ _sprae_ takes convention of _templize directives_ (_alpine_/_vue_ attrs) and bui
|
|
|
325
312
|
* It falls back to element content if uninitialized.
|
|
326
313
|
* It doesn't enforce SPA nor JSX.
|
|
327
314
|
* It enables island hydration.
|
|
328
|
-
* It
|
|
315
|
+
* It reserves minimal syntax space as `:` convention (keeping tree neatly decorated, not scattered).
|
|
329
316
|
* Expressions are naturally reactive and incur minimal updates.
|
|
330
317
|
* Input data may contain [signals](https://ghub.io/@preact/signals) or [reactive values](https://ghub.io/sube).
|
|
318
|
+
* Elements / data API is open and enable easy interop.
|
|
331
319
|
|
|
332
320
|
<p align="center"><a href="https://github.com/krsnzd/license/">🕉</a></p>
|
package/sprae.js
CHANGED
|
@@ -452,11 +452,14 @@ var primitive_pool_default = (key) => {
|
|
|
452
452
|
// src/directives.js
|
|
453
453
|
var directives = {};
|
|
454
454
|
var directives_default = (el, expr, values, name) => {
|
|
455
|
-
|
|
456
|
-
return directives.on(el, `{"${name.split("-").map((n2) => n2.startsWith("on") ? n2.slice(2) : n2).join("-")}": ${expr}}`, values);
|
|
457
|
-
}
|
|
455
|
+
let evt = name.startsWith("on") && name.slice(2);
|
|
458
456
|
let evaluate = parseExpr(el, expr, ":" + name);
|
|
459
|
-
|
|
457
|
+
let value;
|
|
458
|
+
return evt ? (state) => {
|
|
459
|
+
value && removeListener(el, evt, value);
|
|
460
|
+
value = evaluate(state);
|
|
461
|
+
value && addListener(el, evt, value);
|
|
462
|
+
} : (state) => attr(el, name, evaluate(state));
|
|
460
463
|
};
|
|
461
464
|
var attr = (el, name, v2) => {
|
|
462
465
|
if (v2 == null || v2 === false)
|
|
@@ -482,6 +485,13 @@ directives["ref"] = (el, expr, values) => {
|
|
|
482
485
|
;
|
|
483
486
|
values[expr] = el;
|
|
484
487
|
};
|
|
488
|
+
directives["with"] = (el, expr, rootState) => {
|
|
489
|
+
let evaluate = parseExpr(el, expr, "with");
|
|
490
|
+
throw "Unimplemented";
|
|
491
|
+
let withValues = evaluate(rootState);
|
|
492
|
+
let newState = Object.assign(Object.create(rootState), withValues);
|
|
493
|
+
sprae(el, newState);
|
|
494
|
+
};
|
|
485
495
|
directives["if"] = (el, expr, values) => {
|
|
486
496
|
let holder = document.createTextNode(""), clauses = [parseExpr(el, expr, ":if")], els = [el], cur = el;
|
|
487
497
|
while (cur = el.nextElementSibling) {
|
|
@@ -591,10 +601,13 @@ directives["class"] = (el, expr, values) => {
|
|
|
591
601
|
};
|
|
592
602
|
directives["style"] = (el, expr, values) => {
|
|
593
603
|
let evaluate = parseExpr(el, expr, ":style");
|
|
604
|
+
let initStyle = el.getAttribute("style") || "";
|
|
605
|
+
if (!initStyle.endsWith(";"))
|
|
606
|
+
initStyle += "; ";
|
|
594
607
|
return (state) => {
|
|
595
608
|
let v2 = evaluate(state);
|
|
596
609
|
if (typeof v2 === "string")
|
|
597
|
-
el.setAttribute("style", v2);
|
|
610
|
+
el.setAttribute("style", initStyle + v2);
|
|
598
611
|
else
|
|
599
612
|
for (let k in v2)
|
|
600
613
|
el.style[k] = v2[k];
|
|
@@ -608,39 +621,53 @@ directives["text"] = (el, expr, values) => {
|
|
|
608
621
|
return (state) => update(evaluate(state));
|
|
609
622
|
};
|
|
610
623
|
directives["value"] = (el, expr, values) => {
|
|
611
|
-
let evaluate = parseExpr(el, expr, ":
|
|
612
|
-
let update = el.type === "text" || el.type === "" ? (value) => el.setAttribute("value", el.value = value == null ? "" : value) : el.type === "checkbox" ? (value) => (el.value = value ? "on" : "", attr(el, "checked", value)) : el.type === "select-one" ? (value) =>
|
|
624
|
+
let evaluate = parseExpr(el, expr, ":value");
|
|
625
|
+
let update = el.type === "text" || el.type === "" ? (value) => el.setAttribute("value", el.value = value == null ? "" : value) : el.type === "checkbox" ? (value) => (el.value = value ? "on" : "", attr(el, "checked", value)) : el.type === "select-one" ? (value) => {
|
|
626
|
+
for (let option in el.options)
|
|
627
|
+
option.removeAttribute("selected");
|
|
628
|
+
el.value = value;
|
|
629
|
+
el.selectedOptions[0]?.setAttribute("selected", "");
|
|
630
|
+
} : (value) => el.value = value;
|
|
613
631
|
return (state) => update(evaluate(state));
|
|
614
632
|
};
|
|
633
|
+
var _stop = Symbol("stop");
|
|
615
634
|
directives["on"] = (el, expr, values) => {
|
|
616
635
|
let evaluate = parseExpr(el, expr, ":on");
|
|
617
636
|
let listeners = {};
|
|
618
637
|
return (state) => {
|
|
619
638
|
for (let evt in listeners)
|
|
620
|
-
el
|
|
639
|
+
removeListener(el, evt, listeners[evt]);
|
|
621
640
|
listeners = evaluate(state);
|
|
622
|
-
for (let evt in listeners)
|
|
623
|
-
|
|
624
|
-
if (evts.length === 1)
|
|
625
|
-
el.addEventListener(evt, listeners[evt]);
|
|
626
|
-
else {
|
|
627
|
-
const startFn = listeners[evt];
|
|
628
|
-
const nextEvt = (fn, cur = 0) => {
|
|
629
|
-
el.addEventListener(evts[cur], listeners[evt] = (e2) => {
|
|
630
|
-
fn = fn(e2);
|
|
631
|
-
el.removeEventListener(evts[cur], listeners[evt]);
|
|
632
|
-
if (++cur < evts.length && typeof fn === "function")
|
|
633
|
-
nextEvt(fn, cur);
|
|
634
|
-
else
|
|
635
|
-
nextEvt(startFn);
|
|
636
|
-
});
|
|
637
|
-
};
|
|
638
|
-
nextEvt(startFn);
|
|
639
|
-
}
|
|
640
|
-
}
|
|
641
|
-
;
|
|
641
|
+
for (let evt in listeners)
|
|
642
|
+
addListener(el, evt, listeners[evt]);
|
|
642
643
|
};
|
|
643
644
|
};
|
|
645
|
+
var addListener = (el, evt, startFn) => {
|
|
646
|
+
if (evt.indexOf("..") < 0)
|
|
647
|
+
el.addEventListener(evt, startFn);
|
|
648
|
+
else {
|
|
649
|
+
const evts = evt.split("..").map((e2) => e2.startsWith("on") ? e2.slice(2) : e2);
|
|
650
|
+
const nextEvt = (fn, cur = 0) => {
|
|
651
|
+
let curListener = (e2) => {
|
|
652
|
+
el.removeEventListener(evts[cur], curListener);
|
|
653
|
+
if (typeof (fn = fn.call(el, e2)) !== "function")
|
|
654
|
+
fn = () => {
|
|
655
|
+
};
|
|
656
|
+
if (++cur < evts.length)
|
|
657
|
+
nextEvt(fn, cur);
|
|
658
|
+
else if (!startFn[_stop])
|
|
659
|
+
console.log("reset"), nextEvt(startFn);
|
|
660
|
+
};
|
|
661
|
+
el.addEventListener(evts[cur], curListener);
|
|
662
|
+
};
|
|
663
|
+
nextEvt(startFn);
|
|
664
|
+
}
|
|
665
|
+
};
|
|
666
|
+
var removeListener = (el, evt, fn) => {
|
|
667
|
+
if (evt.indexOf("..") >= 0)
|
|
668
|
+
console.log("rewire"), fn[_stop] = true;
|
|
669
|
+
el.removeEventListener(evt, fn);
|
|
670
|
+
};
|
|
644
671
|
directives["data"] = (el, expr, values) => {
|
|
645
672
|
let evaluate = parseExpr(el, expr, ":data");
|
|
646
673
|
return (state) => {
|
|
@@ -712,6 +739,8 @@ function sprae(container, values) {
|
|
|
712
739
|
}
|
|
713
740
|
el.removeAttribute(attr2.name);
|
|
714
741
|
let expr = attr2.value;
|
|
742
|
+
if (!expr)
|
|
743
|
+
continue;
|
|
715
744
|
let attrNames = attr2.name.slice(1).split(":");
|
|
716
745
|
for (let attrName of attrNames) {
|
|
717
746
|
let dir = directives[attrName] || directives_default;
|
package/sprae.min.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
function t(){throw new Error("Cycle detected")}function e(){if(n>1)n--;else{for(var t,e=!1;void 0!==r;){var i=r;for(r=void 0,o++;void 0!==i;){var s=i.o;if(i.o=void 0,i.f&=-3,!(8&i.f)&&l(i))try{i.c()}catch(i){e||(t=i,e=!0)}i=s}}if(o=0,n--,e)throw t}}var i=void 0,r=void 0,n=0,o=0,s=0;function f(t){if(void 0!==i){var e=t.n;if(void 0===e||e.t!==i)return i.s=e={i:0,S:t,p:void 0,n:i.s,t:i,e:void 0,x:void 0,r:e},t.n=e,32&i.f&&t.S(e),e;if(-1===e.i)return e.i=0,void 0!==e.p&&(e.p.n=e.n,void 0!==e.n&&(e.n.p=e.p),e.p=void 0,e.n=i.s,i.s.p=e,i.s=e),e}}function a(t){this.v=t,this.i=0,this.n=void 0,this.t=void 0}function u(t){return new a(t)}function l(t){for(var e=t.s;void 0!==e;e=e.n)if(e.S.i!==e.i||!e.S.h()||e.S.i!==e.i)return!0;return!1}function h(t){for(var e=t.s;void 0!==e;e=e.n){var i=e.S.n;void 0!==i&&(e.r=i),e.S.n=e,e.i=-1}}function c(t){for(var e=t.s,i=void 0;void 0!==e;){var r=e.n;-1===e.i?(e.S.U(e),e.n=void 0):(void 0!==i&&(i.p=e),e.p=void 0,e.n=i,i=e),e.S.n=e.r,void 0!==e.r&&(e.r=void 0),e=r}t.s=i}function v(t){a.call(this,void 0),this.x=t,this.s=void 0,this.g=s-1,this.f=4}function p(t){var r=t.u;if(t.u=void 0,"function"==typeof r){n++;var o=i;i=void 0;try{r()}catch(e){throw t.f&=-2,t.f|=8,d(t),e}finally{i=o,e()}}}function d(t){for(var e=t.s;void 0!==e;e=e.n)e.S.U(e);t.x=void 0,t.s=void 0,p(t)}function y(t){if(i!==this)throw new Error("Out-of-order effect");c(this),i=t,this.f&=-2,8&this.f&&d(this),e()}function b(t){this.x=t,this.u=void 0,this.s=void 0,this.o=void 0,this.f=32}function m(t){var e=new b(t);return e.c(),e.d.bind(e)}a.prototype.h=function(){return!0},a.prototype.S=function(t){this.t!==t&&void 0===t.e&&(t.x=this.t,void 0!==this.t&&(this.t.e=t),this.t=t)},a.prototype.U=function(t){var e=t.e,i=t.x;void 0!==e&&(e.x=i,t.e=void 0),void 0!==i&&(i.e=e,t.x=void 0),t===this.t&&(this.t=i)},a.prototype.subscribe=function(t){var e=this;return m((function(){var i=e.value,r=32&this.f;this.f&=-33;try{t(i)}finally{this.f|=r}}))},a.prototype.valueOf=function(){return this.value},a.prototype.toString=function(){return this.value+""},a.prototype.peek=function(){return this.v},Object.defineProperty(a.prototype,"value",{get:function(){var t=f(this);return void 0!==t&&(t.i=this.i),this.v},set:function(i){if(i!==this.v){o>100&&t(),this.v=i,this.i++,s++,n++;try{for(var r=this.t;void 0!==r;r=r.x)r.t.N()}finally{e()}}}}),(v.prototype=new a).h=function(){if(this.f&=-3,1&this.f)return!1;if(32==(36&this.f))return!0;if(this.f&=-5,this.g===s)return!0;if(this.g=s,this.f|=1,this.i>0&&!l(this))return this.f&=-2,!0;var t=i;try{h(this),i=this;var e=this.x();(16&this.f||this.v!==e||0===this.i)&&(this.v=e,this.f&=-17,this.i++)}catch(t){this.v=t,this.f|=16,this.i++}return i=t,c(this),this.f&=-2,!0},v.prototype.S=function(t){if(void 0===this.t){this.f|=36;for(var e=this.s;void 0!==e;e=e.n)e.S.S(e)}a.prototype.S.call(this,t)},v.prototype.U=function(t){if(a.prototype.U.call(this,t),void 0===this.t){this.f&=-33;for(var e=this.s;void 0!==e;e=e.n)e.S.U(e)}},v.prototype.N=function(){if(!(2&this.f)){this.f|=6;for(var t=this.t;void 0!==t;t=t.x)t.t.N()}},v.prototype.peek=function(){if(this.h()||t(),16&this.f)throw this.v;return this.v},Object.defineProperty(v.prototype,"value",{get:function(){1&this.f&&t();var e=f(this);if(this.h(),void 0!==e&&(e.i=this.i),16&this.f)throw this.v;return this.v}}),b.prototype.c=function(){var t=this.S();try{8&this.f||void 0===this.x||(this.u=this.x())}finally{t()}},b.prototype.S=function(){1&this.f&&t(),this.f|=1,this.f&=-9,p(this),h(this),n++;var e=i;return i=this,y.bind(this,e)},b.prototype.N=function(){2&this.f||(this.f|=2,this.o=r,r=this)},b.prototype.d=function(){this.f|=8,1&this.f||d(this)},Symbol.observable||=Symbol("observable");var g=new FinalizationRegistry((t=>t.call?.())),S=t=>t&&t.peek,x=Symbol("signal-struct");function w(t){if((e=t)&&e[x])return t;var e;let i,r;if(O(t)){i={},r={};let e=Object.getOwnPropertyDescriptors(t);for(let t in e)r[t]=A(i,t,e[t].get?new v(e[t].get.bind(i)):e[t].value);return Object.defineProperty(i,x,{configurable:!1,enumerable:!1,value:!0}),Object.seal(i),i}return Array.isArray(t)?t.map((t=>w(t))):t}function A(t,e,i){let r,n=S(i)?i:O(i)||Array.isArray(i)?u(w(i)):u((r=(o=i)&&!!(o[Symbol.observable]||o[Symbol.asyncIterator]||o.call&&o.set||o.subscribe||o.then))?void 0:i);var o,s,f,a,l,h,c;return r&&(f=t=>n.value=t,(s=i)&&(c=(s[Symbol.observable]?.()||s).subscribe?.(f,a,undefined),h=c&&(()=>c.unsubscribe?.())||s.set&&s.call?.(l,f)||(s.then?.((t=>{!l&&f(t)}),a)||(async t=>{try{for await(t of s){if(l)return;f(t)}}catch(t){}})())&&(t=>l=1),g.register(s,h))),Object.defineProperty(t,e,{get:()=>n.value,set:!S(i)&&O(i)?t=>t?Object.assign(n.value,t):n.value=w(t):t=>n.value=w(t),enumerable:!0,configurable:!1}),n}function O(t){return t&&t.constructor===Object}var j=(t,e,i,r=null)=>{let n,o,s,f=0,a=i.length,u=e.length,{remove:l,same:h,insert:c,replace:v}=j;for(;f<a&&f<u&&h(e[f],i[f]);)f++;for(;f<a&&f<u&&h(i[a-1],e[u-1]);)r=i[(--u,--a)];if(f==u)for(;f<a;)c(r,i[f++],t);else{for(n=e[f];f<a;)s=i[f++],o=n?n.nextSibling:r,h(n,s)?n=o:f<a&&h(i[f],o)?(v(n,s,t),n=o):c(n,s,t);for(;!h(n,r);)o=n.nextSibling,l(n,t),n=o}return i};j.same=(t,e)=>t==e,j.replace=(t,e,i)=>i.replaceChild(e,t),j.insert=(t,e,i)=>i.insertBefore(e,t),j.remove=(t,e)=>e.removeChild(t);var N=j,E={},$={},k={},W={},C=(t,e,i,r)=>{if(r.startsWith("on"))return W.on(t,`{"${r.split("-").map((t=>t.startsWith("on")?t.slice(2):t)).join("-")}": ${e}}`,i);let n=D(t,e,":"+r);return e=>U(t,r,n(e))},U=(t,e,i)=>{null==i||!1===i?t.removeAttribute(e):t.setAttribute(e,!0===i?"":"number"==typeof i||"string"==typeof i?i:"")};W[""]=(t,e,i)=>{let r=D(t,e,":");return e=>{let i=r(e);for(let e in i)U(t,M(e),i[e])}};var B=Symbol(":each"),L=Symbol(":ref");W.ref=(t,e,i)=>{t.hasAttribute(":each")?t[L]=e:i[e]=t},W.if=(t,e,i)=>{let r=document.createTextNode(""),n=[D(t,e,":if")],o=[t],s=t;for(;(s=t.nextElementSibling)&&s.hasAttribute(":else");)s.removeAttribute(":else"),(e=s.getAttribute(":if"))?(s.removeAttribute(":if"),s.remove(),o.push(s),n.push(D(t,e,":else :if"))):(s.remove(),o.push(s),n.push((()=>1)));return t.replaceWith(s=r),t=>{let e=n.findIndex((e=>e(t)));o[e]!=s&&((s[B]||s).replaceWith(s=o[e]||r),F(s,t))}},W.each=(t,e,i)=>{let r=function(t){let e=/,([^,\}\]]*)(?:,([^,\}\]]*))?$/,i=t.match(/([\s\S]*?)\s+(?:in|of)\s+([\s\S]*)/);if(!i)return;let r={};r.items=i[2].trim();let n=i[1].replace(/^\s*\(|\)\s*$/g,"").trim(),o=n.match(e);return o?(r.item=n.replace(e,"").trim(),r.index=o[1].trim()):r.item=n,r}(e);if(!r)return _(new Error,t,e);const n=t[B]=document.createTextNode("");t.replaceWith(n);const o=D(t,r.items,":each"),s=new WeakMap,f=new WeakMap;let a=[];return e=>{let i=o(e);i?"number"==typeof i?i=Array.from({length:i},((t,e)=>[e,e+1])):i.constructor===Object?i=Object.entries(i):Array.isArray(i)?i=i.map(((t,e)=>[e+1,t])):_(Error("Bad list value"),t,r.items,":each"):i=[];let u=[],l=[];for(let[n,o]of i){let i=null===(h=o)?$:void 0===h?k:"number"==typeof h||h instanceof Number?E[h]||(E[h]=new Number(h)):"string"==typeof h||h instanceof String?E[h]||(E[h]=new String(h)):"boolean"==typeof h||h instanceof Boolean?E[h]||(E[h]=new Boolean(h)):h,a=f.get(i);if(a||(a=t.cloneNode(!0),f.set(i,a)),u.push(a),!s.has(i)){let f=Object.create(e);f[r.item]=o,r.index&&(f[r.index]=n),t[L]&&(f[t[L]]=a),s.set(i,f)}l.push(s.get(i))}var h;N(n.parentNode,a,u,n),a=u;for(let t=0;t<u.length;t++)F(u[t],l[t])}},W.id=(t,e,i)=>{let r=D(t,e,":id");return e=>{return i=r(e),t.id=i||0===i?i:"";var i}},W.class=(t,e,i)=>{let r=D(t,e,":class"),n=t.className;return e=>{let i=r(e);t.className=n+typeof i=="string"?i:(Array.isArray(i)?i:Object.entries(i).map((([t,e])=>e?t:""))).filter(Boolean).join(" ")}},W.style=(t,e,i)=>{let r=D(t,e,":style");return e=>{let i=r(e);if("string"==typeof i)t.setAttribute("style",i);else for(let e in i)t.style[e]=i[e]}},W.text=(t,e,i)=>{let r=D(t,e,":text");return e=>{return i=r(e),void(t.textContent=null==i?"":i);var i}},W.value=(t,e,i)=>{let r=D(t,e,":in"),n="text"===t.type||""===t.type?e=>t.setAttribute("value",t.value=null==e?"":e):"checkbox"===t.type?e=>(t.value=e?"on":"",U(t,"checked",e)):"select-one"===t.type?e=>([...t.options].map((t=>t.removeAttribute("selected"))),t.value=e,t.selectedOptions[0]?.setAttribute("selected","")):e=>t.value=e;return t=>n(r(t))},W.on=(t,e,i)=>{let r=D(t,e,":on"),n={};return e=>{for(let e in n)t.removeEventListener(e,n[e]);n=r(e);for(let e in n){const i=e.split("-");if(1===i.length)t.addEventListener(e,n[e]);else{const r=n[e],o=(s,f=0)=>{t.addEventListener(i[f],n[e]=a=>{s=s(a),t.removeEventListener(i[f],n[e]),++f<i.length&&"function"==typeof s?o(s,f):o(r)})};o(r)}}}},W.data=(t,e,i)=>{let r=D(t,e,":data");return e=>{let i=r(e);for(let e in i)t.dataset[e]=i[e]}},W.aria=(t,e,i)=>{let r=D(t,e,":aria");return e=>(e=>{for(let i in e)U(t,"aria-"+M(i),null==e[i]?null:e[i]+"")})(r(e))};var P={};function D(t,e,i){if(P[e])return P[e];let r,n=/^[\n\s]*if.*\(.*\)/.test(e)||/^(let|const)\s/.test(e)?`(() => { ${e} })()`:e;try{r=new Function("__scope",`with (__scope) { return (${n}) };`).bind(t)}catch(r){return _(r,t,e,i)}return P[e]=n=>{let o;try{o=r(n)}catch(r){return _(r,t,e,i)}return o}}function _(t,e,i,r){Object.assign(t,{element:e,expression:i}),console.warn(`∴ ${t.message}\n\n${r}=${i?`"${i}"\n\n`:""}`,e),setTimeout((()=>{throw t}),0)}function M(t){return t.replace(/[A-Z\u00C0-\u00D6\u00D8-\u00DE]/g,(t=>"-"+t.toLowerCase()))}var T=new WeakMap;function F(t,e){if(!t.children)return;if(T.has(t))return T.get(t);e||={};const i=[],r=(t,n=t.parentNode)=>{if(t.attributes)for(let r=0;r<t.attributes.length;){let o=t.attributes[r];if(":"!==o.name[0]){r++;continue}t.removeAttribute(o.name);let s=o.value,f=o.name.slice(1).split(":");for(let r of f){let o=W[r]||C;if(i.push(o(t,s,e,r)||(()=>{})),T.has(t)||t.parentNode!==n)return!1}}for(let e,i=0;e=t.children[i];i++)!1===r(e,t)&&i--};r(t);const n=w(e);for(let t of i)m((()=>t(n)));return T.set(t,n),n}var I=F;export{I as default};
|
|
1
|
+
function t(){throw new Error("Cycle detected")}function e(){if(n>1)n--;else{for(var t,e=!1;void 0!==r;){var i=r;for(r=void 0,o++;void 0!==i;){var s=i.o;if(i.o=void 0,i.f&=-3,!(8&i.f)&&u(i))try{i.c()}catch(i){e||(t=i,e=!0)}i=s}}if(o=0,n--,e)throw t}}var i=void 0,r=void 0,n=0,o=0,s=0;function f(t){if(void 0!==i){var e=t.n;if(void 0===e||e.t!==i)return i.s=e={i:0,S:t,p:void 0,n:i.s,t:i,e:void 0,x:void 0,r:e},t.n=e,32&i.f&&t.S(e),e;if(-1===e.i)return e.i=0,void 0!==e.p&&(e.p.n=e.n,void 0!==e.n&&(e.n.p=e.p),e.p=void 0,e.n=i.s,i.s.p=e,i.s=e),e}}function l(t){this.v=t,this.i=0,this.n=void 0,this.t=void 0}function a(t){return new l(t)}function u(t){for(var e=t.s;void 0!==e;e=e.n)if(e.S.i!==e.i||!e.S.h()||e.S.i!==e.i)return!0;return!1}function h(t){for(var e=t.s;void 0!==e;e=e.n){var i=e.S.n;void 0!==i&&(e.r=i),e.S.n=e,e.i=-1}}function c(t){for(var e=t.s,i=void 0;void 0!==e;){var r=e.n;-1===e.i?(e.S.U(e),e.n=void 0):(void 0!==i&&(i.p=e),e.p=void 0,e.n=i,i=e),e.S.n=e.r,void 0!==e.r&&(e.r=void 0),e=r}t.s=i}function v(t){l.call(this,void 0),this.x=t,this.s=void 0,this.g=s-1,this.f=4}function p(t){var r=t.u;if(t.u=void 0,"function"==typeof r){n++;var o=i;i=void 0;try{r()}catch(e){throw t.f&=-2,t.f|=8,d(t),e}finally{i=o,e()}}}function d(t){for(var e=t.s;void 0!==e;e=e.n)e.S.U(e);t.x=void 0,t.s=void 0,p(t)}function y(t){if(i!==this)throw new Error("Out-of-order effect");c(this),i=t,this.f&=-2,8&this.f&&d(this),e()}function b(t){this.x=t,this.u=void 0,this.s=void 0,this.o=void 0,this.f=32}function m(t){var e=new b(t);return e.c(),e.d.bind(e)}l.prototype.h=function(){return!0},l.prototype.S=function(t){this.t!==t&&void 0===t.e&&(t.x=this.t,void 0!==this.t&&(this.t.e=t),this.t=t)},l.prototype.U=function(t){var e=t.e,i=t.x;void 0!==e&&(e.x=i,t.e=void 0),void 0!==i&&(i.e=e,t.x=void 0),t===this.t&&(this.t=i)},l.prototype.subscribe=function(t){var e=this;return m((function(){var i=e.value,r=32&this.f;this.f&=-33;try{t(i)}finally{this.f|=r}}))},l.prototype.valueOf=function(){return this.value},l.prototype.toString=function(){return this.value+""},l.prototype.peek=function(){return this.v},Object.defineProperty(l.prototype,"value",{get:function(){var t=f(this);return void 0!==t&&(t.i=this.i),this.v},set:function(i){if(i!==this.v){o>100&&t(),this.v=i,this.i++,s++,n++;try{for(var r=this.t;void 0!==r;r=r.x)r.t.N()}finally{e()}}}}),(v.prototype=new l).h=function(){if(this.f&=-3,1&this.f)return!1;if(32==(36&this.f))return!0;if(this.f&=-5,this.g===s)return!0;if(this.g=s,this.f|=1,this.i>0&&!u(this))return this.f&=-2,!0;var t=i;try{h(this),i=this;var e=this.x();(16&this.f||this.v!==e||0===this.i)&&(this.v=e,this.f&=-17,this.i++)}catch(t){this.v=t,this.f|=16,this.i++}return i=t,c(this),this.f&=-2,!0},v.prototype.S=function(t){if(void 0===this.t){this.f|=36;for(var e=this.s;void 0!==e;e=e.n)e.S.S(e)}l.prototype.S.call(this,t)},v.prototype.U=function(t){if(l.prototype.U.call(this,t),void 0===this.t){this.f&=-33;for(var e=this.s;void 0!==e;e=e.n)e.S.U(e)}},v.prototype.N=function(){if(!(2&this.f)){this.f|=6;for(var t=this.t;void 0!==t;t=t.x)t.t.N()}},v.prototype.peek=function(){if(this.h()||t(),16&this.f)throw this.v;return this.v},Object.defineProperty(v.prototype,"value",{get:function(){1&this.f&&t();var e=f(this);if(this.h(),void 0!==e&&(e.i=this.i),16&this.f)throw this.v;return this.v}}),b.prototype.c=function(){var t=this.S();try{8&this.f||void 0===this.x||(this.u=this.x())}finally{t()}},b.prototype.S=function(){1&this.f&&t(),this.f|=1,this.f&=-9,p(this),h(this),n++;var e=i;return i=this,y.bind(this,e)},b.prototype.N=function(){2&this.f||(this.f|=2,this.o=r,r=this)},b.prototype.d=function(){this.f|=8,1&this.f||d(this)},Symbol.observable||=Symbol("observable");var g=new FinalizationRegistry((t=>t.call?.())),S=t=>t&&t.peek,w=Symbol("signal-struct");function x(t){if((e=t)&&e[w])return t;var e;let i,r;if(O(t)){i={},r={};let e=Object.getOwnPropertyDescriptors(t);for(let t in e)r[t]=A(i,t,e[t].get?new v(e[t].get.bind(i)):e[t].value);return Object.defineProperty(i,w,{configurable:!1,enumerable:!1,value:!0}),Object.seal(i),i}return Array.isArray(t)?t.map((t=>x(t))):t}function A(t,e,i){let r,n=S(i)?i:O(i)||Array.isArray(i)?a(x(i)):a((r=(o=i)&&!!(o[Symbol.observable]||o[Symbol.asyncIterator]||o.call&&o.set||o.subscribe||o.then))?void 0:i);var o,s,f,l,u,h,c;return r&&(f=t=>n.value=t,(s=i)&&(c=(s[Symbol.observable]?.()||s).subscribe?.(f,l,undefined),h=c&&(()=>c.unsubscribe?.())||s.set&&s.call?.(u,f)||(s.then?.((t=>{!u&&f(t)}),l)||(async t=>{try{for await(t of s){if(u)return;f(t)}}catch(t){}})())&&(t=>u=1),g.register(s,h))),Object.defineProperty(t,e,{get:()=>n.value,set:!S(i)&&O(i)?t=>t?Object.assign(n.value,t):n.value=x(t):t=>n.value=x(t),enumerable:!0,configurable:!1}),n}function O(t){return t&&t.constructor===Object}var j=(t,e,i,r=null)=>{let n,o,s,f=0,l=i.length,a=e.length,{remove:u,same:h,insert:c,replace:v}=j;for(;f<l&&f<a&&h(e[f],i[f]);)f++;for(;f<l&&f<a&&h(i[l-1],e[a-1]);)r=i[(--a,--l)];if(f==a)for(;f<l;)c(r,i[f++],t);else{for(n=e[f];f<l;)s=i[f++],o=n?n.nextSibling:r,h(n,s)?n=o:f<l&&h(i[f],o)?(v(n,s,t),n=o):c(n,s,t);for(;!h(n,r);)o=n.nextSibling,u(n,t),n=o}return i};j.same=(t,e)=>t==e,j.replace=(t,e,i)=>i.replaceChild(e,t),j.insert=(t,e,i)=>i.insertBefore(e,t),j.remove=(t,e)=>e.removeChild(t);var N=j,E={},W={},k={},$={},U=(t,e,i,r)=>{let n,o=r.startsWith("on")&&r.slice(2),s=T(t,e,":"+r);return o?e=>{n&&_(t,o,n),n=s(e),n&&D(t,o,n)}:e=>C(t,r,s(e))},C=(t,e,i)=>{null==i||!1===i?t.removeAttribute(e):t.setAttribute(e,!0===i?"":"number"==typeof i||"string"==typeof i?i:"")};$[""]=(t,e,i)=>{let r=T(t,e,":");return e=>{let i=r(e);for(let e in i)C(t,I(e),i[e])}};var B=Symbol(":each"),L=Symbol(":ref");$.ref=(t,e,i)=>{t.hasAttribute(":each")?t[L]=e:i[e]=t},$.with=(t,e,i)=>{throw T(t,e,"with"),"Unimplemented"},$.if=(t,e,i)=>{let r=document.createTextNode(""),n=[T(t,e,":if")],o=[t],s=t;for(;(s=t.nextElementSibling)&&s.hasAttribute(":else");)s.removeAttribute(":else"),(e=s.getAttribute(":if"))?(s.removeAttribute(":if"),s.remove(),o.push(s),n.push(T(t,e,":else :if"))):(s.remove(),o.push(s),n.push((()=>1)));return t.replaceWith(s=r),t=>{let e=n.findIndex((e=>e(t)));o[e]!=s&&((s[B]||s).replaceWith(s=o[e]||r),R(s,t))}},$.each=(t,e,i)=>{let r=function(t){let e=/,([^,\}\]]*)(?:,([^,\}\]]*))?$/,i=t.match(/([\s\S]*?)\s+(?:in|of)\s+([\s\S]*)/);if(!i)return;let r={};r.items=i[2].trim();let n=i[1].replace(/^\s*\(|\)\s*$/g,"").trim(),o=n.match(e);return o?(r.item=n.replace(e,"").trim(),r.index=o[1].trim()):r.item=n,r}(e);if(!r)return F(new Error,t,e);const n=t[B]=document.createTextNode("");t.replaceWith(n);const o=T(t,r.items,":each"),s=new WeakMap,f=new WeakMap;let l=[];return e=>{let i=o(e);i?"number"==typeof i?i=Array.from({length:i},((t,e)=>[e,e+1])):i.constructor===Object?i=Object.entries(i):Array.isArray(i)?i=i.map(((t,e)=>[e+1,t])):F(Error("Bad list value"),t,r.items,":each"):i=[];let a=[],u=[];for(let[n,o]of i){let i=null===(h=o)?W:void 0===h?k:"number"==typeof h||h instanceof Number?E[h]||(E[h]=new Number(h)):"string"==typeof h||h instanceof String?E[h]||(E[h]=new String(h)):"boolean"==typeof h||h instanceof Boolean?E[h]||(E[h]=new Boolean(h)):h,l=f.get(i);if(l||(l=t.cloneNode(!0),f.set(i,l)),a.push(l),!s.has(i)){let f=Object.create(e);f[r.item]=o,r.index&&(f[r.index]=n),t[L]&&(f[t[L]]=l),s.set(i,f)}u.push(s.get(i))}var h;N(n.parentNode,l,a,n),l=a;for(let t=0;t<a.length;t++)R(a[t],u[t])}},$.id=(t,e,i)=>{let r=T(t,e,":id");return e=>{return i=r(e),t.id=i||0===i?i:"";var i}},$.class=(t,e,i)=>{let r=T(t,e,":class"),n=t.className;return e=>{let i=r(e);t.className=n+typeof i=="string"?i:(Array.isArray(i)?i:Object.entries(i).map((([t,e])=>e?t:""))).filter(Boolean).join(" ")}},$.style=(t,e,i)=>{let r=T(t,e,":style"),n=t.getAttribute("style")||"";return n.endsWith(";")||(n+="; "),e=>{let i=r(e);if("string"==typeof i)t.setAttribute("style",n+i);else for(let e in i)t.style[e]=i[e]}},$.text=(t,e,i)=>{let r=T(t,e,":text");return e=>{return i=r(e),void(t.textContent=null==i?"":i);var i}},$.value=(t,e,i)=>{let r=T(t,e,":value"),n="text"===t.type||""===t.type?e=>t.setAttribute("value",t.value=null==e?"":e):"checkbox"===t.type?e=>(t.value=e?"on":"",C(t,"checked",e)):"select-one"===t.type?e=>{for(let e in t.options)e.removeAttribute("selected");t.value=e,t.selectedOptions[0]?.setAttribute("selected","")}:e=>t.value=e;return t=>n(r(t))};var P=Symbol("stop");$.on=(t,e,i)=>{let r=T(t,e,":on"),n={};return e=>{for(let e in n)_(t,e,n[e]);n=r(e);for(let e in n)D(t,e,n[e])}};var D=(t,e,i)=>{if(e.indexOf("..")<0)t.addEventListener(e,i);else{const r=e.split("..").map((t=>t.startsWith("on")?t.slice(2):t)),n=(e,o=0)=>{let s=f=>{t.removeEventListener(r[o],s),"function"!=typeof(e=e.call(t,f))&&(e=()=>{}),++o<r.length?n(e,o):i[P]||(console.log("reset"),n(i))};t.addEventListener(r[o],s)};n(i)}},_=(t,e,i)=>{e.indexOf("..")>=0&&(console.log("rewire"),i[P]=!0),t.removeEventListener(e,i)};$.data=(t,e,i)=>{let r=T(t,e,":data");return e=>{let i=r(e);for(let e in i)t.dataset[e]=i[e]}},$.aria=(t,e,i)=>{let r=T(t,e,":aria");return e=>(e=>{for(let i in e)C(t,"aria-"+I(i),null==e[i]?null:e[i]+"")})(r(e))};var M={};function T(t,e,i){if(M[e])return M[e];let r,n=/^[\n\s]*if.*\(.*\)/.test(e)||/^(let|const)\s/.test(e)?`(() => { ${e} })()`:e;try{r=new Function("__scope",`with (__scope) { return (${n}) };`).bind(t)}catch(r){return F(r,t,e,i)}return M[e]=n=>{let o;try{o=r(n)}catch(r){return F(r,t,e,i)}return o}}function F(t,e,i,r){Object.assign(t,{element:e,expression:i}),console.warn(`∴ ${t.message}\n\n${r}=${i?`"${i}"\n\n`:""}`,e),setTimeout((()=>{throw t}),0)}function I(t){return t.replace(/[A-Z\u00C0-\u00D6\u00D8-\u00DE]/g,(t=>"-"+t.toLowerCase()))}var z=new WeakMap;function R(t,e){if(!t.children)return;if(z.has(t))return z.get(t);e||={};const i=[],r=(t,n=t.parentNode)=>{if(t.attributes)for(let r=0;r<t.attributes.length;){let o=t.attributes[r];if(":"!==o.name[0]){r++;continue}t.removeAttribute(o.name);let s=o.value;if(!s)continue;let f=o.name.slice(1).split(":");for(let r of f){let o=$[r]||U;if(i.push(o(t,s,e,r)||(()=>{})),z.has(t)||t.parentNode!==n)return!1}}for(let e,i=0;e=t.children[i];i++)!1===r(e,t)&&i--};r(t);const n=x(e);for(let t of i)m((()=>t(n)));return z.set(t,n),n}var Z=R;export{Z as default};
|
package/src/core.js
CHANGED
|
@@ -9,6 +9,7 @@ export default function sprae(container, values) {
|
|
|
9
9
|
if (memo.has(container)) return memo.get(container)
|
|
10
10
|
|
|
11
11
|
values ||= {};
|
|
12
|
+
// FIXME: init values must read "snapshot" of signals to avoid leaking actual signals to directives
|
|
12
13
|
|
|
13
14
|
const updates = []
|
|
14
15
|
|
|
@@ -20,6 +21,7 @@ export default function sprae(container, values) {
|
|
|
20
21
|
if (attr.name[0] !== ':') {i++; continue}
|
|
21
22
|
el.removeAttribute(attr.name)
|
|
22
23
|
let expr = attr.value
|
|
24
|
+
if (!expr) continue
|
|
23
25
|
let attrNames = attr.name.slice(1).split(':')
|
|
24
26
|
for (let attrName of attrNames) {
|
|
25
27
|
let dir = directives[attrName] || defaultDirective;
|
package/src/directives.js
CHANGED
|
@@ -8,14 +8,16 @@ export const directives = {}
|
|
|
8
8
|
|
|
9
9
|
// any-prop directives
|
|
10
10
|
export default (el, expr, values, name) => {
|
|
11
|
-
|
|
12
|
-
// :ona:onb=x -> :on={a:x,b:x}
|
|
13
|
-
// :ona-onb=x -> :on={aB:x}
|
|
14
|
-
return directives.on(el, `{"${name.split('-').map(n=>n.startsWith('on')?n.slice(2):n).join('-')}": ${expr}}`, values)
|
|
15
|
-
}
|
|
11
|
+
let evt = name.startsWith('on') && name.slice(2)
|
|
16
12
|
let evaluate = parseExpr(el, expr, ':'+name)
|
|
17
13
|
|
|
18
|
-
|
|
14
|
+
let value
|
|
15
|
+
return evt ? state => {
|
|
16
|
+
value && removeListener(el, evt, value)
|
|
17
|
+
value = evaluate(state)
|
|
18
|
+
value && addListener(el, evt, value)
|
|
19
|
+
}
|
|
20
|
+
: state => attr(el, name, evaluate(state))
|
|
19
21
|
}
|
|
20
22
|
|
|
21
23
|
// set attr
|
|
@@ -43,6 +45,30 @@ directives['ref'] = (el, expr, values) => {
|
|
|
43
45
|
values[expr] = el;
|
|
44
46
|
}
|
|
45
47
|
|
|
48
|
+
directives['with'] = (el, expr, rootState) => {
|
|
49
|
+
let evaluate = parseExpr(el, expr, 'with')
|
|
50
|
+
|
|
51
|
+
throw 'Unimplemented';
|
|
52
|
+
|
|
53
|
+
// prev iteration
|
|
54
|
+
// // Instead of extending signals (which is a bit hard since signal-struct internals is not uniform)
|
|
55
|
+
// // we bind updating
|
|
56
|
+
// const params = computed(() => Object.assign({}, rootState, evaluate(rootState)))
|
|
57
|
+
|
|
58
|
+
// // NOTE: initialized element doesn't proceed with its children
|
|
59
|
+
// let state = sprae(el, params.value)
|
|
60
|
+
|
|
61
|
+
// // but update is still called with parent state
|
|
62
|
+
// return (rootState) => batch(() => Object.assign(state, params.value))
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
// must be as simple as
|
|
66
|
+
// don't care if root state updates - we're subscribed to it via inheritance _or_ shadowing it
|
|
67
|
+
let withValues = evaluate(rootState);
|
|
68
|
+
let newState = Object.assign(Object.create(rootState), withValues)
|
|
69
|
+
sprae(el, newState)
|
|
70
|
+
}
|
|
71
|
+
|
|
46
72
|
directives['if'] = (el, expr, values) => {
|
|
47
73
|
let holder = document.createTextNode(''),
|
|
48
74
|
clauses = [parseExpr(el, expr, ':if')],
|
|
@@ -175,9 +201,11 @@ directives['class'] = (el, expr, values) => {
|
|
|
175
201
|
|
|
176
202
|
directives['style'] = (el, expr, values) => {
|
|
177
203
|
let evaluate = parseExpr(el, expr, ':style')
|
|
204
|
+
let initStyle = el.getAttribute('style') || ''
|
|
205
|
+
if (!initStyle.endsWith(';')) initStyle += '; '
|
|
178
206
|
return (state) => {
|
|
179
207
|
let v = evaluate(state)
|
|
180
|
-
if (typeof v === 'string') el.setAttribute('style', v)
|
|
208
|
+
if (typeof v === 'string') el.setAttribute('style', initStyle + v)
|
|
181
209
|
else for (let k in v) el.style[k] = v[k]
|
|
182
210
|
}
|
|
183
211
|
}
|
|
@@ -194,50 +222,58 @@ directives['text'] = (el, expr, values) => {
|
|
|
194
222
|
|
|
195
223
|
// connect expr to element value
|
|
196
224
|
directives['value'] = (el, expr, values) => {
|
|
197
|
-
let evaluate = parseExpr(el, expr, ':
|
|
225
|
+
let evaluate = parseExpr(el, expr, ':value')
|
|
198
226
|
|
|
199
227
|
let update = (
|
|
200
228
|
el.type === 'text' || el.type === '' ? value => el.setAttribute('value', el.value = value == null ? '' : value) :
|
|
201
229
|
el.type === 'checkbox' ? value => (el.value = value ? 'on' : '', attr(el, 'checked', value)) :
|
|
202
|
-
el.type === 'select-one' ? value =>
|
|
203
|
-
|
|
204
|
-
el.value = value
|
|
230
|
+
el.type === 'select-one' ? value => {
|
|
231
|
+
for (let option in el.options) option.removeAttribute('selected')
|
|
232
|
+
el.value = value;
|
|
205
233
|
el.selectedOptions[0]?.setAttribute('selected', '')
|
|
206
|
-
|
|
234
|
+
} :
|
|
207
235
|
value => el.value = value
|
|
208
236
|
)
|
|
209
237
|
|
|
210
238
|
return (state) => update(evaluate(state))
|
|
211
239
|
}
|
|
212
240
|
|
|
241
|
+
const _stop = Symbol('stop')
|
|
213
242
|
directives['on'] = (el, expr, values) => {
|
|
214
243
|
let evaluate = parseExpr(el, expr, ':on')
|
|
215
244
|
let listeners = {}
|
|
216
245
|
|
|
217
246
|
return (state) => {
|
|
218
|
-
for (let evt in listeners) el
|
|
219
|
-
|
|
247
|
+
for (let evt in listeners) removeListener(el, evt, listeners[evt])
|
|
220
248
|
listeners = evaluate(state);
|
|
249
|
+
for (let evt in listeners) addListener(el, evt, listeners[evt])
|
|
250
|
+
}
|
|
251
|
+
}
|
|
221
252
|
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
}
|
|
235
|
-
nextEvt(startFn)
|
|
253
|
+
const addListener = (el, evt, startFn) => {
|
|
254
|
+
if (evt.indexOf('..')<0) el.addEventListener(evt, startFn);
|
|
255
|
+
|
|
256
|
+
// sequences like `a..b`: fn
|
|
257
|
+
else {
|
|
258
|
+
const evts = evt.split('..').map(e => e.startsWith('on') ? e.slice(2) : e)
|
|
259
|
+
const nextEvt = (fn, cur=0) => {
|
|
260
|
+
let curListener = e => {
|
|
261
|
+
el.removeEventListener(evts[cur], curListener)
|
|
262
|
+
if (typeof (fn = fn.call(el,e)) !== 'function') fn = ()=>{}
|
|
263
|
+
if (++cur < evts.length) nextEvt(fn, cur);
|
|
264
|
+
else if (!startFn[_stop]) console.log('reset'), nextEvt(startFn); // update only if chain isn't stopped
|
|
236
265
|
}
|
|
237
|
-
|
|
266
|
+
el.addEventListener(evts[cur],curListener)
|
|
267
|
+
}
|
|
268
|
+
nextEvt(startFn)
|
|
238
269
|
}
|
|
239
270
|
}
|
|
240
271
|
|
|
272
|
+
const removeListener = (el, evt, fn) => {
|
|
273
|
+
if (evt.indexOf('..')>=0) console.log('rewire'), fn[_stop] = true
|
|
274
|
+
el.removeEventListener(evt, fn);
|
|
275
|
+
}
|
|
276
|
+
|
|
241
277
|
directives['data'] = (el, expr, values) => {
|
|
242
278
|
let evaluate = parseExpr(el, expr, ':data')
|
|
243
279
|
|
|
@@ -299,4 +335,4 @@ export function exprError(error, element, expression, dir) {
|
|
|
299
335
|
|
|
300
336
|
function dashcase(str) {
|
|
301
337
|
return str.replace(/[A-Z\u00C0-\u00D6\u00D8-\u00DE]/g, (match) => '-' + match.toLowerCase());
|
|
302
|
-
};
|
|
338
|
+
};
|
package/test/test.js
CHANGED
|
@@ -44,15 +44,21 @@ test('common: reactive', async () => {
|
|
|
44
44
|
is(el.outerHTML, `<label for="email">email</label><input id="email" name="email" type="email"><a href="//google.com"></a><img src="//google.com">`)
|
|
45
45
|
})
|
|
46
46
|
|
|
47
|
-
test('common:
|
|
48
|
-
let el = h`<x :
|
|
47
|
+
test('common: empty strings', async () => {
|
|
48
|
+
let el = h`<x :="" :x=""></x>`
|
|
49
|
+
sprae(el)
|
|
50
|
+
is(el.outerHTML, `<x></x>`)
|
|
51
|
+
})
|
|
52
|
+
|
|
53
|
+
test('style', async () => {
|
|
54
|
+
let el = h`<x style="left: 1px" :style="style"></x>`
|
|
49
55
|
let params = sprae(el, {style: "top: 1px"})
|
|
50
|
-
is(el.outerHTML, `<x style="top: 1px"></x>`)
|
|
56
|
+
is(el.outerHTML, `<x style="left: 1px; top: 1px"></x>`)
|
|
51
57
|
params.style = {top: '2px'}
|
|
52
|
-
is(el.outerHTML, `<x style="top: 2px;"></x>`)
|
|
58
|
+
is(el.outerHTML, `<x style="left: 1px; top: 2px;"></x>`)
|
|
53
59
|
})
|
|
54
60
|
|
|
55
|
-
test('
|
|
61
|
+
test('class', async () => {
|
|
56
62
|
let el = h`<x :class="a"></x><y :class="[b, c]"></y><z :class="{b:true, c:d}"></z>`
|
|
57
63
|
const c = signal('z')
|
|
58
64
|
let params = sprae(el, {a:'x', b:'y', c, d:false});
|
|
@@ -347,24 +353,40 @@ test('on: base', () => {
|
|
|
347
353
|
is(log.value, ['click','x','xx']);
|
|
348
354
|
})
|
|
349
355
|
|
|
356
|
+
test('onevt: this context', e => {
|
|
357
|
+
let el = h`<div :onx="function(){log.push(this)}"></div>`
|
|
358
|
+
let state = sprae(el, {log: []})
|
|
359
|
+
el.dispatchEvent(new window.Event('x'));
|
|
360
|
+
is(state.log, [el])
|
|
361
|
+
})
|
|
362
|
+
|
|
363
|
+
test('on: this in chains refers to el', () => {
|
|
364
|
+
let el = h`<div :ona..onb="function(e){x=this; log.push(1); return () => log.push(2)}"></div>`
|
|
365
|
+
let state = sprae(el, {log:[], x:null})
|
|
366
|
+
el.dispatchEvent(new window.Event('a'));
|
|
367
|
+
is(state.log, [1])
|
|
368
|
+
})
|
|
369
|
+
|
|
350
370
|
test('on: multiple events', e => {
|
|
351
|
-
let el = h`<div :onscroll:onclick="e=>log.push(e.type)"></div>`
|
|
371
|
+
let el = h`<div :onscroll:onclick:onx="e=>log.push(e.type)"></div>`
|
|
352
372
|
let state = sprae(el, {log:[]})
|
|
353
373
|
|
|
354
374
|
el.dispatchEvent(new window.Event('click'));
|
|
355
375
|
is(state.log, ['click'])
|
|
356
376
|
el.dispatchEvent(new window.Event('scroll'));
|
|
357
377
|
is(state.log, ['click','scroll'])
|
|
378
|
+
el.dispatchEvent(new window.Event('x'));
|
|
379
|
+
is(state.log, ['click','scroll','x'])
|
|
358
380
|
})
|
|
359
381
|
|
|
360
382
|
test('on: in-out events', e => {
|
|
361
383
|
// let el = document.createElement('x');
|
|
362
|
-
// el.setAttribute(':onmousedown
|
|
363
|
-
let el = h`<x :onmousedown
|
|
364
|
-
|
|
365
|
-
let state = sprae(el, {log:[]})
|
|
384
|
+
// el.setAttribute(':onmousedown..onmouseup', 'e=>(log.push(e.type),e=>log.push(e.type))')
|
|
385
|
+
let el = h`<x :onmousedown..onmouseup="(e) => { x=this; log.push(e.type); return e=>log.push(e.type); }"></x>`
|
|
366
386
|
|
|
387
|
+
let state = sprae(el, {log:[],x:null})
|
|
367
388
|
el.dispatchEvent(new window.Event('mousedown'));
|
|
389
|
+
is(state.x, el);
|
|
368
390
|
is(state.log, ['mousedown'])
|
|
369
391
|
el.dispatchEvent(new window.Event('mouseup'));
|
|
370
392
|
is(state.log, ['mousedown','mouseup'])
|
|
@@ -374,7 +396,7 @@ test('on: in-out side-effects', e => {
|
|
|
374
396
|
let log = []
|
|
375
397
|
|
|
376
398
|
// 1. skip in event and do directly out
|
|
377
|
-
let el = h`<x :onin
|
|
399
|
+
let el = h`<x :onin..onout="io"></x>`
|
|
378
400
|
sprae(el, { io(e) {
|
|
379
401
|
log.push(e.type)
|
|
380
402
|
return (e) => (log.push(e.type), [1,2,3])
|
|
@@ -401,7 +423,7 @@ test('on: in-out side-effects', e => {
|
|
|
401
423
|
})
|
|
402
424
|
|
|
403
425
|
test('on: chain of events', e => {
|
|
404
|
-
let el = h`<div :onmousedown
|
|
426
|
+
let el = h`<div :onmousedown..onmousemove..onmouseup="e=>(log.push(e.type),e=>(log.push(e.type),e=>log.push(e.type)))"></div>`
|
|
405
427
|
let state = sprae(el, {log:[]})
|
|
406
428
|
|
|
407
429
|
el.dispatchEvent(new window.Event('mousedown'));
|
|
@@ -412,7 +434,36 @@ test('on: chain of events', e => {
|
|
|
412
434
|
is(state.log, ['mousedown','mousemove','mouseup'])
|
|
413
435
|
})
|
|
414
436
|
|
|
415
|
-
test
|
|
437
|
+
test('on: state changes between chain of events', e => {
|
|
438
|
+
let el = h`<x :on="{'x..y':fn}"></x>`
|
|
439
|
+
let log = []
|
|
440
|
+
let state = sprae(el, {log, fn: () => console.log('log1')||log.push(1)})
|
|
441
|
+
console.log('xx')
|
|
442
|
+
el.dispatchEvent(new window.Event('x'));
|
|
443
|
+
el.dispatchEvent(new window.Event('x'));
|
|
444
|
+
is(log, [1])
|
|
445
|
+
state.fn = () => (console.log('log1.1')||log.push(1.1), () => log.push(2))
|
|
446
|
+
is(log, [1])
|
|
447
|
+
// console.log('xx')
|
|
448
|
+
// NOTE: state update registers new chain listener before finishing prev chain
|
|
449
|
+
// el.dispatchEvent(new window.Event('x'));
|
|
450
|
+
// el.dispatchEvent(new window.Event('x'));
|
|
451
|
+
// is(log, [1])
|
|
452
|
+
console.log('yy')
|
|
453
|
+
el.dispatchEvent(new window.Event('y'));
|
|
454
|
+
el.dispatchEvent(new window.Event('y'));
|
|
455
|
+
is(log, [1])
|
|
456
|
+
console.log('xx')
|
|
457
|
+
el.dispatchEvent(new window.Event('x'));
|
|
458
|
+
el.dispatchEvent(new window.Event('x'));
|
|
459
|
+
is(log, [1, 1.1])
|
|
460
|
+
console.log('yy')
|
|
461
|
+
el.dispatchEvent(new window.Event('y'));
|
|
462
|
+
el.dispatchEvent(new window.Event('y'));
|
|
463
|
+
is(log, [1, 1.1, 2])
|
|
464
|
+
})
|
|
465
|
+
|
|
466
|
+
test.todo('with: inline', () => {
|
|
416
467
|
let el = h`<x :with="{foo:'bar', baz}"><y :text="foo + baz"></y></x>`
|
|
417
468
|
let state = sprae(el, {baz: 'qux'})
|
|
418
469
|
// FIXME: this doesn't inherit root scope baz property and instead uses hard-initialized one
|
|
@@ -420,7 +471,7 @@ test.skip('with: inline', () => {
|
|
|
420
471
|
state.baz = 'quux'
|
|
421
472
|
is(el.innerHTML, `<y>barquux</y>`)
|
|
422
473
|
})
|
|
423
|
-
test.
|
|
474
|
+
test.todo('with: inline reactive', () => {
|
|
424
475
|
let el = h`<x :with="{foo:'bar', baz}"><y :text="foo + baz"></y></x>`
|
|
425
476
|
let baz = signal('qux')
|
|
426
477
|
let state = sprae(el, {baz})
|
|
@@ -429,7 +480,7 @@ test.skip('with: inline reactive', () => {
|
|
|
429
480
|
baz.value = 'quux'
|
|
430
481
|
is(el.innerHTML, `<y>barquux</y>`)
|
|
431
482
|
})
|
|
432
|
-
test.
|
|
483
|
+
test.todo('with: data', () => {
|
|
433
484
|
let el = h`<x :with="x"><y :text="foo"></y></x>`
|
|
434
485
|
let state = sprae(el, {x:{foo:'bar'}})
|
|
435
486
|
is(el.innerHTML, `<y>bar</y>`)
|
|
@@ -437,7 +488,7 @@ test.skip('with: data', () => {
|
|
|
437
488
|
Object.assign(state, {x:{foo:'baz'}})
|
|
438
489
|
is(el.innerHTML, `<y>baz</y>`)
|
|
439
490
|
})
|
|
440
|
-
test.
|
|
491
|
+
test.todo('with: transparency', () => {
|
|
441
492
|
// NOTE: y:text initializes through directive, not through parent
|
|
442
493
|
// therefore by default :text uses parent's state, not defined by element itself
|
|
443
494
|
let el = h`<x :with="{foo:'foo'}"><y :with="b" :text="foo+bar"></y></x>`
|
|
@@ -446,7 +497,7 @@ test.skip('with: transparency', () => {
|
|
|
446
497
|
params.b.bar = 'baz'
|
|
447
498
|
is(el.innerHTML, `<y>foobaz</y>`)
|
|
448
499
|
})
|
|
449
|
-
test.
|
|
500
|
+
test.todo('with: reactive transparency', () => {
|
|
450
501
|
let el = h`<x :with="{foo:1}"><y :with="b.c" :text="foo+bar"></y></x>`
|
|
451
502
|
const bar = signal('2')
|
|
452
503
|
sprae(el, {b:{c:{bar}}})
|
|
@@ -454,7 +505,7 @@ test.skip('with: reactive transparency', () => {
|
|
|
454
505
|
bar.value = '3'
|
|
455
506
|
is(el.innerHTML, `<y>13</y>`)
|
|
456
507
|
})
|
|
457
|
-
test.
|
|
508
|
+
test.todo('with: writes to state', () => {
|
|
458
509
|
let a = h`<x :with="{a:1}"><y :on="{x(){a++}}" :text="a"></y></x>`
|
|
459
510
|
sprae(a)
|
|
460
511
|
is(a.innerHTML, `<y>1</y>`)
|
|
@@ -492,13 +543,19 @@ test(':: reactive values', async () => {
|
|
|
492
543
|
is(el.outerHTML, `<x>2</x>`)
|
|
493
544
|
})
|
|
494
545
|
|
|
546
|
+
test(':: null result does nothing', async () => {
|
|
547
|
+
let a = h`<x :="undefined"></x>`
|
|
548
|
+
sprae(a)
|
|
549
|
+
is(a.outerHTML, `<x></x>`)
|
|
550
|
+
})
|
|
551
|
+
|
|
495
552
|
test(':: scope refers to current element', async () => {
|
|
496
553
|
let el = h`<x :text="log.push(this)"></x>`
|
|
497
554
|
let state = sprae(el, {log:[]})
|
|
498
555
|
is(state.log, [el])
|
|
499
556
|
})
|
|
500
557
|
|
|
501
|
-
test.
|
|
558
|
+
test.todo(':: scope directives must come first', async () => {
|
|
502
559
|
// NOTE: we init attributes in order of definition
|
|
503
560
|
let a = h`<x :text="y" :with="{y:1}" :ref="x"></x>`
|
|
504
561
|
sprae(a, {})
|