@ktjs/core 0.27.2 → 0.28.2

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  import { otherstring, HTMLTag, SVGTag, MathMLTag } from '@ktjs/shared';
2
2
  export { HTMLTag, InputElementTag, MathMLTag, SVGTag } from '@ktjs/shared';
3
3
 
4
- declare class KTComputed<T> {
4
+ declare class KTComputed<T> implements KTReactive<T> {
5
5
  /**
6
6
  * Indicates that this is a KTRef instance
7
7
  */
@@ -13,6 +13,14 @@ declare class KTComputed<T> {
13
13
  */
14
14
  get value(): T;
15
15
  set value(_newValue: T);
16
+ /**
17
+ * Force listeners to run once with the latest computed result.
18
+ */
19
+ notify(): void;
20
+ /**
21
+ * Computed values are derived from dependencies and should not be mutated manually.
22
+ */
23
+ mutate<R = void>(): R;
16
24
  /**
17
25
  * Register a callback when the value changes
18
26
  * @param callback (newValue, oldValue) => xxx
@@ -45,15 +53,11 @@ interface KTEffectOptions {
45
53
  */
46
54
  declare function effect(effectFn: () => void, reactives: Array<KTReactive<any>>, options?: Partial<KTEffectOptions>): () => void;
47
55
 
48
- type KTReactive<T> = KTRef<T> | KTComputed<T>;
49
56
  declare const toReactive: <T>(value: T | KTReactive<T>, onChange?: ReactiveChangeHandler<T>) => KTReactive<T>;
50
- type KTReactify<T> = T extends boolean ? KTReactive<boolean> : T extends any ? KTReactive<T> : never;
51
- type KTReactifyObject<T extends object> = {
52
- [K in keyof T]: KTReactify<T[K]>;
53
- };
54
- type KTReactifyProps<T extends object> = {
55
- [K in keyof T]: KTReactify<Exclude<T[K], undefined>> | T[K];
56
- };
57
+ /**
58
+ * Extracts the value from a KTReactive, or returns the value directly if it's not reactive.
59
+ */
60
+ declare function dereactive<T = JSX.Element>(value: T | KTReactive<T>): T;
57
61
 
58
62
  declare const enum KTReactiveType {
59
63
  REF = 1,
@@ -62,9 +66,57 @@ declare const enum KTReactiveType {
62
66
  declare const isKT: <T = any>(obj: any) => obj is KTReactive<T>;
63
67
  declare const isRef: <T = any>(obj: any) => obj is KTRef<T>;
64
68
  declare const isComputed: <T = any>(obj: any) => obj is KTComputed<T>;
69
+
65
70
  type ReactiveChangeHandler<T> = (newValue: T, oldValue: T) => void;
66
71
 
67
- declare class KTRef<T> {
72
+ declare class KTReactive<T> {
73
+ /**
74
+ * Indicates that this is a KTRef instance
75
+ */
76
+ isKT: boolean;
77
+
78
+ ktType: KTReactiveType;
79
+
80
+ /**
81
+ * If new value and old value are both nodes, the old one will be replaced in the DOM
82
+ */
83
+ get value();
84
+ set value(newValue: T);
85
+
86
+ /**
87
+ * Force all listeners to run even when reference identity has not changed.
88
+ * Useful for in-place array/object mutations.
89
+ */
90
+ notify(): void;
91
+
92
+ /**
93
+ * Mutate current value in-place and notify listeners once.
94
+ *
95
+ * @example
96
+ * const items = ref<number[]>([1, 2]);
97
+ * items.mutate((list) => list.push(3));
98
+ */
99
+ mutate<R = void>(mutator: (currentValue: T) => R): R;
100
+ /**
101
+ * Register a callback when the value changes
102
+ * @param callback (newValue, oldValue) => xxx
103
+ */
104
+ addOnChange(callback: ReactiveChangeHandler<T>): void;
105
+ removeOnChange(callback: ReactiveChangeHandler<T>): void;
106
+ }
107
+
108
+ // & Shockingly, If T is boolean, KTReactify<T> becomes KTReactive<true> | KTReactive<false>. It causes @ktjs/mui that disabledRefs not assignable.
109
+ type KTReactify<T> = T extends boolean ? KTReactive<boolean> : T extends any ? KTReactive<T> : never;
110
+
111
+ type KTReactifyObject<T extends object> = {
112
+ [K in keyof T]: KTReactify<T[K]>;
113
+ };
114
+
115
+ type KTReactifyProps<T extends object> = {
116
+ [K in keyof T]: KTReactify<Exclude<T[K], undefined>> | T[K];
117
+ };
118
+
119
+ declare class KTRef<T> implements KTReactive<T> {
68
120
  /**
69
121
  * Indicates that this is a KTRef instance
70
122
  */
@@ -76,6 +128,19 @@ declare class KTRef<T> {
76
128
  */
77
129
  get value(): T;
78
130
  set value(newValue: T);
131
+ /**
132
+ * Force all listeners to run even when reference identity has not changed.
133
+ * Useful for in-place array/object mutations.
134
+ */
135
+ notify(): void;
136
+ /**
137
+ * Mutate current value in-place and notify listeners once.
138
+ *
139
+ * @example
140
+ * const items = ref<number[]>([1, 2]);
141
+ * items.mutate((list) => list.push(3));
142
+ */
143
+ mutate<R = void>(mutator: (currentValue: T) => R): R;
79
144
  /**
80
145
  * Register a callback when the value changes
81
146
  * @param callback (newValue, oldValue) => xxx
@@ -99,7 +164,6 @@ declare function ref<T = JSX.Element>(value?: T, onChange?: ReactiveChangeHandle
99
164
  * @param o value to convert
100
165
  */
101
166
  declare const toRef: <T = any>(o: any) => KTRef<T>;
102
- declare function deref<T = JSX.Element>(value: T | KTReactive<T>): T;
103
167
  type KTSurfaceRef<T extends Object> = {
104
168
  [K in keyof T]: KTRef<T[K]>;
105
169
  } & {
@@ -243,7 +307,7 @@ type KTComponent = (
243
307
  * ## About
244
308
  * @package @ktjs/core
245
309
  * @author Kasukabe Tsumugi <futami16237@gmail.com>
246
- * @version 0.27.2 (Last Update: 2026.02.10 07:27:25.966)
310
+ * @version 0.28.2 (Last Update: 2026.02.10 14:46:21.243)
247
311
  * @license MIT
248
312
  * @link https://github.com/baendlorel/kt.js
249
313
  * @link https://baendlorel.github.io/ Welcome to my site!
@@ -1348,7 +1412,7 @@ declare global {
1348
1412
  view: SVGAttributesMap['view'];
1349
1413
  }
1350
1414
 
1351
- type IntrinsicAttributes = {
1415
+ interface IntrinsicAttributes {
1352
1416
  /**
1353
1417
  * Make a reference to the created element
1354
1418
  */
@@ -1372,7 +1436,7 @@ declare global {
1372
1436
  */
1373
1437
  'k-html'?: any;
1374
1438
  children?: KTRawContent;
1375
- };
1439
+ }
1376
1440
 
1377
1441
  interface ElementChildrenAttribute {
1378
1442
  children: {};
@@ -1404,5 +1468,5 @@ interface KTForProps<T> {
1404
1468
  */
1405
1469
  declare function KTFor<T>(props: KTForProps<T>): KTForElement;
1406
1470
 
1407
- export { $modelOrRef, $setRef, Fragment, KTAsync, KTComputed, KTFor, KTReactiveType, KTRef, computed, h as createElement, createRedrawable, deref, effect, h, isComputed, isKT, isRef, jsx, jsxDEV, jsxs, ref, surfaceRef, toReactive, toRef };
1408
- export type { EventHandler, KTAttribute, KTForElement, KTForProps, KTPrefixedEventAttribute, KTRawAttr, KTRawContent, KTRawContents, KTReactify, KTReactifyObject, KTReactifyProps, KTReactive, KTSurfaceRef, ReactiveChangeHandler };
1471
+ export { $modelOrRef, $setRef, Fragment, KTAsync, KTComputed, KTFor, KTReactive, KTReactiveType, KTRef, computed, h as createElement, createRedrawable, dereactive, effect, h, isComputed, isKT, isRef, jsx, jsxDEV, jsxs, ref, surfaceRef, toReactive, toRef };
1472
+ export type { EventHandler, KTAttribute, KTForElement, KTForProps, KTPrefixedEventAttribute, KTRawAttr, KTRawContent, KTRawContents, KTReactify, KTReactifyObject, KTReactifyProps, KTSurfaceRef, ReactiveChangeHandler };
@@ -14,29 +14,6 @@ var __ktjs_core__ = (function (exports) {
14
14
  };
15
15
  }
16
16
 
17
- // String manipulation utilities
18
- /**
19
- * Default empty function
20
- */
21
- const $emptyFn = (() => true);
22
- const $emptyArray = [];
23
- /**
24
- * Safe and quick forEach implementation that works with array-like objects and handles sparse arrays.
25
- */
26
- const $forEach = (array, callback) => {
27
- const len = array.length;
28
- for (let i = 0; i < len; i++) {
29
- callback(array[i], i, array);
30
- }
31
- };
32
-
33
- const $emptyChildrenRef = { value: $emptyArray };
34
- // each instance shares the same empty array, but it will be replaced when used
35
- Comment.prototype.kisFragmentAnchor = false;
36
- Comment.prototype.kFragmentList = $emptyArray;
37
- Comment.prototype.kredraw = $emptyFn;
38
- Comment.prototype.kchildrenRef = $emptyChildrenRef;
39
-
40
17
  // Shared constants
41
18
  // Empty for now - can be extended with framework-wide constants
42
19
  /**
@@ -107,8 +84,23 @@ var __ktjs_core__ = (function (exports) {
107
84
  element.addEventListener(eventName, () => (valueRef.value = element[propName]));
108
85
  };
109
86
 
87
+ // String manipulation utilities
88
+ /**
89
+ * Default empty function
90
+ */
91
+ const $emptyFn = (() => true);
92
+ /**
93
+ * Safe and quick forEach implementation that works with array-like objects and handles sparse arrays.
94
+ */
95
+ const $forEach = (array, callback) => {
96
+ const len = array.length;
97
+ for (let i = 0; i < len; i++) {
98
+ callback(array[i], i, array);
99
+ }
100
+ };
101
+
110
102
  // incase that symbol is not supported
111
- Object.defineProperty(window, '__ktjs__', { value: '0.23.10' });
103
+ Object.defineProperty(window, '__ktjs__', { value: '0.23.11' });
112
104
 
113
105
  const isKT = (obj) => obj?.isKT;
114
106
  const isRef = (obj) => obj?.ktType === 1 /* KTReactiveType.REF */;
@@ -319,6 +311,14 @@ var __ktjs_core__ = (function (exports) {
319
311
  * @internal
320
312
  */
321
313
  _onChanges;
314
+ /**
315
+ * @internal
316
+ */
317
+ _emit(newValue, oldValue) {
318
+ for (let i = 0; i < this._onChanges.length; i++) {
319
+ this._onChanges[i](newValue, oldValue);
320
+ }
321
+ }
322
322
  constructor(_value, _onChanges) {
323
323
  this._value = _value;
324
324
  this._onChanges = _onChanges;
@@ -336,9 +336,30 @@ var __ktjs_core__ = (function (exports) {
336
336
  const oldValue = this._value;
337
337
  $replaceNode(oldValue, newValue);
338
338
  this._value = newValue;
339
- for (let i = 0; i < this._onChanges.length; i++) {
340
- this._onChanges[i](newValue, oldValue);
339
+ this._emit(newValue, oldValue);
340
+ }
341
+ /**
342
+ * Force all listeners to run even when reference identity has not changed.
343
+ * Useful for in-place array/object mutations.
344
+ */
345
+ notify() {
346
+ this._emit(this._value, this._value);
347
+ }
348
+ /**
349
+ * Mutate current value in-place and notify listeners once.
350
+ *
351
+ * @example
352
+ * const items = ref<number[]>([1, 2]);
353
+ * items.mutate((list) => list.push(3));
354
+ */
355
+ mutate(mutator) {
356
+ if (typeof mutator !== 'function') {
357
+ throw new Error('[kt.js error] KTRef.mutate: mutator must be a function');
341
358
  }
359
+ const oldValue = this._value;
360
+ const result = mutator(this._value);
361
+ this._emit(this._value, oldValue);
362
+ return result;
342
363
  }
343
364
  /**
344
365
  * Register a callback when the value changes
@@ -388,9 +409,6 @@ var __ktjs_core__ = (function (exports) {
388
409
  return ref(o);
389
410
  }
390
411
  };
391
- function deref(value) {
392
- return isKT(value) ? value.value : value;
393
- }
394
412
  function kcollect() {
395
413
  const newObj = {};
396
414
  const entries = $entries(this);
@@ -462,23 +480,37 @@ var __ktjs_core__ = (function (exports) {
462
480
  * @internal
463
481
  */
464
482
  _onChanges = [];
483
+ /**
484
+ * @internal
485
+ */
486
+ _emit(newValue, oldValue) {
487
+ for (let i = 0; i < this._onChanges.length; i++) {
488
+ this._onChanges[i](newValue, oldValue);
489
+ }
490
+ }
491
+ /**
492
+ * @internal
493
+ */
494
+ _recalculate(forceEmit = false) {
495
+ const oldValue = this._value;
496
+ const newValue = this._calculator();
497
+ if (oldValue === newValue) {
498
+ if (forceEmit) {
499
+ this._emit(newValue, oldValue);
500
+ }
501
+ return;
502
+ }
503
+ this._value = newValue;
504
+ $replaceNode(oldValue, newValue);
505
+ this._emit(newValue, oldValue);
506
+ }
465
507
  /**
466
508
  * @internal
467
509
  */
468
510
  _subscribe(reactives) {
469
511
  for (let i = 0; i < reactives.length; i++) {
470
512
  const reactive = reactives[i];
471
- reactive.addOnChange(() => {
472
- const oldValue = this._value;
473
- this._value = this._calculator();
474
- if (oldValue === this._value) {
475
- return;
476
- }
477
- $replaceNode(oldValue, this._value);
478
- for (let i = 0; i < this._onChanges.length; i++) {
479
- this._onChanges[i](this._value, oldValue);
480
- }
481
- });
513
+ reactive.addOnChange(() => this._recalculate());
482
514
  }
483
515
  }
484
516
  constructor(_calculator, reactives) {
@@ -495,6 +527,19 @@ var __ktjs_core__ = (function (exports) {
495
527
  set value(_newValue) {
496
528
  throw new Error('[kt.js error] KTComputed: cannot set value of a computed value');
497
529
  }
530
+ /**
531
+ * Force listeners to run once with the latest computed result.
532
+ */
533
+ notify() {
534
+ this._recalculate(true);
535
+ }
536
+ /**
537
+ * Computed values are derived from dependencies and should not be mutated manually.
538
+ */
539
+ mutate() {
540
+ console.warn('[kt.js warn]','KTComputed.mutate: computed is derived automatically; manual mutate is ignored. Use notify() instead');
541
+ return this._value;
542
+ }
498
543
  /**
499
544
  * Register a callback when the value changes
500
545
  * @param callback (newValue, oldValue) => xxx
@@ -587,6 +632,12 @@ var __ktjs_core__ = (function (exports) {
587
632
  return ref(value, onChange);
588
633
  }
589
634
  };
635
+ /**
636
+ * Extracts the value from a KTReactive, or returns the value directly if it's not reactive.
637
+ */
638
+ function dereactive(value) {
639
+ return isKT(value) ? value.value : value;
640
+ }
590
641
 
591
642
  function applyKModel(element, valueRef) {
592
643
  if (!isKT(valueRef)) {
@@ -626,7 +677,7 @@ var __ktjs_core__ = (function (exports) {
626
677
  * ## About
627
678
  * @package @ktjs/core
628
679
  * @author Kasukabe Tsumugi <futami16237@gmail.com>
629
- * @version 0.27.2 (Last Update: 2026.02.10 07:27:25.966)
680
+ * @version 0.28.2 (Last Update: 2026.02.10 14:46:21.243)
630
681
  * @license MIT
631
682
  * @link https://github.com/baendlorel/kt.js
632
683
  * @link https://baendlorel.github.io/ Welcome to my site!
@@ -661,34 +712,6 @@ var __ktjs_core__ = (function (exports) {
661
712
  return element;
662
713
  };
663
714
 
664
- const kredraw = function () {
665
- const newElements = this.kchildrenRef.value;
666
- const parent = this.parentNode;
667
- if (!parent) {
668
- // If anchor is not in DOM, only update internal state
669
- this.kFragmentList.length = 0;
670
- for (let i = 0; i < newElements.length; i++) {
671
- this.kFragmentList.push(newElements[i]);
672
- }
673
- return;
674
- }
675
- // Simple replacement algorithm: remove all old elements, insert all new elements
676
- // todo Future enhancement: key-based optimization
677
- // 1. Remove all old elements
678
- for (let i = 0; i < this.kFragmentList.length; i++) {
679
- this.kFragmentList[i].remove();
680
- }
681
- // 2. Insert all new elements
682
- const fragment = document.createDocumentFragment();
683
- this.kFragmentList.length = 0;
684
- for (let i = 0; i < newElements.length; i++) {
685
- const element = newElements[i];
686
- this.kFragmentList.push(element);
687
- fragment.appendChild(element);
688
- }
689
- // Insert after anchor
690
- parent.insertBefore(fragment, this.nextSibling);
691
- };
692
715
  /**
693
716
  * Fragment - Container component for managing arrays of child elements
694
717
  *
@@ -710,17 +733,44 @@ var __ktjs_core__ = (function (exports) {
710
733
  */
711
734
  function Fragment$1(props) {
712
735
  // key parameter reserved for future enhancement, currently unused
713
- const { key: _key } = props;
714
- const childrenRef = toReactive(props.children, () => anchor.kredraw());
736
+ // const { key: _key } = props;
737
+ const redraw = () => {
738
+ const newElements = childrenRef.value;
739
+ const parent = anchor.parentNode;
740
+ if (!parent) {
741
+ // If anchor is not in DOM, only update internal state
742
+ elements.length = 0;
743
+ for (let i = 0; i < newElements.length; i++) {
744
+ elements.push(newElements[i]);
745
+ }
746
+ return;
747
+ }
748
+ // Simple replacement algorithm: remove all old elements, insert all new elements
749
+ // todo Future enhancement: key-based optimization
750
+ // 1. Remove all old elements
751
+ for (let i = 0; i < elements.length; i++) {
752
+ elements[i].remove();
753
+ }
754
+ // 2. Insert all new elements
755
+ const fragment = document.createDocumentFragment();
756
+ elements.length = 0;
757
+ for (let i = 0; i < newElements.length; i++) {
758
+ const element = newElements[i];
759
+ elements.push(element);
760
+ fragment.appendChild(element);
761
+ }
762
+ // Insert after anchor
763
+ parent.insertBefore(fragment, anchor.nextSibling);
764
+ };
765
+ let initialized = false;
766
+ const childrenRef = toReactive(props.children, redraw);
767
+ const elements = [];
715
768
  const anchor = document.createComment('kt-fragment');
716
- anchor.kredraw = kredraw;
717
- anchor.kchildrenRef = childrenRef;
718
- anchor.kFragmentList = [];
719
- anchor.kisFragmentAnchor = true;
720
769
  // Observe DOM insertion
721
770
  const observer = new MutationObserver(() => {
722
- if (anchor.isConnected) {
723
- anchor.kredraw();
771
+ if (anchor.isConnected && !initialized) {
772
+ initialized = true;
773
+ redraw();
724
774
  observer.disconnect();
725
775
  }
726
776
  });
@@ -735,7 +785,7 @@ var __ktjs_core__ = (function (exports) {
735
785
  function convertChildrenToElements(children) {
736
786
  const elements = [];
737
787
  const processChild = (child) => {
738
- if (child == null || child === false || child === true) {
788
+ if (child === undefined || child === null || child === false || child === true) {
739
789
  // Ignore null, undefined, false, true
740
790
  return;
741
791
  }
@@ -1075,7 +1125,7 @@ var __ktjs_core__ = (function (exports) {
1075
1125
  exports.computed = computed;
1076
1126
  exports.createElement = h;
1077
1127
  exports.createRedrawable = createRedrawable;
1078
- exports.deref = deref;
1128
+ exports.dereactive = dereactive;
1079
1129
  exports.effect = effect;
1080
1130
  exports.h = h;
1081
1131
  exports.isComputed = isComputed;