@retray-dev/ui-kit 10.2.0 → 12.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (153) hide show
  1. package/COMPONENTS.md +287 -37
  2. package/README.md +11 -2
  3. package/dist/Accordion.mjs +2 -2
  4. package/dist/AlertBanner.mjs +2 -2
  5. package/dist/AppHeader.mjs +3 -3
  6. package/dist/Avatar.mjs +2 -2
  7. package/dist/Badge.mjs +2 -2
  8. package/dist/Button.mjs +2 -2
  9. package/dist/Card.mjs +2 -2
  10. package/dist/CategoryStrip.mjs +2 -2
  11. package/dist/Checkbox.mjs +2 -2
  12. package/dist/Chip.mjs +2 -2
  13. package/dist/ConfirmDialog.d.mts +1 -6
  14. package/dist/ConfirmDialog.d.ts +1 -6
  15. package/dist/ConfirmDialog.js +29 -23
  16. package/dist/ConfirmDialog.mjs +3 -3
  17. package/dist/CurrencyDisplay.mjs +2 -2
  18. package/dist/CurrencyInput.d.mts +3 -8
  19. package/dist/CurrencyInput.d.ts +3 -8
  20. package/dist/CurrencyInput.js +3 -1
  21. package/dist/CurrencyInput.mjs +3 -3
  22. package/dist/DetailRow.mjs +2 -2
  23. package/dist/EmptyState.mjs +3 -3
  24. package/dist/ErrorBoundary.mjs +2 -2
  25. package/dist/Form.mjs +2 -2
  26. package/dist/IconButton.mjs +2 -2
  27. package/dist/IconPicker.js +675 -248
  28. package/dist/IconPicker.mjs +3 -2
  29. package/dist/ImageUpload.mjs +3 -3
  30. package/dist/ImageViewer.mjs +4 -4
  31. package/dist/Input.mjs +2 -2
  32. package/dist/LabelValue.mjs +2 -2
  33. package/dist/ListGroup.mjs +2 -2
  34. package/dist/ListItem.d.mts +7 -7
  35. package/dist/ListItem.d.ts +7 -7
  36. package/dist/ListItem.js +12 -7
  37. package/dist/ListItem.mjs +2 -2
  38. package/dist/MediaCard.mjs +2 -2
  39. package/dist/MenuGroup.mjs +2 -2
  40. package/dist/MenuItem.mjs +2 -2
  41. package/dist/MonthPicker.mjs +2 -2
  42. package/dist/NumberStepper.mjs +2 -2
  43. package/dist/PagerDots.mjs +2 -2
  44. package/dist/Pressable.d.mts +15 -7
  45. package/dist/Pressable.d.ts +15 -7
  46. package/dist/Pressable.js +7 -3
  47. package/dist/Pressable.mjs +1 -1
  48. package/dist/PricingCard.mjs +4 -4
  49. package/dist/Progress.mjs +2 -2
  50. package/dist/RadioGroup.mjs +2 -2
  51. package/dist/RetrayProvider.mjs +3 -3
  52. package/dist/Select.mjs +2 -2
  53. package/dist/SelectableGrid.mjs +2 -2
  54. package/dist/Separator.mjs +2 -2
  55. package/dist/Sheet.d.mts +4 -46
  56. package/dist/Sheet.d.ts +4 -46
  57. package/dist/Sheet.js +46 -114
  58. package/dist/Sheet.mjs +2 -3
  59. package/dist/SheetSelect.mjs +2 -2
  60. package/dist/Skeleton.mjs +2 -2
  61. package/dist/Slider.mjs +2 -2
  62. package/dist/Spinner.mjs +2 -2
  63. package/dist/Stats.d.mts +30 -0
  64. package/dist/Stats.d.ts +30 -0
  65. package/dist/Stats.js +429 -0
  66. package/dist/Stats.mjs +9 -0
  67. package/dist/Switch.mjs +2 -2
  68. package/dist/TabBar.mjs +2 -2
  69. package/dist/Tabs.mjs +2 -2
  70. package/dist/Text.d.mts +3 -1
  71. package/dist/Text.d.ts +3 -1
  72. package/dist/Text.js +3 -3
  73. package/dist/Text.mjs +2 -2
  74. package/dist/Textarea.mjs +2 -2
  75. package/dist/Toast.mjs +2 -2
  76. package/dist/Toggle.mjs +2 -2
  77. package/dist/{chunk-YJ7I257J.mjs → chunk-265G6A46.mjs} +1 -1
  78. package/dist/{chunk-ELXBDILQ.mjs → chunk-2A2LEFZG.mjs} +2 -2
  79. package/dist/{chunk-ID72TK46.mjs → chunk-2CBQKU7H.mjs} +1 -1
  80. package/dist/{chunk-OB4JUQ3O.mjs → chunk-2I2AYECM.mjs} +1 -1
  81. package/dist/{chunk-WJLKJMKR.mjs → chunk-357YO24D.mjs} +4 -4
  82. package/dist/{chunk-GQYFLP3D.mjs → chunk-3GEYJ7I5.mjs} +1 -1
  83. package/dist/{chunk-AV4EMIRH.mjs → chunk-3N2M3WZL.mjs} +1 -1
  84. package/dist/{chunk-VF2ATYN3.mjs → chunk-3UYAZ7I4.mjs} +1 -1
  85. package/dist/{chunk-JMOZEC77.mjs → chunk-4WFMPFZB.mjs} +1 -1
  86. package/dist/chunk-5OLNXP3S.mjs +144 -0
  87. package/dist/{chunk-6SECQ2ZF.mjs → chunk-7HSILTC4.mjs} +2 -2
  88. package/dist/{chunk-IRRY3CRZ.mjs → chunk-AKM4EPOT.mjs} +1 -1
  89. package/dist/{chunk-IX3NYLYQ.mjs → chunk-AQEVCEXV.mjs} +1 -1
  90. package/dist/{chunk-WBOOUHSS.mjs → chunk-BCWEHE34.mjs} +1 -1
  91. package/dist/{chunk-AJ7ZDNBT.mjs → chunk-BOVUP27T.mjs} +1 -1
  92. package/dist/{chunk-BRKYVJVV.mjs → chunk-BQZE3HAW.mjs} +1 -1
  93. package/dist/{chunk-Z6SFHN6T.mjs → chunk-D3Y2T42P.mjs} +1 -1
  94. package/dist/{chunk-T2KCAHOS.mjs → chunk-DF6DU42P.mjs} +1 -1
  95. package/dist/{chunk-TB6SD2FT.mjs → chunk-DI7CBDL6.mjs} +1 -1
  96. package/dist/{chunk-HTHGSXFG.mjs → chunk-DOGIPOF5.mjs} +1 -1
  97. package/dist/{chunk-MBMXYJJV.mjs → chunk-E7NEHHXV.mjs} +7 -3
  98. package/dist/{chunk-MX6HRKMI.mjs → chunk-EFLFRAHD.mjs} +1 -1
  99. package/dist/{chunk-SOYNZDVY.mjs → chunk-EMUWGDWC.mjs} +6 -1
  100. package/dist/{chunk-AJRVDP2H.mjs → chunk-F4V6XLP4.mjs} +3 -3
  101. package/dist/{chunk-DYT7BG5I.mjs → chunk-FA2KMTH5.mjs} +1 -1
  102. package/dist/{chunk-Y2NS74WS.mjs → chunk-FFTYLPSB.mjs} +46 -98
  103. package/dist/{chunk-VKID2D2I.mjs → chunk-FUVYSVGR.mjs} +13 -8
  104. package/dist/{chunk-7LWRKMF5.mjs → chunk-FVTVCJAH.mjs} +1 -1
  105. package/dist/{chunk-TZDGAP5N.mjs → chunk-GK4VRMNE.mjs} +2 -2
  106. package/dist/{chunk-6Q64UFIA.mjs → chunk-HJ46DTJE.mjs} +1 -1
  107. package/dist/{chunk-WF2XDFRK.mjs → chunk-HLMPMUK2.mjs} +1 -1
  108. package/dist/{chunk-GD6KXMG5.mjs → chunk-I4V5XZPS.mjs} +1 -1
  109. package/dist/{chunk-TBNZHU6C.mjs → chunk-ISY26JQJ.mjs} +2 -2
  110. package/dist/{chunk-X4G6APW6.mjs → chunk-J6Q2YJEV.mjs} +1 -1
  111. package/dist/{chunk-WYEUNUTP.mjs → chunk-JCZQOY4O.mjs} +31 -24
  112. package/dist/{chunk-U2XJFYED.mjs → chunk-JNVAIDLK.mjs} +1 -1
  113. package/dist/{chunk-SOA2Z4RB.mjs → chunk-JULSIZDM.mjs} +1 -1
  114. package/dist/chunk-KHYX4IOM.mjs +1114 -0
  115. package/dist/{chunk-RYZC432S.mjs → chunk-LRM4AVYY.mjs} +1 -1
  116. package/dist/{chunk-6L4G6PBT.mjs → chunk-MYZ2EDYU.mjs} +1 -1
  117. package/dist/{chunk-BUMAMSTZ.mjs → chunk-N4ZPVCJH.mjs} +1 -1
  118. package/dist/{chunk-Z4VHZ7B5.mjs → chunk-NXI4YDZ2.mjs} +1 -1
  119. package/dist/{chunk-ZZ2R6KZ3.mjs → chunk-OULVKTWL.mjs} +1 -1
  120. package/dist/{chunk-FCSSQK3L.mjs → chunk-P64WHW4A.mjs} +1 -1
  121. package/dist/{chunk-KOO4WITD.mjs → chunk-P73V2EKS.mjs} +1 -1
  122. package/dist/{chunk-SXLKNTA4.mjs → chunk-PGERH3P7.mjs} +1 -1
  123. package/dist/{chunk-2UYENBLV.mjs → chunk-QSFV2P7O.mjs} +1 -1
  124. package/dist/{chunk-JT7HKXRB.mjs → chunk-S3KJCPEJ.mjs} +1 -1
  125. package/dist/{chunk-BEMIQXXU.mjs → chunk-V6NFJXKO.mjs} +1 -1
  126. package/dist/{chunk-A3A6KNQN.mjs → chunk-WOEWGSTU.mjs} +1 -1
  127. package/dist/{chunk-NMU5FMQJ.mjs → chunk-X26S5EVZ.mjs} +4 -2
  128. package/dist/{chunk-YFZ3ELX5.mjs → chunk-XBAGGKLW.mjs} +2 -2
  129. package/dist/{chunk-S2R7UVOE.mjs → chunk-ZHMSAYLT.mjs} +1 -1
  130. package/dist/fonts.d.mts +1 -7
  131. package/dist/fonts.d.ts +1 -7
  132. package/dist/fonts.js +0 -2
  133. package/dist/fonts.mjs +1 -2
  134. package/dist/index.d.mts +4 -1
  135. package/dist/index.d.ts +4 -1
  136. package/dist/index.js +1184 -708
  137. package/dist/index.mjs +53 -52
  138. package/package.json +3 -3
  139. package/src/components/ConfirmDialog/ConfirmDialog.tsx +39 -30
  140. package/src/components/CurrencyInput/CurrencyInput.tsx +4 -7
  141. package/src/components/IconPicker/IconPicker.tsx +124 -112
  142. package/src/components/ListItem/ListItem.tsx +43 -28
  143. package/src/components/Pressable/Pressable.tsx +20 -8
  144. package/src/components/Sheet/Sheet.tsx +64 -172
  145. package/src/components/Stats/Stats.tsx +226 -0
  146. package/src/components/Stats/index.ts +2 -0
  147. package/src/components/Text/Text.tsx +4 -2
  148. package/src/fonts.ts +0 -7
  149. package/src/index.ts +4 -0
  150. package/src/theme/colorUtils.ts +9 -0
  151. package/src/utils/curatedIcons.ts +698 -135
  152. package/src/utils/fontGuard.ts +2 -1
  153. package/dist/chunk-53Z3NYGE.mjs +0 -742
