@gravity-ui/dynamic-forms 5.15.0 → 5.17.1

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 (37) hide show
  1. package/build/cjs/lib/kit/components/Inputs/ColorPicker/ColorPicker.js +21 -0
  2. package/build/cjs/lib/kit/components/Inputs/ColorPicker/index.js +4 -0
  3. package/build/cjs/lib/kit/components/Inputs/index.js +1 -0
  4. package/build/cjs/lib/kit/components/Views/ColorPickerView/ColorPickerView.css +31 -0
  5. package/build/cjs/lib/kit/components/Views/ColorPickerView/ColorPickerView.js +12 -0
  6. package/build/cjs/lib/kit/components/Views/ColorPickerView/index.js +4 -0
  7. package/build/cjs/lib/kit/components/Views/index.js +1 -0
  8. package/build/cjs/lib/kit/constants/config.js +3 -0
  9. package/build/cjs/lib/kit/i18n/en.json +2 -0
  10. package/build/cjs/lib/kit/i18n/ru.json +2 -0
  11. package/build/cjs/lib/kit/validators/helpers.js +70 -1
  12. package/build/cjs/lib/kit/validators/messages.js +6 -0
  13. package/build/cjs/lib/kit/validators/validators.js +68 -1
  14. package/build/esm/lib/core/types/specs.d.ts +5 -0
  15. package/build/esm/lib/kit/components/Inputs/ColorPicker/ColorPicker.d.ts +6 -0
  16. package/build/esm/lib/kit/components/Inputs/ColorPicker/ColorPicker.js +16 -0
  17. package/build/esm/lib/kit/components/Inputs/ColorPicker/index.d.ts +1 -0
  18. package/build/esm/lib/kit/components/Inputs/ColorPicker/index.js +1 -0
  19. package/build/esm/lib/kit/components/Inputs/index.d.ts +1 -0
  20. package/build/esm/lib/kit/components/Inputs/index.js +1 -0
  21. package/build/esm/lib/kit/components/Views/ColorPickerView/ColorPickerView.css +31 -0
  22. package/build/esm/lib/kit/components/Views/ColorPickerView/ColorPickerView.d.ts +3 -0
  23. package/build/esm/lib/kit/components/Views/ColorPickerView/ColorPickerView.js +8 -0
  24. package/build/esm/lib/kit/components/Views/ColorPickerView/index.d.ts +1 -0
  25. package/build/esm/lib/kit/components/Views/ColorPickerView/index.js +1 -0
  26. package/build/esm/lib/kit/components/Views/index.d.ts +1 -0
  27. package/build/esm/lib/kit/components/Views/index.js +1 -0
  28. package/build/esm/lib/kit/constants/config.js +5 -2
  29. package/build/esm/lib/kit/i18n/en.json +2 -0
  30. package/build/esm/lib/kit/i18n/ru.json +2 -0
  31. package/build/esm/lib/kit/validators/helpers.d.ts +5 -0
  32. package/build/esm/lib/kit/validators/helpers.js +68 -0
  33. package/build/esm/lib/kit/validators/messages.js +6 -0
  34. package/build/esm/lib/kit/validators/types.d.ts +2 -0
  35. package/build/esm/lib/kit/validators/validators.d.ts +4 -1
  36. package/build/esm/lib/kit/validators/validators.js +67 -1
  37. package/package.json +2 -2
@@ -0,0 +1,21 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ColorPicker = void 0;
4
+ const tslib_1 = require("tslib");
5
+ const react_1 = tslib_1.__importDefault(require("react"));
6
+ const unstable_1 = require("@gravity-ui/uikit/unstable");
7
+ const ColorPicker = ({ input, spec, inputProps }) => {
8
+ const { value, onChange, onBlur, onFocus } = input;
9
+ const handleOpenChange = react_1.default.useCallback((open) => {
10
+ var _a;
11
+ (_a = inputProps === null || inputProps === void 0 ? void 0 : inputProps.onOpenChange) === null || _a === void 0 ? void 0 : _a.call(inputProps, open);
12
+ if (open) {
13
+ onFocus();
14
+ }
15
+ else {
16
+ onBlur();
17
+ }
18
+ }, [inputProps, onBlur, onFocus]);
19
+ return (react_1.default.createElement(unstable_1.unstable_ColorPicker, Object.assign({}, inputProps, { value: value || '', onUpdate: onChange, onOpenChange: handleOpenChange, disabled: spec.viewSpec.disabled })));
20
+ };
21
+ exports.ColorPicker = ColorPicker;
@@ -0,0 +1,4 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const tslib_1 = require("tslib");
4
+ tslib_1.__exportStar(require("./ColorPicker"), exports);
@@ -7,6 +7,7 @@ tslib_1.__exportStar(require("./Checkbox"), exports);
7
7
  tslib_1.__exportStar(require("./CheckboxGroup"), exports);
8
8
  tslib_1.__exportStar(require("./FileInput"), exports);
9
9
  tslib_1.__exportStar(require("./DateInput"), exports);
10
+ tslib_1.__exportStar(require("./ColorPicker"), exports);
10
11
  tslib_1.__exportStar(require("./MonacoInput"), exports);
11
12
  tslib_1.__exportStar(require("./MultiOneOf"), exports);
12
13
  tslib_1.__exportStar(require("./MultiSelect"), exports);
