@vertz/ui 0.2.1 → 0.2.2
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/css/public.js +1 -1
- package/dist/index.d.ts +63 -5
- package/dist/index.js +151 -6
- package/dist/internals.js +4 -4
- package/dist/query/public.d.ts +11 -0
- package/dist/query/public.js +2 -2
- package/dist/router/public.d.ts +13 -0
- package/dist/router/public.js +3 -3
- package/dist/shared/{chunk-nmjyj8p9.js → chunk-0xcmwgdb.js} +17 -19
- package/dist/shared/{chunk-wv6kkj1w.js → chunk-hh0dhmb4.js} +71 -7
- package/dist/shared/{chunk-vx0kzack.js → chunk-jrtrk5z4.js} +27 -5
- package/dist/shared/{chunk-cq7xg4xe.js → chunk-ka5ked7n.js} +31 -7
- package/dist/shared/{chunk-pp3a6xbn.js → chunk-n91rwj2r.js} +1 -1
- package/dist/shared/{chunk-0p5f7gmg.js → chunk-qacth5ah.js} +7 -4
- package/dist/test/index.js +2 -2
- package/package.json +3 -3
package/dist/css/public.js
CHANGED
package/dist/index.d.ts
CHANGED
|
@@ -72,6 +72,12 @@ interface Computed<T> extends ReadonlySignal<T> {
|
|
|
72
72
|
/** Dispose function returned by effect(). */
|
|
73
73
|
type DisposeFn = () => void;
|
|
74
74
|
/**
|
|
75
|
+
* A snapshot of context values at a point in time.
|
|
76
|
+
* Each Provider creates a new scope that inherits from the parent.
|
|
77
|
+
* Effects capture this scope so that useContext works in async callbacks.
|
|
78
|
+
*/
|
|
79
|
+
type ContextScope = Map<Context<unknown>, unknown>;
|
|
80
|
+
/**
|
|
75
81
|
* Props for the JSX pattern of Context.Provider.
|
|
76
82
|
*
|
|
77
83
|
* `children` accepts both raw values (what TypeScript sees in JSX) and
|
|
@@ -414,6 +420,34 @@ interface VariantFunction<V extends VariantDefinitions> {
|
|
|
414
420
|
* @returns A function that accepts variant props and returns a className string.
|
|
415
421
|
*/
|
|
416
422
|
declare function variants<V extends VariantDefinitions>(config: VariantsConfig<V>): VariantFunction<V>;
|
|
423
|
+
declare const DialogStackContext: Context<DialogStack>;
|
|
424
|
+
declare function useDialogStack(): DialogStack;
|
|
425
|
+
interface DialogHandle<TResult> {
|
|
426
|
+
close(...args: void extends TResult ? [] : [result: TResult]): void;
|
|
427
|
+
}
|
|
428
|
+
type DialogComponent<
|
|
429
|
+
TResult,
|
|
430
|
+
TProps = Record<string, never>
|
|
431
|
+
> = (props: TProps & {
|
|
432
|
+
dialog: DialogHandle<TResult>;
|
|
433
|
+
}) => Node;
|
|
434
|
+
declare class DialogDismissedError extends Error {
|
|
435
|
+
constructor();
|
|
436
|
+
}
|
|
437
|
+
interface DialogStack {
|
|
438
|
+
open<
|
|
439
|
+
TResult,
|
|
440
|
+
TProps
|
|
441
|
+
>(component: DialogComponent<TResult, TProps>, props: TProps): Promise<TResult>;
|
|
442
|
+
/** @internal — used by useDialogStack() to pass captured context scope */
|
|
443
|
+
openWithScope<
|
|
444
|
+
TResult,
|
|
445
|
+
TProps
|
|
446
|
+
>(component: DialogComponent<TResult, TProps>, props: TProps, scope: ContextScope | null): Promise<TResult>;
|
|
447
|
+
readonly size: number;
|
|
448
|
+
closeAll(): void;
|
|
449
|
+
}
|
|
450
|
+
declare function createDialogStack(container: HTMLElement): DialogStack;
|
|
417
451
|
/**
|
|
418
452
|
* Brand symbol for render nodes.
|
|
419
453
|
* SSR nodes add this to their prototype for fast identification.
|
|
@@ -729,6 +763,17 @@ interface QueryOptions<T> {
|
|
|
729
763
|
cache?: CacheStore<T>;
|
|
730
764
|
/** Timeout in ms for SSR data loading. Default: 300. Set to 0 to disable. */
|
|
731
765
|
ssrTimeout?: number;
|
|
766
|
+
/**
|
|
767
|
+
* Polling interval in ms, or a function for dynamic intervals.
|
|
768
|
+
*
|
|
769
|
+
* - `number` — fixed interval in ms
|
|
770
|
+
* - `false` or `0` — disabled
|
|
771
|
+
* - `(data, iteration) => number | false` — called after each fetch to
|
|
772
|
+
* determine the next interval. Return `false` to stop polling.
|
|
773
|
+
* `iteration` counts polls since the last start/restart (resets to 0
|
|
774
|
+
* when the function returns `false`).
|
|
775
|
+
*/
|
|
776
|
+
refetchInterval?: number | false | ((data: T | undefined, iteration: number) => number | false);
|
|
732
777
|
}
|
|
733
778
|
/** The reactive object returned by query(). */
|
|
734
779
|
interface QueryResult<
|
|
@@ -1004,6 +1049,8 @@ interface PrefetchHandle {
|
|
|
1004
1049
|
abort: () => void;
|
|
1005
1050
|
/** Resolves when SSE stream completes (data or done event). */
|
|
1006
1051
|
done?: Promise<void>;
|
|
1052
|
+
/** Resolves when the first SSE event of any type arrives. */
|
|
1053
|
+
firstEvent?: Promise<void>;
|
|
1007
1054
|
}
|
|
1008
1055
|
/** Options for createRouter(). */
|
|
1009
1056
|
interface RouterOptions {
|
|
@@ -1084,6 +1131,17 @@ interface RouterViewProps {
|
|
|
1084
1131
|
router: Router;
|
|
1085
1132
|
fallback?: () => Node;
|
|
1086
1133
|
}
|
|
1134
|
+
/**
|
|
1135
|
+
* Renders the matched route's component inside a container div.
|
|
1136
|
+
*
|
|
1137
|
+
* Handles sync and async (lazy-loaded) components, stale resolution guards,
|
|
1138
|
+
* page cleanup on navigation, and RouterContext propagation.
|
|
1139
|
+
*
|
|
1140
|
+
* Uses __element() so the container is claimed from SSR during hydration.
|
|
1141
|
+
* On the first hydration render, children are already in the DOM — the
|
|
1142
|
+
* domEffect runs the component factory (to attach reactivity/event handlers)
|
|
1143
|
+
* but skips clearing the container.
|
|
1144
|
+
*/
|
|
1087
1145
|
declare function RouterView({ router, fallback }: RouterViewProps): HTMLElement;
|
|
1088
1146
|
/**
|
|
1089
1147
|
* Parse URLSearchParams into a typed object, optionally through a schema.
|
|
@@ -1149,7 +1207,7 @@ interface EntityStoreOptions {
|
|
|
1149
1207
|
}
|
|
1150
1208
|
/**
|
|
1151
1209
|
* EntityStore - Normalized, signal-backed entity cache for @vertz/ui.
|
|
1152
|
-
*
|
|
1210
|
+
*
|
|
1153
1211
|
* Stores entities by type and ID, with signal-per-entity reactivity.
|
|
1154
1212
|
* Supports field-level merge, SSR hydration, and query result indices.
|
|
1155
1213
|
*/
|
|
@@ -1209,10 +1267,10 @@ declare class EntityStore {
|
|
|
1209
1267
|
}
|
|
1210
1268
|
/**
|
|
1211
1269
|
* Create a pre-populated EntityStore for testing.
|
|
1212
|
-
*
|
|
1270
|
+
*
|
|
1213
1271
|
* @param data - Entity data keyed by type → id → entity
|
|
1214
1272
|
* @returns A new EntityStore with the data already merged
|
|
1215
|
-
*
|
|
1273
|
+
*
|
|
1216
1274
|
* @example
|
|
1217
1275
|
* ```ts
|
|
1218
1276
|
* const store = createTestStore({
|
|
@@ -1221,9 +1279,9 @@ declare class EntityStore {
|
|
|
1221
1279
|
* '2': { id: '2', name: 'Bob' }
|
|
1222
1280
|
* }
|
|
1223
1281
|
* });
|
|
1224
|
-
*
|
|
1282
|
+
*
|
|
1225
1283
|
* expect(store.get('User', '1').value).toEqual({ id: '1', name: 'Alice' });
|
|
1226
1284
|
* ```
|
|
1227
1285
|
*/
|
|
1228
1286
|
declare function createTestStore(data: Record<string, Record<string, unknown>>): EntityStore;
|
|
1229
|
-
export { zoomOut, zoomIn, variants, validate, useSearchParams, useRouter, useParams, useContext, untrack, slideOutToTop, slideOutToRight, slideOutToLeft, slideOutToBottom, slideInFromTop, slideInFromRight, slideInFromLeft, slideInFromBottom, signal, setAdapter, s, resolveChildren, resetInjectedStyles, ref, queryMatch, query, parseSearchParams, palettes, onMount2 as onMount, mount, keyframes, isRenderNode, isQueryDescriptor, injectCSS, hydrate, globalCss, getInjectedCSS, getAdapter, formDataToObject, form, fadeOut, fadeIn, defineTheme, defineRoutes, css, createTestStore, createRouter, createLink, createFieldState, createDOMAdapter, createContext, computed, compileTheme, children, batch, accordionUp, accordionDown, __staticText, __exitChildren, __enterChildren, __element, __append, VariantsConfig, VariantProps, VariantFunction, ValidationResult, UnwrapSignals, TypedRoutes, TypedRouter, ThemeProviderProps, ThemeProvider, ThemeInput, Theme, SuspenseProps, Suspense, StyleValue, StyleEntry, Signal, SerializedStore, SearchParamSchema, SdkMethodWithMeta, SdkMethod, RouterViewProps, RouterView, RouterOptions, RouterContext, Router, RoutePaths, RouteMatch, RouteDefinitionMap, RouteConfig, RenderText, RenderNode, RenderElement, RenderAdapter, Ref, ReadonlySignal, RawDeclaration, RENDER_NODE_BRAND, QueryResult, QueryOptions, QueryMatchHandlers, QueryDescriptor2 as QueryDescriptor, PresenceProps, Presence, PathWithParams, OutletContextValue, OutletContext, Outlet, NavigateOptions, MountOptions, MountHandle, MatchedRoute, LoaderData, LinkProps, LinkFactoryOptions, InferRouteMap, GlobalCSSOutput, GlobalCSSInput, FormSchema, FormOptions, FormInstance, FormDataOptions, FieldState, ExtractParams, ErrorBoundaryProps, ErrorBoundary, EntityStoreOptions, EntityStore, DisposeFn, DisposalScopeError, Context, Computed, ComponentRegistry, ComponentLoader, ComponentFunction, CompiledTheme, CompiledRoute, ColorPalette, ChildrenAccessor, ChildValue, CacheStore, CSSOutput, CSSInput, ANIMATION_EASING, ANIMATION_DURATION };
|
|
1287
|
+
export { zoomOut, zoomIn, variants, validate, useSearchParams, useRouter, useParams, useDialogStack, useContext, untrack, slideOutToTop, slideOutToRight, slideOutToLeft, slideOutToBottom, slideInFromTop, slideInFromRight, slideInFromLeft, slideInFromBottom, signal, setAdapter, s, resolveChildren, resetInjectedStyles, ref, queryMatch, query, parseSearchParams, palettes, onMount2 as onMount, mount, keyframes, isRenderNode, isQueryDescriptor, injectCSS, hydrate, globalCss, getInjectedCSS, getAdapter, formDataToObject, form, fadeOut, fadeIn, defineTheme, defineRoutes, css, createTestStore, createRouter, createLink, createFieldState, createDialogStack, createDOMAdapter, createContext, computed, compileTheme, children, batch, accordionUp, accordionDown, __staticText, __exitChildren, __enterChildren, __element, __append, VariantsConfig, VariantProps, VariantFunction, ValidationResult, UnwrapSignals, TypedRoutes, TypedRouter, ThemeProviderProps, ThemeProvider, ThemeInput, Theme, SuspenseProps, Suspense, StyleValue, StyleEntry, Signal, SerializedStore, SearchParamSchema, SdkMethodWithMeta, SdkMethod, RouterViewProps, RouterView, RouterOptions, RouterContext, Router, RoutePaths, RouteMatch, RouteDefinitionMap, RouteConfig, RenderText, RenderNode, RenderElement, RenderAdapter, Ref, ReadonlySignal, RawDeclaration, RENDER_NODE_BRAND, QueryResult, QueryOptions, QueryMatchHandlers, QueryDescriptor2 as QueryDescriptor, PresenceProps, Presence, PathWithParams, OutletContextValue, OutletContext, Outlet, NavigateOptions, MountOptions, MountHandle, MatchedRoute, LoaderData, LinkProps, LinkFactoryOptions, InferRouteMap, GlobalCSSOutput, GlobalCSSInput, FormSchema, FormOptions, FormInstance, FormDataOptions, FieldState, ExtractParams, ErrorBoundaryProps, ErrorBoundary, EntityStoreOptions, EntityStore, DisposeFn, DisposalScopeError, DialogStackContext, DialogStack, DialogHandle, DialogDismissedError, DialogComponent, Context, Computed, ComponentRegistry, ComponentLoader, ComponentFunction, CompiledTheme, CompiledRoute, ColorPalette, ChildrenAccessor, ChildValue, CacheStore, CSSOutput, CSSInput, ANIMATION_EASING, ANIMATION_DURATION };
|
package/dist/index.js
CHANGED
|
@@ -20,7 +20,7 @@ import {
|
|
|
20
20
|
slideOutToTop,
|
|
21
21
|
zoomIn,
|
|
22
22
|
zoomOut
|
|
23
|
-
} from "./shared/chunk-
|
|
23
|
+
} from "./shared/chunk-n91rwj2r.js";
|
|
24
24
|
import {
|
|
25
25
|
Outlet,
|
|
26
26
|
OutletContext,
|
|
@@ -31,11 +31,11 @@ import {
|
|
|
31
31
|
useParams,
|
|
32
32
|
useRouter,
|
|
33
33
|
useSearchParams
|
|
34
|
-
} from "./shared/chunk-
|
|
34
|
+
} from "./shared/chunk-0xcmwgdb.js";
|
|
35
35
|
import"./shared/chunk-v3yyf79g.js";
|
|
36
36
|
import {
|
|
37
37
|
createRouter
|
|
38
|
-
} from "./shared/chunk-
|
|
38
|
+
} from "./shared/chunk-ka5ked7n.js";
|
|
39
39
|
import {
|
|
40
40
|
defineRoutes
|
|
41
41
|
} from "./shared/chunk-9e92w0wt.js";
|
|
@@ -48,8 +48,8 @@ import {
|
|
|
48
48
|
import {
|
|
49
49
|
query,
|
|
50
50
|
queryMatch
|
|
51
|
-
} from "./shared/chunk-
|
|
52
|
-
import"./shared/chunk-
|
|
51
|
+
} from "./shared/chunk-hh0dhmb4.js";
|
|
52
|
+
import"./shared/chunk-jrtrk5z4.js";
|
|
53
53
|
import {
|
|
54
54
|
ThemeProvider,
|
|
55
55
|
children,
|
|
@@ -63,7 +63,7 @@ import {
|
|
|
63
63
|
resolveChildren,
|
|
64
64
|
s,
|
|
65
65
|
variants
|
|
66
|
-
} from "./shared/chunk-
|
|
66
|
+
} from "./shared/chunk-qacth5ah.js";
|
|
67
67
|
import {
|
|
68
68
|
__append,
|
|
69
69
|
__element,
|
|
@@ -87,9 +87,11 @@ import {
|
|
|
87
87
|
computed,
|
|
88
88
|
createContext,
|
|
89
89
|
domEffect,
|
|
90
|
+
getContextScope,
|
|
90
91
|
popScope,
|
|
91
92
|
pushScope,
|
|
92
93
|
runCleanups,
|
|
94
|
+
setContextScope,
|
|
93
95
|
signal,
|
|
94
96
|
untrack,
|
|
95
97
|
useContext
|
|
@@ -290,6 +292,145 @@ function Suspense(props) {
|
|
|
290
292
|
return placeholder;
|
|
291
293
|
}
|
|
292
294
|
}
|
|
295
|
+
// src/dialog/dialog-stack.ts
|
|
296
|
+
var DialogStackContext = createContext();
|
|
297
|
+
function useDialogStack() {
|
|
298
|
+
const stack = useContext(DialogStackContext);
|
|
299
|
+
if (!stack) {
|
|
300
|
+
throw new Error("useDialogStack() must be called within DialogStackProvider");
|
|
301
|
+
}
|
|
302
|
+
const capturedScope = getContextScope();
|
|
303
|
+
return {
|
|
304
|
+
open(component, props) {
|
|
305
|
+
return stack.openWithScope(component, props, capturedScope);
|
|
306
|
+
},
|
|
307
|
+
openWithScope: stack.openWithScope,
|
|
308
|
+
get size() {
|
|
309
|
+
return stack.size;
|
|
310
|
+
},
|
|
311
|
+
closeAll() {
|
|
312
|
+
stack.closeAll();
|
|
313
|
+
}
|
|
314
|
+
};
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
class DialogDismissedError extends Error {
|
|
318
|
+
constructor() {
|
|
319
|
+
super("Dialog was dismissed");
|
|
320
|
+
this.name = "DialogDismissedError";
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
function createDialogStack(container) {
|
|
324
|
+
const entries = [];
|
|
325
|
+
let nextId = 0;
|
|
326
|
+
function open(component, props, capturedScope) {
|
|
327
|
+
return new Promise((resolve, reject) => {
|
|
328
|
+
if (entries.length > 0) {
|
|
329
|
+
entries[entries.length - 1].wrapper.setAttribute("data-state", "background");
|
|
330
|
+
}
|
|
331
|
+
const wrapper = document.createElement("div");
|
|
332
|
+
wrapper.setAttribute("data-dialog-wrapper", "");
|
|
333
|
+
wrapper.setAttribute("data-state", "open");
|
|
334
|
+
wrapper.setAttribute("data-dialog-depth", "0");
|
|
335
|
+
const entry = {
|
|
336
|
+
id: nextId++,
|
|
337
|
+
wrapper,
|
|
338
|
+
node: null,
|
|
339
|
+
resolve,
|
|
340
|
+
reject,
|
|
341
|
+
cleanups: [],
|
|
342
|
+
dismissible: true
|
|
343
|
+
};
|
|
344
|
+
const prevScope = setContextScope(capturedScope ?? null);
|
|
345
|
+
const scope = pushScope();
|
|
346
|
+
const handle = {
|
|
347
|
+
close: (...args) => {
|
|
348
|
+
closeEntry(entry, args[0]);
|
|
349
|
+
}
|
|
350
|
+
};
|
|
351
|
+
entry.node = component({ ...props, dialog: handle });
|
|
352
|
+
entry.cleanups = [...scope];
|
|
353
|
+
popScope();
|
|
354
|
+
setContextScope(prevScope);
|
|
355
|
+
if (entry.dismissible) {
|
|
356
|
+
wrapper.addEventListener("keydown", (e) => {
|
|
357
|
+
if (e.key === "Escape" && entries[entries.length - 1] === entry) {
|
|
358
|
+
e.preventDefault();
|
|
359
|
+
e.stopPropagation();
|
|
360
|
+
dismissEntry(entry);
|
|
361
|
+
}
|
|
362
|
+
});
|
|
363
|
+
}
|
|
364
|
+
wrapper.appendChild(entry.node);
|
|
365
|
+
container.appendChild(wrapper);
|
|
366
|
+
entries.push(entry);
|
|
367
|
+
updateDepthAttributes();
|
|
368
|
+
});
|
|
369
|
+
}
|
|
370
|
+
function closeEntry(entry, result) {
|
|
371
|
+
const idx = entries.indexOf(entry);
|
|
372
|
+
if (idx === -1)
|
|
373
|
+
return;
|
|
374
|
+
entry.wrapper.setAttribute("data-state", "closed");
|
|
375
|
+
onAnimationsComplete(entry.wrapper, () => {
|
|
376
|
+
runCleanups(entry.cleanups);
|
|
377
|
+
if (entry.wrapper.parentNode === container) {
|
|
378
|
+
container.removeChild(entry.wrapper);
|
|
379
|
+
}
|
|
380
|
+
const entryIdx = entries.indexOf(entry);
|
|
381
|
+
if (entryIdx !== -1) {
|
|
382
|
+
entries.splice(entryIdx, 1);
|
|
383
|
+
}
|
|
384
|
+
if (entries.length > 0) {
|
|
385
|
+
entries[entries.length - 1].wrapper.setAttribute("data-state", "open");
|
|
386
|
+
}
|
|
387
|
+
updateDepthAttributes();
|
|
388
|
+
entry.resolve(result);
|
|
389
|
+
});
|
|
390
|
+
}
|
|
391
|
+
function updateDepthAttributes() {
|
|
392
|
+
for (let i = 0;i < entries.length; i++) {
|
|
393
|
+
entries[i].wrapper.setAttribute("data-dialog-depth", String(entries.length - 1 - i));
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
return {
|
|
397
|
+
open(component, props) {
|
|
398
|
+
return open(component, props);
|
|
399
|
+
},
|
|
400
|
+
openWithScope(component, props, scope) {
|
|
401
|
+
return open(component, props, scope);
|
|
402
|
+
},
|
|
403
|
+
get size() {
|
|
404
|
+
return entries.length;
|
|
405
|
+
},
|
|
406
|
+
closeAll() {
|
|
407
|
+
for (let i = entries.length - 1;i >= 0; i--) {
|
|
408
|
+
dismissEntry(entries[i]);
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
};
|
|
412
|
+
function dismissEntry(entry) {
|
|
413
|
+
const idx = entries.indexOf(entry);
|
|
414
|
+
if (idx === -1)
|
|
415
|
+
return;
|
|
416
|
+
entry.wrapper.setAttribute("data-state", "closed");
|
|
417
|
+
onAnimationsComplete(entry.wrapper, () => {
|
|
418
|
+
runCleanups(entry.cleanups);
|
|
419
|
+
if (entry.wrapper.parentNode === container) {
|
|
420
|
+
container.removeChild(entry.wrapper);
|
|
421
|
+
}
|
|
422
|
+
const entryIdx = entries.indexOf(entry);
|
|
423
|
+
if (entryIdx !== -1) {
|
|
424
|
+
entries.splice(entryIdx, 1);
|
|
425
|
+
}
|
|
426
|
+
if (entries.length > 0) {
|
|
427
|
+
entries[entries.length - 1].wrapper.setAttribute("data-state", "open");
|
|
428
|
+
}
|
|
429
|
+
updateDepthAttributes();
|
|
430
|
+
entry.reject(new DialogDismissedError);
|
|
431
|
+
});
|
|
432
|
+
}
|
|
433
|
+
}
|
|
293
434
|
// src/mount.ts
|
|
294
435
|
function mount(app, selector, options) {
|
|
295
436
|
if (typeof selector !== "string" && !(selector instanceof HTMLElement)) {
|
|
@@ -568,6 +709,7 @@ export {
|
|
|
568
709
|
useSearchParams,
|
|
569
710
|
useRouter,
|
|
570
711
|
useParams,
|
|
712
|
+
useDialogStack,
|
|
571
713
|
useContext,
|
|
572
714
|
untrack,
|
|
573
715
|
slideOutToTop,
|
|
@@ -609,6 +751,7 @@ export {
|
|
|
609
751
|
createRouter,
|
|
610
752
|
createLink,
|
|
611
753
|
createFieldState,
|
|
754
|
+
createDialogStack,
|
|
612
755
|
createDOMAdapter,
|
|
613
756
|
createContext,
|
|
614
757
|
computed,
|
|
@@ -633,6 +776,8 @@ export {
|
|
|
633
776
|
ErrorBoundary,
|
|
634
777
|
EntityStore,
|
|
635
778
|
DisposalScopeError,
|
|
779
|
+
DialogStackContext,
|
|
780
|
+
DialogDismissedError,
|
|
636
781
|
ANIMATION_EASING,
|
|
637
782
|
ANIMATION_DURATION
|
|
638
783
|
};
|
package/dist/internals.js
CHANGED
|
@@ -2,7 +2,7 @@ import {
|
|
|
2
2
|
deserializeProps,
|
|
3
3
|
onAnimationsComplete,
|
|
4
4
|
resolveComponent
|
|
5
|
-
} from "./shared/chunk-
|
|
5
|
+
} from "./shared/chunk-n91rwj2r.js";
|
|
6
6
|
import {
|
|
7
7
|
__attr,
|
|
8
8
|
__classList,
|
|
@@ -17,8 +17,8 @@ import {
|
|
|
17
17
|
import {
|
|
18
18
|
MemoryCache,
|
|
19
19
|
deriveKey
|
|
20
|
-
} from "./shared/chunk-
|
|
21
|
-
import"./shared/chunk-
|
|
20
|
+
} from "./shared/chunk-hh0dhmb4.js";
|
|
21
|
+
import"./shared/chunk-jrtrk5z4.js";
|
|
22
22
|
import {
|
|
23
23
|
ALIGNMENT_MAP,
|
|
24
24
|
COLOR_NAMESPACES,
|
|
@@ -38,7 +38,7 @@ import {
|
|
|
38
38
|
SIZE_KEYWORDS,
|
|
39
39
|
SPACING_SCALE,
|
|
40
40
|
compileTheme
|
|
41
|
-
} from "./shared/chunk-
|
|
41
|
+
} from "./shared/chunk-qacth5ah.js";
|
|
42
42
|
import {
|
|
43
43
|
__append,
|
|
44
44
|
__child,
|
package/dist/query/public.d.ts
CHANGED
|
@@ -45,6 +45,17 @@ interface QueryOptions<T> {
|
|
|
45
45
|
cache?: CacheStore<T>;
|
|
46
46
|
/** Timeout in ms for SSR data loading. Default: 300. Set to 0 to disable. */
|
|
47
47
|
ssrTimeout?: number;
|
|
48
|
+
/**
|
|
49
|
+
* Polling interval in ms, or a function for dynamic intervals.
|
|
50
|
+
*
|
|
51
|
+
* - `number` — fixed interval in ms
|
|
52
|
+
* - `false` or `0` — disabled
|
|
53
|
+
* - `(data, iteration) => number | false` — called after each fetch to
|
|
54
|
+
* determine the next interval. Return `false` to stop polling.
|
|
55
|
+
* `iteration` counts polls since the last start/restart (resets to 0
|
|
56
|
+
* when the function returns `false`).
|
|
57
|
+
*/
|
|
58
|
+
refetchInterval?: number | false | ((data: T | undefined, iteration: number) => number | false);
|
|
48
59
|
}
|
|
49
60
|
/** The reactive object returned by query(). */
|
|
50
61
|
interface QueryResult<
|
package/dist/query/public.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import {
|
|
2
2
|
query,
|
|
3
3
|
queryMatch
|
|
4
|
-
} from "../shared/chunk-
|
|
5
|
-
import"../shared/chunk-
|
|
4
|
+
} from "../shared/chunk-hh0dhmb4.js";
|
|
5
|
+
import"../shared/chunk-jrtrk5z4.js";
|
|
6
6
|
import"../shared/chunk-g4rch80a.js";
|
|
7
7
|
import"../shared/chunk-hrd0mft1.js";
|
|
8
8
|
|
package/dist/router/public.d.ts
CHANGED
|
@@ -251,6 +251,8 @@ interface PrefetchHandle {
|
|
|
251
251
|
abort: () => void;
|
|
252
252
|
/** Resolves when SSE stream completes (data or done event). */
|
|
253
253
|
done?: Promise<void>;
|
|
254
|
+
/** Resolves when the first SSE event of any type arrives. */
|
|
255
|
+
firstEvent?: Promise<void>;
|
|
254
256
|
}
|
|
255
257
|
/** Options for createRouter(). */
|
|
256
258
|
interface RouterOptions {
|
|
@@ -354,6 +356,17 @@ interface RouterViewProps {
|
|
|
354
356
|
router: Router;
|
|
355
357
|
fallback?: () => Node;
|
|
356
358
|
}
|
|
359
|
+
/**
|
|
360
|
+
* Renders the matched route's component inside a container div.
|
|
361
|
+
*
|
|
362
|
+
* Handles sync and async (lazy-loaded) components, stale resolution guards,
|
|
363
|
+
* page cleanup on navigation, and RouterContext propagation.
|
|
364
|
+
*
|
|
365
|
+
* Uses __element() so the container is claimed from SSR during hydration.
|
|
366
|
+
* On the first hydration render, children are already in the DOM — the
|
|
367
|
+
* domEffect runs the component factory (to attach reactivity/event handlers)
|
|
368
|
+
* but skips clearing the container.
|
|
369
|
+
*/
|
|
357
370
|
declare function RouterView({ router, fallback }: RouterViewProps): HTMLElement;
|
|
358
371
|
/**
|
|
359
372
|
* Parse URLSearchParams into a typed object, optionally through a schema.
|
package/dist/router/public.js
CHANGED
|
@@ -8,15 +8,15 @@ import {
|
|
|
8
8
|
useParams,
|
|
9
9
|
useRouter,
|
|
10
10
|
useSearchParams
|
|
11
|
-
} from "../shared/chunk-
|
|
11
|
+
} from "../shared/chunk-0xcmwgdb.js";
|
|
12
12
|
import"../shared/chunk-v3yyf79g.js";
|
|
13
13
|
import {
|
|
14
14
|
createRouter
|
|
15
|
-
} from "../shared/chunk-
|
|
15
|
+
} from "../shared/chunk-ka5ked7n.js";
|
|
16
16
|
import {
|
|
17
17
|
defineRoutes
|
|
18
18
|
} from "../shared/chunk-9e92w0wt.js";
|
|
19
|
-
import"../shared/chunk-
|
|
19
|
+
import"../shared/chunk-jrtrk5z4.js";
|
|
20
20
|
import"../shared/chunk-ryb49346.js";
|
|
21
21
|
import"../shared/chunk-g4rch80a.js";
|
|
22
22
|
import"../shared/chunk-hrd0mft1.js";
|
|
@@ -23,6 +23,17 @@ import {
|
|
|
23
23
|
} from "./chunk-hrd0mft1.js";
|
|
24
24
|
|
|
25
25
|
// src/router/link.ts
|
|
26
|
+
var DANGEROUS_SCHEMES = ["javascript:", "data:", "vbscript:"];
|
|
27
|
+
function isSafeUrl(url) {
|
|
28
|
+
const normalized = url.replace(/\s/g, "").toLowerCase();
|
|
29
|
+
if (normalized.startsWith("//"))
|
|
30
|
+
return false;
|
|
31
|
+
for (const scheme of DANGEROUS_SCHEMES) {
|
|
32
|
+
if (normalized.startsWith(scheme))
|
|
33
|
+
return false;
|
|
34
|
+
}
|
|
35
|
+
return true;
|
|
36
|
+
}
|
|
26
37
|
function createLink(currentPath, navigate, factoryOptions) {
|
|
27
38
|
return function Link({
|
|
28
39
|
href,
|
|
@@ -31,15 +42,16 @@ function createLink(currentPath, navigate, factoryOptions) {
|
|
|
31
42
|
className,
|
|
32
43
|
prefetch
|
|
33
44
|
}) {
|
|
45
|
+
const safeHref = isSafeUrl(href) ? href : "#";
|
|
34
46
|
const handleClick = (event) => {
|
|
35
47
|
const mouseEvent = event;
|
|
36
48
|
if (mouseEvent.ctrlKey || mouseEvent.metaKey || mouseEvent.shiftKey || mouseEvent.altKey) {
|
|
37
49
|
return;
|
|
38
50
|
}
|
|
39
51
|
mouseEvent.preventDefault();
|
|
40
|
-
navigate(
|
|
52
|
+
navigate(safeHref);
|
|
41
53
|
};
|
|
42
|
-
const props = { href };
|
|
54
|
+
const props = { href: safeHref };
|
|
43
55
|
if (className) {
|
|
44
56
|
props.class = className;
|
|
45
57
|
}
|
|
@@ -59,7 +71,7 @@ function createLink(currentPath, navigate, factoryOptions) {
|
|
|
59
71
|
__exitChildren();
|
|
60
72
|
if (activeClass) {
|
|
61
73
|
__classList(el, {
|
|
62
|
-
[activeClass]: () => currentPath.value ===
|
|
74
|
+
[activeClass]: () => currentPath.value === safeHref
|
|
63
75
|
});
|
|
64
76
|
}
|
|
65
77
|
if (prefetch === "hover" && factoryOptions?.onPrefetch) {
|
|
@@ -68,7 +80,7 @@ function createLink(currentPath, navigate, factoryOptions) {
|
|
|
68
80
|
if (prefetched)
|
|
69
81
|
return;
|
|
70
82
|
prefetched = true;
|
|
71
|
-
factoryOptions.onPrefetch?.(
|
|
83
|
+
factoryOptions.onPrefetch?.(safeHref);
|
|
72
84
|
};
|
|
73
85
|
__on(el, "mouseenter", triggerPrefetch);
|
|
74
86
|
__on(el, "focus", triggerPrefetch);
|
|
@@ -152,16 +164,6 @@ function Outlet() {
|
|
|
152
164
|
}
|
|
153
165
|
|
|
154
166
|
// src/router/router-view.ts
|
|
155
|
-
function hasViewTransition(doc) {
|
|
156
|
-
return "startViewTransition" in doc;
|
|
157
|
-
}
|
|
158
|
-
function withTransition(fn) {
|
|
159
|
-
if (typeof document !== "undefined" && hasViewTransition(document)) {
|
|
160
|
-
document.startViewTransition(fn);
|
|
161
|
-
} else {
|
|
162
|
-
fn();
|
|
163
|
-
}
|
|
164
|
-
}
|
|
165
167
|
function RouterView({ router, fallback }) {
|
|
166
168
|
const container = __element("div");
|
|
167
169
|
let isFirstHydrationRender = getIsHydrating();
|
|
@@ -230,11 +232,7 @@ function RouterView({ router, fallback }) {
|
|
|
230
232
|
prevLevels = levels;
|
|
231
233
|
popScope();
|
|
232
234
|
};
|
|
233
|
-
|
|
234
|
-
doRender();
|
|
235
|
-
} else {
|
|
236
|
-
withTransition(doRender);
|
|
237
|
-
}
|
|
235
|
+
doRender();
|
|
238
236
|
});
|
|
239
237
|
});
|
|
240
238
|
__exitChildren();
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
isNavPrefetchActive
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-jrtrk5z4.js";
|
|
4
4
|
import {
|
|
5
5
|
getAdapter,
|
|
6
6
|
isRenderNode
|
|
@@ -51,20 +51,24 @@ function hashString(str) {
|
|
|
51
51
|
import { isQueryDescriptor } from "@vertz/fetch";
|
|
52
52
|
|
|
53
53
|
// src/query/ssr-hydration.ts
|
|
54
|
-
function hydrateQueryFromSSR(key, resolve) {
|
|
54
|
+
function hydrateQueryFromSSR(key, resolve, options) {
|
|
55
55
|
const ssrData = globalThis.__VERTZ_SSR_DATA__;
|
|
56
56
|
if (!ssrData)
|
|
57
57
|
return null;
|
|
58
|
+
const persistent = options?.persistent ?? false;
|
|
58
59
|
const existing = ssrData.find((entry) => entry.key === key);
|
|
59
60
|
if (existing) {
|
|
60
61
|
resolve(existing.data);
|
|
61
|
-
|
|
62
|
+
if (!persistent)
|
|
63
|
+
return () => {};
|
|
62
64
|
}
|
|
63
65
|
const handler = (event) => {
|
|
64
66
|
const detail = event.detail;
|
|
65
67
|
if (detail.key === key) {
|
|
66
68
|
resolve(detail.data);
|
|
67
|
-
|
|
69
|
+
if (!persistent) {
|
|
70
|
+
document.removeEventListener("vertz:ssr-data", handler);
|
|
71
|
+
}
|
|
68
72
|
}
|
|
69
73
|
};
|
|
70
74
|
document.addEventListener("vertz:ssr-data", handler);
|
|
@@ -169,12 +173,13 @@ function query(source, options = {}) {
|
|
|
169
173
|
let navPrefetchDeferred = false;
|
|
170
174
|
if (!isSSR() && enabled && initialData === undefined) {
|
|
171
175
|
const hydrationKey = customKey ?? baseKey;
|
|
176
|
+
const isNavigation = isNavPrefetchActive();
|
|
172
177
|
ssrHydrationCleanup = hydrateQueryFromSSR(hydrationKey, (result) => {
|
|
173
178
|
data.value = result;
|
|
174
179
|
loading.value = false;
|
|
175
180
|
cache.set(hydrationKey, result);
|
|
176
181
|
ssrHydrated = true;
|
|
177
|
-
});
|
|
182
|
+
}, { persistent: isNavigation });
|
|
178
183
|
if (!ssrHydrated && ssrHydrationCleanup !== null && isNavPrefetchActive()) {
|
|
179
184
|
if (customKey) {
|
|
180
185
|
const cached = cache.get(customKey);
|
|
@@ -189,7 +194,7 @@ function query(source, options = {}) {
|
|
|
189
194
|
navPrefetchDeferred = true;
|
|
190
195
|
const doneHandler = () => {
|
|
191
196
|
document.removeEventListener("vertz:nav-prefetch-done", doneHandler);
|
|
192
|
-
if (data.peek() === undefined) {
|
|
197
|
+
if (data.peek() === undefined && !ssrHydrated) {
|
|
193
198
|
refetchTrigger.value = refetchTrigger.peek() + 1;
|
|
194
199
|
}
|
|
195
200
|
};
|
|
@@ -203,8 +208,45 @@ function query(source, options = {}) {
|
|
|
203
208
|
}
|
|
204
209
|
let fetchId = 0;
|
|
205
210
|
let debounceTimer;
|
|
211
|
+
let intervalTimer;
|
|
212
|
+
const refetchIntervalOption = options.refetchInterval;
|
|
213
|
+
const hasInterval = typeof refetchIntervalOption === "function" || typeof refetchIntervalOption === "number" && refetchIntervalOption > 0;
|
|
214
|
+
let intervalIteration = 0;
|
|
206
215
|
const inflightKeys = new Set;
|
|
207
216
|
const refetchTrigger = signal(0);
|
|
217
|
+
let intervalPaused = false;
|
|
218
|
+
function scheduleInterval() {
|
|
219
|
+
if (!hasInterval || isSSR() || intervalPaused)
|
|
220
|
+
return;
|
|
221
|
+
let ms;
|
|
222
|
+
if (typeof refetchIntervalOption === "function") {
|
|
223
|
+
ms = refetchIntervalOption(data.peek(), intervalIteration);
|
|
224
|
+
} else {
|
|
225
|
+
ms = refetchIntervalOption;
|
|
226
|
+
}
|
|
227
|
+
if (ms === false || ms <= 0) {
|
|
228
|
+
intervalIteration = 0;
|
|
229
|
+
return;
|
|
230
|
+
}
|
|
231
|
+
intervalIteration++;
|
|
232
|
+
clearTimeout(intervalTimer);
|
|
233
|
+
intervalTimer = setTimeout(() => {
|
|
234
|
+
refetch();
|
|
235
|
+
}, ms);
|
|
236
|
+
}
|
|
237
|
+
let visibilityHandler;
|
|
238
|
+
if (hasInterval && enabled && !isSSR() && typeof document !== "undefined") {
|
|
239
|
+
visibilityHandler = () => {
|
|
240
|
+
if (document.visibilityState === "hidden") {
|
|
241
|
+
intervalPaused = true;
|
|
242
|
+
clearTimeout(intervalTimer);
|
|
243
|
+
} else {
|
|
244
|
+
intervalPaused = false;
|
|
245
|
+
refetch();
|
|
246
|
+
}
|
|
247
|
+
};
|
|
248
|
+
document.addEventListener("visibilitychange", visibilityHandler);
|
|
249
|
+
}
|
|
208
250
|
function handleFetchPromise(promise, id, key) {
|
|
209
251
|
promise.then((result) => {
|
|
210
252
|
inflight.delete(key);
|
|
@@ -215,6 +257,7 @@ function query(source, options = {}) {
|
|
|
215
257
|
data.value = result;
|
|
216
258
|
loading.value = false;
|
|
217
259
|
revalidating.value = false;
|
|
260
|
+
scheduleInterval();
|
|
218
261
|
}, (err) => {
|
|
219
262
|
inflight.delete(key);
|
|
220
263
|
inflightKeys.delete(key);
|
|
@@ -223,6 +266,7 @@ function query(source, options = {}) {
|
|
|
223
266
|
error.value = err;
|
|
224
267
|
loading.value = false;
|
|
225
268
|
revalidating.value = false;
|
|
269
|
+
scheduleInterval();
|
|
226
270
|
});
|
|
227
271
|
}
|
|
228
272
|
function startFetch(fetchPromise, key) {
|
|
@@ -270,6 +314,19 @@ function query(source, options = {}) {
|
|
|
270
314
|
isFirst = false;
|
|
271
315
|
return;
|
|
272
316
|
}
|
|
317
|
+
} else {
|
|
318
|
+
const trackPromise = callThunkWithCapture();
|
|
319
|
+
trackPromise.catch(() => {});
|
|
320
|
+
const derivedKey = untrack(() => getCacheKey());
|
|
321
|
+
const cached = untrack(() => cache.get(derivedKey));
|
|
322
|
+
if (cached !== undefined) {
|
|
323
|
+
untrack(() => {
|
|
324
|
+
data.value = cached;
|
|
325
|
+
loading.value = false;
|
|
326
|
+
});
|
|
327
|
+
isFirst = false;
|
|
328
|
+
return;
|
|
329
|
+
}
|
|
273
330
|
}
|
|
274
331
|
isFirst = false;
|
|
275
332
|
return;
|
|
@@ -311,7 +368,8 @@ function query(source, options = {}) {
|
|
|
311
368
|
return;
|
|
312
369
|
}
|
|
313
370
|
}
|
|
314
|
-
const
|
|
371
|
+
const isNavigation = ssrHydrationCleanup !== null;
|
|
372
|
+
const shouldCheckCache = isNavigation || (isFirst ? !!customKey : !customKey);
|
|
315
373
|
if (shouldCheckCache) {
|
|
316
374
|
const cached = untrack(() => cache.get(key));
|
|
317
375
|
if (cached !== undefined) {
|
|
@@ -322,12 +380,14 @@ function query(source, options = {}) {
|
|
|
322
380
|
error.value = undefined;
|
|
323
381
|
});
|
|
324
382
|
isFirst = false;
|
|
383
|
+
scheduleInterval();
|
|
325
384
|
return;
|
|
326
385
|
}
|
|
327
386
|
}
|
|
328
387
|
if (isFirst && initialData !== undefined) {
|
|
329
388
|
promise.catch(() => {});
|
|
330
389
|
isFirst = false;
|
|
390
|
+
scheduleInterval();
|
|
331
391
|
return;
|
|
332
392
|
}
|
|
333
393
|
isFirst = false;
|
|
@@ -346,6 +406,10 @@ function query(source, options = {}) {
|
|
|
346
406
|
disposeFn?.();
|
|
347
407
|
ssrHydrationCleanup?.();
|
|
348
408
|
clearTimeout(debounceTimer);
|
|
409
|
+
clearTimeout(intervalTimer);
|
|
410
|
+
if (visibilityHandler && typeof document !== "undefined") {
|
|
411
|
+
document.removeEventListener("visibilitychange", visibilityHandler);
|
|
412
|
+
}
|
|
349
413
|
fetchId++;
|
|
350
414
|
for (const key of inflightKeys) {
|
|
351
415
|
inflight.delete(key);
|
|
@@ -48,18 +48,36 @@ function dispatchPrefetchDone() {
|
|
|
48
48
|
setNavPrefetchActive(false);
|
|
49
49
|
document.dispatchEvent(new CustomEvent("vertz:nav-prefetch-done"));
|
|
50
50
|
}
|
|
51
|
+
var prefetchGen = 0;
|
|
51
52
|
function prefetchNavData(url, options) {
|
|
52
53
|
const controller = new AbortController;
|
|
53
54
|
const timeout = options?.timeout ?? 5000;
|
|
55
|
+
const gen = ++prefetchGen;
|
|
54
56
|
ensureSSRDataBus();
|
|
55
57
|
setNavPrefetchActive(true);
|
|
56
58
|
const timeoutId = setTimeout(() => controller.abort(), timeout);
|
|
59
|
+
let resolveFirstEvent = null;
|
|
60
|
+
const firstEvent = new Promise((r) => {
|
|
61
|
+
resolveFirstEvent = r;
|
|
62
|
+
});
|
|
63
|
+
function onFirstEvent() {
|
|
64
|
+
if (resolveFirstEvent) {
|
|
65
|
+
resolveFirstEvent();
|
|
66
|
+
resolveFirstEvent = null;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
function finishIfCurrent() {
|
|
70
|
+
if (gen === prefetchGen) {
|
|
71
|
+
dispatchPrefetchDone();
|
|
72
|
+
}
|
|
73
|
+
}
|
|
57
74
|
const done = fetch(url, {
|
|
58
75
|
headers: { "X-Vertz-Nav": "1" },
|
|
59
76
|
signal: controller.signal
|
|
60
77
|
}).then(async (response) => {
|
|
61
78
|
if (!response.body) {
|
|
62
|
-
|
|
79
|
+
onFirstEvent();
|
|
80
|
+
finishIfCurrent();
|
|
63
81
|
return;
|
|
64
82
|
}
|
|
65
83
|
const reader = response.body.getReader();
|
|
@@ -73,21 +91,24 @@ function prefetchNavData(url, options) {
|
|
|
73
91
|
const parsed = parseSSE(buffer);
|
|
74
92
|
buffer = parsed.remaining;
|
|
75
93
|
for (const event of parsed.events) {
|
|
94
|
+
onFirstEvent();
|
|
76
95
|
if (event.type === "data") {
|
|
77
96
|
try {
|
|
78
97
|
const { key, data } = JSON.parse(event.data);
|
|
79
98
|
pushNavData(key, data);
|
|
80
99
|
} catch {}
|
|
81
100
|
} else if (event.type === "done") {
|
|
82
|
-
|
|
101
|
+
finishIfCurrent();
|
|
83
102
|
clearTimeout(timeoutId);
|
|
84
103
|
return;
|
|
85
104
|
}
|
|
86
105
|
}
|
|
87
106
|
}
|
|
88
|
-
|
|
107
|
+
onFirstEvent();
|
|
108
|
+
finishIfCurrent();
|
|
89
109
|
}).catch(() => {
|
|
90
|
-
|
|
110
|
+
onFirstEvent();
|
|
111
|
+
finishIfCurrent();
|
|
91
112
|
}).finally(() => {
|
|
92
113
|
clearTimeout(timeoutId);
|
|
93
114
|
});
|
|
@@ -96,7 +117,8 @@ function prefetchNavData(url, options) {
|
|
|
96
117
|
controller.abort();
|
|
97
118
|
clearTimeout(timeoutId);
|
|
98
119
|
},
|
|
99
|
-
done
|
|
120
|
+
done,
|
|
121
|
+
firstEvent
|
|
100
122
|
};
|
|
101
123
|
}
|
|
102
124
|
|
|
@@ -4,7 +4,7 @@ import {
|
|
|
4
4
|
} from "./chunk-9e92w0wt.js";
|
|
5
5
|
import {
|
|
6
6
|
prefetchNavData
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-jrtrk5z4.js";
|
|
8
8
|
import {
|
|
9
9
|
signal
|
|
10
10
|
} from "./chunk-hrd0mft1.js";
|
|
@@ -22,19 +22,38 @@ function createRouter(routes, initialUrl, options) {
|
|
|
22
22
|
url = window.location.pathname + window.location.search;
|
|
23
23
|
}
|
|
24
24
|
const initialMatch = matchRoute(routes, url);
|
|
25
|
+
function normalizeUrl(rawUrl) {
|
|
26
|
+
const qIdx = rawUrl.indexOf("?");
|
|
27
|
+
if (qIdx === -1)
|
|
28
|
+
return rawUrl;
|
|
29
|
+
const pathname = rawUrl.slice(0, qIdx);
|
|
30
|
+
const params = new URLSearchParams(rawUrl.slice(qIdx + 1));
|
|
31
|
+
params.sort();
|
|
32
|
+
const sorted = params.toString();
|
|
33
|
+
return sorted ? `${pathname}?${sorted}` : pathname;
|
|
34
|
+
}
|
|
35
|
+
const visitedUrls = new Set;
|
|
36
|
+
if (initialMatch)
|
|
37
|
+
visitedUrls.add(normalizeUrl(url));
|
|
25
38
|
const current = signal(initialMatch);
|
|
26
39
|
const loaderData = signal([]);
|
|
27
40
|
const loaderError = signal(null);
|
|
28
41
|
const searchParams = signal(initialMatch?.search ?? {});
|
|
29
42
|
let navigationGen = 0;
|
|
43
|
+
let navigateGen = 0;
|
|
30
44
|
let currentAbort = null;
|
|
31
45
|
const serverNavEnabled = !!options?.serverNav;
|
|
32
46
|
const serverNavTimeout = typeof options?.serverNav === "object" ? options.serverNav.timeout : undefined;
|
|
33
47
|
const prefetchFn = options?._prefetchNavData ?? (serverNavEnabled ? prefetchNavData : null);
|
|
34
48
|
let activePrefetch = null;
|
|
49
|
+
let activePrefetchUrl = null;
|
|
35
50
|
function startPrefetch(navUrl) {
|
|
36
51
|
if (!serverNavEnabled || !prefetchFn)
|
|
37
52
|
return null;
|
|
53
|
+
const normalized = normalizeUrl(navUrl);
|
|
54
|
+
if (activePrefetch && activePrefetchUrl === normalized) {
|
|
55
|
+
return activePrefetch;
|
|
56
|
+
}
|
|
38
57
|
if (activePrefetch) {
|
|
39
58
|
activePrefetch.abort();
|
|
40
59
|
}
|
|
@@ -43,15 +62,14 @@ function createRouter(routes, initialUrl, options) {
|
|
|
43
62
|
prefetchOpts.timeout = serverNavTimeout;
|
|
44
63
|
}
|
|
45
64
|
activePrefetch = prefetchFn(navUrl, prefetchOpts);
|
|
65
|
+
activePrefetchUrl = normalized;
|
|
46
66
|
return activePrefetch;
|
|
47
67
|
}
|
|
48
68
|
async function awaitPrefetch(handle) {
|
|
49
|
-
|
|
69
|
+
const target = handle?.firstEvent ?? handle?.done;
|
|
70
|
+
if (!target)
|
|
50
71
|
return;
|
|
51
|
-
await Promise.race([
|
|
52
|
-
handle.done,
|
|
53
|
-
new Promise((r) => setTimeout(r, DEFAULT_NAV_THRESHOLD_MS))
|
|
54
|
-
]);
|
|
72
|
+
await Promise.race([target, new Promise((r) => setTimeout(r, DEFAULT_NAV_THRESHOLD_MS))]);
|
|
55
73
|
}
|
|
56
74
|
if (isSSR) {
|
|
57
75
|
const g = globalThis;
|
|
@@ -93,6 +111,7 @@ function createRouter(routes, initialUrl, options) {
|
|
|
93
111
|
const match = matchRoute(routes, url2);
|
|
94
112
|
current.value = match;
|
|
95
113
|
if (match) {
|
|
114
|
+
visitedUrls.add(normalizeUrl(url2));
|
|
96
115
|
searchParams.value = match.search;
|
|
97
116
|
await runLoaders(match, gen, abort.signal);
|
|
98
117
|
} else {
|
|
@@ -104,6 +123,7 @@ function createRouter(routes, initialUrl, options) {
|
|
|
104
123
|
}
|
|
105
124
|
}
|
|
106
125
|
async function navigate(navUrl, navOptions) {
|
|
126
|
+
const gen = ++navigateGen;
|
|
107
127
|
const handle = startPrefetch(navUrl);
|
|
108
128
|
if (!isSSR) {
|
|
109
129
|
if (navOptions?.replace) {
|
|
@@ -112,9 +132,12 @@ function createRouter(routes, initialUrl, options) {
|
|
|
112
132
|
window.history.pushState(null, "", navUrl);
|
|
113
133
|
}
|
|
114
134
|
}
|
|
115
|
-
|
|
135
|
+
const isCachedNav = visitedUrls.has(normalizeUrl(navUrl));
|
|
136
|
+
if (!isCachedNav && (handle?.firstEvent || handle?.done)) {
|
|
116
137
|
await awaitPrefetch(handle);
|
|
117
138
|
}
|
|
139
|
+
if (gen !== navigateGen)
|
|
140
|
+
return;
|
|
118
141
|
await applyNavigation(navUrl);
|
|
119
142
|
}
|
|
120
143
|
async function revalidate() {
|
|
@@ -148,6 +171,7 @@ function createRouter(routes, initialUrl, options) {
|
|
|
148
171
|
if (activePrefetch) {
|
|
149
172
|
activePrefetch.abort();
|
|
150
173
|
activePrefetch = null;
|
|
174
|
+
activePrefetchUrl = null;
|
|
151
175
|
}
|
|
152
176
|
}
|
|
153
177
|
return {
|
|
@@ -788,6 +788,9 @@ class InlineStyleError extends Error {
|
|
|
788
788
|
}
|
|
789
789
|
|
|
790
790
|
// src/css/theme.ts
|
|
791
|
+
function sanitizeCssValue(value) {
|
|
792
|
+
return value.replace(/[;{}]/g, "").replace(/url\s*\(/gi, "").replace(/expression\s*\(/gi, "").replace(/@import/gi, "");
|
|
793
|
+
}
|
|
791
794
|
function defineTheme(input) {
|
|
792
795
|
return {
|
|
793
796
|
colors: input.colors,
|
|
@@ -817,17 +820,17 @@ function compileTheme(theme) {
|
|
|
817
820
|
for (const [key, value] of Object.entries(values)) {
|
|
818
821
|
if (key === "DEFAULT") {
|
|
819
822
|
const varName = `--color-${name}`;
|
|
820
|
-
rootVars.push(` ${varName}: ${value};`);
|
|
823
|
+
rootVars.push(` ${varName}: ${sanitizeCssValue(value)};`);
|
|
821
824
|
tokenPaths.push(name);
|
|
822
825
|
} else if (key.startsWith("_")) {
|
|
823
826
|
const variant = key.slice(1);
|
|
824
827
|
const varName = `--color-${name}`;
|
|
825
828
|
if (variant === "dark") {
|
|
826
|
-
darkVars.push(` ${varName}: ${value};`);
|
|
829
|
+
darkVars.push(` ${varName}: ${sanitizeCssValue(value)};`);
|
|
827
830
|
}
|
|
828
831
|
} else {
|
|
829
832
|
const varName = `--color-${name}-${key}`;
|
|
830
|
-
rootVars.push(` ${varName}: ${value};`);
|
|
833
|
+
rootVars.push(` ${varName}: ${sanitizeCssValue(value)};`);
|
|
831
834
|
tokenPaths.push(`${name}.${key}`);
|
|
832
835
|
}
|
|
833
836
|
}
|
|
@@ -835,7 +838,7 @@ function compileTheme(theme) {
|
|
|
835
838
|
if (theme.spacing) {
|
|
836
839
|
for (const [name, value] of Object.entries(theme.spacing)) {
|
|
837
840
|
const varName = `--spacing-${name}`;
|
|
838
|
-
rootVars.push(` ${varName}: ${value};`);
|
|
841
|
+
rootVars.push(` ${varName}: ${sanitizeCssValue(value)};`);
|
|
839
842
|
tokenPaths.push(`spacing.${name}`);
|
|
840
843
|
}
|
|
841
844
|
}
|
package/dist/test/index.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import {
|
|
2
2
|
createRouter
|
|
3
|
-
} from "../shared/chunk-
|
|
3
|
+
} from "../shared/chunk-ka5ked7n.js";
|
|
4
4
|
import {
|
|
5
5
|
defineRoutes
|
|
6
6
|
} from "../shared/chunk-9e92w0wt.js";
|
|
7
|
-
import"../shared/chunk-
|
|
7
|
+
import"../shared/chunk-jrtrk5z4.js";
|
|
8
8
|
import"../shared/chunk-hrd0mft1.js";
|
|
9
9
|
|
|
10
10
|
// src/test/interactions.ts
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vertz/ui",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.2",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"description": "Vertz UI framework — signals, components, JSX runtime",
|
|
@@ -67,8 +67,8 @@
|
|
|
67
67
|
},
|
|
68
68
|
"devDependencies": {
|
|
69
69
|
"@happy-dom/global-registrator": "^20.7.0",
|
|
70
|
-
"@vertz/schema": "0.2.
|
|
71
|
-
"bunup": "
|
|
70
|
+
"@vertz/schema": "0.2.2",
|
|
71
|
+
"bunup": "^0.16.31",
|
|
72
72
|
"happy-dom": "^20.7.0",
|
|
73
73
|
"typescript": "^5.7.0"
|
|
74
74
|
},
|