@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,11 +1,11 @@
1
1
  import React from "react";
2
- import { type LooseStyle } from "../lib/styles";
2
+ import { StyleProp, ViewStyle } from "react-native";
3
3
  export type BadgeVariant = "default" | "secondary" | "outline" | "destructive";
4
4
  export interface BadgeProps {
5
5
  children?: React.ReactNode;
6
6
  text?: string;
7
7
  variant?: BadgeVariant;
8
- style?: LooseStyle;
8
+ style?: StyleProp<ViewStyle>;
9
9
  }
10
10
  /**
11
11
  * Badge Component
@@ -1,35 +1,9 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
- import React from "react";
3
- import { css, html } from "react-strict-dom";
2
+ import React, { useMemo } from "react";
3
+ import { View, StyleSheet } from "react-native";
4
4
  import { useTheme } from "../hooks/useTheme.js";
5
+ import { spacing } from "../constants/spacing.js";
5
6
  import { StyledText } from "./StyledText.js";
6
- import { sanitizeWebStyle } from "../lib/styles.js";
7
- // Static structure + function rules for theme-driven colors. The pill radius is
8
- // a static literal (spacing.radiusFull = 9999) so StyleX can inline it; theme
9
- // colors flow through function rules. No flex-child props on the outer element.
10
- const styles = css.create({
11
- badge: {
12
- alignSelf: "flex-start",
13
- borderRadius: 9999,
14
- paddingLeft: 10,
15
- paddingRight: 10,
16
- paddingTop: 2,
17
- paddingBottom: 2,
18
- },
19
- solid: (backgroundColor) => ({ backgroundColor }),
20
- outline: (borderColor) => ({
21
- backgroundColor: "transparent",
22
- borderWidth: 1,
23
- borderStyle: "solid",
24
- borderColor,
25
- }),
26
- });
27
- const TEXT_STYLE = {
28
- fontSize: 12,
29
- fontWeight: "500",
30
- lineHeight: 18,
31
- userSelect: "none",
32
- };
33
7
  /**
34
8
  * Badge Component
35
9
  *
@@ -45,15 +19,15 @@ const TEXT_STYLE = {
45
19
  */
46
20
  function Badge({ children, text, variant = "default", style: styleOverride }) {
47
21
  const { theme } = useTheme();
22
+ const styles = useMemo(() => createStyles(theme), [theme]);
48
23
  const badgeContent = text ?? children;
49
- const textColor = variant === "default"
50
- ? theme.colors.primaryForeground
51
- : variant === "secondary"
52
- ? theme.colors.secondaryForeground
53
- : variant === "destructive"
54
- ? theme.colors.destructiveForeground
55
- : theme.colors.foreground;
56
- const textStyle = { ...TEXT_STYLE, color: textColor };
24
+ const textStyle = [
25
+ styles.text,
26
+ variant === "default" && { color: theme.colors.primaryForeground },
27
+ variant === "secondary" && { color: theme.colors.secondaryForeground },
28
+ variant === "outline" && { color: theme.colors.foreground },
29
+ variant === "destructive" && { color: theme.colors.destructiveForeground },
30
+ ];
57
31
  const normalizedChildren = React.Children.toArray(badgeContent);
58
32
  const hasOnlyTextChildren = normalizedChildren.every((child) => typeof child === "string" || typeof child === "number");
59
33
  const content = hasOnlyTextChildren ? (_jsx(StyledText, { selectable: false, style: textStyle, children: normalizedChildren.join("") })) : (React.Children.map(badgeContent, (child) => {
@@ -62,13 +36,41 @@ function Badge({ children, text, variant = "default", style: styleOverride }) {
62
36
  }
63
37
  return child;
64
38
  }));
65
- const background = variant === "default"
66
- ? styles.solid(theme.colors.primary)
67
- : variant === "secondary"
68
- ? styles.solid(theme.colors.secondary)
69
- : variant === "destructive"
70
- ? styles.solid(theme.colors.destructive)
71
- : styles.outline(theme.colors.border);
72
- return (_jsx(html.div, { style: [styles.badge, background, sanitizeWebStyle(styleOverride)], children: content }));
39
+ return (_jsx(View, { accessibilityRole: "text", style: [
40
+ styles.badge,
41
+ variant === "default" && styles.default,
42
+ variant === "secondary" && styles.secondary,
43
+ variant === "outline" && styles.outline,
44
+ variant === "destructive" && styles.destructive,
45
+ styleOverride,
46
+ ], children: content }));
73
47
  }
