react-native-varia 0.2.2 → 0.2.4
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/bin/cli.js +316 -139
- package/lib/components/Accordion.tsx +113 -0
- package/lib/components/Button.tsx +19 -8
- package/lib/components/CircleProgress.tsx +46 -28
- package/lib/components/Divider.tsx +18 -15
- package/lib/components/Drawer.tsx +496 -0
- package/lib/components/Field.tsx +24 -39
- package/lib/components/GradientBackground.tsx +25 -7
- package/lib/components/GradientText.tsx +38 -11
- package/lib/components/IconWrapper.tsx +20 -14
- package/lib/components/Input.tsx +106 -25
- package/lib/components/NumberInput.tsx +88 -19
- package/lib/components/OldSlider.tsx +327 -0
- package/lib/components/RadioGroup.tsx +55 -17
- package/lib/components/ReText.tsx +1 -1
- package/lib/components/Select.tsx +58 -22
- package/lib/components/Slider.tsx +176 -115
- package/lib/components/Slideshow.tsx +68 -69
- package/lib/components/SlidingDrawer.tsx +72 -29
- package/lib/components/Spinner.tsx +6 -2
- package/lib/components/Toast.tsx +89 -0
- package/lib/components/context/Field.tsx +27 -0
- package/lib/icons/Minus.tsx +24 -0
- package/lib/icons/Plus.tsx +23 -0
- package/lib/theme/Button.recipe.tsx +11 -1
- package/lib/theme/CircleProgress.recipe.tsx +3 -3
- package/lib/theme/Field.recipe.tsx +17 -2
- package/lib/theme/Input.recipe.tsx +12 -3
- package/lib/theme/NumberInput.recipe.tsx +9 -4
- package/lib/theme/RadioGroup.recipe.tsx +7 -1
- package/lib/theme/Select.recipe.tsx +7 -7
- package/lib/theme/Slider.recipe.tsx +366 -22
- package/lib/theme/Slideshow.recipe.tsx +1 -1
- package/lib/theme/SlidingDrawer.recipe.tsx +58 -4
- package/lib/theme/Toast.recipe.tsx +71 -0
- package/package.json +3 -2
- package/lib/components/NewSelect.tsx +0 -202
- package/lib/components/index.tsx +0 -83
- package/lib/components/layoutTest.tsx +0 -74
- package/lib/theme/Button.recipe-old.tsx +0 -67
|
@@ -0,0 +1,327 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
import {type LayoutChangeEvent, View} from 'react-native'
|
|
3
|
+
import {StyleSheet, UnistylesVariants} from 'react-native-unistyles'
|
|
4
|
+
import {Gesture, GestureDetector} from 'react-native-gesture-handler'
|
|
5
|
+
import Animated, {
|
|
6
|
+
useAnimatedStyle,
|
|
7
|
+
useSharedValue,
|
|
8
|
+
} from 'react-native-reanimated'
|
|
9
|
+
import {runOnJS} from 'react-native-worklets'
|
|
10
|
+
import {SliderStyles, SliderDefaultVariants} from '../theme/OldSlider.recipe'
|
|
11
|
+
import {PalettesWithNestedKeys} from '../style/varia/types'
|
|
12
|
+
|
|
13
|
+
type SliderVariants = UnistylesVariants<typeof SliderStyles>
|
|
14
|
+
|
|
15
|
+
type SliderProps = SliderVariants & {
|
|
16
|
+
colorPalette?: PalettesWithNestedKeys
|
|
17
|
+
thickness?: number
|
|
18
|
+
minimumTrackThickness?: number
|
|
19
|
+
minimumTrackColor?: string
|
|
20
|
+
displayStepsOnMinimumTrack?: boolean
|
|
21
|
+
thumbSize?: {
|
|
22
|
+
width: number
|
|
23
|
+
height: number
|
|
24
|
+
}
|
|
25
|
+
thumbChildren?: React.ReactNode
|
|
26
|
+
axis: 'x' | 'y'
|
|
27
|
+
steps?: number
|
|
28
|
+
value?: number
|
|
29
|
+
onValueChange?: (value: number) => void
|
|
30
|
+
onSlidingComplete?: (value: number) => void
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const OldSlider = ({
|
|
34
|
+
colorPalette = 'accent',
|
|
35
|
+
variant = SliderDefaultVariants.variant,
|
|
36
|
+
size = SliderDefaultVariants.size,
|
|
37
|
+
displayStepsOnMinimumTrack = false,
|
|
38
|
+
thumbChildren: ThumbChildren,
|
|
39
|
+
axis = 'x',
|
|
40
|
+
value = 0,
|
|
41
|
+
steps,
|
|
42
|
+
onValueChange,
|
|
43
|
+
onSlidingComplete,
|
|
44
|
+
}: SliderProps) => {
|
|
45
|
+
let styles
|
|
46
|
+
if (axis === 'x') {
|
|
47
|
+
styles = stylesX
|
|
48
|
+
} else {
|
|
49
|
+
styles = stylesY
|
|
50
|
+
}
|
|
51
|
+
SliderStyles.useVariants({
|
|
52
|
+
size,
|
|
53
|
+
variant,
|
|
54
|
+
})
|
|
55
|
+
const thumbStyle = SliderStyles.thumb(colorPalette)
|
|
56
|
+
const maximumTrackStyle = SliderStyles.maximumTrack(colorPalette)
|
|
57
|
+
const minimumTrackStyle = SliderStyles.minimumTrack(colorPalette, false)
|
|
58
|
+
|
|
59
|
+
const halfSize = (thumbStyle.width ?? 0) / 2
|
|
60
|
+
const thumbSize = {
|
|
61
|
+
width: thumbStyle.width,
|
|
62
|
+
height: thumbStyle.height,
|
|
63
|
+
}
|
|
64
|
+
const maximumTrackWidth = maximumTrackStyle.height ?? 30
|
|
65
|
+
const minimumTrackWidth = minimumTrackStyle.height ?? 30
|
|
66
|
+
const context = useSharedValue({x: 0})
|
|
67
|
+
const translate = useSharedValue(value)
|
|
68
|
+
const trackLength = useSharedValue(0)
|
|
69
|
+
const isInsideChild = useSharedValue(false)
|
|
70
|
+
|
|
71
|
+
const handleTrackLayout = (event: LayoutChangeEvent) => {
|
|
72
|
+
const {width, height} = event.nativeEvent.layout
|
|
73
|
+
trackLength.value = axis === 'x' ? width : height
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
const slideGesture = Gesture.Pan()
|
|
77
|
+
.onTouchesDown(() => {
|
|
78
|
+
isInsideChild.value = true
|
|
79
|
+
})
|
|
80
|
+
.onBegin(() => {
|
|
81
|
+
context.value.x = translate.value
|
|
82
|
+
})
|
|
83
|
+
.onUpdate(e => {
|
|
84
|
+
const stepLength = steps ? trackLength.value / steps : 1
|
|
85
|
+
const delta = axis === 'y' ? -e.translationY : e.translationX
|
|
86
|
+
|
|
87
|
+
const rawValue = Math.max(
|
|
88
|
+
0,
|
|
89
|
+
Math.min(context.value.x + delta, trackLength.value),
|
|
90
|
+
)
|
|
91
|
+
|
|
92
|
+
const snappedValue = steps
|
|
93
|
+
? Math.round(rawValue / stepLength) * stepLength
|
|
94
|
+
: rawValue
|
|
95
|
+
|
|
96
|
+
translate.value = snappedValue
|
|
97
|
+
|
|
98
|
+
if (steps) {
|
|
99
|
+
const stepIndex = Math.round(snappedValue / stepLength)
|
|
100
|
+
onValueChange && runOnJS(onValueChange)(stepIndex)
|
|
101
|
+
} else {
|
|
102
|
+
onValueChange &&
|
|
103
|
+
runOnJS(onValueChange)(
|
|
104
|
+
Math.round((snappedValue / trackLength.value) * 100) / 100,
|
|
105
|
+
)
|
|
106
|
+
}
|
|
107
|
+
})
|
|
108
|
+
.onEnd(() => {
|
|
109
|
+
const stepLength = steps ? trackLength.value / steps : 1
|
|
110
|
+
|
|
111
|
+
const snappedValue = steps
|
|
112
|
+
? Math.round(translate.value / stepLength)
|
|
113
|
+
: translate.value / trackLength.value
|
|
114
|
+
|
|
115
|
+
onSlidingComplete && runOnJS(onSlidingComplete)(snappedValue)
|
|
116
|
+
})
|
|
117
|
+
.onFinalize(() => {
|
|
118
|
+
isInsideChild.value = false
|
|
119
|
+
})
|
|
120
|
+
const tapGesture = Gesture.Tap()
|
|
121
|
+
.onBegin(e => {
|
|
122
|
+
if (isInsideChild.value) {
|
|
123
|
+
return
|
|
124
|
+
}
|
|
125
|
+
const tapPosition = axis === 'x' ? e.x : e.y
|
|
126
|
+
const stepLength = steps ? trackLength.value / steps : 1
|
|
127
|
+
|
|
128
|
+
const rawValue =
|
|
129
|
+
axis === 'y'
|
|
130
|
+
? Math.max(
|
|
131
|
+
0,
|
|
132
|
+
Math.min(trackLength.value - tapPosition, trackLength.value),
|
|
133
|
+
)
|
|
134
|
+
: Math.max(0, Math.min(tapPosition, trackLength.value))
|
|
135
|
+
|
|
136
|
+
const snappedValue = steps
|
|
137
|
+
? Math.round(rawValue / stepLength) * stepLength
|
|
138
|
+
: rawValue
|
|
139
|
+
|
|
140
|
+
translate.value = snappedValue
|
|
141
|
+
|
|
142
|
+
if (steps) {
|
|
143
|
+
const stepIndex = Math.round(snappedValue / stepLength)
|
|
144
|
+
onValueChange && runOnJS(onValueChange)(stepIndex)
|
|
145
|
+
onSlidingComplete && runOnJS(onSlidingComplete)(stepIndex)
|
|
146
|
+
} else {
|
|
147
|
+
const normalizedValue =
|
|
148
|
+
Math.round((snappedValue / trackLength.value) * 100) / 100
|
|
149
|
+
onValueChange && runOnJS(onValueChange)(normalizedValue)
|
|
150
|
+
onSlidingComplete && runOnJS(onSlidingComplete)(normalizedValue)
|
|
151
|
+
}
|
|
152
|
+
})
|
|
153
|
+
.simultaneousWithExternalGesture(slideGesture)
|
|
154
|
+
|
|
155
|
+
const animatedTrack = useAnimatedStyle(() => ({
|
|
156
|
+
[axis === 'y' ? 'height' : 'width']: translate.value + halfSize,
|
|
157
|
+
}))
|
|
158
|
+
|
|
159
|
+
const opacityTrack = steps !== undefined && displayStepsOnMinimumTrack
|
|
160
|
+
|
|
161
|
+
return (
|
|
162
|
+
<View
|
|
163
|
+
style={[
|
|
164
|
+
styles.container(maximumTrackWidth, halfSize),
|
|
165
|
+
SliderStyles.container(colorPalette),
|
|
166
|
+
]}>
|
|
167
|
+
{axis === 'x' && <View style={{width: halfSize}} />}
|
|
168
|
+
<GestureDetector gesture={tapGesture}>
|
|
169
|
+
<View
|
|
170
|
+
style={[
|
|
171
|
+
styles.maximumTrack(maximumTrackWidth),
|
|
172
|
+
SliderStyles.maximumTrack(colorPalette),
|
|
173
|
+
]}
|
|
174
|
+
onLayout={handleTrackLayout}>
|
|
175
|
+
{steps && (
|
|
176
|
+
<View style={styles.steps}>
|
|
177
|
+
{Array.from({length: steps + 1}, (_, index) => index).map(
|
|
178
|
+
(_, i) => (
|
|
179
|
+
<View
|
|
180
|
+
key={i}
|
|
181
|
+
style={[
|
|
182
|
+
styles.step(i, steps),
|
|
183
|
+
SliderStyles.step(colorPalette),
|
|
184
|
+
]}
|
|
185
|
+
/>
|
|
186
|
+
),
|
|
187
|
+
)}
|
|
188
|
+
</View>
|
|
189
|
+
)}
|
|
190
|
+
<Animated.View
|
|
191
|
+
style={[
|
|
192
|
+
styles.minimumTrack(halfSize, minimumTrackWidth),
|
|
193
|
+
SliderStyles.minimumTrack(colorPalette, opacityTrack),
|
|
194
|
+
animatedTrack,
|
|
195
|
+
]}>
|
|
196
|
+
<GestureDetector gesture={slideGesture}>
|
|
197
|
+
<View
|
|
198
|
+
style={[
|
|
199
|
+
SliderStyles.thumb(colorPalette),
|
|
200
|
+
styles.thumb(halfSize, thumbSize),
|
|
201
|
+
]}>
|
|
202
|
+
{ThumbChildren || null}
|
|
203
|
+
</View>
|
|
204
|
+
</GestureDetector>
|
|
205
|
+
</Animated.View>
|
|
206
|
+
</View>
|
|
207
|
+
</GestureDetector>
|
|
208
|
+
{axis === 'y' && <View style={{height: halfSize}} />}
|
|
209
|
+
</View>
|
|
210
|
+
)
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
const stylesX = StyleSheet.create(theme => ({
|
|
214
|
+
container: (thickness, halfSize) => ({
|
|
215
|
+
flex: thickness ? 1 : 1,
|
|
216
|
+
maxWidth: 'auto',
|
|
217
|
+
borderRadius: 22,
|
|
218
|
+
width: '100%',
|
|
219
|
+
height: '100%',
|
|
220
|
+
paddingTop: 0,
|
|
221
|
+
paddingRight: halfSize,
|
|
222
|
+
flexDirection: 'row',
|
|
223
|
+
}),
|
|
224
|
+
maximumTrack: thickness => ({
|
|
225
|
+
flex: thickness ? 1 : 1,
|
|
226
|
+
justifyContent: 'center',
|
|
227
|
+
position: 'relative',
|
|
228
|
+
borderRadius: 22,
|
|
229
|
+
}),
|
|
230
|
+
minimumTrack: (halfSize, minimumTrackThickness) => ({
|
|
231
|
+
height: minimumTrackThickness,
|
|
232
|
+
width: '100%',
|
|
233
|
+
left: halfSize * -1,
|
|
234
|
+
bottom: 'auto',
|
|
235
|
+
paddingBottom: 0,
|
|
236
|
+
paddingLeft: halfSize,
|
|
237
|
+
borderBottomLeftRadius: 20,
|
|
238
|
+
borderBottomRightRadius: 0,
|
|
239
|
+
borderTopLeftRadius: 20,
|
|
240
|
+
justifyContent: 'center',
|
|
241
|
+
}),
|
|
242
|
+
thumb: (halfSize, thumbSize) => ({
|
|
243
|
+
width: halfSize ? thumbSize.width : thumbSize.width,
|
|
244
|
+
height: thumbSize.height,
|
|
245
|
+
borderRadius: 25,
|
|
246
|
+
position: 'absolute',
|
|
247
|
+
right: 0,
|
|
248
|
+
zIndex: 2,
|
|
249
|
+
transform: [{translateX: '50%'}],
|
|
250
|
+
}),
|
|
251
|
+
steps: {
|
|
252
|
+
width: '100%',
|
|
253
|
+
height: '100%',
|
|
254
|
+
flexDirection: 'row',
|
|
255
|
+
justifyContent: 'space-between',
|
|
256
|
+
alignItems: 'center',
|
|
257
|
+
zIndex: 0,
|
|
258
|
+
position: 'absolute',
|
|
259
|
+
},
|
|
260
|
+
step: (index, length) => ({
|
|
261
|
+
width: 1,
|
|
262
|
+
height: '100%',
|
|
263
|
+
backgroundColor:
|
|
264
|
+
index === 0 || index === length ? 'transparent' : theme.colors.fg.default,
|
|
265
|
+
}),
|
|
266
|
+
}))
|
|
267
|
+
const stylesY = StyleSheet.create(theme => ({
|
|
268
|
+
container: (maximumTrackWidth, halfSize) => ({
|
|
269
|
+
flex: 1,
|
|
270
|
+
maxWidth: maximumTrackWidth,
|
|
271
|
+
maxHeight: 'auto',
|
|
272
|
+
borderRadius: 22,
|
|
273
|
+
width: '100%',
|
|
274
|
+
height: '100%',
|
|
275
|
+
paddingTop: halfSize,
|
|
276
|
+
paddingRight: 0,
|
|
277
|
+
flexDirection: 'column',
|
|
278
|
+
borderWidth: 1,
|
|
279
|
+
borderColor: 'yellow',
|
|
280
|
+
// minWidth: 30,
|
|
281
|
+
}),
|
|
282
|
+
maximumTrack: maximumTrackWidth => ({
|
|
283
|
+
flex: 1,
|
|
284
|
+
justifyContent: 'center',
|
|
285
|
+
alignItems: 'center',
|
|
286
|
+
position: 'relative',
|
|
287
|
+
maxWidth: maximumTrackWidth,
|
|
288
|
+
}),
|
|
289
|
+
minimumTrack: (halfSize, minimumTrackWidth) => ({
|
|
290
|
+
position: 'absolute',
|
|
291
|
+
height: '100%',
|
|
292
|
+
width: minimumTrackWidth,
|
|
293
|
+
left: 'auto',
|
|
294
|
+
bottom: halfSize * -1,
|
|
295
|
+
paddingBottom: halfSize,
|
|
296
|
+
paddingLeft: 0,
|
|
297
|
+
borderBottomLeftRadius: 20,
|
|
298
|
+
borderBottomRightRadius: 20,
|
|
299
|
+
borderTopLeftRadius: 0,
|
|
300
|
+
alignItems: 'center',
|
|
301
|
+
}),
|
|
302
|
+
thumb: (halfSize, thumbSize) => ({
|
|
303
|
+
borderRadius: 25,
|
|
304
|
+
position: 'absolute',
|
|
305
|
+
top: 0,
|
|
306
|
+
zIndex: 2,
|
|
307
|
+
transform: [{translateY: halfSize * -1}],
|
|
308
|
+
width: thumbSize.height,
|
|
309
|
+
height: thumbSize.width,
|
|
310
|
+
}),
|
|
311
|
+
steps: {
|
|
312
|
+
width: '100%',
|
|
313
|
+
height: '100%',
|
|
314
|
+
flexDirection: 'column',
|
|
315
|
+
justifyContent: 'space-between',
|
|
316
|
+
alignItems: 'center',
|
|
317
|
+
zIndex: 0,
|
|
318
|
+
},
|
|
319
|
+
step: (index, length) => ({
|
|
320
|
+
width: '100%',
|
|
321
|
+
height: 1,
|
|
322
|
+
backgroundColor:
|
|
323
|
+
index === 0 || index === length ? 'transparent' : theme.colors.fg.default,
|
|
324
|
+
}),
|
|
325
|
+
}))
|
|
326
|
+
|
|
327
|
+
export default OldSlider
|
|
@@ -1,11 +1,15 @@
|
|
|
1
1
|
import React, {createContext, useContext, useState, useCallback} from 'react'
|
|
2
|
-
import {View, Text, Pressable} from 'react-native'
|
|
2
|
+
import {View, Text, Pressable, ViewStyle} from 'react-native'
|
|
3
3
|
import {StyleSheet, UnistylesVariants} from 'react-native-unistyles'
|
|
4
4
|
import {
|
|
5
5
|
RadioGroupStyles,
|
|
6
6
|
RadioGroupDefaultVariants,
|
|
7
7
|
} from '../theme/RadioGroup.recipe'
|
|
8
|
-
import {
|
|
8
|
+
import {
|
|
9
|
+
AlignSelf,
|
|
10
|
+
PalettesWithNestedKeys,
|
|
11
|
+
StackDirection,
|
|
12
|
+
} from '../style/varia/types'
|
|
9
13
|
|
|
10
14
|
type RadioGroupVariants = UnistylesVariants<typeof RadioGroupStyles>
|
|
11
15
|
|
|
@@ -14,7 +18,12 @@ type RadioGroupRootProps = RadioGroupVariants & {
|
|
|
14
18
|
colorPalette?: PalettesWithNestedKeys
|
|
15
19
|
defaultValue?: string
|
|
16
20
|
onValueChange?: (value: string) => void
|
|
17
|
-
direction?:
|
|
21
|
+
direction?: StackDirection
|
|
22
|
+
flex?: number
|
|
23
|
+
alignSelf?: AlignSelf
|
|
24
|
+
justifyContent?: string
|
|
25
|
+
alignItems?: string
|
|
26
|
+
gap?: number
|
|
18
27
|
}
|
|
19
28
|
|
|
20
29
|
type RadioGroupContextProps = {
|
|
@@ -23,6 +32,8 @@ type RadioGroupContextProps = {
|
|
|
23
32
|
colorPalette: PalettesWithNestedKeys
|
|
24
33
|
variant: RadioGroupVariants['variant']
|
|
25
34
|
size: RadioGroupVariants['size']
|
|
35
|
+
flex: number
|
|
36
|
+
alignSelf?: AlignSelf
|
|
26
37
|
}
|
|
27
38
|
|
|
28
39
|
const RadioGroupContext = createContext<RadioGroupContextProps | null>(null)
|
|
@@ -44,7 +55,12 @@ function Root({
|
|
|
44
55
|
variant = RadioGroupDefaultVariants.variant,
|
|
45
56
|
size = RadioGroupDefaultVariants.size,
|
|
46
57
|
onValueChange,
|
|
47
|
-
|
|
58
|
+
flex = 0,
|
|
59
|
+
alignSelf = 'auto',
|
|
60
|
+
direction = 'column',
|
|
61
|
+
justifyContent,
|
|
62
|
+
alignItems,
|
|
63
|
+
gap,
|
|
48
64
|
}: RadioGroupRootProps) {
|
|
49
65
|
RadioGroupStyles.useVariants({variant, size})
|
|
50
66
|
const [value, setValue] = useState(defaultValue)
|
|
@@ -65,15 +81,22 @@ function Root({
|
|
|
65
81
|
colorPalette,
|
|
66
82
|
variant,
|
|
67
83
|
size,
|
|
84
|
+
flex,
|
|
68
85
|
}
|
|
69
86
|
|
|
70
87
|
return (
|
|
71
88
|
<RadioGroupContext.Provider value={contextValue}>
|
|
72
89
|
<View
|
|
73
90
|
style={[
|
|
74
|
-
styles.root
|
|
91
|
+
styles.root(
|
|
92
|
+
flex,
|
|
93
|
+
direction,
|
|
94
|
+
alignSelf,
|
|
95
|
+
justifyContent as ViewStyle['justifyContent'],
|
|
96
|
+
alignItems as ViewStyle['alignItems'],
|
|
97
|
+
gap,
|
|
98
|
+
),
|
|
75
99
|
RadioGroupStyles.container(colorPalette),
|
|
76
|
-
direction === 'horizontal' && styles.rootHorizontal,
|
|
77
100
|
]}>
|
|
78
101
|
{children}
|
|
79
102
|
</View>
|
|
@@ -99,17 +122,23 @@ function Item({children, value: itemValue}: PublicItemProps) {
|
|
|
99
122
|
|
|
100
123
|
const childrenWithValue = React.Children.map(children, child => {
|
|
101
124
|
if (React.isValidElement(child)) {
|
|
102
|
-
return React.cloneElement(
|
|
103
|
-
itemValue
|
|
104
|
-
|
|
105
|
-
|
|
125
|
+
return React.cloneElement(
|
|
126
|
+
child as React.ReactElement<{itemValue: string; isSelected: boolean}>,
|
|
127
|
+
{
|
|
128
|
+
itemValue,
|
|
129
|
+
isSelected,
|
|
130
|
+
},
|
|
131
|
+
)
|
|
106
132
|
}
|
|
107
133
|
return child
|
|
108
134
|
})
|
|
109
135
|
|
|
110
136
|
return (
|
|
111
137
|
<Pressable
|
|
112
|
-
style={
|
|
138
|
+
style={({pressed}) => [
|
|
139
|
+
styles.item,
|
|
140
|
+
RadioGroupStyles.item(colorPalette, pressed),
|
|
141
|
+
]}
|
|
113
142
|
onPress={() => onValueChange(itemValue)}
|
|
114
143
|
accessibilityRole="radio"
|
|
115
144
|
accessibilityState={{checked: isSelected}}>
|
|
@@ -175,12 +204,21 @@ function ItemText({
|
|
|
175
204
|
}
|
|
176
205
|
|
|
177
206
|
const styles = StyleSheet.create({
|
|
178
|
-
root:
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
207
|
+
root: (
|
|
208
|
+
flex: number,
|
|
209
|
+
direction: StackDirection,
|
|
210
|
+
alignSelf?: AlignSelf,
|
|
211
|
+
justifyContent?: ViewStyle['justifyContent'],
|
|
212
|
+
alignItems?: ViewStyle['alignItems'],
|
|
213
|
+
gap?: number,
|
|
214
|
+
) => ({
|
|
215
|
+
flex,
|
|
216
|
+
flexDirection: direction,
|
|
217
|
+
alignSelf,
|
|
218
|
+
alignItems,
|
|
219
|
+
gap,
|
|
220
|
+
justifyContent,
|
|
221
|
+
}),
|
|
184
222
|
item: {
|
|
185
223
|
flexDirection: 'row',
|
|
186
224
|
alignItems: 'center',
|
|
@@ -4,24 +4,35 @@ import React, {
|
|
|
4
4
|
useState,
|
|
5
5
|
ReactNode,
|
|
6
6
|
ReactElement,
|
|
7
|
+
useMemo,
|
|
7
8
|
} from 'react'
|
|
8
9
|
import {
|
|
9
10
|
View,
|
|
10
11
|
TouchableOpacity,
|
|
11
12
|
ScrollView,
|
|
12
13
|
TouchableWithoutFeedback,
|
|
13
|
-
|
|
14
|
+
Pressable,
|
|
14
15
|
} from 'react-native'
|
|
15
16
|
import {UnistylesVariants} from 'react-native-unistyles'
|
|
16
17
|
import {Portal} from '@gorhom/portal'
|
|
17
18
|
import Text from './Text'
|
|
18
|
-
import {StyleSheet
|
|
19
|
+
import {StyleSheet} from 'react-native-unistyles'
|
|
19
20
|
import {SelectStyles, SelectDefaultVariants} from '../theme/Select.recipe'
|
|
20
|
-
import {
|
|
21
|
+
import {
|
|
22
|
+
AlignSelf,
|
|
23
|
+
Flex,
|
|
24
|
+
PalettesWithNestedKeys,
|
|
25
|
+
StackDirection,
|
|
26
|
+
} from '../style/varia/types'
|
|
27
|
+
import {computeFlexSync} from '../style/varia/utils'
|
|
21
28
|
|
|
22
29
|
type SelectVariants = UnistylesVariants<typeof SelectStyles>
|
|
23
30
|
|
|
24
|
-
|
|
31
|
+
function hasDisplayName(type: any): type is {displayName: string} {
|
|
32
|
+
return typeof type === 'function' && 'displayName' in type
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
type Option = SelectVariants & {
|
|
25
36
|
label: string
|
|
26
37
|
value: string
|
|
27
38
|
}
|
|
@@ -35,6 +46,7 @@ interface SelectContextType {
|
|
|
35
46
|
colorPalette: PalettesWithNestedKeys
|
|
36
47
|
variant: SelectVariants['variant']
|
|
37
48
|
size: SelectVariants['size']
|
|
49
|
+
flex: Flex
|
|
38
50
|
}
|
|
39
51
|
|
|
40
52
|
const SelectContext = createContext<SelectContextType | undefined>(undefined)
|
|
@@ -53,9 +65,11 @@ type RootProps = SelectVariants & {
|
|
|
53
65
|
defaultValue?: string
|
|
54
66
|
placeholder?: string
|
|
55
67
|
colorPalette?: PalettesWithNestedKeys
|
|
56
|
-
flex?: number
|
|
57
68
|
portalHostName?: string
|
|
58
69
|
children: ReactNode
|
|
70
|
+
flex?: Flex
|
|
71
|
+
stretch?: AlignSelf
|
|
72
|
+
direction?: 'row' | 'column'
|
|
59
73
|
}
|
|
60
74
|
|
|
61
75
|
const SelectRoot = ({
|
|
@@ -66,9 +80,11 @@ const SelectRoot = ({
|
|
|
66
80
|
variant = SelectDefaultVariants.variant,
|
|
67
81
|
size = SelectDefaultVariants.size,
|
|
68
82
|
colorPalette = 'accent',
|
|
69
|
-
flex = 1,
|
|
70
83
|
portalHostName = 'select',
|
|
71
84
|
children,
|
|
85
|
+
flex = 0,
|
|
86
|
+
stretch,
|
|
87
|
+
direction = 'column',
|
|
72
88
|
}: RootProps) => {
|
|
73
89
|
SelectStyles.useVariants({variant, size})
|
|
74
90
|
const [internalValue, setInternalValue] = useState(defaultValue)
|
|
@@ -84,27 +100,32 @@ const SelectRoot = ({
|
|
|
84
100
|
}
|
|
85
101
|
|
|
86
102
|
const trigger = React.Children.toArray(children).find(
|
|
87
|
-
(child:
|
|
103
|
+
(child): child is ReactElement<TriggerProps> =>
|
|
88
104
|
React.isValidElement(child) &&
|
|
89
|
-
(child.type
|
|
105
|
+
hasDisplayName(child.type) &&
|
|
106
|
+
child.type.displayName === 'SelectTrigger',
|
|
90
107
|
)
|
|
91
108
|
|
|
92
109
|
const portalChildren = React.Children.toArray(children).filter(
|
|
93
|
-
(child:
|
|
110
|
+
(child): child is ReactElement<TriggerProps> =>
|
|
94
111
|
!(
|
|
95
112
|
React.isValidElement(child) &&
|
|
96
|
-
(child.type
|
|
113
|
+
hasDisplayName(child.type) &&
|
|
114
|
+
child.type.displayName === 'SelectTrigger'
|
|
97
115
|
),
|
|
98
116
|
)
|
|
99
117
|
|
|
100
118
|
return (
|
|
101
|
-
<View style={[styles.container(flex)]}>
|
|
119
|
+
<View style={[styles.container(flex, stretch)]}>
|
|
102
120
|
{trigger &&
|
|
103
121
|
React.cloneElement(trigger as ReactElement<TriggerProps>, {
|
|
104
122
|
isOpen,
|
|
105
123
|
setIsOpen,
|
|
106
124
|
value,
|
|
107
125
|
options,
|
|
126
|
+
flex,
|
|
127
|
+
stretch,
|
|
128
|
+
direction,
|
|
108
129
|
variant:
|
|
109
130
|
(trigger as ReactElement<TriggerProps>).props.variant ?? variant,
|
|
110
131
|
size: (trigger as ReactElement<TriggerProps>).props.size ?? size,
|
|
@@ -125,6 +146,7 @@ const SelectRoot = ({
|
|
|
125
146
|
colorPalette,
|
|
126
147
|
variant,
|
|
127
148
|
size,
|
|
149
|
+
flex,
|
|
128
150
|
}}>
|
|
129
151
|
<View style={styles.portalContainer}>{portalChildren}</View>
|
|
130
152
|
</SelectContext.Provider>
|
|
@@ -143,6 +165,9 @@ interface TriggerProps {
|
|
|
143
165
|
colorPalette?: PalettesWithNestedKeys
|
|
144
166
|
value?: string
|
|
145
167
|
options?: Option[]
|
|
168
|
+
flex?: Flex
|
|
169
|
+
stretch?: AlignSelf
|
|
170
|
+
direction?: 'row' | 'column'
|
|
146
171
|
}
|
|
147
172
|
|
|
148
173
|
const SelectTrigger = ({
|
|
@@ -153,16 +178,25 @@ const SelectTrigger = ({
|
|
|
153
178
|
size,
|
|
154
179
|
colorPalette = 'accent',
|
|
155
180
|
value,
|
|
181
|
+
flex = 0,
|
|
182
|
+
stretch,
|
|
183
|
+
direction,
|
|
156
184
|
options = [],
|
|
157
185
|
}: TriggerProps) => {
|
|
158
186
|
SelectStyles.useVariants({variant, size})
|
|
187
|
+
const {flex: childFlex} = useMemo(() => {
|
|
188
|
+
return computeFlexSync(flex as number, stretch, direction as StackDirection)
|
|
189
|
+
}, [flex, stretch, direction])
|
|
159
190
|
const selected = options.find(opt => opt.value === value)
|
|
160
191
|
return (
|
|
161
|
-
<
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
192
|
+
<Pressable
|
|
193
|
+
onPress={() => setIsOpen?.(!isOpen)}
|
|
194
|
+
style={({pressed}) => [
|
|
195
|
+
styles.input(childFlex),
|
|
196
|
+
SelectStyles.trigger(colorPalette, pressed),
|
|
197
|
+
]}>
|
|
198
|
+
<Text>{selected ? selected.label : placeholder}</Text>
|
|
199
|
+
</Pressable>
|
|
166
200
|
)
|
|
167
201
|
}
|
|
168
202
|
SelectTrigger.displayName = 'SelectTrigger'
|
|
@@ -236,18 +270,20 @@ const Select = {
|
|
|
236
270
|
|
|
237
271
|
export default Select
|
|
238
272
|
|
|
239
|
-
const styles =
|
|
240
|
-
container: (flex:
|
|
241
|
-
|
|
273
|
+
const styles = StyleSheet.create({
|
|
274
|
+
container: (flex: Flex, stretch: AlignSelf) => ({
|
|
275
|
+
flex,
|
|
242
276
|
flexDirection: 'row',
|
|
243
277
|
justifyContent: 'center',
|
|
278
|
+
...(stretch ? {alignSelf: stretch} : {}),
|
|
279
|
+
alignItems: 'stretch',
|
|
244
280
|
}),
|
|
245
|
-
input: {
|
|
246
|
-
flex
|
|
281
|
+
input: (flex: Flex) => ({
|
|
282
|
+
flex,
|
|
247
283
|
flexDirection: 'row',
|
|
248
284
|
alignItems: 'center',
|
|
249
285
|
justifyContent: 'center',
|
|
250
|
-
},
|
|
286
|
+
}),
|
|
251
287
|
item: {
|
|
252
288
|
alignItems: 'center',
|
|
253
289
|
flexDirection: 'row',
|