@dloizides/ui-nav 1.19.0 → 1.20.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,5 +1,5 @@
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';
@@ -1338,7 +1338,22 @@ var CARD_TITLE_FONT_SIZE = 16;
1338
1338
  var CARD_MESSAGE_FONT_SIZE = 14;
1339
1339
  var CARD_TITLE_MARGIN_BOTTOM = 8;
1340
1340
  var styles3 = StyleSheet.create({
1341
- centerFill: { flex: 1, justifyContent: "center", alignItems: "center" },
1341
+ // Loading is a TRANSITIONAL state: the spinner is an absolute overlay ON TOP of
1342
+ // stably-mounted children, never a replacement for them. Replacing children here
1343
+ // would unmount whatever they host (e.g. a navigator), and on remount React
1344
+ // Navigation re-derives its default route — silently discarding a deep link.
1345
+ // The host wraps children whenever a `state` is passed (not only while loading),
1346
+ // so its element type is invariant across the loading toggle — see `useContentBody`.
1347
+ loadingHost: { flex: 1 },
1348
+ loadingOverlay: {
1349
+ alignItems: "center",
1350
+ bottom: 0,
1351
+ justifyContent: "center",
1352
+ left: 0,
1353
+ position: "absolute",
1354
+ right: 0,
1355
+ top: 0
1356
+ },
1342
1357
  card: {
1343
1358
  padding: CARD_PADDING,
1344
1359
  borderRadius: CARD_BORDER_RADIUS,
@@ -1363,13 +1378,22 @@ function useContentBody(state, children, testID) {
1363
1378
  const { theme } = useUi();
1364
1379
  const primary = theme.palette.primary["500"];
1365
1380
  const errorColor = theme.semantic.error["500"];
1366
- if (state?.forbidden)
1381
+ if (!state) return children;
1382
+ if (state.forbidden)
1367
1383
  return /* @__PURE__ */ jsx(MessageCard, { accentColor: errorColor, message: state.forbidden, testID: `${testID}${APP_SHELL_SUFFIX.forbidden}` });
1368
- if (state?.error)
1384
+ if (state.error)
1369
1385
  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;
1386
+ return /* @__PURE__ */ jsxs(View, { style: styles3.loadingHost, children: [
1387
+ children,
1388
+ state.loading === true ? /* @__PURE__ */ jsx(
1389
+ View,
1390
+ {
1391
+ style: [styles3.loadingOverlay, { backgroundColor: theme.colors.background }],
1392
+ testID: `${testID}${APP_SHELL_SUFFIX.loading}`,
1393
+ children: /* @__PURE__ */ jsx(ActivityIndicator, { color: primary, size: "large" })
1394
+ }
1395
+ ) : null
1396
+ ] });
1373
1397
  }
1374
1398
  var SCRIM_COLOR = "rgba(0, 0, 0, 0.5)";
1375
1399
  var MENU_GLYPH2 = "\u2630";
@@ -1507,7 +1531,10 @@ function useContentMaxWidth(width) {
1507
1531
  var DEFAULT_CONTENT_PADDING = 24;
1508
1532
  var styles5 = StyleSheet.create({
1509
1533
  root: { flex: 1 },
1510
- centerFill: { flex: 1, justifyContent: "center", alignItems: "center" },
1534
+ // Auth-pending spinner: an opaque absolute overlay over the mounted shell, so
1535
+ // `children` (and any navigator they host) are never unmounted while the gate
1536
+ // resolves. See the `gatePending` note in the render body.
1537
+ pendingOverlay: { alignItems: "center", bottom: 0, justifyContent: "center", left: 0, position: "absolute", right: 0, top: 0 },
1511
1538
  scroll: { flex: 1 },
1512
1539
  scrollContent: { flexGrow: 1 },
1513
1540
  // Mobile back-office header: the hamburger sits inline before the header slot.
@@ -1561,8 +1588,7 @@ var AppShell = ({
1561
1588
  useEffect(() => {
1562
1589
  if (isUnauthenticated && gate !== void 0) gate.onRedirect();
1563
1590
  }, [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" }) });
1591
+ const gatePending = gate?.pending === true;
1566
1592
  if (isUnauthenticated) return null;
1567
1593
  const columnStyle = maxWidth === "full" ? styles5.columnFull : [styles5.columnCapped, { maxWidth }];
1568
1594
  const navInnerStyle = chromeAlignment === "content" && maxWidth !== "full" ? [styles5.chromeCapped, { maxWidth, paddingHorizontal: contentPadding }] : void 0;
@@ -1605,6 +1631,14 @@ var AppShell = ({
1605
1631
  sidebar,
1606
1632
  onClose: closeDrawer
1607
1633
  }
1634
+ ) : null,
1635
+ gatePending ? /* @__PURE__ */ jsx(
1636
+ View,
1637
+ {
1638
+ style: [styles5.pendingOverlay, { backgroundColor: theme.colors.background }],
1639
+ testID: `${testID}${APP_SHELL_SUFFIX.pending}`,
1640
+ children: /* @__PURE__ */ jsx(ActivityIndicator, { color: primary, size: "large" })
1641
+ }
1608
1642
  ) : null
1609
1643
  ] });
1610
1644
  };