48
+ const createStyles = (theme) => StyleSheet.create({
49
+ badge: {
50
+ alignSelf: "flex-start",
51
+ borderRadius: spacing.radiusFull,
52
+ paddingHorizontal: 10,
53
+ paddingVertical: 2,
54
+ },
55
+ default: {
56
+ backgroundColor: theme.colors.primary,
57
+ },
58
+ secondary: {
59
+ backgroundColor: theme.colors.secondary,
60
+ },
61
+ outline: {
62
+ backgroundColor: "transparent",
63
+ borderWidth: 1,
64
+ borderColor: theme.colors.border,
65
+ },
66
+ destructive: {
67
+ backgroundColor: theme.colors.destructive,
68
+ },
69
+ text: {
70
+ fontSize: 12,
71
+ fontWeight: "500",
72
+ lineHeight: 18,
73
+ userSelect: "none",
74
+ },
75
+ });
74
76
  export { Badge };
@@ -1,6 +1,5 @@
1
- import * as React from "react";
2
- import { type ScrollViewProps } from "react-native";
3
- import { type LooseStyle } from "../lib/styles";
1
+ import React from "react";
2
+ import { ViewProps, StyleProp, ViewStyle, ScrollViewProps } from "react-native";
4
3
  /**
5
4
  * BottomSheet — a sliding bottom sheet with a compound API, backed by the
6
5
  * platform's native sheet via `@expo/ui/community/bottom-sheet`:
@@ -9,22 +8,49 @@ import { type LooseStyle } from "../lib/styles";
9
8
  * - Android: Material3 `ModalBottomSheet`
10
9
  * - Web: `vaul` drawer (bundled with @expo/ui)
11
10
  *
12
- * react-strict-dom port: the sheet itself stays @expo/ui (vaul on web — not
13
- * RNW); the compound chrome (Content column / Header / Footer / Handle / Close /
14
- * Trigger / close button) is html.* so web is RNW-free. The scrollable Body is
15
- * the one platform split: RSD html.div maps to a NON-scrolling RN View on
16
- * native, so native keeps RN ScrollView (with its onLayout/onContentSizeChange
17
- * overflow detection); web uses an html.div with overflow:auto and measures
18
- * scrollHeight vs clientHeight.
11
+ * The compound surface (Trigger / Content / Handle / Header / Body / Footer /
12
+ * Close), controlled + uncontrolled state, and theming match a hand-rolled
13
+ * sheet, but the platform owns gestures and keyboard avoidance — so there's no
14
+ * PanResponder, snap-physics, or keyboard lift-and-shrink code to maintain.
15
+ *
16
+ * Platform-owned behaviors (props accepted for ergonomics, but the platform
17
+ * decides):
18
+ * - `.Handle` replaces the native drag indicator with a pressable equivalent.
19
+ * Pressing it walks through the configured snap points, reversing direction
20
+ * at either end. Dragging the sheet continues to use the platform gesture.
21
+ * - `swipeEnabled` / `avoidKeyboard` / `dismissKeyboardOnDrag` are accepted
22
+ * for call-site ergonomics but have no effect — the platform handles them.
23
+ * - Sheet *chrome* (corner radius, system background, safe area) is the
24
+ * platform's on native; theming reaches the content + background color.
25
+ * - On Android only two snap states exist (partial / expanded); extra snap
26
+ * points map to the nearest of those two.
27
+ *
28
+ * Scrollable bodies: the native sheet doesn't bound the hosted RN content to
29
+ * the detent height, so a tall `Body` overflows and clips its footer/tail. When
30
+ * `Body` detects overflow, `Content` caps the column to the detent height so the
31
+ * `ScrollView` actually scrolls to the bottom. Swipe-to-dismiss still works (the
32
+ * native sheet coordinates the pan: a pull at the top of the scroll view
33
+ * dismisses, elsewhere it scrolls). A close affordance is also surfaced for
34
+ * scrollable sheets so there's a one-tap close: the `Header` renders a trailing
35
+ * X, or — if there's no `Header` — `Content` floats one in the top-right corner.
36
+ * No extra props required.
19
37
  *
20
38
  * @example
21
39
  * ```tsx
22
40
  * <BottomSheet open={open} onOpenChange={setOpen} snapPoints={["50%"]}>
23
- * <BottomSheet.Trigger asChild><Button>Open</Button></BottomSheet.Trigger>
41
+ * <BottomSheet.Trigger asChild>
42
+ * <Button>Open</Button>
43
+ * </BottomSheet.Trigger>
24
44
  * <BottomSheet.Content>
25
- * <BottomSheet.Header><SansSerifBoldText>Title</SansSerifBoldText></BottomSheet.Header>
26
- * <BottomSheet.Body><SansSerifText>Content</SansSerifText></BottomSheet.Body>
27
- * <BottomSheet.Footer><Button>Action</Button></BottomSheet.Footer>
45
+ * <BottomSheet.Header>
46
+ * <SansSerifBoldText>Title</SansSerifBoldText>
47
+ * </BottomSheet.Header>
48
+ * <BottomSheet.Body>
49
+ * <SansSerifText>Content</SansSerifText>
50
+ * </BottomSheet.Body>
51
+ * <BottomSheet.Footer>
52
+ * <Button>Action</Button>
53
+ * </BottomSheet.Footer>
28
54
  * </BottomSheet.Content>
29
55
  * </BottomSheet>
30
56
  * ```
@@ -39,10 +65,19 @@ interface BottomSheetContextValue {
39
65
  setSnapIndex: (index: number) => void;
40
66
  cycleSnapPoint: () => void;
41
67
  closeOnBackdropPress: boolean;
68
+ /**
69
+ * True when a `Body`'s content overflows its viewport (is genuinely
70
+ * scrollable). Drives two things: `Content` caps the column to the detent
71
+ * height so the `ScrollView` can reach its bottom, and a close affordance is
72
+ * surfaced (a `Header` X, or floating if there's no `Header`) so a long sheet
73
+ * always has a one-tap close. Set by `Body`.
74
+ */
42
75
  scrollable: boolean;
