kt.js 0.20.2 → 0.21.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 +85 -364
- package/dist/jsx-runtime.mjs +47 -25
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -2,11 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
[](https://www.npmjs.com/package/kt.js) [](https://github.com/baendlorel/kt.js/blob/main/LICENSE)
|
|
4
4
|
|
|
5
|
-
[CHANGLOG✨](CHANGELOG.md)
|
|
6
|
-
|
|
7
|
-
## What's New (v0.19.x)
|
|
8
|
-
|
|
9
|
-
- Release `0.19.0` (2026-01-31): build & packaging fixes, TypeScript and lint cleanups, MUI-focused fixes (JSX handling, `jsxImportSource` set to `@ktjs/core`, radio/checkbox/TextField fixes), and repo cleanup (removed alias detection, moved shared utilities into `shared`). See the full details in the CHANGELOG.
|
|
5
|
+
[CHANGLOG✨](../CHANGELOG.md)
|
|
10
6
|
|
|
11
7
|
> Note: This framework is still under development. APIs, type declarations, and other parts **may change frequently**. If you use it, please watch for updates in the near future. Feel free to mail me if you have any questions!
|
|
12
8
|
|
|
@@ -21,6 +17,7 @@ KT.js is now a **monorepo** containing multiple packages:
|
|
|
21
17
|
- **[kt.js](./packages/kt.js)**: Main entry package that re-exports all functionality
|
|
22
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)
|
|
23
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)
|
|
24
21
|
|
|
25
22
|
You can install the full package or individual packages as needed:
|
|
26
23
|
|
|
@@ -28,10 +25,8 @@ You can install the full package or individual packages as needed:
|
|
|
28
25
|
pnpm add kt.js
|
|
29
26
|
|
|
30
27
|
# Or install individual packages
|
|
31
|
-
pnpm add @ktjs/core # Core DOM utilities (independent)
|
|
32
28
|
pnpm add @ktjs/router # Client-side router (requires @ktjs/core)
|
|
33
29
|
pnpm add @ktjs/mui # Material UI components (requires @ktjs/core)
|
|
34
|
-
pnpm add @ktjs/shortcuts # Shortcuts (requires @ktjs/core)
|
|
35
30
|
```
|
|
36
31
|
|
|
37
32
|
## Philosophy
|
|
@@ -40,414 +35,140 @@ As a web framework, repeatedly creating a large number of variables and objects
|
|
|
40
35
|
|
|
41
36
|
KT.js follows one rule: **full control of DOM and avoid unnecessary repainting**.
|
|
42
37
|
|
|
43
|
-
##
|
|
44
|
-
|
|
45
|
-
- **Monorepo Architecture**: Modular packages that can be installed independently or together
|
|
46
|
-
- **Tiny Bundle Size**: Minimal runtime overhead with aggressive tree-shaking
|
|
47
|
-
- **`h` function**: Create DOM elements with a simple, flexible API
|
|
48
|
-
- Shortcut functions for all HTML elements (`div`, `span`, `button`, etc.)
|
|
49
|
-
- Event handlers with `on:<eventName>` syntax or function attributes
|
|
50
|
-
- Full TypeScript support with intelligent type inference
|
|
51
|
-
- **JSX/TSX Support**: Full JSX syntax support with TypeScript integration
|
|
52
|
-
- Zero virtual DOM - JSX compiles directly to `h()` function calls
|
|
53
|
-
- Full HTML element type inference (`<button>` returns `HTMLButtonElement`)
|
|
54
|
-
- Support for `on:click` event handler syntax
|
|
55
|
-
- `redraw()` method for controlled component updates (v0.11+)
|
|
56
|
-
- `k-if` directive for conditional rendering (v0.14.6+)
|
|
57
|
-
- Array children support for seamless `.map()` integration (v0.14.1+)
|
|
58
|
-
- **List Rendering**: Efficient list rendering with `KTFor` component (v0.16.0+)
|
|
59
|
-
- Comment anchor with `__kt_for_list__` array property
|
|
60
|
-
- Key-based DOM reuse for minimal updates
|
|
61
|
-
- Auto-appends list items when anchor added to parent
|
|
62
|
-
- **Async Components**: Built-in support for Promise-based components
|
|
63
|
-
- `KTAsync` component for handling async operations
|
|
64
|
-
- Automatic placeholder management during loading
|
|
65
|
-
- Seamless integration with JSX/TSX syntax
|
|
66
|
-
- **Client-Side Router** (separate package):
|
|
67
|
-
- Hash-based routing only (simplified from v0.14.7+)
|
|
68
|
-
- Async navigation guards with Promise support
|
|
69
|
-
- Dynamic route parameters and query string parsing
|
|
70
|
-
- RouterView component for declarative routing
|
|
71
|
-
- Pure routing logic - no rendering, no dependencies
|
|
72
|
-
- **Shortcuts & Utilities**:
|
|
73
|
-
- `withDefaults`: Wrap element creation functions with default properties
|
|
74
|
-
- Convenient shorthand functions for common operations
|
|
75
|
-
- Form helpers and layout utilities
|
|
76
|
-
- **Full ES5 Compatibility**: Works in IE9+ and all modern browsers
|
|
77
|
-
- Transpiled to ES5 with no modern syntax
|
|
78
|
-
- Optional minimal Promise polyfill for older environments
|
|
79
|
-
- **Shared Runtime**: Efficient code sharing across packages with zero overhead
|
|
80
|
-
|
|
81
|
-
## Getting started
|
|
82
|
-
|
|
83
|
-
Install via package managers:
|
|
84
|
-
|
|
85
|
-
```bash
|
|
86
|
-
npm install kt.js
|
|
87
|
-
# or
|
|
88
|
-
pnpm add kt.js
|
|
89
|
-
```
|
|
90
|
-
|
|
91
|
-
```ts
|
|
92
|
-
import { h, div } from 'kt.js';
|
|
93
|
-
|
|
94
|
-
const container = div('container', [div('header'), div('body', 'something'), div('footer')]);
|
|
95
|
-
const app = h('section', { id: 'app' }, container);
|
|
96
|
-
```
|
|
97
|
-
|
|
98
|
-
This will create the following DOM structure:
|
|
99
|
-
|
|
100
|
-
```html
|
|
101
|
-
<section id="app">
|
|
102
|
-
<div class="container">
|
|
103
|
-
<div class="header"></div>
|
|
104
|
-
<div class="body">something</div>
|
|
105
|
-
<div class="footer"></div>
|
|
106
|
-
</div>
|
|
107
|
-
</section>
|
|
108
|
-
```
|
|
109
|
-
|
|
110
|
-
### Using JSX/TSX
|
|
111
|
-
|
|
112
|
-
KT.js now has full JSX support! With the `@ktjs/jsx` package (included in the main `kt.js` package), you can write components using familiar JSX syntax:
|
|
113
|
-
|
|
114
|
-
**TypeScript Configuration** (`tsconfig.json`):
|
|
115
|
-
|
|
116
|
-
```json
|
|
117
|
-
{
|
|
118
|
-
"compilerOptions": {
|
|
119
|
-
"jsx": "react-jsx",
|
|
120
|
-
"jsxImportSource": "kt.js"
|
|
121
|
-
}
|
|
122
|
-
}
|
|
123
|
-
```
|
|
124
|
-
|
|
125
|
-
**Basic JSX Example**:
|
|
126
|
-
|
|
127
|
-
```tsx
|
|
128
|
-
import { jsx } from 'kt.js';
|
|
129
|
-
|
|
130
|
-
function Counter() {
|
|
131
|
-
const count = 0;
|
|
132
|
-
|
|
133
|
-
return (
|
|
134
|
-
<div class="counter">
|
|
135
|
-
<h1>Counter: {count}</h1>
|
|
136
|
-
<button on:click={() => console.log('Clicked!')}>Increment</button>
|
|
137
|
-
</div>
|
|
138
|
-
);
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
// JSX compiles to direct h() function calls - no virtual DOM!
|
|
142
|
-
const counterElement = <Counter />;
|
|
143
|
-
```
|
|
144
|
-
|
|
145
|
-
**Event Handling with @ Syntax**:
|
|
146
|
-
|
|
147
|
-
```tsx
|
|
148
|
-
function App() {
|
|
149
|
-
const handleClick = () => alert('Button clicked!');
|
|
150
|
-
|
|
151
|
-
return (
|
|
152
|
-
<div>
|
|
153
|
-
<button on:click={handleClick}>Click me</button>
|
|
154
|
-
</div>
|
|
155
|
-
);
|
|
156
|
-
}
|
|
157
|
-
```
|
|
158
|
-
|
|
159
|
-
**Type Safety**:
|
|
160
|
-
|
|
161
|
-
```tsx
|
|
162
|
-
// TypeScript knows this is an HTMLButtonElement
|
|
163
|
-
const button: HTMLButtonElement = <button>Click</button>;
|
|
164
|
-
|
|
165
|
-
// TypeScript knows this is an HTMLInputElement
|
|
166
|
-
const input: HTMLInputElement = <input type="text" value="hello" />;
|
|
167
|
-
|
|
168
|
-
// TypeScript provides autocomplete for HTML attributes
|
|
169
|
-
const div: HTMLDivElement = <div className="container" id="main" />;
|
|
170
|
-
```
|
|
171
|
-
|
|
172
|
-
**Important Notes**:
|
|
173
|
-
|
|
174
|
-
- KT.js JSX has **no Fragment support** - we don't have a Fragment concept
|
|
175
|
-
- JSX compiles directly to `h()` function calls - **zero virtual DOM overhead**
|
|
176
|
-
- Use `on:click` syntax for event handlers to avoid conflicts with existing attributes
|
|
177
|
-
- All JSX elements have proper HTML element type inference in TypeScript
|
|
178
|
-
- Use `k-if` attribute for conditional rendering (v0.14.6+)
|
|
179
|
-
- Children can be arrays for easy `.map()` integration (v0.14.1+)
|
|
180
|
-
|
|
181
|
-
**Conditional Rendering with k-if** (v0.14.6+):
|
|
182
|
-
|
|
183
|
-
```tsx
|
|
184
|
-
import { jsx } from 'kt.js';
|
|
185
|
-
|
|
186
|
-
function UserProfile({ user, isLoggedIn }: { user: any; isLoggedIn: boolean }) {
|
|
187
|
-
return (
|
|
188
|
-
<div>
|
|
189
|
-
<h1>Profile</h1>
|
|
190
|
-
{/* Element only created if condition is true */}
|
|
191
|
-
<div k-if={isLoggedIn}>
|
|
192
|
-
<p>Welcome, {user.name}!</p>
|
|
193
|
-
<button>Logout</button>
|
|
194
|
-
</div>
|
|
195
|
-
{/* Element only created if condition is true */}
|
|
196
|
-
<div k-if={!isLoggedIn}>
|
|
197
|
-
<p>Please log in</p>
|
|
198
|
-
<button>Login</button>
|
|
199
|
-
</div>
|
|
200
|
-
</div>
|
|
201
|
-
);
|
|
202
|
-
}
|
|
203
|
-
```
|
|
204
|
-
|
|
205
|
-
**Array Children Support** (v0.14.1+):
|
|
38
|
+
## Core Features (@ktjs/core)
|
|
206
39
|
|
|
207
|
-
|
|
208
|
-
import { jsx } from 'kt.js';
|
|
209
|
-
|
|
210
|
-
function TodoList({ todos }: { todos: string[] }) {
|
|
211
|
-
return (
|
|
212
|
-
<div>
|
|
213
|
-
<h2>Todo List</h2>
|
|
214
|
-
<ul>
|
|
215
|
-
{/* Map arrays directly as children */}
|
|
216
|
-
{todos.map((todo) => (
|
|
217
|
-
<li>{todo}</li>
|
|
218
|
-
))}
|
|
219
|
-
</ul>
|
|
220
|
-
</div>
|
|
221
|
-
);
|
|
222
|
-
}
|
|
40
|
+
### JSX/TSX Support
|
|
223
41
|
|
|
224
|
-
|
|
225
|
-
function MixedList({ items }: { items: string[] }) {
|
|
226
|
-
return (
|
|
227
|
-
<ul>
|
|
228
|
-
<li>Header Item</li>
|
|
229
|
-
{items.map((item) => (
|
|
230
|
-
<li>{item}</li>
|
|
231
|
-
))}
|
|
232
|
-
<li>Footer Item</li>
|
|
233
|
-
</ul>
|
|
234
|
-
);
|
|
235
|
-
}
|
|
236
|
-
```
|
|
237
|
-
|
|
238
|
-
### Async Components with KTAsync
|
|
239
|
-
|
|
240
|
-
KT.js provides built-in support for async components through the `KTAsync` component:
|
|
42
|
+
KT.js provides first-class JSX/TSX support with zero virtual DOM overhead. JSX compiles directly to `h()` function calls.
|
|
241
43
|
|
|
242
44
|
```tsx
|
|
243
|
-
import {
|
|
244
|
-
|
|
245
|
-
// Define an async component that returns a Promise<HTMLElement>
|
|
246
|
-
const AsyncUserCard = function () {
|
|
247
|
-
return fetch('/api/user')
|
|
248
|
-
.then((res) => res.json())
|
|
249
|
-
.then((user) => (
|
|
250
|
-
<div class="user-card">
|
|
251
|
-
<h2>{user.name}</h2>
|
|
252
|
-
<p>{user.email}</p>
|
|
253
|
-
</div>
|
|
254
|
-
));
|
|
255
|
-
};
|
|
256
|
-
|
|
257
|
-
// Use KTAsync to handle the async component
|
|
258
|
-
function App() {
|
|
259
|
-
return (
|
|
260
|
-
<div class="app">
|
|
261
|
-
<h1>User Profile</h1>
|
|
262
|
-
<KTAsync component={AsyncUserCard} />
|
|
263
|
-
</div>
|
|
264
|
-
);
|
|
265
|
-
}
|
|
266
|
-
|
|
267
|
-
// The component starts with a placeholder comment node
|
|
268
|
-
// When the Promise resolves, it automatically replaces with the actual element
|
|
269
|
-
```
|
|
270
|
-
|
|
271
|
-
**How KTAsync works:**
|
|
45
|
+
import { h } from '@ktjs/core';
|
|
272
46
|
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
47
|
+
// Function components
|
|
48
|
+
const Button = ({ onClick, children }) => (
|
|
49
|
+
<button on:click={onClick} class="btn">
|
|
50
|
+
{children}
|
|
51
|
+
</button>
|
|
52
|
+
);
|
|
279
53
|
|
|
280
|
-
|
|
281
|
-
const
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
<div>
|
|
285
|
-
<p>
|
|
286
|
-
<
|
|
54
|
+
// Conditional rendering with k-if
|
|
55
|
+
const UserCard = ({ user, showDetails }) => (
|
|
56
|
+
<div class="card">
|
|
57
|
+
<h3>{user.name}</h3>
|
|
58
|
+
<div k-if={showDetails}>
|
|
59
|
+
<p>Email: {user.email}</p>
|
|
60
|
+
<p>Role: {user.role}</p>
|
|
287
61
|
</div>
|
|
288
|
-
);
|
|
289
|
-
|
|
290
|
-
// Simulate async data loading
|
|
291
|
-
return new Promise<HTMLElement>((resolve) => {
|
|
292
|
-
setTimeout(() => resolve(container), 500);
|
|
293
|
-
});
|
|
294
|
-
};
|
|
295
|
-
|
|
296
|
-
// Usage
|
|
297
|
-
const app = (
|
|
298
|
-
<div>
|
|
299
|
-
<h1>Loading async content...</h1>
|
|
300
|
-
<KTAsync component={DynamicContent} />
|
|
301
62
|
</div>
|
|
302
63
|
);
|
|
303
|
-
```
|
|
304
|
-
|
|
305
|
-
If you give a function in attributes, it will be treated as an event listener, and the key will be considered as the event name. `@<eventName>` will also be considered as the handler to avoid conflicts with existing attributes:
|
|
306
64
|
|
|
307
|
-
|
|
308
|
-
const
|
|
309
|
-
{
|
|
310
|
-
dblclick: '22',
|
|
311
|
-
'on:dblclick': function trueHandler() {
|
|
312
|
-
/* ... */
|
|
313
|
-
},
|
|
314
|
-
},
|
|
315
|
-
'Click me',
|
|
316
|
-
);
|
|
65
|
+
// List rendering with KTFor
|
|
66
|
+
const UserList = ({ users }) => <KTFor list={users} key={(user) => user.id} map={(user) => <UserCard user={user} />} />;
|
|
317
67
|
|
|
318
|
-
//
|
|
319
|
-
const
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
/* ... */
|
|
324
|
-
});
|
|
68
|
+
// Async components
|
|
69
|
+
const AsyncContent = async () => {
|
|
70
|
+
const data = await fetchData();
|
|
71
|
+
return <div>{data}</div>;
|
|
72
|
+
};
|
|
325
73
|
```
|
|
326
74
|
|
|
327
|
-
###
|
|
328
|
-
|
|
329
|
-
KT.js works seamlessly with CSS-in-JS libraries like `@emotion/css`:
|
|
75
|
+
### Reactive References
|
|
330
76
|
|
|
331
|
-
|
|
332
|
-
import { css } from '@emotion/css';
|
|
333
|
-
import { h, div } from 'kt.js';
|
|
77
|
+
Create reactive values with `ref()` and listen to changes.
|
|
334
78
|
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
font-size: 20px;
|
|
338
|
-
`;
|
|
79
|
+
```typescript
|
|
80
|
+
import { ref } from '@ktjs/core';
|
|
339
81
|
|
|
340
|
-
|
|
341
|
-
|
|
82
|
+
const count = ref(0);
|
|
83
|
+
count.addOnChange((newVal, oldVal) => {
|
|
84
|
+
console.log(`Count changed from ${oldVal} to ${newVal}`);
|
|
85
|
+
});
|
|
342
86
|
|
|
343
|
-
//
|
|
344
|
-
|
|
87
|
+
// Update triggers change listeners
|
|
88
|
+
count.value = 1;
|
|
345
89
|
```
|
|
346
90
|
|
|
347
|
-
###
|
|
348
|
-
|
|
349
|
-
The `withDefaults` function allows you to create element factories with predefined properties:
|
|
350
|
-
|
|
351
|
-
```ts
|
|
352
|
-
import { withDefaults, div, button } from 'kt.js';
|
|
91
|
+
### Redraw Mechanism
|
|
353
92
|
|
|
354
|
-
|
|
355
|
-
const card = withDefaults(div, { class: 'card' });
|
|
356
|
-
const blueCard = withDefaults(card, { style: 'background: blue' });
|
|
93
|
+
Update elements in-place with `redraw()` for minimal DOM updates.
|
|
357
94
|
|
|
358
|
-
|
|
359
|
-
const
|
|
360
|
-
|
|
95
|
+
```typescript
|
|
96
|
+
const element = <div class="counter">{count}</div>;
|
|
97
|
+
element.redraw({ class: 'counter updated' }, [count.value + 1]);
|
|
361
98
|
```
|
|
362
99
|
|
|
363
|
-
## Router
|
|
100
|
+
## Router (@ktjs/router)
|
|
364
101
|
|
|
365
|
-
|
|
102
|
+
Client-side hash-based routing with async navigation guards.
|
|
366
103
|
|
|
367
|
-
|
|
104
|
+
### Basic Routing
|
|
105
|
+
|
|
106
|
+
```typescript
|
|
368
107
|
import { createRouter } from '@ktjs/router';
|
|
369
|
-
import { div, h1 } from 'kt.js';
|
|
370
108
|
|
|
371
109
|
const router = createRouter({
|
|
372
110
|
routes: [
|
|
373
111
|
{
|
|
374
112
|
path: '/',
|
|
375
113
|
name: 'home',
|
|
376
|
-
|
|
377
|
-
// Render your page here
|
|
378
|
-
document.getElementById('app')!.innerHTML = '';
|
|
379
|
-
document.getElementById('app')!.appendChild(div({}, [h1({}, 'Home Page')]));
|
|
380
|
-
},
|
|
114
|
+
component: () => <HomePage />
|
|
381
115
|
},
|
|
382
116
|
{
|
|
383
117
|
path: '/user/:id',
|
|
384
118
|
name: 'user',
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
console.log('Entering user page');
|
|
388
|
-
document.getElementById('app')!.innerHTML = '';
|
|
389
|
-
document.getElementById('app')!.appendChild(div({}, [h1({}, `User ${to.params.id}`)]));
|
|
390
|
-
return true;
|
|
391
|
-
},
|
|
392
|
-
},
|
|
119
|
+
component: (to) => <UserPage id={to.params.id} />
|
|
120
|
+
}
|
|
393
121
|
],
|
|
394
122
|
beforeEach: async (to, from) => {
|
|
395
|
-
//
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
// Called after successful navigation
|
|
401
|
-
document.title = to.name || to.path;
|
|
402
|
-
},
|
|
403
|
-
onError: (error) => {
|
|
404
|
-
console.error('Router error:', error);
|
|
405
|
-
},
|
|
123
|
+
// Auth check
|
|
124
|
+
if (to.path === '/admin' && !isAdmin()) {
|
|
125
|
+
return '/login';
|
|
126
|
+
}
|
|
127
|
+
}
|
|
406
128
|
});
|
|
129
|
+
```
|
|
407
130
|
|
|
408
|
-
|
|
409
|
-
router.push('/user/123');
|
|
410
|
-
router.push('/user/456?page=2');
|
|
131
|
+
### Navigation
|
|
411
132
|
|
|
412
|
-
|
|
413
|
-
|
|
133
|
+
```typescript
|
|
134
|
+
// Programmatic navigation
|
|
135
|
+
router.push('/user/123');
|
|
136
|
+
router.push({ name: 'user', params: { id: '456' } });
|
|
414
137
|
|
|
415
|
-
//
|
|
416
|
-
console.log(router.current?.path, router.current?.params
|
|
138
|
+
// Access current route
|
|
139
|
+
console.log(router.current?.path, router.current?.params);
|
|
417
140
|
```
|
|
418
141
|
|
|
419
|
-
|
|
142
|
+
## Installation
|
|
420
143
|
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
- **Named Routes**: Navigate using route names instead of paths
|
|
425
|
-
- **Async Navigation Guards**:
|
|
426
|
-
- `beforeEach`: Global guard before navigation (async)
|
|
427
|
-
- `beforeEnter`: Per-route guard (can also be used for rendering, async)
|
|
428
|
-
- `afterEach`: Global hook after navigation
|
|
429
|
-
- All guards support Promise-based async operations
|
|
430
|
-
- Guards can return `false` to cancel, string/object to redirect
|
|
431
|
-
- `GuardLevel` for fine-grained control over guard execution
|
|
432
|
-
- **Error Handling**: `onError` and `onNotFound` callbacks
|
|
433
|
-
- **Optimized Performance**: Pre-flattened routes and efficient matching algorithm
|
|
434
|
-
- **Zero Dependencies**: Fully self-contained router implementation (does not require `@ktjs/core` for runtime, only for TypeScript types)
|
|
435
|
-
- **Pure Routing**: No rendering logic - you control the DOM
|
|
436
|
-
- **Automatic Initialization**: Router auto-initializes on creation (v0.14.7+)
|
|
437
|
-
|
|
438
|
-
## Browser Compatibility
|
|
144
|
+
```bash
|
|
145
|
+
# Full package (includes core)
|
|
146
|
+
pnpm add kt.js
|
|
439
147
|
|
|
440
|
-
|
|
148
|
+
# Individual packages
|
|
149
|
+
pnpm add @ktjs/core @ktjs/router
|
|
150
|
+
```
|
|
441
151
|
|
|
442
|
-
|
|
152
|
+
## TypeScript Configuration
|
|
443
153
|
|
|
444
|
-
For
|
|
154
|
+
For JSX/TSX support, configure your `tsconfig.json`:
|
|
445
155
|
|
|
446
|
-
```
|
|
447
|
-
|
|
448
|
-
|
|
156
|
+
```json
|
|
157
|
+
{
|
|
158
|
+
"compilerOptions": {
|
|
159
|
+
"jsx": "react-jsx",
|
|
160
|
+
"jsxImportSource": "@ktjs/core"
|
|
161
|
+
}
|
|
162
|
+
}
|
|
449
163
|
```
|
|
450
164
|
|
|
165
|
+
## Philosophy
|
|
166
|
+
|
|
167
|
+
- **Direct DOM Manipulation**: No virtual DOM, minimal abstraction
|
|
168
|
+
- **Zero Re-renders**: Update only what changes, avoid full component re-renders
|
|
169
|
+
- **Type Safety**: Full TypeScript support with accurate type inference
|
|
170
|
+
- **Lightweight**: Small bundle size, no unnecessary dependencies
|
|
171
|
+
|
|
451
172
|
## License
|
|
452
173
|
|
|
453
174
|
MIT
|
package/dist/jsx-runtime.mjs
CHANGED
|
@@ -9,6 +9,7 @@ const $throw = (message) => {
|
|
|
9
9
|
|
|
10
10
|
// DOM manipulation utilities
|
|
11
11
|
// # dom natives
|
|
12
|
+
const $replaceWith = Element.prototype.replaceWith;
|
|
12
13
|
/**
|
|
13
14
|
* & Remove `bind` because it is shockingly slower than wrapper
|
|
14
15
|
* & `window.document` is safe because it is not configurable and its setter is undefined
|
|
@@ -48,7 +49,7 @@ const { get: $buttonDisabledGetter, set: $buttonDisabledSetter } = Object.getOwn
|
|
|
48
49
|
|
|
49
50
|
// Shared utilities and cached native methods for kt.js framework
|
|
50
51
|
// Re-export all utilities
|
|
51
|
-
Object.defineProperty(window, '@ktjs/shared', { value: '0.20.
|
|
52
|
+
Object.defineProperty(window, '@ktjs/shared', { value: '0.20.2' });
|
|
52
53
|
|
|
53
54
|
const booleanHandler = (element, key, value) => {
|
|
54
55
|
if (key in element) {
|
|
@@ -191,7 +192,6 @@ function applyContent(element, content) {
|
|
|
191
192
|
}
|
|
192
193
|
}
|
|
193
194
|
|
|
194
|
-
document.createElement('div');
|
|
195
195
|
const htmlCreator = (tag) => document.createElement(tag);
|
|
196
196
|
const svgCreator = (tag) => document.createElementNS('http://www.w3.org/2000/svg', tag);
|
|
197
197
|
const mathMLCreator = (tag) => document.createElementNS('http://www.w3.org/1998/Math/MathML', tag);
|
|
@@ -209,7 +209,7 @@ const MATHML_ATTR_FLAG = '__kt_mathml__';
|
|
|
209
209
|
* ## About
|
|
210
210
|
* @package @ktjs/core
|
|
211
211
|
* @author Kasukabe Tsumugi <futami16237@gmail.com>
|
|
212
|
-
* @version 0.
|
|
212
|
+
* @version 0.21.1 (Last Update: 2026.02.02 09:20:07.959)
|
|
213
213
|
* @license MIT
|
|
214
214
|
* @link https://github.com/baendlorel/kt.js
|
|
215
215
|
* @link https://baendlorel.github.io/ Welcome to my site!
|
|
@@ -238,14 +238,6 @@ const h = (tag, attr, content) => {
|
|
|
238
238
|
// * Handle content
|
|
239
239
|
applyAttr(element, attr);
|
|
240
240
|
applyContent(element, content);
|
|
241
|
-
// if (tag === 'svg') {
|
|
242
|
-
// tempWrapper.innerHTML = element.outerHTML;
|
|
243
|
-
// return tempWrapper.firstChild as HTML<T>;
|
|
244
|
-
// }
|
|
245
|
-
// if (tag === 'math') {
|
|
246
|
-
// tempWrapper.innerHTML = element.outerHTML;
|
|
247
|
-
// return tempWrapper.firstChild as HTML<T>;
|
|
248
|
-
// }
|
|
249
241
|
return element;
|
|
250
242
|
};
|
|
251
243
|
|
|
@@ -254,7 +246,13 @@ class KTRef {
|
|
|
254
246
|
* Indicates that this is a KTRef instance
|
|
255
247
|
*/
|
|
256
248
|
isKT = true;
|
|
249
|
+
/**
|
|
250
|
+
* @internal
|
|
251
|
+
*/
|
|
257
252
|
_value;
|
|
253
|
+
/**
|
|
254
|
+
* @internal
|
|
255
|
+
*/
|
|
258
256
|
_onChanges;
|
|
259
257
|
constructor(_value, _onChanges) {
|
|
260
258
|
this._value = _value;
|
|
@@ -296,8 +294,12 @@ class KTRef {
|
|
|
296
294
|
return false;
|
|
297
295
|
}
|
|
298
296
|
}
|
|
297
|
+
const isKTRef = (obj) => {
|
|
298
|
+
return typeof obj === 'object' && obj !== null && obj.isKT === true;
|
|
299
|
+
};
|
|
299
300
|
/**
|
|
300
301
|
* Reference to the created HTML element.
|
|
302
|
+
* - **Only** respond to `ref.value` changes, not reactive to internal changes of the element.
|
|
301
303
|
* - can alse be used to store normal values, but it is not reactive.
|
|
302
304
|
* @param value mostly an HTMLElement
|
|
303
305
|
*/
|
|
@@ -306,27 +308,47 @@ function ref(value, onChange) {
|
|
|
306
308
|
}
|
|
307
309
|
|
|
308
310
|
const dummyRef = { value: null };
|
|
311
|
+
const create = (tag, props) => {
|
|
312
|
+
if (typeof tag === 'function') {
|
|
313
|
+
return tag(props);
|
|
314
|
+
}
|
|
315
|
+
else {
|
|
316
|
+
return h(tag, props, props.children);
|
|
317
|
+
}
|
|
318
|
+
};
|
|
319
|
+
const placeholder = () => document.createComment('k-if');
|
|
309
320
|
/**
|
|
310
321
|
* @param tag html tag or function component
|
|
311
322
|
* @param props properties/attributes
|
|
312
323
|
*/
|
|
313
324
|
function jsx(tag, props) {
|
|
314
|
-
const
|
|
325
|
+
const maybeDummyRef = isKTRef(props.ref) ? props.ref : dummyRef;
|
|
315
326
|
let el;
|
|
316
|
-
if ('k-if' in props
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
327
|
+
if ('k-if' in props) {
|
|
328
|
+
const kif = props['k-if'];
|
|
329
|
+
let condition = kif; // assume boolean by default
|
|
330
|
+
// Handle reactive k-if
|
|
331
|
+
if (isKTRef(kif)) {
|
|
332
|
+
kif.addOnChange((newValue, oldValue) => {
|
|
333
|
+
if (newValue === oldValue) {
|
|
334
|
+
return;
|
|
335
|
+
}
|
|
336
|
+
const oldEl = el;
|
|
337
|
+
el = newValue ? create(tag, props) : placeholder();
|
|
338
|
+
$replaceWith.call(oldEl, el);
|
|
339
|
+
maybeDummyRef.value = el;
|
|
340
|
+
});
|
|
341
|
+
condition = kif.value;
|
|
342
|
+
}
|
|
343
|
+
if (!condition) {
|
|
344
|
+
// & make comment placeholder in case that ref might be redrawn later
|
|
345
|
+
el = placeholder();
|
|
346
|
+
maybeDummyRef.value = el;
|
|
347
|
+
return el;
|
|
348
|
+
}
|
|
328
349
|
}
|
|
329
|
-
|
|
350
|
+
el = create(tag, props);
|
|
351
|
+
maybeDummyRef.value = el;
|
|
330
352
|
return el;
|
|
331
353
|
}
|
|
332
354
|
/**
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "kt.js",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.21.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.21.1"
|
|
45
45
|
},
|
|
46
46
|
"scripts": {
|
|
47
47
|
"build": "rollup -c rollup.config.mjs",
|