@dloizides/ui-nav 1.6.1 → 1.8.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/ADOPTION.md +83 -0
- package/CHANGELOG.md +100 -0
- package/README.md +113 -0
- package/dist/index.d.mts +595 -14
- package/dist/index.d.ts +595 -14
- package/dist/index.js +640 -115
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +603 -95
- package/dist/index.mjs.map +1 -1
- package/package.json +5 -2
package/dist/index.mjs
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import { StyleSheet, Platform, TouchableOpacity, View, Text, useWindowDimensions,
|
|
2
|
-
import { useUi } from '@dloizides/ui-feedback';
|
|
3
|
-
import { useState, useCallback, useMemo, useEffect, useRef } from 'react';
|
|
1
|
+
import { StyleSheet, Platform, TouchableOpacity, View, Text, useWindowDimensions, ActivityIndicator, ScrollView, Pressable } from 'react-native';
|
|
2
|
+
import { useUi, UiProvider } from '@dloizides/ui-feedback';
|
|
3
|
+
import React5, { useState, useCallback, useMemo, useEffect, useRef } from 'react';
|
|
4
4
|
import { jsxs, jsx } from 'react/jsx-runtime';
|
|
5
|
+
import { ModalDropdown } from '@dloizides/ui-layout';
|
|
5
6
|
import { resolveAccessibleRoutes } from '@dloizides/auth-web';
|
|
6
7
|
|
|
7
8
|
// src/Sidebar.tsx
|
|
@@ -12,6 +13,7 @@ function isRouteActive(pathname, route) {
|
|
|
12
13
|
return pathname === route || pathname.startsWith(`${route}/`);
|
|
13
14
|
}
|
|
14
15
|
var ACTIVE_BORDER_RADIUS = 4;
|
|
16
|
+
var NAV_LINK_GAP = 4;
|
|
15
17
|
var navStyles = StyleSheet.create({
|
|
16
18
|
// --- Sidebar ---
|
|
17
19
|
sidebarContainer: {
|
|
@@ -94,22 +96,26 @@ var navBarStyles = StyleSheet.create({
|
|
|
94
96
|
justifyContent: "center"
|
|
95
97
|
},
|
|
96
98
|
toggleGlyph: { fontSize: 20, fontWeight: "700" },
|
|
97
|
-
// Expanded links live
|
|
98
|
-
//
|
|
99
|
-
//
|
|
100
|
-
//
|
|
101
|
-
|
|
99
|
+
// Expanded links live on ONE ROW that never wraps: it is a shrinkable/growable flex child that
|
|
100
|
+
// fills the space between the pinned brand (left) and right slot, and CLIPS overflow. A measure
|
|
101
|
+
// pass (priority+) then keeps only the leading items that fit inline and collapses the rest behind
|
|
102
|
+
// the "More ▾" overflow trigger — so no item is ever cut off; the surplus moves into a dropdown.
|
|
103
|
+
// `overflow:'hidden'` only masks the single unmeasured first frame before the fit resolves.
|
|
104
|
+
linksRow: {
|
|
102
105
|
flexGrow: 1,
|
|
103
106
|
flexShrink: 1,
|
|
104
|
-
minWidth: 0
|
|
105
|
-
},
|
|
106
|
-
links: {
|
|
107
|
+
minWidth: 0,
|
|
107
108
|
flexDirection: "row",
|
|
108
109
|
alignItems: "center",
|
|
109
110
|
flexWrap: "nowrap",
|
|
110
|
-
columnGap:
|
|
111
|
-
|
|
111
|
+
columnGap: NAV_LINK_GAP,
|
|
112
|
+
overflow: "hidden"
|
|
112
113
|
},
|
|
114
|
+
// Each inline link + the overflow trigger sit in a natural-width cell that never shrinks, so their
|
|
115
|
+
// measured widths are their true content widths (what the priority+ fit computation reasons about).
|
|
116
|
+
linkCell: { flexShrink: 0 },
|
|
117
|
+
overflowTrigger: { columnGap: 4 },
|
|
118
|
+
overflowChevron: { fontSize: 12, fontWeight: "700" },
|
|
113
119
|
// Collapsed (below breakpoint): links drop onto their own full-width line,
|
|
114
120
|
// stacked vertically — mirrors the v1 `.gn-links` responsive behaviour.
|
|
115
121
|
linksStacked: {
|
|
@@ -149,6 +155,86 @@ var navBarStyles = StyleSheet.create({
|
|
|
149
155
|
columnGap: 8
|
|
150
156
|
}
|
|
151
157
|
});
|
|
158
|
+
var pillNavStyles = StyleSheet.create({
|
|
159
|
+
// Cross-nav pill row (kefi coral dashboard switcher): fully-rounded pills, wraps.
|
|
160
|
+
container: {
|
|
161
|
+
flexDirection: "row",
|
|
162
|
+
flexWrap: "wrap",
|
|
163
|
+
alignItems: "center",
|
|
164
|
+
columnGap: 8,
|
|
165
|
+
rowGap: 8,
|
|
166
|
+
paddingVertical: 8
|
|
167
|
+
},
|
|
168
|
+
pill: {
|
|
169
|
+
paddingHorizontal: 16,
|
|
170
|
+
paddingVertical: 8,
|
|
171
|
+
borderRadius: 999,
|
|
172
|
+
minHeight: 36,
|
|
173
|
+
flexDirection: "row",
|
|
174
|
+
alignItems: "center",
|
|
175
|
+
columnGap: 6,
|
|
176
|
+
justifyContent: "center"
|
|
177
|
+
},
|
|
178
|
+
pillText: { fontSize: 14, fontWeight: "600" }
|
|
179
|
+
});
|
|
180
|
+
var darkModeStyles = StyleSheet.create({
|
|
181
|
+
// Segmented Light / Dark / System control.
|
|
182
|
+
segmentRow: {
|
|
183
|
+
flexDirection: "row",
|
|
184
|
+
alignItems: "center",
|
|
185
|
+
borderWidth: 1,
|
|
186
|
+
borderRadius: 8,
|
|
187
|
+
overflow: "hidden",
|
|
188
|
+
alignSelf: "flex-start"
|
|
189
|
+
},
|
|
190
|
+
segment: {
|
|
191
|
+
paddingHorizontal: 12,
|
|
192
|
+
paddingVertical: 8,
|
|
193
|
+
minHeight: 36,
|
|
194
|
+
flexDirection: "row",
|
|
195
|
+
alignItems: "center",
|
|
196
|
+
columnGap: 6,
|
|
197
|
+
justifyContent: "center"
|
|
198
|
+
},
|
|
199
|
+
segmentText: { fontSize: 13, fontWeight: "600" },
|
|
200
|
+
// Cycle-button variant.
|
|
201
|
+
cycle: {
|
|
202
|
+
paddingHorizontal: 12,
|
|
203
|
+
paddingVertical: 8,
|
|
204
|
+
minHeight: 44,
|
|
205
|
+
minWidth: 44,
|
|
206
|
+
borderRadius: 8,
|
|
207
|
+
flexDirection: "row",
|
|
208
|
+
alignItems: "center",
|
|
209
|
+
columnGap: 6,
|
|
210
|
+
justifyContent: "center"
|
|
211
|
+
},
|
|
212
|
+
cycleText: { fontSize: 13, fontWeight: "600" }
|
|
213
|
+
});
|
|
214
|
+
var collapsedRailStyles = StyleSheet.create({
|
|
215
|
+
// Intermediate (tablet) icon-only rail: a narrow vertical strip of icon buttons.
|
|
216
|
+
container: {
|
|
217
|
+
width: 64,
|
|
218
|
+
paddingTop: 16,
|
|
219
|
+
paddingHorizontal: 8,
|
|
220
|
+
borderRightWidth: 1,
|
|
221
|
+
height: "100%",
|
|
222
|
+
alignItems: "center"
|
|
223
|
+
},
|
|
224
|
+
// Each item is a ≥44×44 centred touch target with a rounded active/hover pill.
|
|
225
|
+
item: {
|
|
226
|
+
width: 48,
|
|
227
|
+
height: 48,
|
|
228
|
+
marginVertical: 4,
|
|
229
|
+
borderRadius: 8,
|
|
230
|
+
alignItems: "center",
|
|
231
|
+
justifyContent: "center"
|
|
232
|
+
},
|
|
233
|
+
// Glyph fallback (the label's first character) when an item has no renderIcon.
|
|
234
|
+
glyph: { fontSize: 18, fontWeight: "700" },
|
|
235
|
+
spacer: { flex: 1 },
|
|
236
|
+
slot: { alignItems: "center" }
|
|
237
|
+
});
|
|
152
238
|
var expandableStyles = StyleSheet.create({
|
|
153
239
|
childItem: {
|
|
154
240
|
borderRadius: 4,
|
|
@@ -355,7 +441,9 @@ var NAV_TEST_IDS = {
|
|
|
355
441
|
/** responsive hamburger toggle in the horizontal NavBar (shown below the breakpoint). */
|
|
356
442
|
navBarToggle: "navbar-toggle",
|
|
357
443
|
/** links container in the horizontal NavBar. */
|
|
358
|
-
navBarLinks: "navbar-links"
|
|
444
|
+
navBarLinks: "navbar-links",
|
|
445
|
+
/** the "More ▾" priority+ overflow trigger in the horizontal NavBar (shown when items spill). */
|
|
446
|
+
navBarOverflow: "navbar-overflow"
|
|
359
447
|
};
|
|
360
448
|
var APP_SHELL_SUFFIX = {
|
|
361
449
|
content: "-content",
|
|
@@ -363,6 +451,8 @@ var APP_SHELL_SUFFIX = {
|
|
|
363
451
|
nav: "-nav",
|
|
364
452
|
/** The persistent left rail of the back-office layout (only when `sidebar` is supplied). */
|
|
365
453
|
sidebar: "-sidebar",
|
|
454
|
+
/** The intermediate persistent COLLAPSED (icon-only) rail (3-tier layout, tablet band). */
|
|
455
|
+
collapsedSidebar: "-collapsed-sidebar",
|
|
366
456
|
/** Mobile hamburger that opens the nav drawer (back-office layout, below the breakpoint). */
|
|
367
457
|
menuToggle: "-menu-toggle",
|
|
368
458
|
/** The mobile nav drawer panel (holds the `sidebar` slot when opened on a narrow viewport). */
|
|
@@ -570,6 +660,108 @@ var NavBarLink = ({
|
|
|
570
660
|
}
|
|
571
661
|
);
|
|
572
662
|
};
|
|
663
|
+
var TEXT_ON_PRIMARY3 = "#ffffff";
|
|
664
|
+
var NO_ACTIVE_ROUTE = "";
|
|
665
|
+
var NavOverflowMenu = ({
|
|
666
|
+
items,
|
|
667
|
+
pathname,
|
|
668
|
+
onNavigate,
|
|
669
|
+
colors,
|
|
670
|
+
label,
|
|
671
|
+
hint,
|
|
672
|
+
testID,
|
|
673
|
+
variant
|
|
674
|
+
}) => {
|
|
675
|
+
const options = useMemo(() => items.map((item) => ({ label: item.label, value: item.route })), [items]);
|
|
676
|
+
const activeRoute = useMemo(
|
|
677
|
+
() => items.find((item) => isRouteActive(pathname, item.route))?.route ?? NO_ACTIVE_ROUTE,
|
|
678
|
+
[items, pathname]
|
|
679
|
+
);
|
|
680
|
+
const optionTestID = useMemo(() => {
|
|
681
|
+
const byRoute = new Map(items.map((item) => [item.route, item.testID ?? item.key]));
|
|
682
|
+
return (route) => byRoute.get(route) ?? `${testID}-option-${route}`;
|
|
683
|
+
}, [items, testID]);
|
|
684
|
+
const hasActive = activeRoute !== NO_ACTIVE_ROUTE;
|
|
685
|
+
const textColor = hasActive ? TEXT_ON_PRIMARY3 : colors.rest;
|
|
686
|
+
const pillStyle = hasActive ? { backgroundColor: colors.activeBg } : void 0;
|
|
687
|
+
return /* @__PURE__ */ jsx(
|
|
688
|
+
ModalDropdown,
|
|
689
|
+
{
|
|
690
|
+
accessibilityHint: hint,
|
|
691
|
+
accessibilityLabel: label,
|
|
692
|
+
optionTestID,
|
|
693
|
+
options,
|
|
694
|
+
testID,
|
|
695
|
+
value: activeRoute,
|
|
696
|
+
variant,
|
|
697
|
+
onChange: onNavigate,
|
|
698
|
+
renderTrigger: ({ isOpen }) => /* @__PURE__ */ jsxs(View, { style: [navBarStyles.link, navBarStyles.overflowTrigger, pillStyle], children: [
|
|
699
|
+
/* @__PURE__ */ jsx(Text, { style: [navBarStyles.linkText, { color: textColor }], children: label }),
|
|
700
|
+
/* @__PURE__ */ jsx(Text, { style: [navBarStyles.overflowChevron, { color: textColor }], children: isOpen ? "\u25B4" : "\u25BE" })
|
|
701
|
+
] })
|
|
702
|
+
}
|
|
703
|
+
);
|
|
704
|
+
};
|
|
705
|
+
|
|
706
|
+
// src/computeVisibleCount.ts
|
|
707
|
+
function sum(values) {
|
|
708
|
+
return values.reduce((acc, value) => acc + value, 0);
|
|
709
|
+
}
|
|
710
|
+
function computeVisibleCount({ availableWidth, itemWidths, moreWidth, gap }) {
|
|
711
|
+
const total = itemWidths.length;
|
|
712
|
+
if (total === 0) return 0;
|
|
713
|
+
if (availableWidth <= 0 || itemWidths.some((width) => width <= 0)) return total;
|
|
714
|
+
const fullRowWidth = sum(itemWidths) + gap * (total - 1);
|
|
715
|
+
if (fullRowWidth <= availableWidth) return total;
|
|
716
|
+
const reservedForMore = moreWidth + gap;
|
|
717
|
+
let used = 0;
|
|
718
|
+
let count = 0;
|
|
719
|
+
for (let index = 0; index < total; index += 1) {
|
|
720
|
+
const width = itemWidths[index] ?? 0;
|
|
721
|
+
const withGap = used + (count > 0 ? gap : 0) + width;
|
|
722
|
+
if (withGap + reservedForMore > availableWidth) break;
|
|
723
|
+
used = withGap;
|
|
724
|
+
count += 1;
|
|
725
|
+
}
|
|
726
|
+
return count;
|
|
727
|
+
}
|
|
728
|
+
|
|
729
|
+
// src/useNavOverflow.ts
|
|
730
|
+
function resizeWidthBuffer(previous, count) {
|
|
731
|
+
const next = new Array(count).fill(0);
|
|
732
|
+
const shared = Math.min(previous.length, count);
|
|
733
|
+
for (let index = 0; index < shared; index += 1) next[index] = previous[index] ?? 0;
|
|
734
|
+
return next;
|
|
735
|
+
}
|
|
736
|
+
function useNavOverflow(itemCount, gap) {
|
|
737
|
+
const [availableWidth, setAvailableWidthState] = useState(0);
|
|
738
|
+
const [moreWidth, setMoreWidthState] = useState(0);
|
|
739
|
+
const [itemWidths, setItemWidths] = useState(() => new Array(itemCount).fill(0));
|
|
740
|
+
useEffect(() => {
|
|
741
|
+
setItemWidths((previous) => previous.length === itemCount ? previous : resizeWidthBuffer(previous, itemCount));
|
|
742
|
+
}, [itemCount]);
|
|
743
|
+
const setItemWidth = useCallback((index, width) => {
|
|
744
|
+
setItemWidths((previous) => {
|
|
745
|
+
if (index < 0 || index >= previous.length || previous[index] === width) return previous;
|
|
746
|
+
const next = previous.slice();
|
|
747
|
+
next[index] = width;
|
|
748
|
+
return next;
|
|
749
|
+
});
|
|
750
|
+
}, []);
|
|
751
|
+
const setAvailableWidth = useCallback(
|
|
752
|
+
(width) => setAvailableWidthState((previous) => previous === width ? previous : width),
|
|
753
|
+
[]
|
|
754
|
+
);
|
|
755
|
+
const setMoreWidth = useCallback(
|
|
756
|
+
(width) => setMoreWidthState((previous) => previous === width ? previous : width),
|
|
757
|
+
[]
|
|
758
|
+
);
|
|
759
|
+
const visibleCount = useMemo(
|
|
760
|
+
() => computeVisibleCount({ availableWidth, itemWidths, moreWidth, gap }),
|
|
761
|
+
[availableWidth, itemWidths, moreWidth, gap]
|
|
762
|
+
);
|
|
763
|
+
return { visibleCount, setAvailableWidth, setItemWidth, setMoreWidth };
|
|
764
|
+
}
|
|
573
765
|
var REDUCED_MOTION_QUERY = "(prefers-reduced-motion: reduce)";
|
|
574
766
|
function getReducedMotionQuery() {
|
|
575
767
|
if (typeof window === "undefined" || typeof window.matchMedia !== "function") return void 0;
|
|
@@ -594,7 +786,7 @@ var MENU_GLYPH = "\u2630";
|
|
|
594
786
|
function ariaControlsProps(id) {
|
|
595
787
|
return { "aria-controls": id };
|
|
596
788
|
}
|
|
597
|
-
var
|
|
789
|
+
var NavBarInner = ({
|
|
598
790
|
items,
|
|
599
791
|
pathname,
|
|
600
792
|
onNavigate,
|
|
@@ -606,6 +798,8 @@ var NavBar = ({
|
|
|
606
798
|
menuHint = "",
|
|
607
799
|
renderMenuIcon,
|
|
608
800
|
collapseBelow = DEFAULT_COLLAPSE_BELOW,
|
|
801
|
+
overflowLabel = "More",
|
|
802
|
+
overflowHint = "",
|
|
609
803
|
containerStyle
|
|
610
804
|
}) => {
|
|
611
805
|
const { theme } = useUi();
|
|
@@ -615,6 +809,7 @@ var NavBar = ({
|
|
|
615
809
|
const reducedMotion = useReducedMotion();
|
|
616
810
|
const [open, setOpen] = useState(false);
|
|
617
811
|
const [toggleFocused, setToggleFocused] = useState(false);
|
|
812
|
+
const { visibleCount, setAvailableWidth, setItemWidth, setMoreWidth } = useNavOverflow(items.length, NAV_LINK_GAP);
|
|
618
813
|
const collapsed = width < collapseBelow;
|
|
619
814
|
const showLinks = !collapsed || open;
|
|
620
815
|
const toggleMenu = useCallback(() => setOpen((v) => !v), []);
|
|
@@ -666,7 +861,7 @@ var NavBar = ({
|
|
|
666
861
|
}
|
|
667
862
|
) : null,
|
|
668
863
|
showLinks ? (() => {
|
|
669
|
-
const
|
|
864
|
+
const renderLink = (item) => /* @__PURE__ */ jsx(
|
|
670
865
|
NavBarLink,
|
|
671
866
|
{
|
|
672
867
|
collapsed,
|
|
@@ -677,19 +872,50 @@ var NavBar = ({
|
|
|
677
872
|
navigateHint,
|
|
678
873
|
reducedMotion,
|
|
679
874
|
onPress: handlePress
|
|
680
|
-
}
|
|
681
|
-
|
|
682
|
-
)
|
|
683
|
-
|
|
684
|
-
|
|
875
|
+
}
|
|
876
|
+
);
|
|
877
|
+
if (collapsed) {
|
|
878
|
+
return /* @__PURE__ */ jsx(View, { nativeID: LINKS_REGION_ID, style: navBarStyles.linksStacked, testID: NAV_TEST_IDS.navBarLinks, children: items.map((item) => /* @__PURE__ */ jsx(React5.Fragment, { children: renderLink(item) }, item.key)) });
|
|
879
|
+
}
|
|
880
|
+
const visibleItems = items.slice(0, visibleCount);
|
|
881
|
+
const overflowItems = items.slice(visibleCount);
|
|
882
|
+
return /* @__PURE__ */ jsxs(
|
|
883
|
+
View,
|
|
685
884
|
{
|
|
686
|
-
horizontal: true,
|
|
687
|
-
showsHorizontalScrollIndicator: false,
|
|
688
885
|
nativeID: LINKS_REGION_ID,
|
|
689
|
-
style: navBarStyles.
|
|
690
|
-
contentContainerStyle: navBarStyles.links,
|
|
886
|
+
style: navBarStyles.linksRow,
|
|
691
887
|
testID: NAV_TEST_IDS.navBarLinks,
|
|
692
|
-
|
|
888
|
+
onLayout: (event) => setAvailableWidth(event.nativeEvent.layout.width),
|
|
889
|
+
children: [
|
|
890
|
+
visibleItems.map((item, index) => /* @__PURE__ */ jsx(
|
|
891
|
+
View,
|
|
892
|
+
{
|
|
893
|
+
style: navBarStyles.linkCell,
|
|
894
|
+
onLayout: (event) => setItemWidth(index, event.nativeEvent.layout.width),
|
|
895
|
+
children: renderLink(item)
|
|
896
|
+
},
|
|
897
|
+
item.key
|
|
898
|
+
)),
|
|
899
|
+
overflowItems.length > 0 ? /* @__PURE__ */ jsx(
|
|
900
|
+
View,
|
|
901
|
+
{
|
|
902
|
+
style: navBarStyles.linkCell,
|
|
903
|
+
onLayout: (event) => setMoreWidth(event.nativeEvent.layout.width),
|
|
904
|
+
children: /* @__PURE__ */ jsx(
|
|
905
|
+
NavOverflowMenu,
|
|
906
|
+
{
|
|
907
|
+
colors: linkColors,
|
|
908
|
+
hint: overflowHint,
|
|
909
|
+
items: overflowItems,
|
|
910
|
+
label: overflowLabel,
|
|
911
|
+
pathname,
|
|
912
|
+
testID: NAV_TEST_IDS.navBarOverflow,
|
|
913
|
+
onNavigate: handlePress
|
|
914
|
+
}
|
|
915
|
+
)
|
|
916
|
+
}
|
|
917
|
+
) : null
|
|
918
|
+
]
|
|
693
919
|
}
|
|
694
920
|
);
|
|
695
921
|
})() : null,
|
|
@@ -698,7 +924,57 @@ var NavBar = ({
|
|
|
698
924
|
}
|
|
699
925
|
);
|
|
700
926
|
};
|
|
701
|
-
var
|
|
927
|
+
var NavBar = ({ barTheme, ...props }) => {
|
|
928
|
+
const { t, navigate } = useUi();
|
|
929
|
+
if (barTheme === void 0) return /* @__PURE__ */ jsx(NavBarInner, { ...props });
|
|
930
|
+
return /* @__PURE__ */ jsx(UiProvider, { navigate, t, theme: barTheme, children: /* @__PURE__ */ jsx(NavBarInner, { ...props }) });
|
|
931
|
+
};
|
|
932
|
+
var Nav = (props) => {
|
|
933
|
+
if (props.orientation === "vertical") {
|
|
934
|
+
return /* @__PURE__ */ jsx(Sidebar, { ...props });
|
|
935
|
+
}
|
|
936
|
+
return /* @__PURE__ */ jsx(NavBar, { ...props });
|
|
937
|
+
};
|
|
938
|
+
var CARD_PADDING = 20;
|
|
939
|
+
var CARD_BORDER_RADIUS = 12;
|
|
940
|
+
var CARD_BORDER_WIDTH = 1;
|
|
941
|
+
var CARD_TITLE_FONT_SIZE = 16;
|
|
942
|
+
var CARD_MESSAGE_FONT_SIZE = 14;
|
|
943
|
+
var CARD_TITLE_MARGIN_BOTTOM = 8;
|
|
944
|
+
var styles = StyleSheet.create({
|
|
945
|
+
centerFill: { flex: 1, justifyContent: "center", alignItems: "center" },
|
|
946
|
+
card: {
|
|
947
|
+
padding: CARD_PADDING,
|
|
948
|
+
borderRadius: CARD_BORDER_RADIUS,
|
|
949
|
+
borderWidth: CARD_BORDER_WIDTH
|
|
950
|
+
},
|
|
951
|
+
cardTitle: { fontSize: CARD_TITLE_FONT_SIZE, fontWeight: "700", marginBottom: CARD_TITLE_MARGIN_BOTTOM },
|
|
952
|
+
cardMessage: { fontSize: CARD_MESSAGE_FONT_SIZE }
|
|
953
|
+
});
|
|
954
|
+
function MessageCard({
|
|
955
|
+
message,
|
|
956
|
+
accentColor,
|
|
957
|
+
testID
|
|
958
|
+
}) {
|
|
959
|
+
const { theme } = useUi();
|
|
960
|
+
const colors = theme.colors;
|
|
961
|
+
return /* @__PURE__ */ jsxs(View, { style: [styles.card, { backgroundColor: colors.surface, borderColor: accentColor }], testID, children: [
|
|
962
|
+
/* @__PURE__ */ jsx(Text, { style: [styles.cardTitle, { color: accentColor }], children: message.titleText }),
|
|
963
|
+
/* @__PURE__ */ jsx(Text, { style: [styles.cardMessage, { color: colors.textSecondary }], children: message.messageText })
|
|
964
|
+
] });
|
|
965
|
+
}
|
|
966
|
+
function useContentBody(state, children, testID) {
|
|
967
|
+
const { theme } = useUi();
|
|
968
|
+
const primary = theme.palette.primary["500"];
|
|
969
|
+
const errorColor = theme.semantic.error["500"];
|
|
970
|
+
if (state?.forbidden)
|
|
971
|
+
return /* @__PURE__ */ jsx(MessageCard, { accentColor: errorColor, message: state.forbidden, testID: `${testID}${APP_SHELL_SUFFIX.forbidden}` });
|
|
972
|
+
if (state?.error)
|
|
973
|
+
return /* @__PURE__ */ jsx(MessageCard, { accentColor: errorColor, message: state.error, testID: `${testID}${APP_SHELL_SUFFIX.error}` });
|
|
974
|
+
if (state?.loading === true)
|
|
975
|
+
return /* @__PURE__ */ jsx(View, { style: styles.centerFill, testID: `${testID}${APP_SHELL_SUFFIX.loading}`, children: /* @__PURE__ */ jsx(ActivityIndicator, { color: primary, size: "large" }) });
|
|
976
|
+
return children;
|
|
977
|
+
}
|
|
702
978
|
var SCRIM_COLOR = "rgba(0, 0, 0, 0.5)";
|
|
703
979
|
var MENU_GLYPH2 = "\u2630";
|
|
704
980
|
var MENU_MIN_TARGET = 44;
|
|
@@ -712,7 +988,7 @@ var DEFAULT_DRAWER_LABELS = {
|
|
|
712
988
|
closeLabel: "Close menu",
|
|
713
989
|
closeHint: "Close the navigation menu"
|
|
714
990
|
};
|
|
715
|
-
var
|
|
991
|
+
var styles2 = StyleSheet.create({
|
|
716
992
|
overlay: { ...StyleSheet.absoluteFillObject, zIndex: DRAWER_Z_INDEX },
|
|
717
993
|
scrim: { ...StyleSheet.absoluteFillObject, backgroundColor: SCRIM_COLOR },
|
|
718
994
|
panel: { position: "absolute", top: 0, bottom: 0, left: 0 },
|
|
@@ -733,10 +1009,10 @@ var MenuToggle = ({ label, hint, onOpen, testID }) => {
|
|
|
733
1009
|
accessibilityHint: hint,
|
|
734
1010
|
accessibilityLabel: label,
|
|
735
1011
|
ringColor: theme.palette.primary["500"],
|
|
736
|
-
style:
|
|
1012
|
+
style: styles2.menuToggle,
|
|
737
1013
|
testID,
|
|
738
1014
|
onPress: onOpen,
|
|
739
|
-
children: /* @__PURE__ */ jsx(Text, { style: [
|
|
1015
|
+
children: /* @__PURE__ */ jsx(Text, { style: [styles2.menuToggleGlyph, { color: theme.colors.text }], children: MENU_GLYPH2 })
|
|
740
1016
|
}
|
|
741
1017
|
);
|
|
742
1018
|
};
|
|
@@ -759,21 +1035,34 @@ var MobileDrawer = ({
|
|
|
759
1035
|
node.addEventListener("click", handleClick);
|
|
760
1036
|
return () => node.removeEventListener("click", handleClick);
|
|
761
1037
|
}, [onClose]);
|
|
762
|
-
return /* @__PURE__ */ jsxs(View, { style:
|
|
1038
|
+
return /* @__PURE__ */ jsxs(View, { style: styles2.overlay, children: [
|
|
763
1039
|
/* @__PURE__ */ jsx(
|
|
764
1040
|
Pressable,
|
|
765
1041
|
{
|
|
766
1042
|
accessibilityHint: labels.closeHint,
|
|
767
1043
|
accessibilityLabel: labels.closeLabel,
|
|
768
1044
|
accessibilityRole: "button",
|
|
769
|
-
style:
|
|
1045
|
+
style: styles2.scrim,
|
|
770
1046
|
testID: scrimTestID,
|
|
771
1047
|
onPress: onClose
|
|
772
1048
|
}
|
|
773
1049
|
),
|
|
774
|
-
/* @__PURE__ */ jsx(View, { ref: panelRef, "aria-modal": true, role: "dialog", style:
|
|
1050
|
+
/* @__PURE__ */ jsx(View, { ref: panelRef, "aria-modal": true, role: "dialog", style: styles2.panel, testID: drawerTestID, children: sidebar })
|
|
775
1051
|
] });
|
|
776
1052
|
};
|
|
1053
|
+
|
|
1054
|
+
// src/railMode.ts
|
|
1055
|
+
var RAIL_FULL_BREAKPOINT = 768;
|
|
1056
|
+
var DEFAULT_COLLAPSED_RAIL_MAX = 1024;
|
|
1057
|
+
function resolveRailMode({ viewport, hasSidebar, hasCollapsed, range }) {
|
|
1058
|
+
if (!hasSidebar) return "none";
|
|
1059
|
+
if (hasCollapsed) {
|
|
1060
|
+
if (viewport >= range.max) return "full";
|
|
1061
|
+
if (viewport >= range.min) return "collapsed";
|
|
1062
|
+
return "drawer";
|
|
1063
|
+
}
|
|
1064
|
+
return viewport >= RAIL_FULL_BREAKPOINT ? "full" : "drawer";
|
|
1065
|
+
}
|
|
777
1066
|
function resolveContentMaxWidth(width, viewport) {
|
|
778
1067
|
if (width === "full") return "full";
|
|
779
1068
|
const wideMinViewport = width.wideMinViewport ?? Number.POSITIVE_INFINITY;
|
|
@@ -785,13 +1074,7 @@ function useContentMaxWidth(width) {
|
|
|
785
1074
|
return resolveContentMaxWidth(width, viewport);
|
|
786
1075
|
}
|
|
787
1076
|
var DEFAULT_CONTENT_PADDING = 24;
|
|
788
|
-
var
|
|
789
|
-
var CARD_BORDER_RADIUS = 12;
|
|
790
|
-
var CARD_BORDER_WIDTH = 1;
|
|
791
|
-
var CARD_TITLE_FONT_SIZE = 16;
|
|
792
|
-
var CARD_MESSAGE_FONT_SIZE = 14;
|
|
793
|
-
var CARD_TITLE_MARGIN_BOTTOM = 8;
|
|
794
|
-
var styles2 = StyleSheet.create({
|
|
1077
|
+
var styles3 = StyleSheet.create({
|
|
795
1078
|
root: { flex: 1 },
|
|
796
1079
|
centerFill: { flex: 1, justifyContent: "center", alignItems: "center" },
|
|
797
1080
|
scroll: { flex: 1 },
|
|
@@ -805,50 +1088,14 @@ var styles2 = StyleSheet.create({
|
|
|
805
1088
|
columnCapped: { flex: 1, width: "100%", alignSelf: "center" },
|
|
806
1089
|
// Non-flex variant used to cap the nav strip's inner content to the content
|
|
807
1090
|
// column so the nav aligns with the page content instead of running full-bleed.
|
|
808
|
-
chromeCapped: { width: "100%", alignSelf: "center" }
|
|
809
|
-
card: {
|
|
810
|
-
padding: CARD_PADDING,
|
|
811
|
-
borderRadius: CARD_BORDER_RADIUS,
|
|
812
|
-
borderWidth: CARD_BORDER_WIDTH
|
|
813
|
-
},
|
|
814
|
-
cardTitle: { fontSize: CARD_TITLE_FONT_SIZE, fontWeight: "700", marginBottom: CARD_TITLE_MARGIN_BOTTOM },
|
|
815
|
-
cardMessage: { fontSize: CARD_MESSAGE_FONT_SIZE }
|
|
1091
|
+
chromeCapped: { width: "100%", alignSelf: "center" }
|
|
816
1092
|
});
|
|
817
|
-
function MessageCard({
|
|
818
|
-
message,
|
|
819
|
-
accentColor,
|
|
820
|
-
testID
|
|
821
|
-
}) {
|
|
822
|
-
const { theme } = useUi();
|
|
823
|
-
const colors = theme.colors;
|
|
824
|
-
return /* @__PURE__ */ jsxs(
|
|
825
|
-
View,
|
|
826
|
-
{
|
|
827
|
-
style: [styles2.card, { backgroundColor: colors.surface, borderColor: accentColor }],
|
|
828
|
-
testID,
|
|
829
|
-
children: [
|
|
830
|
-
/* @__PURE__ */ jsx(Text, { style: [styles2.cardTitle, { color: accentColor }], children: message.titleText }),
|
|
831
|
-
/* @__PURE__ */ jsx(Text, { style: [styles2.cardMessage, { color: colors.textSecondary }], children: message.messageText })
|
|
832
|
-
]
|
|
833
|
-
}
|
|
834
|
-
);
|
|
835
|
-
}
|
|
836
|
-
function useContentBody(state, children, testID) {
|
|
837
|
-
const { theme } = useUi();
|
|
838
|
-
const primary = theme.palette.primary["500"];
|
|
839
|
-
const errorColor = theme.semantic.error["500"];
|
|
840
|
-
if (state?.forbidden)
|
|
841
|
-
return /* @__PURE__ */ jsx(MessageCard, { accentColor: errorColor, message: state.forbidden, testID: `${testID}${APP_SHELL_SUFFIX.forbidden}` });
|
|
842
|
-
if (state?.error)
|
|
843
|
-
return /* @__PURE__ */ jsx(MessageCard, { accentColor: errorColor, message: state.error, testID: `${testID}${APP_SHELL_SUFFIX.error}` });
|
|
844
|
-
if (state?.loading === true)
|
|
845
|
-
return /* @__PURE__ */ jsx(View, { style: styles2.centerFill, testID: `${testID}${APP_SHELL_SUFFIX.loading}`, children: /* @__PURE__ */ jsx(ActivityIndicator, { color: primary, size: "large" }) });
|
|
846
|
-
return children;
|
|
847
|
-
}
|
|
848
1093
|
var AppShell = ({
|
|
849
1094
|
header,
|
|
850
1095
|
nav,
|
|
851
1096
|
sidebar,
|
|
1097
|
+
collapsedSidebar,
|
|
1098
|
+
collapsedRailRange,
|
|
852
1099
|
mobileMenu,
|
|
853
1100
|
banner,
|
|
854
1101
|
width = "full",
|
|
@@ -864,34 +1111,40 @@ var AppShell = ({
|
|
|
864
1111
|
const maxWidth = useContentMaxWidth(width);
|
|
865
1112
|
const body = useContentBody(state, children, testID);
|
|
866
1113
|
const { width: viewportWidth } = useWindowDimensions();
|
|
867
|
-
const
|
|
868
|
-
const
|
|
1114
|
+
const railRange = collapsedRailRange ?? { min: RAIL_FULL_BREAKPOINT, max: DEFAULT_COLLAPSED_RAIL_MAX };
|
|
1115
|
+
const railMode = resolveRailMode({
|
|
1116
|
+
viewport: viewportWidth,
|
|
1117
|
+
hasSidebar: sidebar !== void 0,
|
|
1118
|
+
hasCollapsed: collapsedSidebar !== void 0,
|
|
1119
|
+
range: railRange
|
|
1120
|
+
});
|
|
1121
|
+
const useDrawer = railMode === "drawer";
|
|
869
1122
|
const [drawerOpen, setDrawerOpen] = useState(false);
|
|
870
1123
|
const openDrawer = useCallback(() => setDrawerOpen(true), []);
|
|
871
1124
|
const closeDrawer = useCallback(() => setDrawerOpen(false), []);
|
|
872
1125
|
useEffect(() => {
|
|
873
|
-
if (!
|
|
874
|
-
}, [
|
|
1126
|
+
if (!useDrawer && drawerOpen) setDrawerOpen(false);
|
|
1127
|
+
}, [useDrawer, drawerOpen]);
|
|
875
1128
|
const drawerLabels = { ...DEFAULT_DRAWER_LABELS, ...mobileMenu };
|
|
876
1129
|
const isUnauthenticated = gate !== void 0 && !gate.pending && !gate.authenticated;
|
|
877
1130
|
useEffect(() => {
|
|
878
1131
|
if (isUnauthenticated && gate !== void 0) gate.onRedirect();
|
|
879
1132
|
}, [isUnauthenticated, gate]);
|
|
880
1133
|
if (gate?.pending === true)
|
|
881
|
-
return /* @__PURE__ */ jsx(View, { style: [
|
|
1134
|
+
return /* @__PURE__ */ jsx(View, { style: [styles3.root, styles3.centerFill, { backgroundColor: theme.colors.background }], testID: `${testID}${APP_SHELL_SUFFIX.pending}`, children: /* @__PURE__ */ jsx(ActivityIndicator, { color: primary, size: "large" }) });
|
|
882
1135
|
if (isUnauthenticated) return null;
|
|
883
|
-
const columnStyle = maxWidth === "full" ?
|
|
884
|
-
const navInnerStyle = chromeAlignment === "content" && maxWidth !== "full" ? [
|
|
1136
|
+
const columnStyle = maxWidth === "full" ? styles3.columnFull : [styles3.columnCapped, { maxWidth }];
|
|
1137
|
+
const navInnerStyle = chromeAlignment === "content" && maxWidth !== "full" ? [styles3.chromeCapped, { maxWidth, paddingHorizontal: contentPadding }] : void 0;
|
|
885
1138
|
const scroller = /* @__PURE__ */ jsx(
|
|
886
1139
|
ScrollView,
|
|
887
1140
|
{
|
|
888
|
-
contentContainerStyle: [
|
|
889
|
-
style:
|
|
1141
|
+
contentContainerStyle: [styles3.scrollContent, { padding: contentPadding }],
|
|
1142
|
+
style: styles3.scroll,
|
|
890
1143
|
testID: `${testID}${APP_SHELL_SUFFIX.content}`,
|
|
891
1144
|
children: /* @__PURE__ */ jsx(View, { style: columnStyle, children: body })
|
|
892
1145
|
}
|
|
893
1146
|
);
|
|
894
|
-
const headerRegion = useDrawer ? /* @__PURE__ */ jsxs(View, { style:
|
|
1147
|
+
const headerRegion = useDrawer ? /* @__PURE__ */ jsxs(View, { style: styles3.headerRow, testID: `${testID}${APP_SHELL_SUFFIX.header}`, children: [
|
|
895
1148
|
/* @__PURE__ */ jsx(
|
|
896
1149
|
MenuToggle,
|
|
897
1150
|
{
|
|
@@ -901,15 +1154,15 @@ var AppShell = ({
|
|
|
901
1154
|
onOpen: openDrawer
|
|
902
1155
|
}
|
|
903
1156
|
),
|
|
904
|
-
/* @__PURE__ */ jsx(View, { style:
|
|
1157
|
+
/* @__PURE__ */ jsx(View, { style: styles3.headerFill, children: header })
|
|
905
1158
|
] }) : /* @__PURE__ */ jsx(View, { testID: `${testID}${APP_SHELL_SUFFIX.header}`, children: header });
|
|
906
|
-
const
|
|
907
|
-
return /* @__PURE__ */ jsxs(View, { style: [
|
|
1159
|
+
const railRegion = railMode === "full" ? /* @__PURE__ */ jsx(View, { testID: `${testID}${APP_SHELL_SUFFIX.sidebar}`, children: sidebar }) : railMode === "collapsed" ? /* @__PURE__ */ jsx(View, { testID: `${testID}${APP_SHELL_SUFFIX.collapsedSidebar}`, children: collapsedSidebar }) : null;
|
|
1160
|
+
return /* @__PURE__ */ jsxs(View, { style: [styles3.root, { backgroundColor: theme.colors.background }], testID, children: [
|
|
908
1161
|
headerRegion,
|
|
909
1162
|
nav !== void 0 ? /* @__PURE__ */ jsx(View, { testID: `${testID}${APP_SHELL_SUFFIX.nav}`, children: navInnerStyle !== void 0 ? /* @__PURE__ */ jsx(View, { style: navInnerStyle, children: nav }) : nav }) : null,
|
|
910
1163
|
banner !== void 0 ? /* @__PURE__ */ jsx(View, { testID: `${testID}${APP_SHELL_SUFFIX.banner}`, children: banner }) : null,
|
|
911
|
-
|
|
912
|
-
|
|
1164
|
+
railRegion !== null ? /* @__PURE__ */ jsxs(View, { style: styles3.bodyRow, children: [
|
|
1165
|
+
railRegion,
|
|
913
1166
|
scroller
|
|
914
1167
|
] }) : scroller,
|
|
915
1168
|
useDrawer && drawerOpen ? /* @__PURE__ */ jsx(
|
|
@@ -924,6 +1177,261 @@ var AppShell = ({
|
|
|
924
1177
|
) : null
|
|
925
1178
|
] });
|
|
926
1179
|
};
|
|
1180
|
+
var COLLAPSED_ICON_SIZE = 22;
|
|
1181
|
+
function glyphFor(label) {
|
|
1182
|
+
return label.trim().charAt(0).toUpperCase();
|
|
1183
|
+
}
|
|
1184
|
+
var CollapsedRail = ({
|
|
1185
|
+
items,
|
|
1186
|
+
pathname,
|
|
1187
|
+
onNavigate,
|
|
1188
|
+
regionLabel,
|
|
1189
|
+
navigateHint = (label) => label,
|
|
1190
|
+
header,
|
|
1191
|
+
footer,
|
|
1192
|
+
containerStyle
|
|
1193
|
+
}) => {
|
|
1194
|
+
const { theme } = useUi();
|
|
1195
|
+
const colors = theme.colors;
|
|
1196
|
+
const primaryColor = theme.palette.primary["500"];
|
|
1197
|
+
return /* @__PURE__ */ jsxs(
|
|
1198
|
+
View,
|
|
1199
|
+
{
|
|
1200
|
+
accessibilityRole: "none",
|
|
1201
|
+
"aria-label": regionLabel,
|
|
1202
|
+
role: "navigation",
|
|
1203
|
+
style: [
|
|
1204
|
+
collapsedRailStyles.container,
|
|
1205
|
+
{ backgroundColor: colors.surface, borderRightColor: colors.border },
|
|
1206
|
+
containerStyle
|
|
1207
|
+
],
|
|
1208
|
+
children: [
|
|
1209
|
+
header !== void 0 ? /* @__PURE__ */ jsx(View, { style: collapsedRailStyles.slot, children: header }) : null,
|
|
1210
|
+
items.map((item) => {
|
|
1211
|
+
const active = isRouteActive(pathname, item.route);
|
|
1212
|
+
const iconColor = active ? primaryColor : colors.textSecondary;
|
|
1213
|
+
return /* @__PURE__ */ jsx(
|
|
1214
|
+
FocusableTouchable,
|
|
1215
|
+
{
|
|
1216
|
+
accessibilityHint: navigateHint(item.label),
|
|
1217
|
+
accessibilityLabel: item.label,
|
|
1218
|
+
ringColor: primaryColor,
|
|
1219
|
+
style: [collapsedRailStyles.item, active ? { backgroundColor: colors.border } : void 0],
|
|
1220
|
+
testID: item.testID ?? item.key,
|
|
1221
|
+
onPress: () => onNavigate(item.route),
|
|
1222
|
+
children: typeof item.renderIcon === "function" ? item.renderIcon(iconColor, COLLAPSED_ICON_SIZE) : /* @__PURE__ */ jsx(Text, { style: [collapsedRailStyles.glyph, { color: iconColor }], children: glyphFor(item.label) })
|
|
1223
|
+
},
|
|
1224
|
+
item.key
|
|
1225
|
+
);
|
|
1226
|
+
}),
|
|
1227
|
+
/* @__PURE__ */ jsx(View, { style: collapsedRailStyles.spacer }),
|
|
1228
|
+
footer !== void 0 ? /* @__PURE__ */ jsx(View, { style: collapsedRailStyles.slot, children: footer }) : null
|
|
1229
|
+
]
|
|
1230
|
+
}
|
|
1231
|
+
);
|
|
1232
|
+
};
|
|
1233
|
+
function wantsTopBar(layout, hasHeader, hasTopBar) {
|
|
1234
|
+
return layout === "top" || layout === "both" || hasHeader || layout === "side" && hasTopBar;
|
|
1235
|
+
}
|
|
1236
|
+
function wantsSideRail(layout, hasSideRail) {
|
|
1237
|
+
return (layout === "side" || layout === "both") && hasSideRail;
|
|
1238
|
+
}
|
|
1239
|
+
var NavShell = ({
|
|
1240
|
+
layout,
|
|
1241
|
+
pathname,
|
|
1242
|
+
onNavigate,
|
|
1243
|
+
regionLabel,
|
|
1244
|
+
navigateHint,
|
|
1245
|
+
topBar,
|
|
1246
|
+
header,
|
|
1247
|
+
sideRail,
|
|
1248
|
+
children,
|
|
1249
|
+
collapsedSidebar: collapsedSidebarProp,
|
|
1250
|
+
collapsedRailRange: collapsedRailRangeProp,
|
|
1251
|
+
...shellProps
|
|
1252
|
+
}) => {
|
|
1253
|
+
const showTopBar = wantsTopBar(layout, header !== void 0, topBar !== void 0);
|
|
1254
|
+
const showSideRail = wantsSideRail(layout, sideRail !== void 0);
|
|
1255
|
+
const headerNode = header !== void 0 ? header : showTopBar && topBar !== void 0 ? /* @__PURE__ */ jsx(
|
|
1256
|
+
NavBar,
|
|
1257
|
+
{
|
|
1258
|
+
barTheme: topBar.barTheme,
|
|
1259
|
+
brand: topBar.brand,
|
|
1260
|
+
collapseBelow: topBar.collapseBelow,
|
|
1261
|
+
containerStyle: topBar.containerStyle,
|
|
1262
|
+
items: topBar.items,
|
|
1263
|
+
menuHint: topBar.menuHint,
|
|
1264
|
+
menuLabel: topBar.menuLabel,
|
|
1265
|
+
navigateHint,
|
|
1266
|
+
overflowHint: topBar.overflowHint,
|
|
1267
|
+
overflowLabel: topBar.overflowLabel,
|
|
1268
|
+
pathname,
|
|
1269
|
+
regionLabel,
|
|
1270
|
+
renderMenuIcon: topBar.renderMenuIcon,
|
|
1271
|
+
right: topBar.right,
|
|
1272
|
+
onNavigate
|
|
1273
|
+
}
|
|
1274
|
+
) : null;
|
|
1275
|
+
const sidebarNode = showSideRail && sideRail !== void 0 ? /* @__PURE__ */ jsx(
|
|
1276
|
+
Sidebar,
|
|
1277
|
+
{
|
|
1278
|
+
collapseHint: sideRail.collapseHint,
|
|
1279
|
+
containerStyle: sideRail.containerStyle,
|
|
1280
|
+
expandHint: sideRail.expandHint,
|
|
1281
|
+
footer: sideRail.footer,
|
|
1282
|
+
header: sideRail.header,
|
|
1283
|
+
items: sideRail.items,
|
|
1284
|
+
navigateHint,
|
|
1285
|
+
pathname,
|
|
1286
|
+
regionLabel,
|
|
1287
|
+
renderChevron: sideRail.renderChevron,
|
|
1288
|
+
title: sideRail.title,
|
|
1289
|
+
onNavigate
|
|
1290
|
+
}
|
|
1291
|
+
) : void 0;
|
|
1292
|
+
const collapsedCfg = showSideRail ? sideRail?.collapsed : void 0;
|
|
1293
|
+
const collapsedNode = collapsedCfg !== void 0 && sideRail !== void 0 ? /* @__PURE__ */ jsx(
|
|
1294
|
+
CollapsedRail,
|
|
1295
|
+
{
|
|
1296
|
+
footer: collapsedCfg.footer,
|
|
1297
|
+
header: collapsedCfg.header,
|
|
1298
|
+
items: collapsedCfg.items ?? sideRail.items,
|
|
1299
|
+
navigateHint,
|
|
1300
|
+
pathname,
|
|
1301
|
+
regionLabel,
|
|
1302
|
+
onNavigate
|
|
1303
|
+
}
|
|
1304
|
+
) : collapsedSidebarProp;
|
|
1305
|
+
const collapsedRange = collapsedCfg?.range ?? collapsedRailRangeProp;
|
|
1306
|
+
return /* @__PURE__ */ jsx(
|
|
1307
|
+
AppShell,
|
|
1308
|
+
{
|
|
1309
|
+
...shellProps,
|
|
1310
|
+
collapsedRailRange: collapsedRange,
|
|
1311
|
+
collapsedSidebar: collapsedNode,
|
|
1312
|
+
header: headerNode,
|
|
1313
|
+
sidebar: sidebarNode,
|
|
1314
|
+
children
|
|
1315
|
+
}
|
|
1316
|
+
);
|
|
1317
|
+
};
|
|
1318
|
+
var TEXT_ON_PRIMARY4 = "#ffffff";
|
|
1319
|
+
var DEFAULT_MIN_ITEMS = 1;
|
|
1320
|
+
var PillNav = ({
|
|
1321
|
+
items,
|
|
1322
|
+
pathname,
|
|
1323
|
+
onNavigate,
|
|
1324
|
+
regionLabel,
|
|
1325
|
+
navigateHint = (label) => label,
|
|
1326
|
+
minItems = DEFAULT_MIN_ITEMS,
|
|
1327
|
+
containerStyle
|
|
1328
|
+
}) => {
|
|
1329
|
+
const { theme } = useUi();
|
|
1330
|
+
const colors = theme.colors;
|
|
1331
|
+
const primaryColor = theme.palette.primary["500"];
|
|
1332
|
+
if (items.length < minItems) return null;
|
|
1333
|
+
return /* @__PURE__ */ jsx(
|
|
1334
|
+
View,
|
|
1335
|
+
{
|
|
1336
|
+
accessibilityRole: "none",
|
|
1337
|
+
"aria-label": regionLabel,
|
|
1338
|
+
role: "navigation",
|
|
1339
|
+
style: [pillNavStyles.container, containerStyle],
|
|
1340
|
+
children: items.map((item) => {
|
|
1341
|
+
const active = isRouteActive(pathname, item.route);
|
|
1342
|
+
const textColor = active ? TEXT_ON_PRIMARY4 : colors.textSecondary;
|
|
1343
|
+
const pillStyle = active ? { backgroundColor: primaryColor } : { backgroundColor: colors.surfaceElevated };
|
|
1344
|
+
return /* @__PURE__ */ jsxs(
|
|
1345
|
+
FocusableTouchable,
|
|
1346
|
+
{
|
|
1347
|
+
accessibilityHint: navigateHint(item.label),
|
|
1348
|
+
accessibilityLabel: item.label,
|
|
1349
|
+
accessibilityRole: "link",
|
|
1350
|
+
ringColor: primaryColor,
|
|
1351
|
+
style: [pillNavStyles.pill, pillStyle],
|
|
1352
|
+
testID: item.testID ?? item.key,
|
|
1353
|
+
onPress: () => onNavigate(item.route),
|
|
1354
|
+
children: [
|
|
1355
|
+
typeof item.renderIcon === "function" ? item.renderIcon(textColor, NAV_ICON_SIZE) : null,
|
|
1356
|
+
/* @__PURE__ */ jsx(Text, { style: [pillNavStyles.pillText, { color: textColor }], children: item.label })
|
|
1357
|
+
]
|
|
1358
|
+
},
|
|
1359
|
+
item.key
|
|
1360
|
+
);
|
|
1361
|
+
})
|
|
1362
|
+
}
|
|
1363
|
+
);
|
|
1364
|
+
};
|
|
1365
|
+
var TEXT_ON_PRIMARY5 = "#ffffff";
|
|
1366
|
+
var DARK_MODE_ICON_SIZE = 16;
|
|
1367
|
+
var DarkModeControl = ({
|
|
1368
|
+
value,
|
|
1369
|
+
options,
|
|
1370
|
+
onChange,
|
|
1371
|
+
regionLabel,
|
|
1372
|
+
variant = "segmented",
|
|
1373
|
+
testID,
|
|
1374
|
+
containerStyle
|
|
1375
|
+
}) => {
|
|
1376
|
+
const { theme } = useUi();
|
|
1377
|
+
const colors = theme.colors;
|
|
1378
|
+
const primaryColor = theme.palette.primary["500"];
|
|
1379
|
+
const currentIndex = options.findIndex((option) => option.value === value);
|
|
1380
|
+
const current = currentIndex >= 0 ? options[currentIndex] : options[0];
|
|
1381
|
+
const advance = useCallback(() => {
|
|
1382
|
+
if (options.length === 0) return;
|
|
1383
|
+
const from = currentIndex >= 0 ? currentIndex : 0;
|
|
1384
|
+
const next = options[(from + 1) % options.length];
|
|
1385
|
+
if (next !== void 0) onChange(next.value);
|
|
1386
|
+
}, [options, currentIndex, onChange]);
|
|
1387
|
+
if (options.length === 0 || current === void 0) return null;
|
|
1388
|
+
if (variant === "cycle") {
|
|
1389
|
+
const iconColor = colors.text;
|
|
1390
|
+
return /* @__PURE__ */ jsx(View, { "aria-label": regionLabel, style: containerStyle, children: /* @__PURE__ */ jsxs(
|
|
1391
|
+
FocusableTouchable,
|
|
1392
|
+
{
|
|
1393
|
+
accessibilityHint: current.hint,
|
|
1394
|
+
accessibilityLabel: current.label,
|
|
1395
|
+
ringColor: primaryColor,
|
|
1396
|
+
style: [darkModeStyles.cycle, { backgroundColor: colors.surfaceElevated }],
|
|
1397
|
+
testID,
|
|
1398
|
+
onPress: advance,
|
|
1399
|
+
children: [
|
|
1400
|
+
typeof current.renderIcon === "function" ? current.renderIcon(iconColor, DARK_MODE_ICON_SIZE) : null,
|
|
1401
|
+
/* @__PURE__ */ jsx(Text, { style: [darkModeStyles.cycleText, { color: colors.text }], children: current.label })
|
|
1402
|
+
]
|
|
1403
|
+
}
|
|
1404
|
+
) });
|
|
1405
|
+
}
|
|
1406
|
+
return /* @__PURE__ */ jsx(
|
|
1407
|
+
View,
|
|
1408
|
+
{
|
|
1409
|
+
"aria-label": regionLabel,
|
|
1410
|
+
role: "group",
|
|
1411
|
+
style: [darkModeStyles.segmentRow, { borderColor: colors.border }, containerStyle],
|
|
1412
|
+
children: options.map((option) => {
|
|
1413
|
+
const active = option.value === value;
|
|
1414
|
+
const textColor = active ? TEXT_ON_PRIMARY5 : colors.textSecondary;
|
|
1415
|
+
return /* @__PURE__ */ jsxs(
|
|
1416
|
+
FocusableTouchable,
|
|
1417
|
+
{
|
|
1418
|
+
accessibilityHint: option.hint,
|
|
1419
|
+
accessibilityLabel: option.label,
|
|
1420
|
+
ringColor: primaryColor,
|
|
1421
|
+
style: [darkModeStyles.segment, active ? { backgroundColor: primaryColor } : void 0],
|
|
1422
|
+
testID: testID !== void 0 ? `${testID}-${option.value}` : void 0,
|
|
1423
|
+
onPress: () => onChange(option.value),
|
|
1424
|
+
children: [
|
|
1425
|
+
typeof option.renderIcon === "function" ? option.renderIcon(textColor, DARK_MODE_ICON_SIZE) : null,
|
|
1426
|
+
/* @__PURE__ */ jsx(Text, { style: [darkModeStyles.segmentText, { color: textColor }], children: option.label })
|
|
1427
|
+
]
|
|
1428
|
+
},
|
|
1429
|
+
option.value
|
|
1430
|
+
);
|
|
1431
|
+
})
|
|
1432
|
+
}
|
|
1433
|
+
);
|
|
1434
|
+
};
|
|
927
1435
|
function roleRoutesToNavItems(routes, translate) {
|
|
928
1436
|
return routes.map((entry) => ({
|
|
929
1437
|
key: entry.role,
|
|
@@ -935,6 +1443,6 @@ function accessibleNavItems(user, table, translate) {
|
|
|
935
1443
|
return roleRoutesToNavItems(resolveAccessibleRoutes(user, table), translate);
|
|
936
1444
|
}
|
|
937
1445
|
|
|
938
|
-
export { ACTIVE_BORDER_RADIUS, APP_SHELL_SUFFIX, AppShell, BASE_INDENT, CHEVRON_ICON_SIZE, NAV_ICON_SIZE, NAV_TEST_IDS, NavBar, NavExpandableItem, Sidebar, Topbar, accessibleNavItems, expandableStyles, isRouteActive, navStyles, resolveContentMaxWidth, roleRoutesToNavItems, useContentMaxWidth };
|
|
1446
|
+
export { ACTIVE_BORDER_RADIUS, APP_SHELL_SUFFIX, AppShell, BASE_INDENT, CHEVRON_ICON_SIZE, CollapsedRail, DEFAULT_COLLAPSED_RAIL_MAX, DarkModeControl, NAV_ICON_SIZE, NAV_LINK_GAP, NAV_TEST_IDS, Nav, NavBar, NavExpandableItem, NavOverflowMenu, NavShell, PillNav, RAIL_FULL_BREAKPOINT, Sidebar, Topbar, accessibleNavItems, collapsedRailStyles, darkModeStyles, expandableStyles, isRouteActive, navStyles, pillNavStyles, resolveContentMaxWidth, resolveRailMode, roleRoutesToNavItems, useContentMaxWidth };
|
|
939
1447
|
//# sourceMappingURL=index.mjs.map
|
|
940
1448
|
//# sourceMappingURL=index.mjs.map
|