@protobase/ui 0.45.8 → 0.45.10
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/app/app.js +2 -1
- package/dist/app/page-places.js +34 -0
- package/dist/app/restore-scroll.js +33 -0
- package/dist/app/router.js +47 -5
- package/dist/app/use-app-address.d.ts +3 -2
- package/dist/app/use-app-address.js +10 -6
- package/dist/app-shell/app-shell.js +2 -1
- package/dist/app-shell/bottom-bar.js +7 -4
- package/dist/app-shell/page-scroll.js +1 -1
- package/dist/app-shell/top-bar.js +10 -6
- package/dist/package.js +1 -1
- package/dist/shell/assets/{app-DpiiLWK-.js → app-a37LI8Ia.js} +8 -8
- package/dist/shell/assets/{index-DAmnAPZI.js → index-AsD8FYy1.js} +2 -2
- package/dist/shell/assets/index-Bq-Va32r.css +2 -0
- package/dist/shell/assets/{records-map-DPcmpL_F.js → records-map-Ph48lNmf.js} +2 -2
- package/dist/shell/assets/{shared-protobase-ui-C-uFkvJy.js → shared-protobase-ui-P9_LY8Im.js} +2 -2
- package/dist/shell/index.html +4 -4
- package/dist/shell/protobase.shell.json +1 -1
- package/dist/shell/protobase.sw.js +2 -2
- package/dist/shell-source-maps/assets/app-a37LI8Ia.js.map +1 -0
- package/dist/shell-source-maps/assets/{index-DAmnAPZI.js.map → index-AsD8FYy1.js.map} +1 -1
- package/dist/shell-source-maps/assets/{records-map-DPcmpL_F.js.map → records-map-Ph48lNmf.js.map} +1 -1
- package/dist/shell-source-maps/assets/{shared-protobase-ui-C-uFkvJy.js.map → shared-protobase-ui-P9_LY8Im.js.map} +1 -1
- package/package.json +4 -4
- package/dist/shell/assets/index-DGJkc-U0.css +0 -2
- package/dist/shell-source-maps/assets/app-DpiiLWK-.js.map +0 -1
package/dist/app/app.js
CHANGED
|
@@ -82,7 +82,7 @@ const Routes = ({ workspace, sidebarMode, assistant, runtime }) => {
|
|
|
82
82
|
const user = state.kind === "signed-in" ? state.user : void 0;
|
|
83
83
|
const staff = state.kind === "signed-in" ? state.staff : void 0;
|
|
84
84
|
const { path, params, basePath } = useRouter();
|
|
85
|
-
const { address, go, open } = useAppAddress();
|
|
85
|
+
const { address, go, open, back } = useAppAddress();
|
|
86
86
|
const place = placeOf(address);
|
|
87
87
|
const shown = recordOf(address);
|
|
88
88
|
const search = useGlobalSearch();
|
|
@@ -248,6 +248,7 @@ const Routes = ({ workspace, sidebarMode, assistant, runtime }) => {
|
|
|
248
248
|
onSwitch: role.switchTo
|
|
249
249
|
} : void 0,
|
|
250
250
|
onNavigate: open,
|
|
251
|
+
onBack: back,
|
|
251
252
|
sidebarNotice: /* @__PURE__ */ jsx(NewVersionBanner, {}),
|
|
252
253
|
banner: (staff || pluginNotice) && /* @__PURE__ */ jsxs(Fragment$1, { children: [staff && /* @__PURE__ */ jsx(StaffBanner, {
|
|
253
254
|
signIn: staff,
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { guarded } from "../lib/guarded.js";
|
|
2
|
+
//#region src/app/page-places.ts
|
|
3
|
+
const storageKey = "protobase:page-places";
|
|
4
|
+
/** The places kept, the most recent ones, so the storage stays small however long the tab is used. */
|
|
5
|
+
const kept = 50;
|
|
6
|
+
const isPlace = (value) => Array.isArray(value) && value.length === 2 && typeof value[0] === "string" && typeof value[1] === "number" && Number.isFinite(value[1]) && value[1] >= 0;
|
|
7
|
+
const read = (storage) => guarded(() => {
|
|
8
|
+
const raw = storage.getItem(storageKey);
|
|
9
|
+
if (raw === null) return [];
|
|
10
|
+
const parsed = JSON.parse(raw);
|
|
11
|
+
return Array.isArray(parsed) ? parsed.filter(isPlace) : [];
|
|
12
|
+
}, []);
|
|
13
|
+
/**
|
|
14
|
+
* How far down the document was scrolled in each history entry of a tab, by the entry's id, kept in `storage` (the
|
|
15
|
+
* tab's session storage) when `persist` is called, so they outlast a reload of the page. An entry never left yet, as
|
|
16
|
+
* one a link just made, starts at the top.
|
|
17
|
+
*/
|
|
18
|
+
const createPagePlaces = (storage) => {
|
|
19
|
+
const places = new Map(read(storage));
|
|
20
|
+
const placeOf = (entry) => places.get(entry) ?? 0;
|
|
21
|
+
const save = (entry, top) => {
|
|
22
|
+
places.delete(entry);
|
|
23
|
+
places.set(entry, Math.max(0, Math.round(top)));
|
|
24
|
+
for (const oldest of [...places.keys()].slice(0, Math.max(0, places.size - kept))) places.delete(oldest);
|
|
25
|
+
};
|
|
26
|
+
const persist = () => guarded(() => storage.setItem(storageKey, JSON.stringify([...places])), void 0);
|
|
27
|
+
return {
|
|
28
|
+
placeOf,
|
|
29
|
+
save,
|
|
30
|
+
persist
|
|
31
|
+
};
|
|
32
|
+
};
|
|
33
|
+
//#endregion
|
|
34
|
+
export { createPagePlaces };
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
//#region src/app/restore-scroll.ts
|
|
2
|
+
/** How long a page may take to grow tall enough, as while its data loads, before it is left where it is. */
|
|
3
|
+
const patience = 3e3;
|
|
4
|
+
/**
|
|
5
|
+
* Scrolls the document to `top`. A page still too short for it, as one whose data is loading, is scrolled again each
|
|
6
|
+
* time it grows, until it is there, a few seconds pass, or anything else scrolls it: the person, or a list that brings
|
|
7
|
+
* itself back to its place. Returns how to stop early.
|
|
8
|
+
*/
|
|
9
|
+
const restoreScroll = (top) => {
|
|
10
|
+
let placed = 0;
|
|
11
|
+
const place = () => {
|
|
12
|
+
window.scrollTo({
|
|
13
|
+
top,
|
|
14
|
+
behavior: "instant"
|
|
15
|
+
});
|
|
16
|
+
placed = window.scrollY;
|
|
17
|
+
return placed >= top - 1;
|
|
18
|
+
};
|
|
19
|
+
if (place()) return () => {};
|
|
20
|
+
const observer = new ResizeObserver(() => place() && stop());
|
|
21
|
+
const moved = () => Math.abs(window.scrollY - placed) > 1 && stop();
|
|
22
|
+
const stop = () => {
|
|
23
|
+
observer.disconnect();
|
|
24
|
+
clearTimeout(timer);
|
|
25
|
+
window.removeEventListener("scroll", moved);
|
|
26
|
+
};
|
|
27
|
+
const timer = setTimeout(stop, patience);
|
|
28
|
+
observer.observe(document.body);
|
|
29
|
+
window.addEventListener("scroll", moved, { passive: true });
|
|
30
|
+
return stop;
|
|
31
|
+
};
|
|
32
|
+
//#endregion
|
|
33
|
+
export { restoreScroll };
|
package/dist/app/router.js
CHANGED
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
import { RouteLink } from "../primitives/route-link.js";
|
|
2
|
+
import { guarded } from "../lib/guarded.js";
|
|
2
3
|
import { Button } from "../primitives/button.js";
|
|
3
4
|
import { Dialog } from "../primitives/dialog.js";
|
|
4
5
|
import { text } from "./messages.js";
|
|
5
|
-
import {
|
|
6
|
+
import { createPagePlaces } from "./page-places.js";
|
|
7
|
+
import { restoreScroll } from "./restore-scroll.js";
|
|
8
|
+
import { createContext, useCallback, useContext, useEffect, useLayoutEffect, useMemo, useRef, useState } from "react";
|
|
6
9
|
import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
|
|
7
10
|
//#region src/app/router.tsx
|
|
8
11
|
const RouterContext = createContext(null);
|
|
@@ -28,12 +31,17 @@ const historyEntry = () => {
|
|
|
28
31
|
}, "");
|
|
29
32
|
return entry;
|
|
30
33
|
};
|
|
34
|
+
const noStorage = {
|
|
35
|
+
getItem: () => null,
|
|
36
|
+
setItem: () => {}
|
|
37
|
+
};
|
|
31
38
|
const Router = ({ basePath = "", initialUrl, children }) => {
|
|
32
39
|
const memory = initialUrl !== void 0;
|
|
33
40
|
const [page, setPage] = useState(() => ({
|
|
34
41
|
url: initialUrl ?? browserUrl(),
|
|
35
42
|
entry: memory ? newEntry() : historyEntry()
|
|
36
43
|
}));
|
|
44
|
+
const [places] = useState(() => createPagePlaces(memory ? noStorage : guarded(() => window.sessionStorage, void 0) ?? noStorage));
|
|
37
45
|
const guards = useRef(/* @__PURE__ */ new Map());
|
|
38
46
|
const losesInput = () => [...guards.current.values()].includes("input-lost");
|
|
39
47
|
const current = useRef(page);
|
|
@@ -44,12 +52,23 @@ const Router = ({ basePath = "", initialUrl, children }) => {
|
|
|
44
52
|
const leaveAfterUndo = useRef(void 0);
|
|
45
53
|
const reloading = useRef(false);
|
|
46
54
|
const [pending, setPending] = useState();
|
|
55
|
+
const rememberScroll = useCallback(() => places.save(current.current.entry, window.scrollY), [places]);
|
|
56
|
+
useLayoutEffect(() => restoreScroll(places.placeOf(page.entry)), [places, page.entry]);
|
|
47
57
|
useEffect(() => {
|
|
48
58
|
if (memory) return;
|
|
59
|
+
window.history.scrollRestoration = "manual";
|
|
49
60
|
window.history.replaceState({
|
|
61
|
+
...historyState(),
|
|
50
62
|
protobaseIndex: position.current,
|
|
51
63
|
protobaseEntry: current.current.entry
|
|
52
64
|
}, "");
|
|
65
|
+
const persist = () => {
|
|
66
|
+
rememberScroll();
|
|
67
|
+
places.persist();
|
|
68
|
+
};
|
|
69
|
+
const hidden = () => document.visibilityState === "hidden" && persist();
|
|
70
|
+
window.addEventListener("pagehide", persist);
|
|
71
|
+
document.addEventListener("visibilitychange", hidden);
|
|
53
72
|
const onPop = () => {
|
|
54
73
|
const next = historyIndex();
|
|
55
74
|
if (undoing.current) {
|
|
@@ -73,28 +92,40 @@ const Router = ({ basePath = "", initialUrl, children }) => {
|
|
|
73
92
|
}
|
|
74
93
|
proceeding.current = false;
|
|
75
94
|
position.current = next;
|
|
95
|
+
rememberScroll();
|
|
76
96
|
setPage({
|
|
77
97
|
url: browserUrl(),
|
|
78
98
|
entry: historyEntry()
|
|
79
99
|
});
|
|
80
100
|
};
|
|
81
101
|
window.addEventListener("popstate", onPop);
|
|
82
|
-
return () =>
|
|
83
|
-
|
|
102
|
+
return () => {
|
|
103
|
+
window.removeEventListener("popstate", onPop);
|
|
104
|
+
window.removeEventListener("pagehide", persist);
|
|
105
|
+
document.removeEventListener("visibilitychange", hidden);
|
|
106
|
+
};
|
|
107
|
+
}, [
|
|
108
|
+
memory,
|
|
109
|
+
rememberScroll,
|
|
110
|
+
places
|
|
111
|
+
]);
|
|
84
112
|
const go = useCallback((full, replace) => {
|
|
85
113
|
const entry = replace ? current.current.entry : newEntry();
|
|
86
114
|
if (!memory) {
|
|
87
115
|
position.current += replace ? 0 : 1;
|
|
116
|
+
const previous = replace ? historyState().protobasePrevious : current.current.url;
|
|
88
117
|
window.history[replace ? "replaceState" : "pushState"]({
|
|
89
118
|
protobaseIndex: position.current,
|
|
90
|
-
protobaseEntry: entry
|
|
119
|
+
protobaseEntry: entry,
|
|
120
|
+
protobasePrevious: previous
|
|
91
121
|
}, "", full);
|
|
92
122
|
}
|
|
123
|
+
rememberScroll();
|
|
93
124
|
setPage({
|
|
94
125
|
url: full,
|
|
95
126
|
entry
|
|
96
127
|
});
|
|
97
|
-
}, [memory]);
|
|
128
|
+
}, [memory, rememberScroll]);
|
|
98
129
|
const navigate = useCallback((to, { replace = false } = {}) => {
|
|
99
130
|
const full = `${basePath}${to}`;
|
|
100
131
|
if (pathOf(full) !== pathOf(current.current.url) && guards.current.size > 0) return setPending({
|
|
@@ -105,6 +136,15 @@ const Router = ({ basePath = "", initialUrl, children }) => {
|
|
|
105
136
|
});
|
|
106
137
|
go(full, replace);
|
|
107
138
|
}, [basePath, go]);
|
|
139
|
+
const back = useCallback((to) => {
|
|
140
|
+
const previous = memory ? void 0 : historyState().protobasePrevious;
|
|
141
|
+
if (previous !== void 0 && pathOf(previous) === pathOf(`${basePath}${to}`)) return window.history.back();
|
|
142
|
+
navigate(to);
|
|
143
|
+
}, [
|
|
144
|
+
memory,
|
|
145
|
+
basePath,
|
|
146
|
+
navigate
|
|
147
|
+
]);
|
|
108
148
|
const registerGuard = useCallback((kind = "draft-kept") => {
|
|
109
149
|
const token = Symbol("guard");
|
|
110
150
|
guards.current.set(token, kind);
|
|
@@ -138,6 +178,7 @@ const Router = ({ basePath = "", initialUrl, children }) => {
|
|
|
138
178
|
basePath,
|
|
139
179
|
entry: page.entry,
|
|
140
180
|
navigate,
|
|
181
|
+
back,
|
|
141
182
|
registerGuard,
|
|
142
183
|
reload,
|
|
143
184
|
reloadConfirmed
|
|
@@ -146,6 +187,7 @@ const Router = ({ basePath = "", initialUrl, children }) => {
|
|
|
146
187
|
page,
|
|
147
188
|
basePath,
|
|
148
189
|
navigate,
|
|
190
|
+
back,
|
|
149
191
|
registerGuard,
|
|
150
192
|
reload,
|
|
151
193
|
reloadConfirmed
|
|
@@ -5,8 +5,8 @@ import { AppAddress } from "@protobase/schema";
|
|
|
5
5
|
/**
|
|
6
6
|
* The app's address as logical values, and the ways to change it: `set` changes the list in place, `listTo` is the
|
|
7
7
|
* list after a change for a link to it, `go` moves to
|
|
8
|
-
* another address, `href` is an address for an `<a href
|
|
9
|
-
* writes the address through this hook, never the query string or a path of its own.
|
|
8
|
+
* another address, `href` is an address for an `<a href>`, `open` follows such an href and `back` goes back to one.
|
|
9
|
+
* Every component reads and writes the address through this hook, never the query string or a path of its own.
|
|
10
10
|
*/
|
|
11
11
|
export declare const useAppAddress: () => {
|
|
12
12
|
address: CurrentAddress | undefined;
|
|
@@ -17,6 +17,7 @@ export declare const useAppAddress: () => {
|
|
|
17
17
|
}) => void;
|
|
18
18
|
href: (to: AppAddress) => string;
|
|
19
19
|
open: (target: string) => void;
|
|
20
|
+
back: (target: string) => void;
|
|
20
21
|
};
|
|
21
22
|
/** A link to an address in the app: an ordinary anchor that the router follows without reloading the page. */
|
|
22
23
|
export declare const AppLink: ({ to, ...rest }: AnchorHTMLAttributes<HTMLAnchorElement> & {
|
|
@@ -9,11 +9,11 @@ import { appPath, readAppPath } from "@protobase/schema";
|
|
|
9
9
|
/**
|
|
10
10
|
* The app's address as logical values, and the ways to change it: `set` changes the list in place, `listTo` is the
|
|
11
11
|
* list after a change for a link to it, `go` moves to
|
|
12
|
-
* another address, `href` is an address for an `<a href
|
|
13
|
-
* writes the address through this hook, never the query string or a path of its own.
|
|
12
|
+
* another address, `href` is an address for an `<a href>`, `open` follows such an href and `back` goes back to one.
|
|
13
|
+
* Every component reads and writes the address through this hook, never the query string or a path of its own.
|
|
14
14
|
*/
|
|
15
15
|
const useAppAddress = () => {
|
|
16
|
-
const { path, params, basePath, navigate } = useRouter();
|
|
16
|
+
const { path, params, basePath, navigate, back: backTo } = useRouter();
|
|
17
17
|
const { pages, resources, views } = useAdminMeta();
|
|
18
18
|
const xs = useIsXs();
|
|
19
19
|
const pageNames = useMemo(() => new Set(Object.keys(pages)), [pages]);
|
|
@@ -37,13 +37,17 @@ const useAppAddress = () => {
|
|
|
37
37
|
if (read?.kind !== "list") throw new Error("listTo and set change the list the app shows; use go to move to another address");
|
|
38
38
|
return changedList(read, change, defaults(read.resource, read.list));
|
|
39
39
|
}, [read, defaults]);
|
|
40
|
+
const set = useCallback((change) => go(listTo(change), { replace: true }), [listTo, go]);
|
|
41
|
+
const href = useCallback((to) => appPath(to, basePath), [basePath]);
|
|
42
|
+
const below = useCallback((target) => target.startsWith(`${basePath}/`) ? target.slice(basePath.length) : target, [basePath]);
|
|
40
43
|
return {
|
|
41
44
|
address,
|
|
42
|
-
set
|
|
45
|
+
set,
|
|
43
46
|
listTo,
|
|
44
47
|
go,
|
|
45
|
-
href
|
|
46
|
-
open: useCallback((target) => navigate(
|
|
48
|
+
href,
|
|
49
|
+
open: useCallback((target) => navigate(below(target)), [below, navigate]),
|
|
50
|
+
back: useCallback((target) => backTo(below(target)), [below, backTo])
|
|
47
51
|
};
|
|
48
52
|
};
|
|
49
53
|
/** A link to an address in the app: an ordinary anchor that the router follows without reloading the page. */
|
|
@@ -11,7 +11,7 @@ import { useEffect, useRef, useState } from "react";
|
|
|
11
11
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
12
12
|
//#region src/app-shell/app-shell.tsx
|
|
13
13
|
const drawerId = "app-drawer";
|
|
14
|
-
const AppShell = ({ sidebarMode, activeItem, breadcrumb, user, workspace, appName, appIcon, logo, actions, rightPanel, sidebarNotice, banner, forceExpanded, profileMenuOpen, navGroups, userMenu, bottomBar, onNavigate, onSignOut, signIn, organizations, roles, theme, search, pageScroll: scrollsByDefault = false, className, children }) => {
|
|
14
|
+
const AppShell = ({ sidebarMode, activeItem, breadcrumb, user, workspace, appName, appIcon, logo, actions, rightPanel, sidebarNotice, banner, forceExpanded, profileMenuOpen, navGroups, userMenu, bottomBar, onNavigate, onBack, onSignOut, signIn, organizations, roles, theme, search, pageScroll: scrollsByDefault = false, className, children }) => {
|
|
15
15
|
const desktop = useIsDesktop();
|
|
16
16
|
const noticeShown = useSidebarNoticeShown(sidebarMode);
|
|
17
17
|
const [drawerOpen, setDrawerOpen] = useState(false);
|
|
@@ -67,6 +67,7 @@ const AppShell = ({ sidebarMode, activeItem, breadcrumb, user, workspace, appNam
|
|
|
67
67
|
sticky: pageScroll,
|
|
68
68
|
breadcrumb,
|
|
69
69
|
onNavigate,
|
|
70
|
+
onBack,
|
|
70
71
|
actions,
|
|
71
72
|
onMenu: desktop || tabs ? void 0 : () => setDrawerOpen(true),
|
|
72
73
|
menuOpen: drawerOpen,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { cn } from "../lib/cn.js";
|
|
2
2
|
import { text } from "./messages.js";
|
|
3
3
|
import { Ellipsis } from "lucide-react";
|
|
4
|
-
import { jsx, jsxs } from "react/jsx-runtime";
|
|
4
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
5
5
|
//#region src/app-shell/bottom-bar.tsx
|
|
6
6
|
const inApp = (event) => event.button === 0 && !event.metaKey && !event.ctrlKey && !event.shiftKey && !event.altKey;
|
|
7
7
|
const tab = "flex min-h-[49px] min-w-0 flex-1 flex-col items-center justify-center gap-1 pb-1 pt-1.5 text-[10px] font-medium leading-none";
|
|
@@ -12,10 +12,13 @@ const tab = "flex min-h-[49px] min-w-0 flex-1 flex-col items-center justify-cent
|
|
|
12
12
|
*/
|
|
13
13
|
const BottomBar = ({ tabs, onNavigate, onMore, moreOpen, moreControls, sticky, ref }) => {
|
|
14
14
|
const moreCurrent = !tabs.some((item) => item.active);
|
|
15
|
-
return /* @__PURE__ */ jsxs("
|
|
15
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [sticky && /* @__PURE__ */ jsx("div", {
|
|
16
|
+
"aria-hidden": true,
|
|
17
|
+
className: "h-[var(--pb-sticky-bottom)] shrink-0 md:hidden"
|
|
18
|
+
}), /* @__PURE__ */ jsxs("nav", {
|
|
16
19
|
ref,
|
|
17
20
|
"aria-label": text("tabs"),
|
|
18
|
-
className: cn("flex shrink-0 border-t bg-background pb-[var(--pb-safe-bottom)] md:hidden", sticky && "
|
|
21
|
+
className: cn("flex shrink-0 border-t bg-background pb-[var(--pb-safe-bottom)] md:hidden", sticky && "fixed bottom-0 left-[var(--pb-safe-left)] right-[var(--pb-safe-right)] z-30"),
|
|
19
22
|
children: [tabs.map((item) => {
|
|
20
23
|
const Icon = item.icon;
|
|
21
24
|
return /* @__PURE__ */ jsxs("a", {
|
|
@@ -53,7 +56,7 @@ const BottomBar = ({ tabs, onNavigate, onMore, moreOpen, moreControls, sticky, r
|
|
|
53
56
|
"aria-hidden": true
|
|
54
57
|
}), text("more")]
|
|
55
58
|
})]
|
|
56
|
-
});
|
|
59
|
+
})] });
|
|
57
60
|
};
|
|
58
61
|
//#endregion
|
|
59
62
|
export { BottomBar };
|
|
@@ -4,7 +4,7 @@ import { createContext, useContext, useLayoutEffect } from "react";
|
|
|
4
4
|
const PageScrollContext = createContext(() => {});
|
|
5
5
|
/**
|
|
6
6
|
* While the calling page is shown and `on`, the document scrolls instead of a box of the screen's height: the top and
|
|
7
|
-
* bottom bars and the sidebar
|
|
7
|
+
* bottom bars and the sidebar stay in view, and the page grows with its content, so the browser's own scrolling applies
|
|
8
8
|
* throughout, scrolling to the top included, as tapping the status bar on an iPhone does. Sticky content in the page
|
|
9
9
|
* stays clear of the bars with `--pb-sticky-top` and `--pb-sticky-bottom`. Not `on`, the page fills the box instead,
|
|
10
10
|
* as a map does. Once the page is gone the shell's `pageScroll` applies again.
|
|
@@ -9,7 +9,7 @@ import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
|
|
|
9
9
|
//#region src/app-shell/top-bar.tsx
|
|
10
10
|
/** An icon button of the top bar on a mobile screen, as the menu, back and search buttons are. */
|
|
11
11
|
const topBarButton = "inline-flex size-11 shrink-0 items-center justify-center rounded-md text-muted-foreground hover:bg-muted md:hidden";
|
|
12
|
-
const TopBar = ({ breadcrumb, onNavigate, actions, onMenu, menuOpen, menuControls, search, sticky, slotRef, ref }) => {
|
|
12
|
+
const TopBar = ({ breadcrumb, onNavigate, onBack, actions, onMenu, menuOpen, menuControls, search, sticky, slotRef, ref }) => {
|
|
13
13
|
const [searchOpen, setSearchOpen] = useState(false);
|
|
14
14
|
const back = backTarget(breadcrumb);
|
|
15
15
|
const desktopSearch = useRef(null);
|
|
@@ -25,9 +25,9 @@ const TopBar = ({ breadcrumb, onNavigate, actions, onMenu, menuOpen, menuControl
|
|
|
25
25
|
window.addEventListener("keydown", onKey);
|
|
26
26
|
return () => window.removeEventListener("keydown", onKey);
|
|
27
27
|
}, [search]);
|
|
28
|
-
return /* @__PURE__ */ jsxs("header", {
|
|
28
|
+
return /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsxs("header", {
|
|
29
29
|
ref,
|
|
30
|
-
className: cn(sticky ? "sticky top-0 z-30" : "relative", "flex h-[calc(3rem+var(--pb-safe-top))] shrink-0 items-center gap-1 border-b bg-background pl-4 pr-2 pt-[var(--pb-safe-top)] md:gap-4 md:px-5"),
|
|
30
|
+
className: cn(sticky ? "sticky top-0 z-30 max-md:fixed max-md:left-[var(--pb-safe-left)] max-md:right-[var(--pb-safe-right)]" : "relative", "flex h-[calc(3rem+var(--pb-safe-top))] shrink-0 items-center gap-1 border-b bg-background pl-4 pr-2 pt-[var(--pb-safe-top)] md:gap-4 md:px-5"),
|
|
31
31
|
children: [
|
|
32
32
|
onMenu && /* @__PURE__ */ jsx("button", {
|
|
33
33
|
type: "button",
|
|
@@ -42,9 +42,10 @@ const TopBar = ({ breadcrumb, onNavigate, actions, onMenu, menuOpen, menuControl
|
|
|
42
42
|
href: back.href,
|
|
43
43
|
"aria-label": text("backTo", back.label),
|
|
44
44
|
onClick: (event) => {
|
|
45
|
-
|
|
45
|
+
const follow = onBack ?? onNavigate;
|
|
46
|
+
if (!follow || !inApp(event)) return;
|
|
46
47
|
event.preventDefault();
|
|
47
|
-
|
|
48
|
+
follow(back.href);
|
|
48
49
|
},
|
|
49
50
|
className: cn("inline-flex size-11 shrink-0 items-center justify-center rounded-md text-muted-foreground hover:bg-muted md:hidden", !onMenu && "-ml-3"),
|
|
50
51
|
children: /* @__PURE__ */ jsx(ChevronLeft, { className: "size-5" })
|
|
@@ -99,7 +100,10 @@ const TopBar = ({ breadcrumb, onNavigate, actions, onMenu, menuOpen, menuControl
|
|
|
99
100
|
})]
|
|
100
101
|
})
|
|
101
102
|
]
|
|
102
|
-
})
|
|
103
|
+
}), sticky && /* @__PURE__ */ jsx("div", {
|
|
104
|
+
"aria-hidden": true,
|
|
105
|
+
className: "h-[var(--pb-sticky-top)] shrink-0 md:hidden"
|
|
106
|
+
})] });
|
|
103
107
|
};
|
|
104
108
|
//#endregion
|
|
105
109
|
export { TopBar, topBarButton };
|
package/dist/package.js
CHANGED