@retray-dev/ui-kit 5.1.0 → 5.2.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/COMPONENTS.md CHANGED
@@ -1,4 +1,4 @@
1
- # @retray-dev/ui-kit — Component Reference (v5.0.0)
1
+ # @retray-dev/ui-kit — Component Reference (v5.2.0)
2
2
 
3
3
  This file is the AI reference for this package. It is shipped inside the npm package so consuming projects can import it into their `CLAUDE.md` with:
4
4
 
@@ -1828,11 +1828,14 @@ Add `react-native-worklets/plugin` (not `react-native-reanimated/plugin`) to `ba
1828
1828
  | description | `string` | — | Supporting text below title |
1829
1829
  | children | `ReactNode` | — | Sheet content |
1830
1830
  | style | `ViewStyle` | — | Inner content container style |
1831
+ | scrollable | `boolean` | `false` | Wraps children in `BottomSheetScrollView` — fixes gesture conflict on both platforms when content needs to scroll |
1832
+ | maxHeight | `number` | — | Caps sheet height (dp). Automatically enables scrolling when content exceeds this value |
1831
1833
 
1832
1834
  **Features:**
1833
1835
  - `enableDynamicSizing` — height auto-fits content, no `snapPoints` needed
1834
1836
  - `enablePanDownToClose` — swipe down to dismiss
1835
1837
  - Backdrop press dismisses
1838
+ - **Scrollable content:** use `scrollable` prop (explicit) or `maxHeight` prop (capped height). Both use `BottomSheetScrollView` internally — do NOT use plain `ScrollView` inside Sheet, it breaks gesture handling on iOS
1836
1839
 
1837
1840
  **Styling:** `RADIUS.lg = 20px` top corners, 16px horizontal + 20px bottom padding.
1838
1841
 
@@ -1855,6 +1858,18 @@ const [open, setOpen] = useState(false)
1855
1858
  />
1856
1859
  </Sheet>
1857
1860
 
1861
+ // Scrollable sheet — long list
1862
+ <Sheet open={open} onClose={() => setOpen(false)} title="Logs" scrollable>
1863
+ {logs.map((log) => (
1864
+ <Text key={log.id}>{log.message}</Text>
1865
+ ))}
1866
+ </Sheet>
1867
+
1868
+ // Capped height — scroll kicks in when content overflows 400dp
1869
+ <Sheet open={open} onClose={() => setOpen(false)} title="Results" maxHeight={400}>
1870
+ {results.map((r) => <ListItem key={r.id} title={r.name} />)}
1871
+ </Sheet>
1872
+
1858
1873
  // Filter sheet
1859
1874
  <Sheet open={filterOpen} onClose={() => setFilterOpen(false)} title="Filters">
1860
1875
  <View style={{ gap: SPACING.lg }}>
package/README.md CHANGED
@@ -3,7 +3,7 @@
3
3
  A personal React Native / Expo UI component library with a built-in design system, dark mode support, haptic feedback, and smooth animations.
4
4
 
5
5
  - 36 components across 7 categories
6
- - Light/dark theme with 23 color tokens and full customization
6
+ - Light/dark theme with 12 public tokens (24 resolved) and full customization
7
7
  - Apple HIG–compliant touch targets and haptic feedback
8
8
  - Animated interactions: spring press, sliding tabs, accordion easing, animated progress
9
9
  - Built with TypeScript — full type declarations included
@@ -113,25 +113,28 @@ import { useTheme } from '@retray-dev/ui-kit'
113
113
  const { colors, colorScheme } = useTheme()
114
114
  ```
115
115
 
116
- **Available tokens:** `background`, `foreground`, `card`, `cardForeground`, `primary`, `primaryForeground`, `secondary`, `secondaryForeground`, `muted`, `mutedForeground`, `accent`, `accentForeground`, `destructive`, `destructiveForeground`, `destructiveTint`, `destructiveBorder`, `border`, `input`, `ring`, `success`, `successForeground`, `successTint`, `successBorder`
116
+ **Public tokens (12 — override these):** `background`, `foreground`, `card`, `primary`, `primaryForeground`, `border`, `destructive`, `destructiveForeground`, `success`, `successForeground`, `warning`, `warningForeground`
117
+
118
+ **Derived tokens (read-only via `useTheme()`):** `foregroundSubtle`, `foregroundMuted`, `surface`, `surfaceStrong`, `destructiveTint`, `destructiveBorder`, `successTint`, `successBorder`, `warningTint`, `warningBorder`, `ring`, `input`
117
119
 
