@dloizides/ui-nav 1.4.0 → 1.6.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 +173 -38
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +175 -40
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { StyleSheet, Platform, TouchableOpacity, View, Text, useWindowDimensions, Pressable,
|
|
1
|
+
import { StyleSheet, Platform, TouchableOpacity, View, Text, useWindowDimensions, Pressable, ScrollView, ActivityIndicator } from 'react-native';
|
|
2
2
|
import { useUi } from '@dloizides/ui-feedback';
|
|
3
|
-
import { useState, useCallback, useMemo, useEffect } from 'react';
|
|
3
|
+
import { useState, useCallback, useMemo, useEffect, useRef } from 'react';
|
|
4
4
|
import { jsxs, jsx } from 'react/jsx-runtime';
|
|
5
5
|
import { resolveAccessibleRoutes } from '@dloizides/auth-web';
|
|
6
6
|
|
|
@@ -74,7 +74,7 @@ var navBarStyles = StyleSheet.create({
|
|
|
74
74
|
rowGap: 4,
|
|
75
75
|
paddingVertical: 8
|
|
76
76
|
},
|
|
77
|
-
brand: { marginRight: 16 },
|
|
77
|
+
brand: { marginRight: 16, flexShrink: 0 },
|
|
78
78
|
toggle: {
|
|
79
79
|
marginLeft: "auto",
|
|
80
80
|
paddingHorizontal: 10,
|
|
@@ -87,10 +87,19 @@ var navBarStyles = StyleSheet.create({
|
|
|
87
87
|
justifyContent: "center"
|
|
88
88
|
},
|
|
89
89
|
toggleGlyph: { fontSize: 20, fontWeight: "700" },
|
|
90
|
+
// Expanded links live inside a horizontal ScrollView so the bar stays ONE ROW no matter how many
|
|
91
|
+
// items an app passes: they never wrap onto a second line — any overflow scrolls instead, with the
|
|
92
|
+
// brand (left) and right slot pinned. `linksScroll` is the ScrollView's own box (a shrinkable,
|
|
93
|
+
// growable flex child that may collapse to 0 and scroll); `links` is its non-wrapping content row.
|
|
94
|
+
linksScroll: {
|
|
95
|
+
flexGrow: 1,
|
|
96
|
+
flexShrink: 1,
|
|
97
|
+
minWidth: 0
|
|
98
|
+
},
|
|
90
99
|
links: {
|
|
91
100
|
flexDirection: "row",
|
|
92
101
|
alignItems: "center",
|
|
93
|
-
flexWrap: "
|
|
102
|
+
flexWrap: "nowrap",
|
|
94
103
|
columnGap: 4,
|
|
95
104
|
rowGap: 4
|
|
96
105
|
},
|
|
@@ -119,6 +128,7 @@ var navBarStyles = StyleSheet.create({
|
|
|
119
128
|
linkText: { fontSize: 15, fontWeight: "600" },
|
|
120
129
|
right: {
|
|
121
130
|
marginLeft: "auto",
|
|
131
|
+
flexShrink: 0,
|
|
122
132
|
flexDirection: "row",
|
|
123
133
|
alignItems: "center",
|
|
124
134
|
columnGap: 8
|
|
@@ -346,6 +356,12 @@ var APP_SHELL_SUFFIX = {
|
|
|
346
356
|
nav: "-nav",
|
|
347
357
|
/** The persistent left rail of the back-office layout (only when `sidebar` is supplied). */
|
|
348
358
|
sidebar: "-sidebar",
|
|
359
|
+
/** Mobile hamburger that opens the nav drawer (back-office layout, below the breakpoint). */
|
|
360
|
+
menuToggle: "-menu-toggle",
|
|
361
|
+
/** The mobile nav drawer panel (holds the `sidebar` slot when opened on a narrow viewport). */
|
|
362
|
+
drawer: "-drawer",
|
|
363
|
+
/** The mobile nav drawer scrim (tap to close). */
|
|
364
|
+
scrim: "-drawer-scrim",
|
|
349
365
|
banner: "-banner",
|
|
350
366
|
pending: "-pending",
|
|
351
367
|
loading: "-loading",
|
|
@@ -642,33 +658,115 @@ var NavBar = ({
|
|
|
642
658
|
children: typeof renderMenuIcon === "function" ? renderMenuIcon(colors.text, open) : /* @__PURE__ */ jsx(Text, { style: [navBarStyles.toggleGlyph, { color: colors.text }], children: MENU_GLYPH })
|
|
643
659
|
}
|
|
644
660
|
) : null,
|
|
645
|
-
showLinks ?
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
661
|
+
showLinks ? (() => {
|
|
662
|
+
const links = items.map((item) => /* @__PURE__ */ jsx(
|
|
663
|
+
NavBarLink,
|
|
664
|
+
{
|
|
665
|
+
collapsed,
|
|
666
|
+
colors: linkColors,
|
|
667
|
+
iconSize: NAV_ICON_SIZE,
|
|
668
|
+
isActive: isRouteActive(pathname, item.route),
|
|
669
|
+
item,
|
|
670
|
+
navigateHint,
|
|
671
|
+
reducedMotion,
|
|
672
|
+
onPress: handlePress
|
|
673
|
+
},
|
|
674
|
+
item.key
|
|
675
|
+
));
|
|
676
|
+
return collapsed ? /* @__PURE__ */ jsx(View, { nativeID: LINKS_REGION_ID, style: navBarStyles.linksStacked, testID: NAV_TEST_IDS.navBarLinks, children: links }) : /* @__PURE__ */ jsx(
|
|
677
|
+
ScrollView,
|
|
678
|
+
{
|
|
679
|
+
horizontal: true,
|
|
680
|
+
showsHorizontalScrollIndicator: false,
|
|
681
|
+
nativeID: LINKS_REGION_ID,
|
|
682
|
+
style: navBarStyles.linksScroll,
|
|
683
|
+
contentContainerStyle: navBarStyles.links,
|
|
684
|
+
testID: NAV_TEST_IDS.navBarLinks,
|
|
685
|
+
children: links
|
|
686
|
+
}
|
|
687
|
+
);
|
|
688
|
+
})() : null,
|
|
667
689
|
showLinks && right !== void 0 ? /* @__PURE__ */ jsx(View, { style: collapsed ? navBarStyles.rightStacked : navBarStyles.right, children: right }) : null
|
|
668
690
|
] })
|
|
669
691
|
}
|
|
670
692
|
);
|
|
671
693
|
};
|
|
694
|
+
var MOBILE_BREAKPOINT = 768;
|
|
695
|
+
var SCRIM_COLOR = "rgba(0, 0, 0, 0.5)";
|
|
696
|
+
var MENU_GLYPH2 = "\u2630";
|
|
697
|
+
var MENU_MIN_TARGET = 44;
|
|
698
|
+
var MENU_GLYPH_FONT_SIZE = 20;
|
|
699
|
+
var MENU_TOGGLE_PADDING = 12;
|
|
700
|
+
var DRAWER_Z_INDEX = 50;
|
|
701
|
+
var NAV_ACTIVATABLE_SELECTOR = 'a, [role="button"], [role="link"]';
|
|
702
|
+
var DEFAULT_DRAWER_LABELS = {
|
|
703
|
+
openLabel: "Menu",
|
|
704
|
+
openHint: "Open the navigation menu",
|
|
705
|
+
closeLabel: "Close menu",
|
|
706
|
+
closeHint: "Close the navigation menu"
|
|
707
|
+
};
|
|
708
|
+
var styles = StyleSheet.create({
|
|
709
|
+
overlay: { ...StyleSheet.absoluteFillObject, zIndex: DRAWER_Z_INDEX },
|
|
710
|
+
scrim: { ...StyleSheet.absoluteFillObject, backgroundColor: SCRIM_COLOR },
|
|
711
|
+
panel: { position: "absolute", top: 0, bottom: 0, left: 0 },
|
|
712
|
+
menuToggle: {
|
|
713
|
+
minWidth: MENU_MIN_TARGET,
|
|
714
|
+
minHeight: MENU_MIN_TARGET,
|
|
715
|
+
alignItems: "center",
|
|
716
|
+
justifyContent: "center",
|
|
717
|
+
paddingHorizontal: MENU_TOGGLE_PADDING
|
|
718
|
+
},
|
|
719
|
+
menuToggleGlyph: { fontSize: MENU_GLYPH_FONT_SIZE, fontWeight: "700" }
|
|
720
|
+
});
|
|
721
|
+
var MenuToggle = ({ label, hint, onOpen, testID }) => {
|
|
722
|
+
const { theme } = useUi();
|
|
723
|
+
return /* @__PURE__ */ jsx(
|
|
724
|
+
FocusableTouchable,
|
|
725
|
+
{
|
|
726
|
+
accessibilityHint: hint,
|
|
727
|
+
accessibilityLabel: label,
|
|
728
|
+
ringColor: theme.palette.primary["500"],
|
|
729
|
+
style: styles.menuToggle,
|
|
730
|
+
testID,
|
|
731
|
+
onPress: onOpen,
|
|
732
|
+
children: /* @__PURE__ */ jsx(Text, { style: [styles.menuToggleGlyph, { color: theme.colors.text }], children: MENU_GLYPH2 })
|
|
733
|
+
}
|
|
734
|
+
);
|
|
735
|
+
};
|
|
736
|
+
var MobileDrawer = ({
|
|
737
|
+
sidebar,
|
|
738
|
+
labels,
|
|
739
|
+
onClose,
|
|
740
|
+
drawerTestID,
|
|
741
|
+
scrimTestID
|
|
742
|
+
}) => {
|
|
743
|
+
const panelRef = useRef(null);
|
|
744
|
+
useEffect(() => {
|
|
745
|
+
const node = panelRef.current;
|
|
746
|
+
if (node === null || typeof node.addEventListener !== "function") return void 0;
|
|
747
|
+
const handleClick = (event) => {
|
|
748
|
+
const target = event.target;
|
|
749
|
+
const onNavItem = target !== null && target.closest(NAV_ACTIVATABLE_SELECTOR) !== null;
|
|
750
|
+
if (onNavItem) onClose();
|
|
751
|
+
};
|
|
752
|
+
node.addEventListener("click", handleClick);
|
|
753
|
+
return () => node.removeEventListener("click", handleClick);
|
|
754
|
+
}, [onClose]);
|
|
755
|
+
return /* @__PURE__ */ jsxs(View, { style: styles.overlay, children: [
|
|
756
|
+
/* @__PURE__ */ jsx(
|
|
757
|
+
Pressable,
|
|
758
|
+
{
|
|
759
|
+
accessibilityHint: labels.closeHint,
|
|
760
|
+
accessibilityLabel: labels.closeLabel,
|
|
761
|
+
accessibilityRole: "button",
|
|
762
|
+
style: styles.scrim,
|
|
763
|
+
testID: scrimTestID,
|
|
764
|
+
onPress: onClose
|
|
765
|
+
}
|
|
766
|
+
),
|
|
767
|
+
/* @__PURE__ */ jsx(View, { ref: panelRef, "aria-modal": true, role: "dialog", style: styles.panel, testID: drawerTestID, children: sidebar })
|
|
768
|
+
] });
|
|
769
|
+
};
|
|
672
770
|
function resolveContentMaxWidth(width, viewport) {
|
|
673
771
|
if (width === "full") return "full";
|
|
674
772
|
const wideMinViewport = width.wideMinViewport ?? Number.POSITIVE_INFINITY;
|
|
@@ -686,11 +784,14 @@ var CARD_BORDER_WIDTH = 1;
|
|
|
686
784
|
var CARD_TITLE_FONT_SIZE = 16;
|
|
687
785
|
var CARD_MESSAGE_FONT_SIZE = 14;
|
|
688
786
|
var CARD_TITLE_MARGIN_BOTTOM = 8;
|
|
689
|
-
var
|
|
787
|
+
var styles2 = StyleSheet.create({
|
|
690
788
|
root: { flex: 1 },
|
|
691
789
|
centerFill: { flex: 1, justifyContent: "center", alignItems: "center" },
|
|
692
790
|
scroll: { flex: 1 },
|
|
693
791
|
scrollContent: { flexGrow: 1 },
|
|
792
|
+
// Mobile back-office header: the hamburger sits inline before the header slot.
|
|
793
|
+
headerRow: { flexDirection: "row", alignItems: "center" },
|
|
794
|
+
headerFill: { flex: 1 },
|
|
694
795
|
// Back-office layout: a persistent left rail beside the scrolling content column.
|
|
695
796
|
bodyRow: { flex: 1, flexDirection: "row" },
|
|
696
797
|
columnFull: { flex: 1 },
|
|
@@ -716,11 +817,11 @@ function MessageCard({
|
|
|
716
817
|
return /* @__PURE__ */ jsxs(
|
|
717
818
|
View,
|
|
718
819
|
{
|
|
719
|
-
style: [
|
|
820
|
+
style: [styles2.card, { backgroundColor: colors.surface, borderColor: accentColor }],
|
|
720
821
|
testID,
|
|
721
822
|
children: [
|
|
722
|
-
/* @__PURE__ */ jsx(Text, { style: [
|
|
723
|
-
/* @__PURE__ */ jsx(Text, { style: [
|
|
823
|
+
/* @__PURE__ */ jsx(Text, { style: [styles2.cardTitle, { color: accentColor }], children: message.titleText }),
|
|
824
|
+
/* @__PURE__ */ jsx(Text, { style: [styles2.cardMessage, { color: colors.textSecondary }], children: message.messageText })
|
|
724
825
|
]
|
|
725
826
|
}
|
|
726
827
|
);
|
|
@@ -734,13 +835,14 @@ function useContentBody(state, children, testID) {
|
|
|
734
835
|
if (state?.error)
|
|
735
836
|
return /* @__PURE__ */ jsx(MessageCard, { accentColor: errorColor, message: state.error, testID: `${testID}${APP_SHELL_SUFFIX.error}` });
|
|
736
837
|
if (state?.loading === true)
|
|
737
|
-
return /* @__PURE__ */ jsx(View, { style:
|
|
838
|
+
return /* @__PURE__ */ jsx(View, { style: styles2.centerFill, testID: `${testID}${APP_SHELL_SUFFIX.loading}`, children: /* @__PURE__ */ jsx(ActivityIndicator, { color: primary, size: "large" }) });
|
|
738
839
|
return children;
|
|
739
840
|
}
|
|
740
841
|
var AppShell = ({
|
|
741
842
|
header,
|
|
742
843
|
nav,
|
|
743
844
|
sidebar,
|
|
845
|
+
mobileMenu,
|
|
744
846
|
banner,
|
|
745
847
|
width = "full",
|
|
746
848
|
contentPadding = DEFAULT_CONTENT_PADDING,
|
|
@@ -754,32 +856,65 @@ var AppShell = ({
|
|
|
754
856
|
const primary = theme.palette.primary["500"];
|
|
755
857
|
const maxWidth = useContentMaxWidth(width);
|
|
756
858
|
const body = useContentBody(state, children, testID);
|
|
859
|
+
const { width: viewportWidth } = useWindowDimensions();
|
|
860
|
+
const isNarrow = viewportWidth < MOBILE_BREAKPOINT;
|
|
861
|
+
const useDrawer = sidebar !== void 0 && isNarrow;
|
|
862
|
+
const [drawerOpen, setDrawerOpen] = useState(false);
|
|
863
|
+
const openDrawer = useCallback(() => setDrawerOpen(true), []);
|
|
864
|
+
const closeDrawer = useCallback(() => setDrawerOpen(false), []);
|
|
865
|
+
useEffect(() => {
|
|
866
|
+
if (!isNarrow && drawerOpen) setDrawerOpen(false);
|
|
867
|
+
}, [isNarrow, drawerOpen]);
|
|
868
|
+
const drawerLabels = { ...DEFAULT_DRAWER_LABELS, ...mobileMenu };
|
|
757
869
|
const isUnauthenticated = gate !== void 0 && !gate.pending && !gate.authenticated;
|
|
758
870
|
useEffect(() => {
|
|
759
871
|
if (isUnauthenticated && gate !== void 0) gate.onRedirect();
|
|
760
872
|
}, [isUnauthenticated, gate]);
|
|
761
873
|
if (gate?.pending === true)
|
|
762
|
-
return /* @__PURE__ */ jsx(View, { style: [
|
|
874
|
+
return /* @__PURE__ */ jsx(View, { style: [styles2.root, styles2.centerFill, { backgroundColor: theme.colors.background }], testID: `${testID}${APP_SHELL_SUFFIX.pending}`, children: /* @__PURE__ */ jsx(ActivityIndicator, { color: primary, size: "large" }) });
|
|
763
875
|
if (isUnauthenticated) return null;
|
|
764
|
-
const columnStyle = maxWidth === "full" ?
|
|
765
|
-
const navInnerStyle = chromeAlignment === "content" && maxWidth !== "full" ? [
|
|
876
|
+
const columnStyle = maxWidth === "full" ? styles2.columnFull : [styles2.columnCapped, { maxWidth }];
|
|
877
|
+
const navInnerStyle = chromeAlignment === "content" && maxWidth !== "full" ? [styles2.chromeCapped, { maxWidth, paddingHorizontal: contentPadding }] : void 0;
|
|
766
878
|
const scroller = /* @__PURE__ */ jsx(
|
|
767
879
|
ScrollView,
|
|
768
880
|
{
|
|
769
|
-
contentContainerStyle: [
|
|
770
|
-
style:
|
|
881
|
+
contentContainerStyle: [styles2.scrollContent, { padding: contentPadding }],
|
|
882
|
+
style: styles2.scroll,
|
|
771
883
|
testID: `${testID}${APP_SHELL_SUFFIX.content}`,
|
|
772
884
|
children: /* @__PURE__ */ jsx(View, { style: columnStyle, children: body })
|
|
773
885
|
}
|
|
774
886
|
);
|
|
775
|
-
|
|
776
|
-
/* @__PURE__ */ jsx(
|
|
887
|
+
const headerRegion = useDrawer ? /* @__PURE__ */ jsxs(View, { style: styles2.headerRow, testID: `${testID}${APP_SHELL_SUFFIX.header}`, children: [
|
|
888
|
+
/* @__PURE__ */ jsx(
|
|
889
|
+
MenuToggle,
|
|
890
|
+
{
|
|
891
|
+
hint: drawerLabels.openHint,
|
|
892
|
+
label: drawerLabels.openLabel,
|
|
893
|
+
testID: `${testID}${APP_SHELL_SUFFIX.menuToggle}`,
|
|
894
|
+
onOpen: openDrawer
|
|
895
|
+
}
|
|
896
|
+
),
|
|
897
|
+
/* @__PURE__ */ jsx(View, { style: styles2.headerFill, children: header })
|
|
898
|
+
] }) : /* @__PURE__ */ jsx(View, { testID: `${testID}${APP_SHELL_SUFFIX.header}`, children: header });
|
|
899
|
+
const showRail = sidebar !== void 0 && !isNarrow;
|
|
900
|
+
return /* @__PURE__ */ jsxs(View, { style: [styles2.root, { backgroundColor: theme.colors.background }], testID, children: [
|
|
901
|
+
headerRegion,
|
|
777
902
|
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,
|
|
778
903
|
banner !== void 0 ? /* @__PURE__ */ jsx(View, { testID: `${testID}${APP_SHELL_SUFFIX.banner}`, children: banner }) : null,
|
|
779
|
-
|
|
904
|
+
showRail ? /* @__PURE__ */ jsxs(View, { style: styles2.bodyRow, children: [
|
|
780
905
|
/* @__PURE__ */ jsx(View, { testID: `${testID}${APP_SHELL_SUFFIX.sidebar}`, children: sidebar }),
|
|
781
906
|
scroller
|
|
782
|
-
] }) : scroller
|
|
907
|
+
] }) : scroller,
|
|
908
|
+
useDrawer && drawerOpen ? /* @__PURE__ */ jsx(
|
|
909
|
+
MobileDrawer,
|
|
910
|
+
{
|
|
911
|
+
drawerTestID: `${testID}${APP_SHELL_SUFFIX.drawer}`,
|
|
912
|
+
labels: drawerLabels,
|
|
913
|
+
scrimTestID: `${testID}${APP_SHELL_SUFFIX.scrim}`,
|
|
914
|
+
sidebar,
|
|
915
|
+
onClose: closeDrawer
|
|
916
|
+
}
|
|
917
|
+
) : null
|
|
783
918
|
] });
|
|
784
919
|
};
|
|
785
920
|
function roleRoutesToNavItems(routes, translate) {
|