@wrnrlr/prelude 0.2.16 → 0.2.18
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 +2 -2
- package/example/context.html +29 -0
- package/example/h.html +24 -0
- package/example/index.html +17 -4
- package/example/reset.css +505 -0
- package/example/style.css +27 -0
- package/example/table.html +25 -0
- package/example/vite.config.js +17 -0
- package/example/widget.html +112 -0
- package/package.json +1 -1
- package/readme.md +5 -1
- package/src/canvas.js +1 -1
- package/src/form.js +80 -7
- package/src/hyperscript.ts +184 -140
- package/src/mod.ts +2 -1
- package/src/reactive.ts +19 -18
- package/src/runtime.ts +3 -17
- package/test/hyperscript.js +4 -7
- package/src/select.js +0 -1
- /package/{src/osm.js → example/atlas.html} +0 -0
@@ -0,0 +1,112 @@
|
|
1
|
+
<!DOCTYPE html>
|
2
|
+
<head>
|
3
|
+
<title>Widget</title>
|
4
|
+
<script type="importmap">{"imports": {"prelude": "../src/mod.ts"}}</script>
|
5
|
+
<link rel="stylesheet" href="./style.css"/>
|
6
|
+
</head>
|
7
|
+
<style>
|
8
|
+
script,script[type="module"] {
|
9
|
+
display: block;
|
10
|
+
}
|
11
|
+
</style>
|
12
|
+
|
13
|
+
<a href="/index.html">Home</a>
|
14
|
+
|
15
|
+
<h1>Widgets</h1>
|
16
|
+
<ul>
|
17
|
+
<li><a href="#dialog">Dialog</a></li>
|
18
|
+
<li><a href="#input">Input</a></li>
|
19
|
+
<li><a href="#checkbox">Checkbox</a></li>
|
20
|
+
<li><a href="#select">Select</a></li>
|
21
|
+
</ul>
|
22
|
+
|
23
|
+
<h2 id="dialog">Dialog</h2>
|
24
|
+
<div class="example" id="dialog_example"></div>
|
25
|
+
<details>
|
26
|
+
<summary>Code</summary>
|
27
|
+
<script type="module">
|
28
|
+
import { h, signal, render, Dialog, useDialog } from '../src/mod.ts'
|
29
|
+
|
30
|
+
function HelloDialog(props) {
|
31
|
+
let dialog
|
32
|
+
return h('', {ref(e){dialog=e}}, [
|
33
|
+
'Hello',
|
34
|
+
h('button', {onClick:_=>dialog.close()}, 'Close')
|
35
|
+
])
|
36
|
+
}
|
37
|
+
|
38
|
+
function MenuDialog(props) {
|
39
|
+
let dialog
|
40
|
+
return h('', {ref(e){dialog=e}}, [
|
41
|
+
'Hello',
|
42
|
+
h('button', {onClick:_=>dialog.close()}, 'Close')
|
43
|
+
])
|
44
|
+
}
|
45
|
+
|
46
|
+
function DialogExample() {
|
47
|
+
const showModal = signal(false)
|
48
|
+
const showDialog = signal(false)
|
49
|
+
return h('', {style:'display: flex; gap: 0.25rem'}, [
|
50
|
+
h('button', {onClick:e=>showModal(true)}, 'Modal'),
|
51
|
+
h(Dialog, {show:()=>showModal, modal: true}, h(HelloDialog)),
|
52
|
+
h('button', {onClick:e=>showDialog(true)}, 'Dialog'),
|
53
|
+
h(Dialog, {show:()=>showDialog}, h(MenuDialog)),
|
54
|
+
])
|
55
|
+
}
|
56
|
+
|
57
|
+
render(DialogExample, document.getElementById('dialog_example'))
|
58
|
+
</script>
|
59
|
+
</details>
|
60
|
+
|
61
|
+
<h2 id="input">Input</h2>
|
62
|
+
<div class="example" id="input_example"></div>
|
63
|
+
|
64
|
+
<h2>Input autosize</h2>
|
65
|
+
<div class="example" id="input_autosize"></div>
|
66
|
+
<script type="module">
|
67
|
+
import { h, signal, render, Input } from '../src/mod.ts'
|
68
|
+
|
69
|
+
function InputExample() {
|
70
|
+
const name = signal('Bob')
|
71
|
+
return h(Input, {value:()=>name})
|
72
|
+
}
|
73
|
+
|
74
|
+
render(InputExample, document.getElementById('input_autosize'))
|
75
|
+
</script>
|
76
|
+
|
77
|
+
<h2 id="checkbox">Checkbox</h2>
|
78
|
+
<div class="example" id="checkbox_example"></div>
|
79
|
+
<script type="module">
|
80
|
+
import { h, signal, render, Checkbox } from "../src/mod.ts"
|
81
|
+
|
82
|
+
function CheckboxExample() {
|
83
|
+
const valid = signal(false)
|
84
|
+
return h(Checkbox, {value:()=>valid})
|
85
|
+
}
|
86
|
+
|
87
|
+
render(CheckboxExample, document.getElementById('checkbox_example'))
|
88
|
+
</script>
|
89
|
+
|
90
|
+
<h2 id="select">Select</h2>
|
91
|
+
<div class="example " id="select_car"></div>
|
92
|
+
<script type="module">
|
93
|
+
import { h, signal, memo, render, Select } from "../src/mod.ts"
|
94
|
+
|
95
|
+
function SelectCar() {
|
96
|
+
const cars = { 'Audi': ['A1','A6'], 'BMW': ['B5', 'BX'] }
|
97
|
+
const companies = Object.keys(cars)
|
98
|
+
const company = signal()
|
99
|
+
const model = signal('')
|
100
|
+
const models = memo(() => cars[company()] || null)
|
101
|
+
return [
|
102
|
+
'', h(Select, {options:companies, value:()=>company}),
|
103
|
+
'', h(Select, {options:models, value:()=>model})
|
104
|
+
]
|
105
|
+
}
|
106
|
+
|
107
|
+
render(SelectCar, document.getElementById('select_car'))
|
108
|
+
</script>
|
109
|
+
|
110
|
+
<style>
|
111
|
+
|
112
|
+
</style>
|
package/package.json
CHANGED
package/readme.md
CHANGED
@@ -157,7 +157,7 @@ const useCounter = () => useContext(CounterCtx)
|
|
157
157
|
function CounterProvider(props) {
|
158
158
|
const count = signal(0)
|
159
159
|
const increment = () => count(i=>i+1)
|
160
|
-
return h(CounterCtx
|
160
|
+
return h(CounterCtx, {value:[count,increment]}, props.children)
|
161
161
|
}
|
162
162
|
|
163
163
|
function Counter() {
|
@@ -227,3 +227,7 @@ deno task test
|
|
227
227
|
* [Homepage](https://wrnrlr.github.io/prelude)
|
228
228
|
* [NPM](https://www.npmjs.com/package/@wrnrlr/prelude)
|
229
229
|
* [JSR](https://jsr.io/@wrnrlr/prelude)
|
230
|
+
|
231
|
+
## More Links
|
232
|
+
|
233
|
+
* [Fine-grained reactivity - A deep dive into SolidJS](https://github.com/everweij/solidjs-fine-grained-reactivity)
|
package/src/canvas.js
CHANGED
package/src/form.js
CHANGED
@@ -1,20 +1,93 @@
|
|
1
|
-
import {signal, context, useContext} from './reactive.ts'
|
1
|
+
import { signal, memo, effect, context, useContext, renderEffect } from './reactive.ts'
|
2
|
+
import { Show } from './show.ts'
|
3
|
+
import { List } from './list.ts'
|
4
|
+
import { h } from './hyperscript.ts'
|
5
|
+
import { nbsp } from './constants.ts'
|
2
6
|
|
3
7
|
const Ctx = context()
|
4
8
|
const useForm = () => useContext(Ctx)
|
5
9
|
|
6
|
-
function Form(props) {
|
10
|
+
export function Form(props) {
|
7
11
|
return h('form', h(Ctx.Provider, {value:[props.value]}, props.children))
|
8
12
|
}
|
9
13
|
|
10
|
-
function Input(props) {
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
14
|
+
export function Input(props) {
|
15
|
+
props.onInput = ((e) => props.value((e?.target)?.value))
|
16
|
+
props.autocomplete = props.autocomplete || 'off'
|
17
|
+
// const onInput = ((e) => props.value((e?.target)?.value))
|
18
|
+
if (props.autosize) props.ref = (r) => props.autosize(r, props)
|
15
19
|
return h('input', props)
|
16
20
|
}
|
17
21
|
|
22
|
+
export function autosize(r, props) {
|
23
|
+
const style = globalThis.getComputedStyle(r)
|
24
|
+
renderEffect(() => {
|
25
|
+
const font = style.getPropertyValue('font')
|
26
|
+
const metrics = fontMetrics(font, props.value().toString())
|
27
|
+
r.style.width = metrics.width + 'px'
|
28
|
+
})
|
29
|
+
}
|
30
|
+
|
31
|
+
let _fontMetricsCtx
|
32
|
+
|
33
|
+
function fontMetrics(font, text) {
|
34
|
+
if (!_fontMetricsCtx) _fontMetricsCtx = document.createElement('canvas').getContext('2d')
|
35
|
+
_fontMetricsCtx.font = font;
|
36
|
+
return _fontMetricsCtx.measureText(text)
|
37
|
+
}
|
38
|
+
|
39
|
+
export function Checkbox(props) {
|
40
|
+
return h('input',{
|
41
|
+
...props,
|
42
|
+
type:'checkbox',
|
43
|
+
checked:props.value,
|
44
|
+
onInput:e => props.value(e.target.checked)
|
45
|
+
})
|
46
|
+
}
|
47
|
+
|
48
|
+
export function Select(props) {
|
49
|
+
// const description = wrap(props.value,'description')
|
50
|
+
const show = signal(false)
|
51
|
+
const selected = memo(()=>{
|
52
|
+
const options = typeof props.options === 'function' ? props.options() : props.options || []
|
53
|
+
return {options}
|
54
|
+
})
|
55
|
+
const fallback = props.placeholder ? h('span', props.placeholder) : 'nbsp'
|
56
|
+
return h('.select', [
|
57
|
+
h('button', {onClick:e=>show(s=>!s)}, h(Show, {when:()=>props.value, fallback}, ()=>props.value() || nbsp)),
|
58
|
+
h(Show, {when: show}, h('.options', {style:'position:absolute'}, h(List, {each:()=>selected().options},
|
59
|
+
(option) => h('.option', {
|
60
|
+
onClick: (_) => { props.value(option()); show(false) },
|
61
|
+
style: 'cursor: pointer'
|
62
|
+
}, option)
|
63
|
+
)))
|
64
|
+
])
|
65
|
+
}
|
66
|
+
|
67
|
+
const DialogCtx = context(null)
|
68
|
+
export const useDialog = () => useContext(DialogCtx)
|
69
|
+
|
70
|
+
export function Dialog(props) {
|
71
|
+
let dialog
|
72
|
+
const close = () => dialog.close()
|
73
|
+
const show = props.modal ? () => dialog.showModal() : () => dialog.show()
|
74
|
+
return h('dialog', {
|
75
|
+
ref(dia) {
|
76
|
+
dialog = dia
|
77
|
+
const closeHandler = (_) => props.show(false)
|
78
|
+
effect(() => props.show() && show())
|
79
|
+
dialog.addEventListener('close', closeHandler)
|
80
|
+
if (props.ref) props.ref(dialog)
|
81
|
+
return () => dialog.removeEventListener('close', closeHandler)
|
82
|
+
}
|
83
|
+
}, h(DialogCtx, {value:()=>[{close:close}]}, props.children))
|
84
|
+
}
|
85
|
+
|
18
86
|
function CurrencyInput(props) {
|
19
87
|
return h(Input, props)
|
20
88
|
}
|
89
|
+
|
90
|
+
export function Button(props) {
|
91
|
+
let checked
|
92
|
+
return h('button', {}, props.children)
|
93
|
+
}
|
package/src/hyperscript.ts
CHANGED
@@ -1,167 +1,211 @@
|
|
1
1
|
import type {DOMElements} from './constants.ts'
|
2
|
-
import type {
|
2
|
+
import type {Mountable} from './runtime.ts'
|
3
3
|
import {r} from './runtime.ts'
|
4
4
|
|
5
|
-
const
|
6
|
-
|
7
|
-
// export type Mountable = View | HTMLElement | string | number | bigint | symbol | boolean | Date | Mountable[];
|
8
|
-
export type Component<T> = ((props:T) => Mountable) | ((props:T) => Mountable[])
|
9
|
-
export type Tag = typeof DOMElements extends Set<infer K> ? K : never;
|
10
|
-
export type Child = { ():Child } | Element | Child[] | string | number | symbol | bigint | boolean | Date | Record<string,unknown> | {():Child, [ELEMENT]:boolean}
|
11
|
-
export type View = {():void, [ELEMENT]?:boolean}
|
5
|
+
// const document = globalThis.document
|
12
6
|
|
13
7
|
export const h = hyperscript(r)
|
14
8
|
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
9
|
+
type MountableElement = Element | Document | ShadowRoot | DocumentFragment | Node
|
10
|
+
|
11
|
+
interface Runtime {
|
12
|
+
insert(parent: MountableElement, accessor: any, marker?: Node | null, init?: any): any;
|
13
|
+
spread(node: Element, accessor: any, isSVG?: Boolean, skipChildren?: Boolean): void;
|
14
|
+
assign(node: Element, props: any, isSVG?: Boolean, skipChildren?: Boolean): void;
|
15
|
+
component(Comp: (props: any) => any, props: any): any;
|
16
|
+
SVGElements: Set<string>;
|
17
|
+
}
|
18
|
+
|
19
|
+
const $ELEMENT = Symbol("hyper-element");
|
25
20
|
|
26
|
-
|
21
|
+
type ExpandableNode = Node & { [key: string]: any };
|
22
|
+
type Props = { [key: string]: any };
|
27
23
|
|
28
24
|
export type HyperScript = {
|
29
|
-
(
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
(first: Component<Record<string,never>>): View
|
35
|
-
<P extends Record<string, unknown>>(first: Component<P>, second: P): View
|
36
|
-
<C>(first: Component<{children:C}>, second: C): View
|
37
|
-
<P extends Record<string, unknown>, C>(first: Component<P & {children:C}>, second:P, third:C): View
|
38
|
-
}
|
25
|
+
(...args: any[]): () => ExpandableNode | ExpandableNode[];
|
26
|
+
Fragment: (props: {
|
27
|
+
children: (() => ExpandableNode) | (() => ExpandableNode)[];
|
28
|
+
}) => ExpandableNode[];
|
29
|
+
};
|
39
30
|
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
function item(e: Element, c: Child, m?: null): void {
|
48
|
-
if (c===null) return
|
49
|
-
const t = typeof c
|
50
|
-
if (isArray(c))
|
51
|
-
for (const child of c)
|
52
|
-
item(e, child, m)
|
53
|
-
else if (t === 'object' && r.isChild(c))
|
54
|
-
r.insert(e, c, m)
|
55
|
-
else if (t === 'string')
|
56
|
-
e.appendChild(r.text((c as string)))
|
57
|
-
else if (t === 'function') {
|
58
|
-
// while (c[ELEMENT]?.call) c = (c as any)()
|
59
|
-
r.insert(e, c, m)
|
60
|
-
} else e.appendChild(r.text(c.toString()))
|
61
|
-
}
|
31
|
+
// Inspired by https://github.com/hyperhype/hyperscript
|
32
|
+
export function hyperscript(r: Runtime): HyperScript {
|
33
|
+
function h() {
|
34
|
+
let args: any = [].slice.call(arguments),
|
35
|
+
e: ExpandableNode | undefined,
|
36
|
+
classes:string[] = [],
|
37
|
+
multiExpression = false;
|
62
38
|
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
39
|
+
while (Array.isArray(args[0])) args = args[0];
|
40
|
+
if (args[0][$ELEMENT]) args.unshift(h.Fragment);
|
41
|
+
typeof args[0] === "string" && detectMultiExpression(args);
|
42
|
+
const ret: (() => ExpandableNode) & { [$ELEMENT]?: boolean } = () => {
|
43
|
+
while (args.length) item(args.shift());
|
44
|
+
if (e instanceof globalThis.Element && classes.length) e.classList.add(...classes)
|
45
|
+
return e as ExpandableNode;
|
46
|
+
};
|
47
|
+
ret[$ELEMENT] = true;
|
48
|
+
return ret;
|
49
|
+
|
50
|
+
function item(l: any) {
|
51
|
+
const type = typeof l;
|
52
|
+
if (l == null) void 0;
|
53
|
+
else if ("string" === type) {
|
54
|
+
if (!e) parseHtmlTag(l);
|
55
|
+
else e.appendChild(globalThis.document.createTextNode(l));
|
56
|
+
} else if (
|
57
|
+
"number" === type ||
|
58
|
+
"boolean" === type ||
|
59
|
+
"bigint" === type ||
|
60
|
+
"symbol" === type ||
|
61
|
+
l instanceof Date ||
|
62
|
+
l instanceof RegExp
|
63
|
+
) {
|
64
|
+
(e as Node).appendChild(globalThis.document.createTextNode(l.toString()));
|
65
|
+
} else if (Array.isArray(l)) {
|
66
|
+
for (let i = 0; i < l.length; i++) item(l[i]);
|
67
|
+
} else if (l instanceof globalThis.Element) {
|
68
|
+
r.insert(e as globalThis.Element, l, multiExpression ? null : undefined);
|
69
|
+
} else if ("object" === type) {
|
70
|
+
let dynamic = false;
|
71
|
+
const d = Object.getOwnPropertyDescriptors(l);
|
72
|
+
for (const k in d) {
|
73
|
+
if (k === "class" && classes.length !== 0) {
|
74
|
+
console.log('classes',classes)
|
75
|
+
const fixedClasses = classes.join(" "),
|
76
|
+
value = typeof d["class"].value === "function" ?
|
77
|
+
()=>[...classes,...(d["class"].value()??'').split(' ')].filter(c=>c).join(' ') :
|
78
|
+
[...classes,...(d["class"].value??'').split(' ')].filter(c=>c).join(' ')
|
79
|
+
Object.defineProperty(l,"class",{...d[k],value})
|
80
|
+
// classes = []
|
81
|
+
}
|
82
|
+
if (k !== "ref" && k.slice(0, 2) !== "on" && typeof d[k].value === "function") {
|
83
|
+
dynamicProperty(l, k);
|
84
|
+
dynamic = true;
|
85
|
+
} else if (d[k].get) dynamic = true;
|
86
|
+
}
|
87
|
+
dynamic
|
88
|
+
? r.spread(e as globalThis.Element, l, !!args.length)
|
89
|
+
: r.assign(e as globalThis.Element, l, !!args.length);
|
90
|
+
} else if ("function" === type) {
|
91
|
+
if (!e) {
|
92
|
+
let props: Props | undefined,
|
93
|
+
next = args[0];
|
94
|
+
if (
|
95
|
+
next == null ||
|
96
|
+
(typeof next === "object" && !Array.isArray(next) && !(next instanceof globalThis.Element))
|
97
|
+
)
|
98
|
+
props = args.shift();
|
99
|
+
props || (props = {});
|
100
|
+
if (args.length) {
|
101
|
+
props.children = args.length > 1 ? args : args[0];
|
102
|
+
}
|
103
|
+
const d = Object.getOwnPropertyDescriptors(props);
|
104
|
+
for (const k in d) {
|
105
|
+
if (Array.isArray(d[k].value)) {
|
106
|
+
const list = d[k].value;
|
107
|
+
props[k] = () => {
|
108
|
+
for (let i = 0; i < list.length; i++) {
|
109
|
+
while (list[i][$ELEMENT]) list[i] = list[i]();
|
110
|
+
}
|
111
|
+
return list;
|
112
|
+
};
|
113
|
+
dynamicProperty(props, k);
|
114
|
+
} else if (typeof d[k].value === "function" && !d[k].value.length)
|
115
|
+
dynamicProperty(props, k);
|
116
|
+
}
|
117
|
+
e = r.component(l, props);
|
118
|
+
args = [];
|
119
|
+
} else {
|
120
|
+
while ((l as any)[$ELEMENT]) l = ((l as unknown) as () => ExpandableNode)();
|
121
|
+
r.insert(e as globalThis.Element, l, multiExpression ? null : undefined);
|
122
|
+
}
|
123
|
+
}
|
77
124
|
}
|
125
|
+
function parseHtmlTag(s: string) {
|
126
|
+
let i:number
|
127
|
+
|
128
|
+
i = s.indexOf('#')
|
129
|
+
if (i===-1) i = s.indexOf('.')
|
130
|
+
if (i===-1) i = s.length
|
131
|
+
const name = s.slice(0, i) || 'div'
|
132
|
+
e = r.SVGElements.has(name)
|
133
|
+
? globalThis.document.createElementNS("http://www.w3.org/2000/svg", name)
|
134
|
+
: globalThis.document.createElement(name);
|
135
|
+
s = s.slice(i)
|
136
|
+
|
137
|
+
if (s[0]==='#') {
|
138
|
+
i = s.indexOf('.')
|
139
|
+
if (i===-1) i = s.length
|
140
|
+
e!.setAttribute("id", s.slice(1, i))
|
141
|
+
s = s.slice(i)
|
142
|
+
}
|
78
143
|
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
const e = r.element(tag.name)
|
85
|
-
const props2 = props as P & {class?: string|(()=>string)}
|
86
|
-
if (tag.id) e.setAttribute('id',tag.id)
|
87
|
-
if (tag.classes?.length) {
|
88
|
-
const cd = Object.getOwnPropertyDescriptor(props2,'class') ?? ({value:'',writable:true,enumerable:true});
|
89
|
-
props2.class = (cd.value?.call) ?
|
90
|
-
() => [...tag.classes,...(cd.value()??'').split(' ')].filter(c=>c).join(' ') :
|
91
|
-
[...tag.classes,...(cd.value??'').split(' ')].filter(c=>c).join(' ')
|
144
|
+
while(s[0]==='.') {
|
145
|
+
i = s.indexOf('.',1)
|
146
|
+
if (i===-1) i = s.length
|
147
|
+
classes.push(s.slice(1, i))
|
148
|
+
s = s.slice(i)
|
92
149
|
}
|
93
|
-
|
94
|
-
|
95
|
-
const
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
150
|
+
}
|
151
|
+
function parseClass(string: string) {
|
152
|
+
const m = string.split(/([\.#]?[^\s#.]+)/);
|
153
|
+
if (/^\.|#/.test(m[1])) e = globalThis.document.createElement("div");
|
154
|
+
for (let i = 0; i < m.length; i++) {
|
155
|
+
let v = m[i]
|
156
|
+
const s = v.substring(1, v.length);
|
157
|
+
if (!v) v = 'div';
|
158
|
+
if (!e)
|
159
|
+
e = r.SVGElements.has(v)
|
160
|
+
? globalThis.document.createElementNS("http://www.w3.org/2000/svg", v)
|
161
|
+
: globalThis.document.createElement(v);
|
162
|
+
if (v[0] === ".") classes.push(s);
|
163
|
+
else if (v[0] === "#") e.setAttribute("id", s);
|
101
164
|
}
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
const list = d[k].value;
|
111
|
-
(props as Record<string, ()=>unknown>)[k] = () => {
|
112
|
-
for (let i = 0; i < list.length; i++)
|
113
|
-
while (list[i][ELEMENT]) list[i] = list[i]()
|
114
|
-
return list
|
115
|
-
}
|
116
|
-
dynamicProperty(props, k)
|
117
|
-
} else if (typeof d[k].value==='function' && !d[k].value.length) { // A function with zero arguments
|
118
|
-
dynamicProperty(props, k)
|
165
|
+
}
|
166
|
+
function detectMultiExpression(list: any[]) {
|
167
|
+
for (let i = 1; i < list.length; i++) {
|
168
|
+
if (typeof list[i] === "function") {
|
169
|
+
multiExpression = true;
|
170
|
+
return;
|
171
|
+
} else if (Array.isArray(list[i])) {
|
172
|
+
detectMultiExpression(list[i]);
|
119
173
|
}
|
120
174
|
}
|
121
|
-
const e = r.component(() => (first as Component<P>)(props))
|
122
|
-
ret = () => e
|
123
175
|
}
|
124
|
-
ret[ELEMENT] = true
|
125
|
-
return ret
|
126
176
|
}
|
127
|
-
}
|
128
177
|
|
129
|
-
|
130
|
-
|
131
|
-
else if (!isArray(children)) return false
|
132
|
-
for (const i of children) {
|
133
|
-
if (typeof i === 'function') return true
|
134
|
-
else if (isArray(i)) return detectMultiExpression(i)
|
135
|
-
}
|
136
|
-
return false
|
178
|
+
h.Fragment = (props: any) => props.children;
|
179
|
+
return h;
|
137
180
|
}
|
138
181
|
|
182
|
+
|
139
183
|
// ^([a-zA-Z]\w*)?(#[a-zA-Z][-\w]*)?(.[a-zA-Z][-\w]*)*
|
140
|
-
export function parseHtmlTag(s:Tag) {
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
i = s.indexOf('#')
|
145
|
-
if (i===-1) i = s.indexOf('.')
|
146
|
-
if (i===-1) i = s.length
|
147
|
-
const name = s.slice(0, i) || 'div'
|
148
|
-
s = s.slice(i)
|
149
|
-
|
150
|
-
if (s[0]==='#') {
|
151
|
-
i = s.indexOf('.')
|
152
|
-
if (i===-1) i = s.length
|
153
|
-
id = s.slice(1, i)
|
154
|
-
s = s.slice(i)
|
155
|
-
}
|
184
|
+
// export function parseHtmlTag(s:Tag) {
|
185
|
+
// const classes:string[] = [];
|
186
|
+
// let id:string|undefined = undefined, i:number
|
156
187
|
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
188
|
+
// i = s.indexOf('#')
|
189
|
+
// if (i===-1) i = s.indexOf('.')
|
190
|
+
// if (i===-1) i = s.length
|
191
|
+
// const name = s.slice(0, i) || 'div'
|
192
|
+
// s = s.slice(i)
|
193
|
+
|
194
|
+
// if (s[0]==='#') {
|
195
|
+
// i = s.indexOf('.')
|
196
|
+
// if (i===-1) i = s.length
|
197
|
+
// id = s.slice(1, i)
|
198
|
+
// s = s.slice(i)
|
199
|
+
// }
|
200
|
+
|
201
|
+
// while(s[0]==='.') {
|
202
|
+
// i = s.indexOf('.',1)
|
203
|
+
// if (i===-1) i = s.length
|
204
|
+
// classes.push(s.slice(1, i))
|
205
|
+
// s = s.slice(i)
|
206
|
+
// }
|
207
|
+
// return {name:name as string,classes,id:id}
|
208
|
+
// }
|
165
209
|
|
166
210
|
function dynamicProperty<T>(props: Record<string, unknown>, key: string) {
|
167
211
|
const src = props[key] as ()=>unknown
|
package/src/mod.ts
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
export type {Getter,Setter,Signal} from './reactive.ts'
|
2
|
-
export {signal,effect,untrack,batch,memo,root,context,useContext,wrap,fuse,onMount,onCleanup} from './reactive.ts'
|
2
|
+
export {signal,effect,untrack,batch,memo,root,context,useContext,wrap,fuse,onMount,onCleanup,renderEffect} from './reactive.ts'
|
3
3
|
export {nbsp} from './constants.ts'
|
4
4
|
export {List} from './list.ts'
|
5
5
|
export {Show} from './show.ts'
|
@@ -10,6 +10,7 @@ export {HashRouter} from './router.js'
|
|
10
10
|
export {resource,makeAbortable,abortable} from './resource.js'
|
11
11
|
import {r} from './runtime.ts'
|
12
12
|
export const render = r.render
|
13
|
+
export {Dialog,useDialog,Input,Checkbox,Select} from './form.js'
|
13
14
|
|
14
15
|
// const r:Runtime = /*#__PURE__*/ (typeof window === 'object') ? runtime(window) : undefined as unknown as Runtime
|
15
16
|
|
package/src/reactive.ts
CHANGED
@@ -59,13 +59,6 @@ export type Setter<T> = {
|
|
59
59
|
(value: T): T
|
60
60
|
}
|
61
61
|
|
62
|
-
export type Context<T> = {
|
63
|
-
id: symbol,
|
64
|
-
defaultValue: T,
|
65
|
-
get(): T,
|
66
|
-
set(value: T): void
|
67
|
-
}
|
68
|
-
|
69
62
|
export type Options<T> = {
|
70
63
|
equals?: false | EqualsFn<T>
|
71
64
|
}
|
@@ -297,22 +290,30 @@ export function root<T>(fn: RootFn<T>): T {
|
|
297
290
|
return new Root().wrap(fn)
|
298
291
|
}
|
299
292
|
|
300
|
-
export
|
301
|
-
|
293
|
+
export type Context<T> = {
|
294
|
+
id: symbol,
|
295
|
+
defaultValue: T,
|
296
|
+
get(): T,
|
297
|
+
set(value: T): void
|
298
|
+
}
|
299
|
+
|
300
|
+
export function context<T>(): Context<T | undefined>
|
301
|
+
export function context<T>(defaultValue: T): Context<T>
|
302
302
|
export function context<T>(defaultValue?: T) {
|
303
303
|
const id = Symbol()
|
304
|
-
const
|
305
|
-
|
306
|
-
|
307
|
-
const f = Object.assign((props:any) => {
|
308
|
-
set(props.value)
|
304
|
+
const s = {id, defaultValue}
|
305
|
+
return Object.assign((props:any) => {
|
306
|
+
OBSERVER?.set(id, props.value)
|
309
307
|
return () => props.children.call ? props.children() : props.children
|
310
|
-
}, s)
|
311
|
-
|
308
|
+
}, s) as unknown as Context<T>
|
309
|
+
}
|
310
|
+
|
311
|
+
export function useContext<T>(ctx: Context<T>): T {
|
312
|
+
return OBSERVER?.get(ctx.id) ?? ctx.defaultValue
|
312
313
|
}
|
313
314
|
|
314
|
-
export function
|
315
|
-
return
|
315
|
+
export function renderEffect(fn:()=>void) {
|
316
|
+
return globalThis.requestAnimationFrame(()=>effect(fn))
|
316
317
|
}
|
317
318
|
|
318
319
|
export type S<T> = Getter<T> | Setter<T>
|