@ktjs/core 0.28.1 → 0.29.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +434 -1468
- package/dist/index.mjs +826 -1022
- package/package.json +2 -2
- package/LICENSE +0 -21
- package/dist/index.iife.js +0 -1143
- package/dist/index.legacy.js +0 -1152
- package/dist/jsx/index.d.ts +0 -1329
- package/dist/jsx/index.mjs +0 -676
- package/dist/jsx/jsx-runtime.d.ts +0 -201
- package/dist/jsx/jsx-runtime.mjs +0 -676
package/dist/jsx/index.mjs
DELETED
|
@@ -1,676 +0,0 @@
|
|
|
1
|
-
// Cached native methods for performance optimization
|
|
2
|
-
const $isArray = Array.isArray;
|
|
3
|
-
const $is = Object.is;
|
|
4
|
-
const $random = Math.random;
|
|
5
|
-
const $isThenable = (o) => typeof o?.then === 'function';
|
|
6
|
-
|
|
7
|
-
if (typeof Symbol === 'undefined') {
|
|
8
|
-
window.Symbol = function Symbol(description) {
|
|
9
|
-
return `@@SYMBOL_${description || ''}_${$random().toString(36).slice(2)}`;
|
|
10
|
-
};
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
// Shared constants
|
|
14
|
-
// Empty for now - can be extended with framework-wide constants
|
|
15
|
-
/**
|
|
16
|
-
* Mark the attribute as SVG to handle special cases during rendering.
|
|
17
|
-
*/
|
|
18
|
-
const SVG_ATTR_FLAG = '__kt_svg__';
|
|
19
|
-
/**
|
|
20
|
-
* Mark the attribute as MathML to handle special cases during rendering.
|
|
21
|
-
*/
|
|
22
|
-
const MATHML_ATTR_FLAG = '__kt_mathml__';
|
|
23
|
-
|
|
24
|
-
// DOM manipulation utilities
|
|
25
|
-
// # dom natives
|
|
26
|
-
const $isNode = (x) => x?.nodeType > 0;
|
|
27
|
-
/**
|
|
28
|
-
* Safe replace `oldNode` With `newNode`
|
|
29
|
-
*/
|
|
30
|
-
const $replaceNode = (oldNode, newNode) => {
|
|
31
|
-
if ($isNode(oldNode) && $isNode(newNode)) {
|
|
32
|
-
if (newNode.contains(oldNode)) {
|
|
33
|
-
newNode.remove();
|
|
34
|
-
}
|
|
35
|
-
oldNode.replaceWith(newNode);
|
|
36
|
-
}
|
|
37
|
-
};
|
|
38
|
-
/**
|
|
39
|
-
* & Remove `bind` because it is shockingly slower than wrapper
|
|
40
|
-
* & `window.document` is safe because it is not configurable and its setter is undefined
|
|
41
|
-
*/
|
|
42
|
-
const $appendChild = HTMLElement.prototype.appendChild;
|
|
43
|
-
const originAppend = HTMLElement.prototype.append;
|
|
44
|
-
const $append = // for ie 9/10/11
|
|
45
|
-
typeof originAppend === 'function'
|
|
46
|
-
? originAppend
|
|
47
|
-
: function (...nodes) {
|
|
48
|
-
if (nodes.length < 50) {
|
|
49
|
-
for (let i = 0; i < nodes.length; i++) {
|
|
50
|
-
const node = nodes[i];
|
|
51
|
-
if (typeof node === 'string') {
|
|
52
|
-
$appendChild.call(this, document.createTextNode(node));
|
|
53
|
-
}
|
|
54
|
-
else {
|
|
55
|
-
$appendChild.call(this, node);
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
else {
|
|
60
|
-
const fragment = document.createDocumentFragment();
|
|
61
|
-
for (let i = 0; i < nodes.length; i++) {
|
|
62
|
-
const node = nodes[i];
|
|
63
|
-
if (typeof node === 'string') {
|
|
64
|
-
$appendChild.call(fragment, document.createTextNode(node));
|
|
65
|
-
}
|
|
66
|
-
else {
|
|
67
|
-
$appendChild.call(fragment, node);
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
$appendChild.call(this, fragment);
|
|
71
|
-
}
|
|
72
|
-
};
|
|
73
|
-
const { get: $buttonDisabledGetter, set: $buttonDisabledSetter } = Object.getOwnPropertyDescriptor(HTMLButtonElement.prototype, 'disabled');
|
|
74
|
-
/**
|
|
75
|
-
* Used for `k-model`
|
|
76
|
-
*/
|
|
77
|
-
const $applyModel = (element, valueRef, propName, eventName) => {
|
|
78
|
-
element[propName] = valueRef.value; // initialize
|
|
79
|
-
valueRef.addOnChange((newValue) => (element[propName] = newValue));
|
|
80
|
-
element.addEventListener(eventName, () => (valueRef.value = element[propName]));
|
|
81
|
-
};
|
|
82
|
-
/**
|
|
83
|
-
* Safe and quick forEach implementation that works with array-like objects and handles sparse arrays.
|
|
84
|
-
*/
|
|
85
|
-
const $forEach = (array, callback) => {
|
|
86
|
-
const len = array.length;
|
|
87
|
-
for (let i = 0; i < len; i++) {
|
|
88
|
-
callback(array[i], i, array);
|
|
89
|
-
}
|
|
90
|
-
};
|
|
91
|
-
|
|
92
|
-
// incase that symbol is not supported
|
|
93
|
-
Object.defineProperty(window, '__ktjs__', { value: '0.23.11' });
|
|
94
|
-
|
|
95
|
-
const isKT = (obj) => obj?.isKT;
|
|
96
|
-
const isRef = (obj) => obj?.ktType === 1 /* KTReactiveType.REF */;
|
|
97
|
-
const isComputed = (obj) => obj?.ktType === 2 /* KTReactiveType.COMPUTED */;
|
|
98
|
-
|
|
99
|
-
const booleanHandler = (element, key, value) => {
|
|
100
|
-
if (key in element) {
|
|
101
|
-
element[key] = !!value;
|
|
102
|
-
}
|
|
103
|
-
else {
|
|
104
|
-
element.setAttribute(key, value);
|
|
105
|
-
}
|
|
106
|
-
};
|
|
107
|
-
const valueHandler = (element, key, value) => {
|
|
108
|
-
if (key in element) {
|
|
109
|
-
element[key] = value;
|
|
110
|
-
}
|
|
111
|
-
else {
|
|
112
|
-
element.setAttribute(key, value);
|
|
113
|
-
}
|
|
114
|
-
};
|
|
115
|
-
// Attribute handlers map for optimized lookup
|
|
116
|
-
const handlers = {
|
|
117
|
-
checked: booleanHandler,
|
|
118
|
-
selected: booleanHandler,
|
|
119
|
-
value: valueHandler,
|
|
120
|
-
valueAsDate: valueHandler,
|
|
121
|
-
valueAsNumber: valueHandler,
|
|
122
|
-
defaultValue: valueHandler,
|
|
123
|
-
defaultChecked: booleanHandler,
|
|
124
|
-
defaultSelected: booleanHandler,
|
|
125
|
-
disabled: booleanHandler,
|
|
126
|
-
readOnly: booleanHandler,
|
|
127
|
-
multiple: booleanHandler,
|
|
128
|
-
required: booleanHandler,
|
|
129
|
-
autofocus: booleanHandler,
|
|
130
|
-
open: booleanHandler,
|
|
131
|
-
controls: booleanHandler,
|
|
132
|
-
autoplay: booleanHandler,
|
|
133
|
-
loop: booleanHandler,
|
|
134
|
-
muted: booleanHandler,
|
|
135
|
-
defer: booleanHandler,
|
|
136
|
-
async: booleanHandler,
|
|
137
|
-
hidden: (element, _key, value) => (element.hidden = !!value),
|
|
138
|
-
};
|
|
139
|
-
|
|
140
|
-
const defaultHandler = (element, key, value) => element.setAttribute(key, value);
|
|
141
|
-
const setElementStyle = (element, style) => {
|
|
142
|
-
if (typeof style === 'string') {
|
|
143
|
-
element.style.cssText = style;
|
|
144
|
-
return;
|
|
145
|
-
}
|
|
146
|
-
for (const key in style) {
|
|
147
|
-
element.style[key] = style[key];
|
|
148
|
-
}
|
|
149
|
-
};
|
|
150
|
-
function attrIsObject(element, attr) {
|
|
151
|
-
const classValue = attr.class || attr.className;
|
|
152
|
-
if (classValue !== undefined) {
|
|
153
|
-
if (isKT(classValue)) {
|
|
154
|
-
element.setAttribute('class', classValue.value);
|
|
155
|
-
classValue.addOnChange((v) => element.setAttribute('class', v));
|
|
156
|
-
}
|
|
157
|
-
else {
|
|
158
|
-
element.setAttribute('class', classValue);
|
|
159
|
-
}
|
|
160
|
-
}
|
|
161
|
-
const style = attr.style;
|
|
162
|
-
if (style) {
|
|
163
|
-
if (typeof style === 'string') {
|
|
164
|
-
element.setAttribute('style', style);
|
|
165
|
-
}
|
|
166
|
-
else if (typeof style === 'object') {
|
|
167
|
-
if (isKT(style)) {
|
|
168
|
-
setElementStyle(element, style.value);
|
|
169
|
-
style.addOnChange((v) => setElementStyle(element, v));
|
|
170
|
-
}
|
|
171
|
-
else {
|
|
172
|
-
setElementStyle(element, style);
|
|
173
|
-
}
|
|
174
|
-
}
|
|
175
|
-
}
|
|
176
|
-
if ('k-html' in attr) {
|
|
177
|
-
const html = attr['k-html'];
|
|
178
|
-
if (isKT(html)) {
|
|
179
|
-
element.innerHTML = html.value;
|
|
180
|
-
html.addOnChange((v) => (element.innerHTML = v));
|
|
181
|
-
}
|
|
182
|
-
else {
|
|
183
|
-
element.innerHTML = html;
|
|
184
|
-
}
|
|
185
|
-
}
|
|
186
|
-
for (const key in attr) {
|
|
187
|
-
if (key === 'k-if' ||
|
|
188
|
-
key === 'k-model' ||
|
|
189
|
-
key === 'ref' ||
|
|
190
|
-
key === 'class' ||
|
|
191
|
-
key === 'className' ||
|
|
192
|
-
key === 'style' ||
|
|
193
|
-
key === 'children' ||
|
|
194
|
-
key === 'k-html') {
|
|
195
|
-
continue;
|
|
196
|
-
}
|
|
197
|
-
const o = attr[key];
|
|
198
|
-
// normal event handler
|
|
199
|
-
if (key.startsWith('on:')) {
|
|
200
|
-
element.addEventListener(key.slice(3), o); // chop off the `on:`
|
|
201
|
-
}
|
|
202
|
-
// normal attributes
|
|
203
|
-
else {
|
|
204
|
-
const handler = handlers[key] || defaultHandler;
|
|
205
|
-
if (isKT(o)) {
|
|
206
|
-
handler(element, key, o.value);
|
|
207
|
-
o.addOnChange((v) => handler(element, key, v));
|
|
208
|
-
}
|
|
209
|
-
else {
|
|
210
|
-
handler(element, key, o);
|
|
211
|
-
}
|
|
212
|
-
}
|
|
213
|
-
}
|
|
214
|
-
}
|
|
215
|
-
function applyAttr(element, attr) {
|
|
216
|
-
if (!attr) {
|
|
217
|
-
return;
|
|
218
|
-
}
|
|
219
|
-
if (typeof attr === 'object' && attr !== null) {
|
|
220
|
-
attrIsObject(element, attr);
|
|
221
|
-
}
|
|
222
|
-
else {
|
|
223
|
-
throw new Error('[kt.js error] attr must be an object.');
|
|
224
|
-
}
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
const assureNode = (o) => ($isNode(o) ? o : document.createTextNode(o));
|
|
228
|
-
function apdSingle(element, c) {
|
|
229
|
-
// & JSX should ignore false, undefined, and null
|
|
230
|
-
if (c === false || c === undefined || c === null) {
|
|
231
|
-
return;
|
|
232
|
-
}
|
|
233
|
-
if (isKT(c)) {
|
|
234
|
-
let node = assureNode(c.value);
|
|
235
|
-
$append.call(element, node);
|
|
236
|
-
c.addOnChange((newValue, oldValue) => {
|
|
237
|
-
if ($isNode(newValue) && $isNode(oldValue)) {
|
|
238
|
-
// & this case is handled automically in `class KTRef`
|
|
239
|
-
return;
|
|
240
|
-
}
|
|
241
|
-
const oldNode = node;
|
|
242
|
-
node = assureNode(newValue);
|
|
243
|
-
oldNode.replaceWith(node);
|
|
244
|
-
});
|
|
245
|
-
}
|
|
246
|
-
else {
|
|
247
|
-
$append.call(element, c);
|
|
248
|
-
// Handle KTFor anchor
|
|
249
|
-
// todo Maybe not needed anymore
|
|
250
|
-
const list = c.__kt_for_list__;
|
|
251
|
-
if ($isArray(list)) {
|
|
252
|
-
apd(element, list);
|
|
253
|
-
}
|
|
254
|
-
}
|
|
255
|
-
}
|
|
256
|
-
function apd(element, c) {
|
|
257
|
-
if ($isThenable(c)) {
|
|
258
|
-
c.then((r) => apd(element, r));
|
|
259
|
-
}
|
|
260
|
-
else if ($isArray(c)) {
|
|
261
|
-
for (let i = 0; i < c.length; i++) {
|
|
262
|
-
// & might be thenable here too
|
|
263
|
-
const ci = c[i];
|
|
264
|
-
if ($isThenable(ci)) {
|
|
265
|
-
const comment = document.createComment('ktjs-promise-placeholder');
|
|
266
|
-
$append.call(element, comment);
|
|
267
|
-
ci.then((awaited) => comment.replaceWith(awaited));
|
|
268
|
-
}
|
|
269
|
-
else {
|
|
270
|
-
apdSingle(element, ci);
|
|
271
|
-
}
|
|
272
|
-
}
|
|
273
|
-
}
|
|
274
|
-
else {
|
|
275
|
-
// & here is thened, so must be a simple elementj
|
|
276
|
-
apdSingle(element, c);
|
|
277
|
-
}
|
|
278
|
-
}
|
|
279
|
-
function applyContent(element, content) {
|
|
280
|
-
if ($isArray(content)) {
|
|
281
|
-
for (let i = 0; i < content.length; i++) {
|
|
282
|
-
apd(element, content[i]);
|
|
283
|
-
}
|
|
284
|
-
}
|
|
285
|
-
else {
|
|
286
|
-
apd(element, content);
|
|
287
|
-
}
|
|
288
|
-
}
|
|
289
|
-
|
|
290
|
-
class KTRef {
|
|
291
|
-
/**
|
|
292
|
-
* Indicates that this is a KTRef instance
|
|
293
|
-
*/
|
|
294
|
-
isKT = true;
|
|
295
|
-
ktType = 1 /* KTReactiveType.REF */;
|
|
296
|
-
/**
|
|
297
|
-
* @internal
|
|
298
|
-
*/
|
|
299
|
-
_value;
|
|
300
|
-
/**
|
|
301
|
-
* @internal
|
|
302
|
-
*/
|
|
303
|
-
_onChanges;
|
|
304
|
-
/**
|
|
305
|
-
* @internal
|
|
306
|
-
*/
|
|
307
|
-
_emit(newValue, oldValue) {
|
|
308
|
-
for (let i = 0; i < this._onChanges.length; i++) {
|
|
309
|
-
this._onChanges[i](newValue, oldValue);
|
|
310
|
-
}
|
|
311
|
-
}
|
|
312
|
-
constructor(_value, _onChanges) {
|
|
313
|
-
this._value = _value;
|
|
314
|
-
this._onChanges = _onChanges;
|
|
315
|
-
}
|
|
316
|
-
/**
|
|
317
|
-
* If new value and old value are both nodes, the old one will be replaced in the DOM
|
|
318
|
-
*/
|
|
319
|
-
get value() {
|
|
320
|
-
return this._value;
|
|
321
|
-
}
|
|
322
|
-
set value(newValue) {
|
|
323
|
-
if ($is(newValue, this._value)) {
|
|
324
|
-
return;
|
|
325
|
-
}
|
|
326
|
-
const oldValue = this._value;
|
|
327
|
-
$replaceNode(oldValue, newValue);
|
|
328
|
-
this._value = newValue;
|
|
329
|
-
this._emit(newValue, oldValue);
|
|
330
|
-
}
|
|
331
|
-
/**
|
|
332
|
-
* Force all listeners to run even when reference identity has not changed.
|
|
333
|
-
* Useful for in-place array/object mutations.
|
|
334
|
-
*/
|
|
335
|
-
notify() {
|
|
336
|
-
this._emit(this._value, this._value);
|
|
337
|
-
}
|
|
338
|
-
/**
|
|
339
|
-
* Mutate current value in-place and notify listeners once.
|
|
340
|
-
*
|
|
341
|
-
* @example
|
|
342
|
-
* const items = ref<number[]>([1, 2]);
|
|
343
|
-
* items.mutate((list) => list.push(3));
|
|
344
|
-
*/
|
|
345
|
-
mutate(mutator) {
|
|
346
|
-
if (typeof mutator !== 'function') {
|
|
347
|
-
throw new Error('[kt.js error] KTRef.mutate: mutator must be a function');
|
|
348
|
-
}
|
|
349
|
-
const oldValue = this._value;
|
|
350
|
-
const result = mutator(this._value);
|
|
351
|
-
this._emit(this._value, oldValue);
|
|
352
|
-
return result;
|
|
353
|
-
}
|
|
354
|
-
/**
|
|
355
|
-
* Register a callback when the value changes
|
|
356
|
-
* @param callback (newValue, oldValue) => xxx
|
|
357
|
-
*/
|
|
358
|
-
addOnChange(callback) {
|
|
359
|
-
if (typeof callback !== 'function') {
|
|
360
|
-
throw new Error('[kt.js error] KTRef.addOnChange: callback must be a function');
|
|
361
|
-
}
|
|
362
|
-
this._onChanges.push(callback);
|
|
363
|
-
}
|
|
364
|
-
removeOnChange(callback) {
|
|
365
|
-
for (let i = this._onChanges.length - 1; i >= 0; i--) {
|
|
366
|
-
if (this._onChanges[i] === callback) {
|
|
367
|
-
this._onChanges.splice(i, 1);
|
|
368
|
-
return true;
|
|
369
|
-
}
|
|
370
|
-
}
|
|
371
|
-
return false;
|
|
372
|
-
}
|
|
373
|
-
}
|
|
374
|
-
/**
|
|
375
|
-
* Reference to the created HTML element.
|
|
376
|
-
* - **Only** respond to `ref.value` changes, not reactive to internal changes of the element.
|
|
377
|
-
* - can alse be used to store normal values, but it is not reactive.
|
|
378
|
-
* - if the value is already a `KTRef`, it will be returned **directly**.
|
|
379
|
-
* @param value mostly an HTMLElement
|
|
380
|
-
*/
|
|
381
|
-
function ref(value, onChange) {
|
|
382
|
-
return new KTRef(value, onChange ? [onChange] : []);
|
|
383
|
-
}
|
|
384
|
-
const $setRef = (props, node) => {
|
|
385
|
-
if ('ref' in props) {
|
|
386
|
-
const r = props.ref;
|
|
387
|
-
if (isRef(r)) {
|
|
388
|
-
r.value = node;
|
|
389
|
-
}
|
|
390
|
-
else {
|
|
391
|
-
throw new Error('[kt.js error] Fragment: ref must be a KTRef');
|
|
392
|
-
}
|
|
393
|
-
}
|
|
394
|
-
};
|
|
395
|
-
|
|
396
|
-
const toReactive = (value, onChange) => {
|
|
397
|
-
if (isKT(value)) {
|
|
398
|
-
if (onChange) {
|
|
399
|
-
value.addOnChange(onChange);
|
|
400
|
-
}
|
|
401
|
-
return value;
|
|
402
|
-
}
|
|
403
|
-
else {
|
|
404
|
-
return ref(value, onChange);
|
|
405
|
-
}
|
|
406
|
-
};
|
|
407
|
-
|
|
408
|
-
function applyKModel(element, valueRef) {
|
|
409
|
-
if (!isKT(valueRef)) {
|
|
410
|
-
console.warn('[kt.js warn]','k-model value must be a KTRef.');
|
|
411
|
-
return;
|
|
412
|
-
}
|
|
413
|
-
if (element instanceof HTMLInputElement) {
|
|
414
|
-
if (element.type === 'radio' || element.type === 'checkbox') {
|
|
415
|
-
$applyModel(element, valueRef, 'checked', 'change');
|
|
416
|
-
}
|
|
417
|
-
else {
|
|
418
|
-
$applyModel(element, valueRef, 'value', 'input');
|
|
419
|
-
}
|
|
420
|
-
}
|
|
421
|
-
else if (element instanceof HTMLSelectElement) {
|
|
422
|
-
$applyModel(element, valueRef, 'value', 'change');
|
|
423
|
-
}
|
|
424
|
-
else if (element instanceof HTMLTextAreaElement) {
|
|
425
|
-
$applyModel(element, valueRef, 'value', 'input');
|
|
426
|
-
}
|
|
427
|
-
else {
|
|
428
|
-
console.warn('[kt.js warn]','not supported element for k-model:');
|
|
429
|
-
}
|
|
430
|
-
}
|
|
431
|
-
|
|
432
|
-
const htmlCreator = (tag) => document.createElement(tag);
|
|
433
|
-
const svgCreator = (tag) => document.createElementNS('http://www.w3.org/2000/svg', tag);
|
|
434
|
-
const mathMLCreator = (tag) => document.createElementNS('http://www.w3.org/1998/Math/MathML', tag);
|
|
435
|
-
let creator = htmlCreator;
|
|
436
|
-
/**
|
|
437
|
-
* Create an enhanced HTMLElement.
|
|
438
|
-
* - Only supports HTMLElements, **NOT** SVGElements or other Elements.
|
|
439
|
-
* @param tag tag of an `HTMLElement`
|
|
440
|
-
* @param attr attribute object or className
|
|
441
|
-
* @param content a string or an array of HTMLEnhancedElement as child nodes
|
|
442
|
-
*
|
|
443
|
-
* ## About
|
|
444
|
-
* @package @ktjs/core
|
|
445
|
-
* @author Kasukabe Tsumugi <futami16237@gmail.com>
|
|
446
|
-
* @version 0.28.1 (Last Update: 2026.02.10 11:23:01.128)
|
|
447
|
-
* @license MIT
|
|
448
|
-
* @link https://github.com/baendlorel/kt.js
|
|
449
|
-
* @link https://baendlorel.github.io/ Welcome to my site!
|
|
450
|
-
* @description Core functionality for kt.js - DOM manipulation utilities with JSX/TSX support
|
|
451
|
-
* @copyright Copyright (c) 2026 Kasukabe Tsumugi. All rights reserved.
|
|
452
|
-
*/
|
|
453
|
-
const h = (tag, attr, content) => {
|
|
454
|
-
if (typeof tag !== 'string') {
|
|
455
|
-
throw new Error('[kt.js error] tagName must be a string.');
|
|
456
|
-
}
|
|
457
|
-
if (attr) {
|
|
458
|
-
if (SVG_ATTR_FLAG in attr) {
|
|
459
|
-
delete attr[SVG_ATTR_FLAG];
|
|
460
|
-
creator = svgCreator;
|
|
461
|
-
}
|
|
462
|
-
else if (MATHML_ATTR_FLAG in attr) {
|
|
463
|
-
delete attr[MATHML_ATTR_FLAG];
|
|
464
|
-
creator = mathMLCreator;
|
|
465
|
-
}
|
|
466
|
-
else {
|
|
467
|
-
creator = htmlCreator;
|
|
468
|
-
}
|
|
469
|
-
}
|
|
470
|
-
// * start creating the element
|
|
471
|
-
const element = creator(tag);
|
|
472
|
-
// * Handle content
|
|
473
|
-
applyAttr(element, attr);
|
|
474
|
-
applyContent(element, content);
|
|
475
|
-
if (typeof attr === 'object' && attr !== null && 'k-model' in attr) {
|
|
476
|
-
applyKModel(element, attr['k-model']);
|
|
477
|
-
}
|
|
478
|
-
return element;
|
|
479
|
-
};
|
|
480
|
-
|
|
481
|
-
/**
|
|
482
|
-
* Fragment - Container component for managing arrays of child elements
|
|
483
|
-
*
|
|
484
|
-
* Features:
|
|
485
|
-
* 1. Returns a comment anchor node, child elements are inserted after the anchor
|
|
486
|
-
* 2. Supports reactive arrays, automatically updates DOM when array changes
|
|
487
|
-
* 3. Basic version uses simple replacement algorithm (remove all old elements, insert all new elements)
|
|
488
|
-
* 4. Future enhancement: key-based optimization
|
|
489
|
-
*
|
|
490
|
-
* Usage example:
|
|
491
|
-
* ```tsx
|
|
492
|
-
* const children = ref([<div>A</div>, <div>B</div>]);
|
|
493
|
-
* const fragment = <Fragment children={children} />;
|
|
494
|
-
* document.body.appendChild(fragment);
|
|
495
|
-
*
|
|
496
|
-
* // Automatic update
|
|
497
|
-
* children.value = [<div>C</div>, <div>D</div>];
|
|
498
|
-
* ```
|
|
499
|
-
*/
|
|
500
|
-
function Fragment$1(props) {
|
|
501
|
-
// key parameter reserved for future enhancement, currently unused
|
|
502
|
-
// const { key: _key } = props;
|
|
503
|
-
const redraw = () => {
|
|
504
|
-
const newElements = childrenRef.value;
|
|
505
|
-
const parent = anchor.parentNode;
|
|
506
|
-
if (!parent) {
|
|
507
|
-
// If anchor is not in DOM, only update internal state
|
|
508
|
-
elements.length = 0;
|
|
509
|
-
for (let i = 0; i < newElements.length; i++) {
|
|
510
|
-
elements.push(newElements[i]);
|
|
511
|
-
}
|
|
512
|
-
return;
|
|
513
|
-
}
|
|
514
|
-
// Simple replacement algorithm: remove all old elements, insert all new elements
|
|
515
|
-
// todo Future enhancement: key-based optimization
|
|
516
|
-
// 1. Remove all old elements
|
|
517
|
-
for (let i = 0; i < elements.length; i++) {
|
|
518
|
-
elements[i].remove();
|
|
519
|
-
}
|
|
520
|
-
// 2. Insert all new elements
|
|
521
|
-
const fragment = document.createDocumentFragment();
|
|
522
|
-
elements.length = 0;
|
|
523
|
-
for (let i = 0; i < newElements.length; i++) {
|
|
524
|
-
const element = newElements[i];
|
|
525
|
-
elements.push(element);
|
|
526
|
-
fragment.appendChild(element);
|
|
527
|
-
}
|
|
528
|
-
// Insert after anchor
|
|
529
|
-
parent.insertBefore(fragment, anchor.nextSibling);
|
|
530
|
-
};
|
|
531
|
-
let initialized = false;
|
|
532
|
-
const childrenRef = toReactive(props.children, redraw);
|
|
533
|
-
const elements = [];
|
|
534
|
-
const anchor = document.createComment('kt-fragment');
|
|
535
|
-
// Observe DOM insertion
|
|
536
|
-
const observer = new MutationObserver(() => {
|
|
537
|
-
if (anchor.isConnected && !initialized) {
|
|
538
|
-
initialized = true;
|
|
539
|
-
redraw();
|
|
540
|
-
observer.disconnect();
|
|
541
|
-
}
|
|
542
|
-
});
|
|
543
|
-
observer.observe(document.body, { childList: true, subtree: true });
|
|
544
|
-
// Set ref reference
|
|
545
|
-
$setRef(props, anchor);
|
|
546
|
-
return anchor;
|
|
547
|
-
}
|
|
548
|
-
/**
|
|
549
|
-
* Convert KTRawContent to HTMLElement array
|
|
550
|
-
*/
|
|
551
|
-
function convertChildrenToElements(children) {
|
|
552
|
-
const elements = [];
|
|
553
|
-
const processChild = (child) => {
|
|
554
|
-
if (child == null || child === false || child === true) {
|
|
555
|
-
// Ignore null, undefined, false, true
|
|
556
|
-
return;
|
|
557
|
-
}
|
|
558
|
-
if ($isArray(child)) {
|
|
559
|
-
// Recursively process array
|
|
560
|
-
$forEach(child, processChild);
|
|
561
|
-
return;
|
|
562
|
-
}
|
|
563
|
-
if (typeof child === 'string' || typeof child === 'number') {
|
|
564
|
-
// & Wrap text in span element? No! use text node instead
|
|
565
|
-
const textNode = document.createTextNode(String(child));
|
|
566
|
-
elements.push(textNode);
|
|
567
|
-
return;
|
|
568
|
-
}
|
|
569
|
-
if (child instanceof HTMLElement) {
|
|
570
|
-
elements.push(child);
|
|
571
|
-
return;
|
|
572
|
-
}
|
|
573
|
-
if (isKT(child)) {
|
|
574
|
-
processChild(child.value);
|
|
575
|
-
return;
|
|
576
|
-
}
|
|
577
|
-
// Other types ignored or converted to string
|
|
578
|
-
console.warn('[kt.js warn]','Fragment: unsupported child type', child);
|
|
579
|
-
};
|
|
580
|
-
processChild(children);
|
|
581
|
-
return elements;
|
|
582
|
-
}
|
|
583
|
-
|
|
584
|
-
const create = (tag, props) => {
|
|
585
|
-
if (typeof tag === 'function') {
|
|
586
|
-
return tag(props);
|
|
587
|
-
}
|
|
588
|
-
else {
|
|
589
|
-
return h(tag, props, props.children);
|
|
590
|
-
}
|
|
591
|
-
};
|
|
592
|
-
const placeholder = () => document.createComment('k-if');
|
|
593
|
-
/**
|
|
594
|
-
* @param tag html tag or function component
|
|
595
|
-
* @param props properties/attributes
|
|
596
|
-
*/
|
|
597
|
-
function jsx(tag, props) {
|
|
598
|
-
if (isComputed(props.ref)) {
|
|
599
|
-
throw new Error('[kt.js error] Cannot assign a computed value to an element.');
|
|
600
|
-
}
|
|
601
|
-
let el;
|
|
602
|
-
if ('k-if' in props) {
|
|
603
|
-
const kif = props['k-if'];
|
|
604
|
-
let condition = kif; // assume boolean by default
|
|
605
|
-
// Handle reactive k-if
|
|
606
|
-
if (isKT(kif)) {
|
|
607
|
-
kif.addOnChange((newValue, oldValue) => {
|
|
608
|
-
if (newValue === oldValue) {
|
|
609
|
-
return;
|
|
610
|
-
}
|
|
611
|
-
const oldEl = el;
|
|
612
|
-
$setRef(props, (el = newValue ? create(tag, props) : placeholder()));
|
|
613
|
-
$replaceNode(oldEl, el);
|
|
614
|
-
});
|
|
615
|
-
condition = kif.value;
|
|
616
|
-
}
|
|
617
|
-
if (!condition) {
|
|
618
|
-
// & make comment placeholder in case that ref might be redrawn later
|
|
619
|
-
$setRef(props, (el = placeholder()));
|
|
620
|
-
return el;
|
|
621
|
-
}
|
|
622
|
-
}
|
|
623
|
-
$setRef(props, (el = create(tag, props)));
|
|
624
|
-
return el;
|
|
625
|
-
}
|
|
626
|
-
/**
|
|
627
|
-
* Fragment support - returns an array of children
|
|
628
|
-
* Enhanced Fragment component that manages arrays of elements
|
|
629
|
-
*/
|
|
630
|
-
function Fragment(props) {
|
|
631
|
-
const { children } = props ?? {};
|
|
632
|
-
if (!children) {
|
|
633
|
-
return document.createComment('kt-fragment-empty');
|
|
634
|
-
}
|
|
635
|
-
const elements = convertChildrenToElements(children);
|
|
636
|
-
return Fragment$1({ children: elements });
|
|
637
|
-
}
|
|
638
|
-
/**
|
|
639
|
-
* JSX Development runtime - same as jsx but with additional dev checks
|
|
640
|
-
*/
|
|
641
|
-
const jsxDEV = (...args) => {
|
|
642
|
-
// console.log('JSX DEV called:', ...args);
|
|
643
|
-
// console.log('children', (args[1] as any)?.children);
|
|
644
|
-
return jsx(...args);
|
|
645
|
-
};
|
|
646
|
-
/**
|
|
647
|
-
* JSX runtime for React 17+ automatic runtime
|
|
648
|
-
* This is called when using jsx: "react-jsx" or "react-jsxdev"
|
|
649
|
-
*/
|
|
650
|
-
const jsxs = jsx;
|
|
651
|
-
/**
|
|
652
|
-
* A helper to create redrawable elements
|
|
653
|
-
* ```tsx
|
|
654
|
-
* export function MyComponent() {
|
|
655
|
-
* let aa = 10;
|
|
656
|
-
* // ...
|
|
657
|
-
* // aa might be changed
|
|
658
|
-
* return createRedrawable(() => <div>{aa}</div>);
|
|
659
|
-
* }
|
|
660
|
-
* ```
|
|
661
|
-
* Then the returned element has a `redraw` method to redraw itself with new values.
|
|
662
|
-
* @param creator a simple creator function that returns an element
|
|
663
|
-
* @returns created element's ref
|
|
664
|
-
*/
|
|
665
|
-
function createRedrawable(creator) {
|
|
666
|
-
const elRef = ref();
|
|
667
|
-
const redraw = () => {
|
|
668
|
-
elRef.value = creator(); // ref setter automatically calls replaceWith
|
|
669
|
-
elRef.redraw = redraw;
|
|
670
|
-
return elRef.value;
|
|
671
|
-
};
|
|
672
|
-
redraw();
|
|
673
|
-
return elRef;
|
|
674
|
-
}
|
|
675
|
-
|
|
676
|
-
export { Fragment, h as createElement, createRedrawable, jsx, jsxDEV, jsxs };
|