@snack-uikit/fields 0.19.0 → 0.19.2

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.
Files changed (38) hide show
  1. package/CHANGELOG.md +24 -0
  2. package/README.md +406 -31
  3. package/dist/components/FieldDate/FieldDate.js +4 -2
  4. package/dist/components/FieldDecorator/FieldDecorator.d.ts +1 -1
  5. package/dist/components/FieldDecorator/FieldDecorator.js +4 -3
  6. package/dist/components/FieldSecure/FieldSecure.js +3 -1
  7. package/dist/components/FieldSelect/FieldSelectMultiple.d.ts +2 -2
  8. package/dist/components/FieldSelect/FieldSelectMultiple.js +4 -2
  9. package/dist/components/FieldSelect/FieldSelectSingle.d.ts +2 -2
  10. package/dist/components/FieldSelect/FieldSelectSingle.js +4 -2
  11. package/dist/components/FieldSelect/types.d.ts +2 -2
  12. package/dist/components/FieldSlider/FieldSlider.js +63 -19
  13. package/dist/components/FieldSlider/helpers/getClosestMark.d.ts +2 -2
  14. package/dist/components/FieldSlider/helpers/getClosestMark.js +3 -3
  15. package/dist/components/FieldSlider/helpers/index.d.ts +1 -0
  16. package/dist/components/FieldSlider/helpers/index.js +1 -0
  17. package/dist/components/FieldSlider/helpers/isMarkObject.d.ts +8 -0
  18. package/dist/components/FieldSlider/helpers/isMarkObject.js +3 -0
  19. package/dist/components/FieldStepper/FieldStepper.js +3 -1
  20. package/dist/components/FieldText/FieldText.js +3 -1
  21. package/dist/components/FieldTextArea/FieldTextArea.js +3 -1
  22. package/dist/utils/getValidationState.d.ts +5 -0
  23. package/dist/utils/getValidationState.js +4 -0
  24. package/package.json +5 -5
  25. package/src/components/FieldDate/FieldDate.tsx +4 -2
  26. package/src/components/FieldDecorator/FieldDecorator.tsx +4 -3
  27. package/src/components/FieldSecure/FieldSecure.tsx +5 -2
  28. package/src/components/FieldSelect/FieldSelectMultiple.tsx +5 -2
  29. package/src/components/FieldSelect/FieldSelectSingle.tsx +5 -2
  30. package/src/components/FieldSelect/types.ts +2 -2
  31. package/src/components/FieldSlider/FieldSlider.tsx +93 -19
  32. package/src/components/FieldSlider/helpers/getClosestMark.ts +7 -3
  33. package/src/components/FieldSlider/helpers/index.ts +1 -0
  34. package/src/components/FieldSlider/helpers/isMarkObject.ts +13 -0
  35. package/src/components/FieldStepper/FieldStepper.tsx +5 -2
  36. package/src/components/FieldText/FieldText.tsx +4 -2
  37. package/src/components/FieldTextArea/FieldTextArea.tsx +5 -2
  38. package/src/utils/getValidationState.ts +6 -0
@@ -21,6 +21,7 @@ import { extractSupportProps } from '@snack-uikit/utils';
21
21
  import { CONTAINER_VARIANT, VALIDATION_STATE } from '../../constants';
22
22
  import { FieldContainerPrivate } from '../../helperComponents';
23
23
  import { useCopyButton } from '../../hooks';
24
+ import { getValidationState } from '../../utils/getValidationState';
24
25
  import { FieldDecorator } from '../FieldDecorator';
25
26
  import { DEFAULT_LOCALE, SlotKey } from './constants';
26
27
  import { useDateField } from './hooks';
@@ -45,6 +46,7 @@ export const FieldDate = forwardRef((_a, ref) => {
45
46
  const showAdditionalButton = Boolean(valueProp && !disabled);
46
47
  const showClearButton = showClearButtonProp && showAdditionalButton && !readonly;
47
48
  const showCopyButton = showCopyButtonProp && showAdditionalButton && readonly;
49
+ const fieldValidationState = getValidationState({ validationState, error });
48
50
  const checkForLeavingFocus = useCallback((event) => {
49
51
  if (event.key === 'ArrowDown') {
50
52
  setPickerAutofocus(true);
@@ -133,7 +135,7 @@ export const FieldDate = forwardRef((_a, ref) => {
133
135
  e.stopPropagation();
134
136
  }
135
137
  }, [dateInputClickHandler, isOpen]);
136
- return (_jsx(FieldDecorator, Object.assign({ className: className, label: label, labelTooltip: labelTooltip, labelTooltipPlacement: labelTooltipPlacement, labelFor: id, required: required, hint: hint, disabled: disabled, readonly: readonly, showHintIcon: showHintIcon, size: size, error: error, validationState: validationState }, extractSupportProps(rest), { children: _jsx(Dropdown, Object.assign({ trigger: 'click', triggerClassName: styles.triggerClassName, widthStrategy: 'gte' }, (readonly || disabled
138
+ return (_jsx(FieldDecorator, Object.assign({ className: className, label: label, labelTooltip: labelTooltip, labelTooltipPlacement: labelTooltipPlacement, labelFor: id, required: required, hint: hint, disabled: disabled, readonly: readonly, showHintIcon: showHintIcon, size: size, error: error, validationState: fieldValidationState }, extractSupportProps(rest), { children: _jsx(Dropdown, Object.assign({ trigger: 'click', triggerClassName: styles.triggerClassName, widthStrategy: 'gte' }, (readonly || disabled
137
139
  ? { open: false }
138
140
  : {
139
141
  open: showDropList,
@@ -143,5 +145,5 @@ export const FieldDate = forwardRef((_a, ref) => {
143
145
  element === null || element === void 0 ? void 0 : element.focus();
144
146
  setPickerAutofocus(false);
145
147
  }
146
- }, onFocusLeave: handleCalendarFocusLeave, locale: locale, "data-test-id": 'field-date__calendar' }) }), children: _jsx(FieldContainerPrivate, { className: styles.container, size: size, validationState: validationState, disabled: disabled, readonly: readonly, variant: CONTAINER_VARIANT.SingleLine, focused: showDropList, inputRef: localRef, postfix: _jsxs(_Fragment, { children: [buttons, _jsx(CalendarSVG, { size: calendarIconSize, className: styles.calendarIcon, "data-size": size })] }), children: _jsx(InputPrivate, { ref: mergeRefs(ref, localRef), "data-size": size, value: value || '', placeholder: mask, onChange: handleChange, onFocus: inputHandlers.onFocus, onMouseDown: inputHandlers.onMouseDown, onBlur: onBlur, onKeyDown: handleInputKeyDown, onClick: onClick, disabled: disabled, readonly: readonly, tabIndex: inputTabIndex, type: 'text', id: id, name: name, "data-test-id": 'field-date__input' }) }) })) })));
148
+ }, onFocusLeave: handleCalendarFocusLeave, locale: locale, "data-test-id": 'field-date__calendar' }) }), children: _jsx(FieldContainerPrivate, { className: styles.container, size: size, validationState: fieldValidationState, disabled: disabled, readonly: readonly, variant: CONTAINER_VARIANT.SingleLine, focused: showDropList, inputRef: localRef, postfix: _jsxs(_Fragment, { children: [buttons, _jsx(CalendarSVG, { size: calendarIconSize, className: styles.calendarIcon, "data-size": size })] }), children: _jsx(InputPrivate, { ref: mergeRefs(ref, localRef), "data-size": size, value: value || '', placeholder: mask, onChange: handleChange, onFocus: inputHandlers.onFocus, onMouseDown: inputHandlers.onMouseDown, onBlur: onBlur, onKeyDown: handleInputKeyDown, onClick: onClick, disabled: disabled, readonly: readonly, tabIndex: inputTabIndex, type: 'text', id: id, name: name, "data-test-id": 'field-date__input' }) }) })) })));
147
149
  });
