feat(mobile): use native select for mobile setting (#9236)

![CleanShot 2024-12-21 at 12.01.32.gif](https://graphite-user-uploaded-assets-prod.s3.amazonaws.com/LakojjjzZNf6ogjOVwKE/8f29afcc-3541-4081-9f8f-f74e3ba08c4d.gif)
This commit is contained in:
CatsJuice
2024-12-24 03:24:51 +00:00
parent 17c2293986
commit 3a8d90d861
3 changed files with 86 additions and 37 deletions

View File

@@ -6,6 +6,7 @@ export const root = style({
display: 'flex',
alignItems: 'center',
gap: 8,
position: 'relative',
});
export const label = style([
bodyRegular,
@@ -15,3 +16,11 @@ export const icon = style({
fontSize: 24,
color: cssVarV2('icon/primary'),
});
export const nativeSelect = style({
position: 'absolute',
top: 0,
left: 0,
width: '100%',
height: '100%',
opacity: 0,
});

View File

@@ -5,7 +5,9 @@ import {
type CSSProperties,
type HTMLAttributes,
type ReactNode,
useCallback,
useMemo,
useRef,
} from 'react';
import * as styles from './dropdown-select.css';
@@ -28,6 +30,7 @@ export interface SettingDropdownSelectProps<
) => void;
emitValue?: E;
menuOptions?: Omit<MenuProps, 'items' | 'children'>;
native?: boolean;
}
export const SettingDropdownSelect = <
@@ -40,12 +43,26 @@ export const SettingDropdownSelect = <
onChange,
className,
menuOptions,
native = true,
...attrs
}: SettingDropdownSelectProps<V, E>) => {
const selectedItem = useMemo(
() => options.find(opt => opt.value === value),
[options, value]
);
if (native) {
return (
<NativeSettingDropdownSelect
options={options}
value={value}
emitValue={emitValue as any}
onChange={onChange}
{...attrs}
/>
);
}
return (
<MobileMenu
items={options.map(opt => (
@@ -76,3 +93,59 @@ export const SettingDropdownSelect = <
</MobileMenu>
);
};
export const NativeSettingDropdownSelect = <
V extends string = string,
E extends boolean | undefined = true,
>({
options = [],
value,
emitValue = true,
onChange,
className,
...attrs
}: Omit<SettingDropdownSelectProps<V, E>, 'native'>) => {
const nativeSelectRef = useRef<HTMLSelectElement>(null);
const selectedItem = useMemo(
() => options.find(opt => opt.value === value),
[options, value]
);
const handleChange = useCallback(
(e: React.ChangeEvent<HTMLSelectElement>) => {
const value = e.target.value;
const opt = options.find(opt => opt.value === value);
if (emitValue) {
onChange?.(e.target.value as any);
} else {
onChange?.(opt as any);
}
},
[emitValue, onChange, options]
);
return (
<div
data-testid="dropdown-select-trigger"
className={clsx(styles.root, className)}
{...attrs}
>
<span className={styles.label}>{selectedItem?.label ?? ''}</span>
<ArrowDownSmallIcon className={styles.icon} />
<select
className={styles.nativeSelect}
ref={nativeSelectRef}
value={value}
onChange={handleChange}
data-testid="native-dropdown-select-trigger"
>
{options.map(opt => (
<option key={opt.value} value={opt.value}>
{opt.label}
</option>
))}
</select>
</div>
);
};