@dloizides/ui-nav 1.3.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 +90 -45
- package/dist/index.d.mts +60 -1
- package/dist/index.d.ts +60 -1
- package/dist/index.js +145 -17
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +146 -18
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { StyleSheet, Platform, TouchableOpacity, View, Text, useWindowDimensions, Pressable, ActivityIndicator, ScrollView } 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
|
|
|
@@ -344,6 +344,14 @@ var APP_SHELL_SUFFIX = {
|
|
|
344
344
|
content: "-content",
|
|
345
345
|
header: "-header",
|
|
346
346
|
nav: "-nav",
|
|
347
|
+
/** The persistent left rail of the back-office layout (only when `sidebar` is supplied). */
|
|
348
|
+
sidebar: "-sidebar",
|
|
349
|
+
/** Mobile hamburger that opens the nav drawer (back-office layout, below the breakpoint). */
|
|
350
|
+
menuToggle: "-menu-toggle",
|
|
351
|
+
/** The mobile nav drawer panel (holds the `sidebar` slot when opened on a narrow viewport). */
|
|
352
|
+
drawer: "-drawer",
|
|
353
|
+
/** The mobile nav drawer scrim (tap to close). */
|
|
354
|
+
scrim: "-drawer-scrim",
|
|
347
355
|
banner: "-banner",
|
|
348
356
|
pending: "-pending",
|
|
349
357
|
loading: "-loading",
|
|
@@ -667,6 +675,82 @@ var NavBar = ({
|
|
|
667
675
|
}
|
|
668
676
|
);
|
|
669
677
|
};
|
|
678
|
+
var MOBILE_BREAKPOINT = 768;
|
|
679
|
+
var SCRIM_COLOR = "rgba(0, 0, 0, 0.5)";
|
|
680
|
+
var MENU_GLYPH2 = "\u2630";
|
|
681
|
+
var MENU_MIN_TARGET = 44;
|
|
682
|
+
var MENU_GLYPH_FONT_SIZE = 20;
|
|
683
|
+
var MENU_TOGGLE_PADDING = 12;
|
|
684
|
+
var DRAWER_Z_INDEX = 50;
|
|
685
|
+
var NAV_ACTIVATABLE_SELECTOR = 'a, [role="button"], [role="link"]';
|
|
686
|
+
var DEFAULT_DRAWER_LABELS = {
|
|
687
|
+
openLabel: "Menu",
|
|
688
|
+
openHint: "Open the navigation menu",
|
|
689
|
+
closeLabel: "Close menu",
|
|
690
|
+
closeHint: "Close the navigation menu"
|
|
691
|
+
};
|
|
692
|
+
var styles = StyleSheet.create({
|
|
693
|
+
overlay: { ...StyleSheet.absoluteFillObject, zIndex: DRAWER_Z_INDEX },
|
|
694
|
+
scrim: { ...StyleSheet.absoluteFillObject, backgroundColor: SCRIM_COLOR },
|
|
695
|
+
panel: { position: "absolute", top: 0, bottom: 0, left: 0 },
|
|
696
|
+
menuToggle: {
|
|
697
|
+
minWidth: MENU_MIN_TARGET,
|
|
698
|
+
minHeight: MENU_MIN_TARGET,
|
|
699
|
+
alignItems: "center",
|
|
700
|
+
justifyContent: "center",
|
|
701
|
+
paddingHorizontal: MENU_TOGGLE_PADDING
|
|
702
|
+
},
|
|
703
|
+
menuToggleGlyph: { fontSize: MENU_GLYPH_FONT_SIZE, fontWeight: "700" }
|
|
704
|
+
});
|
|
705
|
+
var MenuToggle = ({ label, hint, onOpen, testID }) => {
|
|
706
|
+
const { theme } = useUi();
|
|
707
|
+
return /* @__PURE__ */ jsx(
|
|
708
|
+
FocusableTouchable,
|
|
709
|
+
{
|
|
710
|
+
accessibilityHint: hint,
|
|
711
|
+
accessibilityLabel: label,
|
|
712
|
+
ringColor: theme.palette.primary["500"],
|
|
713
|
+
style: styles.menuToggle,
|
|
714
|
+
testID,
|
|
715
|
+
onPress: onOpen,
|
|
716
|
+
children: /* @__PURE__ */ jsx(Text, { style: [styles.menuToggleGlyph, { color: theme.colors.text }], children: MENU_GLYPH2 })
|
|
717
|
+
}
|
|
718
|
+
);
|
|
719
|
+
};
|
|
720
|
+
var MobileDrawer = ({
|
|
721
|
+
sidebar,
|
|
722
|
+
labels,
|
|
723
|
+
onClose,
|
|
724
|
+
drawerTestID,
|
|
725
|
+
scrimTestID
|
|
726
|
+
}) => {
|
|
727
|
+
const panelRef = useRef(null);
|
|
728
|
+
useEffect(() => {
|
|
729
|
+
const node = panelRef.current;
|
|
730
|
+
if (node === null || typeof node.addEventListener !== "function") return void 0;
|
|
731
|
+
const handleClick = (event) => {
|
|
732
|
+
const target = event.target;
|
|
733
|
+
const onNavItem = target !== null && target.closest(NAV_ACTIVATABLE_SELECTOR) !== null;
|
|
734
|
+
if (onNavItem) onClose();
|
|
735
|
+
};
|
|
736
|
+
node.addEventListener("click", handleClick);
|
|
737
|
+
return () => node.removeEventListener("click", handleClick);
|
|
738
|
+
}, [onClose]);
|
|
739
|
+
return /* @__PURE__ */ jsxs(View, { style: styles.overlay, children: [
|
|
740
|
+
/* @__PURE__ */ jsx(
|
|
741
|
+
Pressable,
|
|
742
|
+
{
|
|
743
|
+
accessibilityHint: labels.closeHint,
|
|
744
|
+
accessibilityLabel: labels.closeLabel,
|
|
745
|
+
accessibilityRole: "button",
|
|
746
|
+
style: styles.scrim,
|
|
747
|
+
testID: scrimTestID,
|
|
748
|
+
onPress: onClose
|
|
749
|
+
}
|
|
750
|
+
),
|
|
751
|
+
/* @__PURE__ */ jsx(View, { ref: panelRef, "aria-modal": true, role: "dialog", style: styles.panel, testID: drawerTestID, children: sidebar })
|
|
752
|
+
] });
|
|
753
|
+
};
|
|
670
754
|
function resolveContentMaxWidth(width, viewport) {
|
|
671
755
|
if (width === "full") return "full";
|
|
672
756
|
const wideMinViewport = width.wideMinViewport ?? Number.POSITIVE_INFINITY;
|
|
@@ -684,11 +768,16 @@ var CARD_BORDER_WIDTH = 1;
|
|
|
684
768
|
var CARD_TITLE_FONT_SIZE = 16;
|
|
685
769
|
var CARD_MESSAGE_FONT_SIZE = 14;
|
|
686
770
|
var CARD_TITLE_MARGIN_BOTTOM = 8;
|
|
687
|
-
var
|
|
771
|
+
var styles2 = StyleSheet.create({
|
|
688
772
|
root: { flex: 1 },
|
|
689
773
|
centerFill: { flex: 1, justifyContent: "center", alignItems: "center" },
|
|
690
774
|
scroll: { flex: 1 },
|
|
691
775
|
scrollContent: { flexGrow: 1 },
|
|
776
|
+
// Mobile back-office header: the hamburger sits inline before the header slot.
|
|
777
|
+
headerRow: { flexDirection: "row", alignItems: "center" },
|
|
778
|
+
headerFill: { flex: 1 },
|
|
779
|
+
// Back-office layout: a persistent left rail beside the scrolling content column.
|
|
780
|
+
bodyRow: { flex: 1, flexDirection: "row" },
|
|
692
781
|
columnFull: { flex: 1 },
|
|
693
782
|
columnCapped: { flex: 1, width: "100%", alignSelf: "center" },
|
|
694
783
|
// Non-flex variant used to cap the nav strip's inner content to the content
|
|
@@ -712,11 +801,11 @@ function MessageCard({
|
|
|
712
801
|
return /* @__PURE__ */ jsxs(
|
|
713
802
|
View,
|
|
714
803
|
{
|
|
715
|
-
style: [
|
|
804
|
+
style: [styles2.card, { backgroundColor: colors.surface, borderColor: accentColor }],
|
|
716
805
|
testID,
|
|
717
806
|
children: [
|
|
718
|
-
/* @__PURE__ */ jsx(Text, { style: [
|
|
719
|
-
/* @__PURE__ */ jsx(Text, { style: [
|
|
807
|
+
/* @__PURE__ */ jsx(Text, { style: [styles2.cardTitle, { color: accentColor }], children: message.titleText }),
|
|
808
|
+
/* @__PURE__ */ jsx(Text, { style: [styles2.cardMessage, { color: colors.textSecondary }], children: message.messageText })
|
|
720
809
|
]
|
|
721
810
|
}
|
|
722
811
|
);
|
|
@@ -730,12 +819,14 @@ function useContentBody(state, children, testID) {
|
|
|
730
819
|
if (state?.error)
|
|
731
820
|
return /* @__PURE__ */ jsx(MessageCard, { accentColor: errorColor, message: state.error, testID: `${testID}${APP_SHELL_SUFFIX.error}` });
|
|
732
821
|
if (state?.loading === true)
|
|
733
|
-
return /* @__PURE__ */ jsx(View, { style:
|
|
822
|
+
return /* @__PURE__ */ jsx(View, { style: styles2.centerFill, testID: `${testID}${APP_SHELL_SUFFIX.loading}`, children: /* @__PURE__ */ jsx(ActivityIndicator, { color: primary, size: "large" }) });
|
|
734
823
|
return children;
|
|
735
824
|
}
|
|
736
825
|
var AppShell = ({
|
|
737
826
|
header,
|
|
738
827
|
nav,
|
|
828
|
+
sidebar,
|
|
829
|
+
mobileMenu,
|
|
739
830
|
banner,
|
|
740
831
|
width = "full",
|
|
741
832
|
contentPadding = DEFAULT_CONTENT_PADDING,
|
|
@@ -749,28 +840,65 @@ var AppShell = ({
|
|
|
749
840
|
const primary = theme.palette.primary["500"];
|
|
750
841
|
const maxWidth = useContentMaxWidth(width);
|
|
751
842
|
const body = useContentBody(state, children, testID);
|
|
843
|
+
const { width: viewportWidth } = useWindowDimensions();
|
|
844
|
+
const isNarrow = viewportWidth < MOBILE_BREAKPOINT;
|
|
845
|
+
const useDrawer = sidebar !== void 0 && isNarrow;
|
|
846
|
+
const [drawerOpen, setDrawerOpen] = useState(false);
|
|
847
|
+
const openDrawer = useCallback(() => setDrawerOpen(true), []);
|
|
848
|
+
const closeDrawer = useCallback(() => setDrawerOpen(false), []);
|
|
849
|
+
useEffect(() => {
|
|
850
|
+
if (!isNarrow && drawerOpen) setDrawerOpen(false);
|
|
851
|
+
}, [isNarrow, drawerOpen]);
|
|
852
|
+
const drawerLabels = { ...DEFAULT_DRAWER_LABELS, ...mobileMenu };
|
|
752
853
|
const isUnauthenticated = gate !== void 0 && !gate.pending && !gate.authenticated;
|
|
753
854
|
useEffect(() => {
|
|
754
855
|
if (isUnauthenticated && gate !== void 0) gate.onRedirect();
|
|
755
856
|
}, [isUnauthenticated, gate]);
|
|
756
857
|
if (gate?.pending === true)
|
|
757
|
-
return /* @__PURE__ */ jsx(View, { style: [
|
|
858
|
+
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" }) });
|
|
758
859
|
if (isUnauthenticated) return null;
|
|
759
|
-
const columnStyle = maxWidth === "full" ?
|
|
760
|
-
const navInnerStyle = chromeAlignment === "content" && maxWidth !== "full" ? [
|
|
761
|
-
|
|
762
|
-
|
|
860
|
+
const columnStyle = maxWidth === "full" ? styles2.columnFull : [styles2.columnCapped, { maxWidth }];
|
|
861
|
+
const navInnerStyle = chromeAlignment === "content" && maxWidth !== "full" ? [styles2.chromeCapped, { maxWidth, paddingHorizontal: contentPadding }] : void 0;
|
|
862
|
+
const scroller = /* @__PURE__ */ jsx(
|
|
863
|
+
ScrollView,
|
|
864
|
+
{
|
|
865
|
+
contentContainerStyle: [styles2.scrollContent, { padding: contentPadding }],
|
|
866
|
+
style: styles2.scroll,
|
|
867
|
+
testID: `${testID}${APP_SHELL_SUFFIX.content}`,
|
|
868
|
+
children: /* @__PURE__ */ jsx(View, { style: columnStyle, children: body })
|
|
869
|
+
}
|
|
870
|
+
);
|
|
871
|
+
const headerRegion = useDrawer ? /* @__PURE__ */ jsxs(View, { style: styles2.headerRow, testID: `${testID}${APP_SHELL_SUFFIX.header}`, children: [
|
|
872
|
+
/* @__PURE__ */ jsx(
|
|
873
|
+
MenuToggle,
|
|
874
|
+
{
|
|
875
|
+
hint: drawerLabels.openHint,
|
|
876
|
+
label: drawerLabels.openLabel,
|
|
877
|
+
testID: `${testID}${APP_SHELL_SUFFIX.menuToggle}`,
|
|
878
|
+
onOpen: openDrawer
|
|
879
|
+
}
|
|
880
|
+
),
|
|
881
|
+
/* @__PURE__ */ jsx(View, { style: styles2.headerFill, children: header })
|
|
882
|
+
] }) : /* @__PURE__ */ jsx(View, { testID: `${testID}${APP_SHELL_SUFFIX.header}`, children: header });
|
|
883
|
+
const showRail = sidebar !== void 0 && !isNarrow;
|
|
884
|
+
return /* @__PURE__ */ jsxs(View, { style: [styles2.root, { backgroundColor: theme.colors.background }], testID, children: [
|
|
885
|
+
headerRegion,
|
|
763
886
|
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,
|
|
764
887
|
banner !== void 0 ? /* @__PURE__ */ jsx(View, { testID: `${testID}${APP_SHELL_SUFFIX.banner}`, children: banner }) : null,
|
|
765
|
-
/* @__PURE__ */
|
|
766
|
-
|
|
888
|
+
showRail ? /* @__PURE__ */ jsxs(View, { style: styles2.bodyRow, children: [
|
|
889
|
+
/* @__PURE__ */ jsx(View, { testID: `${testID}${APP_SHELL_SUFFIX.sidebar}`, children: sidebar }),
|
|
890
|
+
scroller
|
|
891
|
+
] }) : scroller,
|
|
892
|
+
useDrawer && drawerOpen ? /* @__PURE__ */ jsx(
|
|
893
|
+
MobileDrawer,
|
|
767
894
|
{
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
895
|
+
drawerTestID: `${testID}${APP_SHELL_SUFFIX.drawer}`,
|
|
896
|
+
labels: drawerLabels,
|
|
897
|
+
scrimTestID: `${testID}${APP_SHELL_SUFFIX.scrim}`,
|
|
898
|
+
sidebar,
|
|
899
|
+
onClose: closeDrawer
|
|
772
900
|
}
|
|
773
|
-
)
|
|
901
|
+
) : null
|
|
774
902
|
] });
|
|
775
903
|
};
|
|
776
904
|
function roleRoutesToNavItems(routes, translate) {
|