@retray-dev/ui-kit 10.2.0 → 12.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/COMPONENTS.md +384 -40
- package/README.md +14 -5
- package/dist/Accordion.d.mts +6 -0
- package/dist/Accordion.d.ts +6 -0
- package/dist/Accordion.js +16 -0
- package/dist/Accordion.mjs +2 -2
- package/dist/AlertBanner.js +2 -0
- package/dist/AlertBanner.mjs +2 -2
- package/dist/AppHeader.js +2 -0
- package/dist/AppHeader.mjs +3 -3
- package/dist/Avatar.js +2 -0
- package/dist/Avatar.mjs +2 -2
- package/dist/Badge.js +2 -0
- package/dist/Badge.mjs +2 -2
- package/dist/Button.js +17 -17
- package/dist/Button.mjs +2 -2
- package/dist/Card.js +2 -0
- package/dist/Card.mjs +2 -2
- package/dist/CategoryStrip.js +2 -0
- package/dist/CategoryStrip.mjs +2 -2
- package/dist/Checkbox.js +2 -0
- package/dist/Checkbox.mjs +2 -2
- package/dist/Chip.js +2 -0
- package/dist/Chip.mjs +2 -2
- package/dist/ConfirmDialog.d.mts +1 -6
- package/dist/ConfirmDialog.d.ts +1 -6
- package/dist/ConfirmDialog.js +53 -41
- package/dist/ConfirmDialog.mjs +3 -3
- package/dist/CurrencyDisplay.js +2 -0
- package/dist/CurrencyDisplay.mjs +2 -2
- package/dist/CurrencyInput.d.mts +3 -8
- package/dist/CurrencyInput.d.ts +3 -8
- package/dist/CurrencyInput.js +5 -1
- package/dist/CurrencyInput.mjs +3 -3
- package/dist/DetailRow.js +2 -0
- package/dist/DetailRow.mjs +2 -2
- package/dist/EmptyState.js +17 -17
- package/dist/EmptyState.mjs +3 -3
- package/dist/ErrorBoundary.js +2 -0
- package/dist/ErrorBoundary.mjs +2 -2
- package/dist/Form.js +2 -0
- package/dist/Form.mjs +2 -2
- package/dist/IconButton.js +2 -0
- package/dist/IconButton.mjs +2 -2
- package/dist/IconPicker.js +677 -248
- package/dist/IconPicker.mjs +3 -2
- package/dist/ImageUpload.d.mts +3 -1
- package/dist/ImageUpload.d.ts +3 -1
- package/dist/ImageUpload.js +10 -3
- package/dist/ImageUpload.mjs +3 -3
- package/dist/ImageViewer.js +2 -0
- package/dist/ImageViewer.mjs +4 -4
- package/dist/Input.js +2 -0
- package/dist/Input.mjs +2 -2
- package/dist/LabelValue.js +2 -0
- package/dist/LabelValue.mjs +2 -2
- package/dist/ListGroup.js +2 -0
- package/dist/ListGroup.mjs +2 -2
- package/dist/ListItem.d.mts +7 -7
- package/dist/ListItem.d.ts +7 -7
- package/dist/ListItem.js +14 -7
- package/dist/ListItem.mjs +2 -2
- package/dist/MediaCard.js +2 -0
- package/dist/MediaCard.mjs +2 -2
- package/dist/MenuGroup.js +2 -0
- package/dist/MenuGroup.mjs +2 -2
- package/dist/MenuItem.js +2 -0
- package/dist/MenuItem.mjs +2 -2
- package/dist/MonthPicker.js +2 -0
- package/dist/MonthPicker.mjs +2 -2
- package/dist/NumberStepper.js +2 -0
- package/dist/NumberStepper.mjs +2 -2
- package/dist/PagerDots.js +2 -0
- package/dist/PagerDots.mjs +2 -2
- package/dist/Pressable.d.mts +15 -7
- package/dist/Pressable.d.ts +15 -7
- package/dist/Pressable.js +7 -3
- package/dist/Pressable.mjs +1 -1
- package/dist/PricingCard.js +17 -17
- package/dist/PricingCard.mjs +4 -4
- package/dist/Progress.js +2 -0
- package/dist/Progress.mjs +2 -2
- package/dist/RadioGroup.js +2 -0
- package/dist/RadioGroup.mjs +2 -2
- package/dist/RetrayProvider.d.mts +1 -1
- package/dist/RetrayProvider.d.ts +1 -1
- package/dist/RetrayProvider.js +2 -0
- package/dist/RetrayProvider.mjs +3 -3
- package/dist/Select.js +2 -0
- package/dist/Select.mjs +2 -2
- package/dist/SelectableCard.d.mts +27 -0
- package/dist/SelectableCard.d.ts +27 -0
- package/dist/SelectableCard.js +511 -0
- package/dist/SelectableCard.mjs +8 -0
- package/dist/SelectableGrid.js +2 -0
- package/dist/SelectableGrid.mjs +2 -2
- package/dist/Separator.js +2 -0
- package/dist/Separator.mjs +2 -2
- package/dist/Sheet.d.mts +4 -46
- package/dist/Sheet.d.ts +4 -46
- package/dist/Sheet.js +55 -115
- package/dist/Sheet.mjs +2 -3
- package/dist/SheetSelect.js +2 -0
- package/dist/SheetSelect.mjs +2 -2
- package/dist/Skeleton.d.mts +3 -1
- package/dist/Skeleton.d.ts +3 -1
- package/dist/Skeleton.js +5 -2
- package/dist/Skeleton.mjs +2 -2
- package/dist/Slider.js +2 -0
- package/dist/Slider.mjs +2 -2
- package/dist/Spinner.js +2 -0
- package/dist/Spinner.mjs +2 -2
- package/dist/Stats.d.mts +33 -0
- package/dist/Stats.d.ts +33 -0
- package/dist/Stats.js +453 -0
- package/dist/Stats.mjs +9 -0
- package/dist/Switch.js +2 -0
- package/dist/Switch.mjs +2 -2
- package/dist/TabBar.js +2 -0
- package/dist/TabBar.mjs +2 -2
- package/dist/Tabs.js +2 -0
- package/dist/Tabs.mjs +2 -2
- package/dist/Text.d.mts +3 -1
- package/dist/Text.d.ts +3 -1
- package/dist/Text.js +5 -3
- package/dist/Text.mjs +2 -2
- package/dist/Textarea.js +2 -0
- package/dist/Textarea.mjs +2 -2
- package/dist/Toast.js +2 -0
- package/dist/Toast.mjs +2 -2
- package/dist/Toggle.js +2 -0
- package/dist/Toggle.mjs +2 -2
- package/dist/{chunk-U2XJFYED.mjs → chunk-2BA3JMKK.mjs} +1 -1
- package/dist/{chunk-NMU5FMQJ.mjs → chunk-2HFD4IHU.mjs} +4 -2
- package/dist/{chunk-S2R7UVOE.mjs → chunk-2LG326TT.mjs} +1 -1
- package/dist/chunk-2P2CB235.mjs +236 -0
- package/dist/{chunk-6L4G6PBT.mjs → chunk-3XCFYSX4.mjs} +1 -1
- package/dist/{chunk-HTHGSXFG.mjs → chunk-4J2PXL36.mjs} +16 -18
- package/dist/{chunk-BEMIQXXU.mjs → chunk-4OORJ2DY.mjs} +1 -1
- package/dist/chunk-4XOB5TTD.mjs +166 -0
- package/dist/{chunk-FCSSQK3L.mjs → chunk-57V2LXCK.mjs} +1 -1
- package/dist/{chunk-6Q64UFIA.mjs → chunk-7AFZWSCI.mjs} +1 -1
- package/dist/{chunk-IX3NYLYQ.mjs → chunk-7ELGZ66G.mjs} +1 -1
- package/dist/{chunk-GD6KXMG5.mjs → chunk-AENAVIKT.mjs} +1 -1
- package/dist/{chunk-ID72TK46.mjs → chunk-BXF4AMHY.mjs} +1 -1
- package/dist/{chunk-SOA2Z4RB.mjs → chunk-C43HRKXH.mjs} +1 -1
- package/dist/{chunk-TZDGAP5N.mjs → chunk-CF27NBXO.mjs} +11 -6
- package/dist/{chunk-SXLKNTA4.mjs → chunk-DF7JA72E.mjs} +1 -1
- package/dist/{chunk-AJRVDP2H.mjs → chunk-E5UKLSJZ.mjs} +3 -3
- package/dist/{chunk-MBMXYJJV.mjs → chunk-E7NEHHXV.mjs} +7 -3
- package/dist/{chunk-VKID2D2I.mjs → chunk-EDLCGYIO.mjs} +13 -8
- package/dist/{chunk-BUMAMSTZ.mjs → chunk-ELGEOM7I.mjs} +1 -1
- package/dist/{chunk-DYT7BG5I.mjs → chunk-F3YTWO3T.mjs} +1 -1
- package/dist/{chunk-VF2ATYN3.mjs → chunk-GH67YXG6.mjs} +1 -1
- package/dist/{chunk-WJLKJMKR.mjs → chunk-GUTDFUNF.mjs} +4 -4
- package/dist/{chunk-6SECQ2ZF.mjs → chunk-HC4VVCWY.mjs} +2 -2
- package/dist/{chunk-A3A6KNQN.mjs → chunk-HEDQPK4I.mjs} +1 -1
- package/dist/{chunk-GQYFLP3D.mjs → chunk-IVSRW4HS.mjs} +1 -1
- package/dist/{chunk-KOO4WITD.mjs → chunk-KSUWPU2F.mjs} +1 -1
- package/dist/{chunk-WBOOUHSS.mjs → chunk-LIS6I5UP.mjs} +1 -1
- package/dist/{chunk-X4G6APW6.mjs → chunk-LNPKGWBG.mjs} +1 -1
- package/dist/{chunk-T2KCAHOS.mjs → chunk-LOBLCFMN.mjs} +1 -1
- package/dist/{chunk-ELXBDILQ.mjs → chunk-LPV4NJJK.mjs} +2 -2
- package/dist/{chunk-Y2NS74WS.mjs → chunk-M3C7XM2M.mjs} +53 -99
- package/dist/{chunk-BRKYVJVV.mjs → chunk-MEPSKGBO.mjs} +1 -1
- package/dist/{chunk-TBNZHU6C.mjs → chunk-MVMGPZN6.mjs} +2 -2
- package/dist/{chunk-YJ7I257J.mjs → chunk-NHDI3VQB.mjs} +15 -1
- package/dist/{chunk-Z6SFHN6T.mjs → chunk-NJG7DHVF.mjs} +1 -1
- package/dist/{chunk-RYZC432S.mjs → chunk-NLZY4TXU.mjs} +1 -1
- package/dist/{chunk-ZZ2R6KZ3.mjs → chunk-OLVJFKXS.mjs} +1 -1
- package/dist/{chunk-AJ7ZDNBT.mjs → chunk-QDAZGZUF.mjs} +4 -3
- package/dist/{chunk-JT7HKXRB.mjs → chunk-QOLWA2PW.mjs} +1 -1
- package/dist/{chunk-WYEUNUTP.mjs → chunk-QXDGGOLC.mjs} +38 -25
- package/dist/{chunk-JMOZEC77.mjs → chunk-RJNLAH76.mjs} +1 -1
- package/dist/{chunk-WF2XDFRK.mjs → chunk-RMRS44MQ.mjs} +1 -1
- package/dist/chunk-SAWUXP3A.mjs +1114 -0
- package/dist/{chunk-OB4JUQ3O.mjs → chunk-TS7DGUIR.mjs} +1 -1
- package/dist/{chunk-AV4EMIRH.mjs → chunk-UBUXUMER.mjs} +1 -1
- package/dist/{chunk-IRRY3CRZ.mjs → chunk-ULGNQPNE.mjs} +1 -1
- package/dist/{chunk-7LWRKMF5.mjs → chunk-UNNRUJTM.mjs} +1 -1
- package/dist/{chunk-TB6SD2FT.mjs → chunk-UQ4742ET.mjs} +1 -1
- package/dist/{chunk-MX6HRKMI.mjs → chunk-VJBUCITV.mjs} +1 -1
- package/dist/{chunk-2UYENBLV.mjs → chunk-YMYIEVZP.mjs} +1 -1
- package/dist/{chunk-SOYNZDVY.mjs → chunk-YTXRIXNZ.mjs} +8 -1
- package/dist/{chunk-YFZ3ELX5.mjs → chunk-ZIMY2QUM.mjs} +2 -2
- package/dist/{chunk-Z4VHZ7B5.mjs → chunk-ZR6HSEAB.mjs} +1 -1
- package/dist/fonts.d.mts +1 -7
- package/dist/fonts.d.ts +1 -7
- package/dist/fonts.js +0 -2
- package/dist/fonts.mjs +1 -2
- package/dist/{index-wt-orHUi.d.ts → index-CY34hxPN.d.mts} +1 -0
- package/dist/{index-wt-orHUi.d.mts → index-CY34hxPN.d.ts} +1 -0
- package/dist/index.d.mts +7 -3
- package/dist/index.d.ts +7 -3
- package/dist/index.js +1517 -761
- package/dist/index.mjs +54 -52
- package/package.json +3 -3
- package/src/components/Accordion/Accordion.tsx +20 -0
- package/src/components/Button/Button.tsx +29 -26
- package/src/components/ConfirmDialog/ConfirmDialog.tsx +47 -31
- package/src/components/CurrencyInput/CurrencyInput.tsx +4 -7
- package/src/components/IconPicker/IconPicker.tsx +124 -112
- package/src/components/ImageUpload/ImageUpload.tsx +10 -3
- package/src/components/ListItem/ListItem.tsx +43 -28
- package/src/components/Pressable/Pressable.tsx +20 -8
- package/src/components/SelectableCard/SelectableCard.tsx +304 -0
- package/src/components/SelectableCard/index.ts +1 -0
- package/src/components/Sheet/Sheet.tsx +72 -173
- package/src/components/Skeleton/Skeleton.tsx +5 -2
- package/src/components/Stats/Stats.tsx +254 -0
- package/src/components/Stats/index.ts +2 -0
- package/src/components/Text/Text.tsx +4 -2
- package/src/fonts.ts +0 -7
- package/src/index.ts +5 -0
- package/src/theme/colorUtils.ts +9 -0
- package/src/theme/colors.ts +7 -0
- package/src/theme/types.ts +4 -1
- package/src/utils/curatedIcons.ts +698 -135
- package/src/utils/fontGuard.ts +2 -1
- package/dist/chunk-53Z3NYGE.mjs +0 -742
|
@@ -24,6 +24,8 @@ export interface SkeletonProps {
|
|
|
24
24
|
preset?: SkeletonPreset
|
|
25
25
|
/** Only used with `preset='circle'` — overrides the diameter. Defaults to 40. */
|
|
26
26
|
diameter?: number
|
|
27
|
+
/** Override the skeleton background color. Defaults to `colors.skeleton`. */
|
|
28
|
+
backgroundColor?: string
|
|
27
29
|
style?: ViewStyle
|
|
28
30
|
}
|
|
29
31
|
|
|
@@ -33,6 +35,7 @@ export function Skeleton({
|
|
|
33
35
|
borderRadius = 6,
|
|
34
36
|
preset = 'base',
|
|
35
37
|
diameter = 40,
|
|
38
|
+
backgroundColor,
|
|
36
39
|
style,
|
|
37
40
|
}: SkeletonProps) {
|
|
38
41
|
const { colors, colorScheme } = useTheme()
|
|
@@ -40,7 +43,7 @@ export function Skeleton({
|
|
|
40
43
|
const [containerWidth, setContainerWidth] = useState(300)
|
|
41
44
|
|
|
42
45
|
const shimmerHighlight =
|
|
43
|
-
colorScheme === 'dark' ? 'rgba(255,255,255,0.08)' : 'rgba(
|
|
46
|
+
colorScheme === 'dark' ? 'rgba(255,255,255,0.08)' : 'rgba(0,0,0,0.07)'
|
|
44
47
|
|
|
45
48
|
useEffect(() => {
|
|
46
49
|
// Repeats indefinitely on the UI thread — zero JS bridge cost per frame.
|
|
@@ -75,7 +78,7 @@ export function Skeleton({
|
|
|
75
78
|
<View
|
|
76
79
|
style={[
|
|
77
80
|
styles.base,
|
|
78
|
-
{ width: resolvedWidth as number | `${number}%`, height: resolvedHeight, borderRadius: resolvedRadius, backgroundColor: colors.
|
|
81
|
+
{ width: resolvedWidth as number | `${number}%`, height: resolvedHeight, borderRadius: resolvedRadius, backgroundColor: backgroundColor ?? colors.skeleton },
|
|
79
82
|
style,
|
|
80
83
|
]}
|
|
81
84
|
onLayout={(e) => setContainerWidth(e.nativeEvent.layout.width)}
|
|
@@ -0,0 +1,254 @@
|
|
|
1
|
+
import React, { useState, useCallback } from 'react'
|
|
2
|
+
import { View, Text, StyleSheet, ViewStyle, LayoutChangeEvent } from 'react-native'
|
|
3
|
+
import { impactLight } from '../../utils/haptics'
|
|
4
|
+
import { useTheme } from '../../theme'
|
|
5
|
+
import { s, vs, ms, mvs } from '../../utils/scaling'
|
|
6
|
+
import { RADIUS } from '../../tokens'
|
|
7
|
+
import { renderIcon } from '../../utils/icons'
|
|
8
|
+
import { PressableCard } from '../../utils/pressable'
|
|
9
|
+
|
|
10
|
+
export type StatsVariant = 'elevated' | 'outlined' | 'filled'
|
|
11
|
+
export type StatsSize = 'default' | 'compact'
|
|
12
|
+
|
|
13
|
+
export interface StatsProps {
|
|
14
|
+
value: string
|
|
15
|
+
label: string
|
|
16
|
+
description?: string
|
|
17
|
+
icon?: React.ReactNode
|
|
18
|
+
iconName?: string
|
|
19
|
+
iconColor?: string
|
|
20
|
+
variant?: StatsVariant
|
|
21
|
+
/** `'compact'` reduces everything proportionally for tight grids. */
|
|
22
|
+
size?: StatsSize
|
|
23
|
+
onPress?: () => void
|
|
24
|
+
style?: ViewStyle
|
|
25
|
+
accessibilityLabel?: string
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export interface StatsGroupProps {
|
|
29
|
+
children: React.ReactNode
|
|
30
|
+
gap?: number
|
|
31
|
+
style?: ViewStyle
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const COMPACT_THRESHOLD = s(150)
|
|
35
|
+
|
|
36
|
+
function StatsComponent({
|
|
37
|
+
value,
|
|
38
|
+
label,
|
|
39
|
+
description,
|
|
40
|
+
icon,
|
|
41
|
+
iconName,
|
|
42
|
+
iconColor,
|
|
43
|
+
variant = 'elevated',
|
|
44
|
+
size = 'default',
|
|
45
|
+
onPress,
|
|
46
|
+
style,
|
|
47
|
+
accessibilityLabel,
|
|
48
|
+
}: StatsProps) {
|
|
49
|
+
const { colors } = useTheme()
|
|
50
|
+
const [containerWidth, setContainerWidth] = useState(0)
|
|
51
|
+
|
|
52
|
+
const handleLayout = useCallback((e: LayoutChangeEvent) => {
|
|
53
|
+
const w = e.nativeEvent.layout.width
|
|
54
|
+
if (w > 0 && w !== containerWidth) {
|
|
55
|
+
setContainerWidth(w)
|
|
56
|
+
}
|
|
57
|
+
}, [containerWidth])
|
|
58
|
+
|
|
59
|
+
const handlePress = () => {
|
|
60
|
+
if (!onPress) return
|
|
61
|
+
impactLight()
|
|
62
|
+
onPress()
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const isCompact = containerWidth > 0 && containerWidth < COMPACT_THRESHOLD && !!(icon ?? iconName)
|
|
66
|
+
|
|
67
|
+
const sizeStyles = size === 'compact'
|
|
68
|
+
? {
|
|
69
|
+
valueFontFamily: 'Sohne-SemiBold' as const,
|
|
70
|
+
valueFontSize: ms(16),
|
|
71
|
+
valueLineHeight: mvs(20),
|
|
72
|
+
labelFontSize: ms(11),
|
|
73
|
+
labelLineHeight: mvs(14),
|
|
74
|
+
descriptionFontSize: ms(10),
|
|
75
|
+
descriptionLineHeight: mvs(14),
|
|
76
|
+
iconSize: ms(18),
|
|
77
|
+
padding: s(12),
|
|
78
|
+
}
|
|
79
|
+
: {
|
|
80
|
+
valueFontFamily: 'Sohne-Bold' as const,
|
|
81
|
+
valueFontSize: ms(21),
|
|
82
|
+
valueLineHeight: mvs(25),
|
|
83
|
+
labelFontSize: ms(13),
|
|
84
|
+
labelLineHeight: mvs(18),
|
|
85
|
+
descriptionFontSize: ms(12),
|
|
86
|
+
descriptionLineHeight: mvs(16),
|
|
87
|
+
iconSize: ms(20),
|
|
88
|
+
padding: s(16),
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
const variantStyle: ViewStyle = {
|
|
92
|
+
elevated: {
|
|
93
|
+
backgroundColor: colors.card,
|
|
94
|
+
borderWidth: 0,
|
|
95
|
+
shadowColor: '#000',
|
|
96
|
+
shadowOffset: { width: 0, height: 4 },
|
|
97
|
+
shadowOpacity: 0.09,
|
|
98
|
+
shadowRadius: 14,
|
|
99
|
+
elevation: 4,
|
|
100
|
+
},
|
|
101
|
+
outlined: {
|
|
102
|
+
backgroundColor: colors.card,
|
|
103
|
+
borderColor: colors.border,
|
|
104
|
+
shadowOpacity: 0,
|
|
105
|
+
elevation: 0,
|
|
106
|
+
},
|
|
107
|
+
filled: {
|
|
108
|
+
backgroundColor: colors.surfaceStrong,
|
|
109
|
+
borderColor: colors.border,
|
|
110
|
+
shadowOpacity: 0,
|
|
111
|
+
elevation: 0,
|
|
112
|
+
},
|
|
113
|
+
}[variant]
|
|
114
|
+
|
|
115
|
+
const iconColorResolved = iconColor ?? colors.primary
|
|
116
|
+
|
|
117
|
+
const resolvedIcon = iconName ? renderIcon(iconName, sizeStyles.iconSize, iconColorResolved) : icon
|
|
118
|
+
|
|
119
|
+
const iconElement = resolvedIcon ? (
|
|
120
|
+
<View style={styles.iconWrapper}>{resolvedIcon}</View>
|
|
121
|
+
) : null
|
|
122
|
+
|
|
123
|
+
const valueElement = (
|
|
124
|
+
<Text style={[styles.value, { color: colors.foreground, fontFamily: sizeStyles.valueFontFamily, fontSize: sizeStyles.valueFontSize, lineHeight: sizeStyles.valueLineHeight }]} allowFontScaling={true}>
|
|
125
|
+
{value}
|
|
126
|
+
</Text>
|
|
127
|
+
)
|
|
128
|
+
|
|
129
|
+
const cardContent = (
|
|
130
|
+
<View style={[styles.card, variantStyle, { padding: sizeStyles.padding }, style]} onLayout={handleLayout}>
|
|
131
|
+
{isCompact ? (
|
|
132
|
+
<>
|
|
133
|
+
{iconElement}
|
|
134
|
+
<View style={styles.compactValue}>{valueElement}</View>
|
|
135
|
+
<Text style={[styles.label, { color: colors.foregroundSubtle, fontSize: sizeStyles.labelFontSize, lineHeight: sizeStyles.labelLineHeight }]} allowFontScaling={true}>
|
|
136
|
+
{label}
|
|
137
|
+
</Text>
|
|
138
|
+
{description ? (
|
|
139
|
+
<Text style={[styles.description, { color: colors.foregroundMuted, fontSize: sizeStyles.descriptionFontSize, lineHeight: sizeStyles.descriptionLineHeight }]} allowFontScaling={true}>
|
|
140
|
+
{description}
|
|
141
|
+
</Text>
|
|
142
|
+
) : null}
|
|
143
|
+
</>
|
|
144
|
+
) : (
|
|
145
|
+
<>
|
|
146
|
+
<View style={styles.valueRow}>
|
|
147
|
+
{iconElement}
|
|
148
|
+
{valueElement}
|
|
149
|
+
</View>
|
|
150
|
+
<Text style={[styles.label, { color: colors.foregroundSubtle, fontSize: sizeStyles.labelFontSize, lineHeight: sizeStyles.labelLineHeight }]} allowFontScaling={true}>
|
|
151
|
+
{label}
|
|
152
|
+
</Text>
|
|
153
|
+
{description ? (
|
|
154
|
+
<Text style={[styles.description, { color: colors.foregroundMuted, fontSize: sizeStyles.descriptionFontSize, lineHeight: sizeStyles.descriptionLineHeight }]} allowFontScaling={true}>
|
|
155
|
+
{description}
|
|
156
|
+
</Text>
|
|
157
|
+
) : null}
|
|
158
|
+
</>
|
|
159
|
+
)}
|
|
160
|
+
</View>
|
|
161
|
+
)
|
|
162
|
+
|
|
163
|
+
if (onPress) {
|
|
164
|
+
return (
|
|
165
|
+
<PressableCard
|
|
166
|
+
onPress={handlePress}
|
|
167
|
+
rippleColor="transparent"
|
|
168
|
+
touchSoundDisabled
|
|
169
|
+
activateOnHover
|
|
170
|
+
accessibilityRole="button"
|
|
171
|
+
accessibilityLabel={accessibilityLabel}
|
|
172
|
+
>
|
|
173
|
+
{cardContent}
|
|
174
|
+
</PressableCard>
|
|
175
|
+
)
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
return cardContent
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
function StatsGroup({ children, gap = s(12), style }: StatsGroupProps) {
|
|
182
|
+
return (
|
|
183
|
+
<View style={[styles.group, { gap }, style]}>
|
|
184
|
+
{React.Children.map(children, (child) => {
|
|
185
|
+
if (!React.isValidElement(child)) return child
|
|
186
|
+
const childStyle = (child.props as { style?: ViewStyle }).style
|
|
187
|
+
const mergedStyle = childStyle
|
|
188
|
+
? [childStyle, { alignSelf: 'stretch' as const }]
|
|
189
|
+
: [{ alignSelf: 'stretch' as const }]
|
|
190
|
+
return (
|
|
191
|
+
<View style={styles.groupItem}>
|
|
192
|
+
{React.cloneElement(
|
|
193
|
+
child as React.ReactElement<{ style?: ViewStyle }>,
|
|
194
|
+
{ style: mergedStyle as unknown as ViewStyle },
|
|
195
|
+
)}
|
|
196
|
+
</View>
|
|
197
|
+
)
|
|
198
|
+
})}
|
|
199
|
+
</View>
|
|
200
|
+
)
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
export const Stats = Object.assign(React.memo(StatsComponent), { Group: StatsGroup })
|
|
204
|
+
|
|
205
|
+
const styles = StyleSheet.create({
|
|
206
|
+
card: {
|
|
207
|
+
borderRadius: RADIUS.md,
|
|
208
|
+
borderWidth: 1,
|
|
209
|
+
padding: s(16),
|
|
210
|
+
alignSelf: 'flex-start',
|
|
211
|
+
alignItems: 'center',
|
|
212
|
+
justifyContent: 'center',
|
|
213
|
+
},
|
|
214
|
+
valueRow: {
|
|
215
|
+
flexDirection: 'row',
|
|
216
|
+
alignItems: 'center',
|
|
217
|
+
justifyContent: 'center',
|
|
218
|
+
gap: s(8),
|
|
219
|
+
},
|
|
220
|
+
iconWrapper: {
|
|
221
|
+
alignItems: 'center',
|
|
222
|
+
justifyContent: 'center',
|
|
223
|
+
},
|
|
224
|
+
compactValue: {
|
|
225
|
+
marginTop: vs(8),
|
|
226
|
+
},
|
|
227
|
+
value: {
|
|
228
|
+
fontFamily: 'Sohne-Bold',
|
|
229
|
+
fontSize: ms(28),
|
|
230
|
+
lineHeight: mvs(32),
|
|
231
|
+
textAlign: 'center',
|
|
232
|
+
},
|
|
233
|
+
label: {
|
|
234
|
+
fontFamily: 'Sohne-Regular',
|
|
235
|
+
fontSize: ms(13),
|
|
236
|
+
lineHeight: mvs(18),
|
|
237
|
+
marginTop: vs(8),
|
|
238
|
+
textAlign: 'center',
|
|
239
|
+
},
|
|
240
|
+
description: {
|
|
241
|
+
fontFamily: 'Sohne-Regular',
|
|
242
|
+
fontSize: ms(12),
|
|
243
|
+
lineHeight: mvs(16),
|
|
244
|
+
marginTop: vs(4),
|
|
245
|
+
textAlign: 'center',
|
|
246
|
+
},
|
|
247
|
+
group: {
|
|
248
|
+
flexDirection: 'row',
|
|
249
|
+
width: '100%',
|
|
250
|
+
},
|
|
251
|
+
groupItem: {
|
|
252
|
+
flex: 1,
|
|
253
|
+
},
|
|
254
|
+
})
|
|
@@ -26,6 +26,8 @@ export type TextVariant =
|
|
|
26
26
|
export interface TextProps extends RNTextProps {
|
|
27
27
|
variant?: TextVariant
|
|
28
28
|
color?: string
|
|
29
|
+
/** Force uppercase text transformation. Useful for labels, headers, and buttons. */
|
|
30
|
+
uppercase?: boolean
|
|
29
31
|
}
|
|
30
32
|
|
|
31
33
|
// Apply scaling to font/line-height values while preserving all other token props
|
|
@@ -68,7 +70,7 @@ const defaultColorVariant: Partial<Record<TextVariant, 'foreground' | 'foregroun
|
|
|
68
70
|
'button-sm': 'foreground',
|
|
69
71
|
}
|
|
70
72
|
|
|
71
|
-
function TextBase({ variant = 'body-md', color, style, children, ...props }: TextProps) {
|
|
73
|
+
function TextBase({ variant = 'body-md', color, style, uppercase, children, ...props }: TextProps) {
|
|
72
74
|
warnIfFontsMissing()
|
|
73
75
|
const { colors } = useTheme()
|
|
74
76
|
|
|
@@ -77,7 +79,7 @@ function TextBase({ variant = 'body-md', color, style, children, ...props }: Tex
|
|
|
77
79
|
|
|
78
80
|
return (
|
|
79
81
|
<RNText
|
|
80
|
-
style={[variantStyles[variant], { color: resolvedColor }, style]}
|
|
82
|
+
style={[variantStyles[variant], { color: resolvedColor }, uppercase && { textTransform: 'uppercase' }, style]}
|
|
81
83
|
allowFontScaling={true}
|
|
82
84
|
{...props}
|
|
83
85
|
>
|
package/src/fonts.ts
CHANGED
|
@@ -63,10 +63,3 @@ export const SohneFontNames = [
|
|
|
63
63
|
|
|
64
64
|
/** Type for any valid Sohne font family name */
|
|
65
65
|
export type SohneFontName = (typeof SohneFontNames)[number]
|
|
66
|
-
|
|
67
|
-
/**
|
|
68
|
-
* @deprecated SohneFonts export removed in v10.0.0.
|
|
69
|
-
* Metro cannot resolve require() calls from node_modules reliably.
|
|
70
|
-
* Copy the static SohneFonts boilerplate from CONSUMER.md into your App.tsx instead.
|
|
71
|
-
*/
|
|
72
|
-
export const SohneFonts = undefined
|
package/src/index.ts
CHANGED
|
@@ -50,6 +50,7 @@ export * from './components/ErrorBoundary'
|
|
|
50
50
|
export * from './components/PagerDots'
|
|
51
51
|
export * from './components/AppHeader'
|
|
52
52
|
export * from './components/SelectableGrid'
|
|
53
|
+
export * from './components/SelectableCard'
|
|
53
54
|
export * from './components/PricingCard'
|
|
54
55
|
export * from './components/TabBar'
|
|
55
56
|
export * from './components/ImageViewer'
|
|
@@ -57,6 +58,7 @@ export * from './components/SheetSelect'
|
|
|
57
58
|
export * from './components/ImageUpload'
|
|
58
59
|
export * from './components/IconPicker'
|
|
59
60
|
export * from './components/NumberStepper'
|
|
61
|
+
export * from './components/Stats'
|
|
60
62
|
// HolographicCard is intentionally NOT re-exported here — it depends on the
|
|
61
63
|
// optional peer @shopify/react-native-skia, so it must stay out of the main
|
|
62
64
|
// barrel's module graph. Deep-import it: '@retray-dev/ui-kit/HolographicCard'.
|
|
@@ -64,6 +66,9 @@ export * from './components/NumberStepper'
|
|
|
64
66
|
// Icon utility
|
|
65
67
|
export { Icon, renderIcon, configureIconFamilies, getValidIconNames } from './utils/icons'
|
|
66
68
|
|
|
69
|
+
// Color utilities
|
|
70
|
+
export { withAlpha } from './theme/colorUtils'
|
|
71
|
+
|
|
67
72
|
// Typography utilities
|
|
68
73
|
export { getResponsiveFontSize } from './utils/typography'
|
|
69
74
|
export type { IconProps, IconFamily } from './utils/icons'
|
package/src/theme/colorUtils.ts
CHANGED
|
@@ -78,3 +78,12 @@ export function isDark(hex: string): boolean {
|
|
|
78
78
|
const L = 0.2126 * toLinear(rgb.r) + 0.7152 * toLinear(rgb.g) + 0.0722 * toLinear(rgb.b)
|
|
79
79
|
return L < 0.5
|
|
80
80
|
}
|
|
81
|
+
|
|
82
|
+
// Convert a hex color to rgba with the given alpha.
|
|
83
|
+
// Returns an rgba() string suitable for use with semi-transparent backgrounds,
|
|
84
|
+
// borders, and overlays.
|
|
85
|
+
export function withAlpha(hex: string, alpha: number): string {
|
|
86
|
+
const rgb = hexToRgb(hex)
|
|
87
|
+
if (!rgb) return hex
|
|
88
|
+
return `rgba(${rgb.r}, ${rgb.g}, ${rgb.b}, ${alpha})`
|
|
89
|
+
}
|
package/src/theme/colors.ts
CHANGED
|
@@ -69,6 +69,12 @@ export function deriveColors(t: ThemeColors, scheme: 'light' | 'dark'): Resolved
|
|
|
69
69
|
? lighten(bg, -0.12)
|
|
70
70
|
: darken(bg, 0.08)
|
|
71
71
|
|
|
72
|
+
// Skeleton needs higher contrast than surface to be visible.
|
|
73
|
+
// Light: 10% darken (was 4% via surface — invisible). Dark: 10% lighten.
|
|
74
|
+
const skeleton = dark
|
|
75
|
+
? lighten(bg, -0.10)
|
|
76
|
+
: darken(bg, 0.10)
|
|
77
|
+
|
|
72
78
|
const destructiveTint = dark
|
|
73
79
|
? withAlphaOnDark(t.destructive, 0.15, bg)
|
|
74
80
|
: withAlphaOnWhite(t.destructive, 0.08)
|
|
@@ -96,6 +102,7 @@ export function deriveColors(t: ThemeColors, scheme: 'light' | 'dark'): Resolved
|
|
|
96
102
|
foregroundMuted,
|
|
97
103
|
surface,
|
|
98
104
|
surfaceStrong,
|
|
105
|
+
skeleton,
|
|
99
106
|
destructiveTint,
|
|
100
107
|
destructiveBorder,
|
|
101
108
|
successTint,
|
package/src/theme/types.ts
CHANGED
|
@@ -29,10 +29,13 @@ export type ResolvedColors = ThemeColors & {
|
|
|
29
29
|
foregroundSubtle: string // ~55% — body text, subtitles
|
|
30
30
|
foregroundMuted: string // ~35% — captions, timestamps, placeholders
|
|
31
31
|
|
|
32
|
-
// Surface fills (chips unselected, input bg, tag bg
|
|
32
|
+
// Surface fills (chips unselected, input bg, tag bg)
|
|
33
33
|
surface: string // background slightly off-canvas
|
|
34
34
|
surfaceStrong: string // slightly stronger fill for pressed/hover states
|
|
35
35
|
|
|
36
|
+
// Skeleton placeholder — higher contrast than surface for visibility
|
|
37
|
+
skeleton: string
|
|
38
|
+
|
|
36
39
|
// Semantic tints (light bg for alert banners, toast backgrounds)
|
|
37
40
|
destructiveTint: string
|
|
38
41
|
destructiveBorder: string
|