-
Notifications
You must be signed in to change notification settings - Fork 94
feat: Select theming #733
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: Select theming #733
Changes from all commits
7d2043c
7c7f718
fb17849
d41080f
f6cc21b
a4f4ae6
0f8c643
3d927b3
89eab96
e5a3d7e
d21b0d1
9b63f6b
d4bed94
f667fd9
19ff4ac
fcbdaab
74f89e3
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,4 +1,4 @@ | ||
| import { forwardRef, memo, useMemo } from 'react'; | ||
| import { forwardRef, memo, useCallback, useMemo } from 'react'; | ||
| import { Pressable, type StyleProp, TouchableOpacity, type ViewStyle } from 'react-native'; | ||
| import type { ThemeVars } from '@coinbase/cds-common/core/theme'; | ||
| import { useInputVariant } from '@coinbase/cds-common/hooks/useInputVariant'; | ||
|
|
@@ -46,6 +46,7 @@ export const DefaultSelectControlComponent = memo( | |
| open, | ||
| placeholder, | ||
| disabled, | ||
| readOnly, | ||
| setOpen, | ||
| variant, | ||
| helperText, | ||
|
|
@@ -57,10 +58,15 @@ export const DefaultSelectControlComponent = memo( | |
| compact, | ||
| align = 'start', | ||
| font = 'body', | ||
| labelColor = 'fg', | ||
| labelFont = 'label1', | ||
| bordered = true, | ||
| borderWidth = bordered ? 100 : 0, | ||
| focusedBorderWidth = bordered ? undefined : 200, | ||
| inputBackground = !disabled && readOnly ? 'bgSecondary' : 'bg', | ||
| borderRadius, | ||
| maxSelectedOptionsToShow = 3, | ||
| accessibilityHint, | ||
| accessibilityLabel, | ||
| hiddenSelectedOptionsLabel = 'more', | ||
| removeSelectedOptionAccessibilityLabel = 'Remove', | ||
|
|
@@ -76,6 +82,13 @@ export const DefaultSelectControlComponent = memo( | |
| ? SelectOptionValue | SelectOptionValue[] | null | ||
| : SelectOptionValue | null; | ||
|
|
||
| const isInteractionBlocked = disabled || readOnly; | ||
|
|
||
| const handleToggleOpen = useCallback(() => { | ||
| if (isInteractionBlocked) return; | ||
| setOpen((currentOpen) => !currentOpen); | ||
| }, [isInteractionBlocked, setOpen]); | ||
|
|
||
| const theme = useTheme(); | ||
| // When compact, labelVariant is ignored | ||
| const labelVariant = compact ? undefined : labelVariantProp; | ||
|
|
@@ -193,28 +206,52 @@ export const DefaultSelectControlComponent = memo( | |
|
|
||
| if (typeof label === 'string') { | ||
| return ( | ||
| <InputLabel color="fg" paddingY={0.5} style={styles?.controlLabelNode}> | ||
| <InputLabel | ||
| color={labelColor} | ||
| font={labelFont} | ||
| paddingY={0.5} | ||
| style={styles?.controlLabelNode} | ||
| > | ||
| {label} | ||
| </InputLabel> | ||
| ); | ||
| } | ||
|
|
||
| return label; | ||
| }, [shouldShowInsideLabel, shouldShowCompactLabel, label, styles?.controlLabelNode]); | ||
| }, [ | ||
| shouldShowInsideLabel, | ||
| shouldShowCompactLabel, | ||
| label, | ||
| labelColor, | ||
| labelFont, | ||
| styles?.controlLabelNode, | ||
| ]); | ||
|
|
||
| const inlineLabelNode = useMemo(() => { | ||
| if (!shouldShowInsideLabel && !shouldShowCompactLabel) return null; | ||
|
|
||
| if (typeof label === 'string') { | ||
| return ( | ||
| <InputLabel color="fg" paddingY={0} style={styles?.controlLabelNode}> | ||
| <InputLabel | ||
| color={labelColor} | ||
| font={labelFont} | ||
| paddingY={0} | ||
| style={styles?.controlLabelNode} | ||
| > | ||
| {label} | ||
| </InputLabel> | ||
| ); | ||
| } | ||
|
|
||
| return label; | ||
| }, [shouldShowInsideLabel, shouldShowCompactLabel, label, styles?.controlLabelNode]); | ||
| }, [ | ||
| shouldShowInsideLabel, | ||
| shouldShowCompactLabel, | ||
| label, | ||
| labelColor, | ||
| labelFont, | ||
| styles?.controlLabelNode, | ||
| ]); | ||
|
|
||
| const valueAlignment = useMemo( | ||
| () => (align === 'end' ? 'flex-end' : align === 'center' ? 'center' : 'flex-start'), | ||
|
|
@@ -255,10 +292,14 @@ export const DefaultSelectControlComponent = memo( | |
| disabled={disabled || option.disabled} | ||
| invertColorScheme={false} | ||
| maxWidth={200} | ||
| onPress={(event) => { | ||
| event?.stopPropagation(); | ||
| onChange?.(option.value as ValueType); | ||
| }} | ||
| onPress={ | ||
| isInteractionBlocked | ||
| ? undefined | ||
| : (event) => { | ||
| event?.stopPropagation(); | ||
| onChange?.(option.value as ValueType); | ||
| } | ||
| } | ||
| > | ||
| {option.label ?? option.description ?? option.value ?? ''} | ||
| </InputChip> | ||
|
|
@@ -294,6 +335,7 @@ export const DefaultSelectControlComponent = memo( | |
| removeSelectedOptionAccessibilityLabel, | ||
| disabled, | ||
| onChange, | ||
| isInteractionBlocked, | ||
| ]); | ||
|
|
||
| // onBlur/onFocus on ViewProps allow null returns but TouchableOpacity's onBlur/onFocus props do not. | ||
|
|
@@ -302,14 +344,14 @@ export const DefaultSelectControlComponent = memo( | |
| () => ( | ||
| <TouchableOpacity | ||
| ref={ref} | ||
| accessibilityHint={accessibilityHint} | ||
| accessibilityLabel={computedControlAccessibilityLabel} | ||
| accessibilityRole="button" | ||
| disabled={disabled} | ||
| onBlur={onBlur ?? undefined} | ||
| onFocus={onFocus ?? undefined} | ||
| onPress={() => setOpen((s) => !s)} | ||
| onPress={handleToggleOpen} | ||
| style={[{ flexGrow: 1 }, styles?.controlInputNode]} | ||
| {...props} | ||
| > | ||
| <HStack | ||
| alignItems="center" | ||
|
|
@@ -367,22 +409,22 @@ export const DefaultSelectControlComponent = memo( | |
| ), | ||
| [ | ||
| ref, | ||
| accessibilityHint, | ||
| computedControlAccessibilityLabel, | ||
| disabled, | ||
| onBlur, | ||
| onFocus, | ||
| styles?.controlInputNode, | ||
| styles?.controlStartNode, | ||
| styles?.controlValueNode, | ||
| props, | ||
| startNode, | ||
| shouldShowCompactLabel, | ||
| shouldShowInsideLabel, | ||
| inlineLabelNode, | ||
| valueAlignment, | ||
| valueNode, | ||
| contentNode, | ||
| setOpen, | ||
| handleToggleOpen, | ||
| ], | ||
| ); | ||
|
|
||
|
|
@@ -391,26 +433,19 @@ export const DefaultSelectControlComponent = memo( | |
| <Pressable | ||
| accessible={customEndNode ? true : false} | ||
| disabled={disabled} | ||
| onPress={() => setOpen((s) => !s)} | ||
| onPress={handleToggleOpen} | ||
| > | ||
| <HStack | ||
| alignItems="center" | ||
| flexGrow={1} | ||
| paddingStart={2} | ||
| style={styles?.controlEndNode} | ||
| > | ||
| {customEndNode ? ( | ||
| customEndNode | ||
| ) : ( | ||
| <AnimatedCaret | ||
| color={!open ? 'fg' : variant ? variantColor[variant] : 'fgPrimary'} | ||
| rotate={open ? 0 : 180} | ||
| /> | ||
| )} | ||
| {customEndNode ? customEndNode : <AnimatedCaret color="fg" rotate={open ? 0 : 180} />} | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nice find! |
||
| </HStack> | ||
| </Pressable> | ||
| ), | ||
| [styles?.controlEndNode, disabled, customEndNode, open, variant, setOpen], | ||
| [styles?.controlEndNode, disabled, customEndNode, open, handleToggleOpen], | ||
| ); | ||
|
|
||
| const inputStackStyles: StyleProp<ViewStyle> = useMemo( | ||
|
|
@@ -426,18 +461,21 @@ export const DefaultSelectControlComponent = memo( | |
| return ( | ||
| <InputStack | ||
| borderFocusedStyle={borderFocusedStyle} | ||
| borderRadius={borderRadius} | ||
| borderStyle={borderUnfocusedStyle} | ||
| borderWidth={borderWidth} | ||
| disabled={disabled} | ||
| endNode={endNode} | ||
| focused={open} | ||
| focused={open && !readOnly} | ||
| focusedBorderWidth={focusedBorderWidth} | ||
| helperTextNode={helperTextNode} | ||
| inputBackground={inputBackground} | ||
| inputNode={inputNode} | ||
| labelNode={labelNode} | ||
| labelVariant={labelVariant} | ||
| onBlur={onBlur} | ||
| onFocus={onFocus} | ||
| style={style} | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We were missing this |
||
| styles={{ input: inputStackStyles }} | ||
| variant={variant} | ||
| {...props} | ||
|
|
||


There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: do you think we should document this behavior when there is business logic/computation involved in the default behavior? Perhaps a brief mention in the jcdocs
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good call, let me update JSDocs