@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/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
|
|
|
@@ -346,6 +346,12 @@ var APP_SHELL_SUFFIX = {
|
|
|
346
346
|
nav: "-nav",
|
|
347
347
|
/** The persistent left rail of the back-office layout (only when `sidebar` is supplied). */
|
|
348
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",
|
|
349
355
|
banner: "-banner",
|
|
350
356
|
pending: "-pending",
|
|
351
357
|
loading: "-loading",
|
|
@@ -669,6 +675,82 @@ var NavBar = ({
|
|
|
669
675
|
}
|
|
670
676
|
);
|
|
671
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
|
+
};
|
|
672
754
|
function resolveContentMaxWidth(width, viewport) {
|
|
673
755
|
if (width === "full") return "full";
|
|
674
756
|
const wideMinViewport = width.wideMinViewport ?? Number.POSITIVE_INFINITY;
|
|
@@ -686,11 +768,14 @@ var CARD_BORDER_WIDTH = 1;
|
|
|
686
768
|
var CARD_TITLE_FONT_SIZE = 16;
|
|
687
769
|
var CARD_MESSAGE_FONT_SIZE = 14;
|
|
688
770
|
var CARD_TITLE_MARGIN_BOTTOM = 8;
|
|
689
|
-
var
|
|
771
|
+
var styles2 = StyleSheet.create({
|
|
690
772
|
root: { flex: 1 },
|
|
691
773
|
centerFill: { flex: 1, justifyContent: "center", alignItems: "center" },
|
|
692
774
|
scroll: { flex: 1 },
|
|
693
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 },
|
|
694
779
|
// Back-office layout: a persistent left rail beside the scrolling content column.
|
|
695
780
|
bodyRow: { flex: 1, flexDirection: "row" },
|
|
696
781
|
columnFull: { flex: 1 },
|
|
@@ -716,11 +801,11 @@ function MessageCard({
|
|
|
716
801
|
return /* @__PURE__ */ jsxs(
|
|
717
802
|
View,
|
|
718
803
|
{
|
|
719
|
-
style: [
|
|
804
|
+
style: [styles2.card, { backgroundColor: colors.surface, borderColor: accentColor }],
|
|
720
805
|
testID,
|
|
721
806
|
children: [
|
|
722
|
-
/* @__PURE__ */ jsx(Text, { style: [
|
|
723
|
-
/* @__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 })
|
|
724
809
|
]
|
|
725
810
|
}
|
|
726
811
|
);
|
|
@@ -734,13 +819,14 @@ function useContentBody(state, children, testID) {
|
|
|
734
819
|
if (state?.error)
|
|
735
820
|
return /* @__PURE__ */ jsx(MessageCard, { accentColor: errorColor, message: state.error, testID: `${testID}${APP_SHELL_SUFFIX.error}` });
|
|
736
821
|
if (state?.loading === true)
|
|
737
|
-
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" }) });
|
|
738
823
|
return children;
|
|
739
824
|
}
|
|
740
825
|
var AppShell = ({
|
|
741
826
|
header,
|
|
742
827
|
nav,
|
|
743
828
|
sidebar,
|
|
829
|
+
mobileMenu,
|
|
744
830
|
banner,
|
|
745
831
|
width = "full",
|
|
746
832
|
contentPadding = DEFAULT_CONTENT_PADDING,
|
|
@@ -754,32 +840,65 @@ var AppShell = ({
|
|
|
754
840
|
const primary = theme.palette.primary["500"];
|
|
755
841
|
const maxWidth = useContentMaxWidth(width);
|
|
756
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 };
|
|
757
853
|
const isUnauthenticated = gate !== void 0 && !gate.pending && !gate.authenticated;
|
|
758
854
|
useEffect(() => {
|
|
759
855
|
if (isUnauthenticated && gate !== void 0) gate.onRedirect();
|
|
760
856
|
}, [isUnauthenticated, gate]);
|
|
761
857
|
if (gate?.pending === true)
|
|
762
|
-
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" }) });
|
|
763
859
|
if (isUnauthenticated) return null;
|
|
764
|
-
const columnStyle = maxWidth === "full" ?
|
|
765
|
-
const navInnerStyle = chromeAlignment === "content" && maxWidth !== "full" ? [
|
|
860
|
+
const columnStyle = maxWidth === "full" ? styles2.columnFull : [styles2.columnCapped, { maxWidth }];
|
|
861
|
+
const navInnerStyle = chromeAlignment === "content" && maxWidth !== "full" ? [styles2.chromeCapped, { maxWidth, paddingHorizontal: contentPadding }] : void 0;
|
|
766
862
|
const scroller = /* @__PURE__ */ jsx(
|
|
767
863
|
ScrollView,
|
|
768
864
|
{
|
|
769
|
-
contentContainerStyle: [
|
|
770
|
-
style:
|
|
865
|
+
contentContainerStyle: [styles2.scrollContent, { padding: contentPadding }],
|
|
866
|
+
style: styles2.scroll,
|
|
771
867
|
testID: `${testID}${APP_SHELL_SUFFIX.content}`,
|
|
772
868
|
children: /* @__PURE__ */ jsx(View, { style: columnStyle, children: body })
|
|
773
869
|
}
|
|
774
870
|
);
|
|
775
|
-
|
|
776
|
-
/* @__PURE__ */ jsx(
|
|
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,
|
|
777
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,
|
|
778
887
|
banner !== void 0 ? /* @__PURE__ */ jsx(View, { testID: `${testID}${APP_SHELL_SUFFIX.banner}`, children: banner }) : null,
|
|
779
|
-
|
|
888
|
+
showRail ? /* @__PURE__ */ jsxs(View, { style: styles2.bodyRow, children: [
|
|
780
889
|
/* @__PURE__ */ jsx(View, { testID: `${testID}${APP_SHELL_SUFFIX.sidebar}`, children: sidebar }),
|
|
781
890
|
scroller
|
|
782
|
-
] }) : scroller
|
|
891
|
+
] }) : scroller,
|
|
892
|
+
useDrawer && drawerOpen ? /* @__PURE__ */ jsx(
|
|
893
|
+
MobileDrawer,
|
|
894
|
+
{
|
|
895
|
+
drawerTestID: `${testID}${APP_SHELL_SUFFIX.drawer}`,
|
|
896
|
+
labels: drawerLabels,
|
|
897
|
+
scrimTestID: `${testID}${APP_SHELL_SUFFIX.scrim}`,
|
|
898
|
+
sidebar,
|
|
899
|
+
onClose: closeDrawer
|
|
900
|
+
}
|
|
901
|
+
) : null
|
|
783
902
|
] });
|
|
784
903
|
};
|
|
785
904
|
function roleRoutesToNavItems(routes, translate) {
|