@mrmeg/expo-ui 0.13.0-rsd.0 → 0.13.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.
Files changed (81) hide show
  1. package/CHANGELOG.md +31 -0
  2. package/LLM_USAGE.md +15 -0
  3. package/README.md +15 -1
  4. package/dist/components/Accordion.d.ts +18 -38
  5. package/dist/components/Accordion.js +121 -137
  6. package/dist/components/AnimatedView.d.ts +24 -13
  7. package/dist/components/AnimatedView.js +29 -70
  8. package/dist/components/Badge.d.ts +2 -2
  9. package/dist/components/Badge.js +47 -45
  10. package/dist/components/BottomSheet.d.ts +73 -30
  11. package/dist/components/BottomSheet.js +225 -191
  12. package/dist/components/Button.d.ts +56 -43
  13. package/dist/components/Button.js +203 -189
  14. package/dist/components/Card.d.ts +10 -7
  15. package/dist/components/Card.js +105 -90
  16. package/dist/components/Checkbox.d.ts +8 -20
  17. package/dist/components/Checkbox.js +80 -77
  18. package/dist/components/Collapsible.d.ts +47 -47
  19. package/dist/components/Collapsible.js +29 -108
  20. package/dist/components/Dialog.d.ts +121 -80
  21. package/dist/components/Dialog.js +132 -225
  22. package/dist/components/DismissKeyboard.js +2 -33
  23. package/dist/components/Drawer.d.ts +83 -37
  24. package/dist/components/Drawer.js +512 -252
  25. package/dist/components/DropdownMenu.d.ts +113 -106
  26. package/dist/components/DropdownMenu.js +204 -350
  27. package/dist/components/EmptyState.d.ts +2 -2
  28. package/dist/components/EmptyState.js +34 -41
  29. package/dist/components/InputOTP.d.ts +53 -19
  30. package/dist/components/InputOTP.js +102 -81
  31. package/dist/components/KeyboardAvoidingView.d.ts +3 -6
  32. package/dist/components/KeyboardAvoidingView.js +5 -18
  33. package/dist/components/Label.d.ts +17 -12
  34. package/dist/components/Label.js +51 -38
  35. package/dist/components/MaxWidthContainer.d.ts +16 -5
  36. package/dist/components/MaxWidthContainer.js +29 -28
  37. package/dist/components/Notification.d.ts +9 -5
  38. package/dist/components/Notification.js +187 -190
  39. package/dist/components/Popover.d.ts +66 -38
  40. package/dist/components/Popover.js +69 -158
  41. package/dist/components/Progress.d.ts +4 -5
  42. package/dist/components/Progress.js +77 -65
  43. package/dist/components/RadioGroup.d.ts +24 -30
  44. package/dist/components/RadioGroup.js +94 -90
  45. package/dist/components/Select.d.ts +74 -62
  46. package/dist/components/Select.js +154 -241
  47. package/dist/components/Separator.d.ts +23 -13
  48. package/dist/components/Separator.js +36 -29
  49. package/dist/components/Skeleton.d.ts +7 -8
  50. package/dist/components/Skeleton.js +61 -74
  51. package/dist/components/StyledText.context.d.ts +2 -6
  52. package/dist/components/StyledText.context.js +0 -3
  53. package/dist/components/StyledText.d.ts +7 -29
  54. package/dist/components/StyledText.js +29 -92
  55. package/dist/components/Switch.d.ts +6 -18
  56. package/dist/components/Switch.js +106 -112
  57. package/dist/components/Tabs.d.ts +16 -26
  58. package/dist/components/Tabs.js +91 -189
  59. package/dist/components/TextInput.d.ts +19 -6
  60. package/dist/components/TextInput.js +198 -226
  61. package/dist/components/Toggle.d.ts +41 -23
  62. package/dist/components/Toggle.js +98 -84
  63. package/dist/components/ToggleGroup.d.ts +29 -37
  64. package/dist/components/ToggleGroup.js +108 -113
  65. package/dist/components/Tooltip.d.ts +111 -41
  66. package/dist/components/Tooltip.js +118 -156
  67. package/dist/components/UIProvider.d.ts +1 -1
  68. package/dist/components/UIProvider.js +1 -1
  69. package/dist/components/keyboardFocusRegistry.d.ts +15 -1
  70. package/dist/components/keyboardFocusRegistry.js +39 -2
  71. package/dist/hooks/useTheme.d.ts +10 -34
  72. package/dist/hooks/useTheme.js +8 -20
  73. package/dist/lib/index.d.ts +0 -2
  74. package/dist/lib/index.js +0 -2
  75. package/package.json +29 -11
  76. package/dist/lib/portal.d.ts +0 -16
  77. package/dist/lib/portal.js +0 -84
  78. package/dist/lib/styles.d.ts +0 -32
  79. package/dist/lib/styles.js +0 -91
  80. package/dist/lib/useAnchoredPosition.d.ts +0 -57
  81. package/dist/lib/useAnchoredPosition.js +0 -120
