feat: Implement enhanced car color selection component
- Replaced CarColorInput with a new CarColorSelect component - Added color selection with visual color indicators - Implemented custom color input option - Updated locales with color selection translations - Improved type safety in helper file
This commit is contained in:
parent
87a4dcefdd
commit
0b9b2f4dbc
@ -26,6 +26,17 @@
|
|||||||
"dry-wash.order-create.form.washing-location-field.label": "Where is the car located?",
|
"dry-wash.order-create.form.washing-location-field.label": "Where is the car located?",
|
||||||
"dry-wash.order-create.form.washing-location-field.placeholder": "Enter the address or select on the map",
|
"dry-wash.order-create.form.washing-location-field.placeholder": "Enter the address or select on the map",
|
||||||
"dry-wash.order-create.form.washing-location-field.help": "For example, 55.754364, 48.743295 Universitetskaya Street, 1, Innopolis, Verkhneuslonsky district, Republic of Tatarstan (Tatarstan), 420500",
|
"dry-wash.order-create.form.washing-location-field.help": "For example, 55.754364, 48.743295 Universitetskaya Street, 1, Innopolis, Verkhneuslonsky district, Republic of Tatarstan (Tatarstan), 420500",
|
||||||
|
"dry-wash.order-create.car-color-select.placeholder": "Input color",
|
||||||
|
"dry-wash.order-create.car-color-select.custom": "Custom",
|
||||||
|
"dry-wash.order-create.car-color-select.custom-label": "Custom:",
|
||||||
|
"dry-wash.order-create.car-color-select.colors.white": "White",
|
||||||
|
"dry-wash.order-create.car-color-select.colors.black": "Black",
|
||||||
|
"dry-wash.order-create.car-color-select.colors.silver": "Silver",
|
||||||
|
"dry-wash.order-create.car-color-select.colors.gray": "Gray",
|
||||||
|
"dry-wash.order-create.car-color-select.colors.beige-brown": "Beige Brown",
|
||||||
|
"dry-wash.order-create.car-color-select.colors.red": "Red",
|
||||||
|
"dry-wash.order-create.car-color-select.colors.blue": "Blue",
|
||||||
|
"dry-wash.order-create.car-color-select.colors.green": "Green",
|
||||||
"dry-wash.order-create.car-body-select.placeholder": "Not specified",
|
"dry-wash.order-create.car-body-select.placeholder": "Not specified",
|
||||||
"dry-wash.order-create.car-body-select.options.sedan": "Sedan",
|
"dry-wash.order-create.car-body-select.options.sedan": "Sedan",
|
||||||
"dry-wash.order-create.car-body-select.options.hatchback" : "Hatchback",
|
"dry-wash.order-create.car-body-select.options.hatchback" : "Hatchback",
|
||||||
|
@ -81,6 +81,17 @@
|
|||||||
"dry-wash.order-create.form.washing-location-field.label": "Где находится автомобиль?",
|
"dry-wash.order-create.form.washing-location-field.label": "Где находится автомобиль?",
|
||||||
"dry-wash.order-create.form.washing-location-field.placeholder": "Введите адрес или выберите на карте",
|
"dry-wash.order-create.form.washing-location-field.placeholder": "Введите адрес или выберите на карте",
|
||||||
"dry-wash.order-create.form.washing-location-field.help": "Например, 55.754364, 48.743295 Университетская улица, 1, Иннополис, Верхнеуслонский район, Республика Татарстан (Татарстан), 420500",
|
"dry-wash.order-create.form.washing-location-field.help": "Например, 55.754364, 48.743295 Университетская улица, 1, Иннополис, Верхнеуслонский район, Республика Татарстан (Татарстан), 420500",
|
||||||
|
"dry-wash.order-create.car-color-select.placeholder": "Введите цвет",
|
||||||
|
"dry-wash.order-create.car-color-select.custom": "Другой",
|
||||||
|
"dry-wash.order-create.car-color-select.custom-label": "Другой:",
|
||||||
|
"dry-wash.order-create.car-color-select.colors.white": "Белый",
|
||||||
|
"dry-wash.order-create.car-color-select.colors.black": "Черный",
|
||||||
|
"dry-wash.order-create.car-color-select.colors.silver": "Серебристый",
|
||||||
|
"dry-wash.order-create.car-color-select.colors.gray": "Серый",
|
||||||
|
"dry-wash.order-create.car-color-select.colors.beige-brown": "Бежево-коричневый",
|
||||||
|
"dry-wash.order-create.car-color-select.colors.red": "Красный",
|
||||||
|
"dry-wash.order-create.car-color-select.colors.blue": "Синий",
|
||||||
|
"dry-wash.order-create.car-color-select.colors.green": "Зеленый",
|
||||||
"dry-wash.order-create.car-body-select.placeholder": "Не указан",
|
"dry-wash.order-create.car-body-select.placeholder": "Не указан",
|
||||||
"dry-wash.order-create.car-body-select.options.sedan": "Седан",
|
"dry-wash.order-create.car-body-select.options.sedan": "Седан",
|
||||||
"dry-wash.order-create.car-body-select.options.hatchback": "Хэтчбек",
|
"dry-wash.order-create.car-body-select.options.hatchback": "Хэтчбек",
|
||||||
|
@ -1,23 +0,0 @@
|
|||||||
import React, { forwardRef, useId } from 'react';
|
|
||||||
import { Input, InputProps } from '@chakra-ui/react';
|
|
||||||
|
|
||||||
import { CAR_COLORS } from './helper';
|
|
||||||
|
|
||||||
export const CarColorInput = forwardRef<HTMLInputElement, InputProps>(
|
|
||||||
function CarColorInput(props, ref) {
|
|
||||||
const listId = useId();
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<Input ref={ref} list={listId} {...props} />
|
|
||||||
<datalist id={listId}>
|
|
||||||
{CAR_COLORS.map(({ code, name }) => (
|
|
||||||
<option key={code} label={name} value={code}>{name}</option>
|
|
||||||
))}
|
|
||||||
</datalist>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
// todo: add option color visual indication
|
|
162
src/components/order-form/form/car-color/car-color-select.tsx
Normal file
162
src/components/order-form/form/car-color/car-color-select.tsx
Normal file
@ -0,0 +1,162 @@
|
|||||||
|
import React, { forwardRef, useState } from 'react';
|
||||||
|
import {
|
||||||
|
Input,
|
||||||
|
Box,
|
||||||
|
Stack,
|
||||||
|
Text,
|
||||||
|
Flex,
|
||||||
|
} from '@chakra-ui/react';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
|
||||||
|
import { CAR_COLORS } from './helper';
|
||||||
|
|
||||||
|
interface CarColorSelectProps {
|
||||||
|
value?: string;
|
||||||
|
onChange?: (event: React.ChangeEvent<HTMLInputElement>) => void;
|
||||||
|
name?: string;
|
||||||
|
isInvalid?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const CarColorSelect = forwardRef<HTMLInputElement, CarColorSelectProps>(
|
||||||
|
function CarColorSelect(props) {
|
||||||
|
const [customColor, setCustomColor] = useState('');
|
||||||
|
const [isCustom, setIsCustom] = useState(false);
|
||||||
|
|
||||||
|
const handleColorChange = (value: string) => {
|
||||||
|
if (value === 'custom') {
|
||||||
|
setIsCustom(true);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
setIsCustom(false);
|
||||||
|
props.onChange?.({
|
||||||
|
target: { value },
|
||||||
|
} as React.ChangeEvent<HTMLInputElement>);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleCustomColorChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||||
|
const value = e.target.value;
|
||||||
|
setCustomColor(value);
|
||||||
|
props.onChange?.({
|
||||||
|
target: { value },
|
||||||
|
} as React.ChangeEvent<HTMLInputElement>);
|
||||||
|
};
|
||||||
|
|
||||||
|
const { t } = useTranslation('~', {
|
||||||
|
keyPrefix: 'dry-wash.order-create.car-color-select',
|
||||||
|
});
|
||||||
|
|
||||||
|
const currentValue = isCustom ? 'custom' : props.value;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Stack spacing={4} width="100%">
|
||||||
|
<Flex gap={3} wrap="nowrap" overflowX="auto" pb={2}>
|
||||||
|
{CAR_COLORS.map(({ name, code }) => (
|
||||||
|
<Box
|
||||||
|
key={name}
|
||||||
|
flexShrink={0}
|
||||||
|
as="button"
|
||||||
|
type="button"
|
||||||
|
onClick={() => handleColorChange(name)}
|
||||||
|
>
|
||||||
|
<Flex
|
||||||
|
align="center"
|
||||||
|
gap={2}
|
||||||
|
p={2}
|
||||||
|
borderRadius="full"
|
||||||
|
borderWidth="2px"
|
||||||
|
borderColor={currentValue === name ? 'primary.500' : 'gray.200'}
|
||||||
|
bg={currentValue === name ? 'primary.50' : 'white'}
|
||||||
|
_hover={{
|
||||||
|
borderColor: 'primary.500',
|
||||||
|
bg: currentValue === name ? 'primary.50' : 'gray.50'
|
||||||
|
}}
|
||||||
|
minW={currentValue === name ? '120px' : 'auto'}
|
||||||
|
h="48px"
|
||||||
|
justify="center"
|
||||||
|
transition="all 0.2s"
|
||||||
|
>
|
||||||
|
<Flex align="center" gap={2}>
|
||||||
|
<Box
|
||||||
|
w="32px"
|
||||||
|
h="32px"
|
||||||
|
borderRadius="full"
|
||||||
|
bg={code}
|
||||||
|
border="1px"
|
||||||
|
borderColor={currentValue === name ? 'primary.500' : 'gray.200'}
|
||||||
|
transition="all 0.2s"
|
||||||
|
boxShadow={currentValue === name ? 'sm' : 'none'}
|
||||||
|
/>
|
||||||
|
{currentValue === name && (
|
||||||
|
<Text fontSize="xs" color="primary.700" fontWeight="medium">
|
||||||
|
{t(`colors.${name}`)}
|
||||||
|
</Text>
|
||||||
|
)}
|
||||||
|
</Flex>
|
||||||
|
</Flex>
|
||||||
|
</Box>
|
||||||
|
))}
|
||||||
|
<Box
|
||||||
|
flexShrink={0}
|
||||||
|
as="button"
|
||||||
|
type="button"
|
||||||
|
onClick={() => handleColorChange('custom')}
|
||||||
|
>
|
||||||
|
<Flex
|
||||||
|
align="center"
|
||||||
|
gap={2}
|
||||||
|
p={2}
|
||||||
|
borderRadius="full"
|
||||||
|
borderWidth="2px"
|
||||||
|
borderColor={isCustom ? 'primary.500' : 'gray.200'}
|
||||||
|
bg={isCustom ? 'primary.50' : 'white'}
|
||||||
|
_hover={{
|
||||||
|
borderColor: 'primary.500',
|
||||||
|
bg: isCustom ? 'primary.50' : 'gray.50'
|
||||||
|
}}
|
||||||
|
minW={isCustom ? '200px' : 'auto'}
|
||||||
|
h="48px"
|
||||||
|
justify="center"
|
||||||
|
transition="all 0.2s"
|
||||||
|
>
|
||||||
|
{isCustom ? (
|
||||||
|
<Flex gap={2} align="center">
|
||||||
|
<Text fontSize="xs" color="primary.700" fontWeight="medium">
|
||||||
|
{t('custom-label')}
|
||||||
|
</Text>
|
||||||
|
<Input
|
||||||
|
size="sm"
|
||||||
|
width="120px"
|
||||||
|
value={customColor}
|
||||||
|
onChange={handleCustomColorChange}
|
||||||
|
placeholder={t('placeholder')}
|
||||||
|
onClick={(e) => e.stopPropagation()}
|
||||||
|
borderColor="primary.200"
|
||||||
|
_focus={{
|
||||||
|
borderColor: 'primary.500',
|
||||||
|
boxShadow: '0 0 0 1px var(--chakra-colors-primary-500)'
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Flex>
|
||||||
|
) : (
|
||||||
|
<Flex align="center" gap={2}>
|
||||||
|
<Box
|
||||||
|
w="32px"
|
||||||
|
h="32px"
|
||||||
|
borderRadius="full"
|
||||||
|
bg="gray.100"
|
||||||
|
border="1px"
|
||||||
|
borderColor="gray.200"
|
||||||
|
transition="all 0.2s"
|
||||||
|
/>
|
||||||
|
<Text fontSize="xs" color="gray.500">
|
||||||
|
{t('custom')}
|
||||||
|
</Text>
|
||||||
|
</Flex>
|
||||||
|
)}
|
||||||
|
</Flex>
|
||||||
|
</Box>
|
||||||
|
</Flex>
|
||||||
|
</Stack>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
@ -1,4 +1,4 @@
|
|||||||
export const CAR_COLORS: Record<'name' | 'code', string>[] = [
|
export const CAR_COLORS = [
|
||||||
{
|
{
|
||||||
name: 'white',
|
name: 'white',
|
||||||
code: '#ffffff'
|
code: '#ffffff'
|
||||||
@ -31,4 +31,4 @@ export const CAR_COLORS: Record<'name' | 'code', string>[] = [
|
|||||||
name: 'green',
|
name: 'green',
|
||||||
code: '#078d51'
|
code: '#078d51'
|
||||||
},
|
},
|
||||||
];
|
] as const satisfies { name: string; code: string }[];
|
@ -1 +1 @@
|
|||||||
export { CarColorInput } from './car-color-input';
|
export { CarColorSelect } from './car-color-select';
|
@ -4,7 +4,6 @@ import { useTranslation } from 'react-i18next';
|
|||||||
import { Box, Flex, FormControl, FormLabel, VStack } from '@chakra-ui/react';
|
import { Box, Flex, FormControl, FormLabel, VStack } from '@chakra-ui/react';
|
||||||
|
|
||||||
import { CarBodySelect } from './car-body';
|
import { CarBodySelect } from './car-body';
|
||||||
import { CarColorInput } from './car-color';
|
|
||||||
import { CarNumberInput } from './car-number';
|
import { CarNumberInput } from './car-number';
|
||||||
import { FormInputField, FormControllerField } from './field';
|
import { FormInputField, FormControllerField } from './field';
|
||||||
import { OrderFormProps, OrderFormValues } from './types';
|
import { OrderFormProps, OrderFormValues } from './types';
|
||||||
@ -18,6 +17,7 @@ import {
|
|||||||
StringLocation,
|
StringLocation,
|
||||||
YMapsProvider,
|
YMapsProvider,
|
||||||
} from './location';
|
} from './location';
|
||||||
|
import { CarColorSelect } from './car-color';
|
||||||
|
|
||||||
export const OrderForm = ({ onSubmit, loading, ...props }: OrderFormProps) => {
|
export const OrderForm = ({ onSubmit, loading, ...props }: OrderFormProps) => {
|
||||||
const {
|
const {
|
||||||
@ -72,7 +72,7 @@ export const OrderForm = ({ onSubmit, loading, ...props }: OrderFormProps) => {
|
|||||||
name='carColor'
|
name='carColor'
|
||||||
label={t('car-color-field.label')}
|
label={t('car-color-field.label')}
|
||||||
errors={errors}
|
errors={errors}
|
||||||
Input={CarColorInput}
|
Input={CarColorSelect}
|
||||||
/>
|
/>
|
||||||
<FormInputField
|
<FormInputField
|
||||||
control={control}
|
control={control}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user