@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
|
@@ -1,30 +1,23 @@
|
|
|
1
1
|
import React from 'react'
|
|
2
2
|
import {
|
|
3
|
-
|
|
3
|
+
View,
|
|
4
4
|
Text,
|
|
5
5
|
ActivityIndicator,
|
|
6
6
|
StyleSheet,
|
|
7
|
-
TouchableOpacityProps,
|
|
8
7
|
ViewStyle,
|
|
9
8
|
TextStyle,
|
|
10
9
|
} from 'react-native'
|
|
11
|
-
import Animated from 'react-native-reanimated'
|
|
12
10
|
import { impactMedium } from '../../utils/haptics'
|
|
13
11
|
import { useTheme } from '../../theme'
|
|
14
12
|
import { s, vs, ms, mvs } from '../../utils/scaling'
|
|
15
13
|
import { renderIcon } from '../../utils/icons'
|
|
16
14
|
import { RADIUS, TYPOGRAPHY } from '../../tokens'
|
|
17
|
-
import {
|
|
18
|
-
import { PRESS_SCALE } from '../../utils/animations'
|
|
15
|
+
import { PressableButton } from '../../utils/pressable'
|
|
19
16
|
|
|
20
|
-
// primary: filled primary — main CTA (pill-shaped, Airbnb-style)
|
|
21
|
-
// secondary: outlined primary border — alternative actions
|
|
22
|
-
// text: fully transparent — low-emphasis, in-context actions
|
|
23
|
-
// destructive: filled destructive — delete/danger actions
|
|
24
17
|
export type ButtonVariant = 'primary' | 'secondary' | 'text' | 'destructive'
|
|
25
18
|
export type ButtonSize = 'sm' | 'md' | 'lg'
|
|
26
19
|
|
|
27
|
-
export interface ButtonProps
|
|
20
|
+
export interface ButtonProps {
|
|
28
21
|
label: string
|
|
29
22
|
variant?: ButtonVariant
|
|
30
23
|
size?: ButtonSize
|
|
@@ -34,11 +27,16 @@ export interface ButtonProps extends TouchableOpacityProps {
|
|
|
34
27
|
iconName?: string
|
|
35
28
|
iconColor?: string
|
|
36
29
|
iconPosition?: 'left' | 'right'
|
|
30
|
+
disabled?: boolean
|
|
31
|
+
style?: ViewStyle
|
|
32
|
+
onPress?: () => void
|
|
33
|
+
accessibilityLabel?: string
|
|
34
|
+
accessibilityHint?: string
|
|
37
35
|
}
|
|
38
36
|
|
|
39
|
-
// Airbnb-spec sizing: md=48px height, padding 14px vertical 24px horizontal
|
|
40
37
|
const containerSizeStyles: Record<ButtonSize, ViewStyle> = {
|
|
41
|
-
|
|
38
|
+
// AUDIT FIX: sm was 40pt — below Apple HIG 44pt minimum touch target.
|
|
39
|
+
sm: { paddingHorizontal: s(16), paddingVertical: vs(12), minHeight: 44 },
|
|
42
40
|
md: { paddingHorizontal: s(24), paddingVertical: vs(14), minHeight: 48 },
|
|
43
41
|
lg: { paddingHorizontal: s(28), paddingVertical: vs(16), minHeight: 56 },
|
|
44
42
|
}
|
|
@@ -51,7 +49,7 @@ const labelSizeStyles: Record<ButtonSize, TextStyle> = {
|
|
|
51
49
|
|
|
52
50
|
const iconSizeMap: Record<ButtonSize, number> = { sm: 16, md: 18, lg: 20 }
|
|
53
51
|
|
|
54
|
-
|
|
52
|
+
function ButtonBase({
|
|
55
53
|
label,
|
|
56
54
|
variant = 'primary',
|
|
57
55
|
size = 'md',
|
|
@@ -66,18 +64,13 @@ export function Button({
|
|
|
66
64
|
onPress,
|
|
67
65
|
accessibilityLabel,
|
|
68
66
|
accessibilityHint,
|
|
69
|
-
...props
|
|
70
67
|
}: ButtonProps) {
|
|
71
68
|
const { colors } = useTheme()
|
|
72
69
|
const isDisabled = disabled || loading
|
|
73
|
-
const { animatedStyle, onPressIn, onPressOut, hoverHandlers } = usePressScale({
|
|
74
|
-
pressScale: PRESS_SCALE.button,
|
|
75
|
-
disabled: isDisabled,
|
|
76
|
-
})
|
|
77
70
|
|
|
78
|
-
const handlePress
|
|
71
|
+
const handlePress = () => {
|
|
79
72
|
impactMedium()
|
|
80
|
-
onPress?.(
|
|
73
|
+
onPress?.()
|
|
81
74
|
}
|
|
82
75
|
|
|
83
76
|
const containerVariantStyle: ViewStyle = {
|
|
@@ -90,7 +83,10 @@ export function Button({
|
|
|
90
83
|
const labelVariantStyle: TextStyle = {
|
|
91
84
|
primary: { color: colors.primaryForeground },
|
|
92
85
|
secondary: { color: colors.primary },
|
|
93
|
-
|
|
86
|
+
// AUDIT FIX: was colors.foreground — visually indistinguishable from plain text,
|
|
87
|
+
// no affordance that it's a CTA. Now uses accentResolved so text-only buttons
|
|
88
|
+
// carry the brand voltage. Falls back to primary when no accent is defined.
|
|
89
|
+
text: { color: colors.accentResolved },
|
|
94
90
|
destructive: { color: colors.destructiveForeground },
|
|
95
91
|
}[variant]
|
|
96
92
|
|
|
@@ -103,19 +99,15 @@ export function Button({
|
|
|
103
99
|
const spinnerColor =
|
|
104
100
|
variant === 'destructive' ? colors.destructiveForeground
|
|
105
101
|
: variant === 'primary' ? colors.primaryForeground
|
|
106
|
-
: colors.
|
|
102
|
+
: colors.accentResolved
|
|
107
103
|
|
|
108
|
-
// Extract flex from style for wrapper — ButtonGroup sets flex: 1
|
|
109
104
|
const styleArray = Array.isArray(style) ? style : style ? [style] : []
|
|
110
105
|
const flatStyle = StyleSheet.flatten(styleArray)
|
|
111
106
|
const { flex, ...restStyle } = flatStyle || {}
|
|
112
107
|
|
|
113
108
|
return (
|
|
114
|
-
<
|
|
115
|
-
|
|
116
|
-
{...hoverHandlers}
|
|
117
|
-
>
|
|
118
|
-
<TouchableOpacity
|
|
109
|
+
<View style={[fullWidth && styles.fullWidth, flex !== undefined && { flex }]}>
|
|
110
|
+
<PressableButton
|
|
119
111
|
style={[
|
|
120
112
|
styles.base,
|
|
121
113
|
containerVariantStyle,
|
|
@@ -124,17 +116,15 @@ export function Button({
|
|
|
124
116
|
isDisabled && styles.disabled,
|
|
125
117
|
restStyle,
|
|
126
118
|
]}
|
|
127
|
-
|
|
128
|
-
activeOpacity={1}
|
|
129
|
-
touchSoundDisabled={true}
|
|
119
|
+
enabled={!isDisabled}
|
|
130
120
|
onPress={handlePress}
|
|
131
|
-
|
|
132
|
-
|
|
121
|
+
rippleColor="transparent"
|
|
122
|
+
touchSoundDisabled
|
|
123
|
+
activateOnHover
|
|
133
124
|
accessibilityRole="button"
|
|
134
125
|
accessibilityLabel={accessibilityLabel ?? label}
|
|
135
126
|
accessibilityHint={accessibilityHint}
|
|
136
127
|
accessibilityState={{ disabled: isDisabled, busy: loading }}
|
|
137
|
-
{...props}
|
|
138
128
|
>
|
|
139
129
|
{loading ? (
|
|
140
130
|
<>
|
|
@@ -160,14 +150,16 @@ export function Button({
|
|
|
160
150
|
{effectiveIcon && iconPosition === 'right' && <>{effectiveIcon}</>}
|
|
161
151
|
</>
|
|
162
152
|
)}
|
|
163
|
-
</
|
|
164
|
-
</
|
|
153
|
+
</PressableButton>
|
|
154
|
+
</View>
|
|
165
155
|
)
|
|
166
156
|
}
|
|
167
157
|
|
|
158
|
+
export const Button = React.memo(ButtonBase)
|
|
159
|
+
|
|
168
160
|
const styles = StyleSheet.create({
|
|
169
161
|
base: {
|
|
170
|
-
borderRadius: RADIUS.md,
|
|
162
|
+
borderRadius: RADIUS.md,
|
|
171
163
|
alignItems: 'center',
|
|
172
164
|
justifyContent: 'center',
|
|
173
165
|
flexDirection: 'row',
|
|
@@ -179,7 +171,7 @@ const styles = StyleSheet.create({
|
|
|
179
171
|
opacity: 0.45,
|
|
180
172
|
},
|
|
181
173
|
label: {
|
|
182
|
-
fontFamily: '
|
|
174
|
+
fontFamily: 'Sohne-Medium',
|
|
183
175
|
flexShrink: 1,
|
|
184
176
|
},
|
|
185
177
|
labelWithIcon: {
|
|
@@ -33,16 +33,19 @@ export function ButtonGroup({ children, gap = 12, vertical = false, style }: But
|
|
|
33
33
|
style,
|
|
34
34
|
]}
|
|
35
35
|
>
|
|
36
|
-
{React.Children.map(children, (child) =>
|
|
37
|
-
React.isValidElement(child)
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
36
|
+
{React.Children.map(children, (child) => {
|
|
37
|
+
if (!React.isValidElement(child)) return child
|
|
38
|
+
const childProps = child.props as Record<string, unknown>
|
|
39
|
+
const extraProps: Record<string, unknown> = {
|
|
40
|
+
style: [(child as React.ReactElement<{ style?: unknown }>).props.style, { flex: 1 }],
|
|
41
|
+
}
|
|
42
|
+
// Horizontal ButtonGroup: default Button children to sm size to prevent
|
|
43
|
+
// oversized layout. Only applies when consumer hasn't explicitly set size.
|
|
44
|
+
if (!vertical && 'label' in childProps && childProps['size'] === undefined) {
|
|
45
|
+
extraProps['size'] = 'sm'
|
|
46
|
+
}
|
|
47
|
+
return React.cloneElement(child as React.ReactElement<Record<string, unknown>>, extraProps)
|
|
48
|
+
})}
|
|
46
49
|
</View>
|
|
47
50
|
)
|
|
48
51
|
}
|
|
@@ -1,59 +1,29 @@
|
|
|
1
1
|
import React from 'react'
|
|
2
|
-
import { View, Text,
|
|
3
|
-
import Animated from 'react-native-reanimated'
|
|
2
|
+
import { View, Text, StyleSheet, ViewStyle, TextStyle } from 'react-native'
|
|
4
3
|
import { impactLight } from '../../utils/haptics'
|
|
5
4
|
import { useTheme } from '../../theme'
|
|
6
5
|
import { s, vs, ms, mvs } from '../../utils/scaling'
|
|
7
6
|
import { RADIUS } from '../../tokens'
|
|
8
|
-
import {
|
|
9
|
-
import { SPRINGS, PRESS_SCALE } from '../../utils/animations'
|
|
7
|
+
import { PressableCard } from '../../utils/pressable'
|
|
10
8
|
|
|
11
9
|
export type CardVariant = 'elevated' | 'outlined' | 'filled'
|
|
12
10
|
|
|
13
11
|
export interface CardProps {
|
|
14
12
|
children: React.ReactNode
|
|
15
|
-
/** Visual style variant. `'elevated'` (default) has shadow, `'outlined'` has border only, `'filled'` uses accent background. */
|
|
16
13
|
variant?: CardVariant
|
|
17
|
-
/** Makes the card tappable. Adds press animation and haptic feedback. */
|
|
18
14
|
onPress?: () => void
|
|
19
15
|
style?: ViewStyle
|
|
20
|
-
/** Accessibility label for the card (when interactive). */
|
|
21
16
|
accessibilityLabel?: string
|
|
22
17
|
}
|
|
23
18
|
|
|
24
|
-
export interface CardHeaderProps {
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
export interface CardTitleProps {
|
|
30
|
-
children: React.ReactNode
|
|
31
|
-
style?: TextStyle
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
export interface CardDescriptionProps {
|
|
35
|
-
children: React.ReactNode
|
|
36
|
-
style?: TextStyle
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
export interface CardContentProps {
|
|
40
|
-
children: React.ReactNode
|
|
41
|
-
style?: ViewStyle
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
export interface CardFooterProps {
|
|
45
|
-
children: React.ReactNode
|
|
46
|
-
style?: ViewStyle
|
|
47
|
-
}
|
|
19
|
+
export interface CardHeaderProps { children: React.ReactNode; style?: ViewStyle }
|
|
20
|
+
export interface CardTitleProps { children: React.ReactNode; style?: TextStyle }
|
|
21
|
+
export interface CardDescriptionProps { children: React.ReactNode; style?: TextStyle }
|
|
22
|
+
export interface CardContentProps { children: React.ReactNode; style?: ViewStyle }
|
|
23
|
+
export interface CardFooterProps { children: React.ReactNode; style?: ViewStyle }
|
|
48
24
|
|
|
49
25
|
export function Card({ children, variant = 'elevated', onPress, style, accessibilityLabel }: CardProps) {
|
|
50
26
|
const { colors } = useTheme()
|
|
51
|
-
const { animatedStyle, onPressIn, onPressOut, hoverHandlers } = usePressScale({
|
|
52
|
-
pressScale: PRESS_SCALE.card,
|
|
53
|
-
pressInSpring: SPRINGS.surfacePressIn,
|
|
54
|
-
pressOutSpring: SPRINGS.surfacePressOut,
|
|
55
|
-
disabled: !onPress,
|
|
56
|
-
})
|
|
57
27
|
|
|
58
28
|
const handlePress = () => {
|
|
59
29
|
if (!onPress) return
|
|
@@ -64,11 +34,14 @@ export function Card({ children, variant = 'elevated', onPress, style, accessibi
|
|
|
64
34
|
const variantStyle: ViewStyle = {
|
|
65
35
|
elevated: {
|
|
66
36
|
backgroundColor: colors.card,
|
|
67
|
-
|
|
37
|
+
// AUDIT FIX: removed borderColor — shadow is the depth signal; a border on
|
|
38
|
+
// top of a shadow creates redundant double-framing that reads as "heavy"
|
|
39
|
+
// rather than "elevated". borderWidth: 0 overrides the base style's borderWidth: 1.
|
|
40
|
+
borderWidth: 0,
|
|
68
41
|
shadowColor: '#000',
|
|
69
|
-
shadowOffset: { width: 0, height:
|
|
70
|
-
shadowOpacity: 0.
|
|
71
|
-
shadowRadius:
|
|
42
|
+
shadowOffset: { width: 0, height: 4 },
|
|
43
|
+
shadowOpacity: 0.09,
|
|
44
|
+
shadowRadius: 14,
|
|
72
45
|
elevation: 4,
|
|
73
46
|
},
|
|
74
47
|
outlined: {
|
|
@@ -93,19 +66,16 @@ export function Card({ children, variant = 'elevated', onPress, style, accessibi
|
|
|
93
66
|
|
|
94
67
|
if (onPress) {
|
|
95
68
|
return (
|
|
96
|
-
<
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
{cardContent}
|
|
107
|
-
</TouchableOpacity>
|
|
108
|
-
</Animated.View>
|
|
69
|
+
<PressableCard
|
|
70
|
+
onPress={handlePress}
|
|
71
|
+
rippleColor="transparent"
|
|
72
|
+
touchSoundDisabled
|
|
73
|
+
activateOnHover
|
|
74
|
+
accessibilityRole="button"
|
|
75
|
+
accessibilityLabel={accessibilityLabel}
|
|
76
|
+
>
|
|
77
|
+
{cardContent}
|
|
78
|
+
</PressableCard>
|
|
109
79
|
)
|
|
110
80
|
}
|
|
111
81
|
|
|
@@ -124,7 +94,7 @@ export function CardTitle({ children, style }: CardTitleProps) {
|
|
|
124
94
|
export function CardDescription({ children, style }: CardDescriptionProps) {
|
|
125
95
|
const { colors } = useTheme()
|
|
126
96
|
return (
|
|
127
|
-
<Text style={[styles.description, { color: colors.
|
|
97
|
+
<Text style={[styles.description, { color: colors.foregroundSubtle }, style]} allowFontScaling={true}>{children}</Text>
|
|
128
98
|
)
|
|
129
99
|
}
|
|
130
100
|
|
|
@@ -147,12 +117,14 @@ const styles = StyleSheet.create({
|
|
|
147
117
|
gap: vs(4),
|
|
148
118
|
},
|
|
149
119
|
title: {
|
|
150
|
-
fontFamily: '
|
|
120
|
+
fontFamily: 'Sohne-SemiBold',
|
|
151
121
|
fontSize: ms(16),
|
|
152
122
|
lineHeight: mvs(22),
|
|
153
123
|
},
|
|
124
|
+
// AUDIT FIX: was foregroundMuted (2.2:1 fail) — description text now uses
|
|
125
|
+
// foregroundSubtle (5.9:1 ✓) which is readable while still visually secondary.
|
|
154
126
|
description: {
|
|
155
|
-
fontFamily: '
|
|
127
|
+
fontFamily: 'Sohne-Regular',
|
|
156
128
|
fontSize: ms(13),
|
|
157
129
|
lineHeight: mvs(18),
|
|
158
130
|
},
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import React from 'react'
|
|
1
|
+
import React, { useCallback } from 'react'
|
|
2
2
|
import {
|
|
3
3
|
ScrollView,
|
|
4
4
|
TouchableOpacity,
|
|
@@ -7,17 +7,14 @@ import {
|
|
|
7
7
|
StyleSheet,
|
|
8
8
|
ViewStyle,
|
|
9
9
|
} from 'react-native'
|
|
10
|
-
import Animated
|
|
11
|
-
|
|
12
|
-
interpolateColor,
|
|
13
|
-
} from 'react-native-reanimated'
|
|
10
|
+
import Animated from 'react-native-reanimated'
|
|
11
|
+
import { EaseView } from 'react-native-ease'
|
|
14
12
|
import { selectionAsync as hapticSelection } from '../../utils/haptics'
|
|
15
13
|
import { useTheme } from '../../theme'
|
|
16
14
|
import { s, vs, ms } from '../../utils/scaling'
|
|
17
15
|
import { renderIcon } from '../../utils/icons'
|
|
18
16
|
import { usePressScale } from '../../utils/usePressScale'
|
|
19
|
-
import {
|
|
20
|
-
import { PRESS_SCALE } from '../../utils/animations'
|
|
17
|
+
import { COLOR_TRANSITION, PRESS_SCALE } from '../../utils/animations'
|
|
21
18
|
import { RADIUS } from '../../tokens'
|
|
22
19
|
|
|
23
20
|
export interface CategoryItem {
|
|
@@ -42,30 +39,19 @@ export interface CategoryStripProps {
|
|
|
42
39
|
accessibilityLabel?: string
|
|
43
40
|
}
|
|
44
41
|
|
|
45
|
-
function CategoryChip({
|
|
42
|
+
const CategoryChip = React.memo(function CategoryChip({
|
|
46
43
|
item,
|
|
47
44
|
selected,
|
|
48
|
-
|
|
45
|
+
onSelect,
|
|
49
46
|
}: {
|
|
50
47
|
item: CategoryItem
|
|
51
48
|
selected: boolean
|
|
52
|
-
|
|
49
|
+
onSelect: (value: string) => void
|
|
53
50
|
}) {
|
|
54
51
|
const { colors } = useTheme()
|
|
55
52
|
const { animatedStyle: scaleStyle, onPressIn, onPressOut, hoverHandlers } = usePressScale({
|
|
56
53
|
pressScale: PRESS_SCALE.chip,
|
|
57
54
|
})
|
|
58
|
-
const progress = useColorTransition(selected)
|
|
59
|
-
|
|
60
|
-
const surfaceStyle = useAnimatedStyle(() => ({
|
|
61
|
-
backgroundColor: interpolateColor(progress.value, [0, 1], [colors.surface, colors.primary]),
|
|
62
|
-
borderColor: interpolateColor(progress.value, [0, 1], [colors.border, colors.primary]),
|
|
63
|
-
}))
|
|
64
|
-
|
|
65
|
-
const textColorStyle = useAnimatedStyle(() => ({
|
|
66
|
-
color: interpolateColor(progress.value, [0, 1], [colors.foregroundSubtle, colors.primaryForeground]),
|
|
67
|
-
}))
|
|
68
|
-
|
|
69
55
|
// Static color for icon — icon families take a static color prop, not animated.
|
|
70
56
|
const iconColor = selected ? colors.primaryForeground : colors.foregroundSubtle
|
|
71
57
|
const resolvedIcon =
|
|
@@ -76,7 +62,7 @@ function CategoryChip({
|
|
|
76
62
|
return (
|
|
77
63
|
<Animated.View style={scaleStyle} {...hoverHandlers}>
|
|
78
64
|
<TouchableOpacity
|
|
79
|
-
onPress={
|
|
65
|
+
onPress={() => onSelect(item.value)}
|
|
80
66
|
onPressIn={onPressIn}
|
|
81
67
|
onPressOut={onPressOut}
|
|
82
68
|
activeOpacity={1}
|
|
@@ -85,11 +71,21 @@ function CategoryChip({
|
|
|
85
71
|
accessibilityLabel={item.label}
|
|
86
72
|
accessibilityState={{ selected }}
|
|
87
73
|
>
|
|
88
|
-
<
|
|
74
|
+
<EaseView
|
|
75
|
+
style={styles.chip}
|
|
76
|
+
animate={{
|
|
77
|
+
backgroundColor: selected ? colors.primary : colors.surface,
|
|
78
|
+
borderColor: selected ? colors.primary : colors.border,
|
|
79
|
+
}}
|
|
80
|
+
transition={COLOR_TRANSITION}
|
|
81
|
+
>
|
|
89
82
|
{resolvedIcon && <View style={styles.chipIcon}>{resolvedIcon}</View>}
|
|
90
|
-
<
|
|
83
|
+
<Text
|
|
84
|
+
style={[styles.chipLabel, { color: selected ? colors.primaryForeground : colors.foregroundSubtle }]}
|
|
85
|
+
allowFontScaling={true}
|
|
86
|
+
>
|
|
91
87
|
{item.label}
|
|
92
|
-
</
|
|
88
|
+
</Text>
|
|
93
89
|
{item.badge !== undefined && item.badge > 0 && (
|
|
94
90
|
<View style={[styles.chipBadge, { backgroundColor: colors.primary }]}>
|
|
95
91
|
<Text style={[styles.chipBadgeText, { color: colors.primaryForeground }]}>
|
|
@@ -97,11 +93,11 @@ function CategoryChip({
|
|
|
97
93
|
</Text>
|
|
98
94
|
</View>
|
|
99
95
|
)}
|
|
100
|
-
</
|
|
96
|
+
</EaseView>
|
|
101
97
|
</TouchableOpacity>
|
|
102
98
|
</Animated.View>
|
|
103
99
|
)
|
|
104
|
-
}
|
|
100
|
+
})
|
|
105
101
|
|
|
106
102
|
export function CategoryStrip({
|
|
107
103
|
categories,
|
|
@@ -114,18 +110,21 @@ export function CategoryStrip({
|
|
|
114
110
|
}: CategoryStripProps) {
|
|
115
111
|
const selected = Array.isArray(value) ? value : value ? [value] : []
|
|
116
112
|
|
|
117
|
-
const handlePress = (
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
113
|
+
const handlePress = useCallback(
|
|
114
|
+
(v: string) => {
|
|
115
|
+
hapticSelection()
|
|
116
|
+
if (multiSelect) {
|
|
117
|
+
const current = Array.isArray(value) ? value : value ? [value] : []
|
|
118
|
+
const next = current.includes(v)
|
|
119
|
+
? current.filter((x) => x !== v)
|
|
120
|
+
: [...current, v]
|
|
121
|
+
onValueChange?.(next)
|
|
122
|
+
} else {
|
|
123
|
+
onValueChange?.(v === value ? '' : v)
|
|
124
|
+
}
|
|
125
|
+
},
|
|
126
|
+
[multiSelect, value, onValueChange],
|
|
127
|
+
)
|
|
129
128
|
|
|
130
129
|
return (
|
|
131
130
|
<ScrollView
|
|
@@ -141,7 +140,7 @@ export function CategoryStrip({
|
|
|
141
140
|
<CategoryChip
|
|
142
141
|
item={cat}
|
|
143
142
|
selected={selected.includes(cat.value)}
|
|
144
|
-
|
|
143
|
+
onSelect={handlePress}
|
|
145
144
|
/>
|
|
146
145
|
</View>
|
|
147
146
|
))}
|
|
@@ -173,7 +172,7 @@ const styles = StyleSheet.create({
|
|
|
173
172
|
justifyContent: 'center',
|
|
174
173
|
},
|
|
175
174
|
chipLabel: {
|
|
176
|
-
fontFamily: '
|
|
175
|
+
fontFamily: 'Sohne-Medium',
|
|
177
176
|
fontSize: ms(13),
|
|
178
177
|
},
|
|
179
178
|
chipBadge: {
|
|
@@ -185,7 +184,7 @@ const styles = StyleSheet.create({
|
|
|
185
184
|
justifyContent: 'center',
|
|
186
185
|
},
|
|
187
186
|
chipBadgeText: {
|
|
188
|
-
fontFamily: '
|
|
187
|
+
fontFamily: 'Sohne-Bold',
|
|
189
188
|
fontSize: ms(9),
|
|
190
189
|
lineHeight: 14,
|
|
191
190
|
},
|
|
@@ -1,16 +1,11 @@
|
|
|
1
1
|
import React from 'react'
|
|
2
|
-
import {
|
|
3
|
-
import
|
|
4
|
-
useAnimatedStyle,
|
|
5
|
-
interpolateColor,
|
|
6
|
-
withTiming,
|
|
7
|
-
} from 'react-native-reanimated'
|
|
2
|
+
import { View, Text, StyleSheet, ViewStyle } from 'react-native'
|
|
3
|
+
import { EaseView } from 'react-native-ease'
|
|
8
4
|
import { selectionAsync as hapticSelection } from '../../utils/haptics'
|
|
9
5
|
import { useTheme } from '../../theme'
|
|
10
6
|
import { s, vs, ms, mvs } from '../../utils/scaling'
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
13
|
-
import { PRESS_SCALE, TIMINGS, EASINGS } from '../../utils/animations'
|
|
7
|
+
import { COLOR_TRANSITION, OPACITY_TRANSITION } from '../../utils/animations'
|
|
8
|
+
import { PressableButton } from '../../utils/pressable'
|
|
14
9
|
|
|
15
10
|
export interface CheckboxProps {
|
|
16
11
|
checked?: boolean
|
|
@@ -30,55 +25,47 @@ export function Checkbox({
|
|
|
30
25
|
accessibilityLabel,
|
|
31
26
|
}: CheckboxProps) {
|
|
32
27
|
const { colors } = useTheme()
|
|
33
|
-
const { animatedStyle: scaleStyle, onPressIn, onPressOut } = usePressScale({
|
|
34
|
-
pressScale: PRESS_SCALE.button,
|
|
35
|
-
disabled,
|
|
36
|
-
})
|
|
37
|
-
const progress = useColorTransition(checked)
|
|
38
28
|
|
|
39
|
-
const
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
const checkStyle = useAnimatedStyle(() => ({
|
|
45
|
-
opacity: withTiming(checked ? 1 : 0, { duration: TIMINGS.state.duration, easing: EASINGS.standard }),
|
|
46
|
-
}))
|
|
29
|
+
const handlePress = () => {
|
|
30
|
+
hapticSelection()
|
|
31
|
+
onCheckedChange?.(!checked)
|
|
32
|
+
}
|
|
47
33
|
|
|
48
34
|
return (
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
activeOpacity={1}
|
|
59
|
-
touchSoundDisabled={true}
|
|
35
|
+
// AUDIT FIX: opacity was applied only to the box, leaving the label at full
|
|
36
|
+
// opacity when disabled — a contradictory visual signal. Now the entire row
|
|
37
|
+
// dims uniformly so label and control communicate the same disabled state.
|
|
38
|
+
<PressableButton
|
|
39
|
+
style={[styles.row, disabled && styles.rowDisabled, style]}
|
|
40
|
+
onPress={handlePress}
|
|
41
|
+
enabled={!disabled}
|
|
42
|
+
rippleColor="transparent"
|
|
43
|
+
touchSoundDisabled
|
|
60
44
|
accessibilityRole="checkbox"
|
|
61
45
|
accessibilityLabel={accessibilityLabel ?? label}
|
|
62
46
|
accessibilityState={{ checked, disabled: !!disabled }}
|
|
63
47
|
>
|
|
64
|
-
<
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
48
|
+
<EaseView
|
|
49
|
+
style={styles.box}
|
|
50
|
+
animate={{
|
|
51
|
+
borderColor: checked ? colors.primary : colors.border,
|
|
52
|
+
backgroundColor: checked ? colors.primary : 'transparent',
|
|
53
|
+
}}
|
|
54
|
+
transition={COLOR_TRANSITION}
|
|
55
|
+
>
|
|
56
|
+
<EaseView animate={{ opacity: checked ? 1 : 0 }} transition={OPACITY_TRANSITION}>
|
|
57
|
+
<View style={[styles.checkmark, { borderColor: colors.primaryForeground }]} />
|
|
58
|
+
</EaseView>
|
|
59
|
+
</EaseView>
|
|
73
60
|
{label ? (
|
|
74
61
|
<Text
|
|
75
|
-
style={[styles.label, { color:
|
|
62
|
+
style={[styles.label, { color: colors.foreground }]}
|
|
76
63
|
allowFontScaling={true}
|
|
77
64
|
>
|
|
78
65
|
{label}
|
|
79
66
|
</Text>
|
|
80
67
|
) : null}
|
|
81
|
-
</
|
|
68
|
+
</PressableButton>
|
|
82
69
|
)
|
|
83
70
|
}
|
|
84
71
|
|
|
@@ -88,6 +75,10 @@ const styles = StyleSheet.create({
|
|
|
88
75
|
alignItems: 'center',
|
|
89
76
|
gap: s(12),
|
|
90
77
|
},
|
|
78
|
+
// AUDIT FIX: was inline opacity on the box only
|
|
79
|
+
rowDisabled: {
|
|
80
|
+
opacity: 0.45,
|
|
81
|
+
},
|
|
91
82
|
box: {
|
|
92
83
|
width: s(24),
|
|
93
84
|
height: s(24),
|
|
@@ -104,7 +95,7 @@ const styles = StyleSheet.create({
|
|
|
104
95
|
transform: [{ rotate: '-45deg' }, { translateY: -1 }],
|
|
105
96
|
},
|
|
106
97
|
label: {
|
|
107
|
-
fontFamily: '
|
|
98
|
+
fontFamily: 'Sohne-Regular',
|
|
108
99
|
fontSize: ms(14),
|
|
109
100
|
lineHeight: mvs(20),
|
|
110
101
|
},
|