@wrnrlr/prelude 0.1.8 → 0.2.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.
Files changed (53) hide show
  1. package/.github/workflows/publish.yml +12 -11
  2. package/{deno.json → deno.jsonc} +13 -8
  3. package/example/index.html +43 -0
  4. package/package.json +6 -2
  5. package/src/constants.ts +110 -52
  6. package/src/controlflow.ts +140 -91
  7. package/src/hyperscript.ts +85 -109
  8. package/src/mod.ts +3 -5
  9. package/src/reactive.ts +29 -17
  10. package/src/runtime.ts +128 -66
  11. package/test/hyperscript.js +7 -6
  12. package/test/reactive.js +22 -2
  13. package/test/types.ts +44 -0
  14. package/src/components.js +0 -20
  15. package/src/ui/accordion.ts +0 -8
  16. package/src/ui/button.ts +0 -9
  17. package/src/ui/canvas.ts +0 -8
  18. package/src/ui/date.ts +0 -8
  19. package/src/ui/dialog.ts +0 -12
  20. package/src/ui/filter.ts +0 -8
  21. package/src/ui/form.ts +0 -7
  22. package/src/ui/h.ts +0 -48
  23. package/src/ui/image.ts +0 -5
  24. package/src/ui/input.ts +0 -49
  25. package/src/ui/mod.ts +0 -12
  26. package/src/ui/multiselect.ts +0 -42
  27. package/src/ui/option.ts +0 -1
  28. package/src/ui/select.ts +0 -28
  29. package/src/ui/tab.ts +0 -7
  30. package/src/ui/table.ts +0 -9
  31. package/src/ui/upload.ts +0 -7
  32. package/www/assets/css/presets.css +0 -504
  33. package/www/assets/css/style.css +0 -90
  34. package/www/demo.html +0 -28
  35. package/www/index.html +0 -211
  36. package/www/playground.html +0 -184
  37. package/www/public/banner.svg +0 -6
  38. package/www/public/example/admin.html +0 -88
  39. package/www/public/example/counter.html +0 -24
  40. package/www/public/example/greeting.html +0 -25
  41. package/www/public/example/select.html +0 -27
  42. package/www/public/example/show.html +0 -18
  43. package/www/public/example/todo.html +0 -70
  44. package/www/public/fonts/fab.ttf +0 -0
  45. package/www/public/fonts/fab.woff2 +0 -0
  46. package/www/public/fonts/far.ttf +0 -0
  47. package/www/public/fonts/far.woff2 +0 -0
  48. package/www/public/fonts/fas.ttf +0 -0
  49. package/www/public/fonts/fas.woff2 +0 -0
  50. package/www/public/logo.svg +0 -16
  51. package/www/typedoc.json +0 -13
  52. package/www/ui.html +0 -49
  53. package/www/vite.config.js +0 -106
@@ -1,6 +1,6 @@
1
1
  // @ts-nocheck:
2
2
  import type { Child } from './hyperscript.ts'
3
- import {signal,untrack,batch,memo,root,type Signal} from './reactive.ts'
3
+ import {signal,untrack,batch,memo,root,onCleanup,type Signal, type Setter, type Mountable} from './reactive.ts'
4
4
 