43
76
  setScrollable: (scrollable: boolean) => void;
77
+ /** Whether a `Header` is mounted, so `Content` knows to render a fallback close. */
44
78
  hasHeader: boolean;
45
79
  setHasHeader: (present: boolean) => void;
80
+ /** Whether a `Footer` is mounted, so `Body` can own the bottom safe-area inset when it isn't. */
46
81
  hasFooter: boolean;
47
82
  setHasFooter: (present: boolean) => void;
48
83
  }
@@ -55,54 +90,62 @@ interface BottomSheetProps {
55
90
  defaultOpen?: boolean;
56
91
  /** Snap point heights (px or percentage strings). Default: ["50%"]. */
57
92
  snapPoints?: SnapPoint[];
58
- /** Whether swiping down / tapping the backdrop dismisses the sheet. Default: true. */
93
+ /**
94
+ * Whether swiping/pulling down (or tapping the backdrop) dismisses the sheet.
95
+ * Maps to the native sheet's `enablePanDownToClose` (iOS
96
+ * `interactiveDismissDisabled`). Default: true.
97
+ *
98
+ * Works with scrollable bodies — the native sheet coordinates the pan (pull at
99
+ * the top of the scroll view dismisses, elsewhere it scrolls). Set to `false`
100
+ * to disable dismiss entirely; the sheet then surfaces an explicit close
101
+ * affordance automatically (a `Header` X, or a floating X if there's no
102
+ * `Header`). Scrollable sheets also get that close affordance regardless, so
103
+ * there's always a one-tap close.
104
+ */
59
105
  closeOnBackdropPress?: boolean;
60
106
  children: React.ReactNode;
61
107
  }
