@gryt/ui-native 0.8.2 → 0.9.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.
@@ -16,9 +16,25 @@ interface ToastContextValue {
16
16
  * Toasts need somewhere to live, which on the web is a portal at the document
17
17
  * root and here is a provider near the top of the tree.
18
18
  *
19
+ * **Mount it above everything else it has to cover.** The viewport is rendered
20
+ * as a sibling *after* `children`, so it paints over anything inside them —
21
+ * including a `Sheet`, whose content goes through `@gorhom/portal` to a host
22
+ * that is itself inside those children. Put `ToastProvider` below
23
+ * `SheetProvider` and the sheet wins instead, which is the wrong way round: a
24
+ * sheet is a surface you are working in, and a toast is the app telling you
25
+ * something happened while you work.
26
+ *
19
27
  * They are deliberately not built on `Modal` like the dialogs are. A modal
20
28
  * intercepts touches for the whole screen, and a toast that blocked the app
21
29
  * until it faded would be worse than no toast.
30
+ *
31
+ * **What that costs, stated rather than discovered:** a `Modal` is a separate
32
+ * native window, so anything built on one — `Dialog`, `AlertDialog`, `Drawer`,
33
+ * and the platform's own `ActionSheetIOS` — draws *over* a toast, whatever the
34
+ * z-index says. React Native has no z-index across windows. A flow that raises
35
+ * a dialog and toasts at the same moment should toast after the dialog closes;
36
+ * there is nothing this component can do about it from inside the tree, and
37
+ * the alternative is a toast that can block the app, which is worse.
22
38
  */
23
39
  export declare function ToastProvider({ children }: {
24
40
  children?: ReactNode;
@@ -1 +1 @@
1
- {"version":3,"file":"Toast.d.ts","sourceRoot":"","sources":["../../../src/components/Toast/Toast.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAuE,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAC5G,OAAO,EAAyB,KAAK,SAAS,EAAE,KAAK,SAAS,EAAE,MAAM,cAAc,CAAC;AAQrF,MAAM,MAAM,aAAa,GAAG,MAAM,GAAG,SAAS,GAAG,SAAS,GAAG,OAAO,CAAC;AAErE,MAAM,WAAW,YAAY;IAC3B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,EAAE,aAAa,CAAC;IACzB,4DAA4D;IAC5D,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC1B;AAMD,UAAU,iBAAiB;IACzB,IAAI,EAAE,CAAC,KAAK,EAAE,YAAY,KAAK,MAAM,CAAC;IACtC,OAAO,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAC;CAC/B;AAID;;;;;;;GAOG;AACH,wBAAgB,aAAa,CAAC,EAAE,QAAQ,EAAE,EAAE;IAAE,QAAQ,CAAC,EAAE,SAAS,CAAA;CAAE,+BAoBnE;AAED,wBAAgB,QAAQ,IAAI,iBAAiB,CAI5C;AAuGD,MAAM,WAAW,UAAU;IACzB,KAAK,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;CAC9B"}
1
+ {"version":3,"file":"Toast.d.ts","sourceRoot":"","sources":["../../../src/components/Toast/Toast.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAuE,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAC5G,OAAO,EAAyB,KAAK,SAAS,EAAE,KAAK,SAAS,EAAE,MAAM,cAAc,CAAC;AASrF,MAAM,MAAM,aAAa,GAAG,MAAM,GAAG,SAAS,GAAG,SAAS,GAAG,OAAO,CAAC;AAErE,MAAM,WAAW,YAAY;IAC3B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,EAAE,aAAa,CAAC;IACzB,4DAA4D;IAC5D,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC1B;AAMD,UAAU,iBAAiB;IACzB,IAAI,EAAE,CAAC,KAAK,EAAE,YAAY,KAAK,MAAM,CAAC;IACtC,OAAO,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAC;CAC/B;AAID;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,wBAAgB,aAAa,CAAC,EAAE,QAAQ,EAAE,EAAE;IAAE,QAAQ,CAAC,EAAE,SAAS,CAAA;CAAE,+BAoBnE;AAED,wBAAgB,QAAQ,IAAI,iBAAiB,CAI5C;AAyHD,MAAM,WAAW,UAAU;IACzB,KAAK,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;CAC9B"}
@@ -1,6 +1,7 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { createContext, useCallback, useContext, useEffect, useRef, useState } from "react";
3
3
  import { Pressable, Text, View } from "react-native";
4
+ import { useSafeAreaInsets } from "react-native-safe-area-context";
4
5
  import Animated, { useAnimatedStyle, useSharedValue } from "react-native-reanimated";
5
6
  import { grytScaleSteps } from "@gryt/theme";
6
7
  import { useReducedMotion } from "../../hooks/useReducedMotion.js";
@@ -11,9 +12,25 @@ const ToastContext = createContext(null);
11
12
  * Toasts need somewhere to live, which on the web is a portal at the document
12
13
  * root and here is a provider near the top of the tree.
13
14
  *
15
+ * **Mount it above everything else it has to cover.** The viewport is rendered
16
+ * as a sibling *after* `children`, so it paints over anything inside them —
17
+ * including a `Sheet`, whose content goes through `@gorhom/portal` to a host
18
+ * that is itself inside those children. Put `ToastProvider` below
19
+ * `SheetProvider` and the sheet wins instead, which is the wrong way round: a
20
+ * sheet is a surface you are working in, and a toast is the app telling you
21
+ * something happened while you work.
22
+ *
14
23
  * They are deliberately not built on `Modal` like the dialogs are. A modal
15
24
  * intercepts touches for the whole screen, and a toast that blocked the app
16
25
  * until it faded would be worse than no toast.
26
+ *
27
+ * **What that costs, stated rather than discovered:** a `Modal` is a separate
28
+ * native window, so anything built on one — `Dialog`, `AlertDialog`, `Drawer`,
29
+ * and the platform's own `ActionSheetIOS` — draws *over* a toast, whatever the
30
+ * z-index says. React Native has no z-index across windows. A flow that raises
31
+ * a dialog and toasts at the same moment should toast after the dialog closes;
32
+ * there is nothing this component can do about it from inside the tree, and
33
+ * the alternative is a toast that can block the app, which is worse.
17
34
  */
18
35
  export function ToastProvider({ children }) {
19
36
  const [toasts, setToasts] = useState([]);
@@ -42,6 +59,7 @@ const RAMP = {
42
59
  };
43
60
  function Viewport({ toasts, onDismiss, }) {
44
61
  const theme = useTheme();
62
+ const insets = useSafeAreaInsets();
45
63
  if (toasts.length === 0)
46
64
  return null;
47
65
  return (_jsx(View
@@ -52,9 +70,26 @@ function Viewport({ toasts, onDismiss, }) {
52
70
  position: "absolute",
53
71
  left: 0,
54
72
  right: 0,
55
- bottom: theme.space(6),
73
+ /* The top, which on a phone is the only edge that is reliably free.
74
+ *
75
+ * The bottom is where a tab bar sits, where the home indicator sits,
76
+ * and where every sheet in the app rises from — so a toast there is
77
+ * either under the chrome or in the way of the gesture. It is also
78
+ * where iOS puts nothing of its own, precisely because that edge is
79
+ * the user's.
80
+ *
81
+ * Below the status bar rather than over it: the clock and the battery
82
+ * are not ours to cover, and a toast that starts under the notch reads
83
+ * as a system banner rather than as this app talking. */
84
+ top: insets.top + theme.space(2),
56
85
  paddingHorizontal: theme.space(4),
57
86
  gap: theme.space(2),
87
+ /* Explicit rather than relying on paint order. Within this tree the
88
+ * viewport is already last, but a caller can put something absolutely
89
+ * positioned after it — a floating bar, a call pill — and the toast
90
+ * has to win. `elevation` is the Android half of the same statement. */
91
+ zIndex: 1000,
92
+ elevation: 24,
58
93
  }, children: toasts.map((toast) => (_jsx(ToastItem, { toast: toast, onDismiss: onDismiss }, toast.id))) }));
59
94
  }
60
95
  function ToastItem({ toast, onDismiss, }) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gryt/ui-native",
3
- "version": "0.8.2",
3
+ "version": "0.9.0",
4
4
  "type": "module",
5
5
  "license": "MIT",
6
6
  "description": "Gryt's design system on React Native. Same tokens as @gryt/ui, a different renderer.",