@ultraviolet/form 2.0.0-next.0 → 2.0.0-next.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,14 +1,12 @@
1
1
  import { Checkbox } from '@ultraviolet/ui';
2
- import { forwardRef } from 'react';
2
+ import { Controller } from 'react-hook-form';
3
3
  import { jsx } from '@emotion/react/jsx-runtime';
4
4
  import { useErrors } from '../../providers/ErrorContext/index.js';
5
- import { useFormField } from '../../hooks/useFormField.js';
6
5
 
7
- const CheckboxField = /*#__PURE__*/forwardRef((_ref, ref) => {
6
+ const CheckboxField = _ref => {
8
7
  let {
9
- validate,
10
8
  name,
11
- label = '',
9
+ label,
12
10
  size,
13
11
  progress,
14
12
  disabled,
@@ -18,58 +16,58 @@ const CheckboxField = /*#__PURE__*/forwardRef((_ref, ref) => {
18
16
  onChange,
19
17
  onBlur,
20
18
  onFocus,
21
- value,
19
+ rules,
22
20
  helper,
23
21
  tooltip,
24
- 'data-testid': dataTestId
22
+ 'data-testid': dataTestId,
23
+ value
25
24
  } = _ref;
26
25
  const {
27
26
  getError
28
27
  } = useErrors();
29
- const {
30
- input,
31
- meta
32
- } = useFormField(name, {
33
- disabled,
34
- required,
35
- type: 'checkbox',
36
- validate,
37
- value
38
- });
39
- const error = getError({
40
- label,
41
- meta: meta,
42
- name,
43
- value: input.value ?? input.checked
44
- });
45
- return jsx(Checkbox, {
46
- name: input.name,
47
- onChange: event => {
48
- input.onChange(event);
49
- onChange?.(event);
50
- },
51
- onBlur: event => {
52
- input.onBlur(event);
53
- onBlur?.(event);
54
- },
55
- onFocus: event => {
56
- input.onFocus(event);
57
- onFocus?.(event);
58
- },
59
- size: size,
60
- progress: progress,
28
+ return jsx(Controller, {
29
+ name: name,
61
30
  disabled: disabled,
62
- checked: input.checked,
63
- error: error,
64
- helper: helper,
65
- ref: ref,
66
- className: className,
67
- value: input.value,
68
- required: required,
69
- "data-testid": dataTestId,
70
- tooltip: tooltip,
71
- children: children
31
+ rules: {
32
+ required,
33
+ ...rules
34
+ },
35
+ render: _ref2 => {
36
+ let {
37
+ field,
38
+ fieldState: {
39
+ error
40
+ }
41
+ } = _ref2;
42
+ return jsx(Checkbox, {
43
+ name: field.name,
44
+ onChange: event => {
45
+ field.onChange(value ? [...(field.value ?? []), value] : event.target.checked);
46
+ onChange?.(event);
47
+ },
48
+ onBlur: event => {
49
+ field.onBlur();
50
+ onBlur?.(event);
51
+ },
52
+ onFocus: onFocus,
53
+ size: size,
54
+ progress: progress,
55
+ disabled: field.disabled,
56
+ checked: Array.isArray(field.value) ? field.value.includes(value) : !!field.value,
57
+ error: getError({
58
+ label: label ?? ''
59
+ }, error),
60
+ ref: field.ref,
61
+ className: className,
62
+ required: required,
63
+ "data-testid": dataTestId,
64
+ helper: helper,
65
+ tooltip: tooltip,
66
+ value: value,
67
+ children: children
68
+ });
69
+ }
72
70
  });
73
- });
71
+ };
74
72
 
75
73
  export { CheckboxField };
@@ -1,17 +1,17 @@
1
1
  import { CheckboxGroup } from '@ultraviolet/ui';
2
- import { useFieldArray } from 'react-final-form-arrays';
2
+ import { Controller } from 'react-hook-form';
3
3
  import { jsx } from '@emotion/react/jsx-runtime';
4
4
  import { useErrors } from '../../providers/ErrorContext/index.js';
5
5
 
6
6
  const CheckboxGroupField = _ref => {
7
7
  let {
8
8
  legend,
9
- value,
10
9
  className,
11
10
  helper,
12
11
  direction,
13
12
  children,
14
13
  onChange,
14
+ label = '',
15
15
  error: customError,
16
16
  name,
17
17
  required = false
@@ -19,38 +19,38 @@ const CheckboxGroupField = _ref => {
19
19
  const {
20
20
  getError
21
21
  } = useErrors();
22
- const {
23
- fields,
24
- meta
25
- } = useFieldArray(name, {
26
- type: 'checkbox',
27
- value,
28
- validate: localValue => required && localValue?.length === 0 ? 'Required' : undefined
29
- });
30
- const error = getError({
31
- label: legend,
32
- meta,
33
- value: fields.value,
34
- name
35
- });
36
- return jsx(CheckboxGroup, {
37
- legend: legend,
38
- name: fields.name,
39
- value: fields.value,
40
- onChange: event => {
41
- if (fields.value?.includes(event.currentTarget.value)) {
42
- fields.remove(fields.value.indexOf(event.currentTarget?.value));
43
- } else {
44
- fields.push(event.currentTarget.value);
45
- }
46
- onChange?.(event);
47
- },
48
- error: error ?? customError,
49
- className: className,
50
- direction: direction,
51
- helper: helper,
52
- required: required,
53
- children: children
22
+ return jsx(Controller, {
23
+ name: name,
24
+ render: _ref2 => {
25
+ let {
26
+ field,
27
+ fieldState: {
28
+ error
29
+ }
30
+ } = _ref2;
31
+ return jsx(CheckboxGroup, {
32
+ legend: legend,
33
+ name: name,
34
+ value: field.value,
35
+ onChange: event => {
36
+ const fieldValue = field.value;
37
+ if (fieldValue?.includes(event.currentTarget.value)) {
38
+ field.onChange(fieldValue?.filter(currentValue => currentValue !== event.currentTarget.value));
39
+ } else {
40
+ field.onChange([...field.value, event.currentTarget.value]);
41
+ }
42
+ onChange?.(event);
43
+ },
44
+ error: getError({
45
+ label
46
+ }, error) ?? customError,
47
+ className: className,
48
+ direction: direction,
49
+ helper: helper,
50
+ required: required,
51
+ children: children
52
+ });
53
+ }
54
54
  });
55
55
  };
56
56
  CheckboxGroupField.Checkbox = CheckboxGroup.Checkbox;
@@ -1,7 +1,9 @@
1
1
  import { DateInput } from '@ultraviolet/ui';
2
+ import { Controller } from 'react-hook-form';
3
+ import { maxDateValidator } from '../../validators/maxDate.js';
4
+ import { minDateValidator } from '../../validators/minDate.js';
2
5
  import { jsx } from '@emotion/react/jsx-runtime';
3
6
  import { useErrors } from '../../providers/ErrorContext/index.js';
4
- import { useFormField } from '../../hooks/useFormField.js';
5
7
 
6
8
  const parseDate = value => typeof value === 'string' ? new Date(value) : value;
7
9
  const isEmpty = value => typeof value === 'string' ? value === '' : value === undefined;
@@ -10,18 +12,15 @@ const DateField = _ref => {
10
12
  required,
11
13
  name,
12
14
  label = '',
13
- validate,
14
15
  format,
15
16
  locale,
16
17
  maxDate,
17
18
  minDate,
18
- initialValue,
19
19
  disabled,
20
- value: inputVal,
21
20
  onChange,
22
21
  onBlur,
23
22
  onFocus,
24
- formatOnBlur,
23
+ rules,
25
24
  autoFocus = false,
26
25
  excludeDates,
27
26
  selectsRange,
@@ -30,67 +29,64 @@ const DateField = _ref => {
30
29
  const {
31
30
  getError
32
31
  } = useErrors();
33
- const {
34
- input,
35
- meta
36
- } = useFormField(name, {
37
- disabled,
38
- formatOnBlur,
39
- initialValue,
40
- maxDate,
41
- minDate,
42
- required,
43
- validate,
44
- value: inputVal
45
- });
46
- const error = getError({
47
- label,
48
- maxDate,
49
- meta: meta,
50
- minDate,
51
- name,
52
- value: input.value
53
- });
54
- return jsx(DateInput, {
55
- label: label,
56
- format: format || (value => value ? parseDate(value).toLocaleDateString() : ''),
57
- locale: locale,
58
- required: required,
59
- value: input.value,
60
- onChange: val => {
61
- if (val && val instanceof Date) {
62
- onChange?.(val);
63
- const newDate = parseDate(val);
64
- if (isEmpty(input.value)) {
65
- input.onChange(newDate);
66
- return;
67
- }
68
- const currentDate = parseDate(input.value);
69
- newDate.setHours(currentDate.getHours(), currentDate.getMinutes());
70
- input.onChange(newDate);
71
- } else if (Array.isArray(val)) {
72
- input.onChange(val);
73
- }
32
+ return jsx(Controller, {
33
+ name: name,
34
+ rules: {
35
+ ...rules,
36
+ validate: {
37
+ ...rules?.validate,
38
+ minDate: minDateValidator(minDate),
39
+ maxDate: maxDateValidator(maxDate)
40
+ },
41
+ required
74
42
  },
75
- onBlur: e => {
76
- input.onBlur(e);
77
- onBlur?.(e);
78
- },
79
- onFocus: e => {
80
- input.onFocus(e);
81
- onFocus?.(e);
82
- },
83
- maxDate: maxDate,
84
- minDate: minDate,
85
- startDate: selectsRange ? input.value[0] : undefined,
86
- endDate: selectsRange ? input.value[1] : undefined,
87
- error: error,
88
- disabled: disabled,
89
- autoFocus: autoFocus,
90
- name: input.name,
91
- "data-testid": dataTestId,
92
- excludeDates: excludeDates,
93
- selectsRange: selectsRange
43
+ render: _ref2 => {
44
+ let {
45
+ field,
46
+ fieldState: {
47
+ error
48
+ }
49
+ } = _ref2;
50
+ return jsx(DateInput, {
51
+ name: field.name,
52
+ label: label,
53
+ value: field.value,
54
+ format: format || (value => value ? parseDate(value).toLocaleDateString() : ''),
55
+ locale: locale,
56
+ required: required,
57
+ onChange: val => {
58
+ if (val && val instanceof Date) {
59
+ onChange?.(val);
60
+ const newDate = parseDate(val);
61
+ if (isEmpty(field.value)) {
62
+ field.onChange(newDate);
63
+ return;
64
+ }
65
+ const currentDate = parseDate(field.value);
66
+ newDate.setHours(currentDate.getHours(), currentDate.getMinutes());
67
+ field.onChange(newDate);
68
+ }
69
+ },
70
+ onBlur: e => {
71
+ field.onBlur();
72
+ onBlur?.(e);
73
+ },
74
+ onFocus: onFocus,
75
+ maxDate: maxDate,
76
+ minDate: minDate,
77
+ error: getError({
78
+ minDate,
79
+ maxDate,
80
+ label
81
+ }, error),
82
+ disabled: disabled,
83
+ autoFocus: autoFocus,
84
+ excludeDates: excludeDates,
85
+ "data-testid": dataTestId,
86
+ startDate: selectsRange ? field.value[0] : undefined,
87
+ endDate: selectsRange ? field.value[1] : undefined
88
+ });
89
+ }
94
90
  });
95
91
  };
96
92
 
@@ -0,0 +1,21 @@
1
+ const defaultErrors = {
2
+ maxDate: () => '',
3
+ maxLength: () => '',
4
+ minLength: () => '',
5
+ max: () => '',
6
+ min: () => '',
7
+ required: () => '',
8
+ pattern: () => '',
9
+ deps: () => '',
10
+ value: () => '',
11
+ onBlur: () => '',
12
+ disabled: () => '',
13
+ onChange: () => '',
14
+ validate: () => '',
15
+ setValueAs: () => '',
16
+ valueAsDate: () => '',
17
+ valueAsNumber: () => '',
18
+ shouldUnregister: () => ''
19
+ };
20
+
21
+ export { defaultErrors };
@@ -1,43 +1,83 @@
1
- import arrayMutators from 'final-form-arrays';
2
- import { Form as Form$1 } from 'react-final-form';
1
+ import React, { useMemo } from 'react';
2
+ import { useForm, FormProvider } from 'react-hook-form';
3
+ import { FORM_ERROR } from '../../constants.js';
4
+ import { defaultErrors } from './defaultErrors.js';
3
5
  import { jsx } from '@emotion/react/jsx-runtime';
4
6
  import { ErrorProvider } from '../../providers/ErrorContext/index.js';
5
7
 
8
+ const FormSubmitContext = /*#__PURE__*/React.createContext({});
6
9
  const Form = _ref => {
7
10
  let {
8
11
  children,
9
- onRawSubmit,
10
- errors,
12
+ methods: methodsProp,
11
13
  initialValues,
12
- validateOnBlur,
13
- validate,
14
- name,
15
- render,
16
- mutators,
17
- keepDirtyOnReinitialize,
18
- className
14
+ errors,
15
+ onRawSubmit,
16
+ name
19
17
  } = _ref;
20
- return jsx(Form$1, {
21
- initialValues: initialValues,
22
- validateOnBlur: validateOnBlur,
23
- validate: validate,
24
- mutators: {
25
- ...arrayMutators,
26
- ...mutators
27
- },
28
- onSubmit: onRawSubmit,
29
- render: render ?? (renderProps => jsx(ErrorProvider, {
30
- errors: errors,
31
- children: jsx("form", {
32
- noValidate: true,
33
- name: name,
34
- onSubmit: renderProps.handleSubmit,
35
- className: className,
36
- children: typeof children === 'function' ? children(renderProps) : children
18
+ const methodsHook = useForm({
19
+ defaultValues: initialValues,
20
+ mode: 'onChange'
21
+ });
22
+ const methods = !methodsProp ? methodsHook : methodsProp;
23
+ const handleSubmit = methods.handleSubmit(async values => {
24
+ const result = await onRawSubmit(values, {
25
+ reset: methods.reset,
26
+ resetFieldState: methods.resetField,
27
+ restart: () => methods.reset(initialValues),
28
+ change: methods.setValue
29
+ });
30
+ if (result === null) {
31
+ methods.setError('root.submit', {
32
+ type: 'custom'
33
+ });
34
+ return;
35
+ }
36
+ if (result && typeof result !== 'boolean' && typeof result !== 'number') {
37
+ methods.setError('root.submit', {
38
+ type: 'custom',
39
+ message: typeof result === 'object' ? result[FORM_ERROR] : result
40
+ });
41
+ }
42
+ });
43
+ const formSubmitContextValue = useMemo(() => ({
44
+ handleSubmit
45
+ }), [handleSubmit]);
46
+ return jsx(FormProvider, {
47
+ ...methods,
48
+ children: jsx(FormSubmitContext.Provider, {
49
+ value: formSubmitContextValue,
50
+ children: jsx(ErrorProvider, {
51
+ errors: {
52
+ ...defaultErrors,
53
+ ...errors
54
+ },
55
+ children: jsx("form", {
56
+ onSubmit: async e => {
57
+ e.preventDefault();
58
+ e.stopPropagation();
59
+ await handleSubmit(e);
60
+ },
61
+ name: name,
62
+ children: typeof children === 'function' ? children({
63
+ values: methods.watch(),
64
+ hasValidationErrors: !methods.formState.isValid,
65
+ errors: methods.formState.errors,
66
+ submitting: methods.formState.isSubmitting,
67
+ pristine: !methods.formState.isDirty,
68
+ handleSubmit,
69
+ submitError: !!methods.formState.errors?.root?.['submit'],
70
+ valid: methods.formState.isValid,
71
+ form: {
72
+ change: methods.setValue,
73
+ reset: methods.reset,
74
+ submit: handleSubmit
75
+ }
76
+ }) : children
77
+ })
37
78
  })
38
- })),
39
- keepDirtyOnReinitialize: keepDirtyOnReinitialize
79
+ })
40
80
  });
41
81
  };
42
82
 
43
- export { Form };
83
+ export { Form, FormSubmitContext };
@@ -0,0 +1,23 @@
1
+ import { useEffect } from 'react';
2
+ import { useForm } from 'react-hook-form';
3
+
4
+ /**
5
+ * @deprecated
6
+ */
7
+ const FormSpy = _ref => {
8
+ let {
9
+ onChange
10
+ } = _ref;
11
+ const {
12
+ watch
13
+ } = useForm();
14
+ useEffect(() => {
15
+ const subscription = watch(values => onChange?.({
16
+ values
17
+ }));
18
+ return () => subscription.unsubscribe();
19
+ }, [watch, onChange]);
20
+ return null;
21
+ };
22
+
23
+ export { FormSpy };
@@ -1,6 +1,7 @@
1
1
  import { NumberInput } from '@ultraviolet/ui';
2
+ import { Controller } from 'react-hook-form';
2
3
  import { jsx } from '@emotion/react/jsx-runtime';
3
- import { useFormField } from '../../hooks/useFormField.js';
4
+ import { useErrors } from '../../providers/ErrorContext/index.js';
4
5
 
5
6
  const NumberInputField = _ref => {
6
7
  let {
@@ -17,48 +18,58 @@ const NumberInputField = _ref => {
17
18
  size,
18
19
  step,
19
20
  text,
20
- validate,
21
- value,
21
+ rules,
22
22
  className,
23
- 'data-testid': dataTestId
23
+ label
24
24
  } = _ref;
25
25
  const {
26
- input
27
- } = useFormField(name, {
28
- disabled,
29
- required,
30
- type: 'number',
31
- validate,
32
- value,
33
- defaultValue: 0,
34
- max: maxValue,
35
- min: minValue
36
- });
37
- return jsx(NumberInput, {
26
+ getError
27
+ } = useErrors();
28
+ return jsx(Controller, {
38
29
  name: name,
39
- disabled: disabled,
40
- onBlur: event => {
41
- input.onBlur(event);
42
- onBlur?.(event);
43
- },
44
- onChange: event => {
45
- input.onChange(event);
46
- onChange?.(event);
47
- },
48
- onFocus: event => {
49
- input.onFocus(event);
50
- onFocus?.(event);
30
+ rules: {
31
+ required,
32
+ max: maxValue,
33
+ min: minValue,
34
+ ...rules
51
35
  },
52
- maxValue: maxValue,
53
- minValue: minValue,
54
- onMinCrossed: onMinCrossed,
55
- onMaxCrossed: onMaxCrossed,
56
- size: size,
57
- step: step,
58
- text: text,
59
- value: input.value,
60
- className: className,
61
- "data-testid": dataTestId
36
+ render: _ref2 => {
37
+ let {
38
+ field,
39
+ fieldState: {
40
+ error
41
+ }
42
+ } = _ref2;
43
+ return jsx(NumberInput, {
44
+ name: field.name,
45
+ value: field.value,
46
+ disabled: disabled,
47
+ onBlur: event => {
48
+ field.onBlur();
49
+ onBlur?.(event);
50
+ },
51
+ onChange: event => {
52
+ // React hook form doesnt allow undefined values after definition https://react-hook-form.com/docs/usecontroller/controller (that make sense)
53
+ field.onChange(event ?? null);
54
+ onChange?.(event);
55
+ },
56
+ onFocus: onFocus,
57
+ maxValue: maxValue,
58
+ minValue: minValue,
59
+ onMinCrossed: onMinCrossed,
60
+ onMaxCrossed: onMaxCrossed,
61
+ size: size,
62
+ step: step,
63
+ text: text,
64
+ className: className,
65
+ label: label,
66
+ error: getError({
67
+ label: label ?? '',
68
+ max: maxValue,
69
+ min: minValue
70
+ }, error)
71
+ });
72
+ }
62
73
  });
63
74
  };
64
75