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…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user