@ktjs/core 0.16.0 → 0.17.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/README.md +11 -18
- package/dist/index.d.ts +19 -58
- package/dist/index.iife.js +88 -181
- package/dist/index.legacy.js +91 -181
- package/dist/index.mjs +89 -181
- package/dist/jsx/index.d.ts +8 -5
- package/dist/jsx/index.mjs +6 -1
- package/dist/jsx/jsx-runtime.d.ts +7 -4
- package/dist/jsx/jsx-runtime.mjs +6 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -29,11 +29,10 @@ Core DOM manipulation utilities for KT.js framework with built-in JSX/TSX suppor
|
|
|
29
29
|
- **k-if directive**: Conditional element creation with `k-if` attribute
|
|
30
30
|
- Array children support for seamless list rendering
|
|
31
31
|
- **KTFor Component**: Efficient list rendering with key-based optimization (v0.16.0)
|
|
32
|
-
-
|
|
33
|
-
-
|
|
34
|
-
-
|
|
32
|
+
- Returns Comment anchor with `__kt_for_list__` array storing rendered elements
|
|
33
|
+
- Auto-appends list items when anchor is added via `applyContent`
|
|
34
|
+
- Key-based DOM reuse with customizable key function (default: identity)
|
|
35
35
|
- Type-safe with full TypeScript generics support
|
|
36
|
-
- `KTForStatic` variant for simple lists without key optimization
|
|
37
36
|
- **KTAsync Component**: Handle async components with ease
|
|
38
37
|
- Automatic handling of Promise-based components
|
|
39
38
|
- Seamless integration with JSX/TSX
|
|
@@ -211,7 +210,7 @@ div.redraw(undefined, 'New content');
|
|
|
211
210
|
The `KTFor` component provides efficient list rendering with key-based DOM reuse:
|
|
212
211
|
|
|
213
212
|
```tsx
|
|
214
|
-
import { KTFor
|
|
213
|
+
import { KTFor } from '@ktjs/core';
|
|
215
214
|
|
|
216
215
|
interface Todo {
|
|
217
216
|
id: number;
|
|
@@ -228,8 +227,8 @@ let todos: Todo[] = [
|
|
|
228
227
|
const todoList = (
|
|
229
228
|
<KTFor
|
|
230
229
|
list={todos}
|
|
231
|
-
key={(item) => item.id} //
|
|
232
|
-
|
|
230
|
+
key={(item) => item.id} // Optional, defaults to identity function
|
|
231
|
+
map={(item, index) => (
|
|
233
232
|
<div class={`todo ${item.done ? 'done' : ''}`}>
|
|
234
233
|
<input type="checkbox" checked={item.done} />
|
|
235
234
|
<span>{item.text}</span>
|
|
@@ -237,28 +236,22 @@ const todoList = (
|
|
|
237
236
|
</div>
|
|
238
237
|
)}
|
|
239
238
|
/>
|
|
240
|
-
)
|
|
239
|
+
);
|
|
241
240
|
|
|
242
|
-
// Add to DOM
|
|
241
|
+
// Add to DOM - anchor comment + __kt_for_list__ items are appended
|
|
243
242
|
document.body.appendChild(todoList);
|
|
244
243
|
|
|
245
244
|
// Update the list - only changed items are updated
|
|
246
245
|
todos = [...todos, { id: 3, text: 'New task', done: false }];
|
|
247
246
|
todoList.redraw({ list: todos });
|
|
248
|
-
|
|
249
|
-
// For simple static lists, use KTForStatic (no key optimization)
|
|
250
|
-
const simpleList = (
|
|
251
|
-
<KTForStatic list={['Apple', 'Banana', 'Orange']} mapper={(item) => <div>{item}</div>} />
|
|
252
|
-
) as KTForAnchor;
|
|
253
247
|
```
|
|
254
248
|
|
|
255
249
|
**How it works:**
|
|
256
250
|
|
|
257
|
-
- Returns
|
|
258
|
-
-
|
|
259
|
-
- Uses key-based diff
|
|
251
|
+
- Returns Comment anchor (`<!-- kt-for -->`) with `__kt_for_list__` array property
|
|
252
|
+
- When appended via `applyContent`, anchor and all list items are added to DOM
|
|
253
|
+
- Uses key-based diff to reuse DOM nodes on `redraw()`
|
|
260
254
|
- Only adds/removes/moves nodes that changed
|
|
261
|
-
- Similar to Svelte's `{#each}` blocks
|
|
262
255
|
|
|
263
256
|
````
|
|
264
257
|
|
package/dist/index.d.ts
CHANGED
|
@@ -17,12 +17,13 @@ interface KTRef<T> {
|
|
|
17
17
|
*/
|
|
18
18
|
declare function ref<T = HTMLElement>(value?: T): KTRef<T>;
|
|
19
19
|
|
|
20
|
-
type
|
|
20
|
+
type Redraw = (props?: KTAttribute, ...args: any[]) => KTHTMLElement;
|
|
21
|
+
type KTHTMLElement<El extends HTMLElement = HTMLElement, R extends Function = Redraw> = El & {
|
|
21
22
|
/**
|
|
22
23
|
* Automically generate a redraw function if it is not provided
|
|
23
24
|
* @param props
|
|
24
25
|
*/
|
|
25
|
-
redraw:
|
|
26
|
+
redraw: R;
|
|
26
27
|
};
|
|
27
28
|
|
|
28
29
|
declare global {
|
|
@@ -44,7 +45,7 @@ declare global {
|
|
|
44
45
|
}
|
|
45
46
|
}
|
|
46
47
|
|
|
47
|
-
type SingleContent = KTRef<any> | HTMLElement | Element | string | number | boolean | null | undefined;
|
|
48
|
+
type SingleContent = KTRef<any> | HTMLElement | Element | Node | string | number | boolean | null | undefined;
|
|
48
49
|
type KTAvailableContent = SingleContent | KTAvailableContent[];
|
|
49
50
|
type KTRawContent = KTAvailableContent | Promise<KTAvailableContent>;
|
|
50
51
|
type KTRawAttr = KTAttribute | string;
|
|
@@ -61,9 +62,11 @@ type EventHandler<T extends Event = Event> = (this: HTMLElement, ev: T) => any;
|
|
|
61
62
|
interface KTBaseAttribute {
|
|
62
63
|
[k: string]: any;
|
|
63
64
|
|
|
65
|
+
// # kt-specific attributes
|
|
64
66
|
ref?: KTRef<HTMLElement>;
|
|
65
67
|
'k-if'?: any;
|
|
66
68
|
|
|
69
|
+
// # normal HTML attributes
|
|
67
70
|
id?: string;
|
|
68
71
|
class?: string;
|
|
69
72
|
style?: string | Partial<CSSStyleDeclaration>;
|
|
@@ -168,7 +171,7 @@ type HTML<T extends (HTMLTag | SVGTag) & otherstring> = T extends SVGTag ? SVGEl
|
|
|
168
171
|
* ## About
|
|
169
172
|
* @package @ktjs/core
|
|
170
173
|
* @author Kasukabe Tsumugi <futami16237@gmail.com>
|
|
171
|
-
* @version 0.
|
|
174
|
+
* @version 0.17.0 (Last Update: 2026.01.28 15:11:59.247)
|
|
172
175
|
* @license MIT
|
|
173
176
|
* @link https://github.com/baendlorel/kt.js
|
|
174
177
|
* @link https://baendlorel.github.io/ Welcome to my site!
|
|
@@ -242,63 +245,21 @@ declare function KTAsync<T extends KTComponent>(props: {
|
|
|
242
245
|
children?: KTRawContent;
|
|
243
246
|
} & ExtractComponentProps<T>): KTHTMLElement;
|
|
244
247
|
|
|
248
|
+
type KForElement = KTHTMLElement & {
|
|
249
|
+
__kt_for_list__: HTMLElement[];
|
|
250
|
+
redraw: (newProps?: KTAttribute) => void;
|
|
251
|
+
};
|
|
245
252
|
interface KTForProps<T> {
|
|
253
|
+
ref?: KTRef<KForElement>;
|
|
246
254
|
list: T[];
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
* Using stable keys enables efficient DOM reuse and updates
|
|
250
|
-
*/
|
|
251
|
-
key: (item: T, index: number) => string | number;
|
|
252
|
-
/**
|
|
253
|
-
* Mapper function to render each item
|
|
254
|
-
*/
|
|
255
|
-
mapper: (item: T, index: number) => HTMLElement;
|
|
256
|
-
}
|
|
257
|
-
/**
|
|
258
|
-
* Extended Comment node with redraw capability for KTFor
|
|
259
|
-
*/
|
|
260
|
-
interface KTForAnchor extends Comment {
|
|
261
|
-
redraw: (newProps?: {
|
|
262
|
-
list?: any[];
|
|
263
|
-
mapper?: (item: any, index: number) => HTMLElement;
|
|
264
|
-
}) => void;
|
|
255
|
+
key?: (item: T, index: number, array: T[]) => any;
|
|
256
|
+
map: (item: T, index: number, array: T[]) => HTMLElement;
|
|
265
257
|
}
|
|
266
258
|
/**
|
|
267
|
-
* KTFor
|
|
268
|
-
*
|
|
269
|
-
*
|
|
270
|
-
* Returns a Comment node as anchor point. All list items are rendered after this anchor.
|
|
271
|
-
*
|
|
272
|
-
* @example
|
|
273
|
-
* ```tsx
|
|
274
|
-
* const listAnchor = <KTFor
|
|
275
|
-
* list={items}
|
|
276
|
-
* key={(item) => item.id}
|
|
277
|
-
* mapper={(item) => <div>{item.name}</div>}
|
|
278
|
-
* /> as KTForAnchor;
|
|
279
|
-
*
|
|
280
|
-
* document.body.appendChild(listAnchor); // Anchor + all items are rendered
|
|
281
|
-
*
|
|
282
|
-
* // Update the list
|
|
283
|
-
* listAnchor.redraw({ list: newItems });
|
|
284
|
-
* ```
|
|
285
|
-
*/
|
|
286
|
-
declare function KTFor<T>(props: KTForProps<T>): KTForAnchor;
|
|
287
|
-
/**
|
|
288
|
-
* Simple list rendering without key optimization
|
|
289
|
-
* Rebuilds the entire list on each update - use for small static lists
|
|
290
|
-
*
|
|
291
|
-
* Returns a Comment anchor node. All items are rendered after this anchor.
|
|
292
|
-
*
|
|
293
|
-
* @example
|
|
294
|
-
* ```tsx
|
|
295
|
-
* const listAnchor = <KTForStatic
|
|
296
|
-
* list={items}
|
|
297
|
-
* mapper={(item) => <div>{item}</div>}
|
|
298
|
-
* /> as KTForAnchor;
|
|
299
|
-
* ```
|
|
259
|
+
* KTFor - List rendering component with key-based optimization
|
|
260
|
+
* Returns a Comment anchor node with rendered elements in __kt_for_list__
|
|
300
261
|
*/
|
|
301
|
-
declare function
|
|
262
|
+
declare function KTFor<T>(props: KTForProps<T>): KForElement;
|
|
302
263
|
|
|
303
|
-
export { Fragment, KTAsync, KTFor,
|
|
304
|
-
export type { EventHandler, HTMLTag, KTAttribute,
|
|
264
|
+
export { Fragment, KTAsync, KTFor, h as createElement, createRedrawable, createRedrawableNoref, h, jsx, jsxDEV, jsxs, ref };
|
|
265
|
+
export type { EventHandler, HTMLTag, KTAttribute, KTForProps, KTHTMLElement, KTRawAttr, KTRawContent, KTRawContents, KTRef, Redraw };
|
package/dist/index.iife.js
CHANGED
|
@@ -158,6 +158,11 @@ var __ktjs_core__ = (function (exports) {
|
|
|
158
158
|
}
|
|
159
159
|
else {
|
|
160
160
|
$append.call(element, c);
|
|
161
|
+
// Handle KTFor anchor
|
|
162
|
+
const list = c.__kt_for_list__;
|
|
163
|
+
if ($isArray(list)) {
|
|
164
|
+
apd(element, list);
|
|
165
|
+
}
|
|
161
166
|
}
|
|
162
167
|
}
|
|
163
168
|
function apd(element, c) {
|
|
@@ -206,7 +211,7 @@ var __ktjs_core__ = (function (exports) {
|
|
|
206
211
|
* ## About
|
|
207
212
|
* @package @ktjs/core
|
|
208
213
|
* @author Kasukabe Tsumugi <futami16237@gmail.com>
|
|
209
|
-
* @version 0.
|
|
214
|
+
* @version 0.17.0 (Last Update: 2026.01.28 15:11:59.247)
|
|
210
215
|
* @license MIT
|
|
211
216
|
* @link https://github.com/baendlorel/kt.js
|
|
212
217
|
* @link https://baendlorel.github.io/ Welcome to my site!
|
|
@@ -379,205 +384,107 @@ var __ktjs_core__ = (function (exports) {
|
|
|
379
384
|
}
|
|
380
385
|
|
|
381
386
|
/**
|
|
382
|
-
* KTFor
|
|
383
|
-
*
|
|
384
|
-
*
|
|
385
|
-
* Returns a Comment node as anchor point. All list items are rendered after this anchor.
|
|
386
|
-
*
|
|
387
|
-
* @example
|
|
388
|
-
* ```tsx
|
|
389
|
-
* const listAnchor = <KTFor
|
|
390
|
-
* list={items}
|
|
391
|
-
* key={(item) => item.id}
|
|
392
|
-
* mapper={(item) => <div>{item.name}</div>}
|
|
393
|
-
* /> as KTForAnchor;
|
|
394
|
-
*
|
|
395
|
-
* document.body.appendChild(listAnchor); // Anchor + all items are rendered
|
|
396
|
-
*
|
|
397
|
-
* // Update the list
|
|
398
|
-
* listAnchor.redraw({ list: newItems });
|
|
399
|
-
* ```
|
|
387
|
+
* KTFor - List rendering component with key-based optimization
|
|
388
|
+
* Returns a Comment anchor node with rendered elements in __kt_for_list__
|
|
400
389
|
*/
|
|
401
390
|
function KTFor(props) {
|
|
402
|
-
const { list
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
// Current key order
|
|
406
|
-
let currentKeys = [];
|
|
407
|
-
// Create anchor comment node - marks the position of the list
|
|
391
|
+
const { list, map } = props;
|
|
392
|
+
const key = props.key ?? ((item) => item);
|
|
393
|
+
// Create anchor comment node
|
|
408
394
|
const anchor = document.createComment('kt-for');
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
const
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
else {
|
|
446
|
-
anchor.after(node);
|
|
447
|
-
}
|
|
448
|
-
}
|
|
449
|
-
}
|
|
450
|
-
};
|
|
451
|
-
/**
|
|
452
|
-
* Smart update method - only modifies DOM nodes that changed
|
|
453
|
-
* Uses key-based diff algorithm to minimize DOM operations
|
|
454
|
-
*/
|
|
455
|
-
const smartUpdate = (newList, newMapper) => {
|
|
456
|
-
const mapperFn = newMapper ?? mapper;
|
|
457
|
-
const newKeys = newList.map((item, index) => getKey(item, index));
|
|
458
|
-
if (!anchor.parentNode) {
|
|
459
|
-
// If anchor is not in DOM yet, just update cache
|
|
460
|
-
nodeCache.clear();
|
|
461
|
-
currentKeys = [];
|
|
462
|
-
for (let i = 0; i < newList.length; i++) {
|
|
463
|
-
const item = newList[i];
|
|
464
|
-
const key = newKeys[i];
|
|
465
|
-
const node = mapperFn(item, i);
|
|
466
|
-
nodeCache.set(key, { node, item });
|
|
467
|
-
currentKeys.push(key);
|
|
468
|
-
}
|
|
469
|
-
return;
|
|
470
|
-
}
|
|
471
|
-
// Find keys to remove (exist in current but not in new)
|
|
472
|
-
const keysToRemove = currentKeys.filter((k) => !newKeys.includes(k));
|
|
473
|
-
for (let i = 0; i < keysToRemove.length; i++) {
|
|
474
|
-
const key = keysToRemove[i];
|
|
475
|
-
const cached = nodeCache.get(key);
|
|
476
|
-
if (cached && cached.node.parentNode) {
|
|
477
|
-
cached.node.parentNode.removeChild(cached.node);
|
|
395
|
+
// Store current state
|
|
396
|
+
let currentList = list;
|
|
397
|
+
let currentKey = key;
|
|
398
|
+
let currentMap = map;
|
|
399
|
+
// Map to track rendered nodes by key
|
|
400
|
+
const nodeMap = new Map();
|
|
401
|
+
// Render initial list
|
|
402
|
+
const elements = [];
|
|
403
|
+
for (let index = 0; index < currentList.length; index++) {
|
|
404
|
+
const item = currentList[index];
|
|
405
|
+
const itemKey = currentKey(item, index, currentList);
|
|
406
|
+
const node = currentMap(item, index, currentList);
|
|
407
|
+
nodeMap.set(itemKey, node);
|
|
408
|
+
elements.push(node);
|
|
409
|
+
}
|
|
410
|
+
// Attach elements array to anchor
|
|
411
|
+
anchor.__kt_for_list__ = elements;
|
|
412
|
+
// Redraw function for updates
|
|
413
|
+
anchor.redraw = (newProps = props) => {
|
|
414
|
+
const newList = (newProps.list ?? currentList);
|
|
415
|
+
const newKey = (newProps.key ?? currentKey);
|
|
416
|
+
const newMap = (newProps.map ?? currentMap);
|
|
417
|
+
// Update stored values
|
|
418
|
+
currentList = newList;
|
|
419
|
+
currentKey = newKey;
|
|
420
|
+
currentMap = newMap;
|
|
421
|
+
const parent = anchor.parentNode;
|
|
422
|
+
if (!parent) {
|
|
423
|
+
// If not in DOM yet, just rebuild the list
|
|
424
|
+
const newElements = [];
|
|
425
|
+
for (let index = 0; index < currentList.length; index++) {
|
|
426
|
+
const item = currentList[index];
|
|
427
|
+
const itemKey = currentKey(item, index, currentList);
|
|
428
|
+
const node = currentMap(item, index, currentList);
|
|
429
|
+
nodeMap.set(itemKey, node);
|
|
430
|
+
newElements.push(node);
|
|
478
431
|
}
|
|
479
|
-
|
|
432
|
+
anchor.__kt_for_list__ = newElements;
|
|
433
|
+
return anchor;
|
|
480
434
|
}
|
|
481
|
-
//
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
435
|
+
// Build new key map
|
|
436
|
+
const newNodeMap = new Map();
|
|
437
|
+
const newKeys = new Set();
|
|
438
|
+
const newElements = [];
|
|
439
|
+
for (let index = 0; index < newList.length; index++) {
|
|
440
|
+
const item = newList[index];
|
|
441
|
+
const itemKey = newKey(item, index, newList);
|
|
442
|
+
newKeys.add(itemKey);
|
|
443
|
+
// Reuse existing node if key exists
|
|
444
|
+
if (nodeMap.has(itemKey)) {
|
|
445
|
+
const existingNode = nodeMap.get(itemKey);
|
|
446
|
+
newNodeMap.set(itemKey, existingNode);
|
|
447
|
+
newElements.push(existingNode);
|
|
492
448
|
}
|
|
493
449
|
else {
|
|
494
|
-
//
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
if (previousNode.nextSibling !== cached.node) {
|
|
499
|
-
if (previousNode.nextSibling) {
|
|
500
|
-
anchor.parentNode.insertBefore(cached.node, previousNode.nextSibling);
|
|
501
|
-
}
|
|
502
|
-
else {
|
|
503
|
-
anchor.parentNode.appendChild(cached.node);
|
|
504
|
-
}
|
|
450
|
+
// Create new node
|
|
451
|
+
const node = newMap(item, index, newList);
|
|
452
|
+
newNodeMap.set(itemKey, node);
|
|
453
|
+
newElements.push(node);
|
|
505
454
|
}
|
|
506
|
-
previousNode = cached.node;
|
|
507
|
-
}
|
|
508
|
-
currentKeys = newKeys;
|
|
509
|
-
};
|
|
510
|
-
// Mount redraw method on anchor
|
|
511
|
-
anchor.redraw = (newProps) => {
|
|
512
|
-
if (newProps?.list) {
|
|
513
|
-
smartUpdate(newProps.list, newProps.mapper);
|
|
514
455
|
}
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
}
|
|
520
|
-
/**
|
|
521
|
-
* Simple list rendering without key optimization
|
|
522
|
-
* Rebuilds the entire list on each update - use for small static lists
|
|
523
|
-
*
|
|
524
|
-
* Returns a Comment anchor node. All items are rendered after this anchor.
|
|
525
|
-
*
|
|
526
|
-
* @example
|
|
527
|
-
* ```tsx
|
|
528
|
-
* const listAnchor = <KTForStatic
|
|
529
|
-
* list={items}
|
|
530
|
-
* mapper={(item) => <div>{item}</div>}
|
|
531
|
-
* /> as KTForAnchor;
|
|
532
|
-
* ```
|
|
533
|
-
*/
|
|
534
|
-
function KTForStatic(props) {
|
|
535
|
-
const { list: initList, mapper } = props;
|
|
536
|
-
// Create anchor comment node
|
|
537
|
-
const anchor = document.createComment('kt-for-static');
|
|
538
|
-
let nodes = [];
|
|
539
|
-
// Simple rebuild on redraw
|
|
540
|
-
const rebuild = (newList) => {
|
|
541
|
-
// Remove all old nodes
|
|
542
|
-
for (let i = 0; i < nodes.length; i++) {
|
|
543
|
-
const node = nodes[i];
|
|
544
|
-
if (node.parentNode) {
|
|
545
|
-
node.parentNode.removeChild(node);
|
|
456
|
+
// Remove nodes that are no longer in the list
|
|
457
|
+
nodeMap.forEach((node, key) => {
|
|
458
|
+
if (!newKeys.has(key)) {
|
|
459
|
+
node.remove();
|
|
546
460
|
}
|
|
547
|
-
}
|
|
548
|
-
nodes
|
|
549
|
-
|
|
550
|
-
let
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
if (anchor.parentNode) {
|
|
556
|
-
if (previousNode.nextSibling) {
|
|
557
|
-
anchor.parentNode.insertBefore(node, previousNode.nextSibling);
|
|
558
|
-
}
|
|
559
|
-
else {
|
|
560
|
-
anchor.parentNode.appendChild(node);
|
|
561
|
-
}
|
|
562
|
-
previousNode = node;
|
|
461
|
+
});
|
|
462
|
+
// Insert/reorder nodes in correct order
|
|
463
|
+
let referenceNode = anchor.nextSibling;
|
|
464
|
+
for (let index = 0; index < newElements.length; index++) {
|
|
465
|
+
const node = newElements[index];
|
|
466
|
+
// If node is not in correct position, insert it
|
|
467
|
+
if (referenceNode !== node) {
|
|
468
|
+
parent.insertBefore(node, referenceNode);
|
|
563
469
|
}
|
|
470
|
+
referenceNode = node.nextSibling;
|
|
564
471
|
}
|
|
472
|
+
// Update node map and element list
|
|
473
|
+
nodeMap.clear();
|
|
474
|
+
newNodeMap.forEach((node, key) => nodeMap.set(key, node));
|
|
475
|
+
anchor.__kt_for_list__ = newElements;
|
|
476
|
+
return anchor;
|
|
565
477
|
};
|
|
566
|
-
//
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
if (newProps?.list) {
|
|
571
|
-
rebuild(newProps.list);
|
|
572
|
-
}
|
|
573
|
-
};
|
|
478
|
+
// Set ref if provided
|
|
479
|
+
if (props.ref?.isKT) {
|
|
480
|
+
props.ref.value = anchor;
|
|
481
|
+
}
|
|
574
482
|
return anchor;
|
|
575
483
|
}
|
|
576
484
|
|
|
577
485
|
exports.Fragment = Fragment;
|
|
578
486
|
exports.KTAsync = KTAsync;
|
|
579
487
|
exports.KTFor = KTFor;
|
|
580
|
-
exports.KTForStatic = KTForStatic;
|
|
581
488
|
exports.createElement = h;
|
|
582
489
|
exports.createRedrawable = createRedrawable;
|
|
583
490
|
exports.createRedrawableNoref = createRedrawableNoref;
|
package/dist/index.legacy.js
CHANGED
|
@@ -180,6 +180,11 @@ var __ktjs_core__ = (function (exports) {
|
|
|
180
180
|
}
|
|
181
181
|
else {
|
|
182
182
|
$append.call(element, c);
|
|
183
|
+
// Handle KTFor anchor
|
|
184
|
+
var list = c.__kt_for_list__;
|
|
185
|
+
if ($isArray(list)) {
|
|
186
|
+
apd(element, list);
|
|
187
|
+
}
|
|
183
188
|
}
|
|
184
189
|
}
|
|
185
190
|
function apd(element, c) {
|
|
@@ -231,7 +236,7 @@ var __ktjs_core__ = (function (exports) {
|
|
|
231
236
|
* ## About
|
|
232
237
|
* @package @ktjs/core
|
|
233
238
|
* @author Kasukabe Tsumugi <futami16237@gmail.com>
|
|
234
|
-
* @version 0.
|
|
239
|
+
* @version 0.17.0 (Last Update: 2026.01.28 15:11:59.247)
|
|
235
240
|
* @license MIT
|
|
236
241
|
* @link https://github.com/baendlorel/kt.js
|
|
237
242
|
* @link https://baendlorel.github.io/ Welcome to my site!
|
|
@@ -444,205 +449,110 @@ var __ktjs_core__ = (function (exports) {
|
|
|
444
449
|
}
|
|
445
450
|
|
|
446
451
|
/**
|
|
447
|
-
* KTFor
|
|
448
|
-
*
|
|
449
|
-
*
|
|
450
|
-
* Returns a Comment node as anchor point. All list items are rendered after this anchor.
|
|
451
|
-
*
|
|
452
|
-
* @example
|
|
453
|
-
* ```tsx
|
|
454
|
-
* const listAnchor = <KTFor
|
|
455
|
-
* list={items}
|
|
456
|
-
* key={(item) => item.id}
|
|
457
|
-
* mapper={(item) => <div>{item.name}</div>}
|
|
458
|
-
* /> as KTForAnchor;
|
|
459
|
-
*
|
|
460
|
-
* document.body.appendChild(listAnchor); // Anchor + all items are rendered
|
|
461
|
-
*
|
|
462
|
-
* // Update the list
|
|
463
|
-
* listAnchor.redraw({ list: newItems });
|
|
464
|
-
* ```
|
|
452
|
+
* KTFor - List rendering component with key-based optimization
|
|
453
|
+
* Returns a Comment anchor node with rendered elements in __kt_for_list__
|
|
465
454
|
*/
|
|
466
455
|
function KTFor(props) {
|
|
467
|
-
var
|
|
468
|
-
|
|
469
|
-
var
|
|
470
|
-
//
|
|
471
|
-
var currentKeys = [];
|
|
472
|
-
// Create anchor comment node - marks the position of the list
|
|
456
|
+
var _a, _b;
|
|
457
|
+
var list = props.list, map = props.map;
|
|
458
|
+
var key = (_a = props.key) !== null && _a !== void 0 ? _a : (function (item) { return item; });
|
|
459
|
+
// Create anchor comment node
|
|
473
460
|
var anchor = document.createComment('kt-for');
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
//
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
}
|
|
513
|
-
}
|
|
514
|
-
}
|
|
515
|
-
};
|
|
516
|
-
/**
|
|
517
|
-
* Smart update method - only modifies DOM nodes that changed
|
|
518
|
-
* Uses key-based diff algorithm to minimize DOM operations
|
|
519
|
-
*/
|
|
520
|
-
var smartUpdate = function (newList, newMapper) {
|
|
521
|
-
var mapperFn = newMapper !== null && newMapper !== void 0 ? newMapper : mapper;
|
|
522
|
-
var newKeys = newList.map(function (item, index) { return getKey(item, index); });
|
|
523
|
-
if (!anchor.parentNode) {
|
|
524
|
-
// If anchor is not in DOM yet, just update cache
|
|
525
|
-
nodeCache.clear();
|
|
526
|
-
currentKeys = [];
|
|
527
|
-
for (var i = 0; i < newList.length; i++) {
|
|
528
|
-
var item = newList[i];
|
|
529
|
-
var key = newKeys[i];
|
|
530
|
-
var node = mapperFn(item, i);
|
|
531
|
-
nodeCache.set(key, { node: node, item: item });
|
|
532
|
-
currentKeys.push(key);
|
|
533
|
-
}
|
|
534
|
-
return;
|
|
535
|
-
}
|
|
536
|
-
// Find keys to remove (exist in current but not in new)
|
|
537
|
-
var keysToRemove = currentKeys.filter(function (k) { return !newKeys.includes(k); });
|
|
538
|
-
for (var i = 0; i < keysToRemove.length; i++) {
|
|
539
|
-
var key = keysToRemove[i];
|
|
540
|
-
var cached = nodeCache.get(key);
|
|
541
|
-
if (cached && cached.node.parentNode) {
|
|
542
|
-
cached.node.parentNode.removeChild(cached.node);
|
|
461
|
+
// Store current state
|
|
462
|
+
var currentList = list;
|
|
463
|
+
var currentKey = key;
|
|
464
|
+
var currentMap = map;
|
|
465
|
+
// Map to track rendered nodes by key
|
|
466
|
+
var nodeMap = new Map();
|
|
467
|
+
// Render initial list
|
|
468
|
+
var elements = [];
|
|
469
|
+
for (var index = 0; index < currentList.length; index++) {
|
|
470
|
+
var item = currentList[index];
|
|
471
|
+
var itemKey = currentKey(item, index, currentList);
|
|
472
|
+
var node = currentMap(item, index, currentList);
|
|
473
|
+
nodeMap.set(itemKey, node);
|
|
474
|
+
elements.push(node);
|
|
475
|
+
}
|
|
476
|
+
// Attach elements array to anchor
|
|
477
|
+
anchor.__kt_for_list__ = elements;
|
|
478
|
+
// Redraw function for updates
|
|
479
|
+
anchor.redraw = function (newProps) {
|
|
480
|
+
var _a, _b, _c;
|
|
481
|
+
if (newProps === void 0) { newProps = props; }
|
|
482
|
+
var newList = ((_a = newProps.list) !== null && _a !== void 0 ? _a : currentList);
|
|
483
|
+
var newKey = ((_b = newProps.key) !== null && _b !== void 0 ? _b : currentKey);
|
|
484
|
+
var newMap = ((_c = newProps.map) !== null && _c !== void 0 ? _c : currentMap);
|
|
485
|
+
// Update stored values
|
|
486
|
+
currentList = newList;
|
|
487
|
+
currentKey = newKey;
|
|
488
|
+
currentMap = newMap;
|
|
489
|
+
var parent = anchor.parentNode;
|
|
490
|
+
if (!parent) {
|
|
491
|
+
// If not in DOM yet, just rebuild the list
|
|
492
|
+
var newElements_1 = [];
|
|
493
|
+
for (var index = 0; index < currentList.length; index++) {
|
|
494
|
+
var item = currentList[index];
|
|
495
|
+
var itemKey = currentKey(item, index, currentList);
|
|
496
|
+
var node = currentMap(item, index, currentList);
|
|
497
|
+
nodeMap.set(itemKey, node);
|
|
498
|
+
newElements_1.push(node);
|
|
543
499
|
}
|
|
544
|
-
|
|
500
|
+
anchor.__kt_for_list__ = newElements_1;
|
|
501
|
+
return anchor;
|
|
545
502
|
}
|
|
546
|
-
//
|
|
547
|
-
var
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
var
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
503
|
+
// Build new key map
|
|
504
|
+
var newNodeMap = new Map();
|
|
505
|
+
var newKeys = new Set();
|
|
506
|
+
var newElements = [];
|
|
507
|
+
for (var index = 0; index < newList.length; index++) {
|
|
508
|
+
var item = newList[index];
|
|
509
|
+
var itemKey = newKey(item, index, newList);
|
|
510
|
+
newKeys.add(itemKey);
|
|
511
|
+
// Reuse existing node if key exists
|
|
512
|
+
if (nodeMap.has(itemKey)) {
|
|
513
|
+
var existingNode = nodeMap.get(itemKey);
|
|
514
|
+
newNodeMap.set(itemKey, existingNode);
|
|
515
|
+
newElements.push(existingNode);
|
|
557
516
|
}
|
|
558
517
|
else {
|
|
559
|
-
//
|
|
560
|
-
|
|
518
|
+
// Create new node
|
|
519
|
+
var node = newMap(item, index, newList);
|
|
520
|
+
newNodeMap.set(itemKey, node);
|
|
521
|
+
newElements.push(node);
|
|
561
522
|
}
|
|
562
|
-
// Ensure node is at correct position (right after previousNode)
|
|
563
|
-
if (previousNode.nextSibling !== cached.node) {
|
|
564
|
-
if (previousNode.nextSibling) {
|
|
565
|
-
anchor.parentNode.insertBefore(cached.node, previousNode.nextSibling);
|
|
566
|
-
}
|
|
567
|
-
else {
|
|
568
|
-
anchor.parentNode.appendChild(cached.node);
|
|
569
|
-
}
|
|
570
|
-
}
|
|
571
|
-
previousNode = cached.node;
|
|
572
|
-
}
|
|
573
|
-
currentKeys = newKeys;
|
|
574
|
-
};
|
|
575
|
-
// Mount redraw method on anchor
|
|
576
|
-
anchor.redraw = function (newProps) {
|
|
577
|
-
if (newProps === null || newProps === void 0 ? void 0 : newProps.list) {
|
|
578
|
-
smartUpdate(newProps.list, newProps.mapper);
|
|
579
523
|
}
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
}
|
|
585
|
-
/**
|
|
586
|
-
* Simple list rendering without key optimization
|
|
587
|
-
* Rebuilds the entire list on each update - use for small static lists
|
|
588
|
-
*
|
|
589
|
-
* Returns a Comment anchor node. All items are rendered after this anchor.
|
|
590
|
-
*
|
|
591
|
-
* @example
|
|
592
|
-
* ```tsx
|
|
593
|
-
* const listAnchor = <KTForStatic
|
|
594
|
-
* list={items}
|
|
595
|
-
* mapper={(item) => <div>{item}</div>}
|
|
596
|
-
* /> as KTForAnchor;
|
|
597
|
-
* ```
|
|
598
|
-
*/
|
|
599
|
-
function KTForStatic(props) {
|
|
600
|
-
var initList = props.list, mapper = props.mapper;
|
|
601
|
-
// Create anchor comment node
|
|
602
|
-
var anchor = document.createComment('kt-for-static');
|
|
603
|
-
var nodes = [];
|
|
604
|
-
// Simple rebuild on redraw
|
|
605
|
-
var rebuild = function (newList) {
|
|
606
|
-
// Remove all old nodes
|
|
607
|
-
for (var i = 0; i < nodes.length; i++) {
|
|
608
|
-
var node = nodes[i];
|
|
609
|
-
if (node.parentNode) {
|
|
610
|
-
node.parentNode.removeChild(node);
|
|
524
|
+
// Remove nodes that are no longer in the list
|
|
525
|
+
nodeMap.forEach(function (node, key) {
|
|
526
|
+
if (!newKeys.has(key)) {
|
|
527
|
+
node.remove();
|
|
611
528
|
}
|
|
612
|
-
}
|
|
613
|
-
nodes
|
|
614
|
-
|
|
615
|
-
var
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
if (anchor.parentNode) {
|
|
621
|
-
if (previousNode.nextSibling) {
|
|
622
|
-
anchor.parentNode.insertBefore(node, previousNode.nextSibling);
|
|
623
|
-
}
|
|
624
|
-
else {
|
|
625
|
-
anchor.parentNode.appendChild(node);
|
|
626
|
-
}
|
|
627
|
-
previousNode = node;
|
|
529
|
+
});
|
|
530
|
+
// Insert/reorder nodes in correct order
|
|
531
|
+
var referenceNode = anchor.nextSibling;
|
|
532
|
+
for (var index = 0; index < newElements.length; index++) {
|
|
533
|
+
var node = newElements[index];
|
|
534
|
+
// If node is not in correct position, insert it
|
|
535
|
+
if (referenceNode !== node) {
|
|
536
|
+
parent.insertBefore(node, referenceNode);
|
|
628
537
|
}
|
|
538
|
+
referenceNode = node.nextSibling;
|
|
629
539
|
}
|
|
540
|
+
// Update node map and element list
|
|
541
|
+
nodeMap.clear();
|
|
542
|
+
newNodeMap.forEach(function (node, key) { return nodeMap.set(key, node); });
|
|
543
|
+
anchor.__kt_for_list__ = newElements;
|
|
544
|
+
return anchor;
|
|
630
545
|
};
|
|
631
|
-
//
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
if (newProps === null || newProps === void 0 ? void 0 : newProps.list) {
|
|
636
|
-
rebuild(newProps.list);
|
|
637
|
-
}
|
|
638
|
-
};
|
|
546
|
+
// Set ref if provided
|
|
547
|
+
if ((_b = props.ref) === null || _b === void 0 ? void 0 : _b.isKT) {
|
|
548
|
+
props.ref.value = anchor;
|
|
549
|
+
}
|
|
639
550
|
return anchor;
|
|
640
551
|
}
|
|
641
552
|
|
|
642
553
|
exports.Fragment = Fragment;
|
|
643
554
|
exports.KTAsync = KTAsync;
|
|
644
555
|
exports.KTFor = KTFor;
|
|
645
|
-
exports.KTForStatic = KTForStatic;
|
|
646
556
|
exports.createElement = h;
|
|
647
557
|
exports.createRedrawable = createRedrawable;
|
|
648
558
|
exports.createRedrawableNoref = createRedrawableNoref;
|
package/dist/index.mjs
CHANGED
|
@@ -155,6 +155,11 @@ function apdSingle(element, c) {
|
|
|
155
155
|
}
|
|
156
156
|
else {
|
|
157
157
|
$append.call(element, c);
|
|
158
|
+
// Handle KTFor anchor
|
|
159
|
+
const list = c.__kt_for_list__;
|
|
160
|
+
if ($isArray(list)) {
|
|
161
|
+
apd(element, list);
|
|
162
|
+
}
|
|
158
163
|
}
|
|
159
164
|
}
|
|
160
165
|
function apd(element, c) {
|
|
@@ -203,7 +208,7 @@ let creator = defaultCreator;
|
|
|
203
208
|
* ## About
|
|
204
209
|
* @package @ktjs/core
|
|
205
210
|
* @author Kasukabe Tsumugi <futami16237@gmail.com>
|
|
206
|
-
* @version 0.
|
|
211
|
+
* @version 0.17.0 (Last Update: 2026.01.28 15:11:59.247)
|
|
207
212
|
* @license MIT
|
|
208
213
|
* @link https://github.com/baendlorel/kt.js
|
|
209
214
|
* @link https://baendlorel.github.io/ Welcome to my site!
|
|
@@ -376,199 +381,102 @@ function KTAsync(props) {
|
|
|
376
381
|
}
|
|
377
382
|
|
|
378
383
|
/**
|
|
379
|
-
* KTFor
|
|
380
|
-
*
|
|
381
|
-
*
|
|
382
|
-
* Returns a Comment node as anchor point. All list items are rendered after this anchor.
|
|
383
|
-
*
|
|
384
|
-
* @example
|
|
385
|
-
* ```tsx
|
|
386
|
-
* const listAnchor = <KTFor
|
|
387
|
-
* list={items}
|
|
388
|
-
* key={(item) => item.id}
|
|
389
|
-
* mapper={(item) => <div>{item.name}</div>}
|
|
390
|
-
* /> as KTForAnchor;
|
|
391
|
-
*
|
|
392
|
-
* document.body.appendChild(listAnchor); // Anchor + all items are rendered
|
|
393
|
-
*
|
|
394
|
-
* // Update the list
|
|
395
|
-
* listAnchor.redraw({ list: newItems });
|
|
396
|
-
* ```
|
|
384
|
+
* KTFor - List rendering component with key-based optimization
|
|
385
|
+
* Returns a Comment anchor node with rendered elements in __kt_for_list__
|
|
397
386
|
*/
|
|
398
387
|
function KTFor(props) {
|
|
399
|
-
const { list
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
// Current key order
|
|
403
|
-
let currentKeys = [];
|
|
404
|
-
// Create anchor comment node - marks the position of the list
|
|
388
|
+
const { list, map } = props;
|
|
389
|
+
const key = props.key ?? ((item) => item);
|
|
390
|
+
// Create anchor comment node
|
|
405
391
|
const anchor = document.createComment('kt-for');
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
const
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
else {
|
|
443
|
-
anchor.after(node);
|
|
444
|
-
}
|
|
445
|
-
}
|
|
446
|
-
}
|
|
447
|
-
};
|
|
448
|
-
/**
|
|
449
|
-
* Smart update method - only modifies DOM nodes that changed
|
|
450
|
-
* Uses key-based diff algorithm to minimize DOM operations
|
|
451
|
-
*/
|
|
452
|
-
const smartUpdate = (newList, newMapper) => {
|
|
453
|
-
const mapperFn = newMapper ?? mapper;
|
|
454
|
-
const newKeys = newList.map((item, index) => getKey(item, index));
|
|
455
|
-
if (!anchor.parentNode) {
|
|
456
|
-
// If anchor is not in DOM yet, just update cache
|
|
457
|
-
nodeCache.clear();
|
|
458
|
-
currentKeys = [];
|
|
459
|
-
for (let i = 0; i < newList.length; i++) {
|
|
460
|
-
const item = newList[i];
|
|
461
|
-
const key = newKeys[i];
|
|
462
|
-
const node = mapperFn(item, i);
|
|
463
|
-
nodeCache.set(key, { node, item });
|
|
464
|
-
currentKeys.push(key);
|
|
465
|
-
}
|
|
466
|
-
return;
|
|
467
|
-
}
|
|
468
|
-
// Find keys to remove (exist in current but not in new)
|
|
469
|
-
const keysToRemove = currentKeys.filter((k) => !newKeys.includes(k));
|
|
470
|
-
for (let i = 0; i < keysToRemove.length; i++) {
|
|
471
|
-
const key = keysToRemove[i];
|
|
472
|
-
const cached = nodeCache.get(key);
|
|
473
|
-
if (cached && cached.node.parentNode) {
|
|
474
|
-
cached.node.parentNode.removeChild(cached.node);
|
|
392
|
+
// Store current state
|
|
393
|
+
let currentList = list;
|
|
394
|
+
let currentKey = key;
|
|
395
|
+
let currentMap = map;
|
|
396
|
+
// Map to track rendered nodes by key
|
|
397
|
+
const nodeMap = new Map();
|
|
398
|
+
// Render initial list
|
|
399
|
+
const elements = [];
|
|
400
|
+
for (let index = 0; index < currentList.length; index++) {
|
|
401
|
+
const item = currentList[index];
|
|
402
|
+
const itemKey = currentKey(item, index, currentList);
|
|
403
|
+
const node = currentMap(item, index, currentList);
|
|
404
|
+
nodeMap.set(itemKey, node);
|
|
405
|
+
elements.push(node);
|
|
406
|
+
}
|
|
407
|
+
// Attach elements array to anchor
|
|
408
|
+
anchor.__kt_for_list__ = elements;
|
|
409
|
+
// Redraw function for updates
|
|
410
|
+
anchor.redraw = (newProps = props) => {
|
|
411
|
+
const newList = (newProps.list ?? currentList);
|
|
412
|
+
const newKey = (newProps.key ?? currentKey);
|
|
413
|
+
const newMap = (newProps.map ?? currentMap);
|
|
414
|
+
// Update stored values
|
|
415
|
+
currentList = newList;
|
|
416
|
+
currentKey = newKey;
|
|
417
|
+
currentMap = newMap;
|
|
418
|
+
const parent = anchor.parentNode;
|
|
419
|
+
if (!parent) {
|
|
420
|
+
// If not in DOM yet, just rebuild the list
|
|
421
|
+
const newElements = [];
|
|
422
|
+
for (let index = 0; index < currentList.length; index++) {
|
|
423
|
+
const item = currentList[index];
|
|
424
|
+
const itemKey = currentKey(item, index, currentList);
|
|
425
|
+
const node = currentMap(item, index, currentList);
|
|
426
|
+
nodeMap.set(itemKey, node);
|
|
427
|
+
newElements.push(node);
|
|
475
428
|
}
|
|
476
|
-
|
|
429
|
+
anchor.__kt_for_list__ = newElements;
|
|
430
|
+
return anchor;
|
|
477
431
|
}
|
|
478
|
-
//
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
432
|
+
// Build new key map
|
|
433
|
+
const newNodeMap = new Map();
|
|
434
|
+
const newKeys = new Set();
|
|
435
|
+
const newElements = [];
|
|
436
|
+
for (let index = 0; index < newList.length; index++) {
|
|
437
|
+
const item = newList[index];
|
|
438
|
+
const itemKey = newKey(item, index, newList);
|
|
439
|
+
newKeys.add(itemKey);
|
|
440
|
+
// Reuse existing node if key exists
|
|
441
|
+
if (nodeMap.has(itemKey)) {
|
|
442
|
+
const existingNode = nodeMap.get(itemKey);
|
|
443
|
+
newNodeMap.set(itemKey, existingNode);
|
|
444
|
+
newElements.push(existingNode);
|
|
489
445
|
}
|
|
490
446
|
else {
|
|
491
|
-
//
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
if (previousNode.nextSibling !== cached.node) {
|
|
496
|
-
if (previousNode.nextSibling) {
|
|
497
|
-
anchor.parentNode.insertBefore(cached.node, previousNode.nextSibling);
|
|
498
|
-
}
|
|
499
|
-
else {
|
|
500
|
-
anchor.parentNode.appendChild(cached.node);
|
|
501
|
-
}
|
|
447
|
+
// Create new node
|
|
448
|
+
const node = newMap(item, index, newList);
|
|
449
|
+
newNodeMap.set(itemKey, node);
|
|
450
|
+
newElements.push(node);
|
|
502
451
|
}
|
|
503
|
-
previousNode = cached.node;
|
|
504
|
-
}
|
|
505
|
-
currentKeys = newKeys;
|
|
506
|
-
};
|
|
507
|
-
// Mount redraw method on anchor
|
|
508
|
-
anchor.redraw = (newProps) => {
|
|
509
|
-
if (newProps?.list) {
|
|
510
|
-
smartUpdate(newProps.list, newProps.mapper);
|
|
511
452
|
}
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
}
|
|
517
|
-
/**
|
|
518
|
-
* Simple list rendering without key optimization
|
|
519
|
-
* Rebuilds the entire list on each update - use for small static lists
|
|
520
|
-
*
|
|
521
|
-
* Returns a Comment anchor node. All items are rendered after this anchor.
|
|
522
|
-
*
|
|
523
|
-
* @example
|
|
524
|
-
* ```tsx
|
|
525
|
-
* const listAnchor = <KTForStatic
|
|
526
|
-
* list={items}
|
|
527
|
-
* mapper={(item) => <div>{item}</div>}
|
|
528
|
-
* /> as KTForAnchor;
|
|
529
|
-
* ```
|
|
530
|
-
*/
|
|
531
|
-
function KTForStatic(props) {
|
|
532
|
-
const { list: initList, mapper } = props;
|
|
533
|
-
// Create anchor comment node
|
|
534
|
-
const anchor = document.createComment('kt-for-static');
|
|
535
|
-
let nodes = [];
|
|
536
|
-
// Simple rebuild on redraw
|
|
537
|
-
const rebuild = (newList) => {
|
|
538
|
-
// Remove all old nodes
|
|
539
|
-
for (let i = 0; i < nodes.length; i++) {
|
|
540
|
-
const node = nodes[i];
|
|
541
|
-
if (node.parentNode) {
|
|
542
|
-
node.parentNode.removeChild(node);
|
|
453
|
+
// Remove nodes that are no longer in the list
|
|
454
|
+
nodeMap.forEach((node, key) => {
|
|
455
|
+
if (!newKeys.has(key)) {
|
|
456
|
+
node.remove();
|
|
543
457
|
}
|
|
544
|
-
}
|
|
545
|
-
nodes
|
|
546
|
-
|
|
547
|
-
let
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
if (anchor.parentNode) {
|
|
553
|
-
if (previousNode.nextSibling) {
|
|
554
|
-
anchor.parentNode.insertBefore(node, previousNode.nextSibling);
|
|
555
|
-
}
|
|
556
|
-
else {
|
|
557
|
-
anchor.parentNode.appendChild(node);
|
|
558
|
-
}
|
|
559
|
-
previousNode = node;
|
|
458
|
+
});
|
|
459
|
+
// Insert/reorder nodes in correct order
|
|
460
|
+
let referenceNode = anchor.nextSibling;
|
|
461
|
+
for (let index = 0; index < newElements.length; index++) {
|
|
462
|
+
const node = newElements[index];
|
|
463
|
+
// If node is not in correct position, insert it
|
|
464
|
+
if (referenceNode !== node) {
|
|
465
|
+
parent.insertBefore(node, referenceNode);
|
|
560
466
|
}
|
|
467
|
+
referenceNode = node.nextSibling;
|
|
561
468
|
}
|
|
469
|
+
// Update node map and element list
|
|
470
|
+
nodeMap.clear();
|
|
471
|
+
newNodeMap.forEach((node, key) => nodeMap.set(key, node));
|
|
472
|
+
anchor.__kt_for_list__ = newElements;
|
|
473
|
+
return anchor;
|
|
562
474
|
};
|
|
563
|
-
//
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
if (newProps?.list) {
|
|
568
|
-
rebuild(newProps.list);
|
|
569
|
-
}
|
|
570
|
-
};
|
|
475
|
+
// Set ref if provided
|
|
476
|
+
if (props.ref?.isKT) {
|
|
477
|
+
props.ref.value = anchor;
|
|
478
|
+
}
|
|
571
479
|
return anchor;
|
|
572
480
|
}
|
|
573
481
|
|
|
574
|
-
export { Fragment, KTAsync, KTFor,
|
|
482
|
+
export { Fragment, KTAsync, KTFor, h as createElement, createRedrawable, createRedrawableNoref, h, jsx, jsxDEV, jsxs, ref };
|
package/dist/jsx/index.d.ts
CHANGED
|
@@ -17,12 +17,13 @@ interface KTRef<T> {
|
|
|
17
17
|
*/
|
|
18
18
|
declare function ref<T = HTMLElement>(value?: T): KTRef<T>;
|
|
19
19
|
|
|
20
|
-
type
|
|
20
|
+
type Redraw = (props?: KTAttribute, ...args: any[]) => KTHTMLElement;
|
|
21
|
+
type KTHTMLElement<El extends HTMLElement = HTMLElement, R extends Function = Redraw> = El & {
|
|
21
22
|
/**
|
|
22
23
|
* Automically generate a redraw function if it is not provided
|
|
23
24
|
* @param props
|
|
24
25
|
*/
|
|
25
|
-
redraw:
|
|
26
|
+
redraw: R;
|
|
26
27
|
};
|
|
27
28
|
|
|
28
29
|
declare global {
|
|
@@ -44,7 +45,7 @@ declare global {
|
|
|
44
45
|
}
|
|
45
46
|
}
|
|
46
47
|
|
|
47
|
-
type SingleContent = KTRef<any> | HTMLElement | Element | string | number | boolean | null | undefined;
|
|
48
|
+
type SingleContent = KTRef<any> | HTMLElement | Element | Node | string | number | boolean | null | undefined;
|
|
48
49
|
type KTAvailableContent = SingleContent | KTAvailableContent[];
|
|
49
50
|
type KTRawContent = KTAvailableContent | Promise<KTAvailableContent>;
|
|
50
51
|
type KTRawAttr = KTAttribute | string;
|
|
@@ -55,9 +56,11 @@ type KTRawAttr = KTAttribute | string;
|
|
|
55
56
|
interface KTBaseAttribute {
|
|
56
57
|
[k: string]: any;
|
|
57
58
|
|
|
59
|
+
// # kt-specific attributes
|
|
58
60
|
ref?: KTRef<HTMLElement>;
|
|
59
61
|
'k-if'?: any;
|
|
60
62
|
|
|
63
|
+
// # normal HTML attributes
|
|
61
64
|
id?: string;
|
|
62
65
|
class?: string;
|
|
63
66
|
style?: string | Partial<CSSStyleDeclaration>;
|
|
@@ -154,7 +157,7 @@ type HTML<T extends (HTMLTag | SVGTag) & otherstring> = T extends SVGTag ? SVGEl
|
|
|
154
157
|
* ## About
|
|
155
158
|
* @package @ktjs/core
|
|
156
159
|
* @author Kasukabe Tsumugi <futami16237@gmail.com>
|
|
157
|
-
* @version 0.
|
|
160
|
+
* @version 0.17.0 (Last Update: 2026.01.28 15:11:59.247)
|
|
158
161
|
* @license MIT
|
|
159
162
|
* @link https://github.com/baendlorel/kt.js
|
|
160
163
|
* @link https://baendlorel.github.io/ Welcome to my site!
|
|
@@ -218,4 +221,4 @@ declare function createRedrawableNoref(creator: () => KTHTMLElement): KTHTMLElem
|
|
|
218
221
|
declare function createRedrawable(creator: () => KTHTMLElement): KTRef<KTHTMLElement>;
|
|
219
222
|
|
|
220
223
|
export { Fragment, h as createElement, createRedrawable, createRedrawableNoref, jsx, jsxDEV, jsxs, ref };
|
|
221
|
-
export type { KTHTMLElement, KTRef };
|
|
224
|
+
export type { KTHTMLElement, KTRef, Redraw };
|
package/dist/jsx/index.mjs
CHANGED
|
@@ -155,6 +155,11 @@ function apdSingle(element, c) {
|
|
|
155
155
|
}
|
|
156
156
|
else {
|
|
157
157
|
$append.call(element, c);
|
|
158
|
+
// Handle KTFor anchor
|
|
159
|
+
const list = c.__kt_for_list__;
|
|
160
|
+
if ($isArray(list)) {
|
|
161
|
+
apd(element, list);
|
|
162
|
+
}
|
|
158
163
|
}
|
|
159
164
|
}
|
|
160
165
|
function apd(element, c) {
|
|
@@ -203,7 +208,7 @@ let creator = defaultCreator;
|
|
|
203
208
|
* ## About
|
|
204
209
|
* @package @ktjs/core
|
|
205
210
|
* @author Kasukabe Tsumugi <futami16237@gmail.com>
|
|
206
|
-
* @version 0.
|
|
211
|
+
* @version 0.17.0 (Last Update: 2026.01.28 15:11:59.247)
|
|
207
212
|
* @license MIT
|
|
208
213
|
* @link https://github.com/baendlorel/kt.js
|
|
209
214
|
* @link https://baendlorel.github.io/ Welcome to my site!
|
|
@@ -11,12 +11,13 @@ interface KTRef<T> {
|
|
|
11
11
|
isKT: true;
|
|
12
12
|
}
|
|
13
13
|
|
|
14
|
-
type
|
|
14
|
+
type Redraw = (props?: KTAttribute, ...args: any[]) => KTHTMLElement;
|
|
15
|
+
type KTHTMLElement<El extends HTMLElement = HTMLElement, R extends Function = Redraw> = El & {
|
|
15
16
|
/**
|
|
16
17
|
* Automically generate a redraw function if it is not provided
|
|
17
18
|
* @param props
|
|
18
19
|
*/
|
|
19
|
-
redraw:
|
|
20
|
+
redraw: R;
|
|
20
21
|
};
|
|
21
22
|
|
|
22
23
|
declare global {
|
|
@@ -38,7 +39,7 @@ declare global {
|
|
|
38
39
|
}
|
|
39
40
|
}
|
|
40
41
|
|
|
41
|
-
type SingleContent = KTRef<any> | HTMLElement | Element | string | number | boolean | null | undefined;
|
|
42
|
+
type SingleContent = KTRef<any> | HTMLElement | Element | Node | string | number | boolean | null | undefined;
|
|
42
43
|
type KTAvailableContent = SingleContent | KTAvailableContent[];
|
|
43
44
|
type KTRawContent = KTAvailableContent | Promise<KTAvailableContent>;
|
|
44
45
|
type KTRawAttr = KTAttribute | string;
|
|
@@ -49,9 +50,11 @@ type KTRawAttr = KTAttribute | string;
|
|
|
49
50
|
interface KTBaseAttribute {
|
|
50
51
|
[k: string]: any;
|
|
51
52
|
|
|
53
|
+
// # kt-specific attributes
|
|
52
54
|
ref?: KTRef<HTMLElement>;
|
|
53
55
|
'k-if'?: any;
|
|
54
56
|
|
|
57
|
+
// # normal HTML attributes
|
|
55
58
|
id?: string;
|
|
56
59
|
class?: string;
|
|
57
60
|
style?: string | Partial<CSSStyleDeclaration>;
|
|
@@ -148,7 +151,7 @@ type HTML<T extends (HTMLTag | SVGTag) & otherstring> = T extends SVGTag ? SVGEl
|
|
|
148
151
|
* ## About
|
|
149
152
|
* @package @ktjs/core
|
|
150
153
|
* @author Kasukabe Tsumugi <futami16237@gmail.com>
|
|
151
|
-
* @version 0.
|
|
154
|
+
* @version 0.17.0 (Last Update: 2026.01.28 15:11:59.247)
|
|
152
155
|
* @license MIT
|
|
153
156
|
* @link https://github.com/baendlorel/kt.js
|
|
154
157
|
* @link https://baendlorel.github.io/ Welcome to my site!
|
package/dist/jsx/jsx-runtime.mjs
CHANGED
|
@@ -155,6 +155,11 @@ function apdSingle(element, c) {
|
|
|
155
155
|
}
|
|
156
156
|
else {
|
|
157
157
|
$append.call(element, c);
|
|
158
|
+
// Handle KTFor anchor
|
|
159
|
+
const list = c.__kt_for_list__;
|
|
160
|
+
if ($isArray(list)) {
|
|
161
|
+
apd(element, list);
|
|
162
|
+
}
|
|
158
163
|
}
|
|
159
164
|
}
|
|
160
165
|
function apd(element, c) {
|
|
@@ -203,7 +208,7 @@ let creator = defaultCreator;
|
|
|
203
208
|
* ## About
|
|
204
209
|
* @package @ktjs/core
|
|
205
210
|
* @author Kasukabe Tsumugi <futami16237@gmail.com>
|
|
206
|
-
* @version 0.
|
|
211
|
+
* @version 0.17.0 (Last Update: 2026.01.28 15:11:59.247)
|
|
207
212
|
* @license MIT
|
|
208
213
|
* @link https://github.com/baendlorel/kt.js
|
|
209
214
|
* @link https://baendlorel.github.io/ Welcome to my site!
|