@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
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Navigation presentation: preparing a route before it is shown, and handing the DOM
|
|
3
|
+
* swap to the browser's View Transition API when there is one.
|
|
4
|
+
*
|
|
5
|
+
* ## The lifecycle this replaces
|
|
6
|
+
*
|
|
7
|
+
* Navigation used to be one synchronous chain. `navigate()` pushed history, the core
|
|
8
|
+
* router recomputed the match and notified, the `Router` component wrote the match
|
|
9
|
+
* signal, and the render memo rebuilt the layer at the depth that changed. The old DOM
|
|
10
|
+
* came out at that moment: whether or not the new route had anything to put in its
|
|
11
|
+
* place:
|
|
12
|
+
*
|
|
13
|
+
* - a route with a `data` loader renders `null` until the resource resolves,
|
|
14
|
+
* - a `lazy()` route whose chunk is not loaded suspends to the nearest fallback.
|
|
15
|
+
*
|
|
16
|
+
* Both are an empty intermediate state with the old page already gone, which is what
|
|
17
|
+
* reads as a flash. It is not a paint-timing artifact and no animation would hide it.
|
|
18
|
+
*
|
|
19
|
+
* ## The lifecycle now
|
|
20
|
+
*
|
|
21
|
+
* navigation-start
|
|
22
|
+
* │ the match is computed, but not committed
|
|
23
|
+
* prepare
|
|
24
|
+
* │ every matched layer's module is loaded (`lazy().preload()`), so the
|
|
25
|
+
* │ component renders synchronously instead of suspending
|
|
26
|
+
* begin transition
|
|
27
|
+
* │ `document.startViewTransition`, when it applies
|
|
28
|
+
* commit
|
|
29
|
+
* │ the match signal is written and effects are flushed, inside the
|
|
30
|
+
* │ transition's callback, so the browser snapshots a settled DOM
|
|
31
|
+
* navigation-complete
|
|
32
|
+
*
|
|
33
|
+
* Nothing here delays anything. The commit happens as soon as preparation resolves; on
|
|
34
|
+
* a warm route that is the same tick it always was.
|
|
35
|
+
*/
|
|
36
|
+
/** How a navigation should be presented. */
|
|
37
|
+
export type TransitionMode = 'auto' | 'none';
|
|
38
|
+
/**
|
|
39
|
+
* Whether this environment can animate a route swap.
|
|
40
|
+
*
|
|
41
|
+
* Read at call time rather than cached at module load: a test swaps the API in and out,
|
|
42
|
+
* and a cached answer would make the second case unreachable.
|
|
43
|
+
*/
|
|
44
|
+
export declare function supportsViewTransitions(): boolean;
|
|
45
|
+
/**
|
|
46
|
+
* Whether the reader asked not to be animated.
|
|
47
|
+
*
|
|
48
|
+
* A stated preference outranks the app's configuration, so this is checked on every
|
|
49
|
+
* navigation rather than once: the setting can change while the page is open.
|
|
50
|
+
*/
|
|
51
|
+
export declare function prefersReducedMotion(): boolean;
|
|
52
|
+
/** Whether this particular commit should go through a view transition. */
|
|
53
|
+
export declare function shouldAnimate(mode: TransitionMode): boolean;
|
|
54
|
+
/**
|
|
55
|
+
* Load what the next route needs to render without suspending.
|
|
56
|
+
*
|
|
57
|
+
* Only the route's own modules. Data loaders are deliberately left alone: the resource
|
|
58
|
+
* belongs to the layer's owner and is created when that layer renders, so calling the
|
|
59
|
+
* loader here would either run the request twice or require a cache this router does not
|
|
60
|
+
* own. See the note in the router on what that leaves.
|
|
61
|
+
*
|
|
62
|
+
* A module that fails to load resolves anyway. Preparation is an optimisation, and a
|
|
63
|
+
* rejected import should surface through the route's own error handling when it renders,
|
|
64
|
+
* not by leaving the navigation permanently pending.
|
|
65
|
+
*/
|
|
66
|
+
export declare function prepareComponents(components: readonly unknown[]): Promise<void> | null;
|
|
67
|
+
/**
|
|
68
|
+
* Run `commit` inside a view transition, or directly when one does not apply.
|
|
69
|
+
*
|
|
70
|
+
* The callback is async because writing the match signal only queues the work: effects
|
|
71
|
+
* are scheduled, so the DOM is not settled when the write returns. `startViewTransition`
|
|
72
|
+
* waits for the returned promise before snapshotting the new state, so flushing inside
|
|
73
|
+
* it is what makes the browser compare two finished frames rather than one finished and
|
|
74
|
+
* one half-built.
|
|
75
|
+
*
|
|
76
|
+
* The returned promise resolves when the DOM has been updated, not when the animation
|
|
77
|
+
* ends. A caller waiting on navigation wants the former; blocking on the latter would
|
|
78
|
+
* make every navigation as slow as its animation.
|
|
79
|
+
*/
|
|
80
|
+
export declare function runTransition(mode: TransitionMode, commit: () => void | Promise<void>): Promise<void>;
|
|
81
|
+
//# sourceMappingURL=transition.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"transition.d.ts","sourceRoot":"","sources":["../../../../src/lib/router/transition.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AAcH,4CAA4C;AAC5C,MAAM,MAAM,cAAc,GAAG,MAAM,GAAG,MAAM,CAAC;AAE7C;;;;;GAKG;AACH,wBAAgB,uBAAuB,IAAI,OAAO,CAMjD;AAED;;;;;GAKG;AACH,wBAAgB,oBAAoB,IAAI,OAAO,CAU9C;AAED,0EAA0E;AAC1E,wBAAgB,aAAa,CAAC,IAAI,EAAE,cAAc,GAAG,OAAO,CAE3D;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,iBAAiB,CAAC,UAAU,EAAE,SAAS,OAAO,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAqBtF;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,aAAa,CAC3B,IAAI,EAAE,cAAc,EACpB,MAAM,EAAE,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,GACjC,OAAO,CAAC,IAAI,CAAC,CAsBf"}
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Navigation presentation: preparing a route before it is shown, and handing the DOM
|
|
3
|
+
* swap to the browser's View Transition API when there is one.
|
|
4
|
+
*
|
|
5
|
+
* ## The lifecycle this replaces
|
|
6
|
+
*
|
|
7
|
+
* Navigation used to be one synchronous chain. `navigate()` pushed history, the core
|
|
8
|
+
* router recomputed the match and notified, the `Router` component wrote the match
|
|
9
|
+
* signal, and the render memo rebuilt the layer at the depth that changed. The old DOM
|
|
10
|
+
* came out at that moment: whether or not the new route had anything to put in its
|
|
11
|
+
* place:
|
|
12
|
+
*
|
|
13
|
+
* - a route with a `data` loader renders `null` until the resource resolves,
|
|
14
|
+
* - a `lazy()` route whose chunk is not loaded suspends to the nearest fallback.
|
|
15
|
+
*
|
|
16
|
+
* Both are an empty intermediate state with the old page already gone, which is what
|
|
17
|
+
* reads as a flash. It is not a paint-timing artifact and no animation would hide it.
|
|
18
|
+
*
|
|
19
|
+
* ## The lifecycle now
|
|
20
|
+
*
|
|
21
|
+
* navigation-start
|
|
22
|
+
* │ the match is computed, but not committed
|
|
23
|
+
* prepare
|
|
24
|
+
* │ every matched layer's module is loaded (`lazy().preload()`), so the
|
|
25
|
+
* │ component renders synchronously instead of suspending
|
|
26
|
+
* begin transition
|
|
27
|
+
* │ `document.startViewTransition`, when it applies
|
|
28
|
+
* commit
|
|
29
|
+
* │ the match signal is written and effects are flushed, inside the
|
|
30
|
+
* │ transition's callback, so the browser snapshots a settled DOM
|
|
31
|
+
* navigation-complete
|
|
32
|
+
*
|
|
33
|
+
* Nothing here delays anything. The commit happens as soon as preparation resolves; on
|
|
34
|
+
* a warm route that is the same tick it always was.
|
|
35
|
+
*/
|
|
36
|
+
/**
|
|
37
|
+
* Whether this environment can animate a route swap.
|
|
38
|
+
*
|
|
39
|
+
* Read at call time rather than cached at module load: a test swaps the API in and out,
|
|
40
|
+
* and a cached answer would make the second case unreachable.
|
|
41
|
+
*/
|
|
42
|
+
export function supportsViewTransitions() {
|
|
43
|
+
return (typeof document !== 'undefined' &&
|
|
44
|
+
typeof document
|
|
45
|
+
.startViewTransition === 'function');
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Whether the reader asked not to be animated.
|
|
49
|
+
*
|
|
50
|
+
* A stated preference outranks the app's configuration, so this is checked on every
|
|
51
|
+
* navigation rather than once: the setting can change while the page is open.
|
|
52
|
+
*/
|
|
53
|
+
export function prefersReducedMotion() {
|
|
54
|
+
if (typeof window === 'undefined' || typeof window.matchMedia !== 'function') {
|
|
55
|
+
return false;
|
|
56
|
+
}
|
|
57
|
+
try {
|
|
58
|
+
return window.matchMedia('(prefers-reduced-motion: reduce)').matches;
|
|
59
|
+
}
|
|
60
|
+
catch {
|
|
61
|
+
// A matchMedia that throws on an unknown query is not a reason to fail navigation.
|
|
62
|
+
return false;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
/** Whether this particular commit should go through a view transition. */
|
|
66
|
+
export function shouldAnimate(mode) {
|
|
67
|
+
return mode !== 'none' && supportsViewTransitions() && !prefersReducedMotion();
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Load what the next route needs to render without suspending.
|
|
71
|
+
*
|
|
72
|
+
* Only the route's own modules. Data loaders are deliberately left alone: the resource
|
|
73
|
+
* belongs to the layer's owner and is created when that layer renders, so calling the
|
|
74
|
+
* loader here would either run the request twice or require a cache this router does not
|
|
75
|
+
* own. See the note in the router on what that leaves.
|
|
76
|
+
*
|
|
77
|
+
* A module that fails to load resolves anyway. Preparation is an optimisation, and a
|
|
78
|
+
* rejected import should surface through the route's own error handling when it renders,
|
|
79
|
+
* not by leaving the navigation permanently pending.
|
|
80
|
+
*/
|
|
81
|
+
export function prepareComponents(components) {
|
|
82
|
+
const pending = [];
|
|
83
|
+
for (const component of components) {
|
|
84
|
+
const preload = component
|
|
85
|
+
?.preload;
|
|
86
|
+
if (typeof preload === 'function') {
|
|
87
|
+
try {
|
|
88
|
+
const result = preload();
|
|
89
|
+
if (result && typeof result.then === 'function') {
|
|
90
|
+
pending.push(result.catch(() => undefined));
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
catch {
|
|
94
|
+
// A preload that throws synchronously is the same as one that rejects.
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
// Null rather than a resolved promise: the caller commits synchronously when there is
|
|
99
|
+
// nothing to wait for, which keeps a warm navigation on the tick it was already on.
|
|
100
|
+
return pending.length ? Promise.all(pending).then(() => undefined) : null;
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Run `commit` inside a view transition, or directly when one does not apply.
|
|
104
|
+
*
|
|
105
|
+
* The callback is async because writing the match signal only queues the work: effects
|
|
106
|
+
* are scheduled, so the DOM is not settled when the write returns. `startViewTransition`
|
|
107
|
+
* waits for the returned promise before snapshotting the new state, so flushing inside
|
|
108
|
+
* it is what makes the browser compare two finished frames rather than one finished and
|
|
109
|
+
* one half-built.
|
|
110
|
+
*
|
|
111
|
+
* The returned promise resolves when the DOM has been updated, not when the animation
|
|
112
|
+
* ends. A caller waiting on navigation wants the former; blocking on the latter would
|
|
113
|
+
* make every navigation as slow as its animation.
|
|
114
|
+
*/
|
|
115
|
+
export function runTransition(mode, commit) {
|
|
116
|
+
if (!shouldAnimate(mode)) {
|
|
117
|
+
return Promise.resolve(commit()).then(() => undefined);
|
|
118
|
+
}
|
|
119
|
+
const start = document
|
|
120
|
+
.startViewTransition;
|
|
121
|
+
try {
|
|
122
|
+
const transition = start.call(document, () => commit());
|
|
123
|
+
// Swallow the rejections a skipped or interrupted transition produces. Both of
|
|
124
|
+
// these reject when the animation is cut short, by `skipTransition()`, by another
|
|
125
|
+
// navigation starting, or by the tab going to the background. The DOM update inside
|
|
126
|
+
// the callback still happened, so a navigation must not fail because an animation
|
|
127
|
+
// did not play, and an unobserved rejection here would surface as an unhandled one.
|
|
128
|
+
transition.finished?.catch(() => undefined);
|
|
129
|
+
transition.ready?.catch(() => undefined);
|
|
130
|
+
return transition.updateCallbackDone.catch(() => undefined);
|
|
131
|
+
}
|
|
132
|
+
catch {
|
|
133
|
+
// A browser that has the method but refuses the call still has to navigate.
|
|
134
|
+
return Promise.resolve(commit()).then(() => undefined);
|
|
135
|
+
}
|
|
136
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
// src/lib/router/transition.ts
|
|
2
|
+
function supportsViewTransitions() {
|
|
3
|
+
return typeof document !== "undefined" && typeof document.startViewTransition === "function";
|
|
4
|
+
}
|
|
5
|
+
function prefersReducedMotion() {
|
|
6
|
+
if (typeof window === "undefined" || typeof window.matchMedia !== "function") {
|
|
7
|
+
return false;
|
|
8
|
+
}
|
|
9
|
+
try {
|
|
10
|
+
return window.matchMedia("(prefers-reduced-motion: reduce)").matches;
|
|
11
|
+
} catch {
|
|
12
|
+
return false;
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
function shouldAnimate(mode) {
|
|
16
|
+
return mode !== "none" && supportsViewTransitions() && !prefersReducedMotion();
|
|
17
|
+
}
|
|
18
|
+
function prepareComponents(components) {
|
|
19
|
+
const pending = [];
|
|
20
|
+
for (const component of components) {
|
|
21
|
+
const preload = component?.preload;
|
|
22
|
+
if (typeof preload === "function") {
|
|
23
|
+
try {
|
|
24
|
+
const result = preload();
|
|
25
|
+
if (result && typeof result.then === "function") {
|
|
26
|
+
pending.push(result.catch(() => void 0));
|
|
27
|
+
}
|
|
28
|
+
} catch {
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
return pending.length ? Promise.all(pending).then(() => void 0) : null;
|
|
33
|
+
}
|
|
34
|
+
function runTransition(mode, commit) {
|
|
35
|
+
if (!shouldAnimate(mode)) {
|
|
36
|
+
return Promise.resolve(commit()).then(() => void 0);
|
|
37
|
+
}
|
|
38
|
+
const start = document.startViewTransition;
|
|
39
|
+
try {
|
|
40
|
+
const transition = start.call(document, () => commit());
|
|
41
|
+
transition.finished?.catch(() => void 0);
|
|
42
|
+
transition.ready?.catch(() => void 0);
|
|
43
|
+
return transition.updateCallbackDone.catch(() => void 0);
|
|
44
|
+
} catch {
|
|
45
|
+
return Promise.resolve(commit()).then(() => void 0);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
export {
|
|
49
|
+
prefersReducedMotion,
|
|
50
|
+
prepareComponents,
|
|
51
|
+
runTransition,
|
|
52
|
+
shouldAnimate,
|
|
53
|
+
supportsViewTransitions
|
|
54
|
+
};
|
|
@@ -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;
|
|
@@ -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/server/hydration.ts
|
|
4
4
|
import { isServer as domIsServer } from "@fluixi/dom";
|
package/dist/src/routes.cjs
CHANGED
|
@@ -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;
|
package/dist/src/routes.mjs
CHANGED
package/dist/src/utils.cjs
CHANGED
|
@@ -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;
|
package/dist/src/utils.mjs
CHANGED