@ktjs/core 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 CHANGED
@@ -1,453 +1 @@
1
- <img src="https://raw.githubusercontent.com/baendlorel/kt.js/dev/.assets/ktjs-0.0.1.svg" alt="KT.js Logo" width="150"/>
2
-
3
- [![npm version](https://img.shields.io/npm/v/kt.js.svg)](https://www.npmjs.com/package/kt.js) [![license](https://img.shields.io/github/license/baendlorel/kt.js.svg)](https://github.com/baendlorel/kt.js/blob/main/LICENSE)
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.
10
-
11
- > 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
-
13
- KT.js is a tiny DOM utility focused on direct DOM manipulation. It favors not forcing re-renders and aims to keep DOM updates to the absolute minimum for maximum performance.
14
-
15
- For more awesome packages, check out [my homepage💛](https://baendlorel.github.io/?repoType=npm)
16
-
17
- ## Architecture
18
-
19
- KT.js is now a **monorepo** containing multiple packages:
20
-
21
- - **[kt.js](./packages/kt.js)**: Main entry package that re-exports all functionality
22
- - **[@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
- - **[@ktjs/router](./packages/router)**: Client-side routing with navigation guards (not included in kt.js package)
24
-
25
- You can install the full package or individual packages as needed:
26
-
27
- ```bash
28
- pnpm add kt.js
29
-
30
- # Or install individual packages
31
- pnpm add @ktjs/core # Core DOM utilities (independent)
32
- pnpm add @ktjs/router # Client-side router (requires @ktjs/core)
33
- pnpm add @ktjs/mui # Material UI components (requires @ktjs/core)
34
- pnpm add @ktjs/shortcuts # Shortcuts (requires @ktjs/core)
35
- ```
36
-
37
- ## Philosophy
38
-
39
- As a web framework, repeatedly creating a large number of variables and objects is unacceptable. So I created KT.js.
40
-
41
- KT.js follows one rule: **full control of DOM and avoid unnecessary repainting**.
42
-
43
- ## Key Features
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+):
206
-
207
- ```tsx
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
- }
223
-
224
- // Mix mapped elements with other elements
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:
241
-
242
- ```tsx
243
- import { KTAsync, ref } from 'kt.js';
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:**
272
-
273
- 1. Creates a placeholder comment node (`ktjs-suspense-placeholder`) immediately
274
- 2. Calls your component function (which should return a `Promise<HTMLElement>` or `HTMLElement`)
275
- 3. When the Promise resolves, automatically replaces the placeholder with the resolved element
276
- 4. If your component returns a non-Promise value, it's used directly without async handling
277
-
278
- **Example with dynamic updates:**
279
-
280
- ```tsx
281
- const DynamicContent = function () {
282
- const count = ref(0);
283
- const container = (
284
- <div>
285
- <p>Count: {count}</p>
286
- <button on:click={() => count.value++}>Increment</button>
287
- </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
- </div>
302
- );
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
-
307
- ```ts
308
- const button = btn(
309
- {
310
- dblclick: '22',
311
- 'on:dblclick': function trueHandler() {
312
- /* ... */
313
- },
314
- },
315
- 'Click me',
316
- );
317
-
318
- // This is equivalent to:
319
- const button = btn(undefined, 'Click me');
320
- button.setAttribute('dblclick', '22');
321
- button.addEventListener('click', () => alert('Clicked!'));
322
- button.addEventListener('dblclick', function trueHandler() {
323
- /* ... */
324
- });
325
- ```
326
-
327
- ### Working with CSS-in-JS Libraries
328
-
329
- KT.js works seamlessly with CSS-in-JS libraries like `@emotion/css`:
330
-
331
- ```ts
332
- import { css } from '@emotion/css';
333
- import { h, div } from 'kt.js';
334
-
335
- const className = css`
336
- color: red;
337
- font-size: 20px;
338
- `;
339
-
340
- // Pass class name as attribute
341
- h('div', { class: className }, 'Styled text');
342
-
343
- // Or as the first string argument
344
- div(className, 'Styled text');
345
- ```
346
-
347
- ### Using Shortcuts with Default Values
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';
353
-
354
- // Create a styled div factory
355
- const card = withDefaults(div, { class: 'card' });
356
- const blueCard = withDefaults(card, { style: 'background: blue' });
357
-
358
- // Use them
359
- const myCard = card('card-body', 'Content'); // <div class="card"><div class="card-body">Content</div></div>
360
- const myBlueCard = blueCard('title', 'Blue!'); // <div class="card" style="background: blue"><div class="title">Blue!</div></div>
361
- ```
362
-
363
- ## Router
364
-
365
- The router is available as a separate package `@ktjs/router`:
366
-
367
- ```ts
368
- import { createRouter } from '@ktjs/router';
369
- import { div, h1 } from 'kt.js';
370
-
371
- const router = createRouter({
372
- routes: [
373
- {
374
- path: '/',
375
- name: 'home',
376
- beforeEnter: (to) => {
377
- // Render your page here
378
- document.getElementById('app')!.innerHTML = '';
379
- document.getElementById('app')!.appendChild(div({}, [h1({}, 'Home Page')]));
380
- },
381
- },
382
- {
383
- path: '/user/:id',
384
- name: 'user',
385
- beforeEnter: (to) => {
386
- // Route-specific guard and rendering
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
- },
393
- ],
394
- beforeEach: async (to, from) => {
395
- // Global navigation guard - return false to block navigation
396
- console.log('Navigating to:', to.path);
397
- return true;
398
- },
399
- afterEach: (to) => {
400
- // Called after successful navigation
401
- document.title = to.name || to.path;
402
- },
403
- onError: (error) => {
404
- console.error('Router error:', error);
405
- },
406
- });
407
-
408
- // Navigate programmatically
409
- router.push('/user/123');
410
- router.push('/user/456?page=2');
411
-
412
- // Navigate by route name
413
- router.push({ name: 'user', params: { id: '789' } });
414
-
415
- // Get current route
416
- console.log(router.current?.path, router.current?.params, router.current?.query);
417
- ```
418
-
419
- ### Router Features
420
-
421
- - **Hash-based Routing Only** (v0.14.7+): Uses URL hash for client-side navigation (`#/path`)
422
- - **Dynamic Parameters**: Support for dynamic route segments (`/user/:id`)
423
- - **Query Strings**: Automatic parsing of query parameters (`?key=value`)
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
439
-
440
- KT.js is transpiled to ES5 and works in all modern browsers as well as legacy browsers including IE9+.
441
-
442
- ### Promise Polyfill
443
-
444
- For environments without native `Promise` support (like IE).
445
-
446
- ```js
447
- import 'some promise polyfill'; // Will fallback to sync version if Promise is not available
448
- import { h, div, createRouter } from 'kt.js';
449
- ```
450
-
451
- ## License
452
-
453
- MIT
1
+ [See Readme](../../README.md)
package/dist/index.d.ts CHANGED
@@ -13,8 +13,6 @@ declare class KTRef<T> {
13
13
  * Indicates that this is a KTRef instance
14
14
  */
15
15
  isKT: boolean;
16
- private _value;
17
- private _onChanges;
18
16
  constructor(_value: T, _onChanges: Array<RefChangeHandler<T>>);
19
17
  /**
20
18
  * If new value and old value are both nodes, the old one will be replaced in the DOM
@@ -24,12 +22,35 @@ declare class KTRef<T> {
24
22
  addOnChange(callback: RefChangeHandler<T>): void;
25
23
  removeOnChange(callback: RefChangeHandler<T>): boolean;
26
24
  }
25
+ declare const isKTRef: <T = any>(obj: any) => obj is KTRef<T>;
27
26
  /**
28
27
  * Reference to the created HTML element.
28
+ * - **Only** respond to `ref.value` changes, not reactive to internal changes of the element.
29
29
  * - can alse be used to store normal values, but it is not reactive.
30
30
  * @param value mostly an HTMLElement
31
31
  */
32
32
  declare function ref<T = JSX.Element>(value?: T, onChange?: RefChangeHandler<T>): KTRef<T>;
33
+ type KTSurfaceRef<T extends Object> = {
34
+ [K in keyof T]: KTRef<T[K]>;
35
+ } & {
36
+ /**
37
+ * Get the dereferenced object like the original one
38
+ */
39
+ kcollect: () => T;
40
+ };
41
+ /**
42
+ * Make all first-level properties of the object a `KTRef`.
43
+ * - `obj.a.b` is not reactive
44
+ */
45
+ declare const surfaceRef: <T extends Object>(obj: T) => KTSurfaceRef<T>;
46
+
47
+ type HTML<T extends (HTMLTag | SVGTag | MathMLTag) & otherstring> = T extends SVGTag
48
+ ? SVGElementTagNameMap[T]
49
+ : T extends HTMLTag
50
+ ? HTMLElementTagNameMap[T]
51
+ : T extends MathMLTag
52
+ ? MathMLElementTagNameMap[T]
53
+ : HTMLElement;
33
54
 
34
55
  type SingleContent = KTRef<any> | HTMLElement | Element | Node | string | number | boolean | null | undefined;
35
56
  type KTAvailableContent = SingleContent | KTAvailableContent[];
@@ -50,7 +71,14 @@ interface KTBaseAttribute {
50
71
 
51
72
  // # kt-specific attributes
52
73
  ref?: KTRef<JSX.Element>;
74
+
75
+ // todo 是否要让k-if是KTRef的时候具备响应能力?
76
+ /**
77
+ * If a `KTRef` is bound, it will be reactive; otherwise, it will be static.
78
+ */
53
79
  'k-if'?: any;
80
+ // todo k-model如何指定value还是checked还是别的什么?
81
+ 'k-model'?: KTRef<any>;
54
82
 
55
83
  // # normal HTML attributes
56
84
  id?: string;
@@ -119,7 +147,6 @@ type KTComponent = (
119
147
  any,
120
148
  ) => JSX.Element | Promise<JSX.Element> | any;
121
149
 
122
- type HTML<T extends (HTMLTag | SVGTag | MathMLTag) & otherstring> = T extends SVGTag ? SVGElementTagNameMap[T] : T extends HTMLTag ? HTMLElementTagNameMap[T] : T extends MathMLTag ? MathMLElementTagNameMap[T] : HTMLElement;
123
150
  /**
124
151
  * Create an enhanced HTMLElement.
125
152
  * - Only supports HTMLElements, **NOT** SVGElements or other Elements.
@@ -130,7 +157,7 @@ type HTML<T extends (HTMLTag | SVGTag | MathMLTag) & otherstring> = T extends SV
130
157
  * ## About
131
158
  * @package @ktjs/core
132
159
  * @author Kasukabe Tsumugi <futami16237@gmail.com>
133
- * @version 0.20.2 (Last Update: 2026.02.01 18:34:51.151)
160
+ * @version 0.21.1 (Last Update: 2026.02.02 09:20:07.959)
134
161
  * @license MIT
135
162
  * @link https://github.com/baendlorel/kt.js
136
163
  * @link https://baendlorel.github.io/ Welcome to my site!
@@ -1435,5 +1462,5 @@ interface KTForProps<T> {
1435
1462
  */
1436
1463
  declare function KTFor<T>(props: KTForProps<T>): KTForElement;
1437
1464
 
1438
- export { Fragment, KTAsync, KTFor, KTRef, h as createElement, createRedrawable, h, jsx, jsxDEV, jsxs, ref };
1439
- export type { EventHandler, HTMLTag, KTAttribute, KTForElement, KTForProps, KTRawAttr, KTRawContent, KTRawContents };
1465
+ export { Fragment, KTAsync, KTFor, KTRef, h as createElement, createRedrawable, h, isKTRef, jsx, jsxDEV, jsxs, ref, surfaceRef };
1466
+ export type { EventHandler, HTMLTag, KTAttribute, KTForElement, KTForProps, KTRawAttr, KTRawContent, KTRawContents, KTSurfaceRef };