@wrnrlr/prelude 0.2.2 → 0.2.4
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/deno.jsonc +1 -1
- package/example/checkbox.html +53 -0
- package/example/index.html +3 -33
- package/example/todo.html +77 -0
- package/package.json +1 -1
- package/src/controlflow.ts +20 -17
- package/src/runtime.ts +1 -1
- package/test/list.js +34 -0
package/deno.jsonc
CHANGED
@@ -0,0 +1,53 @@
|
|
1
|
+
<!DOCTYPE html>
|
2
|
+
<title>Todo</title>
|
3
|
+
|
4
|
+
<script type="module">
|
5
|
+
|
6
|
+
import {h,signal,effect,batch,wrap,render,List} from '../src/mod.ts'
|
7
|
+
|
8
|
+
function Checkbox(props) {
|
9
|
+
// effect(()=>console.log('checkbox value ', props.value))
|
10
|
+
return h('input',{
|
11
|
+
type: 'checkbox',
|
12
|
+
checked: props.value,
|
13
|
+
onInput: e => props.value(e.target.checked)
|
14
|
+
})
|
15
|
+
}
|
16
|
+
|
17
|
+
function App() {
|
18
|
+
// const todos = signal([true,false])
|
19
|
+
const obj = signal({todos:[true]})
|
20
|
+
const todos = wrap(obj,'todos')
|
21
|
+
const first = wrap(todos, 0)
|
22
|
+
const removeItem = i => {
|
23
|
+
// console.log('remove', i)
|
24
|
+
// batch(()=>{
|
25
|
+
const old = todos()
|
26
|
+
todos(old.toSpliced(i(),1))
|
27
|
+
// })
|
28
|
+
}
|
29
|
+
return [
|
30
|
+
h('button', {onClick:(e)=>{const old = todos(); todos([false, ...old])}}, '+'),
|
31
|
+
h('ol', h(List, {each:()=>todos}, (value, i) =>
|
32
|
+
h('li', [
|
33
|
+
h(Checkbox, { value: ()=>value }),
|
34
|
+
h('button', { onClick: e=>removeItem(i) }, '\u2715')
|
35
|
+
])
|
36
|
+
)),
|
37
|
+
h('button', {onClick:(e)=>{const old = todos(); todos([...old, true])}}, '+'),
|
38
|
+
h(Checkbox, { value:()=>first }),
|
39
|
+
h('pre.hello',()=>'todos: '+JSON.stringify(todos(),undefined,2))
|
40
|
+
]
|
41
|
+
}
|
42
|
+
|
43
|
+
render(App, document.body)
|
44
|
+
|
45
|
+
</script>
|
46
|
+
|
47
|
+
<style>
|
48
|
+
html {background-color:lightgray}
|
49
|
+
ul {margin:0;padding:0}
|
50
|
+
/* ul,li {list-style-type: none} */
|
51
|
+
input {border-width: 0 0 1px 0; margin:0.25em}
|
52
|
+
li>button {border:none; background:none}
|
53
|
+
</style>
|
package/example/index.html
CHANGED
@@ -1,38 +1,8 @@
|
|
1
1
|
<!DOCTYPE html>
|
2
|
-
<title>
|
2
|
+
<title>Examples</title>
|
3
3
|
|
4
|
-
<
|
5
|
-
|
6
|
-
import {h,signal,effect,batch,wrap,render,List} from '../src/mod.ts'
|
7
|
-
|
8
|
-
function Checkbox(props) {
|
9
|
-
// effect(()=>console.log('checkbox value ', props.value))
|
10
|
-
return h('input',{
|
11
|
-
type: 'checkbox',
|
12
|
-
checked: props.value,
|
13
|
-
onInput: e => props.value(e.target.checked)
|
14
|
-
})
|
15
|
-
}
|
16
|
-
|
17
|
-
function App() {
|
18
|
-
// const todos = signal([false])
|
19
|
-
const obj = signal({todos:[false]})
|
20
|
-
const todos = wrap(obj,'todos')
|
21
|
-
const first = wrap(todos, 0)
|
22
|
-
return [
|
23
|
-
h('button', {onClick:(e)=>{const old = todos(); todos([...old, false])}}, '+'),
|
24
|
-
h('ol', h(List, {each:()=>todos}, (value, i) =>
|
25
|
-
// (console.log('value',value),h('li', h(Checkbox, { value:()=>value })))
|
26
|
-
h('li', h(Checkbox, { value:()=>value }))
|
27
|
-
)),
|
28
|
-
h(Checkbox, { value:()=>first }),
|
29
|
-
h('pre.hello',()=>'todos: '+JSON.stringify(todos(),undefined,2))
|
30
|
-
]
|
31
|
-
}
|
32
|
-
|
33
|
-
render(App, document.body)
|
34
|
-
|
35
|
-
</script>
|
4
|
+
<a href="checkbox.html">Checkboxes</a>
|
5
|
+
<a href="todo.html">Todo</a>
|
36
6
|
|
37
7
|
<style>
|
38
8
|
html {background-color:lightgray}
|
@@ -0,0 +1,77 @@
|
|
1
|
+
<!DOCTYPE html>
|
2
|
+
<title>Todo</title>
|
3
|
+
|
4
|
+
<script type="module">
|
5
|
+
|
6
|
+
import {h,signal,effect,batch,wrap,render,List} from '../src/mod.ts'
|
7
|
+
|
8
|
+
const data = [
|
9
|
+
// {description:'Buy groceries',done:false},
|
10
|
+
{description:'Clean car',done:false},
|
11
|
+
{description:'File taxes',done:true},
|
12
|
+
]
|
13
|
+
|
14
|
+
function Input(props) {
|
15
|
+
return h('input',{
|
16
|
+
value:props.value,
|
17
|
+
onInput:e => props.value(e.target.value),
|
18
|
+
tabindex:props.tabindex
|
19
|
+
})
|
20
|
+
}
|
21
|
+
|
22
|
+
function Checkbox(props) {
|
23
|
+
return h('input',{
|
24
|
+
type:'checkbox',
|
25
|
+
checked:props.value,
|
26
|
+
onInput:e => props.value(e.target.checked)
|
27
|
+
})
|
28
|
+
}
|
29
|
+
|
30
|
+
function TodoItem(props) {
|
31
|
+
// const description = wrap(props.value,'description')
|
32
|
+
const done = wrap(props.value,'done')
|
33
|
+
return h('li', [
|
34
|
+
h(Checkbox,{value:()=>done}),
|
35
|
+
// h(Input,{value:()=>description}),
|
36
|
+
h('button',{onClick:props.onDelete},'\u2715')
|
37
|
+
])
|
38
|
+
}
|
39
|
+
|
40
|
+
function App() {
|
41
|
+
const name = signal('')
|
42
|
+
const todos = signal(data)
|
43
|
+
function onSubmit(e) {
|
44
|
+
e.preventDefault()
|
45
|
+
todos(v => v.toSpliced(v.length, 0, {description:name(), done:false}))
|
46
|
+
name('')
|
47
|
+
}
|
48
|
+
const removeItem = i => e => {
|
49
|
+
batch(()=>{
|
50
|
+
const old = todos()
|
51
|
+
console.log('old',old, i)
|
52
|
+
todos(old.toSpliced(i,1))
|
53
|
+
})
|
54
|
+
}
|
55
|
+
return [
|
56
|
+
h('h3','Todo'),
|
57
|
+
h('ul',[h(List, {each:()=>todos}, (value,i) =>
|
58
|
+
h(TodoItem, {
|
59
|
+
value:()=>value,
|
60
|
+
onDelete: removeItem(i())
|
61
|
+
})
|
62
|
+
)]),
|
63
|
+
h('form',{onSubmit},[h(Input,{value:()=>name,tabindex:1})]),
|
64
|
+
h('pre.hello',()=>'todos: '+JSON.stringify(todos(),undefined,2))
|
65
|
+
]
|
66
|
+
}
|
67
|
+
|
68
|
+
render(App, document.body)
|
69
|
+
|
70
|
+
</script>
|
71
|
+
|
72
|
+
<style>
|
73
|
+
ul {margin:0;padding:0}
|
74
|
+
ul,li {list-style-type: none}
|
75
|
+
input {border-width: 0 0 1px 0; margin:0.25em}
|
76
|
+
li>button {border:none; background:none}
|
77
|
+
</style>
|
package/package.json
CHANGED
package/src/controlflow.ts
CHANGED
@@ -49,24 +49,24 @@ type ItemHolder<T> = {
|
|
49
49
|
disposer: ()=>void
|
50
50
|
}
|
51
51
|
|
52
|
-
function listArray<T, U extends Mountable>(
|
52
|
+
export function listArray<T, U extends Mountable>(
|
53
53
|
list: Getter<T[]>,
|
54
54
|
mapFn: (v: Getter<T>, i: Getter<number>) => U,
|
55
55
|
options: { fallback?: Mountable }
|
56
56
|
): () => U[]
|
57
|
-
function listArray<T, U extends Mountable>(
|
57
|
+
export function listArray<T, U extends Mountable>(
|
58
58
|
list: Signal<T[]>,
|
59
59
|
mapFn: (v: Signal<T>, i: Getter<number>) => U,
|
60
60
|
options: { fallback?: Mountable }
|
61
61
|
): () => U[]
|
62
|
-
function listArray<T, U extends Mountable>(
|
62
|
+
export function listArray<T, U extends Mountable>(
|
63
63
|
list: Getter<T[]> | Signal<T[]>,
|
64
64
|
mapFn: (v: Signal<T>, i: Getter<number>) => U,
|
65
65
|
options: { fallback?: Mountable } = {}
|
66
66
|
): () => U[] {
|
67
67
|
const items: ListItem<T>[] = [];
|
68
68
|
let mapped: U[] = [],
|
69
|
-
unusedItems: number,
|
69
|
+
// unusedItems: number,
|
70
70
|
i: number,
|
71
71
|
j: number,
|
72
72
|
item: ListItem<T>,
|
@@ -82,8 +82,10 @@ function listArray<T, U extends Mountable>(
|
|
82
82
|
disposeList(items)
|
83
83
|
})
|
84
84
|
|
85
|
+
let newItems
|
86
|
+
|
85
87
|
return () => {
|
86
|
-
|
88
|
+
newItems = typeof list==='function' ? list() || [] : list;
|
87
89
|
return untrack(() => {
|
88
90
|
if (newItems.length > 0 && fallbackDisposer) {
|
89
91
|
fallbackDisposer();
|
@@ -92,7 +94,7 @@ function listArray<T, U extends Mountable>(
|
|
92
94
|
}
|
93
95
|
|
94
96
|
const temp: U[] = new Array(newItems.length); // new mapped array
|
95
|
-
unusedItems = items.length;
|
97
|
+
let unusedItems = items.length;
|
96
98
|
|
97
99
|
// 1) no change when values & indexes match
|
98
100
|
for (j = unusedItems - 1; j >= 0; --j) {
|
@@ -186,8 +188,7 @@ function listArray<T, U extends Mountable>(
|
|
186
188
|
return (mapped = temp);
|
187
189
|
})
|
188
190
|
}
|
189
|
-
|
190
|
-
function newValueGetter(_:unknown) { return newValue }
|
191
|
+
function newValueGetter(a:unknown) { console.log({a}); return newValue }
|
191
192
|
function changeBoth() {
|
192
193
|
item!.index = i!
|
193
194
|
item!.indexSetter?.(i)
|
@@ -195,25 +196,27 @@ function listArray<T, U extends Mountable>(
|
|
195
196
|
item!.valueSetter?.(newValueGetter)
|
196
197
|
}
|
197
198
|
function mapper(disposer: ()=>void) {
|
198
|
-
const
|
199
|
-
const t = {value: newValue, index: I, disposer}
|
199
|
+
const t = {value: newValue, index: i, disposer}
|
200
200
|
items.push(t)
|
201
|
-
|
201
|
+
let sI = (...a) => {
|
202
|
+
sI = signal(t.index);
|
203
|
+
t.indexSetter = sI;
|
204
|
+
return sI(...a)
|
205
|
+
}
|
202
206
|
let sV = (...a) => {
|
203
|
-
const k = I
|
204
207
|
sV = (...a) => {
|
205
208
|
if (a.length===0) {
|
206
|
-
|
207
|
-
return bk
|
209
|
+
return newItems[t.index]
|
208
210
|
} else {
|
209
|
-
const
|
210
|
-
|
211
|
+
const k = t.index
|
212
|
+
const b = newItems.toSpliced(k, 1, typeof a[0] === 'function' ? a[0]() : a[0])
|
213
|
+
return list(b).at(k)
|
211
214
|
}
|
212
215
|
}
|
213
216
|
t.valueSetter = sV
|
214
217
|
return sV(...a)
|
215
218
|
}
|
216
|
-
return mapFn(sV,
|
219
|
+
return mapFn(sV, sI)
|
217
220
|
}
|
218
221
|
}
|
219
222
|
|
package/src/runtime.ts
CHANGED
@@ -339,7 +339,7 @@ function setAttribute(node: Element, name: string, value?: string): undefined {
|
|
339
339
|
}
|
340
340
|
|
341
341
|
function setAttributeNS(node:Node, ns:string, name:string, value?:string):void {
|
342
|
-
value ? node.setAttributeNS(ns, name, value) : node.removeAttributeNS(ns, name)
|
342
|
+
value===undefined || value===null ? node.setAttributeNS(ns, name, value) : node.removeAttributeNS(ns, name)
|
343
343
|
}
|
344
344
|
|
345
345
|
function addEventListener(node: Element, name: string, handler:((e:Event)=>void), delegate:boolean): void {
|
package/test/list.js
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
import {assertEquals,assert} from '@std/assert'
|
2
|
+
import {describe,it} from '@std/testing/bdd'
|
3
|
+
|
4
|
+
import { signal } from '../src/reactive.ts'
|
5
|
+
import { listArray } from '../src/controlflow.ts'
|
6
|
+
|
7
|
+
// Deno.test('listArray applies list to mapFn', ()=>{
|
8
|
+
// const l = signal([0,1,2])
|
9
|
+
// const r = listArray(l, (e,i)=>({e:e(), i:i()}))
|
10
|
+
// assertEquals(r(), [{e:0,i:0}, {e:1,i:1}, {e:2,i:2}])
|
11
|
+
// })
|
12
|
+
|
13
|
+
// Deno.test('listArray append element', ()=>{
|
14
|
+
// const l = signal([0,1,2])
|
15
|
+
// const r = listArray(l, (e,i)=>({e:e(), i:i()}))
|
16
|
+
// assert(r(), [{e:0,i:0}, {e:1,i:1}, {e:2,i:2}])
|
17
|
+
// l(l => [...l,3])
|
18
|
+
// assertEquals(r(), [{e:0,i:0}, {e:1,i:1}, {e:2,i:2}, {e:3,i:3}])
|
19
|
+
// })
|
20
|
+
|
21
|
+
// Deno.test('listArray prepend element', ()=>{
|
22
|
+
// const l = signal([0,1,2])
|
23
|
+
// const r = listArray(l, (e,i)=>({e:e(), i:i()}))
|
24
|
+
// r()
|
25
|
+
// l(l => [3, ...l])
|
26
|
+
// assertEquals(r(), [{e:3,i:0}, {e:0,i:1}, {e:1,i:2}, {e:2,i:3}])
|
27
|
+
// })
|
28
|
+
const l1 = signal(['a'])
|
29
|
+
const r1 = listArray(l1, (e,i)=>([i(),e()]))
|
30
|
+
// r1()
|
31
|
+
l1(l => ['x', ...l])
|
32
|
+
// l1(l => [...l, 'x', 'y'])
|
33
|
+
console.log(JSON.stringify(r1()))
|
34
|
+
// assertEquals(r(), )
|