@cloud-ru/ds-fields 1.0.2-preview-ece1f136.0 → 1.0.2-preview-c048fbb9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/components/FieldSelect/FieldSelect.js +4 -14
- package/dist/cjs/components/FieldTextArea/FieldTextArea.js +5 -0
- package/dist/esm/components/FieldSelect/FieldSelect.js +5 -15
- package/dist/esm/components/FieldTextArea/FieldTextArea.js +6 -1
- package/dist/tsconfig.cjs.tsbuildinfo +1 -1
- package/dist/tsconfig.esm.tsbuildinfo +1 -1
- package/package.json +7 -7
- package/src/components/FieldSelect/FieldSelect.tsx +5 -27
- package/src/components/FieldTextArea/FieldTextArea.tsx +8 -0
|
@@ -61,23 +61,13 @@ exports.FieldSelect = (0, react_1.forwardRef)(function FieldSelect(props, ref) {
|
|
|
61
61
|
}, [multiple, props.value, multipleLocal]);
|
|
62
62
|
const effectiveValidationState = (0, react_1.useMemo)(() => (error ? field_decorator_1.VALIDATION_STATE.Error : validationState), [error, validationState]);
|
|
63
63
|
const allItems = (0, react_1.useMemo)(() => [...(pinTop ?? []), ...items, ...(pinBottom ?? [])], [items, pinTop, pinBottom]);
|
|
64
|
-
// Выбранное значение переживает смену `items` (серверный поиск, ленивая подгрузка).
|
|
65
|
-
const seenItems = (0, react_1.useRef)(new Map());
|
|
66
|
-
const resolveItem = (0, react_1.useMemo)(() => {
|
|
67
|
-
for (const item of (0, utils_2.flatten)(allItems)) {
|
|
68
|
-
if (item.id !== undefined) {
|
|
69
|
-
seenItems.current.set(item.id, item);
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
return (id) => (0, utils_2.findItem)(allItems, id) ?? seenItems.current.get(id);
|
|
73
|
-
}, [allItems]);
|
|
74
64
|
const selectedPairs = (0, react_1.useMemo)(() => {
|
|
75
65
|
if (!multiple || multipleValue.length === 0) {
|
|
76
66
|
return [];
|
|
77
67
|
}
|
|
78
68
|
// Неизвестное значение (ещё не загружены опции) показываем по его id, не теряем (паритет с легаси).
|
|
79
69
|
return multipleValue.map(id => {
|
|
80
|
-
const item =
|
|
70
|
+
const item = (0, utils_2.findItem)(allItems, id);
|
|
81
71
|
return {
|
|
82
72
|
id,
|
|
83
73
|
label: item ? (0, utils_2.extractLabel)(item) : String(id),
|
|
@@ -85,14 +75,14 @@ exports.FieldSelect = (0, react_1.forwardRef)(function FieldSelect(props, ref) {
|
|
|
85
75
|
appearance: (0, utils_2.extractAppearance)(item),
|
|
86
76
|
};
|
|
87
77
|
});
|
|
88
|
-
}, [multiple, multipleValue
|
|
78
|
+
}, [allItems, multiple, multipleValue]);
|
|
89
79
|
const formatPair = (0, react_1.useCallback)((pair) => (selectedOptionFormatter ? selectedOptionFormatter(pair) : pair.label), [selectedOptionFormatter]);
|
|
90
80
|
const selectedLabel = (0, react_1.useMemo)(() => {
|
|
91
81
|
if (!multiple) {
|
|
92
82
|
if (singleValue === undefined) {
|
|
93
83
|
return '';
|
|
94
84
|
}
|
|
95
|
-
const item =
|
|
85
|
+
const item = (0, utils_2.findItem)(allItems, singleValue);
|
|
96
86
|
const label = item ? (0, utils_2.extractLabel)(item) : String(singleValue);
|
|
97
87
|
return formatPair({ id: singleValue, label });
|
|
98
88
|
}
|
|
@@ -103,7 +93,7 @@ exports.FieldSelect = (0, react_1.forwardRef)(function FieldSelect(props, ref) {
|
|
|
103
93
|
return props.formatSelected(selectedPairs);
|
|
104
94
|
}
|
|
105
95
|
return selectedPairs.map(formatPair).join(', ');
|
|
106
|
-
}, [multiple, singleValue, selectedPairs, chips, props, formatPair
|
|
96
|
+
}, [allItems, multiple, singleValue, selectedPairs, chips, props, formatPair]);
|
|
107
97
|
// Строка поиска: controlled через `search.value`, иначе локальный state. Auto-sync к выбранному
|
|
108
98
|
// значению делаем через СТАБИЛЬНЫЙ setLocalInput, а не через setInputValue в deps эффекта —
|
|
109
99
|
// иначе нестабильная ссылка сеттера зацикливает эффект (сбрасывала бы значение постоянно).
|
|
@@ -24,6 +24,11 @@ exports.FieldTextArea = (0, react_1.forwardRef)(function FieldTextArea({ label =
|
|
|
24
24
|
const copyButtonRef = (0, react_1.useRef)(null);
|
|
25
25
|
const [focusVisible, setFocusVisible] = (0, react_1.useState)(false);
|
|
26
26
|
const [hover, setHover] = (0, react_1.useState)(false);
|
|
27
|
+
// autosize меряет высоту до того, как ThemeScope навесит классы темы — пересчитываем после mount.
|
|
28
|
+
const [, remeasure] = (0, react_1.useReducer)((tick) => tick + 1, 0);
|
|
29
|
+
(0, react_1.useEffect)(() => {
|
|
30
|
+
remeasure();
|
|
31
|
+
}, []);
|
|
27
32
|
const resolvedAutoFocus = (0, hooks_1.useAdaptiveAutoFocus)(autoFocus, layoutPresets);
|
|
28
33
|
// controlled/uncontrolled: useValueControl держит внутреннее состояние при отсутствии `value`,
|
|
29
34
|
// событие в `onChange` пробрасываем сами (useValueControl передаёт только значение).
|
|
@@ -16,7 +16,7 @@ import { useAdaptiveAutoFocus } from '../../hooks/index.js';
|
|
|
16
16
|
import { copyTextToClipboard, getAcrylicProps, preventSlotMouseDown, stopSlotClickPropagation, toInputSize, useCopyButton, } from '../shared/index.js';
|
|
17
17
|
import fieldStyles from '../shared/styles.module.css';
|
|
18
18
|
import styles from './styles.module.css';
|
|
19
|
-
import { extractAppearance, extractLabel, filterItems, findItem,
|
|
19
|
+
import { extractAppearance, extractLabel, filterItems, findItem, isMultiple, TAG_SIZE_MAP } from './utils.js';
|
|
20
20
|
export const FieldSelect = forwardRef(function FieldSelect(props, ref) {
|
|
21
21
|
const { label = '', labelTooltip, caption, hint, error, validationState = VALIDATION_STATE.Default, showHintIcon, length, required, size = SIZE.M, className, fieldClassName, items, pinTop, pinBottom, placeholder = '', iconBefore, prefix, postfix, disabled, readonly: readOnly, background = true, open: openProp, onOpenChange, placement, widthStrategy = 'eq', searchable = true, search, autocomplete = false, addOptionByEnter = false, resetSearchOnOptionSelection = true, showClearButton = true, showCopyButton = true, onCopyButtonClick, id, name, autoFocus, layoutPresets, onFocus: onFocusProp, onBlur: onBlurProp, enableFuzzySearch = true, selectedOptionFormatter, footer, footerActiveElementsRefs, loading, noDataState, noResultsState, errorDataState, dataError, dataFiltered, virtualized, scrollToSelectedItem, limitedScrollHeight, untouchableScrollbars, closeOnPopstate, onKeyDown: onKeyDownProp, 'data-test-id': dataTestId = TEST_IDS.fieldSelect, } = props;
|
|
22
22
|
const multiple = isMultiple(props);
|
|
@@ -55,23 +55,13 @@ export const FieldSelect = forwardRef(function FieldSelect(props, ref) {
|
|
|
55
55
|
}, [multiple, props.value, multipleLocal]);
|
|
56
56
|
const effectiveValidationState = useMemo(() => (error ? VALIDATION_STATE.Error : validationState), [error, validationState]);
|
|
57
57
|
const allItems = useMemo(() => [...(pinTop ?? []), ...items, ...(pinBottom ?? [])], [items, pinTop, pinBottom]);
|
|
58
|
-
// Выбранное значение переживает смену `items` (серверный поиск, ленивая подгрузка).
|
|
59
|
-
const seenItems = useRef(new Map());
|
|
60
|
-
const resolveItem = useMemo(() => {
|
|
61
|
-
for (const item of flatten(allItems)) {
|
|
62
|
-
if (item.id !== undefined) {
|
|
63
|
-
seenItems.current.set(item.id, item);
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
return (id) => findItem(allItems, id) ?? seenItems.current.get(id);
|
|
67
|
-
}, [allItems]);
|
|
68
58
|
const selectedPairs = useMemo(() => {
|
|
69
59
|
if (!multiple || multipleValue.length === 0) {
|
|
70
60
|
return [];
|
|
71
61
|
}
|
|
72
62
|
// Неизвестное значение (ещё не загружены опции) показываем по его id, не теряем (паритет с легаси).
|
|
73
63
|
return multipleValue.map(id => {
|
|
74
|
-
const item =
|
|
64
|
+
const item = findItem(allItems, id);
|
|
75
65
|
return {
|
|
76
66
|
id,
|
|
77
67
|
label: item ? extractLabel(item) : String(id),
|
|
@@ -79,14 +69,14 @@ export const FieldSelect = forwardRef(function FieldSelect(props, ref) {
|
|
|
79
69
|
appearance: extractAppearance(item),
|
|
80
70
|
};
|
|
81
71
|
});
|
|
82
|
-
}, [multiple, multipleValue
|
|
72
|
+
}, [allItems, multiple, multipleValue]);
|
|
83
73
|
const formatPair = useCallback((pair) => (selectedOptionFormatter ? selectedOptionFormatter(pair) : pair.label), [selectedOptionFormatter]);
|
|
84
74
|
const selectedLabel = useMemo(() => {
|
|
85
75
|
if (!multiple) {
|
|
86
76
|
if (singleValue === undefined) {
|
|
87
77
|
return '';
|
|
88
78
|
}
|
|
89
|
-
const item =
|
|
79
|
+
const item = findItem(allItems, singleValue);
|
|
90
80
|
const label = item ? extractLabel(item) : String(singleValue);
|
|
91
81
|
return formatPair({ id: singleValue, label });
|
|
92
82
|
}
|
|
@@ -97,7 +87,7 @@ export const FieldSelect = forwardRef(function FieldSelect(props, ref) {
|
|
|
97
87
|
return props.formatSelected(selectedPairs);
|
|
98
88
|
}
|
|
99
89
|
return selectedPairs.map(formatPair).join(', ');
|
|
100
|
-
}, [multiple, singleValue, selectedPairs, chips, props, formatPair
|
|
90
|
+
}, [allItems, multiple, singleValue, selectedPairs, chips, props, formatPair]);
|
|
101
91
|
// Строка поиска: controlled через `search.value`, иначе локальный state. Auto-sync к выбранному
|
|
102
92
|
// значению делаем через СТАБИЛЬНЫЙ setLocalInput, а не через setInputValue в deps эффекта —
|
|
103
93
|
// иначе нестабильная ссылка сеттера зацикливает эффект (сбрасывала бы значение постоянно).
|
|
@@ -5,7 +5,7 @@ import { BAR_HIDE_STRATEGY, RESIZE, Scroll, SIZE as SCROLL_SIZE } from '@cloud-r
|
|
|
5
5
|
import { extractSupportProps, useValueControl } from '@cloud-ru/ds-utils';
|
|
6
6
|
import cn from 'classnames';
|
|
7
7
|
import mergeRefs from 'merge-refs';
|
|
8
|
-
import { forwardRef, useCallback, useMemo, useRef, useState, } from 'react';
|
|
8
|
+
import { forwardRef, useCallback, useEffect, useMemo, useReducer, useRef, useState, } from 'react';
|
|
9
9
|
import TextareaAutosize from 'react-textarea-autosize';
|
|
10
10
|
import { TEST_IDS } from '../../constants.js';
|
|
11
11
|
import { useAdaptiveAutoFocus } from '../../hooks/index.js';
|
|
@@ -18,6 +18,11 @@ export const FieldTextArea = forwardRef(function FieldTextArea({ label = '', lab
|
|
|
18
18
|
const copyButtonRef = useRef(null);
|
|
19
19
|
const [focusVisible, setFocusVisible] = useState(false);
|
|
20
20
|
const [hover, setHover] = useState(false);
|
|
21
|
+
// autosize меряет высоту до того, как ThemeScope навесит классы темы — пересчитываем после mount.
|
|
22
|
+
const [, remeasure] = useReducer((tick) => tick + 1, 0);
|
|
23
|
+
useEffect(() => {
|
|
24
|
+
remeasure();
|
|
25
|
+
}, []);
|
|
21
26
|
const resolvedAutoFocus = useAdaptiveAutoFocus(autoFocus, layoutPresets);
|
|
22
27
|
// controlled/uncontrolled: useValueControl держит внутреннее состояние при отсутствии `value`,
|
|
23
28
|
// событие в `onChange` пробрасываем сами (useValueControl передаёт только значение).
|