118
120
  ## Design Tokens
119
121
 
120
122
  Static structural constants — no provider required:
121
123
 
122
124
  ```ts
123
- import { SPACING, ICON_SIZES, RADIUS, SHADOWS, BREAKPOINTS } from '@retray-dev/ui-kit'
125
+ import { SPACING, ICON_SIZES, RADIUS, SHADOWS, BREAKPOINTS, TYPOGRAPHY } from '@retray-dev/ui-kit'
124
126
 
125
- <View style={{ gap: SPACING.md, padding: SPACING.lg, borderRadius: RADIUS.lg, ...SHADOWS.sm }} />
127
+ <View style={{ gap: SPACING.md, padding: SPACING.base, borderRadius: RADIUS.md, ...SHADOWS.sm }} />
126
128
  ```
127
129
 
128
130
  | Token | Keys |
129
131
  |-------|------|
130
- | `SPACING` | `xs` (4), `sm` (8), `md` (12), `lg` (16), `xl` (24), `2xl` (32), `3xl` (48) |
132
+ | `SPACING` | `xxs` (2), `xs` (4), `sm` (8), `md` (12), `base` (16), `lg` (24), `xl` (32), `xxl` (48), `section` (64) |
131
133
  | `ICON_SIZES` | `sm` (14), `md` (18), `lg` (22), `xl` (28), `2xl` (32) |
132
- | `RADIUS` | `sm` (4), `md` (8), `lg` (12), `xl` (16), `full` (9999) |
134
+ | `RADIUS` | `none` (0), `xs` (4), `sm` (8), `md` (14), `lg` (20), `xl` (32), `full` (9999) |
133
135
  | `SHADOWS` | `sm`, `md`, `lg`, `xl` — cross-platform shadow presets |
134
136
  | `BREAKPOINTS` | `wide` (700) |
137
+ | `TYPOGRAPHY` | 16 variants: `display-hero`, `display-xl`, `display-lg`, `display-md`, `display-sm`, `title-md`, `title-sm`, `body-md`, `body-sm`, `caption`, `caption-sm`, `badge-text`, `micro-label`, `uppercase-tag`, `button-lg`, `button-sm` |
135
138
 
136
139
  ## Components
137
140
 
@@ -139,7 +142,7 @@ import { SPACING, ICON_SIZES, RADIUS, SHADOWS, BREAKPOINTS } from '@retray-dev/u
139
142
  | ----------- | ----------------------------------------------------------------------------------------------- |
140
143
  | Display | `Text`, `Badge`, `Avatar`, `Separator`, `Spinner`, `Skeleton`, `Progress`, `CurrencyDisplay` |
141
144
  | Surfaces | `Card`, `AlertBanner`, `EmptyState`, `MediaCard` |
142
- | Form | `Button`, `IconButton`, `Input`, `CurrencyInput`, `CurrencyInputLarge`, `Textarea`, `Checkbox`, `Switch`, `Toggle`, `RadioGroup`, `Select`, `Slider` |
145
+ | Form | `Button`, `IconButton`, `Input`, `CurrencyInput`, `Textarea`, `Checkbox`, `Switch`, `Toggle`, `RadioGroup`, `Select`, `Slider` |
143
146
  | Composition | `Tabs`, `Accordion` |
144
147
  | Overlays | `Sheet`, `ConfirmDialog` |
145
148
  | Feedback | `Toast` / `ToastProvider` / `useToast` |
