@ktjs/core 0.21.1 → 0.22.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.d.ts CHANGED
@@ -72,12 +72,15 @@ interface KTBaseAttribute {
72
72
  // # kt-specific attributes
73
73
  ref?: KTRef<JSX.Element>;
74
74
 
75
- // todo 是否要让k-if是KTRef的时候具备响应能力?
76
75
  /**
77
76
  * If a `KTRef` is bound, it will be reactive; otherwise, it will be static.
78
77
  */
79
78
  'k-if'?: any;
80
- // todo k-model如何指定value还是checked还是别的什么?
79
+
80
+ /**
81
+ * Register two-way data binding between an input element and a KTRef.
82
+ * - Default to regist `input` event and `value` property(`checked` for checkboxes and radios).
83
+ */
81
84
  'k-model'?: KTRef<any>;
82
85
 
83
86
  // # normal HTML attributes
@@ -157,7 +160,7 @@ type KTComponent = (
157
160
  * ## About
158
161
  * @package @ktjs/core
159
162
  * @author Kasukabe Tsumugi <futami16237@gmail.com>
160
- * @version 0.21.1 (Last Update: 2026.02.02 09:20:07.959)
163
+ * @version 0.22.0 (Last Update: 2026.02.02 17:11:04.703)
161
164
  * @license MIT
162
165
  * @link https://github.com/baendlorel/kt.js
163
166
  * @link https://baendlorel.github.io/ Welcome to my site!
@@ -10,10 +10,6 @@ var __ktjs_core__ = (function (exports) {
10
10
  const $throw = (message) => {
11
11
  throw new Error('@ktjs/shared: ' + message);
12
12
  };
13
-
14
- // DOM manipulation utilities
15
- // # dom natives
16
- const $replaceWith = Element.prototype.replaceWith;
17
13
  /**
18
14
  * & Remove `bind` because it is shockingly slower than wrapper
19
15
  * & `window.document` is safe because it is not configurable and its setter is undefined
@@ -55,6 +51,96 @@ var __ktjs_core__ = (function (exports) {
55
51
  // Re-export all utilities
56
52
  Object.defineProperty(window, '@ktjs/shared', { value: '0.20.2' });
57
53
 
54
+ class KTRef {
55
+ /**
56
+ * Indicates that this is a KTRef instance
57
+ */
58
+ isKT = true;
59
+ /**
60
+ * @internal
61
+ */
62
+ _value;
63
+ /**
64
+ * @internal
65
+ */
66
+ _onChanges;
67
+ constructor(_value, _onChanges) {
68
+ this._value = _value;
69
+ this._onChanges = _onChanges;
70
+ }
71
+ /**
72
+ * If new value and old value are both nodes, the old one will be replaced in the DOM
73
+ */
74
+ get value() {
75
+ return this._value;
76
+ }
77
+ set value(newValue) {
78
+ if (newValue === this._value) {
79
+ return;
80
+ }
81
+ // replace the old node with the new one in the DOM if both are nodes
82
+ if (this._value instanceof Node && newValue instanceof Node) {
83
+ if (newValue.contains(this._value)) {
84
+ this._value.remove();
85
+ }
86
+ this._value.replaceWith(newValue);
87
+ }
88
+ const oldValue = this._value;
89
+ this._value = newValue;
90
+ for (let i = 0; i < this._onChanges.length; i++) {
91
+ this._onChanges[i](newValue, oldValue);
92
+ }
93
+ }
94
+ addOnChange(callback) {
95
+ this._onChanges.push(callback);
96
+ }
97
+ removeOnChange(callback) {
98
+ for (let i = this._onChanges.length - 1; i >= 0; i--) {
99
+ if (this._onChanges[i] === callback) {
100
+ this._onChanges.splice(i, 1);
101
+ return true;
102
+ }
103
+ }
104
+ return false;
105
+ }
106
+ }
107
+ const isKTRef = (obj) => {
108
+ return typeof obj === 'object' && obj !== null && obj.isKT === true;
109
+ };
110
+ /**
111
+ * Reference to the created HTML element.
112
+ * - **Only** respond to `ref.value` changes, not reactive to internal changes of the element.
113
+ * - can alse be used to store normal values, but it is not reactive.
114
+ * @param value mostly an HTMLElement
115
+ */
116
+ function ref(value, onChange) {
117
+ return new KTRef(value, onChange ? [onChange] : []);
118
+ }
119
+ function kcollect() {
120
+ const newObj = {};
121
+ const entries = $entries(this);
122
+ for (let i = 0; i < entries.length; i++) {
123
+ const key = entries[i][0];
124
+ if (key === 'kcollect') {
125
+ continue;
126
+ }
127
+ newObj[key] = entries[i][1].value;
128
+ }
129
+ return newObj;
130
+ }
131
+ /**
132
+ * Make all first-level properties of the object a `KTRef`.
133
+ * - `obj.a.b` is not reactive
134
+ */
135
+ const surfaceRef = (obj) => {
136
+ const entries = $entries(obj);
137
+ const newObj = { kcollect };
138
+ for (let i = 0; i < entries.length; i++) {
139
+ newObj[entries[i][0]] = ref(entries[i][1]);
140
+ }
141
+ return newObj;
142
+ };
143
+
58
144
  const booleanHandler = (element, key, value) => {
59
145
  if (key in element) {
60
146
  element[key] = !!value;
@@ -119,6 +205,7 @@ var __ktjs_core__ = (function (exports) {
119
205
  key === 'style' ||
120
206
  key === 'children' ||
121
207
  key === 'k-if' ||
208
+ key.startsWith('k-model') ||
122
209
  key === 'ref') {
123
210
  continue;
124
211
  }
@@ -196,6 +283,29 @@ var __ktjs_core__ = (function (exports) {
196
283
  }
197
284
  }
198
285
 
286
+ function register(element, valueRef, propName, eventName) {
287
+ element[propName] = valueRef.value; // initialize
288
+ valueRef.addOnChange((newValue) => (element[propName] = newValue));
289
+ element.addEventListener(eventName, () => (valueRef.value = element[propName]));
290
+ }
291
+ function applyModel(element, valueRef) {
292
+ if (element instanceof HTMLInputElement) {
293
+ if (element.type === 'radio' || element.type === 'checkbox') {
294
+ register(element, valueRef, 'checked', 'change');
295
+ }
296
+ else {
297
+ register(element, valueRef, 'value', 'input');
298
+ }
299
+ }
300
+ else if (element instanceof HTMLSelectElement) {
301
+ register(element, valueRef, 'value', 'change');
302
+ }
303
+ else if (element instanceof HTMLTextAreaElement) {
304
+ register(element, valueRef, 'value', 'input');
305
+ }
306
+ console.warn('[kt.js warn] not supported element for k-model:', element.tagName);
307
+ }
308
+
199
309
  const htmlCreator = (tag) => document.createElement(tag);
200
310
  const svgCreator = (tag) => document.createElementNS('http://www.w3.org/2000/svg', tag);
201
311
  const mathMLCreator = (tag) => document.createElementNS('http://www.w3.org/1998/Math/MathML', tag);
@@ -213,7 +323,7 @@ var __ktjs_core__ = (function (exports) {
213
323
  * ## About
214
324
  * @package @ktjs/core
215
325
  * @author Kasukabe Tsumugi <futami16237@gmail.com>
216
- * @version 0.21.1 (Last Update: 2026.02.02 09:20:07.959)
326
+ * @version 0.22.0 (Last Update: 2026.02.02 17:11:04.703)
217
327
  * @license MIT
218
328
  * @link https://github.com/baendlorel/kt.js
219
329
  * @link https://baendlorel.github.io/ Welcome to my site!
@@ -242,97 +352,16 @@ var __ktjs_core__ = (function (exports) {
242
352
  // * Handle content
243
353
  applyAttr(element, attr);
244
354
  applyContent(element, content);
245
- return element;
246
- };
247
-
248
- class KTRef {
249
- /**
250
- * Indicates that this is a KTRef instance
251
- */
252
- isKT = true;
253
- /**
254
- * @internal
255
- */
256
- _value;
257
- /**
258
- * @internal
259
- */
260
- _onChanges;
261
- constructor(_value, _onChanges) {
262
- this._value = _value;
263
- this._onChanges = _onChanges;
264
- }
265
- /**
266
- * If new value and old value are both nodes, the old one will be replaced in the DOM
267
- */
268
- get value() {
269
- return this._value;
270
- }
271
- set value(newValue) {
272
- if (newValue === this._value) {
273
- return;
274
- }
275
- // replace the old node with the new one in the DOM if both are nodes
276
- if (this._value instanceof Node && newValue instanceof Node) {
277
- if (newValue.contains(this._value)) {
278
- this._value.remove();
279
- }
280
- this._value.replaceWith(newValue);
281
- }
282
- const oldValue = this._value;
283
- this._value = newValue;
284
- for (let i = 0; i < this._onChanges.length; i++) {
285
- this._onChanges[i](newValue, oldValue);
355
+ if (typeof attr === 'object' && attr !== null && 'k-model' in attr) {
356
+ const kmodel = attr['k-model'];
357
+ if (isKTRef(kmodel)) {
358
+ applyModel(element, kmodel);
286
359
  }
287
- }
288
- addOnChange(callback) {
289
- this._onChanges.push(callback);
290
- }
291
- removeOnChange(callback) {
292
- for (let i = this._onChanges.length - 1; i >= 0; i--) {
293
- if (this._onChanges[i] === callback) {
294
- this._onChanges.splice(i, 1);
295
- return true;
296
- }
297
- }
298
- return false;
299
- }
300
- }
301
- const isKTRef = (obj) => {
302
- return typeof obj === 'object' && obj !== null && obj.isKT === true;
303
- };
304
- /**
305
- * Reference to the created HTML element.
306
- * - **Only** respond to `ref.value` changes, not reactive to internal changes of the element.
307
- * - can alse be used to store normal values, but it is not reactive.
308
- * @param value mostly an HTMLElement
309
- */
310
- function ref(value, onChange) {
311
- return new KTRef(value, onChange ? [onChange] : []);
312
- }
313
- function kcollect() {
314
- const newObj = {};
315
- const entries = $entries(this);
316
- for (let i = 0; i < entries.length; i++) {
317
- const key = entries[i][0];
318
- if (key === 'kcollect') {
319
- continue;
360
+ else {
361
+ $throw('k-model value must be a KTRef.');
320
362
  }
321
- newObj[key] = entries[i][1].value;
322
363
  }
323
- return newObj;
324
- }
325
- /**
326
- * Make all first-level properties of the object a `KTRef`.
327
- * - `obj.a.b` is not reactive
328
- */
329
- const surfaceRef = (obj) => {
330
- const entries = $entries(obj);
331
- const newObj = { kcollect };
332
- for (let i = 0; i < entries.length; i++) {
333
- newObj[entries[i][0]] = ref(entries[i][1]);
334
- }
335
- return newObj;
364
+ return element;
336
365
  };
337
366
 
338
367
  const dummyRef = { value: null };
@@ -363,7 +392,7 @@ var __ktjs_core__ = (function (exports) {
363
392
  }
364
393
  const oldEl = el;
365
394
  el = newValue ? create(tag, props) : placeholder();
366
- $replaceWith.call(oldEl, el);
395
+ oldEl.replaceWith(el);
367
396
  maybeDummyRef.value = el;
368
397
  });
369
398
  condition = kif.value;
@@ -10,10 +10,6 @@ var __ktjs_core__ = (function (exports) {
10
10
  const $throw = (message) => {
11
11
  throw new Error('@ktjs/shared: ' + message);
12
12
  };
13
-
14
- // DOM manipulation utilities
15
- // # dom natives
16
- const $replaceWith = Element.prototype.replaceWith;
17
13
  /**
18
14
  * & Remove `bind` because it is shockingly slower than wrapper
19
15
  * & `window.document` is safe because it is not configurable and its setter is undefined
@@ -55,6 +51,93 @@ var __ktjs_core__ = (function (exports) {
55
51
  // Re-export all utilities
56
52
  Object.defineProperty(window, '@ktjs/shared', { value: '0.20.2' });
57
53
 
54
+ var KTRef = /** @class */ (function () {
55
+ function KTRef(_value, _onChanges) {
56
+ /**
57
+ * Indicates that this is a KTRef instance
58
+ */
59
+ this.isKT = true;
60
+ this._value = _value;
61
+ this._onChanges = _onChanges;
62
+ }
63
+ Object.defineProperty(KTRef.prototype, "value", {
64
+ /**
65
+ * If new value and old value are both nodes, the old one will be replaced in the DOM
66
+ */
67
+ get: function () {
68
+ return this._value;
69
+ },
70
+ set: function (newValue) {
71
+ if (newValue === this._value) {
72
+ return;
73
+ }
74
+ // replace the old node with the new one in the DOM if both are nodes
75
+ if (this._value instanceof Node && newValue instanceof Node) {
76
+ if (newValue.contains(this._value)) {
77
+ this._value.remove();
78
+ }
79
+ this._value.replaceWith(newValue);
80
+ }
81
+ var oldValue = this._value;
82
+ this._value = newValue;
83
+ for (var i = 0; i < this._onChanges.length; i++) {
84
+ this._onChanges[i](newValue, oldValue);
85
+ }
86
+ },
87
+ enumerable: false,
88
+ configurable: true
89
+ });
90
+ KTRef.prototype.addOnChange = function (callback) {
91
+ this._onChanges.push(callback);
92
+ };
93
+ KTRef.prototype.removeOnChange = function (callback) {
94
+ for (var i = this._onChanges.length - 1; i >= 0; i--) {
95
+ if (this._onChanges[i] === callback) {
96
+ this._onChanges.splice(i, 1);
97
+ return true;
98
+ }
99
+ }
100
+ return false;
101
+ };
102
+ return KTRef;
103
+ }());
104
+ var isKTRef = function (obj) {
105
+ return typeof obj === 'object' && obj !== null && obj.isKT === true;
106
+ };
107
+ /**
108
+ * Reference to the created HTML element.
109
+ * - **Only** respond to `ref.value` changes, not reactive to internal changes of the element.
110
+ * - can alse be used to store normal values, but it is not reactive.
111
+ * @param value mostly an HTMLElement
112
+ */
113
+ function ref(value, onChange) {
114
+ return new KTRef(value, onChange ? [onChange] : []);
115
+ }
116
+ function kcollect() {
117
+ var newObj = {};
118
+ var entries = $entries(this);
119
+ for (var i = 0; i < entries.length; i++) {
120
+ var key = entries[i][0];
121
+ if (key === 'kcollect') {
122
+ continue;
123
+ }
124
+ newObj[key] = entries[i][1].value;
125
+ }
126
+ return newObj;
127
+ }
128
+ /**
129
+ * Make all first-level properties of the object a `KTRef`.
130
+ * - `obj.a.b` is not reactive
131
+ */
132
+ var surfaceRef = function (obj) {
133
+ var entries = $entries(obj);
134
+ var newObj = { kcollect: kcollect };
135
+ for (var i = 0; i < entries.length; i++) {
136
+ newObj[entries[i][0]] = ref(entries[i][1]);
137
+ }
138
+ return newObj;
139
+ };
140
+
58
141
  var booleanHandler = function (element, key, value) {
59
142
  if (key in element) {
60
143
  element[key] = !!value;
@@ -121,6 +204,7 @@ var __ktjs_core__ = (function (exports) {
121
204
  key === 'style' ||
122
205
  key === 'children' ||
123
206
  key === 'k-if' ||
207
+ key.startsWith('k-model') ||
124
208
  key === 'ref') {
125
209
  continue;
126
210
  }
@@ -201,6 +285,29 @@ var __ktjs_core__ = (function (exports) {
201
285
  }
202
286
  }
203
287
 
288
+ function register(element, valueRef, propName, eventName) {
289
+ element[propName] = valueRef.value; // initialize
290
+ valueRef.addOnChange(function (newValue) { return (element[propName] = newValue); });
291
+ element.addEventListener(eventName, function () { return (valueRef.value = element[propName]); });
292
+ }
293
+ function applyModel(element, valueRef) {
294
+ if (element instanceof HTMLInputElement) {
295
+ if (element.type === 'radio' || element.type === 'checkbox') {
296
+ register(element, valueRef, 'checked', 'change');
297
+ }
298
+ else {
299
+ register(element, valueRef, 'value', 'input');
300
+ }
301
+ }
302
+ else if (element instanceof HTMLSelectElement) {
303
+ register(element, valueRef, 'value', 'change');
304
+ }
305
+ else if (element instanceof HTMLTextAreaElement) {
306
+ register(element, valueRef, 'value', 'input');
307
+ }
308
+ console.warn('[kt.js warn] not supported element for k-model:', element.tagName);
309
+ }
310
+
204
311
  var htmlCreator = function (tag) { return document.createElement(tag); };
205
312
  var svgCreator = function (tag) { return document.createElementNS('http://www.w3.org/2000/svg', tag); };
206
313
  var mathMLCreator = function (tag) { return document.createElementNS('http://www.w3.org/1998/Math/MathML', tag); };
@@ -218,7 +325,7 @@ var __ktjs_core__ = (function (exports) {
218
325
  * ## About
219
326
  * @package @ktjs/core
220
327
  * @author Kasukabe Tsumugi <futami16237@gmail.com>
221
- * @version 0.21.1 (Last Update: 2026.02.02 09:20:07.959)
328
+ * @version 0.22.0 (Last Update: 2026.02.02 17:11:04.703)
222
329
  * @license MIT
223
330
  * @link https://github.com/baendlorel/kt.js
224
331
  * @link https://baendlorel.github.io/ Welcome to my site!
@@ -247,94 +354,16 @@ var __ktjs_core__ = (function (exports) {
247
354
  // * Handle content
248
355
  applyAttr(element, attr);
249
356
  applyContent(element, content);
250
- return element;
251
- };
252
-
253
- var KTRef = /** @class */ (function () {
254
- function KTRef(_value, _onChanges) {
255
- /**
256
- * Indicates that this is a KTRef instance
257
- */
258
- this.isKT = true;
259
- this._value = _value;
260
- this._onChanges = _onChanges;
261
- }
262
- Object.defineProperty(KTRef.prototype, "value", {
263
- /**
264
- * If new value and old value are both nodes, the old one will be replaced in the DOM
265
- */
266
- get: function () {
267
- return this._value;
268
- },
269
- set: function (newValue) {
270
- if (newValue === this._value) {
271
- return;
272
- }
273
- // replace the old node with the new one in the DOM if both are nodes
274
- if (this._value instanceof Node && newValue instanceof Node) {
275
- if (newValue.contains(this._value)) {
276
- this._value.remove();
277
- }
278
- this._value.replaceWith(newValue);
279
- }
280
- var oldValue = this._value;
281
- this._value = newValue;
282
- for (var i = 0; i < this._onChanges.length; i++) {
283
- this._onChanges[i](newValue, oldValue);
284
- }
285
- },
286
- enumerable: false,
287
- configurable: true
288
- });
289
- KTRef.prototype.addOnChange = function (callback) {
290
- this._onChanges.push(callback);
291
- };
292
- KTRef.prototype.removeOnChange = function (callback) {
293
- for (var i = this._onChanges.length - 1; i >= 0; i--) {
294
- if (this._onChanges[i] === callback) {
295
- this._onChanges.splice(i, 1);
296
- return true;
297
- }
357
+ if (typeof attr === 'object' && attr !== null && 'k-model' in attr) {
358
+ var kmodel = attr['k-model'];
359
+ if (isKTRef(kmodel)) {
360
+ applyModel(element, kmodel);
298
361
  }
299
- return false;
300
- };
301
- return KTRef;
302
- }());
303
- var isKTRef = function (obj) {
304
- return typeof obj === 'object' && obj !== null && obj.isKT === true;
305
- };
306
- /**
307
- * Reference to the created HTML element.
308
- * - **Only** respond to `ref.value` changes, not reactive to internal changes of the element.
309
- * - can alse be used to store normal values, but it is not reactive.
310
- * @param value mostly an HTMLElement
311
- */
312
- function ref(value, onChange) {
313
- return new KTRef(value, onChange ? [onChange] : []);
314
- }
315
- function kcollect() {
316
- var newObj = {};
317
- var entries = $entries(this);
318
- for (var i = 0; i < entries.length; i++) {
319
- var key = entries[i][0];
320
- if (key === 'kcollect') {
321
- continue;
362
+ else {
363
+ $throw('k-model value must be a KTRef.');
322
364
  }
323
- newObj[key] = entries[i][1].value;
324
365
  }
325
- return newObj;
326
- }
327
- /**
328
- * Make all first-level properties of the object a `KTRef`.
329
- * - `obj.a.b` is not reactive
330
- */
331
- var surfaceRef = function (obj) {
332
- var entries = $entries(obj);
333
- var newObj = { kcollect: kcollect };
334
- for (var i = 0; i < entries.length; i++) {
335
- newObj[entries[i][0]] = ref(entries[i][1]);
336
- }
337
- return newObj;
366
+ return element;
338
367
  };
339
368
 
340
369
  var dummyRef = { value: null };
@@ -365,7 +394,7 @@ var __ktjs_core__ = (function (exports) {
365
394
  }
366
395
  var oldEl = el;
367
396
  el = newValue ? create(tag, props) : placeholder();
368
- $replaceWith.call(oldEl, el);
397
+ oldEl.replaceWith(el);
369
398
  maybeDummyRef.value = el;
370
399
  });
371
400
  condition = kif.value;
package/dist/index.mjs CHANGED
@@ -7,10 +7,6 @@ const $isThenable = (o) => typeof o === 'object' && o !== null && typeof o.then
7
7
  const $throw = (message) => {
8
8
  throw new Error('@ktjs/shared: ' + message);
9
9
  };
10
-
11
- // DOM manipulation utilities
12
- // # dom natives
13
- const $replaceWith = Element.prototype.replaceWith;
14
10
  /**
15
11
  * & Remove `bind` because it is shockingly slower than wrapper
16
12
  * & `window.document` is safe because it is not configurable and its setter is undefined
@@ -52,6 +48,96 @@ const { get: $buttonDisabledGetter, set: $buttonDisabledSetter } = Object.getOwn
52
48
  // Re-export all utilities
53
49
  Object.defineProperty(window, '@ktjs/shared', { value: '0.20.2' });
54
50
 
51
+ class KTRef {
52
+ /**
53
+ * Indicates that this is a KTRef instance
54
+ */
55
+ isKT = true;
56
+ /**
57
+ * @internal
58
+ */
59
+ _value;
60
+ /**
61
+ * @internal
62
+ */
63
+ _onChanges;
64
+ constructor(_value, _onChanges) {
65
+ this._value = _value;
66
+ this._onChanges = _onChanges;
67
+ }
68
+ /**
69
+ * If new value and old value are both nodes, the old one will be replaced in the DOM
70
+ */
71
+ get value() {
72
+ return this._value;
73
+ }
74
+ set value(newValue) {
75
+ if (newValue === this._value) {
76
+ return;
77
+ }
78
+ // replace the old node with the new one in the DOM if both are nodes
79
+ if (this._value instanceof Node && newValue instanceof Node) {
80
+ if (newValue.contains(this._value)) {
81
+ this._value.remove();
82
+ }
83
+ this._value.replaceWith(newValue);
84
+ }
85
+ const oldValue = this._value;
86
+ this._value = newValue;
87
+ for (let i = 0; i < this._onChanges.length; i++) {
88
+ this._onChanges[i](newValue, oldValue);
89
+ }
90
+ }
91
+ addOnChange(callback) {
92
+ this._onChanges.push(callback);
93
+ }
94
+ removeOnChange(callback) {
95
+ for (let i = this._onChanges.length - 1; i >= 0; i--) {
96
+ if (this._onChanges[i] === callback) {
97
+ this._onChanges.splice(i, 1);
98
+ return true;
99
+ }
100
+ }
101
+ return false;
102
+ }
103
+ }
104
+ const isKTRef = (obj) => {
105
+ return typeof obj === 'object' && obj !== null && obj.isKT === true;
106
+ };
107
+ /**
108
+ * Reference to the created HTML element.
109
+ * - **Only** respond to `ref.value` changes, not reactive to internal changes of the element.
110
+ * - can alse be used to store normal values, but it is not reactive.
111
+ * @param value mostly an HTMLElement
112
+ */
113
+ function ref(value, onChange) {
114
+ return new KTRef(value, onChange ? [onChange] : []);
115
+ }
116
+ function kcollect() {
117
+ const newObj = {};
118
+ const entries = $entries(this);
119
+ for (let i = 0; i < entries.length; i++) {
120
+ const key = entries[i][0];
121
+ if (key === 'kcollect') {
122
+ continue;
123
+ }
124
+ newObj[key] = entries[i][1].value;
125
+ }
126
+ return newObj;
127
+ }
128
+ /**
129
+ * Make all first-level properties of the object a `KTRef`.
130
+ * - `obj.a.b` is not reactive
131
+ */
132
+ const surfaceRef = (obj) => {
133
+ const entries = $entries(obj);
134
+ const newObj = { kcollect };
135
+ for (let i = 0; i < entries.length; i++) {
136
+ newObj[entries[i][0]] = ref(entries[i][1]);
137
+ }
138
+ return newObj;
139
+ };
140
+
55
141
  const booleanHandler = (element, key, value) => {
56
142
  if (key in element) {
57
143
  element[key] = !!value;
@@ -116,6 +202,7 @@ function attrIsObject(element, attr) {
116
202
  key === 'style' ||
117
203
  key === 'children' ||
118
204
  key === 'k-if' ||
205
+ key.startsWith('k-model') ||
119
206
  key === 'ref') {
120
207
  continue;
121
208
  }
@@ -193,6 +280,29 @@ function applyContent(element, content) {
193
280
  }
194
281
  }
195
282
 
283
+ function register(element, valueRef, propName, eventName) {
284
+ element[propName] = valueRef.value; // initialize
285
+ valueRef.addOnChange((newValue) => (element[propName] = newValue));
286
+ element.addEventListener(eventName, () => (valueRef.value = element[propName]));
287
+ }
288
+ function applyModel(element, valueRef) {
289
+ if (element instanceof HTMLInputElement) {
290
+ if (element.type === 'radio' || element.type === 'checkbox') {
291
+ register(element, valueRef, 'checked', 'change');
292
+ }
293
+ else {
294
+ register(element, valueRef, 'value', 'input');
295
+ }
296
+ }
297
+ else if (element instanceof HTMLSelectElement) {
298
+ register(element, valueRef, 'value', 'change');
299
+ }
300
+ else if (element instanceof HTMLTextAreaElement) {
301
+ register(element, valueRef, 'value', 'input');
302
+ }
303
+ console.warn('[kt.js warn] not supported element for k-model:', element.tagName);
304
+ }
305
+
196
306
  const htmlCreator = (tag) => document.createElement(tag);
197
307
  const svgCreator = (tag) => document.createElementNS('http://www.w3.org/2000/svg', tag);
198
308
  const mathMLCreator = (tag) => document.createElementNS('http://www.w3.org/1998/Math/MathML', tag);
@@ -210,7 +320,7 @@ const MATHML_ATTR_FLAG = '__kt_mathml__';
210
320
  * ## About
211
321
  * @package @ktjs/core
212
322
  * @author Kasukabe Tsumugi <futami16237@gmail.com>
213
- * @version 0.21.1 (Last Update: 2026.02.02 09:20:07.959)
323
+ * @version 0.22.0 (Last Update: 2026.02.02 17:11:04.703)
214
324
  * @license MIT
215
325
  * @link https://github.com/baendlorel/kt.js
216
326
  * @link https://baendlorel.github.io/ Welcome to my site!
@@ -239,97 +349,16 @@ const h = (tag, attr, content) => {
239
349
  // * Handle content
240
350
  applyAttr(element, attr);
241
351
  applyContent(element, content);
242
- return element;
243
- };
244
-
245
- class KTRef {
246
- /**
247
- * Indicates that this is a KTRef instance
248
- */
249
- isKT = true;
250
- /**
251
- * @internal
252
- */
253
- _value;
254
- /**
255
- * @internal
256
- */
257
- _onChanges;
258
- constructor(_value, _onChanges) {
259
- this._value = _value;
260
- this._onChanges = _onChanges;
261
- }
262
- /**
263
- * If new value and old value are both nodes, the old one will be replaced in the DOM
264
- */
265
- get value() {
266
- return this._value;
267
- }
268
- set value(newValue) {
269
- if (newValue === this._value) {
270
- return;
271
- }
272
- // replace the old node with the new one in the DOM if both are nodes
273
- if (this._value instanceof Node && newValue instanceof Node) {
274
- if (newValue.contains(this._value)) {
275
- this._value.remove();
276
- }
277
- this._value.replaceWith(newValue);
278
- }
279
- const oldValue = this._value;
280
- this._value = newValue;
281
- for (let i = 0; i < this._onChanges.length; i++) {
282
- this._onChanges[i](newValue, oldValue);
352
+ if (typeof attr === 'object' && attr !== null && 'k-model' in attr) {
353
+ const kmodel = attr['k-model'];
354
+ if (isKTRef(kmodel)) {
355
+ applyModel(element, kmodel);
283
356
  }
284
- }
285
- addOnChange(callback) {
286
- this._onChanges.push(callback);
287
- }
288
- removeOnChange(callback) {
289
- for (let i = this._onChanges.length - 1; i >= 0; i--) {
290
- if (this._onChanges[i] === callback) {
291
- this._onChanges.splice(i, 1);
292
- return true;
293
- }
294
- }
295
- return false;
296
- }
297
- }
298
- const isKTRef = (obj) => {
299
- return typeof obj === 'object' && obj !== null && obj.isKT === true;
300
- };
301
- /**
302
- * Reference to the created HTML element.
303
- * - **Only** respond to `ref.value` changes, not reactive to internal changes of the element.
304
- * - can alse be used to store normal values, but it is not reactive.
305
- * @param value mostly an HTMLElement
306
- */
307
- function ref(value, onChange) {
308
- return new KTRef(value, onChange ? [onChange] : []);
309
- }
310
- function kcollect() {
311
- const newObj = {};
312
- const entries = $entries(this);
313
- for (let i = 0; i < entries.length; i++) {
314
- const key = entries[i][0];
315
- if (key === 'kcollect') {
316
- continue;
357
+ else {
358
+ $throw('k-model value must be a KTRef.');
317
359
  }
318
- newObj[key] = entries[i][1].value;
319
360
  }
320
- return newObj;
321
- }
322
- /**
323
- * Make all first-level properties of the object a `KTRef`.
324
- * - `obj.a.b` is not reactive
325
- */
326
- const surfaceRef = (obj) => {
327
- const entries = $entries(obj);
328
- const newObj = { kcollect };
329
- for (let i = 0; i < entries.length; i++) {
330
- newObj[entries[i][0]] = ref(entries[i][1]);
331
- }
332
- return newObj;
361
+ return element;
333
362
  };
334
363
 
335
364
  const dummyRef = { value: null };
@@ -360,7 +389,7 @@ function jsx(tag, props) {
360
389
  }
361
390
  const oldEl = el;
362
391
  el = newValue ? create(tag, props) : placeholder();
363
- $replaceWith.call(oldEl, el);
392
+ oldEl.replaceWith(el);
364
393
  maybeDummyRef.value = el;
365
394
  });
366
395
  condition = kif.value;
@@ -66,12 +66,15 @@ interface KTBaseAttribute {
66
66
  // # kt-specific attributes
67
67
  ref?: KTRef<JSX.Element>;
68
68
 
69
- // todo 是否要让k-if是KTRef的时候具备响应能力?
70
69
  /**
71
70
  * If a `KTRef` is bound, it will be reactive; otherwise, it will be static.
72
71
  */
73
72
  'k-if'?: any;
74
- // todo k-model如何指定value还是checked还是别的什么?
73
+
74
+ /**
75
+ * Register two-way data binding between an input element and a KTRef.
76
+ * - Default to regist `input` event and `value` property(`checked` for checkboxes and radios).
77
+ */
75
78
  'k-model'?: KTRef<any>;
76
79
 
77
80
  // # normal HTML attributes
@@ -143,7 +146,7 @@ type KTAttribute = KTBaseAttribute & KTPrefixedEventHandlers;
143
146
  * ## About
144
147
  * @package @ktjs/core
145
148
  * @author Kasukabe Tsumugi <futami16237@gmail.com>
146
- * @version 0.21.1 (Last Update: 2026.02.02 09:20:07.959)
149
+ * @version 0.22.0 (Last Update: 2026.02.02 17:11:04.703)
147
150
  * @license MIT
148
151
  * @link https://github.com/baendlorel/kt.js
149
152
  * @link https://baendlorel.github.io/ Welcome to my site!
@@ -7,10 +7,6 @@ const $isThenable = (o) => typeof o === 'object' && o !== null && typeof o.then
7
7
  const $throw = (message) => {
8
8
  throw new Error('@ktjs/shared: ' + message);
9
9
  };
10
-
11
- // DOM manipulation utilities
12
- // # dom natives
13
- const $replaceWith = Element.prototype.replaceWith;
14
10
  /**
15
11
  * & Remove `bind` because it is shockingly slower than wrapper
16
12
  * & `window.document` is safe because it is not configurable and its setter is undefined
@@ -52,6 +48,96 @@ const { get: $buttonDisabledGetter, set: $buttonDisabledSetter } = Object.getOwn
52
48
  // Re-export all utilities
53
49
  Object.defineProperty(window, '@ktjs/shared', { value: '0.20.2' });
54
50
 
51
+ class KTRef {
52
+ /**
53
+ * Indicates that this is a KTRef instance
54
+ */
55
+ isKT = true;
56
+ /**
57
+ * @internal
58
+ */
59
+ _value;
60
+ /**
61
+ * @internal
62
+ */
63
+ _onChanges;
64
+ constructor(_value, _onChanges) {
65
+ this._value = _value;
66
+ this._onChanges = _onChanges;
67
+ }
68
+ /**
69
+ * If new value and old value are both nodes, the old one will be replaced in the DOM
70
+ */
71
+ get value() {
72
+ return this._value;
73
+ }
74
+ set value(newValue) {
75
+ if (newValue === this._value) {
76
+ return;
77
+ }
78
+ // replace the old node with the new one in the DOM if both are nodes
79
+ if (this._value instanceof Node && newValue instanceof Node) {
80
+ if (newValue.contains(this._value)) {
81
+ this._value.remove();
82
+ }
83
+ this._value.replaceWith(newValue);
84
+ }
85
+ const oldValue = this._value;
86
+ this._value = newValue;
87
+ for (let i = 0; i < this._onChanges.length; i++) {
88
+ this._onChanges[i](newValue, oldValue);
89
+ }
90
+ }
91
+ addOnChange(callback) {
92
+ this._onChanges.push(callback);
93
+ }
94
+ removeOnChange(callback) {
95
+ for (let i = this._onChanges.length - 1; i >= 0; i--) {
96
+ if (this._onChanges[i] === callback) {
97
+ this._onChanges.splice(i, 1);
98
+ return true;
99
+ }
100
+ }
101
+ return false;
102
+ }
103
+ }
104
+ const isKTRef = (obj) => {
105
+ return typeof obj === 'object' && obj !== null && obj.isKT === true;
106
+ };
107
+ /**
108
+ * Reference to the created HTML element.
109
+ * - **Only** respond to `ref.value` changes, not reactive to internal changes of the element.
110
+ * - can alse be used to store normal values, but it is not reactive.
111
+ * @param value mostly an HTMLElement
112
+ */
113
+ function ref(value, onChange) {
114
+ return new KTRef(value, onChange ? [onChange] : []);
115
+ }
116
+ function kcollect() {
117
+ const newObj = {};
118
+ const entries = $entries(this);
119
+ for (let i = 0; i < entries.length; i++) {
120
+ const key = entries[i][0];
121
+ if (key === 'kcollect') {
122
+ continue;
123
+ }
124
+ newObj[key] = entries[i][1].value;
125
+ }
126
+ return newObj;
127
+ }
128
+ /**
129
+ * Make all first-level properties of the object a `KTRef`.
130
+ * - `obj.a.b` is not reactive
131
+ */
132
+ const surfaceRef = (obj) => {
133
+ const entries = $entries(obj);
134
+ const newObj = { kcollect };
135
+ for (let i = 0; i < entries.length; i++) {
136
+ newObj[entries[i][0]] = ref(entries[i][1]);
137
+ }
138
+ return newObj;
139
+ };
140
+
55
141
  const booleanHandler = (element, key, value) => {
56
142
  if (key in element) {
57
143
  element[key] = !!value;
@@ -116,6 +202,7 @@ function attrIsObject(element, attr) {
116
202
  key === 'style' ||
117
203
  key === 'children' ||
118
204
  key === 'k-if' ||
205
+ key.startsWith('k-model') ||
119
206
  key === 'ref') {
120
207
  continue;
121
208
  }
@@ -193,6 +280,29 @@ function applyContent(element, content) {
193
280
  }
194
281
  }
195
282
 
283
+ function register(element, valueRef, propName, eventName) {
284
+ element[propName] = valueRef.value; // initialize
285
+ valueRef.addOnChange((newValue) => (element[propName] = newValue));
286
+ element.addEventListener(eventName, () => (valueRef.value = element[propName]));
287
+ }
288
+ function applyModel(element, valueRef) {
289
+ if (element instanceof HTMLInputElement) {
290
+ if (element.type === 'radio' || element.type === 'checkbox') {
291
+ register(element, valueRef, 'checked', 'change');
292
+ }
293
+ else {
294
+ register(element, valueRef, 'value', 'input');
295
+ }
296
+ }
297
+ else if (element instanceof HTMLSelectElement) {
298
+ register(element, valueRef, 'value', 'change');
299
+ }
300
+ else if (element instanceof HTMLTextAreaElement) {
301
+ register(element, valueRef, 'value', 'input');
302
+ }
303
+ console.warn('[kt.js warn] not supported element for k-model:', element.tagName);
304
+ }
305
+
196
306
  const htmlCreator = (tag) => document.createElement(tag);
197
307
  const svgCreator = (tag) => document.createElementNS('http://www.w3.org/2000/svg', tag);
198
308
  const mathMLCreator = (tag) => document.createElementNS('http://www.w3.org/1998/Math/MathML', tag);
@@ -210,7 +320,7 @@ const MATHML_ATTR_FLAG = '__kt_mathml__';
210
320
  * ## About
211
321
  * @package @ktjs/core
212
322
  * @author Kasukabe Tsumugi <futami16237@gmail.com>
213
- * @version 0.21.1 (Last Update: 2026.02.02 09:20:07.959)
323
+ * @version 0.22.0 (Last Update: 2026.02.02 17:11:04.703)
214
324
  * @license MIT
215
325
  * @link https://github.com/baendlorel/kt.js
216
326
  * @link https://baendlorel.github.io/ Welcome to my site!
@@ -239,97 +349,16 @@ const h = (tag, attr, content) => {
239
349
  // * Handle content
240
350
  applyAttr(element, attr);
241
351
  applyContent(element, content);
242
- return element;
243
- };
244
-
245
- class KTRef {
246
- /**
247
- * Indicates that this is a KTRef instance
248
- */
249
- isKT = true;
250
- /**
251
- * @internal
252
- */
253
- _value;
254
- /**
255
- * @internal
256
- */
257
- _onChanges;
258
- constructor(_value, _onChanges) {
259
- this._value = _value;
260
- this._onChanges = _onChanges;
261
- }
262
- /**
263
- * If new value and old value are both nodes, the old one will be replaced in the DOM
264
- */
265
- get value() {
266
- return this._value;
267
- }
268
- set value(newValue) {
269
- if (newValue === this._value) {
270
- return;
271
- }
272
- // replace the old node with the new one in the DOM if both are nodes
273
- if (this._value instanceof Node && newValue instanceof Node) {
274
- if (newValue.contains(this._value)) {
275
- this._value.remove();
276
- }
277
- this._value.replaceWith(newValue);
278
- }
279
- const oldValue = this._value;
280
- this._value = newValue;
281
- for (let i = 0; i < this._onChanges.length; i++) {
282
- this._onChanges[i](newValue, oldValue);
352
+ if (typeof attr === 'object' && attr !== null && 'k-model' in attr) {
353
+ const kmodel = attr['k-model'];
354
+ if (isKTRef(kmodel)) {
355
+ applyModel(element, kmodel);
283
356
  }
284
- }
285
- addOnChange(callback) {
286
- this._onChanges.push(callback);
287
- }
288
- removeOnChange(callback) {
289
- for (let i = this._onChanges.length - 1; i >= 0; i--) {
290
- if (this._onChanges[i] === callback) {
291
- this._onChanges.splice(i, 1);
292
- return true;
293
- }
294
- }
295
- return false;
296
- }
297
- }
298
- const isKTRef = (obj) => {
299
- return typeof obj === 'object' && obj !== null && obj.isKT === true;
300
- };
301
- /**
302
- * Reference to the created HTML element.
303
- * - **Only** respond to `ref.value` changes, not reactive to internal changes of the element.
304
- * - can alse be used to store normal values, but it is not reactive.
305
- * @param value mostly an HTMLElement
306
- */
307
- function ref(value, onChange) {
308
- return new KTRef(value, onChange ? [onChange] : []);
309
- }
310
- function kcollect() {
311
- const newObj = {};
312
- const entries = $entries(this);
313
- for (let i = 0; i < entries.length; i++) {
314
- const key = entries[i][0];
315
- if (key === 'kcollect') {
316
- continue;
357
+ else {
358
+ $throw('k-model value must be a KTRef.');
317
359
  }
318
- newObj[key] = entries[i][1].value;
319
360
  }
320
- return newObj;
321
- }
322
- /**
323
- * Make all first-level properties of the object a `KTRef`.
324
- * - `obj.a.b` is not reactive
325
- */
326
- const surfaceRef = (obj) => {
327
- const entries = $entries(obj);
328
- const newObj = { kcollect };
329
- for (let i = 0; i < entries.length; i++) {
330
- newObj[entries[i][0]] = ref(entries[i][1]);
331
- }
332
- return newObj;
361
+ return element;
333
362
  };
334
363
 
335
364
  const dummyRef = { value: null };
@@ -360,7 +389,7 @@ function jsx(tag, props) {
360
389
  }
361
390
  const oldEl = el;
362
391
  el = newValue ? create(tag, props) : placeholder();
363
- $replaceWith.call(oldEl, el);
392
+ oldEl.replaceWith(el);
364
393
  maybeDummyRef.value = el;
365
394
  });
366
395
  condition = kif.value;
@@ -45,12 +45,15 @@ interface KTBaseAttribute {
45
45
  // # kt-specific attributes
46
46
  ref?: KTRef<JSX.Element>;
47
47
 
48
- // todo 是否要让k-if是KTRef的时候具备响应能力?
49
48
  /**
50
49
  * If a `KTRef` is bound, it will be reactive; otherwise, it will be static.
51
50
  */
52
51
  'k-if'?: any;
53
- // todo k-model如何指定value还是checked还是别的什么?
52
+
53
+ /**
54
+ * Register two-way data binding between an input element and a KTRef.
55
+ * - Default to regist `input` event and `value` property(`checked` for checkboxes and radios).
56
+ */
54
57
  'k-model'?: KTRef<any>;
55
58
 
56
59
  // # normal HTML attributes
@@ -122,7 +125,7 @@ type KTAttribute = KTBaseAttribute & KTPrefixedEventHandlers;
122
125
  * ## About
123
126
  * @package @ktjs/core
124
127
  * @author Kasukabe Tsumugi <futami16237@gmail.com>
125
- * @version 0.21.1 (Last Update: 2026.02.02 09:20:07.959)
128
+ * @version 0.22.0 (Last Update: 2026.02.02 17:11:04.703)
126
129
  * @license MIT
127
130
  * @link https://github.com/baendlorel/kt.js
128
131
  * @link https://baendlorel.github.io/ Welcome to my site!
@@ -6,10 +6,6 @@ const $isThenable = (o) => typeof o === 'object' && o !== null && typeof o.then
6
6
  const $throw = (message) => {
7
7
  throw new Error('@ktjs/shared: ' + message);
8
8
  };
9
-
10
- // DOM manipulation utilities
11
- // # dom natives
12
- const $replaceWith = Element.prototype.replaceWith;
13
9
  /**
14
10
  * & Remove `bind` because it is shockingly slower than wrapper
15
11
  * & `window.document` is safe because it is not configurable and its setter is undefined
@@ -51,6 +47,72 @@ const { get: $buttonDisabledGetter, set: $buttonDisabledSetter } = Object.getOwn
51
47
  // Re-export all utilities
52
48
  Object.defineProperty(window, '@ktjs/shared', { value: '0.20.2' });
53
49
 
50
+ class KTRef {
51
+ /**
52
+ * Indicates that this is a KTRef instance
53
+ */
54
+ isKT = true;
55
+ /**
56
+ * @internal
57
+ */
58
+ _value;
59
+ /**
60
+ * @internal
61
+ */
62
+ _onChanges;
63
+ constructor(_value, _onChanges) {
64
+ this._value = _value;
65
+ this._onChanges = _onChanges;
66
+ }
67
+ /**
68
+ * If new value and old value are both nodes, the old one will be replaced in the DOM
69
+ */
70
+ get value() {
71
+ return this._value;
72
+ }
73
+ set value(newValue) {
74
+ if (newValue === this._value) {
75
+ return;
76
+ }
77
+ // replace the old node with the new one in the DOM if both are nodes
78
+ if (this._value instanceof Node && newValue instanceof Node) {
79
+ if (newValue.contains(this._value)) {
80
+ this._value.remove();
81
+ }
82
+ this._value.replaceWith(newValue);
83
+ }
84
+ const oldValue = this._value;
85
+ this._value = newValue;
86
+ for (let i = 0; i < this._onChanges.length; i++) {
87
+ this._onChanges[i](newValue, oldValue);
88
+ }
89
+ }
90
+ addOnChange(callback) {
91
+ this._onChanges.push(callback);
92
+ }
93
+ removeOnChange(callback) {
94
+ for (let i = this._onChanges.length - 1; i >= 0; i--) {
95
+ if (this._onChanges[i] === callback) {
96
+ this._onChanges.splice(i, 1);
97
+ return true;
98
+ }
99
+ }
100
+ return false;
101
+ }
102
+ }
103
+ const isKTRef = (obj) => {
104
+ return typeof obj === 'object' && obj !== null && obj.isKT === true;
105
+ };
106
+ /**
107
+ * Reference to the created HTML element.
108
+ * - **Only** respond to `ref.value` changes, not reactive to internal changes of the element.
109
+ * - can alse be used to store normal values, but it is not reactive.
110
+ * @param value mostly an HTMLElement
111
+ */
112
+ function ref(value, onChange) {
113
+ return new KTRef(value, []);
114
+ }
115
+
54
116
  const booleanHandler = (element, key, value) => {
55
117
  if (key in element) {
56
118
  element[key] = !!value;
@@ -115,6 +177,7 @@ function attrIsObject(element, attr) {
115
177
  key === 'style' ||
116
178
  key === 'children' ||
117
179
  key === 'k-if' ||
180
+ key.startsWith('k-model') ||
118
181
  key === 'ref') {
119
182
  continue;
120
183
  }
@@ -192,6 +255,29 @@ function applyContent(element, content) {
192
255
  }
193
256
  }
194
257
 
258
+ function register(element, valueRef, propName, eventName) {
259
+ element[propName] = valueRef.value; // initialize
260
+ valueRef.addOnChange((newValue) => (element[propName] = newValue));
261
+ element.addEventListener(eventName, () => (valueRef.value = element[propName]));
262
+ }
263
+ function applyModel(element, valueRef) {
264
+ if (element instanceof HTMLInputElement) {
265
+ if (element.type === 'radio' || element.type === 'checkbox') {
266
+ register(element, valueRef, 'checked', 'change');
267
+ }
268
+ else {
269
+ register(element, valueRef, 'value', 'input');
270
+ }
271
+ }
272
+ else if (element instanceof HTMLSelectElement) {
273
+ register(element, valueRef, 'value', 'change');
274
+ }
275
+ else if (element instanceof HTMLTextAreaElement) {
276
+ register(element, valueRef, 'value', 'input');
277
+ }
278
+ console.warn('[kt.js warn] not supported element for k-model:', element.tagName);
279
+ }
280
+
195
281
  const htmlCreator = (tag) => document.createElement(tag);
196
282
  const svgCreator = (tag) => document.createElementNS('http://www.w3.org/2000/svg', tag);
197
283
  const mathMLCreator = (tag) => document.createElementNS('http://www.w3.org/1998/Math/MathML', tag);
@@ -209,7 +295,7 @@ const MATHML_ATTR_FLAG = '__kt_mathml__';
209
295
  * ## About
210
296
  * @package @ktjs/core
211
297
  * @author Kasukabe Tsumugi <futami16237@gmail.com>
212
- * @version 0.21.1 (Last Update: 2026.02.02 09:20:07.959)
298
+ * @version 0.22.0 (Last Update: 2026.02.02 17:11:04.703)
213
299
  * @license MIT
214
300
  * @link https://github.com/baendlorel/kt.js
215
301
  * @link https://baendlorel.github.io/ Welcome to my site!
@@ -238,74 +324,17 @@ const h = (tag, attr, content) => {
238
324
  // * Handle content
239
325
  applyAttr(element, attr);
240
326
  applyContent(element, content);
241
- return element;
242
- };
243
-
244
- class KTRef {
245
- /**
246
- * Indicates that this is a KTRef instance
247
- */
248
- isKT = true;
249
- /**
250
- * @internal
251
- */
252
- _value;
253
- /**
254
- * @internal
255
- */
256
- _onChanges;
257
- constructor(_value, _onChanges) {
258
- this._value = _value;
259
- this._onChanges = _onChanges;
260
- }
261
- /**
262
- * If new value and old value are both nodes, the old one will be replaced in the DOM
263
- */
264
- get value() {
265
- return this._value;
266
- }
267
- set value(newValue) {
268
- if (newValue === this._value) {
269
- return;
327
+ if (typeof attr === 'object' && attr !== null && 'k-model' in attr) {
328
+ const kmodel = attr['k-model'];
329
+ if (isKTRef(kmodel)) {
330
+ applyModel(element, kmodel);
270
331
  }
271
- // replace the old node with the new one in the DOM if both are nodes
272
- if (this._value instanceof Node && newValue instanceof Node) {
273
- if (newValue.contains(this._value)) {
274
- this._value.remove();
275
- }
276
- this._value.replaceWith(newValue);
277
- }
278
- const oldValue = this._value;
279
- this._value = newValue;
280
- for (let i = 0; i < this._onChanges.length; i++) {
281
- this._onChanges[i](newValue, oldValue);
282
- }
283
- }
284
- addOnChange(callback) {
285
- this._onChanges.push(callback);
286
- }
287
- removeOnChange(callback) {
288
- for (let i = this._onChanges.length - 1; i >= 0; i--) {
289
- if (this._onChanges[i] === callback) {
290
- this._onChanges.splice(i, 1);
291
- return true;
292
- }
332
+ else {
333
+ $throw('k-model value must be a KTRef.');
293
334
  }
294
- return false;
295
335
  }
296
- }
297
- const isKTRef = (obj) => {
298
- return typeof obj === 'object' && obj !== null && obj.isKT === true;
336
+ return element;
299
337
  };
300
- /**
301
- * Reference to the created HTML element.
302
- * - **Only** respond to `ref.value` changes, not reactive to internal changes of the element.
303
- * - can alse be used to store normal values, but it is not reactive.
304
- * @param value mostly an HTMLElement
305
- */
306
- function ref(value, onChange) {
307
- return new KTRef(value, []);
308
- }
309
338
 
310
339
  const dummyRef = { value: null };
311
340
  const create = (tag, props) => {
@@ -335,7 +364,7 @@ function jsx(tag, props) {
335
364
  }
336
365
  const oldEl = el;
337
366
  el = newValue ? create(tag, props) : placeholder();
338
- $replaceWith.call(oldEl, el);
367
+ oldEl.replaceWith(el);
339
368
  maybeDummyRef.value = el;
340
369
  });
341
370
  condition = kif.value;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ktjs/core",
3
- "version": "0.21.1",
3
+ "version": "0.22.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",