@fluixi/core 1.0.0-alpha.84 → 1.0.0-alpha.85
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/cdn/core.global.js +2 -2
- package/dist/src/cdn/{chunk-VNLCQBFQ.mjs → chunk-MDJLC3PE.mjs} +87 -4
- package/dist/src/cdn/core.cjs +91 -5
- package/dist/src/cdn/core.mjs +3 -3
- package/dist/src/cdn/{router-WMN2AA7E.mjs → router-JYFZP3EZ.mjs} +5 -1
- package/dist/src/index.cjs +84 -5
- package/dist/src/index.mjs +86 -6
- package/dist/src/lib/client/{chunk-2C2KDDQV.mjs → chunk-7IUN2FLI.mjs} +1 -1
- package/dist/src/lib/client/index.cjs +92 -6
- package/dist/src/lib/client/index.mjs +4 -4
- package/dist/src/lib/client/{router-X526HSWQ.mjs → router-NOFEMOEH.mjs} +89 -6
- package/dist/src/lib/core.cjs +83 -4
- package/dist/src/lib/core.mjs +85 -5
- package/dist/src/lib/i18n/index.cjs +1 -1
- package/dist/src/lib/i18n/index.mjs +1 -1
- package/dist/src/lib/index.cjs +1 -1
- package/dist/src/lib/index.mjs +1 -1
- package/dist/src/lib/jsx-runtime/index.cjs +1 -1
- package/dist/src/lib/jsx-runtime/index.mjs +1 -1
- package/dist/src/lib/jsx-runtime/jsx-dev-runtime.cjs +1 -1
- package/dist/src/lib/jsx-runtime/jsx-dev-runtime.mjs +1 -1
- package/dist/src/lib/plugins/index.cjs +1 -1
- package/dist/src/lib/plugins/index.mjs +1 -1
- package/dist/src/lib/render/index.cjs +1 -1
- package/dist/src/lib/render/index.mjs +1 -1
- package/dist/src/lib/render/versions.cjs +1 -1
- package/dist/src/lib/render/versions.mjs +1 -1
- package/dist/src/lib/router/index.cjs +86 -4
- package/dist/src/lib/router/index.d.ts +17 -14
- package/dist/src/lib/router/index.d.ts.map +1 -1
- package/dist/src/lib/router/index.js +76 -4
- package/dist/src/lib/router/index.mjs +88 -5
- package/dist/src/lib/router/transition.cjs +75 -0
- package/dist/src/lib/router/transition.d.ts +81 -0
- package/dist/src/lib/router/transition.d.ts.map +1 -0
- package/dist/src/lib/router/transition.js +136 -0
- package/dist/src/lib/router/transition.mjs +54 -0
- package/dist/src/lib/server/index.cjs +1 -1
- package/dist/src/lib/server/index.mjs +1 -1
- package/dist/src/routes.cjs +1 -1
- package/dist/src/routes.mjs +1 -1
- package/dist/src/utils.cjs +1 -1
- package/dist/src/utils.mjs +1 -1
- package/dist/src/version.generated.cjs +1 -1
- package/dist/src/version.generated.d.ts +1 -1
- package/dist/src/version.generated.js +1 -1
- package/dist/src/version.generated.mjs +1 -1
- package/dist/tsconfig.lib.tsbuildinfo +1 -1
- package/package.json +11 -11
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/*! @fluixi/core v1.0.0-alpha.
|
|
1
|
+
/*! @fluixi/core v1.0.0-alpha.85 | (c) 2026 Ibrahima Touré and Fluixi contributors | MIT */
|
|
2
2
|
"use strict";
|
|
3
3
|
var __create = Object.create;
|
|
4
4
|
var __defProp = Object.defineProperty;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/*! @fluixi/core v1.0.0-alpha.
|
|
1
|
+
/*! @fluixi/core v1.0.0-alpha.85 | (c) 2026 Ibrahima Touré and Fluixi contributors | MIT */
|
|
2
2
|
|
|
3
3
|
// src/lib/plugins/vite.ts
|
|
4
4
|
import { fluixi, createVitePlugin } from "@fluixi/vite-plugin";
|
|
@@ -63,7 +63,7 @@ var import_signal6 = require("@fluixi/reactive/signal");
|
|
|
63
63
|
var import_dom = require("@fluixi/dom");
|
|
64
64
|
|
|
65
65
|
// src/version.generated.ts
|
|
66
|
-
var VERSION = "1.0.0-alpha.
|
|
66
|
+
var VERSION = "1.0.0-alpha.85";
|
|
67
67
|
|
|
68
68
|
// src/lib/render/versions.ts
|
|
69
69
|
var import_dom2 = require("@fluixi/dom");
|
|
@@ -6,7 +6,7 @@ import { createRoot as createRoot2 } from "@fluixi/reactive/signal";
|
|
|
6
6
|
import { registerCoreVersion } from "@fluixi/dom";
|
|
7
7
|
|
|
8
8
|
// src/version.generated.ts
|
|
9
|
-
var VERSION = "1.0.0-alpha.
|
|
9
|
+
var VERSION = "1.0.0-alpha.85";
|
|
10
10
|
|
|
11
11
|
// src/lib/render/versions.ts
|
|
12
12
|
import { stampVersions, warnOnVersionSkew, versions } from "@fluixi/dom";
|
|
@@ -28,7 +28,7 @@ module.exports = __toCommonJS(versions_exports);
|
|
|
28
28
|
var import_dom = require("@fluixi/dom");
|
|
29
29
|
|
|
30
30
|
// src/version.generated.ts
|
|
31
|
-
var VERSION = "1.0.0-alpha.
|
|
31
|
+
var VERSION = "1.0.0-alpha.85";
|
|
32
32
|
|
|
33
33
|
// src/lib/render/versions.ts
|
|
34
34
|
var import_dom2 = require("@fluixi/dom");
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/*! @fluixi/core v1.0.0-alpha.
|
|
1
|
+
/*! @fluixi/core v1.0.0-alpha.85 | (c) 2026 Ibrahima Touré and Fluixi contributors | MIT */
|
|
2
2
|
"use strict";
|
|
3
3
|
var __defProp = Object.defineProperty;
|
|
4
4
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
@@ -35,10 +35,12 @@ __export(index_exports, {
|
|
|
35
35
|
lazy: () => lazy,
|
|
36
36
|
matchPath: () => import_router2.matchPath,
|
|
37
37
|
matchRoutes: () => import_router2.matchRoutes,
|
|
38
|
+
prefersReducedMotion: () => prefersReducedMotion,
|
|
38
39
|
preloadRoutes: () => preloadRoutes,
|
|
39
40
|
revalidate: () => revalidate,
|
|
40
41
|
runWithOwner: () => import_signal7.runWithOwner,
|
|
41
42
|
select: () => select,
|
|
43
|
+
supportsViewTransitions: () => supportsViewTransitions,
|
|
42
44
|
useAction: () => useAction,
|
|
43
45
|
useLevelParams: () => useLevelParams,
|
|
44
46
|
useLinkNavigation: () => useLinkNavigation,
|
|
@@ -55,6 +57,56 @@ __export(index_exports, {
|
|
|
55
57
|
});
|
|
56
58
|
module.exports = __toCommonJS(index_exports);
|
|
57
59
|
var import_signal5 = require("@fluixi/reactive/signal");
|
|
60
|
+
|
|
61
|
+
// src/lib/router/transition.ts
|
|
62
|
+
function supportsViewTransitions() {
|
|
63
|
+
return typeof document !== "undefined" && typeof document.startViewTransition === "function";
|
|
64
|
+
}
|
|
65
|
+
function prefersReducedMotion() {
|
|
66
|
+
if (typeof window === "undefined" || typeof window.matchMedia !== "function") {
|
|
67
|
+
return false;
|
|
68
|
+
}
|
|
69
|
+
try {
|
|
70
|
+
return window.matchMedia("(prefers-reduced-motion: reduce)").matches;
|
|
71
|
+
} catch {
|
|
72
|
+
return false;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
function shouldAnimate(mode) {
|
|
76
|
+
return mode !== "none" && supportsViewTransitions() && !prefersReducedMotion();
|
|
77
|
+
}
|
|
78
|
+
function prepareComponents(components) {
|
|
79
|
+
const pending = [];
|
|
80
|
+
for (const component of components) {
|
|
81
|
+
const preload = component?.preload;
|
|
82
|
+
if (typeof preload === "function") {
|
|
83
|
+
try {
|
|
84
|
+
const result = preload();
|
|
85
|
+
if (result && typeof result.then === "function") {
|
|
86
|
+
pending.push(result.catch(() => void 0));
|
|
87
|
+
}
|
|
88
|
+
} catch {
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
return pending.length ? Promise.all(pending).then(() => void 0) : null;
|
|
93
|
+
}
|
|
94
|
+
function runTransition(mode, commit) {
|
|
95
|
+
if (!shouldAnimate(mode)) {
|
|
96
|
+
return Promise.resolve(commit()).then(() => void 0);
|
|
97
|
+
}
|
|
98
|
+
const start = document.startViewTransition;
|
|
99
|
+
try {
|
|
100
|
+
const transition = start.call(document, () => commit());
|
|
101
|
+
transition.finished?.catch(() => void 0);
|
|
102
|
+
transition.ready?.catch(() => void 0);
|
|
103
|
+
return transition.updateCallbackDone.catch(() => void 0);
|
|
104
|
+
} catch {
|
|
105
|
+
return Promise.resolve(commit()).then(() => void 0);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// src/lib/router/index.ts
|
|
58
110
|
var import_dom4 = require("@fluixi/dom");
|
|
59
111
|
var import_signal6 = require("@fluixi/reactive/signal");
|
|
60
112
|
var import_object = require("@fluixi/utils/object");
|
|
@@ -347,7 +399,7 @@ function renderChild(ctx) {
|
|
|
347
399
|
);
|
|
348
400
|
(0, import_signal5.createEffect)(() => {
|
|
349
401
|
const c = ctx.matched()[childIdx]?.component;
|
|
350
|
-
setChildComp(() => c);
|
|
402
|
+
if ((0, import_signal5.untrack)(childComp) !== c) setChildComp(() => c);
|
|
351
403
|
});
|
|
352
404
|
return (0, import_signal5.createMemo)(() => {
|
|
353
405
|
const component = childComp();
|
|
@@ -385,6 +437,7 @@ function buildDefaultNotFound() {
|
|
|
385
437
|
el.appendChild(p);
|
|
386
438
|
return el;
|
|
387
439
|
}
|
|
440
|
+
var pendingTransitionMode;
|
|
388
441
|
function Router(props) {
|
|
389
442
|
const history = props.history ?? (0, import_router.createBrowserHistory)();
|
|
390
443
|
const router = (0, import_router.createRouter)({
|
|
@@ -393,10 +446,35 @@ function Router(props) {
|
|
|
393
446
|
history
|
|
394
447
|
});
|
|
395
448
|
const [state, setState] = (0, import_signal5.createSignal)(router.state());
|
|
396
|
-
|
|
449
|
+
let navigationId = 0;
|
|
450
|
+
const takeMode = () => {
|
|
451
|
+
const mode = pendingTransitionMode ?? (props.transition ?? "auto");
|
|
452
|
+
pendingTransitionMode = void 0;
|
|
453
|
+
return mode;
|
|
454
|
+
};
|
|
455
|
+
const unsub = router.subscribe((next) => {
|
|
456
|
+
const id = ++navigationId;
|
|
457
|
+
const mode = takeMode();
|
|
458
|
+
const prepared = prepareComponents(
|
|
459
|
+
(next.match?.matched ?? []).map((m) => m.component).filter(Boolean)
|
|
460
|
+
);
|
|
461
|
+
const commit = () => {
|
|
462
|
+
setState(next);
|
|
463
|
+
return (0, import_signal5.flush)();
|
|
464
|
+
};
|
|
465
|
+
if (!prepared) {
|
|
466
|
+
void runTransition(mode, commit);
|
|
467
|
+
return;
|
|
468
|
+
}
|
|
469
|
+
void prepared.then(() => {
|
|
470
|
+
if (id !== navigationId) return;
|
|
471
|
+
return runTransition(mode, commit);
|
|
472
|
+
});
|
|
473
|
+
});
|
|
397
474
|
(0, import_signal5.onCleanup)(() => {
|
|
398
475
|
unsub();
|
|
399
476
|
router.destroy();
|
|
477
|
+
navigationId++;
|
|
400
478
|
});
|
|
401
479
|
const matched = (0, import_signal5.createMemo)(
|
|
402
480
|
() => (state().match?.matched ?? []).filter((m) => m.component)
|
|
@@ -529,12 +607,16 @@ function useLinkNavigation(options = {}) {
|
|
|
529
607
|
if (url.hash && url.pathname === window.location.pathname && url.search === window.location.search) return;
|
|
530
608
|
if (options.shouldNavigate && !options.shouldNavigate(url, anchor)) return;
|
|
531
609
|
event.preventDefault();
|
|
610
|
+
pendingTransitionMode = options.transition;
|
|
532
611
|
ctx.router.navigate(url.pathname + url.search + url.hash, { replace: options.replace });
|
|
533
612
|
};
|
|
534
613
|
}
|
|
535
614
|
function useNavigate() {
|
|
536
615
|
const { router } = useRouter();
|
|
537
|
-
return (to, options) =>
|
|
616
|
+
return (to, options) => {
|
|
617
|
+
pendingTransitionMode = options?.transition;
|
|
618
|
+
router.navigate(to, options);
|
|
619
|
+
};
|
|
538
620
|
}
|
|
539
621
|
function useRouteData() {
|
|
540
622
|
const acc = (0, import_signal6.useContext)(RouteDataContext2);
|
|
@@ -1,16 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
*
|
|
3
|
-
* Renders the resolved match tree TOP-DOWN. Each layer's component is rendered
|
|
4
|
-
* inside the PARENT layer's <Outlet/> scope, so a child route inherits whatever
|
|
5
|
-
* context a parent layout provides. <Outlet/> is reactive but keyed on the child's
|
|
6
|
-
* component reference, so it only re-renders when the child route at that depth
|
|
7
|
-
* actually changes: sibling navigation reuses the layout, deeper navigation
|
|
8
|
-
* leaves shallower layouts untouched.
|
|
9
|
-
*
|
|
10
|
-
* No parentGate, no deferred content memo: a child is rendered straight from the
|
|
11
|
-
* matched array, never by re-entering a still-building parent, so deeply nested
|
|
12
|
-
* <Outlet/> works at any depth.
|
|
13
|
-
*/
|
|
1
|
+
import { type TransitionMode } from './transition.js';
|
|
14
2
|
import { type GetAtPath } from '@fluixi/utils/object';
|
|
15
3
|
import type { IsUndefined } from '@fluixi/utils/primitive';
|
|
16
4
|
import { type RouteDefinition, type Router as CoreRouter, type HistoryAdapter, type RouterLocation, type NavigateOptions } from '@fluixi/router';
|
|
@@ -29,6 +17,16 @@ export interface RouterProps<C = any> {
|
|
|
29
17
|
* that keeps your layout/chrome, add a catch-all file route instead (`[...slug].tsx` -> `*`).
|
|
30
18
|
*/
|
|
31
19
|
notFound?: any;
|
|
20
|
+
/**
|
|
21
|
+
* How route changes are presented. `'auto'` (the default) hands the DOM swap to the
|
|
22
|
+
* browser's View Transition API where there is one, and navigates directly where
|
|
23
|
+
* there is not; `'none'` always navigates directly.
|
|
24
|
+
*
|
|
25
|
+
* A reader who asked for reduced motion never gets an animation either way. Styling
|
|
26
|
+
* is the native mechanism: `::view-transition-old(root)` and
|
|
27
|
+
* `::view-transition-new(root)`: so the router imposes no visual style of its own.
|
|
28
|
+
*/
|
|
29
|
+
transition?: TransitionMode;
|
|
32
30
|
}
|
|
33
31
|
/**
|
|
34
32
|
* The router. Matches the current URL against `routes` and renders what matches.
|
|
@@ -118,6 +116,8 @@ export declare function useLocation(): RouterLocation;
|
|
|
118
116
|
export interface LinkNavigationOptions {
|
|
119
117
|
/** Replace the current history entry instead of pushing a new one. */
|
|
120
118
|
replace?: boolean;
|
|
119
|
+
/** How this navigation is presented. Defaults to the Router's own setting. */
|
|
120
|
+
transition?: TransitionMode;
|
|
121
121
|
/**
|
|
122
122
|
* Called with the resolved URL before navigating. Return false to leave the click
|
|
123
123
|
* alone, for a section of the app that must load as a document (a different
|
|
@@ -164,7 +164,9 @@ export declare function useLinkNavigation(options?: LinkNavigationOptions): (eve
|
|
|
164
164
|
* navigate(`/posts/${id}`, { replace: true });
|
|
165
165
|
* ```
|
|
166
166
|
*/
|
|
167
|
-
export declare function useNavigate(): (to: string, options?: NavigateOptions
|
|
167
|
+
export declare function useNavigate(): (to: string, options?: NavigateOptions & {
|
|
168
|
+
transition?: TransitionMode;
|
|
169
|
+
}) => void;
|
|
168
170
|
/**
|
|
169
171
|
* The resolved data for the nearest route that declared a `data` loader. Always
|
|
170
172
|
* returns a callable accessor (matching the old router): it suspends under
|
|
@@ -244,5 +246,6 @@ export type { RouteDefinition, RouteMatch, MatchedRoute, RouterLocation, Navigat
|
|
|
244
246
|
export { cache, action, revalidate, createAsync, useSubmission, useAction, } from './data.js';
|
|
245
247
|
export type { RouteDataArgs, RouteLoadFunc, Action, Submission, CacheFn, } from './data.js';
|
|
246
248
|
export { lazy } from '../render/lazy.js';
|
|
249
|
+
export { supportsViewTransitions, prefersReducedMotion, type TransitionMode, } from './transition.js';
|
|
247
250
|
export { runWithOwner } from '@fluixi/reactive/signal';
|
|
248
251
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/lib/router/index.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/lib/router/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -11,7 +11,8 @@
|
|
|
11
11
|
* matched array, never by re-entering a still-building parent, so deeply nested
|
|
12
12
|
* <Outlet/> works at any depth.
|
|
13
13
|
*/
|
|
14
|
-
import { createSignal, createMemo, createResource, createEffect, getOwner, onCleanup, untrack, } from '@fluixi/reactive/signal';
|
|
14
|
+
import { createSignal, createMemo, createResource, createEffect, getOwner, onCleanup, untrack, flush, } from '@fluixi/reactive/signal';
|
|
15
|
+
import { prepareComponents, runTransition, } from './transition.js';
|
|
15
16
|
import { insert, createComponent, createNativeElement, setAttribute, } from '@fluixi/dom';
|
|
16
17
|
// Reactive context (not fluixi createProvider, which destructures children
|
|
17
18
|
// eagerly) so a `get children()` getter renders after the context is stamped.
|
|
@@ -88,7 +89,11 @@ function renderChild(ctx) {
|
|
|
88
89
|
const [childComp, setChildComp] = createSignal(untrack(() => ctx.matched()[childIdx]?.component));
|
|
89
90
|
createEffect(() => {
|
|
90
91
|
const c = ctx.matched()[childIdx]?.component;
|
|
91
|
-
|
|
92
|
+
// `matched` is invalidated more than once per navigation, so this effect runs more
|
|
93
|
+
// than once. Writing only on a real change keeps the redundant runs from touching
|
|
94
|
+
// the graph at all.
|
|
95
|
+
if (untrack(childComp) !== c)
|
|
96
|
+
setChildComp(() => c);
|
|
92
97
|
});
|
|
93
98
|
return createMemo(() => {
|
|
94
99
|
const component = childComp();
|
|
@@ -134,6 +139,11 @@ function buildDefaultNotFound() {
|
|
|
134
139
|
el.appendChild(p);
|
|
135
140
|
return el;
|
|
136
141
|
}
|
|
142
|
+
/**
|
|
143
|
+
* The presentation for the navigation about to happen, handed from `useNavigate` to the
|
|
144
|
+
* subscriber in `Router`. Read and cleared within the same synchronous call.
|
|
145
|
+
*/
|
|
146
|
+
let pendingTransitionMode;
|
|
137
147
|
/**
|
|
138
148
|
* The router. Matches the current URL against `routes` and renders what matches.
|
|
139
149
|
*
|
|
@@ -156,10 +166,62 @@ export function Router(props) {
|
|
|
156
166
|
history,
|
|
157
167
|
});
|
|
158
168
|
const [state, setState] = createSignal(router.state());
|
|
159
|
-
|
|
169
|
+
/**
|
|
170
|
+
* Which navigation is allowed to commit.
|
|
171
|
+
*
|
|
172
|
+
* Preparation is async, so two navigations can be in flight at once: A→B starts
|
|
173
|
+
* loading B's chunk, the reader clicks through to C before it lands. Each navigation
|
|
174
|
+
* takes the next number and checks it is still the current one before writing the
|
|
175
|
+
* match signal, so B resolving late finds itself superseded and returns without
|
|
176
|
+
* touching anything. Without this, B would commit over C, the URL would say /c and
|
|
177
|
+
* the page would show B.
|
|
178
|
+
*/
|
|
179
|
+
let navigationId = 0;
|
|
180
|
+
/**
|
|
181
|
+
* How to present the next commit, set by `useNavigate` immediately before it calls
|
|
182
|
+
* into the core router.
|
|
183
|
+
*
|
|
184
|
+
* A module-level hand-off rather than a parameter because the core router's
|
|
185
|
+
* `navigate` does not carry options through to its listeners, and it notifies
|
|
186
|
+
* synchronously: the value is written and read within the same call, never left
|
|
187
|
+
* lying around for an unrelated navigation to pick up.
|
|
188
|
+
*/
|
|
189
|
+
const takeMode = () => {
|
|
190
|
+
const mode = pendingTransitionMode ?? (props.transition ?? 'auto');
|
|
191
|
+
pendingTransitionMode = undefined;
|
|
192
|
+
return mode;
|
|
193
|
+
};
|
|
194
|
+
const unsub = router.subscribe((next) => {
|
|
195
|
+
const id = ++navigationId;
|
|
196
|
+
const mode = takeMode();
|
|
197
|
+
// Load the modules the next route needs before anything is torn down, so a lazy
|
|
198
|
+
// route renders straight through instead of suspending into a fallback with the
|
|
199
|
+
// old page already gone.
|
|
200
|
+
const prepared = prepareComponents((next.match?.matched ?? []).map((m) => m.component).filter(Boolean));
|
|
201
|
+
const commit = () => {
|
|
202
|
+
setState(next);
|
|
203
|
+
// The write only queues; effects are scheduled. Flushing here is what lets the
|
|
204
|
+
// browser snapshot a settled DOM rather than a half-built one.
|
|
205
|
+
return flush();
|
|
206
|
+
};
|
|
207
|
+
if (!prepared) {
|
|
208
|
+
// Nothing to wait for: stay on the tick this navigation was already on.
|
|
209
|
+
void runTransition(mode, commit);
|
|
210
|
+
return;
|
|
211
|
+
}
|
|
212
|
+
void prepared.then(() => {
|
|
213
|
+
// Superseded while the module loaded. The newer navigation owns the DOM now.
|
|
214
|
+
if (id !== navigationId)
|
|
215
|
+
return;
|
|
216
|
+
return runTransition(mode, commit);
|
|
217
|
+
});
|
|
218
|
+
});
|
|
160
219
|
onCleanup(() => {
|
|
161
220
|
unsub();
|
|
162
221
|
router.destroy();
|
|
222
|
+
// Anything still preparing is now stale, so nothing can commit into a torn-down
|
|
223
|
+
// router.
|
|
224
|
+
navigationId++;
|
|
163
225
|
});
|
|
164
226
|
// Drop pathless layers (no component), their params still appear in the merged
|
|
165
227
|
// params exposed by RouterContext.
|
|
@@ -390,6 +452,7 @@ export function useLinkNavigation(options = {}) {
|
|
|
390
452
|
if (options.shouldNavigate && !options.shouldNavigate(url, anchor))
|
|
391
453
|
return;
|
|
392
454
|
event.preventDefault();
|
|
455
|
+
pendingTransitionMode = options.transition;
|
|
393
456
|
ctx.router.navigate(url.pathname + url.search + url.hash, { replace: options.replace });
|
|
394
457
|
};
|
|
395
458
|
}
|
|
@@ -414,7 +477,13 @@ export function useNavigate() {
|
|
|
414
477
|
// detached), so navigation degrades to a no-op during SSR and only throws on
|
|
415
478
|
// genuine client-side misuse outside <Router>.
|
|
416
479
|
const { router } = useRouter();
|
|
417
|
-
return (to, options) =>
|
|
480
|
+
return (to, options) => {
|
|
481
|
+
// Handed to the Router's subscriber, which runs synchronously inside the call
|
|
482
|
+
// below and clears it. `transition: 'none'` on one navigation leaves the router's
|
|
483
|
+
// own setting alone for the next.
|
|
484
|
+
pendingTransitionMode = options?.transition;
|
|
485
|
+
router.navigate(to, options);
|
|
486
|
+
};
|
|
418
487
|
}
|
|
419
488
|
/**
|
|
420
489
|
* The resolved data for the nearest route that declared a `data` loader. Always
|
|
@@ -598,5 +667,8 @@ export { createMemoryHistory, createBrowserHistory, matchRoutes, matchPath, } fr
|
|
|
598
667
|
// swap its import path wholesale. (Routing hooks above are the router ones.)
|
|
599
668
|
export { cache, action, revalidate, createAsync, useSubmission, useAction, } from './data.js';
|
|
600
669
|
export { lazy } from '../render/lazy.js';
|
|
670
|
+
// Navigation presentation. The router owns the integration; these are exported for
|
|
671
|
+
// apps that need to ask what the environment supports.
|
|
672
|
+
export { supportsViewTransitions, prefersReducedMotion, } from './transition.js';
|
|
601
673
|
// Carried over from the barrel this replaced; route codegen imports it from here.
|
|
602
674
|
export { runWithOwner } from '@fluixi/reactive/signal';
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/*! @fluixi/core v1.0.0-alpha.
|
|
1
|
+
/*! @fluixi/core v1.0.0-alpha.85 | (c) 2026 Ibrahima Touré and Fluixi contributors | MIT */
|
|
2
2
|
|
|
3
3
|
// src/lib/router/index.ts
|
|
4
4
|
import {
|
|
@@ -8,8 +8,59 @@ import {
|
|
|
8
8
|
createEffect,
|
|
9
9
|
getOwner,
|
|
10
10
|
onCleanup,
|
|
11
|
-
untrack as untrack2
|
|
11
|
+
untrack as untrack2,
|
|
12
|
+
flush
|
|
12
13
|
} from "@fluixi/reactive/signal";
|
|
14
|
+
|
|
15
|
+
// src/lib/router/transition.ts
|
|
16
|
+
function supportsViewTransitions() {
|
|
17
|
+
return typeof document !== "undefined" && typeof document.startViewTransition === "function";
|
|
18
|
+
}
|
|
19
|
+
function prefersReducedMotion() {
|
|
20
|
+
if (typeof window === "undefined" || typeof window.matchMedia !== "function") {
|
|
21
|
+
return false;
|
|
22
|
+
}
|
|
23
|
+
try {
|
|
24
|
+
return window.matchMedia("(prefers-reduced-motion: reduce)").matches;
|
|
25
|
+
} catch {
|
|
26
|
+
return false;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
function shouldAnimate(mode) {
|
|
30
|
+
return mode !== "none" && supportsViewTransitions() && !prefersReducedMotion();
|
|
31
|
+
}
|
|
32
|
+
function prepareComponents(components) {
|
|
33
|
+
const pending = [];
|
|
34
|
+
for (const component of components) {
|
|
35
|
+
const preload = component?.preload;
|
|
36
|
+
if (typeof preload === "function") {
|
|
37
|
+
try {
|
|
38
|
+
const result = preload();
|
|
39
|
+
if (result && typeof result.then === "function") {
|
|
40
|
+
pending.push(result.catch(() => void 0));
|
|
41
|
+
}
|
|
42
|
+
} catch {
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
return pending.length ? Promise.all(pending).then(() => void 0) : null;
|
|
47
|
+
}
|
|
48
|
+
function runTransition(mode, commit) {
|
|
49
|
+
if (!shouldAnimate(mode)) {
|
|
50
|
+
return Promise.resolve(commit()).then(() => void 0);
|
|
51
|
+
}
|
|
52
|
+
const start = document.startViewTransition;
|
|
53
|
+
try {
|
|
54
|
+
const transition = start.call(document, () => commit());
|
|
55
|
+
transition.finished?.catch(() => void 0);
|
|
56
|
+
transition.ready?.catch(() => void 0);
|
|
57
|
+
return transition.updateCallbackDone.catch(() => void 0);
|
|
58
|
+
} catch {
|
|
59
|
+
return Promise.resolve(commit()).then(() => void 0);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// src/lib/router/index.ts
|
|
13
64
|
import {
|
|
14
65
|
insert,
|
|
15
66
|
createComponent as createComponent2,
|
|
@@ -326,7 +377,7 @@ function renderChild(ctx) {
|
|
|
326
377
|
);
|
|
327
378
|
createEffect(() => {
|
|
328
379
|
const c = ctx.matched()[childIdx]?.component;
|
|
329
|
-
setChildComp(() => c);
|
|
380
|
+
if (untrack2(childComp) !== c) setChildComp(() => c);
|
|
330
381
|
});
|
|
331
382
|
return createMemo2(() => {
|
|
332
383
|
const component = childComp();
|
|
@@ -364,6 +415,7 @@ function buildDefaultNotFound() {
|
|
|
364
415
|
el.appendChild(p);
|
|
365
416
|
return el;
|
|
366
417
|
}
|
|
418
|
+
var pendingTransitionMode;
|
|
367
419
|
function Router(props) {
|
|
368
420
|
const history = props.history ?? createBrowserHistory();
|
|
369
421
|
const router = createCoreRouter({
|
|
@@ -372,10 +424,35 @@ function Router(props) {
|
|
|
372
424
|
history
|
|
373
425
|
});
|
|
374
426
|
const [state, setState] = createSignal2(router.state());
|
|
375
|
-
|
|
427
|
+
let navigationId = 0;
|
|
428
|
+
const takeMode = () => {
|
|
429
|
+
const mode = pendingTransitionMode ?? (props.transition ?? "auto");
|
|
430
|
+
pendingTransitionMode = void 0;
|
|
431
|
+
return mode;
|
|
432
|
+
};
|
|
433
|
+
const unsub = router.subscribe((next) => {
|
|
434
|
+
const id = ++navigationId;
|
|
435
|
+
const mode = takeMode();
|
|
436
|
+
const prepared = prepareComponents(
|
|
437
|
+
(next.match?.matched ?? []).map((m) => m.component).filter(Boolean)
|
|
438
|
+
);
|
|
439
|
+
const commit = () => {
|
|
440
|
+
setState(next);
|
|
441
|
+
return flush();
|
|
442
|
+
};
|
|
443
|
+
if (!prepared) {
|
|
444
|
+
void runTransition(mode, commit);
|
|
445
|
+
return;
|
|
446
|
+
}
|
|
447
|
+
void prepared.then(() => {
|
|
448
|
+
if (id !== navigationId) return;
|
|
449
|
+
return runTransition(mode, commit);
|
|
450
|
+
});
|
|
451
|
+
});
|
|
376
452
|
onCleanup(() => {
|
|
377
453
|
unsub();
|
|
378
454
|
router.destroy();
|
|
455
|
+
navigationId++;
|
|
379
456
|
});
|
|
380
457
|
const matched = createMemo2(
|
|
381
458
|
() => (state().match?.matched ?? []).filter((m) => m.component)
|
|
@@ -508,12 +585,16 @@ function useLinkNavigation(options = {}) {
|
|
|
508
585
|
if (url.hash && url.pathname === window.location.pathname && url.search === window.location.search) return;
|
|
509
586
|
if (options.shouldNavigate && !options.shouldNavigate(url, anchor)) return;
|
|
510
587
|
event.preventDefault();
|
|
588
|
+
pendingTransitionMode = options.transition;
|
|
511
589
|
ctx.router.navigate(url.pathname + url.search + url.hash, { replace: options.replace });
|
|
512
590
|
};
|
|
513
591
|
}
|
|
514
592
|
function useNavigate() {
|
|
515
593
|
const { router } = useRouter();
|
|
516
|
-
return (to, options) =>
|
|
594
|
+
return (to, options) => {
|
|
595
|
+
pendingTransitionMode = options?.transition;
|
|
596
|
+
router.navigate(to, options);
|
|
597
|
+
};
|
|
517
598
|
}
|
|
518
599
|
function useRouteData() {
|
|
519
600
|
const acc = useContext2(RouteDataContext2);
|
|
@@ -619,10 +700,12 @@ export {
|
|
|
619
700
|
lazy,
|
|
620
701
|
matchPath2 as matchPath,
|
|
621
702
|
matchRoutes2 as matchRoutes,
|
|
703
|
+
prefersReducedMotion,
|
|
622
704
|
preloadRoutes,
|
|
623
705
|
revalidate,
|
|
624
706
|
runWithOwner,
|
|
625
707
|
select,
|
|
708
|
+
supportsViewTransitions,
|
|
626
709
|
useAction,
|
|
627
710
|
useLevelParams,
|
|
628
711
|
useLinkNavigation,
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/lib/router/transition.ts
|
|
21
|
+
var transition_exports = {};
|
|
22
|
+
__export(transition_exports, {
|
|
23
|
+
prefersReducedMotion: () => prefersReducedMotion,
|
|
24
|
+
prepareComponents: () => prepareComponents,
|
|
25
|
+
runTransition: () => runTransition,
|
|
26
|
+
shouldAnimate: () => shouldAnimate,
|
|
27
|
+
supportsViewTransitions: () => supportsViewTransitions
|
|
28
|
+
});
|
|
29
|
+
module.exports = __toCommonJS(transition_exports);
|
|
30
|
+
function supportsViewTransitions() {
|
|
31
|
+
return typeof document !== "undefined" && typeof document.startViewTransition === "function";
|
|
32
|
+
}
|
|
33
|
+
function prefersReducedMotion() {
|
|
34
|
+
if (typeof window === "undefined" || typeof window.matchMedia !== "function") {
|
|
35
|
+
return false;
|
|
36
|
+
}
|
|
37
|
+
try {
|
|
38
|
+
return window.matchMedia("(prefers-reduced-motion: reduce)").matches;
|
|
39
|
+
} catch {
|
|
40
|
+
return false;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
function shouldAnimate(mode) {
|
|
44
|
+
return mode !== "none" && supportsViewTransitions() && !prefersReducedMotion();
|
|
45
|
+
}
|
|
46
|
+
function prepareComponents(components) {
|
|
47
|
+
const pending = [];
|
|
48
|
+
for (const component of components) {
|
|
49
|
+
const preload = component?.preload;
|
|
50
|
+
if (typeof preload === "function") {
|
|
51
|
+
try {
|
|
52
|
+
const result = preload();
|
|
53
|
+
if (result && typeof result.then === "function") {
|
|
54
|
+
pending.push(result.catch(() => void 0));
|
|
55
|
+
}
|
|
56
|
+
} catch {
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
return pending.length ? Promise.all(pending).then(() => void 0) : null;
|
|
61
|
+
}
|
|
62
|
+
function runTransition(mode, commit) {
|
|
63
|
+
if (!shouldAnimate(mode)) {
|
|
64
|
+
return Promise.resolve(commit()).then(() => void 0);
|
|
65
|
+
}
|
|
66
|
+
const start = document.startViewTransition;
|
|
67
|
+
try {
|
|
68
|
+
const transition = start.call(document, () => commit());
|
|
69
|
+
transition.finished?.catch(() => void 0);
|
|
70
|
+
transition.ready?.catch(() => void 0);
|
|
71
|
+
return transition.updateCallbackDone.catch(() => void 0);
|
|
72
|
+
} catch {
|
|
73
|
+
return Promise.resolve(commit()).then(() => void 0);
|
|
74
|
+
}
|
|
75
|
+
}
|