62
108
  interface BottomSheetTriggerProps {
63
109
  asChild?: boolean;
64
110
  children: React.ReactNode;
65
- style?: LooseStyle;
111
+ style?: StyleProp<ViewStyle>;
66
112
  }
67
- interface BottomSheetContentProps {
113
+ interface BottomSheetContentProps extends ViewProps {
68
114
  /** Accepted for call-site ergonomics; ignored (platform owns gestures). */
69
115
  swipeEnabled?: boolean;
70
116
  /** Accepted for call-site ergonomics; ignored (platform owns keyboard avoidance). */
71
117
  avoidKeyboard?: boolean;
72
118
  /** Accepted for call-site ergonomics; ignored (platform owns keyboard). */
73
119
  dismissKeyboardOnDrag?: boolean;
74
- style?: LooseStyle;
120
+ style?: StyleProp<ViewStyle>;
75
121
  children: React.ReactNode;
76
122
  }
77
- interface BottomSheetHeaderProps {
123
+ interface BottomSheetHeaderProps extends ViewProps {
78
124
  children: React.ReactNode;
79
- style?: LooseStyle;
80
125
  }
81
126
  interface BottomSheetBodyProps extends ScrollViewProps {
82
127
  children: React.ReactNode;
83
- testID?: string;
84
128
  }
85
- interface BottomSheetFooterProps {
129
+ interface BottomSheetFooterProps extends ViewProps {
86
130
  children: React.ReactNode;
87
- style?: LooseStyle;
88
131
  }
89
132
  interface BottomSheetHandleProps {
90
- style?: LooseStyle;
133
+ style?: StyleProp<ViewStyle>;
91
134
  }
92
135
  interface BottomSheetCloseProps {
93
136
  asChild?: boolean;
94
137
  children: React.ReactNode;
95
- style?: LooseStyle;
138
+ style?: StyleProp<ViewStyle>;
96
139
  }
97
140
  declare function useBottomSheetContext(): BottomSheetContextValue;
98
141
  declare function BottomSheetRoot({ open: controlledOpen, onOpenChange: controlledOnOpenChange, defaultOpen, snapPoints, closeOnBackdropPress, children, }: BottomSheetProps): React.JSX.Element;
99
- declare function BottomSheetTrigger({ asChild, children, style }: BottomSheetTriggerProps): React.JSX.Element;
142
+ declare function BottomSheetTrigger({ asChild, children, style: styleOverride }: BottomSheetTriggerProps): React.JSX.Element;
100
143
  declare function BottomSheetContent({ swipeEnabled: _swipeEnabled, avoidKeyboard: _avoidKeyboard, dismissKeyboardOnDrag: _dismissKeyboardOnDrag, style: styleOverride, children, }: BottomSheetContentProps): React.JSX.Element;
101
144
  declare function BottomSheetHandle({ style }: BottomSheetHandleProps): React.JSX.Element;
102
- declare function BottomSheetHeader({ children, style }: BottomSheetHeaderProps): React.JSX.Element;
103
- declare function BottomSheetBody(props: BottomSheetBodyProps): React.JSX.Element;
104
- declare function BottomSheetFooter({ children, style }: BottomSheetFooterProps): React.JSX.Element;
105
- declare function BottomSheetClose({ asChild, children, style }: BottomSheetCloseProps): React.JSX.Element;
145
+ declare function BottomSheetHeader({ children, style, ...props }: BottomSheetHeaderProps): React.JSX.Element;
146
+ declare function BottomSheetBody({ children, style, contentContainerStyle, onContentSizeChange, onLayout, ...props }: BottomSheetBodyProps): React.JSX.Element;
147
+ declare function BottomSheetFooter({ children, style, ...props }: BottomSheetFooterProps): React.JSX.Element;
148
+ declare function BottomSheetClose({ asChild, children, style: styleOverride }: BottomSheetCloseProps): React.JSX.Element;
106
149
  declare const BottomSheet: typeof BottomSheetRoot & {
107
150
  Trigger: typeof BottomSheetTrigger;
108
151
  Content: typeof BottomSheetContent;