@wrnrlr/prelude 0.0.1 → 0.1.3
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 +46 -5
- package/deno.json +18 -9
- package/package.json +4 -6
- package/readme.md +163 -41
- package/src/constants.ts +2 -1
- package/src/{controlflow.js → controlflow.ts} +63 -50
- package/src/hyperscript.ts +7 -6
- package/src/mod.ts +19 -17
- package/src/reactive.ts +42 -14
- package/src/resource.js +184 -0
- package/src/router.js +65 -0
- package/src/runtime.ts +9 -8
- package/test/hyperscript.js +2 -2
- package/test/reactive.js +12 -4
- package/www/assets/css/presets.css +504 -0
- package/www/assets/css/style.css +90 -0
- package/www/demo.html +15 -0
- package/www/index.html +211 -0
- package/www/playground.html +183 -0
- package/www/public/example/admin.html +88 -0
- package/{example → www/public/example}/counter.html +1 -1
- package/{example → www/public/example}/greeting.html +1 -1
- package/{example → www/public/example}/show.html +1 -1
- package/{example → www/public/example}/todo.html +1 -1
- 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 +16 -0
- package/www/typedoc.json +13 -0
- package/www/vite.config.js +106 -0
- package/example/paint.html +0 -22
- package/index.html +0 -230
- package/presets.css +0 -284
- package/public/logo.svg +0 -5
- package/test/runtime.js +0 -7
- package/typedoc.jsonc +0 -31
- /package/{public → www/public}/banner.svg +0 -0
@@ -5,12 +5,53 @@ on:
|
|
5
5
|
branches:
|
6
6
|
- main
|
7
7
|
|
8
|
+
permissions:
|
9
|
+
contents: read
|
10
|
+
pages: write
|
11
|
+
id-token: write
|
12
|
+
|
8
13
|
jobs:
|
9
14
|
publish:
|
10
15
|
runs-on: ubuntu-latest
|
11
|
-
|
12
|
-
|
13
|
-
|
16
|
+
environment:
|
17
|
+
name: github-pages
|
18
|
+
url: ${{ steps.deployment.outputs.page_url }}
|
14
19
|
steps:
|
15
|
-
-
|
16
|
-
|
20
|
+
- name: Check out the repository to the runner
|
21
|
+
uses: actions/checkout@v4
|
22
|
+
|
23
|
+
- name: Setup Deno
|
24
|
+
uses: denoland/setup-deno@v2
|
25
|
+
with:
|
26
|
+
deno-version: v2.x
|
27
|
+
|
28
|
+
- name: Deno install
|
29
|
+
run: deno install
|
30
|
+
|
31
|
+
# - name: Lint
|
32
|
+
# run: deno lint
|
33
|
+
|
34
|
+
- name: Run tests
|
35
|
+
run: deno task test
|
36
|
+
|
37
|
+
- name: Build
|
38
|
+
run: deno task build
|
39
|
+
|
40
|
+
- name: Setup Pages
|
41
|
+
uses: actions/configure-pages@v5
|
42
|
+
|
43
|
+
- name: Upload artifact
|
44
|
+
uses: actions/upload-pages-artifact@v3
|
45
|
+
with:
|
46
|
+
path: './www/dist'
|
47
|
+
|
48
|
+
- name: Deploy to GitHub Pages
|
49
|
+
id: deployment
|
50
|
+
uses: actions/deploy-pages@v4
|
51
|
+
|
52
|
+
# - name: Setup NPM
|
53
|
+
# run: echo "//registry.npmjs.org/:_authToken=${NPM_TOKEN}" > /home/runner/.npmrc
|
54
|
+
# env:
|
55
|
+
# NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
|
56
|
+
# - name: Run release script
|
57
|
+
# run: /home/runner/.deno/bin/deno task release
|
package/deno.json
CHANGED
@@ -1,23 +1,32 @@
|
|
1
1
|
{
|
2
2
|
"name": "@wrnrlr/prelude",
|
3
|
-
"version": "0.1.
|
3
|
+
"version": "0.1.3",
|
4
4
|
"exports": "./src/mod.ts",
|
5
5
|
"compilerOptions": {
|
6
|
-
"strict":
|
6
|
+
"strict": false,
|
7
7
|
"checkJs": false,
|
8
8
|
"noImplicitThis": false,
|
9
|
-
"lib": ["dom","dom.iterable","dom.asynciterable","deno.ns"]
|
9
|
+
"lib": ["dom", "dom.iterable", "dom.asynciterable", "deno.ns"]
|
10
10
|
},
|
11
11
|
"imports": {
|
12
12
|
"@std/assert": "jsr:@std/assert@^1.0.0",
|
13
|
+
"@std/fs": "jsr:@std/fs@^1.0.5",
|
14
|
+
"@std/path": "jsr:@std/path@^1.0.6",
|
13
15
|
"@std/testing": "jsr:@std/testing@^0.225.3",
|
14
|
-
"
|
15
|
-
"
|
16
|
-
"
|
16
|
+
"jsdom": "npm:jsdom",
|
17
|
+
"typedoc": "npm:typedoc@^0.26.6",
|
18
|
+
"vite": "npm:vite@^5.4.9",
|
19
|
+
"esbuild": "npm:esbuild@^0.24.0"
|
17
20
|
},
|
18
21
|
"tasks": {
|
19
|
-
"dev": "deno run -A npm:vite
|
20
|
-
"test": "deno test -A
|
21
|
-
"docs": "deno run -A npm:typedoc "
|
22
|
+
"dev": "deno run -A npm:vite --config www/vite.config.js",
|
23
|
+
"test": "deno test -A ./test/*.[jt]s",
|
24
|
+
"docs": "deno run -A npm:typedoc --options www/typedoc.json",
|
25
|
+
"build": "deno run -A npm:vite build --mode production --config www/vite.config.js",
|
26
|
+
"release": "deno publish --allow-slow-types --allow-dirty && npm publish --access public",
|
27
|
+
"clean": "rm -rf dist/ www/dist www/docs"
|
28
|
+
},
|
29
|
+
"lint": {
|
30
|
+
"include": ["src"]
|
22
31
|
}
|
23
32
|
}
|
package/package.json
CHANGED
@@ -1,12 +1,10 @@
|
|
1
1
|
{
|
2
2
|
"name": "@wrnrlr/prelude",
|
3
|
-
"
|
3
|
+
"type": "module",
|
4
|
+
"version": "0.1.3",
|
4
5
|
"author": "Werner Laurensse",
|
5
6
|
"description": "A signal based frontend library with fine-grained reactivity",
|
6
7
|
"main": "./src/mod.ts",
|
7
|
-
"
|
8
|
-
|
9
|
-
"test": "test"
|
10
|
-
},
|
11
|
-
"scripts": {}
|
8
|
+
"scripts": {},
|
9
|
+
"dependencies": { "@codemirror/lang-html": "^6.4.9", "codemirror": "^6.0.1" }
|
12
10
|
}
|
package/readme.md
CHANGED
@@ -1,86 +1,208 @@
|
|
1
|
-
#
|
1
|
+
# Prelude
|
2
|
+
[Home](https://wrnrlr.github.io/prelude/) [NPM](https://www.npmjs.com/package/@wrnrlr/prelude) [JSR](https://jsr.io/@wrnrlr/prelude)
|
3
|
+
|
4
|
+
Prelude lets you develop web applications in a familiar component-based functional style.
|
5
|
+
It is build with the desire to have a lightweight frontend framework that works
|
6
|
+
using just JavaScript but that nontheless can handle complex web applications without
|
7
|
+
sacrificing on developer expierence.
|
2
8
|
|
3
9
|
## Get Started
|
4
10
|
|
5
|
-
|
6
|
-
|
11
|
+
Prelude works with most popular JavaScript runtimes: Node, Deno, Bun or the borwser.
|
12
|
+
It is available on NPM and JSR under the package named `@wrnrlr/prelude`.
|
13
|
+
|
14
|
+
The quickest way to get started with Prelude is uing the [Playground](https://wrnrlr.github.io/prelude/playground) app on the homepage.
|
15
|
+
It offers a IDE complete with a code editor, live preview and a number of examples.
|
16
|
+
Aternativaly you can develop on your local machine using `vite`.
|
17
|
+
|
18
|
+
Some Prelude APIs can also be used in the REPL to expore their behaviour interactively.
|
19
|
+
|
20
|
+
## Basic Example
|
21
|
+
|
22
|
+
This is a example of a button that increments a counter when it is clicked.
|
23
|
+
|
24
|
+
```html
|
25
|
+
<!DOCTYPE html>
|
26
|
+
<title>Counter</title>
|
27
|
+
|
28
|
+
<script type="module">
|
7
29
|
import {h, signal, render} from 'https://esm.sh/@wrnrlr/prelude'
|
8
|
-
```
|
9
30
|
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
31
|
+
function Counter() {
|
32
|
+
const n = signal(1)
|
33
|
+
return h('button', {onClick: e => n(n=>n+1)}, n)
|
34
|
+
}
|
35
|
+
|
36
|
+
render(Counter, document.body)
|
37
|
+
</script>
|
38
|
+
```
|
39
|
+
|
40
|
+
## Hyperscript
|
14
41
|
|
15
|
-
|
16
|
-
```js
|
17
|
-
import {h, signal, render} from 'jsr:wrnrlr/prelude'
|
18
|
-
```
|
42
|
+
Prelude does not use JSX or a templating language to descript html instead we use a DSL called HyperScript.
|
19
43
|
|
20
|
-
|
44
|
+
The `h` function is used in either of two ways based on the type of the first argument,
|
45
|
+
when it is a string it will create a html element like ,
|
46
|
+
and when it is a function it will create a reactive component.
|
21
47
|
|
22
48
|
```js
|
23
|
-
|
49
|
+
h('div',{},[
|
50
|
+
h('label','Name'),h('input',{})
|
51
|
+
])
|
52
|
+
```
|
24
53
|
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
54
|
+
### Event Handler
|
55
|
+
|
56
|
+
Prelude tries to integrate with the existing web APIs as much as possible, handeling user events is no different,
|
57
|
+
use a function to the event callback.
|
29
58
|
|
30
|
-
|
59
|
+
In the following example we listen for `onclick` events for a button, and increment the value of the `n` signal.
|
60
|
+
|
61
|
+
```js
|
62
|
+
h('button', {onClick:e => n(i => i + 1)}, n)
|
31
63
|
```
|
32
64
|
|
33
|
-
|
65
|
+
Be adviced, the event handler MUST always have one argument even if this is not being used, lest HyperScript confuses it for a signal
|
66
|
+
and ignores the events.
|
34
67
|
|
35
68
|
```js
|
36
|
-
|
37
|
-
|
69
|
+
// Ok
|
70
|
+
h('button', {onClick: e => console.log('Ok')})
|
71
|
+
h('button', {onClick: _ => console.log('Ok')})
|
72
|
+
// This event handler will be ignored
|
73
|
+
h('button', {onClick: () => console.log('Wrong')})
|
74
|
+
```
|
75
|
+
|
76
|
+
## Reactivity
|
77
|
+
|
78
|
+
### Signals
|
79
|
+
|
80
|
+
A signal is an object that holds a value with a setter to update this value and a getter that returns this value whenever it is updated.
|
81
|
+
|
82
|
+
Create signal.
|
83
|
+
```js
|
84
|
+
// Create a signal with value one
|
85
|
+
const n = signal(1)
|
86
|
+
|
87
|
+
// Get value from signal
|
88
|
+
n()
|
89
|
+
|
90
|
+
// Set value for signal
|
91
|
+
n(2)
|
92
|
+
|
93
|
+
// Set value with an update function
|
94
|
+
n(i=>i+1)
|
95
|
+
|
96
|
+
// Derived signal
|
97
|
+
const n2 = () => n() * 2
|
98
|
+
```
|
99
|
+
|
100
|
+
### Effects
|
101
|
+
|
102
|
+
The `effect` function lets you subscribe to signals and perform side-effects whenever the signal chages.
|
103
|
+
|
104
|
+
```js
|
105
|
+
const a = signal(1), b = signal(2)
|
38
106
|
effect(() => console.log('a+b', a()+b()))
|
39
107
|
const c = () => a()+b()
|
40
108
|
effect(() => console.log('c', c()))
|
41
109
|
a(i => i+1)
|
42
110
|
```
|
43
111
|
|
44
|
-
|
112
|
+
### Memo
|
45
113
|
|
46
|
-
The `
|
114
|
+
The `memo` function caches the result of the function passed to it.
|
47
115
|
|
48
116
|
```js
|
49
|
-
|
50
|
-
h('label','Name'),h('input',{})
|
51
|
-
])
|
117
|
+
const n2 = memo(() => n() * 2)
|
52
118
|
```
|
53
119
|
|
54
|
-
|
120
|
+
### Untrack
|
55
121
|
|
56
|
-
|
57
|
-
otherwise hyperscript will confuse it for a signal.
|
122
|
+
## Conditional Rendering
|
58
123
|
|
59
124
|
```js
|
60
|
-
|
61
|
-
h('button', {onClick: e => console.log('Ok')}, 'Hi')
|
62
|
-
h('button', {onClick: _ => console.log('Ok')}, 'Hi')
|
63
|
-
// This event handler will be ignored
|
64
|
-
h('button', {onClick: () => console.log('Wrong')}, '')
|
125
|
+
h(Show, {when:() => n()%2 === 0, fallback:'odd'}, 'even')
|
65
126
|
```
|
66
127
|
|
67
|
-
|
128
|
+
It is also possible to conditionally render a component by prefixing it with a JavaScript *and-expression*, like in the example below,
|
129
|
+
but using `Show` is going to be faster.
|
68
130
|
|
69
131
|
```js
|
70
|
-
h(
|
132
|
+
h('',show&&'Hi')
|
71
133
|
```
|
72
134
|
|
73
135
|
## Rendering Lists
|
74
136
|
|
75
137
|
```js
|
76
|
-
h(List, {each:
|
138
|
+
h(List, {each:['a','b','c']}, (v,i)=>`${i()}:${v()}`)
|
139
|
+
```
|
140
|
+
|
141
|
+
## Fetching Resources
|
142
|
+
|
143
|
+
The `resource()` function lets you define a asynchronous signal.
|
144
|
+
|
145
|
+
|
146
|
+
```js
|
147
|
+
resource(async ()=>getPosts())
|
77
148
|
```
|
78
149
|
|
79
|
-
##
|
150
|
+
## Dependency Injection
|
80
151
|
Prelude supports dependency injection with the `contect` and `useContext` APIs.
|
81
152
|
|
82
|
-
|
153
|
+
```js
|
154
|
+
const CounterCtx = context()
|
155
|
+
const useCounter = () => useContext(CounterCtx)
|
156
|
+
|
157
|
+
function CounterProvider(props) {
|
158
|
+
const count = signal(0)
|
159
|
+
const increment = () => count(i=>i+1)
|
160
|
+
return h(CounterCtx.Provider, {value:[count,increment]}, props.children)
|
161
|
+
}
|
162
|
+
|
163
|
+
function Counter() {
|
164
|
+
const [n, increment] = useCounter()
|
165
|
+
return h('button', {onClick:e=>increment()}, n)
|
166
|
+
}
|
167
|
+
|
168
|
+
function
|
169
|
+
|
170
|
+
function App() {
|
171
|
+
h(CounterProvider, h(Counter))
|
172
|
+
}
|
173
|
+
```
|
174
|
+
|
175
|
+
## Router
|
83
176
|
|
84
177
|
```js
|
85
|
-
h(
|
178
|
+
h(Router,[
|
179
|
+
{path:'/', component:Posts},
|
180
|
+
{path:'/user', component:Users}
|
181
|
+
])
|
86
182
|
```
|
183
|
+
|
184
|
+
## Learn More
|
185
|
+
|
186
|
+
* [API Reference]()
|
187
|
+
* [SolidJS Docs]():
|
188
|
+
The documentation of SolidJS also a good place for background information because Prelude is lacking extensive documentation at this time.
|
189
|
+
Prelude started as a SolidJS clone, but with better HyperScript support. A lot of the concepts are the same but naming conventions can vary.
|
190
|
+
|
191
|
+
## TODO
|
192
|
+
|
193
|
+
* [ ] tailwind styling
|
194
|
+
* SSR
|
195
|
+
* Hydration
|
196
|
+
* Components
|
197
|
+
* [ ] Select
|
198
|
+
* [ ] Multiselect
|
199
|
+
* [ ] DataTable
|
200
|
+
* [ ] Dropdown
|
201
|
+
* [ ] Dialog
|
202
|
+
* [ ] Dynamic
|
203
|
+
|
204
|
+
## Links
|
205
|
+
|
206
|
+
* [Homepage](https://wrnrlr.github.io/prelude)
|
207
|
+
* [NPM](https://www.npmjs.com/package/@wrnrlr/prelude)
|
208
|
+
* [JSR](https://jsr.io/@wrnrlr/prelude)
|
package/src/constants.ts
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
// @ts-nocheck:
|
1
2
|
/**
|
2
3
|
* Non-breakable space in Unicode
|
3
4
|
* @group Utils
|
@@ -13,7 +14,7 @@ declare type DocumentFragment = any
|
|
13
14
|
export declare type Node = any
|
14
15
|
|
15
16
|
export type Mountable = Elem | Document | ShadowRoot | DocumentFragment | Node;
|
16
|
-
type ExpandableNode = Node & { [key: string]:
|
17
|
+
type ExpandableNode = Node & { [key: string]: unknown };
|
17
18
|
|
18
19
|
// type Expect<T extends true> = T;
|
19
20
|
// type Equal<X, Y> = (<T>() => T extends X ? 1 : 2) extends <T>() => T extends Y ? 1 : 2 ? true : false;
|
@@ -1,81 +1,94 @@
|
|
1
|
-
|
1
|
+
// @ts-nocheck:
|
2
|
+
import type { Child } from './hyperscript.ts'
|
3
|
+
import {signal,untrack,batch,memo,root,type Signal} from './reactive.ts'
|
4
|
+
|
5
|
+
export type ShowProps<T> = {
|
6
|
+
when: T,
|
7
|
+
children: Child | ((a:()=>T)=>void),
|
8
|
+
fallback: unknown
|
9
|
+
}
|
2
10
|
|
3
11
|
/**
|
4
12
|
Show children if `when` prop is true, otherwise show `fallback`.
|
5
13
|
@group Components
|
6
14
|
*/
|
7
|
-
export function Show(props) {
|
15
|
+
export function Show<T>(props:ShowProps<T>) {
|
8
16
|
const condition = memo(()=>props.when)
|
9
17
|
return memo(()=>{
|
10
18
|
const c = condition()
|
11
19
|
if (c) {
|
12
20
|
const child = props.children
|
13
21
|
const fn = typeof child === "function" && child.length > 0
|
14
|
-
return fn ?
|
22
|
+
return fn ? untrack(() => child(() => props.when)) : child
|
15
23
|
} else return props.fallback
|
16
24
|
})
|
17
25
|
}
|
18
26
|
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
}
|
33
|
-
throw new Error('Cannot wrap signal')
|
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
|
34
40
|
}
|
35
41
|
|
36
42
|
/**
|
37
43
|
List
|
38
44
|
@group Components
|
39
45
|
*/
|
40
|
-
export function List(props) {
|
46
|
+
export function List<T>(props:ListProps<T>) {
|
41
47
|
const fallback = "fallback" in props && { fallback: () => props.fallback }
|
42
48
|
const list = props.each
|
43
|
-
const cb = props.children
|
44
|
-
let items = [],
|
45
|
-
|
46
|
-
|
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[],
|
56
|
+
oldIndex: number,
|
57
|
+
oldValue: unknown
|
58
|
+
const indexes = cb.length > 1 ? [] : null;
|
59
|
+
function newValueGetter(_:unknown) { return newValue }
|
47
60
|
function changeBoth() {
|
48
|
-
item
|
49
|
-
item
|
50
|
-
item
|
51
|
-
item
|
61
|
+
item!.index = i!
|
62
|
+
item!.indexSetter?.(i)
|
63
|
+
item!.value = newValue!
|
64
|
+
item!.valueSetter?.(newValueGetter)
|
52
65
|
}
|
53
|
-
function mapperWithIndexes(disposer) {
|
66
|
+
function mapperWithIndexes(disposer:any) {
|
54
67
|
const V = newValue, I = i, Is = signal(I), Vs = signal(V)
|
55
|
-
items.push({value: newValue, index: I
|
68
|
+
items.push({value: newValue, index: I!, disposer, indexSetter: Is, valueSetter: Vs})
|
56
69
|
return cb(
|
57
|
-
(...a) => a.length ?
|
58
|
-
|
70
|
+
(...a:any[]) => a.length ?
|
71
|
+
untrack(()=>list((list:any)=>list.toSpliced(I,1,a[0])))
|
59
72
|
: Vs(),
|
60
73
|
()=>Is())
|
61
74
|
}
|
62
|
-
function mapperWithoutIndexes(disposer) {
|
75
|
+
function mapperWithoutIndexes(disposer:any) {
|
63
76
|
const V = newValue, I = i, Vs = signal(V)
|
64
|
-
items.push({value: V, index: i
|
65
|
-
return cb((...a) => a.length ?
|
66
|
-
|
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])))
|
67
80
|
: Vs())
|
68
81
|
}
|
69
82
|
const mapper = indexes ? mapperWithIndexes : mapperWithoutIndexes
|
70
83
|
return memo(() => {
|
71
|
-
const newItems = list()
|
84
|
+
const newItems = list.call ? list() : list
|
72
85
|
// (newItems)[$TRACK]; // top level tracking
|
73
|
-
return
|
86
|
+
return untrack(() => {
|
74
87
|
const temp = new Array(newItems.length) // new mapped array
|
75
|
-
unusedItems = items.length
|
88
|
+
let unusedItems = items.length
|
76
89
|
|
77
90
|
// 1) no change when values & indexes match
|
78
|
-
for (j = unusedItems - 1; j >= 0; --j) {
|
91
|
+
for (let j = unusedItems - 1; j >= 0; --j) {
|
79
92
|
item = items[j]
|
80
93
|
oldIndex = item.index
|
81
94
|
if (oldIndex < newItems.length && newItems[oldIndex] === item.value) {
|
@@ -90,7 +103,7 @@ export function List(props) {
|
|
90
103
|
// #2 prepare values matcher
|
91
104
|
const matcher = new Map()
|
92
105
|
const matchedItems = new Uint8Array(unusedItems)
|
93
|
-
for (j = unusedItems - 1; j >= 0; --j) {
|
106
|
+
for (let j = unusedItems - 1; j >= 0; --j) {
|
94
107
|
oldValue = items[j].value
|
95
108
|
matcher.get(oldValue)?.push(j) ?? matcher.set(oldValue, [j])
|
96
109
|
}
|
@@ -99,19 +112,19 @@ export function List(props) {
|
|
99
112
|
for (i = 0; i < newItems.length; ++i) {
|
100
113
|
if (i in temp) continue
|
101
114
|
newValue = newItems[i]
|
102
|
-
j = matcher.get(newValue)?.pop() ?? -1
|
115
|
+
const j = matcher.get(newValue)?.pop() ?? -1
|
103
116
|
if (j >= 0) {
|
104
|
-
item = items[j]
|
105
|
-
oldIndex = item
|
117
|
+
item = items[j as number]
|
118
|
+
oldIndex = item!.index
|
106
119
|
temp[i] = mapped[oldIndex]
|
107
|
-
item
|
108
|
-
item
|
109
|
-
matchedItems[j] = 1
|
120
|
+
item!.index = i
|
121
|
+
item!.indexSetter?.(i)
|
122
|
+
matchedItems[j as number] = 1
|
110
123
|
}
|
111
124
|
}
|
112
125
|
|
113
126
|
// 3) reduce unusedItems for matched items
|
114
|
-
for (j = matchedItems.length - 1; j >= 0; --j) {
|
127
|
+
for (let j = matchedItems.length - 1; j >= 0; --j) {
|
115
128
|
if (matchedItems[j] && --unusedItems !== j) {
|
116
129
|
item = items[j]
|
117
130
|
items[j] = items[unusedItems]
|
@@ -120,9 +133,9 @@ export function List(props) {
|
|
120
133
|
}
|
121
134
|
|
122
135
|
// 4) change values when indexes match
|
123
|
-
for (j = unusedItems - 1; j >= 0; --j) {
|
136
|
+
for (let j = unusedItems - 1; j >= 0; --j) {
|
124
137
|
item = items[j];
|
125
|
-
oldIndex = item
|
138
|
+
oldIndex = item!.index;
|
126
139
|
if (!(oldIndex in temp) && oldIndex < newItems.length) {
|
127
140
|
temp[oldIndex] = mapped[oldIndex]
|
128
141
|
newValue = newItems[oldIndex]
|
@@ -156,7 +169,7 @@ export function List(props) {
|
|
156
169
|
})
|
157
170
|
}
|
158
171
|
|
159
|
-
function disposeList(list) {
|
172
|
+
function disposeList(list:any[]) {
|
160
173
|
for (let i = 0; i < list.length; i++) {
|
161
174
|
list[i]?.disposer()
|
162
175
|
}
|
package/src/hyperscript.ts
CHANGED
@@ -1,5 +1,6 @@
|
|
1
|
-
|
2
|
-
import {
|
1
|
+
// @ts-nocheck:
|
2
|
+
import {untrack} from './reactive.ts'
|
3
|
+
import type {Properties,BooleanAttributes,DelegatedEvents,DOMElements, Mountable} from './constants.ts'
|
3
4
|
import type {Runtime,$RUNTIME} from './runtime.ts'
|
4
5
|
|
5
6
|
const ELEMENT: unique symbol = Symbol(), {isArray} = Array
|
@@ -89,7 +90,7 @@ export function hyperscript(r:Runtime, patch?:any):HyperScript {
|
|
89
90
|
let children: Child
|
90
91
|
|
91
92
|
if (typeof second === 'object' && !isArray(second)) {
|
92
|
-
children = third || [];
|
93
|
+
children = (third as Child) || [];
|
93
94
|
props = ((second ?? {}) as T&{children:K})
|
94
95
|
} else {
|
95
96
|
children = (second as Child) || []
|
@@ -115,7 +116,7 @@ export function hyperscript(r:Runtime, patch?:any):HyperScript {
|
|
115
116
|
dynamicProperty(props as any, k)
|
116
117
|
}
|
117
118
|
}
|
118
|
-
e =
|
119
|
+
e = untrack(()=>(element as Component<T&{children:K}>)(props as T&{children:K}))
|
119
120
|
ret = () => e
|
120
121
|
} else {
|
121
122
|
const tag = parseTag(element as Tag)
|
@@ -160,12 +161,12 @@ function detectMultiExpression(list:any):boolean {
|
|
160
161
|
// ^([a-zA-Z]\w*)?(#[a-zA-Z][-\w]*)?(.[a-zA-Z][-\w]*)*
|
161
162
|
function parseTag(s:string):{name:string,id?:string,classes:string[]} {
|
162
163
|
const classes:string[] = [];
|
163
|
-
let
|
164
|
+
let id:string|undefined = undefined, i:number
|
164
165
|
|
165
166
|
i = s.indexOf('#')
|
166
167
|
if (i===-1) i = s.indexOf('.')
|
167
168
|
if (i===-1) i = s.length
|
168
|
-
name = s.slice(0, i) || 'div'
|
169
|
+
const name = s.slice(0, i) || 'div'
|
169
170
|
s = s.slice(i)
|
170
171
|
|
171
172
|
if (s[0]==='#') {
|
package/src/mod.ts
CHANGED
@@ -1,15 +1,18 @@
|
|
1
|
+
// @ts-nocheck:
|
1
2
|
export type {Getter,Setter,Fn,EqualsFn,ErrorFn,RootFn,UpdateFn} from './reactive.ts'
|
2
|
-
export {signal,effect,
|
3
|
+
export {signal,effect,untrack,batch,memo,root,wrap,onMount} from './reactive.ts'
|
3
4
|
export {nbsp} from './constants.ts'
|
4
|
-
export {
|
5
|
+
export {Show,List} from './controlflow.ts'
|
5
6
|
export {runtime, type Runtime} from './runtime.ts'
|
6
7
|
import {runtime, type Runtime} from './runtime.ts'
|
7
8
|
export {hyperscript,type HyperScript,type Child,type Props,type Tag,type View,type Component} from './hyperscript.ts'
|
8
9
|
import {type HyperScript, hyperscript} from './hyperscript.ts'
|
9
|
-
export {
|
10
|
-
export
|
10
|
+
export {Router} from './router.js'
|
11
|
+
export {resource,makeAbortable,abortable} from './resource.js'
|
12
|
+
// export {Input,Table} from './components.js'
|
13
|
+
// export * from './canvas.js'
|
11
14
|
|
12
|
-
const r:Runtime = runtime(window as any)
|
15
|
+
const r:Runtime|undefined = /*#__PURE__*/ (typeof window === 'object') ? runtime(window as any) : undefined
|
13
16
|
|
14
17
|
/** h
|
15
18
|
@example Element with a single child
|
@@ -26,20 +29,19 @@ h(Input,{onInput:e => {}})
|
|
26
29
|
```
|
27
30
|
@group Hyperscript
|
28
31
|
*/
|
29
|
-
const h:HyperScript = hyperscript(r)
|
32
|
+
const h:HyperScript|undefined = /*#__PURE__*/ r ? hyperscript(r) : undefined
|
30
33
|
|
31
|
-
const render = r
|
34
|
+
const render = /*#__PURE__*/ r?.render
|
32
35
|
|
33
|
-
import {signal} from './reactive.ts'
|
34
|
-
import {wrap} from './controlflow.js'
|
36
|
+
// import {signal,wrap} from './reactive.ts'
|
35
37
|
|
36
|
-
/**
|
37
|
-
@group Utils
|
38
|
-
*/
|
39
|
-
export function $(a:any,b:any):any {
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
}
|
38
|
+
// /**
|
39
|
+
// @group Utils
|
40
|
+
// */
|
41
|
+
// export function $(a:any,b:any):any {
|
42
|
+
// const t = typeof a
|
43
|
+
// if (t==='function') return wrap(a,b)
|
44
|
+
// else return signal(a,b)
|
45
|
+
// }
|
44
46
|
|
45
47
|
export {h,render}
|