@cloud-ru/ds-fields 1.0.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 +8 -0
- package/LICENSE +201 -0
- package/README.md +888 -0
- package/dist/cjs/components/FieldColor/FieldColor.d.ts +48 -0
- package/dist/cjs/components/FieldColor/FieldColor.js +147 -0
- package/dist/cjs/components/FieldColor/index.d.ts +1 -0
- package/dist/cjs/components/FieldColor/index.js +17 -0
- package/dist/cjs/components/FieldColor/styles.module.css +79 -0
- package/dist/cjs/components/FieldColor/utils.d.ts +7 -0
- package/dist/cjs/components/FieldColor/utils.js +19 -0
- package/dist/cjs/components/FieldCombo/FieldCombo.d.ts +91 -0
- package/dist/cjs/components/FieldCombo/FieldCombo.js +200 -0
- package/dist/cjs/components/FieldCombo/index.d.ts +1 -0
- package/dist/cjs/components/FieldCombo/index.js +17 -0
- package/dist/cjs/components/FieldDate/FieldDate.d.ts +2 -0
- package/dist/cjs/components/FieldDate/FieldDate.js +369 -0
- package/dist/cjs/components/FieldDate/constants.d.ts +10 -0
- package/dist/cjs/components/FieldDate/constants.js +13 -0
- package/dist/cjs/components/FieldDate/index.d.ts +3 -0
- package/dist/cjs/components/FieldDate/index.js +19 -0
- package/dist/cjs/components/FieldDate/mask.d.ts +20 -0
- package/dist/cjs/components/FieldDate/mask.js +117 -0
- package/dist/cjs/components/FieldDate/styles.module.css +43 -0
- package/dist/cjs/components/FieldDate/types.d.ts +100 -0
- package/dist/cjs/components/FieldDate/types.js +2 -0
- package/dist/cjs/components/FieldDate/utils.d.ts +3 -0
- package/dist/cjs/components/FieldDate/utils.js +8 -0
- package/dist/cjs/components/FieldSecure/FieldSecure.d.ts +64 -0
- package/dist/cjs/components/FieldSecure/FieldSecure.js +158 -0
- package/dist/cjs/components/FieldSecure/index.d.ts +1 -0
- package/dist/cjs/components/FieldSecure/index.js +17 -0
- package/dist/cjs/components/FieldSelect/FieldSelect.d.ts +2 -0
- package/dist/cjs/components/FieldSelect/FieldSelect.js +360 -0
- package/dist/cjs/components/FieldSelect/constants.d.ts +4 -0
- package/dist/cjs/components/FieldSelect/constants.js +7 -0
- package/dist/cjs/components/FieldSelect/index.d.ts +3 -0
- package/dist/cjs/components/FieldSelect/index.js +19 -0
- package/dist/cjs/components/FieldSelect/styles.module.css +92 -0
- package/dist/cjs/components/FieldSelect/types.d.ts +197 -0
- package/dist/cjs/components/FieldSelect/types.js +2 -0
- package/dist/cjs/components/FieldSelect/utils.d.ts +19 -0
- package/dist/cjs/components/FieldSelect/utils.js +104 -0
- package/dist/cjs/components/FieldSlider/FieldSlider.d.ts +92 -0
- package/dist/cjs/components/FieldSlider/FieldSlider.js +196 -0
- package/dist/cjs/components/FieldSlider/index.d.ts +2 -0
- package/dist/cjs/components/FieldSlider/index.js +18 -0
- package/dist/cjs/components/FieldSlider/styles.module.css +44 -0
- package/dist/cjs/components/FieldSlider/types.d.ts +4 -0
- package/dist/cjs/components/FieldSlider/types.js +2 -0
- package/dist/cjs/components/FieldSlider/utils/generateAllowedValues.d.ts +1 -0
- package/dist/cjs/components/FieldSlider/utils/generateAllowedValues.js +16 -0
- package/dist/cjs/components/FieldSlider/utils/getClosestMark.d.ts +4 -0
- package/dist/cjs/components/FieldSlider/utils/getClosestMark.js +16 -0
- package/dist/cjs/components/FieldSlider/utils/getMarkLabel.d.ts +2 -0
- package/dist/cjs/components/FieldSlider/utils/getMarkLabel.js +7 -0
- package/dist/cjs/components/FieldSlider/utils/getTextFieldValue.d.ts +2 -0
- package/dist/cjs/components/FieldSlider/utils/getTextFieldValue.js +9 -0
- package/dist/cjs/components/FieldSlider/utils/index.d.ts +5 -0
- package/dist/cjs/components/FieldSlider/utils/index.js +21 -0
- package/dist/cjs/components/FieldSlider/utils/isMarkObject.d.ts +6 -0
- package/dist/cjs/components/FieldSlider/utils/isMarkObject.js +6 -0
- package/dist/cjs/components/FieldStepper/FieldStepper.d.ts +81 -0
- package/dist/cjs/components/FieldStepper/FieldStepper.js +160 -0
- package/dist/cjs/components/FieldStepper/index.d.ts +1 -0
- package/dist/cjs/components/FieldStepper/index.js +17 -0
- package/dist/cjs/components/FieldStepper/styles.module.css +78 -0
- package/dist/cjs/components/FieldStepper/utils.d.ts +14 -0
- package/dist/cjs/components/FieldStepper/utils.js +45 -0
- package/dist/cjs/components/FieldText/FieldText.d.ts +11 -0
- package/dist/cjs/components/FieldText/FieldText.js +14 -0
- package/dist/cjs/components/FieldText/index.d.ts +1 -0
- package/dist/cjs/components/FieldText/index.js +17 -0
- package/dist/cjs/components/FieldTextArea/FieldTextArea.d.ts +95 -0
- package/dist/cjs/components/FieldTextArea/FieldTextArea.js +114 -0
- package/dist/cjs/components/FieldTextArea/index.d.ts +1 -0
- package/dist/cjs/components/FieldTextArea/index.js +17 -0
- package/dist/cjs/components/FieldTextArea/styles.module.css +123 -0
- package/dist/cjs/components/FieldTime/FieldTime.d.ts +74 -0
- package/dist/cjs/components/FieldTime/FieldTime.js +167 -0
- package/dist/cjs/components/FieldTime/index.d.ts +2 -0
- package/dist/cjs/components/FieldTime/index.js +17 -0
- package/dist/cjs/components/FieldTime/mask.d.ts +3 -0
- package/dist/cjs/components/FieldTime/mask.js +24 -0
- package/dist/cjs/components/FieldTime/styles.module.css +3 -0
- package/dist/cjs/components/index.d.ts +14 -0
- package/dist/cjs/components/index.js +32 -0
- package/dist/cjs/components/shared/helperComponents/FieldElementButton/FieldElementButton.d.ts +35 -0
- package/dist/cjs/components/shared/helperComponents/FieldElementButton/FieldElementButton.js +47 -0
- package/dist/cjs/components/shared/helperComponents/FieldElementButton/FieldElementButtonList.d.ts +26 -0
- package/dist/cjs/components/shared/helperComponents/FieldElementButton/FieldElementButtonList.js +24 -0
- package/dist/cjs/components/shared/helperComponents/FieldElementButton/constants.d.ts +9 -0
- package/dist/cjs/components/shared/helperComponents/FieldElementButton/constants.js +12 -0
- package/dist/cjs/components/shared/helperComponents/FieldElementButton/index.d.ts +4 -0
- package/dist/cjs/components/shared/helperComponents/FieldElementButton/index.js +20 -0
- package/dist/cjs/components/shared/helperComponents/FieldElementButton/styles.module.css +204 -0
- package/dist/cjs/components/shared/helperComponents/FieldElementButton/types.d.ts +4 -0
- package/dist/cjs/components/shared/helperComponents/FieldElementButton/types.js +2 -0
- package/dist/cjs/components/shared/helperComponents/FieldShell/FieldShell.d.ts +43 -0
- package/dist/cjs/components/shared/helperComponents/FieldShell/FieldShell.js +22 -0
- package/dist/cjs/components/shared/helperComponents/FieldShell/index.d.ts +1 -0
- package/dist/cjs/components/shared/helperComponents/FieldShell/index.js +17 -0
- package/dist/cjs/components/shared/helperComponents/FieldShell/styles.module.css +11 -0
- package/dist/cjs/components/shared/hooks/useCopyButton.d.ts +20 -0
- package/dist/cjs/components/shared/hooks/useCopyButton.js +58 -0
- package/dist/cjs/components/shared/index.d.ts +4 -0
- package/dist/cjs/components/shared/index.js +20 -0
- package/dist/cjs/components/shared/styles.module.css +673 -0
- package/dist/cjs/components/shared/utils.d.ts +69 -0
- package/dist/cjs/components/shared/utils.js +104 -0
- package/dist/cjs/constants.d.ts +61 -0
- package/dist/cjs/constants.js +76 -0
- package/dist/cjs/hooks/index.d.ts +1 -0
- package/dist/cjs/hooks/index.js +17 -0
- package/dist/cjs/hooks/useAdaptiveAutoFocus.d.ts +7 -0
- package/dist/cjs/hooks/useAdaptiveAutoFocus.js +10 -0
- package/dist/cjs/index.d.ts +3 -0
- package/dist/cjs/index.js +19 -0
- package/dist/cjs/package.json +3 -0
- package/dist/cjs/segments/index.d.ts +2 -0
- package/dist/cjs/segments/index.js +18 -0
- package/dist/cjs/segments/segments.d.ts +22 -0
- package/dist/cjs/segments/segments.js +46 -0
- package/dist/cjs/segments/useSegmentedMask.d.ts +43 -0
- package/dist/cjs/segments/useSegmentedMask.js +282 -0
- package/dist/cjs/style.css +1350 -0
- package/dist/esm/components/FieldColor/FieldColor.d.ts +48 -0
- package/dist/esm/components/FieldColor/FieldColor.js +141 -0
- package/dist/esm/components/FieldColor/index.d.ts +1 -0
- package/dist/esm/components/FieldColor/index.js +1 -0
- package/dist/esm/components/FieldColor/styles.module.css +79 -0
- package/dist/esm/components/FieldColor/utils.d.ts +7 -0
- package/dist/esm/components/FieldColor/utils.js +16 -0
- package/dist/esm/components/FieldCombo/FieldCombo.d.ts +91 -0
- package/dist/esm/components/FieldCombo/FieldCombo.js +194 -0
- package/dist/esm/components/FieldCombo/index.d.ts +1 -0
- package/dist/esm/components/FieldCombo/index.js +1 -0
- package/dist/esm/components/FieldDate/FieldDate.d.ts +2 -0
- package/dist/esm/components/FieldDate/FieldDate.js +363 -0
- package/dist/esm/components/FieldDate/constants.d.ts +10 -0
- package/dist/esm/components/FieldDate/constants.js +10 -0
- package/dist/esm/components/FieldDate/index.d.ts +3 -0
- package/dist/esm/components/FieldDate/index.js +3 -0
- package/dist/esm/components/FieldDate/mask.d.ts +20 -0
- package/dist/esm/components/FieldDate/mask.js +111 -0
- package/dist/esm/components/FieldDate/styles.module.css +43 -0
- package/dist/esm/components/FieldDate/types.d.ts +100 -0
- package/dist/esm/components/FieldDate/types.js +1 -0
- package/dist/esm/components/FieldDate/utils.d.ts +3 -0
- package/dist/esm/components/FieldDate/utils.js +5 -0
- package/dist/esm/components/FieldSecure/FieldSecure.d.ts +64 -0
- package/dist/esm/components/FieldSecure/FieldSecure.js +152 -0
- package/dist/esm/components/FieldSecure/index.d.ts +1 -0
- package/dist/esm/components/FieldSecure/index.js +1 -0
- package/dist/esm/components/FieldSelect/FieldSelect.d.ts +2 -0
- package/dist/esm/components/FieldSelect/FieldSelect.js +354 -0
- package/dist/esm/components/FieldSelect/constants.d.ts +4 -0
- package/dist/esm/components/FieldSelect/constants.js +4 -0
- package/dist/esm/components/FieldSelect/index.d.ts +3 -0
- package/dist/esm/components/FieldSelect/index.js +3 -0
- package/dist/esm/components/FieldSelect/styles.module.css +92 -0
- package/dist/esm/components/FieldSelect/types.d.ts +197 -0
- package/dist/esm/components/FieldSelect/types.js +1 -0
- package/dist/esm/components/FieldSelect/utils.d.ts +19 -0
- package/dist/esm/components/FieldSelect/utils.js +93 -0
- package/dist/esm/components/FieldSlider/FieldSlider.d.ts +92 -0
- package/dist/esm/components/FieldSlider/FieldSlider.js +190 -0
- package/dist/esm/components/FieldSlider/index.d.ts +2 -0
- package/dist/esm/components/FieldSlider/index.js +2 -0
- package/dist/esm/components/FieldSlider/styles.module.css +44 -0
- package/dist/esm/components/FieldSlider/types.d.ts +4 -0
- package/dist/esm/components/FieldSlider/types.js +1 -0
- package/dist/esm/components/FieldSlider/utils/generateAllowedValues.d.ts +1 -0
- package/dist/esm/components/FieldSlider/utils/generateAllowedValues.js +13 -0
- package/dist/esm/components/FieldSlider/utils/getClosestMark.d.ts +4 -0
- package/dist/esm/components/FieldSlider/utils/getClosestMark.js +13 -0
- package/dist/esm/components/FieldSlider/utils/getMarkLabel.d.ts +2 -0
- package/dist/esm/components/FieldSlider/utils/getMarkLabel.js +4 -0
- package/dist/esm/components/FieldSlider/utils/getTextFieldValue.d.ts +2 -0
- package/dist/esm/components/FieldSlider/utils/getTextFieldValue.js +6 -0
- package/dist/esm/components/FieldSlider/utils/index.d.ts +5 -0
- package/dist/esm/components/FieldSlider/utils/index.js +5 -0
- package/dist/esm/components/FieldSlider/utils/isMarkObject.d.ts +6 -0
- package/dist/esm/components/FieldSlider/utils/isMarkObject.js +3 -0
- package/dist/esm/components/FieldStepper/FieldStepper.d.ts +81 -0
- package/dist/esm/components/FieldStepper/FieldStepper.js +154 -0
- package/dist/esm/components/FieldStepper/index.d.ts +1 -0
- package/dist/esm/components/FieldStepper/index.js +1 -0
- package/dist/esm/components/FieldStepper/styles.module.css +78 -0
- package/dist/esm/components/FieldStepper/utils.d.ts +14 -0
- package/dist/esm/components/FieldStepper/utils.js +38 -0
- package/dist/esm/components/FieldText/FieldText.d.ts +11 -0
- package/dist/esm/components/FieldText/FieldText.js +11 -0
- package/dist/esm/components/FieldText/index.d.ts +1 -0
- package/dist/esm/components/FieldText/index.js +1 -0
- package/dist/esm/components/FieldTextArea/FieldTextArea.d.ts +95 -0
- package/dist/esm/components/FieldTextArea/FieldTextArea.js +108 -0
- package/dist/esm/components/FieldTextArea/index.d.ts +1 -0
- package/dist/esm/components/FieldTextArea/index.js +1 -0
- package/dist/esm/components/FieldTextArea/styles.module.css +123 -0
- package/dist/esm/components/FieldTime/FieldTime.d.ts +74 -0
- package/dist/esm/components/FieldTime/FieldTime.js +161 -0
- package/dist/esm/components/FieldTime/index.d.ts +2 -0
- package/dist/esm/components/FieldTime/index.js +1 -0
- package/dist/esm/components/FieldTime/mask.d.ts +3 -0
- package/dist/esm/components/FieldTime/mask.js +20 -0
- package/dist/esm/components/FieldTime/styles.module.css +3 -0
- package/dist/esm/components/index.d.ts +14 -0
- package/dist/esm/components/index.js +12 -0
- package/dist/esm/components/shared/helperComponents/FieldElementButton/FieldElementButton.d.ts +35 -0
- package/dist/esm/components/shared/helperComponents/FieldElementButton/FieldElementButton.js +41 -0
- package/dist/esm/components/shared/helperComponents/FieldElementButton/FieldElementButtonList.d.ts +26 -0
- package/dist/esm/components/shared/helperComponents/FieldElementButton/FieldElementButtonList.js +21 -0
- package/dist/esm/components/shared/helperComponents/FieldElementButton/constants.d.ts +9 -0
- package/dist/esm/components/shared/helperComponents/FieldElementButton/constants.js +9 -0
- package/dist/esm/components/shared/helperComponents/FieldElementButton/index.d.ts +4 -0
- package/dist/esm/components/shared/helperComponents/FieldElementButton/index.js +4 -0
- package/dist/esm/components/shared/helperComponents/FieldElementButton/styles.module.css +204 -0
- package/dist/esm/components/shared/helperComponents/FieldElementButton/types.d.ts +4 -0
- package/dist/esm/components/shared/helperComponents/FieldElementButton/types.js +1 -0
- package/dist/esm/components/shared/helperComponents/FieldShell/FieldShell.d.ts +43 -0
- package/dist/esm/components/shared/helperComponents/FieldShell/FieldShell.js +16 -0
- package/dist/esm/components/shared/helperComponents/FieldShell/index.d.ts +1 -0
- package/dist/esm/components/shared/helperComponents/FieldShell/index.js +1 -0
- package/dist/esm/components/shared/helperComponents/FieldShell/styles.module.css +11 -0
- package/dist/esm/components/shared/hooks/useCopyButton.d.ts +20 -0
- package/dist/esm/components/shared/hooks/useCopyButton.js +55 -0
- package/dist/esm/components/shared/index.d.ts +4 -0
- package/dist/esm/components/shared/index.js +4 -0
- package/dist/esm/components/shared/styles.module.css +673 -0
- package/dist/esm/components/shared/utils.d.ts +69 -0
- package/dist/esm/components/shared/utils.js +91 -0
- package/dist/esm/constants.d.ts +61 -0
- package/dist/esm/constants.js +73 -0
- package/dist/esm/hooks/index.d.ts +1 -0
- package/dist/esm/hooks/index.js +1 -0
- package/dist/esm/hooks/useAdaptiveAutoFocus.d.ts +7 -0
- package/dist/esm/hooks/useAdaptiveAutoFocus.js +7 -0
- package/dist/esm/index.d.ts +3 -0
- package/dist/esm/index.js +3 -0
- package/dist/esm/segments/index.d.ts +2 -0
- package/dist/esm/segments/index.js +2 -0
- package/dist/esm/segments/segments.d.ts +22 -0
- package/dist/esm/segments/segments.js +43 -0
- package/dist/esm/segments/useSegmentedMask.d.ts +43 -0
- package/dist/esm/segments/useSegmentedMask.js +279 -0
- package/dist/esm/style.css +1350 -0
- package/dist/tsconfig.cjs.tsbuildinfo +1 -0
- package/dist/tsconfig.esm.tsbuildinfo +1 -0
- package/package.json +62 -0
- package/src/components/FieldColor/FieldColor.tsx +426 -0
- package/src/components/FieldColor/index.ts +1 -0
- package/src/components/FieldColor/styles.module.scss +120 -0
- package/src/components/FieldColor/utils.ts +17 -0
- package/src/components/FieldCombo/FieldCombo.tsx +495 -0
- package/src/components/FieldCombo/index.ts +1 -0
- package/src/components/FieldDate/FieldDate.tsx +658 -0
- package/src/components/FieldDate/constants.ts +11 -0
- package/src/components/FieldDate/index.ts +3 -0
- package/src/components/FieldDate/mask.ts +123 -0
- package/src/components/FieldDate/styles.module.scss +56 -0
- package/src/components/FieldDate/types.ts +124 -0
- package/src/components/FieldDate/utils.ts +7 -0
- package/src/components/FieldSecure/FieldSecure.tsx +398 -0
- package/src/components/FieldSecure/index.ts +1 -0
- package/src/components/FieldSelect/FieldSelect.tsx +689 -0
- package/src/components/FieldSelect/constants.ts +4 -0
- package/src/components/FieldSelect/index.ts +3 -0
- package/src/components/FieldSelect/styles.module.scss +104 -0
- package/src/components/FieldSelect/types.ts +212 -0
- package/src/components/FieldSelect/utils.ts +134 -0
- package/src/components/FieldSlider/FieldSlider.tsx +502 -0
- package/src/components/FieldSlider/index.ts +2 -0
- package/src/components/FieldSlider/styles.module.scss +47 -0
- package/src/components/FieldSlider/types.ts +5 -0
- package/src/components/FieldSlider/utils/generateAllowedValues.ts +13 -0
- package/src/components/FieldSlider/utils/getClosestMark.ts +21 -0
- package/src/components/FieldSlider/utils/getMarkLabel.ts +7 -0
- package/src/components/FieldSlider/utils/getTextFieldValue.ts +8 -0
- package/src/components/FieldSlider/utils/index.ts +5 -0
- package/src/components/FieldSlider/utils/isMarkObject.ts +7 -0
- package/src/components/FieldStepper/FieldStepper.tsx +457 -0
- package/src/components/FieldStepper/index.ts +1 -0
- package/src/components/FieldStepper/styles.module.scss +98 -0
- package/src/components/FieldStepper/utils.ts +44 -0
- package/src/components/FieldText/FieldText.tsx +27 -0
- package/src/components/FieldText/index.ts +1 -0
- package/src/components/FieldTextArea/FieldTextArea.tsx +374 -0
- package/src/components/FieldTextArea/index.ts +1 -0
- package/src/components/FieldTextArea/styles.module.scss +192 -0
- package/src/components/FieldTime/FieldTime.tsx +401 -0
- package/src/components/FieldTime/index.ts +2 -0
- package/src/components/FieldTime/mask.ts +22 -0
- package/src/components/FieldTime/styles.module.scss +3 -0
- package/src/components/index.ts +14 -0
- package/src/components/shared/helperComponents/FieldElementButton/FieldElementButton.tsx +127 -0
- package/src/components/shared/helperComponents/FieldElementButton/FieldElementButtonList.tsx +72 -0
- package/src/components/shared/helperComponents/FieldElementButton/constants.ts +10 -0
- package/src/components/shared/helperComponents/FieldElementButton/index.ts +4 -0
- package/src/components/shared/helperComponents/FieldElementButton/styles.module.scss +137 -0
- package/src/components/shared/helperComponents/FieldElementButton/types.ts +6 -0
- package/src/components/shared/helperComponents/FieldShell/FieldShell.tsx +100 -0
- package/src/components/shared/helperComponents/FieldShell/index.ts +1 -0
- package/src/components/shared/helperComponents/FieldShell/styles.module.scss +13 -0
- package/src/components/shared/hooks/useCopyButton.tsx +103 -0
- package/src/components/shared/index.ts +4 -0
- package/src/components/shared/styles.module.scss +409 -0
- package/src/components/shared/utils.ts +128 -0
- package/src/constants.ts +73 -0
- package/src/hooks/index.ts +1 -0
- package/src/hooks/useAdaptiveAutoFocus.ts +16 -0
- package/src/index.ts +3 -0
- package/src/segments/index.ts +2 -0
- package/src/segments/segments.ts +74 -0
- package/src/segments/useSegmentedMask.ts +361 -0
|
@@ -0,0 +1,457 @@
|
|
|
1
|
+
import { Button } from '@cloud-ru/ds-button';
|
|
2
|
+
import { Divider } from '@cloud-ru/ds-divider';
|
|
3
|
+
import { FieldDecorator, FieldDecoratorProps, SIZE, VALIDATION_STATE } from '@cloud-ru/ds-field-decorator';
|
|
4
|
+
import { CheckSVG, CopySVG, MinusSVG, PlusSVG } from '@cloud-ru/ds-icons/interface/system';
|
|
5
|
+
import { BUTTON_SIZE_MAP, InputPrivate, TYPE } from '@cloud-ru/ds-input-private';
|
|
6
|
+
import { Tooltip, TooltipProps } from '@cloud-ru/ds-tooltip';
|
|
7
|
+
import { extractSupportProps, useValueControl } from '@cloud-ru/ds-utils';
|
|
8
|
+
import cn from 'classnames';
|
|
9
|
+
import mergeRefs from 'merge-refs';
|
|
10
|
+
import {
|
|
11
|
+
ChangeEvent,
|
|
12
|
+
CSSProperties,
|
|
13
|
+
FocusEvent,
|
|
14
|
+
forwardRef,
|
|
15
|
+
KeyboardEvent,
|
|
16
|
+
MouseEvent,
|
|
17
|
+
ReactNode,
|
|
18
|
+
useCallback,
|
|
19
|
+
useEffect,
|
|
20
|
+
useMemo,
|
|
21
|
+
useRef,
|
|
22
|
+
useState,
|
|
23
|
+
} from 'react';
|
|
24
|
+
|
|
25
|
+
import { TEST_IDS } from '../../constants';
|
|
26
|
+
import { FieldLayoutPresets, useAdaptiveAutoFocus } from '../../hooks';
|
|
27
|
+
import { copyTextToClipboard, FieldElementButton, getAcrylicProps, VARIANT } from '../shared';
|
|
28
|
+
import fieldStyles from '../shared/styles.module.scss';
|
|
29
|
+
import styles from './styles.module.scss';
|
|
30
|
+
import { applyStep, clamp, defaultClampMaxText, defaultClampMinText, getDefaultValue } from './utils';
|
|
31
|
+
|
|
32
|
+
type FieldStepperDecoratorProps = Omit<FieldDecoratorProps, 'children'>;
|
|
33
|
+
|
|
34
|
+
type FieldStepperOwnProps = {
|
|
35
|
+
/** CSS-класс корня `FieldDecorator` */
|
|
36
|
+
className?: string;
|
|
37
|
+
/** CSS-класс оболочки поля */
|
|
38
|
+
fieldClassName?: string;
|
|
39
|
+
/** Значение (controlled-режим) */
|
|
40
|
+
value?: number;
|
|
41
|
+
/** Начальное значение (uncontrolled-режим). По умолчанию выводится из `min`/`max`. */
|
|
42
|
+
defaultValue?: number;
|
|
43
|
+
/** Колбек смены значения. Второй аргумент — событие, если изменение пришло из ручного ввода. */
|
|
44
|
+
onChange?(value: number, event?: ChangeEvent<HTMLInputElement>): void;
|
|
45
|
+
/** Минимум */
|
|
46
|
+
min?: number;
|
|
47
|
+
/** Максимум */
|
|
48
|
+
max?: number;
|
|
49
|
+
/**
|
|
50
|
+
* Шаг приращения
|
|
51
|
+
* @default 1
|
|
52
|
+
*/
|
|
53
|
+
step?: number;
|
|
54
|
+
/**
|
|
55
|
+
* Разрешить ввод значений вне `min`/`max`. Если `false`, на blur значение клампится.
|
|
56
|
+
* @default true
|
|
57
|
+
*/
|
|
58
|
+
allowMoreThanLimits?: boolean;
|
|
59
|
+
/** Поле выключено */
|
|
60
|
+
disabled?: boolean;
|
|
61
|
+
/** Только для чтения */
|
|
62
|
+
readonly?: boolean;
|
|
63
|
+
/** HTML id */
|
|
64
|
+
id?: string;
|
|
65
|
+
/** HTML name */
|
|
66
|
+
name?: string;
|
|
67
|
+
/** Автофокус. На mobile выключается адаптивно (см. `layoutPresets`) */
|
|
68
|
+
autoFocus?: boolean;
|
|
69
|
+
/**
|
|
70
|
+
* Переопределение адаптивных дефолтов по раскладке. Участвует `autoFocus`: на mobile он выключен
|
|
71
|
+
* (открывает клавиатуру без действия). Вернуть на mobile — `layoutPresets={{ mobile: { autoFocus: true } }}`.
|
|
72
|
+
*/
|
|
73
|
+
layoutPresets?: FieldLayoutPresets;
|
|
74
|
+
/**
|
|
75
|
+
* Фон поля (acrylic)
|
|
76
|
+
* @default true
|
|
77
|
+
*/
|
|
78
|
+
background?: boolean;
|
|
79
|
+
/**
|
|
80
|
+
* Показывать кнопку копирования значения (видна в readonly, при `!disabled`).
|
|
81
|
+
* @default true
|
|
82
|
+
*/
|
|
83
|
+
showCopyButton?: boolean;
|
|
84
|
+
/** Колбек после успешного копирования значения. */
|
|
85
|
+
onCopyButtonClick?(): void;
|
|
86
|
+
/** Префикс — текст или иконка слева от значения */
|
|
87
|
+
prefix?: ReactNode;
|
|
88
|
+
/** Постфикс — текст или иконка справа от значения (например, единица измерения) */
|
|
89
|
+
postfix?: ReactNode;
|
|
90
|
+
/** Тултип над кнопкой `+` */
|
|
91
|
+
plusButtonTooltip?: TooltipProps;
|
|
92
|
+
/** Тултип над кнопкой `−` */
|
|
93
|
+
minusButtonTooltip?: TooltipProps;
|
|
94
|
+
/**
|
|
95
|
+
* Тексты тултипа клампа (показывается на 2с после blur с выходом за `min`/`max`).
|
|
96
|
+
* @default { min: 'Значение должно быть больше либо равно {value}', max: 'Значение должно быть меньше либо равно {value}' }
|
|
97
|
+
*/
|
|
98
|
+
clampTooltipText?: {
|
|
99
|
+
min?: (value: number) => string;
|
|
100
|
+
max?: (value: number) => string;
|
|
101
|
+
};
|
|
102
|
+
/** Колбек фокуса */
|
|
103
|
+
onFocus?(event: FocusEvent<HTMLInputElement>): void;
|
|
104
|
+
/** Колбек блюра */
|
|
105
|
+
onBlur?(event: FocusEvent<HTMLInputElement>): void;
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
export type FieldStepperProps = FieldStepperDecoratorProps & FieldStepperOwnProps;
|
|
109
|
+
|
|
110
|
+
export const FieldStepper = forwardRef<HTMLInputElement, FieldStepperProps>(function FieldStepper(
|
|
111
|
+
{
|
|
112
|
+
label = '',
|
|
113
|
+
labelTooltip,
|
|
114
|
+
caption,
|
|
115
|
+
hint,
|
|
116
|
+
error,
|
|
117
|
+
validationState = VALIDATION_STATE.Default,
|
|
118
|
+
showHintIcon,
|
|
119
|
+
length,
|
|
120
|
+
required,
|
|
121
|
+
size = SIZE.M,
|
|
122
|
+
className,
|
|
123
|
+
fieldClassName,
|
|
124
|
+
background = true,
|
|
125
|
+
showCopyButton = true,
|
|
126
|
+
onCopyButtonClick,
|
|
127
|
+
value: valueProp,
|
|
128
|
+
defaultValue,
|
|
129
|
+
onChange,
|
|
130
|
+
min,
|
|
131
|
+
max,
|
|
132
|
+
step = 1,
|
|
133
|
+
allowMoreThanLimits = true,
|
|
134
|
+
disabled,
|
|
135
|
+
readonly: readOnly,
|
|
136
|
+
id,
|
|
137
|
+
name,
|
|
138
|
+
autoFocus,
|
|
139
|
+
layoutPresets,
|
|
140
|
+
prefix,
|
|
141
|
+
postfix,
|
|
142
|
+
plusButtonTooltip,
|
|
143
|
+
minusButtonTooltip,
|
|
144
|
+
clampTooltipText,
|
|
145
|
+
onFocus,
|
|
146
|
+
onBlur,
|
|
147
|
+
'data-test-id': dataTestId,
|
|
148
|
+
...rest
|
|
149
|
+
},
|
|
150
|
+
ref,
|
|
151
|
+
) {
|
|
152
|
+
const localRef = useRef<HTMLInputElement>(null);
|
|
153
|
+
const copyButtonRef = useRef<HTMLButtonElement>(null);
|
|
154
|
+
const [focusVisible, setFocusVisible] = useState(false);
|
|
155
|
+
const [hover, setHover] = useState(false);
|
|
156
|
+
const [clampTooltip, setClampTooltip] = useState<string | null>(null);
|
|
157
|
+
const [copied, setCopied] = useState(false);
|
|
158
|
+
const clampTimerRef = useRef<ReturnType<typeof setTimeout> | undefined>(undefined);
|
|
159
|
+
const copiedTimerRef = useRef<ReturnType<typeof setTimeout> | undefined>(undefined);
|
|
160
|
+
|
|
161
|
+
const resolvedAutoFocus = useAdaptiveAutoFocus(autoFocus, layoutPresets);
|
|
162
|
+
|
|
163
|
+
useEffect(
|
|
164
|
+
() => () => {
|
|
165
|
+
if (clampTimerRef.current) {
|
|
166
|
+
clearTimeout(clampTimerRef.current);
|
|
167
|
+
}
|
|
168
|
+
if (copiedTimerRef.current) {
|
|
169
|
+
clearTimeout(copiedTimerRef.current);
|
|
170
|
+
}
|
|
171
|
+
},
|
|
172
|
+
[],
|
|
173
|
+
);
|
|
174
|
+
|
|
175
|
+
// controlled/uncontrolled: внутреннее состояние при отсутствии `value`; событие в `onChange`
|
|
176
|
+
// (ручной ввод) пробрасываем сами, useValueControl передаёт только значение.
|
|
177
|
+
const [value = getDefaultValue(min, max), setValue] = useValueControl<number>({ value: valueProp, defaultValue });
|
|
178
|
+
|
|
179
|
+
const effectiveValidationState = useMemo(
|
|
180
|
+
() => (error ? VALIDATION_STATE.Error : validationState),
|
|
181
|
+
[error, validationState],
|
|
182
|
+
);
|
|
183
|
+
|
|
184
|
+
const isMinusDisabled = (typeof min === 'number' && value <= min) || disabled || readOnly;
|
|
185
|
+
const isPlusDisabled = (typeof max === 'number' && value >= max) || disabled || readOnly;
|
|
186
|
+
|
|
187
|
+
// Кнопки −/+ и ручной ввод приводятся к единой модели относительно `allowMoreThanLimits`:
|
|
188
|
+
// при `false` значение клампится сразу, при `true` остаётся как есть (кламп откладывается до blur).
|
|
189
|
+
const emitValue = useCallback(
|
|
190
|
+
(next: number, event?: ChangeEvent<HTMLInputElement>) => {
|
|
191
|
+
const resolved = allowMoreThanLimits ? next : clamp(next, min, max);
|
|
192
|
+
setValue(resolved);
|
|
193
|
+
onChange?.(resolved, event);
|
|
194
|
+
},
|
|
195
|
+
[allowMoreThanLimits, max, min, onChange, setValue],
|
|
196
|
+
);
|
|
197
|
+
|
|
198
|
+
const handleMinus = useCallback(() => {
|
|
199
|
+
emitValue(applyStep(value, -step));
|
|
200
|
+
}, [emitValue, step, value]);
|
|
201
|
+
|
|
202
|
+
const handlePlus = useCallback(() => {
|
|
203
|
+
emitValue(applyStep(value, step));
|
|
204
|
+
}, [emitValue, step, value]);
|
|
205
|
+
|
|
206
|
+
const handleInputChange = useCallback(
|
|
207
|
+
(raw: string, event?: ChangeEvent<HTMLInputElement>) => {
|
|
208
|
+
if (raw === '' || raw === '-') {
|
|
209
|
+
return;
|
|
210
|
+
}
|
|
211
|
+
const parsed = Number(raw);
|
|
212
|
+
if (Number.isNaN(parsed)) {
|
|
213
|
+
return;
|
|
214
|
+
}
|
|
215
|
+
// Во время набора значение не клампим даже при allowMoreThanLimits=false —
|
|
216
|
+
// кламп откладывается до blur (паритет с легаси), там же показывается clampTooltip.
|
|
217
|
+
setValue(parsed);
|
|
218
|
+
onChange?.(parsed, event);
|
|
219
|
+
},
|
|
220
|
+
[onChange, setValue],
|
|
221
|
+
);
|
|
222
|
+
|
|
223
|
+
const showClampTooltip = useCallback((message: string) => {
|
|
224
|
+
setClampTooltip(message);
|
|
225
|
+
if (clampTimerRef.current) {
|
|
226
|
+
clearTimeout(clampTimerRef.current);
|
|
227
|
+
}
|
|
228
|
+
clampTimerRef.current = setTimeout(() => setClampTooltip(null), 2000);
|
|
229
|
+
}, []);
|
|
230
|
+
|
|
231
|
+
const handleBlur = useCallback(
|
|
232
|
+
(event: FocusEvent<HTMLInputElement>) => {
|
|
233
|
+
if (!allowMoreThanLimits) {
|
|
234
|
+
if (typeof max === 'number' && value > max) {
|
|
235
|
+
setValue(max);
|
|
236
|
+
onChange?.(max);
|
|
237
|
+
showClampTooltip((clampTooltipText?.max ?? defaultClampMaxText)(max));
|
|
238
|
+
} else if (typeof min === 'number' && value < min) {
|
|
239
|
+
setValue(min);
|
|
240
|
+
onChange?.(min);
|
|
241
|
+
showClampTooltip((clampTooltipText?.min ?? defaultClampMinText)(min));
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
onBlur?.(event);
|
|
245
|
+
},
|
|
246
|
+
[allowMoreThanLimits, clampTooltipText, max, min, onBlur, onChange, setValue, showClampTooltip, value],
|
|
247
|
+
);
|
|
248
|
+
|
|
249
|
+
const handleFocus = useCallback(
|
|
250
|
+
(event: FocusEvent<HTMLInputElement>) => {
|
|
251
|
+
setFocusVisible(event.target.matches(':focus-visible'));
|
|
252
|
+
if (clampTimerRef.current) {
|
|
253
|
+
clearTimeout(clampTimerRef.current);
|
|
254
|
+
}
|
|
255
|
+
setClampTooltip(null);
|
|
256
|
+
onFocus?.(event);
|
|
257
|
+
},
|
|
258
|
+
[onFocus],
|
|
259
|
+
);
|
|
260
|
+
|
|
261
|
+
// Ширина инпута по числу символов значения (минимум 2ch), задаётся через `ch`-юниты в CSS-переменной —
|
|
262
|
+
// без px-хардкода и подгонки под ширину глифа.
|
|
263
|
+
const inputWidthCh = Math.max(2, String(value).length);
|
|
264
|
+
|
|
265
|
+
const handleMouseEnter = useCallback(() => {
|
|
266
|
+
if (!readOnly) {
|
|
267
|
+
setHover(true);
|
|
268
|
+
}
|
|
269
|
+
}, [readOnly]);
|
|
270
|
+
|
|
271
|
+
const handleMouseLeave = useCallback(() => {
|
|
272
|
+
setHover(false);
|
|
273
|
+
}, []);
|
|
274
|
+
|
|
275
|
+
const showCopyUi = Boolean(showCopyButton && readOnly && !disabled);
|
|
276
|
+
|
|
277
|
+
const handleCopy = useCallback(
|
|
278
|
+
(event: MouseEvent<HTMLButtonElement>) => {
|
|
279
|
+
event.preventDefault();
|
|
280
|
+
event.stopPropagation();
|
|
281
|
+
copyTextToClipboard(String(value)).then(copied => {
|
|
282
|
+
if (!copied) {
|
|
283
|
+
return;
|
|
284
|
+
}
|
|
285
|
+
onCopyButtonClick?.();
|
|
286
|
+
setCopied(true);
|
|
287
|
+
if (copiedTimerRef.current) {
|
|
288
|
+
clearTimeout(copiedTimerRef.current);
|
|
289
|
+
}
|
|
290
|
+
copiedTimerRef.current = setTimeout(() => setCopied(false), 2000);
|
|
291
|
+
});
|
|
292
|
+
},
|
|
293
|
+
[onCopyButtonClick, value],
|
|
294
|
+
);
|
|
295
|
+
|
|
296
|
+
// Клавиатурный путь к copy в readonly (кнопка с tabIndex −1 недостижима по Tab): ArrowRight из
|
|
297
|
+
// инпута → copy, ArrowLeft с copy → назад. input type=number не даёт selection API — гейт только
|
|
298
|
+
// по readonly. Кнопки −/+ остаются вне пути (tabIndex −1, паритет с легаси ButtonFunction).
|
|
299
|
+
const handleInputKeyDown = useCallback(
|
|
300
|
+
(event: KeyboardEvent<HTMLInputElement>) => {
|
|
301
|
+
if (event.key === 'ArrowRight' && readOnly && showCopyUi && copyButtonRef.current) {
|
|
302
|
+
event.preventDefault();
|
|
303
|
+
copyButtonRef.current.focus();
|
|
304
|
+
}
|
|
305
|
+
},
|
|
306
|
+
[readOnly, showCopyUi],
|
|
307
|
+
);
|
|
308
|
+
|
|
309
|
+
const handleCopyButtonKeyDown = useCallback((event: KeyboardEvent<HTMLButtonElement>) => {
|
|
310
|
+
if (event.key === 'ArrowLeft') {
|
|
311
|
+
event.preventDefault();
|
|
312
|
+
localRef.current?.focus();
|
|
313
|
+
}
|
|
314
|
+
}, []);
|
|
315
|
+
|
|
316
|
+
const renderStepButton = (variant: 'minus' | 'plus') => {
|
|
317
|
+
const isMinus = variant === 'minus';
|
|
318
|
+
const tooltip = isMinus ? minusButtonTooltip : plusButtonTooltip;
|
|
319
|
+
const button = (
|
|
320
|
+
<FieldElementButton
|
|
321
|
+
variant={isMinus ? VARIANT.Before : VARIANT.After}
|
|
322
|
+
size={size}
|
|
323
|
+
action={isMinus ? <MinusSVG /> : <PlusSVG />}
|
|
324
|
+
onClick={isMinus ? handleMinus : handlePlus}
|
|
325
|
+
disabled={isMinus ? isMinusDisabled : isPlusDisabled}
|
|
326
|
+
tabIndex={-1}
|
|
327
|
+
data-test-id={isMinus ? TEST_IDS.fieldStepperMinus : TEST_IDS.fieldStepperPlus}
|
|
328
|
+
/>
|
|
329
|
+
);
|
|
330
|
+
|
|
331
|
+
return tooltip ? (
|
|
332
|
+
<Tooltip {...tooltip} triggerClassName={styles.stepButtonTrigger}>
|
|
333
|
+
{button}
|
|
334
|
+
</Tooltip>
|
|
335
|
+
) : (
|
|
336
|
+
button
|
|
337
|
+
);
|
|
338
|
+
};
|
|
339
|
+
|
|
340
|
+
const shell = (
|
|
341
|
+
<div
|
|
342
|
+
className={cn(fieldStyles.fieldWrapper, fieldClassName)}
|
|
343
|
+
data-size={size}
|
|
344
|
+
data-validation-state={effectiveValidationState}
|
|
345
|
+
data-disabled={disabled || undefined}
|
|
346
|
+
data-readonly={readOnly || undefined}
|
|
347
|
+
data-withbackground={background || undefined}
|
|
348
|
+
data-focusvisible={focusVisible || undefined}
|
|
349
|
+
data-hover={!readOnly && hover ? true : undefined}
|
|
350
|
+
onMouseEnter={handleMouseEnter}
|
|
351
|
+
onMouseLeave={handleMouseLeave}
|
|
352
|
+
>
|
|
353
|
+
<div className={fieldStyles.backgroundWrapper}>
|
|
354
|
+
<div
|
|
355
|
+
className={fieldStyles.materialLayer}
|
|
356
|
+
{...getAcrylicProps({
|
|
357
|
+
validationState: effectiveValidationState,
|
|
358
|
+
disabled,
|
|
359
|
+
readonly: readOnly,
|
|
360
|
+
hover,
|
|
361
|
+
focusVisible,
|
|
362
|
+
})}
|
|
363
|
+
>
|
|
364
|
+
<div className={fieldStyles.acrylicBg} aria-hidden />
|
|
365
|
+
</div>
|
|
366
|
+
<div className={fieldStyles.borderStateLayer} data-state='borderOnBackground' />
|
|
367
|
+
<div className={fieldStyles.focusLayer} />
|
|
368
|
+
</div>
|
|
369
|
+
<div className={fieldStyles.elementWrapperBefore}>
|
|
370
|
+
{renderStepButton('minus')}
|
|
371
|
+
<div className={fieldStyles.lineWrapper} data-outline={background || undefined}>
|
|
372
|
+
<Divider orientation='vertical' variant='regular' />
|
|
373
|
+
</div>
|
|
374
|
+
</div>
|
|
375
|
+
<div className={fieldStyles.fieldContainer}>
|
|
376
|
+
<div className={fieldStyles.contentWrapper}>
|
|
377
|
+
<div className={styles.valueArea}>
|
|
378
|
+
{prefix && <span className={styles.affix}>{prefix}</span>}
|
|
379
|
+
<div className={styles.inputWrapper} style={{ '--field-stepper-input-ch': inputWidthCh } as CSSProperties}>
|
|
380
|
+
<InputPrivate
|
|
381
|
+
ref={mergeRefs(ref, localRef)}
|
|
382
|
+
className={cn(fieldStyles.fieldInput, styles.input)}
|
|
383
|
+
value={String(value)}
|
|
384
|
+
type={TYPE.Number}
|
|
385
|
+
disabled={disabled}
|
|
386
|
+
readonly={readOnly}
|
|
387
|
+
onChange={handleInputChange}
|
|
388
|
+
onFocus={handleFocus}
|
|
389
|
+
onBlur={handleBlur}
|
|
390
|
+
onKeyDown={handleInputKeyDown}
|
|
391
|
+
id={id}
|
|
392
|
+
name={name}
|
|
393
|
+
min={min}
|
|
394
|
+
max={max}
|
|
395
|
+
step={step}
|
|
396
|
+
autoFocus={resolvedAutoFocus}
|
|
397
|
+
data-test-id={TEST_IDS.fieldStepperInput}
|
|
398
|
+
{...extractSupportProps(rest)}
|
|
399
|
+
/>
|
|
400
|
+
</div>
|
|
401
|
+
{postfix && <span className={styles.affix}>{postfix}</span>}
|
|
402
|
+
</div>
|
|
403
|
+
</div>
|
|
404
|
+
</div>
|
|
405
|
+
<div className={fieldStyles.elementWrapperAfter}>
|
|
406
|
+
{showCopyUi && (
|
|
407
|
+
<span className={styles.copyButton}>
|
|
408
|
+
<Button
|
|
409
|
+
innerRef={copyButtonRef}
|
|
410
|
+
type='button'
|
|
411
|
+
view='function'
|
|
412
|
+
appearance='neutral'
|
|
413
|
+
size={BUTTON_SIZE_MAP[size]}
|
|
414
|
+
icon={copied ? <CheckSVG /> : <CopySVG />}
|
|
415
|
+
onClick={handleCopy}
|
|
416
|
+
onKeyDown={handleCopyButtonKeyDown}
|
|
417
|
+
tabIndex={-1}
|
|
418
|
+
data-test-id={TEST_IDS.fieldStepperCopyButton}
|
|
419
|
+
/>
|
|
420
|
+
</span>
|
|
421
|
+
)}
|
|
422
|
+
<div className={fieldStyles.lineWrapper} data-outline={background || undefined}>
|
|
423
|
+
<Divider orientation='vertical' variant='regular' />
|
|
424
|
+
</div>
|
|
425
|
+
{renderStepButton('plus')}
|
|
426
|
+
</div>
|
|
427
|
+
</div>
|
|
428
|
+
);
|
|
429
|
+
|
|
430
|
+
return (
|
|
431
|
+
<FieldDecorator
|
|
432
|
+
className={className}
|
|
433
|
+
label={label}
|
|
434
|
+
labelTooltip={labelTooltip}
|
|
435
|
+
caption={caption}
|
|
436
|
+
hint={hint}
|
|
437
|
+
error={error}
|
|
438
|
+
validationState={effectiveValidationState}
|
|
439
|
+
showHintIcon={showHintIcon}
|
|
440
|
+
length={length}
|
|
441
|
+
required={required}
|
|
442
|
+
labelFor={id}
|
|
443
|
+
disabled={disabled}
|
|
444
|
+
readonly={readOnly}
|
|
445
|
+
size={size}
|
|
446
|
+
data-test-id={dataTestId}
|
|
447
|
+
>
|
|
448
|
+
{clampTooltip ? (
|
|
449
|
+
<Tooltip tip={clampTooltip} open data-test-id={TEST_IDS.fieldStepperLimitTooltip}>
|
|
450
|
+
{shell}
|
|
451
|
+
</Tooltip>
|
|
452
|
+
) : (
|
|
453
|
+
shell
|
|
454
|
+
)}
|
|
455
|
+
</FieldDecorator>
|
|
456
|
+
);
|
|
457
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './FieldStepper';
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
@use '@cloud-ru/ds-figma-variables/build/scss/styles/styles.module' as base;
|
|
2
|
+
@use '@cloud-ru/ds-figma-variables/build/scss/components/field.module' as field;
|
|
3
|
+
|
|
4
|
+
$sizes: 's', 'm', 'l';
|
|
5
|
+
|
|
6
|
+
// Figma-typo-мост: stepper inputLine.gap для size=m ссылается на токен size=s (=2px),
|
|
7
|
+
// а не на size=m (=4px). s→s(2), m→s(2), l→l(6).
|
|
8
|
+
$inputLineGapSizes: (
|
|
9
|
+
's': 's',
|
|
10
|
+
'm': 's',
|
|
11
|
+
'l': 'l',
|
|
12
|
+
);
|
|
13
|
+
|
|
14
|
+
// Tooltip оборачивает −/+ в block-триггер: растягиваем его на всю высоту слота,
|
|
15
|
+
// чтобы buttonField-кнопка внутри занимала полную высоту поля (как в FieldText).
|
|
16
|
+
.stepButtonTrigger {
|
|
17
|
+
display: flex;
|
|
18
|
+
align-items: stretch;
|
|
19
|
+
height: 100%;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
// Copy-кнопка (readonly) центрируется во всю высоту слота, как и step-триггеры.
|
|
23
|
+
.copyButton {
|
|
24
|
+
display: flex;
|
|
25
|
+
align-items: center;
|
|
26
|
+
height: 100%;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
.valueArea {
|
|
30
|
+
display: flex;
|
|
31
|
+
flex: 1 1 auto;
|
|
32
|
+
align-items: baseline;
|
|
33
|
+
justify-content: center;
|
|
34
|
+
|
|
35
|
+
min-width: 0;
|
|
36
|
+
|
|
37
|
+
color: base.$sn-theme-color-available-version-textMain;
|
|
38
|
+
text-align: center;
|
|
39
|
+
|
|
40
|
+
@each $size, $tokenSize in $inputLineGapSizes {
|
|
41
|
+
[data-size='#{$size}'] & {
|
|
42
|
+
gap: base.simple-var(
|
|
43
|
+
field.$field,
|
|
44
|
+
'anatomy',
|
|
45
|
+
'size',
|
|
46
|
+
$tokenSize,
|
|
47
|
+
'fieldContainer',
|
|
48
|
+
'contentWrapper',
|
|
49
|
+
'inputLine',
|
|
50
|
+
'gap'
|
|
51
|
+
);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
.inputWrapper {
|
|
57
|
+
display: flex;
|
|
58
|
+
flex: 0 0 auto;
|
|
59
|
+
|
|
60
|
+
// ширина по числу символов значения (var выставляется компонентом), минимум 2ch.
|
|
61
|
+
// +1ch — запас под каретку и субпиксельное округление: без него значение шириной ровно
|
|
62
|
+
// N символов (напр. «100» = 3ch) обрезается ellipsis'ом инпута до «1…».
|
|
63
|
+
width: calc((var(--field-stepper-input-ch, 2) + 1) * 1ch);
|
|
64
|
+
max-width: 100%;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
.input {
|
|
68
|
+
flex: 0 0 auto;
|
|
69
|
+
|
|
70
|
+
width: 100%;
|
|
71
|
+
min-width: 1ch;
|
|
72
|
+
|
|
73
|
+
text-align: center;
|
|
74
|
+
|
|
75
|
+
/* stylelint-disable-next-line property-no-vendor-prefix */
|
|
76
|
+
-moz-appearance: textfield;
|
|
77
|
+
|
|
78
|
+
&::-webkit-outer-spin-button,
|
|
79
|
+
&::-webkit-inner-spin-button {
|
|
80
|
+
margin: 0;
|
|
81
|
+
|
|
82
|
+
/* stylelint-disable-next-line property-no-vendor-prefix */
|
|
83
|
+
-webkit-appearance: none;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
.affix {
|
|
88
|
+
flex-shrink: 0;
|
|
89
|
+
color: base.$sn-theme-color-available-version-textTertiary;
|
|
90
|
+
|
|
91
|
+
// DS-типографика по size из корневого fieldWrapper. Без этого фолбэк на Nunito Sans 16/24
|
|
92
|
+
// визуально не сходится с инпутом.
|
|
93
|
+
@each $size in $sizes {
|
|
94
|
+
[data-size='#{$size}'] & {
|
|
95
|
+
@include base.composite-var(base.$sn-regular, 'body', $size);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/** Начальное значение для uncontrolled-режима: выводится из `min`/`max`, иначе `0`. */
|
|
2
|
+
export function getDefaultValue(min?: number, max?: number): number {
|
|
3
|
+
if (typeof min === 'number' && min > 0) {
|
|
4
|
+
return min;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
if (typeof max === 'number' && max < 0) {
|
|
8
|
+
return max;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
return 0;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/** Зажимает значение в диапазон `[min, max]` (границы опциональны). */
|
|
15
|
+
export function clamp(value: number, min?: number, max?: number): number {
|
|
16
|
+
let next = value;
|
|
17
|
+
if (typeof min === 'number') {
|
|
18
|
+
next = Math.max(next, min);
|
|
19
|
+
}
|
|
20
|
+
if (typeof max === 'number') {
|
|
21
|
+
next = Math.min(next, max);
|
|
22
|
+
}
|
|
23
|
+
return next;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/** Приращение значения на `step` с компенсацией погрешности float (округление до 10 знаков). */
|
|
27
|
+
export function applyStep(value: number, step: number): number {
|
|
28
|
+
const sum = value + step;
|
|
29
|
+
return Number(sum.toFixed(10));
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Дефолтный текст тултипа клампа для нижней границы.
|
|
34
|
+
*/
|
|
35
|
+
export function defaultClampMinText(value: number): string {
|
|
36
|
+
return `Значение должно быть больше либо равно ${value}`;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Дефолтный текст тултипа клампа для верхней границы.
|
|
41
|
+
*/
|
|
42
|
+
export function defaultClampMaxText(value: number): string {
|
|
43
|
+
return `Значение должно быть меньше либо равно ${value}`;
|
|
44
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { forwardRef } from 'react';
|
|
2
|
+
|
|
3
|
+
import { TEST_IDS } from '../../constants';
|
|
4
|
+
import { FieldCombo, FieldComboProps } from '../FieldCombo';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Пропсы простого текстового поля. От `FieldCombo` отличается отсутствием слотов:
|
|
8
|
+
* префикса, постфикса, иконок и кнопок-сегментов. Нужен слот — берите `FieldCombo`.
|
|
9
|
+
*/
|
|
10
|
+
export type FieldTextProps = Omit<
|
|
11
|
+
FieldComboProps,
|
|
12
|
+
'prefix' | 'postfix' | 'iconBefore' | 'iconAfter' | 'prefixIcon' | 'elementBefore' | 'elementAfter' | 'innerTestIds'
|
|
13
|
+
>;
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Текстовое поле: заголовок, подсказка, плейсхолдер, значение и кнопки очистки и копирования.
|
|
17
|
+
* Слоты вокруг строки ввода не поддерживает — для них есть `FieldCombo`.
|
|
18
|
+
*/
|
|
19
|
+
export const FieldText = forwardRef<HTMLInputElement, FieldTextProps>(function FieldText(props, ref) {
|
|
20
|
+
return (
|
|
21
|
+
<FieldCombo
|
|
22
|
+
{...props}
|
|
23
|
+
ref={ref}
|
|
24
|
+
innerTestIds={{ shell: TEST_IDS.fieldTextShell, input: TEST_IDS.fieldTextInput }}
|
|
25
|
+
/>
|
|
26
|
+
);
|
|
27
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './FieldText';
|