@ktjs/core 0.26.9 → 0.27.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
@@ -117,6 +117,9 @@ declare const surfaceRef: <T extends Object>(obj: T) => KTSurfaceRef<T>;
117
117
  * Assert k-model to be a ref object
118
118
  */
119
119
  declare const $modelOrRef: <T = any>(props: any, defaultValue?: T) => KTRef<T>;
120
+ declare const $setRef: (props: {
121
+ ref?: KTRef<any>;
122
+ }, node: Node) => void;
120
123
 
121
124
  type HTML<T extends (HTMLTag | SVGTag | MathMLTag) & otherstring> = T extends SVGTag
122
125
  ? SVGElementTagNameMap[T]
@@ -240,7 +243,7 @@ type KTComponent = (
240
243
  * ## About
241
244
  * @package @ktjs/core
242
245
  * @author Kasukabe Tsumugi <futami16237@gmail.com>
243
- * @version 0.26.9 (Last Update: 2026.02.06 16:29:24.402)
246
+ * @version 0.27.2 (Last Update: 2026.02.10 07:27:25.966)
244
247
  * @license MIT
245
248
  * @link https://github.com/baendlorel/kt.js
246
249
  * @link https://baendlorel.github.io/ Welcome to my site!
@@ -257,11 +260,11 @@ type JSXTag = HTMLTag | ((props?: any) => HTMLElement) | ((props?: any) => Promi
257
260
  declare function jsx(tag: JSXTag, props: KTAttribute): JSX.Element;
258
261
  /**
259
262
  * Fragment support - returns an array of children
260
- * Note: kt.js doesn't have a real Fragment concept,
263
+ * Enhanced Fragment component that manages arrays of elements
261
264
  */
262
- declare function Fragment(_props: {
265
+ declare function Fragment(props: {
263
266
  children?: KTRawContent;
264
- }): HTMLElement;
267
+ }): JSX.Element;
265
268
  /**
266
269
  * JSX Development runtime - same as jsx but with additional dev checks
267
270
  */
@@ -1388,12 +1391,10 @@ declare function KTAsync<T extends KTComponent>(props: {
1388
1391
  children?: KTRawContent;
1389
1392
  } & ExtractComponentProps<T>): JSX.Element;
1390
1393
 
1391
- type KTForElement = JSX.Element & {
1392
- redraw: (newProps?: KTAttribute) => void;
1393
- };
1394
+ type KTForElement = JSX.Element;
1394
1395
  interface KTForProps<T> {
1395
1396
  ref?: KTRef<KTForElement>;
1396
- list: T[];
1397
+ list: T[] | KTReactive<T[]>;
1397
1398
  key?: (item: T, index: number, array: T[]) => any;
1398
1399
  map: (item: T, index: number, array: T[]) => HTMLElement;
1399
1400
  }
@@ -1403,5 +1404,5 @@ interface KTForProps<T> {
1403
1404
  */
1404
1405
  declare function KTFor<T>(props: KTForProps<T>): KTForElement;
1405
1406
 
1406
- export { $modelOrRef, Fragment, KTAsync, KTComputed, KTFor, KTReactiveType, KTRef, computed, h as createElement, createRedrawable, deref, effect, h, isComputed, isKT, isRef, jsx, jsxDEV, jsxs, ref, surfaceRef, toReactive, toRef };
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 };
1407
1408
  export type { EventHandler, KTAttribute, KTForElement, KTForProps, KTPrefixedEventAttribute, KTRawAttr, KTRawContent, KTRawContents, KTReactify, KTReactifyObject, KTReactifyProps, KTReactive, KTSurfaceRef, ReactiveChangeHandler };
@@ -1,6 +1,42 @@
1
1
  var __ktjs_core__ = (function (exports) {
2
2
  'use strict';
3
3
 
4
+ // Cached native methods for performance optimization
5
+ const $isArray = Array.isArray;
6
+ const $is = Object.is;
7
+ const $entries = Object.entries;
8
+ const $random = Math.random;
9
+ const $isThenable = (o) => typeof o?.then === 'function';
10
+
11
+ if (typeof Symbol === 'undefined') {
12
+ window.Symbol = function Symbol(description) {
13
+ return `@@SYMBOL_${description || ''}_${$random().toString(36).slice(2)}`;
14
+ };
15
+ }
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
+
4
40
  // Shared constants
5
41
  // Empty for now - can be extended with framework-wide constants
6
42
  /**
@@ -12,13 +48,6 @@ var __ktjs_core__ = (function (exports) {
12
48
  */
13
49
  const MATHML_ATTR_FLAG = '__kt_mathml__';
14
50
 
15
- // Cached native methods for performance optimization
16
- const $isArray = Array.isArray;
17
- const $is = Object.is;
18
- const $entries = Object.entries;
19
- const $random = Math.random;
20
- const $isThenable = (o) => typeof o?.then === 'function';
21
-
22
51
  // DOM manipulation utilities
23
52
  // # dom natives
24
53
  const $isNode = (x) => x?.nodeType > 0;
@@ -72,26 +101,14 @@ var __ktjs_core__ = (function (exports) {
72
101
  /**
73
102
  * Used for `k-model`
74
103
  */
75
- const applyModel = (element, valueRef, propName, eventName) => {
104
+ const $applyModel = (element, valueRef, propName, eventName) => {
76
105
  element[propName] = valueRef.value; // initialize
77
106
  valueRef.addOnChange((newValue) => (element[propName] = newValue));
78
107
  element.addEventListener(eventName, () => (valueRef.value = element[propName]));
79
108
  };
80
109
 
81
- // String manipulation utilities
82
- /**
83
- * Default empty function
84
- */
85
- const $emptyFn = (() => true);
86
-
87
- if (typeof Symbol === 'undefined') {
88
- window.Symbol = function Symbol(description) {
89
- return `@@SYMBOL_${description || ''}_${$random().toString(36).slice(2)}`;
90
- };
91
- }
92
-
93
- // Shared utilities and cached native methods for kt.js framework
94
- Object.defineProperty(window, '__ktjs__', { value: '0.23.3' });
110
+ // incase that symbol is not supported
111
+ Object.defineProperty(window, '__ktjs__', { value: '0.23.10' });
95
112
 
96
113
  const isKT = (obj) => obj?.isKT;
97
114
  const isRef = (obj) => obj?.ktType === 1 /* KTReactiveType.REF */;
@@ -247,6 +264,7 @@ var __ktjs_core__ = (function (exports) {
247
264
  else {
248
265
  $append.call(element, c);
249
266
  // Handle KTFor anchor
267
+ // todo Maybe not needed anymore
250
268
  const list = c.__kt_for_list__;
251
269
  if ($isArray(list)) {
252
270
  apd(element, list);
@@ -405,13 +423,26 @@ var __ktjs_core__ = (function (exports) {
405
423
  // & props is an object. Won't use it in any other place
406
424
  if ('k-model' in props) {
407
425
  const kmodel = props['k-model'];
408
- if (!kmodel?.isKT) {
426
+ if (isRef(kmodel)) {
427
+ return kmodel;
428
+ }
429
+ else {
409
430
  throw new Error(`[kt.js error] k-model data must be a KTRef object, please use 'ref(...)' to wrap it.`);
410
431
  }
411
- return kmodel;
412
432
  }
413
433
  return ref(defaultValue);
414
434
  };
435
+ const $setRef = (props, node) => {
436
+ if ('ref' in props) {
437
+ const r = props.ref;
438
+ if (isRef(r)) {
439
+ r.value = node;
440
+ }
441
+ else {
442
+ throw new Error('[kt.js error] Fragment: ref must be a KTRef');
443
+ }
444
+ }
445
+ };
415
446
 
416
447
  class KTComputed {
417
448
  /**
@@ -564,17 +595,17 @@ var __ktjs_core__ = (function (exports) {
564
595
  }
565
596
  if (element instanceof HTMLInputElement) {
566
597
  if (element.type === 'radio' || element.type === 'checkbox') {
567
- applyModel(element, valueRef, 'checked', 'change');
598
+ $applyModel(element, valueRef, 'checked', 'change');
568
599
  }
569
600
  else {
570
- applyModel(element, valueRef, 'value', 'input');
601
+ $applyModel(element, valueRef, 'value', 'input');
571
602
  }
572
603
  }
573
604
  else if (element instanceof HTMLSelectElement) {
574
- applyModel(element, valueRef, 'value', 'change');
605
+ $applyModel(element, valueRef, 'value', 'change');
575
606
  }
576
607
  else if (element instanceof HTMLTextAreaElement) {
577
- applyModel(element, valueRef, 'value', 'input');
608
+ $applyModel(element, valueRef, 'value', 'input');
578
609
  }
579
610
  else {
580
611
  console.warn('[kt.js warn]','not supported element for k-model:');
@@ -595,7 +626,7 @@ var __ktjs_core__ = (function (exports) {
595
626
  * ## About
596
627
  * @package @ktjs/core
597
628
  * @author Kasukabe Tsumugi <futami16237@gmail.com>
598
- * @version 0.26.9 (Last Update: 2026.02.06 16:29:24.402)
629
+ * @version 0.27.2 (Last Update: 2026.02.10 07:27:25.966)
599
630
  * @license MIT
600
631
  * @link https://github.com/baendlorel/kt.js
601
632
  * @link https://baendlorel.github.io/ Welcome to my site!
@@ -630,7 +661,110 @@ var __ktjs_core__ = (function (exports) {
630
661
  return element;
631
662
  };
632
663
 
633
- const dummyRef = { value: null };
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
+ /**
693
+ * Fragment - Container component for managing arrays of child elements
694
+ *
695
+ * Features:
696
+ * 1. Returns a comment anchor node, child elements are inserted after the anchor
697
+ * 2. Supports reactive arrays, automatically updates DOM when array changes
698
+ * 3. Basic version uses simple replacement algorithm (remove all old elements, insert all new elements)
699
+ * 4. Future enhancement: key-based optimization
700
+ *
701
+ * Usage example:
702
+ * ```tsx
703
+ * const children = ref([<div>A</div>, <div>B</div>]);
704
+ * const fragment = <Fragment children={children} />;
705
+ * document.body.appendChild(fragment);
706
+ *
707
+ * // Automatic update
708
+ * children.value = [<div>C</div>, <div>D</div>];
709
+ * ```
710
+ */
711
+ function Fragment$1(props) {
712
+ // key parameter reserved for future enhancement, currently unused
713
+ const { key: _key } = props;
714
+ const childrenRef = toReactive(props.children, () => anchor.kredraw());
715
+ const anchor = document.createComment('kt-fragment');
716
+ anchor.kredraw = kredraw;
717
+ anchor.kchildrenRef = childrenRef;
718
+ anchor.kFragmentList = [];
719
+ anchor.kisFragmentAnchor = true;
720
+ // Observe DOM insertion
721
+ const observer = new MutationObserver(() => {
722
+ if (anchor.isConnected) {
723
+ anchor.kredraw();
724
+ observer.disconnect();
725
+ }
726
+ });
727
+ observer.observe(document.body, { childList: true, subtree: true });
728
+ // Set ref reference
729
+ $setRef(props, anchor);
730
+ return anchor;
731
+ }
732
+ /**
733
+ * Convert KTRawContent to HTMLElement array
734
+ */
735
+ function convertChildrenToElements(children) {
736
+ const elements = [];
737
+ const processChild = (child) => {
738
+ if (child == null || child === false || child === true) {
739
+ // Ignore null, undefined, false, true
740
+ return;
741
+ }
742
+ if ($isArray(child)) {
743
+ // Recursively process array
744
+ $forEach(child, processChild);
745
+ return;
746
+ }
747
+ if (typeof child === 'string' || typeof child === 'number') {
748
+ // & Wrap text in span element? No! use text node instead
749
+ const textNode = document.createTextNode(String(child));
750
+ elements.push(textNode);
751
+ return;
752
+ }
753
+ if (child instanceof HTMLElement) {
754
+ elements.push(child);
755
+ return;
756
+ }
757
+ if (isKT(child)) {
758
+ processChild(child.value);
759
+ return;
760
+ }
761
+ // Other types ignored or converted to string
762
+ console.warn('[kt.js warn]','Fragment: unsupported child type', child);
763
+ };
764
+ processChild(children);
765
+ return elements;
766
+ }
767
+
634
768
  const create = (tag, props) => {
635
769
  if (typeof tag === 'function') {
636
770
  return tag(props);
@@ -648,7 +782,6 @@ var __ktjs_core__ = (function (exports) {
648
782
  if (isComputed(props.ref)) {
649
783
  throw new Error('[kt.js error] Cannot assign a computed value to an element.');
650
784
  }
651
- const maybeDummyRef = isRef(props.ref) ? props.ref : dummyRef;
652
785
  let el;
653
786
  if ('k-if' in props) {
654
787
  const kif = props['k-if'];
@@ -660,49 +793,31 @@ var __ktjs_core__ = (function (exports) {
660
793
  return;
661
794
  }
662
795
  const oldEl = el;
663
- el = newValue ? create(tag, props) : placeholder();
796
+ $setRef(props, (el = newValue ? create(tag, props) : placeholder()));
664
797
  $replaceNode(oldEl, el);
665
- maybeDummyRef.value = el;
666
798
  });
667
799
  condition = kif.value;
668
800
  }
669
801
  if (!condition) {
670
802
  // & make comment placeholder in case that ref might be redrawn later
671
- el = placeholder();
672
- maybeDummyRef.value = el;
803
+ $setRef(props, (el = placeholder()));
673
804
  return el;
674
805
  }
675
806
  }
676
- el = create(tag, props);
677
- maybeDummyRef.value = el;
807
+ $setRef(props, (el = create(tag, props)));
678
808
  return el;
679
809
  }
680
810
  /**
681
811
  * Fragment support - returns an array of children
682
- * Note: kt.js doesn't have a real Fragment concept,
812
+ * Enhanced Fragment component that manages arrays of elements
683
813
  */
684
- function Fragment(_props) {
685
- throw new Error("[kt.js error] doesn't have a Fragment concept");
686
- // const { children } = props ?? {};
687
- // if (!children) {
688
- // return ;
689
- // }
690
- // // If single child, return it directly
691
- // if (!Array.isArray(children)) {
692
- // return children as HTMLElement;
693
- // }
694
- // // For multiple children, create a document fragment wrapper
695
- // // This is a limitation - JSX fragments must be wrapped in kt.js
696
- // const wrapper = document.createElement('div');
697
- // wrapper.setAttribute('data-kt-fragment', 'true');
698
- // children.forEach((child) => {
699
- // if (typeof child === 'string') {
700
- // wrapper.appendChild(document.createTextNode(child));
701
- // } else if (child instanceof HTMLElement) {
702
- // wrapper.appendChild(child);
703
- // }
704
- // });
705
- // return wrapper;
814
+ function Fragment(props) {
815
+ const { children } = props ?? {};
816
+ if (!children) {
817
+ return document.createComment('kt-fragment-empty');
818
+ }
819
+ const elements = convertChildrenToElements(children);
820
+ return Fragment$1({ children: elements });
706
821
  }
707
822
  /**
708
823
  * JSX Development runtime - same as jsx but with additional dev checks
@@ -759,45 +874,17 @@ var __ktjs_core__ = (function (exports) {
759
874
  * Returns a Comment anchor node with rendered elements in __kt_for_list__
760
875
  */
761
876
  function KTFor(props) {
762
- const { list, map } = props;
763
- const key = props.key ?? ((item) => item);
764
- // Create anchor comment node
765
- const anchor = document.createComment('kt-for');
766
- // Store current state
767
- let currentList = list;
768
- let currentKey = key;
769
- let currentMap = map;
770
- // Map to track rendered nodes by key
771
- const nodeMap = new Map();
772
- // Render initial list
773
- const elements = [];
774
- for (let index = 0; index < currentList.length; index++) {
775
- const item = currentList[index];
776
- const itemKey = currentKey(item, index, currentList);
777
- const node = currentMap(item, index, currentList);
778
- nodeMap.set(itemKey, node);
779
- elements.push(node);
780
- }
781
- // Attach elements array to anchor
782
- anchor.__kt_for_list__ = elements;
783
- // Redraw function for updates
784
- anchor.redraw = (newProps) => {
785
- const newList = (newProps?.list ?? currentList);
786
- const newKey = (newProps?.key ?? currentKey);
787
- const newMap = (newProps?.map ?? currentMap);
788
- // Update stored values
789
- currentList = newList;
790
- currentKey = newKey;
791
- currentMap = newMap;
877
+ const redraw = () => {
878
+ const newList = listRef.value;
792
879
  const parent = anchor.parentNode;
793
880
  if (!parent) {
794
881
  // If not in DOM yet, just rebuild the list
795
882
  const newElements = [];
796
883
  nodeMap.clear();
797
- for (let index = 0; index < currentList.length; index++) {
798
- const item = currentList[index];
799
- const itemKey = currentKey(item, index, currentList);
800
- const node = currentMap(item, index, currentList);
884
+ for (let index = 0; index < newList.length; index++) {
885
+ const item = newList[index];
886
+ const itemKey = currentKey(item, index, newList);
887
+ const node = currentMap(item, index, newList);
801
888
  nodeMap.set(itemKey, node);
802
889
  newElements.push(node);
803
890
  }
@@ -819,8 +906,8 @@ var __ktjs_core__ = (function (exports) {
819
906
  const fragment = document.createDocumentFragment();
820
907
  for (let i = 0; i < newLength; i++) {
821
908
  const item = newList[i];
822
- const itemKey = newKey(item, i, newList);
823
- const node = newMap(item, i, newList);
909
+ const itemKey = currentKey(item, i, newList);
910
+ const node = currentMap(item, i, newList);
824
911
  nodeMap.set(itemKey, node);
825
912
  newElements.push(node);
826
913
  fragment.appendChild(node);
@@ -836,7 +923,7 @@ var __ktjs_core__ = (function (exports) {
836
923
  let moved = false;
837
924
  for (let i = 0; i < newLength; i++) {
838
925
  const item = newList[i];
839
- const itemKey = newKey(item, i, newList);
926
+ const itemKey = currentKey(item, i, newList);
840
927
  newKeyToNewIndex.set(itemKey, i);
841
928
  if (nodeMap.has(itemKey)) {
842
929
  // Reuse existing node
@@ -852,7 +939,7 @@ var __ktjs_core__ = (function (exports) {
852
939
  }
853
940
  else {
854
941
  // Create new node
855
- newElements[i] = newMap(item, i, newList);
942
+ newElements[i] = currentMap(item, i, newList);
856
943
  }
857
944
  }
858
945
  // Remove nodes not in new list
@@ -868,7 +955,7 @@ var __ktjs_core__ = (function (exports) {
868
955
  // Update DOM with minimal operations
869
956
  if (moved) {
870
957
  // Use longest increasing subsequence to minimize moves
871
- const seq = getSequence(newElements.map((el, i) => (nodeMap.has(newKey(newList[i], i, newList)) ? i : -1)));
958
+ const seq = getSequence(newElements.map((el, i) => (nodeMap.has(currentKey(newList[i], i, newList)) ? i : -1)));
872
959
  let j = seq.length - 1;
873
960
  let anchor = null;
874
961
  // Traverse from end to start for stable insertions
@@ -911,16 +998,28 @@ var __ktjs_core__ = (function (exports) {
911
998
  // Update maps
912
999
  nodeMap.clear();
913
1000
  for (let i = 0; i < newLength; i++) {
914
- const itemKey = newKey(newList[i], i, newList);
1001
+ const itemKey = currentKey(newList[i], i, newList);
915
1002
  nodeMap.set(itemKey, newElements[i]);
916
1003
  }
917
1004
  anchor.__kt_for_list__ = newElements;
918
1005
  return anchor;
919
1006
  };
920
- // Set ref if provided
921
- if (isRef(props.ref)) {
922
- props.ref.value = anchor;
1007
+ const { key: currentKey = (item) => item, map: currentMap } = props;
1008
+ const listRef = toReactive(props.list, redraw);
1009
+ const anchor = document.createComment('kt-for');
1010
+ // Map to track rendered nodes by key
1011
+ const nodeMap = new Map();
1012
+ // Render initial list
1013
+ const elements = [];
1014
+ for (let index = 0; index < listRef.value.length; index++) {
1015
+ const item = listRef.value[index];
1016
+ const itemKey = currentKey(item, index, listRef.value);
1017
+ const node = currentMap(item, index, listRef.value);
1018
+ nodeMap.set(itemKey, node);
1019
+ elements.push(node);
923
1020
  }
1021
+ anchor.__kt_for_list__ = elements;
1022
+ $setRef(props, anchor);
924
1023
  return anchor;
925
1024
  }
926
1025
  // Longest Increasing Subsequence algorithm (optimized for diff)
@@ -967,6 +1066,7 @@ var __ktjs_core__ = (function (exports) {
967
1066
  }
968
1067
 
969
1068
  exports.$modelOrRef = $modelOrRef;
1069
+ exports.$setRef = $setRef;
970
1070
  exports.Fragment = Fragment;
971
1071
  exports.KTAsync = KTAsync;
972
1072
  exports.KTComputed = KTComputed;