@snack-uikit/fields 0.19.1 → 0.19.3-preview-34ffedb5.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/CHANGELOG.md +12 -0
- package/README.md +3 -2
- package/dist/components/FieldSecure/FieldSecure.d.ts +2 -0
- package/dist/components/FieldSecure/FieldSecure.js +60 -13
- package/dist/components/FieldSelect/FieldSelectMultiple.d.ts +2 -2
- package/dist/components/FieldSelect/FieldSelectSingle.d.ts +2 -2
- package/dist/components/FieldSelect/types.d.ts +2 -2
- package/dist/helperComponents/ButtonCopyValue/ButtonCopyValue.d.ts +2 -0
- package/dist/helperComponents/ButtonCopyValue/ButtonCopyValue.js +19 -7
- package/dist/hooks/useCopyButton.d.ts +3 -1
- package/dist/hooks/useCopyButton.js +3 -3
- package/package.json +5 -4
- package/src/components/FieldSecure/FieldSecure.tsx +72 -28
- package/src/components/FieldSelect/types.ts +2 -2
- package/src/helperComponents/ButtonCopyValue/ButtonCopyValue.tsx +23 -6
- package/src/hooks/useCopyButton.tsx +20 -3
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,18 @@
|
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
|
4
4
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
|
5
5
|
|
|
6
|
+
## 0.19.2 (2024-04-25)
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
### Bug Fixes
|
|
10
|
+
|
|
11
|
+
* **PDS-0000:** remove required pinTop/pinBottom ([51bbe9f](https://github.com/cloud-ru-tech/snack-uikit/commit/51bbe9f6d79c50bd2aa41128a12b125eb91f625b))
|
|
12
|
+
* **PDS-0000:** set default button type as button ([4ec64de](https://github.com/cloud-ru-tech/snack-uikit/commit/4ec64de508826df4a1e82fd8c75448d9919e563f))
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
|
|
6
18
|
## 0.19.1 (2024-04-24)
|
|
7
19
|
|
|
8
20
|
|
package/README.md
CHANGED
|
@@ -252,6 +252,7 @@ const [isOpen, setIsOpen] = useState(false);
|
|
|
252
252
|
| showCopyButton | `boolean` | - | Отображение кнопки копирования |
|
|
253
253
|
| allowMoreThanMaxLength | `boolean` | - | Можно ли вводить больше разрешённого кол-ва символов |
|
|
254
254
|
| prefixIcon | `ReactElement<any, string \| JSXElementConstructor<any>>` | - | Иконка-префикс для поля |
|
|
255
|
+
| onDataRequest | `() => Promise<void>` | - | Свойство позволяет грузить данные в поле по требованию |
|
|
255
256
|
| value | `string` | - | Значение input |
|
|
256
257
|
| onChange | `(value: string, e?: ChangeEvent<HTMLInputElement>) => void` | - | Колбек смены значения |
|
|
257
258
|
| disabled | `boolean` | - | Является ли поле деактивированным |
|
|
@@ -312,8 +313,6 @@ const [isOpen, setIsOpen] = useState(false);
|
|
|
312
313
|
### Props
|
|
313
314
|
| name | type | default value | description |
|
|
314
315
|
|------|------|---------------|-------------|
|
|
315
|
-
| pinBottom* | `OptionProps[]` | - | |
|
|
316
|
-
| pinTop* | `OptionProps[]` | - | |
|
|
317
316
|
| options* | `OptionProps[]` | - | |
|
|
318
317
|
| disabled | `boolean` | false | Является ли поле деактивированным |
|
|
319
318
|
| readonly | `boolean` | false false | Является ли поле доступным только для чтения |
|
|
@@ -337,6 +336,8 @@ const [isOpen, setIsOpen] = useState(false);
|
|
|
337
336
|
| value | `ItemId \| ItemId[]` | - | Controlled состояние |
|
|
338
337
|
| onChange | `OnChangeHandler<any>` | - | Controlled обработчик измения состояния |
|
|
339
338
|
| defaultValue | `ItemId \| ItemId[]` | - | Начальное состояние |
|
|
339
|
+
| pinTop | `OptionProps[]` | - | |
|
|
340
|
+
| pinBottom | `OptionProps[]` | - | |
|
|
340
341
|
| searchable | `boolean` | - | |
|
|
341
342
|
| showCopyButton | `boolean` | - | Отображение кнопки Копировать для поля (актуально только для `readonly = true`) |
|
|
342
343
|
| showClearButton | `boolean` | true | Отображение кнопки очистки поля |
|
|
@@ -15,6 +15,8 @@ type FieldSecureOwnProps = {
|
|
|
15
15
|
allowMoreThanMaxLength?: boolean;
|
|
16
16
|
/** Иконка-префикс для поля */
|
|
17
17
|
prefixIcon?: ReactElement;
|
|
18
|
+
/** Свойство позволяет грузить данные в поле по требованию */
|
|
19
|
+
onDataRequest?(): Promise<void>;
|
|
18
20
|
};
|
|
19
21
|
export type FieldSecureProps = WithSupportProps<FieldSecureOwnProps & InputProps & WrapperProps>;
|
|
20
22
|
export declare const FieldSecure: import("react").ForwardRefExoticComponent<{
|
|
@@ -1,3 +1,12 @@
|
|
|
1
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
2
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
3
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
4
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
5
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
6
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
7
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
8
|
+
});
|
|
9
|
+
};
|
|
1
10
|
var __rest = (this && this.__rest) || function (s, e) {
|
|
2
11
|
var t = {};
|
|
3
12
|
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
|
|
@@ -11,8 +20,9 @@ var __rest = (this && this.__rest) || function (s, e) {
|
|
|
11
20
|
};
|
|
12
21
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
13
22
|
import mergeRefs from 'merge-refs';
|
|
14
|
-
import { forwardRef, useMemo, useRef } from 'react';
|
|
23
|
+
import { forwardRef, useMemo, useRef, useState } from 'react';
|
|
15
24
|
import { InputPrivate, moveCursorToEnd, runAfterRerender, SIZE, useButtonNavigation, } from '@snack-uikit/input-private';
|
|
25
|
+
import { Skeleton, WithSkeleton } from '@snack-uikit/skeleton';
|
|
16
26
|
import { extractSupportProps } from '@snack-uikit/utils';
|
|
17
27
|
import { CONTAINER_VARIANT, VALIDATION_STATE } from '../../constants';
|
|
18
28
|
import { FieldContainerPrivate } from '../../helperComponents';
|
|
@@ -20,10 +30,12 @@ import { useCopyButton, useHideButton, useValueControl } from '../../hooks';
|
|
|
20
30
|
import { getValidationState } from '../../utils/getValidationState';
|
|
21
31
|
import { FieldDecorator } from '../FieldDecorator';
|
|
22
32
|
export const FieldSecure = forwardRef((_a, ref) => {
|
|
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"]);
|
|
33
|
+
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, onDataRequest } = _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", "onDataRequest"]);
|
|
24
34
|
const localRef = useRef(null);
|
|
25
35
|
const copyButtonRef = useRef(null);
|
|
26
36
|
const hideButtonRef = useRef(null);
|
|
37
|
+
const [isDataRequested, setIsDataRequested] = useState(false);
|
|
38
|
+
const [isLoading, setIsLoading] = useState(false);
|
|
27
39
|
const [value = '', onChange] = useValueControl({
|
|
28
40
|
value: valueProp,
|
|
29
41
|
defaultValue: '',
|
|
@@ -37,23 +49,58 @@ export const FieldSecure = forwardRef((_a, ref) => {
|
|
|
37
49
|
const showCopyButton = showCopyButtonProp && Boolean(value) && readonly && !disabled;
|
|
38
50
|
const showHideButton = !(readonly && !value);
|
|
39
51
|
const fieldValidationState = getValidationState({ validationState, error });
|
|
40
|
-
const
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
(
|
|
46
|
-
|
|
47
|
-
}
|
|
52
|
+
const handleDataRequest = () => __awaiter(void 0, void 0, void 0, function* () {
|
|
53
|
+
if (!isDataRequested && onDataRequest) {
|
|
54
|
+
setIsLoading(true);
|
|
55
|
+
try {
|
|
56
|
+
yield onDataRequest();
|
|
57
|
+
setIsDataRequested(true);
|
|
58
|
+
return true;
|
|
59
|
+
}
|
|
60
|
+
catch (e) {
|
|
61
|
+
return false;
|
|
62
|
+
}
|
|
63
|
+
finally {
|
|
64
|
+
setIsLoading(false);
|
|
65
|
+
}
|
|
48
66
|
}
|
|
67
|
+
return true;
|
|
68
|
+
});
|
|
69
|
+
const toggleHidden = () => {
|
|
70
|
+
handleDataRequest().then(isSuccess => {
|
|
71
|
+
if (isSuccess) {
|
|
72
|
+
setHidden(!hidden);
|
|
73
|
+
if (!readonly) {
|
|
74
|
+
runAfterRerender(() => {
|
|
75
|
+
var _a;
|
|
76
|
+
(_a = localRef.current) === null || _a === void 0 ? void 0 : _a.focus();
|
|
77
|
+
moveCursorToEnd(localRef.current);
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
});
|
|
49
82
|
};
|
|
50
|
-
const copyButtonSettings = useCopyButton({
|
|
51
|
-
|
|
83
|
+
const copyButtonSettings = useCopyButton({
|
|
84
|
+
copyButtonRef,
|
|
85
|
+
showCopyButton,
|
|
86
|
+
size,
|
|
87
|
+
valueToCopy: value,
|
|
88
|
+
onDataRequest: handleDataRequest,
|
|
89
|
+
disabled: isLoading,
|
|
90
|
+
});
|
|
91
|
+
const hideButtonSettings = useHideButton({
|
|
92
|
+
hideButtonRef,
|
|
93
|
+
showHideButton,
|
|
94
|
+
size,
|
|
95
|
+
toggleHidden,
|
|
96
|
+
hidden,
|
|
97
|
+
disabled: disabled || isLoading,
|
|
98
|
+
});
|
|
52
99
|
const { buttons, inputTabIndex, onInputKeyDown } = useButtonNavigation({
|
|
53
100
|
inputRef: localRef,
|
|
54
101
|
buttons: useMemo(() => [copyButtonSettings, hideButtonSettings], [copyButtonSettings, hideButtonSettings]),
|
|
55
102
|
readonly,
|
|
56
103
|
submitKeys: ['Enter', 'Space', 'Tab'],
|
|
57
104
|
});
|
|
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' }) }) })));
|
|
105
|
+
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(WithSkeleton, { skeleton: _jsx(Skeleton, { width: '100%', borderRadius: 2 }), loading: isLoading, 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' }) }) }) })));
|
|
59
106
|
});
|
|
@@ -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
|
|
13
|
-
pinBottom
|
|
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;
|
|
@@ -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
|
|
11
|
-
pinBottom
|
|
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;
|
|
@@ -37,8 +37,8 @@ export type FieldSelectPrivateProps = InputProps & WrapperProps & {
|
|
|
37
37
|
};
|
|
38
38
|
type FiledSelectCommonProps = WithSupportProps<{
|
|
39
39
|
options: OptionProps[];
|
|
40
|
-
pinTop
|
|
41
|
-
pinBottom
|
|
40
|
+
pinTop?: OptionProps[];
|
|
41
|
+
pinBottom?: OptionProps[];
|
|
42
42
|
searchable?: boolean;
|
|
43
43
|
/** Отображение кнопки Копировать для поля (актуально только для `readonly = true`) */
|
|
44
44
|
showCopyButton?: boolean;
|
|
@@ -6,6 +6,8 @@ type ButtonCopyValueProps = {
|
|
|
6
6
|
onKeyDown?: KeyboardEventHandler<HTMLButtonElement>;
|
|
7
7
|
onClick?: MouseEventHandler<HTMLButtonElement>;
|
|
8
8
|
tabIndex?: number;
|
|
9
|
+
onDataRequest?(): Promise<boolean>;
|
|
10
|
+
disabled?: boolean;
|
|
9
11
|
};
|
|
10
12
|
export declare const ButtonCopyValue: import("react").ForwardRefExoticComponent<ButtonCopyValueProps & import("react").RefAttributes<HTMLButtonElement>>;
|
|
11
13
|
export {};
|
|
@@ -3,22 +3,34 @@ import copyToClipboard from 'copy-to-clipboard';
|
|
|
3
3
|
import { forwardRef, useEffect, useRef, useState } from 'react';
|
|
4
4
|
import { getIcon } from './helpers';
|
|
5
5
|
import styles from './styles.module.css';
|
|
6
|
-
export const ButtonCopyValue = forwardRef(({ size, valueToCopy, tabIndex = -1, onKeyDown, onClick }, ref) => {
|
|
6
|
+
export const ButtonCopyValue = forwardRef(({ size, valueToCopy, tabIndex = -1, onKeyDown, onClick, onDataRequest, disabled }, ref) => {
|
|
7
7
|
const [isChecked, setIsChecked] = useState(false);
|
|
8
8
|
const timerId = useRef();
|
|
9
9
|
const openTooltip = () => setIsChecked(true);
|
|
10
10
|
const closeTooltip = () => setIsChecked(false);
|
|
11
11
|
const handleClick = event => {
|
|
12
|
+
const handleCopy = () => {
|
|
13
|
+
valueToCopy && copyToClipboard(valueToCopy, { format: 'text/plain' });
|
|
14
|
+
openTooltip();
|
|
15
|
+
clearTimeout(timerId.current);
|
|
16
|
+
timerId.current = setTimeout(closeTooltip, 2000);
|
|
17
|
+
onClick === null || onClick === void 0 ? void 0 : onClick(event);
|
|
18
|
+
};
|
|
12
19
|
event.stopPropagation();
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
20
|
+
if (onDataRequest) {
|
|
21
|
+
onDataRequest().then(isSuccess => {
|
|
22
|
+
if (isSuccess) {
|
|
23
|
+
handleCopy();
|
|
24
|
+
}
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
else {
|
|
28
|
+
handleCopy();
|
|
29
|
+
}
|
|
18
30
|
};
|
|
19
31
|
useEffect(() => () => {
|
|
20
32
|
closeTooltip();
|
|
21
33
|
clearTimeout(timerId.current);
|
|
22
34
|
}, []);
|
|
23
|
-
return (_jsx("button", { className: styles.buttonCopyValue, "data-size": size, onClick: handleClick, "data-test-id": 'button-copy-value', ref: ref, onKeyDown: onKeyDown, tabIndex: tabIndex, type: 'button', children: getIcon({ size, isChecked }) }));
|
|
35
|
+
return (_jsx("button", { className: styles.buttonCopyValue, "data-size": size, "data-disabled": disabled || undefined, onClick: handleClick, "data-test-id": 'button-copy-value', ref: ref, onKeyDown: onKeyDown, tabIndex: tabIndex, type: 'button', disabled: disabled, children: getIcon({ size, isChecked }) }));
|
|
24
36
|
});
|
|
@@ -5,6 +5,8 @@ type UseCopyButtonProps = {
|
|
|
5
5
|
showCopyButton: boolean;
|
|
6
6
|
valueToCopy: string;
|
|
7
7
|
size: Size;
|
|
8
|
+
onDataRequest?(): Promise<boolean>;
|
|
9
|
+
disabled?: boolean;
|
|
8
10
|
};
|
|
9
|
-
export declare function useCopyButton({ copyButtonRef, showCopyButton, size, valueToCopy }: UseCopyButtonProps): ButtonProps;
|
|
11
|
+
export declare function useCopyButton({ copyButtonRef, showCopyButton, size, valueToCopy, onDataRequest, disabled, }: UseCopyButtonProps): ButtonProps;
|
|
10
12
|
export {};
|
|
@@ -2,11 +2,11 @@ import { jsx as _jsx } from "react/jsx-runtime";
|
|
|
2
2
|
import { useMemo } from 'react';
|
|
3
3
|
import { BUTTON_SIZE_MAP } from '@snack-uikit/input-private';
|
|
4
4
|
import { ButtonCopyValue } from '../helperComponents';
|
|
5
|
-
export function useCopyButton({ copyButtonRef, showCopyButton, size, valueToCopy }) {
|
|
5
|
+
export function useCopyButton({ copyButtonRef, showCopyButton, size, valueToCopy, onDataRequest, disabled, }) {
|
|
6
6
|
return useMemo(() => ({
|
|
7
7
|
id: 'copy',
|
|
8
8
|
ref: copyButtonRef,
|
|
9
9
|
show: showCopyButton,
|
|
10
|
-
render: props => _jsx(ButtonCopyValue, Object.assign({}, props, { size: BUTTON_SIZE_MAP[size], valueToCopy: valueToCopy })),
|
|
11
|
-
}), [copyButtonRef, showCopyButton, size, valueToCopy]);
|
|
10
|
+
render: props => (_jsx(ButtonCopyValue, Object.assign({}, props, { size: BUTTON_SIZE_MAP[size], valueToCopy: valueToCopy, onDataRequest: onDataRequest, disabled: disabled }))),
|
|
11
|
+
}), [copyButtonRef, disabled, onDataRequest, showCopyButton, size, valueToCopy]);
|
|
12
12
|
}
|
package/package.json
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
"access": "public"
|
|
5
5
|
},
|
|
6
6
|
"title": "Fields",
|
|
7
|
-
"version": "0.19.
|
|
7
|
+
"version": "0.19.3-preview-34ffedb5.0",
|
|
8
8
|
"sideEffects": [
|
|
9
9
|
"*.css",
|
|
10
10
|
"*.woff",
|
|
@@ -33,12 +33,13 @@
|
|
|
33
33
|
"scripts": {},
|
|
34
34
|
"dependencies": {
|
|
35
35
|
"@snack-uikit/button": "0.17.1",
|
|
36
|
-
"@snack-uikit/calendar": "0.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.
|
|
40
|
+
"@snack-uikit/list": "0.11.2",
|
|
41
41
|
"@snack-uikit/scroll": "0.5.3",
|
|
42
|
+
"@snack-uikit/skeleton": "0.3.4",
|
|
42
43
|
"@snack-uikit/slider": "0.1.9",
|
|
43
44
|
"@snack-uikit/tag": "0.9.1",
|
|
44
45
|
"@snack-uikit/tooltip": "0.13.2",
|
|
@@ -58,5 +59,5 @@
|
|
|
58
59
|
"peerDependencies": {
|
|
59
60
|
"@snack-uikit/locale": "*"
|
|
60
61
|
},
|
|
61
|
-
"gitHead": "
|
|
62
|
+
"gitHead": "7c21433a0f290c78176650374b137e2abafced96"
|
|
62
63
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import mergeRefs from 'merge-refs';
|
|
2
|
-
import { forwardRef, ReactElement, useMemo, useRef } from 'react';
|
|
2
|
+
import { forwardRef, ReactElement, useMemo, useRef, useState } from 'react';
|
|
3
3
|
|
|
4
4
|
import {
|
|
5
5
|
InputPrivate,
|
|
@@ -9,6 +9,7 @@ import {
|
|
|
9
9
|
SIZE,
|
|
10
10
|
useButtonNavigation,
|
|
11
11
|
} from '@snack-uikit/input-private';
|
|
12
|
+
import { Skeleton, WithSkeleton } from '@snack-uikit/skeleton';
|
|
12
13
|
import { extractSupportProps, WithSupportProps } from '@snack-uikit/utils';
|
|
13
14
|
|
|
14
15
|
import { CONTAINER_VARIANT, VALIDATION_STATE } from '../../constants';
|
|
@@ -45,6 +46,8 @@ type FieldSecureOwnProps = {
|
|
|
45
46
|
allowMoreThanMaxLength?: boolean;
|
|
46
47
|
/** Иконка-префикс для поля */
|
|
47
48
|
prefixIcon?: ReactElement;
|
|
49
|
+
/** Свойство позволяет грузить данные в поле по требованию */
|
|
50
|
+
onDataRequest?(): Promise<void>;
|
|
48
51
|
};
|
|
49
52
|
|
|
50
53
|
export type FieldSecureProps = WithSupportProps<FieldSecureOwnProps & InputProps & WrapperProps>;
|
|
@@ -77,6 +80,7 @@ export const FieldSecure = forwardRef<HTMLInputElement, FieldSecureProps>(
|
|
|
77
80
|
validationState = VALIDATION_STATE.Default,
|
|
78
81
|
prefixIcon,
|
|
79
82
|
error,
|
|
83
|
+
onDataRequest,
|
|
80
84
|
...rest
|
|
81
85
|
},
|
|
82
86
|
ref,
|
|
@@ -84,6 +88,8 @@ export const FieldSecure = forwardRef<HTMLInputElement, FieldSecureProps>(
|
|
|
84
88
|
const localRef = useRef<HTMLInputElement>(null);
|
|
85
89
|
const copyButtonRef = useRef<HTMLButtonElement>(null);
|
|
86
90
|
const hideButtonRef = useRef<HTMLButtonElement>(null);
|
|
91
|
+
const [isDataRequested, setIsDataRequested] = useState(false);
|
|
92
|
+
const [isLoading, setIsLoading] = useState(false);
|
|
87
93
|
const [value = '', onChange] = useValueControl<string>({
|
|
88
94
|
value: valueProp,
|
|
89
95
|
defaultValue: '',
|
|
@@ -99,19 +105,55 @@ export const FieldSecure = forwardRef<HTMLInputElement, FieldSecureProps>(
|
|
|
99
105
|
|
|
100
106
|
const fieldValidationState = getValidationState({ validationState, error });
|
|
101
107
|
|
|
102
|
-
const
|
|
103
|
-
|
|
108
|
+
const handleDataRequest = async (): Promise<boolean> => {
|
|
109
|
+
if (!isDataRequested && onDataRequest) {
|
|
110
|
+
setIsLoading(true);
|
|
104
111
|
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
})
|
|
112
|
+
try {
|
|
113
|
+
await onDataRequest();
|
|
114
|
+
setIsDataRequested(true);
|
|
115
|
+
return true;
|
|
116
|
+
} catch (e) {
|
|
117
|
+
return false;
|
|
118
|
+
} finally {
|
|
119
|
+
setIsLoading(false);
|
|
120
|
+
}
|
|
110
121
|
}
|
|
122
|
+
|
|
123
|
+
return true;
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
const toggleHidden = () => {
|
|
127
|
+
handleDataRequest().then(isSuccess => {
|
|
128
|
+
if (isSuccess) {
|
|
129
|
+
setHidden(!hidden);
|
|
130
|
+
|
|
131
|
+
if (!readonly) {
|
|
132
|
+
runAfterRerender(() => {
|
|
133
|
+
localRef.current?.focus();
|
|
134
|
+
moveCursorToEnd(localRef.current);
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
});
|
|
111
139
|
};
|
|
112
140
|
|
|
113
|
-
const copyButtonSettings = useCopyButton({
|
|
114
|
-
|
|
141
|
+
const copyButtonSettings = useCopyButton({
|
|
142
|
+
copyButtonRef,
|
|
143
|
+
showCopyButton,
|
|
144
|
+
size,
|
|
145
|
+
valueToCopy: value,
|
|
146
|
+
onDataRequest: handleDataRequest,
|
|
147
|
+
disabled: isLoading,
|
|
148
|
+
});
|
|
149
|
+
const hideButtonSettings = useHideButton({
|
|
150
|
+
hideButtonRef,
|
|
151
|
+
showHideButton,
|
|
152
|
+
size,
|
|
153
|
+
toggleHidden,
|
|
154
|
+
hidden,
|
|
155
|
+
disabled: disabled || isLoading,
|
|
156
|
+
});
|
|
115
157
|
const { buttons, inputTabIndex, onInputKeyDown } = useButtonNavigation({
|
|
116
158
|
inputRef: localRef,
|
|
117
159
|
buttons: useMemo(() => [copyButtonSettings, hideButtonSettings], [copyButtonSettings, hideButtonSettings]),
|
|
@@ -147,24 +189,26 @@ export const FieldSecure = forwardRef<HTMLInputElement, FieldSecureProps>(
|
|
|
147
189
|
prefix={prefixIcon}
|
|
148
190
|
postfix={buttons}
|
|
149
191
|
>
|
|
150
|
-
<
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
192
|
+
<WithSkeleton skeleton={<Skeleton width='100%' borderRadius={2} />} loading={isLoading}>
|
|
193
|
+
<InputPrivate
|
|
194
|
+
ref={mergeRefs(ref, localRef)}
|
|
195
|
+
data-size={size}
|
|
196
|
+
value={value}
|
|
197
|
+
onChange={onChange}
|
|
198
|
+
onFocus={onFocus}
|
|
199
|
+
onBlur={onBlur}
|
|
200
|
+
onKeyDown={onInputKeyDown}
|
|
201
|
+
tabIndex={inputTabIndex}
|
|
202
|
+
placeholder={placeholder}
|
|
203
|
+
disabled={disabled}
|
|
204
|
+
readonly={readonly}
|
|
205
|
+
type={hidden ? 'password' : 'text'}
|
|
206
|
+
maxLength={allowMoreThanMaxLength ? undefined : maxLength || undefined}
|
|
207
|
+
id={id}
|
|
208
|
+
name={name}
|
|
209
|
+
data-test-id='field-secure__input'
|
|
210
|
+
/>
|
|
211
|
+
</WithSkeleton>
|
|
168
212
|
</FieldContainerPrivate>
|
|
169
213
|
</FieldDecorator>
|
|
170
214
|
);
|
|
@@ -83,8 +83,8 @@ export type FieldSelectPrivateProps = InputProps & WrapperProps & { options: Opt
|
|
|
83
83
|
type FiledSelectCommonProps = WithSupportProps<{
|
|
84
84
|
options: OptionProps[];
|
|
85
85
|
|
|
86
|
-
pinTop
|
|
87
|
-
pinBottom
|
|
86
|
+
pinTop?: OptionProps[];
|
|
87
|
+
pinBottom?: OptionProps[];
|
|
88
88
|
|
|
89
89
|
searchable?: boolean;
|
|
90
90
|
/** Отображение кнопки Копировать для поля (актуально только для `readonly = true`) */
|
|
@@ -12,21 +12,36 @@ type ButtonCopyValueProps = {
|
|
|
12
12
|
onKeyDown?: KeyboardEventHandler<HTMLButtonElement>;
|
|
13
13
|
onClick?: MouseEventHandler<HTMLButtonElement>;
|
|
14
14
|
tabIndex?: number;
|
|
15
|
+
onDataRequest?(): Promise<boolean>;
|
|
16
|
+
disabled?: boolean;
|
|
15
17
|
};
|
|
16
18
|
export const ButtonCopyValue = forwardRef<HTMLButtonElement, ButtonCopyValueProps>(
|
|
17
|
-
({ size, valueToCopy, tabIndex = -1, onKeyDown, onClick }, ref) => {
|
|
19
|
+
({ size, valueToCopy, tabIndex = -1, onKeyDown, onClick, onDataRequest, disabled }, ref) => {
|
|
18
20
|
const [isChecked, setIsChecked] = useState(false);
|
|
19
21
|
const timerId = useRef<ReturnType<typeof setTimeout>>();
|
|
20
22
|
const openTooltip = () => setIsChecked(true);
|
|
21
23
|
const closeTooltip = () => setIsChecked(false);
|
|
22
24
|
|
|
23
25
|
const handleClick: MouseEventHandler<HTMLButtonElement> = event => {
|
|
26
|
+
const handleCopy = () => {
|
|
27
|
+
valueToCopy && copyToClipboard(valueToCopy, { format: 'text/plain' });
|
|
28
|
+
openTooltip();
|
|
29
|
+
clearTimeout(timerId.current);
|
|
30
|
+
timerId.current = setTimeout(closeTooltip, 2000);
|
|
31
|
+
onClick?.(event);
|
|
32
|
+
};
|
|
33
|
+
|
|
24
34
|
event.stopPropagation();
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
35
|
+
|
|
36
|
+
if (onDataRequest) {
|
|
37
|
+
onDataRequest().then(isSuccess => {
|
|
38
|
+
if (isSuccess) {
|
|
39
|
+
handleCopy();
|
|
40
|
+
}
|
|
41
|
+
});
|
|
42
|
+
} else {
|
|
43
|
+
handleCopy();
|
|
44
|
+
}
|
|
30
45
|
};
|
|
31
46
|
|
|
32
47
|
useEffect(
|
|
@@ -41,12 +56,14 @@ export const ButtonCopyValue = forwardRef<HTMLButtonElement, ButtonCopyValueProp
|
|
|
41
56
|
<button
|
|
42
57
|
className={styles.buttonCopyValue}
|
|
43
58
|
data-size={size}
|
|
59
|
+
data-disabled={disabled || undefined}
|
|
44
60
|
onClick={handleClick}
|
|
45
61
|
data-test-id='button-copy-value'
|
|
46
62
|
ref={ref}
|
|
47
63
|
onKeyDown={onKeyDown}
|
|
48
64
|
tabIndex={tabIndex}
|
|
49
65
|
type='button'
|
|
66
|
+
disabled={disabled}
|
|
50
67
|
>
|
|
51
68
|
{getIcon({ size, isChecked })}
|
|
52
69
|
</button>
|
|
@@ -9,16 +9,33 @@ type UseCopyButtonProps = {
|
|
|
9
9
|
showCopyButton: boolean;
|
|
10
10
|
valueToCopy: string;
|
|
11
11
|
size: Size;
|
|
12
|
+
onDataRequest?(): Promise<boolean>;
|
|
13
|
+
disabled?: boolean;
|
|
12
14
|
};
|
|
13
15
|
|
|
14
|
-
export function useCopyButton({
|
|
16
|
+
export function useCopyButton({
|
|
17
|
+
copyButtonRef,
|
|
18
|
+
showCopyButton,
|
|
19
|
+
size,
|
|
20
|
+
valueToCopy,
|
|
21
|
+
onDataRequest,
|
|
22
|
+
disabled,
|
|
23
|
+
}: UseCopyButtonProps): ButtonProps {
|
|
15
24
|
return useMemo(
|
|
16
25
|
() => ({
|
|
17
26
|
id: 'copy',
|
|
18
27
|
ref: copyButtonRef,
|
|
19
28
|
show: showCopyButton,
|
|
20
|
-
render: props =>
|
|
29
|
+
render: props => (
|
|
30
|
+
<ButtonCopyValue
|
|
31
|
+
{...props}
|
|
32
|
+
size={BUTTON_SIZE_MAP[size]}
|
|
33
|
+
valueToCopy={valueToCopy}
|
|
34
|
+
onDataRequest={onDataRequest}
|
|
35
|
+
disabled={disabled}
|
|
36
|
+
/>
|
|
37
|
+
),
|
|
21
38
|
}),
|
|
22
|
-
[copyButtonRef, showCopyButton, size, valueToCopy],
|
|
39
|
+
[copyButtonRef, disabled, onDataRequest, showCopyButton, size, valueToCopy],
|
|
23
40
|
);
|
|
24
41
|
}
|