첫 커밋
This commit is contained in:
80
src/shared/ui/selects/select-template.tsx
Normal file
80
src/shared/ui/selects/select-template.tsx
Normal file
@@ -0,0 +1,80 @@
|
||||
import clsx from 'clsx';
|
||||
import { useEffect, useRef } from 'react';
|
||||
|
||||
export type ElementType<T> = T extends (infer U)[] ? U : never;
|
||||
|
||||
export interface RadioChangeProps<ValueType> {
|
||||
event: React.ChangeEvent<HTMLInputElement>;
|
||||
label?: string;
|
||||
value: ValueType;
|
||||
}
|
||||
export interface SelectTemplateProps<ValueType> {
|
||||
values?: ValueType[];
|
||||
valueKey?: keyof ValueType;
|
||||
labels?: string[];
|
||||
selectedLabel?: string;
|
||||
selectedValue?: string;
|
||||
onRadioChange?: ({ event, label, value }: RadioChangeProps<ValueType>) => void;
|
||||
}
|
||||
|
||||
export const SelectTemplate = ({
|
||||
values = [],
|
||||
valueKey,
|
||||
labels = [],
|
||||
selectedLabel,
|
||||
selectedValue,
|
||||
onRadioChange,
|
||||
}: SelectTemplateProps<any>) => {
|
||||
const refs = useRef<(HTMLLabelElement | null)[]>([]);
|
||||
|
||||
const handleRadioChange = ({ event, label, value }: RadioChangeProps<ElementType<typeof values>>) => {
|
||||
onRadioChange?.({ event, label, value });
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
const scrollIntoView = (array: any[], selected: string | undefined) => {
|
||||
const index = array?.indexOf(selected ?? '');
|
||||
if (index !== -1 && refs.current[index]) {
|
||||
refs.current[index]?.scrollIntoView({ behavior: 'smooth', block: 'center' });
|
||||
}
|
||||
};
|
||||
scrollIntoView(labels, selectedLabel);
|
||||
scrollIntoView(values, selectedValue);
|
||||
}, [selectedLabel, labels, selectedValue, values]);
|
||||
|
||||
return (
|
||||
<div
|
||||
style={{ height: '100%', maxHeight: '50dvh', overflowY: 'auto' }}
|
||||
className="show-scrollbar mx-[24px] pb-[30px] pt-[20px]"
|
||||
>
|
||||
{values?.map((value, index) => {
|
||||
return (
|
||||
<label
|
||||
htmlFor={labels?.[index]}
|
||||
key={labels?.[index]}
|
||||
className={clsx('btm-sheet-btn block h-[52px] text-[15px]', {
|
||||
'selected font-bold text-[#366FC4]':
|
||||
selectedLabel === labels?.[index] ||
|
||||
selectedValue === (valueKey ? values?.[index]?.[valueKey] : values?.[index]),
|
||||
})}
|
||||
>
|
||||
<input
|
||||
className="hidden"
|
||||
type="radio"
|
||||
id={labels?.[index]}
|
||||
// name={name}
|
||||
value={valueKey ? value?.[valueKey] : `${value}`}
|
||||
onChange={(event) => handleRadioChange({
|
||||
event,
|
||||
label: labels?.[index],
|
||||
value
|
||||
})}
|
||||
/>
|
||||
{labels?.[index]}
|
||||
<br />
|
||||
</label>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
71
src/shared/ui/selects/select.tsx
Normal file
71
src/shared/ui/selects/select.tsx
Normal file
@@ -0,0 +1,71 @@
|
||||
/* eslint-disable react-hooks/exhaustive-deps */
|
||||
import { ElementType, Fragment, useEffect, useState } from 'react';
|
||||
import { RegisterOptions, useFormContext, UseFormRegister } from 'react-hook-form';
|
||||
|
||||
import { BottomSheet } from '~/shared/ui/bottom-sheets/bottom-sheet';
|
||||
import { SelectTemplate, RadioChangeProps } from '~/shared/ui/selects/select-template';
|
||||
import { TextInput } from '~/shared/ui/text-input/text-input';
|
||||
|
||||
interface SelectProps {
|
||||
placeholder: string;
|
||||
labels: string[];
|
||||
values: string[];
|
||||
name: string;
|
||||
option?: RegisterOptions<any>;
|
||||
readOnly?: boolean;
|
||||
onClick?: () => void;
|
||||
register: UseFormRegister<any>;
|
||||
classStr?: string;
|
||||
}
|
||||
|
||||
export const Select = ({ placeholder, register, name, option, labels, values, classStr }: SelectProps) => {
|
||||
const [isOpenBottomSheet, setIsOpenBottomSheet] = useState(false);
|
||||
|
||||
const handleCloseBottomSheet = () => {
|
||||
setIsOpenBottomSheet(false);
|
||||
if ((!value || value?.length < 0) && option?.required) {
|
||||
setError(name, { type: 'required', message: option?.required.toString() ?? null });
|
||||
}
|
||||
};
|
||||
|
||||
const { setValue, clearErrors, watch, getValues, setError } = useFormContext();
|
||||
const value = watch(name);
|
||||
|
||||
const defaultValue = getValues(name);
|
||||
const selectedValue = watch(name, defaultValue);
|
||||
const selectedLabel = labels[values.indexOf(selectedValue)];
|
||||
|
||||
const onInputClick = (event: React.MouseEvent<HTMLDivElement, MouseEvent>) => {
|
||||
event.preventDefault();
|
||||
setIsOpenBottomSheet(true);
|
||||
};
|
||||
|
||||
const onRadioChange = ({ event }: RadioChangeProps<ElementType<typeof values>>) => {
|
||||
setValue(name, event.target.value);
|
||||
setIsOpenBottomSheet(false);
|
||||
clearErrors(name);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (value?.length > 0) {
|
||||
setValue(name, selectedValue);
|
||||
}
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<Fragment>
|
||||
<TextInput
|
||||
label={placeholder}
|
||||
{...register(name)}
|
||||
readOnly
|
||||
onClick={onInputClick}
|
||||
selectLabel={selectedLabel}
|
||||
selectMode
|
||||
classStr={classStr}
|
||||
/>
|
||||
<BottomSheet title={placeholder} open={isOpenBottomSheet} onClose={handleCloseBottomSheet}>
|
||||
<SelectTemplate values={values} labels={labels} selectedLabel={selectedLabel} onRadioChange={onRadioChange} />
|
||||
</BottomSheet>
|
||||
</Fragment>
|
||||
);
|
||||
};
|
||||
Reference in New Issue
Block a user