@dloizides/ui-nav 1.19.0 → 1.21.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/dist/index.mjs CHANGED
@@ -1,9 +1,9 @@
1
1
  import React, { useState, useCallback, useMemo, useEffect, useRef } from 'react';
2
- import { Platform, StyleSheet, Pressable, Text, View, TextInput, useWindowDimensions, ActivityIndicator, ScrollView } from 'react-native';
2
+ import { Platform, StyleSheet, Pressable, Text, View, TextInput, useWindowDimensions, ScrollView, ActivityIndicator } from 'react-native';
3
3
  import { useUi, UiProvider } from '@dloizides/ui-feedback';
4
4
  import { jsxs, jsx } from 'react/jsx-runtime';
5
5
  import { Collapse } from '@dloizides/ui-motion';
6
- import { ModalDropdown } from '@dloizides/ui-layout';
6
+ import { LAYOUT_COLLAPSE_BREAKPOINT, ModalDropdown } from '@dloizides/ui-layout';
7
7
  import { resolveAccessibleRoutes } from '@dloizides/auth-web';
8
8
 
9
9
  // src/Sidebar.tsx
@@ -640,9 +640,7 @@ var NavExpandableItem = ({
640
640
  )) }) })
641
641
  ] });
642
642
  };
643
-
644
- // src/searchAffordance.ts
645
- var DEFAULT_SEARCH_BREAKPOINT = 768;
643
+ var DEFAULT_SEARCH_BREAKPOINT = LAYOUT_COLLAPSE_BREAKPOINT;
646
644
  function resolveSearchAffordance(viewport, breakpoint = DEFAULT_SEARCH_BREAKPOINT) {
647
645
  return viewport >= breakpoint ? "inline" : "palette";
648
646
  }
@@ -1338,7 +1336,22 @@ var CARD_TITLE_FONT_SIZE = 16;
1338
1336
  var CARD_MESSAGE_FONT_SIZE = 14;
1339
1337
  var CARD_TITLE_MARGIN_BOTTOM = 8;
1340
1338
  var styles3 = StyleSheet.create({
1341
- centerFill: { flex: 1, justifyContent: "center", alignItems: "center" },
1339
+ // Loading is a TRANSITIONAL state: the spinner is an absolute overlay ON TOP of
1340
+ // stably-mounted children, never a replacement for them. Replacing children here
1341
+ // would unmount whatever they host (e.g. a navigator), and on remount React
1342
+ // Navigation re-derives its default route — silently discarding a deep link.
1343
+ // The host wraps children whenever a `state` is passed (not only while loading),
1344
+ // so its element type is invariant across the loading toggle — see `useContentBody`.
1345
+ loadingHost: { flex: 1 },
1346
+ loadingOverlay: {
1347
+ alignItems: "center",
1348
+ bottom: 0,
1349
+ justifyContent: "center",
1350
+ left: 0,
1351
+ position: "absolute",
1352
+ right: 0,
1353
+ top: 0
1354
+ },
1342
1355
  card: {
1343
1356
  padding: CARD_PADDING,
1344
1357
  borderRadius: CARD_BORDER_RADIUS,
@@ -1363,13 +1376,22 @@ function useContentBody(state, children, testID) {
1363
1376
  const { theme } = useUi();
1364
1377
  const primary = theme.palette.primary["500"];
1365
1378
  const errorColor = theme.semantic.error["500"];
1366
- if (state?.forbidden)
1379
+ if (!state) return children;
1380
+ if (state.forbidden)
1367
1381
  return /* @__PURE__ */ jsx(MessageCard, { accentColor: errorColor, message: state.forbidden, testID: `${testID}${APP_SHELL_SUFFIX.forbidden}` });
1368
- if (state?.error)
1382
+ if (state.error)
1369
1383
  return /* @__PURE__ */ jsx(MessageCard, { accentColor: errorColor, message: state.error, testID: `${testID}${APP_SHELL_SUFFIX.error}` });
1370
- if (state?.loading === true)
1371
- return /* @__PURE__ */ jsx(View, { style: styles3.centerFill, testID: `${testID}${APP_SHELL_SUFFIX.loading}`, children: /* @__PURE__ */ jsx(ActivityIndicator, { color: primary, size: "large" }) });
1372
- return children;
1384
+ return /* @__PURE__ */ jsxs(View, { style: styles3.loadingHost, children: [
1385
+ children,
1386
+ state.loading === true ? /* @__PURE__ */ jsx(
1387
+ View,
1388
+ {
1389
+ style: [styles3.loadingOverlay, { backgroundColor: theme.colors.background }],
1390
+ testID: `${testID}${APP_SHELL_SUFFIX.loading}`,
1391
+ children: /* @__PURE__ */ jsx(ActivityIndicator, { color: primary, size: "large" })
1392
+ }
1393
+ ) : null
1394
+ ] });
1373
1395
  }
1374
1396
  var SCRIM_COLOR = "rgba(0, 0, 0, 0.5)";
1375
1397
  var MENU_GLYPH2 = "\u2630";
@@ -1481,9 +1503,7 @@ var MobileDrawer = ({
1481
1503
  )
1482
1504
  ] });
1483
1505
  };
