sprae 1.0.0 → 2.1.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/test/test.js CHANGED
@@ -1,4 +1,5 @@
1
- import { signal } from '@preact/signals'
1
+ // import { signal } from 'usignal/sync'
2
+ import { signal } from '@preact/signals-core'
2
3
  import test, {is, any, throws} from 'tst'
3
4
  import {tick, time} from 'wait-please'
4
5
  import sprae from '../src/index.js'
@@ -21,34 +22,34 @@ test.skip('autoinit', async () => {
21
22
  test('hidden: core', async () => {
22
23
  let el = h`<div :hidden="hidden"></div>`
23
24
  let params = sprae(el, {hidden:true})
24
- is(el.outerHTML, `<div class="∴hidden" hidden=""></div>`)
25
+ is(el.outerHTML, `<div hidden=""></div>`)
25
26
  params.hidden = false
26
- is(el.outerHTML, `<div class="∴hidden"></div>`)
27
+ is(el.outerHTML, `<div></div>`)
27
28
  })
28
29
 
29
30
  test('hidden: reactive', async () => {
30
31
  const hidden = signal(true)
31
32
  let el = h`<div :hidden="hidden"></div>`
32
33
  sprae(el, {hidden})
33
- is(el.outerHTML, `<div class="∴hidden" hidden=""></div>`)
34
+ is(el.outerHTML, `<div hidden=""></div>`)
34
35
  hidden.value = false
35
- is(el.outerHTML, `<div class="∴hidden"></div>`)
36
+ is(el.outerHTML, `<div></div>`)
36
37
  })
37
38
 
38
39
  test('common: reactive', async () => {
39
40
  let el = h`<label :for="name" :text="name" ></label><input :id="name" :name="name" :type="name" :disabled="!name"/><a :href="url"></a><img :src="url"/>`
40
41
  let params = sprae(el, {name:'text', url:'//google.com'})
41
- is(el.outerHTML, `<label class="∴text ∴for" for="text">text</label><input class="∴id ∴name ∴type ∴disabled" id="text" name="text" type="text"><a class="∴href" href="//google.com"></a><img class="∴src" src="//google.com">`)
42
+ is(el.outerHTML, `<label for="text">text</label><input id="text" name="text" type="text"><a href="//google.com"></a><img src="//google.com">`)
42
43
  params.name = 'email'
43
- is(el.outerHTML, `<label class="∴text ∴for" for="email">email</label><input class="∴id ∴name ∴type ∴disabled" id="email" name="email" type="email"><a class="∴href" href="//google.com"></a><img class="∴src" src="//google.com">`)
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">`)
44
45
  })
45
46
 
46
47
  test('common: style', async () => {
47
48
  let el = h`<x :style="style"></x>`
48
49
  let params = sprae(el, {style: "top: 1px"})
49
- is(el.outerHTML, `<x class="∴style" style="top: 1px"></x>`)
50
+ is(el.outerHTML, `<x style="top: 1px"></x>`)
50
51
  params.style = {top: '2px'}
51
- is(el.outerHTML, `<x class="∴style" style="top: 2px"></x>`)
52
+ is(el.outerHTML, `<x style="top: 2px"></x>`)
52
53
  })
53
54
 
54
55
  test('common: class', async () => {
@@ -62,115 +63,115 @@ test('common: class', async () => {
62
63
  is(el.outerHTML, `<x class="x"></x><y class="y w"></y><z class="b c"></z>`);
63
64
  })
64
65
 
65
- test('props: base', async () => {
66
- let el = h`<input :prop="{for:1, title:2, help:3, type:4, placeholder: 5}"/>`
66
+ test('spread props: base', async () => {
67
+ let el = h`<input :id="0" :="{for:1, title:2, help:3, type:4, placeholder: 5, value: 6}" :value="7"/>`
67
68
  let params = sprae(el)
68
- is(el.outerHTML, `<input class="∴prop" for="1" title="2" help="3" type="4" placeholder="5">`)
69
+ is(el.outerHTML, `<input id="0" for="1" title="2" help="3" type="4" placeholder="5" value="7">`)
69
70
  })
70
71
 
71
72
  test('data: base', async () => {
72
73
  let el = h`<input :data="{a:1, fooBar:2}"/>`
73
74
  let params = sprae(el)
74
- is(el.outerHTML, `<input class="∴data" data-a="1" data-foo-bar="2">`)
75
+ is(el.outerHTML, `<input data-a="1" data-foo-bar="2">`)
75
76
  })
76
77
 
77
78
  test('aria: base', async () => {
78
- let el = h`<input type="text" id="jokes" role="combobox" :aria="{controls:'joketypes', autocomplete:'list', expanded:false, activeOption:'item1', activedescendant:''}"/>`
79
+ let el = h`<input type="text" id="jokes" role="combobox" :aria="{controls:'joketypes', autocomplete:'list', expanded:false, activeOption:'item1', activedescendant:'', xxx:null}"/>`
79
80
  sprae(el)
80
- is(el.outerHTML, `<input type="text" id="jokes" role="combobox" class="∴aria" aria-controls="joketypes" aria-autocomplete="list" aria-expanded="false" aria-active-option="item1" aria-activedescendant="">`)
81
+ is(el.outerHTML, `<input type="text" id="jokes" role="combobox" aria-controls="joketypes" aria-autocomplete="list" aria-expanded="false" aria-active-option="item1" aria-activedescendant="">`)
81
82
  })
82
83
 
83
- test('input: direct', async () => {
84
+ test('value: direct', async () => {
84
85
  let el = h`<input :value="a" />`
85
86
  let state = sprae(el, {a:1})
86
87
  is(el.value, '1')
87
- is(el.outerHTML, `<input class="∴value" value="1">`)
88
+ is(el.outerHTML, `<input value="1">`)
88
89
  state.a = 2
89
90
  is(el.value, '2')
90
- is(el.outerHTML, `<input class="∴value" value="2">`)
91
+ is(el.outerHTML, `<input value="2">`)
91
92
 
92
93
  el.value = 3
93
- el.dispatchEvent(new window.Event('change'))
94
- is(state.a, '3')
94
+ // el.dispatchEvent(new window.Event('change'))
95
+ // is(state.a, '3')
95
96
  })
96
97
 
97
98
  test('text: core', async () => {
98
99
  let el = h`<div :text="text"></div>`
99
100
  let params = sprae(el, {text:'abc'})
100
- is(el.outerHTML, `<div class="∴text">abc</div>`)
101
+ is(el.outerHTML, `<div>abc</div>`)
101
102
  params.text = null
102
- is(el.outerHTML, `<div class="∴text"></div>`)
103
+ is(el.outerHTML, `<div></div>`)
103
104
  })
104
105
 
105
106
  test('conditions: base', async () => {
106
107
  let el = h`<p>
107
108
  <span :if="a==1">a</span>
108
- <span :else-if="a==2">b</span>
109
+ <span :else :if="a==2">b</span>
109
110
  <span :else >c</span>
110
111
  </p>`
111
112
 
112
113
  const params = sprae(el, { a: 1 })
113
114
 
114
- is(el.innerHTML, '<span class="∴if">a</span>')
115
+ is(el.innerHTML, '<span>a</span>')
115
116
  params.a = 2
116
- is(el.innerHTML, '<span class="∴else-if">b</span>')
117
+ is(el.innerHTML, '<span>b</span>')
117
118
  params.a = 3
118
- is(el.innerHTML, '<span class="∴else">c</span>')
119
-
120
- delete params.a
119
+ is(el.innerHTML, '<span>c</span>')
120
+ params.a = null
121
+ is(el.innerHTML, '<span>c</span>')
121
122
  })
122
123
 
123
124
  test('conditions: short with insertions', async () => {
124
125
  let el = h`<p>
125
126
  <span :if="a==1" :text="'1:'+a"></span>
126
- <span :else-if="a==2" :text="'2:'+a"></span>
127
+ <span :else :if="a==2" :text="'2:'+a"></span>
127
128
  <span :else :text="a"></span>
128
129
  </p>`
129
130
 
130
131
  const params = sprae(el, { a: 1 })
131
132
 
132
- is(el.innerHTML, '<span class="∴text ∴if">1:1</span>')
133
+ is(el.innerHTML, '<span>1:1</span>')
133
134
  params.a = 2
134
- is(el.innerHTML, '<span class="∴text ∴else-if">2:2</span>')
135
+ is(el.innerHTML, '<span>2:2</span>')
135
136
  params.a = 3
136
- is(el.innerHTML, '<span class="∴text ∴else">3</span>')
137
+ is(el.innerHTML, '<span>3</span>')
137
138
  params.a = 4
138
- is(el.innerHTML, '<span class="∴text ∴else">4</span>')
139
+ is(el.innerHTML, '<span>4</span>')
139
140
 
140
141
  params.a = 1
141
- is(el.innerHTML, '<span class="∴text ∴if">1:1</span>')
142
+ is(el.innerHTML, '<span>1:1</span>')
142
143
  params.a = 4
143
- is(el.innerHTML, '<span class="∴text ∴else">4</span>')
144
+ is(el.innerHTML, '<span>4</span>')
144
145
 
145
- delete params.a
146
+ params.a = null
146
147
  })
147
148
 
148
149
  test('conditions: reactive values', async () => {
149
150
  let el = h`<p>
150
151
  <span :if="a==1" :text="'1:'+a"></span>
151
- <span :else-if="a==2" :text="'2:'+a"></span>
152
+ <span :else :if="a==2" :text="'2:'+a"></span>
152
153
  <span :else :text="a"></span>
153
154
  </p>`
154
155
 
155
156
  const a = signal(1)
156
157
  sprae(el, { a })
157
158
 
158
- is(el.innerHTML, '<span class="∴text ∴if">1:1</span>')
159
+ is(el.innerHTML, '<span>1:1</span>')
159
160
  a.value = 2
160
- is(el.innerHTML, '<span class="∴text ∴else-if">2:2</span>')
161
+ is(el.innerHTML, '<span>2:2</span>')
161
162
  a.value = 3
162
- is(el.innerHTML, '<span class="∴text ∴else">3</span>')
163
+ is(el.innerHTML, '<span>3</span>')
163
164
  a.value = 4
164
- is(el.innerHTML, '<span class="∴text ∴else">4</span>')
165
+ is(el.innerHTML, '<span>4</span>')
165
166
 
166
167
  a.value = 1
167
- is(el.innerHTML, '<span class="∴text ∴if">1:1</span>')
168
+ is(el.innerHTML, '<span>1:1</span>')
168
169
  a.value = 4
169
- is(el.innerHTML, '<span class="∴text ∴else">4</span>')
170
+ is(el.innerHTML, '<span>4</span>')
170
171
  })
171
172
 
172
173
 
173
- test('each: base', async () => {
174
+ test('each: array', async () => {
174
175
  // FIXME: in some conspicuous reason jsdom fails to update text nodes somehow
175
176
  let el = h`<p>
176
177
  <span :each="a in b" :text="a"></span>
@@ -179,14 +180,53 @@ test('each: base', async () => {
179
180
  const params = sprae(el, { b: [] })
180
181
 
181
182
  is(el.innerHTML, '')
183
+ console.log('set 1,2')
182
184
  params.b = [1,2]
183
- is(el.innerHTML, '<span class="∴each ∴text">1</span><span class="∴each ∴text">2</span>')
185
+ is(el.innerHTML, '<span>1</span><span>2</span>')
184
186
  params.b = []
185
187
  is(el.innerHTML, '')
186
- delete params.b
188
+ params.b = null
187
189
  is(el.innerHTML, '')
188
190
  })
189
191
 
192
+ test('each: object', async () => {
193
+ // FIXME: in some conspicuous reason jsdom fails to update text nodes somehow
194
+ let el = h`<p>
195
+ <span :each="x,key in b" :text="[key,x]"></span>
196
+ </p>`
197
+
198
+ const params = sprae(el, { b: null })
199
+
200
+ is(el.innerHTML, '')
201
+ console.log('set 1,2')
202
+ params.b = { x:1, y:2 }
203
+ is(el.innerHTML, '<span>x,1</span><span>y,2</span>')
204
+ params.b = []
205
+ is(el.innerHTML, '')
206
+ params.b = null
207
+ is(el.innerHTML, '')
208
+ })
209
+
210
+ test('each: loop within loop', async () => {
211
+ let el = h`<p>
212
+ <x :each="b in c"><y :each="a in b" :text="a"></y></x>
213
+ </p>`
214
+
215
+ const params = sprae(el, { c: [[1,2], [3,4]] })
216
+
217
+ is(el.innerHTML, '<x><y>1</y><y>2</y></x><x><y>3</y><y>4</y></x>')
218
+ params.c = [[5,6], [3,4]]
219
+ is(el.innerHTML, '<x><y>5</y><y>6</y></x><x><y>3</y><y>4</y></x>')
220
+ // params.c[1] = [7,8]
221
+ params.c = [params.c[0], [7,8]]
222
+ is(el.innerHTML, '<x><y>5</y><y>6</y></x><x><y>7</y><y>8</y></x>')
223
+ // is(el.innerHTML, '<span>1</span><span>2</span>')
224
+ params.c = []
225
+ is(el.innerHTML, '')
226
+ // params.b = null
227
+ // is(el.innerHTML, '')
228
+ })
229
+
190
230
  test('each: reactive values', async () => {
191
231
  let el = h`<p>
192
232
  <span :each="a in b" :text="a"></span>
@@ -197,40 +237,63 @@ test('each: reactive values', async () => {
197
237
 
198
238
  is(el.innerHTML, '')
199
239
  b.value = [1,2]
200
- is(el.innerHTML, '<span class="∴each ∴text">1</span><span class="∴each ∴text">2</span>')
240
+ is(el.innerHTML, '<span>1</span><span>2</span>')
201
241
  b.value = []
202
242
  is(el.innerHTML, '')
203
- delete params.b
243
+ params.b = null
204
244
  is(el.innerHTML, '')
205
245
  })
206
246
 
207
247
  test('each: loop with condition', async () => {
248
+ // NOTE: there doesn't seem to be much value in exactly that
249
+ // also it creates confusion with :else directive
250
+ // prohibitin that allows in-order directives init
208
251
  let el = h`<p>
209
252
  <span :each="a in b" :text="a" :if="c"></span>
210
253
  </p>`
211
254
 
212
- const params = sprae(el, { b: [1,2], c:false })
255
+ const params = sprae(el, { b: [1,2], c: false })
213
256
 
214
257
  is(el.innerHTML, '')
215
258
  params.c = true
216
- is(el.innerHTML, '<span class="∴each ∴text ∴if">1</span><span class="∴each ∴text ∴if">2</span>')
259
+ is(el.innerHTML, '<span>1</span><span>2</span>')
260
+ params.b = [1]
261
+ is(el.innerHTML, '<span>1</span>')
262
+ params.b = null
263
+ is(el.innerHTML, '')
264
+ })
265
+
266
+ test('each: condition with loop', async () => {
267
+ let el = h`<p>
268
+ <span :if="c" :each="a in b" :text="a"></span>
269
+ <span :else :text="c"></span>
270
+ </p>`
271
+
272
+ const params = sprae(el, { b: [1,2], c: false })
273
+
274
+ is(el.innerHTML, '<span>false</span>')
275
+ params.c = true
276
+ is(el.innerHTML, '<span>1</span><span>2</span>')
217
277
  params.b = [1]
218
- is(el.innerHTML, '<span class="∴each ∴text ∴if">1</span>')
219
- delete params.b
278
+ is(el.innerHTML, '<span>1</span>')
279
+ params.b = null
220
280
  is(el.innerHTML, '')
281
+ console.log('c=false')
282
+ params.c = false
283
+ is(el.innerHTML, '<span>false</span>')
221
284
  })
222
285
 
223
286
  test('each: loop within condition', async () => {
224
287
  let el = h`<p>
225
288
  <x :if="a==1"><y :each="i in a" :text="i"></y></x>
226
- <x :else-if="a==2"><y :each="i in a" :text="-i"></y></x>
289
+ <x :else :if="a==2"><y :each="i in a" :text="-i"></y></x>
227
290
  </p>`
228
291
 
229
292
  const params = sprae(el, { a: 1 })
230
293
 
231
- is(el.innerHTML, '<x class="∴if"><y class="∴each ∴text">1</y></x>')
294
+ is(el.innerHTML, '<x><y>1</y></x>')
232
295
  params.a = 2
233
- is(el.innerHTML, '<x class="∴else-if"><y class="∴each ∴text">-1</y><y class="∴each ∴text">-2</y></x>')
296
+ is(el.innerHTML, '<x><y>-1</y><y>-2</y></x>')
234
297
  params.a = 0
235
298
  is(el.innerHTML, '')
236
299
  })
@@ -239,17 +302,17 @@ test('each: condition within loop', async () => {
239
302
  let el = h`<p>
240
303
  <x :each="a in b">
241
304
  <y :if="a==1" :text="'1:'+a"></y>
242
- <y :else-if="a==2" :text="'2:'+a"></y>
305
+ <y :else :if="a==2" :text="'2:'+a"></y>
243
306
  <y :else :text="a"></y>
244
307
  </x>
245
308
  </p>`
246
309
 
247
310
  const params = sprae(el, { b: [1,2,3] })
248
311
 
249
- is(el.innerHTML, '<x class="∴each"><y class="∴text ∴if">1:1</y></x><x class="∴each"><y class="∴text ∴else-if">2:2</y></x><x class="∴each"><y class="∴text ∴else">3</y></x>')
312
+ is(el.innerHTML, '<x><y>1:1</y></x><x><y>2:2</y></x><x><y>3</y></x>')
250
313
  params.b = [2]
251
- is(el.innerHTML, '<x class="∴each"><y class="∴text ∴else-if">2:2</y></x>')
252
- delete params.b
314
+ is(el.innerHTML, '<x><y>2:2</y></x>')
315
+ params.b = null
253
316
  is(el.innerHTML, '')
254
317
  })
255
318
 
@@ -258,7 +321,7 @@ test('on: base', () => {
258
321
  let log = signal([])
259
322
  let params = sprae(el, {x(){log.value.push('x')}, log})
260
323
 
261
- is(el.outerHTML, `<div class="∴on"></div>`);
324
+ is(el.outerHTML, `<div></div>`);
262
325
  el.dispatchEvent(new window.Event('click'));
263
326
  is(log.value, ['click'])
264
327
  el.dispatchEvent(new window.Event('x'));
@@ -268,7 +331,7 @@ test('on: base', () => {
268
331
  el.dispatchEvent(new window.Event('x'));
269
332
  is(log.value, ['click','x','xx']);
270
333
 
271
- delete params.x;
334
+ params.x = null;
272
335
  el.dispatchEvent(new window.Event('x'));
273
336
  is(log.value, ['click','x','xx']);
274
337
  })
@@ -277,27 +340,83 @@ test('with: inline', () => {
277
340
  let el = h`<x :with="{foo:'bar', baz}"><y :text="foo + baz"></y></x>`
278
341
  let state = sprae(el, {baz: 'qux'})
279
342
  // FIXME: this doesn't inherit root scope baz property and instead uses hard-initialized one
280
- is(el.innerHTML, `<y class="∴text">barqux</y>`)
343
+ is(el.innerHTML, `<y>barqux</y>`)
281
344
  state.baz = 'quux'
282
- is(el.innerHTML, `<y class="∴text">barquux</y>`)
345
+ is(el.innerHTML, `<y>barquux</y>`)
346
+ })
347
+ test('with: inline reactive', () => {
348
+ let el = h`<x :with="{foo:'bar', baz}"><y :text="foo + baz"></y></x>`
349
+ let baz = signal('qux')
350
+ let state = sprae(el, {baz})
351
+ // FIXME: this doesn't inherit root scope baz property and instead uses hard-initialized one
352
+ is(el.innerHTML, `<y>barqux</y>`)
353
+ baz.value = 'quux'
354
+ is(el.innerHTML, `<y>barquux</y>`)
283
355
  })
284
356
  test('with: data', () => {
285
357
  let el = h`<x :with="x"><y :text="foo"></y></x>`
286
- let [state, update] = sprae(el, {x:{foo:'bar'}})
287
- is(el.innerHTML, `<y class="∴text">bar</y>`)
288
- update({x:{foo:'baz'}})
289
- is(el.innerHTML, `<y class="∴text">baz</y>`)
358
+ let state = sprae(el, {x:{foo:'bar'}})
359
+ is(el.innerHTML, `<y>bar</y>`)
360
+ console.log('update')
361
+ Object.assign(state, {x:{foo:'baz'}})
362
+ is(el.innerHTML, `<y>baz</y>`)
290
363
  })
291
- test('with: inheritance', () => {
364
+ test('with: transparency', () => {
292
365
  // NOTE: y:text initializes through directive, not through parent
293
366
  // therefore by default :text uses parent's state, not defined by element itself
294
367
  let el = h`<x :with="{foo:'foo'}"><y :with="b" :text="foo+bar"></y></x>`
295
- sprae(el, {b:{bar:'bar'}})
296
- is(el.innerHTML, `<y class="∴with ∴text">foobar</y>`)
368
+ let params = sprae(el, {b:{bar:'bar'}})
369
+ is(el.innerHTML, `<y>foobar</y>`)
370
+ params.b.bar = 'baz'
371
+ is(el.innerHTML, `<y>foobaz</y>`)
297
372
  })
298
- test('with: reactive inheritance', () => {
373
+ test('with: reactive transparency', () => {
299
374
  let el = h`<x :with="{foo:1}"><y :with="b.c" :text="foo+bar"></y></x>`
300
375
  const bar = signal('2')
301
376
  sprae(el, {b:{c:{bar}}})
302
- is(el.innerHTML, `<y class="∴with ∴text">12</y>`)
377
+ is(el.innerHTML, `<y>12</y>`)
378
+ bar.value = '3'
379
+ is(el.innerHTML, `<y>13</y>`)
380
+ })
381
+ test('with: writes to state', () => {
382
+ let a = h`<x :with="{a:1}"><y :on="{x(){a++}}" :text="a"></y></x>`
383
+ sprae(a)
384
+ is(a.innerHTML, `<y>1</y>`)
385
+ a.firstChild.dispatchEvent(new window.Event('x'))
386
+ is(a.innerHTML, `<y>2</y>`)
387
+ a.firstChild.dispatchEvent(new window.Event('x'))
388
+ is(a.innerHTML, `<y>3</y>`)
389
+ })
390
+
391
+ test('ref: base', () => {
392
+ let a = h`<a :ref="a" :init="log.push(a), null" :text="b"></a>`
393
+ let state = sprae(a, {log:[], b:1})
394
+ is(state.log[0], a)
395
+ is(a.outerHTML, `<a>1</a>`)
396
+ state.b = 2
397
+ is(a.outerHTML, `<a>2</a>`)
398
+ })
399
+
400
+ test('ref with each', () => {
401
+ let a = h`<y><x :ref="x" :each="item in items" :text="log.push(x), item"/></y>`
402
+ let state = sprae(a, {log: [], items: [1,2]})
403
+ is(a.innerHTML, `<x>1</x><x>2</x>`)
404
+ is(state.log, [...a.children])
405
+ })
406
+
407
+ test('reactive values', async () => {
408
+ let a = new Promise((ok) => setTimeout(() => ok(2), 10))
409
+
410
+ let el = h`<x :text="a">1</x>`
411
+ sprae(el, {a})
412
+ is(el.outerHTML, `<x></x>`)
413
+
414
+ await time(20)
415
+ is(el.outerHTML, `<x>2</x>`)
416
+ })
417
+
418
+ test('scope directives must come first', async () => {
419
+ let a = h`<x :text="y" :with="{y:1}" :ref="x"></x>`
420
+ sprae(a, {})
421
+ is(a.outerHTML, `<x>1</x>`)
303
422
  })
@@ -1,10 +0,0 @@
1
- import { directive, parseExpr } from '../core.js'
2
- import { prop } from 'element-props'
3
-
4
- directive(':aria', (el, expr) => {
5
- let evaluate = parseExpr(expr);
6
- return (state) => {
7
- let value = evaluate(state);
8
- for (let key in value) prop(el, 'aria'+key[0].toUpperCase()+key.slice(1), value[key]);
9
- }
10
- })
@@ -1,15 +0,0 @@
1
- // common directives just set/map value as is
2
- import { directive, parseExpr } from '../core.js'
3
- import {prop} from 'element-props'
4
-
5
- common(`id`), common(`name`), common(`for`), common(`type`), common(`hidden`), common(`disabled`), common(`href`), common(`src`), common(`style`), common(`class`)
6
-
7
- function common(name) {
8
- directive(':'+name, (el,expr) => {
9
- let evaluate = parseExpr(expr)
10
- return state => {
11
- let value = evaluate(state);
12
- prop(el, name, value)
13
- }
14
- })
15
- }
@@ -1,10 +0,0 @@
1
- import { directive, parseExpr } from '../core.js'
2
- import { prop } from 'element-props'
3
-
4
- directive(':data', (el, expr) => {
5
- let evaluate = parseExpr(expr);
6
- return (state) => {
7
- let value = evaluate(state);
8
- for (let key in value) el.dataset[key] = value[key];
9
- }
10
- })
@@ -1,53 +0,0 @@
1
- import sprae, { directive, exprError, parseExpr } from '../core.js'
2
-
3
- directive(':each', (el, expr) => {
4
- let each = parseForExpression(expr);
5
- if (!each) return exprError(new Error, expr);
6
-
7
- const getItems = parseExpr(each.items);
8
-
9
- const holder = new Text
10
- el.replaceWith(holder)
11
-
12
- // FIXME: make sure no memory leak here
13
- // FIXME: there can DOM swapper be used instead
14
- let els = [];
15
- return state => {
16
- els.forEach(el => el.remove()); els = [];
17
- let items = getItems(state);
18
- if (typeof items === 'number') items = Array.from({length: items}, (item, i)=>i+1)
19
- items?.forEach((item,i) => {
20
- const scope = {...state};
21
- scope[each.item] = item;
22
- if (each.index) scope[each.index] = i;
23
- let itemEl = el.cloneNode(true);
24
- els.push(itemEl);
25
- holder.before(itemEl);
26
- sprae(itemEl, scope);
27
- });
28
- }
29
- })
30
-
31
- // This was taken AlpineJS, former VueJS 2.* core. Thanks Alpine & Vue!
32
- function parseForExpression(expression) {
33
- let forIteratorRE = /,([^,\}\]]*)(?:,([^,\}\]]*))?$/
34
- let stripParensRE = /^\s*\(|\)\s*$/g
35
- let forAliasRE = /([\s\S]*?)\s+(?:in|of)\s+([\s\S]*)/
36
- let inMatch = expression.match(forAliasRE)
37
-
38
- if (!inMatch) return
39
-
40
- let res = {}
41
- res.items = inMatch[2].trim()
42
- let item = inMatch[1].replace(stripParensRE, '').trim()
43
- let iteratorMatch = item.match(forIteratorRE)
44
-
45
- if (iteratorMatch) {
46
- res.item = item.replace(forIteratorRE, '').trim()
47
- res.index = iteratorMatch[1].trim()
48
- } else {
49
- res.item = item
50
- }
51
-
52
- return res
53
- }
@@ -1,31 +0,0 @@
1
- import { directive, parseExpr } from '../core.js'
2
-
3
- directive(':if', (el, expr) => {
4
- let cur = el, els = [el], clauses = [parseExpr(expr)], holder = new Text
5
-
6
- // collect clauses
7
- while (cur = el.nextElementSibling) {
8
- if (expr = cur.getAttribute(':else-if')) {
9
- cur.removeAttribute(':else-if');
10
- cur.classList.add('∴else-if')
11
- cur.remove();
12
- els.push(cur); clauses.push(parseExpr(expr));
13
- continue
14
- }
15
- if (cur.hasAttribute(':else')) {
16
- cur.removeAttribute(':else');
17
- cur.classList.add('∴else')
18
- cur.remove();
19
- els.push(cur); clauses.push(() => 1);
20
- }
21
- break;
22
- }
23
-
24
- cur = els[0]
25
-
26
- return state => {
27
- let idx = clauses.findIndex(match => match(state));
28
- if (idx >= 0) cur.replaceWith(cur = els[idx]);
29
- else cur.replaceWith(cur = holder);
30
- }
31
- })
@@ -1,11 +0,0 @@
1
- // order defines precedence
2
- import './with.js'
3
- import './each.js'
4
- import './text.js'
5
- import './if.js' // if must go last, after other directives are initialized, since it removes :else, :else-if from tree
6
- import './common.js'
7
- import './prop.js'
8
- import './data.js'
9
- import './aria.js'
10
- import './value.js'
11
- import './on.js'
@@ -1,12 +0,0 @@
1
- import { directive, parseExpr } from '../core.js'
2
-
3
- directive(':on', (el, expr) => {
4
- let evaluate = parseExpr(expr);
5
- let listeners = {}
6
- return (state) => {
7
- for (let evt in listeners) el.removeEventListener(evt, listeners[evt]);
8
- listeners = evaluate(state);
9
- for (let evt in listeners) el.addEventListener(evt, listeners[evt]);
10
- }
11
- })
12
-
@@ -1,10 +0,0 @@
1
- import { directive, parseExpr } from '../core.js'
2
- import { prop } from 'element-props'
3
-
4
- directive(':prop', (el, expr) => {
5
- let evaluate = parseExpr(expr);
6
- return (state) => {
7
- let value = evaluate(state);
8
- for (let key in value) prop(el, key, value[key]);
9
- }
10
- })
@@ -1,10 +0,0 @@
1
- import { directive, parseExpr } from '../core.js'
2
-
3
- directive(':text', (el, expr) => {
4
- let evaluate = parseExpr(expr);
5
- return (state) => {
6
- let value = evaluate(state);
7
- el.textContent = value == null ? '' : value;
8
- }
9
- })
10
-
@@ -1,26 +0,0 @@
1
- import { input, prop } from "element-props";
2
- import { directive, parseExpr } from '../core.js';
3
-
4
- // connect expr to element value
5
- directive(':value', (el, expr) => {
6
- let evaluateGet = parseExpr(expr);
7
- let [get, set] = input(el);
8
- let evaluateSet = parseSetter(expr);
9
- let curState, onchange = e => evaluateSet(curState, get(el));
10
- el.addEventListener('input', onchange);
11
- el.addEventListener('change', onchange);
12
- return (state) => {
13
- let value = evaluateGet(curState = state);
14
- prop(el, 'value', value)
15
- set(value);
16
- }
17
- })
18
-
19
- const memo = {}
20
- function parseSetter(expr) {
21
- if (memo[expr]) return memo[expr]
22
- return memo[expr] = new Function(
23
- ['scope', 'value'],
24
- `with (scope) { ${expr} = value };`
25
- )
26
- }