@@ -1,232 +1,84 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import * as React from "react";
3
- import { createContext, use, useCallback, useEffect, useId, useMemo, useRef, useState, } from "react";
4
- import { Platform } from "react-native";
5
- import { css, html } from "react-strict-dom";
6
- import { Portal } from "../lib/portal.js";
7
- import { flattenStyle } from "../lib/styles.js";
3
+ import { Platform, StyleSheet, View } from "react-native";
4
+ import { FullWindowOverlay as RNFullWindowOverlay } from "react-native-screens";
5
+ import * as DialogPrimitive from "@rn-primitives/dialog";
6
+ import * as AlertDialogPrimitive from "@rn-primitives/alert-dialog";
7
+ import { AnimatedView } from "./AnimatedView.js";
8
8
  import { TextClassContext, TextColorContext } from "./StyledText.context";
9
9
  import { StyledText } from "./StyledText.js";
10
10
  import { useTheme } from "../hooks/useTheme.js";
11
11
  import { spacing } from "../constants/spacing.js";
12
12
  import { palette } from "../constants/colors.js";
13
- const isWeb = Platform.OS === "web";
14
- const DialogContext = createContext(null);
15
- function useDialogContext(component) {
16
- const ctx = use(DialogContext);
17
- if (!ctx) {
18
- throw new Error(`${component} must be used within a <Dialog> / <AlertDialog>`);
19
- }
20
- return ctx;
21
- }
22
- function createDialogRoot(variant) {
23
- return function DialogRoot({ children, open: openProp, defaultOpen, onOpenChange, }) {
24
- const [uncontrolledOpen, setUncontrolledOpen] = useState(defaultOpen ?? false);
25
- const isControlled = openProp !== undefined;
26
- const open = isControlled ? openProp : uncontrolledOpen;
27
- const baseId = useId();
28
- const setOpen = useCallback((next) => {
29
- if (!isControlled)
30
- setUncontrolledOpen(next);
31
- onOpenChange?.(next);
32
- }, [isControlled, onOpenChange]);
33
- const value = useMemo(() => ({
34
- open,
35
- setOpen,
36
- variant,
37
- titleId: `${baseId}-title`,
38
- descriptionId: `${baseId}-desc`,
39
- dismissOnOverlay: variant === "dialog",
40
- }), [open, setOpen, baseId]);
41
- return _jsx(DialogContext.Provider, { value: value, children: children });
42
- };
43
- }
13
+ const FullWindowOverlay = Platform.OS === "ios" ? RNFullWindowOverlay : React.Fragment;
44
14
  // ============================================================================
45
- // Trigger / Close (asChild slot pattern)
15
+ // Dialog
46
16
  // ============================================================================
