diff --git a/packages/plasma-new-hope/src/components/Combobox/ComboboxNew/Combobox.tsx b/packages/plasma-new-hope/src/components/Combobox/ComboboxNew/Combobox.tsx index b2c5efd13a..e32b71017a 100644 --- a/packages/plasma-new-hope/src/components/Combobox/ComboboxNew/Combobox.tsx +++ b/packages/plasma-new-hope/src/components/Combobox/ComboboxNew/Combobox.tsx @@ -1,5 +1,14 @@ -import React, { forwardRef, useState, useReducer, useMemo, createContext, useLayoutEffect, useRef } from 'react'; -import type { ChangeEvent } from 'react'; +import React, { + forwardRef, + useState, + useReducer, + useMemo, + createContext, + useLayoutEffect, + useRef, + useEffect, +} from 'react'; +import type { ChangeEvent, ForwardedRef } from 'react'; import { safeUseId, useForkRef } from '@salutejs/plasma-core'; import { RootProps } from '../../../engines'; @@ -26,19 +35,23 @@ import { Ul, base, StyledArrow, IconArrowWrapper, StyledEmptyState } from './Com import type { ItemContext, ComboboxProps } from './Combobox.types'; import { base as viewCSS } from './variations/_view/base'; import { base as sizeCSS } from './variations/_size/base'; -import type { ItemOptionTransformed } from './ui/Inner/ui/Item/Item.types'; +import type { ItemOption, ItemOptionTransformed } from './ui/Inner/ui/Item/Item.types'; +import { SelectNative } from './ui/SelectNative/SelectNative'; export const Context = createContext({} as ItemContext); /** * Поле ввода с выпадающим списком и возможностью фильтрации и выбора элементов. */ + export const comboboxRoot = (Root: RootProps>) => forwardRef((props, ref) => { const { + name, multiple, value: outerValue, onChange: outerOnChange, + defaultValue, isTargetAmount, targetAmount, items, @@ -67,6 +80,7 @@ export const comboboxRoot = (Root: RootProps initialItemsTransform(items || []), [items]); // Создаем структуры для быстрой работы с деревом @@ -127,12 +141,26 @@ export const comboboxRoot = (Root: RootProps) => { + // Эта функция срабатывает при изменении Combobox и + // при изменении нативного Select для формы (срабатывает только после изменения internalValue и рендера). + const onChange = (newValue: string | Array | ChangeEvent | null) => { + // Условие для отправки изменений наружу if (outerOnChange) { - outerOnChange(newValue as any); + // Условие для отправки если комбобокс используется без формы. + if (!name && (typeof newValue === 'string' || Array.isArray(newValue))) { + outerOnChange(newValue as any); + } + + // Условие для отправки если комбобокс используется с формой. + if (name && typeof newValue === 'object' && !Array.isArray(newValue)) { + outerOnChange(newValue as any); + } } - setInternalValue(newValue); + // Условие для изменения внутреннего значения (только если newValue строка или массив строк). + if (typeof newValue === 'string' || Array.isArray(newValue)) { + setInternalValue(newValue); + } }; const handleClickArrow = () => { @@ -337,8 +365,32 @@ export const comboboxRoot = (Root: RootProps { + if (defaultValue) { + setInternalValue(defaultValue); + } + }, [defaultValue]); + return ( - + + {name && ( + } + /> + )}
( )} inputWrapperRef={referenceRef} value={textValue} onChange={handleTextValueChange} @@ -413,6 +465,7 @@ export const comboboxRoot = (Root: RootProps
    = - | { - multiple?: false; - value?: string; - onChange?: (value: string) => void; - /** - * Если включено - будет выведено общее количество выбранных элементов вместо перечисления. - * @default false - */ - isTargetAmount?: never | false; - /** - * Ручная настройка количества выбранных элементов. Только при isTargetAmount === true. - * @default undefined - */ - targetAmount?: never; - renderValue?: never; - } - | { - multiple: true; - value?: Array; - onChange?: (value: Array) => void; - isTargetAmount?: true; - targetAmount?: number; - /** - * Callback для кастомной настройки значения в селекте. - */ - renderValue?: (item: T) => string; - }; + | ({ name?: never; defaultValue?: never } & ( + | { + multiple?: false; + value?: string; + onChange?: (value: string) => void; + /** + * Если включено - будет выведено общее количество выбранных элементов вместо перечисления. + * @default false + */ + isTargetAmount?: never | false; + /** + * Ручная настройка количества выбранных элементов. Только при isTargetAmount === true. + * @default undefined + */ + targetAmount?: never; + /** + * Callback для кастомной настройки значения в селекте. + */ + renderValue?: never; + } + | { + multiple: true; + value?: string[]; + onChange?: (value: string[]) => void; + isTargetAmount?: true; + targetAmount?: number; + renderValue?: (item: T) => string; + } + )) + | ({ name: string; onChange?: ChangeEventHandler } & ( + | { + multiple?: false; + defaultValue?: string; + value?: never; + isTargetAmount?: never | false; + targetAmount?: never; + renderValue?: never; + } + | { + multiple: true; + defaultValue?: string[]; + value?: never; + isTargetAmount?: true; + targetAmount?: number; + renderValue?: (item: T) => string; + } + )); + +// type VS = (value: string) => void; +// type VSA = (value: string[]) => void; + +// type IsMultiselect = { +// name?: string; +// multiple?: boolean; +// value?: string | string[]; +// defaultValue?: string | string[]; +// onChange?: VS | VSA | ChangeEventHandler; +// isTargetAmount?: boolean; +// targetAmount?: number; +// renderValue?: (item: T) => string; +// }; type ViewStateProps = | { @@ -176,7 +210,7 @@ export type ComboboxProps = BasicProps & ViewStateProps & IsMultiselect & RequiredProps & - Omit, 'value' | 'onChange'>; + Omit, 'value' | 'onChange' | 'name' | 'defaultValue'>; export type FloatingPopoverProps = { target: React.ReactNode | ((ref: React.MutableRefObject) => React.ReactNode); diff --git a/packages/plasma-new-hope/src/components/Combobox/ComboboxNew/ui/SelectNative/SelectNative.styles.ts b/packages/plasma-new-hope/src/components/Combobox/ComboboxNew/ui/SelectNative/SelectNative.styles.ts new file mode 100644 index 0000000000..c5ae9af79b --- /dev/null +++ b/packages/plasma-new-hope/src/components/Combobox/ComboboxNew/ui/SelectNative/SelectNative.styles.ts @@ -0,0 +1,7 @@ +import { styled } from '@linaria/react'; + +import { applyHidden } from '../../../../../mixins'; + +export const SelectHidden = styled.select` + ${applyHidden()}; +`; diff --git a/packages/plasma-new-hope/src/components/Combobox/ComboboxNew/ui/SelectNative/SelectNative.tsx b/packages/plasma-new-hope/src/components/Combobox/ComboboxNew/ui/SelectNative/SelectNative.tsx new file mode 100644 index 0000000000..a878826e4d --- /dev/null +++ b/packages/plasma-new-hope/src/components/Combobox/ComboboxNew/ui/SelectNative/SelectNative.tsx @@ -0,0 +1,59 @@ +import React, { ChangeEvent, forwardRef, useEffect, useRef } from 'react'; +import { useForkRef } from '@salutejs/plasma-core'; + +import { createEvent } from '../../../../../utils'; +import { ComboboxProps } from '../../Combobox.types'; +import { ValueToCheckedMapType } from '../../hooks/getPathMaps'; + +import { SelectHidden } from './SelectNative.styles'; + +type Props = Pick & { + onChange: (value: ChangeEvent | null) => void; + onSetValue: (value: string) => void; + items: ValueToCheckedMapType; +}; + +export const SelectNative = forwardRef( + ({ name, multiple, items, value, onChange, onSetValue }, ref) => { + const values = (multiple ? value : [value]) as string[]; + const selectRef = useRef(null); + const forkRef = useForkRef(selectRef, ref as any); + const options = Array.from(items.keys()); + + useEffect(() => { + const event = createEvent(selectRef); + if (onChange) { + onChange(event); + } + }, [values]); + + useEffect(() => { + if (selectRef.current) { + const valueInit = selectRef.current.value; + const item = options.find((v) => v === valueInit); + + if (item) { + onSetValue(item); + } + } + }, [selectRef]); + + return ( + <> + + + ); + }, +); diff --git a/packages/plasma-new-hope/src/examples/plasma_b2c/components/Combobox/Combobox.stories.tsx b/packages/plasma-new-hope/src/examples/plasma_b2c/components/Combobox/Combobox.stories.tsx index 5c475e3b14..e59d04aebc 100644 --- a/packages/plasma-new-hope/src/examples/plasma_b2c/components/Combobox/Combobox.stories.tsx +++ b/packages/plasma-new-hope/src/examples/plasma_b2c/components/Combobox/Combobox.stories.tsx @@ -353,7 +353,6 @@ const items = [ const SingleStory = (args: StorySelectProps) => { const [value, setValue] = useState(''); - return (
    ( + ref: RefObject, +) => { + if (ref.current) { + const event = new Event('change', { bubbles: true }); + Object.defineProperty(event, 'target', { writable: false, value: ref.current }); + const syntheticEvent = createSyntheticEvent(event) as React.ChangeEvent; + return syntheticEvent; + } + + return null; +}; + +const createSyntheticEvent = (event: E): React.SyntheticEvent => { + let isDefaultPrevented = false; + let isPropagationStopped = false; + const preventDefault = () => { + isDefaultPrevented = true; + event.preventDefault(); + }; + const stopPropagation = () => { + isPropagationStopped = true; + event.stopPropagation(); + }; + return { + nativeEvent: event, + currentTarget: event.currentTarget as EventTarget & T, + target: event.target as EventTarget & T, + bubbles: event.bubbles, + cancelable: event.cancelable, + defaultPrevented: event.defaultPrevented, + eventPhase: event.eventPhase, + isTrusted: event.isTrusted, + preventDefault, + isDefaultPrevented: () => isDefaultPrevented, + stopPropagation, + isPropagationStopped: () => isPropagationStopped, + persist: () => {}, + timeStamp: event.timeStamp, + type: event.type, + }; +}; diff --git a/packages/plasma-new-hope/src/utils/index.ts b/packages/plasma-new-hope/src/utils/index.ts index d11a23626b..d087e66ff7 100644 --- a/packages/plasma-new-hope/src/utils/index.ts +++ b/packages/plasma-new-hope/src/utils/index.ts @@ -7,6 +7,7 @@ export { IS_REACT_18, safeUseId } from './react'; export { isNumber } from './isNumber'; export { mergeRefs, setRefList } from './setRefList'; export { isEmpty } from './isEmpty'; +export { createEvent } from './createEvent'; export * as constants from './constants'; export * from './getPopoverPlacement'; export { noop } from './noop'; diff --git a/scaffold/template-docs/docs/form/Components.mdx b/scaffold/template-docs/docs/form/Components.mdx index 6f53cf0b9b..bfe4dc20d1 100644 --- a/scaffold/template-docs/docs/form/Components.mdx +++ b/scaffold/template-docs/docs/form/Components.mdx @@ -74,3 +74,38 @@ sidebar_position: 4 ] ``` + + +### Combobox + +#### React Hook Form + +Изначальные значения задаются в defaultValue через параметр в компоненте. Возвращаемый и изначальный тип `string`. + +#### Нативная форма + +Изначальные значения задаются в defaultValue через параметр в компоненте. Возвращаемый и изначальный тип `string`. + + +### Combobox Multiple + +#### React Hook Form + +Изначальные значения задаются в defaultValue (`string[]`) через параметр в компоненте. Возвращаемый тип `string[]`. + +#### Нативная форма + +Изначальные значения задаются в defaultValue (`number[]`) через параметр в компоненте. Возвращается количество выбранных элементов с ключем `name` и значением `value`. +``` + +[ + ['textfield': 'test'], + ... + ['combobox': 'test_1'], + ['combobox': 'test_2'], + ... + ['textfield2': 'test'], +] + +``` + diff --git a/scaffold/template-docs/docs/form/NativeForm.mdx b/scaffold/template-docs/docs/form/NativeForm.mdx index 5c934ae7dd..da8ff1fa29 100644 --- a/scaffold/template-docs/docs/form/NativeForm.mdx +++ b/scaffold/template-docs/docs/form/NativeForm.mdx @@ -107,8 +107,9 @@ export function App() { /> ))} - + + diff --git a/scaffold/template-docs/docs/form/ReactHookForm.mdx b/scaffold/template-docs/docs/form/ReactHookForm.mdx index cee3befeda..be6ddc3549 100644 --- a/scaffold/template-docs/docs/form/ReactHookForm.mdx +++ b/scaffold/template-docs/docs/form/ReactHookForm.mdx @@ -105,6 +105,8 @@ export function App() { + + ); diff --git a/website/plasma-b2c-docs/docs/form/Components.mdx b/website/plasma-b2c-docs/docs/form/Components.mdx index 6f53cf0b9b..f9c92d24d3 100644 --- a/website/plasma-b2c-docs/docs/form/Components.mdx +++ b/website/plasma-b2c-docs/docs/form/Components.mdx @@ -74,3 +74,37 @@ sidebar_position: 4 ] ``` + + +### Combobox + +#### React Hook Form + +Изначальные значения задаются в defaultValue через параметр в компоненте. Возвращаемый и изначальный тип `string`. + +#### Нативная форма + +Изначальные значения задаются в defaultValue через параметр в компоненте. Возвращаемый и изначальный тип `string`. + + +### Combobox Multiple + +#### React Hook Form + +Изначальные значения задаются в defaultValue (`string[]`) через параметр в компоненте. Возвращаемый тип `string[]`. + +#### Нативная форма + +Изначальные значения задаются в defaultValue (`number[]`) через параметр в компоненте. Возвращается количество выбранных элементов с ключем `name` и значением `value`. +``` + +[ + ['textfield': 'test'], + ... + ['combobox': 'test_1'], + ['combobox': 'test_2'], + ... + ['textfield2': 'test'], +] + +``` \ No newline at end of file diff --git a/website/plasma-b2c-docs/docs/form/NativeForm.mdx b/website/plasma-b2c-docs/docs/form/NativeForm.mdx index ad6e259f25..b4603274e4 100644 --- a/website/plasma-b2c-docs/docs/form/NativeForm.mdx +++ b/website/plasma-b2c-docs/docs/form/NativeForm.mdx @@ -14,7 +14,7 @@ import TabItem from '@theme/TabItem'; ```tsx live import React from 'react'; -import { Button, TextField, TextArea, Checkbox, Switch, Radiobox, RadioGroup, Slider, DatePicker, Autocomplite } from "@salutejs/plasma-b2c"; +import { Button, TextField, TextArea, Checkbox, Switch, Radiobox, RadioGroup, Slider, DatePicker, Combobox, Autocomplete } from "@salutejs/plasma-b2c"; export function App() { const langName = 'language'; @@ -108,9 +108,10 @@ export function App() { /> ))} - + + ); diff --git a/website/plasma-b2c-docs/docs/form/ReactHookForm.mdx b/website/plasma-b2c-docs/docs/form/ReactHookForm.mdx index de3a7b9303..f229446c1c 100644 --- a/website/plasma-b2c-docs/docs/form/ReactHookForm.mdx +++ b/website/plasma-b2c-docs/docs/form/ReactHookForm.mdx @@ -14,7 +14,7 @@ sidebar_position: 2 import { useForm } from 'react-hook-form'; import React from 'react'; -import { Button, TextField, TextArea, Checkbox, Switch, Radiobox, RadioGroup, Slider, DatePicker, DatePickerRange, Autocomplete } from "@salutejs/plasma-b2c"; +import { Button, TextField, TextArea, Checkbox, Switch, Radiobox, RadioGroup, Slider, DatePicker, DatePickerRange, Combobox, Autocomplete } from "@salutejs/plasma-b2c"; export function App() { const langName = 'language'; @@ -105,6 +105,8 @@ export function App() { + + ); diff --git a/website/plasma-web-docs/docs/form/Components.mdx b/website/plasma-web-docs/docs/form/Components.mdx index 6f53cf0b9b..bfe4dc20d1 100644 --- a/website/plasma-web-docs/docs/form/Components.mdx +++ b/website/plasma-web-docs/docs/form/Components.mdx @@ -74,3 +74,38 @@ sidebar_position: 4 ] ``` + + +### Combobox + +#### React Hook Form + +Изначальные значения задаются в defaultValue через параметр в компоненте. Возвращаемый и изначальный тип `string`. + +#### Нативная форма + +Изначальные значения задаются в defaultValue через параметр в компоненте. Возвращаемый и изначальный тип `string`. + + +### Combobox Multiple + +#### React Hook Form + +Изначальные значения задаются в defaultValue (`string[]`) через параметр в компоненте. Возвращаемый тип `string[]`. + +#### Нативная форма + +Изначальные значения задаются в defaultValue (`number[]`) через параметр в компоненте. Возвращается количество выбранных элементов с ключем `name` и значением `value`. +``` + +[ + ['textfield': 'test'], + ... + ['combobox': 'test_1'], + ['combobox': 'test_2'], + ... + ['textfield2': 'test'], +] + +``` + diff --git a/website/plasma-web-docs/docs/form/NativeForm.mdx b/website/plasma-web-docs/docs/form/NativeForm.mdx index 71bd58bbfb..96768fa0de 100644 --- a/website/plasma-web-docs/docs/form/NativeForm.mdx +++ b/website/plasma-web-docs/docs/form/NativeForm.mdx @@ -107,9 +107,10 @@ export function App() { /> ))} - + + ); diff --git a/website/plasma-web-docs/docs/form/ReactHookForm.mdx b/website/plasma-web-docs/docs/form/ReactHookForm.mdx index bbe422368a..bd78a5139c 100644 --- a/website/plasma-web-docs/docs/form/ReactHookForm.mdx +++ b/website/plasma-web-docs/docs/form/ReactHookForm.mdx @@ -105,6 +105,8 @@ export function App() { + + ); diff --git a/website/sdds-cs-docs/docs/form/Components.mdx b/website/sdds-cs-docs/docs/form/Components.mdx index 6f53cf0b9b..bfe4dc20d1 100644 --- a/website/sdds-cs-docs/docs/form/Components.mdx +++ b/website/sdds-cs-docs/docs/form/Components.mdx @@ -74,3 +74,38 @@ sidebar_position: 4 ] ``` + + +### Combobox + +#### React Hook Form + +Изначальные значения задаются в defaultValue через параметр в компоненте. Возвращаемый и изначальный тип `string`. + +#### Нативная форма + +Изначальные значения задаются в defaultValue через параметр в компоненте. Возвращаемый и изначальный тип `string`. + + +### Combobox Multiple + +#### React Hook Form + +Изначальные значения задаются в defaultValue (`string[]`) через параметр в компоненте. Возвращаемый тип `string[]`. + +#### Нативная форма + +Изначальные значения задаются в defaultValue (`number[]`) через параметр в компоненте. Возвращается количество выбранных элементов с ключем `name` и значением `value`. +``` + +[ + ['textfield': 'test'], + ... + ['combobox': 'test_1'], + ['combobox': 'test_2'], + ... + ['textfield2': 'test'], +] + +``` + diff --git a/website/sdds-cs-docs/docs/form/NativeForm.mdx b/website/sdds-cs-docs/docs/form/NativeForm.mdx index 012a28235d..ada33a4436 100644 --- a/website/sdds-cs-docs/docs/form/NativeForm.mdx +++ b/website/sdds-cs-docs/docs/form/NativeForm.mdx @@ -107,9 +107,10 @@ export function App() { /> ))} - + + ); diff --git a/website/sdds-cs-docs/docs/form/ReactHookForm.mdx b/website/sdds-cs-docs/docs/form/ReactHookForm.mdx index d98e812bf4..00f30f19e1 100644 --- a/website/sdds-cs-docs/docs/form/ReactHookForm.mdx +++ b/website/sdds-cs-docs/docs/form/ReactHookForm.mdx @@ -105,6 +105,8 @@ export function App() { + + ); diff --git a/website/sdds-dfa-docs/docs/form/Components.mdx b/website/sdds-dfa-docs/docs/form/Components.mdx index 6f53cf0b9b..bfe4dc20d1 100644 --- a/website/sdds-dfa-docs/docs/form/Components.mdx +++ b/website/sdds-dfa-docs/docs/form/Components.mdx @@ -74,3 +74,38 @@ sidebar_position: 4 ] ``` + + +### Combobox + +#### React Hook Form + +Изначальные значения задаются в defaultValue через параметр в компоненте. Возвращаемый и изначальный тип `string`. + +#### Нативная форма + +Изначальные значения задаются в defaultValue через параметр в компоненте. Возвращаемый и изначальный тип `string`. + + +### Combobox Multiple + +#### React Hook Form + +Изначальные значения задаются в defaultValue (`string[]`) через параметр в компоненте. Возвращаемый тип `string[]`. + +#### Нативная форма + +Изначальные значения задаются в defaultValue (`number[]`) через параметр в компоненте. Возвращается количество выбранных элементов с ключем `name` и значением `value`. +``` + +[ + ['textfield': 'test'], + ... + ['combobox': 'test_1'], + ['combobox': 'test_2'], + ... + ['textfield2': 'test'], +] + +``` + diff --git a/website/sdds-dfa-docs/docs/form/NativeForm.mdx b/website/sdds-dfa-docs/docs/form/NativeForm.mdx index 5957a7b471..4823b3571b 100644 --- a/website/sdds-dfa-docs/docs/form/NativeForm.mdx +++ b/website/sdds-dfa-docs/docs/form/NativeForm.mdx @@ -107,9 +107,10 @@ export function App() { /> ))} - + + ); diff --git a/website/sdds-dfa-docs/docs/form/ReactHookForm.mdx b/website/sdds-dfa-docs/docs/form/ReactHookForm.mdx index 53d3360d73..1052728bcc 100644 --- a/website/sdds-dfa-docs/docs/form/ReactHookForm.mdx +++ b/website/sdds-dfa-docs/docs/form/ReactHookForm.mdx @@ -103,6 +103,8 @@ export function App() { + + ); diff --git a/website/sdds-insol-docs/docs/form/NativeForm.mdx b/website/sdds-insol-docs/docs/form/NativeForm.mdx index 5958abfbd5..1a56f4e4ca 100644 --- a/website/sdds-insol-docs/docs/form/NativeForm.mdx +++ b/website/sdds-insol-docs/docs/form/NativeForm.mdx @@ -107,9 +107,10 @@ export function App() { /> ))} - + + ); diff --git a/website/sdds-insol-docs/docs/form/ReactHookForm.mdx b/website/sdds-insol-docs/docs/form/ReactHookForm.mdx index 66efa6f4fb..5f524ebe75 100644 --- a/website/sdds-insol-docs/docs/form/ReactHookForm.mdx +++ b/website/sdds-insol-docs/docs/form/ReactHookForm.mdx @@ -103,6 +103,8 @@ export function App() { + + ); diff --git a/website/sdds-serv-docs/docs/form/NativeForm.mdx b/website/sdds-serv-docs/docs/form/NativeForm.mdx index 7ae123961a..2df4346ee1 100644 --- a/website/sdds-serv-docs/docs/form/NativeForm.mdx +++ b/website/sdds-serv-docs/docs/form/NativeForm.mdx @@ -107,9 +107,10 @@ export function App() { /> ))} - + + ); diff --git a/website/sdds-serv-docs/docs/form/ReactHookForm.mdx b/website/sdds-serv-docs/docs/form/ReactHookForm.mdx index a0ef0d2c5c..bff8ce11f2 100644 --- a/website/sdds-serv-docs/docs/form/ReactHookForm.mdx +++ b/website/sdds-serv-docs/docs/form/ReactHookForm.mdx @@ -105,6 +105,8 @@ export function App() { + + );