package/dist/index.mjs CHANGED
@@ -1,63 +1,64 @@
1
- export { Switch } from './chunk-WF2XDFRK.mjs';
2
- export { TabBar } from './chunk-Z6SFHN6T.mjs';
3
- export { Tabs, TabsContent } from './chunk-GQYFLP3D.mjs';
4
- export { Text } from './chunk-WJLKJMKR.mjs';
5
- export { Textarea } from './chunk-U2XJFYED.mjs';
6
- export { Toggle } from './chunk-7LWRKMF5.mjs';
7
1
  export { VirtualList } from './chunk-NC5ZTR2Y.mjs';
8
- export { Select } from './chunk-A3A6KNQN.mjs';
9
- export { SelectableGrid } from './chunk-Z4VHZ7B5.mjs';
10
- export { Separator } from './chunk-MX6HRKMI.mjs';
11
- export { BottomSheetModalProvider, Sheet, BottomSheetTextInput as SheetTextInput } from './chunk-Y2NS74WS.mjs';
12
- export { SheetSelect } from './chunk-KOO4WITD.mjs';
13
- export { Skeleton } from './chunk-AJ7ZDNBT.mjs';
14
- export { Slider } from './chunk-JMOZEC77.mjs';
15
- export { MonthPicker, dateToMonthPickerValue, monthPickerValueToDate } from './chunk-GD6KXMG5.mjs';
16
- export { NumberStepper } from './chunk-BUMAMSTZ.mjs';
17
- export { Pressable } from './chunk-MBMXYJJV.mjs';
18
- export { PricingCard } from './chunk-AJRVDP2H.mjs';
19
- export { Progress } from './chunk-OB4JUQ3O.mjs';
20
- export { RadioGroup } from './chunk-X4G6APW6.mjs';
21
- export { RetrayProvider } from './chunk-YFZ3ELX5.mjs';
22
- export { ToastProvider, sonnerToast as toast, useToast } from './chunk-2UYENBLV.mjs';
23
- export { ImageViewer } from './chunk-ELXBDILQ.mjs';
24
- export { PagerDots } from './chunk-ZZ2R6KZ3.mjs';
25
- export { LabelValue } from './chunk-FCSSQK3L.mjs';
26
- export { ListGroup, ListGroupFooter, ListGroupHeader } from './chunk-SOA2Z4RB.mjs';
27
- export { ListItem } from './chunk-VKID2D2I.mjs';
28
- export { MediaCard } from './chunk-IX3NYLYQ.mjs';
29
- export { MenuGroup, MenuGroupFooter, MenuGroupHeader } from './chunk-IRRY3CRZ.mjs';
30
- export { MenuItem } from './chunk-TB6SD2FT.mjs';
31
- export { DetailRow } from './chunk-S2R7UVOE.mjs';
32
- export { EmptyState } from './chunk-6SECQ2ZF.mjs';
33
- export { ErrorBoundary } from './chunk-RYZC432S.mjs';
34
- export { Form, FormField, FormFooter, FormSection } from './chunk-6Q64UFIA.mjs';
35
- export { IconPicker } from './chunk-53Z3NYGE.mjs';
36
- export { ImageUpload } from './chunk-TZDGAP5N.mjs';
37
- export { Spinner } from './chunk-WBOOUHSS.mjs';
2
+ export { Stats } from './chunk-5OLNXP3S.mjs';
3
+ export { Switch } from './chunk-HLMPMUK2.mjs';
4
+ export { TabBar } from './chunk-D3Y2T42P.mjs';
5
+ export { Tabs, TabsContent } from './chunk-3GEYJ7I5.mjs';
6
+ export { Text } from './chunk-357YO24D.mjs';
7
+ export { Textarea } from './chunk-JNVAIDLK.mjs';
8
+ export { Toggle } from './chunk-FVTVCJAH.mjs';
9
+ export { Select } from './chunk-WOEWGSTU.mjs';
10
+ export { SelectableGrid } from './chunk-NXI4YDZ2.mjs';
11
+ export { Separator } from './chunk-EFLFRAHD.mjs';
12
+ export { BottomSheetModalProvider, Sheet, BottomSheetTextInput as SheetTextInput } from './chunk-FFTYLPSB.mjs';
13
+ export { SheetSelect } from './chunk-P73V2EKS.mjs';
14
+ export { Skeleton } from './chunk-BOVUP27T.mjs';
15
+ export { Slider } from './chunk-4WFMPFZB.mjs';
16
+ export { MonthPicker, dateToMonthPickerValue, monthPickerValueToDate } from './chunk-I4V5XZPS.mjs';
17
+ export { NumberStepper } from './chunk-N4ZPVCJH.mjs';
18
+ export { Pressable } from './chunk-E7NEHHXV.mjs';
19
+ export { PricingCard } from './chunk-F4V6XLP4.mjs';
20
+ export { Progress } from './chunk-2I2AYECM.mjs';
21
+ export { RadioGroup } from './chunk-J6Q2YJEV.mjs';
22
+ export { RetrayProvider } from './chunk-XBAGGKLW.mjs';
23
+ export { ToastProvider, sonnerToast as toast, useToast } from './chunk-QSFV2P7O.mjs';
24
+ export { ImageViewer } from './chunk-2A2LEFZG.mjs';
25
+ export { PagerDots } from './chunk-OULVKTWL.mjs';
26
+ export { LabelValue } from './chunk-P64WHW4A.mjs';
27
+ export { ListGroup, ListGroupFooter, ListGroupHeader } from './chunk-JULSIZDM.mjs';
28
+ export { ListItem } from './chunk-FUVYSVGR.mjs';
29
+ export { MediaCard } from './chunk-AQEVCEXV.mjs';
30
+ export { MenuGroup, MenuGroupFooter, MenuGroupHeader } from './chunk-AKM4EPOT.mjs';
31
+ export { MenuItem } from './chunk-DI7CBDL6.mjs';
32
+ export { DetailRow } from './chunk-ZHMSAYLT.mjs';
33
+ export { EmptyState } from './chunk-7HSILTC4.mjs';
34
+ export { ErrorBoundary } from './chunk-LRM4AVYY.mjs';
35
+ export { Form, FormField, FormFooter, FormSection } from './chunk-HJ46DTJE.mjs';
36
+ export { IconPicker } from './chunk-KHYX4IOM.mjs';
37
+ export { ImageUpload } from './chunk-GK4VRMNE.mjs';
38
+ export { Spinner } from './chunk-BCWEHE34.mjs';
38
39
  export { ButtonGroup } from './chunk-3BBOZ3OQ.mjs';