1484
-
1485
- // src/railMode.ts
1486
- var RAIL_FULL_BREAKPOINT = 768;
1506
+ var RAIL_FULL_BREAKPOINT = LAYOUT_COLLAPSE_BREAKPOINT;
1487
1507
  var DEFAULT_COLLAPSED_RAIL_MAX = 1024;
1488
1508
  function resolveRailMode({ viewport, hasSidebar, hasCollapsed, range }) {
1489
1509
  if (!hasSidebar) return "none";
@@ -1507,7 +1527,10 @@ function useContentMaxWidth(width) {
1507
1527
  var DEFAULT_CONTENT_PADDING = 24;
1508
1528
  var styles5 = StyleSheet.create({
1509
1529
  root: { flex: 1 },
1510
- centerFill: { flex: 1, justifyContent: "center", alignItems: "center" },
1530
+ // Auth-pending spinner: an opaque absolute overlay over the mounted shell, so
1531
+ // `children` (and any navigator they host) are never unmounted while the gate
1532
+ // resolves. See the `gatePending` note in the render body.
1533
+ pendingOverlay: { alignItems: "center", bottom: 0, justifyContent: "center", left: 0, position: "absolute", right: 0, top: 0 },
1511
1534
  scroll: { flex: 1 },
1512
1535
  scrollContent: { flexGrow: 1 },
1513
1536
  // Mobile back-office header: the hamburger sits inline before the header slot.
@@ -1561,8 +1584,7 @@ var AppShell = ({
1561
1584
  useEffect(() => {
1562
1585
  if (isUnauthenticated && gate !== void 0) gate.onRedirect();
1563
1586
  }, [isUnauthenticated, gate]);
1564
- if (gate?.pending === true)
1565
- return /* @__PURE__ */ jsx(View, { style: [styles5.root, styles5.centerFill, { backgroundColor: theme.colors.background }], testID: `${testID}${APP_SHELL_SUFFIX.pending}`, children: /* @__PURE__ */ jsx(ActivityIndicator, { color: primary, size: "large" }) });
1587
+ const gatePending = gate?.pending === true;
1566
1588
  if (isUnauthenticated) return null;
1567
1589
  const columnStyle = maxWidth === "full" ? styles5.columnFull : [styles5.columnCapped, { maxWidth }];
1568
1590
  const navInnerStyle = chromeAlignment === "content" && maxWidth !== "full" ? [styles5.chromeCapped, { maxWidth, paddingHorizontal: contentPadding }] : void 0;
@@ -1605,6 +1627,14 @@ var AppShell = ({
1605
1627
  sidebar,
1606
1628
  onClose: closeDrawer
1607
1629
  }
1630
+ ) : null,
1631
+ gatePending ? /* @__PURE__ */ jsx(
1632
+ View,
1633
+ {
1634
+ style: [styles5.pendingOverlay, { backgroundColor: theme.colors.background }],
1635
+ testID: `${testID}${APP_SHELL_SUFFIX.pending}`,
1636
+ children: /* @__PURE__ */ jsx(ActivityIndicator, { color: primary, size: "large" })
1637
+ }
1608
1638
  ) : null
1609
1639
  ] });
1610
1640
  };