47
- /**
48
- * Clone a single child element to inject an onPress handler (the package's
49
- * interactive components — Button — expose onPress). Falls back to a plain
50
- * html.button when `asChild` is not used.
51
- */
52
- function SlotPressable({ asChild, onActivate, children, }) {
53
- if (asChild && React.isValidElement(children)) {
54
- const child = children;
55
- return React.cloneElement(child, {
56
- onPress: () => {
57
- child.props.onPress?.();
58
- onActivate();
59
- },
60
- });
61
- }
62
- return (_jsx(html.button, { type: "button", onClick: onActivate, children: children }));
63
- }
64
- function DialogTrigger({ asChild, children, }) {
65
- const { setOpen } = useDialogContext("DialogTrigger");
66
- return (_jsx(SlotPressable, { asChild: asChild, onActivate: () => setOpen(true), children: children }));
67
- }
68
- function DialogClose({ asChild, children, }) {
69
- const { setOpen } = useDialogContext("DialogClose");
70
- return (_jsx(SlotPressable, { asChild: asChild, onActivate: () => setOpen(false), children: children }));
17
+ const DialogTrigger = DialogPrimitive.Trigger;
18
+ const DialogClose = DialogPrimitive.Close;
19
+ function DialogRoot({ children, ...props }) {
20
+ return _jsx(DialogPrimitive.Root, { ...props, children: children });
71
21
  }
72
- // ============================================================================
73
- // Content (Portal + Overlay + centered card)
74
- // ============================================================================
75
- // css.keyframes() MUST be called inline and unconditionally, before the
76
- // css.create() that references it: StyleX's babel plugin (web) only folds a
77
- // keyframe name defined in the same file, and it cannot fold a runtime ternary
78
- // condition (`Platform.OS === "web" ? ... : ""` throws "Referenced constant is
79
- // not defined" in dev). On native css.keyframes is a no-op returning its arg,
80
- // and the animationName has no visible effect (native animation fidelity gap).
81
- const fadeIn = css.keyframes({
82
- from: { opacity: 0 },
83
- to: { opacity: 1 },
84
- });
85
- const scaleIn = css.keyframes({
86
- from: { opacity: 0, transform: "scale(0.95)" },
87
- to: { opacity: 1, transform: "scale(1)" },
88
- });
89
- const styles = css.create({
90
- overlay: (backgroundColor, zIndex) => ({
91
- position: "fixed",
92
- top: 0,
93
- left: 0,
94
- right: 0,
95
- bottom: 0,
96
- backgroundColor,
97
- display: "flex",
98
- alignItems: "center",
99
- justifyContent: "center",
100
- zIndex,
101
- // Fade-in on web; instant on native (animation props are no-ops there).
102
- animationName: fadeIn,
103
- animationDuration: "0.2s",
104
- animationTimingFunction: "ease-out",
105
- }),
106
- // Native uses absolute fill rather than position:fixed (RSD maps fixed→
107
- // absolute on native, but be explicit for the full-screen scrim).
108
- overlayNative: {
109
- position: "absolute",
110
- },
111
- // html.div defaults to display:block on web (not RN flexbox), so set flex
112
- // explicitly on the wrapper + card to lay out their children predictably.
113
- sizer: {
114
- display: "flex",
115
- flexDirection: "column",
116
- width: "90%",
117
- maxWidth: 450,
118
- maxHeight: "85%",
119
- animationName: scaleIn,
120
- animationDuration: "0.25s",
121
- animationTimingFunction: "ease-out",
122
- },
123
- card: (backgroundColor, borderColor, borderRadius, padding, boxShadow) => ({
124
- display: "flex",
125
- flexDirection: "column",
126
- backgroundColor,
127
- borderColor,
128
- borderWidth: 1,
129
- borderStyle: "solid",
130
- borderRadius,
131
- padding,
132
- width: "100%",
133
- boxShadow,
134
- }),
135
- header: (gap, marginBottom) => ({
136
- display: "flex",
137
- flexDirection: "column",
138
- gap,
139
- marginBottom,
140
- }),
141
- footer: (gap, marginTop) => ({
142
- display: "flex",
143
- flexDirection: "row",
144
- justifyContent: "flex-end",
145
- gap,
146
- marginTop,
147
- }),
148
- });
149
- function DialogContentImpl({ children, style, portalHost }) {
150
- const ctx = useDialogContext("DialogContent");
151
- const { open, setOpen, variant, titleId, descriptionId, dismissOnOverlay } = ctx;
22
+ function DialogContent({ portalHost, style, children, ...props }) {
152
23
  const { theme, getShadowStyle, getContrastingColor } = useTheme();
153
- const cardRef = useRef(null);
154
24
  const textColor = getContrastingColor(theme.colors.popover, palette.white, palette.black);
155
- const zIndex = variant === "alertdialog" ? 52 : 50;
156
- // Escape-to-close on web.
157
- useEffect(() => {
158
- if (!open || !isWeb || typeof document === "undefined")
159
- return;
160
- const onKeyDown = (e) => {
161
- if (e.key === "Escape")
162
- setOpen(false);
163
- };
164
- document.addEventListener("keydown", onKeyDown);
165
- return () => document.removeEventListener("keydown", onKeyDown);
166
- }, [open, setOpen]);
167
- // Move focus into the dialog card on open (web).
168
- useEffect(() => {
169
- if (!open || !isWeb)
170
- return;
171
- cardRef.current?.focus?.();
172
- }, [open]);
173
- if (!open)
174
- return null;
175
- const handleOverlayClick = () => {
176
- if (dismissOnOverlay)
177
- setOpen(false);
178
- };
179
- // The Portal renders into a separate subtree (the PortalHost), so the
180
- // DialogContext from the provider above is NOT visible there. Re-provide it
181
- // inside the portal so Title/Description (which read titleId/descriptionId)
182
- // and Close work from within the portaled content.
183
- return (_jsx(Portal, { hostName: portalHost, children: _jsx(DialogContext.Provider, { value: ctx, children: _jsx(html.div, { style: [styles.overlay(theme.colors.overlay, zIndex), !isWeb ? styles.overlayNative : null], onClick: dismissOnOverlay ? handleOverlayClick : undefined, children: _jsx(html.div, { style: styles.sizer, children: _jsx(TextColorContext.Provider, { value: textColor, children: _jsx(TextClassContext.Provider, { value: "", children: _jsx(html.div, { ref: cardRef, role: variant === "alertdialog" ? "alertdialog" : "dialog", "aria-modal": true, "aria-labelledby": titleId, "aria-describedby": descriptionId, tabIndex: -1,
184
- // Stop clicks inside the card from bubbling to the overlay scrim.
185
- onClick: (e) => e.stopPropagation?.(), style: [
186
- styles.card(theme.colors.popover, theme.colors.border, spacing.radiusLg, spacing.lg, getShadowStyle("soft").boxShadow ?? "none"),
187
- style,
188
- ], children: children }) }) }) }) }) }) }));
189
- }
190
- // ============================================================================
191
- // Header / Footer / Title / Description
192
- // ============================================================================
193
- function DialogHeader({ children, style }) {
194
- return (_jsx(html.div, { style: [styles.header(spacing.xs, spacing.md), style], children: children }));
25
+ return (_jsx(DialogPrimitive.Portal, { hostName: portalHost, children: _jsx(FullWindowOverlay, { children: _jsx(DialogPrimitive.Overlay, { style: StyleSheet.flatten([
26
+ StyleSheet.absoluteFill,
27
+ { backgroundColor: theme.colors.overlay },
28
+ Platform.OS === "web" && { zIndex: 50 },
29
+ ]), children: _jsx(AnimatedView, { type: "fade", enterDuration: 200, style: StyleSheet.absoluteFill, children: _jsx(View, { style: overlayStyles.centeredContainer, children: _jsx(AnimatedView, { type: "scale", enterDuration: 250, style: overlayStyles.sizer, children: _jsx(TextColorContext.Provider, { value: textColor, children: _jsx(TextClassContext.Provider, { value: "", children: _jsx(DialogPrimitive.Content, { style: StyleSheet.flatten([
30
+ {
31
+ backgroundColor: theme.colors.popover,
32
+ borderColor: theme.colors.border,
33
+ borderWidth: 1,
34
+ borderRadius: spacing.radiusLg,
35
+ padding: spacing.lg,
36
+ width: "100%",
37
+ ...getShadowStyle("soft"),
38
+ },
39
+ style,
40
+ ]), ...props, children: children }) }) }) }) }) }) }) }) }));
195
41
  }
196
- function DialogFooter({ children, style }) {
197
- return (_jsx(html.div, { style: [styles.footer(spacing.sm, spacing.lg), style], children: children }));
42
+ function DialogHeader({ children, style, ...props }) {
43
+ return (_jsx(View, { style: StyleSheet.flatten([{ gap: spacing.xs, marginBottom: spacing.md }, style]), ...props, children: children }));
198
44
  }
199
- function createTitle(variant) {
200
- return function DialogTitle({ children, style, }) {
201
- const { theme } = useTheme();
202
- const { titleId } = useDialogContext(variant === "alertdialog" ? "AlertDialogTitle" : "DialogTitle");
203
- const merged = flattenStyle([
204
- { fontSize: 18, lineHeight: 24, letterSpacing: 0, color: theme.colors.text },
45
+ function DialogFooter({ children, style, ...props }) {
46
+ return (_jsx(View, { style: StyleSheet.flatten([
47
+ {
48
+ flexDirection: "row",
49
+ justifyContent: "flex-end",
50
+ gap: spacing.sm,
51
+ marginTop: spacing.lg,
52
+ },
205
53
  style,
206
- ]);
207
- return (_jsx(StyledText, { id: titleId, fontWeight: "semibold", style: merged, children: children }));
208
- };
54
+ ]), ...props, children: children }));
209
55
  }
210
- function createDescription(variant) {
211
- return function DialogDescription({ children, style, }) {
212
- const { theme } = useTheme();
213
- const { descriptionId } = useDialogContext(variant === "alertdialog" ? "AlertDialogDescription" : "DialogDescription");
214
- const merged = flattenStyle([
215
- { fontSize: 14, lineHeight: 20, color: theme.colors.textDim },
216
- style,
217
- ]);
218
- return (_jsx(StyledText, { id: descriptionId, style: merged, children: children }));
219
- };
56
+ function DialogTitle({ children, style, ...props }) {
57
+ const { theme } = useTheme();
58
+ return (_jsx(DialogPrimitive.Title, { asChild: true, children: _jsx(StyledText, { fontWeight: "semibold", style: StyleSheet.flatten([
59
+ {
60
+ fontSize: 18,
61
+ lineHeight: 24,
62
+ letterSpacing: 0,
63
+ color: theme.colors.text,
64
+ },
65
+ style,
66
+ ]), ...props, children: children }) }));
67
+ }
68
+ function DialogDescription({ children, style, ...props }) {
69
+ const { theme } = useTheme();
70
+ return (_jsx(DialogPrimitive.Description, { asChild: true, children: _jsx(StyledText, { style: StyleSheet.flatten([
71
+ {
72
+ fontSize: 14,
73
+ lineHeight: 20,
74
+ color: theme.colors.textDim,
75
+ },
76
+ style,
77
+ ]), ...props, children: children }) }));
220
78
  }
221
- // ============================================================================
222
- // Dialog assembly
223
- // ============================================================================
224
- const DialogRoot = createDialogRoot("dialog");
225
- const DialogTitle = createTitle("dialog");
226
- const DialogDescription = createDescription("dialog");
227
79
  const Dialog = Object.assign(DialogRoot, {
228
80
  Trigger: DialogTrigger,
229
- Content: DialogContentImpl,
81
+ Content: DialogContent,
230
82
  Header: DialogHeader,
231
83
  Footer: DialogFooter,
232
84
  Title: DialogTitle,
@@ -234,25 +86,60 @@ const Dialog = Object.assign(DialogRoot, {
234
86
  Close: DialogClose,
235
87
  });
236
88
  // ============================================================================
237
- // AlertDialog assembly
89
+ // AlertDialog
238
90
  // ============================================================================
239
- const AlertDialogRoot = createDialogRoot("alertdialog");
240
- const AlertDialogTitle = createTitle("alertdialog");
241
- const AlertDialogDescription = createDescription("alertdialog");
242
- function AlertDialogTrigger(props) {
243
- return _jsx(DialogTrigger, { ...props });
91
+ const AlertDialogTrigger = AlertDialogPrimitive.Trigger;
92
+ const AlertDialogAction = AlertDialogPrimitive.Action;
93
+ const AlertDialogCancel = AlertDialogPrimitive.Cancel;
94
+ function AlertDialogRoot({ children, ...props }) {
95
+ return _jsx(AlertDialogPrimitive.Root, { ...props, children: children });
244
96
  }
245
- /** AlertDialog action button — closes the dialog after the child's onPress. */
246
- function AlertDialogAction({ asChild, children, }) {
247
- return _jsx(DialogClose, { asChild: asChild, children: children });
97
+ function AlertDialogContent({ portalHost, style, children, ...props }) {
98
+ const { theme, getShadowStyle, getContrastingColor } = useTheme();
99
+ const textColor = getContrastingColor(theme.colors.popover, palette.white, palette.black);
100
+ return (_jsx(AlertDialogPrimitive.Portal, { hostName: portalHost, children: _jsx(FullWindowOverlay, { children: _jsx(AlertDialogPrimitive.Overlay, { style: StyleSheet.flatten([
101
+ StyleSheet.absoluteFill,
102
+ { backgroundColor: theme.colors.overlay },
103
+ Platform.OS === "web" && { zIndex: 52 },
104
+ ]), children: _jsx(AnimatedView, { type: "fade", enterDuration: 200, style: StyleSheet.absoluteFill, children: _jsx(View, { style: overlayStyles.centeredContainer, children: _jsx(AnimatedView, { type: "scale", enterDuration: 250, style: overlayStyles.sizer, children: _jsx(TextColorContext.Provider, { value: textColor, children: _jsx(TextClassContext.Provider, { value: "", children: _jsx(AlertDialogPrimitive.Content, { style: StyleSheet.flatten([
105
+ {
106
+ backgroundColor: theme.colors.popover,
107
+ borderColor: theme.colors.border,
108
+ borderWidth: 1,
109
+ borderRadius: spacing.radiusLg,
110
+ padding: spacing.lg,
111
+ width: "100%",
112
+ ...getShadowStyle("soft"),
113
+ },
114
+ style,
115
+ ]), ...props, children: children }) }) }) }) }) }) }) }) }));
248
116
  }
249
- /** AlertDialog cancel button — closes the dialog after the child's onPress. */
250
- function AlertDialogCancel({ asChild, children, }) {
251
- return _jsx(DialogClose, { asChild: asChild, children: children });
117
+ function AlertDialogTitle({ children, style, ...props }) {
118
+ const { theme } = useTheme();
119
+ return (_jsx(AlertDialogPrimitive.Title, { asChild: true, children: _jsx(StyledText, { fontWeight: "semibold", style: StyleSheet.flatten([
120
+ {
121
+ fontSize: 18,
122
+ lineHeight: 24,
123
+ letterSpacing: 0,
124
+ color: theme.colors.text,
125
+ },
126
+ style,
127
+ ]), ...props, children: children }) }));
128
+ }
129
+ function AlertDialogDescription({ children, style, ...props }) {
130
+ const { theme } = useTheme();
131
+ return (_jsx(AlertDialogPrimitive.Description, { asChild: true, children: _jsx(StyledText, { style: StyleSheet.flatten([
132
+ {
133
+ fontSize: 14,
134
+ lineHeight: 20,
135
+ color: theme.colors.textDim,
136
+ },
137
+ style,
138
+ ]), ...props, children: children }) }));
252
139
  }
253
140
  const AlertDialog = Object.assign(AlertDialogRoot, {
254
141
  Trigger: AlertDialogTrigger,
255
- Content: DialogContentImpl,
142
+ Content: AlertDialogContent,
256
143
  Header: DialogHeader,
257
144
  Footer: DialogFooter,
258
145
  Title: AlertDialogTitle,
@@ -261,8 +148,28 @@ const AlertDialog = Object.assign(AlertDialogRoot, {
261
148
  Cancel: AlertDialogCancel,
262
149
  });
263
150
  // ============================================================================
151
+ // Shared styles
152
+ // ============================================================================
153
+ const overlayStyles = StyleSheet.create({
154
+ centeredContainer: {
155
+ flex: 1,
156
+ justifyContent: "center",
157
+ alignItems: "center",
158
+ },
159
+ // Sizes the dialog relative to the full-screen centered container. Both the
160
+ // width AND maxHeight must live on this wrapper (the direct flex child of the
161
+ // full-screen container) rather than on Content: a percentage resolves
162
+ // against the parent's resolved box, and this wrapper's box is the screen.
163
+ // Putting `maxHeight: "85%"` on Content instead resolves it against this
164
+ // (content-sized) wrapper — clamping the card to 85% of its own content
165
+ // height, so the footer spills out the bottom.
166
+ sizer: {
167
+ width: "90%",
168
+ maxWidth: 450,
169
+ maxHeight: "85%",
170
+ },
171
+ });
172
+ // ============================================================================
264
173
  // Exports
265
174
  // ============================================================================
266
- const DialogContent = DialogContentImpl;
267
- const AlertDialogContent = DialogContentImpl;
268
175
  export { Dialog, DialogTrigger, DialogContent, DialogHeader, DialogFooter, DialogTitle, DialogDescription, DialogClose, AlertDialog, AlertDialogTrigger, AlertDialogContent, AlertDialogTitle, AlertDialogDescription, AlertDialogAction, AlertDialogCancel, };
@@ -1,12 +1,9 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import React from "react";
3
3
  import { Platform, ScrollView, View, } from "react-native";
4
- import { css, html } from "react-strict-dom";
5
4
  import { KeyboardController, useKeyboardContext, useKeyboardState, } from "react-native-keyboard-controller";
6
5
  import { KeyboardAvoidingView, useKeyboardAvoidance } from "./KeyboardAvoidingView.js";
7
6
  import { isTouchInsideKeyboardFocusedInput } from "./keyboardFocusRegistry.js";
8
- import { sanitizeWebStyle } from "../lib/styles.js";
9
- const IS_WEB = Platform.OS === "web";
10
7
  /**
11
8
  * Native tap-away keyboard dismissal.
12
9
  *
@@ -35,35 +32,10 @@ function useDismissKeyboardOnOutsideTouch() {
35
32
  return false;
36
33
  }, [focusedInput, isVisible]);
37
34
  }
38
- const webStyles = css.create({
39
- fill: { display: "flex", flexDirection: "column", flexGrow: 1, flexShrink: 1, flexBasis: "0%" },
40
- scroller: {
41
- display: "flex",
42
- flexDirection: "column",
43
- flexGrow: 1,
44
- flexShrink: 1,
45
- flexBasis: "0%",
46
- justifyContent: "center",
47
- overflowY: "auto",
48
- },
49
- });
50
35
  /**
51
- * Web layout. There's no software keyboard to dismiss on web, so the native
52
- * tap-away + responder-capture logic is inert here — this is purely the layout
53
- * wrapper, rebuilt on react-strict-dom (html.div) so web is RNW-free. A
54
- * scrollable variant uses overflowY:auto; otherwise it's a flex fill.
55
- */
56
- function WebDismissKeyboardLayout({ children, style, scrollable = true }) {
57
- return (_jsx(html.div, { style: [
58
- scrollable ? webStyles.scroller : webStyles.fill,
59
- sanitizeWebStyle(style),
60
- ], children: children }));
61
- }
62
- /**
63
- * Native layout — unchanged: RN ScrollView/View (no RSD scroll analog) plus the
64
- * KeyboardAvoidingView path and the responder-capture tap-away dismissal.
36
+ * @returns Wrapper for a view that dismisses the keyboard when tapped outside of a text input
65
37
  */
66
- function NativeDismissKeyboardLayout({ children, style, avoidKeyboard = true, scrollable = true, dismissOnTouchStart, }) {
38
+ function DismissKeyboardLayout({ children, style, avoidKeyboard = true, scrollable = true, dismissOnTouchStart, }) {
67
39
  const hasKeyboardAvoidance = useKeyboardAvoidance();
68
40
  const content = scrollable ? (_jsx(ScrollView, { style: { flex: 1 }, contentContainerStyle: { flexGrow: 1, justifyContent: "center" }, keyboardShouldPersistTaps: "handled", showsVerticalScrollIndicator: false, children: children })) : (children);
69
41
  if (!avoidKeyboard || hasKeyboardAvoidance) {
@@ -71,9 +43,6 @@ function NativeDismissKeyboardLayout({ children, style, avoidKeyboard = true, sc
71
43
  }
72
44
  return (_jsx(KeyboardAvoidingView, { style: [{ flex: 1, width: "100%" }, style], keyboardVerticalOffset: Platform.OS === "ios" ? 0 : 0, onStartShouldSetResponderCapture: dismissOnTouchStart, children: content }));
73
45
  }
74
- function DismissKeyboardLayout(props) {
75
- return IS_WEB ? _jsx(WebDismissKeyboardLayout, { ...props }) : _jsx(NativeDismissKeyboardLayout, { ...props });
76
- }
77
46
  function NativeDismissKeyboard(props) {
78
47
  const dismissOnTouchStart = useDismissKeyboardOnOutsideTouch();
79
48
  return (_jsx(DismissKeyboardLayout, { ...props, dismissOnTouchStart: dismissOnTouchStart }));
@@ -1,74 +1,120 @@
1
- import * as React from "react";
2
- import { type LooseStyle } from "../lib/styles";
1
+ import React from "react";
2
+ import { ViewProps, StyleProp, ViewStyle } from "react-native";
3
+ type DrawerSide = "left" | "right";
3
4
  /**
4
- * Drawer Component (react-strict-dom)
5
- *
6
- * A sliding drawer overlay (left/right) plus a docked, collapsible "rail"
7
- * variant. Controlled or uncontrolled. Web uses CSS keyframes (overlay slide-in
8
- * + backdrop fade) and a CSS width transition (rail). Native shows instantly —
9
- * the package ships no Reanimated, and there's no RSD swipe-gesture analog, so
10
- * the previous PanResponder swipe-to-close is a documented native gap.
5
+ * Drawer presentation mode.
6
+ * - `"overlay"` (default): modal drawer that slides in over content with a backdrop.
7
+ * - `"rail"`: docked, always-mounted collapsible sidebar (icon strip that expands to
8
+ * a labeled panel). It is in-flow and pushes sibling content as it grows.
11
9
  */
12
- type DrawerSide = "left" | "right";
13
10
  type DrawerVariant = "overlay" | "rail";
14
- export interface DrawerProps {
11
+ interface DrawerProps {
12
+ /** Controlled open state */
15
13
  open?: boolean;
14
+ /** Callback when open state changes */
16
15
  onOpenChange?: (open: boolean) => void;
16
+ /** Default open state for uncontrolled mode */
17
17
  defaultOpen?: boolean;
18
+ /** Which side the drawer appears from */
18
19
  side?: DrawerSide;
19
- /** Drawer width in px or percentage string (overlay mode). */
20
+ /** Drawer width in pixels or percentage string (overlay mode only) */
20
21
  width?: number | `${number}%`;
22
+ /** Whether to close when backdrop is pressed */
21
23
  closeOnBackdropPress?: boolean;
24
+ /**
25
+ * Presentation mode. `"overlay"` (default) is the classic modal drawer;
26
+ * `"rail"` is a docked collapsible sidebar. See {@link DrawerVariant}.
27
+ */
22
28
  variant?: DrawerVariant;
23
- /** Collapsed (icon-strip) width for rail mode. @default 72 */
29
+ /** Collapsed (icon-strip) width in pixels for rail mode. @default 72 */
24
30
  collapsedWidth?: number;
25
- /** Expanded (labeled-panel) width for rail mode. @default 240 */
31
+ /** Expanded (labeled-panel) width in pixels for rail mode. @default 240 */
26
32
  expandedWidth?: number;
27
- /** Whether the rail expands on hover (web only). @default true on web */
33
+ /**
34
+ * Whether the rail expands on hover (web only — native has no hover).
35
+ * @default true on web, false on native
36
+ */
28
37
  expandOnHover?: boolean;
38
+ /** Default expanded state for uncontrolled rail mode. @default false */
29
39
  defaultExpanded?: boolean;
40
+ /** Controlled expanded state for rail mode */
30
41
  expanded?: boolean;
42
+ /** Callback when rail expanded state changes */
31
43
  onExpandedChange?: (expanded: boolean) => void;
44
+ /** Children components */
32
45
  children: React.ReactNode;
33
46
  }
34
- export interface DrawerTriggerProps {
35
- /** Accepted for API parity; the wrapper always handles the press. */
47
+ interface DrawerTriggerProps {
48
+ /** Use child component as trigger */
36
49
  asChild?: boolean;
50
+ /** Children components */
37
51
  children: React.ReactNode;
38
- style?: LooseStyle;
52
+ /** Optional style override */
53
+ style?: StyleProp<ViewStyle>;
39
54
  }
40
- export interface DrawerContentProps {
41
- /** Accepted for API parity (native swipe is a documented gap). */
55
+ interface DrawerContentProps extends ViewProps {
56
+ /** Whether to enable swipe gestures (native only) */
42
57
  swipeEnabled?: boolean;
58
+ /** Swipe threshold to trigger close (0-1, percentage of width) */
43
59
  swipeThreshold?: number;
60
+ /** Velocity threshold for quick swipe */
44
61
  velocityThreshold?: number;
45
- style?: LooseStyle;
62
+ /** Optional style override */
63
+ style?: StyleProp<ViewStyle>;
64
+ /** Children components */
46
65
  children: React.ReactNode;
47
- testID?: string;
48
66
  }
49
- export interface DrawerSectionProps {
67
+ interface DrawerHeaderProps extends ViewProps {
68
+ /** Custom header content. Used when the structured slots are omitted. */
69
+ children?: React.ReactNode;
70
+ /** Brand or app icon shown before the title. */
71
+ icon?: React.ReactNode;
72
+ /** Header title. Strings receive the package's default drawer title style. */
73
+ title?: React.ReactNode;
74
+ /** Trailing control, such as a close or rail-collapse button. */
75
+ action?: React.ReactNode;
76
+ }
77
+ interface DrawerBodyProps extends ViewProps {
50
78
  children: React.ReactNode;
51
- style?: LooseStyle;
52
79
  }
53
- declare function DrawerRoot({ open: controlledOpen, onOpenChange: controlledOnOpenChange, defaultOpen, side, width, closeOnBackdropPress, variant, collapsedWidth, expandedWidth, expandOnHover, defaultExpanded, expanded: controlledExpanded, onExpandedChange: controlledOnExpandedChange, children, }: DrawerProps): React.JSX.Element;
54
- declare function DrawerTrigger({ asChild, children, style }: DrawerTriggerProps): React.JSX.Element;
55
- export interface DrawerToggleCollapseProps {
56
- asChild?: boolean;
80
+ interface DrawerFooterProps extends ViewProps {
57
81
  children: React.ReactNode;
58
- style?: LooseStyle;
59
82
  }
60
- declare function DrawerToggleCollapse({ asChild, children, style }: DrawerToggleCollapseProps): React.JSX.Element;
61
- export interface DrawerCloseProps {
83
+ declare function DrawerRoot({ open: controlledOpen, onOpenChange: controlledOnOpenChange, defaultOpen, side, width, closeOnBackdropPress, variant, collapsedWidth, expandedWidth, expandOnHover, defaultExpanded, expanded: controlledExpanded, onExpandedChange: controlledOnExpandedChange, children, }: DrawerProps): React.JSX.Element;
84
+ declare function DrawerTrigger({ asChild, children, style: styleOverride }: DrawerTriggerProps): React.JSX.Element;
85
+ interface DrawerToggleCollapseProps {
86
+ /** Use child component as the toggle */
62
87
  asChild?: boolean;
88
+ /** Children components */
63
89
  children: React.ReactNode;
64
- style?: LooseStyle;
90
+ /** Optional style override */
91
+ style?: StyleProp<ViewStyle>;
65
92
  }
66
- declare function DrawerClose({ asChild, children, style }: DrawerCloseProps): React.JSX.Element;
93
+ /**
94
+ * Toggles the rail's expanded state. Native has no hover, so a rail needs an
95
+ * explicit expand/collapse control; this provides it. On web it works too and
96
+ * coexists with `expandOnHover`.
97
+ */
98
+ declare function DrawerToggleCollapse({ asChild, children, style: styleOverride }: DrawerToggleCollapseProps): React.JSX.Element;
99
+ /**
100
+ * DrawerContent dispatches to the overlay or rail implementation based on the
101
+ * `variant` set on the Drawer root. Both implementations are separate components
102
+ * so their hooks never run conditionally.
103
+ */
67
104
  declare function DrawerContent(props: DrawerContentProps): React.JSX.Element;
68
- declare function DrawerHeader({ children, style }: DrawerSectionProps): React.JSX.Element;
69
- declare function DrawerBody({ children, style }: DrawerSectionProps): React.JSX.Element;
70
- declare function DrawerFooter({ children, style }: DrawerSectionProps): React.JSX.Element;
105
+ declare function DrawerHeader({ children, icon, title, action, style, ...props }: DrawerHeaderProps): React.JSX.Element;
106
+ declare function DrawerBody({ children, style, ...props }: DrawerBodyProps): React.JSX.Element;
107
+ declare function DrawerFooter({ children, style, ...props }: DrawerFooterProps): React.JSX.Element;
71
108
  declare function useDrawerClose(): () => void;
109
+ interface DrawerCloseProps {
110
+ /** Use child component as close button */
111
+ asChild?: boolean;
112
+ /** Children components */
113
+ children: React.ReactNode;
114
+ /** Optional style override */
115
+ style?: StyleProp<ViewStyle>;
116
+ }
117
+ declare function DrawerClose({ asChild, children, style: styleOverride }: DrawerCloseProps): React.JSX.Element;
72
118
  declare const Drawer: typeof DrawerRoot & {
73
119
  Trigger: typeof DrawerTrigger;
74
120
  Content: typeof DrawerContent;
@@ -79,4 +125,4 @@ declare const Drawer: typeof DrawerRoot & {
79
125
  ToggleCollapse: typeof DrawerToggleCollapse;
80
126
  };
81
127
  export { Drawer, DrawerTrigger, DrawerContent, DrawerHeader, DrawerBody, DrawerFooter, DrawerClose, DrawerToggleCollapse, useDrawerClose, };
82
- export type { DrawerVariant, DrawerSide };
128
+ export type { DrawerProps, DrawerVariant, DrawerTriggerProps, DrawerContentProps, DrawerHeaderProps, DrawerBodyProps, DrawerFooterProps, DrawerCloseProps, DrawerToggleCollapseProps, };