@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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wrnrlr/prelude",
3
- "version": "0.2.2",
3
+ "version": "0.2.4",
4
4
  "exports": "./src/mod.ts",
5
5
  "compilerOptions": {
6
6
  "strict": true,
@@ -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>
@@ -1,38 +1,8 @@
1
1
  <!DOCTYPE html>
2
- <title>Todo</title>
2
+ <title>Examples</title>
3
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([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
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@wrnrlr/prelude",
3
3
  "type": "module",
4
- "version": "0.2.2",
4
+ "version": "0.2.4",
5
5
  "author": "Werner Laurensse",
6
6
  "description": "A signal based frontend library with fine-grained reactivity",
7
7
  "main": "./src/mod.ts",
@@ -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
- const newItems = typeof list==='function' ? list() || [] : list;
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
- // const indexes = cb.length > 1 ? [] : null;
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 I = i
199
- const t = {value: newValue, index: I, disposer}
199
+ const t = {value: newValue, index: i, disposer}
200
200
  items.push(t)
201
- const sI = () => { t.indexSetter = I; return signal(I) }
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
- const bk = list()[k]
207
- return bk
209
+ return newItems[t.index]
208
210
  } else {
209
- const b = untrack(list)
210
- return list(b.toSpliced(k, 1, a[0])).at(k)
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, () => sI())
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(), )