kt.js 0.23.0 → 0.24.1
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/README.md +116 -161
- package/dist/jsx-runtime.mjs +120 -100
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -1,43 +1,54 @@
|
|
|
1
|
-
|
|
1
|
+
# KT.js
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
A lightweight, manual‑control web framework that creates real DOM elements with built‑in reactive state management.
|
|
4
4
|
|
|
5
|
-
[
|
|
5
|
+
[](https://www.npmjs.com/package/kt.js)
|
|
6
|
+
[](https://www.npmjs.com/package/kt.js)
|
|
7
|
+
[](https://opensource.org/licenses/MIT)
|
|
6
8
|
|
|
7
|
-
|
|
9
|
+
[Changelog](../../CHANGELOG.md)
|
|
8
10
|
|
|
9
|
-
|
|
11
|
+
## Features
|
|
10
12
|
|
|
11
|
-
|
|
13
|
+
- **Real DOM** – JSX compiles directly to `HTMLElement` creation, with zero virtual‑DOM overhead.
|
|
14
|
+
- **Manual Updates** – You decide when the DOM updates; no automatic re‑renders.
|
|
15
|
+
- **Reactive State** – Built‑in `ref()` and `computed()` for reactive values with change listeners.
|
|
16
|
+
- **Zero Forced Re‑renders** – Update only what changes; avoid full‑component repaints.
|
|
17
|
+
- **Full TypeScript Support** – Accurate type inference and JSX/TSX integration.
|
|
18
|
+
- **Lightweight** – Small bundle size, no unnecessary dependencies.
|
|
12
19
|
|
|
13
|
-
##
|
|
20
|
+
## Quick Start
|
|
14
21
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
- **[kt.js](./packages/kt.js)**: Main entry package that re-exports all functionality
|
|
18
|
-
- **[@ktjs/core](./packages/core)**: Core DOM manipulation utilities and the `h` function. SX/TSX support with full TypeScript integration (included in kt.js package)
|
|
19
|
-
- **[@ktjs/router](./packages/router)**: Client-side routing with navigation guards (not included in kt.js package)
|
|
20
|
-
- **[@ktjs/mui](./packages/mui)**: Material UI components built on top of KT.js (not included in kt.js package)
|
|
21
|
-
|
|
22
|
-
You can install the full package or individual packages as needed:
|
|
22
|
+
### Installation
|
|
23
23
|
|
|
24
24
|
```bash
|
|
25
|
+
# Full package (includes core)
|
|
25
26
|
pnpm add kt.js
|
|
26
27
|
|
|
27
|
-
#
|
|
28
|
-
pnpm add @ktjs/
|
|
29
|
-
pnpm add @ktjs/mui # Material UI components (requires @ktjs/core)
|
|
28
|
+
# Individual packages
|
|
29
|
+
pnpm add @ktjs/core @ktjs/router
|
|
30
30
|
```
|
|
31
31
|
|
|
32
|
-
|
|
32
|
+
### Basic Usage
|
|
33
33
|
|
|
34
|
-
|
|
34
|
+
```tsx
|
|
35
|
+
import { ref } from 'kt.js';
|
|
35
36
|
|
|
36
|
-
|
|
37
|
+
function Counter() {
|
|
38
|
+
const count = ref(0);
|
|
37
39
|
|
|
38
|
-
|
|
40
|
+
const button = <button on:click={() => count.value++}>Count: {count.value}</button>;
|
|
39
41
|
|
|
40
|
-
|
|
42
|
+
return button;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// Mount to DOM
|
|
46
|
+
document.body.appendChild(Counter());
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
### TypeScript Configuration
|
|
50
|
+
|
|
51
|
+
For JSX/TSX support, set your `tsconfig.json`:
|
|
41
52
|
|
|
42
53
|
```json
|
|
43
54
|
{
|
|
@@ -48,192 +59,136 @@ for TSX/JSX support, add this to your `tsconfig.json`:
|
|
|
48
59
|
}
|
|
49
60
|
```
|
|
50
61
|
|
|
51
|
-
## Core Features
|
|
62
|
+
## Core Features
|
|
52
63
|
|
|
53
|
-
###
|
|
54
|
-
|
|
55
|
-
KT.js provides first-class JSX/TSX support with zero virtual DOM overhead. JSX compiles directly to `h()` function calls.
|
|
64
|
+
### Reactive System
|
|
56
65
|
|
|
57
66
|
```tsx
|
|
58
|
-
|
|
59
|
-
const Button = ({ onClick, children }) => (
|
|
60
|
-
<button on:click={onClick} class="btn">
|
|
61
|
-
{children}
|
|
62
|
-
</button>
|
|
63
|
-
);
|
|
64
|
-
|
|
65
|
-
// Conditional rendering with k-if
|
|
66
|
-
const UserCard = ({ user, showDetails }) => (
|
|
67
|
-
<div class="card">
|
|
68
|
-
<h3>{user.name}</h3>
|
|
69
|
-
<div k-if={showDetails}>
|
|
70
|
-
<p>Email: {user.email}</p>
|
|
71
|
-
<p>Role: {user.role}</p>
|
|
72
|
-
</div>
|
|
73
|
-
</div>
|
|
74
|
-
);
|
|
67
|
+
import { ref, computed } from 'kt.js';
|
|
75
68
|
|
|
76
|
-
//
|
|
77
|
-
const
|
|
69
|
+
// Reactive references
|
|
70
|
+
const count = ref(0);
|
|
71
|
+
const double = computed(() => count.value * 2, [count]);
|
|
78
72
|
|
|
79
|
-
//
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
};
|
|
84
|
-
```
|
|
73
|
+
// Listen to changes
|
|
74
|
+
count.addOnChange((newVal, oldVal) => {
|
|
75
|
+
console.log(`Count changed from ${oldVal} to ${newVal}`);
|
|
76
|
+
});
|
|
85
77
|
|
|
86
|
-
|
|
78
|
+
// Update triggers change listeners
|
|
79
|
+
count.value = 10;
|
|
80
|
+
```
|
|
87
81
|
|
|
88
|
-
|
|
82
|
+
### Conditional Rendering
|
|
89
83
|
|
|
90
84
|
```tsx
|
|
91
|
-
|
|
92
|
-
```
|
|
85
|
+
const show = ref(true);
|
|
93
86
|
|
|
94
|
-
|
|
87
|
+
const element = <div k-if={show}>This content is conditionally rendered</div>;
|
|
95
88
|
|
|
96
|
-
|
|
89
|
+
// Toggle visibility
|
|
90
|
+
show.value = false; // Element becomes a comment placeholder
|
|
91
|
+
```
|
|
97
92
|
|
|
98
|
-
|
|
93
|
+
### List Rendering
|
|
99
94
|
|
|
100
95
|
```tsx
|
|
101
|
-
import { ref } from 'kt.js';
|
|
102
|
-
const name = ref('');
|
|
96
|
+
import { KTFor, ref } from 'kt.js';
|
|
103
97
|
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
</div>
|
|
109
|
-
);
|
|
110
|
-
}
|
|
98
|
+
const items = ref([
|
|
99
|
+
{ id: 1, name: 'Item 1' },
|
|
100
|
+
{ id: 2, name: 'Item 2' },
|
|
101
|
+
]);
|
|
111
102
|
|
|
112
|
-
|
|
113
|
-
```
|
|
103
|
+
const list = <KTFor list={items.value} key={(item) => item.id} map={(item) => <div>{item.name}</div>} />;
|
|
114
104
|
|
|
115
|
-
|
|
105
|
+
// Update list
|
|
106
|
+
items.value = [...items.value, { id: 3, name: 'Item 3' }];
|
|
107
|
+
```
|
|
116
108
|
|
|
117
|
-
|
|
109
|
+
### Two‑way Data Binding
|
|
118
110
|
|
|
119
111
|
```tsx
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
el.redraw();
|
|
126
|
-
// el is now `<div>bb</div>`
|
|
112
|
+
function InputComponent() {
|
|
113
|
+
const text = ref('');
|
|
114
|
+
|
|
115
|
+
return <input k-model={text} />;
|
|
116
|
+
}
|
|
127
117
|
```
|
|
128
118
|
|
|
129
|
-
###
|
|
119
|
+
### Redrawable Components
|
|
130
120
|
|
|
131
|
-
|
|
121
|
+
```tsx
|
|
122
|
+
import { createRedrawable, ref } from 'kt.js';
|
|
132
123
|
|
|
133
|
-
|
|
134
|
-
|
|
124
|
+
function DynamicCounter() {
|
|
125
|
+
const count = ref(0);
|
|
135
126
|
|
|
136
|
-
const
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
}
|
|
127
|
+
const counter = createRedrawable(() => (
|
|
128
|
+
<div>
|
|
129
|
+
Count: {count.value}
|
|
130
|
+
<button on:click={() => count.value++}>+</button>
|
|
131
|
+
</div>
|
|
132
|
+
));
|
|
140
133
|
|
|
141
|
-
//
|
|
142
|
-
count.
|
|
134
|
+
// Redraw when count changes
|
|
135
|
+
count.addOnChange(() => counter.redraw());
|
|
136
|
+
|
|
137
|
+
return counter;
|
|
138
|
+
}
|
|
143
139
|
```
|
|
144
140
|
|
|
145
|
-
|
|
141
|
+
## Package Structure
|
|
146
142
|
|
|
147
|
-
|
|
148
|
-
|
|
143
|
+
- **[@ktjs/core](./packages/core)** – Core framework with JSX, reactivity, and DOM utilities.
|
|
144
|
+
- **[@ktjs/router](./packages/router)** – Client‑side router with navigation guards.
|
|
145
|
+
- **[@ktjs/mui](./packages/mui)** – Material UI components built on top of KT.js.
|
|
149
146
|
|
|
150
|
-
|
|
151
|
-
const obj = surfaceRef({ a: 1, b: 2 });
|
|
152
|
-
obj.a.value = 3; // Triggers change listeners for obj.a
|
|
147
|
+
## Philosophy
|
|
153
148
|
|
|
154
|
-
|
|
155
|
-
|
|
149
|
+
KT.js follows one rule: **full control of the DOM and avoid unnecessary repainting**.
|
|
150
|
+
|
|
151
|
+
As a web framework, repeatedly creating a large number of variables and objects is unacceptable. That’s why KT.js was built.
|
|
156
152
|
|
|
157
|
-
|
|
153
|
+
## Advanced Usage
|
|
158
154
|
|
|
159
|
-
|
|
155
|
+
### Surface References
|
|
160
156
|
|
|
161
157
|
```tsx
|
|
162
|
-
|
|
163
|
-
element.redraw({ list: [1, 2, 3] });
|
|
164
|
-
```
|
|
158
|
+
import { surfaceRef } from 'kt.js';
|
|
165
159
|
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
### Basic Routing
|
|
171
|
-
|
|
172
|
-
```typescript
|
|
173
|
-
import { createRouter } from '@ktjs/router';
|
|
174
|
-
|
|
175
|
-
const router = createRouter({
|
|
176
|
-
routes: [
|
|
177
|
-
{
|
|
178
|
-
path: '/',
|
|
179
|
-
name: 'home',
|
|
180
|
-
component: () => <HomePage />
|
|
181
|
-
},
|
|
182
|
-
{
|
|
183
|
-
path: '/user/:id',
|
|
184
|
-
name: 'user',
|
|
185
|
-
component: (to) => <UserPage id={to.params.id} />
|
|
186
|
-
}
|
|
187
|
-
],
|
|
188
|
-
beforeEach: async (to, from) => {
|
|
189
|
-
// Auth check
|
|
190
|
-
if (to.path === '/admin' && !isAdmin()) {
|
|
191
|
-
return '/login';
|
|
192
|
-
}
|
|
193
|
-
}
|
|
160
|
+
const user = surfaceRef({
|
|
161
|
+
name: 'John',
|
|
162
|
+
age: 30,
|
|
194
163
|
});
|
|
195
|
-
```
|
|
196
|
-
|
|
197
|
-
### Navigation
|
|
198
164
|
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
router.push('/user/123');
|
|
202
|
-
router.push({ name: 'user', params: { id: '456' } });
|
|
165
|
+
// Access reactive properties
|
|
166
|
+
user.name.value = 'Jane';
|
|
203
167
|
|
|
204
|
-
//
|
|
205
|
-
|
|
168
|
+
// Get the original object
|
|
169
|
+
const original = user.kcollect();
|
|
206
170
|
```
|
|
207
171
|
|
|
208
|
-
|
|
172
|
+
### Event Handling
|
|
209
173
|
|
|
210
|
-
```
|
|
211
|
-
|
|
212
|
-
|
|
174
|
+
```tsx
|
|
175
|
+
const handleClick = (event) => {
|
|
176
|
+
console.log('Clicked!', event);
|
|
177
|
+
};
|
|
213
178
|
|
|
214
|
-
|
|
215
|
-
pnpm add @ktjs/core @ktjs/router
|
|
179
|
+
const button = <button on:click={handleClick}>Click me</button>;
|
|
216
180
|
```
|
|
217
181
|
|
|
218
|
-
##
|
|
219
|
-
|
|
220
|
-
For JSX/TSX support, configure your `tsconfig.json`:
|
|
182
|
+
## Performance Benefits
|
|
221
183
|
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
"jsxImportSource": "@ktjs/core"
|
|
227
|
-
}
|
|
228
|
-
}
|
|
229
|
-
```
|
|
184
|
+
- **No Virtual DOM** – Direct DOM manipulation eliminates reconciliation overhead.
|
|
185
|
+
- **Manual Updates** – Only update what you need, when you need it.
|
|
186
|
+
- **Minimal Abstraction** – Close to native DOM APIs for maximum performance.
|
|
187
|
+
- **Small Bundle** – Minimal runtime overhead.
|
|
230
188
|
|
|
231
|
-
##
|
|
189
|
+
## Browser Support
|
|
232
190
|
|
|
233
|
-
|
|
234
|
-
- **Zero Re-renders**: Update only what changes, avoid full component re-renders
|
|
235
|
-
- **Type Safety**: Full TypeScript support with accurate type inference
|
|
236
|
-
- **Lightweight**: Small bundle size, no unnecessary dependencies
|
|
191
|
+
KT.js supports all modern browsers and IE11+ with appropriate polyfills.
|
|
237
192
|
|
|
238
193
|
## License
|
|
239
194
|
|
package/dist/jsx-runtime.mjs
CHANGED
|
@@ -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 $
|
|
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
|
-
|
|
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
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
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 (
|
|
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 === '
|
|
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-
|
|
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 (
|
|
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 (!
|
|
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.
|
|
363
|
+
* @version 0.24.1 (Last Update: 2026.02.05 12:51:38.436)
|
|
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
|
-
|
|
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
|
-
|
|
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 (
|
|
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
|
|
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": "kt.js",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.24.1",
|
|
4
4
|
"author": {
|
|
5
5
|
"name": "Kasukabe Tsumugi",
|
|
6
6
|
"email": "futami16237@gmail.com"
|
|
@@ -41,7 +41,7 @@
|
|
|
41
41
|
],
|
|
42
42
|
"license": "MIT",
|
|
43
43
|
"dependencies": {
|
|
44
|
-
"@ktjs/core": "0.
|
|
44
|
+
"@ktjs/core": "0.24.1"
|
|
45
45
|
},
|
|
46
46
|
"scripts": {
|
|
47
47
|
"build": "rollup -c rollup.config.mjs",
|