39
- export { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle } from './chunk-ID72TK46.mjs';
40
- export { CategoryStrip } from './chunk-6L4G6PBT.mjs';
40
+ export { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle } from './chunk-2CBQKU7H.mjs';
41
+ export { CategoryStrip } from './chunk-MYZ2EDYU.mjs';
41
42
  import './chunk-YNROWHQJ.mjs';
42
- export { Checkbox } from './chunk-AV4EMIRH.mjs';
43
- export { Chip, ChipGroup } from './chunk-DYT7BG5I.mjs';
44
- export { ConfirmDialog } from './chunk-WYEUNUTP.mjs';
45
- export { CurrencyDisplay } from './chunk-BRKYVJVV.mjs';
46
- export { CurrencyInput } from './chunk-NMU5FMQJ.mjs';
47
- export { Input } from './chunk-SXLKNTA4.mjs';
48
- export { Accordion } from './chunk-YJ7I257J.mjs';
49
- export { AlertBanner } from './chunk-BEMIQXXU.mjs';
50
- export { AppHeader } from './chunk-TBNZHU6C.mjs';
51
- export { IconButton } from './chunk-T2KCAHOS.mjs';
52
- export { Avatar } from './chunk-JT7HKXRB.mjs';
53
- export { Badge } from './chunk-VF2ATYN3.mjs';
54
- export { Button } from './chunk-HTHGSXFG.mjs';
43
+ export { Checkbox } from './chunk-3N2M3WZL.mjs';
44
+ export { Chip, ChipGroup } from './chunk-FA2KMTH5.mjs';
45
+ export { ConfirmDialog } from './chunk-JCZQOY4O.mjs';
46
+ export { CurrencyDisplay } from './chunk-BQZE3HAW.mjs';
47
+ export { CurrencyInput } from './chunk-X26S5EVZ.mjs';
48
+ export { Input } from './chunk-PGERH3P7.mjs';
49
+ export { Accordion } from './chunk-265G6A46.mjs';
50
+ export { AlertBanner } from './chunk-V6NFJXKO.mjs';
51
+ export { AppHeader } from './chunk-ISY26JQJ.mjs';
52
+ export { IconButton } from './chunk-DF6DU42P.mjs';
53
+ export { Avatar } from './chunk-S3KJCPEJ.mjs';
54
+ export { Badge } from './chunk-3UYAZ7I4.mjs';
55
+ export { Button } from './chunk-DOGIPOF5.mjs';
55
56
  import './chunk-3DKJ2GIC.mjs';
