@ktjs/core 0.23.0 → 0.24.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.
@@ -1,11 +1,34 @@
1
+ // Shared constants
2
+ // Empty for now - can be extended with framework-wide constants
3
+ /**
4
+ * Mark the attribute as SVG to handle special cases during rendering.
5
+ */
6
+ const SVG_ATTR_FLAG = '__kt_svg__';
7
+ /**
8
+ * Mark the attribute as MathML to handle special cases during rendering.
9
+ */
10
+ const MATHML_ATTR_FLAG = '__kt_mathml__';
11
+
1
12
  // Cached native methods for performance optimization
2
13
  const $isArray = Array.isArray;
3
- const $entries = Object.entries;
4
- const $isThenable = (o) => typeof o === 'object' && o !== null && typeof o.then === 'function';
14
+ const $is = Object.is;
15
+ const $random = Math.random;
16
+ const $isThenable = (o) => typeof o?.then === 'function';
5
17
 
6
18
  // DOM manipulation utilities
7
19
  // # dom natives
8
20
  const $isNode = (x) => x?.nodeType > 0;
21
+ /**
22
+ * Safe replace `oldNode` With `newNode`
23
+ */
24
+ const $replaceNode = (oldNode, newNode) => {
25
+ if ($isNode(oldNode) && $isNode(newNode)) {
26
+ if (newNode.contains(oldNode)) {
27
+ newNode.remove();
28
+ }
29
+ oldNode.replaceWith(newNode);
30
+ }
31
+ };
9
32
  /**
10
33
  * & Remove `bind` because it is shockingly slower than wrapper
11
34
  * & `window.document` is safe because it is not configurable and its setter is undefined
@@ -51,130 +74,21 @@ const applyModel = (element, valueRef, propName, eventName) => {
51
74
  element.addEventListener(eventName, () => (valueRef.value = element[propName]));
52
75
  };
53
76
 
77
+ if (typeof Symbol === 'undefined') {
78
+ window.Symbol = function Symbol(description) {
79
+ return `@@SYMBOL_${description || ''}_${$random().toString(36).slice(2)}`;
80
+ };
81
+ }
82
+
54
83
  // Shared utilities and cached native methods for kt.js framework
55
- // import './misc/symbol-polyfill.js';
56
- // Re-export all utilities
57
- Object.defineProperty(window, '__ktjs__', { value: '0.22.3' });
84
+ Object.defineProperty(window, '__ktjs__', { value: '0.22.6' });
58
85
 
59
- class KTRef {
60
- /**
61
- * Indicates that this is a KTRef instance
62
- */
63
- isKT = true;
64
- /**
65
- * @internal
66
- */
67
- _value;
68
- /**
69
- * @internal
70
- */
71
- _onChanges;
72
- constructor(_value, _onChanges) {
73
- this._value = _value;
74
- this._onChanges = _onChanges;
75
- }
76
- /**
77
- * If new value and old value are both nodes, the old one will be replaced in the DOM
78
- */
79
- get value() {
80
- return this._value;
81
- }
82
- set value(newValue) {
83
- if (newValue === this._value) {
84
- return;
85
- }
86
- // replace the old node with the new one in the DOM if both are nodes
87
- if (this._value instanceof Node && newValue instanceof Node) {
88
- if (newValue.contains(this._value)) {
89
- this._value.remove();
90
- }
91
- this._value.replaceWith(newValue);
92
- }
93
- const oldValue = this._value;
94
- this._value = newValue;
95
- for (let i = 0; i < this._onChanges.length; i++) {
96
- this._onChanges[i](newValue, oldValue);
97
- }
98
- }
99
- /**
100
- * Register a callback when the value changes
101
- * @param callback (newValue, oldValue) => xxx
102
- */
103
- addOnChange(callback) {
104
- if (typeof callback !== 'function') {
105
- throw new Error('[kt.js error] KTRef.addOnChange: callback must be a function');
106
- }
107
- this._onChanges.push(callback);
108
- }
109
- removeOnChange(callback) {
110
- for (let i = this._onChanges.length - 1; i >= 0; i--) {
111
- if (this._onChanges[i] === callback) {
112
- this._onChanges.splice(i, 1);
113
- return true;
114
- }
115
- }
116
- return false;
117
- }
118
- }
119
- const isKTRef = (obj) => obj?.isKT === true;
120
- /**
121
- * Reference to the created HTML element.
122
- * - **Only** respond to `ref.value` changes, not reactive to internal changes of the element.
123
- * - can alse be used to store normal values, but it is not reactive.
124
- * - if the value is already a `KTRef`, it will be returned **directly**.
125
- * @param value mostly an HTMLElement
126
- */
127
- function ref(value, onChange) {
128
- if (isKTRef(value)) {
129
- if (onChange) {
130
- value.addOnChange(onChange);
131
- }
132
- return value;
133
- }
134
- return new KTRef(value, onChange ? [onChange] : []);
135
- }
136
- function deref(value) {
137
- return isKTRef(value) ? value.value : value;
138
- }
139
- function kcollect() {
140
- const newObj = {};
141
- const entries = $entries(this);
142
- for (let i = 0; i < entries.length; i++) {
143
- const key = entries[i][0];
144
- if (key === 'kcollect') {
145
- continue;
146
- }
147
- newObj[key] = entries[i][1].value;
148
- }
149
- return newObj;
150
- }
151
- /**
152
- * Make all first-level properties of the object a `KTRef`.
153
- * - `obj.a.b` is not reactive
154
- */
155
- const surfaceRef = (obj) => {
156
- const entries = $entries(obj);
157
- const newObj = { kcollect };
158
- for (let i = 0; i < entries.length; i++) {
159
- newObj[entries[i][0]] = ref(entries[i][1]);
160
- }
161
- return newObj;
162
- };
163
- // # asserts
164
- /**
165
- * Assert k-model to be a ref object
166
- */
167
- const $modelOrRef = (props, defaultValue) => {
168
- // & props is an object. Won't use it in any other place
169
- if ('k-model' in props) {
170
- const kmodel = props['k-model'];
171
- if (!kmodel?.isKT) {
172
- throw new Error(`[kt.js error] k-model data must be a KTRef object, please use 'ref(...)' to wrap it.`);
173
- }
174
- return kmodel;
175
- }
176
- return ref(defaultValue);
177
- };
86
+ // export const KT_TYPE_REF = 1 as const;
87
+ // export const KT_TYPE_COMPUTED = 2 as const;
88
+ // export type KTReactiveTypeEnum = typeof KT_TYPE_REF | typeof KT_TYPE_COMPUTED;
89
+ const isKT = (obj) => obj?.isKT;
90
+ const isRef = (obj) => obj?.ktType === 1 /* KTReactiveType.REF */;
91
+ const isComputed = (obj) => obj?.ktType === 2 /* KTReactiveType.COMPUTED */;
178
92
 