package/dist/index.d.mts CHANGED
@@ -413,10 +413,14 @@ interface SheetProps {
413
413
  title?: string;
414
414
  description?: string;
415
415
  children?: React.ReactNode;
416
- /** Style for the inner `BottomSheetView` content container. */
416
+ /** Style for the inner content container. */
417
417
  style?: ViewStyle;
418
+ /** Render children inside BottomSheetScrollView so gestures are handled correctly on both platforms. */
419
+ scrollable?: boolean;
420
+ /** Cap sheet height (dp). Children scroll when content exceeds this value. */
421
+ maxHeight?: number;
418
422
  }
419
- declare function Sheet({ open, onClose, title, description, children, style, }: SheetProps): React.JSX.Element;
423
+ declare function Sheet({ open, onClose, title, description, children, style, scrollable, maxHeight, }: SheetProps): React.JSX.Element;
420
424
 
421
425
  interface SelectOption {
422
426
  label: string;
package/dist/index.d.ts CHANGED
@@ -413,10 +413,14 @@ interface SheetProps {
413
413
  title?: string;
414
414
  description?: string;
415
415
  children?: React.ReactNode;
416
- /** Style for the inner `BottomSheetView` content container. */
416
+ /** Style for the inner content container. */
417
417
  style?: ViewStyle;
418
+ /** Render children inside BottomSheetScrollView so gestures are handled correctly on both platforms. */
419
+ scrollable?: boolean;
420
+ /** Cap sheet height (dp). Children scroll when content exceeds this value. */
421
+ maxHeight?: number;
418
422
  }
419
- declare function Sheet({ open, onClose, title, description, children, style, }: SheetProps): React.JSX.Element;
423
+ declare function Sheet({ open, onClose, title, description, children, style, scrollable, maxHeight, }: SheetProps): React.JSX.Element;
420
424
 
421
425
  interface SelectOption {
422
426
  label: string;
package/dist/index.js CHANGED
@@ -2159,7 +2159,9 @@ function Sheet({
2159
2159
  title,
2160
2160
  description,
2161
2161
  children,
2162
- style
2162
+ style,
2163
+ scrollable,
2164
+ maxHeight
2163
2165
  }) {
2164
2166
  const { colors } = useTheme();
2165
2167
  const ref = React25.useRef(null);
@@ -2180,6 +2182,8 @@ function Sheet({
2180
2182
  pressBehavior: "close"
2181
2183
  }
2182
2184
  );
2185
+ const headerNode = title || description ? /* @__PURE__ */ React25__default.default.createElement(reactNative.View, { style: styles21.header }, title ? /* @__PURE__ */ React25__default.default.createElement(reactNative.Text, { style: [styles21.title, { color: colors.foreground }], allowFontScaling: true }, title) : null, description ? /* @__PURE__ */ React25__default.default.createElement(reactNative.Text, { style: [styles21.description, { color: colors.foregroundMuted }], allowFontScaling: true }, description) : null) : null;
2186
+ const useScroll = scrollable || !!maxHeight;
2183
2187
  return /* @__PURE__ */ React25__default.default.createElement(
2184
2188
  bottomSheet.BottomSheetModal,
2185
2189
  {
@@ -2191,7 +2195,7 @@ function Sheet({
2191
2195
  handleIndicatorStyle: [styles21.handle, { backgroundColor: colors.border }],
2192
2196
  enablePanDownToClose: true
2193
2197
  },
2194
- /* @__PURE__ */ React25__default.default.createElement(bottomSheet.BottomSheetView, { style: [styles21.content, style] }, title || description ? /* @__PURE__ */ React25__default.default.createElement(reactNative.View, { style: styles21.header }, title ? /* @__PURE__ */ React25__default.default.createElement(reactNative.Text, { style: [styles21.title, { color: colors.foreground }], allowFontScaling: true }, title) : null, description ? /* @__PURE__ */ React25__default.default.createElement(reactNative.Text, { style: [styles21.description, { color: colors.foregroundMuted }], allowFontScaling: true }, description) : null) : null, children)
2198
+ /* @__PURE__ */ React25__default.default.createElement(bottomSheet.BottomSheetView, { style: maxHeight ? { maxHeight } : void 0 }, useScroll ? /* @__PURE__ */ React25__default.default.createElement(bottomSheet.BottomSheetScrollView, { contentContainerStyle: [styles21.content, style] }, headerNode, children) : /* @__PURE__ */ React25__default.default.createElement(bottomSheet.BottomSheetView, { style: [styles21.content, style] }, headerNode, children))
2195
2199
  );
2196
2200
  }
2197
2201
  var styles21 = reactNative.StyleSheet.create({
@@ -3024,15 +3028,6 @@ function ConfirmDialog({
3024
3028
  enablePanDownToClose: true
3025
3029
  },
3026
3030
  /* @__PURE__ */ React25__default.default.createElement(bottomSheet.BottomSheetView, { style: styles27.content }, /* @__PURE__ */ React25__default.default.createElement(reactNative.Text, { style: [styles27.title, { color: colors.foreground }], allowFontScaling: true }, title), description ? /* @__PURE__ */ React25__default.default.createElement(reactNative.Text, { style: [styles27.description, { color: colors.foregroundMuted }], allowFontScaling: true }, description) : null, /* @__PURE__ */ React25__default.default.createElement(reactNative.View, { style: styles27.actions }, /* @__PURE__ */ React25__default.default.createElement(
3027
- Button,
3028
- {
3029
- label: cancelLabel,
3030
- variant: "secondary",
3031
- fullWidth: true,
3032
- onPress: onCancel,
3033
- icon: /* @__PURE__ */ React25__default.default.createElement(vectorIcons.Feather, { name: "x", size: 15, color: colors.foreground })
3034
- }
3035
- ), /* @__PURE__ */ React25__default.default.createElement(
3036
3031
  Button,
3037
3032
  {
3038
3033
  label: confirmLabel,
@@ -3048,6 +3043,15 @@ function ConfirmDialog({
3048
3043
  }
3049
3044
  )
3050
3045
  }
3046
+ ), /* @__PURE__ */ React25__default.default.createElement(
3047
+ Button,
3048
+ {
3049
+ label: cancelLabel,
3050
+ variant: "secondary",
3051
+ fullWidth: true,
3052
+ onPress: onCancel,
3053
+ icon: /* @__PURE__ */ React25__default.default.createElement(vectorIcons.Feather, { name: "x", size: 15, color: colors.foreground })
3054
+ }
3051
3055
  )))
3052
3056
  );
3053
3057
  }
package/dist/index.mjs CHANGED
@@ -11,7 +11,7 @@ import { AntDesign as AntDesign$1, Feather as Feather$1, Entypo as Entypo$1, Fon
11
11
  import { LinearGradient } from 'expo-linear-gradient';
12
12
  import Animated11, { useSharedValue, useDerivedValue, withTiming, Easing as Easing$1, useAnimatedStyle, withSpring } from 'react-native-reanimated';
13
13
  import RNSlider from '@react-native-community/slider';
14
- import { BottomSheetModal, BottomSheetView, BottomSheetBackdrop } from '@gorhom/bottom-sheet';
14
+ import { BottomSheetModal, BottomSheetView, BottomSheetScrollView, BottomSheetBackdrop } from '@gorhom/bottom-sheet';
15
15
  export { BottomSheetModalProvider } from '@gorhom/bottom-sheet';
16
16
  import { Picker } from '@react-native-picker/picker';
17
17
  import { scheduleOnRN } from 'react-native-worklets';
@@ -2146,7 +2146,9 @@ function Sheet({
2146
2146
  title,
2147
2147
  description,
2148
2148
  children,
2149
- style
2149
+ style,
2150
+ scrollable,
2151
+ maxHeight
2150
2152
  }) {
2151
2153
  const { colors } = useTheme();
2152
2154
  const ref = useRef(null);
@@ -2167,6 +2169,8 @@ function Sheet({
2167
2169
  pressBehavior: "close"
2168
2170
  }
2169
2171
  );
2172
+ const headerNode = title || description ? /* @__PURE__ */ React25.createElement(View, { style: styles21.header }, title ? /* @__PURE__ */ React25.createElement(Text, { style: [styles21.title, { color: colors.foreground }], allowFontScaling: true }, title) : null, description ? /* @__PURE__ */ React25.createElement(Text, { style: [styles21.description, { color: colors.foregroundMuted }], allowFontScaling: true }, description) : null) : null;
2173
+ const useScroll = scrollable || !!maxHeight;
2170
2174
  return /* @__PURE__ */ React25.createElement(
2171
2175
  BottomSheetModal,
2172
2176
  {
@@ -2178,7 +2182,7 @@ function Sheet({
2178
2182
  handleIndicatorStyle: [styles21.handle, { backgroundColor: colors.border }],
2179
2183
  enablePanDownToClose: true
2180
2184
  },
2181
- /* @__PURE__ */ React25.createElement(BottomSheetView, { style: [styles21.content, style] }, title || description ? /* @__PURE__ */ React25.createElement(View, { style: styles21.header }, title ? /* @__PURE__ */ React25.createElement(Text, { style: [styles21.title, { color: colors.foreground }], allowFontScaling: true }, title) : null, description ? /* @__PURE__ */ React25.createElement(Text, { style: [styles21.description, { color: colors.foregroundMuted }], allowFontScaling: true }, description) : null) : null, children)
2185
+ /* @__PURE__ */ React25.createElement(BottomSheetView, { style: maxHeight ? { maxHeight } : void 0 }, useScroll ? /* @__PURE__ */ React25.createElement(BottomSheetScrollView, { contentContainerStyle: [styles21.content, style] }, headerNode, children) : /* @__PURE__ */ React25.createElement(BottomSheetView, { style: [styles21.content, style] }, headerNode, children))
2182
2186
  );
2183
2187
  }
2184
2188
  var styles21 = StyleSheet.create({
@@ -3011,15 +3015,6 @@ function ConfirmDialog({
3011
3015
  enablePanDownToClose: true
3012
3016
  },
3013
3017
  /* @__PURE__ */ React25.createElement(BottomSheetView, { style: styles27.content }, /* @__PURE__ */ React25.createElement(Text, { style: [styles27.title, { color: colors.foreground }], allowFontScaling: true }, title), description ? /* @__PURE__ */ React25.createElement(Text, { style: [styles27.description, { color: colors.foregroundMuted }], allowFontScaling: true }, description) : null, /* @__PURE__ */ React25.createElement(View, { style: styles27.actions }, /* @__PURE__ */ React25.createElement(
3014
- Button,
3015
- {
3016
- label: cancelLabel,
3017
- variant: "secondary",
3018
- fullWidth: true,
3019
- onPress: onCancel,
3020
- icon: /* @__PURE__ */ React25.createElement(Feather$1, { name: "x", size: 15, color: colors.foreground })
3021
- }
3022
- ), /* @__PURE__ */ React25.createElement(
3023
3018
  Button,
3024
3019
  {
3025
3020
  label: confirmLabel,
@@ -3035,6 +3030,15 @@ function ConfirmDialog({
3035
3030
  }
3036
3031
  )
3037
3032
  }
3033
+ ), /* @__PURE__ */ React25.createElement(
3034
+ Button,
3035
+ {
3036
+ label: cancelLabel,
3037
+ variant: "secondary",
3038
+ fullWidth: true,
3039
+ onPress: onCancel,
3040
+ icon: /* @__PURE__ */ React25.createElement(Feather$1, { name: "x", size: 15, color: colors.foreground })
3041
+ }
3038
3042
  )))
3039
3043
  );
3040
3044
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@retray-dev/ui-kit",
3
- "version": "5.1.0",
3
+ "version": "5.2.0",
4
4
  "description": "Personal UI Kit for React Native / Expo",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.mjs",
@@ -74,13 +74,6 @@ export function ConfirmDialog({
74
74
  </Text>
75
75
  ) : null}
76
76
  <View style={styles.actions}>
77
- <Button
78
- label={cancelLabel}
79
- variant="secondary"
80
- fullWidth
81
- onPress={onCancel}
82
- icon={<Feather name="x" size={15} color={colors.foreground} />}
83
- />
84
77
  <Button
85
78
  label={confirmLabel}
86
79
  variant={confirmVariant}
@@ -98,6 +91,13 @@ export function ConfirmDialog({
98
91
  />
99
92
  }
100
93
  />
94
+ <Button
95
+ label={cancelLabel}
96
+ variant="secondary"
97
+ fullWidth
98
+ onPress={onCancel}
99
+ icon={<Feather name="x" size={15} color={colors.foreground} />}
100
+ />
101
101
  </View>
102
102
  </BottomSheetView>
103
103
  </BottomSheetModal>
@@ -3,6 +3,7 @@ import { View, Text, StyleSheet, ViewStyle } from 'react-native'
3
3
  import {
4
4
  BottomSheetModal,
5
5
  BottomSheetView,
6
+ BottomSheetScrollView,
6
7
  BottomSheetBackdrop,
7
8
  BottomSheetModalProvider,
8
9
  type BottomSheetBackdropProps,
@@ -19,8 +20,12 @@ export interface SheetProps {
19
20
  title?: string
20
21
  description?: string
21
22
  children?: React.ReactNode
22
- /** Style for the inner `BottomSheetView` content container. */
23
+ /** Style for the inner content container. */
23
24
  style?: ViewStyle
25
+ /** Render children inside BottomSheetScrollView so gestures are handled correctly on both platforms. */
26
+ scrollable?: boolean
27
+ /** Cap sheet height (dp). Children scroll when content exceeds this value. */
28
+ maxHeight?: number
24
29
  }
25
30
 
26
31
  export function Sheet({
@@ -30,6 +35,8 @@ export function Sheet({
30
35
  description,
31
36
  children,
32
37
  style,
38
+ scrollable,
39
+ maxHeight,
33
40
  }: SheetProps) {
34
41
  const { colors } = useTheme()
35
42
  const ref = useRef<BottomSheetModal>(null)
@@ -52,6 +59,21 @@ export function Sheet({
52
59
  />
53
60
  )
54
61
 
62
+ const headerNode = (title || description) ? (
63
+ <View style={styles.header}>
64
+ {title ? (
65
+ <Text style={[styles.title, { color: colors.foreground }]} allowFontScaling={true}>{title}</Text>
66
+ ) : null}
67
+ {description ? (
68
+ <Text style={[styles.description, { color: colors.foregroundMuted }]} allowFontScaling={true}>
69
+ {description}
70
+ </Text>
71
+ ) : null}
72
+ </View>
73
+ ) : null
74
+
75
+ const useScroll = scrollable || !!maxHeight
76
+
55
77
  return (
56
78
  <BottomSheetModal
57
79
  ref={ref}
@@ -62,20 +84,18 @@ export function Sheet({
62
84
  handleIndicatorStyle={[styles.handle, { backgroundColor: colors.border }]}
63
85
  enablePanDownToClose
64
86
  >
65
- <BottomSheetView style={[styles.content, style]}>
66
- {title || description ? (
67
- <View style={styles.header}>
68
- {title ? (
69
- <Text style={[styles.title, { color: colors.foreground }]} allowFontScaling={true}>{title}</Text>
70
- ) : null}
71
- {description ? (
72
- <Text style={[styles.description, { color: colors.foregroundMuted }]} allowFontScaling={true}>
73
- {description}
74
- </Text>
75
- ) : null}
76
- </View>
77
- ) : null}
78
- {children}
87
+ <BottomSheetView style={maxHeight ? { maxHeight } : undefined}>
88
+ {useScroll ? (
89
+ <BottomSheetScrollView contentContainerStyle={[styles.content, style]}>
90
+ {headerNode}
91
+ {children}
92
+ </BottomSheetScrollView>
93
+ ) : (
94
+ <BottomSheetView style={[styles.content, style]}>
95
+ {headerNode}
96
+ {children}
97
+ </BottomSheetView>
98
+ )}
79
99
  </BottomSheetView>
80
100
  </BottomSheetModal>
81
101
  )