@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.
- package/.github/workflows/publish.yml +12 -11
- package/{deno.json → deno.jsonc} +13 -8
- package/example/index.html +43 -0
- package/package.json +6 -2
- package/src/constants.ts +110 -52
- package/src/controlflow.ts +140 -91
- package/src/hyperscript.ts +85 -109
- package/src/mod.ts +3 -5
- package/src/reactive.ts +29 -17
- package/src/runtime.ts +128 -66
- package/test/hyperscript.js +7 -6
- package/test/reactive.js +22 -2
- package/test/types.ts +44 -0
- package/src/components.js +0 -20
- package/src/ui/accordion.ts +0 -8
- package/src/ui/button.ts +0 -9
- package/src/ui/canvas.ts +0 -8
- package/src/ui/date.ts +0 -8
- package/src/ui/dialog.ts +0 -12
- package/src/ui/filter.ts +0 -8
- package/src/ui/form.ts +0 -7
- package/src/ui/h.ts +0 -48
- package/src/ui/image.ts +0 -5
- package/src/ui/input.ts +0 -49
- package/src/ui/mod.ts +0 -12
- package/src/ui/multiselect.ts +0 -42
- package/src/ui/option.ts +0 -1
- package/src/ui/select.ts +0 -28
- package/src/ui/tab.ts +0 -7
- package/src/ui/table.ts +0 -9
- package/src/ui/upload.ts +0 -7
- package/www/assets/css/presets.css +0 -504
- package/www/assets/css/style.css +0 -90
- package/www/demo.html +0 -28
- package/www/index.html +0 -211
- package/www/playground.html +0 -184
- package/www/public/banner.svg +0 -6
- package/www/public/example/admin.html +0 -88
- package/www/public/example/counter.html +0 -24
- package/www/public/example/greeting.html +0 -25
- package/www/public/example/select.html +0 -27
- package/www/public/example/show.html +0 -18
- package/www/public/example/todo.html +0 -70
- package/www/public/fonts/fab.ttf +0 -0
- package/www/public/fonts/fab.woff2 +0 -0
- package/www/public/fonts/far.ttf +0 -0
- package/www/public/fonts/far.woff2 +0 -0
- package/www/public/fonts/fas.ttf +0 -0
- package/www/public/fonts/fas.woff2 +0 -0
- package/www/public/logo.svg +0 -16
- package/www/typedoc.json +0 -13
- package/www/ui.html +0 -49
- package/www/vite.config.js +0 -106
package/src/controlflow.ts
CHANGED
@@ -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
|
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
|
28
|
-
|
29
|
-
|
30
|
-
|
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>(
|
47
|
-
|
48
|
-
|
49
|
-
const
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
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
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
}
|
66
|
-
|
67
|
-
|
68
|
-
|
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
|
-
|
88
|
-
|
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 (
|
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 (
|
107
|
-
oldValue = items[j]
|
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
|
-
|
121
|
+
newValue = newItems[i]!
|
122
|
+
j = matcher.get(newValue)?.pop() ?? -1;
|
116
123
|
if (j >= 0) {
|
117
|
-
item = items[j
|
118
|
-
oldIndex = item
|
119
|
-
temp[i] = mapped[oldIndex]
|
120
|
-
item
|
121
|
-
item
|
122
|
-
matchedItems[j
|
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
|
-
//
|
127
|
-
for (
|
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
|
-
//
|
136
|
-
for (
|
137
|
-
item = items[j]
|
138
|
-
oldIndex = item
|
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?.(
|
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
|
-
//
|
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[]) {
|
package/src/hyperscript.ts
CHANGED
@@ -1,165 +1,141 @@
|
|
1
|
-
|
2
|
-
import {
|
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
|
9
|
-
export type
|
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
|
-
|
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,
|
30
|
+
export function hyperscript(r: Runtime, parseTag: TagParser = parseHtmlTag) {
|
68
31
|
|
69
|
-
function item
|
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 (
|
38
|
+
else if (t === 'object' && r.isChild(c))
|
75
39
|
r.insert(e, c, m)
|
76
|
-
else if (
|
77
|
-
|
78
|
-
else if (
|
79
|
-
while (
|
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
|
45
|
+
} else e.appendChild(r.text(c.toString()))
|
82
46
|
}
|
83
47
|
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
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:
|
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
|
68
|
+
props = ((second ?? {}) as P & {children:C})
|
95
69
|
} else {
|
96
70
|
children = (second as Child) || []
|
97
|
-
props = {} as
|
71
|
+
props = {} as P & {children:C}
|
98
72
|
}
|
99
73
|
|
100
74
|
let ret:View
|
101
75
|
|
102
|
-
|
103
|
-
|
104
|
-
const
|
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
|
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}
|
129
|
-
|
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
|
138
|
-
dynamicProperty(props2
|
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
|
143
|
-
item(e,children
|
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(
|
152
|
-
if (
|
153
|
-
else if (!isArray(
|
154
|
-
for (const i of
|
155
|
-
if (i
|
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
|
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,
|
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
|
9
|
-
import {
|
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
|
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
|
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
|
298
|
-
export function wrap<T>(s:S<
|
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 (
|
303
|
-
const b =
|
304
|
-
return
|
305
|
-
}
|
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 (
|
308
|
-
|
309
|
-
|
310
|
-
|
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 (
|
313
|
-
const i =
|
314
|
-
if (c==='number') return a.length ?
|
315
|
-
|
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
|
-
}
|
329
|
+
}
|
318
330
|
throw new Error('Cannot wrap signal')
|
319
331
|
}
|
320
332
|
|