@ttoss/forms 0.32.2 → 0.32.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -85,11 +85,21 @@ const MyForm = () => {
85
85
 
86
86
  ### Validation Messages
87
87
 
88
- Invalid fields display default error messages like "Field is required". Customize these messages via i18n configuration—see [React-i18n](https://ttoss.dev/docs/modules/packages/react-i18n/) and [i18n-CLI](https://ttoss.dev/docs/modules/packages/i18n-cli/).
88
+ Invalid fields display default error messages like "Field is required". These messages are defined using i18n and can be customized for each locale.
89
89
 
90
- ### Custom Error Messages
90
+ #### Default Yup Messages
91
91
 
92
- Provide custom error messages using i18n patterns:
92
+ The package provides internationalized default messages for common Yup validation errors. These are automatically extracted when you run `pnpm run i18n`:
93
+
94
+ - **Required field**: "Field is required"
95
+ - **Type mismatch**: "Invalid Value for Field of type {type}"
96
+ - **Minimum length**: "Field must be at least {min} characters"
97
+
98
+ To customize these messages for your locale, extract the i18n messages and translate them in your application's i18n files (e.g., `i18n/compiled/pt-BR.json`). See the [i18n-CLI documentation](https://ttoss.dev/docs/modules/packages/i18n-cli/) for more details.
99
+
100
+ #### Custom Schema Messages
101
+
102
+ You can also provide custom error messages directly in your Yup schemas using i18n patterns:
93
103
 
94
104
  ```tsx
95
105
  import { useI18n } from '@ttoss/react-i18n';
@@ -226,6 +236,55 @@ All form field components share common props:
226
236
  - `warning`: Warning message displayed below the field
227
237
  - `sx`: Theme-UI styling object
228
238
 
239
+ ### Disabling Form Fields
240
+
241
+ You can disable form fields in two ways:
242
+
243
+ **1. Disable the entire form:**
244
+
245
+ Set `disabled: true` in `useForm` to disable all fields at once:
246
+
247
+ ```tsx
248
+ const formMethods = useForm({
249
+ disabled: true, // Disables all fields
250
+ });
251
+ ```
252
+
253
+ This is particularly useful for preventing user interaction during asynchronous operations:
254
+
255
+ ```tsx
256
+ const [isSubmitting, setIsSubmitting] = useState(false);
257
+
258
+ const formMethods = useForm({
259
+ disabled: isSubmitting, // Disable form during submission
260
+ });
261
+
262
+ const onSubmit = async (data) => {
263
+ setIsSubmitting(true);
264
+ await saveData(data);
265
+ setIsSubmitting(false);
266
+ };
267
+ ```
268
+
269
+ **2. Disable individual fields:**
270
+
271
+ Use the `disabled` prop on specific form field components:
272
+
273
+ ```tsx
274
+ <FormFieldInput name="email" label="Email" disabled />
275
+ ```
276
+
277
+ Field-level `disabled` props override the form-level setting:
278
+
279
+ ```tsx
280
+ const formMethods = useForm({
281
+ disabled: false,
282
+ });
283
+
284
+ // This field will be disabled even though the form is enabled
285
+ <FormFieldInput name="id" label="ID" disabled />;
286
+ ```
287
+
229
288
  ### FormFieldInput
230
289
 
231
290
  Text input field supporting all HTML input types.
@@ -382,17 +441,30 @@ Numeric input with formatting support (decimals, thousands separators).
382
441
 
383
442
  ### FormFieldCurrencyInput
384
443
 
385
- Currency input with locale-based formatting.
444
+ Currency input with locale-based formatting. The decimal and thousand separators are automatically determined by the locale set in the `I18nProvider`.
386
445
 
387
446
  ```tsx
388
- <FormFieldCurrencyInput
389
- name="amount"
390
- label="Amount"
391
- prefix="$"
392
- decimalsLimit={2}
393
- />
447
+ <FormFieldCurrencyInput name="amount" label="Amount" prefix="$" />
394
448
  ```
395
449
 
450
+ #### Customizing Separators per Locale
451
+
452
+ The component uses i18n messages to determine the decimal and thousand separators based on the current locale. You can customize these for each locale in your application:
453
+
454
+ 1. First, extract the i18n messages by running `pnpm run i18n` in your package
455
+ 2. In your application's i18n files (e.g., `i18n/compiled/pt-BR.json`), add the custom separators:
456
+
457
+ ```json
458
+ {
459
+ "JnCaDG": ",", // Decimal separator (default: ".")
460
+ "0+4wTp": "." // Thousand separator (default: ",")
461
+ }
462
+ ```
463
+
464
+ This approach allows each locale to define its own number formatting rules, which will be automatically applied to all currency inputs.
465
+
466
+ For more information about the i18n workflow, see the [i18n-CLI documentation](https://ttoss.dev/docs/modules/packages/i18n-cli/).
467
+
396
468
  ### FormFieldPatternFormat
397
469
 
398
470
  Input with custom format patterns.
@@ -1,7 +1,7 @@
1
1
  import * as react_jsx_runtime from 'react/jsx-runtime';
2
2
  import { FieldValues, FieldPath } from 'react-hook-form';
3
3
  import { PatternFormatProps } from 'react-number-format';
4
- import { F as FormFieldProps, a as FormFieldPatternFormatProps } from '../FormFieldPatternFormat-CxkCeniP.js';
4
+ import { F as FormFieldProps, a as FormFieldPatternFormatProps } from '../FormFieldPatternFormat-SKHmLjAO.js';
5
5
  import '@ttoss/ui';
6
6
  import 'react';
7
7
 
@@ -31,7 +31,7 @@ type FormFieldProps<TFieldValues extends FieldValues = FieldValues, TName extend
31
31
  type FormFieldCompleteProps<TFieldValues extends FieldValues = FieldValues, TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>> = {
32
32
  render: (props: UseControllerReturn<TFieldValues, TName>) => React.ReactElement;
33
33
  } & FormFieldProps<TFieldValues, TName>;
34
- declare const FormField: <TFieldValues extends FieldValues = FieldValues, TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>>({ label, id: idProp, name, defaultValue, disabled, tooltip, inputTooltip, sx, css, render, warning, rules, }: FormFieldCompleteProps<TFieldValues, TName>) => react_jsx_runtime.JSX.Element;
34
+ declare const FormField: <TFieldValues extends FieldValues = FieldValues, TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>>({ label, id: idProp, name, defaultValue, disabled: propsDisabled, tooltip, inputTooltip, sx, css, render, warning, rules, }: FormFieldCompleteProps<TFieldValues, TName>) => react_jsx_runtime.JSX.Element;
35
35
 
36
36
  type FormFieldPatternFormatProps<TFieldValues extends FieldValues = FieldValues, TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>> = FormFieldProps<TFieldValues, TName> & Omit<PatternFormatProps, 'name'>;
37
37
  declare const FormFieldPatternFormat: <TFieldValues extends FieldValues = FieldValues, TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>>({ disabled, ...props }: FormFieldPatternFormatProps<TFieldValues, TName>) => react_jsx_runtime.JSX.Element;
@@ -1,6 +1,6 @@
1
1
  /** Powered by @ttoss/config. https://ttoss.dev/docs/modules/packages/config/ */
2
2
  import * as React from 'react';
3
- import { FormField, FormFieldCNPJ, FormFieldPatternFormat, __name, isCnpjValid } from "../chunk-X42ZUF2A.js";
3
+ import { FormField, FormFieldCNPJ, FormFieldPatternFormat, __name, isCnpjValid } from "../chunk-A5YD5USY.js";
4
4
 
5
5
  // src/Brazil/FormFieldPhone.tsx
6
6
  import { Input } from "@ttoss/ui";
@@ -50,7 +50,7 @@ var FormFieldPhone = /* @__PURE__ */__name(({
50
50
  format,
51
51
  customInput: Input,
52
52
  placeholder,
53
- disabled
53
+ disabled: disabled ?? field.disabled
54
54
  });
55
55
  }, "render")
56
56
  });
@@ -1,7 +1,7 @@
1
1
  /** Powered by @ttoss/config. https://ttoss.dev/docs/modules/packages/config/ */
2
2
  import * as React from 'react';
3
- import { Form, useForm, yupResolver } from "../chunk-WZLEU3SB.js";
4
- import { __name } from "../chunk-X42ZUF2A.js";
3
+ import { Form, useForm, yupResolver } from "../chunk-L2FEA5RZ.js";
4
+ import { __name } from "../chunk-A5YD5USY.js";
5
5
 
6
6
  // src/MultistepForm/MultistepForm.tsx
7
7
  import { Flex as Flex6 } from "@ttoss/ui";
@@ -54,7 +54,7 @@ var FormField = /* @__PURE__ */__name(({
54
54
  id: idProp,
55
55
  name,
56
56
  defaultValue,
57
- disabled,
57
+ disabled: propsDisabled,
58
58
  tooltip,
59
59
  inputTooltip,
60
60
  sx,
@@ -73,6 +73,7 @@ var FormField = /* @__PURE__ */__name(({
73
73
  errors
74
74
  }
75
75
  } = useFormContext2();
76
+ const disabled = propsDisabled ?? controllerReturn.field.disabled;
76
77
  const hasError = !!errors[name];
77
78
  const uniqueId = React2.useId();
78
79
  const id = idProp || `form-field-${name}-${uniqueId}`;
@@ -138,8 +139,17 @@ var FormField = /* @__PURE__ */__name(({
138
139
  return element.type === component;
139
140
  });
140
141
  }, "isCheckboxOrSwitch");
142
+ const controllerReturnWithDisabled = React2.useMemo(() => {
143
+ return {
144
+ ...controllerReturn,
145
+ field: {
146
+ ...controllerReturn.field,
147
+ disabled
148
+ }
149
+ };
150
+ }, [controllerReturn, disabled]);
141
151
  const memoizedRender = React2.useMemo(() => {
142
- return React2.Children.map(render(controllerReturn), child => {
152
+ return React2.Children.map(render(controllerReturnWithDisabled), child => {
143
153
  if (! /* @__PURE__ */React2.isValidElement(child)) {
144
154
  return null;
145
155
  }
@@ -192,7 +202,7 @@ var FormField = /* @__PURE__ */__name(({
192
202
  tooltip
193
203
  }, label), /* @__PURE__ */React2.createElement(child.type, elementProps), tooltipElement);
194
204
  });
195
- }, [render, controllerReturn, label, disabled, id, tooltip, warning, inputTooltip, showInputTooltip, tooltipId, handleInputClick, handleInputFocus, handleInputBlur, tooltipElement]);
205
+ }, [render, controllerReturnWithDisabled, label, disabled, id, tooltip, warning, inputTooltip, showInputTooltip, tooltipId, handleInputClick, handleInputFocus, handleInputBlur, tooltipElement]);
196
206
  return /* @__PURE__ */React2.createElement(Flex, {
197
207
  sx: {
198
208
  flexDirection: "column",
@@ -260,7 +270,7 @@ var FormFieldPatternFormat = /* @__PURE__ */__name(({
260
270
  field.onChange(values.value);
261
271
  }, "onValueChange"),
262
272
  customInput: Input,
263
- disabled,
273
+ disabled: disabled ?? field.disabled,
264
274
  "aria-invalid": fieldState.error ? "true" : void 0
265
275
  });
266
276
  }, "render")
@@ -352,7 +362,7 @@ var FormFieldCNPJ = /* @__PURE__ */__name(({
352
362
  format: "##.###.###/####-##",
353
363
  customInput: Input2,
354
364
  placeholder,
355
- disabled
365
+ disabled: disabled ?? field.disabled
356
366
  });
357
367
  }, "render")
358
368
  });
@@ -1,6 +1,6 @@
1
1
  /** Powered by @ttoss/config. https://ttoss.dev/docs/modules/packages/config/ */
2
2
  import * as React from 'react';
3
- import { FormErrorMessage, FormField, FormFieldPatternFormat, __name, isCnpjValid } from "./chunk-X42ZUF2A.js";
3
+ import { FormErrorMessage, FormField, FormFieldPatternFormat, __name, isCnpjValid } from "./chunk-A5YD5USY.js";
4
4
 
5
5
  // src/Form.tsx
6
6
  import { Box } from "@ttoss/ui";
@@ -60,7 +60,7 @@ var FormFieldCheckbox = /* @__PURE__ */__name(({
60
60
  return /* @__PURE__ */React.createElement(Checkbox, {
61
61
  ...checkboxProps,
62
62
  ...field,
63
- disabled,
63
+ disabled: disabled ?? field.disabled,
64
64
  "aria-invalid": !!fieldState.error
65
65
  });
66
66
  }, "render")
@@ -80,6 +80,9 @@ var FormFieldCreditCardNumber = /* @__PURE__ */__name(({
80
80
  });
81
81
  }, "FormFieldCreditCardNumber");
82
82
 
83
+ // src/FormFieldCurrencyInput.tsx
84
+ import { defineMessages, useI18n } from "@ttoss/react-i18n";
85
+
83
86
  // src/FormFieldNumericFormat.tsx
84
87
  import { Input } from "@ttoss/ui";
85
88
  import { NumericFormat } from "react-number-format";
@@ -124,26 +127,47 @@ var FormFieldNumericFormat = /* @__PURE__ */__name(({
124
127
  field.onChange(values.floatValue);
125
128
  }, "onValueChange"),
126
129
  customInput: Input,
127
- disabled
130
+ disabled: disabled ?? field.disabled
128
131
  });
129
132
  }, "render")
130
133
  });
131
134
  }, "FormFieldNumericFormat");
132
135
 
133
136
  // src/FormFieldCurrencyInput.tsx
137
+ var messages = defineMessages({
138
+ decimalSeparator: {
139
+ id: "JnCaDG",
140
+ defaultMessage: [{
141
+ "type": 0,
142
+ "value": "."
143
+ }]
144
+ },
145
+ thousandSeparator: {
146
+ id: "0+4wTp",
147
+ defaultMessage: [{
148
+ "type": 0,
149
+ "value": ","
150
+ }]
151
+ }
152
+ });
134
153
  var FormFieldCurrencyInput = /* @__PURE__ */__name(({
135
154
  prefix,
136
- decimalSeparator = ",",
137
- thousandSeparator = ".",
155
+ decimalSeparator,
156
+ thousandSeparator,
138
157
  ...formFieldNumericFormatProps
139
158
  }) => {
159
+ const {
160
+ intl
161
+ } = useI18n();
162
+ const finalDecimalSeparator = decimalSeparator ?? intl.formatMessage(messages.decimalSeparator);
163
+ const finalThousandSeparator = thousandSeparator ?? intl.formatMessage(messages.thousandSeparator);
140
164
  return /* @__PURE__ */React.createElement(FormFieldNumericFormat, {
141
165
  fixedDecimalScale: true,
142
166
  decimalScale: 2,
143
167
  prefix,
144
- decimalSeparator,
145
- thousandSeparator,
146
- placeholder: `${prefix} 0${decimalSeparator}00`,
168
+ decimalSeparator: finalDecimalSeparator,
169
+ thousandSeparator: finalThousandSeparator,
170
+ placeholder: `${prefix} 0${finalDecimalSeparator}00`,
147
171
  allowNegative: false,
148
172
  ...formFieldNumericFormatProps
149
173
  });
@@ -187,7 +211,7 @@ var FormFieldInput = /* @__PURE__ */__name(({
187
211
  return /* @__PURE__ */React.createElement(Input2, {
188
212
  ...inputProps,
189
213
  ...field,
190
- disabled,
214
+ disabled: disabled ?? field.disabled,
191
215
  "aria-invalid": fieldState.error ? "true" : void 0
192
216
  });
193
217
  }, "render")
@@ -232,7 +256,7 @@ var FormFieldPassword = /* @__PURE__ */__name(({
232
256
  return /* @__PURE__ */React.createElement(InputPassword, {
233
257
  ...inputProps,
234
258
  ...field,
235
- disabled,
259
+ disabled: disabled ?? field.disabled,
236
260
  "aria-invalid": fieldState.error ? "true" : void 0
237
261
  });
238
262
  }, "render")
@@ -293,7 +317,7 @@ var FormFieldRadio = /* @__PURE__ */__name(({
293
317
  value: option.value,
294
318
  checked: field.value === option.value,
295
319
  name,
296
- disabled,
320
+ disabled: disabled ?? field.disabled,
297
321
  ...radioProps
298
322
  }), option.label);
299
323
  }));
@@ -368,7 +392,7 @@ var FormFieldRadioCard = /* @__PURE__ */__name(({
368
392
  value: option.value,
369
393
  checked: field.value === option.value,
370
394
  name,
371
- disabled
395
+ disabled: disabled ?? field.disabled
372
396
  }), /* @__PURE__ */React.createElement(Flex2, {
373
397
  sx: {
374
398
  flexDirection: "column",
@@ -385,8 +409,7 @@ var FormFieldRadioCard = /* @__PURE__ */__name(({
385
409
  }, "FormFieldRadioCard");
386
410
 
387
411
  // src/FormFieldRadioCardIcony.tsx
388
- import { Box as Box3, Flex as Flex3, Text as Text2 } from "@ttoss/ui";
389
- import { Tag } from "@ttoss/ui";
412
+ import { Box as Box3, Flex as Flex3, Tag, Text as Text2 } from "@ttoss/ui";
390
413
  import * as React3 from "react";
391
414
  var FormFieldRadioCardIcony = /* @__PURE__ */__name(({
392
415
  disabled,
@@ -422,8 +445,9 @@ var FormFieldRadioCardIcony = /* @__PURE__ */__name(({
422
445
  render: /* @__PURE__ */__name(({
423
446
  field
424
447
  }) => {
448
+ const isDisabled = disabled ?? field.disabled;
425
449
  const handleOptionClick = /* @__PURE__ */__name(optionValue => {
426
- if (!disabled) {
450
+ if (!isDisabled) {
427
451
  field.onChange(optionValue);
428
452
  field.onBlur();
429
453
  }
@@ -455,8 +479,8 @@ var FormFieldRadioCardIcony = /* @__PURE__ */__name(({
455
479
  alignItems: "center",
456
480
  justifyContent: "center",
457
481
  textAlign: "center",
458
- cursor: disabled ? "not-allowed" : "pointer",
459
- opacity: disabled ? 0.5 : 1,
482
+ cursor: isDisabled ? "not-allowed" : "pointer",
483
+ opacity: isDisabled ? 0.5 : 1,
460
484
  transition: "all 0.2s ease-in-out"
461
485
  }
462
486
  }, IconComponent && /* @__PURE__ */React3.createElement(Box3, {
@@ -534,7 +558,7 @@ var FormFieldSelect = /* @__PURE__ */__name(({
534
558
  return /* @__PURE__ */React.createElement(Select, {
535
559
  ...selectProps,
536
560
  ...field,
537
- isDisabled: disabled,
561
+ isDisabled: disabled ?? field.disabled,
538
562
  "aria-invalid": fieldState.error ? "true" : void 0
539
563
  });
540
564
  }, "render")
@@ -579,7 +603,7 @@ var FormFieldSwitch = /* @__PURE__ */__name(({
579
603
  return /* @__PURE__ */React.createElement(Switch, {
580
604
  ...switchProps,
581
605
  ...field,
582
- disabled,
606
+ disabled: disabled ?? field.disabled,
583
607
  "aria-invalid": !!fieldState.error
584
608
  });
585
609
  }, "render")
@@ -624,7 +648,7 @@ var FormFieldTextarea = /* @__PURE__ */__name(({
624
648
  return /* @__PURE__ */React.createElement(Textarea, {
625
649
  ...textareaProps,
626
650
  ...field,
627
- disabled,
651
+ disabled: disabled ?? field.disabled,
628
652
  "aria-invalid": fieldState.error ? "true" : void 0
629
653
  });
630
654
  }, "render")
package/dist/esm/index.js CHANGED
@@ -1,4 +1,4 @@
1
1
  /** Powered by @ttoss/config. https://ttoss.dev/docs/modules/packages/config/ */
2
- import { Controller, Form, FormFieldCheckbox, FormFieldCreditCardNumber, FormFieldCurrencyInput, FormFieldInput, FormFieldNumericFormat, FormFieldPassword, FormFieldRadio, FormFieldRadioCard, FormFieldRadioCardIcony, FormFieldSelect, FormFieldSwitch, FormFieldTextarea, FormGroup, FormProvider, useController, useFieldArray, useForm, useFormContext, useFormGroup, useFormState, useWatch, yup, yupResolver } from "./chunk-WZLEU3SB.js";
3
- import { FormErrorMessage, FormField, FormFieldPatternFormat } from "./chunk-X42ZUF2A.js";
2
+ import { Controller, Form, FormFieldCheckbox, FormFieldCreditCardNumber, FormFieldCurrencyInput, FormFieldInput, FormFieldNumericFormat, FormFieldPassword, FormFieldRadio, FormFieldRadioCard, FormFieldRadioCardIcony, FormFieldSelect, FormFieldSwitch, FormFieldTextarea, FormGroup, FormProvider, useController, useFieldArray, useForm, useFormContext, useFormGroup, useFormState, useWatch, yup, yupResolver } from "./chunk-L2FEA5RZ.js";
3
+ import { FormErrorMessage, FormField, FormFieldPatternFormat } from "./chunk-A5YD5USY.js";
4
4
  export { Controller, Form, FormErrorMessage, FormField, FormFieldCheckbox, FormFieldCreditCardNumber, FormFieldCurrencyInput, FormFieldInput, FormFieldNumericFormat, FormFieldPassword, FormFieldPatternFormat, FormFieldRadio, FormFieldRadioCard, FormFieldRadioCardIcony, FormFieldSelect, FormFieldSwitch, FormFieldTextarea, FormGroup, FormProvider, useController, useFieldArray, useForm, useFormContext, useFormGroup, useFormState, useWatch, yup, yupResolver };
package/dist/index.d.ts CHANGED
@@ -4,8 +4,8 @@ import * as React from 'react';
4
4
  import { FieldValues, FormProviderProps, FieldName, FieldPath } from 'react-hook-form';
5
5
  export * from 'react-hook-form';
6
6
  export { Controller, FormProvider, useController, useFieldArray, useForm, useFormContext, useFormState, useWatch } from 'react-hook-form';
7
- import { F as FormFieldProps, a as FormFieldPatternFormatProps } from './FormFieldPatternFormat-CxkCeniP.js';
8
- export { b as FormField, c as FormFieldPatternFormat } from './FormFieldPatternFormat-CxkCeniP.js';
7
+ import { F as FormFieldProps, a as FormFieldPatternFormatProps } from './FormFieldPatternFormat-SKHmLjAO.js';
8
+ export { b as FormField, c as FormFieldPatternFormat } from './FormFieldPatternFormat-SKHmLjAO.js';
9
9
  import { NumericFormatProps } from 'react-number-format';
10
10
  import './typings.d-HZBqJJjn.js';
11
11
  import * as yup from 'yup';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ttoss/forms",
3
- "version": "0.32.2",
3
+ "version": "0.32.4",
4
4
  "license": "MIT",
5
5
  "author": "ttoss",
6
6
  "contributors": [
@@ -51,11 +51,11 @@
51
51
  "tsup": "^8.5.1",
52
52
  "yup": "^1.7.1",
53
53
  "@ttoss/config": "^1.35.11",
54
- "@ttoss/i18n-cli": "^0.7.37",
55
- "@ttoss/react-i18n": "^2.0.21",
56
54
  "@ttoss/react-icons": "^0.5.2",
57
55
  "@ttoss/test-utils": "^3.0.3",
58
- "@ttoss/ui": "^5.10.7"
56
+ "@ttoss/ui": "^5.10.7",
57
+ "@ttoss/react-i18n": "^2.0.21",
58
+ "@ttoss/i18n-cli": "^0.7.37"
59
59
  },
60
60
  "publishConfig": {
61
61
  "access": "public",