@@ -12,4 +12,4 @@ export type FieldDecoratorProps = WithSupportProps<{
12
12
  readonly?: boolean;
13
13
  error?: string;
14
14
  } & HeaderProps & FooterProps>;
15
- export declare function FieldDecorator({ children, className, label, labelTooltip, required, labelFor, length, hint, disabled, readonly, showHintIcon, labelTooltipPlacement, size, error, validationState: validationStateProp, ...rest }: FieldDecoratorProps): import("react/jsx-runtime").JSX.Element;
15
+ export declare function FieldDecorator({ children, className, label, labelTooltip, required, labelFor, length, hint, disabled, readonly, showHintIcon, labelTooltipPlacement, size, error, validationState, ...rest }: FieldDecoratorProps): import("react/jsx-runtime").JSX.Element;
@@ -14,12 +14,13 @@ import cn from 'classnames';
14
14
  import { SIZE } from '@snack-uikit/input-private';
15
15
  import { extractSupportProps } from '@snack-uikit/utils';
16
16
  import { VALIDATION_STATE } from '../../constants';
17
+ import { getValidationState } from '../../utils/getValidationState';
17
18
  import { Footer } from './Footer';
18
19
  import { Header } from './Header';
19
20
  import styles from './styles.module.css';
20
21
  export function FieldDecorator(_a) {
21
- var { children, className, label, labelTooltip, required, labelFor, length, hint, disabled, readonly, showHintIcon, labelTooltipPlacement, size = SIZE.S, error, validationState: validationStateProp = VALIDATION_STATE.Default } = _a, rest = __rest(_a, ["children", "className", "label", "labelTooltip", "required", "labelFor", "length", "hint", "disabled", "readonly", "showHintIcon", "labelTooltipPlacement", "size", "error", "validationState"]);
22
+ var { children, className, label, labelTooltip, required, labelFor, length, hint, disabled, readonly, showHintIcon, labelTooltipPlacement, size = SIZE.S, error, validationState = VALIDATION_STATE.Default } = _a, rest = __rest(_a, ["children", "className", "label", "labelTooltip", "required", "labelFor", "length", "hint", "disabled", "readonly", "showHintIcon", "labelTooltipPlacement", "size", "error", "validationState"]);
22
23
  const isFieldActive = !disabled && !readonly;
23
- const validationState = error ? VALIDATION_STATE.Error : validationStateProp;
24
- return (_jsxs("div", Object.assign({ className: cn(styles.decorator, className) }, extractSupportProps(rest), { "data-size": size, children: [label && (_jsx(Header, { labelTooltipPlacement: labelTooltipPlacement, required: required, label: label, labelTooltip: labelTooltip, labelFor: labelFor, size: size })), children, _jsx(Footer, { length: isFieldActive ? length : undefined, hint: error || hint, showHintIcon: showHintIcon, size: size, validationState: isFieldActive ? validationState : VALIDATION_STATE.Default })] })));
24
+ const fieldValidationState = getValidationState({ validationState, error });
25
+ return (_jsxs("div", Object.assign({ className: cn(styles.decorator, className) }, extractSupportProps(rest), { "data-size": size, children: [label && (_jsx(Header, { labelTooltipPlacement: labelTooltipPlacement, required: required, label: label, labelTooltip: labelTooltip, labelFor: labelFor, size: size })), children, _jsx(Footer, { length: isFieldActive ? length : undefined, hint: error || hint, showHintIcon: showHintIcon, size: size, validationState: isFieldActive ? fieldValidationState : VALIDATION_STATE.Default })] })));
25
26
  }
@@ -17,6 +17,7 @@ import { extractSupportProps } from '@snack-uikit/utils';
17
17
  import { CONTAINER_VARIANT, VALIDATION_STATE } from '../../constants';
18
18
  import { FieldContainerPrivate } from '../../helperComponents';
19
19
  import { useCopyButton, useHideButton, useValueControl } from '../../hooks';
20
+ import { getValidationState } from '../../utils/getValidationState';
20
21
  import { FieldDecorator } from '../FieldDecorator';