@@ -0,0 +1,31 @@
1
+ /*
2
+ @mixin variables {
3
+ --g-spacing-base: 4px;
4
+
5
+ --g-spacing-0: calc(var(--g-spacing-base) * 0); //0px
6
+ --g-spacing-half: calc(var(--g-spacing-base) * 0.5); //2px
7
+ --g-spacing-1: var(--g-spacing-base); // 4px
8
+ --g-spacing-2: calc(var(--g-spacing-base) * 2); // 8px
9
+ --g-spacing-3: calc(var(--g-spacing-base) * 3); // 12px
10
+ --g-spacing-4: calc(var(--g-spacing-base) * 4); // 16px
11
+ --g-spacing-5: calc(var(--g-spacing-base) * 5); // 20px
12
+ --g-spacing-6: calc(var(--g-spacing-base) * 6); // 24px
13
+ --g-spacing-7: calc(var(--g-spacing-base) * 7); // 28px
14
+ --g-spacing-8: calc(var(--g-spacing-base) * 8); // 32px
15
+ --g-spacing-9: calc(var(--g-spacing-base) * 9); // 36px
16
+ --g-spacing-10: calc(var(--g-spacing-base) * 10); // 40px
17
+ }
18
+ */
19
+ /* Border-radius */
20
+ .df-color-picker-view {
21
+ display: flex;
22
+ align-items: center;
23
+ gap: var(--g-spacing-2);
24
+ }
25
+ .df-color-picker-view__preview {
26
+ width: 16px;
27
+ height: 16px;
28
+ flex-shrink: 0;
29
+ border: 1px solid var(--g-color-line-generic);
30
+ border-radius: var(--g-border-radius-xs);
31
+ }
@@ -0,0 +1,12 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ColorPickerView = void 0;
4
+ const tslib_1 = require("tslib");
5
+ const react_1 = tslib_1.__importDefault(require("react"));
6
+ const components_1 = require("../../../components");
7
+ const utils_1 = require("../../../utils");
8
+ const b = (0, utils_1.block)('color-picker-view');
9
+ const ColorPickerView = ({ value = '' }) => (react_1.default.createElement("div", { className: b() },
10
+ value ? react_1.default.createElement("div", { className: b('preview'), style: { backgroundColor: value } }) : null,
11
+ react_1.default.createElement(components_1.LongValue, { value: value })));
12
+ exports.ColorPickerView = ColorPickerView;
@@ -0,0 +1,4 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const tslib_1 = require("tslib");
4
+ tslib_1.__exportStar(require("./ColorPickerView"), exports);
@@ -6,6 +6,7 @@ tslib_1.__exportStar(require("./BaseView"), exports);
6
6
  tslib_1.__exportStar(require("./CheckboxGroupView"), exports);
7
7
  tslib_1.__exportStar(require("./CardOneOfView"), exports);
8
8
  tslib_1.__exportStar(require("./FileInputView"), exports);
9
+ tslib_1.__exportStar(require("./ColorPickerView"), exports);
9
10
  tslib_1.__exportStar(require("./MonacoInputView"), exports);
10
11
  tslib_1.__exportStar(require("./MultiOneOfView"), exports);
11
12
  tslib_1.__exportStar(require("./MultiSelectView"), exports);
@@ -103,6 +103,7 @@ exports.dynamicConfig = {
103
103
  base: { Component: components_1.Text },
104
104
  file_input: { Component: components_1.FileInput },
105
105
  date_input: { Component: components_1.DateInput },
106
+ color_picker: { Component: components_1.ColorPicker },
106
107
  number_with_scale: { Component: components_1.NumberWithScale },
107
108
  monaco_input: { Component: components_1.MonacoInput },
108
109
  text_content: { Component: components_1.TextContent, independent: true },
@@ -123,6 +124,7 @@ exports.dynamicConfig = {
123
124
  validators: {
124
125
  base: (0, validators_1.getStringValidator)(),
125
126
  number: (0, validators_1.getNumberValidator)(),
127
+ number_with_scale: (0, validators_1.getNumberWithScaleValidator)(),
126
128
  },
127
129
  },
128
130
  };
@@ -213,6 +215,7 @@ exports.dynamicViewConfig = {
213
215
  select: { Component: components_1.BaseView },
214
216
  base: { Component: components_1.BaseView },
215
217
  date_input: { Component: components_1.DateView },
218
+ color_picker: { Component: components_1.ColorPickerView },
216
219
  file_input: { Component: components_1.FileInputView },
217
220
  number_with_scale: { Component: components_1.NumberWithScaleView },
218
221
  monaco_input: { Component: components_1.MonacoView },
@@ -37,6 +37,8 @@
37
37
  ],
38
38
  "label_error-min-number": "Value must be an integer no less than {{count}}",
39
39
  "label_error-max-number": "Value must not be greater than {{count}}",
40
+ "label_error-min-number-with-scale": "Value must be no less than {{count}} {{scale}}",
41
+ "label_error-max-number-with-scale": "Value must not be greater than {{count}} {{scale}}",
40
42
  "label_error-space-end": "Value must not end with a space",
41
43
  "label_error-space-start": "Value must not start with a space",
42
44
  "label_error-zero-start": "Value must not start with a zero",
@@ -37,6 +37,8 @@
37
37
  ],
38
38
  "label_error-min-number": "Значение должно быть числом не меньше {{count}}",
39
39
  "label_error-max-number": "Значение должно быть числом не больше {{count}}",
40
+ "label_error-min-number-with-scale": "Значение должно быть не меньше {{count}} {{scale}}",
41
+ "label_error-max-number-with-scale": "Значение должно быть не больше {{count}} {{scale}}",
40
42
  "label_error-space-end": "Значение не должно заканчиваться пробелом",
