@retray-dev/ui-kit 6.2.0 → 9.0.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 +997 -20
- package/EXAMPLES.md +250 -2
- package/README.md +21 -14
- package/dist/Accordion.d.mts +28 -0
- package/dist/Accordion.d.ts +28 -0
- package/dist/Accordion.js +392 -0
- package/dist/Accordion.mjs +7 -0
- package/dist/AlertBanner.d.mts +16 -0
- package/dist/AlertBanner.d.ts +16 -0
- package/dist/AlertBanner.js +250 -0
- package/dist/AlertBanner.mjs +6 -0
- package/dist/AppHeader.d.mts +40 -0
- package/dist/AppHeader.d.ts +40 -0
- package/dist/AppHeader.js +515 -0
- package/dist/AppHeader.mjs +10 -0
- package/dist/Avatar.d.mts +20 -0
- package/dist/Avatar.d.ts +20 -0
- package/dist/Avatar.js +244 -0
- package/dist/Avatar.mjs +4 -0
- package/dist/Badge.d.mts +26 -0
- package/dist/Badge.d.ts +26 -0
- package/dist/Badge.js +257 -0
- package/dist/Badge.mjs +5 -0
- package/dist/Button.d.mts +30 -0
- package/dist/Button.d.ts +30 -0
- package/dist/Button.js +432 -0
- package/dist/Button.mjs +9 -0
- package/dist/ButtonGroup.d.mts +26 -0
- package/dist/ButtonGroup.d.ts +26 -0
- package/dist/ButtonGroup.js +52 -0
- package/dist/ButtonGroup.mjs +3 -0
- package/dist/Card.d.mts +39 -0
- package/dist/Card.d.ts +39 -0
- package/dist/Card.js +349 -0
- package/dist/Card.mjs +8 -0
- package/dist/CategoryStrip.d.mts +26 -0
- package/dist/CategoryStrip.d.ts +26 -0
- package/dist/CategoryStrip.js +453 -0
- package/dist/CategoryStrip.mjs +9 -0
- package/dist/Checkbox.d.mts +14 -0
- package/dist/Checkbox.d.ts +14 -0
- package/dist/Checkbox.js +336 -0
- package/dist/Checkbox.mjs +7 -0
- package/dist/Chip.d.mts +31 -0
- package/dist/Chip.d.ts +31 -0
- package/dist/Chip.js +403 -0
- package/dist/Chip.mjs +8 -0
- package/dist/ConfirmDialog.d.mts +15 -0
- package/dist/ConfirmDialog.d.ts +15 -0
- package/dist/ConfirmDialog.js +560 -0
- package/dist/ConfirmDialog.mjs +10 -0
- package/dist/CurrencyDisplay.d.mts +24 -0
- package/dist/CurrencyDisplay.d.ts +24 -0
- package/dist/CurrencyDisplay.js +189 -0
- package/dist/CurrencyDisplay.mjs +4 -0
- package/dist/CurrencyInput.d.mts +26 -0
- package/dist/CurrencyInput.d.ts +26 -0
- package/dist/CurrencyInput.js +408 -0
- package/dist/CurrencyInput.mjs +8 -0
- package/dist/DetailRow.d.mts +32 -0
- package/dist/DetailRow.d.ts +32 -0
- package/dist/DetailRow.js +275 -0
- package/dist/DetailRow.mjs +5 -0
- package/dist/EmptyState.d.mts +27 -0
- package/dist/EmptyState.d.ts +27 -0
- package/dist/EmptyState.js +523 -0
- package/dist/EmptyState.mjs +10 -0
- package/dist/ErrorBoundary.d.mts +42 -0
- package/dist/ErrorBoundary.d.ts +42 -0
- package/dist/ErrorBoundary.js +351 -0
- package/dist/ErrorBoundary.mjs +7 -0
- package/dist/Form.d.mts +52 -0
- package/dist/Form.d.ts +52 -0
- package/dist/Form.js +204 -0
- package/dist/Form.mjs +4 -0
- package/dist/HolographicCard.d.mts +55 -0
- package/dist/HolographicCard.d.ts +55 -0
- package/dist/HolographicCard.js +316 -0
- package/dist/HolographicCard.mjs +191 -0
- package/dist/IconButton.d.mts +27 -0
- package/dist/IconButton.d.ts +27 -0
- package/dist/IconButton.js +400 -0
- package/dist/IconButton.mjs +8 -0
- package/dist/ImageViewer.d.mts +23 -0
- package/dist/ImageViewer.d.ts +23 -0
- package/dist/ImageViewer.js +582 -0
- package/dist/ImageViewer.mjs +8 -0
- package/dist/Input.d.mts +23 -0
- package/dist/Input.d.ts +23 -0
- package/dist/Input.js +351 -0
- package/dist/Input.mjs +7 -0
- package/dist/LabelValue.d.mts +16 -0
- package/dist/LabelValue.d.ts +16 -0
- package/dist/LabelValue.js +225 -0
- package/dist/LabelValue.mjs +5 -0
- package/dist/ListGroup.d.mts +34 -0
- package/dist/ListGroup.d.ts +34 -0
- package/dist/ListGroup.js +217 -0
- package/dist/ListGroup.mjs +5 -0
- package/dist/ListItem.d.mts +64 -0
- package/dist/ListItem.d.ts +64 -0
- package/dist/ListItem.js +444 -0
- package/dist/ListItem.mjs +9 -0
- package/dist/MediaCard.d.mts +39 -0
- package/dist/MediaCard.d.ts +39 -0
- package/dist/MediaCard.js +475 -0
- package/dist/MediaCard.mjs +9 -0
- package/dist/MenuGroup.d.mts +34 -0
- package/dist/MenuGroup.d.ts +34 -0
- package/dist/MenuGroup.js +217 -0
- package/dist/MenuGroup.mjs +5 -0
- package/dist/MenuItem.d.mts +48 -0
- package/dist/MenuItem.d.ts +48 -0
- package/dist/MenuItem.js +415 -0
- package/dist/MenuItem.mjs +9 -0
- package/dist/MonthPicker.d.mts +28 -0
- package/dist/MonthPicker.d.ts +28 -0
- package/dist/MonthPicker.js +297 -0
- package/dist/MonthPicker.mjs +5 -0
- package/dist/PagerDots.d.mts +35 -0
- package/dist/PagerDots.d.ts +35 -0
- package/dist/PagerDots.js +392 -0
- package/dist/PagerDots.mjs +7 -0
- package/dist/Pressable.d.mts +34 -0
- package/dist/Pressable.d.ts +34 -0
- package/dist/Pressable.js +143 -0
- package/dist/Pressable.mjs +5 -0
- package/dist/PricingCard.d.mts +50 -0
- package/dist/PricingCard.d.ts +50 -0
- package/dist/PricingCard.js +636 -0
- package/dist/PricingCard.mjs +11 -0
- package/dist/Progress.d.mts +14 -0
- package/dist/Progress.d.ts +14 -0
- package/dist/Progress.js +191 -0
- package/dist/Progress.mjs +5 -0
- package/dist/RadioGroup.d.mts +19 -0
- package/dist/RadioGroup.d.ts +19 -0
- package/dist/RadioGroup.js +392 -0
- package/dist/RadioGroup.mjs +7 -0
- package/dist/RetrayProvider.d.mts +2 -0
- package/dist/RetrayProvider.d.ts +2 -0
- package/dist/RetrayProvider.js +214 -0
- package/dist/RetrayProvider.mjs +5 -0
- package/dist/Select.d.mts +22 -0
- package/dist/Select.d.ts +22 -0
- package/dist/Select.js +488 -0
- package/dist/Select.mjs +7 -0
- package/dist/SelectableGrid.d.mts +44 -0
- package/dist/SelectableGrid.d.ts +44 -0
- package/dist/SelectableGrid.js +448 -0
- package/dist/SelectableGrid.mjs +9 -0
- package/dist/Separator.d.mts +10 -0
- package/dist/Separator.d.ts +10 -0
- package/dist/Separator.js +156 -0
- package/dist/Separator.mjs +3 -0
- package/dist/Sheet.d.mts +93 -0
- package/dist/Sheet.d.ts +93 -0
- package/dist/Sheet.js +450 -0
- package/dist/Sheet.mjs +6 -0
- package/dist/Skeleton.d.mts +67 -0
- package/dist/Skeleton.d.ts +67 -0
- package/dist/Skeleton.js +266 -0
- package/dist/Skeleton.mjs +6 -0
- package/dist/Slider.d.mts +20 -0
- package/dist/Slider.d.ts +20 -0
- package/dist/Slider.js +279 -0
- package/dist/Slider.mjs +5 -0
- package/dist/Spinner.d.mts +12 -0
- package/dist/Spinner.d.ts +12 -0
- package/dist/Spinner.js +193 -0
- package/dist/Spinner.mjs +4 -0
- package/dist/Switch.d.mts +13 -0
- package/dist/Switch.d.ts +13 -0
- package/dist/Switch.js +311 -0
- package/dist/Switch.mjs +6 -0
- package/dist/TabBar.d.mts +42 -0
- package/dist/TabBar.d.ts +42 -0
- package/dist/TabBar.js +361 -0
- package/dist/TabBar.mjs +6 -0
- package/dist/Tabs.d.mts +27 -0
- package/dist/Tabs.d.ts +27 -0
- package/dist/Tabs.js +419 -0
- package/dist/Tabs.mjs +7 -0
- package/dist/Text.d.mts +12 -0
- package/dist/Text.d.ts +12 -0
- package/dist/Text.js +327 -0
- package/dist/Text.mjs +5 -0
- package/dist/Textarea.d.mts +16 -0
- package/dist/Textarea.d.ts +16 -0
- package/dist/Textarea.js +333 -0
- package/dist/Textarea.mjs +7 -0
- package/dist/Toast.d.mts +47 -0
- package/dist/Toast.d.ts +47 -0
- package/dist/Toast.js +185 -0
- package/dist/Toast.mjs +4 -0
- package/dist/Toggle.d.mts +36 -0
- package/dist/Toggle.d.ts +36 -0
- package/dist/Toggle.js +412 -0
- package/dist/Toggle.mjs +8 -0
- package/dist/VirtualList.d.mts +19 -0
- package/dist/VirtualList.d.ts +19 -0
- package/dist/VirtualList.js +38 -0
- package/dist/VirtualList.mjs +2 -0
- package/dist/chunk-26BCI223.mjs +14 -0
- package/dist/chunk-2CE3TQVY.mjs +11 -0
- package/dist/chunk-2TFTAWVJ.mjs +131 -0
- package/dist/chunk-2UYENBLV.mjs +49 -0
- package/dist/chunk-3BBOZ3OQ.mjs +41 -0
- package/dist/chunk-3DKJ2GIC.mjs +30 -0
- package/dist/chunk-3U4SSNWP.mjs +120 -0
- package/dist/chunk-4I7D47FH.mjs +139 -0
- package/dist/chunk-4K625MVM.mjs +142 -0
- package/dist/chunk-6OAZJ577.mjs +98 -0
- package/dist/chunk-6Q64UFIA.mjs +71 -0
- package/dist/chunk-756RAKE4.mjs +145 -0
- package/dist/chunk-7QHVVCB3.mjs +115 -0
- package/dist/chunk-A3A6KNQN.mjs +245 -0
- package/dist/chunk-A4MDAP7G.mjs +42 -0
- package/dist/chunk-AJ7ZDNBT.mjs +120 -0
- package/dist/chunk-AV4EMIRH.mjs +94 -0
- package/dist/chunk-AZJF2BLK.mjs +115 -0
- package/dist/chunk-BNP626TY.mjs +159 -0
- package/dist/chunk-BRKYVJVV.mjs +60 -0
- package/dist/chunk-DVK4G2GT.mjs +59 -0
- package/dist/chunk-EH745HE5.mjs +127 -0
- package/dist/chunk-EJ7ZPXOH.mjs +163 -0
- package/dist/chunk-GD6KXMG5.mjs +106 -0
- package/dist/chunk-GQYFLP3D.mjs +187 -0
- package/dist/chunk-ID72TK46.mjs +111 -0
- package/dist/chunk-IRRY3CRZ.mjs +82 -0
- package/dist/chunk-JB67UOB5.mjs +92 -0
- package/dist/chunk-JMOZEC77.mjs +90 -0
- package/dist/chunk-JT7HKXRB.mjs +114 -0
- package/dist/chunk-KIHCWCWL.mjs +124 -0
- package/dist/chunk-LXJIIOYQ.mjs +104 -0
- package/dist/chunk-M6ZXVBTK.mjs +64 -0
- package/dist/chunk-MAC465BB.mjs +61 -0
- package/dist/chunk-MBMXYJJV.mjs +36 -0
- package/dist/chunk-MLF3EZFW.mjs +119 -0
- package/dist/chunk-MX6HRKMI.mjs +29 -0
- package/dist/chunk-NA7PARID.mjs +147 -0
- package/dist/chunk-NC5ZTR2Y.mjs +32 -0
- package/dist/chunk-O3HA6TYM.mjs +139 -0
- package/dist/chunk-OB4JUQ3O.mjs +51 -0
- package/dist/chunk-PFZTM6D5.mjs +238 -0
- package/dist/chunk-QKH5ZOD5.mjs +97 -0
- package/dist/chunk-QY3X2UYR.mjs +191 -0
- package/dist/chunk-SOA2Z4RB.mjs +82 -0
- package/dist/chunk-SOYNZDVY.mjs +151 -0
- package/dist/chunk-T7XZ7H7Y.mjs +57 -0
- package/dist/chunk-TERDKCLE.mjs +74 -0
- package/dist/chunk-UREA2GYY.mjs +113 -0
- package/dist/chunk-VGTDN7SW.mjs +164 -0
- package/dist/chunk-VQ57HWPL.mjs +144 -0
- package/dist/chunk-WBOOUHSS.mjs +62 -0
- package/dist/chunk-WJLKJMKR.mjs +78 -0
- package/dist/chunk-X4G6APW6.mjs +134 -0
- package/dist/chunk-Y6FXYEAI.mjs +8 -0
- package/dist/chunk-YFZ3ELX5.mjs +16 -0
- package/dist/chunk-YNROWHQJ.mjs +46 -0
- package/dist/chunk-Z4BVUWW6.mjs +196 -0
- package/dist/chunk-ZJKGQMYH.mjs +131 -0
- package/dist/index-wt-orHUi.d.mts +85 -0
- package/dist/index-wt-orHUi.d.ts +85 -0
- package/dist/index.d.mts +149 -920
- package/dist/index.d.ts +149 -920
- package/dist/index.js +2560 -970
- package/dist/index.mjs +60 -3895
- package/package.json +55 -16
- package/src/assets/fonts/Sohne-Bold.otf +0 -0
- package/src/assets/fonts/Sohne-BoldItalic.otf +0 -0
- package/src/assets/fonts/Sohne-ExtraBold.otf +0 -0
- package/src/assets/fonts/Sohne-ExtraBoldItalic.otf +0 -0
- package/src/assets/fonts/Sohne-ExtraLight.otf +0 -0
- package/src/assets/fonts/Sohne-ExtraLightItalic.otf +0 -0
- package/src/assets/fonts/Sohne-Italic.otf +0 -0
- package/src/assets/fonts/Sohne-Light.otf +0 -0
- package/src/assets/fonts/Sohne-LightItalic.otf +0 -0
- package/src/assets/fonts/Sohne-Medium.otf +0 -0
- package/src/assets/fonts/Sohne-MediumItalic.otf +0 -0
- package/src/assets/fonts/Sohne-Regular.otf +0 -0
- package/src/assets/fonts/Sohne-SemiBold.otf +0 -0
- package/src/assets/fonts/Sohne-SemiBoldItalic.otf +0 -0
- package/src/assets/fonts/SohneMono-Bold.otf +0 -0
- package/src/assets/fonts/SohneMono-BoldItalic.otf +0 -0
- package/src/assets/fonts/SohneMono-ExtraBold.otf +0 -0
- package/src/assets/fonts/SohneMono-ExtraBoldItalic.otf +0 -0
- package/src/assets/fonts/SohneMono-ExtraLight.otf +0 -0
- package/src/assets/fonts/SohneMono-ExtraLightItalic.otf +0 -0
- package/src/assets/fonts/SohneMono-Italic.otf +0 -0
- package/src/assets/fonts/SohneMono-Light.otf +0 -0
- package/src/assets/fonts/SohneMono-LightItalic.otf +0 -0
- package/src/assets/fonts/SohneMono-Medium.otf +0 -0
- package/src/assets/fonts/SohneMono-MediumItalic.otf +0 -0
- package/src/assets/fonts/SohneMono-Regular.otf +0 -0
- package/src/assets/fonts/SohneMono-SemiBold.otf +0 -0
- package/src/assets/fonts/SohneMono-SemiBoldItalic.otf +0 -0
- package/src/components/Accordion/Accordion.tsx +15 -4
- package/src/components/AlertBanner/AlertBanner.tsx +38 -12
- package/src/components/AppHeader/AppHeader.tsx +172 -0
- package/src/components/AppHeader/index.ts +1 -0
- package/src/components/Avatar/Avatar.tsx +14 -4
- package/src/components/Badge/Badge.tsx +12 -3
- package/src/components/Button/Button.tsx +30 -38
- package/src/components/ButtonGroup/ButtonGroup.tsx +13 -10
- package/src/components/Card/Card.tsx +29 -57
- package/src/components/CategoryStrip/CategoryStrip.tsx +41 -42
- package/src/components/Checkbox/Checkbox.tsx +36 -45
- package/src/components/Chip/Chip.tsx +41 -48
- package/src/components/ConfirmDialog/ConfirmDialog.tsx +2 -2
- package/src/components/CurrencyDisplay/CurrencyDisplay.tsx +4 -2
- package/src/components/CurrencyInput/CurrencyInput.tsx +12 -10
- package/src/components/DetailRow/DetailRow.tsx +9 -7
- package/src/components/EmptyState/EmptyState.tsx +4 -3
- package/src/components/ErrorBoundary/ErrorBoundary.tsx +153 -0
- package/src/components/ErrorBoundary/index.ts +1 -0
- package/src/components/Form/Form.tsx +149 -0
- package/src/components/Form/index.ts +1 -0
- package/src/components/HolographicCard/HolographicCard.tsx +315 -0
- package/src/components/HolographicCard/index.ts +1 -0
- package/src/components/IconButton/IconButton.tsx +23 -29
- package/src/components/ImageViewer/ImageViewer.tsx +290 -0
- package/src/components/ImageViewer/index.ts +1 -0
- package/src/components/Input/Input.tsx +27 -31
- package/src/components/LabelValue/LabelValue.tsx +6 -4
- package/src/components/ListGroup/ListGroup.tsx +145 -0
- package/src/components/ListGroup/index.ts +1 -0
- package/src/components/ListItem/ListItem.tsx +78 -76
- package/src/components/MediaCard/MediaCard.tsx +15 -7
- package/src/components/MenuGroup/MenuGroup.tsx +145 -0
- package/src/components/MenuGroup/index.ts +1 -0
- package/src/components/MenuItem/MenuItem.tsx +16 -33
- package/src/components/MonthPicker/MonthPicker.tsx +41 -15
- package/src/components/MonthPicker/index.ts +1 -1
- package/src/components/PagerDots/PagerDots.tsx +200 -0
- package/src/components/PagerDots/index.ts +1 -0
- package/src/components/Pressable/Pressable.tsx +19 -35
- package/src/components/PricingCard/PricingCard.tsx +220 -0
- package/src/components/PricingCard/index.ts +1 -0
- package/src/components/RadioGroup/RadioGroup.tsx +23 -39
- package/src/components/RetrayProvider/RetrayProvider.tsx +59 -0
- package/src/components/RetrayProvider/index.ts +1 -0
- package/src/components/Select/Select.tsx +6 -6
- package/src/components/SelectableGrid/SelectableGrid.tsx +205 -0
- package/src/components/SelectableGrid/index.ts +1 -0
- package/src/components/Separator/Separator.tsx +1 -3
- package/src/components/Sheet/Sheet.tsx +146 -18
- package/src/components/Skeleton/Skeleton.tsx +143 -2
- package/src/components/Slider/Slider.tsx +2 -2
- package/src/components/Spinner/Spinner.tsx +18 -3
- package/src/components/Switch/Switch.tsx +44 -49
- package/src/components/TabBar/TabBar.tsx +169 -0
- package/src/components/TabBar/index.ts +1 -0
- package/src/components/Tabs/Tabs.tsx +45 -44
- package/src/components/Text/Text.tsx +5 -1
- package/src/components/Textarea/Textarea.tsx +18 -14
- package/src/components/Toast/Toast.tsx +6 -6
- package/src/components/Toggle/Toggle.tsx +80 -72
- package/src/components/VirtualList/VirtualList.tsx +60 -0
- package/src/components/VirtualList/index.ts +1 -0
- package/src/fonts.ts +41 -20
- package/src/index.ts +28 -3
- package/src/theme/colors.ts +53 -39
- package/src/theme/types.ts +3 -0
- package/src/tokens.ts +49 -39
- package/src/utils/animations.ts +29 -1
- package/src/utils/fontGuard.ts +34 -0
- package/src/utils/haptics.ts +211 -9
- package/src/utils/icons.ts +47 -20
- package/src/utils/pressable.ts +66 -0
- package/src/utils/usePressScale.ts +2 -0
- package/src/assets/fonts/Poppins-Black.ttf +0 -0
- package/src/assets/fonts/Poppins-BlackItalic.ttf +0 -0
- package/src/assets/fonts/Poppins-Bold.ttf +0 -0
- package/src/assets/fonts/Poppins-BoldItalic.ttf +0 -0
- package/src/assets/fonts/Poppins-ExtraBold.ttf +0 -0
- package/src/assets/fonts/Poppins-ExtraBoldItalic.ttf +0 -0
- package/src/assets/fonts/Poppins-ExtraLight.ttf +0 -0
- package/src/assets/fonts/Poppins-ExtraLightItalic.ttf +0 -0
- package/src/assets/fonts/Poppins-Italic.ttf +0 -0
- package/src/assets/fonts/Poppins-Light.ttf +0 -0
- package/src/assets/fonts/Poppins-LightItalic.ttf +0 -0
- package/src/assets/fonts/Poppins-Medium.ttf +0 -0
- package/src/assets/fonts/Poppins-MediumItalic.ttf +0 -0
- package/src/assets/fonts/Poppins-Regular.ttf +0 -0
- package/src/assets/fonts/Poppins-SemiBold.ttf +0 -0
- package/src/assets/fonts/Poppins-SemiBoldItalic.ttf +0 -0
- package/src/assets/fonts/Poppins-Thin.ttf +0 -0
- package/src/assets/fonts/Poppins-ThinItalic.ttf +0 -0
package/COMPONENTS.md
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# @retray-dev/ui-kit — Component Reference (
|
|
1
|
+
# @retray-dev/ui-kit — Component Reference (v8.0.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
|
|
|
@@ -11,7 +11,27 @@ This file is the AI reference for this package. It is shipped inside the npm pac
|
|
|
11
11
|
|
|
12
12
|
## Setup (Required)
|
|
13
13
|
|
|
14
|
-
|
|
14
|
+
### Recommended: `RetrayProvider` (one wrapper)
|
|
15
|
+
|
|
16
|
+
Since v8, a single `RetrayProvider` wires all five required providers in the correct order — use this and skip the manual nesting:
|
|
17
|
+
|
|
18
|
+
```tsx
|
|
19
|
+
import { RetrayProvider } from '@retray-dev/ui-kit'
|
|
20
|
+
|
|
21
|
+
export default function App() {
|
|
22
|
+
const [fontsLoaded] = useFonts(SohneFonts)
|
|
23
|
+
if (!fontsLoaded) return null
|
|
24
|
+
return (
|
|
25
|
+
<RetrayProvider colorScheme="system">
|
|
26
|
+
{/* your app */}
|
|
27
|
+
</RetrayProvider>
|
|
28
|
+
)
|
|
29
|
+
}
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
### Manual (equivalent) — if you need a custom tree
|
|
33
|
+
|
|
34
|
+
`RetrayProvider` is exactly this; the individual providers stay exported. Order is mandatory:
|
|
15
35
|
|
|
16
36
|
```tsx
|
|
17
37
|
import { SafeAreaProvider, initialWindowMetrics } from 'react-native-safe-area-context'
|
|
@@ -42,16 +62,76 @@ export default function App() {
|
|
|
42
62
|
- `BottomSheetModalProvider` must be inside `GestureHandlerRootView`
|
|
43
63
|
- `ToastProvider` wraps children and renders `Toaster` (from `sonner-native`) internally
|
|
44
64
|
|
|
45
|
-
|
|
65
|
+
### Peer dependencies
|
|
66
|
+
|
|
67
|
+
Install all required peers (Expo projects: swap `pnpm add` for `npx expo install` to get SDK-pinned versions):
|
|
68
|
+
|
|
69
|
+
```bash
|
|
70
|
+
pnpm add expo-font expo-haptics expo-linear-gradient react-native-safe-area-context @gorhom/bottom-sheet react-native-reanimated react-native-gesture-handler react-native-worklets @react-native-picker/picker @react-native-community/slider @expo/vector-icons react-native-size-matters react-native-svg react-native-screens sonner-native pressto
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
Then add the Worklets Babel plugin to `babel.config.js` (required by `@gorhom/bottom-sheet` and the pressable animations):
|
|
74
|
+
|
|
75
|
+
```js
|
|
76
|
+
module.exports = function (api) {
|
|
77
|
+
api.cache(true)
|
|
78
|
+
return {
|
|
79
|
+
presets: ['babel-preset-expo'],
|
|
80
|
+
plugins: ['react-native-worklets/plugin'], // NOT react-native-reanimated/plugin
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
| Peer | Required? | Used by | Notes |
|
|
86
|
+
|------|-----------|---------|-------|
|
|
87
|
+
| `pressto` | **Required** | Every interactive component | Press-scale animations (`src/utils/pressable.ts`). Static import via the barrel — omitting it crashes the module load, not just one component. |
|
|
88
|
+
| `sonner-native` | **Required** | `Toast` | Also pulls `react-native-svg` + `react-native-screens`. |
|
|
89
|
+
| `react-native-reanimated` (≥4.0) | **Required** | Animations, `Sheet`, pressables | Needs the `react-native-worklets/plugin` Babel plugin. |
|
|
90
|
+
| `react-native-gesture-handler` | **Required** | `Sheet`, pressables | Wrap app in `GestureHandlerRootView`. |
|
|
91
|
+
| `@gorhom/bottom-sheet` (≥5.2.0) | **Required** | `Sheet`, `ConfirmDialog` | 5.1.x crashes on Reanimated v4. |
|
|
92
|
+
| `expo-haptics` | **Required** | Haptic feedback (all interactions) | Web-safe wrapper, no-op on web. |
|
|
93
|
+
| `expo-font` | **Required** | All `Text` | Load `SohneFonts` at app root before rendering. |
|
|
94
|
+
| `expo-linear-gradient` | **Required** | `Skeleton` shimmer | |
|
|
95
|
+
| `@react-native-picker/picker` | **Required** | `Select` | |
|
|
96
|
+
| `@react-native-community/slider` | **Required** | `Slider` | |
|
|
97
|
+
| `@expo/vector-icons` | **Required** | Icons everywhere | |
|
|
98
|
+
| `react-native-size-matters` | **Required** | Responsive scaling | |
|
|
99
|
+
| `react-native-pulsar` | **Optional** | Rich haptics on dev builds | Loaded via dynamic `import()` + try/catch; falls back to `expo-haptics`. Skip in Expo Go. `pnpm add react-native-pulsar` |
|
|
100
|
+
| `@shopify/react-native-skia` + `expo-sensors` | **Optional** | Deep-import `HolographicCard` only | `pnpm add @shopify/react-native-skia expo-sensors` |
|
|
101
|
+
|
|
102
|
+
> **Dev build vs Expo Go:** Everything in the kit runs in **Expo Go** — `react-native-pulsar` is the only native module that needs a custom dev build, and it degrades gracefully (basic haptics) when absent. No component is broken by running in Expo Go.
|
|
103
|
+
|
|
104
|
+
### Troubleshooting: `react-native-screens` codegen on RN 0.83
|
|
105
|
+
|
|
106
|
+
Toast pulls `sonner-native`, which imports `react-native-screens` via its `src/*` path. On **React Native 0.83** that source trips a `@react-native/codegen` parse error (`CT.WithDefault` not parseable). It is a `react-native-screens` issue, not the kit — but since Toast surfaces it, add this resolver to your `metro.config.js` to force the compiled `lib/commonjs` build:
|
|
107
|
+
|
|
108
|
+
```js
|
|
109
|
+
// metro.config.js
|
|
110
|
+
const { getDefaultConfig } = require('expo/metro-config')
|
|
111
|
+
const config = getDefaultConfig(__dirname)
|
|
112
|
+
|
|
113
|
+
const defaultResolveRequest = config.resolver.resolveRequest
|
|
114
|
+
config.resolver.resolveRequest = (context, moduleName, platform) => {
|
|
115
|
+
// Force react-native-screens to its compiled output, bypassing the src codegen bug.
|
|
116
|
+
if (moduleName.startsWith('react-native-screens/src')) {
|
|
117
|
+
moduleName = moduleName.replace('react-native-screens/src', 'react-native-screens/lib/commonjs')
|
|
118
|
+
}
|
|
119
|
+
return (defaultResolveRequest ?? context.resolveRequest)(context, moduleName, platform)
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
module.exports = config
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
## Typography — Sohne (Required)
|
|
46
126
|
|
|
47
|
-
All components use **
|
|
127
|
+
All components use **Sohne** as the font family. You **must** load it before rendering any UI kit component.
|
|
48
128
|
|
|
49
129
|
```tsx
|
|
50
130
|
import { useFonts } from 'expo-font'
|
|
51
|
-
import {
|
|
131
|
+
import { SohneFonts } from '@retray-dev/ui-kit/fonts'
|
|
52
132
|
|
|
53
133
|
export default function App() {
|
|
54
|
-
const [fontsLoaded] = useFonts(
|
|
134
|
+
const [fontsLoaded] = useFonts(SohneFonts)
|
|
55
135
|
if (!fontsLoaded) return null
|
|
56
136
|
return (
|
|
57
137
|
// ... your providers and app
|
|
@@ -60,16 +140,16 @@ export default function App() {
|
|
|
60
140
|
```
|
|
61
141
|
|
|
62
142
|
**How it works:**
|
|
63
|
-
1. Import `
|
|
143
|
+
1. Import `SohneFonts` from `@retray-dev/ui-kit/fonts` (separate export path)
|
|
64
144
|
2. Pass it to `expo-font`'s `useFonts()` hook at your app root
|
|
65
145
|
3. Metro resolves font files from `node_modules/@retray-dev/ui-kit/src/assets/fonts/` at bundle time
|
|
66
|
-
4. All library components reference fonts by family name (e.g., `fontFamily: '
|
|
146
|
+
4. All library components reference fonts by family name (e.g., `fontFamily: 'Sohne-SemiBold'`)
|
|
67
147
|
|
|
68
148
|
**Included weights:**
|
|
69
|
-
-
|
|
70
|
-
-
|
|
149
|
+
- Sohne: `Sohne-ExtraLight`, `Sohne-Light`, `Sohne-Regular`, `Sohne-Medium`, `Sohne-SemiBold`, `Sohne-Bold`, `Sohne-ExtraBold` + italic variants
|
|
150
|
+
- SohneMono: `SohneMono-ExtraLight`, `SohneMono-Light`, `SohneMono-Regular`, `SohneMono-Medium`, `SohneMono-SemiBold`, `SohneMono-Bold`, `SohneMono-ExtraBold` + italic variants
|
|
71
151
|
|
|
72
|
-
**Total:
|
|
152
|
+
**Total: 28 font files** exported from the package. Font `.otf` files ship as raw assets in `src/assets/fonts/` — NOT bundled into `dist/`. Metro resolves `require()` calls at build time.
|
|
73
153
|
|
|
74
154
|
Pair with `expo-splash-screen` in production:
|
|
75
155
|
```tsx
|
|
@@ -403,6 +483,92 @@ Access resolved values via `useTheme().colors.overlay`, `.accentResolved`, `.acc
|
|
|
403
483
|
|
|
404
484
|
---
|
|
405
485
|
|
|
486
|
+
## Migration Guide: v6 → v7
|
|
487
|
+
|
|
488
|
+
### Breaking Changes
|
|
489
|
+
|
|
490
|
+
**No prop API changes** — all component imports and props are identical. This is a breaking version bump due to visual behavior changes that affect rendered output.
|
|
491
|
+
|
|
492
|
+
**Typography scale corrected:**
|
|
493
|
+
|
|
494
|
+
| Token | v6 | v7 | Reason |
|
|
495
|
+
|-------|----|----|--------|
|
|
496
|
+
| `display-lg` | 22px / 500 | 24px / 600 | Removed weight inversion vs display-md |
|
|
497
|
+
| `display-md` | 21px / 700 | 20px / 600 | 4px gap preserved; weight normalised |
|
|
498
|
+
| `title-md` | 16px / 600 | 17px / 600 | Now visibly distinct from title-sm |
|
|
499
|
+
| `title-sm` | 16px / 500 | 15px / 500 | Was same px as title-md |
|
|
500
|
+
| `uppercase-tag` | 10px / 0.8 letterSpacing | 11px / 0.6 | Below Apple HIG 11pt minimum |
|
|
501
|
+
| `badge-text-md` | (missing) | 13px / 600 | New canonical token for Badge md |
|
|
502
|
+
|
|
503
|
+
**Default color values changed (WCAG AA fixes):**
|
|
504
|
+
|
|
505
|
+
| Token | v6 | v7 | WCAG impact |
|
|
506
|
+
|-------|----|----|-------------|
|
|
507
|
+
| `foregroundMuted` opacity | 0.38 (~#ababab) | 0.62 (~#767676) | 2.2:1 ❌ → 4.5:1 ✓ |
|
|
508
|
+
| `foregroundSubtle` opacity | 0.55 (~#858585) | 0.70 (~#646464) | 3.5:1 ❌ → 5.9:1 ✓ |
|
|
509
|
+
| `warning` (light) | `#e67e00` | `#9a5200` | 2.86:1 ❌ → 5.86:1 ✓ |
|
|
510
|
+
| `warningForeground` (dark) | `#ffffff` | `#0f0f0f` | Dark text on amber, 8.6:1 ✓ |
|
|
511
|
+
| `destructive` (light) | `#e53935` | `#c72828` | 4.22:1 ❌ → 5.59:1 ✓ |
|
|
512
|
+
| `accent` (light) | `= primary` | `#d4561d` | Explicit brand accent |
|
|
513
|
+
| `accent` (dark) | `= primary` | `#e87645` | Warm accent for dark surfaces |
|
|
514
|
+
|
|
515
|
+
**Component visual behavior changes:**
|
|
516
|
+
|
|
517
|
+
| Component | Change |
|
|
518
|
+
|-----------|--------|
|
|
519
|
+
| `AlertBanner` | Background now uses semantic tint per variant (was white card); 1px semantic border added |
|
|
520
|
+
| `Button` (text variant) | Label color: `foreground` → `accentResolved` — clearer CTA signal |
|
|
521
|
+
| `Card` (elevated variant) | `borderWidth: 0` — shadow is sole depth signal; border was redundant |
|
|
522
|
+
| `Chip` | `paddingVertical` doubled to hit 44pt WCAG 2.5.5 tap target |
|
|
523
|
+
| `Input` / `Textarea` | Border: 1px at rest → animates to 2px on focus (was always 2px) |
|
|
524
|
+
| `Switch` | Off-state now shows animated 1.5px border (invisible on white surfaces before) |
|
|
525
|
+
| `Tabs` | Labels always `Sohne-SemiBold`; active = color only — eliminates layout reflow on selection |
|
|
526
|
+
| `Checkbox` / `RadioGroup` | Disabled `opacity: 0.45` now on full row (was box only) |
|
|
527
|
+
| `Toast` | `richColors={true}` — semantic variants now visually distinct by color |
|
|
528
|
+
|
|
529
|
+
**If you customised any of these values** via `ThemeProvider` overrides, your overrides continue to take precedence — default palette changes only affect apps using the out-of-the-box defaults.
|
|
530
|
+
|
|
531
|
+
### New Compound Component Section
|
|
532
|
+
|
|
533
|
+
The example app now has a dedicated **Compound Components** section showcasing `Card`, `ButtonGroup`, `Form`, `ListGroup`, and `MenuGroup` with all sub-components and variants.
|
|
534
|
+
|
|
535
|
+
---
|
|
536
|
+
|
|
537
|
+
## Migration Guide: v7 → v8
|
|
538
|
+
|
|
539
|
+
### Breaking Changes
|
|
540
|
+
|
|
541
|
+
**1. New required peer dependency: `sonner-native`**
|
|
542
|
+
Toast (`ToastProvider` / `toast` / `useToast`) imports `sonner-native`, but it was never declared as a peer — installs silently lacked it and Metro failed with `Unable to resolve "sonner-native"`. It is now a declared peer. Install it:
|
|
543
|
+
```bash
|
|
544
|
+
pnpm add sonner-native
|
|
545
|
+
```
|
|
546
|
+
(You already need its companions `react-native-svg` and `react-native-screens`.)
|
|
547
|
+
|
|
548
|
+
**2. `@gorhom/bottom-sheet` peer range tightened to `>=5.2.0`**
|
|
549
|
+
5.1.x crashes with Reanimated v4 (`useWorkletCallback is not a function`), which broke `Sheet`, `ConfirmDialog`, and `Select` on open. The range no longer admits broken versions. Pin `5.2.8` if you also use Worklets `0.5.x`.
|
|
550
|
+
|
|
551
|
+
**3. `./fonts` stays at `src/fonts.ts` (unchanged)**
|
|
552
|
+
The `./fonts` export deliberately points at `src/fonts.ts`, not `dist`. The `.otf` files ship as raw assets in `src/assets/fonts/` and are resolved by Metro at your build time via `require()`. Compiling this entry to `dist` and repointing the `require()` paths breaks Metro asset resolution under pnpm symlinked workspaces (`requiring unknown module ../src/assets/fonts/...`), so it is intentionally left as source. No API change — import as before:
|
|
553
|
+
```ts
|
|
554
|
+
import { SohneFonts } from '@retray-dev/ui-kit/fonts'
|
|
555
|
+
```
|
|
556
|
+
|
|
557
|
+
### New — packaging & DX
|
|
558
|
+
|
|
559
|
+
- **`RetrayProvider`** — one wrapper replacing the five-provider boilerplate (see Setup below).
|
|
560
|
+
- **Dev font guard** — if you render a UI kit component without loading `SohneFonts`, `Text` now logs a one-time `console.warn` in dev (silent in production) instead of failing invisibly.
|
|
561
|
+
|
|
562
|
+
### New components
|
|
563
|
+
|
|
564
|
+
`RetrayProvider`, `AppHeader`, `TabBar`, `PagerDots`, `SelectableGrid`, `PricingCard`, `ErrorBoundary`, `ImageViewer`, plus `Skeleton.MediaCard` / `Skeleton.ListItem` sub-skeletons and the optional deep-import-only `HolographicCard` (Skia foil card). `MonthPicker` gained `minValue`/`maxValue` and `Date` bridge helpers.
|
|
565
|
+
|
|
566
|
+
### Optional peers (only if you use the matching component)
|
|
567
|
+
|
|
568
|
+
- `@shopify/react-native-skia` + `expo-sensors` → `HolographicCard` (deep-import only, not in the main barrel).
|
|
569
|
+
|
|
570
|
+
---
|
|
571
|
+
|
|
406
572
|
## Components
|
|
407
573
|
|
|
408
574
|
---
|
|
@@ -1247,6 +1413,74 @@ const [amount, setAmount] = useState(0)
|
|
|
1247
1413
|
|
|
1248
1414
|
---
|
|
1249
1415
|
|
|
1416
|
+
### VirtualList
|
|
1417
|
+
|
|
1418
|
+
**Import:** `import { VirtualList } from '@retray-dev/ui-kit'`
|
|
1419
|
+
|
|
1420
|
+
**When to use:** Large lists (100+ items) where you need efficient rendering. Thin wrapper over `FlatList` with sane defaults for stable keys and optional fixed-height fast path.
|
|
1421
|
+
|
|
1422
|
+
| Prop | Type | Default | Notes |
|
|
1423
|
+
|------|------|---------|-------|
|
|
1424
|
+
| data | `T[]` | — | Array of items to render |
|
|
1425
|
+
| renderItem | `ListRenderItem<T>` | — | Render function for each item |
|
|
1426
|
+
| itemHeight | `number` | — | Fixed row height in px. Enables `getItemLayout` for performance with large datasets |
|
|
1427
|
+
| keyExtractor | `(item: T, index: number) => string` | Auto | Defaults to `item.id` or index. Override for custom keys |
|
|
1428
|
+
| ...FlatListProps | — | — | All standard FlatList props pass through |
|
|
1429
|
+
|
|
1430
|
+
**Performance optimization:** When `itemHeight` is provided, `VirtualList` enables `getItemLayout` so FlatList skips async measurement. For 10k+ rows, combine with `React.memo`-wrapped `renderItem` so only on-screen rows mount and re-render.
|
|
1431
|
+
|
|
1432
|
+
**Default key extraction:** Uses `item.id` (converted to string) or falls back to index. Override with `keyExtractor` for custom logic.
|
|
1433
|
+
|
|
1434
|
+
**Example — simple list:**
|
|
1435
|
+
```tsx
|
|
1436
|
+
<VirtualList
|
|
1437
|
+
data={items}
|
|
1438
|
+
renderItem={({ item }) => (
|
|
1439
|
+
<ListItem
|
|
1440
|
+
title={item.title}
|
|
1441
|
+
subtitle={item.subtitle}
|
|
1442
|
+
onPress={() => handlePress(item.id)}
|
|
1443
|
+
/>
|
|
1444
|
+
)}
|
|
1445
|
+
itemHeight={56}
|
|
1446
|
+
/>
|
|
1447
|
+
```
|
|
1448
|
+
|
|
1449
|
+
**Example — large dataset with memoized render:**
|
|
1450
|
+
```tsx
|
|
1451
|
+
const renderItem = useCallback(({ item }) => (
|
|
1452
|
+
<ListItem
|
|
1453
|
+
title={item.title}
|
|
1454
|
+
subtitle={item.subtitle}
|
|
1455
|
+
leftIcon="circle"
|
|
1456
|
+
showSeparator
|
|
1457
|
+
onPress={() => navigate('detail', { id: item.id })}
|
|
1458
|
+
/>
|
|
1459
|
+
), [])
|
|
1460
|
+
|
|
1461
|
+
<VirtualList
|
|
1462
|
+
data={thousands}
|
|
1463
|
+
renderItem={renderItem}
|
|
1464
|
+
itemHeight={64}
|
|
1465
|
+
/>
|
|
1466
|
+
```
|
|
1467
|
+
|
|
1468
|
+
**Example — variable height rows (omit itemHeight):**
|
|
1469
|
+
```tsx
|
|
1470
|
+
<VirtualList
|
|
1471
|
+
data={posts}
|
|
1472
|
+
renderItem={({ item }) => (
|
|
1473
|
+
<Card style={{ margin: SPACING.sm }}>
|
|
1474
|
+
<CardContent>
|
|
1475
|
+
<Text>{item.content}</Text>
|
|
1476
|
+
</CardContent>
|
|
1477
|
+
</Card>
|
|
1478
|
+
)}
|
|
1479
|
+
/>
|
|
1480
|
+
```
|
|
1481
|
+
|
|
1482
|
+
---
|
|
1483
|
+
|
|
1250
1484
|
### Separator
|
|
1251
1485
|
|
|
1252
1486
|
**Import:** `import { Separator } from '@retray-dev/ui-kit'`
|
|
@@ -1350,15 +1584,29 @@ const [amount, setAmount] = useState(0)
|
|
|
1350
1584
|
</View>
|
|
1351
1585
|
```
|
|
1352
1586
|
|
|
1353
|
-
**
|
|
1587
|
+
**Sub-skeletons (v8):** ready-made placeholders that mirror a component's footprint, so grids/lists don't reflow when real data arrives.
|
|
1588
|
+
|
|
1354
1589
|
```tsx
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
1590
|
+
// Matches <MediaCard> — image block + title/subtitle lines
|
|
1591
|
+
<Skeleton.MediaCard aspectRatio="4:3" /> // props: aspectRatio ('1:1'|'4:3'|'16:9'|'4:5'|'3:2'), showSubtitle, style
|
|
1592
|
+
|
|
1593
|
+
// Matches <ListItem> — leading circle + title/subtitle lines
|
|
1594
|
+
<Skeleton.ListItem /> // props: showAvatar, showSubtitle, style
|
|
1595
|
+
|
|
1596
|
+
// Repeated list/grid placeholder — for VirtualList / FlatList while loading.
|
|
1597
|
+
// columns=1 → stacked ListItemSkeleton; columns>1 → grid of MediaCardSkeleton.
|
|
1598
|
+
<Skeleton.List count={6} /> // stacked list
|
|
1599
|
+
<Skeleton.List count={6} columns={2} /> // 2-col card grid
|
|
1600
|
+
// props: count, columns, gap, aspectRatio (grid), showAvatar (list), style
|
|
1601
|
+
|
|
1602
|
+
// Also exported as named components: MediaCardSkeleton, ListItemSkeleton, ListSkeleton
|
|
1603
|
+
|
|
1604
|
+
// As a VirtualList loading state
|
|
1605
|
+
<VirtualList
|
|
1606
|
+
data={loading ? [] : rows}
|
|
1607
|
+
renderItem={renderItem}
|
|
1608
|
+
ListEmptyComponent={loading ? <Skeleton.List count={8} /> : <EmptyState .../>}
|
|
1609
|
+
/>
|
|
1362
1610
|
```
|
|
1363
1611
|
|
|
1364
1612
|
---
|
|
@@ -1976,10 +2224,13 @@ Add `react-native-worklets/plugin` (not `react-native-reanimated/plugin`) to `ba
|
|
|
1976
2224
|
| android_keyboardInputMode | `'adjustPan' \| 'adjustResize'` | `'adjustPan'` | Android-only: `'adjustPan'` moves the window (default — fixes restore issues with dynamic sizing). `'adjustResize'` resizes the container (can cause transparent gap when keyboard dismisses) |
|
|
1977
2225
|
| footer | `ReactNode` | — | Sticky footer below scroll area (sticky above keyboard) |
|
|
1978
2226
|
| snapPoints | `(string \| number)[]` | — | Optional snap points (e.g., `['50%', '85%']`). When omitted, uses dynamic sizing (auto-fits content) |
|
|
2227
|
+
| responsive | `boolean` | `false` | **(v8)** On wide screens (width ≥ `BREAKPOINTS.wide`) render a centered modal dialog instead of a bottom sheet. Stays a bottom sheet on phones |
|
|
2228
|
+
| dialogMaxWidth | `number` | `480` | Max width of the centered dialog. Only applies when `responsive` |
|
|
1979
2229
|
|
|
1980
2230
|
**Features:**
|
|
1981
2231
|
- `enableDynamicSizing` — height auto-fits content, no `snapPoints` needed (default behavior when `snapPoints` is omitted)
|
|
1982
2232
|
- `snapPoints` — optionally provide custom snap points (e.g., `['50%', '85%']`). Disables dynamic sizing when provided
|
|
2233
|
+
- `responsive` — on tablets/web, becomes a centered dialog (max width `dialogMaxWidth`). Note: the dialog path uses a plain RN `Modal`, so `SheetTextInput` is **not** required there — use a regular `TextInput`
|
|
1983
2234
|
- `enablePanDownToClose` — swipe down to dismiss
|
|
1984
2235
|
- Backdrop press dismisses
|
|
1985
2236
|
- **Scrollable content:** use `scrollable` prop or `maxHeight`. Both use `BottomSheetScrollView` — do NOT use plain `ScrollView` inside Sheet
|
|
@@ -2058,6 +2309,195 @@ const [open, setOpen] = useState(false)
|
|
|
2058
2309
|
- Default `android_keyboardInputMode="adjustPan"` fixes the transparent gap that occurs with `adjustResize` when keyboard dismisses
|
|
2059
2310
|
- `enableBlurKeyboardOnGesture={true}` (default) dismisses keyboard when dragging sheet
|
|
2060
2311
|
|
|
2312
|
+
**Compound components:**
|
|
2313
|
+
|
|
2314
|
+
Sheet also supports compound components for custom layouts: `Sheet.Header`, `Sheet.Content`, `Sheet.Footer`.
|
|
2315
|
+
|
|
2316
|
+
**Sheet.Header Props:**
|
|
2317
|
+
| Prop | Type | Notes |
|
|
2318
|
+
|------|------|-------|
|
|
2319
|
+
| children | `ReactNode` | Custom header content — replaces title/subtitle/showCloseButton |
|
|
2320
|
+
| style | `ViewStyle` | — |
|
|
2321
|
+
|
|
2322
|
+
**Sheet.Content Props:**
|
|
2323
|
+
| Prop | Type | Notes |
|
|
2324
|
+
|------|------|-------|
|
|
2325
|
+
| children | `ReactNode` | Body content with automatic gap spacing |
|
|
2326
|
+
| style | `ViewStyle` | — |
|
|
2327
|
+
|
|
2328
|
+
**Sheet.Footer Props:**
|
|
2329
|
+
| Prop | Type | Notes |
|
|
2330
|
+
|------|------|-------|
|
|
2331
|
+
| children | `ReactNode` | Footer content (e.g., action buttons) |
|
|
2332
|
+
| style | `ViewStyle` | — |
|
|
2333
|
+
|
|
2334
|
+
**Compound component examples:**
|
|
2335
|
+
```tsx
|
|
2336
|
+
// Custom header with close button
|
|
2337
|
+
<Sheet open={open} onClose={() => setOpen(false)}>
|
|
2338
|
+
<Sheet.Header>
|
|
2339
|
+
<View style={{ flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center' }}>
|
|
2340
|
+
<Text variant="title-md">Custom Header</Text>
|
|
2341
|
+
<IconButton iconName="x" onPress={() => setOpen(false)} />
|
|
2342
|
+
</View>
|
|
2343
|
+
</Sheet.Header>
|
|
2344
|
+
<Sheet.Content>
|
|
2345
|
+
<Text>Content with auto spacing</Text>
|
|
2346
|
+
<Input placeholder="Name" />
|
|
2347
|
+
<Input placeholder="Email" />
|
|
2348
|
+
</Sheet.Content>
|
|
2349
|
+
<Sheet.Footer>
|
|
2350
|
+
<Button label="Cancel" variant="outline" onPress={() => setOpen(false)} />
|
|
2351
|
+
<Button label="Save" onPress={handleSave} />
|
|
2352
|
+
</Sheet.Footer>
|
|
2353
|
+
</Sheet>
|
|
2354
|
+
|
|
2355
|
+
// Mix compound components with props
|
|
2356
|
+
<Sheet open={open} onClose={() => setOpen(false)} title="Edit Profile">
|
|
2357
|
+
<Sheet.Content>
|
|
2358
|
+
<Input label="Name" value={name} onChangeText={setName} />
|
|
2359
|
+
<Input label="Email" value={email} onChangeText={setEmail} />
|
|
2360
|
+
</Sheet.Content>
|
|
2361
|
+
<Sheet.Footer>
|
|
2362
|
+
<Button label="Save" fullWidth onPress={handleSave} />
|
|
2363
|
+
</Sheet.Footer>
|
|
2364
|
+
</Sheet>
|
|
2365
|
+
```
|
|
2366
|
+
|
|
2367
|
+
When using compound components:
|
|
2368
|
+
- `Sheet.Header` replaces `title`, `subtitle`, and `showCloseButton` props
|
|
2369
|
+
- `Sheet.Content` automatically applies `gap: vs(16)` between children
|
|
2370
|
+
- `Sheet.Footer` renders sticky footer with automatic border, padding, and horizontal button layout
|
|
2371
|
+
|
|
2372
|
+
---
|
|
2373
|
+
|
|
2374
|
+
### Form
|
|
2375
|
+
|
|
2376
|
+
**Import:** `import { Form } from '@retray-dev/ui-kit'`
|
|
2377
|
+
|
|
2378
|
+
**When to use:** Standardize form layouts with consistent spacing, labels, and validation errors. Compound component pattern for wrapping Input, Select, Textarea, etc.
|
|
2379
|
+
|
|
2380
|
+
**Sub-components:**
|
|
2381
|
+
- `Form` — Root wrapper with consistent gap spacing between fields
|
|
2382
|
+
- `Form.Field` — Wraps a single input with optional label and error
|
|
2383
|
+
- `Form.Section` — Groups related fields with title and description
|
|
2384
|
+
- `Form.Footer` — Footer area for submit/cancel buttons
|
|
2385
|
+
|
|
2386
|
+
**Form Props:**
|
|
2387
|
+
| Prop | Type | Notes |
|
|
2388
|
+
|------|------|-------|
|
|
2389
|
+
| children | `ReactNode` | Form.Field, Form.Section, Form.Footer components |
|
|
2390
|
+
| style | `ViewStyle` | — |
|
|
2391
|
+
|
|
2392
|
+
**Form.Field Props:**
|
|
2393
|
+
| Prop | Type | Default | Notes |
|
|
2394
|
+
|------|------|---------|-------|
|
|
2395
|
+
| children | `ReactNode` | required | Input, Select, Textarea, etc. |
|
|
2396
|
+
| label | `string` | — | Label above input |
|
|
2397
|
+
| error | `string` | — | Error message below input |
|
|
2398
|
+
| required | `boolean` | `false` | Shows asterisk after label |
|
|
2399
|
+
| style | `ViewStyle` | — | — |
|
|
2400
|
+
| labelStyle | `TextStyle` | — | — |
|
|
2401
|
+
| errorStyle | `TextStyle` | — | — |
|
|
2402
|
+
|
|
2403
|
+
**Form.Section Props:**
|
|
2404
|
+
| Prop | Type | Notes |
|
|
2405
|
+
|------|------|-------|
|
|
2406
|
+
| children | `ReactNode` | Form.Field components |
|
|
2407
|
+
| title | `string` | Section heading |
|
|
2408
|
+
| description | `string` | Supporting text below title |
|
|
2409
|
+
| style | `ViewStyle` | — |
|
|
2410
|
+
|
|
2411
|
+
**Form.Footer Props:**
|
|
2412
|
+
| Prop | Type | Notes |
|
|
2413
|
+
|------|------|-------|
|
|
2414
|
+
| children | `ReactNode` | Button components |
|
|
2415
|
+
| style | `ViewStyle` | — |
|
|
2416
|
+
|
|
2417
|
+
**Features:**
|
|
2418
|
+
- **Automatic spacing:** `gap: vs(16)` between Form.Field components
|
|
2419
|
+
- **Required indicator:** Red asterisk (*) when `required={true}`
|
|
2420
|
+
- **Error styling:** Error text in destructive color below input
|
|
2421
|
+
- **Label hierarchy:** Sohne-Medium 14px for labels, 12px for errors
|
|
2422
|
+
- **Footer layout:** Horizontal row with `gap: s(12)` for button grouping
|
|
2423
|
+
|
|
2424
|
+
**Examples:**
|
|
2425
|
+
```tsx
|
|
2426
|
+
// Basic form
|
|
2427
|
+
<Form>
|
|
2428
|
+
<Form.Field label="Full Name" required>
|
|
2429
|
+
<Input value={name} onChangeText={setName} placeholder="John Doe" />
|
|
2430
|
+
</Form.Field>
|
|
2431
|
+
|
|
2432
|
+
<Form.Field label="Email" required error={emailError}>
|
|
2433
|
+
<Input
|
|
2434
|
+
value={email}
|
|
2435
|
+
onChangeText={setEmail}
|
|
2436
|
+
placeholder="john@example.com"
|
|
2437
|
+
keyboardType="email-address"
|
|
2438
|
+
/>
|
|
2439
|
+
</Form.Field>
|
|
2440
|
+
|
|
2441
|
+
<Form.Field label="Country">
|
|
2442
|
+
<Select
|
|
2443
|
+
value={country}
|
|
2444
|
+
onValueChange={setCountry}
|
|
2445
|
+
options={countries}
|
|
2446
|
+
/>
|
|
2447
|
+
</Form.Field>
|
|
2448
|
+
|
|
2449
|
+
<Form.Footer>
|
|
2450
|
+
<Button label="Cancel" variant="outline" onPress={onCancel} />
|
|
2451
|
+
<Button label="Submit" onPress={handleSubmit} />
|
|
2452
|
+
</Form.Footer>
|
|
2453
|
+
</Form>
|
|
2454
|
+
|
|
2455
|
+
// With sections
|
|
2456
|
+
<Form>
|
|
2457
|
+
<Form.Section title="Personal Information" description="Your basic profile details">
|
|
2458
|
+
<Form.Field label="Name" required>
|
|
2459
|
+
<Input value={name} onChangeText={setName} />
|
|
2460
|
+
</Form.Field>
|
|
2461
|
+
<Form.Field label="Email" required error={emailError}>
|
|
2462
|
+
<Input value={email} onChangeText={setEmail} />
|
|
2463
|
+
</Form.Field>
|
|
2464
|
+
</Form.Section>
|
|
2465
|
+
|
|
2466
|
+
<Form.Section title="Preferences">
|
|
2467
|
+
<Form.Field label="Language">
|
|
2468
|
+
<Select value={lang} onValueChange={setLang} options={languages} />
|
|
2469
|
+
</Form.Field>
|
|
2470
|
+
<Form.Field label="Timezone">
|
|
2471
|
+
<Select value={tz} onValueChange={setTz} options={timezones} />
|
|
2472
|
+
</Form.Field>
|
|
2473
|
+
</Form.Section>
|
|
2474
|
+
|
|
2475
|
+
<Form.Footer>
|
|
2476
|
+
<Button label="Save" fullWidth onPress={handleSave} />
|
|
2477
|
+
</Form.Footer>
|
|
2478
|
+
</Form>
|
|
2479
|
+
|
|
2480
|
+
// Inside Sheet
|
|
2481
|
+
<Sheet open={open} onClose={() => setOpen(false)} title="Edit Profile">
|
|
2482
|
+
<Form>
|
|
2483
|
+
<Form.Field label="Name" required>
|
|
2484
|
+
<SheetTextInput value={name} onChangeText={setName} />
|
|
2485
|
+
</Form.Field>
|
|
2486
|
+
<Form.Field label="Bio">
|
|
2487
|
+
<SheetTextInput
|
|
2488
|
+
value={bio}
|
|
2489
|
+
onChangeText={setBio}
|
|
2490
|
+
multiline
|
|
2491
|
+
style={{ minHeight: 80 }}
|
|
2492
|
+
/>
|
|
2493
|
+
</Form.Field>
|
|
2494
|
+
</Form>
|
|
2495
|
+
<View style={{ marginTop: 16 }}>
|
|
2496
|
+
<Button label="Save" fullWidth onPress={handleSave} />
|
|
2497
|
+
</View>
|
|
2498
|
+
</Sheet>
|
|
2499
|
+
```
|
|
2500
|
+
|
|
2061
2501
|
---
|
|
2062
2502
|
|
|
2063
2503
|
### Toast / useToast
|
|
@@ -2297,6 +2737,77 @@ dismiss(id)
|
|
|
2297
2737
|
|
|
2298
2738
|
---
|
|
2299
2739
|
|
|
2740
|
+
### ListGroup
|
|
2741
|
+
|
|
2742
|
+
**Import:** `import { ListGroup } from '@retray-dev/ui-kit'`
|
|
2743
|
+
|
|
2744
|
+
**When to use:** Wrap multiple `ListItem` components to standardize spacing, add automatic separators, and optionally apply a card surface. Identical pattern to `MenuGroup`.
|
|
2745
|
+
|
|
2746
|
+
| Prop | Type | Default | Notes |
|
|
2747
|
+
|------|------|---------|-------|
|
|
2748
|
+
| children | `ReactNode` | required | ListItem components + ListGroup.Header + ListGroup.Footer |
|
|
2749
|
+
| variant | `'plain' \| 'card'` | `'plain'` | `plain`: no background. `card`: card surface with border |
|
|
2750
|
+
| style | `ViewStyle` | — | — |
|
|
2751
|
+
|
|
2752
|
+
**Sub-components:**
|
|
2753
|
+
- `ListGroup.Header` — Header text or custom content
|
|
2754
|
+
- `ListGroup.Footer` — Footer text or custom content
|
|
2755
|
+
|
|
2756
|
+
**Features:**
|
|
2757
|
+
- **Auto-separators:** Automatically adds `showSeparator={true}` to all ListItem children except the last
|
|
2758
|
+
- **Individual override:** ListItem can override with `showSeparator={false}`
|
|
2759
|
+
- **Variant `card`:** Applies card surface (`RADIUS.md`, border, shadow)
|
|
2760
|
+
|
|
2761
|
+
**Examples:**
|
|
2762
|
+
```tsx
|
|
2763
|
+
// Transaction list with auto separators
|
|
2764
|
+
<ListGroup variant="card">
|
|
2765
|
+
<ListGroup.Header>Recent Transactions</ListGroup.Header>
|
|
2766
|
+
<ListItem
|
|
2767
|
+
leftIcon="coffee"
|
|
2768
|
+
title="Rappi"
|
|
2769
|
+
subtitle="Food · May 12"
|
|
2770
|
+
rightRender={<Text color={colors.destructive}>−$45.000</Text>}
|
|
2771
|
+
onPress={() => {}}
|
|
2772
|
+
/>
|
|
2773
|
+
<ListItem
|
|
2774
|
+
leftIcon="car"
|
|
2775
|
+
title="Uber"
|
|
2776
|
+
subtitle="Transport · May 11"
|
|
2777
|
+
rightRender={<Text color={colors.destructive}>−$18.200</Text>}
|
|
2778
|
+
onPress={() => {}}
|
|
2779
|
+
/>
|
|
2780
|
+
<ListItem
|
|
2781
|
+
leftIcon="briefcase"
|
|
2782
|
+
title="Salary"
|
|
2783
|
+
subtitle="Income · May 1"
|
|
2784
|
+
rightRender={<Text color={colors.success}>+$3.500.000</Text>}
|
|
2785
|
+
onPress={() => {}}
|
|
2786
|
+
/>
|
|
2787
|
+
<ListGroup.Footer>Last 7 days</ListGroup.Footer>
|
|
2788
|
+
</ListGroup>
|
|
2789
|
+
|
|
2790
|
+
// Plain variant (inside Card)
|
|
2791
|
+
<Card>
|
|
2792
|
+
<CardContent>
|
|
2793
|
+
<ListGroup variant="plain">
|
|
2794
|
+
{users.map((user) => (
|
|
2795
|
+
<ListItem
|
|
2796
|
+
key={user.id}
|
|
2797
|
+
leftRender={<Avatar src={user.avatar} fallback={user.name[0]} />}
|
|
2798
|
+
title={user.name}
|
|
2799
|
+
subtitle={user.role}
|
|
2800
|
+
rightRender={<Badge label={user.status} variant="success" />}
|
|
2801
|
+
onPress={() => navigate('user', { id: user.id })}
|
|
2802
|
+
/>
|
|
2803
|
+
))}
|
|
2804
|
+
</ListGroup>
|
|
2805
|
+
</CardContent>
|
|
2806
|
+
</Card>
|
|
2807
|
+
```
|
|
2808
|
+
|
|
2809
|
+
---
|
|
2810
|
+
|
|
2300
2811
|
### MenuItem
|
|
2301
2812
|
|
|
2302
2813
|
**Import:** `import { MenuItem } from '@retray-dev/ui-kit'`
|
|
@@ -2376,6 +2887,61 @@ dismiss(id)
|
|
|
2376
2887
|
|
|
2377
2888
|
---
|
|
2378
2889
|
|
|
2890
|
+
### MenuGroup
|
|
2891
|
+
|
|
2892
|
+
**Import:** `import { MenuGroup } from '@retray-dev/ui-kit'`
|
|
2893
|
+
|
|
2894
|
+
**When to use:** Wrap multiple `MenuItem` components to standardize spacing, add automatic separators, and optionally apply a card surface. Compound component pattern like `Card`.
|
|
2895
|
+
|
|
2896
|
+
| Prop | Type | Default | Notes |
|
|
2897
|
+
|------|------|---------|-------|
|
|
2898
|
+
| children | `ReactNode` | required | MenuItem components + MenuGroup.Header + MenuGroup.Footer |
|
|
2899
|
+
| variant | `'plain' \| 'card'` | `'plain'` | `plain`: no background. `card`: card surface with border |
|
|
2900
|
+
| style | `ViewStyle` | — | — |
|
|
2901
|
+
|
|
2902
|
+
**Sub-components:**
|
|
2903
|
+
- `MenuGroup.Header` — Header text or custom content
|
|
2904
|
+
- `MenuGroup.Footer` — Footer text or custom content
|
|
2905
|
+
|
|
2906
|
+
**Features:**
|
|
2907
|
+
- **Auto-separators:** Automatically adds `showSeparator={true}` to all MenuItem children except the last
|
|
2908
|
+
- **Individual override:** MenuItem can override with `showSeparator={false}`
|
|
2909
|
+
- **Variant `card`:** Applies card surface (`RADIUS.md`, border, shadow) like standalone MenuItems with `variant="card"`
|
|
2910
|
+
|
|
2911
|
+
**Examples:**
|
|
2912
|
+
```tsx
|
|
2913
|
+
// Basic group with auto separators
|
|
2914
|
+
<MenuGroup variant="card">
|
|
2915
|
+
<MenuItem label="Personal Info" iconName="user" onPress={() => {}} />
|
|
2916
|
+
<MenuItem label="Security" iconName="shield" onPress={() => {}} />
|
|
2917
|
+
<MenuItem label="Privacy" iconName="lock" onPress={() => {}} />
|
|
2918
|
+
</MenuGroup>
|
|
2919
|
+
|
|
2920
|
+
// With header and footer
|
|
2921
|
+
<MenuGroup variant="card">
|
|
2922
|
+
<MenuGroup.Header>Account Settings</MenuGroup.Header>
|
|
2923
|
+
<MenuItem label="Personal Info" subtitle="Name, email, phone" iconName="user" onPress={() => {}} />
|
|
2924
|
+
<MenuItem label="Security" iconName="shield" onPress={() => {}} />
|
|
2925
|
+
<MenuItem label="Privacy" iconName="lock" onPress={() => {}} />
|
|
2926
|
+
<MenuGroup.Footer>Last updated today</MenuGroup.Footer>
|
|
2927
|
+
</MenuGroup>
|
|
2928
|
+
|
|
2929
|
+
// Plain variant (inside another container)
|
|
2930
|
+
<Card>
|
|
2931
|
+
<CardHeader>
|
|
2932
|
+
<CardTitle>Settings</CardTitle>
|
|
2933
|
+
</CardHeader>
|
|
2934
|
+
<CardContent>
|
|
2935
|
+
<MenuGroup variant="plain">
|
|
2936
|
+
<MenuItem label="Notifications" iconName="bell" rightRender={<Switch checked={notifs} onCheckedChange={setNotifs} />} />
|
|
2937
|
+
<MenuItem label="Dark Mode" iconName="moon" rightRender={<Switch checked={dark} onCheckedChange={setDark} />} />
|
|
2938
|
+
</MenuGroup>
|
|
2939
|
+
</CardContent>
|
|
2940
|
+
</Card>
|
|
2941
|
+
```
|
|
2942
|
+
|
|
2943
|
+
---
|
|
2944
|
+
|
|
2379
2945
|
### Chip / ChipGroup
|
|
2380
2946
|
|
|
2381
2947
|
**Import:** `import { Chip, ChipGroup } from '@retray-dev/ui-kit'`
|
|
@@ -2644,6 +3210,8 @@ Total ·············· $50.000
|
|
|
2644
3210
|
|------|------|---------|-------|
|
|
2645
3211
|
| value | `MonthPickerValue` | required | `{ month: 1–12, year: number }` |
|
|
2646
3212
|
| onChange | `(value: MonthPickerValue) => void` | required | Called on navigation |
|
|
3213
|
+
| minValue | `MonthPickerValue` | — | Earliest selectable month (inclusive). Prev arrow disables + dims at this bound |
|
|
3214
|
+
| maxValue | `MonthPickerValue` | — | Latest selectable month (inclusive). Next arrow disables + dims at this bound — e.g. cap at the current month |
|
|
2647
3215
|
| locale | `string` | `'en'` | BCP 47 locale. Built-in: `'en'`, `'es'`, `'pt'`, `'fr'`. Other locales: use `formatLabel` |
|
|
2648
3216
|
| formatLabel | `(value: MonthPickerValue) => string` | — | Custom label formatter. Takes precedence over `locale` |
|
|
2649
3217
|
| style | `ViewStyle` | — | — |
|
|
@@ -2653,7 +3221,15 @@ Total ·············· $50.000
|
|
|
2653
3221
|
{ month: number; year: number } // month is 1-indexed (January = 1)
|
|
2654
3222
|
```
|
|
2655
3223
|
|
|
2656
|
-
**
|
|
3224
|
+
**Date bridge helpers** (exported): for domains that store `Date` (e.g. finance apps in a fixed TZ), convert at the boundary instead of changing your model.
|
|
3225
|
+
```ts
|
|
3226
|
+
import { dateToMonthPickerValue, monthPickerValueToDate } from '@retray-dev/ui-kit'
|
|
3227
|
+
|
|
3228
|
+
dateToMonthPickerValue(new Date()) // → { month, year } (local time)
|
|
3229
|
+
monthPickerValueToDate({ month: 6, year: 2026 }) // → Date at first day of the month
|
|
3230
|
+
```
|
|
3231
|
+
|
|
3232
|
+
**Navigation:** Year wraps correctly at boundaries (December → January increments year, January → December decrements year). When `minValue`/`maxValue` are set, the boundary arrow is disabled (opacity 0.3) and presses are ignored. Fully controlled — no internal state.
|
|
2657
3233
|
|
|
2658
3234
|
**Styling:** Centered row with `←` / `→` buttons (44×44px each). Label: 17pt / Medium / 160px min-width, centered.
|
|
2659
3235
|
|
|
@@ -2752,6 +3328,407 @@ const [period, setPeriod] = useState({
|
|
|
2752
3328
|
|
|
2753
3329
|
---
|
|
2754
3330
|
|
|
3331
|
+
### RetrayProvider
|
|
3332
|
+
|
|
3333
|
+
**Import:** `import { RetrayProvider } from '@retray-dev/ui-kit'`
|
|
3334
|
+
|
|
3335
|
+
**When to use:** At your app root, instead of hand-nesting the five required providers. Removes an entire class of provider-order bugs. The individual providers stay exported for custom trees.
|
|
3336
|
+
|
|
3337
|
+
| Prop | Type | Default | Notes |
|
|
3338
|
+
|------|------|---------|-------|
|
|
3339
|
+
| children | `ReactNode` | required | Your app |
|
|
3340
|
+
| theme | `Theme` | — | Per-scheme token overrides, forwarded to `ThemeProvider` |
|
|
3341
|
+
| colorScheme | `'system' \| 'light' \| 'dark'` | `'system'` | Forwarded to `ThemeProvider` |
|
|
3342
|
+
|
|
3343
|
+
**Wraps (in order):** `SafeAreaProvider` (with `initialWindowMetrics`) → `GestureHandlerRootView` → `ThemeProvider` → `BottomSheetModalProvider` → `ToastProvider`.
|
|
3344
|
+
|
|
3345
|
+
**Example:**
|
|
3346
|
+
```tsx
|
|
3347
|
+
import { RetrayProvider } from '@retray-dev/ui-kit'
|
|
3348
|
+
|
|
3349
|
+
export default function App() {
|
|
3350
|
+
const [fontsLoaded] = useFonts(SohneFonts)
|
|
3351
|
+
if (!fontsLoaded) return null
|
|
3352
|
+
return (
|
|
3353
|
+
<RetrayProvider colorScheme="system" theme={{ light: { primary: '#ff385c' } }}>
|
|
3354
|
+
<RootNavigator />
|
|
3355
|
+
</RetrayProvider>
|
|
3356
|
+
)
|
|
3357
|
+
}
|
|
3358
|
+
```
|
|
3359
|
+
|
|
3360
|
+
---
|
|
3361
|
+
|
|
3362
|
+
### AppHeader
|
|
3363
|
+
|
|
3364
|
+
**Import:** `import { AppHeader } from '@retray-dev/ui-kit'`
|
|
3365
|
+
|
|
3366
|
+
**When to use:** Top app bar / screen chrome — back button, title/subtitle, and a right action slot. Responsive: title left-aligns on phones and centers when width ≥ `BREAKPOINTS.wide`.
|
|
3367
|
+
|
|
3368
|
+
**Expo Router compatibility:** ✅ Fully compatible. Use with `useRouter()` for navigation:
|
|
3369
|
+
|
|
3370
|
+
```tsx
|
|
3371
|
+
import { useRouter } from 'expo-router'
|
|
3372
|
+
import { AppHeader } from '@retray-dev/ui-kit'
|
|
3373
|
+
|
|
3374
|
+
export default function Screen() {
|
|
3375
|
+
const router = useRouter()
|
|
3376
|
+
return (
|
|
3377
|
+
<AppHeader
|
|
3378
|
+
title="Settings"
|
|
3379
|
+
onBack={() => router.back()}
|
|
3380
|
+
right={<IconButton iconName="more-horizontal" variant="text" />}
|
|
3381
|
+
/>
|
|
3382
|
+
)
|
|
3383
|
+
}
|
|
3384
|
+
```
|
|
3385
|
+
|
|
3386
|
+
| Prop | Type | Default | Notes |
|
|
3387
|
+
|------|------|---------|-------|
|
|
3388
|
+
| title | `string` | — | Primary title (truncates to 1 line) |
|
|
3389
|
+
| subtitle | `string` | — | Secondary line under the title |
|
|
3390
|
+
| onBack | `() => void` | — | Shows a back button on the left when provided |
|
|
3391
|
+
| backIconName | `string` | `'chevron-left'` | Icon for the back button |
|
|
3392
|
+
| left | `ReactNode` | — | Custom left content — overrides the back button |
|
|
3393
|
+
| right | `ReactNode` | — | Custom right content (actions) |
|
|
3394
|
+
| titleAlign | `'auto' \| 'left' \| 'center'` | `'auto'` | `auto` = left on narrow, centered on wide |
|
|
3395
|
+
| bordered | `boolean` | `true` | Hairline border underneath |
|
|
3396
|
+
| withSafeArea | `boolean` | `true` | Apply top safe-area inset as padding |
|
|
3397
|
+
| backgroundColor | `string` | `colors.background` | — |
|
|
3398
|
+
| style | `ViewStyle` | — | — |
|
|
3399
|
+
|
|
3400
|
+
**Example:**
|
|
3401
|
+
```tsx
|
|
3402
|
+
<AppHeader
|
|
3403
|
+
title="Settings"
|
|
3404
|
+
onBack={navigation.goBack}
|
|
3405
|
+
right={<IconButton iconName="more-horizontal" variant="text" onPress={openMenu} />}
|
|
3406
|
+
/>
|
|
3407
|
+
```
|
|
3408
|
+
|
|
3409
|
+
---
|
|
3410
|
+
|
|
3411
|
+
### TabBar
|
|
3412
|
+
|
|
3413
|
+
**Import:** `import { TabBar } from '@retray-dev/ui-kit'`
|
|
3414
|
+
|
|
3415
|
+
**When to use:** Bottom tab navigation — icon + label tabs with active tint and badges. Pair with your navigator, or drive with local state for a single-screen app.
|
|
3416
|
+
|
|
3417
|
+
**Expo Router compatibility:** ✅ Fully compatible. Use in `_layout.tsx` to build custom tab navigation:
|
|
3418
|
+
|
|
3419
|
+
```tsx
|
|
3420
|
+
// app/(tabs)/_layout.tsx
|
|
3421
|
+
import { Tabs, useRouter, useSegments } from 'expo-router'
|
|
3422
|
+
import { TabBar } from '@retray-dev/ui-kit'
|
|
3423
|
+
|
|
3424
|
+
export default function TabLayout() {
|
|
3425
|
+
const router = useRouter()
|
|
3426
|
+
const segments = useSegments()
|
|
3427
|
+
const activeKey = segments[1] || 'home'
|
|
3428
|
+
|
|
3429
|
+
return (
|
|
3430
|
+
<Tabs
|
|
3431
|
+
tabBar={() => (
|
|
3432
|
+
<TabBar
|
|
3433
|
+
items={[
|
|
3434
|
+
{ key: 'home', label: 'Home', iconName: 'home' },
|
|
3435
|
+
{ key: 'search', label: 'Search', iconName: 'search' },
|
|
3436
|
+
{ key: 'profile', label: 'Profile', iconName: 'user', badge: 3 },
|
|
3437
|
+
]}
|
|
3438
|
+
activeKey={activeKey}
|
|
3439
|
+
onTabPress={(key) => router.push(`/(tabs)/${key}`)}
|
|
3440
|
+
/>
|
|
3441
|
+
)}
|
|
3442
|
+
>
|
|
3443
|
+
{/* ... screens */}
|
|
3444
|
+
</Tabs>
|
|
3445
|
+
)
|
|
3446
|
+
}
|
|
3447
|
+
```
|
|
3448
|
+
|
|
3449
|
+
| Prop | Type | Default | Notes |
|
|
3450
|
+
|------|------|---------|-------|
|
|
3451
|
+
| items | `TabBarItem[]` | required | `{ key, label?, iconName?, icon?, badge? }` |
|
|
3452
|
+
| activeKey | `string` | required | Key of the active tab |
|
|
3453
|
+
| onTabPress | `(key: string) => void` | required | — |
|
|
3454
|
+
| activeColor | `string` | `colors.primary` | — |
|
|
3455
|
+
| inactiveColor | `string` | `colors.foregroundMuted` | — |
|
|
3456
|
+
| withSafeArea | `boolean` | `true` | Apply bottom safe-area inset |
|
|
3457
|
+
| style | `ViewStyle` | — | — |
|
|
3458
|
+
|
|
3459
|
+
**TabBarItem.badge:** `true` shows a dot; a number shows a count (capped at `99+`).
|
|
3460
|
+
|
|
3461
|
+
**Haptics:** `selectionAsync` when switching to a new tab.
|
|
3462
|
+
|
|
3463
|
+
**Example:**
|
|
3464
|
+
```tsx
|
|
3465
|
+
<TabBar
|
|
3466
|
+
activeKey={tab}
|
|
3467
|
+
onTabPress={setTab}
|
|
3468
|
+
items={[
|
|
3469
|
+
{ key: 'home', label: 'Home', iconName: 'home' },
|
|
3470
|
+
{ key: 'wallet', label: 'Wallet', iconName: 'credit-card' },
|
|
3471
|
+
{ key: 'profile', label: 'Profile', iconName: 'user', badge: 3 },
|
|
3472
|
+
]}
|
|
3473
|
+
/>
|
|
3474
|
+
```
|
|
3475
|
+
|
|
3476
|
+
---
|
|
3477
|
+
|
|
3478
|
+
### PagerDots
|
|
3479
|
+
|
|
3480
|
+
**Import:** `import { PagerDots } from '@retray-dev/ui-kit'`
|
|
3481
|
+
|
|
3482
|
+
**When to use:** Page indicator for carousels / document pagers. The active dot stretches into a pill and color-crossfades — all on the UI thread. Now supports optional previous/next control buttons.
|
|
3483
|
+
|
|
3484
|
+
| Prop | Type | Default | Notes |
|
|
3485
|
+
|------|------|---------|-------|
|
|
3486
|
+
| count | `number` | required | Total pages |
|
|
3487
|
+
| activeIndex | `number` | required | 0-based active page |
|
|
3488
|
+
| onDotPress | `(index: number) => void` | — | Omit to make dots non-interactive |
|
|
3489
|
+
| showControls | `boolean \| { onPrevious?, onNext? }` | `false` | Show prev/next buttons. If `true`, uses `onDotPress(activeIndex ± 1)`. If object, uses custom handlers |
|
|
3490
|
+
| dotSize | `number` | `8` | Inactive dot diameter (dp) |
|
|
3491
|
+
| spacing | `number` | `8` | Gap between dots (dp) |
|
|
3492
|
+
| activeColor | `string` | `colors.primary` | — |
|
|
3493
|
+
| inactiveColor | `string` | `colors.border` | — |
|
|
3494
|
+
| style | `ViewStyle` | — | — |
|
|
3495
|
+
|
|
3496
|
+
**Example:**
|
|
3497
|
+
```tsx
|
|
3498
|
+
<PagerDots count={pages.length} activeIndex={page} onDotPress={setPage} showControls />
|
|
3499
|
+
```
|
|
3500
|
+
|
|
3501
|
+
---
|
|
3502
|
+
|
|
3503
|
+
### SelectableGrid
|
|
3504
|
+
|
|
3505
|
+
**Import:** `import { SelectableGrid } from '@retray-dev/ui-kit'`
|
|
3506
|
+
|
|
3507
|
+
**When to use:** Grid of selectable cells (icon + label) — store / category / emoji pickers where a list would be the wrong shape. Single or multi select. Generic over the value type (`string | number`). Now supports horizontal scrolling mode for single-row layouts.
|
|
3508
|
+
|
|
3509
|
+
| Prop | Type | Default | Notes |
|
|
3510
|
+
|------|------|---------|-------|
|
|
3511
|
+
| items | `SelectableGridItem<T>[]` | required | `{ value, label?, iconName?, icon?, disabled? }` |
|
|
3512
|
+
| value | `T \| T[] \| null` | required | Selected value(s) — array when `multiple` |
|
|
3513
|
+
| onChange | `(value: T) => void` | required | Fires with the toggled item's value |
|
|
3514
|
+
| multiple | `boolean` | `false` | Allow multiple selection (`value` should be an array) |
|
|
3515
|
+
| numColumns | `number` | `4` | Cells per row (exact-fit width is measured, not percentage). Ignored when `orientation='horizontal'` |
|
|
3516
|
+
| gap | `number` | `12` | Gap between cells (dp) |
|
|
3517
|
+
| orientation | `'grid' \| 'horizontal'` | `'grid'` | `'grid'` wraps into rows, `'horizontal'` creates a single scrollable row |
|
|
3518
|
+
| style | `ViewStyle` | — | — |
|
|
3519
|
+
|
|
3520
|
+
**Selected cell:** primary border + tinted background; icon/label switch to `primary`. **Press scale:** chip (0.94). **Haptics:** `selectionAsync` on press.
|
|
3521
|
+
|
|
3522
|
+
**Horizontal mode:** Fixed 96dp cell width, horizontal ScrollView, no vertical wrapping.
|
|
3523
|
+
|
|
3524
|
+
**Example:**
|
|
3525
|
+
```tsx
|
|
3526
|
+
{/* Grid layout */}
|
|
3527
|
+
<SelectableGrid
|
|
3528
|
+
items={categories}
|
|
3529
|
+
value={category}
|
|
3530
|
+
onChange={setCategory}
|
|
3531
|
+
numColumns={4}
|
|
3532
|
+
/>
|
|
3533
|
+
|
|
3534
|
+
{/* Horizontal scrolling */}
|
|
3535
|
+
<SelectableGrid
|
|
3536
|
+
items={categories}
|
|
3537
|
+
value={category}
|
|
3538
|
+
onChange={setCategory}
|
|
3539
|
+
orientation="horizontal"
|
|
3540
|
+
/>
|
|
3541
|
+
```
|
|
3542
|
+
|
|
3543
|
+
**Example:**
|
|
3544
|
+
```tsx
|
|
3545
|
+
<SelectableGrid
|
|
3546
|
+
items={[
|
|
3547
|
+
{ value: 'food', label: 'Food', iconName: 'coffee' },
|
|
3548
|
+
{ value: 'transport', label: 'Transport', iconName: 'truck' },
|
|
3549
|
+
{ value: 'home', label: 'Home', iconName: 'home' },
|
|
3550
|
+
]}
|
|
3551
|
+
value={category}
|
|
3552
|
+
onChange={setCategory}
|
|
3553
|
+
numColumns={4}
|
|
3554
|
+
/>
|
|
3555
|
+
```
|
|
3556
|
+
|
|
3557
|
+
---
|
|
3558
|
+
|
|
3559
|
+
### PricingCard
|
|
3560
|
+
|
|
3561
|
+
**Import:** `import { PricingCard } from '@retray-dev/ui-kit'`
|
|
3562
|
+
|
|
3563
|
+
**When to use:** Paywalls and freemium tiers — price header, feature list with check marks, optional promo badge, and CTA. Set `highlighted` on the recommended plan.
|
|
3564
|
+
|
|
3565
|
+
| Prop | Type | Default | Notes |
|
|
3566
|
+
|------|------|---------|-------|
|
|
3567
|
+
| name | `string` | required | Plan name |
|
|
3568
|
+
| price | `string` | required | Formatted price, e.g. `"$9"` or `"Free"` |
|
|
3569
|
+
| period | `string` | — | Billing suffix, e.g. `"/mo"` |
|
|
3570
|
+
| description | `string` | — | Short text under the price |
|
|
3571
|
+
| features | `(string \| PricingFeature)[]` | `[]` | Strings = included; `{ label, included: false }` for excluded (dashed, struck) |
|
|
3572
|
+
| ctaLabel | `string` | — | CTA button label (renders a `Button`) |
|
|
3573
|
+
| onCtaPress | `() => void` | — | — |
|
|
3574
|
+
| badge | `string` | — | Promo badge, e.g. `"Most popular"` |
|
|
3575
|
+
| highlighted | `boolean` | `false` | Primary border + elevation + primary CTA |
|
|
3576
|
+
| footnote | `string` | — | Small print under the CTA |
|
|
3577
|
+
| style | `ViewStyle` | — | — |
|
|
3578
|
+
|
|
3579
|
+
**Example:**
|
|
3580
|
+
```tsx
|
|
3581
|
+
<PricingCard
|
|
3582
|
+
name="Pro"
|
|
3583
|
+
price="$9"
|
|
3584
|
+
period="/mo"
|
|
3585
|
+
badge="Most popular"
|
|
3586
|
+
highlighted
|
|
3587
|
+
features={['Unlimited docs', 'Priority support', { label: 'SSO', included: false }]}
|
|
3588
|
+
ctaLabel="Start trial"
|
|
3589
|
+
onCtaPress={subscribe}
|
|
3590
|
+
footnote="Cancel anytime"
|
|
3591
|
+
/>
|
|
3592
|
+
```
|
|
3593
|
+
|
|
3594
|
+
---
|
|
3595
|
+
|
|
3596
|
+
### ErrorBoundary
|
|
3597
|
+
|
|
3598
|
+
**Import:** `import { ErrorBoundary } from '@retray-dev/ui-kit'`
|
|
3599
|
+
|
|
3600
|
+
**When to use:** Wrap screens or risky subtrees so a render error shows a themed fallback instead of unmounting the whole app.
|
|
3601
|
+
|
|
3602
|
+
| Prop | Type | Default | Notes |
|
|
3603
|
+
|------|------|---------|-------|
|
|
3604
|
+
| children | `ReactNode` | required | Protected subtree |
|
|
3605
|
+
| fallback | `ReactNode \| (props: { error, reset }) => ReactNode` | themed card | Custom fallback |
|
|
3606
|
+
| title | `string` | `'Something went wrong'` | Default fallback title |
|
|
3607
|
+
| message | `string` | `error.message` | Default fallback body |
|
|
3608
|
+
| onError | `(error, info) => void` | — | Wire your crash reporter here |
|
|
3609
|
+
|
|
3610
|
+
**Default fallback:** centered themed card with a destructive icon, title, message, and a "Try again" button that calls `reset()`.
|
|
3611
|
+
|
|
3612
|
+
**Example:**
|
|
3613
|
+
```tsx
|
|
3614
|
+
<ErrorBoundary onError={reportCrash}>
|
|
3615
|
+
<DocumentViewer />
|
|
3616
|
+
</ErrorBoundary>
|
|
3617
|
+
|
|
3618
|
+
// Custom fallback
|
|
3619
|
+
<ErrorBoundary fallback={({ error, reset }) => <MyFallback error={error} onRetry={reset} />}>
|
|
3620
|
+
<RiskyScreen />
|
|
3621
|
+
</ErrorBoundary>
|
|
3622
|
+
```
|
|
3623
|
+
|
|
3624
|
+
---
|
|
3625
|
+
|
|
3626
|
+
### ImageViewer
|
|
3627
|
+
|
|
3628
|
+
**Import:** `import { ImageViewer } from '@retray-dev/ui-kit'`
|
|
3629
|
+
|
|
3630
|
+
**When to use:** Full-screen zoomable image gallery — multi-page documents, photo viewers. Horizontal paging + pinch / double-tap zoom + pan, with page dots and a close button. Requires `react-native-gesture-handler` (already a peer).
|
|
3631
|
+
|
|
3632
|
+
| Prop | Type | Default | Notes |
|
|
3633
|
+
|------|------|---------|-------|
|
|
3634
|
+
| images | `ImageSourcePropType[]` | required | `{ uri }` or `require()` sources |
|
|
3635
|
+
| visible | `boolean` | required | Controls the modal |
|
|
3636
|
+
| onClose | `() => void` | required | — |
|
|
3637
|
+
| initialIndex | `number` | `0` | Page to open on |
|
|
3638
|
+
|
|
3639
|
+
**Behavior:** pinch to zoom (max 3×), double-tap to toggle 2.5× zoom, pan while zoomed. Paging locks automatically while a page is zoomed in. `PagerDots` shown when there is more than one image.
|
|
3640
|
+
|
|
3641
|
+
**Example:**
|
|
3642
|
+
```tsx
|
|
3643
|
+
<ImageViewer
|
|
3644
|
+
images={doc.pages.map((uri) => ({ uri }))}
|
|
3645
|
+
visible={viewerOpen}
|
|
3646
|
+
initialIndex={page}
|
|
3647
|
+
onClose={() => setViewerOpen(false)}
|
|
3648
|
+
/>
|
|
3649
|
+
```
|
|
3650
|
+
|
|
3651
|
+
---
|
|
3652
|
+
|
|
3653
|
+
### HolographicCard
|
|
3654
|
+
|
|
3655
|
+
**Import (deep import only):** `import { HolographicCard, FOIL_PRESETS, FoilPreset } from '@retray-dev/ui-kit/HolographicCard'`
|
|
3656
|
+
|
|
3657
|
+
> Not re-exported from the main barrel — it depends on the **optional** peers `@shopify/react-native-skia` and `expo-sensors`, which are deliberately kept out of the main module graph. Deep-import it so consumers who don't use it never pull Skia.
|
|
3658
|
+
|
|
3659
|
+
**When to use:** Holographic / foil trading-card surface (Pokémon-TCG style). Renders art on a Skia canvas with an animated rainbow sheen and tilts in 3D toward device motion; the sheen tracks the tilt so the foil "catches the light".
|
|
3660
|
+
|
|
3661
|
+
**Install:** `pnpm add @shopify/react-native-skia expo-sensors`
|
|
3662
|
+
|
|
3663
|
+
| Prop | Type | Default | Notes |
|
|
3664
|
+
|------|------|---------|-------|
|
|
3665
|
+
| source | `require()` / `{ uri }` | — | Card art. Omitted = gradient-only foil surface |
|
|
3666
|
+
| width | `number` | `300` | Card width (dp) |
|
|
3667
|
+
| height | `number` | `width * 1.4` | Trading-card ratio by default |
|
|
3668
|
+
| borderRadius | `number` | `RADIUS.md` | — |
|
|
3669
|
+
| enableTilt | `boolean` | `true` | React to gyroscope. No-op on web; static sheen if `expo-sensors` is absent |
|
|
3670
|
+
| intensity | `number` | `1` | Foil sheen strength, 0–1 |
|
|
3671
|
+
| onPress | `() => void` | — | Adds card press-scale + `impactLight` |
|
|
3672
|
+
| style | `ViewStyle` | — | — |
|
|
3673
|
+
| foilPreset | `FoilPreset` | `'rainbow'` | Foil color preset. Ignored if `foilColors` is provided |
|
|
3674
|
+
| foilColors | `string[]` | — | Custom foil gradient colors (array of RGBA strings). Overrides `foilPreset` |
|
|
3675
|
+
| maxTiltDegrees | `number` | `10` | Maximum tilt angle in degrees (0–45) |
|
|
3676
|
+
| tiltSensitivity | `number` | `1` | Sensitivity of tilt to device motion (0.1–2). Higher = more responsive |
|
|
3677
|
+
| sheenSpread | `number` | `0.6` | How far the sheen moves relative to tilt (0–1) |
|
|
3678
|
+
| tiltAnimationDuration | `number` | `80` | Animation duration for tilt smoothing in ms |
|
|
3679
|
+
| perspective | `number` | `800` | Perspective depth for 3D effect (200–2000) |
|
|
3680
|
+
| blendMode | `string` | `'plus'` | Blend mode: `'plus'` \| `'screen'` \| `'overlay'` \| `'softLight'` \| `'hardLight'` |
|
|
3681
|
+
|
|
3682
|
+
**Available foil presets (`FoilPreset`):**
|
|
3683
|
+
|
|
3684
|
+
| Preset | Description |
|
|
3685
|
+
|--------|-------------|
|
|
3686
|
+
| `rainbow` | Classic holographic rainbow (default) |
|
|
3687
|
+
| `gold` | Premium gold foil |
|
|
3688
|
+
| `silver` | Chrome silver foil |
|
|
3689
|
+
| `cosmic` | Deep space cosmic |
|
|
3690
|
+
| `emerald` | Emerald green luxury |
|
|
3691
|
+
| `rose` | Rose gold / pink |
|
|
3692
|
+
| `ocean` | Deep ocean blue |
|
|
3693
|
+
| `fire` | Hot fire gradient |
|
|
3694
|
+
| `aurora` | Aurora borealis |
|
|
3695
|
+
| `neon` | Neon cyberpunk |
|
|
3696
|
+
|
|
3697
|
+
**Example:**
|
|
3698
|
+
```tsx
|
|
3699
|
+
import { HolographicCard, FOIL_PRESETS } from '@retray-dev/ui-kit/HolographicCard'
|
|
3700
|
+
|
|
3701
|
+
// Basic usage
|
|
3702
|
+
<HolographicCard source={require('./assets/card.png')} width={320} onPress={openCard} />
|
|
3703
|
+
|
|
3704
|
+
// Gold foil preset with higher tilt
|
|
3705
|
+
<HolographicCard
|
|
3706
|
+
source={{ uri: 'https://example.com/card.png' }}
|
|
3707
|
+
width={280}
|
|
3708
|
+
foilPreset="gold"
|
|
3709
|
+
maxTiltDegrees={25}
|
|
3710
|
+
tiltSensitivity={1.5}
|
|
3711
|
+
intensity={0.9}
|
|
3712
|
+
/>
|
|
3713
|
+
|
|
3714
|
+
// Custom foil colors
|
|
3715
|
+
<HolographicCard
|
|
3716
|
+
width={280}
|
|
3717
|
+
foilColors={[
|
|
3718
|
+
'rgba(255, 0, 0, 0.5)',
|
|
3719
|
+
'rgba(255, 255, 0, 0.4)',
|
|
3720
|
+
'rgba(0, 255, 0, 0.5)',
|
|
3721
|
+
]}
|
|
3722
|
+
blendMode="screen"
|
|
3723
|
+
perspective={1200}
|
|
3724
|
+
/>
|
|
3725
|
+
|
|
3726
|
+
// Foil-only surface (no image)
|
|
3727
|
+
<HolographicCard foilPreset="cosmic" width={200} maxTiltDegrees={30} />
|
|
3728
|
+
```
|
|
3729
|
+
|
|
3730
|
+
---
|
|
3731
|
+
|
|
2755
3732
|
## Icon System
|
|
2756
3733
|
|
|
2757
3734
|
The library ships a built-in icon resolver — pass any icon name string to components without manual imports.
|