@ktjs/core 0.29.8 → 0.29.11
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/dist/index.d.ts +1469 -10
- package/dist/index.mjs +1093 -301
- package/dist/index.mjs.map +1 -0
- package/package.json +7 -7
- package/dist/h/attr-helpers.d.ts +0 -6
- package/dist/h/attr.d.ts +0 -2
- package/dist/h/content.d.ts +0 -2
- package/dist/h/index.d.ts +0 -12
- package/dist/h/model.d.ts +0 -3
- package/dist/jsx/async.d.ts +0 -13
- package/dist/jsx/common.d.ts +0 -4
- package/dist/jsx/for.d.ts +0 -14
- package/dist/jsx/fragment.d.ts +0 -35
- package/dist/jsx/if.d.ts +0 -10
- package/dist/jsx/index.d.ts +0 -2
- package/dist/jsx/index.mjs +0 -1
- package/dist/jsx/jsx-runtime.d.ts +0 -43
- package/dist/jsx/jsx-runtime.mjs +0 -2
- package/dist/jsx-runtime-DGJHUQEM.js +0 -600
- package/dist/reactive/computed.d.ts +0 -63
- package/dist/reactive/core.d.ts +0 -9
- package/dist/reactive/effect.d.ts +0 -15
- package/dist/reactive/index.d.ts +0 -10
- package/dist/reactive/ref.d.ts +0 -89
package/dist/index.mjs
CHANGED
|
@@ -1,323 +1,1115 @@
|
|
|
1
|
-
import { $
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
import { $isArray, $isThenable, $append, $isNode, $emptyFn, $is, $replaceNode, $entries, $applyModel, $forEach } from '@ktjs/shared';
|
|
2
|
+
|
|
3
|
+
const isKT = (obj) => obj?.isKT;
|
|
4
|
+
const isRef = (obj) => obj?.ktType === 1 /* KTReactiveType.REF */;
|
|
5
|
+
const isComputed = (obj) => obj?.ktType === 2 /* KTReactiveType.COMPUTED */;
|
|
6
|
+
|
|
7
|
+
const booleanHandler = (element, key, value) => {
|
|
8
|
+
if (key in element) {
|
|
9
|
+
element[key] = !!value;
|
|
10
|
+
}
|
|
11
|
+
else {
|
|
12
|
+
element.setAttribute(key, value);
|
|
13
|
+
}
|
|
14
|
+
};
|
|
15
|
+
const valueHandler = (element, key, value) => {
|
|
16
|
+
if (key in element) {
|
|
17
|
+
element[key] = value;
|
|
18
|
+
}
|
|
19
|
+
else {
|
|
20
|
+
element.setAttribute(key, value);
|
|
21
|
+
}
|
|
22
|
+
};
|
|
23
|
+
// Attribute handlers map for optimized lookup
|
|
24
|
+
const handlers = {
|
|
25
|
+
checked: booleanHandler,
|
|
26
|
+
selected: booleanHandler,
|
|
27
|
+
value: valueHandler,
|
|
28
|
+
valueAsDate: valueHandler,
|
|
29
|
+
valueAsNumber: valueHandler,
|
|
30
|
+
defaultValue: valueHandler,
|
|
31
|
+
defaultChecked: booleanHandler,
|
|
32
|
+
defaultSelected: booleanHandler,
|
|
33
|
+
disabled: booleanHandler,
|
|
34
|
+
readOnly: booleanHandler,
|
|
35
|
+
multiple: booleanHandler,
|
|
36
|
+
required: booleanHandler,
|
|
37
|
+
autofocus: booleanHandler,
|
|
38
|
+
open: booleanHandler,
|
|
39
|
+
controls: booleanHandler,
|
|
40
|
+
autoplay: booleanHandler,
|
|
41
|
+
loop: booleanHandler,
|
|
42
|
+
muted: booleanHandler,
|
|
43
|
+
defer: booleanHandler,
|
|
44
|
+
async: booleanHandler,
|
|
45
|
+
hidden: (element, _key, value) => (element.hidden = !!value),
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
const defaultHandler = (element, key, value) => element.setAttribute(key, value);
|
|
49
|
+
const setElementStyle = (element, style) => {
|
|
50
|
+
if (typeof style === 'string') {
|
|
51
|
+
element.style.cssText = style;
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
for (const key in style) {
|
|
55
|
+
element.style[key] = style[key];
|
|
56
|
+
}
|
|
57
|
+
};
|
|
58
|
+
function attrIsObject(element, attr) {
|
|
59
|
+
const classValue = attr.class || attr.className;
|
|
60
|
+
if (classValue !== undefined) {
|
|
61
|
+
if (isKT(classValue)) {
|
|
62
|
+
element.setAttribute('class', classValue.value);
|
|
63
|
+
classValue.addOnChange((v) => element.setAttribute('class', v));
|
|
64
|
+
}
|
|
65
|
+
else {
|
|
66
|
+
element.setAttribute('class', classValue);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
const style = attr.style;
|
|
70
|
+
if (style) {
|
|
71
|
+
if (typeof style === 'string') {
|
|
72
|
+
element.setAttribute('style', style);
|
|
73
|
+
}
|
|
74
|
+
else if (typeof style === 'object') {
|
|
75
|
+
if (isKT(style)) {
|
|
76
|
+
setElementStyle(element, style.value);
|
|
77
|
+
style.addOnChange((v) => setElementStyle(element, v));
|
|
78
|
+
}
|
|
79
|
+
else {
|
|
80
|
+
setElementStyle(element, style);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
if ('k-html' in attr) {
|
|
85
|
+
const html = attr['k-html'];
|
|
86
|
+
if (isKT(html)) {
|
|
87
|
+
element.innerHTML = html.value;
|
|
88
|
+
html.addOnChange((v) => (element.innerHTML = v));
|
|
89
|
+
}
|
|
90
|
+
else {
|
|
91
|
+
element.innerHTML = html;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
for (const key in attr) {
|
|
95
|
+
// & Arranged in order of usage frequency
|
|
96
|
+
if (key === 'k-if' ||
|
|
97
|
+
key === 'k-model' ||
|
|
98
|
+
key === 'k-for' ||
|
|
99
|
+
key === 'k-key' ||
|
|
100
|
+
key === 'ref' ||
|
|
101
|
+
key === 'class' ||
|
|
102
|
+
key === 'className' ||
|
|
103
|
+
key === 'style' ||
|
|
104
|
+
key === 'children' ||
|
|
105
|
+
key === 'k-html' ||
|
|
106
|
+
key === 'k-else') {
|
|
107
|
+
continue;
|
|
108
|
+
}
|
|
109
|
+
const o = attr[key];
|
|
110
|
+
// normal event handler
|
|
111
|
+
if (key.startsWith('on:')) {
|
|
112
|
+
element.addEventListener(key.slice(3), o); // chop off the `on:`
|
|
113
|
+
}
|
|
114
|
+
// normal attributes
|
|
115
|
+
else {
|
|
116
|
+
const handler = handlers[key] || defaultHandler;
|
|
117
|
+
if (isKT(o)) {
|
|
118
|
+
handler(element, key, o.value);
|
|
119
|
+
o.addOnChange((v) => handler(element, key, v));
|
|
120
|
+
}
|
|
121
|
+
else {
|
|
122
|
+
handler(element, key, o);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
function applyAttr(element, attr) {
|
|
128
|
+
if (!attr) {
|
|
129
|
+
return;
|
|
130
|
+
}
|
|
131
|
+
if (typeof attr === 'object' && attr !== null) {
|
|
132
|
+
attrIsObject(element, attr);
|
|
133
|
+
}
|
|
134
|
+
else {
|
|
135
|
+
$throw('attr must be an object.');
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
const assureNode = (o) => ($isNode(o) ? o : document.createTextNode(o));
|
|
140
|
+
function apdSingle(element, c) {
|
|
141
|
+
// & JSX should ignore false, undefined, and null
|
|
142
|
+
if (c === false || c === undefined || c === null) {
|
|
143
|
+
return;
|
|
144
|
+
}
|
|
145
|
+
if (isKT(c)) {
|
|
146
|
+
let node = assureNode(c.value);
|
|
147
|
+
$append.call(element, node);
|
|
148
|
+
c.addOnChange((newValue, oldValue) => {
|
|
149
|
+
if ($isNode(newValue) && $isNode(oldValue)) {
|
|
150
|
+
// & this case is handled automically in `class KTRef`
|
|
151
|
+
return;
|
|
152
|
+
}
|
|
153
|
+
const oldNode = node;
|
|
154
|
+
node = assureNode(newValue);
|
|
155
|
+
oldNode.replaceWith(node);
|
|
156
|
+
});
|
|
157
|
+
}
|
|
158
|
+
else {
|
|
159
|
+
$append.call(element, c);
|
|
160
|
+
// Handle KTFor anchor
|
|
161
|
+
const list = c.__kt_for_list__;
|
|
162
|
+
if ($isArray(list)) {
|
|
163
|
+
apd(element, list);
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
function apd(element, c) {
|
|
168
|
+
if ($isThenable(c)) {
|
|
169
|
+
c.then((r) => apd(element, r));
|
|
170
|
+
}
|
|
171
|
+
else if ($isArray(c)) {
|
|
172
|
+
for (let i = 0; i < c.length; i++) {
|
|
173
|
+
// & might be thenable here too
|
|
174
|
+
const ci = c[i];
|
|
175
|
+
if ($isThenable(ci)) {
|
|
176
|
+
const comment = document.createComment('ktjs-promise-placeholder');
|
|
177
|
+
$append.call(element, comment);
|
|
178
|
+
ci.then((awaited) => comment.replaceWith(awaited));
|
|
179
|
+
}
|
|
180
|
+
else {
|
|
181
|
+
apdSingle(element, ci);
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
else {
|
|
186
|
+
// & here is thened, so must be a simple elementj
|
|
187
|
+
apdSingle(element, c);
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
function applyContent(element, content) {
|
|
191
|
+
if ($isArray(content)) {
|
|
192
|
+
for (let i = 0; i < content.length; i++) {
|
|
193
|
+
apd(element, content[i]);
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
else {
|
|
197
|
+
apd(element, content);
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
class KTRef {
|
|
202
|
+
/**
|
|
203
|
+
* Indicates that this is a KTRef instance
|
|
204
|
+
*/
|
|
205
|
+
isKT = true;
|
|
206
|
+
ktType = 1 /* KTReactiveType.REF */;
|
|
207
|
+
/**
|
|
208
|
+
* @internal
|
|
209
|
+
*/
|
|
210
|
+
_value;
|
|
211
|
+
/**
|
|
212
|
+
* @internal
|
|
213
|
+
*/
|
|
214
|
+
_onChanges;
|
|
215
|
+
/**
|
|
216
|
+
* @internal
|
|
217
|
+
*/
|
|
218
|
+
_emit(newValue, oldValue) {
|
|
219
|
+
for (let i = 0; i < this._onChanges.length; i++) {
|
|
220
|
+
this._onChanges[i](newValue, oldValue);
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
constructor(_value, _onChanges) {
|
|
224
|
+
this._value = _value;
|
|
225
|
+
this._onChanges = _onChanges;
|
|
226
|
+
}
|
|
227
|
+
/**
|
|
228
|
+
* If new value and old value are both nodes, the old one will be replaced in the DOM
|
|
229
|
+
*/
|
|
230
|
+
get value() {
|
|
231
|
+
return this._value;
|
|
232
|
+
}
|
|
233
|
+
set value(newValue) {
|
|
234
|
+
if ($is(newValue, this._value)) {
|
|
235
|
+
return;
|
|
236
|
+
}
|
|
237
|
+
const oldValue = this._value;
|
|
238
|
+
$replaceNode(oldValue, newValue);
|
|
239
|
+
this._value = newValue;
|
|
240
|
+
this._emit(newValue, oldValue);
|
|
241
|
+
}
|
|
242
|
+
/**
|
|
243
|
+
* Force all listeners to run even when reference identity has not changed.
|
|
244
|
+
* Useful for in-place array/object mutations.
|
|
245
|
+
*/
|
|
246
|
+
notify() {
|
|
247
|
+
this._emit(this._value, this._value);
|
|
248
|
+
}
|
|
249
|
+
/**
|
|
250
|
+
* Mutate current value in-place and notify listeners once.
|
|
251
|
+
*
|
|
252
|
+
* @example
|
|
253
|
+
* const items = ref<number[]>([1, 2]);
|
|
254
|
+
* items.mutate((list) => list.push(3));
|
|
255
|
+
*/
|
|
256
|
+
mutate(mutator) {
|
|
257
|
+
if (typeof mutator !== 'function') {
|
|
258
|
+
$throw('KTRef.mutate: mutator must be a function');
|
|
259
|
+
}
|
|
260
|
+
const oldValue = this._value;
|
|
261
|
+
const result = mutator(this._value);
|
|
262
|
+
this._emit(this._value, oldValue);
|
|
263
|
+
return result;
|
|
264
|
+
}
|
|
265
|
+
/**
|
|
266
|
+
* Register a callback when the value changes
|
|
267
|
+
* @param callback (newValue, oldValue) => xxx
|
|
268
|
+
*/
|
|
269
|
+
addOnChange(callback) {
|
|
270
|
+
if (typeof callback !== 'function') {
|
|
271
|
+
$throw('KTRef.addOnChange: callback must be a function');
|
|
272
|
+
}
|
|
273
|
+
this._onChanges.push(callback);
|
|
274
|
+
}
|
|
275
|
+
removeOnChange(callback) {
|
|
276
|
+
for (let i = this._onChanges.length - 1; i >= 0; i--) {
|
|
277
|
+
if (this._onChanges[i] === callback) {
|
|
278
|
+
this._onChanges.splice(i, 1);
|
|
279
|
+
return true;
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
return false;
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
/**
|
|
286
|
+
* Reference to the created HTML element.
|
|
287
|
+
* - **Only** respond to `ref.value` changes, not reactive to internal changes of the element.
|
|
288
|
+
* - can alse be used to store normal values, but it is not reactive.
|
|
289
|
+
* - if the value is already a `KTRef`, it will be returned **directly**.
|
|
290
|
+
* @param value mostly an HTMLElement
|
|
291
|
+
*/
|
|
292
|
+
function ref(value, onChange) {
|
|
293
|
+
return new KTRef(value, onChange ? [onChange] : []);
|
|
294
|
+
}
|
|
295
|
+
/**
|
|
296
|
+
* Convert a value to `KTRef`.
|
|
297
|
+
* - Returns the original value if it is already a `KTRef`.
|
|
298
|
+
* - Throws error if the value is a `KTComputed`.
|
|
299
|
+
* - Otherwise wraps the value with `ref()`.
|
|
300
|
+
* @param o value to convert
|
|
301
|
+
*/
|
|
302
|
+
const toRef = (o) => {
|
|
303
|
+
if (isRef(o)) {
|
|
304
|
+
return o;
|
|
305
|
+
}
|
|
306
|
+
else if (isComputed(o)) {
|
|
307
|
+
$throw('Computed values cannot be used as KTRef.');
|
|
308
|
+
}
|
|
309
|
+
else {
|
|
310
|
+
return ref(o);
|
|
311
|
+
}
|
|
312
|
+
};
|
|
313
|
+
function kcollect() {
|
|
314
|
+
const newObj = {};
|
|
315
|
+
const entries = $entries(this);
|
|
316
|
+
for (let i = 0; i < entries.length; i++) {
|
|
317
|
+
const key = entries[i][0];
|
|
318
|
+
if (key === 'kcollect') {
|
|
319
|
+
continue;
|
|
320
|
+
}
|
|
321
|
+
newObj[key] = entries[i][1].value;
|
|
322
|
+
}
|
|
323
|
+
return newObj;
|
|
324
|
+
}
|
|
325
|
+
/**
|
|
326
|
+
* Make all first-level properties of the object a `KTRef`.
|
|
327
|
+
* - `obj.a.b` is not reactive
|
|
328
|
+
*/
|
|
329
|
+
const surfaceRef = (obj) => {
|
|
330
|
+
const entries = $entries(obj);
|
|
331
|
+
const newObj = { kcollect };
|
|
332
|
+
for (let i = 0; i < entries.length; i++) {
|
|
333
|
+
newObj[entries[i][0]] = ref(entries[i][1]);
|
|
334
|
+
}
|
|
335
|
+
return newObj;
|
|
336
|
+
};
|
|
337
|
+
// # asserts
|
|
338
|
+
/**
|
|
339
|
+
* Assert k-model to be a ref object
|
|
340
|
+
*/
|
|
341
|
+
const $modelOrRef = (props, defaultValue) => {
|
|
342
|
+
// & props is an object. Won't use it in any other place
|
|
343
|
+
if ('k-model' in props) {
|
|
344
|
+
const kmodel = props['k-model'];
|
|
345
|
+
if (isRef(kmodel)) {
|
|
346
|
+
return kmodel;
|
|
347
|
+
}
|
|
348
|
+
else {
|
|
349
|
+
$throw(`k-model data must be a KTRef object, please use 'ref(...)' to wrap it.`);
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
return ref(defaultValue);
|
|
353
|
+
};
|
|
354
|
+
const $refSetter = (props, node) => (props.ref.value = node);
|
|
355
|
+
/**
|
|
356
|
+
* Whether `props.ref` is a `KTRef` only needs to be checked in the initial render
|
|
357
|
+
*/
|
|
358
|
+
const $initRef = (props, node) => {
|
|
359
|
+
if (!('ref' in props)) {
|
|
360
|
+
return $emptyFn;
|
|
361
|
+
}
|
|
362
|
+
const r = props.ref;
|
|
363
|
+
if (isRef(r)) {
|
|
364
|
+
r.value = node;
|
|
365
|
+
return $refSetter;
|
|
366
|
+
}
|
|
367
|
+
else {
|
|
368
|
+
$throw('Fragment: ref must be a KTRef');
|
|
369
|
+
}
|
|
370
|
+
};
|
|
4
371
|
|
|
5
372
|
class KTComputed {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
373
|
+
/**
|
|
374
|
+
* Indicates that this is a KTRef instance
|
|
375
|
+
*/
|
|
376
|
+
isKT = true;
|
|
377
|
+
ktType = 2 /* KTReactiveType.COMPUTED */;
|
|
378
|
+
/**
|
|
379
|
+
* @internal
|
|
380
|
+
*/
|
|
381
|
+
_calculator;
|
|
382
|
+
/**
|
|
383
|
+
* @internal
|
|
384
|
+
*/
|
|
385
|
+
_value;
|
|
386
|
+
/**
|
|
387
|
+
* @internal
|
|
388
|
+
*/
|
|
389
|
+
_onChanges = [];
|
|
390
|
+
/**
|
|
391
|
+
* @internal
|
|
392
|
+
*/
|
|
393
|
+
_emit(newValue, oldValue) {
|
|
394
|
+
for (let i = 0; i < this._onChanges.length; i++) {
|
|
395
|
+
this._onChanges[i](newValue, oldValue);
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
/**
|
|
399
|
+
* @internal
|
|
400
|
+
*/
|
|
401
|
+
_recalculate(forceEmit = false) {
|
|
402
|
+
const oldValue = this._value;
|
|
403
|
+
const newValue = this._calculator();
|
|
404
|
+
if (oldValue === newValue) {
|
|
405
|
+
if (forceEmit) {
|
|
406
|
+
this._emit(newValue, oldValue);
|
|
407
|
+
}
|
|
408
|
+
return;
|
|
409
|
+
}
|
|
410
|
+
this._value = newValue;
|
|
411
|
+
$replaceNode(oldValue, newValue);
|
|
39
412
|
this._emit(newValue, oldValue);
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
this._onChanges.splice(i, 1);
|
|
101
|
-
return true;
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
return false;
|
|
105
|
-
}
|
|
413
|
+
}
|
|
414
|
+
/**
|
|
415
|
+
* @internal
|
|
416
|
+
*/
|
|
417
|
+
_subscribe(reactives) {
|
|
418
|
+
for (let i = 0; i < reactives.length; i++) {
|
|
419
|
+
const reactive = reactives[i];
|
|
420
|
+
reactive.addOnChange(() => this._recalculate());
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
constructor(_calculator, reactives) {
|
|
424
|
+
this._calculator = _calculator;
|
|
425
|
+
this._value = _calculator();
|
|
426
|
+
this._subscribe(reactives);
|
|
427
|
+
}
|
|
428
|
+
/**
|
|
429
|
+
* If new value and old value are both nodes, the old one will be replaced in the DOM
|
|
430
|
+
*/
|
|
431
|
+
get value() {
|
|
432
|
+
return this._value;
|
|
433
|
+
}
|
|
434
|
+
set value(_newValue) {
|
|
435
|
+
$throw('KTComputed: cannot set value of a computed value');
|
|
436
|
+
}
|
|
437
|
+
/**
|
|
438
|
+
* Force listeners to run once with the latest computed result.
|
|
439
|
+
*/
|
|
440
|
+
notify() {
|
|
441
|
+
this._recalculate(true);
|
|
442
|
+
}
|
|
443
|
+
/**
|
|
444
|
+
* Computed values are derived from dependencies and should not be mutated manually.
|
|
445
|
+
*/
|
|
446
|
+
mutate() {
|
|
447
|
+
$warn('KTComputed.mutate: computed is derived automatically; manual mutate is ignored. Use notify() instead');
|
|
448
|
+
return this._value;
|
|
449
|
+
}
|
|
450
|
+
/**
|
|
451
|
+
* Register a callback when the value changes
|
|
452
|
+
* @param callback (newValue, oldValue) => xxx
|
|
453
|
+
*/
|
|
454
|
+
addOnChange(callback) {
|
|
455
|
+
if (typeof callback !== 'function') {
|
|
456
|
+
$throw('KTRef.addOnChange: callback must be a function');
|
|
457
|
+
}
|
|
458
|
+
this._onChanges.push(callback);
|
|
459
|
+
}
|
|
460
|
+
/**
|
|
461
|
+
* Unregister a callback
|
|
462
|
+
* @param callback (newValue, oldValue) => xxx
|
|
463
|
+
*/
|
|
464
|
+
removeOnChange(callback) {
|
|
465
|
+
for (let i = this._onChanges.length - 1; i >= 0; i--) {
|
|
466
|
+
if (this._onChanges[i] === callback) {
|
|
467
|
+
this._onChanges.splice(i, 1);
|
|
468
|
+
return true;
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
return false;
|
|
472
|
+
}
|
|
106
473
|
}
|
|
474
|
+
/**
|
|
475
|
+
* Create a reactive computed value
|
|
476
|
+
* @param computeFn
|
|
477
|
+
* @param reactives refs and computeds that this computed depends on
|
|
478
|
+
*/
|
|
107
479
|
function computed(computeFn, reactives) {
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
480
|
+
if (reactives.some((v) => !isKT(v))) {
|
|
481
|
+
$throw('computed: all reactives must be KTRef or KTComputed instances');
|
|
482
|
+
}
|
|
483
|
+
return new KTComputed(computeFn, reactives);
|
|
112
484
|
}
|
|
113
485
|
|
|
486
|
+
/**
|
|
487
|
+
* Register a reactive effect with options.
|
|
488
|
+
* @param effectFn The effect function to run when dependencies change
|
|
489
|
+
* @param reactives The reactive dependencies
|
|
490
|
+
* @param options Effect options: lazy, onCleanup, debugName
|
|
491
|
+
* @returns stop function to remove all listeners
|
|
492
|
+
*/
|
|
114
493
|
function effect(effectFn, reactives, options) {
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
if (!lazy) {
|
|
132
|
-
run();
|
|
133
|
-
}
|
|
134
|
-
return () => {
|
|
135
|
-
if (!active) {
|
|
136
|
-
return;
|
|
137
|
-
}
|
|
138
|
-
active = false;
|
|
494
|
+
const { lazy = false, onCleanup = $emptyFn, debugName = '' } = Object(options);
|
|
495
|
+
let active = true;
|
|
496
|
+
const run = () => {
|
|
497
|
+
if (!active) {
|
|
498
|
+
return;
|
|
499
|
+
}
|
|
500
|
+
// cleanup before rerun
|
|
501
|
+
onCleanup();
|
|
502
|
+
try {
|
|
503
|
+
effectFn();
|
|
504
|
+
}
|
|
505
|
+
catch (err) {
|
|
506
|
+
$debug('effect error:', debugName, err);
|
|
507
|
+
}
|
|
508
|
+
};
|
|
509
|
+
// subscribe to dependencies
|
|
139
510
|
for (let i = 0; i < reactives.length; i++) {
|
|
140
|
-
|
|
511
|
+
reactives[i].addOnChange(run);
|
|
512
|
+
}
|
|
513
|
+
// auto run unless lazy
|
|
514
|
+
if (!lazy) {
|
|
515
|
+
run();
|
|
516
|
+
}
|
|
517
|
+
// stop function
|
|
518
|
+
return () => {
|
|
519
|
+
if (!active) {
|
|
520
|
+
return;
|
|
521
|
+
}
|
|
522
|
+
active = false;
|
|
523
|
+
for (let i = 0; i < reactives.length; i++) {
|
|
524
|
+
reactives[i].removeOnChange(run);
|
|
525
|
+
}
|
|
526
|
+
// final cleanup
|
|
527
|
+
onCleanup();
|
|
528
|
+
};
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
const toReactive = (value, onChange) => {
|
|
532
|
+
if (isKT(value)) {
|
|
533
|
+
if (onChange) {
|
|
534
|
+
value.addOnChange(onChange);
|
|
535
|
+
}
|
|
536
|
+
return value;
|
|
537
|
+
}
|
|
538
|
+
else {
|
|
539
|
+
return ref(value, onChange);
|
|
540
|
+
}
|
|
541
|
+
};
|
|
542
|
+
/**
|
|
543
|
+
* Extracts the value from a KTReactive, or returns the value directly if it's not reactive.
|
|
544
|
+
*/
|
|
545
|
+
function dereactive(value) {
|
|
546
|
+
return isKT(value) ? value.value : value;
|
|
547
|
+
}
|
|
548
|
+
|
|
549
|
+
function applyKModel(element, valueRef) {
|
|
550
|
+
if (!isKT(valueRef)) {
|
|
551
|
+
$warn('k-model value must be a KTRef.');
|
|
552
|
+
return;
|
|
553
|
+
}
|
|
554
|
+
if (element instanceof HTMLInputElement) {
|
|
555
|
+
if (element.type === 'radio' || element.type === 'checkbox') {
|
|
556
|
+
$applyModel(element, valueRef, 'checked', 'change');
|
|
557
|
+
}
|
|
558
|
+
else {
|
|
559
|
+
$applyModel(element, valueRef, 'value', 'input');
|
|
560
|
+
}
|
|
561
|
+
}
|
|
562
|
+
else if (element instanceof HTMLSelectElement) {
|
|
563
|
+
$applyModel(element, valueRef, 'value', 'change');
|
|
564
|
+
}
|
|
565
|
+
else if (element instanceof HTMLTextAreaElement) {
|
|
566
|
+
$applyModel(element, valueRef, 'value', 'input');
|
|
567
|
+
}
|
|
568
|
+
else {
|
|
569
|
+
$warn('not supported element for k-model:');
|
|
570
|
+
}
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
const htmlCreator = (tag) => document.createElement(tag);
|
|
574
|
+
const svgCreator = (tag) => document.createElementNS('http://www.w3.org/2000/svg', tag);
|
|
575
|
+
const mathMLCreator = (tag) => document.createElementNS('http://www.w3.org/1998/Math/MathML', tag);
|
|
576
|
+
let creator = htmlCreator;
|
|
577
|
+
/**
|
|
578
|
+
* Create an enhanced HTMLElement.
|
|
579
|
+
* - Only supports HTMLElements, **NOT** SVGElements or other Elements.
|
|
580
|
+
* @param tag tag of an `HTMLElement`
|
|
581
|
+
* @param attr attribute object or className
|
|
582
|
+
* @param content a string or an array of HTMLEnhancedElement as child nodes
|
|
583
|
+
*
|
|
584
|
+
* __PKG_INFO__
|
|
585
|
+
*/
|
|
586
|
+
const h = (tag, attr, content) => {
|
|
587
|
+
if (typeof tag !== 'string') {
|
|
588
|
+
$throw('tagName must be a string.');
|
|
589
|
+
}
|
|
590
|
+
if (attr) {
|
|
591
|
+
if (flags.svg in attr) {
|
|
592
|
+
delete attr[flags.svg];
|
|
593
|
+
creator = svgCreator;
|
|
594
|
+
}
|
|
595
|
+
else if (flags.mathml in attr) {
|
|
596
|
+
delete attr[flags.mathml];
|
|
597
|
+
creator = mathMLCreator;
|
|
598
|
+
}
|
|
599
|
+
else {
|
|
600
|
+
creator = htmlCreator;
|
|
601
|
+
}
|
|
602
|
+
}
|
|
603
|
+
// * start creating the element
|
|
604
|
+
const element = creator(tag);
|
|
605
|
+
// * Handle content
|
|
606
|
+
applyAttr(element, attr);
|
|
607
|
+
applyContent(element, content);
|
|
608
|
+
if (typeof attr === 'object' && attr !== null && 'k-model' in attr) {
|
|
609
|
+
applyKModel(element, attr['k-model']);
|
|
610
|
+
}
|
|
611
|
+
return element;
|
|
612
|
+
};
|
|
613
|
+
|
|
614
|
+
const FRAGMENT_MOUNT_PATCHED = '__kt_fragment_mount_patched__';
|
|
615
|
+
const FRAGMENT_MOUNT = '__kt_fragment_mount__';
|
|
616
|
+
if (typeof Node !== 'undefined' && !globalThis[FRAGMENT_MOUNT_PATCHED]) {
|
|
617
|
+
globalThis[FRAGMENT_MOUNT_PATCHED] = true;
|
|
618
|
+
const originAppendChild = Node.prototype.appendChild;
|
|
619
|
+
Node.prototype.appendChild = function (node) {
|
|
620
|
+
const result = originAppendChild.call(this, node);
|
|
621
|
+
const mount = node[FRAGMENT_MOUNT];
|
|
622
|
+
if (typeof mount === 'function') {
|
|
623
|
+
mount();
|
|
624
|
+
}
|
|
625
|
+
return result;
|
|
626
|
+
};
|
|
627
|
+
const originInsertBefore = Node.prototype.insertBefore;
|
|
628
|
+
Node.prototype.insertBefore = function (node, child) {
|
|
629
|
+
const result = originInsertBefore.call(this, node, child);
|
|
630
|
+
const mount = node[FRAGMENT_MOUNT];
|
|
631
|
+
if (typeof mount === 'function') {
|
|
632
|
+
mount();
|
|
633
|
+
}
|
|
634
|
+
return result;
|
|
635
|
+
};
|
|
636
|
+
}
|
|
637
|
+
/**
|
|
638
|
+
* Fragment - Container component for managing arrays of child elements
|
|
639
|
+
*
|
|
640
|
+
* Features:
|
|
641
|
+
* 1. Returns a comment anchor node, child elements are inserted after the anchor
|
|
642
|
+
* 2. Supports reactive arrays, automatically updates DOM when array changes
|
|
643
|
+
* 3. Basic version uses simple replacement algorithm (remove all old elements, insert all new elements)
|
|
644
|
+
* 4. Future enhancement: key-based optimization
|
|
645
|
+
*
|
|
646
|
+
* Usage example:
|
|
647
|
+
* ```tsx
|
|
648
|
+
* const children = ref([<div>A</div>, <div>B</div>]);
|
|
649
|
+
* const fragment = <Fragment children={children} />;
|
|
650
|
+
* document.body.appendChild(fragment);
|
|
651
|
+
*
|
|
652
|
+
* // Automatic update
|
|
653
|
+
* children.value = [<div>C</div>, <div>D</div>];
|
|
654
|
+
* ```
|
|
655
|
+
*/
|
|
656
|
+
function Fragment$1(props) {
|
|
657
|
+
const elements = [];
|
|
658
|
+
const anchor = document.createComment('kt-fragment');
|
|
659
|
+
let inserted = false;
|
|
660
|
+
let observer;
|
|
661
|
+
const redraw = () => {
|
|
662
|
+
const newElements = childrenRef.value;
|
|
663
|
+
const parent = anchor.parentNode;
|
|
664
|
+
if (!parent) {
|
|
665
|
+
elements.length = 0;
|
|
666
|
+
for (let i = 0; i < newElements.length; i++) {
|
|
667
|
+
elements.push(newElements[i]);
|
|
668
|
+
}
|
|
669
|
+
anchor.__kt_fragment_list__ = elements;
|
|
670
|
+
return;
|
|
671
|
+
}
|
|
672
|
+
for (let i = 0; i < elements.length; i++) {
|
|
673
|
+
elements[i].remove();
|
|
674
|
+
}
|
|
675
|
+
const fragment = document.createDocumentFragment();
|
|
676
|
+
elements.length = 0;
|
|
677
|
+
for (let i = 0; i < newElements.length; i++) {
|
|
678
|
+
const element = newElements[i];
|
|
679
|
+
elements.push(element);
|
|
680
|
+
fragment.appendChild(element);
|
|
681
|
+
}
|
|
682
|
+
parent.insertBefore(fragment, anchor.nextSibling);
|
|
683
|
+
inserted = true;
|
|
684
|
+
delete anchor[FRAGMENT_MOUNT];
|
|
685
|
+
observer?.disconnect();
|
|
686
|
+
observer = undefined;
|
|
687
|
+
anchor.__kt_fragment_list__ = elements;
|
|
688
|
+
};
|
|
689
|
+
const childrenRef = toReactive(props.children, redraw);
|
|
690
|
+
const renderInitial = () => {
|
|
691
|
+
const current = childrenRef.value;
|
|
692
|
+
elements.length = 0;
|
|
693
|
+
const fragment = document.createDocumentFragment();
|
|
694
|
+
for (let i = 0; i < current.length; i++) {
|
|
695
|
+
const element = current[i];
|
|
696
|
+
elements.push(element);
|
|
697
|
+
fragment.appendChild(element);
|
|
698
|
+
}
|
|
699
|
+
anchor.__kt_fragment_list__ = elements;
|
|
700
|
+
const parent = anchor.parentNode;
|
|
701
|
+
if (parent && !inserted) {
|
|
702
|
+
parent.insertBefore(fragment, anchor.nextSibling);
|
|
703
|
+
inserted = true;
|
|
704
|
+
}
|
|
705
|
+
};
|
|
706
|
+
renderInitial();
|
|
707
|
+
anchor[FRAGMENT_MOUNT] = () => {
|
|
708
|
+
if (!inserted && anchor.parentNode) {
|
|
709
|
+
redraw();
|
|
710
|
+
}
|
|
711
|
+
};
|
|
712
|
+
observer = new MutationObserver(() => {
|
|
713
|
+
if (anchor.parentNode && !inserted) {
|
|
714
|
+
redraw();
|
|
715
|
+
observer?.disconnect();
|
|
716
|
+
observer = undefined;
|
|
717
|
+
}
|
|
718
|
+
});
|
|
719
|
+
observer.observe(document.body, { childList: true, subtree: true });
|
|
720
|
+
$initRef(props, anchor);
|
|
721
|
+
return anchor;
|
|
722
|
+
}
|
|
723
|
+
/**
|
|
724
|
+
* Convert KTRawContent to HTMLElement array
|
|
725
|
+
*/
|
|
726
|
+
function convertChildrenToElements(children) {
|
|
727
|
+
const elements = [];
|
|
728
|
+
const processChild = (child) => {
|
|
729
|
+
if (child === undefined || child === null || child === false || child === true) {
|
|
730
|
+
// Ignore null, undefined, false, true
|
|
731
|
+
return;
|
|
732
|
+
}
|
|
733
|
+
if ($isArray(child)) {
|
|
734
|
+
// Recursively process array
|
|
735
|
+
$forEach(child, processChild);
|
|
736
|
+
return;
|
|
737
|
+
}
|
|
738
|
+
if (typeof child === 'string' || typeof child === 'number') {
|
|
739
|
+
const span = document.createElement('span');
|
|
740
|
+
span.textContent = String(child);
|
|
741
|
+
elements.push(span);
|
|
742
|
+
return;
|
|
743
|
+
}
|
|
744
|
+
if (child instanceof HTMLElement) {
|
|
745
|
+
elements.push(child);
|
|
746
|
+
return;
|
|
747
|
+
}
|
|
748
|
+
if (isKT(child)) {
|
|
749
|
+
processChild(child.value);
|
|
750
|
+
return;
|
|
751
|
+
}
|
|
752
|
+
console.warn('Fragment: unsupported child type', child);
|
|
753
|
+
};
|
|
754
|
+
processChild(children);
|
|
755
|
+
return elements;
|
|
756
|
+
}
|
|
757
|
+
|
|
758
|
+
const jsxh = (tag, props) => {
|
|
759
|
+
if (typeof tag === 'function') {
|
|
760
|
+
return tag(props);
|
|
761
|
+
}
|
|
762
|
+
else {
|
|
763
|
+
return h(tag, props, props.children);
|
|
764
|
+
}
|
|
765
|
+
};
|
|
766
|
+
const placeholder = (data) => document.createComment(data);
|
|
767
|
+
|
|
768
|
+
// # Only supports k-if/k-else, not k-else-if
|
|
769
|
+
function kif(tag, props) {
|
|
770
|
+
const kif = toReactive(props['k-if']);
|
|
771
|
+
let el = (kif.value ? jsxh(tag, props) : placeholder('k-if'));
|
|
772
|
+
el.__kif__ = kif;
|
|
773
|
+
const setter = $initRef(props, el);
|
|
774
|
+
kif.addOnChange((newValue) => {
|
|
775
|
+
const old = el;
|
|
776
|
+
el = (newValue ? jsxh(tag, props) : placeholder('k-if'));
|
|
777
|
+
el.__kif__ = kif;
|
|
778
|
+
setter(props, el);
|
|
779
|
+
$replaceNode(old, el);
|
|
780
|
+
});
|
|
781
|
+
return el;
|
|
782
|
+
}
|
|
783
|
+
function kelse(tag, props) {
|
|
784
|
+
// k-else is not rendered by default
|
|
785
|
+
// until its corresponding k-if is true, then it will be rendered
|
|
786
|
+
// this will be detected when dealing with `children`
|
|
787
|
+
let el = placeholder('k-else');
|
|
788
|
+
const setter = $initRef(props, el);
|
|
789
|
+
el.__kelse__ = (newValue) => {
|
|
790
|
+
const old = el;
|
|
791
|
+
// reverse logic of `if`
|
|
792
|
+
el = (newValue ? placeholder('k-else') : jsxh(tag, props));
|
|
793
|
+
el.__kelse__ = old.__kelse__; // inherit
|
|
794
|
+
setter(props, el);
|
|
795
|
+
$replaceNode(old, el);
|
|
796
|
+
};
|
|
797
|
+
return el;
|
|
798
|
+
}
|
|
799
|
+
/**
|
|
800
|
+
* Deal with `k-if` and `k-else`, checking syntax and applying listeners
|
|
801
|
+
* @param el parent element
|
|
802
|
+
*/
|
|
803
|
+
function kifelseApply(el) {
|
|
804
|
+
const childNodes = el.childNodes;
|
|
805
|
+
// & We only need to check for k-else
|
|
806
|
+
if (childNodes.length === 0) {
|
|
807
|
+
return;
|
|
808
|
+
}
|
|
809
|
+
if (childNodes[0].__kelse__) {
|
|
810
|
+
$throw('k-else cannot be the first child of its parent element.');
|
|
811
|
+
}
|
|
812
|
+
for (let i = 1; i < childNodes.length; i++) {
|
|
813
|
+
const child = childNodes[i];
|
|
814
|
+
if (!child.__kelse__) {
|
|
815
|
+
continue;
|
|
816
|
+
}
|
|
817
|
+
const last = childNodes[i - 1];
|
|
818
|
+
if (!('__kif__' in last)) {
|
|
819
|
+
$throw('k-else must be immediately preceded by a k-if element.');
|
|
820
|
+
}
|
|
821
|
+
// falsy __kif__, not rendering
|
|
822
|
+
const kif = last.__kif__;
|
|
823
|
+
if (!kif) {
|
|
824
|
+
continue;
|
|
825
|
+
}
|
|
826
|
+
if (!isKT(kif)) {
|
|
827
|
+
continue;
|
|
828
|
+
}
|
|
829
|
+
kif.addOnChange(child.__kelse__);
|
|
830
|
+
if (!kif.value) {
|
|
831
|
+
child.__kelse__(false);
|
|
832
|
+
}
|
|
141
833
|
}
|
|
142
|
-
|
|
143
|
-
|
|
834
|
+
}
|
|
835
|
+
|
|
836
|
+
/**
|
|
837
|
+
* @param tag html tag or function component
|
|
838
|
+
* @param props properties/attributes
|
|
839
|
+
*/
|
|
840
|
+
function jsx(tag, props) {
|
|
841
|
+
if (isComputed(props.ref)) {
|
|
842
|
+
$throw('Cannot assign a computed value to an element.');
|
|
843
|
+
}
|
|
844
|
+
if ('k-if' in props) {
|
|
845
|
+
return kif(tag, props);
|
|
846
|
+
}
|
|
847
|
+
if ('k-else' in props) {
|
|
848
|
+
return kelse(tag, props);
|
|
849
|
+
}
|
|
850
|
+
const el = jsxh(tag, props);
|
|
851
|
+
$initRef(props, el);
|
|
852
|
+
kifelseApply(el);
|
|
853
|
+
return el;
|
|
854
|
+
}
|
|
855
|
+
/**
|
|
856
|
+
* Fragment support - returns an array of children
|
|
857
|
+
* Enhanced Fragment component that manages arrays of elements
|
|
858
|
+
*/
|
|
859
|
+
function Fragment(props) {
|
|
860
|
+
const { children } = props ?? {};
|
|
861
|
+
if (!children) {
|
|
862
|
+
return placeholder('kt-fragment-empty');
|
|
863
|
+
}
|
|
864
|
+
const elements = convertChildrenToElements(children);
|
|
865
|
+
return Fragment$1({ children: elements });
|
|
866
|
+
}
|
|
867
|
+
/**
|
|
868
|
+
* JSX Development runtime - same as jsx but with additional dev checks
|
|
869
|
+
*/
|
|
870
|
+
const jsxDEV = (...args) => {
|
|
871
|
+
// console.log('JSX DEV called:', ...args);
|
|
872
|
+
// console.log('children', (args[1] as any)?.children);
|
|
873
|
+
return jsx(...args);
|
|
874
|
+
};
|
|
875
|
+
/**
|
|
876
|
+
* JSX runtime for React 17+ automatic runtime
|
|
877
|
+
* This is called when using jsx: "react-jsx" or "react-jsxdev"
|
|
878
|
+
*/
|
|
879
|
+
const jsxs = jsx;
|
|
880
|
+
/**
|
|
881
|
+
* A helper to create redrawable elements
|
|
882
|
+
* ```tsx
|
|
883
|
+
* export function MyComponent() {
|
|
884
|
+
* let aa = 10;
|
|
885
|
+
* // ...
|
|
886
|
+
* // aa might be changed
|
|
887
|
+
* return createRedrawable(() => <div>{aa}</div>);
|
|
888
|
+
* }
|
|
889
|
+
* ```
|
|
890
|
+
* Then the returned element has a `redraw` method to redraw itself with new values.
|
|
891
|
+
* @param creator a simple creator function that returns an element
|
|
892
|
+
* @returns created element's ref
|
|
893
|
+
*/
|
|
894
|
+
function createRedrawable(creator) {
|
|
895
|
+
const elRef = ref();
|
|
896
|
+
const redraw = () => {
|
|
897
|
+
elRef.value = creator(); // ref setter automatically calls replaceWith
|
|
898
|
+
elRef.redraw = redraw;
|
|
899
|
+
return elRef.value;
|
|
900
|
+
};
|
|
901
|
+
redraw();
|
|
902
|
+
return elRef;
|
|
144
903
|
}
|
|
145
904
|
|
|
146
905
|
function KTAsync(props) {
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
906
|
+
const raw = props.component(props);
|
|
907
|
+
let comp = props.skeleton ?? document.createComment('ktjs-suspense-placeholder');
|
|
908
|
+
if ($isThenable(raw)) {
|
|
909
|
+
raw.then((resolved) => comp.replaceWith(resolved));
|
|
910
|
+
}
|
|
911
|
+
else {
|
|
912
|
+
comp = raw;
|
|
913
|
+
}
|
|
914
|
+
return comp;
|
|
155
915
|
}
|
|
156
916
|
|
|
917
|
+
// todo kfor的编译方式:在插件帮助下,将k-for编译为 k-for接收一个list,如果可以定义一个item变量和index,那么将children编译为()=>children的函数,用以创建
|
|
918
|
+
/**
|
|
919
|
+
* KTFor - List rendering component with key-based optimization
|
|
920
|
+
* Returns a Comment anchor node with rendered elements in __kt_for_list__
|
|
921
|
+
*/
|
|
157
922
|
function KTFor(props) {
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
923
|
+
const redraw = () => {
|
|
924
|
+
const newList = listRef.value;
|
|
925
|
+
const parent = anchor.parentNode;
|
|
926
|
+
if (!parent) {
|
|
927
|
+
// If not in DOM yet, just rebuild the list
|
|
928
|
+
const newElements = [];
|
|
929
|
+
nodeMap.clear();
|
|
930
|
+
for (let index = 0; index < newList.length; index++) {
|
|
931
|
+
const item = newList[index];
|
|
932
|
+
const itemKey = currentKey(item, index, newList);
|
|
933
|
+
const node = currentMap(item, index, newList);
|
|
934
|
+
nodeMap.set(itemKey, node);
|
|
935
|
+
newElements.push(node);
|
|
936
|
+
}
|
|
937
|
+
anchor.__kt_for_list__ = newElements;
|
|
938
|
+
return anchor;
|
|
939
|
+
}
|
|
940
|
+
const oldLength = anchor.__kt_for_list__.length;
|
|
941
|
+
const newLength = newList.length;
|
|
942
|
+
// Fast path: empty list
|
|
943
|
+
if (newLength === 0) {
|
|
944
|
+
nodeMap.forEach((node) => node.remove());
|
|
945
|
+
nodeMap.clear();
|
|
946
|
+
anchor.__kt_for_list__ = [];
|
|
947
|
+
return anchor;
|
|
948
|
+
}
|
|
949
|
+
// Fast path: all new items
|
|
950
|
+
if (oldLength === 0) {
|
|
951
|
+
const newElements = [];
|
|
952
|
+
const fragment = document.createDocumentFragment();
|
|
953
|
+
for (let i = 0; i < newLength; i++) {
|
|
954
|
+
const item = newList[i];
|
|
955
|
+
const itemKey = currentKey(item, i, newList);
|
|
956
|
+
const node = currentMap(item, i, newList);
|
|
957
|
+
nodeMap.set(itemKey, node);
|
|
958
|
+
newElements.push(node);
|
|
959
|
+
fragment.appendChild(node);
|
|
960
|
+
}
|
|
961
|
+
parent.insertBefore(fragment, anchor.nextSibling);
|
|
962
|
+
anchor.__kt_for_list__ = newElements;
|
|
963
|
+
return anchor;
|
|
964
|
+
}
|
|
965
|
+
// Build key index map and new elements array in one pass
|
|
966
|
+
const newKeyToNewIndex = new Map();
|
|
967
|
+
const newElements = new Array(newLength);
|
|
968
|
+
let maxNewIndexSoFar = 0;
|
|
969
|
+
let moved = false;
|
|
970
|
+
for (let i = 0; i < newLength; i++) {
|
|
971
|
+
const item = newList[i];
|
|
972
|
+
const itemKey = currentKey(item, i, newList);
|
|
973
|
+
newKeyToNewIndex.set(itemKey, i);
|
|
974
|
+
if (nodeMap.has(itemKey)) {
|
|
975
|
+
// Reuse existing node
|
|
976
|
+
const node = nodeMap.get(itemKey);
|
|
977
|
+
newElements[i] = node;
|
|
978
|
+
// Track if items moved
|
|
979
|
+
if (i < maxNewIndexSoFar) {
|
|
980
|
+
moved = true;
|
|
981
|
+
}
|
|
982
|
+
else {
|
|
983
|
+
maxNewIndexSoFar = i;
|
|
984
|
+
}
|
|
985
|
+
}
|
|
986
|
+
else {
|
|
987
|
+
// Create new node
|
|
988
|
+
newElements[i] = currentMap(item, i, newList);
|
|
989
|
+
}
|
|
990
|
+
}
|
|
991
|
+
// Remove nodes not in new list
|
|
992
|
+
const toRemove = [];
|
|
993
|
+
nodeMap.forEach((node, key) => {
|
|
994
|
+
if (!newKeyToNewIndex.has(key)) {
|
|
995
|
+
toRemove.push(node);
|
|
996
|
+
}
|
|
997
|
+
});
|
|
998
|
+
for (let i = 0; i < toRemove.length; i++) {
|
|
999
|
+
toRemove[i].remove();
|
|
1000
|
+
}
|
|
1001
|
+
// Update DOM with minimal operations
|
|
1002
|
+
if (moved) {
|
|
1003
|
+
// Use longest increasing subsequence to minimize moves
|
|
1004
|
+
const seq = getSequence(newElements.map((el, i) => (nodeMap.has(currentKey(newList[i], i, newList)) ? i : -1)));
|
|
1005
|
+
let j = seq.length - 1;
|
|
1006
|
+
let anchor = null;
|
|
1007
|
+
// Traverse from end to start for stable insertions
|
|
1008
|
+
for (let i = newLength - 1; i >= 0; i--) {
|
|
1009
|
+
const node = newElements[i];
|
|
1010
|
+
if (j < 0 || i !== seq[j]) {
|
|
1011
|
+
// Node needs to be moved or inserted
|
|
1012
|
+
if (anchor) {
|
|
1013
|
+
parent.insertBefore(node, anchor);
|
|
1014
|
+
}
|
|
1015
|
+
else {
|
|
1016
|
+
// Insert at end
|
|
1017
|
+
let nextSibling = anchor.nextSibling;
|
|
1018
|
+
let temp = nextSibling;
|
|
1019
|
+
while (temp && newElements.includes(temp)) {
|
|
1020
|
+
temp = temp.nextSibling;
|
|
1021
|
+
}
|
|
1022
|
+
parent.insertBefore(node, temp);
|
|
1023
|
+
}
|
|
1024
|
+
}
|
|
1025
|
+
else {
|
|
1026
|
+
j--;
|
|
1027
|
+
}
|
|
1028
|
+
anchor = node;
|
|
1029
|
+
}
|
|
1030
|
+
}
|
|
1031
|
+
else {
|
|
1032
|
+
// No moves needed, just insert new nodes
|
|
1033
|
+
let currentNode = anchor.nextSibling;
|
|
1034
|
+
for (let i = 0; i < newLength; i++) {
|
|
1035
|
+
const node = newElements[i];
|
|
1036
|
+
if (currentNode !== node) {
|
|
1037
|
+
parent.insertBefore(node, currentNode);
|
|
1038
|
+
}
|
|
1039
|
+
else {
|
|
1040
|
+
currentNode = currentNode.nextSibling;
|
|
1041
|
+
}
|
|
240
1042
|
}
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
1043
|
+
}
|
|
1044
|
+
// Update maps
|
|
1045
|
+
nodeMap.clear();
|
|
1046
|
+
for (let i = 0; i < newLength; i++) {
|
|
1047
|
+
const itemKey = currentKey(newList[i], i, newList);
|
|
1048
|
+
nodeMap.set(itemKey, newElements[i]);
|
|
1049
|
+
}
|
|
1050
|
+
anchor.__kt_for_list__ = newElements;
|
|
1051
|
+
return anchor;
|
|
1052
|
+
};
|
|
1053
|
+
const { key: currentKey = (item) => item, map: currentMap } = props;
|
|
1054
|
+
const listRef = toReactive(props.list, redraw);
|
|
1055
|
+
const anchor = document.createComment('kt-for');
|
|
1056
|
+
// Map to track rendered nodes by key
|
|
1057
|
+
const nodeMap = new Map();
|
|
1058
|
+
// Render initial list
|
|
1059
|
+
const elements = [];
|
|
1060
|
+
for (let index = 0; index < listRef.value.length; index++) {
|
|
1061
|
+
const item = listRef.value[index];
|
|
1062
|
+
const itemKey = currentKey(item, index, listRef.value);
|
|
1063
|
+
const node = currentMap(item, index, listRef.value);
|
|
1064
|
+
nodeMap.set(itemKey, node);
|
|
1065
|
+
elements.push(node);
|
|
1066
|
+
}
|
|
1067
|
+
anchor.__kt_for_list__ = elements;
|
|
1068
|
+
$initRef(props, anchor);
|
|
265
1069
|
return anchor;
|
|
266
|
-
};
|
|
267
|
-
const { key: currentKey = (item) => item, map: currentMap } = props;
|
|
268
|
-
const listRef = toReactive(props.list, redraw);
|
|
269
|
-
const anchor = document.createComment("kt-for");
|
|
270
|
-
const nodeMap = /* @__PURE__ */ new Map();
|
|
271
|
-
const elements = [];
|
|
272
|
-
for (let index = 0; index < listRef.value.length; index++) {
|
|
273
|
-
const item = listRef.value[index];
|
|
274
|
-
const itemKey = currentKey(item, index, listRef.value);
|
|
275
|
-
const node = currentMap(item, index, listRef.value);
|
|
276
|
-
nodeMap.set(itemKey, node);
|
|
277
|
-
elements.push(node);
|
|
278
|
-
}
|
|
279
|
-
anchor.__kt_for_list__ = elements;
|
|
280
|
-
$initRef(props, anchor);
|
|
281
|
-
return anchor;
|
|
282
1070
|
}
|
|
1071
|
+
// Longest Increasing Subsequence algorithm (optimized for diff)
|
|
283
1072
|
function getSequence(arr) {
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
result[u
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
1073
|
+
const p = arr.slice();
|
|
1074
|
+
const result = [0];
|
|
1075
|
+
let i, j, u, v, c;
|
|
1076
|
+
const len = arr.length;
|
|
1077
|
+
for (i = 0; i < len; i++) {
|
|
1078
|
+
const arrI = arr[i];
|
|
1079
|
+
if (arrI === -1)
|
|
1080
|
+
continue;
|
|
1081
|
+
j = result[result.length - 1];
|
|
1082
|
+
if (arr[j] < arrI) {
|
|
1083
|
+
p[i] = j;
|
|
1084
|
+
result.push(i);
|
|
1085
|
+
continue;
|
|
1086
|
+
}
|
|
1087
|
+
u = 0;
|
|
1088
|
+
v = result.length - 1;
|
|
1089
|
+
while (u < v) {
|
|
1090
|
+
c = ((u + v) / 2) | 0;
|
|
1091
|
+
if (arr[result[c]] < arrI) {
|
|
1092
|
+
u = c + 1;
|
|
1093
|
+
}
|
|
1094
|
+
else {
|
|
1095
|
+
v = c;
|
|
1096
|
+
}
|
|
1097
|
+
}
|
|
1098
|
+
if (arrI < arr[result[u]]) {
|
|
1099
|
+
if (u > 0) {
|
|
1100
|
+
p[i] = result[u - 1];
|
|
1101
|
+
}
|
|
1102
|
+
result[u] = i;
|
|
1103
|
+
}
|
|
1104
|
+
}
|
|
1105
|
+
u = result.length;
|
|
1106
|
+
v = result[u - 1];
|
|
1107
|
+
while (u-- > 0) {
|
|
1108
|
+
result[u] = v;
|
|
1109
|
+
v = p[v];
|
|
1110
|
+
}
|
|
1111
|
+
return result;
|
|
321
1112
|
}
|
|
322
1113
|
|
|
323
|
-
export { $initRef, KTAsync, KTComputed, KTFor,
|
|
1114
|
+
export { $initRef, $modelOrRef, Fragment, KTAsync, KTComputed, KTFor, KTRef, computed, h as createElement, createRedrawable, dereactive, effect, h, isComputed, isKT, isRef, jsx, jsxDEV, jsxs, ref, surfaceRef, toReactive, toRef };
|
|
1115
|
+
//# sourceMappingURL=index.mjs.map
|