@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,10 +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 $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';
4
17
 
5
18
  // DOM manipulation utilities
6
19
  // # dom natives
7
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
+ };
8
32
  /**
9
33
  * & Remove `bind` because it is shockingly slower than wrapper
10
34
  * & `window.document` is safe because it is not configurable and its setter is undefined
@@ -50,85 +74,21 @@ const applyModel = (element, valueRef, propName, eventName) => {
50
74
  element.addEventListener(eventName, () => (valueRef.value = element[propName]));
51
75
  };
52
76
 
77
+ if (typeof Symbol === 'undefined') {
78
+ window.Symbol = function Symbol(description) {
79
+ return `@@SYMBOL_${description || ''}_${$random().toString(36).slice(2)}`;
80
+ };
81
+ }
82
+
53
83
  // Shared utilities and cached native methods for kt.js framework
54
- // import './misc/symbol-polyfill.js';
55
- // Re-export all utilities
56
- Object.defineProperty(window, '__ktjs__', { value: '0.22.3' });
84
+ Object.defineProperty(window, '__ktjs__', { value: '0.22.6' });
57
85
 
58
- class KTRef {
59
- /**
60
- * Indicates that this is a KTRef instance
61
- */
62
- isKT = true;
63
- /**
64
- * @internal
65
- */
66
- _value;
67
- /**
68
- * @internal
69
- */
70
- _onChanges;
71
- constructor(_value, _onChanges) {
72
- this._value = _value;
73
- this._onChanges = _onChanges;
74
- }
75
- /**
76
- * If new value and old value are both nodes, the old one will be replaced in the DOM
77
- */
78
- get value() {
79
- return this._value;
80
- }
81
- set value(newValue) {
82
- if (newValue === this._value) {
83
- return;
84
- }
85
- // replace the old node with the new one in the DOM if both are nodes
86
- if (this._value instanceof Node && newValue instanceof Node) {
87
- if (newValue.contains(this._value)) {
88
- this._value.remove();
89
- }
90
- this._value.replaceWith(newValue);
91
- }
92
- const oldValue = this._value;
93
- this._value = newValue;
94
- for (let i = 0; i < this._onChanges.length; i++) {
95
- this._onChanges[i](newValue, oldValue);
96
- }
97
- }
98
- /**
99
- * Register a callback when the value changes
100
- * @param callback (newValue, oldValue) => xxx
101
- */
102
- addOnChange(callback) {
103
- if (typeof callback !== 'function') {
104
- throw new Error('[kt.js error] KTRef.addOnChange: callback must be a function');
105
- }
106
- this._onChanges.push(callback);
107
- }
108
- removeOnChange(callback) {
109
- for (let i = this._onChanges.length - 1; i >= 0; i--) {
110
- if (this._onChanges[i] === callback) {
111
- this._onChanges.splice(i, 1);
112
- return true;
113
- }
114
- }
115
- return false;
116
- }
117
- }
118
- const isKTRef = (obj) => obj?.isKT === true;
119
- /**
120
- * Reference to the created HTML element.
121
- * - **Only** respond to `ref.value` changes, not reactive to internal changes of the element.
122
- * - can alse be used to store normal values, but it is not reactive.
123
- * - if the value is already a `KTRef`, it will be returned **directly**.
124
- * @param value mostly an HTMLElement
125
- */
126
- function ref(value, onChange) {
127
- if (isKTRef(value)) {
128
- return value;
129
- }
130
- return new KTRef(value, []);
131
- }
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 */;
132
92
 
