@dloizides/ui-nav 1.4.0 → 1.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/CHANGELOG.md +27 -0
- package/dist/index.d.mts +44 -1
- package/dist/index.d.ts +44 -1
- package/dist/index.js +133 -14
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +134 -15
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,32 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 1.5.0
|
|
4
|
+
|
|
5
|
+
Make the `AppShell` back-office `sidebar` layout **responsive** — a mobile drawer.
|
|
6
|
+
|
|
7
|
+
The `sidebar` slot (1.4.0) always rendered a persistent fixed-width left rail in a
|
|
8
|
+
`flexDirection:'row'` body with no width breakpoint, so below ~768px the content column
|
|
9
|
+
starved (headings wrapped a character per line, inputs truncated) — the merchant admin was
|
|
10
|
+
unusable on a phone. Now `AppShell` reads `useWindowDimensions`:
|
|
11
|
+
|
|
12
|
+
- **Desktop (≥768px): IDENTICAL to 1.4.0** — the persistent rail + row layout, untouched.
|
|
13
|
+
Purely additive; no consuming product's desktop layout changes.
|
|
14
|
+
- **Below 768px:** the rail is not rendered beside the content; instead a **hamburger**
|
|
15
|
+
(in the header) opens the same `Sidebar` as an overlay **drawer** (a tap-to-close scrim +
|
|
16
|
+
a left panel) and the content takes the full width. The drawer closes on a scrim tap and
|
|
17
|
+
on a nav-item press (web: a bubbled click on a `role="button"` / link inside the panel).
|
|
18
|
+
|
|
19
|
+
Accessibility: the hamburger has a testID + `accessibilityLabel` + `accessibilityHint` and a
|
|
20
|
+
themed focus ring; the drawer panel is `role="dialog"` `aria-modal`; the scrim is a labelled
|
|
21
|
+
button. New optional prop `mobileMenu?: Partial<DrawerLabels>` supplies pre-localized
|
|
22
|
+
open/close labels (English defaults otherwise — this package has no i18n). New testID
|
|
23
|
+
suffixes: `-menu-toggle`, `-drawer`, `-drawer-scrim`.
|
|
24
|
+
|
|
25
|
+
**Backwards compatible.** No breaking prop changes — consumers pass the same `sidebar` slot.
|
|
26
|
+
Omit `sidebar` and the tree is byte-identical to 1.4.0 (erevna / katalogos / kefi untouched).
|
|
27
|
+
Only a `sidebar` consumer on a narrow viewport sees the drawer. Pinned by tests asserting the
|
|
28
|
+
rail is present on desktop and absent (hamburger present) on a narrow viewport.
|
|
29
|
+
|
|
3
30
|
## 1.4.0
|
|
4
31
|
|
|
5
32
|
Add an optional **`sidebar`** slot to `AppShell` — the back-office layout.
|
package/dist/index.d.mts
CHANGED
|
@@ -195,6 +195,33 @@ interface NavBarProps {
|
|
|
195
195
|
}
|
|
196
196
|
declare const NavBar: ({ items, pathname, onNavigate, regionLabel, navigateHint, brand, right, menuLabel, menuHint, renderMenuIcon, collapseBelow, containerStyle, }: NavBarProps) => React.ReactElement;
|
|
197
197
|
|
|
198
|
+
/**
|
|
199
|
+
* MobileDrawer — the below-the-breakpoint face of `AppShell`'s back-office
|
|
200
|
+
* `sidebar` layout. On a narrow viewport (<768px) the persistent left rail would
|
|
201
|
+
* starve the content column, so instead of the rail we render a hamburger toggle
|
|
202
|
+
* (`MenuToggle`) in the header and slide the same `Sidebar` in as an overlay
|
|
203
|
+
* drawer (a scrim + a left panel). The drawer closes on a scrim tap and on any
|
|
204
|
+
* nav-item press inside the panel (web: a bubbled click on a `role="button"` /
|
|
205
|
+
* link element). Desktop is untouched — AppShell never mounts this above 768px.
|
|
206
|
+
*
|
|
207
|
+
* Contract discipline (same as the rest of `@dloizides/ui-nav`): NO FM / router /
|
|
208
|
+
* store imports. Labels are pre-localized strings (English defaults, overridable
|
|
209
|
+
* by the consumer via `AppShell`'s `mobileMenu` prop); every colour is read from
|
|
210
|
+
* the `@dloizides/ui-feedback` UiProvider theme.
|
|
211
|
+
*/
|
|
212
|
+
|
|
213
|
+
/** Pre-localized labels for the drawer's hamburger + scrim. */
|
|
214
|
+
interface DrawerLabels {
|
|
215
|
+
/** Accessible name of the hamburger that opens the drawer. */
|
|
216
|
+
openLabel: string;
|
|
217
|
+
/** Accessible hint of the hamburger that opens the drawer. */
|
|
218
|
+
openHint: string;
|
|
219
|
+
/** Accessible name of the scrim (tap to close). */
|
|
220
|
+
closeLabel: string;
|
|
221
|
+
/** Accessible hint of the scrim (tap to close). */
|
|
222
|
+
closeHint: string;
|
|
223
|
+
}
|
|
224
|
+
|
|
198
225
|
/** Content-width policy: a capped column, optionally wider past a viewport breakpoint, or full-bleed. */
|
|
199
226
|
type AppShellWidth = {
|
|
200
227
|
max: number;
|
|
@@ -245,8 +272,18 @@ interface AppShellProps {
|
|
|
245
272
|
* supplied, the region below header/nav/banner lays out as a row: the rail on the
|
|
246
273
|
* left, the scrolling content column on the right. Omit for the default vertical
|
|
247
274
|
* stack — the tree is then byte-identical to the pre-`sidebar` AppShell.
|
|
275
|
+
*
|
|
276
|
+
* Responsive: at/under ~768px the persistent rail is REPLACED by a hamburger (in
|
|
277
|
+
* the header) that opens the same `sidebar` as an overlay drawer, so the content
|
|
278
|
+
* takes the full width. Desktop (≥768px) is unchanged. No effect without `sidebar`.
|
|
248
279
|
*/
|
|
249
280
|
sidebar?: React.ReactNode;
|
|
281
|
+
/**
|
|
282
|
+
* Pre-localized labels for the mobile nav drawer (only used in the `sidebar`
|
|
283
|
+
* layout, below the breakpoint): the hamburger's open label/hint and the scrim's
|
|
284
|
+
* close label/hint. Optional — English defaults apply. NO i18n in this package.
|
|
285
|
+
*/
|
|
286
|
+
mobileMenu?: Partial<DrawerLabels>;
|
|
250
287
|
/** Banner slot (e.g. verification-pending). */
|
|
251
288
|
banner?: React.ReactNode;
|
|
252
289
|
/** Content-width policy. Defaults to full-bleed. */
|
|
@@ -275,7 +312,7 @@ interface AppShellProps {
|
|
|
275
312
|
testID: string;
|
|
276
313
|
children: React.ReactNode;
|
|
277
314
|
}
|
|
278
|
-
declare const AppShell: ({ header, nav, sidebar, banner, width, contentPadding, chromeAlignment, gate, state, testID, children, }: AppShellProps) => React.ReactElement | null;
|
|
315
|
+
declare const AppShell: ({ header, nav, sidebar, mobileMenu, banner, width, contentPadding, chromeAlignment, gate, state, testID, children, }: AppShellProps) => React.ReactElement | null;
|
|
279
316
|
|
|
280
317
|
/**
|
|
281
318
|
* Default testIDs for `@dloizides/ui-nav`. Kept as a small central map (mirrors
|
|
@@ -305,6 +342,12 @@ declare const APP_SHELL_SUFFIX: {
|
|
|
305
342
|
readonly nav: "-nav";
|
|
306
343
|
/** The persistent left rail of the back-office layout (only when `sidebar` is supplied). */
|
|
307
344
|
readonly sidebar: "-sidebar";
|
|
345
|
+
/** Mobile hamburger that opens the nav drawer (back-office layout, below the breakpoint). */
|
|
346
|
+
readonly menuToggle: "-menu-toggle";
|
|
347
|
+
/** The mobile nav drawer panel (holds the `sidebar` slot when opened on a narrow viewport). */
|
|
348
|
+
readonly drawer: "-drawer";
|
|
349
|
+
/** The mobile nav drawer scrim (tap to close). */
|
|
350
|
+
readonly scrim: "-drawer-scrim";
|
|
308
351
|
readonly banner: "-banner";
|
|
309
352
|
readonly pending: "-pending";
|
|
310
353
|
readonly loading: "-loading";
|
package/dist/index.d.ts
CHANGED
|
@@ -195,6 +195,33 @@ interface NavBarProps {
|
|
|
195
195
|
}
|
|
196
196
|
declare const NavBar: ({ items, pathname, onNavigate, regionLabel, navigateHint, brand, right, menuLabel, menuHint, renderMenuIcon, collapseBelow, containerStyle, }: NavBarProps) => React.ReactElement;
|
|
197
197
|
|
|
198
|
+
/**
|
|
199
|
+
* MobileDrawer — the below-the-breakpoint face of `AppShell`'s back-office
|
|
200
|
+
* `sidebar` layout. On a narrow viewport (<768px) the persistent left rail would
|
|
201
|
+
* starve the content column, so instead of the rail we render a hamburger toggle
|
|
202
|
+
* (`MenuToggle`) in the header and slide the same `Sidebar` in as an overlay
|
|
203
|
+
* drawer (a scrim + a left panel). The drawer closes on a scrim tap and on any
|
|
204
|
+
* nav-item press inside the panel (web: a bubbled click on a `role="button"` /
|
|
205
|
+
* link element). Desktop is untouched — AppShell never mounts this above 768px.
|
|
206
|
+
*
|
|
207
|
+
* Contract discipline (same as the rest of `@dloizides/ui-nav`): NO FM / router /
|
|
208
|
+
* store imports. Labels are pre-localized strings (English defaults, overridable
|
|
209
|
+
* by the consumer via `AppShell`'s `mobileMenu` prop); every colour is read from
|
|
210
|
+
* the `@dloizides/ui-feedback` UiProvider theme.
|
|
211
|
+
*/
|
|
212
|
+
|
|
213
|
+
/** Pre-localized labels for the drawer's hamburger + scrim. */
|
|
214
|
+
interface DrawerLabels {
|
|
215
|
+
/** Accessible name of the hamburger that opens the drawer. */
|
|
216
|
+
openLabel: string;
|
|
217
|
+
/** Accessible hint of the hamburger that opens the drawer. */
|
|
218
|
+
openHint: string;
|
|
219
|
+
/** Accessible name of the scrim (tap to close). */
|
|
220
|
+
closeLabel: string;
|
|
221
|
+
/** Accessible hint of the scrim (tap to close). */
|
|
222
|
+
closeHint: string;
|
|
223
|
+
}
|
|
224
|
+
|
|
198
225
|
/** Content-width policy: a capped column, optionally wider past a viewport breakpoint, or full-bleed. */
|
|
199
226
|
type AppShellWidth = {
|
|
200
227
|
max: number;
|
|
@@ -245,8 +272,18 @@ interface AppShellProps {
|
|
|
245
272
|
* supplied, the region below header/nav/banner lays out as a row: the rail on the
|
|
246
273
|
* left, the scrolling content column on the right. Omit for the default vertical
|
|
247
274
|
* stack — the tree is then byte-identical to the pre-`sidebar` AppShell.
|
|
275
|
+
*
|
|
276
|
+
* Responsive: at/under ~768px the persistent rail is REPLACED by a hamburger (in
|
|
277
|
+
* the header) that opens the same `sidebar` as an overlay drawer, so the content
|
|
278
|
+
* takes the full width. Desktop (≥768px) is unchanged. No effect without `sidebar`.
|
|
248
279
|
*/
|
|
249
280
|
sidebar?: React.ReactNode;
|
|
281
|
+
/**
|
|
282
|
+
* Pre-localized labels for the mobile nav drawer (only used in the `sidebar`
|
|
283
|
+
* layout, below the breakpoint): the hamburger's open label/hint and the scrim's
|
|
284
|
+
* close label/hint. Optional — English defaults apply. NO i18n in this package.
|
|
285
|
+
*/
|
|
286
|
+
mobileMenu?: Partial<DrawerLabels>;
|
|
250
287
|
/** Banner slot (e.g. verification-pending). */
|
|
251
288
|
banner?: React.ReactNode;
|
|
252
289
|
/** Content-width policy. Defaults to full-bleed. */
|
|
@@ -275,7 +312,7 @@ interface AppShellProps {
|
|
|
275
312
|
testID: string;
|
|
276
313
|
children: React.ReactNode;
|
|
277
314
|
}
|
|
278
|
-
declare const AppShell: ({ header, nav, sidebar, banner, width, contentPadding, chromeAlignment, gate, state, testID, children, }: AppShellProps) => React.ReactElement | null;
|
|
315
|
+
declare const AppShell: ({ header, nav, sidebar, mobileMenu, banner, width, contentPadding, chromeAlignment, gate, state, testID, children, }: AppShellProps) => React.ReactElement | null;
|
|
279
316
|
|
|
280
317
|
/**
|
|
281
318
|
* Default testIDs for `@dloizides/ui-nav`. Kept as a small central map (mirrors
|
|
@@ -305,6 +342,12 @@ declare const APP_SHELL_SUFFIX: {
|
|
|
305
342
|
readonly nav: "-nav";
|
|
306
343
|
/** The persistent left rail of the back-office layout (only when `sidebar` is supplied). */
|
|
307
344
|
readonly sidebar: "-sidebar";
|
|
345
|
+
/** Mobile hamburger that opens the nav drawer (back-office layout, below the breakpoint). */
|
|
346
|
+
readonly menuToggle: "-menu-toggle";
|
|
347
|
+
/** The mobile nav drawer panel (holds the `sidebar` slot when opened on a narrow viewport). */
|
|
348
|
+
readonly drawer: "-drawer";
|
|
349
|
+
/** The mobile nav drawer scrim (tap to close). */
|
|
350
|
+
readonly scrim: "-drawer-scrim";
|
|
308
351
|
readonly banner: "-banner";
|
|
309
352
|
readonly pending: "-pending";
|
|
310
353
|
readonly loading: "-loading";
|
package/dist/index.js
CHANGED
|
@@ -348,6 +348,12 @@ var APP_SHELL_SUFFIX = {
|
|
|
348
348
|
nav: "-nav",
|
|
349
349
|
/** The persistent left rail of the back-office layout (only when `sidebar` is supplied). */
|
|
350
350
|
sidebar: "-sidebar",
|
|
351
|
+
/** Mobile hamburger that opens the nav drawer (back-office layout, below the breakpoint). */
|
|
352
|
+
menuToggle: "-menu-toggle",
|
|
353
|
+
/** The mobile nav drawer panel (holds the `sidebar` slot when opened on a narrow viewport). */
|
|
354
|
+
drawer: "-drawer",
|
|
355
|
+
/** The mobile nav drawer scrim (tap to close). */
|
|
356
|
+
scrim: "-drawer-scrim",
|
|
351
357
|
banner: "-banner",
|
|
352
358
|
pending: "-pending",
|
|
353
359
|
loading: "-loading",
|
|
@@ -671,6 +677,82 @@ var NavBar = ({
|
|
|
671
677
|
}
|
|
672
678
|
);
|
|
673
679
|
};
|
|
680
|
+
var MOBILE_BREAKPOINT = 768;
|
|
681
|
+
var SCRIM_COLOR = "rgba(0, 0, 0, 0.5)";
|
|
682
|
+
var MENU_GLYPH2 = "\u2630";
|
|
683
|
+
var MENU_MIN_TARGET = 44;
|
|
684
|
+
var MENU_GLYPH_FONT_SIZE = 20;
|
|
685
|
+
var MENU_TOGGLE_PADDING = 12;
|
|
686
|
+
var DRAWER_Z_INDEX = 50;
|
|
687
|
+
var NAV_ACTIVATABLE_SELECTOR = 'a, [role="button"], [role="link"]';
|
|
688
|
+
var DEFAULT_DRAWER_LABELS = {
|
|
689
|
+
openLabel: "Menu",
|
|
690
|
+
openHint: "Open the navigation menu",
|
|
691
|
+
closeLabel: "Close menu",
|
|
692
|
+
closeHint: "Close the navigation menu"
|
|
693
|
+
};
|
|
694
|
+
var styles = reactNative.StyleSheet.create({
|
|
695
|
+
overlay: { ...reactNative.StyleSheet.absoluteFillObject, zIndex: DRAWER_Z_INDEX },
|
|
696
|
+
scrim: { ...reactNative.StyleSheet.absoluteFillObject, backgroundColor: SCRIM_COLOR },
|
|
697
|
+
panel: { position: "absolute", top: 0, bottom: 0, left: 0 },
|
|
698
|
+
menuToggle: {
|
|
699
|
+
minWidth: MENU_MIN_TARGET,
|
|
700
|
+
minHeight: MENU_MIN_TARGET,
|
|
701
|
+
alignItems: "center",
|
|
702
|
+
justifyContent: "center",
|
|
703
|
+
paddingHorizontal: MENU_TOGGLE_PADDING
|
|
704
|
+
},
|
|
705
|
+
menuToggleGlyph: { fontSize: MENU_GLYPH_FONT_SIZE, fontWeight: "700" }
|
|
706
|
+
});
|
|
707
|
+
var MenuToggle = ({ label, hint, onOpen, testID }) => {
|
|
708
|
+
const { theme } = uiFeedback.useUi();
|
|
709
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
710
|
+
FocusableTouchable,
|
|
711
|
+
{
|
|
712
|
+
accessibilityHint: hint,
|
|
713
|
+
accessibilityLabel: label,
|
|
714
|
+
ringColor: theme.palette.primary["500"],
|
|
715
|
+
style: styles.menuToggle,
|
|
716
|
+
testID,
|
|
717
|
+
onPress: onOpen,
|
|
718
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [styles.menuToggleGlyph, { color: theme.colors.text }], children: MENU_GLYPH2 })
|
|
719
|
+
}
|
|
720
|
+
);
|
|
721
|
+
};
|
|
722
|
+
var MobileDrawer = ({
|
|
723
|
+
sidebar,
|
|
724
|
+
labels,
|
|
725
|
+
onClose,
|
|
726
|
+
drawerTestID,
|
|
727
|
+
scrimTestID
|
|
728
|
+
}) => {
|
|
729
|
+
const panelRef = react.useRef(null);
|
|
730
|
+
react.useEffect(() => {
|
|
731
|
+
const node = panelRef.current;
|
|
732
|
+
if (node === null || typeof node.addEventListener !== "function") return void 0;
|
|
733
|
+
const handleClick = (event) => {
|
|
734
|
+
const target = event.target;
|
|
735
|
+
const onNavItem = target !== null && target.closest(NAV_ACTIVATABLE_SELECTOR) !== null;
|
|
736
|
+
if (onNavItem) onClose();
|
|
737
|
+
};
|
|
738
|
+
node.addEventListener("click", handleClick);
|
|
739
|
+
return () => node.removeEventListener("click", handleClick);
|
|
740
|
+
}, [onClose]);
|
|
741
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: styles.overlay, children: [
|
|
742
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
743
|
+
reactNative.Pressable,
|
|
744
|
+
{
|
|
745
|
+
accessibilityHint: labels.closeHint,
|
|
746
|
+
accessibilityLabel: labels.closeLabel,
|
|
747
|
+
accessibilityRole: "button",
|
|
748
|
+
style: styles.scrim,
|
|
749
|
+
testID: scrimTestID,
|
|
750
|
+
onPress: onClose
|
|
751
|
+
}
|
|
752
|
+
),
|
|
753
|
+
/* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { ref: panelRef, "aria-modal": true, role: "dialog", style: styles.panel, testID: drawerTestID, children: sidebar })
|
|
754
|
+
] });
|
|
755
|
+
};
|
|
674
756
|
function resolveContentMaxWidth(width, viewport) {
|
|
675
757
|
if (width === "full") return "full";
|
|
676
758
|
const wideMinViewport = width.wideMinViewport ?? Number.POSITIVE_INFINITY;
|
|
@@ -688,11 +770,14 @@ var CARD_BORDER_WIDTH = 1;
|
|
|
688
770
|
var CARD_TITLE_FONT_SIZE = 16;
|
|
689
771
|
var CARD_MESSAGE_FONT_SIZE = 14;
|
|
690
772
|
var CARD_TITLE_MARGIN_BOTTOM = 8;
|
|
691
|
-
var
|
|
773
|
+
var styles2 = reactNative.StyleSheet.create({
|
|
692
774
|
root: { flex: 1 },
|
|
693
775
|
centerFill: { flex: 1, justifyContent: "center", alignItems: "center" },
|
|
694
776
|
scroll: { flex: 1 },
|
|
695
777
|
scrollContent: { flexGrow: 1 },
|
|
778
|
+
// Mobile back-office header: the hamburger sits inline before the header slot.
|
|
779
|
+
headerRow: { flexDirection: "row", alignItems: "center" },
|
|
780
|
+
headerFill: { flex: 1 },
|
|
696
781
|
// Back-office layout: a persistent left rail beside the scrolling content column.
|
|
697
782
|
bodyRow: { flex: 1, flexDirection: "row" },
|
|
698
783
|
columnFull: { flex: 1 },
|
|
@@ -718,11 +803,11 @@ function MessageCard({
|
|
|
718
803
|
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
719
804
|
reactNative.View,
|
|
720
805
|
{
|
|
721
|
-
style: [
|
|
806
|
+
style: [styles2.card, { backgroundColor: colors.surface, borderColor: accentColor }],
|
|
722
807
|
testID,
|
|
723
808
|
children: [
|
|
724
|
-
/* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [
|
|
725
|
-
/* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [
|
|
809
|
+
/* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [styles2.cardTitle, { color: accentColor }], children: message.titleText }),
|
|
810
|
+
/* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [styles2.cardMessage, { color: colors.textSecondary }], children: message.messageText })
|
|
726
811
|
]
|
|
727
812
|
}
|
|
728
813
|
);
|
|
@@ -736,13 +821,14 @@ function useContentBody(state, children, testID) {
|
|
|
736
821
|
if (state?.error)
|
|
737
822
|
return /* @__PURE__ */ jsxRuntime.jsx(MessageCard, { accentColor: errorColor, message: state.error, testID: `${testID}${APP_SHELL_SUFFIX.error}` });
|
|
738
823
|
if (state?.loading === true)
|
|
739
|
-
return /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style:
|
|
824
|
+
return /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: styles2.centerFill, testID: `${testID}${APP_SHELL_SUFFIX.loading}`, children: /* @__PURE__ */ jsxRuntime.jsx(reactNative.ActivityIndicator, { color: primary, size: "large" }) });
|
|
740
825
|
return children;
|
|
741
826
|
}
|
|
742
827
|
var AppShell = ({
|
|
743
828
|
header,
|
|
744
829
|
nav,
|
|
745
830
|
sidebar,
|
|
831
|
+
mobileMenu,
|
|
746
832
|
banner,
|
|
747
833
|
width = "full",
|
|
748
834
|
contentPadding = DEFAULT_CONTENT_PADDING,
|
|
@@ -756,32 +842,65 @@ var AppShell = ({
|
|
|
756
842
|
const primary = theme.palette.primary["500"];
|
|
757
843
|
const maxWidth = useContentMaxWidth(width);
|
|
758
844
|
const body = useContentBody(state, children, testID);
|
|
845
|
+
const { width: viewportWidth } = reactNative.useWindowDimensions();
|
|
846
|
+
const isNarrow = viewportWidth < MOBILE_BREAKPOINT;
|
|
847
|
+
const useDrawer = sidebar !== void 0 && isNarrow;
|
|
848
|
+
const [drawerOpen, setDrawerOpen] = react.useState(false);
|
|
849
|
+
const openDrawer = react.useCallback(() => setDrawerOpen(true), []);
|
|
850
|
+
const closeDrawer = react.useCallback(() => setDrawerOpen(false), []);
|
|
851
|
+
react.useEffect(() => {
|
|
852
|
+
if (!isNarrow && drawerOpen) setDrawerOpen(false);
|
|
853
|
+
}, [isNarrow, drawerOpen]);
|
|
854
|
+
const drawerLabels = { ...DEFAULT_DRAWER_LABELS, ...mobileMenu };
|
|
759
855
|
const isUnauthenticated = gate !== void 0 && !gate.pending && !gate.authenticated;
|
|
760
856
|
react.useEffect(() => {
|
|
761
857
|
if (isUnauthenticated && gate !== void 0) gate.onRedirect();
|
|
762
858
|
}, [isUnauthenticated, gate]);
|
|
763
859
|
if (gate?.pending === true)
|
|
764
|
-
return /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: [
|
|
860
|
+
return /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: [styles2.root, styles2.centerFill, { backgroundColor: theme.colors.background }], testID: `${testID}${APP_SHELL_SUFFIX.pending}`, children: /* @__PURE__ */ jsxRuntime.jsx(reactNative.ActivityIndicator, { color: primary, size: "large" }) });
|
|
765
861
|
if (isUnauthenticated) return null;
|
|
766
|
-
const columnStyle = maxWidth === "full" ?
|
|
767
|
-
const navInnerStyle = chromeAlignment === "content" && maxWidth !== "full" ? [
|
|
862
|
+
const columnStyle = maxWidth === "full" ? styles2.columnFull : [styles2.columnCapped, { maxWidth }];
|
|
863
|
+
const navInnerStyle = chromeAlignment === "content" && maxWidth !== "full" ? [styles2.chromeCapped, { maxWidth, paddingHorizontal: contentPadding }] : void 0;
|
|
768
864
|
const scroller = /* @__PURE__ */ jsxRuntime.jsx(
|
|
769
865
|
reactNative.ScrollView,
|
|
770
866
|
{
|
|
771
|
-
contentContainerStyle: [
|
|
772
|
-
style:
|
|
867
|
+
contentContainerStyle: [styles2.scrollContent, { padding: contentPadding }],
|
|
868
|
+
style: styles2.scroll,
|
|
773
869
|
testID: `${testID}${APP_SHELL_SUFFIX.content}`,
|
|
774
870
|
children: /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: columnStyle, children: body })
|
|
775
871
|
}
|
|
776
872
|
);
|
|
777
|
-
|
|
778
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
873
|
+
const headerRegion = useDrawer ? /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: styles2.headerRow, testID: `${testID}${APP_SHELL_SUFFIX.header}`, children: [
|
|
874
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
875
|
+
MenuToggle,
|
|
876
|
+
{
|
|
877
|
+
hint: drawerLabels.openHint,
|
|
878
|
+
label: drawerLabels.openLabel,
|
|
879
|
+
testID: `${testID}${APP_SHELL_SUFFIX.menuToggle}`,
|
|
880
|
+
onOpen: openDrawer
|
|
881
|
+
}
|
|
882
|
+
),
|
|
883
|
+
/* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: styles2.headerFill, children: header })
|
|
884
|
+
] }) : /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { testID: `${testID}${APP_SHELL_SUFFIX.header}`, children: header });
|
|
885
|
+
const showRail = sidebar !== void 0 && !isNarrow;
|
|
886
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: [styles2.root, { backgroundColor: theme.colors.background }], testID, children: [
|
|
887
|
+
headerRegion,
|
|
779
888
|
nav !== void 0 ? /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { testID: `${testID}${APP_SHELL_SUFFIX.nav}`, children: navInnerStyle !== void 0 ? /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: navInnerStyle, children: nav }) : nav }) : null,
|
|
780
889
|
banner !== void 0 ? /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { testID: `${testID}${APP_SHELL_SUFFIX.banner}`, children: banner }) : null,
|
|
781
|
-
|
|
890
|
+
showRail ? /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: styles2.bodyRow, children: [
|
|
782
891
|
/* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { testID: `${testID}${APP_SHELL_SUFFIX.sidebar}`, children: sidebar }),
|
|
783
892
|
scroller
|
|
784
|
-
] }) : scroller
|
|
893
|
+
] }) : scroller,
|
|
894
|
+
useDrawer && drawerOpen ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
895
|
+
MobileDrawer,
|
|
896
|
+
{
|
|
897
|
+
drawerTestID: `${testID}${APP_SHELL_SUFFIX.drawer}`,
|
|
898
|
+
labels: drawerLabels,
|
|
899
|
+
scrimTestID: `${testID}${APP_SHELL_SUFFIX.scrim}`,
|
|
900
|
+
sidebar,
|
|
901
|
+
onClose: closeDrawer
|
|
902
|
+
}
|
|
903
|
+
) : null
|
|
785
904
|
] });
|
|
786
905
|
};
|
|
787
906
|
function roleRoutesToNavItems(routes, translate) {
|