21
22
  export const FieldSecure = forwardRef((_a, ref) => {
22
23
  var { id, name, value: valueProp, placeholder, maxLength, disabled = false, readonly = false, showCopyButton: showCopyButtonProp = true, allowMoreThanMaxLength = false, hidden: hiddenProp, onHiddenChange, showHintIcon, onChange: onChangeProp, onFocus, onBlur, className, label, labelTooltip, labelTooltipPlacement, required = false, hint, size = SIZE.S, validationState = VALIDATION_STATE.Default, prefixIcon, error } = _a, rest = __rest(_a, ["id", "name", "value", "placeholder", "maxLength", "disabled", "readonly", "showCopyButton", "allowMoreThanMaxLength", "hidden", "onHiddenChange", "showHintIcon", "onChange", "onFocus", "onBlur", "className", "label", "labelTooltip", "labelTooltipPlacement", "required", "hint", "size", "validationState", "prefixIcon", "error"]);
@@ -35,6 +36,7 @@ export const FieldSecure = forwardRef((_a, ref) => {
35
36
  });
36
37
  const showCopyButton = showCopyButtonProp && Boolean(value) && readonly && !disabled;
37
38
  const showHideButton = !(readonly && !value);
39
+ const fieldValidationState = getValidationState({ validationState, error });
38
40
  const toggleHidden = () => {
39
41
  setHidden(!hidden);
40
42
  if (!readonly) {
@@ -53,5 +55,5 @@ export const FieldSecure = forwardRef((_a, ref) => {
53
55
  readonly,
54
56
  submitKeys: ['Enter', 'Space', 'Tab'],
55
57
  });
56
- return (_jsx(FieldDecorator, Object.assign({ className: className, label: label, labelTooltip: labelTooltip, labelTooltipPlacement: labelTooltipPlacement, labelFor: id, required: required, length: maxLength ? { max: maxLength, current: value.length } : undefined, hint: hint, disabled: disabled, readonly: readonly, showHintIcon: showHintIcon, size: size, error: error, validationState: validationState }, extractSupportProps(rest), { children: _jsx(FieldContainerPrivate, { size: size, validationState: validationState, disabled: disabled, readonly: readonly, variant: CONTAINER_VARIANT.SingleLine, inputRef: localRef, prefix: prefixIcon, postfix: buttons, children: _jsx(InputPrivate, { ref: mergeRefs(ref, localRef), "data-size": size, value: value, onChange: onChange, onFocus: onFocus, onBlur: onBlur, onKeyDown: onInputKeyDown, tabIndex: inputTabIndex, placeholder: placeholder, disabled: disabled, readonly: readonly, type: hidden ? 'password' : 'text', maxLength: allowMoreThanMaxLength ? undefined : maxLength || undefined, id: id, name: name, "data-test-id": 'field-secure__input' }) }) })));
58
+ return (_jsx(FieldDecorator, Object.assign({ className: className, label: label, labelTooltip: labelTooltip, labelTooltipPlacement: labelTooltipPlacement, labelFor: id, required: required, length: maxLength ? { max: maxLength, current: value.length } : undefined, hint: hint, disabled: disabled, readonly: readonly, showHintIcon: showHintIcon, size: size, error: error, validationState: fieldValidationState }, extractSupportProps(rest), { children: _jsx(FieldContainerPrivate, { size: size, validationState: fieldValidationState, disabled: disabled, readonly: readonly, variant: CONTAINER_VARIANT.SingleLine, inputRef: localRef, prefix: prefixIcon, postfix: buttons, children: _jsx(InputPrivate, { ref: mergeRefs(ref, localRef), "data-size": size, value: value, onChange: onChange, onFocus: onFocus, onBlur: onBlur, onKeyDown: onInputKeyDown, tabIndex: inputTabIndex, placeholder: placeholder, disabled: disabled, readonly: readonly, type: hidden ? 'password' : 'text', maxLength: allowMoreThanMaxLength ? undefined : maxLength || undefined, id: id, name: name, "data-test-id": 'field-secure__input' }) }) })));
57
59
  });
@@ -9,8 +9,8 @@ export declare const FieldSelectMultiple: import("react").ForwardRefExoticCompon
9
9
  'data-test-id'?: string | undefined;
10
10
  } & import("react").AriaAttributes & {
11
11
  options: import("./types").OptionProps[];
12
- pinTop: import("./types").OptionProps[];
13
- pinBottom: import("./types").OptionProps[];
12
+ pinTop?: import("./types").OptionProps[] | undefined;
13
+ pinBottom?: import("./types").OptionProps[] | undefined;
14
14
  searchable?: boolean | undefined;
15
15
  showCopyButton?: boolean | undefined;
16
16
  showClearButton?: boolean | undefined;
@@ -19,6 +19,7 @@ import { Tag } from '@snack-uikit/tag';
19
19
  import { extractSupportProps } from '@snack-uikit/utils';
20
20
  import { FieldContainerPrivate } from '../../helperComponents';
21
21
  import { useValueControl } from '../../hooks';
22
+ import { getValidationState } from '../../utils/getValidationState';
22
23
  import { FieldDecorator } from '../FieldDecorator';
23
24
  import { extractFieldDecoratorProps } from '../FieldDecorator/utils';
24
25
  import { useButtons, useHandleDeleteItem, useHandleOnKeyDown, useSearchInput } from './hooks';
@@ -115,7 +116,8 @@ export const FieldSelectMultiple = forwardRef((_a, ref) => {
115
116
  };
116
117
  const fuzzySearch = useFuzzySearch(items);
117
118
  const result = autocomplete || !searchable || prevInputValue.current === inputValue ? items : fuzzySearch(inputValue);
118
- return (_jsx(FieldDecorator, Object.assign({}, extractSupportProps(rest), extractFieldDecoratorProps(rest), { labelFor: id, size: size, validationState: validationState, children: _jsx(Droplist, Object.assign({}, extractListProps(rest), { items: result, triggerElemRef: localRef, selection: {
119
+ const fieldValidationState = getValidationState({ validationState, error: rest.error });
120
+ return (_jsx(FieldDecorator, Object.assign({}, extractSupportProps(rest), extractFieldDecoratorProps(rest), { labelFor: id, size: size, validationState: fieldValidationState, children: _jsx(Droplist, Object.assign({}, extractListProps(rest), { items: result, triggerElemRef: localRef, selection: {
119
121
  mode: 'multiple',
120
122
  value: value,
121
123
  onChange: value => {
@@ -124,7 +126,7 @@ export const FieldSelectMultiple = forwardRef((_a, ref) => {
124
126
  },
125
127
  }, dataFiltered: (_b = rest.dataFiltered) !== null && _b !== void 0 ? _b : Boolean(inputValue.length), size: size, open: !disabled && !readonly && open, onOpenChange: handleOpenChange, children: ({ onKeyDown }) => {
126
128
  var _a, _b, _c, _d;
127
- return (_jsx(FieldContainerPrivate, { className: cn(styles.container, styles.tagContainer), validationState: validationState, disabled: disabled, readonly: readonly, focused: open, variant: 'single-line-container', inputRef: localRef, size: size, prefix: prefixIcon, children: _jsxs(_Fragment, { children: [_jsxs("div", { className: styles.contentWrapper, ref: contentRef, children: [selectedItems &&
129
+ return (_jsx(FieldContainerPrivate, { className: cn(styles.container, styles.tagContainer), validationState: fieldValidationState, disabled: disabled, readonly: readonly, focused: open, variant: 'single-line-container', inputRef: localRef, size: size, prefix: prefixIcon, children: _jsxs(_Fragment, { children: [_jsxs("div", { className: styles.contentWrapper, ref: contentRef, children: [selectedItems &&
128
130
  selectedItems.map(option => {
129
131
  var _a;
130
132
  return (_jsx(Tag, { size: size === 'l' ? 's' : 'xs', tabIndex: -1, label: selectedOptionFormatter(option), appearance: (_a = option.appearance) !== null && _a !== void 0 ? _a : 'neutral', onDelete: !option.disabled && !disabled && !readonly ? handleItemDelete(option) : undefined }, option.id));
@@ -7,8 +7,8 @@ export declare const FieldSelectSingle: import("react").ForwardRefExoticComponen
7
7
  'data-test-id'?: string | undefined;
8
8
  } & import("react").AriaAttributes & {
9
9
  options: import("./types").OptionProps[];
10
- pinTop: import("./types").OptionProps[];
11
- pinBottom: import("./types").OptionProps[];
10
+ pinTop?: import("./types").OptionProps[] | undefined;
11
+ pinBottom?: import("./types").OptionProps[] | undefined;
12
12
  searchable?: boolean | undefined;
13
13
  showCopyButton?: boolean | undefined;
14
14
  showClearButton?: boolean | undefined;
@@ -18,6 +18,7 @@ import { Droplist } from '@snack-uikit/list';
18
18
  import { extractSupportProps } from '@snack-uikit/utils';
19
19
  import { FieldContainerPrivate } from '../../helperComponents';
20
20
  import { useValueControl } from '../../hooks';
21
+ import { getValidationState } from '../../utils/getValidationState';
21
22
  import { FieldDecorator } from '../FieldDecorator';
22
23
  import { extractFieldDecoratorProps } from '../FieldDecorator/utils';
23
24
  import { useButtons, useHandleOnKeyDown, useSearchInput } from './hooks';
@@ -122,11 +123,12 @@ export const FieldSelectSingle = forwardRef((_a, ref) => {
122
123
  const result = autocomplete || !searchable || selectedOptionFormatter(selectedItem) === inputValue
123
124
  ? items
124
125
  : fuzzySearch(inputValue);
125
- return (_jsx(FieldDecorator, Object.assign({}, extractSupportProps(rest), extractFieldDecoratorProps(rest), { validationState: validationState, required: required, readonly: readonly, labelFor: id, disabled: disabled, size: size, children: _jsx(Droplist, Object.assign({}, extractListProps(rest), { items: result, selection: {
126
+ const fieldValidationState = getValidationState({ validationState, error: rest.error });
127
+ return (_jsx(FieldDecorator, Object.assign({}, extractSupportProps(rest), extractFieldDecoratorProps(rest), { validationState: fieldValidationState, required: required, readonly: readonly, labelFor: id, disabled: disabled, size: size, children: _jsx(Droplist, Object.assign({}, extractListProps(rest), { items: result, selection: {
126
128
  mode: 'single',
127
129
  value: value,
128
130
  onChange: handleSelectionChange,
129
- }, size: size, open: open, onOpenChange: handleOpenChange, triggerElemRef: localRef, children: ({ onKeyDown }) => (_jsxs(FieldContainerPrivate, { className: styles.container, validationState: validationState, disabled: disabled, readonly: readonly, focused: open, variant: 'single-line-container', inputRef: localRef, size: size, prefix: prefixIcon, children: [_jsx(InputPrivate, { id: id, name: name, type: 'text', disabled: disabled, placeholder: placeholder, ref: mergeRefs(ref, localRef), onChange: searchable ? setInputValue : undefined, value: searchable ? inputValue : selectedOptionFormatter(selectedItem), readonly: readonly, "data-test-id": 'field-select__input', onKeyDown: handleOnKeyDown(onKeyDown), onBlur: handleBlur, className: cn({
131
+ }, size: size, open: open, onOpenChange: handleOpenChange, triggerElemRef: localRef, children: ({ onKeyDown }) => (_jsxs(FieldContainerPrivate, { className: styles.container, validationState: fieldValidationState, disabled: disabled, readonly: readonly, focused: open, variant: 'single-line-container', inputRef: localRef, size: size, prefix: prefixIcon, children: [_jsx(InputPrivate, { id: id, name: name, type: 'text', disabled: disabled, placeholder: placeholder, ref: mergeRefs(ref, localRef), onChange: searchable ? setInputValue : undefined, value: searchable ? inputValue : selectedOptionFormatter(selectedItem), readonly: readonly, "data-test-id": 'field-select__input', onKeyDown: handleOnKeyDown(onKeyDown), onBlur: handleBlur, className: cn({
130
132
  [styles.readonlyCursor]: !searchable,
131
133
  }) }), _jsxs("div", { className: styles.postfix, children: [buttons, _jsx(ArrowIcon, { size: arrowIconSize, className: styles.arrowIcon })] })] })) })) })));
132
134
  });
@@ -37,8 +37,8 @@ export type FieldSelectPrivateProps = InputProps & WrapperProps & {
37
37
  };
38
38
  type FiledSelectCommonProps = WithSupportProps<{
39
39
  options: OptionProps[];
40
- pinTop: OptionProps[];
41
- pinBottom: OptionProps[];
40
+ pinTop?: OptionProps[];
41
+ pinBottom?: OptionProps[];
42
42
  searchable?: boolean;
43
43
  /** Отображение кнопки Копировать для поля (актуально только для `readonly = true`) */
44
44
  showCopyButton?: boolean;
@@ -11,7 +11,7 @@ var __rest = (this && this.__rest) || function (s, e) {
11
11
  };
12
12
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
13
13
  import mergeRefs from 'merge-refs';
14
- import { forwardRef, useEffect, useRef, useState } from 'react';
14
+ import { forwardRef, useCallback, useEffect, useMemo, useRef, useState, } from 'react';
15
15
  import { InputPrivate, SIZE } from '@snack-uikit/input-private';
16
16
  import { Slider } from '@snack-uikit/slider';
17
17
  import { extractSupportProps } from '@snack-uikit/utils';
@@ -19,7 +19,7 @@ import { CONTAINER_VARIANT, VALIDATION_STATE } from '../../constants';
19
19
  import { FieldContainerPrivate } from '../../helperComponents';
20
20
  import { useValueControl } from '../../hooks';
21
21
  import { FieldDecorator } from '../FieldDecorator';
22
- import { generateAllowedValues, getClosestMark, getTextFieldValue } from './helpers';
22
+ import { generateAllowedValues, getClosestMark, getTextFieldValue, isMarkObject } from './helpers';
23
23
  import styles from './styles.module.css';
24
24
  const getDefaultValue = (range, min, max, value) => {
25
25
  if (range) {
@@ -32,6 +32,14 @@ const getDefaultValue = (range, min, max, value) => {
32
32
  };
33
33
  export const FieldSlider = forwardRef((_a, ref) => {
34
34
  var { id, name, min, max, step, marks, showScaleBar = true, value: valueProp, range = false, disabled = false, readonly = false, onChange: onChangeProp, onFocus, onBlur, className, label, labelTooltip, labelTooltipPlacement, required, hint, showHintIcon, size = SIZE.S, postfixIcon, textInputFormatter } = _a, rest = __rest(_a, ["id", "name", "min", "max", "step", "marks", "showScaleBar", "value", "range", "disabled", "readonly", "onChange", "onFocus", "onBlur", "className", "label", "labelTooltip", "labelTooltipPlacement", "required", "hint", "showHintIcon", "size", "postfixIcon", "textInputFormatter"]);
35
+ const getMarkValue = useCallback((key) => {
36
+ const mark = marks[key];
37
+ if (isMarkObject(mark)) {
38
+ return mark.label;
39
+ }
40
+ return mark;
41
+ }, [marks]);
42
+ const hasMarksEqualToValues = useMemo(() => Object.keys(marks).every(key => key === getMarkValue(key)), [getMarkValue, marks]);
35
43
  const [value = getDefaultValue(range, min, max, valueProp), onChange] = useValueControl({
36
44
  value: valueProp,
37
45
  defaultValue: getDefaultValue(range, min, max, valueProp),
@@ -40,46 +48,82 @@ export const FieldSlider = forwardRef((_a, ref) => {
40
48
  const [textFieldInputValue, setTextFieldInputValue] = useState(getTextFieldValue(value, textInputFormatter));
41
49
  const localRef = useRef(null);
42
50
  const onTextFieldChange = (textFieldValue) => {
43
- const numValue = Number(textFieldValue);
44
- if (Number.isNaN(numValue)) {
51
+ const numValue = parseInt(textFieldValue);
52
+ if (textFieldValue && Number.isNaN(numValue)) {
45
53
  return;
46
54
  }
47
55
  setTextFieldInputValue(textFieldValue);
48
56
  };
49
- const handleTextValueChange = () => {
50
- const textFieldNumValue = Number(textFieldInputValue);
51
- if (Number.isNaN(textFieldNumValue)) {
57
+ const handleNonEqualMarksSliderChange = (textFieldNumValue) => {
58
+ const handleChange = (key) => {
59
+ setTextFieldInputValue(String(getMarkValue(key)));
60
+ onChange(Number(key));
61
+ };
62
+ const allowedValues = Object.keys(marks).map(key => ({
63
+ key,
64
+ value: parseInt(String(getMarkValue(key))),
65
+ }));
66
+ const suitableEntry = allowedValues.find(entry => entry.value === textFieldNumValue);
67
+ if (suitableEntry) {
68
+ handleChange(suitableEntry.key);
69
+ return;
70
+ }
71
+ const actualMin = parseInt(String(getMarkValue(min)));
72
+ const actualMax = parseInt(String(getMarkValue(max)));
73
+ if (textFieldNumValue < actualMin) {
74
+ handleChange(min);
75
+ return;
76
+ }
77
+ if (textFieldNumValue > actualMax) {
78
+ handleChange(max);
52
79
  return;
53
80
  }
81
+ const { mark } = getClosestMark(textFieldNumValue, allowedValues, mark => mark.value);
82
+ handleChange(mark.key);
83
+ };
84
+ const handleEqualMarksSliderChange = (textFieldNumValue) => {
85
+ const handleChange = (value) => {
86
+ setTextFieldInputValue(String(value));
87
+ onChange(value);
88
+ };
54
89
  if (textFieldNumValue < min) {
55
- setTextFieldInputValue(String(min));
56
- onChange(min);
90
+ handleChange(min);
57
91
  return;
58
92
  }
59
93
  if (textFieldNumValue > max) {
60
- setTextFieldInputValue(String(max));
61
- onChange(max);
94
+ handleChange(max);
62
95
  return;
63
96
  }
64
97
  if (step === null) {
65
98
  const allowedValues = Object.keys(marks).map(Number);
66
99
  if (allowedValues.includes(textFieldNumValue)) {
67
- onChange(textFieldNumValue);
100
+ setTextFieldInputValue(String(textFieldNumValue));
101
+ handleChange(textFieldNumValue);
68
102
  return;
69
103
  }
70
- const { mark } = getClosestMark(textFieldNumValue, allowedValues);
71
- setTextFieldInputValue(String(mark));
72
- onChange(mark);
104
+ const { mark } = getClosestMark(textFieldNumValue, allowedValues, mark => mark);
105
+ handleChange(mark);
73
106
  return;
74
107
  }
75
108
  const allowedValues = generateAllowedValues(min, max, step);
76
109
  if (allowedValues.includes(textFieldNumValue)) {
77
- onChange(textFieldNumValue);
110
+ handleChange(textFieldNumValue);
78
111
  return;
79
112
  }
80
- const { mark } = getClosestMark(textFieldNumValue, allowedValues);
81
- setTextFieldInputValue(String(mark));
82
- onChange(mark);
113
+ const { mark } = getClosestMark(textFieldNumValue, allowedValues, mark => mark);
114
+ handleChange(mark);
115
+ };
116
+ const handleTextValueChange = () => {
117
+ const textFieldNumValue = parseInt(textFieldInputValue);
118
+ if (Number.isNaN(textFieldNumValue)) {
119
+ return;
120
+ }
121
+ if (hasMarksEqualToValues) {
122
+ handleEqualMarksSliderChange(textFieldNumValue);
123
+ }
124
+ else {
125
+ handleNonEqualMarksSliderChange(textFieldNumValue);
126
+ }
83
127
  };
84
128
  const onTextFieldBlur = (e) => {
85
129
  onBlur === null || onBlur === void 0 ? void 0 : onBlur(e);
@@ -1,4 +1,4 @@
1
- export declare const getClosestMark: (value: number, marks: number[]) => {
1
+ export declare const getClosestMark: <T>(value: number, marks: T[], getMarkValue: (value: T) => number) => {
2
2
  lowestDiff: number;
3
- mark: number;
3
+ mark: T;
4
4
  };
@@ -1,6 +1,6 @@
1
1
  const getDiff = (value, mark) => Math.abs(mark - value);
2
- export const getClosestMark = (value, marks) => marks.reduce((accResult, mark) => {
3
- const diff = getDiff(value, mark);
2
+ export const getClosestMark = (value, marks, getMarkValue) => marks.reduce((accResult, mark) => {
3
+ const diff = getDiff(value, getMarkValue(mark));
4
4
  if (diff < accResult.lowestDiff) {
5
5
  return {
6
6
  lowestDiff: diff,
@@ -9,6 +9,6 @@ export const getClosestMark = (value, marks) => marks.reduce((accResult, mark) =
9
9
  }
10
10
  return accResult;
11
11
  }, {
12
- lowestDiff: getDiff(value, marks[0]),
12
+ lowestDiff: getDiff(value, getMarkValue(marks[0])),
13
13
  mark: marks[0],
14
14
  });
@@ -1,3 +1,4 @@
1
1
  export * from './getTextFieldValue';
2
2
  export * from './getClosestMark';
3
3
  export * from './generateAllowedValues';
4
+ export * from './isMarkObject';
@@ -1,3 +1,4 @@
1
1
  export * from './getTextFieldValue';
2
2
  export * from './getClosestMark';
3
3
  export * from './generateAllowedValues';
4
+ export * from './isMarkObject';
@@ -0,0 +1,8 @@
1
+ import { ReactNode } from 'react';
2
+ import { SliderProps as SliderComponentProps } from '@snack-uikit/slider';
3
+ type Marks = NonNullable<SliderComponentProps['marks']>;
4
+ type MarkObject = {
5
+ label: ReactNode;
6
+ };
7
+ export declare function isMarkObject(mark: Marks[keyof Marks]): mark is MarkObject;
8
+ export {};
@@ -0,0 +1,3 @@
1
+ export function isMarkObject(mark) {
2
+ return Boolean(mark && typeof mark === 'object' && 'label' in mark);
3
+ }
@@ -21,6 +21,7 @@ import { extractSupportProps } from '@snack-uikit/utils';
21
21
  import { CONTAINER_VARIANT, VALIDATION_STATE } from '../../constants';
22
22
  import { FieldContainerPrivate } from '../../helperComponents';
23
23
  import { useValueControl } from '../../hooks';
24
+ import { getValidationState } from '../../utils/getValidationState';
24
25
  import { FieldDecorator } from '../FieldDecorator';
25
26
  import styles from './styles.module.css';
26
27
  const TOOLTIP_TIMEOUT = 2000;
@@ -49,6 +50,7 @@ export const FieldStepper = forwardRef((_a, ref) => {
49
50
  const plusButtonRef = useRef(null);
50
51
  const isMinusButtonDisabled = (typeof min === 'number' && value <= min) || readonly || disabled;
51
52
  const isPlusButtonDisabled = (typeof max === 'number' && value >= max) || readonly || disabled;
53
+ const fieldValidationState = getValidationState({ validationState, error });
52
54
  const adjustValue = (value) => {
53
55
  setValue(value);
54
56
  setTooltipOpen(true);
@@ -122,5 +124,5 @@ export const FieldStepper = forwardRef((_a, ref) => {
122
124
  (_a = inputRef.current) === null || _a === void 0 ? void 0 : _a.focus();
123
125
  }
124
126
  };
125
- return (_jsx(FieldDecorator, Object.assign({ className: className, label: label, labelTooltip: labelTooltip, labelTooltipPlacement: labelTooltipPlacement, labelFor: id, required: required, hint: hint, disabled: disabled, readonly: readonly, showHintIcon: showHintIcon, size: size, validationState: validationState, error: error }, extractSupportProps(rest), { children: _jsx(Tooltip, { tip: tooltip, open: allowMoreThanLimits ? false : tooltipOpen, "data-test-id": 'field-stepper__limit-tooltip', children: _jsx(FieldContainerPrivate, { size: size, validationState: validationState, disabled: disabled, readonly: readonly, variant: CONTAINER_VARIANT.SingleLine, inputRef: inputRef, prefix: _jsx(ButtonFunction, { tabIndex: -1, ref: minusButtonRef, size: 'xs', className: styles.button, icon: _jsx(MinusSVG, {}), onClick: handleMinusButtonClick, onKeyDown: handleMinusButtonKeyDown, disabled: isMinusButtonDisabled, "data-test-id": 'field-stepper__minus-button' }), postfix: _jsx(ButtonFunction, { ref: plusButtonRef, tabIndex: -1, size: 'xs', className: styles.button, icon: _jsx(PlusSVG, {}), onClick: handlePlusButtonClick, onKeyDown: handlePlusButtonKeyDown, disabled: isPlusButtonDisabled, "data-test-id": 'field-stepper__plus-button' }), children: _jsx(InputPrivate, { ref: mergeRefs(ref, inputRef), className: styles.stepper, "data-size": size, value: String(value), tabIndex: 0, onKeyDown: handleInputKeyDown, onChange: handleInputChange, onBlur: handleInputBlur, onFocus: handleInputFocus, disabled: disabled, readonly: readonly, type: 'number', id: id, name: name, min: min, max: max, "data-test-id": 'field-stepper__input' }) }) }) })));
127
+ return (_jsx(FieldDecorator, Object.assign({ className: className, label: label, labelTooltip: labelTooltip, labelTooltipPlacement: labelTooltipPlacement, labelFor: id, required: required, hint: hint, disabled: disabled, readonly: readonly, showHintIcon: showHintIcon, size: size, validationState: fieldValidationState, error: error }, extractSupportProps(rest), { children: _jsx(Tooltip, { tip: tooltip, open: allowMoreThanLimits ? false : tooltipOpen, "data-test-id": 'field-stepper__limit-tooltip', children: _jsx(FieldContainerPrivate, { size: size, validationState: fieldValidationState, disabled: disabled, readonly: readonly, variant: CONTAINER_VARIANT.SingleLine, inputRef: inputRef, prefix: _jsx(ButtonFunction, { tabIndex: -1, ref: minusButtonRef, size: 'xs', className: styles.button, icon: _jsx(MinusSVG, {}), onClick: handleMinusButtonClick, onKeyDown: handleMinusButtonKeyDown, disabled: isMinusButtonDisabled, "data-test-id": 'field-stepper__minus-button' }), postfix: _jsx(ButtonFunction, { ref: plusButtonRef, tabIndex: -1, size: 'xs', className: styles.button, icon: _jsx(PlusSVG, {}), onClick: handlePlusButtonClick, onKeyDown: handlePlusButtonKeyDown, disabled: isPlusButtonDisabled, "data-test-id": 'field-stepper__plus-button' }), children: _jsx(InputPrivate, { ref: mergeRefs(ref, inputRef), className: styles.stepper, "data-size": size, value: String(value), tabIndex: 0, onKeyDown: handleInputKeyDown, onChange: handleInputChange, onBlur: handleInputBlur, onFocus: handleInputFocus, disabled: disabled, readonly: readonly, type: 'number', id: id, name: name, min: min, max: max, "data-test-id": 'field-stepper__input' }) }) }) })));
126
128
  });
@@ -17,6 +17,7 @@ import { extractSupportProps } from '@snack-uikit/utils';
17
17
  import { CONTAINER_VARIANT, VALIDATION_STATE } from '../../constants';
18
18
  import { FieldContainerPrivate } from '../../helperComponents';
19
19
  import { useCopyButton, useValueControl } from '../../hooks';
20
+ import { getValidationState } from '../../utils/getValidationState';
20
21
  import { FieldDecorator } from '../FieldDecorator';
21
22
  export const FieldText = forwardRef((_a, ref) => {
22
23
  var { id, name, value: valueProp, placeholder, maxLength, disabled = false, readonly = false, showCopyButton: showCopyButtonProp = true, showClearButton: showClearButtonProp = true, allowMoreThanMaxLength = false, onChange: onChangeProp, onFocus, onBlur, className, label, labelTooltip, labelTooltipPlacement, required = false, hint, showHintIcon, size = SIZE.S, validationState = VALIDATION_STATE.Default, prefixIcon, error } = _a, rest = __rest(_a, ["id", "name", "value", "placeholder", "maxLength", "disabled", "readonly", "showCopyButton", "showClearButton", "allowMoreThanMaxLength", "onChange", "onFocus", "onBlur", "className", "label", "labelTooltip", "labelTooltipPlacement", "required", "hint", "showHintIcon", "size", "validationState", "prefixIcon", "error"]);
@@ -31,6 +32,7 @@ export const FieldText = forwardRef((_a, ref) => {
31
32
  const showAdditionalButton = Boolean(value && !disabled);
32
33
  const showClearButton = showClearButtonProp && showAdditionalButton && !readonly;
33
34
  const showCopyButton = showCopyButtonProp && showAdditionalButton && readonly;
35
+ const fieldValidationState = getValidationState({ validationState, error });
34
36
  const onClear = () => {
35
37
  var _a;
36
38
  onChange('');
@@ -46,5 +48,5 @@ export const FieldText = forwardRef((_a, ref) => {
46
48
  readonly,
47
49
  submitKeys: ['Enter', 'Space', 'Tab'],
48
50
  });
49
- return (_jsx(FieldDecorator, Object.assign({ className: className, label: label, labelTooltip: labelTooltip, labelTooltipPlacement: labelTooltipPlacement, labelFor: id, required: required, length: maxLength ? { max: maxLength, current: value.length } : undefined, hint: hint, disabled: disabled, readonly: readonly, showHintIcon: showHintIcon, size: size, validationState: validationState, error: error }, extractSupportProps(rest), { children: _jsx(FieldContainerPrivate, { size: size, validationState: validationState, disabled: disabled, readonly: readonly, variant: CONTAINER_VARIANT.SingleLine, inputRef: localRef, prefix: prefixIcon, postfix: buttons, children: _jsx(InputPrivate, { ref: mergeRefs(ref, localRef), "data-size": size, value: value, onChange: onChange, onFocus: onFocus, onBlur: onBlur, tabIndex: inputTabIndex, onKeyDown: onInputKeyDown, placeholder: placeholder, disabled: disabled, readonly: readonly, type: 'text', maxLength: allowMoreThanMaxLength ? undefined : maxLength || undefined, id: id, name: name, "data-test-id": 'field-text__input' }) }) })));
51
+ return (_jsx(FieldDecorator, Object.assign({ className: className, label: label, labelTooltip: labelTooltip, labelTooltipPlacement: labelTooltipPlacement, labelFor: id, required: required, length: maxLength ? { max: maxLength, current: value.length } : undefined, hint: hint, disabled: disabled, readonly: readonly, showHintIcon: showHintIcon, size: size, validationState: fieldValidationState, error: error }, extractSupportProps(rest), { children: _jsx(FieldContainerPrivate, { size: size, validationState: fieldValidationState, disabled: disabled, readonly: readonly, variant: CONTAINER_VARIANT.SingleLine, inputRef: localRef, prefix: prefixIcon, postfix: buttons, children: _jsx(InputPrivate, { ref: mergeRefs(ref, localRef), "data-size": size, value: value, onChange: onChange, onFocus: onFocus, onBlur: onBlur, tabIndex: inputTabIndex, onKeyDown: onInputKeyDown, placeholder: placeholder, disabled: disabled, readonly: readonly, type: 'text', maxLength: allowMoreThanMaxLength ? undefined : maxLength || undefined, id: id, name: name, "data-test-id": 'field-text__input' }) }) })));
50
52
  });
@@ -18,6 +18,7 @@ import { extractSupportProps } from '@snack-uikit/utils';
18
18
  import { CONTAINER_VARIANT, VALIDATION_STATE } from '../../constants';
19
19
  import { FieldContainerPrivate, TextArea } from '../../helperComponents';
20
20
  import { useCopyButton, useValueControl } from '../../hooks';
21
+ import { getValidationState } from '../../utils/getValidationState';
21
22
  import { FieldDecorator } from '../FieldDecorator';
22
23
  import styles from './styles.module.css';
23
24
  export const FieldTextArea = forwardRef((_a, ref) => {
@@ -33,6 +34,7 @@ export const FieldTextArea = forwardRef((_a, ref) => {
33
34
  });
34
35
  const showCopyButton = showCopyButtonProp && Boolean(value) && !disabled && readonly;
35
36
  const showClearButton = showClearButtonProp && Boolean(value) && !disabled && !readonly;
37
+ const fieldValidationState = getValidationState({ validationState, error });
36
38
  const onClear = () => {
37
39
  var _a;
38
40
  onChange('');
@@ -48,5 +50,5 @@ export const FieldTextArea = forwardRef((_a, ref) => {
48
50
  readonly,
49
51
  submitKeys: ['Enter', 'Space', 'Tab'],
50
52
  });
51
- return (_jsx(FieldDecorator, Object.assign({ className: className, label: label, labelTooltip: labelTooltip, labelTooltipPlacement: labelTooltipPlacement, labelFor: id, required: required, length: maxLength ? { max: maxLength, current: value.length } : undefined, hint: hint, disabled: disabled, readonly: readonly, showHintIcon: showHintIcon, size: size, error: error, validationState: validationState }, extractSupportProps(rest), { children: _jsx(FieldContainerPrivate, { className: styles.container, size: size, validationState: validationState, disabled: disabled, readonly: readonly, "data-resizable": isResizable || undefined, variant: CONTAINER_VARIANT.MultiLine, style: { '--max-rows': maxRows }, inputRef: localRef, postfix: _jsx("span", { className: styles.postfix, children: buttons }), children: _jsx(Scroll, { className: styles.scrollContainer, size: 's', barHideStrategy: 'never', resize: isResizable ? 'vertical' : 'none', "data-test-id": 'field-textarea__scroll-area', children: _jsx(TextArea, { className: styles.textarea, "data-size": size, value: value, onChange: onChange, placeholder: placeholder, disabled: disabled, readonly: readonly, id: id, name: name, ref: mergeRefs(ref, localRef), onFocus: onFocus, onBlur: onBlur, onKeyDown: onInputKeyDown, tabIndex: inputTabIndex, maxLength: allowMoreThanMaxLength ? undefined : maxLength || undefined, "data-test-id": 'field-textarea__input' }) }) }) })));
53
+ return (_jsx(FieldDecorator, Object.assign({ className: className, label: label, labelTooltip: labelTooltip, labelTooltipPlacement: labelTooltipPlacement, labelFor: id, required: required, length: maxLength ? { max: maxLength, current: value.length } : undefined, hint: hint, disabled: disabled, readonly: readonly, showHintIcon: showHintIcon, size: size, error: error, validationState: fieldValidationState }, extractSupportProps(rest), { children: _jsx(FieldContainerPrivate, { className: styles.container, size: size, validationState: fieldValidationState, disabled: disabled, readonly: readonly, "data-resizable": isResizable || undefined, variant: CONTAINER_VARIANT.MultiLine, style: { '--max-rows': maxRows }, inputRef: localRef, postfix: _jsx("span", { className: styles.postfix, children: buttons }), children: _jsx(Scroll, { className: styles.scrollContainer, size: 's', barHideStrategy: 'never', resize: isResizable ? 'vertical' : 'none', "data-test-id": 'field-textarea__scroll-area', children: _jsx(TextArea, { className: styles.textarea, "data-size": size, value: value, onChange: onChange, placeholder: placeholder, disabled: disabled, readonly: readonly, id: id, name: name, ref: mergeRefs(ref, localRef), onFocus: onFocus, onBlur: onBlur, onKeyDown: onInputKeyDown, tabIndex: inputTabIndex, maxLength: allowMoreThanMaxLength ? undefined : maxLength || undefined, "data-test-id": 'field-textarea__input' }) }) }) })));
52
54
  });
@@ -0,0 +1,5 @@
1
+ import { ValidationState } from '../types';
2
+ export declare function getValidationState({ validationState, error }: {
3
+ validationState?: ValidationState;
4
+ error?: string;
5
+ }): ValidationState;
@@ -0,0 +1,4 @@
1
+ import { VALIDATION_STATE } from '../constants';
2
+ export function getValidationState({ validationState, error }) {
3
+ return error ? VALIDATION_STATE.Error : validationState !== null && validationState !== void 0 ? validationState : VALIDATION_STATE.Default;
4
+ }
package/package.json CHANGED
@@ -4,7 +4,7 @@
4
4
  "access": "public"
5
5
  },
6
6
  "title": "Fields",
7
- "version": "0.19.0",
7
+ "version": "0.19.2",
8
8
  "sideEffects": [
9
9
  "*.css",
10
10
  "*.woff",
@@ -33,13 +33,13 @@
33
33
  "scripts": {},
34
34
  "dependencies": {
35
35
  "@snack-uikit/button": "0.17.1",
36
- "@snack-uikit/calendar": "0.7.7",
36
+ "@snack-uikit/calendar": "0.7.8",
37
37
  "@snack-uikit/dropdown": "0.2.2",
38
38
  "@snack-uikit/icons": "0.20.1",
39
39
  "@snack-uikit/input-private": "3.1.2",
40
- "@snack-uikit/list": "0.11.1",
40
+ "@snack-uikit/list": "0.11.2",
41
41
  "@snack-uikit/scroll": "0.5.3",
42
- "@snack-uikit/slider": "0.1.8",
42
+ "@snack-uikit/slider": "0.1.9",
43
43
  "@snack-uikit/tag": "0.9.1",
44
44
  "@snack-uikit/tooltip": "0.13.2",
45
45
  "@snack-uikit/truncate-string": "0.4.13",
@@ -58,5 +58,5 @@
58
58
  "peerDependencies": {
59
59
  "@snack-uikit/locale": "*"
60
60
  },
61
- "gitHead": "bf1e1f3cfabccc1ee288c18722e33009bd001798"
61
+ "gitHead": "17bfcf1150776cd35c77d9cca8f45dde97472bf1"
62
62
  }
@@ -30,6 +30,7 @@ import { extractSupportProps, WithSupportProps } from '@snack-uikit/utils';
30
30
  import { CONTAINER_VARIANT, VALIDATION_STATE } from '../../constants';
31
31
  import { FieldContainerPrivate } from '../../helperComponents';
32
32
  import { useCopyButton } from '../../hooks';
33
+ import { getValidationState } from '../../utils/getValidationState';
33
34
  import { FieldDecorator, FieldDecoratorProps } from '../FieldDecorator';
34
35
  import { DEFAULT_LOCALE, SlotKey } from './constants';
35
36
  import { useDateField } from './hooks';
@@ -122,6 +123,7 @@ export const FieldDate = forwardRef<HTMLInputElement, FieldDateProps>(
122
123
  const showAdditionalButton = Boolean(valueProp && !disabled);
123
124
  const showClearButton = showClearButtonProp && showAdditionalButton && !readonly;
124
125
  const showCopyButton = showCopyButtonProp && showAdditionalButton && readonly;
126
+ const fieldValidationState = getValidationState({ validationState, error });
125
127
 
126
128
  const checkForLeavingFocus = useCallback(
127
129
  <T extends HTMLInputElement | HTMLButtonElement>(event: KeyboardEvent<T>) => {
@@ -263,7 +265,7 @@ export const FieldDate = forwardRef<HTMLInputElement, FieldDateProps>(
263
265
  showHintIcon={showHintIcon}
264
266
  size={size}
265
267
  error={error}
266
- validationState={validationState}
268
+ validationState={fieldValidationState}
267
269
  {...extractSupportProps(rest)}
268
270
  >
269
271
  <Dropdown
@@ -300,7 +302,7 @@ export const FieldDate = forwardRef<HTMLInputElement, FieldDateProps>(
300
302
  <FieldContainerPrivate
301
303
  className={styles.container}
302
304
  size={size}
303
- validationState={validationState}
305
+ validationState={fieldValidationState}
304
306
  disabled={disabled}
305
307
  readonly={readonly}
306
308
  variant={CONTAINER_VARIANT.SingleLine}
@@ -5,6 +5,7 @@ import { SIZE } from '@snack-uikit/input-private';
5
5
  import { extractSupportProps, WithSupportProps } from '@snack-uikit/utils';
6
6
 
7
7
  import { VALIDATION_STATE } from '../../constants';
8
+ import { getValidationState } from '../../utils/getValidationState';
8
9
  import { Footer, FooterProps } from './Footer';
9
10
  import { Header, HeaderProps } from './Header';
10
11
  import styles from './styles.module.scss';
@@ -39,11 +40,11 @@ export function FieldDecorator({
39
40
  labelTooltipPlacement,
40
41
  size = SIZE.S,
41
42
  error,
42
- validationState: validationStateProp = VALIDATION_STATE.Default,
43
+ validationState = VALIDATION_STATE.Default,
43
44
  ...rest
44
45
  }: FieldDecoratorProps) {
45
46
  const isFieldActive = !disabled && !readonly;
46
- const validationState = error ? VALIDATION_STATE.Error : validationStateProp;
47
+ const fieldValidationState = getValidationState({ validationState, error });
47
48
 
48
49
  return (
49
50
  <div className={cn(styles.decorator, className)} {...extractSupportProps(rest)} data-size={size}>
@@ -63,7 +64,7 @@ export function FieldDecorator({
63
64
  hint={error || hint}
64
65
  showHintIcon={showHintIcon}
65
66
  size={size}
66
- validationState={isFieldActive ? validationState : VALIDATION_STATE.Default}
67
+ validationState={isFieldActive ? fieldValidationState : VALIDATION_STATE.Default}
67
68
  />
68
69
  </div>
69
70
  );
@@ -14,6 +14,7 @@ import { extractSupportProps, WithSupportProps } from '@snack-uikit/utils';
14
14
  import { CONTAINER_VARIANT, VALIDATION_STATE } from '../../constants';
15
15
  import { FieldContainerPrivate } from '../../helperComponents';
16
16
  import { useCopyButton, useHideButton, useValueControl } from '../../hooks';
17
+ import { getValidationState } from '../../utils/getValidationState';
17
18
  import { FieldDecorator, FieldDecoratorProps } from '../FieldDecorator';
18
19
 
19
20
  type InputProps = Pick<Partial<InputPrivateProps>, 'value' | 'onChange'> &
@@ -96,6 +97,8 @@ export const FieldSecure = forwardRef<HTMLInputElement, FieldSecureProps>(
96
97
  const showCopyButton = showCopyButtonProp && Boolean(value) && readonly && !disabled;
97
98
  const showHideButton = !(readonly && !value);
98
99
 
100
+ const fieldValidationState = getValidationState({ validationState, error });
101
+
99
102
  const toggleHidden = () => {
100
103
  setHidden(!hidden);
101
104
 
@@ -131,12 +134,12 @@ export const FieldSecure = forwardRef<HTMLInputElement, FieldSecureProps>(
131
134
  showHintIcon={showHintIcon}
132
135
  size={size}
133
136
  error={error}
134
- validationState={validationState}
137
+ validationState={fieldValidationState}
135
138
  {...extractSupportProps(rest)}
136
139
  >
137
140
  <FieldContainerPrivate
138
141
  size={size}
139
- validationState={validationState}
142
+ validationState={fieldValidationState}
140
143
  disabled={disabled}
141
144
  readonly={readonly}
142
145
  variant={CONTAINER_VARIANT.SingleLine}