56
57
  export { impactHeavy, impactLight, impactMedium, notificationError, notificationSuccess, notificationWarning, richHaptics, selectionAsync } from './chunk-EJ7ZPXOH.mjs';
57
58
  import './chunk-DVK4G2GT.mjs';
58
59
  export { BREAKPOINTS, ICON_SIZES, RADIUS, SHADOWS, SPACING, TYPOGRAPHY } from './chunk-QY3X2UYR.mjs';
59
60
  export { Icon, configureIconFamilies, getValidIconNames, renderIcon } from './chunk-KA7LTET3.mjs';
60
- export { ThemeProvider, defaultDark, defaultLight, deriveColors, useTheme } from './chunk-SOYNZDVY.mjs';
61
+ export { ThemeProvider, defaultDark, defaultLight, deriveColors, useTheme, withAlpha } from './chunk-EMUWGDWC.mjs';
61
62
  import './chunk-2CE3TQVY.mjs';
62
63
  import './chunk-Y6FXYEAI.mjs';
63
64
  import { useState, useCallback } from 'react';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@retray-dev/ui-kit",
3
- "version": "10.2.0",
3
+ "version": "12.1.0",
4
4
  "description": "Personal UI Kit for React Native / Expo",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.mjs",
@@ -110,7 +110,7 @@
110
110
  "react-native-worklets": "0.5.1",
