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