sprae 6.1.1 → 6.1.2
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 +8 -1
- package/readme.md +55 -41
- package/sprae.auto.js +1 -1
- package/sprae.auto.min.js +1 -1
- package/sprae.js +1 -1
- package/sprae.min.js +1 -1
- package/src/core.js +5 -5
- package/src/directives.js +36 -37
- package/.gitattributes +0 -2
- package/.github/workflows/node.js.yml +0 -30
- package/examples/todomvc.html +0 -98
- package/r&d.md +0 -620
- package/test/attrs.js +0 -754
- package/test/events.js +0 -138
- package/test/index.html +0 -36
- package/test/register.cjs +0 -20
- package/test/state.js +0 -296
- package/test/test.js +0 -13
- package/todo.md +0 -54
package/test/events.js
DELETED
|
@@ -1,138 +0,0 @@
|
|
|
1
|
-
// import { signal } from 'usignal/sync'
|
|
2
|
-
import test, {is, any, throws} from 'tst'
|
|
3
|
-
import {tick, time} from 'wait-please'
|
|
4
|
-
import sprae from '../src/index.js'
|
|
5
|
-
import h from 'hyperf'
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
test('events: this context', e => {
|
|
9
|
-
let el = h`<div @x="log.push(this)"></div>`
|
|
10
|
-
let state = sprae(el, {log: []})
|
|
11
|
-
el.dispatchEvent(new window.Event('x'));
|
|
12
|
-
is(state.log, [el])
|
|
13
|
-
})
|
|
14
|
-
|
|
15
|
-
test('events: multiple events', e => {
|
|
16
|
-
let el = h`<div @scroll@click@x="log.push(event.type)"></div>`
|
|
17
|
-
let state = sprae(el, {log:[]})
|
|
18
|
-
|
|
19
|
-
el.dispatchEvent(new window.Event('click'));
|
|
20
|
-
is(state.log, ['click'])
|
|
21
|
-
el.dispatchEvent(new window.Event('scroll'));
|
|
22
|
-
is(state.log, ['click','scroll'])
|
|
23
|
-
el.dispatchEvent(new window.Event('x'));
|
|
24
|
-
is(state.log, ['click','scroll','x'])
|
|
25
|
-
})
|
|
26
|
-
|
|
27
|
-
test('events: once', e => {
|
|
28
|
-
// NOTE: if callback updates it's still rebound
|
|
29
|
-
let el = h`<x @x.once="(x&&log.push(this))" ></x>`
|
|
30
|
-
let log = []
|
|
31
|
-
let state = sprae(el, {log, x:1})
|
|
32
|
-
el.dispatchEvent(new window.Event('x'));
|
|
33
|
-
is(log, [el])
|
|
34
|
-
el.dispatchEvent(new window.Event('x'));
|
|
35
|
-
is(log, [el])
|
|
36
|
-
state.x = 2
|
|
37
|
-
el.dispatchEvent(new window.Event('x'));
|
|
38
|
-
el.dispatchEvent(new window.Event('x'));
|
|
39
|
-
is(log, [el])
|
|
40
|
-
})
|
|
41
|
-
|
|
42
|
-
test('events: capture, stop, prevent', e => {
|
|
43
|
-
let el = h`<x @x.capture="log.push(1)"><y @x="log.push(2)"></y></x>`
|
|
44
|
-
let state = sprae(el, {log:[]})
|
|
45
|
-
el.firstChild.dispatchEvent(new window.Event('x', {bubbles:true}));
|
|
46
|
-
is(state.log, [1,2])
|
|
47
|
-
|
|
48
|
-
let el2 = h`<x @x="log.push(1)"><y @x.stop="log.push(2)"></y></x>`
|
|
49
|
-
let state2 = sprae(el2, {log:[]})
|
|
50
|
-
el2.firstChild.dispatchEvent(new window.Event('x', {bubbles:true}));
|
|
51
|
-
is(state2.log, [2])
|
|
52
|
-
})
|
|
53
|
-
|
|
54
|
-
test('events: window, self', e => {
|
|
55
|
-
let el = h`<x @x.self="log.push(1)"><y @x.window="log.push(2)"></y></x>`
|
|
56
|
-
let state = sprae(el, {log:[]})
|
|
57
|
-
el.firstChild.dispatchEvent(new window.Event('x', {bubbles:true}));
|
|
58
|
-
is(state.log, [])
|
|
59
|
-
el.dispatchEvent(new window.Event('x', {bubbles:true}));
|
|
60
|
-
is(state.log, [1])
|
|
61
|
-
window.dispatchEvent(new window.Event('x', {bubbles:true}));
|
|
62
|
-
is(state.log, [1,2])
|
|
63
|
-
})
|
|
64
|
-
|
|
65
|
-
test('events: keys', e => {
|
|
66
|
-
let el = h`<x @keydown.enter="log.push(1)"></x>`
|
|
67
|
-
let state = {log:[]}
|
|
68
|
-
sprae(el, state)
|
|
69
|
-
el.dispatchEvent(new window.KeyboardEvent('keydown', { key: '' }));
|
|
70
|
-
is(state.log,[])
|
|
71
|
-
el.dispatchEvent(new window.KeyboardEvent('keydown', { key: 'Enter' }));
|
|
72
|
-
el.dispatchEvent(new window.KeyboardEvent('keydown', { key: '' }));
|
|
73
|
-
is(state.log,[1])
|
|
74
|
-
el.dispatchEvent(new window.KeyboardEvent('keydown', { key: 'Enter' }));
|
|
75
|
-
is(state.log,[1,1])
|
|
76
|
-
})
|
|
77
|
-
|
|
78
|
-
test('events: key combinations', e => {
|
|
79
|
-
let el = h`<x @keydown.ctrl-enter="log.push(1)"></x>`
|
|
80
|
-
let state = {log:[]}
|
|
81
|
-
sprae(el, state)
|
|
82
|
-
el.dispatchEvent(new window.KeyboardEvent('keydown', { key: '' }));
|
|
83
|
-
is(state.log,[])
|
|
84
|
-
el.dispatchEvent(new window.KeyboardEvent('keydown', { key: 'Enter' }));
|
|
85
|
-
el.dispatchEvent(new window.KeyboardEvent('keydown', { key: '' }));
|
|
86
|
-
is(state.log,[])
|
|
87
|
-
el.dispatchEvent(new window.KeyboardEvent('keydown', { key: 'Enter', ctrlKey: true }));
|
|
88
|
-
el.dispatchEvent(new window.KeyboardEvent('keydown', { key: 'Enter' }));
|
|
89
|
-
is(state.log,[1])
|
|
90
|
-
el.dispatchEvent(new window.KeyboardEvent('keydown', { key: 'Enter' }));
|
|
91
|
-
el.dispatchEvent(new window.KeyboardEvent('keydown', { key: 'Enter', ctrlKey: true }));
|
|
92
|
-
is(state.log,[1,1])
|
|
93
|
-
let el2 = h`<x @keydown.ctrl-alt-enter="log.push(1)"></x>`
|
|
94
|
-
})
|
|
95
|
-
|
|
96
|
-
test('events: keys with prevent', e => {
|
|
97
|
-
let el = h`<y @keydown="log.push(event.key)"><x :ref="x" @keydown.enter.stop></x></y>`
|
|
98
|
-
let state = sprae(el, {log:[]})
|
|
99
|
-
state.x.dispatchEvent(new window.KeyboardEvent('keydown', { key: 'x', bubbles: true }));
|
|
100
|
-
console.log('enter')
|
|
101
|
-
state.x.dispatchEvent(new window.KeyboardEvent('keydown', { key: 'Enter', bubbles: true }));
|
|
102
|
-
is(state.log,['x'])
|
|
103
|
-
})
|
|
104
|
-
|
|
105
|
-
test('events: debounce', async e => {
|
|
106
|
-
let el = h`<x @keydown.debounce-1="log.push(event.key)"></x>`
|
|
107
|
-
let state = sprae(el, {log:[]})
|
|
108
|
-
el.dispatchEvent(new window.KeyboardEvent('keydown', { key: 'x', bubbles: true }));
|
|
109
|
-
is(state.log, [])
|
|
110
|
-
await time(2)
|
|
111
|
-
is(state.log, ['x'])
|
|
112
|
-
})
|
|
113
|
-
|
|
114
|
-
test('events: debounce 0', async e => {
|
|
115
|
-
let el = h`<x @keydown.debounce-0="log.push(event.key)"></x>`
|
|
116
|
-
let state = sprae(el, {log:[]})
|
|
117
|
-
el.dispatchEvent(new window.KeyboardEvent('keydown', { key: 'x', bubbles: true }));
|
|
118
|
-
is(state.log, [])
|
|
119
|
-
await time(2)
|
|
120
|
-
is(state.log, ['x'])
|
|
121
|
-
})
|
|
122
|
-
|
|
123
|
-
test('events: throttle', async e => {
|
|
124
|
-
let el = h`<x @keydown.throttle-10="log.push(event.key)"></x>`
|
|
125
|
-
let state = sprae(el, {log:[]})
|
|
126
|
-
el.dispatchEvent(new window.KeyboardEvent('keydown', { key: 'x', bubbles: true }));
|
|
127
|
-
el.dispatchEvent(new window.KeyboardEvent('keydown', { key: 'x', bubbles: true }));
|
|
128
|
-
is(state.log, ['x'])
|
|
129
|
-
await time(5)
|
|
130
|
-
el.dispatchEvent(new window.KeyboardEvent('keydown', { key: 'x', bubbles: true }));
|
|
131
|
-
is(state.log, ['x'])
|
|
132
|
-
await time(10)
|
|
133
|
-
is(state.log, ['x', 'x'])
|
|
134
|
-
await time(10)
|
|
135
|
-
is(state.log, ['x', 'x'])
|
|
136
|
-
el.dispatchEvent(new window.KeyboardEvent('keydown', { key: 'x', bubbles: true }));
|
|
137
|
-
is(state.log, ['x', 'x', 'x'])
|
|
138
|
-
})
|
package/test/index.html
DELETED
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
<!doctype html>
|
|
2
|
-
<meta charset=utf-8>
|
|
3
|
-
<title>Test</title>
|
|
4
|
-
|
|
5
|
-
<script defer src="../sprae.auto.js"></script>
|
|
6
|
-
<div :with="{data:'autoinit works'}">
|
|
7
|
-
<span :text="data">autoinit is broken</span>
|
|
8
|
-
</div>
|
|
9
|
-
|
|
10
|
-
<script async src="../node_modules/es-module-shims/dist/es-module-shims.js"></script>
|
|
11
|
-
<script type="importmap">
|
|
12
|
-
{
|
|
13
|
-
"imports": {
|
|
14
|
-
"sprae" : "../src/index.js",
|
|
15
|
-
"tst" : "../node_modules/tst/tst.js",
|
|
16
|
-
"wait-please" : "../node_modules/wait-please/index.js",
|
|
17
|
-
"value-ref" : "../node_modules/value-ref/value-ref.js",
|
|
18
|
-
"hyperf" : "../node_modules/hyperf/hyperf.js",
|
|
19
|
-
"sube" : "../node_modules/sube/sube.js",
|
|
20
|
-
"signal-struct" : "../node_modules/signal-struct/signal-struct.js",
|
|
21
|
-
"element-props" : "../node_modules/element-props/element-props.js",
|
|
22
|
-
"swapdom" : "../node_modules/swapdom/swap-inflate.js",
|
|
23
|
-
"swapdom/swap-deflate" : "../node_modules/swapdom/swap-deflate.js",
|
|
24
|
-
"swapdom/swap-inflate" : "../node_modules/swapdom/swap-inflate.js",
|
|
25
|
-
"primitive-pool" : "../node_modules/primitive-pool/index.js",
|
|
26
|
-
"preact": "../node_modules/preact/dist/preact.mjs",
|
|
27
|
-
"preact/hooks": "../node_modules/preact/hooks/dist/hooks.mjs",
|
|
28
|
-
"@preact/signals-core": "../node_modules/@preact/signals-core/dist/signals-core.mjs",
|
|
29
|
-
"usignal/sync": "../node_modules/usignal/esm/sync.js",
|
|
30
|
-
"staet": "../node_modules/staet/staet.js"
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
</script>
|
|
34
|
-
|
|
35
|
-
<script src="./test.js" type="module"></script>
|
|
36
|
-
|
package/test/register.cjs
DELETED
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
// provide DOM env for node tests
|
|
2
|
-
let { JSDOM } = require('jsdom')
|
|
3
|
-
|
|
4
|
-
const { window } = new JSDOM(`<!DOCTYPE html>`, {
|
|
5
|
-
url: "http://localhost/",
|
|
6
|
-
storageQuota: 10000000,
|
|
7
|
-
pretendToBeVisual: true,
|
|
8
|
-
FetchExternalResources: false,
|
|
9
|
-
ProcessExternalResources: false
|
|
10
|
-
})
|
|
11
|
-
|
|
12
|
-
let props = Object.getOwnPropertyNames(window)
|
|
13
|
-
|
|
14
|
-
props.forEach(prop => {
|
|
15
|
-
if (prop in global) return
|
|
16
|
-
Object.defineProperty(global, prop, {
|
|
17
|
-
configurable: true,
|
|
18
|
-
get: () => window[prop]
|
|
19
|
-
})
|
|
20
|
-
})
|
package/test/state.js
DELETED
|
@@ -1,296 +0,0 @@
|
|
|
1
|
-
import {state, fx} from '../src/state.js'
|
|
2
|
-
import t, {is, ok} from 'tst'
|
|
3
|
-
import signalStruct from 'signal-struct'
|
|
4
|
-
import { effect } from '@preact/signals-core'
|
|
5
|
-
import {tick} from 'wait-please'
|
|
6
|
-
|
|
7
|
-
t('state: basic', async t => {
|
|
8
|
-
let s = state({x:0, y:1})
|
|
9
|
-
|
|
10
|
-
let xy; fx(() => xy = s.x + s.y)
|
|
11
|
-
is(xy, 1, 'Eq')
|
|
12
|
-
console.log('set 2')
|
|
13
|
-
s.x = 2
|
|
14
|
-
console.log('set 3')
|
|
15
|
-
s.y = 3
|
|
16
|
-
await tick()
|
|
17
|
-
is(xy, 5, 'Eq')
|
|
18
|
-
s.y = 4
|
|
19
|
-
await tick()
|
|
20
|
-
is(xy, 6, 'Eq')
|
|
21
|
-
})
|
|
22
|
-
|
|
23
|
-
t('state: signal-struct basics', async t => {
|
|
24
|
-
let s = state({
|
|
25
|
-
x: 0,
|
|
26
|
-
y: 1,
|
|
27
|
-
z: { r: 2, i: 3 },
|
|
28
|
-
v: function(){return 1},
|
|
29
|
-
w: [1,2],
|
|
30
|
-
get xy () { return this.x + this.y },
|
|
31
|
-
set xy ([x,y]) { return this.x = x, this.y = y }
|
|
32
|
-
})
|
|
33
|
-
|
|
34
|
-
// functions are signals too
|
|
35
|
-
is(s.v(), 1)
|
|
36
|
-
// subscribes to only x and y without need for .value access
|
|
37
|
-
const zilog = []
|
|
38
|
-
fx(() => zilog.push(s.z.i))
|
|
39
|
-
is(zilog, [3])
|
|
40
|
-
|
|
41
|
-
let xy; fx(() => xy = s.x + s.y)
|
|
42
|
-
is(xy, 1)
|
|
43
|
-
s.x = 2
|
|
44
|
-
s.y = 3
|
|
45
|
-
await tick()
|
|
46
|
-
is(xy, 5)
|
|
47
|
-
s.y = 4
|
|
48
|
-
await tick()
|
|
49
|
-
is(xy, 6)
|
|
50
|
-
|
|
51
|
-
// getters are computed
|
|
52
|
-
is(s.xy, 6)
|
|
53
|
-
s.xy = [4,2]
|
|
54
|
-
is(s.x, 4)
|
|
55
|
-
is(s.y, 2)
|
|
56
|
-
is(s.xy, 6)
|
|
57
|
-
})
|
|
58
|
-
|
|
59
|
-
t('state: deep props', async () => {
|
|
60
|
-
let s = state({
|
|
61
|
-
z: { r: 2, i: 3 }
|
|
62
|
-
})
|
|
63
|
-
|
|
64
|
-
// subscribes to deep values too: only z.r and z.i update result
|
|
65
|
-
let len; fx(() => (len = (s.z.r**2 + s.z.i**2)**0.5))
|
|
66
|
-
s.z.r = 3
|
|
67
|
-
s.z.i = 4
|
|
68
|
-
await tick()
|
|
69
|
-
is(len, 5)
|
|
70
|
-
s.z.r = 4
|
|
71
|
-
s.z.i = 3
|
|
72
|
-
await tick()
|
|
73
|
-
is(len, 5)
|
|
74
|
-
|
|
75
|
-
// updating internal objects/arrays turns them into signals too
|
|
76
|
-
s.z = { r: 5, i: 12}
|
|
77
|
-
await tick()
|
|
78
|
-
is(len, 13)
|
|
79
|
-
})
|
|
80
|
-
|
|
81
|
-
t('state: array', async () => {
|
|
82
|
-
let s = state({
|
|
83
|
-
w: [1,2]
|
|
84
|
-
})
|
|
85
|
-
|
|
86
|
-
// updating array is fine
|
|
87
|
-
let mult; fx(() => mult = s.w?.[0] * s.w?.[1] || 0)
|
|
88
|
-
is(mult, 2)
|
|
89
|
-
s.w = [3,4]
|
|
90
|
-
await tick()
|
|
91
|
-
is(mult,12)
|
|
92
|
-
|
|
93
|
-
// nullifying is fine
|
|
94
|
-
s.w = null
|
|
95
|
-
await tick()
|
|
96
|
-
is(mult, 0)
|
|
97
|
-
|
|
98
|
-
// delete is fine
|
|
99
|
-
delete s.w
|
|
100
|
-
|
|
101
|
-
await tick()
|
|
102
|
-
is(mult, 0)
|
|
103
|
-
|
|
104
|
-
console.log('set w')
|
|
105
|
-
s.w = [1,2]
|
|
106
|
-
await tick()
|
|
107
|
-
is(mult, 2)
|
|
108
|
-
})
|
|
109
|
-
|
|
110
|
-
t('state: bulk-update', async () => {
|
|
111
|
-
let s = state({
|
|
112
|
-
x: 0,
|
|
113
|
-
y: 1,
|
|
114
|
-
z: { r: 2, i: 3 },
|
|
115
|
-
v: function(){return 1},
|
|
116
|
-
w: [1,2],
|
|
117
|
-
get xy () { return this.x + this.y },
|
|
118
|
-
set xy ([x,y]) { return this.x = x, this.y = y }
|
|
119
|
-
})
|
|
120
|
-
|
|
121
|
-
// FIXME: number of invocations must be minimized
|
|
122
|
-
let xy; fx(() => xy = s.x + s.y)
|
|
123
|
-
let len; fx(() => (len = (s.z.r**2 + s.z.i**2)**0.5))
|
|
124
|
-
|
|
125
|
-
// bulk-update is deep
|
|
126
|
-
// let [signals, update] = s
|
|
127
|
-
// update({ x: 1, y: 1, z: { r: 3, i: 4 } })
|
|
128
|
-
Object.assign(s, { x: 1, y: 1, z: { r: 3, i: 4 } })
|
|
129
|
-
await tick()
|
|
130
|
-
is(xy, 2)
|
|
131
|
-
is(len, 5, 'len after update')
|
|
132
|
-
})
|
|
133
|
-
|
|
134
|
-
t('state: type consistency', () => {
|
|
135
|
-
let s = state({})
|
|
136
|
-
|
|
137
|
-
// signals retain same type as init data
|
|
138
|
-
is(s.constructor, Object)
|
|
139
|
-
|
|
140
|
-
// re-initializing returns itself
|
|
141
|
-
let s1 = state(s)
|
|
142
|
-
is(s, s1)
|
|
143
|
-
})
|
|
144
|
-
|
|
145
|
-
t('state: iteration is safe', () => {
|
|
146
|
-
// it is not enumerable
|
|
147
|
-
let s2 = state([])
|
|
148
|
-
let log = []
|
|
149
|
-
for (let i of s2) log.push(i)
|
|
150
|
-
is(log, [], 'doesn\'t iterate')
|
|
151
|
-
})
|
|
152
|
-
|
|
153
|
-
t('state: state from same instance', () => {
|
|
154
|
-
// NOTE: I guess we may wan to have multiple proxies for same target, don't we?
|
|
155
|
-
let s = {x:1}
|
|
156
|
-
let s1 = state(s)
|
|
157
|
-
let s2 = state(s)
|
|
158
|
-
ok(s1 === s2)
|
|
159
|
-
})
|
|
160
|
-
|
|
161
|
-
t('state: state from state', () => {
|
|
162
|
-
let s = {x:1}
|
|
163
|
-
let s1 = state(s)
|
|
164
|
-
let s2 = state(s1)
|
|
165
|
-
ok(s1 === s2)
|
|
166
|
-
})
|
|
167
|
-
|
|
168
|
-
t('state: inheritance', () => {
|
|
169
|
-
let s = state({ x: 0 })
|
|
170
|
-
let s1 = state({ y: 2 }, s)
|
|
171
|
-
is(s1.x, 0)
|
|
172
|
-
is(s1.y, 2)
|
|
173
|
-
|
|
174
|
-
// descendants are detected as instances
|
|
175
|
-
// let s3 = Object.create(s1), s3s = state(s3)
|
|
176
|
-
// is(s3, s3s)
|
|
177
|
-
|
|
178
|
-
// can subscribe to reactive sources too
|
|
179
|
-
// let s4 = state({
|
|
180
|
-
// p: new Promise(ok => setTimeout(() => ok(123)))
|
|
181
|
-
// })
|
|
182
|
-
// is(s4.p, undefined)
|
|
183
|
-
// setTimeout(() => {
|
|
184
|
-
// is(s4.p, 123)
|
|
185
|
-
// })
|
|
186
|
-
|
|
187
|
-
// let s43 = Object.create(s4, s3)
|
|
188
|
-
// setTimeout(() => {
|
|
189
|
-
// is(s43.p,123)
|
|
190
|
-
// is(s43.y,1)
|
|
191
|
-
// })
|
|
192
|
-
})
|
|
193
|
-
|
|
194
|
-
t('state: inheritance: updating values in chain', async () => {
|
|
195
|
-
let s1 = {x:1}
|
|
196
|
-
let s = state(s1, {y:2})
|
|
197
|
-
// console.group('fx')
|
|
198
|
-
let xy = 0; fx(() => xy = s.x + s.y);
|
|
199
|
-
// console.groupEnd('fx')
|
|
200
|
-
await tick()
|
|
201
|
-
is(xy, 3)
|
|
202
|
-
s.x++
|
|
203
|
-
await tick()
|
|
204
|
-
is(xy, 4)
|
|
205
|
-
console.log('y++')
|
|
206
|
-
s.y++
|
|
207
|
-
await tick()
|
|
208
|
-
is(xy, 5)
|
|
209
|
-
})
|
|
210
|
-
|
|
211
|
-
t('state: array items', async () => {
|
|
212
|
-
// arrays get each item converted to signal struct
|
|
213
|
-
let s5 = state({list: [{x:1}, {x:2}]})
|
|
214
|
-
let sum; fx(()=> sum = s5.list.reduce((sum, item)=>item.x + sum, 0))
|
|
215
|
-
is(sum, 3)
|
|
216
|
-
s5.list[0].x = 2
|
|
217
|
-
await tick()
|
|
218
|
-
is(sum, 4)
|
|
219
|
-
console.log('set array value')
|
|
220
|
-
s5.list = [{x:3}, {x:3}]
|
|
221
|
-
await tick()
|
|
222
|
-
is(sum, 6)
|
|
223
|
-
s5.list = [{x:3}, {x:3}, {x:4}]
|
|
224
|
-
await tick()
|
|
225
|
-
is(sum, 10)
|
|
226
|
-
})
|
|
227
|
-
|
|
228
|
-
t('state: arrays retain reference', () => {
|
|
229
|
-
// arrays retain reference
|
|
230
|
-
let list = [1,2,3]
|
|
231
|
-
let s6 = state({list})
|
|
232
|
-
s6.list[1] = 4
|
|
233
|
-
is(list,[1,4,3])
|
|
234
|
-
})
|
|
235
|
-
|
|
236
|
-
t('state: direct list', async () => {
|
|
237
|
-
// works with arrays as well
|
|
238
|
-
let list = state([{x:1}, {x:2}])
|
|
239
|
-
let sum; fx(()=> sum = list.reduce((sum, item)=>item.x + sum, 0))
|
|
240
|
-
is(sum, 3)
|
|
241
|
-
list[0].x = 2
|
|
242
|
-
is(list[0].x, 2)
|
|
243
|
-
await tick()
|
|
244
|
-
is(sum, 4)
|
|
245
|
-
list.splice(0, 2, {x:3}, {x:3})
|
|
246
|
-
await tick()
|
|
247
|
-
console.log(list)
|
|
248
|
-
is(sum, 6)
|
|
249
|
-
})
|
|
250
|
-
|
|
251
|
-
t('state: array methods', () => {
|
|
252
|
-
// FIXME: somehow sprae was falling with something like this
|
|
253
|
-
let a = state({a:[1]})
|
|
254
|
-
let b = a['a']['map'](x => x * 2)
|
|
255
|
-
is(b, [2])
|
|
256
|
-
})
|
|
257
|
-
|
|
258
|
-
t('state: circular?', () => {
|
|
259
|
-
let a = state([])
|
|
260
|
-
fx(() => a.push(1))
|
|
261
|
-
a.push(2)
|
|
262
|
-
is (a, [1, 2])
|
|
263
|
-
})
|
|
264
|
-
|
|
265
|
-
t.skip('state: batch', () => {
|
|
266
|
-
let s = state({x:1, y:2})
|
|
267
|
-
let log = []; fx(() => log.push(s.x + s.y))
|
|
268
|
-
is(log, [3])
|
|
269
|
-
batch(() => (s.x++, s.y++))
|
|
270
|
-
is(log, [3, 5])
|
|
271
|
-
batch(() => (
|
|
272
|
-
batch(() => s.x++)
|
|
273
|
-
))
|
|
274
|
-
is(log, [3, 5, 6])
|
|
275
|
-
})
|
|
276
|
-
|
|
277
|
-
t('state: bench', () => {
|
|
278
|
-
const N = 100000
|
|
279
|
-
|
|
280
|
-
let s2 = signalStruct({x:1, y:2}), xy2
|
|
281
|
-
effect(() => xy2 = s2.x * s2.y)
|
|
282
|
-
console.time('signalStruct')
|
|
283
|
-
for (let i = 0; i < N; i++) {
|
|
284
|
-
s2.x++, s2.y++
|
|
285
|
-
}
|
|
286
|
-
console.timeEnd('signalStruct')
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
let s1 = state({x:1, y:2}), xy
|
|
290
|
-
fx(() => xy = s1.x * s1.y)
|
|
291
|
-
console.time('fxs')
|
|
292
|
-
for (let i = 0; i < N; i++) {
|
|
293
|
-
s1.x++, s1.y++
|
|
294
|
-
}
|
|
295
|
-
console.timeEnd('fxs')
|
|
296
|
-
})
|
package/test/test.js
DELETED
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
import './state.js'
|
|
2
|
-
import './attrs.js'
|
|
3
|
-
import './events.js'
|
|
4
|
-
|
|
5
|
-
Object.defineProperty(DocumentFragment.prototype, 'outerHTML', {
|
|
6
|
-
get() {
|
|
7
|
-
let s = ''
|
|
8
|
-
this.childNodes.forEach(n => {
|
|
9
|
-
s += n.nodeType === 3 ? n.textContent : n.outerHTML != null ? n.outerHTML : ''
|
|
10
|
-
})
|
|
11
|
-
return s
|
|
12
|
-
}
|
|
13
|
-
})
|
package/todo.md
DELETED
|
@@ -1,54 +0,0 @@
|
|
|
1
|
-
* [x] finish directives
|
|
2
|
-
* [x] better list diffing
|
|
3
|
-
* [x] ordered directives init (:each + :if vs :if + :each) -> find out if really needed and which is faster
|
|
4
|
-
-> yes, needed and solve many init issues.
|
|
5
|
-
* [?] autoinit -> too much maintenance burden
|
|
6
|
-
* [x] node tests
|
|
7
|
-
* [x] better deps updating -> cumulative signal
|
|
8
|
-
* [x] combinations: :else :if
|
|
9
|
-
* [x] :each :if, :if :each
|
|
10
|
-
* [x] :each :each
|
|
11
|
-
* [x] :with must be able to write state value as well
|
|
12
|
-
* [x] docs: give example to each directive
|
|
13
|
-
* [x] initialize per-element: <x :each><y :if></y><x> - tree-dependent (:each comes first).
|
|
14
|
-
* [x] generalize common attributes :prop="xyz"
|
|
15
|
-
* [x] spread props
|
|
16
|
-
* [x] optimization: arrays with multiple elements can be slow on creation. Maybe signal-struct must ignore arrays.
|
|
17
|
-
-> yep: arrays are rarely changed as `a[i]=newItem` and regularly they're mapped.
|
|
18
|
-
* [x] expand to any subscribables: both as state vars
|
|
19
|
-
* [x] :ref
|
|
20
|
-
* [x] :ref + :each
|
|
21
|
-
* [x] event chains :ona-onb
|
|
22
|
-
* [x] bulk events :ona:onb
|
|
23
|
-
* [x] multiprop setter :a:b="c"
|
|
24
|
-
* [x] make `this` in expression an element
|
|
25
|
-
* ~~[x] replace :ref with :with="this as x"~~
|
|
26
|
-
* [x] :ref creates instance in current state, not creates a new state
|
|
27
|
-
* [x] to avoid extending signal-struct, we must collect state data before, and call updates after for extended state
|
|
28
|
-
* [x] optimization: replace element-props with direct (better) setters
|
|
29
|
-
* [x] Make sure `false` gets serialized, not removes attr
|
|
30
|
-
* [x] Sandbox expressions: no global, no "scope" object name, no "arguments"
|
|
31
|
-
* ~~[x] report usignal problem~~ author is not really interested
|
|
32
|
-
* [x] `this` doesn't refer to element/scope in event handlers
|
|
33
|
-
* [x] :text="" empty values shouldn't throw
|
|
34
|
-
* [x] implement :with
|
|
35
|
-
* [x] update :value without losing focus / position
|
|
36
|
-
* ~~[x] run tiredown if element got removed from condition or loop (free memory)~~ no need just make sure no refs to elements stored
|
|
37
|
-
* [x] `sprae(el, newState)` can update element's state directly (as batch!?) -> must be tested against repeats in directives
|
|
38
|
-
* [x] :if :ref, :if :with -> context setters must come first always
|
|
39
|
-
* [x] :style="{'--x':value}"
|
|
40
|
-
* [x] :onkeydown.ctrl-alt-D
|
|
41
|
-
* [x] examples
|
|
42
|
-
* [x] todomvc
|
|
43
|
-
* [x] waveplay
|
|
44
|
-
* [x] evt modifiers
|
|
45
|
-
* [x] once, capture, passive
|
|
46
|
-
* [x] ...rest
|
|
47
|
-
* [x] parallel chains
|
|
48
|
-
* [x] Sandbox
|
|
49
|
-
* [x] Autorun
|
|
50
|
-
* [x] There's some bug with prostogreen not triggering effect. (caused by special array.length case)
|
|
51
|
-
* [x] Getters must become evaluable
|
|
52
|
-
* [x] `<li :each="item in items" :with="{collapsed:true}"><button :onclick='e=>collapsed=false'></li>`
|
|
53
|
-
* [ ] `:with="{likes:[], like(){ /* likes should not be undefined here */ }}"`
|
|
54
|
-
* [ ] frameworks benchmark
|