5
5
  export type ShowProps<T> = {
6
6
  when: T,
@@ -12,7 +12,7 @@ export type ShowProps<T> = {
12
12
  Show children if `when` prop is true, otherwise show `fallback`.
13
13
  @group Components
14
14
  */
15
- export function Show<T>(props:ShowProps<T>) {
15
+ export function Show<T>(props:ShowProps) {
16
16
  const condition = memo(()=>props.when)
17
17
  return memo(()=>{
18
18
  const c = condition()
@@ -24,149 +24,198 @@ export function Show<T>(props:ShowProps<T>) {
24
24
  })
25
25
  }
26
26
 
27
- type ItemHolder = {
28
- index:number,
29
- indexSetter?:any,
30
- value:unknown,
31
- valueSetter:any,
32
- disposer: any
33
- }
34
-
35
- export type ListProps<T> = {
36
- when: T,
37
- each: any,
38
- children: Child | ((a:()=>T)=>void),
39
- fallback: unknown
27
+ export type ListProps<T, U extends Mountable, F = Getter | Signal> = {
28
+ each: F<T[]>,
29
+ children: (item: F<T>, index: F<number>) => U,
30
+ fallback?: Mountable
40
31
  }
41
32
 
42
33
  /**
43
34
  List
44
35
  @group Components
45
36
  */
46
- export function List<T>(props:ListProps<T>) {
47
- const fallback = "fallback" in props && { fallback: () => props.fallback }
48
- const list = props.each
49
- const cb:any = (props.children as any)?.call ? props.children : (v:any)=>v
50
- let items:ItemHolder[] = [],
51
- item: undefined|ItemHolder,
52
- // unusedItems,
53
- i: undefined|number,
54
- newValue: undefined|number,
55
- mapped: number[],
37
+ export function List<T>(
38
+ props: ListProps<T>
39
+ ) {
40
+ const fallback = "fallback" in props && { fallback: () => props.fallback };
41
+ return memo(listArray<T>(props.each, props.children, fallback || undefined))
42
+ }
43
+
44
+ type ItemHolder<T> = {
45
+ index: number,
46
+ indexSetter?: Getter<number>,
47
+ value: T,
48
+ valueSetter?: Setter<T>,
49
+ disposer: ()=>void
50
+ }
51
+
52
+ function listArray<T, U extends Mountable>(
53
+ list: Getter<T[]>,
54
+ mapFn: (v: Getter<T>, i: Getter<number>) => U,
55
+ options: { fallback?: Mountable }
56
+ ): () => U[]
57
+ function listArray<T, U extends Mountable>(
58
+ list: Signal<T[]>,
59
+ mapFn: (v: Signal<T>, i: Getter<number>) => U,
60
+ options: { fallback?: Mountable }
61
+ ): () => U[]
62
+ function listArray<T, U extends Mountable>(
63
+ list: Getter<T[]> | Signal<T[]>,
64
+ mapFn: (v: Signal<T>, i: Getter<number>) => U,
65
+ options: { fallback?: Mountable } = {}
66
+ ): () => U[] {
67
+ const items: ListItem<T>[] = [];
68
+ let mapped: U[] = [],
69
+ unusedItems: number,
70
+ i: number,
71
+ j: number,
72
+ item: ListItem<T>,
73
+ oldValue: T,
56
74
  oldIndex: number,
57
- oldValue: unknown
58
- const indexes = cb.length > 1 ? [] : null;
59
- function newValueGetter(_:unknown) { return newValue }
60
- function changeBoth() {
61
- item!.index = i!
62
- item!.indexSetter?.(i)
63
- item!.value = newValue!
64
- item!.valueSetter?.(newValueGetter)
65
- }
66
- function mapperWithIndexes(disposer:any) {
67
- const V = newValue, I = i, Is = signal(I), Vs = signal(V)
68
- items.push({value: newValue, index: I!, disposer, indexSetter: Is, valueSetter: Vs})
69
- return cb(
70
- (...a:any[]) => a.length ?
71
- untrack(()=>list((list:any)=>list.toSpliced(I,1,a[0])))
72
- : Vs(),
73
- ()=>Is())
74
- }
75
- function mapperWithoutIndexes(disposer:any) {
76
- const V = newValue, I = i, Vs = signal(V)
77
- items.push({value: V, index: i!, disposer, valueSetter: Vs})
78
- return cb((...a:unknown[]) => a.length ?
79
- untrack(()=>list((list:any)=>list.toSpliced(I,1,a[0])))
80
- : Vs())
81
- }
82
- const mapper = indexes ? mapperWithIndexes : mapperWithoutIndexes
83
- return memo(() => {
84
- const newItems = list.call ? list() : list
85
- // (newItems)[$TRACK]; // top level tracking
75
+ newValue: T,
76
+ fallback: U[] | undefined,
77
+ fallbackDisposer: undefined | (() => void)
78
+
79
+ onCleanup(() => {
80
+ fallbackDisposer?.()
81
+ fallbackDisposer = undefined
82
+ disposeList(items)
83
+ })
84
+
85
+ return () => {
86
+ const newItems = typeof list==='function' ? list() || [] : list;
86
87
  return untrack(() => {
87
- const temp = new Array(newItems.length) // new mapped array
88
- let unusedItems = items.length
88
+ if (newItems.length > 0 && fallbackDisposer) {
89
+ fallbackDisposer();
90
+ fallbackDisposer = undefined;
91
+ fallback = undefined;
92
+ }
93
+
94
+ const temp: U[] = new Array(newItems.length); // new mapped array
95
+ unusedItems = items.length;
89
96
 
90
97
  // 1) no change when values & indexes match
91
- for (let j = unusedItems - 1; j >= 0; --j) {
92
- item = items[j]
98
+ for (j = unusedItems - 1; j >= 0; --j) {
99
+ item = items[j]!;
93
100
  oldIndex = item.index
94
101
  if (oldIndex < newItems.length && newItems[oldIndex] === item.value) {
95
- temp[oldIndex] = mapped[oldIndex]
102
+ temp[oldIndex] = mapped[oldIndex]!
96
103
  if (--unusedItems !== j) {
97
- items[j] = items[unusedItems]
104
+ items[j] = items[unusedItems]!
98
105
  items[unusedItems] = item
99
106
  }
100
107
  }
101
108
  }
102
109
 
103
110
  // #2 prepare values matcher
104
- const matcher = new Map()
111
+ const matcher = new Map<T, number[]>()
105
112
  const matchedItems = new Uint8Array(unusedItems)
106
- for (let j = unusedItems - 1; j >= 0; --j) {
107
- oldValue = items[j].value
113
+ for (j = unusedItems - 1; j >= 0; --j) {
114
+ oldValue = items[j]!.value
108
115
  matcher.get(oldValue)?.push(j) ?? matcher.set(oldValue, [j])
109
116
  }
110
117
 
111
118
  // 2) change indexes when values match
112
119
  for (i = 0; i < newItems.length; ++i) {
113
120
  if (i in temp) continue
114
- newValue = newItems[i]
115
- const j = matcher.get(newValue)?.pop() ?? -1
121
+ newValue = newItems[i]!
122
+ j = matcher.get(newValue)?.pop() ?? -1;
116
123
  if (j >= 0) {
117
- item = items[j as number]
118
- oldIndex = item!.index
119
- temp[i] = mapped[oldIndex]
120
- item!.index = i
121
- item!.indexSetter?.(i)
122
- matchedItems[j as number] = 1
124
+ item = items[j]!
125
+ oldIndex = item.index
126
+ temp[i] = mapped[oldIndex]!
127
+ item.index = i
128
+ item.indexSetter?.(i)
129
+ matchedItems[j] = 1
123
130
  }
124
131
  }
125
132
 
126
- // 3) reduce unusedItems for matched items
127
- for (let j = matchedItems.length - 1; j >= 0; --j) {
133
+ // #2 reduce unusedItems for matched items
134
+ for (j = matchedItems.length - 1; j >= 0; --j) {
128
135
  if (matchedItems[j] && --unusedItems !== j) {
129
- item = items[j]
130
- items[j] = items[unusedItems]
136
+ item = items[j]!
137
+ items[j] = items[unusedItems]!
131
138
  items[unusedItems] = item
132
139
  }
133
140
  }
134
141
 
135
- // 4) change values when indexes match
136
- for (let j = unusedItems - 1; j >= 0; --j) {
137
- item = items[j];
138
- oldIndex = item!.index;
142
+ // 3) change values when indexes match
143
+ for (j = unusedItems - 1; j >= 0; --j) {
144
+ item = items[j]!
145
+ oldIndex = item.index;
139
146
  if (!(oldIndex in temp) && oldIndex < newItems.length) {
140
- temp[oldIndex] = mapped[oldIndex]
141
- newValue = newItems[oldIndex]
147
+ temp[oldIndex] = mapped[oldIndex]!
148
+ newValue = newItems[oldIndex]!
142
149
  item.value = newValue
143
- item.valueSetter?.(item.valueSetter)
150
+ item.valueSetter?.(newValue)
144
151
  if (--unusedItems !== j) {
145
- items[j] = items[unusedItems]
152
+ items[j] = items[unusedItems]!
146
153
  items[unusedItems] = item
147
154
  }
148
155
  }
149
156
  }
150
157
 
151
- // 5) change value & index when none matched and create new if no unused items left
158
+ // 4) change value & index when none matched
159
+ // 5) create new if no unused items left
152
160
  for (i = 0; i < newItems.length; ++i) {
153
- if (i in temp) continue
154
- newValue = newItems[i]
161
+ if (i in temp) continue;
162
+ newValue = newItems[i]!;
155
163
  if (unusedItems > 0) {
156
- item = items[--unusedItems]
157
- temp[i] = mapped[item.index]
164
+ item = items[--unusedItems]!;
165
+ temp[i] = mapped[item.index]!;
158
166
  batch(changeBoth);
159
167
  } else {
160
- temp[i] = root(mapper)
168
+ temp[i] = root(mapper);
161
169
  }
162
170
  }
163
171
 
164
172
  // 6) delete any old unused items left
165
- disposeList(items.splice(0, unusedItems))
173
+ disposeList(items.splice(0, unusedItems));
166
174
 
175
+ if (newItems.length === 0 && options.fallback) {
176
+ if (!fallbackDisposer) {
177
+ fallback = [
178
+ root(d => {
179
+ fallbackDisposer = d;
180
+ return options.fallback!();
181
+ }),
182
+ ];
183
+ }
184
+ return fallback!;
185
+ }
167
186
  return (mapped = temp);
168
187
  })
169
- })
188
+ }
189
+ // const indexes = cb.length > 1 ? [] : null;
190
+ function newValueGetter(_:unknown) { return newValue }
191
+ function changeBoth() {
192
+ item!.index = i!
193
+ item!.indexSetter?.(i)
194
+ item!.value = newValue!
195
+ item!.valueSetter?.(newValueGetter)
196
+ }
197
+ function mapper(disposer: ()=>void) {
198
+ const V = newValue
199
+ const I = i
200
+ const t = {value: newValue, index: I, disposer}
201
+ items.push(t)
202
+ const sI = () => { t.indexSetter = I; return signal(I) }
203
+ let sV = (...a) => {
204
+ const k = I
205
+ sV = (...a) => {
206
+ if (a.length===0) {
207
+ const bk = list()[k]
208
+ return bk
209
+ } else {
210
+ const b = untrack(list)
211
+ return list(b.toSpliced(k, 1, a[0])).at(k)
212
+ }
213
+ }
214
+ t.valueSetter = sV
215
+ return sV(...a)
216
+ }
217
+ return mapFn(sV, () => sI())
218
+ }
170
219
  }
171
220
 
172
221
  function disposeList(list:any[]) {
@@ -1,165 +1,141 @@
1
- // @ts-nocheck:
2
- import {untrack} from './reactive.ts'
3
- import type {Properties,BooleanAttributes,DelegatedEvents,DOMElements, Mountable} from './constants.ts'
4
- import type {Runtime,$RUNTIME} from './runtime.ts'
1
+ import type {DOMElements} from './constants.ts'
2
+ import type {Runtime} from './runtime.ts'
5
3
 
6
4
  const ELEMENT: unique symbol = Symbol(), {isArray} = Array
7
5
 
8
- export type PropsKeys = typeof Properties extends Set<infer K> ? K : never;
9
- export type BooleanProps = typeof BooleanAttributes extends Set<infer K> ? K : never;
10
- export type HandlerProps = typeof DelegatedEvents extends Set<infer K> ? K : never;
11
- export type ChildProps = {children?:any[]}
12
-
13
- export type ElementProps = BooleanProps & HandlerProps & ChildProps & {class: string}
14
- export type ComponentProps = { children?:Child }
15
-
16
- /**
17
- * @group Hyperscript
18
- */
19
- export type Props = {}
20
-
21
- type AllProps = ElementProps | ComponentProps
22
-
23
- type EmptyProps = Record<string,never>
24
-
25
- /**
26
- @group Hyperscript
27
- */
28
- export type HyperScript = {
29
- // (children:Child[]): View
30
-
31
- (element:Tag, props:ElementProps, children:Child): View
32
- (element:Tag, props:ElementProps): View
33
- (element:Tag, children:Child): View
34
- (element:Tag): View
35
-
36
- <T,K>(element:Component<T & {children:K}>, props:T, children:K): View
37
- <T>(element:Component<T>, props:T): View
38
- <K>(element:Component<{children:K}>, children:K): View
39
- (element:Component<undefined>): View
40
- }
41
-
42
- /**
43
- * @group Hyperscript
44
- */
45
- export type Child = { call:any } | Child[] | string | number | symbol | bigint | boolean | Record<string,unknown> | {():Child, [ELEMENT]:boolean}
46
- /**
47
- * @group Hyperscript
48
- */
49
- export type View = {():void, [ELEMENT]?:boolean}
50
- /**
51
- * @group Hyperscript
52
- */
53
- export type Component<T> = {(props:T): Mountable, [ELEMENT]?: Runtime}
54
- /**
55
- * @group Hyperscript
56
- */
6
+ export type Mountable = View | HTMLElement | string | number | bigint | symbol | boolean | Date | Mountable[];
7
+ export type Component<T> = ((props:T) => Mountable) | ((props:T) => Mountable[])
57
8
  export type Tag = typeof DOMElements extends Set<infer K> ? K : never;
9
+ export type Child = { ():Child } | Element | Child[] | string | number | symbol | bigint | boolean | Date | Record<string,unknown> | {():Child, [ELEMENT]:boolean}
10
+ export type View = {():void, [ELEMENT]?:boolean}
58
11
 
59
- const Fragment:Component<Props> = <T extends Props>(props:T):Mountable => (props as any).children
12
+ // export type PropsKeys = typeof Properties extends Set<infer K> ? K : never;
13
+ // export type BooleanProps = typeof BooleanAttributes ext ends Set<infer K> ? K : never;
14
+ // export type HandlerProps = typeof DelegatedEvents extends Set<infer K> ? K : never;
15
+ // export type ChildProps = {children?:any[]}
16
+ // export type ElementProps = BooleanProps & HandlerProps & ChildProps & {class: string}
17
+ // export type ComponentProps = { children?:Child }
18
+ // export type Props = Record<string, any>
19
+ // type AllProps = ElementProps | ComponentProps
20
+ // type EmptyProps = Record<string,never>
21
+ // const Fragment:Component<Props> = <T extends Props>(props:T):Mountable => (props as any).children
60
22
 
61
- /**
23
+ export type TagParser = <T extends string>(s:T) => {name:string,id?:string,classes:string[],namespace?:string}
62
24
 
25
+ /**
63
26
  @param r
64
27
  @param patch
65
28
  @group Hyperscript
66
29
  */
67
- export function hyperscript(r:Runtime, patch?:any):HyperScript {
30
+ export function hyperscript(r: Runtime, parseTag: TagParser = parseHtmlTag) {
68
31
 
69
- function item<T extends Props>(e: Element, c: Child, m?: null) {
32
+ function item(e: Element, c: Child, m?: null): void {
70
33
  if (c===null) return
34
+ const t = typeof c
71
35
  if (isArray(c))
72
36
  for (const child of c)
73
37
  item(e, child, m)
74
- else if (typeof c === 'object' && r.isChild(c))
38
+ else if (t === 'object' && r.isChild(c))
75
39
  r.insert(e, c, m)
76
- else if (typeof c==='string')
77
- (e as Element).appendChild(r.text(c))
78
- else if ((c as any).call) {
79
- while ((c as any)[ELEMENT]?.call) c = (c as any)()
40
+ else if (t === 'string')
41
+ e.appendChild(r.text((c as string)))
42
+ else if (t === 'function') {
43
+ // while (c[ELEMENT]?.call) c = (c as any)()
80
44
  r.insert(e, c, m)
81
- } else (e as Element).appendChild(r.text(c.toString()))
45
+ } else e.appendChild(r.text(c.toString()))
82
46
  }
83
47
 
84
- return function h<T,K=unknown>(
85
- element:Component<T&{children:K}>|Tag, // , Child[]
86
- second?:T|K|Child,
87
- third?:K|Child
48
+ function h(first: Tag): View
49
+ function h<P>(first: Tag, second: P): View
50
+ function h<C extends Child>(first: Tag, second: C): View
51
+ function h<C extends Child, P>(first: Tag, second: P, third: C): View
52
+
53
+ function h(first: Component<Record<string,never>>): View
54
+ function h<P extends Record<string, unknown>>(first: Component<P>, second: P): View
55
+ function h<C>(first: Component<{children:C}>, second: C): View
56
+ function h<P extends Record<string, unknown>, C>(first: Component<P & {children:C}>, second:P, third:C): View
57
+
58
+ function h<P extends Record<string,unknown>, C = never>(
59
+ first: Tag | Component<P>,
60
+ second?: P | C,
61
+ third?: C
88
62
  ): View {
89
- let props: T
63
+ let props: P
90
64
  let children: Child
91
65
 
92
66
  if (typeof second === 'object' && !isArray(second)) {
93
67
  children = (third as Child) || [];
94
- props = ((second ?? {}) as T&{children:K})
68
+ props = ((second ?? {}) as P & {children:C})
95
69
  } else {
96
70
  children = (second as Child) || []
97
- props = {} as T&{children:K}
71
+ props = {} as P & {children:C}
98
72
  }
99
73
 
100
74
  let ret:View
101
75
 
102
- if ((element as Component<T>).call) {
103
- let e:any
104
- const d = Object.getOwnPropertyDescriptors(props)
105
- if (children) (props as any).children = children
106
- for (const k in d) {
107
- if (isArray(d[k].value)) {
108
- const list:any[] = d[k].value;
109
- (props as any)[k] = () => {
110
- for (let i = 0; i < list.length; i++)
111
- while (list[i][ELEMENT]) list[i] = list[i]()
112
- return list
113
- }
114
- dynamicProperty(props as any, k)
115
- } else if (d[k].value?.call && !d[k].value.length) { // A function with zero arguments
116
- dynamicProperty(props as any, k)
117
- }
118
- }
119
- e = untrack(()=>(element as Component<T&{children:K}>)(props as T&{children:K}))
120
- ret = () => e
121
- } else {
122
- const tag = parseTag(element as Tag)
76
+ const t1 = typeof first
77
+ if (t1 === 'string') {
78
+ const tag = parseTag(first as Tag)
123
79
  const multiExpression = detectMultiExpression(children) ? null : undefined
124
80
  const e = r.element(tag.name)
125
- const props2 = props as T
81
+ const props2 = props as P & {class?: string|(()=>string)}
126
82
  if (tag.id) e.setAttribute('id',tag.id)
127
83
  if (tag.classes?.length) {
128
- const cd = Object.getOwnPropertyDescriptor(props2,'class') ?? ({value:'',writable:true,enumerable:true} as any);
129
- (props2 as any).class = (cd.value?.call) ?
84
+ const cd = Object.getOwnPropertyDescriptor(props2,'class') ?? ({value:'',writable:true,enumerable:true});
85
+ props2.class = (cd.value?.call) ?
130
86
  () => [...tag.classes,...(cd.value()??'').split(' ')].filter(c=>c).join(' ') :
131
87
  [...tag.classes,...(cd.value??'').split(' ')].filter(c=>c).join(' ')
132
88
  }
133
- if (patch) patch(props2)
89
+ // if (patch) patch(props2)
134
90
  let dynamic = false
135
91
  const d = Object.getOwnPropertyDescriptors(props2)
136
92
  for (const k in d) {
137
- if (k !== 'ref' && !k.startsWith('on') && d[k].value?.call) {
138
- dynamicProperty(props2 as any, k)
93
+ if (k !== 'ref' && !k.startsWith('on') && typeof d[k].value === 'function') {
94
+ dynamicProperty(props2, k)
139
95
  dynamic = true
140
96
  } else if (d[k].get) dynamic = true
141
97
  }
142
- (dynamic ? r.spread : r.assign) (e, props2, !!(children as Child[])?.length)
143
- item(e,children as any,multiExpression)
98
+ (dynamic ? r.spread : r.assign) (e, props2, !!(children as any)?.length)
99
+ item(e, children, multiExpression)
100
+ ret = () => e
101
+ } else {
102
+ const d = Object.getOwnPropertyDescriptors(props)
103
+ if (children) (props as any).children = children
104
+ for (const k in d) {
105
+ if (isArray(d[k].value)) {
106
+ const list = d[k].value;
107
+ (props as any)[k] = () => {
108
+ for (let i = 0; i < list.length; i++)
109
+ while (list[i][ELEMENT]) list[i] = list[i]()
110
+ return list
111
+ }
112
+ dynamicProperty(props, k)
113
+ } else if (typeof d[k].value==='function' && !d[k].value.length) { // A function with zero arguments
114
+ dynamicProperty(props, k)
115
+ }
116
+ }
117
+ const e = r.component(() => (first as Component<P>)(props))
144
118
  ret = () => e
145
119
  }
146
120
  ret[ELEMENT] = true
147
121
  return ret
148
122
  }
123
+
124
+ return h
149
125
  }
150
126
 
151
- function detectMultiExpression(list:any):boolean {
152
- if (list.call) return true
153
- else if (!isArray(list)) return false
154
- for (const i of list) {
155
- if (i.call) return true
127
+ function detectMultiExpression(children: Child): boolean {
128
+ if (typeof children === 'function') return true
129
+ else if (!isArray(children)) return false
130
+ for (const i of children) {
131
+ if (typeof i === 'function') return true
156
132
  else if (isArray(i)) return detectMultiExpression(i)
157
133
  }
158
134
  return false
159
135
  }
160
136
 
161
137
  // ^([a-zA-Z]\w*)?(#[a-zA-Z][-\w]*)?(.[a-zA-Z][-\w]*)*
162
- function parseTag(s:string):{name:string,id?:string,classes:string[]} {
138
+ export function parseHtmlTag(s:Tag) {
163
139
  const classes:string[] = [];
164
140
  let id:string|undefined = undefined, i:number
165
141
 
@@ -185,10 +161,10 @@ function parseTag(s:string):{name:string,id?:string,classes:string[]} {
185
161
  return {name:name as string,classes,id:id}
186
162
  }
187
163
 
188
- function dynamicProperty(props:Record<string,any>, key:string):Record<string,any> {
189
- const src = props[key]
190
- Object.defineProperty(props, key, {get() {return src()},enumerable:true})
191
- return props
164
+ function dynamicProperty<T>(props: Record<string, unknown>, key: string) {
165
+ const src = props[key] as ()=>unknown
166
+ Object.defineProperty(props, key, {get() {return src()}, enumerable:true})
167
+ // return props
192
168
  }
193
169
 
194
170
  // function tw(rules) {
package/src/mod.ts CHANGED
@@ -5,12 +5,10 @@ export {nbsp} from './constants.ts'
5
5
  export {Show,List} from './controlflow.ts'
6
6
  export {runtime, type Runtime} from './runtime.ts'
7
7
  import {runtime, type Runtime} from './runtime.ts'
8
- export {hyperscript,type HyperScript,type Child,type Props,type Tag,type View,type Component} from './hyperscript.ts'
9
- import {type HyperScript, hyperscript} from './hyperscript.ts'
8
+ export {hyperscript,type Child,type Tag,type View,type Component} from './hyperscript.ts'
9
+ import {hyperscript, parseHtmlTag} from './hyperscript.ts'
10
10
  export {Router} from './router.js'
11
11
  export {resource,makeAbortable,abortable} from './resource.js'
12
- // export {Input,Table} from './components.js'
13
- // export * from './canvas.js'
14
12
 
15
13
  const r:Runtime = /*#__PURE__*/ (typeof window === 'object') ? runtime(window as any) : undefined as any
16
14
 
@@ -29,7 +27,7 @@ h(Input,{onInput:e => {}})
29
27
  ```
30
28
  @group Hyperscript
31
29
  */
32
- const h:HyperScript = /*#__PURE__*/ r ? hyperscript(r) : undefined
30
+ const h = /*#__PURE__*/ hyperscript(r, parseHtmlTag)
33
31
 
34
32
  const render = /*#__PURE__*/ r?.render
35
33
 
package/src/reactive.ts CHANGED
@@ -65,7 +65,9 @@ const SYMBOL_ERRORS = Symbol()
65
65
  // // Value node
66
66
  // class VNode {}
67
67
 
68
- export class Signal<T = unknown> {
68
+ export type Signal = Getter<T> & Setter<T>
69
+
70
+ class Signal<T = unknown> {
69
71
  public parent: Computation<T> | undefined
70
72
  public value: T
71
73
  private readonly equals: EqualsFn<T>
@@ -209,7 +211,7 @@ n(3)
209
211
  */
210
212
  export function signal<T>(value:T, options?:Options<T>): Getter<T> & Setter<T> {
211
213
  const s = new Signal<T>(value,options)
212
- const f = Object.assign((...args:T[]) => args.length ? s.set(args[0]) as T : s.get() as T, s)
214
+ const f = Object.assign((...args:T[]) => args.length ? s.set(args[(0)]) as T : s.get() as T, s)
213
215
  return f as unknown as Getter<T> & Setter<T>
214
216
  }
215
217
 
@@ -294,27 +296,37 @@ export type S<T> = Getter<T> | Setter<T>
294
296
  @param s Signal
295
297
  @param k
296
298
  */
297
- export function wrap<T>(s:S<Array<T>>, k:number|(()=>number)): S<T>
298
- export function wrap<T>(s:S<Record<string,T>>, k:string|(()=>string)): S<T>
299
+ export function wrap<T>(s:S<Array<T>>, k:number): S<T>
300
+ export function wrap<T>(s:S<Array<T>>, k:()=>number): S<T>
301
+ export function wrap<T>(s:S<Record<string,T>>, k:string): S<T>
302
+ export function wrap<T>(s:S<Record<string,T>>, k:()=>string): S<T>
299
303
  export function wrap<T>(s:S<Array<T>>|S<Record<string,T>>, k:number|string|(()=>number)|(()=>string)): S<T> {
300
304
  const t = typeof k
301
305
  if (t === 'number') {
302
- return ((...a:T[]) => {
303
- const b = (s as Getter<Array<T>>)()
304
- return (a.length) ? (s as Setter<Array<T>>)((b as any).toSpliced(k as number, 1, a[0])).at(k as number) : b.at(k as number)
305
- }) as S<T>
306
+ return (...a) => {
307
+ const b = s()
308
+ return a.length ? s(b.toSpliced(k, 1, a[0])).at(k) : b.at(k)
309
+ }
306
310
  } else if (t === 'string') {
307
- return ((...a:T[]) => {
308
- const b = (s as Getter<Record<string,T>>)()
309
- return (a.length) ? (s as Setter<Record<string,T>>)({...b, [k as string]:a[0]})[k as string] : b[k as string]
310
- }) as S<T>
311
+ return (...a) => {
312
+ if (a.length) {
313
+ const b = s()
314
+ return s({...b, [k]:a[0]})[k]
315
+ } else {
316
+ return s()[k]
317
+ }
318
+ }
311
319
  } else if (t === 'function')
312
- return ((...a:T[]) => {
313
- const i = (k as ()=>string|number)(), c = typeof i
314
- if (c==='number') return a.length ? (s as Setter<Array<T>>)((old:any) => old.toSpliced(i, 1, a[0]))[i as number] : (s as Getter<Array<T>>)()[i as number]
315
- else if (c === 'string') return a.length ? (s as Setter<Record<string,T>>)((b) => ({...b, [i]:a[0]}))[i as string] : (s as Getter<Record<string,T>>)()[i]
320
+ return (...a) => {
321
+ const i = k(), c = typeof i
322
+ if (c==='number') return a.length ?
323
+ s(old => old.toSpliced(i, 1, a[0]))[i] :
324
+ s()[i]
325
+ else if (c === 'string') return a.length ?
326
+ s(b => ({...b, [i]:a[0]}))[i] :
327
+ s()[i]
316
328
  throw new Error('Cannot wrap signal')
317
- }) as S<T>
329
+ }
318
330
  throw new Error('Cannot wrap signal')
319
331
  }
320
332