@inglorious/ui 7.3.1 → 7.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@inglorious/ui",
|
|
3
|
-
"version": "7.
|
|
3
|
+
"version": "7.5.0",
|
|
4
4
|
"description": "A design system for Inglorious Web with render-function components and customizable themes",
|
|
5
5
|
"author": "Matteo Antony Mistretta <antony.mistretta@gmail.com> (https://ingloriouscoderz.it)",
|
|
6
6
|
"license": "MIT",
|
|
@@ -14,7 +14,10 @@
|
|
|
14
14
|
bottom: 0;
|
|
15
15
|
z-index: var(--iw-z-modal, 1050);
|
|
16
16
|
|
|
17
|
+
/* As tall as an app bar, so the line under a drawer and the app bar beside
|
|
18
|
+
it runs straight across. */
|
|
17
19
|
.iw-drawer-header {
|
|
20
|
+
min-height: var(--iw-space-16);
|
|
18
21
|
border-bottom: 1px solid var(--iw-color-border);
|
|
19
22
|
flex-shrink: 0;
|
|
20
23
|
|
|
@@ -1,44 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @typedef {import("../../../types/navigation/view-transitions").TransitionsStore} Store
|
|
3
3
|
* @typedef {import("../../../types/navigation/view-transitions").ViewTransitionType} ViewTransitionType
|
|
4
|
+
* @typedef {import("../../../types/navigation/view-transitions").TransitionsOptions} TransitionsOptions
|
|
4
5
|
*/
|
|
5
6
|
|
|
6
|
-
/** The path the router matches against. Hashes are deliberately excluded: they
|
|
7
|
-
* change the fragment, not the page, so they must not animate one. */
|
|
8
|
-
function getPath() {
|
|
9
|
-
return window.location.pathname + window.location.search
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
/** The position of the current entry in the session history, stamped by the
|
|
13
|
-
* Router as it navigates. Comparing it to the previous one is what tells a push
|
|
14
|
-
* from a traversal, and in which direction. */
|
|
15
|
-
function getIndex() {
|
|
16
|
-
return window.history.state?.idx ?? 0
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
function supportsViewTransitions() {
|
|
20
|
-
return typeof document.startViewTransition === "function"
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
/** `types` landed after `startViewTransition` itself, so an engine can have the
|
|
24
|
-
* method and still throw when handed the object form of the argument. */
|
|
25
|
-
function supportsTransitionTypes() {
|
|
26
|
-
return (
|
|
27
|
-
typeof ViewTransition !== "undefined" && "types" in ViewTransition.prototype
|
|
28
|
-
)
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
/**
|
|
32
|
-
* @param {number} index - The index of the entry now current.
|
|
33
|
-
* @param {number} previousIndex - The index of the entry before this render.
|
|
34
|
-
* @returns {ViewTransitionType}
|
|
35
|
-
*/
|
|
36
|
-
function getTransitionType(index, previousIndex) {
|
|
37
|
-
if (index < previousIndex) return "back"
|
|
38
|
-
if (index > previousIndex) return "forward"
|
|
39
|
-
return "replace"
|
|
40
|
-
}
|
|
41
|
-
|
|
42
7
|
/**
|
|
43
8
|
* Wraps a store so that the renders which follow a navigation are run inside a
|
|
44
9
|
* view transition, tagged with the direction they navigated in. Pair it with
|
|
@@ -57,11 +22,28 @@ function getTransitionType(index, previousIndex) {
|
|
|
57
22
|
*
|
|
58
23
|
* mount(withTransitions(store), renderApp, document.getElementById("root"))
|
|
59
24
|
*
|
|
25
|
+
* Pass `routes` to choose which navigations animate; the rest change page
|
|
26
|
+
* without a transition. A list of route names animates every navigation into
|
|
27
|
+
* or out of one of them. A function receives `{ type, from, to }`, where `type`
|
|
28
|
+
* is the direction read from the history and `from` / `to` carry the `path`,
|
|
29
|
+
* `index` and `route` of each side, and returns whether to animate.
|
|
30
|
+
*
|
|
31
|
+
* @example
|
|
32
|
+
* // Slide into and out of detail pages; switch tabs instantly.
|
|
33
|
+
* withTransitions(store, { routes: ["eventDetail", "userDetail"] })
|
|
34
|
+
*
|
|
35
|
+
* @example
|
|
36
|
+
* // Slide only when going deeper or coming back up, never sideways.
|
|
37
|
+
* withTransitions(store, {
|
|
38
|
+
* routes: ({ from, to }) => from.path.startsWith(to.path) || to.path.startsWith(from.path),
|
|
39
|
+
* })
|
|
40
|
+
*
|
|
60
41
|
* @param {Store} store - The store to wrap.
|
|
42
|
+
* @param {TransitionsOptions} [options]
|
|
61
43
|
* @returns {Store} The same store, with a wrapped `subscribe`.
|
|
62
44
|
* @throws {Error} If the store has no `Router` type.
|
|
63
45
|
*/
|
|
64
|
-
export function withTransitions(store) {
|
|
46
|
+
export function withTransitions(store, { routes } = {}) {
|
|
65
47
|
if (!store.getType("Router")) {
|
|
66
48
|
throw new Error(
|
|
67
49
|
'[withTransitions] Requires the "Router" type from @inglorious/web. The ' +
|
|
@@ -71,38 +53,60 @@ export function withTransitions(store) {
|
|
|
71
53
|
}
|
|
72
54
|
|
|
73
55
|
const subscribe = store.subscribe
|
|
56
|
+
const shouldTransition = toPredicate(routes)
|
|
74
57
|
const listeners = new Set()
|
|
75
58
|
|
|
76
59
|
let currentPath = ""
|
|
77
60
|
let currentIndex = 0
|
|
61
|
+
let currentRoute
|
|
78
62
|
let unsubscribeStore = null
|
|
79
63
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
64
|
+
store.subscribe = (listener) => {
|
|
65
|
+
/* Taken here rather than when the store is wrapped, so that anything that
|
|
66
|
+
navigated in between — an initial redirect, a renderer mounting late — is
|
|
67
|
+
part of the baseline instead of a navigation in its own right. It also
|
|
68
|
+
keeps `window` untouched until something subscribes, so wrapping a store
|
|
69
|
+
during a server render is harmless. */
|
|
70
|
+
if (!unsubscribeStore) {
|
|
71
|
+
currentPath = getPath()
|
|
72
|
+
currentIndex = getIndex()
|
|
73
|
+
currentRoute = getRoute(store.getState())
|
|
74
|
+
unsubscribeStore = subscribe.call(store, onStateChange)
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
listeners.add(listener)
|
|
78
|
+
|
|
79
|
+
return () => {
|
|
80
|
+
listeners.delete(listener)
|
|
81
|
+
|
|
82
|
+
if (!listeners.size) {
|
|
83
|
+
unsubscribeStore?.()
|
|
84
|
+
unsubscribeStore = null
|
|
85
|
+
}
|
|
86
|
+
}
|
|
89
87
|
}
|
|
90
88
|
|
|
91
89
|
function onStateChange() {
|
|
92
90
|
const path = getPath()
|
|
93
91
|
const index = getIndex()
|
|
92
|
+
const route = getRoute(store.getState())
|
|
94
93
|
/* Both are compared against the baseline before it moves forward,
|
|
95
94
|
* otherwise the comparison would always be against itself and every
|
|
96
95
|
* navigation would read as a replacement. */
|
|
97
96
|
const hasNavigated = path !== currentPath
|
|
98
|
-
const
|
|
97
|
+
const from = { path: currentPath, index: currentIndex, route: currentRoute }
|
|
98
|
+
const to = { path, index, route }
|
|
99
99
|
|
|
100
100
|
currentPath = path
|
|
101
101
|
currentIndex = index
|
|
102
|
+
currentRoute = route
|
|
102
103
|
|
|
103
104
|
if (!hasNavigated || !supportsViewTransitions()) return notify()
|
|
104
105
|
|
|
105
|
-
const
|
|
106
|
+
const type = getTransitionType(to.index, from.index)
|
|
107
|
+
if (!shouldTransition({ type, from, to })) return notify()
|
|
108
|
+
|
|
109
|
+
const types = [type]
|
|
106
110
|
|
|
107
111
|
if (supportsTransitionTypes()) {
|
|
108
112
|
document.startViewTransition({ update: notify, types })
|
|
@@ -113,29 +117,67 @@ export function withTransitions(store) {
|
|
|
113
117
|
}
|
|
114
118
|
}
|
|
115
119
|
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
}
|
|
120
|
+
/* The update callback of a view transition does not run synchronously: the
|
|
121
|
+
* browser captures the old state first and calls it on a later task. Reading
|
|
122
|
+
* the state here rather than in the subscription keeps a listener from
|
|
123
|
+
* replaying whatever the store held when the transition started. */
|
|
124
|
+
function notify() {
|
|
125
|
+
const state = store.getState()
|
|
126
|
+
// Copied, so that a listener subscribing during this pass waits for the
|
|
127
|
+
// next one instead of being notified by the pass that added it.
|
|
128
|
+
;[...listeners].forEach((listener) => listener(state))
|
|
129
|
+
}
|
|
127
130
|
|
|
128
|
-
|
|
131
|
+
return store
|
|
132
|
+
}
|
|
129
133
|
|
|
130
|
-
|
|
131
|
-
|
|
134
|
+
/** `routes` as a predicate over a navigation. Without it every navigation
|
|
135
|
+
* animates. */
|
|
136
|
+
function toPredicate(routes) {
|
|
137
|
+
if (!routes) return () => true
|
|
138
|
+
if (typeof routes === "function") return routes
|
|
132
139
|
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
}
|
|
137
|
-
}
|
|
138
|
-
}
|
|
140
|
+
const names = new Set(routes)
|
|
141
|
+
return ({ from, to }) => names.has(from.route) || names.has(to.route)
|
|
142
|
+
}
|
|
139
143
|
|
|
140
|
-
|
|
144
|
+
/** The path the router matches against. Hashes are deliberately excluded: they
|
|
145
|
+
* change the fragment, not the page, so they must not animate one. */
|
|
146
|
+
function getPath() {
|
|
147
|
+
return window.location.pathname + window.location.search
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/** The position of the current entry in the session history, stamped by the
|
|
151
|
+
* Router as it navigates. Comparing it to the previous one is what tells a push
|
|
152
|
+
* from a traversal, and in which direction. */
|
|
153
|
+
function getIndex() {
|
|
154
|
+
return window.history.state?.idx ?? 0
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/** The route the Router entity resolved, whatever the entity is called. */
|
|
158
|
+
function getRoute(state) {
|
|
159
|
+
return Object.values(state).find((entity) => entity?.type === "Router")?.route
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
function supportsViewTransitions() {
|
|
163
|
+
return typeof document.startViewTransition === "function"
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* @param {number} index - The index of the entry now current.
|
|
168
|
+
* @param {number} previousIndex - The index of the entry before this render.
|
|
169
|
+
* @returns {ViewTransitionType}
|
|
170
|
+
*/
|
|
171
|
+
function getTransitionType(index, previousIndex) {
|
|
172
|
+
if (index < previousIndex) return "back"
|
|
173
|
+
if (index > previousIndex) return "forward"
|
|
174
|
+
return "replace"
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/** `types` landed after `startViewTransition` itself, so an engine can have the
|
|
178
|
+
* method and still throw when handed the object form of the argument. */
|
|
179
|
+
function supportsTransitionTypes() {
|
|
180
|
+
return (
|
|
181
|
+
typeof ViewTransition !== "undefined" && "types" in ViewTransition.prototype
|
|
182
|
+
)
|
|
141
183
|
}
|
|
@@ -10,8 +10,33 @@ export interface TransitionsStore<S = unknown> {
|
|
|
10
10
|
getType(name: string): unknown
|
|
11
11
|
}
|
|
12
12
|
|
|
13
|
+
/** One side of a navigation. */
|
|
14
|
+
export interface NavigationSide {
|
|
15
|
+
path: string
|
|
16
|
+
/** The history index the Router stamped on the entry. */
|
|
17
|
+
index: number
|
|
18
|
+
/** The route the Router resolved, if any. */
|
|
19
|
+
route?: string
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export interface Navigation {
|
|
23
|
+
/** The direction read from the history. */
|
|
24
|
+
type: ViewTransitionType
|
|
25
|
+
from: NavigationSide
|
|
26
|
+
to: NavigationSide
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export interface TransitionsOptions {
|
|
30
|
+
/** Which navigations animate: those into or out of the named routes, or
|
|
31
|
+
* those the function accepts. Omit it to animate every navigation. */
|
|
32
|
+
routes?: string[] | ((navigation: Navigation) => boolean)
|
|
33
|
+
}
|
|
34
|
+
|
|
13
35
|
export interface WithTransitions {
|
|
14
|
-
<S>(
|
|
36
|
+
<S>(
|
|
37
|
+
store: TransitionsStore<S>,
|
|
38
|
+
options?: TransitionsOptions,
|
|
39
|
+
): TransitionsStore<S>
|
|
15
40
|
}
|
|
16
41
|
|
|
17
42
|
export declare const withTransitions: WithTransitions
|