@sigx/runtime-terminal 0.1.3 → 0.1.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +2 -2
- package/dist/index.js +522 -124
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -1,10 +1,18 @@
|
|
|
1
1
|
//#region ../runtime-core/src/plugins.ts
|
|
2
|
-
const plugins = [];
|
|
2
|
+
const plugins$1 = [];
|
|
3
3
|
/**
|
|
4
4
|
* Get all registered plugins (internal use)
|
|
5
5
|
*/
|
|
6
|
-
function getComponentPlugins() {
|
|
7
|
-
return plugins;
|
|
6
|
+
function getComponentPlugins$1() {
|
|
7
|
+
return plugins$1;
|
|
8
|
+
}
|
|
9
|
+
const contextExtensions = [];
|
|
10
|
+
/**
|
|
11
|
+
* Apply all registered context extensions to a context object.
|
|
12
|
+
* Called internally by the renderer when creating component contexts.
|
|
13
|
+
*/
|
|
14
|
+
function applyContextExtensions(ctx) {
|
|
15
|
+
for (const extension of contextExtensions) extension(ctx);
|
|
8
16
|
}
|
|
9
17
|
|
|
10
18
|
//#endregion
|
|
@@ -119,7 +127,7 @@ function onCleanup(fn) {
|
|
|
119
127
|
if (currentComponentContext) currentComponentContext.onCleanup(fn);
|
|
120
128
|
else console.warn("onCleanup called outside of component setup");
|
|
121
129
|
}
|
|
122
|
-
const componentRegistry = /* @__PURE__ */ new Map();
|
|
130
|
+
const componentRegistry$1 = /* @__PURE__ */ new Map();
|
|
123
131
|
/**
|
|
124
132
|
* Define a component. Returns a JSX factory function.
|
|
125
133
|
*
|
|
@@ -159,11 +167,11 @@ function defineComponent(setup, options) {
|
|
|
159
167
|
factory.__events = null;
|
|
160
168
|
factory.__ref = null;
|
|
161
169
|
factory.__slots = null;
|
|
162
|
-
componentRegistry.set(factory, {
|
|
170
|
+
componentRegistry$1.set(factory, {
|
|
163
171
|
name: options?.name,
|
|
164
172
|
setup
|
|
165
173
|
});
|
|
166
|
-
getComponentPlugins().forEach((p) => p.onDefine?.(options?.name, factory, setup));
|
|
174
|
+
getComponentPlugins$1().forEach((p) => p.onDefine?.(options?.name, factory, setup));
|
|
167
175
|
return factory;
|
|
168
176
|
}
|
|
169
177
|
|
|
@@ -186,15 +194,17 @@ function batch(fn) {
|
|
|
186
194
|
}
|
|
187
195
|
}
|
|
188
196
|
function runEffect(fn) {
|
|
189
|
-
const
|
|
190
|
-
cleanup(
|
|
191
|
-
activeEffect =
|
|
197
|
+
const effectFn = function() {
|
|
198
|
+
cleanup(effectFn);
|
|
199
|
+
activeEffect = effectFn;
|
|
192
200
|
fn();
|
|
193
201
|
activeEffect = null;
|
|
194
202
|
};
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
203
|
+
effectFn.deps = [];
|
|
204
|
+
effectFn();
|
|
205
|
+
const runner = (() => effectFn());
|
|
206
|
+
runner.stop = () => cleanup(effectFn);
|
|
207
|
+
return runner;
|
|
198
208
|
}
|
|
199
209
|
function cleanup(effect$1) {
|
|
200
210
|
if (!effect$1.deps) return;
|
|
@@ -330,6 +340,292 @@ const arrayInstrumentations = {};
|
|
|
330
340
|
const Fragment$1 = Symbol.for("sigx.Fragment");
|
|
331
341
|
const Text$1 = Symbol.for("sigx.Text");
|
|
332
342
|
|
|
343
|
+
//#endregion
|
|
344
|
+
//#region ../runtime-core/src/lazy.tsx
|
|
345
|
+
/**
|
|
346
|
+
* Lazy loading utilities for sigx components.
|
|
347
|
+
*
|
|
348
|
+
* Provides runtime-only lazy loading with no build dependencies.
|
|
349
|
+
* Works with any bundler that supports dynamic import().
|
|
350
|
+
*/
|
|
351
|
+
let currentSuspenseBoundary$1 = null;
|
|
352
|
+
/**
|
|
353
|
+
* Register a promise with the current Suspense boundary
|
|
354
|
+
* @internal
|
|
355
|
+
*/
|
|
356
|
+
function registerPendingPromise$1(promise) {
|
|
357
|
+
const boundary = currentSuspenseBoundary$1;
|
|
358
|
+
if (boundary) {
|
|
359
|
+
boundary.pending.add(promise);
|
|
360
|
+
promise.finally(() => {
|
|
361
|
+
boundary.pending.delete(promise);
|
|
362
|
+
if (boundary.pending.size === 0) boundary.onResolve();
|
|
363
|
+
});
|
|
364
|
+
return true;
|
|
365
|
+
}
|
|
366
|
+
return false;
|
|
367
|
+
}
|
|
368
|
+
/**
|
|
369
|
+
* Suspense boundary component for handling async loading states.
|
|
370
|
+
*
|
|
371
|
+
* Wraps lazy-loaded components and shows a fallback while they load.
|
|
372
|
+
*
|
|
373
|
+
* @example
|
|
374
|
+
* ```tsx
|
|
375
|
+
* import { lazy, Suspense } from 'sigx';
|
|
376
|
+
*
|
|
377
|
+
* const LazyDashboard = lazy(() => import('./Dashboard'));
|
|
378
|
+
*
|
|
379
|
+
* // Basic usage
|
|
380
|
+
* <Suspense fallback={<div>Loading...</div>}>
|
|
381
|
+
* <LazyDashboard />
|
|
382
|
+
* </Suspense>
|
|
383
|
+
*
|
|
384
|
+
* // With spinner component
|
|
385
|
+
* <Suspense fallback={<Spinner size="large" />}>
|
|
386
|
+
* <LazyDashboard />
|
|
387
|
+
* <LazyCharts />
|
|
388
|
+
* </Suspense>
|
|
389
|
+
* ```
|
|
390
|
+
*/
|
|
391
|
+
const Suspense$1 = defineComponent((ctx) => {
|
|
392
|
+
const { props, slots } = ctx;
|
|
393
|
+
const state = ctx.signal({
|
|
394
|
+
isReady: false,
|
|
395
|
+
pendingCount: 0
|
|
396
|
+
});
|
|
397
|
+
const boundary = {
|
|
398
|
+
pending: /* @__PURE__ */ new Set(),
|
|
399
|
+
onResolve: () => {
|
|
400
|
+
state.pendingCount = boundary.pending.size;
|
|
401
|
+
if (boundary.pending.size === 0) state.isReady = true;
|
|
402
|
+
}
|
|
403
|
+
};
|
|
404
|
+
ctx.onMount(() => {
|
|
405
|
+
if (boundary.pending.size === 0) state.isReady = true;
|
|
406
|
+
});
|
|
407
|
+
return () => {
|
|
408
|
+
state.isReady;
|
|
409
|
+
state.pendingCount;
|
|
410
|
+
const prevBoundary = currentSuspenseBoundary$1;
|
|
411
|
+
currentSuspenseBoundary$1 = boundary;
|
|
412
|
+
try {
|
|
413
|
+
const children = slots.default();
|
|
414
|
+
if (boundary.pending.size > 0) {
|
|
415
|
+
const fallback = props.fallback;
|
|
416
|
+
if (typeof fallback === "function") return fallback();
|
|
417
|
+
return fallback ?? null;
|
|
418
|
+
}
|
|
419
|
+
if (Array.isArray(children)) {
|
|
420
|
+
const filtered = children.filter((c) => c != null && c !== false && c !== true);
|
|
421
|
+
if (filtered.length === 0) return null;
|
|
422
|
+
if (filtered.length === 1) return filtered[0];
|
|
423
|
+
return filtered;
|
|
424
|
+
}
|
|
425
|
+
return children;
|
|
426
|
+
} catch (err) {
|
|
427
|
+
if (err instanceof Promise) {
|
|
428
|
+
registerPendingPromise$1(err);
|
|
429
|
+
const fallback = props.fallback;
|
|
430
|
+
if (typeof fallback === "function") return fallback();
|
|
431
|
+
return fallback ?? null;
|
|
432
|
+
}
|
|
433
|
+
throw err;
|
|
434
|
+
} finally {
|
|
435
|
+
currentSuspenseBoundary$1 = prevBoundary;
|
|
436
|
+
}
|
|
437
|
+
};
|
|
438
|
+
}, { name: "Suspense" });
|
|
439
|
+
|
|
440
|
+
//#endregion
|
|
441
|
+
//#region ../runtime-core/src/utils/props-accessor.ts
|
|
442
|
+
/**
|
|
443
|
+
* Creates a props accessor that can be called with defaults or accessed directly.
|
|
444
|
+
* After calling with defaults, direct property access uses those defaults.
|
|
445
|
+
*
|
|
446
|
+
* @example
|
|
447
|
+
* ```ts
|
|
448
|
+
* // In component setup:
|
|
449
|
+
* const props = createPropsAccessor(reactiveProps);
|
|
450
|
+
*
|
|
451
|
+
* // Set defaults
|
|
452
|
+
* props({ count: 0, label: 'Default' });
|
|
453
|
+
*
|
|
454
|
+
* // Access props (falls back to defaults if not provided)
|
|
455
|
+
* const count = props.count;
|
|
456
|
+
* ```
|
|
457
|
+
*/
|
|
458
|
+
function createPropsAccessor(reactiveProps) {
|
|
459
|
+
let defaults = {};
|
|
460
|
+
const proxy = new Proxy(function propsAccessor() {}, {
|
|
461
|
+
get(_, key) {
|
|
462
|
+
if (typeof key === "symbol") return void 0;
|
|
463
|
+
const value = reactiveProps[key];
|
|
464
|
+
return value != null ? value : defaults[key];
|
|
465
|
+
},
|
|
466
|
+
apply(_, __, args) {
|
|
467
|
+
if (args[0] && typeof args[0] === "object") defaults = {
|
|
468
|
+
...defaults,
|
|
469
|
+
...args[0]
|
|
470
|
+
};
|
|
471
|
+
return proxy;
|
|
472
|
+
},
|
|
473
|
+
has(_, key) {
|
|
474
|
+
if (typeof key === "symbol") return false;
|
|
475
|
+
return key in reactiveProps || key in defaults;
|
|
476
|
+
},
|
|
477
|
+
ownKeys() {
|
|
478
|
+
return [...new Set([...Object.keys(reactiveProps), ...Object.keys(defaults)])];
|
|
479
|
+
},
|
|
480
|
+
getOwnPropertyDescriptor(_, key) {
|
|
481
|
+
if (typeof key === "symbol") return void 0;
|
|
482
|
+
if (key in reactiveProps || key in defaults) return {
|
|
483
|
+
enumerable: true,
|
|
484
|
+
configurable: true,
|
|
485
|
+
writable: false
|
|
486
|
+
};
|
|
487
|
+
}
|
|
488
|
+
});
|
|
489
|
+
return proxy;
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
//#endregion
|
|
493
|
+
//#region ../runtime-core/src/utils/slots.ts
|
|
494
|
+
/**
|
|
495
|
+
* Slots system for component children.
|
|
496
|
+
* Supports default and named slots with reactivity.
|
|
497
|
+
*/
|
|
498
|
+
/**
|
|
499
|
+
* Create slots object from children and slots prop.
|
|
500
|
+
* Uses a version signal to trigger re-renders when children change.
|
|
501
|
+
*
|
|
502
|
+
* Supports named slots via:
|
|
503
|
+
* - `slots` prop object (e.g., `slots={{ header: () => <div>...</div> }}`)
|
|
504
|
+
* - `slot` prop on children (e.g., `<div slot="header">...</div>`)
|
|
505
|
+
*
|
|
506
|
+
* @example
|
|
507
|
+
* ```tsx
|
|
508
|
+
* // Parent component
|
|
509
|
+
* <Card slots={{ header: () => <h1>Title</h1> }}>
|
|
510
|
+
* <p>Default content</p>
|
|
511
|
+
* <span slot="footer">Footer text</span>
|
|
512
|
+
* </Card>
|
|
513
|
+
*
|
|
514
|
+
* // Card component setup
|
|
515
|
+
* const slots = createSlots(children, slotsFromProps);
|
|
516
|
+
* return () => (
|
|
517
|
+
* <div>
|
|
518
|
+
* {slots.header()}
|
|
519
|
+
* {slots.default()}
|
|
520
|
+
* {slots.footer()}
|
|
521
|
+
* </div>
|
|
522
|
+
* );
|
|
523
|
+
* ```
|
|
524
|
+
*/
|
|
525
|
+
function createSlots(children, slotsFromProps) {
|
|
526
|
+
const versionSignal = signal({ v: 0 });
|
|
527
|
+
function extractNamedSlotsFromChildren(c) {
|
|
528
|
+
const defaultChildren = [];
|
|
529
|
+
const namedSlots = {};
|
|
530
|
+
if (c == null) return {
|
|
531
|
+
defaultChildren,
|
|
532
|
+
namedSlots
|
|
533
|
+
};
|
|
534
|
+
const items = Array.isArray(c) ? c : [c];
|
|
535
|
+
for (const child of items) if (child && typeof child === "object" && child.props && child.props.slot) {
|
|
536
|
+
const slotName = child.props.slot;
|
|
537
|
+
if (!namedSlots[slotName]) namedSlots[slotName] = [];
|
|
538
|
+
namedSlots[slotName].push(child);
|
|
539
|
+
} else defaultChildren.push(child);
|
|
540
|
+
return {
|
|
541
|
+
defaultChildren,
|
|
542
|
+
namedSlots
|
|
543
|
+
};
|
|
544
|
+
}
|
|
545
|
+
const slotsObj = {
|
|
546
|
+
_children: children,
|
|
547
|
+
_slotsFromProps: slotsFromProps || {},
|
|
548
|
+
_version: versionSignal,
|
|
549
|
+
_isPatching: false,
|
|
550
|
+
default: function() {
|
|
551
|
+
this._version.v;
|
|
552
|
+
const c = this._children;
|
|
553
|
+
const { defaultChildren } = extractNamedSlotsFromChildren(c);
|
|
554
|
+
return defaultChildren.filter((child) => child != null && child !== false && child !== true);
|
|
555
|
+
}
|
|
556
|
+
};
|
|
557
|
+
return new Proxy(slotsObj, { get(target, prop) {
|
|
558
|
+
if (prop in target) return target[prop];
|
|
559
|
+
if (typeof prop === "string") return function(scopedProps) {
|
|
560
|
+
target._version.v;
|
|
561
|
+
if (target._slotsFromProps && typeof target._slotsFromProps[prop] === "function") {
|
|
562
|
+
const result = target._slotsFromProps[prop](scopedProps);
|
|
563
|
+
if (result == null) return [];
|
|
564
|
+
return Array.isArray(result) ? result : [result];
|
|
565
|
+
}
|
|
566
|
+
const { namedSlots } = extractNamedSlotsFromChildren(target._children);
|
|
567
|
+
return namedSlots[prop] || [];
|
|
568
|
+
};
|
|
569
|
+
} });
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
//#endregion
|
|
573
|
+
//#region ../runtime-core/src/utils/normalize.ts
|
|
574
|
+
/**
|
|
575
|
+
* VNode normalization utilities.
|
|
576
|
+
* Converts render results into proper VNode structures.
|
|
577
|
+
*/
|
|
578
|
+
/**
|
|
579
|
+
* Normalize render result to a VNode (wrapping arrays in Fragment).
|
|
580
|
+
* Handles null, undefined, false, true by returning an empty Text node.
|
|
581
|
+
*
|
|
582
|
+
* This is used to normalize the return value of component render functions
|
|
583
|
+
* into a consistent VNode structure for the renderer to process.
|
|
584
|
+
*
|
|
585
|
+
* @example
|
|
586
|
+
* ```ts
|
|
587
|
+
* // Conditional rendering returns null/false
|
|
588
|
+
* normalizeSubTree(null) // → empty Text node
|
|
589
|
+
* normalizeSubTree(false) // → empty Text node
|
|
590
|
+
*
|
|
591
|
+
* // Arrays become Fragments
|
|
592
|
+
* normalizeSubTree([<A/>, <B/>]) // → Fragment with children
|
|
593
|
+
*
|
|
594
|
+
* // Primitives become Text nodes
|
|
595
|
+
* normalizeSubTree("hello") // → Text node
|
|
596
|
+
* normalizeSubTree(42) // → Text node
|
|
597
|
+
*
|
|
598
|
+
* // VNodes pass through
|
|
599
|
+
* normalizeSubTree(<div/>) // → same VNode
|
|
600
|
+
* ```
|
|
601
|
+
*/
|
|
602
|
+
function normalizeSubTree(result) {
|
|
603
|
+
if (result == null || result === false || result === true) return {
|
|
604
|
+
type: Text$1,
|
|
605
|
+
props: {},
|
|
606
|
+
key: null,
|
|
607
|
+
children: [],
|
|
608
|
+
dom: null,
|
|
609
|
+
text: ""
|
|
610
|
+
};
|
|
611
|
+
if (Array.isArray(result)) return {
|
|
612
|
+
type: Fragment$1,
|
|
613
|
+
props: {},
|
|
614
|
+
key: null,
|
|
615
|
+
children: result,
|
|
616
|
+
dom: null
|
|
617
|
+
};
|
|
618
|
+
if (typeof result === "string" || typeof result === "number") return {
|
|
619
|
+
type: Text$1,
|
|
620
|
+
props: {},
|
|
621
|
+
key: null,
|
|
622
|
+
children: [],
|
|
623
|
+
dom: null,
|
|
624
|
+
text: result
|
|
625
|
+
};
|
|
626
|
+
return result;
|
|
627
|
+
}
|
|
628
|
+
|
|
333
629
|
//#endregion
|
|
334
630
|
//#region ../runtime-core/src/renderer.ts
|
|
335
631
|
/**
|
|
@@ -340,7 +636,6 @@ function isComponent(type) {
|
|
|
340
636
|
}
|
|
341
637
|
function createRenderer(options) {
|
|
342
638
|
const { insert: hostInsert, remove: hostRemove, patchProp: hostPatchProp, createElement: hostCreateElement, createText: hostCreateText, createComment: hostCreateComment, setText: hostSetText, setElementText: hostSetElementText, parentNode: hostParentNode, nextSibling: hostNextSibling, cloneNode: hostCloneNode, insertStaticContent: hostInsertStaticContent } = options;
|
|
343
|
-
let isPatching = false;
|
|
344
639
|
let currentAppContext = null;
|
|
345
640
|
function render$1(element, container, appContext) {
|
|
346
641
|
if (appContext) currentAppContext = appContext;
|
|
@@ -354,6 +649,13 @@ function createRenderer(options) {
|
|
|
354
649
|
dom: null,
|
|
355
650
|
text: element
|
|
356
651
|
};
|
|
652
|
+
else if (isComponent(element)) vnode = {
|
|
653
|
+
type: element,
|
|
654
|
+
props: {},
|
|
655
|
+
key: null,
|
|
656
|
+
children: [],
|
|
657
|
+
dom: null
|
|
658
|
+
};
|
|
357
659
|
else vnode = element;
|
|
358
660
|
if (vnode) {
|
|
359
661
|
if (oldVNode) patch(oldVNode, vnode, container);
|
|
@@ -365,6 +667,7 @@ function createRenderer(options) {
|
|
|
365
667
|
}
|
|
366
668
|
}
|
|
367
669
|
function mount(vnode, container, before = null) {
|
|
670
|
+
if (vnode == null || vnode === false || vnode === true) return;
|
|
368
671
|
if (vnode.type === Text$1) {
|
|
369
672
|
const node = hostCreateText(String(vnode.text));
|
|
370
673
|
vnode.dom = node;
|
|
@@ -376,7 +679,7 @@ function createRenderer(options) {
|
|
|
376
679
|
const anchor = hostCreateComment("");
|
|
377
680
|
vnode.dom = anchor;
|
|
378
681
|
hostInsert(anchor, container, before);
|
|
379
|
-
vnode.children.forEach((child) => mount(child, container, anchor));
|
|
682
|
+
if (vnode.children) vnode.children.forEach((child) => mount(child, container, anchor));
|
|
380
683
|
return;
|
|
381
684
|
}
|
|
382
685
|
if (isComponent(vnode.type)) {
|
|
@@ -393,17 +696,18 @@ function createRenderer(options) {
|
|
|
393
696
|
else if (typeof vnode.props.ref === "object") vnode.props.ref.current = element;
|
|
394
697
|
}
|
|
395
698
|
}
|
|
396
|
-
vnode.children.forEach((child) => {
|
|
699
|
+
if (vnode.children) vnode.children.forEach((child) => {
|
|
397
700
|
child.parent = vnode;
|
|
398
701
|
mount(child, element);
|
|
399
702
|
});
|
|
400
703
|
hostInsert(element, container, before);
|
|
401
704
|
}
|
|
402
705
|
function unmount(vnode, container) {
|
|
403
|
-
|
|
706
|
+
const internalVNode = vnode;
|
|
707
|
+
if (internalVNode._effect) internalVNode._effect.stop();
|
|
404
708
|
if (vnode.cleanup) vnode.cleanup();
|
|
405
709
|
if (isComponent(vnode.type)) {
|
|
406
|
-
const subTree =
|
|
710
|
+
const subTree = internalVNode._subTree;
|
|
407
711
|
if (subTree) unmount(subTree, container);
|
|
408
712
|
if (vnode.dom) hostRemove(vnode.dom);
|
|
409
713
|
if (vnode.props?.ref) {
|
|
@@ -413,7 +717,7 @@ function createRenderer(options) {
|
|
|
413
717
|
return;
|
|
414
718
|
}
|
|
415
719
|
if (vnode.type === Fragment$1) {
|
|
416
|
-
vnode.children.forEach((child) => unmount(child, container));
|
|
720
|
+
if (vnode.children) vnode.children.forEach((child) => unmount(child, container));
|
|
417
721
|
if (vnode.dom) hostRemove(vnode.dom);
|
|
418
722
|
return;
|
|
419
723
|
}
|
|
@@ -433,13 +737,15 @@ function createRenderer(options) {
|
|
|
433
737
|
mount(newVNode, parent, nextSibling);
|
|
434
738
|
return;
|
|
435
739
|
}
|
|
436
|
-
|
|
740
|
+
const oldInternal = oldVNode;
|
|
741
|
+
const newInternal = newVNode;
|
|
742
|
+
if (oldInternal._effect) {
|
|
437
743
|
newVNode.dom = oldVNode.dom;
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
const props =
|
|
442
|
-
|
|
744
|
+
newInternal._effect = oldInternal._effect;
|
|
745
|
+
newInternal._subTree = oldInternal._subTree;
|
|
746
|
+
newInternal._slots = oldInternal._slots;
|
|
747
|
+
const props = oldInternal._componentProps;
|
|
748
|
+
newInternal._componentProps = props;
|
|
443
749
|
if (props) {
|
|
444
750
|
const newProps$1 = newVNode.props || {};
|
|
445
751
|
untrack(() => {
|
|
@@ -449,20 +755,20 @@ function createRenderer(options) {
|
|
|
449
755
|
for (const key in props) if (!(key in newProps$1) && key !== "children" && key !== "key" && key !== "ref") delete props[key];
|
|
450
756
|
});
|
|
451
757
|
}
|
|
452
|
-
const slotsRef =
|
|
758
|
+
const slotsRef = oldInternal._slots;
|
|
453
759
|
const newChildren = newVNode.props?.children;
|
|
454
760
|
const newSlotsFromProps = newVNode.props?.slots;
|
|
455
761
|
if (slotsRef) {
|
|
456
762
|
if (newChildren !== void 0) slotsRef._children = newChildren;
|
|
457
763
|
if (newSlotsFromProps !== void 0) slotsRef._slotsFromProps = newSlotsFromProps;
|
|
458
|
-
if (!
|
|
459
|
-
|
|
764
|
+
if (!slotsRef._isPatching) {
|
|
765
|
+
slotsRef._isPatching = true;
|
|
460
766
|
try {
|
|
461
767
|
untrack(() => {
|
|
462
768
|
slotsRef._version.v++;
|
|
463
769
|
});
|
|
464
770
|
} finally {
|
|
465
|
-
|
|
771
|
+
slotsRef._isPatching = false;
|
|
466
772
|
}
|
|
467
773
|
}
|
|
468
774
|
}
|
|
@@ -576,9 +882,10 @@ function createRenderer(options) {
|
|
|
576
882
|
let exposeCalled = false;
|
|
577
883
|
const { children, slots: slotsFromProps, ...propsData } = vnode.props || {};
|
|
578
884
|
const reactiveProps = signal(propsData);
|
|
579
|
-
|
|
885
|
+
const internalVNode = vnode;
|
|
886
|
+
internalVNode._componentProps = reactiveProps;
|
|
580
887
|
const slots = createSlots(children, slotsFromProps);
|
|
581
|
-
|
|
888
|
+
internalVNode._slots = slots;
|
|
582
889
|
const mountHooks = [];
|
|
583
890
|
const cleanupHooks = [];
|
|
584
891
|
const parentInstance = getCurrentInstance();
|
|
@@ -586,7 +893,7 @@ function createRenderer(options) {
|
|
|
586
893
|
const ctx = {
|
|
587
894
|
el: container,
|
|
588
895
|
signal,
|
|
589
|
-
props: reactiveProps,
|
|
896
|
+
props: createPropsAccessor(reactiveProps),
|
|
590
897
|
slots,
|
|
591
898
|
emit: (event, ...args) => {
|
|
592
899
|
const handler = reactiveProps[`on${event[0].toUpperCase() + event.slice(1)}`];
|
|
@@ -602,8 +909,11 @@ function createRenderer(options) {
|
|
|
602
909
|
expose: (exposedValue) => {
|
|
603
910
|
exposed = exposedValue;
|
|
604
911
|
exposeCalled = true;
|
|
605
|
-
}
|
|
912
|
+
},
|
|
913
|
+
renderFn: null,
|
|
914
|
+
update: () => {}
|
|
606
915
|
};
|
|
916
|
+
applyContextExtensions(ctx);
|
|
607
917
|
ctx.__name = componentName;
|
|
608
918
|
if (currentAppContext) ctx._appContext = currentAppContext;
|
|
609
919
|
const componentInstance = {
|
|
@@ -614,7 +924,9 @@ function createRenderer(options) {
|
|
|
614
924
|
const prev = setCurrentInstance(ctx);
|
|
615
925
|
let renderFn;
|
|
616
926
|
try {
|
|
617
|
-
|
|
927
|
+
const setupResult = setup(ctx);
|
|
928
|
+
if (setupResult && typeof setupResult.then === "function") throw new Error(`Async setup in component "${componentName}" is only supported during SSR. On the client, use pre-loaded data from hydration or fetch in onMount.`);
|
|
929
|
+
renderFn = setupResult;
|
|
618
930
|
notifyComponentCreated(currentAppContext, componentInstance);
|
|
619
931
|
} catch (err) {
|
|
620
932
|
if (!handleComponentError(currentAppContext, err, componentInstance, "setup")) throw err;
|
|
@@ -626,24 +938,31 @@ function createRenderer(options) {
|
|
|
626
938
|
if (typeof vnode.props.ref === "function") vnode.props.ref(refValue);
|
|
627
939
|
else if (vnode.props.ref && typeof vnode.props.ref === "object") vnode.props.ref.current = refValue;
|
|
628
940
|
}
|
|
629
|
-
if (renderFn)
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
const
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
941
|
+
if (renderFn) {
|
|
942
|
+
ctx.renderFn = renderFn;
|
|
943
|
+
const componentEffect = effect(() => {
|
|
944
|
+
const prevInstance = setCurrentInstance(ctx);
|
|
945
|
+
try {
|
|
946
|
+
const subTreeResult = ctx.renderFn();
|
|
947
|
+
if (subTreeResult == null) return;
|
|
948
|
+
const subTree = normalizeSubTree(subTreeResult);
|
|
949
|
+
const prevSubTree = internalVNode._subTree;
|
|
950
|
+
if (prevSubTree) {
|
|
951
|
+
patch(prevSubTree, subTree, container);
|
|
952
|
+
notifyComponentUpdated(currentAppContext, componentInstance);
|
|
953
|
+
} else mount(subTree, container, anchor);
|
|
954
|
+
internalVNode._subTree = subTree;
|
|
955
|
+
} catch (err) {
|
|
956
|
+
if (!handleComponentError(currentAppContext, err, componentInstance, "render")) throw err;
|
|
957
|
+
} finally {
|
|
958
|
+
setCurrentInstance(prevInstance);
|
|
959
|
+
}
|
|
960
|
+
});
|
|
961
|
+
internalVNode._effect = componentEffect;
|
|
962
|
+
ctx.update = () => {
|
|
963
|
+
componentEffect();
|
|
964
|
+
};
|
|
965
|
+
}
|
|
647
966
|
const mountCtx = { el: container };
|
|
648
967
|
mountHooks.forEach((hook) => hook(mountCtx));
|
|
649
968
|
notifyComponentMounted(currentAppContext, componentInstance);
|
|
@@ -652,81 +971,12 @@ function createRenderer(options) {
|
|
|
652
971
|
cleanupHooks.forEach((hook) => hook(mountCtx));
|
|
653
972
|
};
|
|
654
973
|
}
|
|
655
|
-
/**
|
|
656
|
-
* Create slots object from children and slots prop.
|
|
657
|
-
* Uses a version signal to trigger re-renders when children change.
|
|
658
|
-
* Supports named slots via:
|
|
659
|
-
* - `slots` prop object (e.g., slots={{ header: () => <div>...</div> }})
|
|
660
|
-
* - `slot` prop on children (e.g., <div slot="header">...</div>)
|
|
661
|
-
*/
|
|
662
|
-
function createSlots(children, slotsFromProps) {
|
|
663
|
-
const versionSignal = signal({ v: 0 });
|
|
664
|
-
function extractNamedSlotsFromChildren(c) {
|
|
665
|
-
const defaultChildren = [];
|
|
666
|
-
const namedSlots = {};
|
|
667
|
-
if (c == null) return {
|
|
668
|
-
defaultChildren,
|
|
669
|
-
namedSlots
|
|
670
|
-
};
|
|
671
|
-
const items = Array.isArray(c) ? c : [c];
|
|
672
|
-
for (const child of items) if (child && typeof child === "object" && child.props && child.props.slot) {
|
|
673
|
-
const slotName = child.props.slot;
|
|
674
|
-
if (!namedSlots[slotName]) namedSlots[slotName] = [];
|
|
675
|
-
namedSlots[slotName].push(child);
|
|
676
|
-
} else defaultChildren.push(child);
|
|
677
|
-
return {
|
|
678
|
-
defaultChildren,
|
|
679
|
-
namedSlots
|
|
680
|
-
};
|
|
681
|
-
}
|
|
682
|
-
const slotsObj = {
|
|
683
|
-
_children: children,
|
|
684
|
-
_slotsFromProps: slotsFromProps || {},
|
|
685
|
-
_version: versionSignal,
|
|
686
|
-
default: function() {
|
|
687
|
-
this._version.v;
|
|
688
|
-
const c = this._children;
|
|
689
|
-
const { defaultChildren } = extractNamedSlotsFromChildren(c);
|
|
690
|
-
return defaultChildren;
|
|
691
|
-
}
|
|
692
|
-
};
|
|
693
|
-
return new Proxy(slotsObj, { get(target, prop) {
|
|
694
|
-
if (prop in target) return target[prop];
|
|
695
|
-
if (typeof prop === "string") return function(scopedProps) {
|
|
696
|
-
target._version.v;
|
|
697
|
-
if (target._slotsFromProps && typeof target._slotsFromProps[prop] === "function") {
|
|
698
|
-
const result = target._slotsFromProps[prop](scopedProps);
|
|
699
|
-
if (result == null) return [];
|
|
700
|
-
return Array.isArray(result) ? result : [result];
|
|
701
|
-
}
|
|
702
|
-
const { namedSlots } = extractNamedSlotsFromChildren(target._children);
|
|
703
|
-
return namedSlots[prop] || [];
|
|
704
|
-
};
|
|
705
|
-
} });
|
|
706
|
-
}
|
|
707
|
-
/**
|
|
708
|
-
* Normalize render result to a VNode (wrapping arrays in Fragment)
|
|
709
|
-
*/
|
|
710
|
-
function normalizeSubTree(result) {
|
|
711
|
-
if (Array.isArray(result)) return {
|
|
712
|
-
type: Fragment$1,
|
|
713
|
-
props: {},
|
|
714
|
-
key: null,
|
|
715
|
-
children: result,
|
|
716
|
-
dom: null
|
|
717
|
-
};
|
|
718
|
-
if (typeof result === "string" || typeof result === "number") return {
|
|
719
|
-
type: Text$1,
|
|
720
|
-
props: {},
|
|
721
|
-
key: null,
|
|
722
|
-
children: [],
|
|
723
|
-
dom: null,
|
|
724
|
-
text: result
|
|
725
|
-
};
|
|
726
|
-
return result;
|
|
727
|
-
}
|
|
728
974
|
return {
|
|
729
975
|
render: render$1,
|
|
976
|
+
patch,
|
|
977
|
+
mount,
|
|
978
|
+
unmount,
|
|
979
|
+
mountComponent,
|
|
730
980
|
createApp: (rootComponent) => {
|
|
731
981
|
return { mount(selectorOrContainer) {
|
|
732
982
|
let container = null;
|
|
@@ -811,6 +1061,60 @@ let platformSyncProcessor = null;
|
|
|
811
1061
|
function getPlatformSyncProcessor() {
|
|
812
1062
|
return platformSyncProcessor;
|
|
813
1063
|
}
|
|
1064
|
+
const plugins = [];
|
|
1065
|
+
/**
|
|
1066
|
+
* Get all registered plugins (internal use)
|
|
1067
|
+
*/
|
|
1068
|
+
function getComponentPlugins() {
|
|
1069
|
+
return plugins;
|
|
1070
|
+
}
|
|
1071
|
+
const componentRegistry = /* @__PURE__ */ new Map();
|
|
1072
|
+
/**
|
|
1073
|
+
* Define a component. Returns a JSX factory function.
|
|
1074
|
+
*
|
|
1075
|
+
* @param setup - Setup function that receives context and returns a render function
|
|
1076
|
+
* @param options - Optional configuration (e.g., name for DevTools)
|
|
1077
|
+
*
|
|
1078
|
+
* @example
|
|
1079
|
+
* ```tsx
|
|
1080
|
+
* type CardProps = DefineProp<"title", string> & DefineSlot<"header">;
|
|
1081
|
+
*
|
|
1082
|
+
* export const Card = defineComponent<CardProps>((ctx) => {
|
|
1083
|
+
* const { title } = ctx.props;
|
|
1084
|
+
* const { slots } = ctx;
|
|
1085
|
+
*
|
|
1086
|
+
* return () => (
|
|
1087
|
+
* <div class="card">
|
|
1088
|
+
* {slots.header?.() ?? <h2>{title}</h2>}
|
|
1089
|
+
* {slots.default()}
|
|
1090
|
+
* </div>
|
|
1091
|
+
* );
|
|
1092
|
+
* });
|
|
1093
|
+
* ```
|
|
1094
|
+
*/
|
|
1095
|
+
function defineComponent$1(setup, options) {
|
|
1096
|
+
const factory = function(props) {
|
|
1097
|
+
return {
|
|
1098
|
+
type: factory,
|
|
1099
|
+
props: props || {},
|
|
1100
|
+
key: props?.key || null,
|
|
1101
|
+
children: [],
|
|
1102
|
+
dom: null
|
|
1103
|
+
};
|
|
1104
|
+
};
|
|
1105
|
+
factory.__setup = setup;
|
|
1106
|
+
factory.__name = options?.name;
|
|
1107
|
+
factory.__props = null;
|
|
1108
|
+
factory.__events = null;
|
|
1109
|
+
factory.__ref = null;
|
|
1110
|
+
factory.__slots = null;
|
|
1111
|
+
componentRegistry.set(factory, {
|
|
1112
|
+
name: options?.name,
|
|
1113
|
+
setup
|
|
1114
|
+
});
|
|
1115
|
+
getComponentPlugins().forEach((p) => p.onDefine?.(options?.name, factory, setup));
|
|
1116
|
+
return factory;
|
|
1117
|
+
}
|
|
814
1118
|
const Fragment = Symbol.for("sigx.Fragment");
|
|
815
1119
|
const Text = Symbol.for("sigx.Text");
|
|
816
1120
|
function normalizeChildren(children) {
|
|
@@ -905,6 +1209,100 @@ function jsx(type, props, key) {
|
|
|
905
1209
|
function jsxs(type, props, key) {
|
|
906
1210
|
return jsx(type, props, key);
|
|
907
1211
|
}
|
|
1212
|
+
/**
|
|
1213
|
+
* Lazy loading utilities for sigx components.
|
|
1214
|
+
*
|
|
1215
|
+
* Provides runtime-only lazy loading with no build dependencies.
|
|
1216
|
+
* Works with any bundler that supports dynamic import().
|
|
1217
|
+
*/
|
|
1218
|
+
let currentSuspenseBoundary = null;
|
|
1219
|
+
/**
|
|
1220
|
+
* Register a promise with the current Suspense boundary
|
|
1221
|
+
* @internal
|
|
1222
|
+
*/
|
|
1223
|
+
function registerPendingPromise(promise) {
|
|
1224
|
+
const boundary = currentSuspenseBoundary;
|
|
1225
|
+
if (boundary) {
|
|
1226
|
+
boundary.pending.add(promise);
|
|
1227
|
+
promise.finally(() => {
|
|
1228
|
+
boundary.pending.delete(promise);
|
|
1229
|
+
if (boundary.pending.size === 0) boundary.onResolve();
|
|
1230
|
+
});
|
|
1231
|
+
return true;
|
|
1232
|
+
}
|
|
1233
|
+
return false;
|
|
1234
|
+
}
|
|
1235
|
+
/**
|
|
1236
|
+
* Suspense boundary component for handling async loading states.
|
|
1237
|
+
*
|
|
1238
|
+
* Wraps lazy-loaded components and shows a fallback while they load.
|
|
1239
|
+
*
|
|
1240
|
+
* @example
|
|
1241
|
+
* ```tsx
|
|
1242
|
+
* import { lazy, Suspense } from 'sigx';
|
|
1243
|
+
*
|
|
1244
|
+
* const LazyDashboard = lazy(() => import('./Dashboard'));
|
|
1245
|
+
*
|
|
1246
|
+
* // Basic usage
|
|
1247
|
+
* <Suspense fallback={<div>Loading...</div>}>
|
|
1248
|
+
* <LazyDashboard />
|
|
1249
|
+
* </Suspense>
|
|
1250
|
+
*
|
|
1251
|
+
* // With spinner component
|
|
1252
|
+
* <Suspense fallback={<Spinner size="large" />}>
|
|
1253
|
+
* <LazyDashboard />
|
|
1254
|
+
* <LazyCharts />
|
|
1255
|
+
* </Suspense>
|
|
1256
|
+
* ```
|
|
1257
|
+
*/
|
|
1258
|
+
const Suspense = defineComponent$1((ctx) => {
|
|
1259
|
+
const { props, slots } = ctx;
|
|
1260
|
+
const state = ctx.signal({
|
|
1261
|
+
isReady: false,
|
|
1262
|
+
pendingCount: 0
|
|
1263
|
+
});
|
|
1264
|
+
const boundary = {
|
|
1265
|
+
pending: /* @__PURE__ */ new Set(),
|
|
1266
|
+
onResolve: () => {
|
|
1267
|
+
state.pendingCount = boundary.pending.size;
|
|
1268
|
+
if (boundary.pending.size === 0) state.isReady = true;
|
|
1269
|
+
}
|
|
1270
|
+
};
|
|
1271
|
+
ctx.onMount(() => {
|
|
1272
|
+
if (boundary.pending.size === 0) state.isReady = true;
|
|
1273
|
+
});
|
|
1274
|
+
return () => {
|
|
1275
|
+
state.isReady;
|
|
1276
|
+
state.pendingCount;
|
|
1277
|
+
const prevBoundary = currentSuspenseBoundary;
|
|
1278
|
+
currentSuspenseBoundary = boundary;
|
|
1279
|
+
try {
|
|
1280
|
+
const children = slots.default();
|
|
1281
|
+
if (boundary.pending.size > 0) {
|
|
1282
|
+
const fallback = props.fallback;
|
|
1283
|
+
if (typeof fallback === "function") return fallback();
|
|
1284
|
+
return fallback ?? null;
|
|
1285
|
+
}
|
|
1286
|
+
if (Array.isArray(children)) {
|
|
1287
|
+
const filtered = children.filter((c) => c != null && c !== false && c !== true);
|
|
1288
|
+
if (filtered.length === 0) return null;
|
|
1289
|
+
if (filtered.length === 1) return filtered[0];
|
|
1290
|
+
return filtered;
|
|
1291
|
+
}
|
|
1292
|
+
return children;
|
|
1293
|
+
} catch (err) {
|
|
1294
|
+
if (err instanceof Promise) {
|
|
1295
|
+
registerPendingPromise(err);
|
|
1296
|
+
const fallback = props.fallback;
|
|
1297
|
+
if (typeof fallback === "function") return fallback();
|
|
1298
|
+
return fallback ?? null;
|
|
1299
|
+
}
|
|
1300
|
+
throw err;
|
|
1301
|
+
} finally {
|
|
1302
|
+
currentSuspenseBoundary = prevBoundary;
|
|
1303
|
+
}
|
|
1304
|
+
};
|
|
1305
|
+
}, { name: "Suspense" });
|
|
908
1306
|
|
|
909
1307
|
//#endregion
|
|
910
1308
|
//#region src/components/Input.tsx
|