@wecareu/select 0.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.
- package/README.md +165 -0
- package/lib/commonjs/Select.js +272 -0
- package/lib/commonjs/Select.js.map +1 -0
- package/lib/commonjs/Select.types.js +6 -0
- package/lib/commonjs/Select.types.js.map +1 -0
- package/lib/commonjs/SelectChip.js +74 -0
- package/lib/commonjs/SelectChip.js.map +1 -0
- package/lib/commonjs/SelectDropdown.js +156 -0
- package/lib/commonjs/SelectDropdown.js.map +1 -0
- package/lib/commonjs/SelectErrorMessage.js +32 -0
- package/lib/commonjs/SelectErrorMessage.js.map +1 -0
- package/lib/commonjs/SelectIcon.js +64 -0
- package/lib/commonjs/SelectIcon.js.map +1 -0
- package/lib/commonjs/SelectOption.js +140 -0
- package/lib/commonjs/SelectOption.js.map +1 -0
- package/lib/commonjs/SelectSearch.js +79 -0
- package/lib/commonjs/SelectSearch.js.map +1 -0
- package/lib/commonjs/animations/shake.js +45 -0
- package/lib/commonjs/animations/shake.js.map +1 -0
- package/lib/commonjs/index.js +13 -0
- package/lib/commonjs/index.js.map +1 -0
- package/lib/commonjs/index.stories.js +383 -0
- package/lib/commonjs/index.stories.js.map +1 -0
- package/lib/module/Select.js +265 -0
- package/lib/module/Select.js.map +1 -0
- package/lib/module/Select.types.js +2 -0
- package/lib/module/Select.types.js.map +1 -0
- package/lib/module/SelectChip.js +67 -0
- package/lib/module/SelectChip.js.map +1 -0
- package/lib/module/SelectDropdown.js +149 -0
- package/lib/module/SelectDropdown.js.map +1 -0
- package/lib/module/SelectErrorMessage.js +25 -0
- package/lib/module/SelectErrorMessage.js.map +1 -0
- package/lib/module/SelectIcon.js +57 -0
- package/lib/module/SelectIcon.js.map +1 -0
- package/lib/module/SelectOption.js +133 -0
- package/lib/module/SelectOption.js.map +1 -0
- package/lib/module/SelectSearch.js +72 -0
- package/lib/module/SelectSearch.js.map +1 -0
- package/lib/module/animations/shake.js +39 -0
- package/lib/module/animations/shake.js.map +1 -0
- package/lib/module/index.js +2 -0
- package/lib/module/index.js.map +1 -0
- package/lib/module/index.stories.js +376 -0
- package/lib/module/index.stories.js.map +1 -0
- package/lib/typescript/src/Select.d.ts +3 -0
- package/lib/typescript/src/Select.d.ts.map +1 -0
- package/lib/typescript/src/Select.types.d.ts +245 -0
- package/lib/typescript/src/Select.types.d.ts.map +1 -0
- package/lib/typescript/src/SelectChip.d.ts +3 -0
- package/lib/typescript/src/SelectChip.d.ts.map +1 -0
- package/lib/typescript/src/SelectDropdown.d.ts +3 -0
- package/lib/typescript/src/SelectDropdown.d.ts.map +1 -0
- package/lib/typescript/src/SelectErrorMessage.d.ts +3 -0
- package/lib/typescript/src/SelectErrorMessage.d.ts.map +1 -0
- package/lib/typescript/src/SelectIcon.d.ts +3 -0
- package/lib/typescript/src/SelectIcon.d.ts.map +1 -0
- package/lib/typescript/src/SelectOption.d.ts +3 -0
- package/lib/typescript/src/SelectOption.d.ts.map +1 -0
- package/lib/typescript/src/SelectSearch.d.ts +3 -0
- package/lib/typescript/src/SelectSearch.d.ts.map +1 -0
- package/lib/typescript/src/animations/shake.d.ts +32 -0
- package/lib/typescript/src/animations/shake.d.ts.map +1 -0
- package/lib/typescript/src/index.d.ts +3 -0
- package/lib/typescript/src/index.d.ts.map +1 -0
- package/package.json +66 -0
- package/src/Select.tsx +338 -0
- package/src/Select.types.ts +256 -0
- package/src/SelectChip.tsx +75 -0
- package/src/SelectDropdown.tsx +184 -0
- package/src/SelectErrorMessage.tsx +31 -0
- package/src/SelectIcon.tsx +63 -0
- package/src/SelectOption.tsx +168 -0
- package/src/SelectSearch.tsx +81 -0
- package/src/animations/shake.ts +76 -0
- package/src/index.stories.tsx +336 -0
- package/src/index.tsx +2 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SelectIcon.d.ts","sourceRoot":"","sources":["../../../src/SelectIcon.tsx"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAA;AAErD,wBAAgB,UAAU,CAAC,EACzB,kBAAkB,EAClB,KAAK,EACL,IAAI,EACJ,OAAO,EACP,IAAI,EACJ,MAAM,EACP,EAAE,eAAe,GAAG,GAAG,CAAC,OAAO,CAmC/B"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SelectOption.d.ts","sourceRoot":"","sources":["../../../src/SelectOption.tsx"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,gBAAgB,CAAA;AAE3D,wBAAgB,gBAAgB,CAAC,EAC/B,QAAQ,EACR,OAAO,EACP,UAAU,EACV,OAAO,EACP,MAAM,EACP,EAAE,qBAAqB,GAAG,GAAG,CAAC,OAAO,CAkFrC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SelectSearch.d.ts","sourceRoot":"","sources":["../../../src/SelectSearch.tsx"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAA;AAIvD,wBAAgB,YAAY,CAAC,EAAE,YAAY,EAAE,KAAK,EAAE,EAAE,iBAAiB,GAAG,GAAG,CAAC,OAAO,CA2CpF"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { Animated } from 'react-native';
|
|
2
|
+
interface UseShakeAnimationParams {
|
|
3
|
+
/**
|
|
4
|
+
* Distance in pixels applied to the shake translation
|
|
5
|
+
*/
|
|
6
|
+
distance?: number;
|
|
7
|
+
/**
|
|
8
|
+
* Duration of each shake step in milliseconds
|
|
9
|
+
*/
|
|
10
|
+
duration?: number;
|
|
11
|
+
/**
|
|
12
|
+
* Number of oscillations executed per trigger
|
|
13
|
+
*/
|
|
14
|
+
iterations?: number;
|
|
15
|
+
}
|
|
16
|
+
interface UseShakeAnimationReturn {
|
|
17
|
+
/**
|
|
18
|
+
* Animated style applied to the container that needs feedback
|
|
19
|
+
*/
|
|
20
|
+
animatedStyle: {
|
|
21
|
+
transform: {
|
|
22
|
+
translateX: Animated.AnimatedInterpolation<number>;
|
|
23
|
+
}[];
|
|
24
|
+
};
|
|
25
|
+
/**
|
|
26
|
+
* Function responsible for starting the shake effect
|
|
27
|
+
*/
|
|
28
|
+
triggerShake: () => void;
|
|
29
|
+
}
|
|
30
|
+
export declare function useShakeAnimation({ distance, duration, iterations }?: UseShakeAnimationParams): UseShakeAnimationReturn;
|
|
31
|
+
export {};
|
|
32
|
+
//# sourceMappingURL=shake.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"shake.d.ts","sourceRoot":"","sources":["../../../../src/animations/shake.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAA;AAEvC,UAAU,uBAAuB;IAC/B;;OAEG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB;;OAEG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB;;OAEG;IACH,UAAU,CAAC,EAAE,MAAM,CAAA;CACpB;AAED,UAAU,uBAAuB;IAC/B;;OAEG;IACH,aAAa,EAAE;QACb,SAAS,EAAE;YAAE,UAAU,EAAE,QAAQ,CAAC,qBAAqB,CAAC,MAAM,CAAC,CAAA;SAAE,EAAE,CAAA;KACpE,CAAA;IACD;;OAEG;IACH,YAAY,EAAE,MAAM,IAAI,CAAA;CACzB;AAED,wBAAgB,iBAAiB,CAAC,EAChC,QAAY,EACZ,QAAa,EACb,UAAc,EACf,GAAE,uBAA4B,GAAG,uBAAuB,CAuCxD"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/index.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,UAAU,CAAA;AACjC,YAAY,EAAE,gBAAgB,EAAE,YAAY,EAAE,WAAW,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAA"}
|
package/package.json
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@wecareu/select",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Componente de seleção controlado do design system WeCareU com suporte a modal, busca e múltipla seleção",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"react-native",
|
|
7
|
+
"select",
|
|
8
|
+
"dropdown",
|
|
9
|
+
"design-system",
|
|
10
|
+
"wecareu"
|
|
11
|
+
],
|
|
12
|
+
"license": "UNLICENSED",
|
|
13
|
+
"private": false,
|
|
14
|
+
"publishConfig": {
|
|
15
|
+
"access": "public"
|
|
16
|
+
},
|
|
17
|
+
"main": "lib/commonjs/index.js",
|
|
18
|
+
"module": "lib/module/index.js",
|
|
19
|
+
"react-native": "lib/module/index.js",
|
|
20
|
+
"types": "lib/typescript/src/index.d.ts",
|
|
21
|
+
"files": [
|
|
22
|
+
"lib",
|
|
23
|
+
"src",
|
|
24
|
+
"README.md"
|
|
25
|
+
],
|
|
26
|
+
"scripts": {
|
|
27
|
+
"build": "bob build",
|
|
28
|
+
"clean": "rimraf lib",
|
|
29
|
+
"lint": "eslint .",
|
|
30
|
+
"test": "jest"
|
|
31
|
+
},
|
|
32
|
+
"peerDependencies": {
|
|
33
|
+
"@wecareu/icons": "*",
|
|
34
|
+
"@wecareu/theme": "*",
|
|
35
|
+
"@wecareu/tokens": "*",
|
|
36
|
+
"react": ">=18.2.0",
|
|
37
|
+
"react-native": ">=0.74.0-0 <0.76.0"
|
|
38
|
+
},
|
|
39
|
+
"devDependencies": {
|
|
40
|
+
"@testing-library/jest-native": "^5.4.3",
|
|
41
|
+
"@testing-library/react-native": "^12.7.2",
|
|
42
|
+
"@types/jest": "^29.5.0",
|
|
43
|
+
"jest": "^29.7.0",
|
|
44
|
+
"react": "18.2.0",
|
|
45
|
+
"react-native": "0.74.5",
|
|
46
|
+
"react-native-builder-bob": "^0.23.2",
|
|
47
|
+
"typescript": "^5.4.0"
|
|
48
|
+
},
|
|
49
|
+
"exports": {
|
|
50
|
+
".": {
|
|
51
|
+
"types": "./lib/typescript/src/index.d.ts",
|
|
52
|
+
"react-native": "./lib/module/index.js",
|
|
53
|
+
"import": "./lib/module/index.js",
|
|
54
|
+
"require": "./lib/commonjs/index.js"
|
|
55
|
+
}
|
|
56
|
+
},
|
|
57
|
+
"react-native-builder-bob": {
|
|
58
|
+
"source": "src",
|
|
59
|
+
"output": "lib",
|
|
60
|
+
"targets": [
|
|
61
|
+
"commonjs",
|
|
62
|
+
"module",
|
|
63
|
+
"typescript"
|
|
64
|
+
]
|
|
65
|
+
}
|
|
66
|
+
}
|
package/src/Select.tsx
ADDED
|
@@ -0,0 +1,338 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
|
|
3
|
+
import { Animated, Pressable, ScrollView, StyleSheet, Text, View } from 'react-native'
|
|
4
|
+
|
|
5
|
+
import { useTheme } from '@wecareu/theme'
|
|
6
|
+
import type { Theme } from '@wecareu/theme'
|
|
7
|
+
|
|
8
|
+
import { useShakeAnimation } from './animations/shake'
|
|
9
|
+
import type { SelectMultiProps, SelectOption, SelectProps, SelectSingleProps } from './Select.types'
|
|
10
|
+
import { SelectChip } from './SelectChip'
|
|
11
|
+
import { SelectDropdown } from './SelectDropdown'
|
|
12
|
+
import { SelectErrorMessage } from './SelectErrorMessage'
|
|
13
|
+
import { SelectIcon } from './SelectIcon'
|
|
14
|
+
|
|
15
|
+
export function Select(props: SelectProps): JSX.Element | null {
|
|
16
|
+
const theme = useTheme()
|
|
17
|
+
const styles = React.useMemo(() => createStyles(theme), [theme])
|
|
18
|
+
|
|
19
|
+
const {
|
|
20
|
+
clearable = false,
|
|
21
|
+
disabled = false,
|
|
22
|
+
errorMessage,
|
|
23
|
+
inputError = false,
|
|
24
|
+
labels,
|
|
25
|
+
leftIcon,
|
|
26
|
+
multiple,
|
|
27
|
+
onValidation,
|
|
28
|
+
options,
|
|
29
|
+
placeholder,
|
|
30
|
+
readonly = false,
|
|
31
|
+
required = false,
|
|
32
|
+
searchable = false,
|
|
33
|
+
style,
|
|
34
|
+
testID,
|
|
35
|
+
value
|
|
36
|
+
} = props
|
|
37
|
+
|
|
38
|
+
if (!options || options.length === 0) {
|
|
39
|
+
console.error('[Select] The "options" prop must be a non-empty array. Received:', options)
|
|
40
|
+
return null
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const isInteractive = !(disabled || readonly)
|
|
44
|
+
const [isOpen, setIsOpen] = React.useState(false)
|
|
45
|
+
const [touched, setTouched] = React.useState(false)
|
|
46
|
+
|
|
47
|
+
const { animatedStyle, triggerShake } = useShakeAnimation({
|
|
48
|
+
distance: theme.spacing.sm
|
|
49
|
+
})
|
|
50
|
+
|
|
51
|
+
const isEmpty = multiple ? (value as Array<string | number>).length === 0 : value === null || value === undefined
|
|
52
|
+
|
|
53
|
+
const internalHasError = React.useMemo(() => {
|
|
54
|
+
if (!required || !touched) {
|
|
55
|
+
return false
|
|
56
|
+
}
|
|
57
|
+
return isEmpty
|
|
58
|
+
}, [isEmpty, required, touched])
|
|
59
|
+
|
|
60
|
+
React.useEffect(() => {
|
|
61
|
+
if (!required || !touched) {
|
|
62
|
+
return
|
|
63
|
+
}
|
|
64
|
+
onValidation?.(!internalHasError)
|
|
65
|
+
}, [internalHasError, onValidation, required, touched])
|
|
66
|
+
|
|
67
|
+
const shouldShowError = inputError || internalHasError
|
|
68
|
+
const requiredErrorMessage = labels?.requiredError ?? 'This field is required'
|
|
69
|
+
const effectiveErrorMessage = inputError ? errorMessage : internalHasError ? requiredErrorMessage : undefined
|
|
70
|
+
|
|
71
|
+
const previousErrorRef = React.useRef(false)
|
|
72
|
+
|
|
73
|
+
React.useEffect(() => {
|
|
74
|
+
if (shouldShowError && !previousErrorRef.current) {
|
|
75
|
+
triggerShake()
|
|
76
|
+
}
|
|
77
|
+
previousErrorRef.current = shouldShowError
|
|
78
|
+
}, [shouldShowError, triggerShake])
|
|
79
|
+
|
|
80
|
+
const selectedValues = React.useMemo<Array<string | number>>(() => {
|
|
81
|
+
if (multiple) {
|
|
82
|
+
return (value as Array<string | number>) ?? []
|
|
83
|
+
}
|
|
84
|
+
if (value !== null && value !== undefined) {
|
|
85
|
+
return [value as string | number]
|
|
86
|
+
}
|
|
87
|
+
return []
|
|
88
|
+
}, [multiple, value])
|
|
89
|
+
|
|
90
|
+
const selectedOptions = React.useMemo(
|
|
91
|
+
() => options.filter(o => selectedValues.includes(o.value)),
|
|
92
|
+
[options, selectedValues]
|
|
93
|
+
)
|
|
94
|
+
|
|
95
|
+
const singleLabel = React.useMemo(() => {
|
|
96
|
+
if (multiple || isEmpty) {
|
|
97
|
+
return null
|
|
98
|
+
}
|
|
99
|
+
return options.find(o => o.value === (value as string | number))?.label ?? null
|
|
100
|
+
}, [isEmpty, multiple, options, value])
|
|
101
|
+
|
|
102
|
+
const handleOpen = React.useCallback(() => {
|
|
103
|
+
if (!isInteractive) {
|
|
104
|
+
return
|
|
105
|
+
}
|
|
106
|
+
setTouched(true)
|
|
107
|
+
setIsOpen(true)
|
|
108
|
+
}, [isInteractive])
|
|
109
|
+
|
|
110
|
+
const handleClose = React.useCallback(() => {
|
|
111
|
+
setIsOpen(false)
|
|
112
|
+
}, [])
|
|
113
|
+
|
|
114
|
+
const handleToggleOption = React.useCallback(
|
|
115
|
+
(option: SelectOption) => {
|
|
116
|
+
if (multiple) {
|
|
117
|
+
const multiProps = props as SelectMultiProps
|
|
118
|
+
const current = multiProps.value
|
|
119
|
+
const isSelected = current.includes(option.value)
|
|
120
|
+
const next = isSelected ? current.filter(v => v !== option.value) : [...current, option.value]
|
|
121
|
+
multiProps.onChange(next)
|
|
122
|
+
} else {
|
|
123
|
+
const singleProps = props as SelectSingleProps
|
|
124
|
+
singleProps.onChange(option.value)
|
|
125
|
+
setIsOpen(false)
|
|
126
|
+
}
|
|
127
|
+
},
|
|
128
|
+
[multiple, props]
|
|
129
|
+
)
|
|
130
|
+
|
|
131
|
+
const handleClear = React.useCallback(
|
|
132
|
+
() => {
|
|
133
|
+
if (multiple) {
|
|
134
|
+
const multiProps = props as SelectMultiProps
|
|
135
|
+
multiProps.onChange([])
|
|
136
|
+
} else {
|
|
137
|
+
const singleProps = props as SelectSingleProps
|
|
138
|
+
singleProps.onChange(null)
|
|
139
|
+
}
|
|
140
|
+
},
|
|
141
|
+
[multiple, props]
|
|
142
|
+
)
|
|
143
|
+
|
|
144
|
+
const handleRemoveChip = React.useCallback(
|
|
145
|
+
(optionValue: string | number) => {
|
|
146
|
+
if (!multiple) {
|
|
147
|
+
return
|
|
148
|
+
}
|
|
149
|
+
const multiProps = props as SelectMultiProps
|
|
150
|
+
multiProps.onChange(multiProps.value.filter(v => v !== optionValue))
|
|
151
|
+
},
|
|
152
|
+
[multiple, props]
|
|
153
|
+
)
|
|
154
|
+
|
|
155
|
+
const showClearButton = clearable && !isEmpty && isInteractive
|
|
156
|
+
|
|
157
|
+
const renderFieldContent = () => {
|
|
158
|
+
if (multiple && selectedOptions.length > 0) {
|
|
159
|
+
return (
|
|
160
|
+
<ScrollView
|
|
161
|
+
horizontal
|
|
162
|
+
keyboardShouldPersistTaps='handled'
|
|
163
|
+
showsHorizontalScrollIndicator={false}
|
|
164
|
+
style={styles.chipsScroll}
|
|
165
|
+
contentContainerStyle={styles.chipsContainer}
|
|
166
|
+
>
|
|
167
|
+
{selectedOptions.map(opt => (
|
|
168
|
+
<SelectChip
|
|
169
|
+
key={String(opt.value)}
|
|
170
|
+
disabled={disabled}
|
|
171
|
+
label={opt.label}
|
|
172
|
+
onRemove={() => handleRemoveChip(opt.value)}
|
|
173
|
+
/>
|
|
174
|
+
))}
|
|
175
|
+
</ScrollView>
|
|
176
|
+
)
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
if (!isEmpty && singleLabel) {
|
|
180
|
+
return (
|
|
181
|
+
<Text numberOfLines={1} style={styles.selectedLabel}>
|
|
182
|
+
{singleLabel}
|
|
183
|
+
</Text>
|
|
184
|
+
)
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
return (
|
|
188
|
+
<Text numberOfLines={1} style={styles.placeholder}>
|
|
189
|
+
{placeholder ?? 'Select...'}
|
|
190
|
+
</Text>
|
|
191
|
+
)
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
return (
|
|
195
|
+
<View style={styles.wrapper}>
|
|
196
|
+
<Animated.View
|
|
197
|
+
accessible={false}
|
|
198
|
+
style={[
|
|
199
|
+
styles.container,
|
|
200
|
+
!isInteractive && styles.containerDisabled,
|
|
201
|
+
shouldShowError && styles.containerError,
|
|
202
|
+
animatedStyle,
|
|
203
|
+
style
|
|
204
|
+
]}
|
|
205
|
+
testID={testID}
|
|
206
|
+
>
|
|
207
|
+
{leftIcon ? (
|
|
208
|
+
<View style={styles.iconLeft}>
|
|
209
|
+
<SelectIcon {...leftIcon} />
|
|
210
|
+
</View>
|
|
211
|
+
) : null}
|
|
212
|
+
|
|
213
|
+
<Pressable
|
|
214
|
+
accessibilityHint={placeholder}
|
|
215
|
+
accessibilityLabel={singleLabel ?? placeholder ?? 'Select option'}
|
|
216
|
+
accessibilityRole='combobox'
|
|
217
|
+
accessibilityState={{ disabled: !isInteractive, expanded: isOpen }}
|
|
218
|
+
disabled={!isInteractive}
|
|
219
|
+
onPress={handleOpen}
|
|
220
|
+
style={styles.fieldPressable}
|
|
221
|
+
>
|
|
222
|
+
{renderFieldContent()}
|
|
223
|
+
</Pressable>
|
|
224
|
+
|
|
225
|
+
<View style={styles.rightIcons}>
|
|
226
|
+
{showClearButton ? (
|
|
227
|
+
<Pressable
|
|
228
|
+
accessibilityLabel='Clear selection'
|
|
229
|
+
accessibilityRole='button'
|
|
230
|
+
hitSlop={8}
|
|
231
|
+
onPress={handleClear}
|
|
232
|
+
>
|
|
233
|
+
<SelectIcon
|
|
234
|
+
color={theme.colors.text.tertiary}
|
|
235
|
+
name='close'
|
|
236
|
+
size={theme.spacing.md}
|
|
237
|
+
/>
|
|
238
|
+
</Pressable>
|
|
239
|
+
) : (
|
|
240
|
+
<Pressable
|
|
241
|
+
accessibilityLabel={isOpen ? 'Close options' : 'Open options'}
|
|
242
|
+
accessibilityRole='button'
|
|
243
|
+
disabled={!isInteractive}
|
|
244
|
+
hitSlop={8}
|
|
245
|
+
onPress={handleOpen}
|
|
246
|
+
>
|
|
247
|
+
<SelectIcon
|
|
248
|
+
color={theme.colors.text.tertiary}
|
|
249
|
+
name={isOpen ? 'chevronUp' : 'chevronDown'}
|
|
250
|
+
size={theme.spacing.md}
|
|
251
|
+
/>
|
|
252
|
+
</Pressable>
|
|
253
|
+
)}
|
|
254
|
+
</View>
|
|
255
|
+
</Animated.View>
|
|
256
|
+
|
|
257
|
+
<SelectErrorMessage
|
|
258
|
+
message={shouldShowError ? effectiveErrorMessage : undefined}
|
|
259
|
+
testID={effectiveErrorMessage ? `${testID ?? 'select'}-error` : undefined}
|
|
260
|
+
/>
|
|
261
|
+
|
|
262
|
+
<SelectDropdown
|
|
263
|
+
isMulti={multiple}
|
|
264
|
+
isOpen={isOpen}
|
|
265
|
+
onClose={handleClose}
|
|
266
|
+
onToggleOption={handleToggleOption}
|
|
267
|
+
options={options}
|
|
268
|
+
searchable={searchable}
|
|
269
|
+
selectedValues={selectedValues}
|
|
270
|
+
/>
|
|
271
|
+
</View>
|
|
272
|
+
)
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
function createStyles(theme: Theme) {
|
|
276
|
+
return StyleSheet.create({
|
|
277
|
+
chipsContainer: {
|
|
278
|
+
alignItems: 'center',
|
|
279
|
+
flexDirection: 'row',
|
|
280
|
+
gap: theme.spacing.xs,
|
|
281
|
+
paddingVertical: 2
|
|
282
|
+
},
|
|
283
|
+
chipsScroll: {
|
|
284
|
+
flex: 1
|
|
285
|
+
},
|
|
286
|
+
container: {
|
|
287
|
+
alignItems: 'center',
|
|
288
|
+
backgroundColor: 'transparent',
|
|
289
|
+
borderColor: theme.colors.brand.primary,
|
|
290
|
+
borderRadius: theme.radius.xxl,
|
|
291
|
+
borderWidth: 1,
|
|
292
|
+
flexDirection: 'row',
|
|
293
|
+
minHeight: 48,
|
|
294
|
+
paddingHorizontal: theme.spacing.xl,
|
|
295
|
+
paddingVertical: theme.spacing.xs,
|
|
296
|
+
width: '100%'
|
|
297
|
+
},
|
|
298
|
+
containerDisabled: {
|
|
299
|
+
backgroundColor: theme.colors.surface.disabled,
|
|
300
|
+
borderColor: theme.colors.border.disabled
|
|
301
|
+
},
|
|
302
|
+
containerError: {
|
|
303
|
+
borderColor: theme.colors.status.error
|
|
304
|
+
},
|
|
305
|
+
fieldPressable: {
|
|
306
|
+
alignItems: 'center',
|
|
307
|
+
flex: 1,
|
|
308
|
+
flexDirection: 'row',
|
|
309
|
+
justifyContent: 'center',
|
|
310
|
+
minHeight: 32
|
|
311
|
+
},
|
|
312
|
+
iconLeft: {
|
|
313
|
+
marginRight: theme.spacing.xs
|
|
314
|
+
},
|
|
315
|
+
placeholder: {
|
|
316
|
+
color: theme.colors.text.tertiary,
|
|
317
|
+
flex: 1,
|
|
318
|
+
fontFamily: theme.typography.fontFamily.body,
|
|
319
|
+
lineHeight: theme.typography.lineHeight.bodySmall,
|
|
320
|
+
textAlign: 'center'
|
|
321
|
+
},
|
|
322
|
+
rightIcons: {
|
|
323
|
+
alignItems: 'center',
|
|
324
|
+
flexDirection: 'row',
|
|
325
|
+
marginLeft: theme.spacing.xs
|
|
326
|
+
},
|
|
327
|
+
selectedLabel: {
|
|
328
|
+
color: theme.colors.text.primary,
|
|
329
|
+
flex: 1,
|
|
330
|
+
fontFamily: theme.typography.fontFamily.body,
|
|
331
|
+
lineHeight: theme.typography.lineHeight.bodySmall,
|
|
332
|
+
textAlign: 'center'
|
|
333
|
+
},
|
|
334
|
+
wrapper: {
|
|
335
|
+
width: '100%'
|
|
336
|
+
}
|
|
337
|
+
})
|
|
338
|
+
}
|