@dloizides/ui-nav 1.2.0 → 1.4.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 +63 -22
- package/README.md +15 -2
- package/dist/index.d.mts +26 -3
- package/dist/index.d.ts +26 -3
- package/dist/index.js +208 -56
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +209 -57
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -81,7 +81,12 @@ var navBarStyles = reactNative.StyleSheet.create({
|
|
|
81
81
|
marginLeft: "auto",
|
|
82
82
|
paddingHorizontal: 10,
|
|
83
83
|
paddingVertical: 6,
|
|
84
|
-
borderRadius: 4
|
|
84
|
+
borderRadius: 4,
|
|
85
|
+
// a11y: keep the hamburger a comfortable ≥44×44 touch target.
|
|
86
|
+
minWidth: 44,
|
|
87
|
+
minHeight: 44,
|
|
88
|
+
alignItems: "center",
|
|
89
|
+
justifyContent: "center"
|
|
85
90
|
},
|
|
86
91
|
toggleGlyph: { fontSize: 20, fontWeight: "700" },
|
|
87
92
|
links: {
|
|
@@ -106,7 +111,10 @@ var navBarStyles = reactNative.StyleSheet.create({
|
|
|
106
111
|
paddingHorizontal: 14,
|
|
107
112
|
borderRadius: 8,
|
|
108
113
|
flexDirection: "row",
|
|
109
|
-
alignItems: "center"
|
|
114
|
+
alignItems: "center",
|
|
115
|
+
// a11y: comfortable ≥44 tall touch target (rounded-pill hover/active affordance).
|
|
116
|
+
minHeight: 44,
|
|
117
|
+
justifyContent: "center"
|
|
110
118
|
},
|
|
111
119
|
linkStacked: { paddingVertical: 10 },
|
|
112
120
|
linkIcon: { marginRight: 6 },
|
|
@@ -149,6 +157,31 @@ var expandableStyles = reactNative.StyleSheet.create({
|
|
|
149
157
|
var BASE_INDENT = 12;
|
|
150
158
|
var NAV_ICON_SIZE = 14;
|
|
151
159
|
var CHEVRON_ICON_SIZE = 12;
|
|
160
|
+
var IS_WEB = reactNative.Platform.OS === "web";
|
|
161
|
+
var FOCUS_RING_WIDTH = 2;
|
|
162
|
+
var FOCUS_RING_OFFSET = 2;
|
|
163
|
+
var HOVER_TRANSITION_MS = 150;
|
|
164
|
+
function focusRingStyle(focused, ringColor) {
|
|
165
|
+
if (!IS_WEB || !focused) return void 0;
|
|
166
|
+
const ring = {
|
|
167
|
+
outlineWidth: FOCUS_RING_WIDTH,
|
|
168
|
+
outlineStyle: "solid",
|
|
169
|
+
outlineColor: ringColor,
|
|
170
|
+
outlineOffset: FOCUS_RING_OFFSET
|
|
171
|
+
};
|
|
172
|
+
return ring;
|
|
173
|
+
}
|
|
174
|
+
function hoverTransitionStyle(reducedMotion) {
|
|
175
|
+
if (!IS_WEB) return void 0;
|
|
176
|
+
const transition = {
|
|
177
|
+
transitionProperty: "color, background-color",
|
|
178
|
+
transitionDuration: reducedMotion ? "0ms" : `${HOVER_TRANSITION_MS}ms`
|
|
179
|
+
};
|
|
180
|
+
return transition;
|
|
181
|
+
}
|
|
182
|
+
function ariaCurrentProps(isActive) {
|
|
183
|
+
return isActive ? { "aria-current": "page" } : {};
|
|
184
|
+
}
|
|
152
185
|
var NavExpandableItem = ({
|
|
153
186
|
item,
|
|
154
187
|
pathname,
|
|
@@ -160,6 +193,7 @@ var NavExpandableItem = ({
|
|
|
160
193
|
depth = 0
|
|
161
194
|
}) => {
|
|
162
195
|
const [expanded, setExpanded] = react.useState(false);
|
|
196
|
+
const [focused, setFocused] = react.useState(false);
|
|
163
197
|
const { theme } = uiFeedback.useUi();
|
|
164
198
|
const colors = theme.colors;
|
|
165
199
|
const primaryColor = theme.palette.primary["500"];
|
|
@@ -180,9 +214,18 @@ var NavExpandableItem = ({
|
|
|
180
214
|
accessibilityHint: navigateHint(item.label),
|
|
181
215
|
accessibilityLabel: item.label,
|
|
182
216
|
accessibilityRole: "button",
|
|
183
|
-
|
|
217
|
+
accessibilityState: { selected: isActive },
|
|
218
|
+
style: [
|
|
219
|
+
expandableStyles.childItem,
|
|
220
|
+
paddingStyle,
|
|
221
|
+
isActive ? activeItemStyle : void 0,
|
|
222
|
+
focusRingStyle(focused, primaryColor)
|
|
223
|
+
],
|
|
184
224
|
testID: item.testID ?? item.key,
|
|
225
|
+
onBlur: () => setFocused(false),
|
|
226
|
+
onFocus: () => setFocused(true),
|
|
185
227
|
onPress: () => onNavigate(item.route),
|
|
228
|
+
...ariaCurrentProps(isActive),
|
|
186
229
|
children: [
|
|
187
230
|
typeof item.renderIcon === "function" ? /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: expandableStyles.iconWrapper, children: item.renderIcon(colors.textSecondary, NAV_ICON_SIZE) }) : null,
|
|
188
231
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
@@ -206,8 +249,10 @@ var NavExpandableItem = ({
|
|
|
206
249
|
accessibilityLabel: item.label,
|
|
207
250
|
accessibilityRole: "button",
|
|
208
251
|
accessibilityState: { expanded },
|
|
209
|
-
style: [expandableStyles.header, headerPaddingStyle],
|
|
252
|
+
style: [expandableStyles.header, headerPaddingStyle, focusRingStyle(focused, primaryColor)],
|
|
210
253
|
testID: item.testID ?? item.key,
|
|
254
|
+
onBlur: () => setFocused(false),
|
|
255
|
+
onFocus: () => setFocused(true),
|
|
211
256
|
onPress: toggle,
|
|
212
257
|
children: [
|
|
213
258
|
typeof item.renderIcon === "function" ? /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: expandableStyles.iconWrapper, children: item.renderIcon(colors.text, NAV_ICON_SIZE) }) : null,
|
|
@@ -301,12 +346,40 @@ var APP_SHELL_SUFFIX = {
|
|
|
301
346
|
content: "-content",
|
|
302
347
|
header: "-header",
|
|
303
348
|
nav: "-nav",
|
|
349
|
+
/** The persistent left rail of the back-office layout (only when `sidebar` is supplied). */
|
|
350
|
+
sidebar: "-sidebar",
|
|
304
351
|
banner: "-banner",
|
|
305
352
|
pending: "-pending",
|
|
306
353
|
loading: "-loading",
|
|
307
354
|
error: "-error",
|
|
308
355
|
forbidden: "-forbidden"
|
|
309
356
|
};
|
|
357
|
+
var FocusableTouchable = ({
|
|
358
|
+
onPress,
|
|
359
|
+
accessibilityLabel,
|
|
360
|
+
accessibilityHint,
|
|
361
|
+
accessibilityRole = "button",
|
|
362
|
+
ringColor,
|
|
363
|
+
style,
|
|
364
|
+
testID,
|
|
365
|
+
children
|
|
366
|
+
}) => {
|
|
367
|
+
const [focused, setFocused] = react.useState(false);
|
|
368
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
369
|
+
reactNative.Pressable,
|
|
370
|
+
{
|
|
371
|
+
accessibilityHint,
|
|
372
|
+
accessibilityLabel,
|
|
373
|
+
accessibilityRole,
|
|
374
|
+
style: [style, focusRingStyle(focused, ringColor)],
|
|
375
|
+
testID,
|
|
376
|
+
onBlur: () => setFocused(false),
|
|
377
|
+
onFocus: () => setFocused(true),
|
|
378
|
+
onPress,
|
|
379
|
+
children
|
|
380
|
+
}
|
|
381
|
+
);
|
|
382
|
+
};
|
|
310
383
|
var TEXT_ON_PRIMARY = "#ffffff";
|
|
311
384
|
function isAccountState(account) {
|
|
312
385
|
return "displayName" in account;
|
|
@@ -344,11 +417,11 @@ var Topbar = ({
|
|
|
344
417
|
/* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: navStyles.topbarLeft, children: left }),
|
|
345
418
|
/* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: navStyles.topbarRight, children: [
|
|
346
419
|
language ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
347
|
-
|
|
420
|
+
FocusableTouchable,
|
|
348
421
|
{
|
|
349
422
|
accessibilityHint: language.hint,
|
|
350
423
|
accessibilityLabel: language.label,
|
|
351
|
-
|
|
424
|
+
ringColor: primaryColor,
|
|
352
425
|
style: navStyles.topbarRowItem,
|
|
353
426
|
onPress: language.onPress,
|
|
354
427
|
children: /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [navStyles.topbarLabel, { color: colors.text }], children: language.label })
|
|
@@ -361,11 +434,11 @@ var Topbar = ({
|
|
|
361
434
|
] }) : null,
|
|
362
435
|
richAccount ? /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: navStyles.userBlock, children: [
|
|
363
436
|
richAccount.onAccount ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
364
|
-
|
|
437
|
+
FocusableTouchable,
|
|
365
438
|
{
|
|
366
439
|
accessibilityHint: richAccount.displayName,
|
|
367
440
|
accessibilityLabel: richAccount.displayName,
|
|
368
|
-
|
|
441
|
+
ringColor: primaryColor,
|
|
369
442
|
testID: NAV_TEST_IDS.accountName,
|
|
370
443
|
onPress: richAccount.onAccount,
|
|
371
444
|
children: /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [navStyles.userName, { color: colors.text }], children: richAccount.displayName })
|
|
@@ -389,11 +462,11 @@ var Topbar = ({
|
|
|
389
462
|
) : null
|
|
390
463
|
] }) : null,
|
|
391
464
|
legacyAccount ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
392
|
-
|
|
465
|
+
FocusableTouchable,
|
|
393
466
|
{
|
|
394
467
|
accessibilityHint: legacyAccount.hint,
|
|
395
468
|
accessibilityLabel: legacyAccount.label,
|
|
396
|
-
|
|
469
|
+
ringColor: primaryColor,
|
|
397
470
|
style: [navStyles.accountBtn, { backgroundColor: primaryColor }],
|
|
398
471
|
testID: legacyAccount.testID,
|
|
399
472
|
onPress: legacyAccount.onPress,
|
|
@@ -401,11 +474,11 @@ var Topbar = ({
|
|
|
401
474
|
}
|
|
402
475
|
) : null,
|
|
403
476
|
upgrade ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
404
|
-
|
|
477
|
+
FocusableTouchable,
|
|
405
478
|
{
|
|
406
479
|
accessibilityHint: upgrade.hint,
|
|
407
480
|
accessibilityLabel: upgrade.label,
|
|
408
|
-
|
|
481
|
+
ringColor: primaryColor,
|
|
409
482
|
style: [navStyles.accountBtn, { backgroundColor: primaryColor }],
|
|
410
483
|
testID: upgrade.testID ?? NAV_TEST_IDS.accountUpgrade,
|
|
411
484
|
onPress: upgrade.onPress,
|
|
@@ -413,12 +486,11 @@ var Topbar = ({
|
|
|
413
486
|
}
|
|
414
487
|
) : null,
|
|
415
488
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
416
|
-
|
|
489
|
+
FocusableTouchable,
|
|
417
490
|
{
|
|
418
|
-
accessible: true,
|
|
419
491
|
accessibilityHint: logout.hint,
|
|
420
492
|
accessibilityLabel: logout.label,
|
|
421
|
-
|
|
493
|
+
ringColor: primaryColor,
|
|
422
494
|
style: [navStyles.accountBtn, { backgroundColor: primaryColor }],
|
|
423
495
|
testID: logout.testID,
|
|
424
496
|
onPress: richAccount ? richAccount.onLogout : logout.onPress,
|
|
@@ -431,10 +503,75 @@ var Topbar = ({
|
|
|
431
503
|
);
|
|
432
504
|
};
|
|
433
505
|
var TEXT_ON_PRIMARY2 = "#ffffff";
|
|
506
|
+
function ariaCurrentProps2(isActive) {
|
|
507
|
+
return isActive ? { "aria-current": "page" } : {};
|
|
508
|
+
}
|
|
509
|
+
var NavBarLink = ({
|
|
510
|
+
item,
|
|
511
|
+
isActive,
|
|
512
|
+
collapsed,
|
|
513
|
+
iconSize,
|
|
514
|
+
reducedMotion,
|
|
515
|
+
colors,
|
|
516
|
+
navigateHint,
|
|
517
|
+
onPress
|
|
518
|
+
}) => {
|
|
519
|
+
const [hovered, setHovered] = react.useState(false);
|
|
520
|
+
const [focused, setFocused] = react.useState(false);
|
|
521
|
+
const isHovered = hovered && !isActive;
|
|
522
|
+
const textColor = isActive ? TEXT_ON_PRIMARY2 : isHovered ? colors.hoverText : colors.rest;
|
|
523
|
+
const pillStyle = isActive ? { backgroundColor: colors.activeBg } : isHovered ? { backgroundColor: colors.hoverBg } : void 0;
|
|
524
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
525
|
+
reactNative.Pressable,
|
|
526
|
+
{
|
|
527
|
+
accessibilityHint: navigateHint(item.label),
|
|
528
|
+
accessibilityLabel: item.label,
|
|
529
|
+
accessibilityRole: "link",
|
|
530
|
+
accessibilityState: { selected: isActive },
|
|
531
|
+
style: [
|
|
532
|
+
navBarStyles.link,
|
|
533
|
+
collapsed ? navBarStyles.linkStacked : void 0,
|
|
534
|
+
pillStyle,
|
|
535
|
+
hoverTransitionStyle(reducedMotion),
|
|
536
|
+
focusRingStyle(focused, colors.ring)
|
|
537
|
+
],
|
|
538
|
+
testID: item.testID ?? item.key,
|
|
539
|
+
onBlur: () => setFocused(false),
|
|
540
|
+
onFocus: () => setFocused(true),
|
|
541
|
+
onHoverIn: () => setHovered(true),
|
|
542
|
+
onHoverOut: () => setHovered(false),
|
|
543
|
+
onPress: () => onPress(item.route),
|
|
544
|
+
...ariaCurrentProps2(isActive),
|
|
545
|
+
children: [
|
|
546
|
+
typeof item.renderIcon === "function" ? /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: navBarStyles.linkIcon, children: item.renderIcon(textColor, iconSize) }) : null,
|
|
547
|
+
/* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [navBarStyles.linkText, { color: textColor }], children: item.label })
|
|
548
|
+
]
|
|
549
|
+
}
|
|
550
|
+
);
|
|
551
|
+
};
|
|
552
|
+
var REDUCED_MOTION_QUERY = "(prefers-reduced-motion: reduce)";
|
|
553
|
+
function getReducedMotionQuery() {
|
|
554
|
+
if (typeof window === "undefined" || typeof window.matchMedia !== "function") return void 0;
|
|
555
|
+
return window.matchMedia(REDUCED_MOTION_QUERY);
|
|
556
|
+
}
|
|
557
|
+
function useReducedMotion() {
|
|
558
|
+
const [reduced, setReduced] = react.useState(() => getReducedMotionQuery()?.matches ?? false);
|
|
559
|
+
react.useEffect(() => {
|
|
560
|
+
const mql = getReducedMotionQuery();
|
|
561
|
+
if (mql === void 0 || mql.addEventListener === void 0) return void 0;
|
|
562
|
+
const onChange = () => setReduced(mql.matches);
|
|
563
|
+
mql.addEventListener("change", onChange);
|
|
564
|
+
return () => {
|
|
565
|
+
mql.removeEventListener?.("change", onChange);
|
|
566
|
+
};
|
|
567
|
+
}, []);
|
|
568
|
+
return reduced;
|
|
569
|
+
}
|
|
570
|
+
var LINKS_REGION_ID = "navbar-links-region";
|
|
434
571
|
var DEFAULT_COLLAPSE_BELOW = 760;
|
|
435
572
|
var MENU_GLYPH = "\u2630";
|
|
436
|
-
function
|
|
437
|
-
return
|
|
573
|
+
function ariaControlsProps(id) {
|
|
574
|
+
return { "aria-controls": id };
|
|
438
575
|
}
|
|
439
576
|
var NavBar = ({
|
|
440
577
|
items,
|
|
@@ -454,7 +591,9 @@ var NavBar = ({
|
|
|
454
591
|
const colors = theme.colors;
|
|
455
592
|
const primaryColor = theme.palette.primary["500"];
|
|
456
593
|
const { width } = reactNative.useWindowDimensions();
|
|
594
|
+
const reducedMotion = useReducedMotion();
|
|
457
595
|
const [open, setOpen] = react.useState(false);
|
|
596
|
+
const [toggleFocused, setToggleFocused] = react.useState(false);
|
|
458
597
|
const collapsed = width < collapseBelow;
|
|
459
598
|
const showLinks = !collapsed || open;
|
|
460
599
|
const toggleMenu = react.useCallback(() => setOpen((v) => !v), []);
|
|
@@ -465,33 +604,16 @@ var NavBar = ({
|
|
|
465
604
|
},
|
|
466
605
|
[onNavigate]
|
|
467
606
|
);
|
|
468
|
-
const
|
|
469
|
-
() => ({
|
|
470
|
-
|
|
607
|
+
const linkColors = react.useMemo(
|
|
608
|
+
() => ({
|
|
609
|
+
rest: colors.textSecondary,
|
|
610
|
+
hoverText: colors.text,
|
|
611
|
+
hoverBg: colors.surfaceElevated,
|
|
612
|
+
activeBg: primaryColor,
|
|
613
|
+
ring: primaryColor
|
|
614
|
+
}),
|
|
615
|
+
[colors.textSecondary, colors.text, colors.surfaceElevated, primaryColor]
|
|
471
616
|
);
|
|
472
|
-
const renderLink = (item) => {
|
|
473
|
-
const isActive = isRouteActive(pathname, item.route);
|
|
474
|
-
const textColor = isActive ? TEXT_ON_PRIMARY2 : colors.textSecondary;
|
|
475
|
-
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
476
|
-
reactNative.TouchableOpacity,
|
|
477
|
-
{
|
|
478
|
-
accessible: true,
|
|
479
|
-
accessibilityHint: navigateHint(item.label),
|
|
480
|
-
accessibilityLabel: item.label,
|
|
481
|
-
accessibilityRole: "link",
|
|
482
|
-
accessibilityState: { selected: isActive },
|
|
483
|
-
style: [navBarStyles.link, collapsed ? navBarStyles.linkStacked : void 0, isActive ? activeLinkStyle : void 0],
|
|
484
|
-
testID: item.testID ?? item.key,
|
|
485
|
-
onPress: () => handlePress(item.route),
|
|
486
|
-
...activeAriaProps(isActive),
|
|
487
|
-
children: [
|
|
488
|
-
typeof item.renderIcon === "function" ? /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: navBarStyles.linkIcon, children: item.renderIcon(textColor, NAV_ICON_SIZE) }) : null,
|
|
489
|
-
/* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [navBarStyles.linkText, { color: textColor }], children: item.label })
|
|
490
|
-
]
|
|
491
|
-
},
|
|
492
|
-
item.key
|
|
493
|
-
);
|
|
494
|
-
};
|
|
495
617
|
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
496
618
|
reactNative.View,
|
|
497
619
|
{
|
|
@@ -506,21 +628,44 @@ var NavBar = ({
|
|
|
506
628
|
children: /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: navBarStyles.inner, children: [
|
|
507
629
|
brand !== void 0 ? /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: navBarStyles.brand, children: brand }) : null,
|
|
508
630
|
collapsed ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
509
|
-
reactNative.
|
|
631
|
+
reactNative.Pressable,
|
|
510
632
|
{
|
|
511
|
-
accessible: true,
|
|
512
633
|
accessibilityHint: menuHint,
|
|
513
634
|
accessibilityLabel: menuLabel,
|
|
514
635
|
accessibilityRole: "button",
|
|
515
636
|
accessibilityState: { expanded: open },
|
|
516
637
|
"aria-expanded": open,
|
|
517
|
-
style: navBarStyles.toggle,
|
|
638
|
+
style: [navBarStyles.toggle, focusRingStyle(toggleFocused, primaryColor)],
|
|
518
639
|
testID: NAV_TEST_IDS.navBarToggle,
|
|
640
|
+
onBlur: () => setToggleFocused(false),
|
|
641
|
+
onFocus: () => setToggleFocused(true),
|
|
519
642
|
onPress: toggleMenu,
|
|
643
|
+
...ariaControlsProps(LINKS_REGION_ID),
|
|
520
644
|
children: typeof renderMenuIcon === "function" ? renderMenuIcon(colors.text, open) : /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [navBarStyles.toggleGlyph, { color: colors.text }], children: MENU_GLYPH })
|
|
521
645
|
}
|
|
522
646
|
) : null,
|
|
523
|
-
showLinks ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
647
|
+
showLinks ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
648
|
+
reactNative.View,
|
|
649
|
+
{
|
|
650
|
+
nativeID: LINKS_REGION_ID,
|
|
651
|
+
style: collapsed ? navBarStyles.linksStacked : navBarStyles.links,
|
|
652
|
+
testID: NAV_TEST_IDS.navBarLinks,
|
|
653
|
+
children: items.map((item) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
654
|
+
NavBarLink,
|
|
655
|
+
{
|
|
656
|
+
collapsed,
|
|
657
|
+
colors: linkColors,
|
|
658
|
+
iconSize: NAV_ICON_SIZE,
|
|
659
|
+
isActive: isRouteActive(pathname, item.route),
|
|
660
|
+
item,
|
|
661
|
+
navigateHint,
|
|
662
|
+
reducedMotion,
|
|
663
|
+
onPress: handlePress
|
|
664
|
+
},
|
|
665
|
+
item.key
|
|
666
|
+
))
|
|
667
|
+
}
|
|
668
|
+
) : null,
|
|
524
669
|
showLinks && right !== void 0 ? /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: collapsed ? navBarStyles.rightStacked : navBarStyles.right, children: right }) : null
|
|
525
670
|
] })
|
|
526
671
|
}
|
|
@@ -548,6 +693,8 @@ var styles = reactNative.StyleSheet.create({
|
|
|
548
693
|
centerFill: { flex: 1, justifyContent: "center", alignItems: "center" },
|
|
549
694
|
scroll: { flex: 1 },
|
|
550
695
|
scrollContent: { flexGrow: 1 },
|
|
696
|
+
// Back-office layout: a persistent left rail beside the scrolling content column.
|
|
697
|
+
bodyRow: { flex: 1, flexDirection: "row" },
|
|
551
698
|
columnFull: { flex: 1 },
|
|
552
699
|
columnCapped: { flex: 1, width: "100%", alignSelf: "center" },
|
|
553
700
|
// Non-flex variant used to cap the nav strip's inner content to the content
|
|
@@ -595,6 +742,7 @@ function useContentBody(state, children, testID) {
|
|
|
595
742
|
var AppShell = ({
|
|
596
743
|
header,
|
|
597
744
|
nav,
|
|
745
|
+
sidebar,
|
|
598
746
|
banner,
|
|
599
747
|
width = "full",
|
|
600
748
|
contentPadding = DEFAULT_CONTENT_PADDING,
|
|
@@ -617,19 +765,23 @@ var AppShell = ({
|
|
|
617
765
|
if (isUnauthenticated) return null;
|
|
618
766
|
const columnStyle = maxWidth === "full" ? styles.columnFull : [styles.columnCapped, { maxWidth }];
|
|
619
767
|
const navInnerStyle = chromeAlignment === "content" && maxWidth !== "full" ? [styles.chromeCapped, { maxWidth, paddingHorizontal: contentPadding }] : void 0;
|
|
768
|
+
const scroller = /* @__PURE__ */ jsxRuntime.jsx(
|
|
769
|
+
reactNative.ScrollView,
|
|
770
|
+
{
|
|
771
|
+
contentContainerStyle: [styles.scrollContent, { padding: contentPadding }],
|
|
772
|
+
style: styles.scroll,
|
|
773
|
+
testID: `${testID}${APP_SHELL_SUFFIX.content}`,
|
|
774
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: columnStyle, children: body })
|
|
775
|
+
}
|
|
776
|
+
);
|
|
620
777
|
return /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: [styles.root, { backgroundColor: theme.colors.background }], testID, children: [
|
|
621
778
|
/* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { testID: `${testID}${APP_SHELL_SUFFIX.header}`, children: header }),
|
|
622
779
|
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,
|
|
623
780
|
banner !== void 0 ? /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { testID: `${testID}${APP_SHELL_SUFFIX.banner}`, children: banner }) : null,
|
|
624
|
-
/* @__PURE__ */ jsxRuntime.
|
|
625
|
-
reactNative.
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
style: styles.scroll,
|
|
629
|
-
testID: `${testID}${APP_SHELL_SUFFIX.content}`,
|
|
630
|
-
children: /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: columnStyle, children: body })
|
|
631
|
-
}
|
|
632
|
-
)
|
|
781
|
+
sidebar !== void 0 ? /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: styles.bodyRow, children: [
|
|
782
|
+
/* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { testID: `${testID}${APP_SHELL_SUFFIX.sidebar}`, children: sidebar }),
|
|
783
|
+
scroller
|
|
784
|
+
] }) : scroller
|
|
633
785
|
] });
|
|
634
786
|
};
|
|
635
787
|
function roleRoutesToNavItems(routes, translate) {
|