41
43
  "label_error-space-start": "Значение не должно начинаться с пробела",
42
44
  "label_error-zero-start": "Значение не должно начинаться с нуля",
@@ -1,6 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.isFloat = exports.isInt = void 0;
3
+ exports.getScaledLimit = exports.isFloat = exports.isInt = void 0;
4
+ const utils_1 = require("../utils");
4
5
  const isInt = (value) => {
5
6
  const regex = /^(?:[-+]?(?:0|[1-9]\d*))$/;
6
7
  return regex.test(value);
@@ -11,3 +12,71 @@ const isFloat = (value) => {
11
12
  return regex.test(value);
12
13
  };
13
14
  exports.isFloat = isFloat;
15
+ const scaleValue = (value, scaleFactor, defaultFactor) => {
16
+ if (defaultFactor === scaleFactor) {
17
+ return String(value);
18
+ }
19
+ const valueStr = String(value);
20
+ try {
21
+ if (BigInt(scaleFactor) > BigInt(defaultFactor)) {
22
+ const ratio = (0, utils_1.divide)(scaleFactor, defaultFactor);
23
+ return ratio ? (0, utils_1.divide)(valueStr, ratio, 6) : null;
24
+ }
25
+ const ratio = (0, utils_1.divide)(defaultFactor, scaleFactor);
26
+ return ratio ? (0, utils_1.multiply)(valueStr, ratio, 6) : null;
27
+ }
28
+ catch (_a) {
29
+ return null;
30
+ }
31
+ };
32
+ const isReadable = (numStr) => {
33
+ if (numStr === null) {
34
+ return false;
35
+ }
36
+ // Math.abs does not support BigInt
37
+ const intPart = numStr.split('.')[0].replace('-', '');
38
+ try {
39
+ return BigInt(intPart) >= BigInt(1);
40
+ }
41
+ catch (_a) {
42
+ return false;
43
+ }
44
+ };
45
+ const getMostAppropriateScale = (sizeParams, value) => {
46
+ const { defaultType, scale } = sizeParams;
47
+ const valueStr = String(value);
48
+ const defaultFactor = scale[defaultType].factor;
49
+ let bestType = defaultType;
50
+ Object.keys(scale).forEach((key) => {
51
+ if (BigInt(scale[key].factor) > BigInt(scale[bestType].factor)) {
52
+ const ratio = (0, utils_1.divide)(scale[key].factor, defaultFactor);
53
+ if (!ratio) {
54
+ return;
55
+ }
56
+ if (!isReadable((0, utils_1.divide)(valueStr, ratio, 2))) {
57
+ return;
58
+ }
59
+ bestType = key;
60
+ }
61
+ });
62
+ return bestType;
63
+ };
64
+ const getScaledLimit = (spec, limit) => {
65
+ var _a;
66
+ const sizeParams = (_a = spec.viewSpec) === null || _a === void 0 ? void 0 : _a.sizeParams;
67
+ if (!sizeParams) {
68
+ return undefined;
69
+ }
70
+ const mostAppropriateScale = getMostAppropriateScale(sizeParams, limit);
71
+ const scaleEntry = sizeParams.scale[mostAppropriateScale];
72
+ const defaultEntry = sizeParams.scale[sizeParams.defaultType];
73
+ if (!scaleEntry || !defaultEntry) {
74
+ return undefined;
75
+ }
76
+ const count = scaleValue(limit, scaleEntry.factor, defaultEntry.factor);
77
+ if (count === null) {
78
+ return undefined;
79
+ }
80
+ return { count, scaleTitle: scaleEntry.title };
81
+ };
82
+ exports.getScaledLimit = getScaledLimit;
@@ -27,6 +27,12 @@ const getErrorMessages = () => ({
27
27
  maxNumber(count) {
28
28
  return (0, i18n_1.default)('label_error-max-number', { count });
29
29
  },
30
+ minNumberWithScale(count, scale) {
31
+ return (0, i18n_1.default)('label_error-min-number-with-scale', { count, scale });
32
+ },
33
+ maxNumberWithScale(count, scale) {
34
+ return (0, i18n_1.default)('label_error-max-number-with-scale', { count, scale });
35
+ },
30
36
  SPACE_START: (0, i18n_1.default)('label_error-space-start'),
31
37
  SPACE_END: (0, i18n_1.default)('label_error-space-end'),
32
38
  DOT_END: (0, i18n_1.default)('label_error-dot-end'),
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.getStringValidator = exports.getObjectValidator = exports.getNumberValidator = exports.getBooleanValidator = exports.getArrayValidator = void 0;
3
+ exports.getStringValidator = exports.getObjectValidator = exports.getNumberWithScaleValidator = exports.getNumberValidator = exports.getBooleanValidator = exports.getArrayValidator = void 0;
4
4
  const tslib_1 = require("tslib");
5
5
  const isArray_1 = tslib_1.__importDefault(require("lodash/isArray"));
6
6
  const isNumber_1 = tslib_1.__importDefault(require("lodash/isNumber"));
@@ -101,6 +101,73 @@ const getNumberValidator = (params = {}) => {
101
101
  };
102
102
  };
103
103
  exports.getNumberValidator = getNumberValidator;
104
+ const getNumberWithScaleValidator = (params = {}) => {
105
+ const { ignoreRequiredCheck, ignoreSpaceStartCheck, ignoreSpaceEndCheck, ignoreNumberCheck, ignoreMaximumCheck, ignoreMinimumCheck, ignoreIntCheck, ignoreDotEnd, ignoreZeroStart, ignoreInvalidZeroFormat, ignoreZeroEnd, customErrorMessages, } = params;
106
+ // eslint-disable-next-line complexity
107
+ return (spec, value = '') => {
108
+ const errorMessages = Object.assign(Object.assign({}, validators_1.ErrorMessages), customErrorMessages);
109
+ const stringValue = String(value);
110
+ if (!ignoreRequiredCheck && spec.required && !stringValue.length) {
111
+ return errorMessages.REQUIRED;
112
+ }
113
+ if (stringValue.length) {
114
+ if (!ignoreSpaceStartCheck && !stringValue[0].trim()) {
115
+ return errorMessages.SPACE_START;
116
+ }
117
+ if (!ignoreSpaceEndCheck && !stringValue[stringValue.length - 1].trim()) {
118
+ return errorMessages.SPACE_END;
119
+ }
120
+ if (!ignoreDotEnd && stringValue[stringValue.length - 1] === '.') {
121
+ return errorMessages.DOT_END;
122
+ }
123
+ if (!ignoreNumberCheck && !(0, helpers_1.isFloat)(stringValue)) {
124
+ return errorMessages.NUMBER;
125
+ }
126
+ if (!ignoreZeroStart &&
127
+ ((stringValue.length > 1 && stringValue[0] === '0' && stringValue[1] !== '.') ||
128
+ (stringValue.length > 2 &&
129
+ stringValue.substring(0, 2) === '-0' &&
130
+ stringValue[2] !== '.'))) {
131
+ return errorMessages.ZERO_START;
132
+ }
133
+ if (!ignoreInvalidZeroFormat &&
134
+ stringValue.trim().length > 1 &&
135
+ Number(stringValue.trim()) === 0) {
136
+ return errorMessages.INVALID_ZERO_FORMAT;
137
+ }
138
+ if (!ignoreZeroEnd &&
139
+ !(0, helpers_1.isInt)(stringValue) &&
140
+ stringValue[stringValue.length - 1] === '0') {
141
+ return errorMessages.ZERO_END;
142
+ }
143
+ }
144
+ if (!ignoreMaximumCheck &&
145
+ (0, isNumber_1.default)(spec.maximum) &&
146
+ stringValue.length &&
147
+ Number(stringValue) > spec.maximum) {
148
+ const scaled = (0, helpers_1.getScaledLimit)(spec, spec.maximum);
149
+ return scaled
150
+ ? errorMessages.maxNumberWithScale(scaled.count, scaled.scaleTitle)
151
+ : errorMessages.maxNumber(spec.maximum);
152
+ }
153
+ if (!ignoreMinimumCheck &&
154
+ (0, isNumber_1.default)(spec.minimum) &&
155
+ stringValue.length &&
156
+ spec.minimum > Number(stringValue)) {
157
+ const scaled = (0, helpers_1.getScaledLimit)(spec, spec.minimum);
158
+ return scaled
159
+ ? errorMessages.minNumberWithScale(scaled.count, scaled.scaleTitle)
160
+ : errorMessages.minNumber(spec.minimum);
161
+ }
162
+ if ((0, isString_1.default)(spec.format) && stringValue.length) {
163
+ if (!ignoreIntCheck && spec.format === 'int64' && !(0, helpers_1.isInt)(stringValue)) {
164
+ return errorMessages.INT;
165
+ }
166
+ }
167
+ return false;
168
+ };
169
+ };
170
+ exports.getNumberWithScaleValidator = getNumberWithScaleValidator;
104
171
  const getObjectValidator = (params = {}) => {
105
172
  const { ignoreRequiredCheck, customErrorMessages } = params;
106
173
  return (spec, value) => {
@@ -186,4 +186,9 @@ export interface StringSpec<LinkType = any, InputComponentProps extends Record<s
186
186
  generateRandomValueButton?: boolean;
187
187
  };
188
188
  }
189
+ export interface NumberWithScaleSpec extends StringSpec {
190
+ minimum?: number;
191
+ maximum?: number;
192
+ format?: 'float' | 'int64';
193
+ }
189
194
  export type Spec<LinkType = any, InputComponentProps extends Record<string, any> | undefined = undefined, LayoutComponentProps extends Record<string, any> | undefined = undefined> = ArraySpec<LinkType, InputComponentProps, LayoutComponentProps> | BooleanSpec<LinkType, InputComponentProps, LayoutComponentProps> | NumberSpec<LinkType, InputComponentProps, LayoutComponentProps> | ObjectSpec<LinkType, InputComponentProps, LayoutComponentProps> | StringSpec<LinkType, InputComponentProps, LayoutComponentProps>;
@@ -0,0 +1,6 @@
1
+ import React from 'react';
2
+ import { unstable_ColorPicker as ColorPickerBase } from '@gravity-ui/uikit/unstable';
3
+ import type { StringInput } from '../../../../core';
4
+ export interface ColorPickerProps extends Omit<React.ComponentProps<typeof ColorPickerBase>, 'value' | 'onUpdate' | 'disabled'> {
5
+ }
6
+ export declare const ColorPicker: StringInput<ColorPickerProps>;
@@ -0,0 +1,16 @@
1
+ import React from 'react';
2
+ import { unstable_ColorPicker as ColorPickerBase } from '@gravity-ui/uikit/unstable';
3
+ export const ColorPicker = ({ input, spec, inputProps }) => {
4
+ const { value, onChange, onBlur, onFocus } = input;
5
+ const handleOpenChange = React.useCallback((open) => {
6
+ var _a;
7
+ (_a = inputProps === null || inputProps === void 0 ? void 0 : inputProps.onOpenChange) === null || _a === void 0 ? void 0 : _a.call(inputProps, open);
8
+ if (open) {
9
+ onFocus();
10
+ }
11
+ else {
12
+ onBlur();
13
+ }
14
+ }, [inputProps, onBlur, onFocus]);
15
+ return (React.createElement(ColorPickerBase, Object.assign({}, inputProps, { value: value || '', onUpdate: onChange, onOpenChange: handleOpenChange, disabled: spec.viewSpec.disabled })));
16
+ };
@@ -0,0 +1 @@
1
+ export * from './ColorPicker';
@@ -0,0 +1 @@
1
+ export * from './ColorPicker';
@@ -4,6 +4,7 @@ export * from './Checkbox';
4
4
  export * from './CheckboxGroup';
5
5
  export * from './FileInput';
6
6
  export * from './DateInput';
7
+ export * from './ColorPicker';
7
8
  export * from './MonacoInput';
8
9
  export * from './MultiOneOf';
9
10
  export * from './MultiSelect';
@@ -4,6 +4,7 @@ export * from './Checkbox';
4
4
  export * from './CheckboxGroup';
5
5
  export * from './FileInput';
6
6
  export * from './DateInput';
7
+ export * from './ColorPicker';
7
8
  export * from './MonacoInput';
8
9
  export * from './MultiOneOf';
9
10
  export * from './MultiSelect';
@@ -0,0 +1,31 @@
1
+ /*
2
+ @mixin variables {
3
+ --g-spacing-base: 4px;
4
+
5
+ --g-spacing-0: calc(var(--g-spacing-base) * 0); //0px
6
+ --g-spacing-half: calc(var(--g-spacing-base) * 0.5); //2px
7
+ --g-spacing-1: var(--g-spacing-base); // 4px
8
+ --g-spacing-2: calc(var(--g-spacing-base) * 2); // 8px
9
+ --g-spacing-3: calc(var(--g-spacing-base) * 3); // 12px
10
+ --g-spacing-4: calc(var(--g-spacing-base) * 4); // 16px
11
+ --g-spacing-5: calc(var(--g-spacing-base) * 5); // 20px
12
+ --g-spacing-6: calc(var(--g-spacing-base) * 6); // 24px
13
+ --g-spacing-7: calc(var(--g-spacing-base) * 7); // 28px
14
+ --g-spacing-8: calc(var(--g-spacing-base) * 8); // 32px
15
+ --g-spacing-9: calc(var(--g-spacing-base) * 9); // 36px
16
+ --g-spacing-10: calc(var(--g-spacing-base) * 10); // 40px
17
+ }
18
+ */
19
+ /* Border-radius */
20
+ .df-color-picker-view {
21
+ display: flex;
22
+ align-items: center;
23
+ gap: var(--g-spacing-2);
24
+ }
25
+ .df-color-picker-view__preview {
26
+ width: 16px;
27
+ height: 16px;
28
+ flex-shrink: 0;
29
+ border: 1px solid var(--g-color-line-generic);
30
+ border-radius: var(--g-border-radius-xs);
31
+ }
@@ -0,0 +1,3 @@
1
+ import type { StringView } from '../../../../core';
2
+ import './ColorPickerView.css';
3
+ export declare const ColorPickerView: StringView;
@@ -0,0 +1,8 @@
1
+ import React from 'react';
2
+ import { LongValue } from '../../../components';
3
+ import { block } from '../../../utils';
4
+ import './ColorPickerView.css';
5
+ const b = block('color-picker-view');
6
+ export const ColorPickerView = ({ value = '' }) => (React.createElement("div", { className: b() },
7
+ value ? React.createElement("div", { className: b('preview'), style: { backgroundColor: value } }) : null,
8
+ React.createElement(LongValue, { value: value })));
@@ -0,0 +1 @@
1
+ export * from './ColorPickerView';
@@ -0,0 +1 @@
1
+ export * from './ColorPickerView';
@@ -3,6 +3,7 @@ export * from './BaseView';
3
3
  export * from './CheckboxGroupView';
4
4
  export * from './CardOneOfView';
5
5
  export * from './FileInputView';
6
+ export * from './ColorPickerView';
6
7
  export * from './MonacoInputView';
7
8
  export * from './MultiOneOfView';
8
9
  export * from './MultiSelectView';
@@ -3,6 +3,7 @@ export * from './BaseView';
3
3
  export * from './CheckboxGroupView';
4
4
  export * from './CardOneOfView';
5
5
  export * from './FileInputView';
6
+ export * from './ColorPickerView';
6
7
  export * from './MonacoInputView';
7
8
  export * from './MultiOneOfView';
8
9
  export * from './MultiSelectView';
@@ -1,5 +1,5 @@
1
- import { Accordeon, AccordeonCardForm, ArrayBase, ArrayBaseView, BaseView, CardAccordeon, CardOneOf, CardOneOfView, CardSection, Checkbox, CheckboxGroup, CheckboxGroupView, Column, DateInput, DateView, FileInput, FileInputView, Group, Group2, MonacoInput, MonacoView, MultiOneOf, MultiOneOfFlat, MultiOneOfFlatView, MultiOneOfView, MultiSelect, MultiSelectView, NumInput, NumberWithScale, NumberWithScaleView, ObjectBase, ObjectBaseView, ObjectInline, ObjectInlineView, ObjectValueInput, ObjectValueInputView, OneOf, OneOfFlat, OneOfFlatView, OneOfView, RadioGroup, RangeInputPicker, RangeInputPickerNumber, RangeInputPickerNumberView, RangeInputPickerView, Row, RowVerbose, Secret, Section, Section2, Select, Switch, TableArrayInput, TableArrayView, TableCell, Text, TextArea, TextAreaView, TextContent, TextContentView, TextLink, TextLinkView, TimeRangeSelector, TimeRangeSelectorView, Transparent, ViewAccordeon, ViewAccordeonCard, ViewCardAccordeon, ViewCardSection, ViewColumn, ViewGroup, ViewGroup2, ViewRow, ViewSection, ViewSection2, ViewTableCell, ViewTransparent, } from '../components';
2
- import { getArrayValidator, getBooleanValidator, getNumberValidator, getObjectValidator, getStringValidator, } from '../validators';
1
+ import { Accordeon, AccordeonCardForm, ArrayBase, ArrayBaseView, BaseView, CardAccordeon, CardOneOf, CardOneOfView, CardSection, Checkbox, CheckboxGroup, CheckboxGroupView, ColorPicker, ColorPickerView, Column, DateInput, DateView, FileInput, FileInputView, Group, Group2, MonacoInput, MonacoView, MultiOneOf, MultiOneOfFlat, MultiOneOfFlatView, MultiOneOfView, MultiSelect, MultiSelectView, NumInput, NumberWithScale, NumberWithScaleView, ObjectBase, ObjectBaseView, ObjectInline, ObjectInlineView, ObjectValueInput, ObjectValueInputView, OneOf, OneOfFlat, OneOfFlatView, OneOfView, RadioGroup, RangeInputPicker, RangeInputPickerNumber, RangeInputPickerNumberView, RangeInputPickerView, Row, RowVerbose, Secret, Section, Section2, Select, Switch, TableArrayInput, TableArrayView, TableCell, Text, TextArea, TextAreaView, TextContent, TextContentView, TextLink, TextLinkView, TimeRangeSelector, TimeRangeSelectorView, Transparent, ViewAccordeon, ViewAccordeonCard, ViewCardAccordeon, ViewCardSection, ViewColumn, ViewGroup, ViewGroup2, ViewRow, ViewSection, ViewSection2, ViewTableCell, ViewTransparent, } from '../components';
2
+ import { getArrayValidator, getBooleanValidator, getNumberValidator, getNumberWithScaleValidator, getObjectValidator, getStringValidator, } from '../validators';
3
3
  export const dynamicConfig = {
4
4
  array: {
5
5
  inputs: {
@@ -100,6 +100,7 @@ export const dynamicConfig = {
100
100
  base: { Component: Text },
101
101
  file_input: { Component: FileInput },
102
102
  date_input: { Component: DateInput },
103
+ color_picker: { Component: ColorPicker },
103
104
  number_with_scale: { Component: NumberWithScale },
104
105
  monaco_input: { Component: MonacoInput },
105
106
  text_content: { Component: TextContent, independent: true },
@@ -120,6 +121,7 @@ export const dynamicConfig = {
120
121
  validators: {
121
122
  base: getStringValidator(),
122
123
  number: getNumberValidator(),
124
+ number_with_scale: getNumberWithScaleValidator(),
123
125
  },
124
126
  },
125
127
  };
@@ -210,6 +212,7 @@ export const dynamicViewConfig = {
210
212
  select: { Component: BaseView },
211
213
  base: { Component: BaseView },
212
214
  date_input: { Component: DateView },
215
+ color_picker: { Component: ColorPickerView },
213
216
  file_input: { Component: FileInputView },
214
217
  number_with_scale: { Component: NumberWithScaleView },
215
218
  monaco_input: { Component: MonacoView },
@@ -37,6 +37,8 @@
37
37
  ],
38
38
  "label_error-min-number": "Value must be an integer no less than {{count}}",
39
39
  "label_error-max-number": "Value must not be greater than {{count}}",
40
+ "label_error-min-number-with-scale": "Value must be no less than {{count}} {{scale}}",
41
+ "label_error-max-number-with-scale": "Value must not be greater than {{count}} {{scale}}",
40
42
  "label_error-space-end": "Value must not end with a space",
41
43
  "label_error-space-start": "Value must not start with a space",
42
44
  "label_error-zero-start": "Value must not start with a zero",
@@ -37,6 +37,8 @@
37
37
  ],
38
38
  "label_error-min-number": "Значение должно быть числом не меньше {{count}}",
39
39
  "label_error-max-number": "Значение должно быть числом не больше {{count}}",
40
+ "label_error-min-number-with-scale": "Значение должно быть не меньше {{count}} {{scale}}",
41
+ "label_error-max-number-with-scale": "Значение должно быть не больше {{count}} {{scale}}",
40
42
  "label_error-space-end": "Значение не должно заканчиваться пробелом",
41
43
  "label_error-space-start": "Значение не должно начинаться с пробела",
42
44
  "label_error-zero-start": "Значение не должно начинаться с нуля",
@@ -1,2 +1,7 @@
1
+ import type { StringSpec } from 'src/lib/core';
1
2
  export declare const isInt: (value: string) => boolean;
2
3
  export declare const isFloat: (value: string) => boolean;
4
+ export declare const getScaledLimit: (spec: StringSpec, limit: number) => {
5
+ count: string;
6
+ scaleTitle: string;
7
+ } | undefined;
@@ -1,3 +1,4 @@
1
+ import { divide, multiply } from '../utils';
1
2
  export const isInt = (value) => {
2
3
  const regex = /^(?:[-+]?(?:0|[1-9]\d*))$/;
3
4
  return regex.test(value);
@@ -6,3 +7,70 @@ export const isFloat = (value) => {
6
7
  const regex = /^(?:[-+]?(?:\d+))?(?:\.\d*)?(?:[eE][+-]?(?:\d+))?$/;
7
8
  return regex.test(value);
8
9
  };
10
+ const scaleValue = (value, scaleFactor, defaultFactor) => {
11
+ if (defaultFactor === scaleFactor) {
12
+ return String(value);
13
+ }
14
+ const valueStr = String(value);
15
+ try {
16
+ if (BigInt(scaleFactor) > BigInt(defaultFactor)) {
17
+ const ratio = divide(scaleFactor, defaultFactor);
18
+ return ratio ? divide(valueStr, ratio, 6) : null;
19
+ }
20
+ const ratio = divide(defaultFactor, scaleFactor);
21
+ return ratio ? multiply(valueStr, ratio, 6) : null;
22
+ }
23
+ catch (_a) {
24
+ return null;
25
+ }
26
+ };
27
+ const isReadable = (numStr) => {
28
+ if (numStr === null) {
29
+ return false;
30
+ }
31
+ // Math.abs does not support BigInt
32
+ const intPart = numStr.split('.')[0].replace('-', '');
33
+ try {
34
+ return BigInt(intPart) >= BigInt(1);
35
+ }
36
+ catch (_a) {
37
+ return false;
38
+ }
39
+ };
40
+ const getMostAppropriateScale = (sizeParams, value) => {
41
+ const { defaultType, scale } = sizeParams;
42
+ const valueStr = String(value);
43
+ const defaultFactor = scale[defaultType].factor;
44
+ let bestType = defaultType;
45
+ Object.keys(scale).forEach((key) => {
46
+ if (BigInt(scale[key].factor) > BigInt(scale[bestType].factor)) {
47
+ const ratio = divide(scale[key].factor, defaultFactor);
48
+ if (!ratio) {
49
+ return;
50
+ }
51
+ if (!isReadable(divide(valueStr, ratio, 2))) {
52
+ return;
53
+ }
54
+ bestType = key;
55
+ }
56
+ });
57
+ return bestType;
58
+ };
59
+ export const getScaledLimit = (spec, limit) => {
60
+ var _a;
61
+ const sizeParams = (_a = spec.viewSpec) === null || _a === void 0 ? void 0 : _a.sizeParams;
62
+ if (!sizeParams) {
63
+ return undefined;
64
+ }
65
+ const mostAppropriateScale = getMostAppropriateScale(sizeParams, limit);
66
+ const scaleEntry = sizeParams.scale[mostAppropriateScale];
67
+ const defaultEntry = sizeParams.scale[sizeParams.defaultType];
68
+ if (!scaleEntry || !defaultEntry) {
69
+ return undefined;
70
+ }
71
+ const count = scaleValue(limit, scaleEntry.factor, defaultEntry.factor);
72
+ if (count === null) {
73
+ return undefined;
74
+ }
75
+ return { count, scaleTitle: scaleEntry.title };
76
+ };
@@ -23,6 +23,12 @@ const getErrorMessages = () => ({
23
23
  maxNumber(count) {
24
24
  return i18n('label_error-max-number', { count });
25
25
  },
26
+ minNumberWithScale(count, scale) {
27
+ return i18n('label_error-min-number-with-scale', { count, scale });
28
+ },
29
+ maxNumberWithScale(count, scale) {
30
+ return i18n('label_error-max-number-with-scale', { count, scale });
31
+ },
26
32
  SPACE_START: i18n('label_error-space-start'),
27
33
  SPACE_END: i18n('label_error-space-end'),
28
34
  DOT_END: i18n('label_error-dot-end'),
@@ -9,6 +9,8 @@ export interface ErrorMessagesType {
9
9
  maxLengthArr: (count: number | bigint) => string;
10
10
  minNumber: (count: number | bigint) => string;
11
11
  maxNumber: (count: number | bigint) => string;
12
+ minNumberWithScale: (count: number | bigint | string, scale: string) => string;
13
+ maxNumberWithScale: (count: number | bigint | string, scale: string) => string;
12
14
  SPACE_START: string;
13
15
  SPACE_END: string;
14
16
  DOT_END: string;
@@ -1,4 +1,4 @@
1
- import type { ArraySpec, ArrayValue, BooleanSpec, NumberSpec, ObjectSpec, ObjectValue, StringSpec } from '../../core';
1
+ import type { ArraySpec, ArrayValue, BooleanSpec, NumberSpec, NumberWithScaleSpec, ObjectSpec, ObjectValue, StringSpec } from '../../core';
2
2
  import type { ErrorMessagesType } from './types';
3
3
  interface CommonValidatorParams {
4
4
  ignoreRequiredCheck?: boolean;
@@ -25,6 +25,9 @@ export interface GetNumberValidatorParams extends CommonValidatorParams {
25
25
  ignoreZeroEnd?: boolean;
26
26
  }
27
27
  export declare const getNumberValidator: (params?: GetNumberValidatorParams) => (spec: NumberSpec, value?: string | number) => string | false;
28
+ export interface GetNumberWithScaleValidatorParams extends GetNumberValidatorParams {
29
+ }
30
+ export declare const getNumberWithScaleValidator: (params?: GetNumberWithScaleValidatorParams) => (spec: NumberWithScaleSpec, value?: string) => string | false;
28
31
  export interface GetObjectValidatorParams extends CommonValidatorParams {
29
32
  }
30
33
  export declare const getObjectValidator: (params?: GetObjectValidatorParams) => (spec: ObjectSpec, value?: ObjectValue) => string | false;
@@ -2,7 +2,7 @@ import isArray from 'lodash/isArray';
2
2
  import isNumber from 'lodash/isNumber';
3
3
  import isString from 'lodash/isString';
4
4
  import { ErrorMessages } from '../validators';
5
- import { isFloat, isInt } from './helpers';
5
+ import { getScaledLimit, isFloat, isInt } from './helpers';
6
6
  export const getArrayValidator = (params = {}) => {
7
7
  const { ignoreRequiredCheck, ignoreMaxLengthCheck, ignoreMinLengthCheck, customErrorMessages } = params;
8
8
  return (spec, value) => {
@@ -94,6 +94,72 @@ export const getNumberValidator = (params = {}) => {
94
94
  return false;
95
95
  };
96
96
  };
97
+ export const getNumberWithScaleValidator = (params = {}) => {
98
+ const { ignoreRequiredCheck, ignoreSpaceStartCheck, ignoreSpaceEndCheck, ignoreNumberCheck, ignoreMaximumCheck, ignoreMinimumCheck, ignoreIntCheck, ignoreDotEnd, ignoreZeroStart, ignoreInvalidZeroFormat, ignoreZeroEnd, customErrorMessages, } = params;
99
+ // eslint-disable-next-line complexity
100
+ return (spec, value = '') => {
101
+ const errorMessages = Object.assign(Object.assign({}, ErrorMessages), customErrorMessages);
102
+ const stringValue = String(value);
103
+ if (!ignoreRequiredCheck && spec.required && !stringValue.length) {
104
+ return errorMessages.REQUIRED;
105
+ }
106
+ if (stringValue.length) {
107
+ if (!ignoreSpaceStartCheck && !stringValue[0].trim()) {
108
+ return errorMessages.SPACE_START;
109
+ }
110
+ if (!ignoreSpaceEndCheck && !stringValue[stringValue.length - 1].trim()) {
111
+ return errorMessages.SPACE_END;
112
+ }
113
+ if (!ignoreDotEnd && stringValue[stringValue.length - 1] === '.') {
114
+ return errorMessages.DOT_END;
115
+ }
116
+ if (!ignoreNumberCheck && !isFloat(stringValue)) {
117
+ return errorMessages.NUMBER;
118
+ }
119
+ if (!ignoreZeroStart &&
120
+ ((stringValue.length > 1 && stringValue[0] === '0' && stringValue[1] !== '.') ||
121
+ (stringValue.length > 2 &&
122
+ stringValue.substring(0, 2) === '-0' &&
123
+ stringValue[2] !== '.'))) {
124
+ return errorMessages.ZERO_START;
125
+ }
126
+ if (!ignoreInvalidZeroFormat &&
127
+ stringValue.trim().length > 1 &&
128
+ Number(stringValue.trim()) === 0) {
129
+ return errorMessages.INVALID_ZERO_FORMAT;
130
+ }
131
+ if (!ignoreZeroEnd &&
132
+ !isInt(stringValue) &&
133
+ stringValue[stringValue.length - 1] === '0') {
134
+ return errorMessages.ZERO_END;
135
+ }
136
+ }
137
+ if (!ignoreMaximumCheck &&
138
+ isNumber(spec.maximum) &&
139
+ stringValue.length &&
140
+ Number(stringValue) > spec.maximum) {
141
+ const scaled = getScaledLimit(spec, spec.maximum);
142
+ return scaled
143
+ ? errorMessages.maxNumberWithScale(scaled.count, scaled.scaleTitle)
144
+ : errorMessages.maxNumber(spec.maximum);
145
+ }
146
+ if (!ignoreMinimumCheck &&
147
+ isNumber(spec.minimum) &&
148
+ stringValue.length &&
149
+ spec.minimum > Number(stringValue)) {
150
+ const scaled = getScaledLimit(spec, spec.minimum);
151
+ return scaled
152
+ ? errorMessages.minNumberWithScale(scaled.count, scaled.scaleTitle)
153
+ : errorMessages.minNumber(spec.minimum);
154
+ }
155
+ if (isString(spec.format) && stringValue.length) {
156
+ if (!ignoreIntCheck && spec.format === 'int64' && !isInt(stringValue)) {
157
+ return errorMessages.INT;
158
+ }
159
+ }
160
+ return false;
161
+ };
162
+ };
97
163
  export const getObjectValidator = (params = {}) => {
98
164
  const { ignoreRequiredCheck, customErrorMessages } = params;
99
165
  return (spec, value) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gravity-ui/dynamic-forms",
3
- "version": "5.15.0",
3
+ "version": "5.17.1",
4
4
  "description": "",
5
5
  "license": "MIT",
6
6
  "main": "build/cjs/index.js",
@@ -60,7 +60,7 @@
60
60
  "@gravity-ui/prettier-config": "^1.1.0",
61
61
  "@gravity-ui/stylelint-config": "^4.0.1",
62
62
  "@gravity-ui/tsconfig": "^1.0.0",
63
- "@gravity-ui/uikit": "^7.25.0",
63
+ "@gravity-ui/uikit": "^7.41.0",
64
64
  "@playwright/experimental-ct-react": "^1.40.0",
65
65
  "@playwright/test": "^1.40.0",
66
66
  "@storybook/addon-essentials": "^8.6.4",