mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-02-12 12:28:42 +00:00
feat(mobile): use native select for mobile setting (#9236)

This commit is contained in:
@@ -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,
|
||||
});
|
||||
|
||||
@@ -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>
|
||||
);
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user