fix: option select (#55) #70
@ -3,6 +3,7 @@ export { default as CrossoverImg } from './crossover.webp';
|
|||||||
export { default as HatchbackImg } from './hatchback.webp';
|
export { default as HatchbackImg } from './hatchback.webp';
|
||||||
export { default as LiftbackImg } from './liftback.webp';
|
export { default as LiftbackImg } from './liftback.webp';
|
||||||
export { default as MinivanImg } from './minivan.webp';
|
export { default as MinivanImg } from './minivan.webp';
|
||||||
|
export { default as OtherImg } from './other.webp';
|
||||||
export { default as PickupImg } from './pickup.webp';
|
export { default as PickupImg } from './pickup.webp';
|
||||||
export { default as SedanImg } from './sedan.webp';
|
export { default as SedanImg } from './sedan.webp';
|
||||||
export { default as SportsCarImg } from './sports-car.webp';
|
export { default as SportsCarImg } from './sports-car.webp';
|
||||||
|
BIN
src/assets/images/car-body-type/other.webp
Normal file
BIN
src/assets/images/car-body-type/other.webp
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.2 KiB |
@ -1,34 +1,38 @@
|
|||||||
import React, { forwardRef, useState } from 'react';
|
import React, { forwardRef } from 'react';
|
||||||
import {
|
import {
|
||||||
Input,
|
Input,
|
||||||
Image,
|
|
||||||
InputProps,
|
|
||||||
Box,
|
Box,
|
||||||
Popover,
|
Popover,
|
||||||
PopoverAnchor,
|
PopoverAnchor,
|
||||||
PopoverContent,
|
PopoverContent,
|
||||||
PopoverBody,
|
PopoverBody,
|
||||||
List,
|
useRadioGroup,
|
||||||
ListItem,
|
Grid,
|
||||||
|
GridItem,
|
||||||
|
UseRadioGroupProps,
|
||||||
|
useDisclosure,
|
||||||
} from '@chakra-ui/react';
|
} from '@chakra-ui/react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
|
|
||||||
import { carBodySelectOptions } from './helper';
|
import { carBodySelectOptions, getInputValue } from './helper';
|
||||||
import { CarBodySelectOption } from './types';
|
import { CarBodyOption } from './option';
|
||||||
|
import { CarBodySelectProps } from './types';
|
||||||
|
|
||||||
export const CarBodySelect = forwardRef<HTMLInputElement, InputProps>(
|
export const CarBodySelect = forwardRef<HTMLInputElement, CarBodySelectProps>(
|
||||||
function CarBodySelect(props, ref) {
|
function CarBodySelect(props, ref) {
|
||||||
const initialOption = carBodySelectOptions.find(({ value }) => value === Number(props.value));
|
const handleOptionClick: UseRadioGroupProps['onChange'] = (value) => {
|
||||||
const [selected, setSelected] = useState<Partial<CarBodySelectOption>>(initialOption);
|
|
||||||
|
|
||||||
const handleOptionClick = (option: CarBodySelectOption) => {
|
|
||||||
setSelected(option);
|
|
||||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
props.onChange(option.value);
|
props.onChange(value);
|
||||||
};
|
};
|
||||||
|
|
||||||
const [isDropdownOpen, setIsDropdownOpen] = useState<boolean>(false);
|
const { value, getRadioProps, getRootProps } = useRadioGroup({
|
||||||
|
defaultValue: props.value,
|
||||||
|
value: props.value,
|
||||||
|
onChange: handleOptionClick,
|
||||||
|
});
|
||||||
|
|
||||||
|
const { isOpen, onOpen, onClose } = useDisclosure();
|
||||||
|
|
||||||
const { t } = useTranslation('~', {
|
const { t } = useTranslation('~', {
|
||||||
keyPrefix: 'dry-wash.order-create.car-body-select',
|
keyPrefix: 'dry-wash.order-create.car-body-select',
|
||||||
@ -37,7 +41,7 @@ export const CarBodySelect = forwardRef<HTMLInputElement, InputProps>(
|
|||||||
return (
|
return (
|
||||||
<Box width='100%'>
|
<Box width='100%'>
|
||||||
<Popover
|
<Popover
|
||||||
isOpen={isDropdownOpen}
|
isOpen={isOpen}
|
||||||
autoFocus={false}
|
autoFocus={false}
|
||||||
placement='bottom-start'
|
placement='bottom-start'
|
||||||
matchWidth
|
matchWidth
|
||||||
@ -46,45 +50,29 @@ export const CarBodySelect = forwardRef<HTMLInputElement, InputProps>(
|
|||||||
<Input
|
<Input
|
||||||
{...props}
|
{...props}
|
||||||
ref={ref}
|
ref={ref}
|
||||||
value={
|
value={getInputValue(Number(value), t) ?? props.value}
|
||||||
selected?.labelTKey
|
|
||||||
? t(`options.${selected.labelTKey}`)
|
|
||||||
: undefined
|
|
||||||
}
|
|
||||||
readOnly
|
readOnly
|
||||||
onClick={() => setIsDropdownOpen(true)}
|
onClick={onOpen}
|
||||||
onBlur={() => setIsDropdownOpen(false)}
|
onBlur={onClose}
|
||||||
placeholder={t('placeholder')}
|
placeholder={t('placeholder')}
|
||||||
/>
|
/>
|
||||||
</PopoverAnchor>
|
</PopoverAnchor>
|
||||||
<PopoverContent width='100%' maxWidth='100%'>
|
<PopoverContent width='100%' maxWidth='100%'>
|
||||||
<PopoverBody border='1px' borderColor='gray.300' p={0}>
|
<PopoverBody border='1px' borderColor='gray.300' p={0}>
|
||||||
<List
|
<Grid
|
||||||
display='grid'
|
templateColumns='repeat(auto-fit, minmax(150px, 1fr))'
|
||||||
gridTemplateColumns='repeat(auto-fit, minmax(150px, 1fr))'
|
{...getRootProps()}
|
||||||
>
|
>
|
||||||
{carBodySelectOptions.map((option) => (
|
{carBodySelectOptions.map(({ value, img, labelTKey }) => (
|
||||||
<ListItem
|
<GridItem key={value}>
|
||||||
key={option.value}
|
<CarBodyOption
|
||||||
display='flex'
|
image={img}
|
||||||
flexDirection='column'
|
label={t(`options.${labelTKey}`)}
|
||||||
justifyContent='flex-end'
|
{...getRadioProps({ value: String(value) })}
|
||||||
alignItems='center'
|
/>
|
||||||
p={2}
|
</GridItem>
|
||||||
cursor='pointer'
|
|
||||||
_hover={{
|
|
||||||
bgColor: 'primary.50',
|
|
||||||
}}
|
|
||||||
_active={{
|
|
||||||
bgColor: 'primary.100',
|
|
||||||
}}
|
|
||||||
onClick={() => handleOptionClick(option)}
|
|
||||||
>
|
|
||||||
<Image src={option.img} />
|
|
||||||
{t(`options.${option.labelTKey}`)}
|
|
||||||
</ListItem>
|
|
||||||
))}
|
))}
|
||||||
</List>
|
</Grid>
|
||||||
</PopoverBody>
|
</PopoverBody>
|
||||||
</PopoverContent>
|
</PopoverContent>
|
||||||
</Popover>
|
</Popover>
|
||||||
|
@ -1,3 +1,6 @@
|
|||||||
|
import { TFunction } from "i18next";
|
||||||
|
import { InputProps } from "@chakra-ui/react";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
CoupeImg,
|
CoupeImg,
|
||||||
CrossoverImg,
|
CrossoverImg,
|
||||||
@ -8,7 +11,8 @@ import {
|
|||||||
SedanImg,
|
SedanImg,
|
||||||
SportsCarImg,
|
SportsCarImg,
|
||||||
StationWagonImg,
|
StationWagonImg,
|
||||||
SuvImg
|
SuvImg,
|
||||||
|
OtherImg
|
||||||
} from "../../../../assets/images";
|
} from "../../../../assets/images";
|
||||||
import { Car } from "../../../../models/landing";
|
import { Car } from "../../../../models/landing";
|
||||||
|
|
||||||
@ -67,6 +71,17 @@ export const carBodySelectOptions: CarBodySelectOption[] = [
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
value: Car.BodyStyle.OTHER,
|
value: Car.BodyStyle.OTHER,
|
||||||
labelTKey: 'other'
|
labelTKey: 'other',
|
||||||
|
img: OtherImg
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
|
export const getInputValue = (value: Car.BodyStyle, t: TFunction<"~", "dry-wash.order-create.car-body-select">): InputProps['value'] => {
|
||||||
|
const { labelTKey } = carBodySelectOptions.find((option) => value === option.value) ?? {};
|
||||||
|
|
||||||
|
if (labelTKey) {
|
||||||
|
return t(`options.${labelTKey}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
};
|
@ -0,0 +1,39 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import {
|
||||||
|
ImageProps,
|
||||||
|
StackProps,
|
||||||
|
Image,
|
||||||
|
useRadio,
|
||||||
|
chakra,
|
||||||
|
Box,
|
||||||
|
UseRadioProps,
|
||||||
|
Flex,
|
||||||
|
} from '@chakra-ui/react';
|
||||||
|
|
||||||
|
import { getPropsByState } from './helper';
|
||||||
|
|
||||||
|
type CarBodyOptionProps = {
|
||||||
|
image: ImageProps['src'];
|
||||||
|
label: StackProps['children'];
|
||||||
|
} & UseRadioProps;
|
||||||
|
|
||||||
|
export const CarBodyOption = ({
|
||||||
|
image,
|
||||||
|
label,
|
||||||
|
...radioProps
|
||||||
|
}: CarBodyOptionProps) => {
|
||||||
|
const { state, getInputProps, getRadioProps, htmlProps, getLabelProps } =
|
||||||
|
useRadio(radioProps);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<chakra.label {...htmlProps} cursor='pointer'>
|
||||||
|
<input {...getInputProps({})} hidden />
|
||||||
|
<Box {...getRadioProps()} p={2} {...getPropsByState(state)}>
|
||||||
|
<Flex direction='column' alignItems='center' {...getLabelProps()}>
|
||||||
|
<Image src={image} rounded={4} />
|
||||||
|
{label}
|
||||||
|
</Flex>
|
||||||
|
</Box>
|
||||||
|
</chakra.label>
|
||||||
|
);
|
||||||
|
};
|
19
src/components/order-form/form/car-body/option/helper.ts
Normal file
19
src/components/order-form/form/car-body/option/helper.ts
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
import {
|
||||||
|
BoxProps,
|
||||||
|
} from '@chakra-ui/react';
|
||||||
|
import { RadioState } from '@chakra-ui/react/dist/types/radio/use-radio';
|
||||||
|
|
||||||
|
export const getPropsByState = ({ isChecked }: RadioState): BoxProps => {
|
||||||
|
if (isChecked) {
|
||||||
|
return {
|
||||||
|
bgColor: 'primary.200',
|
||||||
|
_hover: { bgColor: 'primary.100' },
|
||||||
|
_active: { bgColor: 'primary.300' },
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
_hover: { bgColor: 'primary.50' },
|
||||||
|
_active: { bgColor: 'primary.100' },
|
||||||
|
};
|
||||||
|
};
|
1
src/components/order-form/form/car-body/option/index.ts
Normal file
1
src/components/order-form/form/car-body/option/index.ts
Normal file
@ -0,0 +1 @@
|
|||||||
|
export { CarBodyOption } from './car-body-option';
|
@ -1,3 +1,5 @@
|
|||||||
|
import { InputProps } from "@chakra-ui/react";
|
||||||
|
|
||||||
import { Car } from "../../../../models/landing";
|
import { Car } from "../../../../models/landing";
|
||||||
|
|
||||||
export type CarBodySelectOption = {
|
export type CarBodySelectOption = {
|
||||||
@ -16,3 +18,7 @@ export type CarBodySelectOption = {
|
|||||||
'other';
|
'other';
|
||||||
img?: string;
|
img?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type CarBodySelectProps = {
|
||||||
|
value?: string;
|
||||||
|
} & Pick<InputProps, 'onChange'>;
|
@ -9,6 +9,7 @@ export const defaultValues: Partial<OrderFormValues> = {
|
|||||||
phone: '',
|
phone: '',
|
||||||
carNumber: '',
|
carNumber: '',
|
||||||
carColor: '',
|
carColor: '',
|
||||||
|
carBody: '',
|
||||||
availableDatetimeBegin: '',
|
availableDatetimeBegin: '',
|
||||||
availableDatetimeEnd: '',
|
availableDatetimeEnd: '',
|
||||||
};
|
};
|
||||||
|
Loading…
Reference in New Issue
Block a user