133
93
  const booleanHandler = (element, key, value) => {
134
94
  if (key in element) {
@@ -190,7 +150,7 @@ function attrIsObject(element, attr) {
190
150
  }
191
151
  if ('k-html' in attr) {
192
152
  const html = attr['k-html'];
193
- if (isKTRef(html)) {
153
+ if (isKT(html)) {
194
154
  element.innerHTML = html.value;
195
155
  html.addOnChange((v) => (element.innerHTML = v));
196
156
  }
@@ -199,14 +159,14 @@ function attrIsObject(element, attr) {
199
159
  }
200
160
  }
201
161
  for (const key in attr) {
202
- if (key === 'class' ||
162
+ if (key === 'k-if' ||
163
+ key === 'k-model' ||
164
+ key === 'ref' ||
165
+ key === 'class' ||
203
166
  key === 'className' ||
204
167
  key === 'style' ||
205
168
  key === 'children' ||
206
- key === 'k-if' ||
207
- key === 'k-model' ||
208
- key === 'k-html' ||
209
- key === 'ref') {
169
+ key === 'k-html') {
210
170
  continue;
211
171
  }
212
172
  const o = attr[key];
@@ -239,7 +199,7 @@ function apdSingle(element, c) {
239
199
  if (c === false || c === undefined || c === null) {
240
200
  return;
241
201
  }
242
- if (isKTRef(c)) {
202
+ if (isKT(c)) {
243
203
  let node = assureNode(c.value);
244
204
  $append.call(element, node);
245
205
  c.addOnChange((newValue, oldValue) => {
@@ -296,8 +256,74 @@ function applyContent(element, content) {
296
256
  }
297
257
  }
298
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
+
299
325
  function applyKModel(element, valueRef) {
300
- if (!isKTRef(valueRef)) {
326
+ if (!isKT(valueRef)) {
301
327
  console.warn('[kt.js warn] k-model value must be a KTRef.');
302
328
  return;
303
329
  }
@@ -324,9 +350,6 @@ const htmlCreator = (tag) => document.createElement(tag);
324
350
  const svgCreator = (tag) => document.createElementNS('http://www.w3.org/2000/svg', tag);
325
351
  const mathMLCreator = (tag) => document.createElementNS('http://www.w3.org/1998/Math/MathML', tag);
326
352
  let creator = htmlCreator;
327
- // # consts
328
- const SVG_ATTR_FLAG = '__kt_svg__';
329
- const MATHML_ATTR_FLAG = '__kt_mathml__';
330
353
  /**
331
354
  * Create an enhanced HTMLElement.
332
355
  * - Only supports HTMLElements, **NOT** SVGElements or other Elements.
@@ -337,7 +360,7 @@ const MATHML_ATTR_FLAG = '__kt_mathml__';
337
360
  * ## About
338
361
  * @package @ktjs/core
339
362
  * @author Kasukabe Tsumugi <futami16237@gmail.com>
340
- * @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)
341
364
  * @license MIT
342
365
  * @link https://github.com/baendlorel/kt.js
343
366
  * @link https://baendlorel.github.io/ Welcome to my site!
@@ -367,13 +390,7 @@ const h = (tag, attr, content) => {
367
390
  applyAttr(element, attr);
368
391
  applyContent(element, content);
369
392
  if (typeof attr === 'object' && attr !== null && 'k-model' in attr) {
370
- const kmodel = attr['k-model'];
371
- if (isKTRef(kmodel)) {
372
- applyKModel(element, kmodel);
373
- }
374
- else {
375
- throw new Error('[kt.js error] k-model value must be a KTRef.');
376
- }
393
+ applyKModel(element, attr['k-model']);
377
394
  }
378
395
  return element;
379
396
  };
@@ -393,20 +410,23 @@ const placeholder = () => document.createComment('k-if');
393
410
  * @param props properties/attributes
394
411
  */
395
412
  function jsx(tag, props) {
396
- 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;
397
417
  let el;
398
418
  if ('k-if' in props) {
399
419
  const kif = props['k-if'];
400
420
  let condition = kif; // assume boolean by default
401
421
  // Handle reactive k-if
402
- if (isKTRef(kif)) {
422
+ if (isKT(kif)) {
403
423
  kif.addOnChange((newValue, oldValue) => {
404
424
  if (newValue === oldValue) {
405
425
  return;
406
426
  }
407
427
  const oldEl = el;
408
428
  el = newValue ? create(tag, props) : placeholder();
409
- oldEl.replaceWith(el);
429
+ $replaceNode(oldEl, el);
410
430
  maybeDummyRef.value = el;
411
431
  });
412
432
  condition = kif.value;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ktjs/core",
3
- "version": "0.23.0",
3
+ "version": "0.24.0",
4
4
  "description": "Core functionality for kt.js - DOM manipulation utilities with JSX/TSX support",
5
5
  "type": "module",
6
6
  "module": "./dist/index.mjs",
@@ -44,7 +44,7 @@
44
44
  "directory": "packages/core"
45
45
  },
46
46
  "dependencies": {
47
- "@ktjs/shared": "0.22.3"
47
+ "@ktjs/shared": "0.22.6"
48
48
  },
49
49
  "scripts": {
50
50
  "build": "rollup -c rollup.config.mjs",