@dloizides/ui-nav 1.37.0 → 1.38.1

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/README.md CHANGED
@@ -153,17 +153,17 @@ import { NavShell, type NavItem } from '@dloizides/ui-nav';
153
153
 
154
154
  ### Brand position, rail width and phone drawer (`NavShell`, NAV-BRAND-1)
155
155
 
156
- One prop per choice; every default keeps today's layout.
156
+ One `brand` prop holds every choice; every default (`NAV_BRAND_DEFAULTS`) keeps today's layout.
157
157
 
158
- | Prop | Values | Default |
158
+ | `brand` field | Values | Default |
159
159
  |---|---|---|
160
- | `brandPlacement` | `'sidebar'` (rail top) · `'topbar'` (top-left of the header, >= 768 px) | `'sidebar'` |
161
- | `sidebarWidth` | `'default'` 220 · `'sm'` 240 · `'md'` 264 · `'lg'` 288 px (`SIDEBAR_WIDTH_PX`) | `'default'` |
160
+ | `placement` | `'sidebar'` (rail top) · `'topbar'` (top-left of the header, >= 768 px, same background and bottom border as the top bar) | `'sidebar'` |
161
+ | `railWidth` | `'default'` 220 · `'sm'` 240 · `'md'` 264 · `'lg'` 288 px (`SIDEBAR_WIDTH_PX`) | `'default'` |
162
162
  | `drawerHeader` | `'none'` · `'brand'` (brand once in the open drawer's header, with a close button) | `'none'` |
163
163
  | `drawerWidth` | `'default'` (280 px panel + scrim) · `'full'` (whole screen, close button) | `'default'` |
164
164
 
165
165
  ```tsx
166
- <NavShell layout="side" brandPlacement="topbar" sidebarWidth="sm" drawerHeader="brand" drawerWidth="full"
166
+ <NavShell layout="side" brand={{ placement: 'topbar', railWidth: 'sm', drawerHeader: 'brand', drawerWidth: 'full' }}
167
167
  sideRail={{ items, title, brand: <AppBrand /> }} header={<Topbar ... />} {...rest} />
168
168
  ```
169
169
 
package/dist/index.d.mts CHANGED
@@ -53,10 +53,10 @@ type DrawerHeader = 'none' | 'brand';
53
53
  type DrawerWidth = 'default' | 'full';
54
54
  /** Pixel width of each named side-rail width token. */
55
55
  declare const SIDEBAR_WIDTH_PX: Readonly<Record<SidebarWidth, number>>;
56
- /** Shell brand and drawer options; each is one prop with a default that keeps today's layout. */
56
+ /** Fields of NavShell's one `brand` prop; each has a default that keeps today's layout. */
57
57
  interface NavBrandOptions {
58
- brandPlacement?: BrandPlacement;
59
- sidebarWidth?: SidebarWidth;
58
+ placement?: BrandPlacement;
59
+ railWidth?: SidebarWidth;
60
60
  drawerHeader?: DrawerHeader;
61
61
  drawerWidth?: DrawerWidth;
62
62
  }
@@ -175,8 +175,6 @@ interface SidebarProps {
175
175
  onNavigate: (route: string) => void;
176
176
  /** Localized menu title (heading); `null` leaves the slot empty. */
177
177
  title: React.ReactNode;
178
- /** Named rail width (default 220 px). */
179
- width?: SidebarWidth;
180
178
  /** Localized accessibility label for the navigation landmark. */
181
179
  regionLabel: string;
182
180
  /** a11y hint for a leaf item, given its label. Defaults to the label. */
@@ -215,7 +213,7 @@ interface SidebarProps {
215
213
  /** Extra container style overrides. */
216
214
  containerStyle?: ViewStyle | ViewStyle[];
217
215
  }
218
- declare const Sidebar: ({ items, pathname, onNavigate, title, width: railWidth, regionLabel, navigateHint, expandHint, collapseHint, renderChevron, enableInlineSearch, search, paletteTrigger, searchQuery, onSearchChange, header, footer, containerStyle, }: SidebarProps) => React.ReactElement;
216
+ declare const Sidebar: ({ items, pathname, onNavigate, title, regionLabel, navigateHint, expandHint, collapseHint, renderChevron, enableInlineSearch, search, paletteTrigger, searchQuery, onSearchChange, header, footer, containerStyle, }: SidebarProps) => React.ReactElement;
219
217
 
220
218
  /**
221
219
  * SidebarChevron — the built-in disclosure caret for an expandable sidebar
@@ -871,7 +869,9 @@ interface NavShellSideRail {
871
869
  * auth gate, state cards, banner, and width policy — passes through untouched.
872
870
  */
873
871
  type ForwardedShellProps = Omit<AppShellProps, 'header' | 'sidebar' | 'children'>;
874
- interface NavShellProps extends ForwardedShellProps, NavBrandOptions {
872
+ interface NavShellProps extends ForwardedShellProps {
873
+ /** Brand placement, rail width and phone drawer options; defaults keep today's layout. */
874
+ brand?: NavBrandOptions;
875
875
  /** Which regions to render: a top bar, a side rail, or both. */
876
876
  layout: NavShellLayout;
877
877
  /** Current active route/path — drives active highlighting on both regions. */
@@ -903,7 +903,7 @@ interface NavShellProps extends ForwardedShellProps, NavBrandOptions {
903
903
  sideRail?: NavShellSideRail;
904
904
  children: React.ReactNode;
905
905
  }
906
- declare const NavShell: ({ layout, pathname, onNavigate, regionLabel, navigateHint, topBar, header, sideRail, children, collapsedSidebar: collapsedSidebarProp, collapsedRailRange: collapsedRailRangeProp, brandPlacement, sidebarWidth, drawerHeader, drawerWidth, mobileMenu, ...shellProps }: NavShellProps) => React.ReactElement | null;
906
+ declare const NavShell: ({ layout, pathname, onNavigate, regionLabel, navigateHint, topBar, header, sideRail, children, collapsedSidebar: collapsedSidebarProp, collapsedRailRange: collapsedRailRangeProp, brand, mobileMenu, ...shellProps }: NavShellProps) => React.ReactElement | null;
907
907
 
908
908
  /**
909
909
  * CollapsedRail — the icon-only vertical rail for the intermediate (tablet) tier
package/dist/index.d.ts CHANGED
@@ -53,10 +53,10 @@ type DrawerHeader = 'none' | 'brand';
53
53
  type DrawerWidth = 'default' | 'full';
54
54
  /** Pixel width of each named side-rail width token. */
55
55
  declare const SIDEBAR_WIDTH_PX: Readonly<Record<SidebarWidth, number>>;
56
- /** Shell brand and drawer options; each is one prop with a default that keeps today's layout. */
56
+ /** Fields of NavShell's one `brand` prop; each has a default that keeps today's layout. */
57
57
  interface NavBrandOptions {
58
- brandPlacement?: BrandPlacement;
59
- sidebarWidth?: SidebarWidth;
58
+ placement?: BrandPlacement;
59
+ railWidth?: SidebarWidth;
60
60
  drawerHeader?: DrawerHeader;
61
61
  drawerWidth?: DrawerWidth;
62
62
  }
@@ -175,8 +175,6 @@ interface SidebarProps {
175
175
  onNavigate: (route: string) => void;
176
176
  /** Localized menu title (heading); `null` leaves the slot empty. */
177
177
  title: React.ReactNode;
178
- /** Named rail width (default 220 px). */
179
- width?: SidebarWidth;
180
178
  /** Localized accessibility label for the navigation landmark. */
181
179
  regionLabel: string;
182
180
  /** a11y hint for a leaf item, given its label. Defaults to the label. */
@@ -215,7 +213,7 @@ interface SidebarProps {
215
213
  /** Extra container style overrides. */
216
214
  containerStyle?: ViewStyle | ViewStyle[];
217
215
  }
218
- declare const Sidebar: ({ items, pathname, onNavigate, title, width: railWidth, regionLabel, navigateHint, expandHint, collapseHint, renderChevron, enableInlineSearch, search, paletteTrigger, searchQuery, onSearchChange, header, footer, containerStyle, }: SidebarProps) => React.ReactElement;
216
+ declare const Sidebar: ({ items, pathname, onNavigate, title, regionLabel, navigateHint, expandHint, collapseHint, renderChevron, enableInlineSearch, search, paletteTrigger, searchQuery, onSearchChange, header, footer, containerStyle, }: SidebarProps) => React.ReactElement;
219
217
 
220
218
  /**
221
219
  * SidebarChevron — the built-in disclosure caret for an expandable sidebar
@@ -871,7 +869,9 @@ interface NavShellSideRail {
871
869
  * auth gate, state cards, banner, and width policy — passes through untouched.
872
870
  */
873
871
  type ForwardedShellProps = Omit<AppShellProps, 'header' | 'sidebar' | 'children'>;
874
- interface NavShellProps extends ForwardedShellProps, NavBrandOptions {
872
+ interface NavShellProps extends ForwardedShellProps {
873
+ /** Brand placement, rail width and phone drawer options; defaults keep today's layout. */
874
+ brand?: NavBrandOptions;
875
875
  /** Which regions to render: a top bar, a side rail, or both. */
876
876
  layout: NavShellLayout;
877
877
  /** Current active route/path — drives active highlighting on both regions. */
@@ -903,7 +903,7 @@ interface NavShellProps extends ForwardedShellProps, NavBrandOptions {
903
903
  sideRail?: NavShellSideRail;
904
904
  children: React.ReactNode;
905
905
  }
906
- declare const NavShell: ({ layout, pathname, onNavigate, regionLabel, navigateHint, topBar, header, sideRail, children, collapsedSidebar: collapsedSidebarProp, collapsedRailRange: collapsedRailRangeProp, brandPlacement, sidebarWidth, drawerHeader, drawerWidth, mobileMenu, ...shellProps }: NavShellProps) => React.ReactElement | null;
906
+ declare const NavShell: ({ layout, pathname, onNavigate, regionLabel, navigateHint, topBar, header, sideRail, children, collapsedSidebar: collapsedSidebarProp, collapsedRailRange: collapsedRailRangeProp, brand, mobileMenu, ...shellProps }: NavShellProps) => React.ReactElement | null;
907
907
 
908
908
  /**
909
909
  * CollapsedRail — the icon-only vertical rail for the intermediate (tablet) tier
package/dist/index.js CHANGED
@@ -845,8 +845,8 @@ var SIDEBAR_WIDTH_PX = {
845
845
  lg: 288
846
846
  };
847
847
  var NAV_BRAND_DEFAULTS = {
848
- brandPlacement: "sidebar",
849
- sidebarWidth: "default",
848
+ placement: "sidebar",
849
+ railWidth: "default",
850
850
  drawerHeader: "none",
851
851
  drawerWidth: "default"
852
852
  };
@@ -867,7 +867,6 @@ var Sidebar = ({
867
867
  pathname,
868
868
  onNavigate,
869
869
  title,
870
- width: railWidth = "default",
871
870
  regionLabel,
872
871
  navigateHint = (label) => label,
873
872
  expandHint = "",
@@ -915,7 +914,7 @@ var Sidebar = ({
915
914
  role: "navigation",
916
915
  style: [
917
916
  navStyles.sidebarContainer,
918
- { width: SIDEBAR_WIDTH_PX[railWidth] },
917
+ { width: SIDEBAR_WIDTH_PX.default },
919
918
  { backgroundColor: colors.surface, borderRightColor: colors.border },
920
919
  containerStyle
921
920
  ],
@@ -1915,7 +1914,7 @@ var styles9 = reactNative.StyleSheet.create({
1915
1914
  // so on react-native-web it — and the nav leaves inside it — sit above the scrim
1916
1915
  // and receive presses instead of the scrim swallowing them.
1917
1916
  panel: { position: "absolute", top: 0, bottom: 0, left: 0, width: DRAWER_WIDTH, zIndex: PANEL_Z_INDEX },
1918
- panelFull: { width: "100%" },
1917
+ windowFill: { right: void 0, bottom: void 0 },
1919
1918
  panelBody: { flex: 1 },
1920
1919
  menuToggle: {
1921
1920
  minWidth: MIN_TOUCH_TARGET,
@@ -1953,6 +1952,8 @@ var MobileDrawer = ({
1953
1952
  const { theme } = uiFeedback.useUi();
1954
1953
  const panelRef = React.useRef(null);
1955
1954
  const isFull = width === "full";
1955
+ const viewport = reactNative.useWindowDimensions();
1956
+ const windowSize = isFull ? [styles9.windowFill, { width: viewport.width, height: viewport.height }] : null;
1956
1957
  const showHeaderBar = header !== void 0 || isFull;
1957
1958
  React.useEffect(() => {
1958
1959
  const node = panelRef.current;
@@ -1967,7 +1968,7 @@ var MobileDrawer = ({
1967
1968
  node.addEventListener("click", handleClick);
1968
1969
  return () => node.removeEventListener("click", handleClick);
1969
1970
  }, [onClose]);
1970
- return /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: styles9.overlay, children: [
1971
+ return /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: [styles9.overlay, windowSize], children: [
1971
1972
  isFull ? null : /* @__PURE__ */ jsxRuntime.jsx(
1972
1973
  reactNative.Pressable,
1973
1974
  {
@@ -1985,7 +1986,7 @@ var MobileDrawer = ({
1985
1986
  ref: panelRef,
1986
1987
  "aria-modal": true,
1987
1988
  role: "dialog",
1988
- style: [styles9.panel, isFull ? styles9.panelFull : null, { backgroundColor: theme.colors.surface }],
1989
+ style: [styles9.panel, windowSize, { backgroundColor: theme.colors.surface }],
1989
1990
  testID: drawerTestID,
1990
1991
  children: [
1991
1992
  showHeaderBar ? /* @__PURE__ */ jsxRuntime.jsx(
@@ -2194,7 +2195,7 @@ function renderShellSidebar({ sideRail, nav, width, showBrand }) {
2194
2195
  Sidebar,
2195
2196
  {
2196
2197
  collapseHint: sideRail.collapseHint,
2197
- containerStyle: sideRail.containerStyle,
2198
+ containerStyle: { width: SIDEBAR_WIDTH_PX[width], ...reactNative.StyleSheet.flatten(sideRail.containerStyle) },
2198
2199
  enableInlineSearch: sideRail.enableInlineSearch,
2199
2200
  expandHint: sideRail.expandHint,
2200
2201
  footer: sideRail.footer,
@@ -2208,7 +2209,6 @@ function renderShellSidebar({ sideRail, nav, width, showBrand }) {
2208
2209
  search: sideRail.search,
2209
2210
  searchQuery: sideRail.searchQuery,
2210
2211
  title: showBrand ? brandSlot : null,
2211
- width,
2212
2212
  onNavigate: nav.onNavigate,
2213
2213
  onSearchChange: sideRail.onSearchChange
2214
2214
  }
@@ -2216,14 +2216,15 @@ function renderShellSidebar({ sideRail, nav, width, showBrand }) {
2216
2216
  }
2217
2217
  var styles11 = reactNative.StyleSheet.create({
2218
2218
  row: { flexDirection: "row", alignItems: "center" },
2219
- brand: { justifyContent: "center", paddingHorizontal: 12 },
2219
+ brand: { justifyContent: "center", alignSelf: "stretch", paddingHorizontal: 12, borderBottomWidth: 1 },
2220
2220
  fill: { flex: 1 }
2221
2221
  });
2222
2222
  var TopbarBrandRow = ({ brand, brandWidth, testID, children }) => {
2223
2223
  const { width } = reactNative.useWindowDimensions();
2224
+ const { colors } = uiFeedback.useUi().theme;
2224
2225
  if (width < RAIL_FULL_BREAKPOINT) return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children });
2225
2226
  return /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: styles11.row, testID, children: [
2226
- /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: [styles11.brand, brandWidth !== void 0 ? { width: brandWidth } : null], testID: `${testID}-brand`, children: brand }),
2227
+ /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: [styles11.brand, { backgroundColor: colors.background, borderBottomColor: colors.border }, brandWidth !== void 0 ? { width: brandWidth } : null], testID: `${testID}-brand`, children: brand }),
2227
2228
  /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: styles11.fill, children })
2228
2229
  ] });
2229
2230
  };
@@ -2245,10 +2246,7 @@ var NavShell = ({
2245
2246
  children,
2246
2247
  collapsedSidebar: collapsedSidebarProp,
2247
2248
  collapsedRailRange: collapsedRailRangeProp,
2248
- brandPlacement = NAV_BRAND_DEFAULTS.brandPlacement,
2249
- sidebarWidth = NAV_BRAND_DEFAULTS.sidebarWidth,
2250
- drawerHeader = NAV_BRAND_DEFAULTS.drawerHeader,
2251
- drawerWidth = NAV_BRAND_DEFAULTS.drawerWidth,
2249
+ brand,
2252
2250
  mobileMenu,
2253
2251
  ...shellProps
2254
2252
  }) => {
@@ -2277,11 +2275,12 @@ var NavShell = ({
2277
2275
  ) : null;
2278
2276
  const nav = { pathname, onNavigate, regionLabel, navigateHint };
2279
2277
  const rail = showSideRail ? sideRail : void 0;
2280
- const brandInBar = brandPlacement === "topbar" && rail?.brand !== void 0;
2278
+ const { placement, railWidth, drawerHeader, drawerWidth } = { ...NAV_BRAND_DEFAULTS, ...brand };
2279
+ const brandInBar = placement === "topbar" && rail?.brand !== void 0;
2281
2280
  const brandInDrawer = drawerHeader === "brand" ? rail?.brand : void 0;
2282
- const sidebarNode = rail === void 0 ? void 0 : renderShellSidebar({ sideRail: rail, nav, width: sidebarWidth, showBrand: !brandInBar });
2283
- const drawerSidebar = rail !== void 0 && brandInDrawer !== void 0 && !brandInBar ? renderShellSidebar({ sideRail: rail, nav, width: sidebarWidth, showBrand: false }) : void 0;
2284
- const shellHeader = brandInBar ? /* @__PURE__ */ jsxRuntime.jsx(TopbarBrandRow, { brand: rail?.brand, brandWidth: SIDEBAR_WIDTH_PX[sidebarWidth], testID: `${shellProps.testID}-topbar-brand-row`, children: headerNode }) : headerNode;
2281
+ const sidebarNode = rail === void 0 ? void 0 : renderShellSidebar({ sideRail: rail, nav, width: railWidth, showBrand: !brandInBar });
2282
+ const drawerSidebar = rail !== void 0 && brandInDrawer !== void 0 && !brandInBar ? renderShellSidebar({ sideRail: rail, nav, width: railWidth, showBrand: false }) : void 0;
2283
+ const shellHeader = brandInBar ? /* @__PURE__ */ jsxRuntime.jsx(TopbarBrandRow, { brand: rail?.brand, brandWidth: SIDEBAR_WIDTH_PX[railWidth], testID: `${shellProps.testID}-topbar-brand-row`, children: headerNode }) : headerNode;
2285
2284
  const collapsedCfg = showSideRail ? sideRail?.collapsed : void 0;
2286
2285
  const collapsedNode = collapsedCfg !== void 0 && sideRail !== void 0 ? /* @__PURE__ */ jsxRuntime.jsx(
2287
2286
  CollapsedRail,