@itcase/forms 1.1.10 → 1.1.11

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.
@@ -1,3041 +0,0 @@
1
- import { isPossiblePhoneNumber } from 'libphonenumber-js';
2
- import React$1, { useMemo, useEffect, useCallback, useState } from 'react';
3
- import { setIn, FORM_ERROR, getIn } from 'final-form';
4
- import { useForm, Field, Form, FormSpy } from 'react-final-form';
5
- export { Field, useForm, useFormState } from 'react-final-form';
6
- import clsx from 'clsx';
7
- import { Checkbox } from '@itcase/ui/components/Checkbox';
8
- import { Divider } from '@itcase/ui/components/Divider';
9
- import { Text } from '@itcase/ui/components/Text';
10
- import { useDeviceTargetClass } from '@itcase/ui/hooks/useDeviceTargetClass';
11
- import { useStyles } from '@itcase/ui/hooks/useStyles';
12
- import camelCase from 'lodash/camelCase';
13
- import snakeCase from 'lodash/snakeCase';
14
- import { Choice } from '@itcase/ui/components/Choice';
15
- import { Icon } from '@itcase/ui/components/Icon';
16
- import { Code } from '@itcase/ui/components/Code';
17
- import { DatePickerInput } from '@itcase/ui/components/DatePicker';
18
- import { useDevicePropsGenerator } from '@itcase/ui/hooks/useDevicePropsGenerator';
19
- import axios from 'axios';
20
- import { fromEvent } from 'file-selector';
21
- import castArray from 'lodash/castArray';
22
- import { useDropzone, ErrorCode } from 'react-dropzone';
23
- import { createFileFromDataURL } from '@itcase/common';
24
- import { Button } from '@itcase/ui/components/Button';
25
- import { Group as Group$1 } from '@itcase/ui/components/Group';
26
- import { Loader } from '@itcase/ui/components/Loader';
27
- import { Title } from '@itcase/ui/components/Title';
28
- import { Input } from '@itcase/ui/components/Input';
29
- import { Radio } from '@itcase/ui/components/Radio';
30
- import { Segmented } from '@itcase/ui/components/Segmented';
31
- import { Select } from '@itcase/ui/components/Select';
32
- import { Switch } from '@itcase/ui/components/Switch';
33
- import { Textarea } from '@itcase/ui/components/Textarea';
34
- import { useIMask } from 'react-imask';
35
- import { Chips } from '@itcase/ui/components/Chips';
36
- import { NotificationItem } from '@itcase/ui/components/Notification';
37
- import createDecorator from 'final-form-focus';
38
-
39
- const phoneValidation = value => {
40
- if (!value) {
41
- return true;
42
- }
43
- return isPossiblePhoneNumber(value, 'RU');
44
- };
45
- const emailValidation = value => {
46
- // from https://emailregex.com/
47
- if (!value) {
48
- return true;
49
- }
50
-
51
- // from https://www.regular-expressions.info/email.html
52
- const regexp = /^[a-za-яёЁ0-9_-]+(?:\.[a-za-яёЁ0-9_-]+)*@(?:[a-za-яёЁ0-9](?:[a-za-яёЁ0-9-]*[a-za-яёЁ0-9])?\.)+[a-za-яёЁ0-9]+$/;
53
- return regexp.test(String(value).toLowerCase());
54
- };
55
- const dateValidation = value => {
56
- const valueDate = value instanceof Date ? value : new Date(value);
57
- const isDateValid = !isNaN(valueDate.getTime());
58
- return isDateValid;
59
- };
60
- const addRequiredFieldsParamToSchema = schema => {
61
- const fields = Object.entries(schema.fields);
62
- schema.requiredFields = fields.reduce((list, [fieldName, validationProps]) => {
63
- if (validationProps.exclusiveTests?.required) {
64
- list.push(fieldName);
65
- }
66
- return list;
67
- }, []);
68
- return schema;
69
- };
70
-
71
- /**
72
- * Sets the `innerError.message` in an `errors` object at the key
73
- * defined by `innerError.path`.
74
- * @param {Object} errors The object to set the error in.
75
- * @param {{ path: string, message: string }} innerError A `yup` field error.
76
- * @returns {Object} The result of setting the new error message onto `errors`.
77
- */
78
- const setInError = (errors, innerError) => {
79
- return setIn(errors, innerError.path, innerError.message);
80
- };
81
-
82
- /**
83
- * Empty object map with no prototype. Used as default
84
- * value for reducing the `err.inner` array of errors
85
- * from a `yup~ValidationError`.
86
- */
87
- const emptyObj = Object.create(null);
88
-
89
- /**
90
- * Takes a `yup` validation schema and returns a function that expects
91
- * a map of values to validate. If the validation passes, the function resolves to `undefined`
92
- * (signalling that the values are valid). If the validation doesn't pass, it resolves
93
- * to a map of invalid field names to errors.
94
- * @param {import('yup').ObjectSchema} schema `yup` schema definition.
95
- * @returns {(values: Object) => Promise<?Object>} An async function that expects some `values`
96
- * and resolves to either `undefined` or a map of field names to error messages.
97
- */
98
-
99
- const makeValidate = schema => {
100
- return async function validate(values) {
101
- try {
102
- await schema.validate(values, {
103
- abortEarly: false
104
- });
105
- } catch (error) {
106
- if (error.inner) {
107
- return error.inner.reduce(setInError, emptyObj);
108
- } else {
109
- console.warn('itcase-forms schema.validate error: An error not related to the form occurred during validation. Validation ignored.');
110
- }
111
- }
112
- };
113
- };
114
- function useYupValidationSchema(schema, language) {
115
- const validate = useMemo(() => schema && makeValidate(schema), [schema, language]);
116
- return validate;
117
- }
118
-
119
- const defaultFieldProps = {
120
- width: 'fill',
121
- direction: 'vertical',
122
- labelTextColor: 'surfaceTextPrimary',
123
- labelTextSize: 'm',
124
- errorMessageTextColor: 'errorTextSecondary',
125
- errorMessageTextSize: 's',
126
- dividerFill: 'errorPrimary',
127
- helpTextColor: 'surfaceTextQuaternary',
128
- helpTextSize: 's',
129
- messageTextColor: 'surfaceTextSecondary',
130
- messageTextSize: 's',
131
- requiredMessageTextColor: 'warningTextSecondary',
132
- requiredMessageTextSize: 's',
133
- showMessage: true
134
- };
135
-
136
- function FieldWrapperBase(props) {
137
- const {
138
- id,
139
- className,
140
- type = 'normal',
141
- label,
142
- labelHidden,
143
- labelTextColor,
144
- labelTextSize,
145
- labelTextSizeMobile,
146
- labelTextSizeTablet,
147
- labelTextWeight,
148
- desc,
149
- descTextColor,
150
- descTextSize,
151
- descTextWeight,
152
- errorKey,
153
- errorMessage,
154
- isErrorState,
155
- metaError,
156
- helpTextColorSuccess,
157
- isDisabled,
158
- afterItem,
159
- beforeItem,
160
- dataTour,
161
- dividerDirection,
162
- dividerFill,
163
- dividerSize,
164
- dividerWidth,
165
- fieldClassName,
166
- helpText,
167
- helpTextColor,
168
- helpTextSize,
169
- helpTextSizeMobile,
170
- helpTextWeight,
171
- inputName,
172
- inputValue,
173
- messageTextSize,
174
- metaActive,
175
- showDivider,
176
- showMessage,
177
- tag: Tag = 'div',
178
- before,
179
- after,
180
- isHidden,
181
- isRequired,
182
- isValidState,
183
- children
184
- } = props;
185
- const formFieldClass = useMemo(() => clsx(className, isValidState && 'form__item_state_success', isRequired && 'form__item_state_required', isDisabled && 'form__item_state_disabled', metaActive && 'form__item_state_focus', inputValue && 'form__item_state_filled', isErrorState && `form__item_state_${errorKey}`), [className, isErrorState, isValidState, isRequired, metaActive, inputValue, isDisabled, metaError]);
186
- const fieldClass = useMemo(() => clsx(fieldClassName, isValidState && `${fieldClassName}_state_success`, metaActive && `${fieldClassName}_state_focus`, inputValue && `${fieldClassName}_state_filled`, isDisabled && `${fieldClassName}_state_disabled`, isErrorState && `${fieldClassName}_state_${errorKey}`), [fieldClassName, isErrorState, isValidState, metaActive, inputValue, isDisabled, metaError]);
187
- const sizeClass = useDeviceTargetClass(props, {
188
- prefix: 'form-field_size_',
189
- propsKey: 'size'
190
- });
191
- const fillClass = useDeviceTargetClass(props, {
192
- prefix: 'fill_',
193
- propsKey: 'fill'
194
- });
195
- const inputFillClass = useDeviceTargetClass(props, {
196
- prefix: 'fill_',
197
- propsKey: 'inputFill'
198
- });
199
- const shapeClass = useDeviceTargetClass(props, {
200
- prefix: 'form-field_shape_',
201
- propsKey: 'shape'
202
- });
203
- const inputShapeClass = useDeviceTargetClass(props, {
204
- prefix: 'form-field__item-value_shape_',
205
- propsKey: 'inputShape'
206
- });
207
- const directionClass = useDeviceTargetClass(props, {
208
- prefix: 'direction_',
209
- propsKey: 'direction'
210
- });
211
- const widthClass = useDeviceTargetClass(props, {
212
- prefix: 'width_',
213
- propsKey: 'width'
214
- });
215
- const {
216
- styles: formFieldStyles
217
- } = useStyles(props);
218
- const errorTextSize = props[`${errorKey}MessageTextSize`];
219
- const errorTextColor = props[`${errorKey}MessageTextColor`];
220
- const errorTextWeight = props[`${errorKey}MessageTextWeight`];
221
- return /*#__PURE__*/React$1.createElement(Tag, {
222
- className: clsx(formFieldClass, 'form__item', 'form-field', type && `form-field_type_${type}`, sizeClass, fillClass, shapeClass, isDisabled && `form-field_state_disabled`, isHidden && `form-field_state_hidden`, directionClass, widthClass),
223
- "data-test-id": `${inputName}Field`,
224
- "data-tour": dataTour,
225
- style: formFieldStyles
226
- }, before, (label || labelHidden) && /*#__PURE__*/React$1.createElement("div", {
227
- className: clsx('form-field__label'),
228
- "data-test-id": `${inputName}FieldLabel`,
229
- htmlFor: id
230
- }, /*#__PURE__*/React$1.createElement(Text, {
231
- size: labelTextSize,
232
- textColor: labelTextColor,
233
- textWeight: labelTextWeight,
234
- sizeMobile: labelTextSizeMobile,
235
- sizeTablet: labelTextSizeTablet
236
- }, label, labelHidden && '\u00A0')), desc && /*#__PURE__*/React$1.createElement("div", {
237
- className: "form-field__desc",
238
- "data-test-id": `${inputName}FieldDesc`
239
- }, /*#__PURE__*/React$1.createElement(Text, {
240
- size: descTextSize,
241
- textColor: descTextColor,
242
- textWeight: descTextWeight
243
- }, desc)), /*#__PURE__*/React$1.createElement("div", {
244
- className: clsx('form-field__content', inputFillClass, inputShapeClass)
245
- }, /*#__PURE__*/React$1.createElement("div", {
246
- className: clsx('form-field__content-inner', fieldClass)
247
- }, beforeItem, children, afterItem), showDivider && /*#__PURE__*/React$1.createElement(Divider, {
248
- className: "form-field__item-divider",
249
- width: dividerWidth,
250
- direction: dividerDirection,
251
- size: dividerSize,
252
- fill: dividerFill
253
- })), showMessage && /*#__PURE__*/React$1.createElement("div", {
254
- className: "form-field__message",
255
- "data-test-id": `${inputName}FieldMessage`
256
- }, isErrorState && errorMessage && /*#__PURE__*/React$1.createElement(Text, {
257
- id: `${inputName}-error`,
258
- className: "form-field__message-item form-field__message-item_type-error",
259
- size: errorTextSize,
260
- textColor: errorTextColor,
261
- textWeight: errorTextWeight,
262
- dataTestId: `${inputName}FieldMessageError`
263
- }, errorMessage), Boolean(helpText) && (!isErrorState || !errorMessage) && /*#__PURE__*/React$1.createElement(Text, {
264
- className: "form-field__message-item form-field__message-item_type_help-text",
265
- size: helpTextSize,
266
- textColor: isValidState ? helpTextColorSuccess : helpTextColor,
267
- textWeight: helpTextWeight,
268
- dataTestId: `${inputName}FieldMessageHelpText`,
269
- sizeMobile: helpTextSizeMobile
270
- }, helpText), (!isErrorState && !helpText || isErrorState && !helpText && !errorMessage) && /*#__PURE__*/React$1.createElement(Text, {
271
- className: "form-field__message-item form-field__message-item_type_help-text",
272
- size: messageTextSize,
273
- dataTestId: `${inputName}FieldMessageHelpText`
274
- }, '\u00A0')), after);
275
- }
276
- function FieldWrapper(props) {
277
- const {
278
- inputName
279
- } = props;
280
- const {
281
- change
282
- } = useForm(); // , mutators
283
-
284
- useEffect(() => {
285
- return () => {
286
- change(inputName, undefined);
287
- };
288
- }, []);
289
- return /*#__PURE__*/React$1.createElement(FieldWrapperBase, props);
290
- }
291
-
292
- // Whether to display an error message based on "fieldProps" and meta-objects
293
- function useFieldValidationState(props) {
294
- const {
295
- fieldProps = {},
296
- meta = {},
297
- input = {}
298
- } = props;
299
- // Determines if there's a submission error that hasn't been rectified since the last submission.
300
- const submitError = !meta.modifiedSinceLastSubmit && meta.submitError;
301
-
302
- // Determines a key for the error, defaulting to 'error' if no specific key is found.
303
- const errorKey = meta.error?.key || 'error';
304
- const successKey = 'success';
305
-
306
- // Determines if the field is in an error state based on various conditions.
307
- const isErrorState = useMemo(() => {
308
- if (fieldProps.showErrorsOnSubmit) {
309
- return Boolean(meta.submitFailed && meta.touched && (meta.error || submitError));
310
- } else {
311
- return Boolean(meta.touched && (meta.error || submitError));
312
- }
313
- }, [fieldProps.showErrorsOnSubmit, meta.submitFailed, meta.touched, meta.error, submitError]);
314
-
315
- // Determines if the field's input state is valid
316
- const isValidState = useMemo(() => {
317
- const hasValue = Array.isArray(input?.value) ? input?.value.length : input?.value;
318
- const isModifiedAfterSubmit = meta.modifiedSinceLastSubmit && !meta.error && submitError;
319
- return Boolean(hasValue && (meta.valid || isModifiedAfterSubmit));
320
- }, [input?.value, meta.valid, meta.error, submitError, meta.modifiedSinceLastSubmit]);
321
- const errorMessage = useMemo(() => {
322
- // If final-form field has error in "meta" render-property.
323
- // If field not modified after last form submit - use submit error
324
- const error = meta.error || submitError || false;
325
- if (error) {
326
- // And error in "meta" is string
327
- if (typeof error === 'string') {
328
- // Return error as message
329
- return error;
330
- }
331
- // Or if error in "meta" has "message" property(is object of error)
332
- if (error.message) {
333
- // Return message from error
334
- return error.message;
335
- }
336
- }
337
-
338
- // Field doesn't have error message
339
- return '';
340
- }, [meta.error, submitError]);
341
- return {
342
- errorKey,
343
- successKey,
344
- isErrorState,
345
- isValidState,
346
- errorMessage
347
- };
348
- }
349
-
350
- // This hook changes the props of the child component depending on the type of error,
351
- // looks at what props were in initialProps, if they are there then changes
352
- function useValidationAppearanceInputProps(props) {
353
- const {
354
- validationStateKey,
355
- inputProps
356
- } = props;
357
-
358
- // TODO: need to add props that can change during errors in this field
359
- const validationAppearanceInputProps = useMemo(() => {
360
- // const resultAppearanceProps = {
361
- // messageTextColor: props.errorMessageTextColor,
362
- // messageTextSize: props.errorMessageTextSize,
363
- // messageTextWeight: props.errorMessageTextWeight,
364
- // // Override appearance(styled) props for child input
365
- // inputProps: {},
366
- // }
367
- const updatedInputProps = {};
368
- if (validationStateKey) {
369
- Object.entries(inputProps).forEach(([propKey, propValue]) => {
370
- // Convert the input property key to "snake_case" format.
371
- // e.g. "requiredBorderColor" -> "required_border_color".
372
- const propKeySnake = snakeCase(propKey);
373
- // Check if this property is for appearance.
374
- // Key maybe starts with: "error", "required", "success", etc from validation config.
375
- const isPropForValidationState = propKeySnake.startsWith(`${validationStateKey}_`);
376
-
377
- // If property is for appearance
378
- if (isPropForValidationState) {
379
- // Remove validation state part from begin of property key, to make clean appearance property.
380
- // e.g. "required_border_color" -> "border_color".
381
- const stateTargetKeySnake = propKeySnake.replace(`${validationStateKey}_`, '');
382
- // Convert clean appearance property key to "camelCase" format.
383
- // e.g. "border_color" -> "borderColor"
384
- const stateTargetKey = camelCase(stateTargetKeySnake);
385
- // And save the value with a new clean property key
386
- updatedInputProps[stateTargetKey] = propValue;
387
- // Else if not already added earlier
388
- } else if (!updatedInputProps[propKey]) {
389
- // Just save original property
390
- updatedInputProps[propKey] = propValue;
391
- }
392
- });
393
- }
394
- return updatedInputProps;
395
- }, [validationStateKey, inputProps]);
396
- return validationAppearanceInputProps;
397
- }
398
-
399
- const defaultCheckboxProps = {
400
- appearance: 'defaultPrimary',
401
- width: 'fill',
402
- errorBorderColor: 'errorBorderSecondary',
403
- requiredBorderColor: 'warningBorderSecondary'
404
- };
405
-
406
- const CheckboxField = /*#__PURE__*/React$1.memo(function CheckboxField(props) {
407
- const {
408
- name,
409
- initialValue,
410
- isDisabled,
411
- classNameGroupItem,
412
- fieldProps = {},
413
- inputProps = {},
414
- showMessage,
415
- isRequired,
416
- onChange
417
- } = props;
418
- return /*#__PURE__*/React$1.createElement(Field, {
419
- type: "checkbox",
420
- name: name,
421
- initialValue: initialValue
422
- }, function Render({
423
- input,
424
- meta
425
- }) {
426
- /** Note:
427
- * Create "Render" function by "eslint-react-hooks/rules-of-hooks":
428
- * React Hooks cannot be called inside a callback.
429
- * React Hooks must be called in a React function component or a
430
- * custom React Hook function.
431
- */
432
-
433
- const onChangeField = useCallback(event => {
434
- input.onChange(event);
435
- if (onChange) {
436
- onChange(event.target.checked, input.name);
437
- }
438
- }, [onChange, input.onChange]);
439
- const {
440
- errorKey,
441
- errorMessage,
442
- isErrorState,
443
- isValidState
444
- } = useFieldValidationState({
445
- fieldProps: fieldProps,
446
- input: input,
447
- meta: meta
448
- });
449
- const updatedInputProps = useValidationAppearanceInputProps({
450
- inputProps: inputProps,
451
- validationStateKey: isErrorState ? errorKey : 'success'
452
- });
453
- return /*#__PURE__*/React$1.createElement(FieldWrapper, Object.assign({
454
- className: clsx('form-field_type_checkbox', 'form__item_type_checkbox', classNameGroupItem),
455
- errorKey: errorKey,
456
- errorMessage: errorMessage,
457
- isErrorState: isErrorState,
458
- metaError: meta.error,
459
- isDisabled: isDisabled,
460
- fieldClassName: "form-checkbox",
461
- inputName: input.name,
462
- inputValue: input.checked,
463
- metaActive: meta.active,
464
- showMessage: showMessage,
465
- tag: "label",
466
- isRequired: isRequired,
467
- isValidState: isValidState
468
- }, fieldProps), /*#__PURE__*/React$1.createElement(Checkbox, Object.assign({
469
- type: "checkbox",
470
- name: input.name,
471
- isDisabled: isDisabled,
472
- autoComplete: "nope",
473
- checked: input.checked,
474
- onBlur: input.onBlur,
475
- onChange: onChangeField,
476
- onFocus: input.onFocus
477
- }, updatedInputProps)));
478
- });
479
- });
480
-
481
- const defaultChoiceProps = {
482
- width: 'fill',
483
- borderColor: 'surfaceBorderTertiary',
484
- // error
485
- errorBorderColor: 'errorBorderPrimary',
486
- fill: 'surfaceSecondary',
487
- fillActive: 'accentPrimary',
488
- fillActiveDisabled: 'surfaceTertiary',
489
- fillHover: 'surfacePrimaryHover',
490
- indicatorFill: 'accentPrimary',
491
- labelTextActiveColor: 'accentTextPrimary',
492
- labelTextActiveColorDisabled: 'surfaceTextDisabled',
493
- labelTextColor: 'surfaceTextPrimary',
494
- labelTextColorDisabled: 'surfaceTextDisabled',
495
- labelTextSize: 'm',
496
- requiredBorderColor: 'warningBorderPrimary',
497
- shape: 'rounded'
498
- };
499
-
500
- const ChoiceField = /*#__PURE__*/React$1.memo(function ChoiceField(props) {
501
- const {
502
- name,
503
- initialValue,
504
- label,
505
- isDisabled,
506
- classNameGroupItem,
507
- fieldProps,
508
- inputProps,
509
- messageType,
510
- options,
511
- placeholder,
512
- showMessage,
513
- isCheckbox,
514
- isRequired,
515
- onChange
516
- } = props;
517
- const {
518
- change
519
- } = useForm();
520
- const setActiveSegment = useCallback((option, isChecked) => {
521
- change(name, isChecked && option.value);
522
- if (onChange) {
523
- onChange(option.value, name, isChecked);
524
- }
525
- }, [change, onChange]);
526
- return /*#__PURE__*/React$1.createElement(Field, {
527
- initialValue: initialValue,
528
- name: name
529
- }, function Render({
530
- input,
531
- meta
532
- }) {
533
- /** Note:
534
- * Create "Render" function by "eslint-react-hooks/rules-of-hooks":
535
- * React Hooks cannot be called inside a callback.
536
- * React Hooks must be called in a React function component or a
537
- * custom React Hook function.
538
- */
539
- const activeOption = useMemo(() => {
540
- const emptyOption = {
541
- value: null,
542
- label: null
543
- };
544
- if (input.value) {
545
- const currentOption = options.find(option => option.value === input.value);
546
- return currentOption || emptyOption;
547
- }
548
- return emptyOption;
549
- }, [input.value]);
550
- const {
551
- isErrorState,
552
- isValidState,
553
- errorKey,
554
- errorMessage
555
- } = useFieldValidationState({
556
- fieldProps: fieldProps,
557
- input: input,
558
- meta: meta
559
- });
560
- const updatedInputProps = useValidationAppearanceInputProps({
561
- inputProps: inputProps,
562
- validationStateKey: isErrorState ? errorKey : 'success'
563
- });
564
- return /*#__PURE__*/React$1.createElement(FieldWrapper, Object.assign({
565
- className: clsx('form-field_type_choice', 'form__item_type_choice', classNameGroupItem),
566
- label: label,
567
- errorKey: errorKey,
568
- errorMessage: errorMessage,
569
- isErrorState: isErrorState,
570
- metaError: meta.error,
571
- isDisabled: isDisabled,
572
- fieldClassName: "form-choice",
573
- inputName: input.name,
574
- inputValue: input.value || [],
575
- messageType: messageType,
576
- metaActive: meta.active,
577
- showMessage: showMessage,
578
- isRequired: isRequired,
579
- isValidState: isValidState
580
- }, fieldProps), /*#__PURE__*/React$1.createElement(Choice, Object.assign({
581
- className: clsx(meta.active && 'form-choice_state_focus', meta.error && meta.touched && `form-choice_state_${errorKey}`),
582
- name: input.name,
583
- isDisabled: isDisabled,
584
- active: activeOption,
585
- inputValue: input.value || [],
586
- options: options,
587
- placeholder: placeholder,
588
- isCheckbox: isCheckbox,
589
- isRequired: isRequired,
590
- setActiveSegment: setActiveSegment
591
- }, updatedInputProps)));
592
- });
593
- });
594
-
595
- const CustomField = /*#__PURE__*/React$1.memo(function CustomField(props) {
596
- const {
597
- Component,
598
- isDisabled,
599
- isRequired,
600
- name,
601
- initialValue,
602
- fieldProps = {},
603
- classNameGroupItem,
604
- showMessage,
605
- clearIcon,
606
- clearIconFill,
607
- clearIconFillHover,
608
- clearIconShape,
609
- clearIconSize,
610
- onClickClearIcon
611
- } = props;
612
- return /*#__PURE__*/React$1.createElement(Field, {
613
- initialValue: initialValue,
614
- name: name
615
- }, function Render({
616
- input,
617
- meta
618
- }) {
619
- /** Note:
620
- * Create "Render" function by "eslint-react-hooks/rules-of-hooks":
621
- * React Hooks cannot be called inside a callback.
622
- * React Hooks must be called in a React function component or a
623
- * custom React Hook function.
624
- */
625
-
626
- const {
627
- isErrorState,
628
- isValidState,
629
- errorKey,
630
- errorMessage
631
- } = useFieldValidationState({
632
- fieldProps: fieldProps,
633
- input: input,
634
- meta: meta
635
- });
636
- const updatedInputProps = useValidationAppearanceInputProps({
637
- validationStateKey: isErrorState ? errorKey : 'success',
638
- // For "Custom" field we pass all props. Can contain some special props, we don't known.
639
- inputProps: props
640
- });
641
- return /*#__PURE__*/React$1.createElement(FieldWrapper, Object.assign({
642
- className: clsx('form-field_type_custom', 'form__item_type_custom', classNameGroupItem),
643
- errorKey: errorKey,
644
- errorMessage: errorMessage,
645
- fieldClassName: 'form-custom',
646
- inputName: input.name,
647
- inputValue: input.value,
648
- isDisabled: isDisabled,
649
- isErrorState: isErrorState,
650
- isRequired: isRequired,
651
- isValidState: isValidState,
652
- metaActive: meta.active,
653
- metaError: meta.error,
654
- showMessage: showMessage
655
- }, fieldProps), /*#__PURE__*/React$1.createElement(Component, Object.assign({}, updatedInputProps, {
656
- input: input,
657
- isDisabled: isDisabled,
658
- meta: meta
659
- })), clearIcon && /*#__PURE__*/React$1.createElement(Icon, {
660
- className: "form-field__icon",
661
- iconFill: clearIconFill,
662
- iconFillHover: clearIconFillHover,
663
- imageSrc: clearIcon,
664
- shape: clearIconShape,
665
- size: clearIconSize,
666
- SvgImage: clearIcon,
667
- onClick: onClickClearIcon
668
- }));
669
- });
670
- });
671
-
672
- const defaultCodeProps = {
673
- fieldProps: {
674
- size: 'l',
675
- labelTextColor: 'surfaceTextPrimary',
676
- labelTextSize: 's',
677
- labelTextWeight: 'normal',
678
- helpText: 'Supporting text',
679
- helpTextColor: 'surfaceTextPrimary',
680
- helpTextSize: 's',
681
- helpTextWeight: 'normal',
682
- showMessage: true
683
- },
684
- inputProps: {
685
- width: 'fill',
686
- size: 'l',
687
- fill: 'surfaceSecondary',
688
- inputBorderColor: 'surfaceBorderTertiary',
689
- inputBorderColorHover: 'surfaceBorderQuaternary',
690
- inputBorderFocusColor: 'surfaceBorderAccent',
691
- inputCaretColor: 'surfaceItemAccent',
692
- inputFill: 'surfacePrimary',
693
- inputFillHover: 'surfaceSecondary',
694
- inputPlaceholderTextColor: 'surfaceSecondary',
695
- inputSize: 'l',
696
- inputTextColor: 'surfaceSecondary',
697
- inputTextSize: 'xxl',
698
- inputTextWeight: 'normal'
699
- }
700
- };
701
-
702
- const CodeField = /*#__PURE__*/React$1.memo(function CodeField(props) {
703
- const {
704
- name,
705
- initialValue,
706
- isDisabled,
707
- classNameGroupItem,
708
- fieldProps = {},
709
- inputProps = {},
710
- showMessage,
711
- isRequired
712
- } = props;
713
- return /*#__PURE__*/React$1.createElement(Field, {
714
- name: name,
715
- initialValue: initialValue
716
- }, function Render({
717
- input,
718
- meta
719
- }) {
720
- /** Note:
721
- * Create "Render" function by "eslint-react-hooks/rules-of-hooks":
722
- * React Hooks cannot be called inside a callback.
723
- * React Hooks must be called in a React function component or a
724
- * custom React Hook function.
725
- */
726
-
727
- const {
728
- isErrorState,
729
- isValidState,
730
- errorKey} = useFieldValidationState({
731
- fieldProps: fieldProps,
732
- input: input,
733
- meta: meta
734
- });
735
- const updatedInputProps = useValidationAppearanceInputProps({
736
- inputProps: inputProps,
737
- validationStateKey: isErrorState ? errorKey : 'success'
738
- });
739
- return /*#__PURE__*/React$1.createElement(FieldWrapper, Object.assign({
740
- className: clsx('form-field_type_code', 'form__item_type_code', classNameGroupItem),
741
- fieldClassName: 'form-code',
742
- inputName: input.name,
743
- inputValue: input.value,
744
- metaActive: meta.active,
745
- showMessage: showMessage,
746
- isRequired: isRequired,
747
- isValidState: isValidState
748
- }, fieldProps), /*#__PURE__*/React$1.createElement(Code, Object.assign({
749
- name: input.name,
750
- isDisabled: isDisabled,
751
- autoComplete: "nope",
752
- value: input.value,
753
- onBlur: input.onBlur,
754
- onChange: input.onChange,
755
- onFocus: input.onFocus
756
- }, updatedInputProps)));
757
- });
758
- });
759
-
760
- const defaultDatepickerProps = {
761
- dateFormat: 'dd/MM/yyyy - HH:mm',
762
- readOnly: false,
763
- selectsRange: false,
764
- showTimeSelect: true,
765
- timeCaption: 'Время',
766
- timeFormat: 'p',
767
- timeIntervals: 60,
768
- isClearable: true,
769
- isStartDefaultNull: true
770
- };
771
-
772
- function DatePickerField(props) {
773
- const {
774
- name,
775
- isDisabled,
776
- classNameGroupItem,
777
- datePickerProps,
778
- fieldProps = {},
779
- inputProps = {},
780
- showMessage,
781
- isRequired,
782
- onChange
783
- } = props;
784
- return /*#__PURE__*/React$1.createElement(Field, {
785
- name: name
786
- }, function Render({
787
- input,
788
- meta
789
- }) {
790
- /** Note:
791
- * Create "Render" function by "eslint-react-hooks/rules-of-hooks":
792
- * React Hooks cannot be called inside a callback.
793
- * React Hooks must be called in a React function component or a
794
- * custom React Hook function.
795
- */
796
-
797
- const onChangeField = useCallback((startDate, endDate) => {
798
- if (!datePickerProps.selectsRange) {
799
- // When we need to save single date, value is date
800
- // TODO: make object with one date? need to check all forms with DatePickerField
801
- input.onChange(startDate);
802
- } else {
803
- // When we need to save range, value is object with two date
804
- input.onChange({
805
- endDate,
806
- startDate
807
- });
808
- }
809
- if (onChange) {
810
- onChange(startDate, endDate);
811
- }
812
- }, [input.onChange, onChange]);
813
- const {
814
- errorKey,
815
- errorMessage,
816
- isErrorState,
817
- isValidState
818
- } = useFieldValidationState({
819
- fieldProps: fieldProps,
820
- input: input,
821
- meta: meta
822
- });
823
- const updatedInputProps = useValidationAppearanceInputProps({
824
- inputProps: inputProps,
825
- validationStateKey: isErrorState ? errorKey : 'success'
826
- });
827
- return /*#__PURE__*/React$1.createElement(FieldWrapper, Object.assign({
828
- className: clsx('form-field_type_datepicker', 'form__item_type_datepicker', classNameGroupItem),
829
- errorKey: errorKey,
830
- errorMessage: errorMessage,
831
- isErrorState: isErrorState,
832
- metaError: meta.error,
833
- isDisabled: isDisabled,
834
- fieldClassName: "form-datepicker",
835
- inputName: input.name,
836
- inputValue: input.value || '',
837
- metaActive: meta.active,
838
- showMessage: showMessage,
839
- isRequired: isRequired,
840
- isValidState: isValidState
841
- }, fieldProps), /*#__PURE__*/React$1.createElement(DatePickerInput, {
842
- name: input.name,
843
- isDisabled: isDisabled,
844
- datePickerProps: datePickerProps,
845
- endValue: datePickerProps.selectsRange ? input.value.endDate : null,
846
- inputProps: updatedInputProps,
847
- value: datePickerProps.selectsRange ? input.value.startDate : input.value,
848
- onBlur: input.onBlur,
849
- onChange: onChangeField,
850
- onFocus: input.onFocus
851
- }));
852
- });
853
- }
854
-
855
- const defaultDropzoneProps = {
856
- fill: 'surfacePrimary',
857
- borderColor: 'surfaceBorderTertiary',
858
- borderColorHover: 'surfaceBorderQuaternary',
859
- hintTitle: 'Перетащите изображение или выберите файл с компьютера',
860
- hintTitleTextColor: 'surfaceTextPrimary',
861
- hintTitleTextSize: 'm',
862
- removeThumbAppearance: 'dangerPrimary sizeM',
863
- removeThumbShape: 'rounded',
864
- shape: 'rounded',
865
- showFilename: true,
866
- thumbBorderColor: 'surfaceBorderTertiary',
867
- thumbBorderColorHover: 'surfaceBorderQuaternary',
868
- thumbBorderWidth: 1,
869
- thumbNameTextColor: 'surfaceTextPrimary',
870
- thumbNameTextSize: 's',
871
- isPreviews: true
872
- };
873
-
874
- const FileInputDropzone = /*#__PURE__*/React$1.memo(function FileInputDropzone(props) {
875
- const {
876
- className,
877
- maxFiles,
878
- maxSize,
879
- size,
880
- fileErrorText,
881
- dropzoneProps = {},
882
- hintDescription,
883
- hintTitle,
884
- inputName,
885
- inputValue,
886
- showFilename,
887
- thumbColumn,
888
- isPreviews,
889
- onAddFiles,
890
- onDeleteFile
891
- } = props;
892
-
893
- // TODO: delete react-final-form things out of here?
894
- const {
895
- change
896
- } = useForm();
897
- const [fileError, setFileError] = useState('');
898
- const [fileIsLoading, setFileIsLoading] = useState(false);
899
- const filesList = useMemo(() => inputValue ? castArray(inputValue) : [], [inputValue]);
900
- const changeFormState = useCallback(newFiles => {
901
- // If max files in dropzone is 1 - return file as it self, else as array of files
902
- // ps: for old projects compatibility
903
- const toSave = dropzoneProps.maxFiles == 1 ? newFiles[0] : newFiles;
904
- change(inputName, toSave);
905
- return toSave;
906
- },
907
- // If "inputName" will be changes, then it should be a different field
908
- // eslint-disable-next-line react-hooks/exhaustive-deps
909
- [dropzoneProps, change]);
910
-
911
- //
912
- const convertFiledValueAndSaveAsFiles = useCallback(async currentFilesList => {
913
- setFileIsLoading(true);
914
- const newFiles = [];
915
- for (const fileItem of currentFilesList) {
916
- if (typeof fileItem === 'string') {
917
- const newFile = await convertToFile(fileItem, isPreviews);
918
- if (newFile) {
919
- newFiles.push(newFile);
920
- }
921
- } else {
922
- newFiles.push(fileItem);
923
- }
924
- }
925
- changeFormState(newFiles);
926
- setFileIsLoading(false);
927
- }, [isPreviews, changeFormState]);
928
-
929
- // Delete file from dropzone
930
- const removeFile = useCallback((event, index) => {
931
- event.stopPropagation();
932
- event.preventDefault();
933
- const newFiles = [...filesList];
934
- newFiles.splice(index, 1);
935
- if (onDeleteFile) {
936
- onDeleteFile(filesList[index], inputName);
937
- }
938
- changeFormState(newFiles);
939
- },
940
- // If "inputName" will be changes, then it should be a different field
941
- // eslint-disable-next-line react-hooks/exhaustive-deps
942
- [filesList, changeFormState, onDeleteFile]);
943
-
944
- // Create dropzone options
945
- const {
946
- getInputProps,
947
- getRootProps
948
- } = useDropzone({
949
- maxFiles: maxFiles || 5,
950
- maxSize: maxSize || 10485760,
951
- // 10mb
952
- // accept: { 'image/*': [] },
953
- ...dropzoneProps,
954
- getFilesFromEvent: async event => {
955
- const result = await fromEvent(event);
956
- const newFiles = result.filter(item => item instanceof File);
957
- // Add exists and new files to accepted(or rejected)
958
- return [...filesList, ...newFiles];
959
- },
960
- onDropAccepted: acceptedFiles => {
961
- // If dropped files has accepted and we need a previews
962
- if (isPreviews) {
963
- // Add preview to every file
964
- acceptedFiles.forEach(file => {
965
- if (!file.error) {
966
- file.preview = URL.createObjectURL(file);
967
- }
968
- });
969
- }
970
- // Save to form data (including empty when files are not valid)
971
- const filesToSave = changeFormState(acceptedFiles);
972
- setFileError('');
973
-
974
- // Save DataURL for all files
975
- const readerPromisesList = acceptedFiles.map(file => {
976
- return new Promise(resolve => setFileDataURL(file, resolve));
977
- });
978
- // Save files to form values
979
- Promise.all(readerPromisesList).then(() => {
980
- if (onAddFiles) {
981
- onAddFiles(filesToSave, inputName);
982
- }
983
- });
984
- },
985
- onDropRejected: rejectedFiles => {
986
- // If dropped files has rejected
987
- if (rejectedFiles.length) {
988
- let fileErrorMessage = 'Ошибка при добавлении файла';
989
- const firstFileErrorItem = rejectedFiles[0].errors[0];
990
- if (firstFileErrorItem) {
991
- if (firstFileErrorItem.code === ErrorCode.TooManyFiles) {
992
- fileErrorMessage = `Максимальное количество файлов: ${maxFiles}`;
993
- } else {
994
- fileErrorMessage = firstFileErrorItem.message;
995
- }
996
- }
997
- // Show error
998
- setFileError(fileErrorMessage);
999
- } else {
1000
- // Else clean error
1001
- setFileError('');
1002
- }
1003
- }
1004
- });
1005
- useEffect(() => {
1006
- const currentFilesList = castArray(inputValue);
1007
- const isNeedToConvert = currentFilesList.some(fileItem => typeof fileItem === 'string');
1008
- if (isNeedToConvert) {
1009
- // First time convert value to Files and save to local and form state
1010
- convertFiledValueAndSaveAsFiles(currentFilesList);
1011
- }
1012
-
1013
- // Make sure to revoke the data uris to avoid memory leaks, will run on unmount
1014
- return () => {
1015
- filesList.forEach(file => {
1016
- if (file?.preview) {
1017
- URL.revokeObjectURL(file.preview);
1018
- }
1019
- });
1020
- };
1021
- // eslint-disable-next-line react-hooks/exhaustive-deps
1022
- }, [inputValue]);
1023
- const propsGenerator = useDevicePropsGenerator(props);
1024
- const {
1025
- fillClass,
1026
- fillHoverClass,
1027
- borderColorClass,
1028
- borderColorHoverClass,
1029
- borderTypeClass,
1030
- borderWidthClass,
1031
- errorMessageTextColor,
1032
- errorMessageTextSize,
1033
- errorMessageTextWeight,
1034
- hintDescriptionTextColor,
1035
- hintDescriptionTextSize,
1036
- hintDescriptionTextWeight,
1037
- hintDescriptionTextWrap,
1038
- hintTitleTextColor,
1039
- hintTitleTextSize,
1040
- hintTitleTextWeight,
1041
- hintTitleTextWrap,
1042
- removeThumbAppearance,
1043
- removeThumbShape,
1044
- removeThumbText,
1045
- removeThumbTextWeight,
1046
- shapeClass,
1047
- thumbBorderColorClass,
1048
- thumbBorderColorHoverClass,
1049
- thumbBorderTypeClass,
1050
- thumbBorderWidthClass,
1051
- thumbDirectionClass,
1052
- thumbNameTextColor,
1053
- thumbNameTextSize,
1054
- thumbNameTextWeight,
1055
- thumbNameTextWrap
1056
- } = propsGenerator;
1057
- return /*#__PURE__*/React$1.createElement(React$1.Fragment, null, /*#__PURE__*/React$1.createElement("div", getRootProps({
1058
- className: `form-dropzone__dropzone dropzone ${className} form-dropzone__dropzone_size_${size} ${shapeClass}`
1059
- }), /*#__PURE__*/React$1.createElement("input", Object.assign({}, getInputProps(), {
1060
- name: inputName
1061
- })), /*#__PURE__*/React$1.createElement("div", {
1062
- className: clsx('form-dropzone__dropzone-wrapper', thumbColumn && `form-dropzone__dropzone-wrapper_column_${thumbColumn}`, fillClass && `fill_${fillClass}`, fillHoverClass && `fill_hover_${fillHoverClass}`, borderColorClass && `border-color_${borderColorClass}`, borderColorHoverClass && `border-color_hover_${borderColorHoverClass}`, borderWidthClass && `border-width_${borderWidthClass}`, borderTypeClass)
1063
- }, filesList.map((file, index) => /*#__PURE__*/React$1.createElement("aside", {
1064
- className: clsx('form-dropzone__thumb', fillClass, thumbDirectionClass, thumbBorderWidthClass, thumbBorderColorClass, thumbBorderColorHoverClass, thumbBorderTypeClass),
1065
- key: file.id || `${file.name}_${index}`
1066
- }, isPreviews && !file.error && /*#__PURE__*/React$1.createElement("div", {
1067
- className: "form-dropzone__thumb-image"
1068
- }, /*#__PURE__*/React$1.createElement("img", {
1069
- className: "form-dropzone__thumb-image-inner",
1070
- src: file.preview || file.image,
1071
- onLoad: () => {
1072
- // Revoke data uri after image is loaded
1073
- URL.revokeObjectURL(file.preview);
1074
- }
1075
- })), file.error && /*#__PURE__*/React$1.createElement("div", null, /*#__PURE__*/React$1.createElement(Text, {
1076
- size: thumbNameTextSize,
1077
- textColor: thumbNameTextColor,
1078
- textWeight: thumbNameTextWeight,
1079
- textWrap: thumbNameTextWrap
1080
- }, fileErrorText || file.error)), showFilename && /*#__PURE__*/React$1.createElement("div", {
1081
- className: "form-dropzone__thumb-name"
1082
- }, /*#__PURE__*/React$1.createElement(Text, {
1083
- className: "form-dropzone__thumb-name-inner",
1084
- size: thumbNameTextSize,
1085
- textColor: thumbNameTextColor,
1086
- textWeight: thumbNameTextWeight,
1087
- textWrap: thumbNameTextWrap
1088
- }, file.name)), fileIsLoading && /*#__PURE__*/React$1.createElement("div", {
1089
- className: "form-dropzone__thumb-loader"
1090
- }, /*#__PURE__*/React$1.createElement(Loader, {
1091
- width: "fill",
1092
- height: "fill",
1093
- fill: "surfacePrimary",
1094
- itemFill: "surfaceItemAccent",
1095
- set: "simple"
1096
- })), /*#__PURE__*/React$1.createElement("div", {
1097
- className: clsx('form-dropzone__thumb-remove')
1098
- }, /*#__PURE__*/React$1.createElement(Button, {
1099
- className: "form-dropzone__thumb-remove-text",
1100
- appearance: removeThumbAppearance,
1101
- label: removeThumbText || 'Удалить',
1102
- labelTextWeight: removeThumbTextWeight,
1103
- shape: removeThumbShape,
1104
- onClick: event => removeFile(event, index)
1105
- })))), !filesList.length ? /*#__PURE__*/React$1.createElement("div", {
1106
- className: "form-dropzone__hint"
1107
- }, /*#__PURE__*/React$1.createElement(Text, {
1108
- className: "form-dropzone__hint-title",
1109
- size: hintTitleTextSize,
1110
- textColor: hintTitleTextColor,
1111
- textWeight: hintTitleTextWeight,
1112
- textWrap: hintTitleTextWrap
1113
- }, hintTitle || 'Select a file or drag in form'), hintDescription && /*#__PURE__*/React$1.createElement(Text, {
1114
- className: "form-dropzone__hint-text",
1115
- size: hintDescriptionTextSize,
1116
- textColor: hintDescriptionTextColor,
1117
- textWeight: hintDescriptionTextWeight,
1118
- textWrap: hintDescriptionTextWrap
1119
- }, hintDescription)) : /*#__PURE__*/React$1.createElement("div", {
1120
- className: "form-dropzone__hint form-dropzone__hint_type_add-more"
1121
- }, /*#__PURE__*/React$1.createElement(Text, {
1122
- className: "form-dropzone__hint-title",
1123
- size: hintTitleTextSize,
1124
- textColor: hintTitleTextColor,
1125
- textWeight: hintTitleTextWeight,
1126
- textWrap: hintTitleTextWrap
1127
- }, hintTitle || 'Select a file or drag in form'), hintDescription && /*#__PURE__*/React$1.createElement(Text, {
1128
- className: "form-dropzone__hint-text",
1129
- size: hintDescriptionTextSize,
1130
- textColor: hintDescriptionTextColor,
1131
- textWeight: hintDescriptionTextWeight,
1132
- textWrap: hintDescriptionTextWrap
1133
- }, hintDescription)))), fileError && /*#__PURE__*/React$1.createElement("div", {
1134
- className: "form-field__message"
1135
- }, /*#__PURE__*/React$1.createElement(Text, {
1136
- className: "form-field__message-item form-field__message-item_type_message",
1137
- size: errorMessageTextSize,
1138
- textColor: errorMessageTextColor,
1139
- textWeight: errorMessageTextWeight
1140
- }, fileError)));
1141
- });
1142
- async function getFileByURL(url) {
1143
- try {
1144
- const response = await axios({
1145
- url: url,
1146
- responseType: 'blob'
1147
- });
1148
- const blobObject = response.data;
1149
- const dirtyFilename = response.headers['content-disposition']?.split('filename=')[1];
1150
- // Remove double quotes
1151
- let filename = dirtyFilename?.substring(1).slice(0, -1);
1152
- if (!filename) {
1153
- filename = url.split('/').at(-1);
1154
- // const typeParts = blobObject.type.split('/')
1155
- // const fileType = typeParts[typeParts.length - 1]
1156
- // filename = `${new Date().getTime()}.${fileType}`
1157
- }
1158
- return new File([blobObject], filename, {
1159
- type: blobObject.type
1160
- });
1161
- } catch (error) {
1162
- console.log('error: ', error);
1163
- return null;
1164
- }
1165
- }
1166
- async function convertToFile(inputValue, isPreviews) {
1167
- let newFile = null;
1168
-
1169
- // Download image by url and save as File instance
1170
- const isURL = typeof inputValue === 'string' && inputValue.includes('/');
1171
- if (inputValue.image || isURL) {
1172
- newFile = await getFileByURL(inputValue.image || inputValue);
1173
- if (newFile) {
1174
- setFileDataURL(newFile);
1175
- }
1176
- }
1177
-
1178
- // Convert dataURL to File instance
1179
- if (inputValue.dataURL) {
1180
- newFile = createFileFromDataURL(inputValue.name || inputValue.path, inputValue.dataURL);
1181
- newFile.dataURL = inputValue.dataURL;
1182
- }
1183
-
1184
- // Save new File to state
1185
- if (newFile) {
1186
- newFile.id = inputValue.id;
1187
- if (isPreviews) {
1188
- newFile.preview = URL.createObjectURL(newFile);
1189
- }
1190
- }
1191
- return newFile;
1192
- }
1193
- function setFileDataURL(file, resolve) {
1194
- resolve = resolve || (() => {});
1195
- // Init reader and save his file
1196
- const reader = new FileReader();
1197
- reader._readedFile = file;
1198
-
1199
- // Set handlers
1200
- reader.onabort = () => resolve();
1201
- reader.onerror = () => resolve();
1202
- reader.onload = event => {
1203
- event.target._readedFile.dataURL = reader.result;
1204
- resolve();
1205
- };
1206
- // Run reader
1207
- if (file instanceof File) {
1208
- reader.readAsDataURL(file);
1209
- } else {
1210
- resolve();
1211
- }
1212
- }
1213
-
1214
- const FileInput = /*#__PURE__*/React$1.memo(function FileInput(props) {
1215
- const {
1216
- className,
1217
- name,
1218
- width,
1219
- maxFiles,
1220
- maxSize,
1221
- label,
1222
- fileErrorText,
1223
- classNameGroupItem,
1224
- dropzoneProps,
1225
- fieldProps,
1226
- hintDescription,
1227
- hintTitle,
1228
- showFilename,
1229
- showMessage,
1230
- isPreviews,
1231
- isRequired,
1232
- onAddFiles,
1233
- onDeleteFile
1234
- } = props;
1235
- const propsGenerator = useDevicePropsGenerator(props);
1236
- const {
1237
- size,
1238
- fill,
1239
- fillHover,
1240
- labelTextColor,
1241
- borderColorHover,
1242
- borderType,
1243
- borderWidth,
1244
- errorMessageTextColor,
1245
- errorMessageTextSize,
1246
- errorMessageTextWeight,
1247
- hintDescriptionTextColor,
1248
- hintDescriptionTextSize,
1249
- hintDescriptionTextWeight,
1250
- hintDescriptionTextWrap,
1251
- hintTitleTextColor,
1252
- hintTitleTextSize,
1253
- hintTitleTextWeight,
1254
- hintTitleTextWrap,
1255
- removeThumbAppearance,
1256
- removeThumbText,
1257
- removeThumbTextWeight,
1258
- removeThumbShape,
1259
- shape,
1260
- thumbBorderColor,
1261
- thumbBorderColorHover,
1262
- thumbBorderType,
1263
- thumbBorderWidth,
1264
- thumbColumn = 1,
1265
- thumbDirection = 'vertical',
1266
- thumbNameTextColor,
1267
- thumbNameTextSize,
1268
- thumbNameTextWeight,
1269
- thumbNameTextWrap
1270
- } = propsGenerator;
1271
- return /*#__PURE__*/React$1.createElement(Field, {
1272
- name: name
1273
- }, function Render({
1274
- input,
1275
- meta
1276
- }) {
1277
- /** Note:
1278
- * Create "Render" function by "eslint-react-hooks/rules-of-hooks":
1279
- * React Hooks cannot be called inside a callback.
1280
- * React Hooks must be called in a React function component or a
1281
- * custom React Hook function.
1282
- */
1283
-
1284
- const {
1285
- errorKey,
1286
- errorMessage,
1287
- isErrorState,
1288
- isValidState
1289
- } = useFieldValidationState({
1290
- fieldProps: fieldProps,
1291
- input: input,
1292
- meta: meta
1293
- });
1294
- const updatedInputProps = useValidationAppearanceInputProps({
1295
- inputProps: props,
1296
- validationStateKey: isErrorState ? errorKey : 'success'
1297
- });
1298
-
1299
- /** TODO:
1300
- * REFACTOR PROPERTIES
1301
- */
1302
- return /*#__PURE__*/React$1.createElement(FieldWrapper, Object.assign({
1303
- className: clsx('form-field_type_dropzone', 'form__item_type_dropzone', classNameGroupItem),
1304
- width: width,
1305
- label: label,
1306
- labelTextColor: labelTextColor,
1307
- errorKey: errorKey,
1308
- errorMessage: errorMessage,
1309
- isErrorState: isErrorState,
1310
- metaError: meta.error,
1311
- fieldClassName: "form-dropzone",
1312
- inputName: input.name,
1313
- inputValue: input.value,
1314
- metaActive: meta.active,
1315
- metaTouched: meta.touched,
1316
- showMessage: showMessage,
1317
- isRequired: isRequired,
1318
- isValidState: isValidState
1319
- }, fieldProps), /*#__PURE__*/React$1.createElement(FileInputDropzone, {
1320
- className: className,
1321
- maxFiles: maxFiles,
1322
- maxSize: maxSize,
1323
- size: size,
1324
- fill: fill,
1325
- fillHover: fillHover,
1326
- borderColor: updatedInputProps.borderColor,
1327
- borderColorHover: borderColorHover,
1328
- borderType: borderType,
1329
- borderWidth: borderWidth,
1330
- errorMessageTextColor: errorMessageTextColor,
1331
- errorMessageTextSize: errorMessageTextSize,
1332
- errorMessageWeight: errorMessageTextWeight,
1333
- fileErrorText: fileErrorText,
1334
- metaError: meta.error,
1335
- dropzoneProps: dropzoneProps,
1336
- hintDescription: hintDescription,
1337
- hintDescriptionTextColor: hintDescriptionTextColor,
1338
- hintDescriptionTextSize: hintDescriptionTextSize,
1339
- hintDescriptionTextWeight: hintDescriptionTextWeight,
1340
- hintDescriptionTextWrap: hintDescriptionTextWrap,
1341
- hintTitle: hintTitle,
1342
- hintTitleTextColor: hintTitleTextColor,
1343
- hintTitleTextSize: hintTitleTextSize,
1344
- hintTitleTextWeight: hintTitleTextWeight,
1345
- hintTitleTextWrap: hintTitleTextWrap,
1346
- inputName: input.name,
1347
- inputValue: input.value,
1348
- metaTouched: meta.touched,
1349
- removeThumbAppearance: removeThumbAppearance,
1350
- removeThumbText: removeThumbText,
1351
- removeThumbTextWeight: removeThumbTextWeight,
1352
- removeThumbShape: removeThumbShape,
1353
- shape: shape,
1354
- showFilename: showFilename,
1355
- thumbBorderColor: thumbBorderColor,
1356
- thumbBorderColorHover: thumbBorderColorHover,
1357
- thumbBorderType: thumbBorderType,
1358
- thumbBorderWidth: thumbBorderWidth,
1359
- thumbColumn: thumbColumn,
1360
- thumbDirection: thumbDirection,
1361
- thumbNameTextColor: thumbNameTextColor,
1362
- thumbNameTextSize: thumbNameTextSize,
1363
- thumbNameTextWeight: thumbNameTextWeight,
1364
- thumbNameTextWrap: thumbNameTextWrap,
1365
- isPreviews: isPreviews,
1366
- onAddFiles: onAddFiles,
1367
- onDeleteFile: onDeleteFile
1368
- }));
1369
- });
1370
- });
1371
-
1372
- const Group = /*#__PURE__*/React$1.memo(function Group(props) {
1373
- const {
1374
- className,
1375
- name,
1376
- label,
1377
- labelTextColor,
1378
- labelTextSize,
1379
- labelTextWeight,
1380
- message,
1381
- dataTour,
1382
- messageTextColor = 'surfaceTextTertiary',
1383
- messageTextSize = 's',
1384
- messageTextWeight,
1385
- showGroupMessage,
1386
- before,
1387
- after,
1388
- isHidden,
1389
- children
1390
- } = props;
1391
- return /*#__PURE__*/React$1.createElement(Field, {
1392
- name: name
1393
- }, function Render({
1394
- input,
1395
- meta
1396
- }) {
1397
- /** Note:
1398
- * Create "Render" function by "eslint-react-hooks/rules-of-hooks":
1399
- * React Hooks cannot be called inside a callback.
1400
- * React Hooks must be called in a React function component or a
1401
- * custom React Hook function.
1402
- */
1403
- const {
1404
- isErrorState,
1405
- errorKey,
1406
- errorMessage
1407
- } = useFieldValidationState({
1408
- fieldProps: props,
1409
- // or fieldProps?
1410
- input: input,
1411
- meta: meta
1412
- });
1413
- const updatedProps = useValidationAppearanceInputProps({
1414
- inputProps: props,
1415
- validationStateKey: isErrorState ? errorKey : 'success'
1416
- });
1417
- return /*#__PURE__*/React$1.createElement("div", {
1418
- className: clsx('form__group', className, isHidden && 'form__group_hidden'),
1419
- "data-tour": dataTour
1420
- }, /*#__PURE__*/React$1.createElement("div", {
1421
- className: "form__group-wrapper"
1422
- }, before, label && /*#__PURE__*/React$1.createElement("div", {
1423
- className: "form__group-label"
1424
- }, /*#__PURE__*/React$1.createElement(Title, {
1425
- size: labelTextSize,
1426
- textColor: labelTextColor,
1427
- textWeight: labelTextWeight
1428
- }, label)), /*#__PURE__*/React$1.createElement("div", {
1429
- className: "form__group-items"
1430
- }, children), after), showGroupMessage && /*#__PURE__*/React$1.createElement(React$1.Fragment, null, isErrorState && errorMessage && /*#__PURE__*/React$1.createElement(Text, {
1431
- className: `form__group-message form__group-message_type-${errorKey}`,
1432
- id: `${name}-error`,
1433
- size: updatedProps.messageTextSize,
1434
- textColor: updatedProps.messageTextColor,
1435
- textWeight: updatedProps.messageTextWeight
1436
- }, errorMessage), Boolean(message) && (!isErrorState || !errorMessage) && /*#__PURE__*/React$1.createElement(Text, {
1437
- className: "form__group-message",
1438
- size: messageTextSize,
1439
- textColor: messageTextColor,
1440
- textWeight: messageTextWeight
1441
- }, message), !isErrorState && !message && /*#__PURE__*/React$1.createElement(Text, {
1442
- className: "form__group-message",
1443
- size: messageTextSize
1444
- }, '\u00A0')));
1445
- });
1446
- });
1447
-
1448
- const defaultInputProps = {
1449
- appearance: 'sizeM defaultSecondary',
1450
- width: 'fill',
1451
- errorBorderColor: 'errorBorderSecondary',
1452
- requiredBorderColor: 'warningBorderSecondary',
1453
- shape: 'rounded'
1454
- };
1455
-
1456
- const InputField = /*#__PURE__*/React$1.memo(function InputField(props) {
1457
- const {
1458
- name,
1459
- initialValue,
1460
- isDisabled,
1461
- classNameGroupItem,
1462
- // dataTestId,
1463
- // iconBorder,
1464
- // iconBorderHover,
1465
- clearIcon,
1466
- clearIconFill,
1467
- clearIconFillHover,
1468
- clearIconShape,
1469
- clearIconSize,
1470
- fieldProps = {},
1471
- iconFill,
1472
- iconFillHover,
1473
- iconRevealableHide,
1474
- iconRevealableShow,
1475
- iconShape,
1476
- iconSize,
1477
- inputProps = {},
1478
- parse,
1479
- showMessage,
1480
- isPassword,
1481
- isRequired,
1482
- isRevealable,
1483
- onChange,
1484
- onClickClearIcon
1485
- } = props;
1486
- const [isRevealed, setIsRevealed] = useState(false);
1487
- const inputType = useMemo(() => {
1488
- if (isPassword) {
1489
- return isRevealed ? 'text' : 'password';
1490
- } else {
1491
- return 'text';
1492
- }
1493
- }, [isRevealed, isPassword]);
1494
- const onClickIconReveal = useCallback(event => {
1495
- event.preventDefault();
1496
- setIsRevealed(prev => !prev);
1497
- }, []);
1498
- return /*#__PURE__*/React$1.createElement(Field, {
1499
- name: name,
1500
- initialValue: initialValue,
1501
- parse: parse
1502
- }, function Render({
1503
- input,
1504
- meta
1505
- }) {
1506
- /** Note:
1507
- * Create "Render" function by "eslint-react-hooks/rules-of-hooks":
1508
- * React Hooks cannot be called inside a callback.
1509
- * React Hooks must be called in a React function component or a
1510
- * custom React Hook function.
1511
- */
1512
-
1513
- const onChangeField = useCallback(event => {
1514
- input.onChange(event);
1515
- if (onChange) {
1516
- onChange(event.target.value, input.name);
1517
- }
1518
- }, [onChange, input.onChange]);
1519
- const {
1520
- isErrorState,
1521
- isValidState,
1522
- errorKey,
1523
- errorMessage
1524
- } = useFieldValidationState({
1525
- fieldProps: fieldProps,
1526
- input: input,
1527
- meta: meta
1528
- });
1529
- const updatedInputProps = useValidationAppearanceInputProps({
1530
- inputProps: inputProps,
1531
- validationStateKey: isErrorState ? errorKey : 'success'
1532
- });
1533
- return /*#__PURE__*/React$1.createElement(FieldWrapper, Object.assign({
1534
- className: clsx('form-field_type_input', 'form__item_type_input', classNameGroupItem),
1535
- errorKey: errorKey,
1536
- errorMessage: errorMessage,
1537
- isErrorState: isErrorState,
1538
- metaError: meta.error,
1539
- isDisabled: isDisabled,
1540
- fieldClassName: isRevealable ? 'form-password' : 'form-input',
1541
- inputName: input.name,
1542
- inputValue: input.value || '',
1543
- metaActive: meta.active,
1544
- showMessage: showMessage,
1545
- isRequired: isRequired,
1546
- isValidState: isValidState
1547
- }, fieldProps), /*#__PURE__*/React$1.createElement(Input, Object.assign({
1548
- className: clsx(meta.active && 'input_state_focus', meta.error && meta.touched && `input_state_${errorKey}`),
1549
- type: inputType,
1550
- name: input.name,
1551
- isDisabled: isDisabled,
1552
- autoComplete: "nope",
1553
- dataTestId: `${input.name}FieldInput`,
1554
- value: input.value || '',
1555
- onBlur: input.onBlur,
1556
- onChange: onChangeField,
1557
- onFocus: input.onFocus
1558
- }, updatedInputProps)), isRevealable && /*#__PURE__*/React$1.createElement(Icon, {
1559
- className: "form-field__icon",
1560
- size: iconSize,
1561
- iconFill: iconFill,
1562
- iconFillHover: iconFillHover,
1563
- imageSrc: isRevealed ? iconRevealableHide : iconRevealableShow,
1564
- shape: iconShape,
1565
- SvgImage: isRevealed ? iconRevealableHide : iconRevealableShow,
1566
- onClick: onClickIconReveal
1567
- }), clearIcon && /*#__PURE__*/React$1.createElement(Icon, {
1568
- className: "form-field__icon",
1569
- size: clearIconSize,
1570
- iconFill: clearIconFill,
1571
- iconFillHover: clearIconFillHover,
1572
- imageSrc: clearIcon,
1573
- shape: clearIconShape,
1574
- SvgImage: clearIcon,
1575
- onClick: onClickClearIcon
1576
- }));
1577
- });
1578
- });
1579
-
1580
- const defaultRadioProps = {
1581
- fieldProps: {
1582
- width: 'fill',
1583
- size: 'm',
1584
- labelTextColor: 'surfaceTextPrimary',
1585
- labelTextSize: 's',
1586
- labelTextWeight: 'normal',
1587
- textColor: 'surfaceTextPrimary',
1588
- helpText: 'Supporting text',
1589
- helpTextColor: 'surfaceTextPrimary',
1590
- helpTextSize: 's',
1591
- helpTextWeight: 'normal',
1592
- showMessage: true
1593
- },
1594
- inputProps: {
1595
- width: 'fill',
1596
- size: 'm',
1597
- labelTextColor: 'surfaceTextPrimary',
1598
- labelTextSize: 's',
1599
- descTextColor: 'surfaceTextPrimary',
1600
- descTextSize: 's'
1601
- }
1602
- };
1603
-
1604
- function RadioGroupInput(props) {
1605
- const {
1606
- input,
1607
- value,
1608
- onChange
1609
- } = props;
1610
- const onChangeField = useCallback(event => onChange(event.target.value), [onChange]);
1611
- return /*#__PURE__*/React.createElement(Input, Object.assign({
1612
- name: input.name,
1613
- autoComplete: "nope",
1614
- value: value,
1615
- onBlur: input.onBlur,
1616
- onChange: onChangeField,
1617
- onFocus: input.onFocus
1618
- }, props));
1619
- }
1620
-
1621
- function RadioGroupItem(props) {
1622
- const {
1623
- input,
1624
- inputProps,
1625
- option,
1626
- onChange
1627
- } = props;
1628
- const onChangeField = useCallback(event => {
1629
- if (event.target.checked) {
1630
- onChange(option.value);
1631
- }
1632
- }, [onChange]);
1633
- return /*#__PURE__*/React.createElement(Radio, Object.assign({
1634
- className: "form-radio__item",
1635
- type: "radio",
1636
- name: input.name,
1637
- label: option.label,
1638
- checked: option.value === input.value,
1639
- value: option.value,
1640
- onBlur: input.onBlur,
1641
- onChange: onChangeField,
1642
- onFocus: input.onFocus
1643
- }, inputProps));
1644
- }
1645
-
1646
- function RadioGroupList(props) {
1647
- const {
1648
- editableProps,
1649
- input,
1650
- inputProps,
1651
- options,
1652
- onChange
1653
- } = props;
1654
- const [editableValue, setEditableValue] = useState(() => {
1655
- const isRadioValue = options.find(option => option.value === input.value);
1656
- if (!isRadioValue) {
1657
- return input.value;
1658
- }
1659
- return '';
1660
- });
1661
- useEffect(() => {
1662
- // When a new value from outside enters the form
1663
- if (input.value) {
1664
- // Check value for radio type
1665
- const isRadioValue = options.find(option => option.value === input.value && !option.editable);
1666
- // If new value not in radio list - set to editable input
1667
- setEditableValue(isRadioValue ? '' : input.value);
1668
- } else {
1669
- // If new value is empty - clear editable input
1670
- setEditableValue('');
1671
- }
1672
- }, [input.value]);
1673
-
1674
- // Callback for value changes
1675
- const onChangeSomeInput = useCallback(value => {
1676
- // Save to form values
1677
- input.onChange(value);
1678
- if (onChange) {
1679
- // Pass to custom event
1680
- onChange(value, input.name);
1681
- }
1682
- }, [input, onChange]);
1683
-
1684
- // Handle for radio inputs
1685
- const onChangeRadio = useCallback(value => {
1686
- setEditableValue('');
1687
- onChangeSomeInput(value);
1688
- }, [onChangeSomeInput]);
1689
-
1690
- // Handle for text input
1691
- const onChangeEditable = useCallback(value => {
1692
- setEditableValue(value);
1693
- onChangeSomeInput(value);
1694
- }, [onChangeSomeInput]);
1695
- return /*#__PURE__*/React$1.createElement(React$1.Fragment, null, options.map(option => option.editable ? /*#__PURE__*/React$1.createElement(RadioGroupInput, {
1696
- key: option.label,
1697
- editableProps: editableProps,
1698
- input: input,
1699
- inputProps: inputProps,
1700
- option: option,
1701
- value: editableValue,
1702
- onChange: onChangeEditable
1703
- }) : /*#__PURE__*/React$1.createElement(RadioGroupItem, {
1704
- key: option.value,
1705
- input: input,
1706
- inputProps: inputProps,
1707
- option: option,
1708
- onChange: onChangeRadio
1709
- })));
1710
- }
1711
-
1712
- const RadioGroup = /*#__PURE__*/React$1.memo(function RadioGroup(props) {
1713
- const {
1714
- name,
1715
- isDisabled,
1716
- editableProps = {},
1717
- fieldProps = {},
1718
- inputProps = {},
1719
- options = [],
1720
- showMessage,
1721
- isRequired,
1722
- onChange
1723
- } = props;
1724
- return /*#__PURE__*/React$1.createElement(Field, {
1725
- name: name
1726
- }, function Render({
1727
- input,
1728
- meta
1729
- }) {
1730
- /** Note:
1731
- * Create "Render" function by "eslint-react-hooks/rules-of-hooks":
1732
- * React Hooks cannot be called inside a callback.
1733
- * React Hooks must be called in a React function component or a
1734
- * custom React Hook function.
1735
- */
1736
-
1737
- const {
1738
- errorKey,
1739
- errorMessage,
1740
- isErrorState,
1741
- isValidState
1742
- } = useFieldValidationState({
1743
- fieldProps: fieldProps,
1744
- input: input,
1745
- meta: meta
1746
- });
1747
- const updatedInputProps = useValidationAppearanceInputProps({
1748
- inputProps: inputProps,
1749
- validationStateKey: isErrorState ? errorKey : 'success'
1750
- });
1751
- return /*#__PURE__*/React$1.createElement(FieldWrapper, Object.assign({
1752
- className: clsx('form-field_type_radio', 'form__item_type_radio"', classNameGroupItem),
1753
- errorKey: errorKey,
1754
- errorMessage: errorMessage,
1755
- isErrorState: isErrorState,
1756
- metaError: meta.error,
1757
- isDisabled: isDisabled,
1758
- fieldClassName: 'form-radio',
1759
- inputName: input.name,
1760
- inputValue: input.value || '',
1761
- metaActive: meta.active,
1762
- showMessage: showMessage,
1763
- isRequired: isRequired,
1764
- isValidState: isValidState
1765
- }, fieldProps), /*#__PURE__*/React$1.createElement(RadioGroupList, {
1766
- isDisabled: isDisabled,
1767
- editableProps: editableProps,
1768
- input: input,
1769
- inputProps: updatedInputProps,
1770
- options: options,
1771
- onChange: onChange
1772
- }));
1773
- });
1774
- });
1775
-
1776
- const defaultSegmentedProps = {
1777
- appearance: 'sizeM surfacePrimary',
1778
- width: 'fill',
1779
- errorLabelTextColor: 'errorTextPrimary',
1780
- requiredLabelTextColor: 'warningTextPrimary',
1781
- shape: 'rounded'
1782
- };
1783
-
1784
- function SegmentedField(props) {
1785
- const {
1786
- name,
1787
- isDisabled,
1788
- fieldProps,
1789
- inputProps,
1790
- options,
1791
- showMessage,
1792
- isRequired
1793
- } = props;
1794
- const {
1795
- change
1796
- } = useForm();
1797
- const setActiveSegment = useCallback(option => {
1798
- change(name, option.value);
1799
- }, [change]);
1800
- return /*#__PURE__*/React$1.createElement(Field, {
1801
- name: name
1802
- }, function Render({
1803
- input,
1804
- meta
1805
- }) {
1806
- /** Note:
1807
- * Create "Render" function by "eslint-react-hooks/rules-of-hooks":
1808
- * React Hooks cannot be called inside a callback.
1809
- * React Hooks must be called in a React function component or a
1810
- * custom React Hook function.
1811
- */
1812
-
1813
- const activeOption = useMemo(() => {
1814
- const emptyOption = {
1815
- label: null,
1816
- value: null
1817
- };
1818
- if (input.value) {
1819
- const currentOption = options.find(option => option.value === input.value);
1820
- return currentOption || emptyOption;
1821
- }
1822
- return emptyOption;
1823
- }, [input.value]);
1824
- const {
1825
- errorKey,
1826
- errorMessage,
1827
- isErrorState,
1828
- isValidState
1829
- } = useFieldValidationState({
1830
- fieldProps: fieldProps,
1831
- input: input,
1832
- meta: meta
1833
- });
1834
- const updatedInputProps = useValidationAppearanceInputProps({
1835
- inputProps: inputProps,
1836
- validationStateKey: isErrorState ? errorKey : 'success'
1837
- });
1838
- return /*#__PURE__*/React$1.createElement(FieldWrapper, Object.assign({
1839
- className: clsx('form-field_type_segmented', 'form__item_type_segmented'),
1840
- errorKey: errorKey,
1841
- errorMessage: errorMessage,
1842
- isErrorState: isErrorState,
1843
- metaError: meta.error,
1844
- isDisabled: isDisabled,
1845
- fieldClassName: "form-segmented",
1846
- inputName: input.name,
1847
- inputValue: input.value || [],
1848
- metaActive: meta.active,
1849
- showMessage: showMessage,
1850
- isRequired: isRequired,
1851
- isValidState: isValidState
1852
- }, fieldProps), /*#__PURE__*/React$1.createElement(Segmented, Object.assign({
1853
- isDisabled: isDisabled,
1854
- activeSegment: activeOption,
1855
- segments: options,
1856
- setActiveSegment: setActiveSegment
1857
- }, updatedInputProps)));
1858
- });
1859
- }
1860
-
1861
- const defaultSelectProps = {
1862
- elevation: 8,
1863
- isClearable: true,
1864
- isSearchable: true,
1865
- badgeAppearance: 'accent',
1866
- badgeSize: 'm',
1867
- badgeTextSize: 'm',
1868
- // clearIcon: icon24.Clear,
1869
- clearIconFill: 'surfaceItemPrimary',
1870
- closeMenuOnSelect: true,
1871
- // optionSelected: <Icon iconFill="surfaceItemAccent" SvgImage={icon24.Check} />,
1872
-
1873
- dividerDirection: 'horizontal',
1874
- dividerFill: 'surfaceTertiary',
1875
- dividerSize: 'xxs',
1876
- // dropdownIcon: icon24.ChevronDownSmall,
1877
- dropdownIconFill: 'surfaceItemPrimary',
1878
- // error
1879
- errorInputBorderColor: 'errorBorderPrimary',
1880
- headingFill: 'surfaceSecondary',
1881
- loadingMessage: /*#__PURE__*/React$1.createElement(Loader, {
1882
- width: "fill",
1883
- height: "fill",
1884
- fill: "surfacePrimary",
1885
- position: "absolute",
1886
- left: "0px",
1887
- right: "0px",
1888
- zIndex: "1",
1889
- itemFill: "surfaceItemAccent",
1890
- set: "simple"
1891
- })
1892
- };
1893
-
1894
- function getDefaultValue(options, selectValue) {
1895
- const selectValues = Array.isArray(selectValue) ? selectValue : [selectValue];
1896
- let result = [];
1897
- options.forEach(item => {
1898
- const isValue = selectValues.includes(item.value);
1899
- const isLabel = selectValues.includes(item.label);
1900
- let childOptions = [];
1901
- if (item.options) {
1902
- childOptions = getDefaultValue(item.options, selectValue);
1903
- }
1904
- if (isValue || isLabel) {
1905
- result.push(item);
1906
- } else if (childOptions.length) {
1907
- result = result.concat(childOptions);
1908
- }
1909
- });
1910
- return result;
1911
- }
1912
- const SelectField = /*#__PURE__*/React$1.memo(function SelectField(props) {
1913
- const {
1914
- isDisabled,
1915
- isRequired,
1916
- classNameGroupItem,
1917
- fieldProps,
1918
- initialValue,
1919
- name,
1920
- options = [],
1921
- selectProps,
1922
- selectRef,
1923
- showMessage,
1924
- onChange,
1925
- onInputChange
1926
- } = props;
1927
- return /*#__PURE__*/React$1.createElement(Field, {
1928
- name: name,
1929
- initialValue: initialValue
1930
- }, function Render({
1931
- input,
1932
- meta
1933
- }) {
1934
- /** Note:
1935
- * Create "Render" function by "eslint-react-hooks/rules-of-hooks":
1936
- * React Hooks cannot be called inside a callback.
1937
- * React Hooks must be called in a React function component or a
1938
- * custom React Hook function.
1939
- */
1940
- const [selectedOptions, setSelectedOptions] = useState(null);
1941
- const defaultValue = useMemo(() => {
1942
- const optionsValues = getDefaultValue(options, input.value);
1943
- if (!optionsValues.length && input.value?.length) {
1944
- optionsValues.push({
1945
- label: input.value,
1946
- value: input.value
1947
- });
1948
- }
1949
- return optionsValues;
1950
- }, [input.value]);
1951
- const onChangeField = useCallback(value => {
1952
- input.onChange(value);
1953
- if (onChange) {
1954
- onChange(value, input.name);
1955
- }
1956
- }, [onChange, input.onChange]);
1957
- const onChangeValue = useCallback((option, actionMeta) => {
1958
- const value = Array.isArray(option) ? option.map(o => o.value) : option?.value || null;
1959
- setSelectedOptions(option);
1960
- onChangeField(value);
1961
- }, [onChangeField]);
1962
- useEffect(() => {
1963
- setSelectedOptions(defaultValue);
1964
- }, [defaultValue]);
1965
- const {
1966
- isErrorState,
1967
- isValidState,
1968
- errorKey,
1969
- errorMessage
1970
- } = useFieldValidationState({
1971
- fieldProps: fieldProps,
1972
- input: input,
1973
- meta: meta
1974
- });
1975
- const updatedSelectProps = useValidationAppearanceInputProps({
1976
- inputProps: selectProps,
1977
- validationStateKey: isErrorState ? errorKey : 'success'
1978
- });
1979
- return /*#__PURE__*/React$1.createElement(FieldWrapper, Object.assign({
1980
- className: clsx('form-field_type_select', 'form__item_type_select', classNameGroupItem),
1981
- errorKey: errorKey,
1982
- errorMessage: errorMessage,
1983
- isErrorState: isErrorState,
1984
- metaError: meta.error,
1985
- isDisabled: isDisabled,
1986
- fieldClassName: 'form-select',
1987
- inputName: input.name,
1988
- inputValue: input.value,
1989
- metaActive: meta.active,
1990
- showMessage: showMessage,
1991
- isRequired: isRequired,
1992
- isValidState: isValidState
1993
- }, fieldProps), /*#__PURE__*/React$1.createElement(Select, Object.assign({
1994
- className: "form-select-item",
1995
- isDisabled: isDisabled,
1996
- instanceId: `id_${input.name}`,
1997
- options: options,
1998
- ref: selectRef,
1999
- value: selectedOptions,
2000
- onChange: onChangeValue,
2001
- onInputChange: onInputChange
2002
- }, updatedSelectProps)));
2003
- });
2004
- });
2005
-
2006
- const defaultSwitchProps = {
2007
- fieldProps: {
2008
- width: 'fill',
2009
- size: 'xl',
2010
- labelTextColor: 'surfaceTextPrimary',
2011
- labelTextSize: 's',
2012
- labelTextWeight: 'normal',
2013
- textColor: 'surfaceTextPrimary',
2014
- helpText: 'Supporting text',
2015
- helpTextColor: 'surfaceTextPrimary',
2016
- helpTextSize: 's',
2017
- helpTextWeight: 'normal',
2018
- showMessage: true
2019
- },
2020
- inputProps: {
2021
- size: 'm',
2022
- fill: 'surfaceSecondary',
2023
- title: 'Switch',
2024
- titleTextColor: 'surfaceTextPrimary',
2025
- titleTextSize: 's',
2026
- desc: 'Description',
2027
- descTextColor: 'surfaceTextPrimary',
2028
- descTextSize: 'xs'
2029
- }
2030
- };
2031
-
2032
- const SwitchField = /*#__PURE__*/React$1.memo(function SwitchField(props) {
2033
- const {
2034
- name,
2035
- isDisabled,
2036
- classNameGroupItem,
2037
- fieldProps = {},
2038
- inputProps = {},
2039
- showMessage,
2040
- isRequired,
2041
- onChange
2042
- } = props;
2043
- return /*#__PURE__*/React$1.createElement(Field, {
2044
- type: "checkbox",
2045
- name: name
2046
- }, function Render({
2047
- input,
2048
- meta
2049
- }) {
2050
- /** Note:
2051
- * Create "Render" function by "eslint-react-hooks/rules-of-hooks":
2052
- * React Hooks cannot be called inside a callback.
2053
- * React Hooks must be called in a React function component or a
2054
- * custom React Hook function.
2055
- */
2056
-
2057
- const onChangeField = useCallback(event => {
2058
- input.onChange(event);
2059
- if (onChange) {
2060
- onChange(event.target.checked, input.name);
2061
- }
2062
- }, [onChange, input.onChange]);
2063
- const {
2064
- errorKey,
2065
- errorMessage,
2066
- isErrorState,
2067
- isValidState
2068
- } = useFieldValidationState({
2069
- fieldProps: fieldProps,
2070
- input: input,
2071
- meta: meta
2072
- });
2073
- const updatedInputProps = useValidationAppearanceInputProps({
2074
- inputProps: inputProps,
2075
- validationStateKey: isErrorState ? errorKey : 'success'
2076
- });
2077
- return /*#__PURE__*/React$1.createElement(FieldWrapper, Object.assign({
2078
- className: clsx('form-field_type_switch', 'form__item_type_switch', classNameGroupItem),
2079
- errorKey: errorKey,
2080
- errorMessage: errorMessage,
2081
- isErrorState: isErrorState,
2082
- metaError: meta.error,
2083
- isDisabled: isDisabled,
2084
- fieldClassName: "form-switch",
2085
- inputName: input.name,
2086
- inputValue: input.checked,
2087
- metaActive: meta.active,
2088
- showMessage: showMessage,
2089
- tag: "label",
2090
- isRequired: isRequired,
2091
- isValidState: isValidState
2092
- }, fieldProps), /*#__PURE__*/React$1.createElement(Switch, Object.assign({
2093
- type: "checkbox",
2094
- name: input.name,
2095
- isDisabled: isDisabled,
2096
- autoComplete: "nope",
2097
- checked: input.checked,
2098
- onBlur: input.onBlur,
2099
- onChange: onChangeField,
2100
- onFocus: input.onFocus
2101
- }, updatedInputProps)));
2102
- });
2103
- });
2104
-
2105
- const defaultTextareaProps = {
2106
- appearance: 'sizeM defaultSecondary',
2107
- width: 'fill',
2108
- errorBorderColor: 'errorBorderSecondary',
2109
- requiredBorderColor: 'warningBorderSecondary',
2110
- shape: 'rounded'
2111
- };
2112
-
2113
- const TextareaField = /*#__PURE__*/React$1.memo(function TextareaField(props) {
2114
- const {
2115
- name,
2116
- isDisabled,
2117
- classNameGroupItem,
2118
- fieldProps = {},
2119
- inputProps = {},
2120
- showMessage,
2121
- isRequired
2122
- } = props;
2123
- return /*#__PURE__*/React$1.createElement(Field, {
2124
- name: name
2125
- }, function Render({
2126
- input,
2127
- meta
2128
- }) {
2129
- /** Note:
2130
- * Create "Render" function by "eslint-react-hooks/rules-of-hooks":
2131
- * React Hooks cannot be called inside a callback.
2132
- * React Hooks must be called in a React function component or a
2133
- * custom React Hook function.
2134
- */
2135
-
2136
- const {
2137
- errorKey,
2138
- errorMessage,
2139
- isErrorState,
2140
- isValidState
2141
- } = useFieldValidationState({
2142
- fieldProps: fieldProps,
2143
- input: input,
2144
- meta: meta
2145
- });
2146
- const updatedInputProps = useValidationAppearanceInputProps({
2147
- inputProps: inputProps,
2148
- validationStateKey: isErrorState ? errorKey : 'success'
2149
- });
2150
- return /*#__PURE__*/React$1.createElement(FieldWrapper, Object.assign({
2151
- className: clsx('form-field_type_textarea', 'form__item_type_textarea', classNameGroupItem),
2152
- errorKey: errorKey,
2153
- errorMessage: errorMessage,
2154
- isErrorState: isErrorState,
2155
- metaError: meta.error,
2156
- isDisabled: isDisabled,
2157
- fieldClassName: 'form-textarea',
2158
- inputName: input.name,
2159
- inputValue: input.value,
2160
- metaActive: meta.active,
2161
- showMessage: showMessage,
2162
- isRequired: isRequired,
2163
- isValidState: isValidState
2164
- }, fieldProps), /*#__PURE__*/React$1.createElement(Textarea, Object.assign({
2165
- name: input.name,
2166
- isDisabled: isDisabled,
2167
- autoComplete: "nope",
2168
- value: input.value,
2169
- onBlur: input.onBlur,
2170
- onChange: input.onChange,
2171
- onFocus: input.onFocus
2172
- }, updatedInputProps)));
2173
- });
2174
- });
2175
-
2176
- const MaskedInputField = /*#__PURE__*/React$1.memo(function MaskedInputField(props) {
2177
- const {
2178
- name,
2179
- initialValue,
2180
- isDisabled,
2181
- classNameGroupItem,
2182
- clearIcon,
2183
- clearIconFill,
2184
- clearIconFillHover,
2185
- clearIconShape,
2186
- clearIconSize,
2187
- fieldProps = {},
2188
- inputProps = {},
2189
- optionsMask,
2190
- showMessage,
2191
- unmasked,
2192
- isRequired,
2193
- onClickClearIcon
2194
- } = props;
2195
- return /*#__PURE__*/React$1.createElement(Field, {
2196
- name: name,
2197
- initialValue: initialValue
2198
- }, function Render({
2199
- input,
2200
- meta
2201
- }) {
2202
- /** Note:
2203
- * Create "Render" function by "eslint-react-hooks/rules-of-hooks":
2204
- * React Hooks cannot be called inside a callback.
2205
- * React Hooks must be called in a React function component or a
2206
- * custom React Hook function.
2207
- */
2208
-
2209
- const {
2210
- ref,
2211
- unmaskedValue,
2212
- value,
2213
- setUnmaskedValue
2214
- } = useIMask(optionsMask, {
2215
- onAccept: (newValue, event, element) => {
2216
- if (element) {
2217
- input.onChange(event._unmaskedValue);
2218
- }
2219
- }
2220
- });
2221
- useEffect(() => {
2222
- if (input.value !== unmaskedValue) {
2223
- setUnmaskedValue(input.value.replace(unmasked, ''));
2224
- }
2225
- }, [input.value]);
2226
- const {
2227
- errorKey,
2228
- errorMessage,
2229
- isErrorState,
2230
- isValidState
2231
- } = useFieldValidationState({
2232
- fieldProps: fieldProps,
2233
- input: input,
2234
- meta: meta
2235
- });
2236
- const updatedInputProps = useValidationAppearanceInputProps({
2237
- inputProps: inputProps,
2238
- validationStateKey: isErrorState ? errorKey : 'success'
2239
- });
2240
- return /*#__PURE__*/React$1.createElement(FieldWrapper, Object.assign({
2241
- className: clsx('form-field_type_maskedInput', 'form__item_type_maskedInput', classNameGroupItem),
2242
- errorKey: errorKey,
2243
- errorMessage: errorMessage,
2244
- isErrorState: isErrorState,
2245
- metaError: meta.error,
2246
- isDisabled: isDisabled,
2247
- fieldClassName: 'form-maskedInput',
2248
- inputName: input.name,
2249
- inputValue: input.value,
2250
- metaActive: meta.active,
2251
- showMessage: showMessage,
2252
- isRequired: isRequired,
2253
- isValidState: isValidState
2254
- }, fieldProps), /*#__PURE__*/React$1.createElement(Input, Object.assign({
2255
- className: clsx(meta.active && 'input_state_focus', meta.error && meta.touched && `input_state_${errorKey}`),
2256
- ref: ref,
2257
- value: value,
2258
- onBlur: input.onBlur,
2259
- onFocus: input.onFocus
2260
- }, updatedInputProps)), clearIcon && /*#__PURE__*/React$1.createElement(Icon, {
2261
- className: "form-field__icon",
2262
- size: clearIconSize,
2263
- iconFill: clearIconFill,
2264
- iconFillHover: clearIconFillHover,
2265
- imageSrc: clearIcon,
2266
- shape: clearIconShape,
2267
- SvgImage: clearIcon,
2268
- onClick: onClickClearIcon
2269
- }));
2270
- });
2271
- });
2272
-
2273
- const defaultChipsProps = {
2274
- appearance: 'surfacePrimary',
2275
- width: 'fill',
2276
- errorBorderColor: 'errorBorderSecondary',
2277
- requiredBorderColor: 'warningBorderSecondary'
2278
- };
2279
-
2280
- function ChipsField(props) {
2281
- const {
2282
- name,
2283
- initialValue,
2284
- isDisabled,
2285
- classNameGroupItem,
2286
- emptyMessage,
2287
- emptyMessageTextColor,
2288
- emptyMessageTextSize,
2289
- fieldProps,
2290
- inputProps,
2291
- options,
2292
- showMessage,
2293
- isRequired,
2294
- onChange
2295
- } = props;
2296
- const {
2297
- change
2298
- } = useForm();
2299
-
2300
- // Callback for value changes
2301
- const onChangeSomeInput = useCallback((inputValue, newOptionValue) => {
2302
- const updatedValues = inputValue.includes(newOptionValue) ? inputValue.filter(selectedValue => selectedValue !== newOptionValue) : [...inputValue, newOptionValue];
2303
- change(name, updatedValues);
2304
- onChange && onChange(updatedValues);
2305
- }, [change, name, onChange]);
2306
- useEffect(() => {
2307
- initialValue && change(name, initialValue);
2308
- // update the form value only when the initialValue changes, so use disable eslint to ignore the warning
2309
- // eslint-disable-next-line react-hooks/exhaustive-deps
2310
- }, [initialValue]);
2311
- return /*#__PURE__*/React$1.createElement(Field, {
2312
- name: name,
2313
- initialValue: initialValue
2314
- }, function Render({
2315
- input,
2316
- meta
2317
- }) {
2318
- const {
2319
- errorKey,
2320
- errorMessage,
2321
- isErrorState,
2322
- isValidState
2323
- } = useFieldValidationState({
2324
- fieldProps: fieldProps,
2325
- input: input,
2326
- meta: meta
2327
- });
2328
- const updatedInputProps = useValidationAppearanceInputProps({
2329
- inputProps: inputProps,
2330
- validationStateKey: isErrorState ? errorKey : 'success'
2331
- });
2332
- const activeOptionsList = useMemo(() => {
2333
- const emptyOptionsList = [{
2334
- label: null,
2335
- value: null
2336
- }];
2337
- if (input?.value) {
2338
- const currentOptions = options.filter(option => input.value?.includes(option.value));
2339
- return currentOptions || emptyOptionsList;
2340
- }
2341
- return emptyOptionsList;
2342
- }, [input.value]);
2343
- return /*#__PURE__*/React$1.createElement(FieldWrapper, Object.assign({
2344
- className: clsx('form-field_type_chips', 'form__item_type_chips', classNameGroupItem),
2345
- errorKey: errorKey,
2346
- errorMessage: errorMessage,
2347
- isErrorState: isErrorState,
2348
- metaError: meta.error,
2349
- isDisabled: isDisabled,
2350
- fieldClassName: "form-chips",
2351
- inputName: input.name,
2352
- inputValue: input.value,
2353
- metaActive: meta.active,
2354
- showMessage: showMessage,
2355
- isRequired: isRequired,
2356
- isValidState: isValidState
2357
- }, fieldProps), options.length ? options.map(option => /*#__PURE__*/React$1.createElement(Chips, Object.assign({
2358
- className: clsx(meta.active && 'form-chips_state_focus', meta.error && meta.touched && `form-chips_state_${errorKey}`),
2359
- key: option.value,
2360
- label: option.label,
2361
- isDisabled: option.isDisabled,
2362
- value: option.value,
2363
- isActive: activeOptionsList.some(activeOption => activeOption.value === option.value),
2364
- onClick: () => onChangeSomeInput(input.value, option.value)
2365
- }, updatedInputProps))) : /*#__PURE__*/React$1.createElement(Text, {
2366
- size: emptyMessageTextSize,
2367
- textColor: emptyMessageTextColor
2368
- }, emptyMessage));
2369
- });
2370
- }
2371
-
2372
- const formTypes = {
2373
- code: 'code',
2374
- text: 'text',
2375
- textarea: 'textarea',
2376
- custom: 'custom',
2377
- checkbox: 'checkbox',
2378
- chips: 'chips',
2379
- choice: 'choice',
2380
- datePicker: 'datePicker',
2381
- dateRangePicker: 'dateRangePicker',
2382
- fileInput: 'fileInput',
2383
- group: 'group',
2384
- maskedInput: 'maskedInput',
2385
- radioGroup: 'radioGroup',
2386
- segmented: 'segmented',
2387
- select: 'select',
2388
- switch: 'switch'
2389
- };
2390
- function generateField(field, config, props) {
2391
- switch (field.type) {
2392
- case formTypes.checkbox:
2393
- {
2394
- return /*#__PURE__*/React$1.createElement(CheckboxField, Object.assign({
2395
- key: config.key
2396
- }, field, props));
2397
- }
2398
- case formTypes.choice:
2399
- {
2400
- return /*#__PURE__*/React$1.createElement(ChoiceField, Object.assign({
2401
- key: config.key
2402
- }, field, props));
2403
- }
2404
- case formTypes.chips:
2405
- {
2406
- return /*#__PURE__*/React$1.createElement(ChipsField, Object.assign({
2407
- key: config.key
2408
- }, field, props));
2409
- }
2410
- case formTypes.code:
2411
- {
2412
- return /*#__PURE__*/React$1.createElement(CodeField, Object.assign({
2413
- key: config.key
2414
- }, field, props));
2415
- }
2416
- case formTypes.switch:
2417
- {
2418
- return /*#__PURE__*/React$1.createElement(SwitchField, Object.assign({
2419
- key: config.key
2420
- }, field, props));
2421
- }
2422
- case formTypes.segmented:
2423
- {
2424
- return /*#__PURE__*/React$1.createElement(SegmentedField, Object.assign({
2425
- key: config.key
2426
- }, field, props));
2427
- }
2428
- case formTypes.datePicker:
2429
- {
2430
- return /*#__PURE__*/React$1.createElement(DatePickerField, Object.assign({
2431
- key: config.key
2432
- }, field, props));
2433
- }
2434
- case formTypes.fileInput:
2435
- {
2436
- return /*#__PURE__*/React$1.createElement(FileInput, Object.assign({
2437
- key: config.key
2438
- }, field, props));
2439
- }
2440
- case formTypes.radioGroup:
2441
- {
2442
- return /*#__PURE__*/React$1.createElement(RadioGroup, Object.assign({
2443
- key: config.key
2444
- }, field, props));
2445
- }
2446
- case formTypes.select:
2447
- {
2448
- return /*#__PURE__*/React$1.createElement(SelectField, Object.assign({
2449
- key: config.key
2450
- }, field, props));
2451
- }
2452
- case formTypes.text:
2453
- {
2454
- return /*#__PURE__*/React$1.createElement(InputField, Object.assign({
2455
- key: config.key
2456
- }, field, props));
2457
- }
2458
- case formTypes.textarea:
2459
- {
2460
- return /*#__PURE__*/React$1.createElement(TextareaField, Object.assign({
2461
- key: config.key
2462
- }, field, props));
2463
- }
2464
- case formTypes.maskedInput:
2465
- {
2466
- return /*#__PURE__*/React$1.createElement(MaskedInputField, Object.assign({
2467
- key: config.key
2468
- }, field, props));
2469
- }
2470
- case formTypes.custom:
2471
- {
2472
- return /*#__PURE__*/React$1.createElement(CustomField, Object.assign({
2473
- key: config.key
2474
- }, field, props));
2475
- }
2476
- case formTypes.group:
2477
- {
2478
- return /*#__PURE__*/React$1.createElement(Group, Object.assign({
2479
- key: config.key
2480
- }, field, props), Object.entries(field.group).map(([key, value]) => {
2481
- const groupProps = {
2482
- ...value,
2483
- classNameGroupItem: value.classNameGroupItem || 'form__group-item',
2484
- showMessage: field.showMessage
2485
- };
2486
- return generateField(groupProps, {
2487
- key: key + '_form_group'
2488
- }, props);
2489
- }));
2490
- }
2491
- }
2492
- }
2493
-
2494
- const focusOnError = (formElementsList, errors) => {
2495
- const selectsIds = Object.keys(errors).map(fieldName => {
2496
- if (fieldName === FORM_ERROR) {
2497
- // TODO: get from somewhere
2498
- return 'notification__item_status_error';
2499
- }
2500
- return `react-select-id_${fieldName}-input`;
2501
- });
2502
- const errorFieldElement = formElementsList.find(element => {
2503
- if (element.name) {
2504
- return getIn(errors, element.name);
2505
- } else {
2506
- return selectsIds.includes(element.id);
2507
- }
2508
- });
2509
- const errorsList = Object.keys(errors);
2510
- if (!errorFieldElement && errorsList.length) {
2511
- let errorElement;
2512
- try {
2513
- const fieldName = errorsList[0];
2514
- if (fieldName === FORM_ERROR) {
2515
- errorElement = document.querySelector('notification__item_status_error');
2516
- } else {
2517
- errorElement = document.querySelector(`#${fieldName}-error`);
2518
- if (!errorElement) {
2519
- errorElement = document.querySelector(`#id_${fieldName}`);
2520
- }
2521
- }
2522
- } catch (err) {
2523
- console.warn(err);
2524
- }
2525
- if (errorElement) {
2526
- errorElement.scrollIntoView({
2527
- block: 'center'
2528
- }); // , behavior: 'smooth'
2529
- }
2530
- }
2531
-
2532
- // The field is covered by the header because header is "sticky",
2533
- // that's we scroll manually so that the field falls into the center of the visible area
2534
- if (errorFieldElement) {
2535
- errorFieldElement.scrollIntoView({
2536
- block: 'center'
2537
- });
2538
- }
2539
- return null;
2540
- };
2541
- const focusOnErrorDecorator = createDecorator(null, focusOnError);
2542
- const setErrorsMutator = (args, state) => {
2543
- const [fieldName, data] = args;
2544
- const submitError = data.submitError;
2545
- const fieldError = data.error;
2546
- if (fieldName === 'non_field_errors') {
2547
- // state.formState.invalid = true
2548
- // state.formState.valid = false
2549
- state.formState.error = fieldError;
2550
- state.formState.submitError = submitError;
2551
- } else if (fieldName in state.fields) {
2552
- if (fieldError) {
2553
- const errorsState = Object.assign({}, state.formState.errors, {
2554
- [fieldName]: fieldError
2555
- });
2556
- state.fields[fieldName].touched = true;
2557
- state.fields[fieldName].error = fieldError;
2558
- state.formState.errors = errorsState;
2559
- }
2560
- if (submitError) {
2561
- const submitErrorsState = Object.assign({}, state.formState.submitErrors, {
2562
- [fieldName]: submitError
2563
- });
2564
-
2565
- // state.fields[fieldName].submitFailed = true
2566
- // state.fields[fieldName].submitSucceeded = false
2567
- state.fields[fieldName].submitError = submitError;
2568
- state.formState.submitErrors = submitErrorsState;
2569
- state.formState.submitFailed = true;
2570
- state.formState.submitSucceeded = false;
2571
- state.formState.lastSubmittedValues = state.formState.values;
2572
- }
2573
- }
2574
- };
2575
- const sendFormDataToServer = async (url, data) => {
2576
- try {
2577
- const response = await axios({
2578
- url: url,
2579
- method: 'POST',
2580
- data: data
2581
- });
2582
- return {
2583
- success: true,
2584
- response
2585
- };
2586
- } catch (error) {
2587
- const formErrors = {};
2588
- if (typeof error.response?.data === 'string') {
2589
- formErrors[FORM_ERROR] = 'Something went wrong';
2590
- }
2591
- if (typeof error.response?.data === 'object') {
2592
- Object.entries(error.response.data).forEach(([fieldName, errorsList]) => {
2593
- formErrors[fieldName] = errorsList[0];
2594
- });
2595
- }
2596
- return {
2597
- success: false,
2598
- formErrors,
2599
- error
2600
- };
2601
- }
2602
- };
2603
-
2604
- const FinalForm = /*#__PURE__*/React$1.forwardRef(function FinalForm(props, ref) {
2605
- const {
2606
- className,
2607
- type,
2608
- initialValues,
2609
- initialValuesEqual,
2610
- config,
2611
- title,
2612
- titleFill,
2613
- titleTextColor,
2614
- titleTextSize,
2615
- titleTextWeight,
2616
- desc,
2617
- descSize,
2618
- descTextColor,
2619
- descTextWeight,
2620
- primaryButton,
2621
- primaryButtonFill,
2622
- primaryButtonFillHover,
2623
- primaryButtonLabel,
2624
- primaryButtonLabelSize,
2625
- primaryButtonLabelTextColor,
2626
- primaryButtonLabelTextWeight,
2627
- primaryButtonSize,
2628
- secondaryButton,
2629
- secondaryButtonFill,
2630
- secondaryButtonFillHover,
2631
- secondaryButtonLabel,
2632
- secondaryButtonLabelSize,
2633
- secondaryButtonLabelTextColor,
2634
- secondaryButtonLabelTextWeight,
2635
- secondaryButtonSize,
2636
- tertiaryButton,
2637
- tertiaryButtonFill,
2638
- tertiaryButtonFillHover,
2639
- tertiaryButtonLabel,
2640
- tertiaryButtonLabelSize,
2641
- tertiaryButtonLabelTextColor,
2642
- tertiaryButtonLabelTextWeight,
2643
- tertiaryButtonSize,
2644
- dataTestIdPrimaryButton,
2645
- dataTourPrimaryButton,
2646
- dataTestIdSecondaryButton,
2647
- dataTourSecondaryButton,
2648
- onClickSecondaryButton,
2649
- dataTestIdTertiaryButton,
2650
- dataTourTertiaryButton,
2651
- onClickTertiaryButton,
2652
- additionalProps = {},
2653
- buttonDirection = 'vertical',
2654
- buttonFill,
2655
- buttonGap,
2656
- buttonJustifyContent,
2657
- buttonPadding,
2658
- buttonPosition,
2659
- dataTestId,
2660
- dataTestIdButtons,
2661
- dataTour,
2662
- dataTourButtons,
2663
- disableFieldsAutoComplete = false,
2664
- fieldsGap,
2665
- formName,
2666
- groupGap,
2667
- language,
2668
- loader,
2669
- loaderFill,
2670
- loaderItemFill,
2671
- loaderShape,
2672
- loaderSize,
2673
- loaderText,
2674
- loaderType,
2675
- mutators,
2676
- notificationCloseButton,
2677
- notificationType,
2678
- renderFieldsWrapper = wrapperChildren => wrapperChildren,
2679
- validationSchema,
2680
- before,
2681
- after,
2682
- isLoading,
2683
- onChangeFormValues,
2684
- onSubmit
2685
- } = props;
2686
- const validate = useYupValidationSchema(validationSchema, language);
2687
- const onRefFormInstance = useCallback(formInstance => {
2688
- if (ref) {
2689
- ref.current = formInstance;
2690
- }
2691
- }, [ref]);
2692
- const propsGenerator = useDevicePropsGenerator(props);
2693
- const {
2694
- directionClass,
2695
- fillClass,
2696
- elevationClass,
2697
- shapeClass
2698
- } = propsGenerator;
2699
- const {
2700
- styles: formStyles,
2701
- wrapper: wrapperStyles
2702
- } = useStyles(props);
2703
- return /*#__PURE__*/React$1.createElement(Form, {
2704
- initialValues: initialValues,
2705
- initialValuesEqual: initialValuesEqual,
2706
- render: ({
2707
- submitError,
2708
- form,
2709
- handleSubmit,
2710
- modifiedSinceLastSubmit
2711
- }) => {
2712
- return /*#__PURE__*/React$1.createElement("form", {
2713
- className: clsx(className, 'form', type && `form_type_${type}`, buttonPosition && `form_button-position_${buttonPosition}`, directionClass, fillClass, shapeClass, elevationClass),
2714
- name: formName,
2715
- autoCapitalize: disableFieldsAutoComplete ? 'off' : undefined,
2716
- autoComplete: disableFieldsAutoComplete ? 'off' : undefined,
2717
- autoCorrect: disableFieldsAutoComplete ? 'off' : undefined,
2718
- "data-test-id": dataTestId,
2719
- "data-tour": dataTour
2720
- // We no need set reference to html element, we need reference to "final-form" instance
2721
- ,
2722
- ref: () => onRefFormInstance(form),
2723
- spellCheck: disableFieldsAutoComplete ? 'false' : undefined,
2724
- style: formStyles,
2725
- onSubmit: handleSubmit
2726
- }, before, title && /*#__PURE__*/React$1.createElement(Title, {
2727
- className: "form__title",
2728
- size: titleTextSize,
2729
- fill: titleFill,
2730
- textColor: titleTextColor,
2731
- textWeight: titleTextWeight
2732
- }, title), desc && /*#__PURE__*/React$1.createElement(Text, {
2733
- className: "form__desc",
2734
- size: descSize,
2735
- textColor: descTextColor,
2736
- textWeight: descTextWeight
2737
- }, desc), submitError && !modifiedSinceLastSubmit && /*#__PURE__*/React$1.createElement("div", {
2738
- className: clsx('notification', 'form-notification', notificationType ? `form-notification_type_${notificationType}` : 'form-notification_type_global')
2739
- }, /*#__PURE__*/React$1.createElement(NotificationItem, {
2740
- className: "form-notification__item",
2741
- title: form.getState().submitError,
2742
- titleTextSize: "h6",
2743
- status: "error",
2744
- closeButton: notificationCloseButton,
2745
- set: "form"
2746
- })), onChangeFormValues && /*#__PURE__*/React$1.createElement(FormSpy, {
2747
- subscription: {
2748
- values: true
2749
- },
2750
- onChange: onChangeFormValues
2751
- }), Boolean(Object.keys(config).length) && /*#__PURE__*/React$1.createElement(React$1.Fragment, null, renderFieldsWrapper(/*#__PURE__*/React$1.createElement(Group$1, {
2752
- className: "form__wrapper",
2753
- direction: "vertical",
2754
- gap: fieldsGap || groupGap,
2755
- style: wrapperStyles
2756
- }, Object.keys(config).map(key => generateField(config[key], {
2757
- key
2758
- }, additionalProps[config[key].name])), isLoading && (loader || /*#__PURE__*/React$1.createElement(Loader, {
2759
- className: "form__loader",
2760
- type: loaderType,
2761
- size: loaderSize,
2762
- fill: loaderFill,
2763
- text: loaderText,
2764
- itemFill: loaderItemFill,
2765
- shape: loaderShape
2766
- }))))), (primaryButtonLabel || primaryButton || secondaryButtonLabel || secondaryButton || tertiaryButton || tertiaryButtonLabel) && /*#__PURE__*/React$1.createElement(Group$1, {
2767
- className: "form__button",
2768
- direction: buttonDirection,
2769
- justifyContent: buttonJustifyContent,
2770
- fill: buttonFill,
2771
- padding: buttonPadding,
2772
- gap: buttonGap,
2773
- dataTestId: dataTestIdButtons,
2774
- dataTour: dataTourButtons
2775
- }, primaryButtonLabel ? /*#__PURE__*/React$1.createElement(Button, {
2776
- className: "form__button-item",
2777
- width: "fill",
2778
- size: primaryButtonSize,
2779
- fill: primaryButtonFill,
2780
- fillHover: primaryButtonFillHover,
2781
- label: primaryButtonLabel,
2782
- labelTextColor: primaryButtonLabelTextColor,
2783
- labelTextSize: primaryButtonLabelSize,
2784
- labelTextWeight: primaryButtonLabelTextWeight,
2785
- dataTestId: dataTestIdPrimaryButton,
2786
- dataTour: dataTourPrimaryButton
2787
- }) : primaryButton, secondaryButtonLabel ? /*#__PURE__*/React$1.createElement(Button, {
2788
- className: "form__button-item",
2789
- width: "fill",
2790
- size: secondaryButtonSize,
2791
- fill: secondaryButtonFill,
2792
- fillHover: secondaryButtonFillHover,
2793
- label: secondaryButtonLabel,
2794
- labelTextColor: secondaryButtonLabelTextColor,
2795
- labelTextSize: secondaryButtonLabelSize,
2796
- labelTextWeight: secondaryButtonLabelTextWeight,
2797
- dataTestId: dataTestIdSecondaryButton,
2798
- dataTour: dataTourSecondaryButton,
2799
- onClick: onClickSecondaryButton
2800
- }) : secondaryButton, tertiaryButtonLabel ? /*#__PURE__*/React$1.createElement(Button, {
2801
- className: "form__button-item",
2802
- width: "fill",
2803
- size: tertiaryButtonSize,
2804
- fill: tertiaryButtonFill,
2805
- fillHover: tertiaryButtonFillHover,
2806
- label: tertiaryButtonLabel,
2807
- labelTextColor: tertiaryButtonLabelTextColor,
2808
- labelTextSize: tertiaryButtonLabelSize,
2809
- labelTextWeight: tertiaryButtonLabelTextWeight,
2810
- dataTestId: dataTestIdTertiaryButton,
2811
- dataTour: dataTourTertiaryButton,
2812
- onClick: onClickTertiaryButton
2813
- }) : tertiaryButton), after);
2814
- },
2815
- decorators: [focusOnErrorDecorator],
2816
- mutators: mutators,
2817
- subscription: {
2818
- submitError: true,
2819
- modifiedSinceLastSubmit: true,
2820
- pristine: true,
2821
- submitting: true
2822
- },
2823
- validate: validate,
2824
- onSubmit: onSubmit
2825
- });
2826
- });
2827
- FinalForm.defaultProps = {
2828
- direction: 'vertical'
2829
- };
2830
-
2831
- const DEFAULT_MESSAGES_FIELDS = {
2832
- /*
2833
- !!! it also works without props simply based on the class and key as before `input_state_${meta.error.key}`
2834
- the KEY is needed for example for border color
2835
- the name of the key is anything you want, the main thing is that there is a props with the same KEY and color in FieldProps
2836
- ...example
2837
- required - KEY for yellow color
2838
- error - KEY for red color
2839
- custom or blue - KEY blue or other color
2840
- requiredBorderColor: 'warningBorderPrimary',
2841
- errorBorderColor: 'errorBorderPrimary',
2842
- customBorderColor: 'customBorderPrimary',
2843
- blueBorderColor: 'blueBorderPrimary',
2844
- const defaultFieldProps = {
2845
- messageTextSize: 's',
2846
- messageTextColor: 'surfaceTextSecondary',
2847
- requiredMessageTextSize: 's',
2848
- requiredMessageTextColor: 'warningTextPrimary',
2849
- errorMessageTextSize: 's',
2850
- errorMessageTextColor: 'errorTextPrimary',
2851
- }
2852
- // INPUT
2853
- const defaultInputProps = {
2854
- ... other
2855
- stateBorderColor: 'surfaceBorderTertiary',
2856
- requiredStateBorderColor: 'warningBorderPrimary',
2857
- errorStateBorderColor: 'errorBorderPrimary',
2858
- }
2859
- // RADIO
2860
- const defaultRadioProps = {
2861
- ... other
2862
- stateBorderColor: 'surfaceBorderTertiary',
2863
- requiredStateBorderColor: 'warningBorderPrimary',
2864
- errorStateBorderColor: 'errorBorderPrimary',
2865
- }
2866
- // SELECT
2867
- const defaultSelectProps = {
2868
- ... other
2869
- borderColor: 'surfaceBorderTertiary',
2870
- requiredBorderColor: 'warningBorderPrimary',
2871
- errorBorderColor: 'errorBorderPrimary',
2872
- inputBorderColor: 'surfaceBorderTertiary',
2873
- requiredInputBorderColor: 'warningBorderPrimary',
2874
- errorInputBorderColor: 'errorBorderPrimary',
2875
- }
2876
- ... etc
2877
- */
2878
-
2879
- // DEFAULT
2880
- // required - KEY for yellow color
2881
- // error - KEY for red color
2882
-
2883
- // key: 'required'
2884
- required: {
2885
- key: 'required',
2886
- message: 'Обязательное поле'
2887
- },
2888
- phone_required: {
2889
- key: 'required',
2890
- message: 'Укажите номер телефона'
2891
- },
2892
- email_required: {
2893
- key: 'required',
2894
- message: 'Укажите адрес электронной почты'
2895
- },
2896
- password_required: {
2897
- key: 'required',
2898
- message: 'Введите пароль'
2899
- },
2900
- phone_or_email_required: {
2901
- key: 'required',
2902
- message: 'Введите телефон или адрес эл. почты'
2903
- },
2904
- // key: 'error'
2905
- matches: {
2906
- key: 'error',
2907
- message: 'Допускается ввод только цифр от 0 до 9'
2908
- },
2909
- min: {
2910
- key: 'error',
2911
- message: ({
2912
- min
2913
- }) => `Значение должно быть не менее ${min} символов`
2914
- },
2915
- max: {
2916
- key: 'error',
2917
- message: ({
2918
- max
2919
- }) => `Значение должно быть не менее ${max} символов`
2920
- },
2921
- url: {
2922
- key: 'error',
2923
- message: 'Введите корректный URL-адрес'
2924
- },
2925
- invalid_value: {
2926
- key: 'error',
2927
- message: 'Некорректное значение'
2928
- },
2929
- numeric_value: {
2930
- key: 'error',
2931
- message: 'Только числовое значение'
2932
- },
2933
- phone_error: {
2934
- key: 'error',
2935
- message: 'Введите корректный номер телефона'
2936
- },
2937
- email_error: {
2938
- key: 'error',
2939
- message: 'Введите корректный адрес электронной почты'
2940
- }
2941
- };
2942
-
2943
- const parseNumericField = value => {
2944
- const numberValue = value.slice(0, 10).replace(/,/g, '.').replace(/[^\d.]/g, '');
2945
- const parsedValue = parseFloat(numberValue);
2946
- if (parsedValue || parsedValue === 0) {
2947
- if (numberValue.endsWith('.')) {
2948
- if ((numberValue.match(/\./g) || []).length > 1) {
2949
- return numberValue.slice(0, -1);
2950
- }
2951
- return numberValue;
2952
- }
2953
- return numberValue;
2954
- }
2955
- return '';
2956
- };
2957
-
2958
- // const getErrorsForFinalForm = (errorData) => {
2959
- // /*
2960
- // * errorData - its an "axios" error
2961
- // */
2962
-
2963
- // const formErrors = {}
2964
-
2965
- // const responseErrorMessage = errorData.toJSON ? errorData.toJSON().message : errorData.message
2966
-
2967
- // // const status = (errorData.response && errorData.response.status) || null
2968
- // const problemError = getProblemFromError(errorData)
2969
- // // const problemStatus = getProblemFromStatus(status)
2970
-
2971
- // if (problemError === NETWORK_ERROR || problemError === CONNECTION_ERROR) {
2972
- // // Say to "react-final-form" that we have general error
2973
- // formErrors[FORM_ERROR] = 'Проблемы с подключением к сервису'
2974
- // } else if (errorData.response?.data) {
2975
- // // Collect errors for some fields, which in the response from server
2976
- // const serverErrors = errorData.response.data
2977
- // if (typeof serverErrors === 'string') {
2978
- // if (errorData.response.status === 500) {
2979
- // formErrors[FORM_ERROR] =
2980
- // 'Во время обработки запроса произошла ошибка, попробуйте повторить запрос'
2981
- // }
2982
- // // formErrors[FORM_ERROR] = responseErrorMessage
2983
- // } else {
2984
- // if (errorData.response.status === 500) {
2985
- // formErrors[FORM_ERROR] =
2986
- // 'Во время обработки запроса произошла ошибка, попробуйте повторить запрос'
2987
- // }
2988
- // for (const key in serverErrors) {
2989
- // // TODO: what is forms has "detail" field? show as form error is well?
2990
- // const errorFieldKey = key === 'non_field_errors' || key === 'detail' ? FORM_ERROR : key
2991
- // // Say to "react-final-form" that we have some fields errors
2992
- // formErrors[errorFieldKey] = castArray(serverErrors[key])[0]
2993
- // }
2994
- // }
2995
- // } else if (typeof errorData === 'object' && Object.keys(errorData).length) {
2996
- // for (const key in errorData) {
2997
- // const errorFieldKey = key === 'non_field_errors' || key === 'detail' ? FORM_ERROR : key
2998
- // // Say to "react-final-form" that we have some fields errors
2999
- // formErrors[errorFieldKey] = castArray(errorData[key])[0]
3000
- // }
3001
- // } else {
3002
- // // Say to "react-final-form" that we have general error
3003
- // formErrors[FORM_ERROR] = responseErrorMessage || 'Произошла ошибка'
3004
- // }
3005
-
3006
- // return formErrors
3007
- // }
3008
-
3009
- const getErrorsForFinalForm = error => {
3010
- /*
3011
- * error - its an "axios" error in many cases
3012
- */
3013
-
3014
- const formErrors = {};
3015
- const serverErrors = error.response?.data;
3016
- if (serverErrors) {
3017
- // Collect errors for some fields, which in the response from server
3018
- if (typeof serverErrors === 'string') {
3019
- // Server may send an html page as error data
3020
- formErrors[FORM_ERROR] = 'Во время обработки запроса произошла ошибка, попробуйте повторить запрос';
3021
- } else {
3022
- for (const key in serverErrors) {
3023
- // "non_field_errors" and "detail" is special keys in django to mark errors not for fields
3024
- const errorFieldKey = key === 'non_field_errors' || key === 'detail' ? FORM_ERROR : key;
3025
-
3026
- // Say to "react-final-form" that we have some fields errors
3027
- formErrors[errorFieldKey] = castArray(serverErrors[key])[0];
3028
- }
3029
- }
3030
- } else {
3031
- // const responseErrorMessage = error.toJSON
3032
- // ? error.toJSON().message
3033
- // : error.message
3034
-
3035
- // Say to "react-final-form" that we have general error
3036
- formErrors[FORM_ERROR] = error.message || 'Произошла ошибка';
3037
- }
3038
- return formErrors;
3039
- };
3040
-
3041
- export { CheckboxField, ChipsField, ChoiceField, CodeField, CustomField, DEFAULT_MESSAGES_FIELDS, DatePickerField, FieldWrapper, FieldWrapperBase, FileInput, FinalForm, Group, InputField, MaskedInputField, RadioGroup, SegmentedField, SelectField, SwitchField, TextareaField, addRequiredFieldsParamToSchema, dateValidation, defaultCheckboxProps, defaultChipsProps, defaultChoiceProps, defaultCodeProps, defaultDatepickerProps, defaultDropzoneProps, defaultFieldProps, defaultInputProps, defaultRadioProps, defaultSegmentedProps, defaultSelectProps, defaultSwitchProps, defaultTextareaProps, emailValidation, focusOnError, focusOnErrorDecorator, formTypes, generateField, getErrorsForFinalForm, parseNumericField, phoneValidation, sendFormDataToServer, setErrorsMutator, useYupValidationSchema };