179
93
  const booleanHandler = (element, key, value) => {
180
94
  if (key in element) {
@@ -236,7 +150,7 @@ function attrIsObject(element, attr) {
236
150
  }
237
151
  if ('k-html' in attr) {
238
152
  const html = attr['k-html'];
239
- if (isKTRef(html)) {
153
+ if (isKT(html)) {
240
154
  element.innerHTML = html.value;
241
155
  html.addOnChange((v) => (element.innerHTML = v));
242
156
  }
@@ -245,14 +159,14 @@ function attrIsObject(element, attr) {
245
159
  }
246
160
  }
247
161
  for (const key in attr) {
248
- if (key === 'class' ||
162
+ if (key === 'k-if' ||
163
+ key === 'k-model' ||
164
+ key === 'ref' ||
165
+ key === 'class' ||
249
166
  key === 'className' ||
250
167
  key === 'style' ||
251
168
  key === 'children' ||
252
- key === 'k-if' ||
253
- key === 'k-model' ||
254
- key === 'k-html' ||
255
- key === 'ref') {
169
+ key === 'k-html') {
256
170
  continue;
257
171
  }
258
172
  const o = attr[key];
@@ -285,7 +199,7 @@ function apdSingle(element, c) {
285
199
  if (c === false || c === undefined || c === null) {
286
200
  return;
287
201
  }
288
- if (isKTRef(c)) {
202
+ if (isKT(c)) {
289
203
  let node = assureNode(c.value);
290
204
  $append.call(element, node);
291
205
  c.addOnChange((newValue, oldValue) => {
@@ -342,8 +256,74 @@ function applyContent(element, content) {
342
256
  }
343
257
  }
344
258
 
259
+ class KTRef {
260
+ /**
261
+ * Indicates that this is a KTRef instance
262
+ */
263
+ isKT = true;
264
+ ktType = 1 /* KTReactiveType.REF */;
265
+ /**
266
+ * @internal
267
+ */
268
+ _value;
269
+ /**
270
+ * @internal
271
+ */
272
+ _onChanges;
273
+ constructor(_value, _onChanges) {
274
+ this._value = _value;
275
+ this._onChanges = _onChanges;
276
+ }
277
+ /**
278
+ * If new value and old value are both nodes, the old one will be replaced in the DOM
279
+ */
280
+ get value() {
281
+ return this._value;
282
+ }
283
+ set value(newValue) {
284
+ if ($is(newValue, this._value)) {
285
+ return;
286
+ }
287
+ const oldValue = this._value;
288
+ $replaceNode(oldValue, newValue);
289
+ this._value = newValue;
290
+ for (let i = 0; i < this._onChanges.length; i++) {
291
+ this._onChanges[i](newValue, oldValue);
292
+ }
293
+ }
294
+ /**
295
+ * Register a callback when the value changes
296
+ * @param callback (newValue, oldValue) => xxx
297
+ */
298
+ addOnChange(callback) {
299
+ if (typeof callback !== 'function') {
300
+ throw new Error('[kt.js error] KTRef.addOnChange: callback must be a function');
301
+ }
302
+ this._onChanges.push(callback);
303
+ }
304
+ removeOnChange(callback) {
305
+ for (let i = this._onChanges.length - 1; i >= 0; i--) {
306
+ if (this._onChanges[i] === callback) {
307
+ this._onChanges.splice(i, 1);
308
+ return true;
309
+ }
310
+ }
311
+ return false;
312
+ }
313
+ }
314
+ /**
315
+ * Reference to the created HTML element.
316
+ * - **Only** respond to `ref.value` changes, not reactive to internal changes of the element.
317
+ * - can alse be used to store normal values, but it is not reactive.
318
+ * - if the value is already a `KTRef`, it will be returned **directly**.
319
+ * @param value mostly an HTMLElement
320
+ */
321
+ function ref(value, onChange) {
322
+ return new KTRef(value, []);
323
+ }
324
+
345
325
  function applyKModel(element, valueRef) {
346
- if (!isKTRef(valueRef)) {
326
+ if (!isKT(valueRef)) {
347
327
  console.warn('[kt.js warn] k-model value must be a KTRef.');
348
328
  return;
349
329
  }
@@ -370,9 +350,6 @@ const htmlCreator = (tag) => document.createElement(tag);
370
350
  const svgCreator = (tag) => document.createElementNS('http://www.w3.org/2000/svg', tag);
371
351
  const mathMLCreator = (tag) => document.createElementNS('http://www.w3.org/1998/Math/MathML', tag);
372
352
  let creator = htmlCreator;
373
- // # consts
374
- const SVG_ATTR_FLAG = '__kt_svg__';
375
- const MATHML_ATTR_FLAG = '__kt_mathml__';
376
353
  /**
377
354
  * Create an enhanced HTMLElement.
378
355
  * - Only supports HTMLElements, **NOT** SVGElements or other Elements.
@@ -383,7 +360,7 @@ const MATHML_ATTR_FLAG = '__kt_mathml__';
383
360
  * ## About
384
361
  * @package @ktjs/core
385
362
  * @author Kasukabe Tsumugi <futami16237@gmail.com>
386
- * @version 0.23.0 (Last Update: 2026.02.03 17:15:01.812)
363
+ * @version 0.24.0 (Last Update: 2026.02.05 12:08:54.164)
387
364
  * @license MIT
388
365
  * @link https://github.com/baendlorel/kt.js
389
366
  * @link https://baendlorel.github.io/ Welcome to my site!
@@ -413,13 +390,7 @@ const h = (tag, attr, content) => {
413
390
  applyAttr(element, attr);
414
391
  applyContent(element, content);
415
392
  if (typeof attr === 'object' && attr !== null && 'k-model' in attr) {
416
- const kmodel = attr['k-model'];
417
- if (isKTRef(kmodel)) {
418
- applyKModel(element, kmodel);
419
- }
420
- else {
421
- throw new Error('[kt.js error] k-model value must be a KTRef.');
422
- }
393
+ applyKModel(element, attr['k-model']);
423
394
  }
424
395
  return element;
425
396
  };
@@ -439,20 +410,23 @@ const placeholder = () => document.createComment('k-if');
439
410
  * @param props properties/attributes
440
411
  */
441
412
  function jsx(tag, props) {
442
- const maybeDummyRef = isKTRef(props.ref) ? props.ref : dummyRef;
413
+ if (isComputed(props.ref)) {
414
+ throw new Error('[kt.js error] Cannot assign a computed value to an element.');
415
+ }
416
+ const maybeDummyRef = isRef(props.ref) ? props.ref : dummyRef;
443
417
  let el;
444
418
  if ('k-if' in props) {
445
419
  const kif = props['k-if'];
446
420
  let condition = kif; // assume boolean by default
447
421
  // Handle reactive k-if
448
- if (isKTRef(kif)) {
422
+ if (isKT(kif)) {
449
423
  kif.addOnChange((newValue, oldValue) => {
450
424
  if (newValue === oldValue) {
451
425
  return;
452
426
  }
453
427
  const oldEl = el;
454
428
  el = newValue ? create(tag, props) : placeholder();
455
- oldEl.replaceWith(el);
429
+ $replaceNode(oldEl, el);
456
430
  maybeDummyRef.value = el;
457
431
  });
458
432
  condition = kif.value;
@@ -533,4 +507,4 @@ function createRedrawable(creator) {
533
507
  return elRef;
534
508
  }
535
509
 
536
- export { $modelOrRef, Fragment, KTRef, h as createElement, createRedrawable, deref, isKTRef, jsx, jsxDEV, jsxs, ref, surfaceRef };
510
+ export { Fragment, h as createElement, createRedrawable, jsx, jsxDEV, jsxs };
@@ -7,13 +7,19 @@ type HTMLTag = keyof HTMLElementTagNameMap;
7
7
  type SVGTag = keyof SVGElementTagNameMap;
8
8
  type MathMLTag = keyof MathMLElementTagNameMap;
9
9
 
10
- type RefChangeHandler<T> = (newValue: T, oldValue: T) => void;
10
+ declare const enum KTReactiveType {
11
+ REF = 1,
12
+ COMPUTED = 2
13
+ }
14
+ type ReactiveChangeHandler<T> = (newValue: T, oldValue: T) => void;
15
+
11
16
  declare class KTRef<T> {
12
17
  /**
13
18
  * Indicates that this is a KTRef instance
14
19
  */
15
- isKT: boolean;
16
- constructor(_value: T, _onChanges: Array<RefChangeHandler<T>>);
20
+ isKT: true;
21
+ ktType: KTReactiveType;
22
+ constructor(_value: T, _onChanges: Array<ReactiveChangeHandler<T>>);
17
23
  /**
18
24
  * If new value and old value are both nodes, the old one will be replaced in the DOM
19
25
  */
@@ -23,8 +29,8 @@ declare class KTRef<T> {
23
29
  * Register a callback when the value changes
24
30
  * @param callback (newValue, oldValue) => xxx
25
31
  */
26
- addOnChange(callback: RefChangeHandler<T>): void;
27
- removeOnChange(callback: RefChangeHandler<T>): boolean;
32
+ addOnChange(callback: ReactiveChangeHandler<T>): void;
33
+ removeOnChange(callback: ReactiveChangeHandler<T>): boolean;
28
34
  }
29
35
 
30
36
  type HTML<T extends (HTMLTag | SVGTag | MathMLTag) & otherstring> = T extends SVGTag
@@ -119,11 +125,11 @@ interface KTBaseAttribute {
119
125
  method?: 'POST' | 'GET' | 'PUT' | 'DELETE' | 'PATCH' | 'HEAD' | 'OPTIONS' | 'CONNECT' | 'TRACE' | otherstring;
120
126
  }
121
127
 
122
- type KTPrefixedEventHandlers = {
128
+ type KTPrefixedEventAttribute = {
123
129
  [EventName in keyof HTMLElementEventMap as `on:${EventName}`]?: (ev: HTMLElementEventMap[EventName]) => void;
124
130
  };
125
131
 
126
- type KTAttribute = KTBaseAttribute & KTPrefixedEventHandlers;
132
+ type KTAttribute = KTBaseAttribute & KTPrefixedEventAttribute;
127
133
 
128
134
  /**
129
135
  * Create an enhanced HTMLElement.
@@ -135,7 +141,7 @@ type KTAttribute = KTBaseAttribute & KTPrefixedEventHandlers;
135
141
  * ## About
136
142
  * @package @ktjs/core
137
143
  * @author Kasukabe Tsumugi <futami16237@gmail.com>
138
- * @version 0.23.0 (Last Update: 2026.02.03 17:15:01.812)
144
+ * @version 0.24.0 (Last Update: 2026.02.05 12:08:54.164)
139
145
  * @license MIT
140
146
  * @link https://github.com/baendlorel/kt.js
141
147
  * @link https://baendlorel.github.io/ Welcome to my site!