@wrnrlr/prelude 0.0.1
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 +16 -0
- package/LICENSE +1 -0
- package/deno.json +23 -0
- package/example/counter.html +24 -0
- package/example/greeting.html +25 -0
- package/example/paint.html +22 -0
- package/example/show.html +18 -0
- package/example/todo.html +70 -0
- package/index.html +230 -0
- package/package.json +12 -0
- package/presets.css +284 -0
- package/public/banner.svg +6 -0
- package/public/logo.svg +5 -0
- package/readme.md +86 -0
- package/src/canvas.js +114 -0
- package/src/components.js +20 -0
- package/src/constants.ts +515 -0
- package/src/controlflow.js +163 -0
- package/src/hyperscript.ts +237 -0
- package/src/mod.ts +45 -0
- package/src/reactive.ts +359 -0
- package/src/runtime.ts +434 -0
- package/test/hyperscript.js +102 -0
- package/test/reactive.js +98 -0
- package/test/runtime.js +7 -0
- package/typedoc.jsonc +31 -0
package/src/constants.ts
ADDED
@@ -0,0 +1,515 @@
|
|
1
|
+
/**
|
2
|
+
* Non-breakable space in Unicode
|
3
|
+
* @group Utils
|
4
|
+
*/
|
5
|
+
export const nbsp:string = '\u00A0'
|
6
|
+
|
7
|
+
export declare type Window = { document: Document; SVGElement: typeof SVGElements}
|
8
|
+
export declare type Elem = any
|
9
|
+
declare type SVGElement = any
|
10
|
+
declare type Document = any
|
11
|
+
declare type ShadowRoot = any
|
12
|
+
declare type DocumentFragment = any
|
13
|
+
export declare type Node = any
|
14
|
+
|
15
|
+
export type Mountable = Elem | Document | ShadowRoot | DocumentFragment | Node;
|
16
|
+
type ExpandableNode = Node & { [key: string]: any };
|
17
|
+
|
18
|
+
// type Expect<T extends true> = T;
|
19
|
+
// type Equal<X, Y> = (<T>() => T extends X ? 1 : 2) extends <T>() => T extends Y ? 1 : 2 ? true : false;
|
20
|
+
// const test = {children:[] as any[]}
|
21
|
+
// type test1 = Expect<Equal<typeof test, ElementProps>>
|
22
|
+
|
23
|
+
const booleans:string[] = [
|
24
|
+
'allowfullscreen',
|
25
|
+
'async',
|
26
|
+
'autofocus',
|
27
|
+
'autoplay',
|
28
|
+
'checked',
|
29
|
+
'controls',
|
30
|
+
'default',
|
31
|
+
'disabled',
|
32
|
+
'formnovalidate',
|
33
|
+
'hidden',
|
34
|
+
'indeterminate',
|
35
|
+
'inert',
|
36
|
+
'ismap',
|
37
|
+
'loop',
|
38
|
+
'multiple',
|
39
|
+
'muted',
|
40
|
+
'nomodule',
|
41
|
+
'novalidate',
|
42
|
+
'open',
|
43
|
+
'playsinline',
|
44
|
+
'readonly',
|
45
|
+
'required',
|
46
|
+
'reversed',
|
47
|
+
'seamless',
|
48
|
+
'selected'
|
49
|
+
];
|
50
|
+
|
51
|
+
const BooleanAttributes:Set<string> = /*#__PURE__*/ new Set(booleans);
|
52
|
+
|
53
|
+
const Properties:Set<string> = /*#__PURE__*/ new Set([
|
54
|
+
'className',
|
55
|
+
'value',
|
56
|
+
'readOnly',
|
57
|
+
'formNoValidate',
|
58
|
+
'isMap',
|
59
|
+
'noModule',
|
60
|
+
'playsInline',
|
61
|
+
...booleans
|
62
|
+
]);
|
63
|
+
|
64
|
+
const ChildProperties:Set<string> = /*#__PURE__*/ new Set([
|
65
|
+
'innerHTML',
|
66
|
+
'textContent',
|
67
|
+
'innerText',
|
68
|
+
'children'
|
69
|
+
]);
|
70
|
+
|
71
|
+
// React Compat
|
72
|
+
const Aliases:Record<string,string> = /*#__PURE__*/ Object.assign(Object.create(null), {
|
73
|
+
className: 'class',
|
74
|
+
htmlFor: 'for'
|
75
|
+
});
|
76
|
+
|
77
|
+
const PropAliases = /*#__PURE__*/ Object.assign(Object.create(null), {
|
78
|
+
class: 'className',
|
79
|
+
formnovalidate: {
|
80
|
+
$: 'formNoValidate',
|
81
|
+
BUTTON: 1,
|
82
|
+
INPUT: 1
|
83
|
+
},
|
84
|
+
ismap: {
|
85
|
+
$: 'isMap',
|
86
|
+
IMG: 1
|
87
|
+
},
|
88
|
+
nomodule: {
|
89
|
+
$: 'noModule',
|
90
|
+
SCRIPT: 1
|
91
|
+
},
|
92
|
+
playsinline: {
|
93
|
+
$: 'playsInline',
|
94
|
+
VIDEO: 1
|
95
|
+
},
|
96
|
+
readonly: {
|
97
|
+
$: 'readOnly',
|
98
|
+
INPUT: 1,
|
99
|
+
TEXTAREA: 1
|
100
|
+
}
|
101
|
+
});
|
102
|
+
|
103
|
+
function getPropAlias(prop:string,tagName:string):string | undefined {
|
104
|
+
const a = PropAliases[prop];
|
105
|
+
return typeof a === 'object' ? (a[tagName] ? a['$'] : undefined) : a;
|
106
|
+
}
|
107
|
+
|
108
|
+
// list of Element events that will be delegated
|
109
|
+
const DelegatedEvents:Set<string> = /*#__PURE__*/ new Set([
|
110
|
+
'beforeinput',
|
111
|
+
'click',
|
112
|
+
'dblclick',
|
113
|
+
'contextmenu',
|
114
|
+
'focusin',
|
115
|
+
'focusout',
|
116
|
+
'input',
|
117
|
+
'keydown',
|
118
|
+
'keyup',
|
119
|
+
'mousedown',
|
120
|
+
'mousemove',
|
121
|
+
'mouseout',
|
122
|
+
'mouseover',
|
123
|
+
'mouseup',
|
124
|
+
'pointerdown',
|
125
|
+
'pointermove',
|
126
|
+
'pointerout',
|
127
|
+
'pointerover',
|
128
|
+
'pointerup',
|
129
|
+
'touchend',
|
130
|
+
'touchmove',
|
131
|
+
'touchstart'
|
132
|
+
]);
|
133
|
+
|
134
|
+
const SVGElements:Set<string> = /*#__PURE__*/ new Set([
|
135
|
+
// 'a',
|
136
|
+
'altGlyph',
|
137
|
+
'altGlyphDef',
|
138
|
+
'altGlyphItem',
|
139
|
+
'animate',
|
140
|
+
'animateColor',
|
141
|
+
'animateMotion',
|
142
|
+
'animateTransform',
|
143
|
+
'circle',
|
144
|
+
'clipPath',
|
145
|
+
'color-profile',
|
146
|
+
'cursor',
|
147
|
+
'defs',
|
148
|
+
'desc',
|
149
|
+
'ellipse',
|
150
|
+
'feBlend',
|
151
|
+
'feColorMatrix',
|
152
|
+
'feComponentTransfer',
|
153
|
+
'feComposite',
|
154
|
+
'feConvolveMatrix',
|
155
|
+
'feDiffuseLighting',
|
156
|
+
'feDisplacementMap',
|
157
|
+
'feDistantLight',
|
158
|
+
'feDropShadow',
|
159
|
+
'feFlood',
|
160
|
+
'feFuncA',
|
161
|
+
'feFuncB',
|
162
|
+
'feFuncG',
|
163
|
+
'feFuncR',
|
164
|
+
'feGaussianBlur',
|
165
|
+
'feImage',
|
166
|
+
'feMerge',
|
167
|
+
'feMergeNode',
|
168
|
+
'feMorphology',
|
169
|
+
'feOffset',
|
170
|
+
'fePointLight',
|
171
|
+
'feSpecularLighting',
|
172
|
+
'feSpotLight',
|
173
|
+
'feTile',
|
174
|
+
'feTurbulence',
|
175
|
+
'filter',
|
176
|
+
'font',
|
177
|
+
'font-face',
|
178
|
+
'font-face-format',
|
179
|
+
'font-face-name',
|
180
|
+
'font-face-src',
|
181
|
+
'font-face-uri',
|
182
|
+
'foreignObject',
|
183
|
+
'g',
|
184
|
+
'glyph',
|
185
|
+
'glyphRef',
|
186
|
+
'hkern',
|
187
|
+
'image',
|
188
|
+
'line',
|
189
|
+
'linearGradient',
|
190
|
+
'marker',
|
191
|
+
'mask',
|
192
|
+
'metadata',
|
193
|
+
'missing-glyph',
|
194
|
+
'mpath',
|
195
|
+
'path',
|
196
|
+
'pattern',
|
197
|
+
'polygon',
|
198
|
+
'polyline',
|
199
|
+
'radialGradient',
|
200
|
+
'rect',
|
201
|
+
// 'script',
|
202
|
+
'set',
|
203
|
+
'stop',
|
204
|
+
// 'style',
|
205
|
+
'svg',
|
206
|
+
'switch',
|
207
|
+
'symbol',
|
208
|
+
'text',
|
209
|
+
'textPath',
|
210
|
+
// 'title',
|
211
|
+
'tref',
|
212
|
+
'tspan',
|
213
|
+
'use',
|
214
|
+
'view',
|
215
|
+
'vkern'
|
216
|
+
]);
|
217
|
+
|
218
|
+
const SVGNamespace:Record<string,string> = {
|
219
|
+
xlink: 'http://www.w3.org/1999/xlink',
|
220
|
+
xml: 'http://www.w3.org/XML/1998/namespace'
|
221
|
+
};
|
222
|
+
|
223
|
+
const DOMElements:Set<string> = /*#__PURE__*/ new Set([
|
224
|
+
'html',
|
225
|
+
'base',
|
226
|
+
'head',
|
227
|
+
'link',
|
228
|
+
'meta',
|
229
|
+
'style',
|
230
|
+
'title',
|
231
|
+
'body',
|
232
|
+
'address',
|
233
|
+
'article',
|
234
|
+
'aside',
|
235
|
+
'footer',
|
236
|
+
'header',
|
237
|
+
'main',
|
238
|
+
'nav',
|
239
|
+
'section',
|
240
|
+
'body',
|
241
|
+
'blockquote',
|
242
|
+
'dd',
|
243
|
+
'div',
|
244
|
+
'dl',
|
245
|
+
'dt',
|
246
|
+
'figcaption',
|
247
|
+
'figure',
|
248
|
+
'hr',
|
249
|
+
'li',
|
250
|
+
'ol',
|
251
|
+
'p',
|
252
|
+
'pre',
|
253
|
+
'ul',
|
254
|
+
'a',
|
255
|
+
'abbr',
|
256
|
+
'b',
|
257
|
+
'bdi',
|
258
|
+
'bdo',
|
259
|
+
'br',
|
260
|
+
'cite',
|
261
|
+
'code',
|
262
|
+
'data',
|
263
|
+
'dfn',
|
264
|
+
'em',
|
265
|
+
'i',
|
266
|
+
'kbd',
|
267
|
+
'mark',
|
268
|
+
'q',
|
269
|
+
'rp',
|
270
|
+
'rt',
|
271
|
+
'ruby',
|
272
|
+
's',
|
273
|
+
'samp',
|
274
|
+
'small',
|
275
|
+
'span',
|
276
|
+
'strong',
|
277
|
+
'sub',
|
278
|
+
'sup',
|
279
|
+
'time',
|
280
|
+
'u',
|
281
|
+
'var',
|
282
|
+
'wbr',
|
283
|
+
'area',
|
284
|
+
'audio',
|
285
|
+
'img',
|
286
|
+
'map',
|
287
|
+
'track',
|
288
|
+
'video',
|
289
|
+
'embed',
|
290
|
+
'iframe',
|
291
|
+
'object',
|
292
|
+
'param',
|
293
|
+
'picture',
|
294
|
+
'portal',
|
295
|
+
'source',
|
296
|
+
'svg',
|
297
|
+
'math',
|
298
|
+
'canvas',
|
299
|
+
'noscript',
|
300
|
+
'script',
|
301
|
+
'del',
|
302
|
+
'ins',
|
303
|
+
'caption',
|
304
|
+
'col',
|
305
|
+
'colgroup',
|
306
|
+
'table',
|
307
|
+
'tbody',
|
308
|
+
'td',
|
309
|
+
'tfoot',
|
310
|
+
'th',
|
311
|
+
'thead',
|
312
|
+
'tr',
|
313
|
+
'button',
|
314
|
+
'datalist',
|
315
|
+
'fieldset',
|
316
|
+
'form',
|
317
|
+
'input',
|
318
|
+
'label',
|
319
|
+
'legend',
|
320
|
+
'meter',
|
321
|
+
'optgroup',
|
322
|
+
'option',
|
323
|
+
'output',
|
324
|
+
'progress',
|
325
|
+
'select',
|
326
|
+
'textarea',
|
327
|
+
'details',
|
328
|
+
'dialog',
|
329
|
+
'menu',
|
330
|
+
'summary',
|
331
|
+
'details',
|
332
|
+
'slot',
|
333
|
+
'template',
|
334
|
+
// 'acronym',
|
335
|
+
// 'applet',
|
336
|
+
// 'basefont',
|
337
|
+
// 'bgsound',
|
338
|
+
// 'big',
|
339
|
+
// 'blink',
|
340
|
+
// 'center',
|
341
|
+
'content',
|
342
|
+
// 'dir',
|
343
|
+
// 'font',
|
344
|
+
'frame',
|
345
|
+
// 'frameset',
|
346
|
+
'hgroup',
|
347
|
+
'image',
|
348
|
+
// 'keygen',
|
349
|
+
// 'marquee',
|
350
|
+
'menuitem',
|
351
|
+
// 'nobr',
|
352
|
+
// 'noembed',
|
353
|
+
// 'noframes',
|
354
|
+
'plaintext',
|
355
|
+
'rb',
|
356
|
+
'rtc',
|
357
|
+
'shadow',
|
358
|
+
'spacer',
|
359
|
+
'strike',
|
360
|
+
'tt',
|
361
|
+
'xmp',
|
362
|
+
'a',
|
363
|
+
'abbr',
|
364
|
+
'acronym',
|
365
|
+
'address',
|
366
|
+
// 'applet',
|
367
|
+
'area',
|
368
|
+
'article',
|
369
|
+
'aside',
|
370
|
+
'audio',
|
371
|
+
'b',
|
372
|
+
'base',
|
373
|
+
// 'basefont',
|
374
|
+
'bdi',
|
375
|
+
'bdo',
|
376
|
+
// 'bgsound',
|
377
|
+
// 'big',
|
378
|
+
// 'blink',
|
379
|
+
'blockquote',
|
380
|
+
'body',
|
381
|
+
'br',
|
382
|
+
'button',
|
383
|
+
'canvas',
|
384
|
+
'caption',
|
385
|
+
// 'center',
|
386
|
+
'cite',
|
387
|
+
'code',
|
388
|
+
'col',
|
389
|
+
'colgroup',
|
390
|
+
'content',
|
391
|
+
'data',
|
392
|
+
'datalist',
|
393
|
+
'dd',
|
394
|
+
'del',
|
395
|
+
'details',
|
396
|
+
'dfn',
|
397
|
+
'dialog',
|
398
|
+
// 'dir',
|
399
|
+
'div',
|
400
|
+
'dl',
|
401
|
+
'dt',
|
402
|
+
'em',
|
403
|
+
// 'embed',
|
404
|
+
'fieldset',
|
405
|
+
'figcaption',
|
406
|
+
'figure',
|
407
|
+
// 'font',
|
408
|
+
'footer',
|
409
|
+
'form',
|
410
|
+
// 'frame',
|
411
|
+
// 'frameset',
|
412
|
+
'head',
|
413
|
+
'header',
|
414
|
+
'hgroup',
|
415
|
+
'hr',
|
416
|
+
'html',
|
417
|
+
'i',
|
418
|
+
'iframe',
|
419
|
+
'image',
|
420
|
+
'img',
|
421
|
+
'input',
|
422
|
+
'ins',
|
423
|
+
'kbd',
|
424
|
+
// 'keygen',
|
425
|
+
'label',
|
426
|
+
'legend',
|
427
|
+
'li',
|
428
|
+
'link',
|
429
|
+
'main',
|
430
|
+
'map',
|
431
|
+
'mark',
|
432
|
+
// 'marquee',
|
433
|
+
// 'menu',
|
434
|
+
// 'menuitem',
|
435
|
+
'meta',
|
436
|
+
'meter',
|
437
|
+
'nav',
|
438
|
+
// 'nobr',
|
439
|
+
// 'noembed',
|
440
|
+
// 'noframes',
|
441
|
+
'noscript',
|
442
|
+
'object',
|
443
|
+
'ol',
|
444
|
+
'optgroup',
|
445
|
+
'option',
|
446
|
+
'output',
|
447
|
+
'p',
|
448
|
+
// 'param',
|
449
|
+
'picture',
|
450
|
+
// 'plaintext',
|
451
|
+
'portal',
|
452
|
+
'pre',
|
453
|
+
'progress',
|
454
|
+
'q',
|
455
|
+
// 'rb',
|
456
|
+
'rp',
|
457
|
+
'rt',
|
458
|
+
// 'rtc',
|
459
|
+
'ruby',
|
460
|
+
// 's',
|
461
|
+
'samp',
|
462
|
+
'script',
|
463
|
+
'section',
|
464
|
+
'select',
|
465
|
+
'shadow',
|
466
|
+
'slot',
|
467
|
+
'small',
|
468
|
+
'source',
|
469
|
+
// 'spacer',
|
470
|
+
'span',
|
471
|
+
// 'strike',
|
472
|
+
'strong',
|
473
|
+
'style',
|
474
|
+
'sub',
|
475
|
+
'summary',
|
476
|
+
'sup',
|
477
|
+
'table',
|
478
|
+
'tbody',
|
479
|
+
'td',
|
480
|
+
'template',
|
481
|
+
'textarea',
|
482
|
+
'tfoot',
|
483
|
+
'th',
|
484
|
+
'thead',
|
485
|
+
'time',
|
486
|
+
'title',
|
487
|
+
'tr',
|
488
|
+
'track',
|
489
|
+
// 'tt',
|
490
|
+
'u',
|
491
|
+
'ul',
|
492
|
+
// 'var',
|
493
|
+
'video',
|
494
|
+
'wbr',
|
495
|
+
// 'xmp',
|
496
|
+
'input',
|
497
|
+
'h1',
|
498
|
+
'h2',
|
499
|
+
'h3',
|
500
|
+
'h4',
|
501
|
+
'h5',
|
502
|
+
'h6'
|
503
|
+
]);
|
504
|
+
|
505
|
+
export {
|
506
|
+
BooleanAttributes,
|
507
|
+
Properties,
|
508
|
+
ChildProperties,
|
509
|
+
getPropAlias,
|
510
|
+
Aliases,
|
511
|
+
DelegatedEvents,
|
512
|
+
SVGElements,
|
513
|
+
SVGNamespace,
|
514
|
+
DOMElements
|
515
|
+
};
|
@@ -0,0 +1,163 @@
|
|
1
|
+
import {signal,sample,batch,memo,root,Signal} from './reactive.ts'
|
2
|
+
|
3
|
+
/**
|
4
|
+
Show children if `when` prop is true, otherwise show `fallback`.
|
5
|
+
@group Components
|
6
|
+
*/
|
7
|
+
export function Show(props) {
|
8
|
+
const condition = memo(()=>props.when)
|
9
|
+
return memo(()=>{
|
10
|
+
const c = condition()
|
11
|
+
if (c) {
|
12
|
+
const child = props.children
|
13
|
+
const fn = typeof child === "function" && child.length > 0
|
14
|
+
return fn ? sample(() => child(() => props.when)) : child
|
15
|
+
} else return props.fallback
|
16
|
+
})
|
17
|
+
}
|
18
|
+
|
19
|
+
export function wrap(s,k) {
|
20
|
+
const t = typeof k
|
21
|
+
if (t === 'number') return (...a) => {
|
22
|
+
const b = s()
|
23
|
+
return (a.length) ? s(b.toSpliced(k, 1, a[0])).at(k) : b.at(k)
|
24
|
+
}; else if (t === 'string') return (...a) => {
|
25
|
+
const b = s()
|
26
|
+
return a.length ? s(({ ...b, [k]: a[0] }))[k] : b[k]
|
27
|
+
}; else if (t === 'function') return (...a) => {
|
28
|
+
const i = k(), c = typeof i
|
29
|
+
if (c==='number') return a.length ? s(old => old.toSpliced(i, 1, a[0]))[i] : s()[i]
|
30
|
+
else if (c === 'string') return a => a.length ? s(b => ({...b, [i]:a[0]}))[i] : s()[i]
|
31
|
+
throw new Error('Cannot wrap signal')
|
32
|
+
}
|
33
|
+
throw new Error('Cannot wrap signal')
|
34
|
+
}
|
35
|
+
|
36
|
+
/**
|
37
|
+
List
|
38
|
+
@group Components
|
39
|
+
*/
|
40
|
+
export function List(props) {
|
41
|
+
const fallback = "fallback" in props && { fallback: () => props.fallback }
|
42
|
+
const list = props.each
|
43
|
+
const cb = props.children.call ? props.children : (v)=>v
|
44
|
+
let items = [], item, unusedItems, i, j, newValue, mapped, oldIndex, oldValue,
|
45
|
+
indexes = cb.length > 1 ? [] : null;
|
46
|
+
function newValueGetter(_) { return newValue }
|
47
|
+
function changeBoth() {
|
48
|
+
item.index = i
|
49
|
+
item.indexSetter?.(i)
|
50
|
+
item.value = newValue
|
51
|
+
item.valueSetter?.(newValueGetter)
|
52
|
+
}
|
53
|
+
function mapperWithIndexes(disposer) {
|
54
|
+
const V = newValue, I = i, Is = signal(I), Vs = signal(V)
|
55
|
+
items.push({value: newValue, index: I, disposer, indexSetter: Is, valueSetter: Vs})
|
56
|
+
return cb(
|
57
|
+
(...a) => a.length ?
|
58
|
+
sample(()=>list(list=>list.toSpliced(I,1,a[0])))
|
59
|
+
: Vs(),
|
60
|
+
()=>Is())
|
61
|
+
}
|
62
|
+
function mapperWithoutIndexes(disposer) {
|
63
|
+
const V = newValue, I = i, Vs = signal(V)
|
64
|
+
items.push({value: V, index: i, disposer, valueSetter: Vs})
|
65
|
+
return cb((...a) => a.length ?
|
66
|
+
sample(()=>list(list=>list.toSpliced(I,1,a[0])))
|
67
|
+
: Vs())
|
68
|
+
}
|
69
|
+
const mapper = indexes ? mapperWithIndexes : mapperWithoutIndexes
|
70
|
+
return memo(() => {
|
71
|
+
const newItems = list() || []
|
72
|
+
// (newItems)[$TRACK]; // top level tracking
|
73
|
+
return sample(() => {
|
74
|
+
const temp = new Array(newItems.length) // new mapped array
|
75
|
+
unusedItems = items.length
|
76
|
+
|
77
|
+
// 1) no change when values & indexes match
|
78
|
+
for (j = unusedItems - 1; j >= 0; --j) {
|
79
|
+
item = items[j]
|
80
|
+
oldIndex = item.index
|
81
|
+
if (oldIndex < newItems.length && newItems[oldIndex] === item.value) {
|
82
|
+
temp[oldIndex] = mapped[oldIndex]
|
83
|
+
if (--unusedItems !== j) {
|
84
|
+
items[j] = items[unusedItems]
|
85
|
+
items[unusedItems] = item
|
86
|
+
}
|
87
|
+
}
|
88
|
+
}
|
89
|
+
|
90
|
+
// #2 prepare values matcher
|
91
|
+
const matcher = new Map()
|
92
|
+
const matchedItems = new Uint8Array(unusedItems)
|
93
|
+
for (j = unusedItems - 1; j >= 0; --j) {
|
94
|
+
oldValue = items[j].value
|
95
|
+
matcher.get(oldValue)?.push(j) ?? matcher.set(oldValue, [j])
|
96
|
+
}
|
97
|
+
|
98
|
+
// 2) change indexes when values match
|
99
|
+
for (i = 0; i < newItems.length; ++i) {
|
100
|
+
if (i in temp) continue
|
101
|
+
newValue = newItems[i]
|
102
|
+
j = matcher.get(newValue)?.pop() ?? -1
|
103
|
+
if (j >= 0) {
|
104
|
+
item = items[j]
|
105
|
+
oldIndex = item.index
|
106
|
+
temp[i] = mapped[oldIndex]
|
107
|
+
item.index = i
|
108
|
+
item.indexSetter?.(i)
|
109
|
+
matchedItems[j] = 1
|
110
|
+
}
|
111
|
+
}
|
112
|
+
|
113
|
+
// 3) reduce unusedItems for matched items
|
114
|
+
for (j = matchedItems.length - 1; j >= 0; --j) {
|
115
|
+
if (matchedItems[j] && --unusedItems !== j) {
|
116
|
+
item = items[j]
|
117
|
+
items[j] = items[unusedItems]
|
118
|
+
items[unusedItems] = item
|
119
|
+
}
|
120
|
+
}
|
121
|
+
|
122
|
+
// 4) change values when indexes match
|
123
|
+
for (j = unusedItems - 1; j >= 0; --j) {
|
124
|
+
item = items[j];
|
125
|
+
oldIndex = item.index;
|
126
|
+
if (!(oldIndex in temp) && oldIndex < newItems.length) {
|
127
|
+
temp[oldIndex] = mapped[oldIndex]
|
128
|
+
newValue = newItems[oldIndex]
|
129
|
+
item.value = newValue
|
130
|
+
item.valueSetter?.(item.valueSetter)
|
131
|
+
if (--unusedItems !== j) {
|
132
|
+
items[j] = items[unusedItems]
|
133
|
+
items[unusedItems] = item
|
134
|
+
}
|
135
|
+
}
|
136
|
+
}
|
137
|
+
|
138
|
+
// 5) change value & index when none matched and create new if no unused items left
|
139
|
+
for (i = 0; i < newItems.length; ++i) {
|
140
|
+
if (i in temp) continue
|
141
|
+
newValue = newItems[i]
|
142
|
+
if (unusedItems > 0) {
|
143
|
+
item = items[--unusedItems]
|
144
|
+
temp[i] = mapped[item.index]
|
145
|
+
batch(changeBoth);
|
146
|
+
} else {
|
147
|
+
temp[i] = root(mapper)
|
148
|
+
}
|
149
|
+
}
|
150
|
+
|
151
|
+
// 6) delete any old unused items left
|
152
|
+
disposeList(items.splice(0, unusedItems))
|
153
|
+
|
154
|
+
return (mapped = temp);
|
155
|
+
})
|
156
|
+
})
|
157
|
+
}
|
158
|
+
|
159
|
+
function disposeList(list) {
|
160
|
+
for (let i = 0; i < list.length; i++) {
|
161
|
+
list[i]?.disposer()
|
162
|
+
}
|
163
|
+
}
|