111
111
  "@types/react": "19.1.17",
112
112
  "react-native-safe-area-context": "5.6.2",
113
- "@gorhom/bottom-sheet": "5.2.8"
113
+ "@gorhom/bottom-sheet": "5.2.14"
114
114
  },
115
115
  "onlyBuiltDependencies": [
116
116
  "esbuild"
@@ -119,7 +119,7 @@
119
119
  "devDependencies": {
120
120
  "@eslint/js": "^9.0.0",
121
121
  "@expo/vector-icons": "^15.1.1",
122
- "@gorhom/bottom-sheet": "5.2.8",
122
+ "@gorhom/bottom-sheet": "5.2.14",
123
123
  "@react-native-community/slider": "5.0.1",
124
124
  "@react-native-picker/picker": "2.11.1",
125
125
  "@shopify/react-native-skia": "2.2.12",
@@ -1,10 +1,12 @@
1
- import React, { useEffect, useRef } from 'react'
1
+ import React, { useCallback, useEffect, useRef, useId } from 'react'
2
2
  import { View, Text, TouchableOpacity, StyleSheet } from 'react-native'
3
- import BottomSheet, {
3
+ import {
4
+ BottomSheetModal,
4
5
  BottomSheetView,
5
6
  BottomSheetBackdrop,
6
7
  type BottomSheetBackdropProps,
7
8
  } from '@gorhom/bottom-sheet'
9
+ import { useSafeAreaInsets } from 'react-native-safe-area-context'
8
10
  import { Feather } from '@expo/vector-icons'
9
11
  import { impactMedium, notificationSuccess, selectionAsync as hapticSelection } from '../../utils/haptics'
10
12
  import { useTheme } from '../../theme'
@@ -14,16 +16,11 @@ import { s, vs, ms, mvs } from '../../utils/scaling'
14
16
  export interface ConfirmDialogProps {
15
17
  visible: boolean
16
18
  title: string
17
- /** Secondary text below title. */
18
19
  subtitle?: string
19
- /** @deprecated Use `subtitle` instead. */
20
- description?: string
21
20
  confirmLabel?: string
22
21
  cancelLabel?: string
23
22
  confirmVariant?: 'primary' | 'destructive'
24
- /** Show a loading spinner in the confirm button (e.g. while async action completes). */
25
23
  loading?: boolean
26
- /** Show an X close button in the top-right corner. */
27
24
  showCloseButton?: boolean
28
25
  onConfirm: () => void
29
26
  onCancel: () => void
@@ -33,7 +30,6 @@ export function ConfirmDialog({
33
30
  visible,
34
31
  title,
35
32
  subtitle,
36
- description,
37
33
  confirmLabel = 'Confirm',
38
34
  cancelLabel = 'Cancel',
39
35
  confirmVariant = 'primary',
@@ -43,37 +39,44 @@ export function ConfirmDialog({
43
39
  onCancel,
44
40
  }: ConfirmDialogProps) {
45
41
  const { colors } = useTheme()
46
- const ref = useRef<BottomSheet>(null)
47
- const effectiveSubtitle = subtitle ?? description
42
+ const insets = useSafeAreaInsets()
43
+ const ref = useRef<BottomSheetModal>(null)
44
+ const wasOpened = useRef(false)
45
+ const name = useId()
48
46
 
49
47
  useEffect(() => {
50
48
  if (visible) {
51
49
  impactMedium()
52
- ref.current?.snapToIndex(0)
53
- } else {
54
- ref.current?.close()
50
+ ref.current?.present()
51
+ wasOpened.current = true
52
+ } else if (wasOpened.current) {
53
+ ref.current?.dismiss()
55
54
  }
56
55
  }, [visible])
57
56
 
58
- const renderBackdrop = (props: BottomSheetBackdropProps) => (
59
- <BottomSheetBackdrop
60
- {...props}
61
- disappearsOnIndex={-1}
62
- appearsOnIndex={0}
63
- pressBehavior="close"
64
- />
57
+ const renderBackdrop = useCallback(
58
+ (props: BottomSheetBackdropProps) => (
59
+ <BottomSheetBackdrop
60
+ {...props}
61
+ disappearsOnIndex={-1}
62
+ appearsOnIndex={0}
63
+ pressBehavior="close"
64
+ />
65
+ ),
66
+ []
65
67
  )
66
68
 
67
69
  return (
68
- <BottomSheet
70
+ <BottomSheetModal
69
71
  ref={ref}
70
- index={-1}
71
- onClose={onCancel}
72
+ name={name}
73
+ onDismiss={onCancel}
72
74
  enableDynamicSizing
73
75
  backdropComponent={renderBackdrop}
74
- backgroundStyle={[styles.background, { backgroundColor: colors.card }]}
75
- handleIndicatorStyle={[styles.handle, { backgroundColor: colors.border }]}
76
+ backgroundStyle={{ ...styles.background, backgroundColor: colors.card }}
77
+ handleIndicatorStyle={{ ...styles.handle, backgroundColor: colors.border }}
76
78
  enablePanDownToClose
79
+ topInset={insets.top}
77
80
  >
78
81
  <BottomSheetView style={styles.content}>
79
82
  <View style={styles.header} accessibilityRole="header">
@@ -95,9 +98,9 @@ export function ConfirmDialog({
95
98
  </TouchableOpacity>
96
99
  ) : null}
97
100
  </View>
98
- {effectiveSubtitle ? (
101
+ {subtitle ? (
99
102
  <Text style={[styles.subtitle, { color: colors.foregroundMuted }]} allowFontScaling={true}>
100
- {effectiveSubtitle}
103
+ {subtitle}
101
104
  </Text>
102
105
  ) : null}
103
106
  </View>
@@ -108,7 +111,10 @@ export function ConfirmDialog({
108
111
  fullWidth
109
112
  loading={loading}
110
113
  disabled={loading}
111
- onPress={() => { notificationSuccess(); onConfirm() }}
114
+ onPress={() => {
115
+ notificationSuccess()
116
+ onConfirm()
117
+ }}
112
118
  icon={
113
119
  <Feather
114
120
  name={confirmVariant === 'destructive' ? 'trash-2' : 'check'}
@@ -125,12 +131,15 @@ export function ConfirmDialog({
125
131
  label={cancelLabel}
126
132
  variant="secondary"
127
133
  fullWidth
128
- onPress={() => { hapticSelection(); onCancel() }}
134
+ onPress={() => {
135
+ hapticSelection()
136
+ onCancel()
137
+ }}
129
138
  icon={<Feather name="x" size={15} color={colors.foreground} />}
130
139
  />
131
140
  </View>
132
141
  </BottomSheetView>
133
- </BottomSheet>
142
+ </BottomSheetModal>
134
143
  )
135
144
  }
136
145
 
@@ -1,11 +1,9 @@
1
1
  import React from 'react'
2
- import { ViewStyle, TextStyle } from 'react-native'
2
+ import { ViewStyle, TextStyle, TextInputProps } from 'react-native'
3
3
  import { Input } from '../Input'
4
4
  import { ms, vs } from '../../utils/scaling'
5
5
 
6
- export interface CurrencyInputProps {
7
- value?: string
8
- onChangeText?: (formatted: string) => void
6
+ export interface CurrencyInputProps extends TextInputProps {
9
7
  /** Called with the parsed numeric value (no separators, no prefix). */
10
8
  onChangeValue?: (raw: number) => void
11
9
  /** Currency symbol shown left of the value. Any string (`'$'`, `'€'`, `'£'`, `'COP '`). Defaults to `'$'`. */
@@ -18,10 +16,7 @@ export interface CurrencyInputProps {
18
16
  /** Red helper text; also changes input border to destructive color. */
19
17
  error?: string
20
18
  hint?: string
21
- placeholder?: string
22
- editable?: boolean
23
19
  containerStyle?: ViewStyle
24
- style?: TextStyle
25
20
  /** Use inside a Sheet/BottomSheet — passes sheetMode to the underlying Input. */
26
21
  sheetMode?: boolean
27
22
  }
@@ -47,6 +42,7 @@ export function CurrencyInput({
47
42
  containerStyle,
48
43
  style,
49
44
  sheetMode,
45
+ ...props
50
46
  }: CurrencyInputProps) {
51
47
  const handleChange = (text: string) => {
52
48
  const withoutPrefix = prefix && text.startsWith(prefix) ? text.slice(prefix.length) : text
@@ -73,6 +69,7 @@ export function CurrencyInput({
73
69
 
74
70
  return (
75
71
  <Input
72
+ {...props}
76
73
  value={displayValue}
77
74
  onChangeText={handleChange}
78
75
  keyboardType="numeric"
@@ -1,4 +1,4 @@
1
- import React, { useRef, useState, useCallback, useEffect, useMemo, useId } from 'react'
1
+ import React, { useRef, useState, useCallback, useMemo, useId } from 'react'
2
2
  import { View, Text, TouchableOpacity, StyleSheet, ViewStyle, Dimensions } from 'react-native'
3
3
  import { ScrollView } from 'react-native-gesture-handler'
4
4
  import {
@@ -13,6 +13,7 @@ import { CURATED_ICONS, ALL_CURATED_ICONS } from '../../utils/curatedIcons'
13
13
  import { selectionAsync as hapticSelection, impactMedium } from '../../utils/haptics'
14
14
  import { s, vs, ms } from '../../utils/scaling'
15
15
  import { RADIUS } from '../../tokens'
16
+ import { Spinner } from '../Spinner'
16
17
  import type { BottomSheetBackdropProps, BottomSheetModal as BottomSheetModalType } from '@gorhom/bottom-sheet'
17
18
 
18
19
  const NUM_COLUMNS = 6
@@ -74,10 +75,10 @@ export function IconPicker({
74
75
  const { colors } = useTheme()
75
76
  const insets = useSafeAreaInsets()
76
77
  const sheetRef = useRef<BottomSheetModalType>(null)
77
- const catScrollRef = useRef<any>(null)
78
- const [open, setOpen] = useState(false)
78
+ const catScrollRef = useRef<ScrollView>(null)
79
79
  const [activeCategory, setActiveCategory] = useState<string | null>(null)
80
80
  const [containerWidth, setContainerWidth] = useState(() => Dimensions.get('window').width - s(16) * 2)
81
+ const [ready, setReady] = useState(false)
81
82
 
82
83
  const sheetName = useId()
83
84
 
@@ -101,35 +102,26 @@ export function IconPicker({
101
102
  return result
102
103
  }, [activeIcons, numColumns])
103
104
 
104
- useEffect(() => {
105
- if (open) {
106
- impactMedium()
107
- sheetRef.current?.present()
108
- } else {
109
- sheetRef.current?.dismiss()
110
- }
111
- }, [open])
105
+ const handleDismiss = useCallback(() => {
106
+ setActiveCategory(null)
107
+ setReady(false)
108
+ }, [])
112
109
 
113
110
  const handleSelect = useCallback(
114
111
  (iconName: string) => {
115
112
  onChange(iconName)
116
- setOpen(false)
117
- setActiveCategory(null)
118
113
  },
119
114
  [onChange],
120
115
  )
121
116
 
122
117
  const handleOpen = useCallback(() => {
123
118
  if (disabled) return
119
+ impactMedium()
124
120
  setActiveCategory(null)
125
- setOpen(true)
121
+ setReady(false)
122
+ sheetRef.current?.present()
126
123
  }, [disabled])
127
124
 
128
- const handleClose = useCallback(() => {
129
- setOpen(false)
130
- setActiveCategory(null)
131
- }, [])
132
-
133
125
  const renderBackdrop = useCallback(
134
126
  (props: BottomSheetBackdropProps) => (
135
127
  <BottomSheetBackdrop {...props} disappearsOnIndex={-1} appearsOnIndex={0} pressBehavior="close" />
@@ -185,12 +177,12 @@ export function IconPicker({
185
177
  <BottomSheetModal
186
178
  ref={sheetRef}
187
179
  name={sheetName}
188
- onDismiss={handleClose}
180
+ onDismiss={handleDismiss}
189
181
  enableDynamicSizing={true}
190
182
  maxDynamicContentSize={SCREEN_HEIGHT * 0.7}
191
183
  backdropComponent={renderBackdrop}
192
- backgroundStyle={[styles.sheetBackground, { backgroundColor: colors.card }]}
193
- handleIndicatorStyle={[styles.handle, { backgroundColor: colors.border }]}
184
+ backgroundStyle={{ ...styles.sheetBackground, backgroundColor: colors.card }}
185
+ handleIndicatorStyle={{ ...styles.handle, backgroundColor: colors.border }}
194
186
  enablePanDownToClose
195
187
  topInset={insets.top}
196
188
  android_keyboardInputMode="adjustPan"
@@ -199,103 +191,118 @@ export function IconPicker({
199
191
  contentContainerStyle={styles.sheetContent}
200
192
  showsVerticalScrollIndicator={true}
201
193
  >
202
- {/* Category section label */}
203
- <Text style={[styles.sectionLabel, { color: colors.foregroundSubtle }]} allowFontScaling={true}>
204
- Categorías
205
- </Text>
206
-
207
- {/* Horizontal scrollable category chips */}
208
- <ScrollView
209
- ref={catScrollRef}
210
- horizontal
211
- showsHorizontalScrollIndicator={false}
212
- contentContainerStyle={styles.categoryStrip}
213
- style={styles.categoryScroll}
194
+ {/* Measuring container always rendered so onLayout fires */}
195
+ <View
196
+ style={styles.gridContainer}
197
+ onLayout={(e) => {
198
+ setContainerWidth(e.nativeEvent.layout.width)
199
+ setReady(true)
200
+ }}
214
201
  >
215
- <TouchableOpacity
216
- onPress={() => setActiveCategory(null)}
217
- activeOpacity={0.7}
218
- touchSoundDisabled={true}
219
- accessibilityRole="button"
220
- accessibilityLabel="Todos"
221
- accessibilityState={{ selected: activeCategory === null }}
222
- style={[
223
- styles.categoryChip,
224
- {
225
- backgroundColor: activeCategory === null ? colors.primary : colors.surface,
226
- borderColor: activeCategory === null ? colors.primary : colors.border,
227
- },
228
- ]}
229
- >
230
- <View style={styles.categoryChipInner}>
231
- {renderIcon('grid', ms(14), activeCategory === null ? colors.primaryForeground : colors.foregroundSubtle)}
232
- <Text
233
- style={[
234
- styles.categoryChipText,
235
- { color: activeCategory === null ? colors.primaryForeground : colors.foreground },
236
- ]}
237
- allowFontScaling={true}
238
- numberOfLines={1}
239
- >
240
- Todos
241
- </Text>
202
+ {!ready ? (
203
+ <View style={styles.loader}>
204
+ <Spinner size="md" color={colors.primary} label="Cargando iconos..." />
242
205
  </View>
243
- </TouchableOpacity>
244
- {CURATED_ICONS.map((cat) => (
245
- <TouchableOpacity
246
- key={cat.name}
247
- onPress={() => setActiveCategory(cat.name)}
248
- activeOpacity={0.7}
249
- touchSoundDisabled={true}
250
- accessibilityRole="button"
251
- accessibilityLabel={cat.labelEs}
252
- accessibilityState={{ selected: activeCategory === cat.name }}
253
- style={[
254
- styles.categoryChip,
255
- {
256
- backgroundColor: activeCategory === cat.name ? colors.primary : colors.surface,
257
- borderColor: activeCategory === cat.name ? colors.primary : colors.border,
258
- },
259
- ]}
260
- >
261
- <View style={styles.categoryChipInner}>
262
- {renderIcon(cat.categoryIcon, ms(14), activeCategory === cat.name ? colors.primaryForeground : colors.foregroundSubtle)}
263
- <Text
206
+ ) : (
207
+ <>
208
+ {/* Category section label */}
209
+ <Text style={[styles.sectionLabel, { color: colors.foregroundSubtle }]} allowFontScaling={true}>
210
+ Categorías
211
+ </Text>
212
+
213
+ {/* Horizontal scrollable category chips */}
214
+ <ScrollView
215
+ ref={catScrollRef}
216
+ horizontal
217
+ showsHorizontalScrollIndicator={false}
218
+ contentContainerStyle={styles.categoryStrip}
219
+ style={styles.categoryScroll}
220
+ >
221
+ <TouchableOpacity
222
+ onPress={() => setActiveCategory(null)}
223
+ activeOpacity={0.7}
224
+ touchSoundDisabled={true}
225
+ accessibilityRole="button"
226
+ accessibilityLabel="Todos"
227
+ accessibilityState={{ selected: activeCategory === null }}
264
228
  style={[
265
- styles.categoryChipText,
266
- { color: activeCategory === cat.name ? colors.primaryForeground : colors.foreground },
229
+ styles.categoryChip,
230
+ {
231
+ backgroundColor: activeCategory === null ? colors.primary : colors.surface,
232
+ borderColor: activeCategory === null ? colors.primary : colors.border,
233
+ },
267
234
  ]}
268
- allowFontScaling={true}
269
- numberOfLines={1}
270
235
  >
271
- {cat.labelEs}
272
- </Text>
273
- </View>
274
- </TouchableOpacity>
275
- ))}
276
- </ScrollView>
236
+ <View style={styles.categoryChipInner}>
237
+ {renderIcon('grid', ms(14), activeCategory === null ? colors.primaryForeground : colors.foregroundSubtle)}
238
+ <Text
239
+ style={[
240
+ styles.categoryChipText,
241
+ { color: activeCategory === null ? colors.primaryForeground : colors.foreground },
242
+ ]}
243
+ allowFontScaling={true}
244
+ numberOfLines={1}
245
+ >
246
+ Todos
247
+ </Text>
248
+ </View>
249
+ </TouchableOpacity>
250
+ {CURATED_ICONS.map((cat) => (
251
+ <TouchableOpacity
252
+ key={cat.name}
253
+ onPress={() => setActiveCategory(cat.name)}
254
+ activeOpacity={0.7}
255
+ touchSoundDisabled={true}
256
+ accessibilityRole="button"
257
+ accessibilityLabel={cat.labelEs}
258
+ accessibilityState={{ selected: activeCategory === cat.name }}
259
+ style={[
260
+ styles.categoryChip,
261
+ {
262
+ backgroundColor: activeCategory === cat.name ? colors.primary : colors.surface,
263
+ borderColor: activeCategory === cat.name ? colors.primary : colors.border,
264
+ },
265
+ ]}
266
+ >
267
+ <View style={styles.categoryChipInner}>
268
+ {renderIcon(cat.categoryIcon, ms(14), activeCategory === cat.name ? colors.primaryForeground : colors.foregroundSubtle)}
269
+ <Text
270
+ style={[
271
+ styles.categoryChipText,
272
+ { color: activeCategory === cat.name ? colors.primaryForeground : colors.foreground },
273
+ ]}
274
+ allowFontScaling={true}
275
+ numberOfLines={1}
276
+ >
277
+ {cat.labelEs}
278
+ </Text>
279
+ </View>
280
+ </TouchableOpacity>
281
+ ))}
282
+ </ScrollView>
277
283
 
278
- {/* Separator */}
279
- <View style={[styles.separator, { backgroundColor: colors.border }]} />
284
+ {/* Separator */}
285
+ <View style={[styles.separator, { backgroundColor: colors.border }]} />
280
286
 
281
- {/* Icon grid — rendered directly inside BottomSheetScrollView */}
282
- <View style={styles.gridContainer} onLayout={(e) => setContainerWidth(e.nativeEvent.layout.width)}>
283
- {cellSize > 0 ? rows.map((row, i) => (
284
- <View key={row[0] ?? `row-${i}`} style={[styles.row, { marginBottom: gapPx }]}>
285
- {row.map((name) => (
286
- <IconCellMemo
287
- key={name}
288
- name={name}
289
- selected={value === name}
290
- size={cellSize}
291
- onPress={() => handleSelect(name)}
292
- />
293
- ))}
294
- {Array.from({ length: numColumns - row.length }).map((_, j) => (
295
- <View key={`spacer-${j}`} style={{ width: cellSize, height: cellSize }} />
296
- ))}
297
- </View>
298
- )) : null}
287
+ {/* Icon grid */}
288
+ {cellSize > 0 ? rows.map((row, i) => (
289
+ <View key={row[0] ?? `row-${i}`} style={[styles.row, { marginBottom: gapPx }]}>
290
+ {row.map((name) => (
291
+ <IconCellMemo
292
+ key={name}
293
+ name={name}
294
+ selected={value === name}
295
+ size={cellSize}
296
+ onPress={() => { handleSelect(name); sheetRef.current?.dismiss() }}
297
+ />
298
+ ))}
299
+ {Array.from({ length: numColumns - row.length }).map((_, j) => (
300
+ <View key={`spacer-${j}`} style={{ width: cellSize, height: cellSize }} />
301
+ ))}
302
+ </View>
303
+ )) : null}
304
+ </>
305
+ )}
299
306
  </View>
300
307
  </BottomSheetScrollView>
301
308
  </BottomSheetModal>
@@ -380,4 +387,9 @@ const styles = StyleSheet.create({
380
387
  alignItems: 'center',
381
388
  justifyContent: 'center',
382
389
  },
390
+ loader: {
391
+ minHeight: vs(200),
392
+ alignItems: 'center',
393
+ justifyContent: 'center',
394
+ },
383
395
  })