@ttoss/forms 0.42.0 → 0.43.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -571,6 +571,46 @@ Input with custom format patterns.
571
571
  />
572
572
  ```
573
573
 
574
+ ### FormFieldPhone
575
+
576
+ Generic phone number field with an optional country-code dropdown. By default it renders a dropdown populated with `COMMON_PHONE_COUNTRY_CODES` (15 common countries + a Manual entry). The component manages the selected country code internally — no external state is needed.
577
+
578
+ ```tsx
579
+ import { FormFieldPhone, COMMON_PHONE_COUNTRY_CODES } from '@ttoss/forms';
580
+
581
+ // Minimal — dropdown defaults to COMMON_PHONE_COUNTRY_CODES, starts in Manual mode
582
+ <FormFieldPhone name="phone" label="Phone" />
583
+
584
+ // Set an initial country code (US +1)
585
+ <FormFieldPhone name="phone" label="Phone" defaultCountryCode="+1" />
586
+
587
+ // React to country-code changes without managing state
588
+ <FormFieldPhone
589
+ name="phone"
590
+ label="Phone"
591
+ defaultCountryCode="+1"
592
+ onCountryCodeChange={(code) => console.log('selected', code)}
593
+ />
594
+
595
+ // No dropdown — plain masked input with a fixed country code
596
+ <FormFieldPhone
597
+ name="phone"
598
+ label="Phone"
599
+ defaultCountryCode="+1"
600
+ format="(###) ###-####"
601
+ countryCodeOptions={[]}
602
+ />
603
+ ```
604
+
605
+ The submitted value always includes the country code prefix (e.g. `{ phone: '+15555555555' }`). When the user selects the **Manual** entry, the mask is removed and the user can type any international number freely (the value is stored with a `+` prefix).
606
+
607
+ **Key props:**
608
+
609
+ - `defaultCountryCode`: Initial calling code (e.g. `'+1'`). Defaults to the first entry in `countryCodeOptions`.
610
+ - `format`: Pattern string or function for the local number part (e.g. `'(###) ###-####'`).
611
+ - `countryCodeOptions`: List of `CountryCodeOption` objects. Defaults to `COMMON_PHONE_COUNTRY_CODES`. Pass `[]` to hide the dropdown.
612
+ - `onCountryCodeChange`: Optional callback fired when the user picks a different country code.
613
+
574
614
  ### FormFieldCreditCardNumber
575
615
 
576
616
  Credit card input with automatic formatting.
@@ -611,7 +651,7 @@ The package also exports `isCnpjValid(cnpj: string)` for standalone validation.
611
651
 
612
652
  #### FormFieldPhone
613
653
 
614
- Brazilian phone number input with formatting.
654
+ Brazilian phone number input with automatic formatting and the `+55` country code pre-set.
615
655
 
616
656
  ```tsx
617
657
  <FormFieldPhone name="phone" label="Phone" />
@@ -1,6 +1,6 @@
1
1
  import * as react_jsx_runtime from 'react/jsx-runtime';
2
2
  import { FieldValues, FieldPath } from 'react-hook-form';
3
- import { a as FormFieldPatternFormatProps, F as FormFieldProps, d as FormFieldPhoneProps$1 } from '../FormFieldPhone-BKeOS0i6.js';
3
+ import { a as FormFieldPatternFormatProps, F as FormFieldProps, d as FormFieldPhoneProps$1 } from '../FormFieldPhone--tC0Hlcf.js';
4
4
  import { PatternFormatProps, NumberFormatBaseProps } from 'react-number-format';
5
5
  import '@ttoss/ui';
6
6
  import 'react';
@@ -19,7 +19,7 @@ declare const FormFieldCPF: <TFieldValues extends FieldValues = FieldValues, TNa
19
19
  type FormFieldCPFOrCNPJProps<TFieldValues extends FieldValues = FieldValues, TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>> = FormFieldProps<TFieldValues, TName> & Omit<NumberFormatBaseProps, 'name' | 'format'>;
20
20
  declare const FormFieldCPFOrCNPJ: <TFieldValues extends FieldValues = FieldValues, TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>>({ disabled, ...props }: FormFieldCPFOrCNPJProps<TFieldValues, TName>) => react_jsx_runtime.JSX.Element;
21
21
 
22
- type FormFieldPhoneProps<TFieldValues extends FieldValues = FieldValues, TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>> = Omit<FormFieldPhoneProps$1<TFieldValues, TName>, 'countryCode' | 'format'>;
22
+ type FormFieldPhoneProps<TFieldValues extends FieldValues = FieldValues, TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>> = Omit<FormFieldPhoneProps$1<TFieldValues, TName>, 'defaultCountryCode' | 'format'>;
23
23
  /**
24
24
  * Brazilian phone number form field.
25
25
  *
@@ -109,11 +109,12 @@ declare const COMMON_PHONE_COUNTRY_CODES: CountryCodeOption[];
109
109
 
110
110
  type FormFieldPhoneProps<TFieldValues extends FieldValues = FieldValues, TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>> = FormFieldProps<TFieldValues, TName> & Omit<PatternFormatProps, 'name' | 'format'> & {
111
111
  /**
112
- * The country calling code to display as a literal prefix in the input.
112
+ * The initial country calling code to display as a literal prefix in the input.
113
113
  * For example, '+55' for Brazil or '+1' for the United States.
114
114
  * Defaults to the first entry in `countryCodeOptions` when not provided.
115
+ * The component manages the selected code internally — no external state needed.
115
116
  */
116
- countryCode?: string;
117
+ defaultCountryCode?: string;
117
118
  /**
118
119
  * The pattern format for the local part of the phone number.
119
120
  * Accepts either a static string (e.g., '(##) #####-####') or a function
@@ -136,9 +137,10 @@ type FormFieldPhoneProps<TFieldValues extends FieldValues = FieldValues, TName e
136
137
  */
137
138
  countryCodeOptions?: CountryCodeOption[];
138
139
  /**
139
- * Called with the newly selected country code value when the user changes
140
- * the country code via the dropdown. Only relevant when
141
- * countryCodeOptions is non-empty.
140
+ * Optional callback fired with the newly selected country code value when
141
+ * the user changes the country code via the dropdown. The component
142
+ * manages the selected code internally, so this is only needed when the
143
+ * caller wants to react to country-code changes.
142
144
  */
143
145
  onCountryCodeChange?: (countryCode: string) => void;
144
146
  };
@@ -168,26 +170,30 @@ type FormFieldPhoneProps<TFieldValues extends FieldValues = FieldValues, TName e
168
170
  *
169
171
  * @example
170
172
  * ```tsx
171
- * // Default: dropdown with COMMON_PHONE_COUNTRY_CODES, US (+1) pre-selected
172
- * <FormFieldPhone name="phone" label="Phone" countryCode="+1" />
173
+ * // Default: dropdown with COMMON_PHONE_COUNTRY_CODES, country code managed internally.
174
+ * // The submitted value includes the country code prefix (e.g. '+15555555555').
175
+ * <FormFieldPhone name="phone" label="Phone" />
173
176
  * ```
174
177
  *
175
178
  * @example
176
179
  * ```tsx
177
- * // Controlled country code submitted value includes the prefix
178
- * // e.g. { phone: '+15555555555' }
179
- * const [countryCode, setCountryCode] = React.useState('+1');
180
+ * // Set a custom initial country code; the component manages further changes.
181
+ * <FormFieldPhone name="phone" label="Phone" defaultCountryCode="+55" />
182
+ * ```
183
+ *
184
+ * @example
185
+ * ```tsx
186
+ * // Listen for country-code changes without managing state externally.
180
187
  * <FormFieldPhone
181
188
  * name="phone"
182
189
  * label="Phone"
183
- * countryCode={countryCode}
184
- * onCountryCodeChange={setCountryCode}
190
+ * onCountryCodeChange={(code) => console.log('selected', code)}
185
191
  * />
186
192
  * ```
187
193
  *
188
194
  * @example
189
195
  * ```tsx
190
- * // No dropdown — plain phone input; value includes the prefix
196
+ * // No dropdown — plain phone input; value includes the prefix.
191
197
  * // e.g. { phone: '+15555555555' }
192
198
  * <FormFieldPhone
193
199
  * name="phone"
@@ -212,6 +218,6 @@ type FormFieldPhoneProps<TFieldValues extends FieldValues = FieldValues, TName e
212
218
  * />
213
219
  * ```
214
220
  */
215
- declare const FormFieldPhone: <TFieldValues extends FieldValues = FieldValues, TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>>({ disabled, countryCode: countryCodeProp, format, countryCodeOptions, onCountryCodeChange, ...props }: FormFieldPhoneProps<TFieldValues, TName>) => react_jsx_runtime.JSX.Element;
221
+ declare const FormFieldPhone: <TFieldValues extends FieldValues = FieldValues, TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>>({ disabled, defaultCountryCode: countryCodeProp, format, countryCodeOptions, onCountryCodeChange, ...props }: FormFieldPhoneProps<TFieldValues, TName>) => react_jsx_runtime.JSX.Element;
216
222
 
217
223
  export { type CountryCodeOption as C, type FormFieldProps as F, MANUAL_PHONE_COUNTRY_CODE as M, type FormFieldPatternFormatProps as a, FormField as b, FormFieldPatternFormat as c, type FormFieldPhoneProps as d, FormFieldPhone as e, COMMON_PHONE_COUNTRY_CODES as f };
@@ -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, FormFieldCPF, FormFieldPatternFormat, FormFieldPhone, __name, isCnpjValid, isCpfValid } from "../chunk-ZORKZWD7.js";
3
+ import { FormField, FormFieldCNPJ, FormFieldCPF, FormFieldPatternFormat, FormFieldPhone, __name, isCnpjValid, isCpfValid } from "../chunk-U7QBQHMU.js";
4
4
 
5
5
  // src/Brazil/FormFieldCEP.tsx
6
6
  var FormFieldCEP = /* @__PURE__ */__name(({
@@ -84,7 +84,7 @@ var FormFieldPhone2 = /* @__PURE__ */__name(({
84
84
  }) => {
85
85
  return /* @__PURE__ */React.createElement(FormFieldPhone, {
86
86
  ...props,
87
- countryCode: BRAZIL_COUNTRY_CODE,
87
+ defaultCountryCode: BRAZIL_COUNTRY_CODE,
88
88
  format: getBrazilPhoneFormat,
89
89
  placeholder,
90
90
  countryCodeOptions
@@ -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-YCW3GPTZ.js";
4
- import { __name } from "../chunk-ZORKZWD7.js";
3
+ import { Form, useForm, yupResolver } from "../chunk-3M2OR5NH.js";
4
+ import { __name } from "../chunk-U7QBQHMU.js";
5
5
 
6
6
  // src/MultistepForm/MultistepForm.tsx
7
7
  import { Flex as Flex6 } from "@ttoss/ui";
@@ -1,26 +1,139 @@
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, isCpfValid } from "./chunk-ZORKZWD7.js";
3
+ import { FormErrorMessage, FormField, FormFieldPatternFormat, __name, isCnpjValid, isCpfValid } from "./chunk-U7QBQHMU.js";
4
4
 
5
5
  // src/Form.tsx
6
- import { Box } from "@ttoss/ui";
7
- import * as React2 from "react";
6
+ import { Box as Box2 } from "@ttoss/ui";
8
7
  import { FormProvider } from "react-hook-form";
9
- var Form = /* @__PURE__ */__name(({
8
+
9
+ // src/FormActions.tsx
10
+ import { Flex } from "@ttoss/ui";
11
+ var alignMap = {
12
+ left: "flex-start",
13
+ center: "center",
14
+ right: "flex-end"
15
+ };
16
+ var FormActions = /* @__PURE__ */__name(({
17
+ align = "right",
18
+ children,
19
+ sticky = false,
20
+ sx,
21
+ ...flexProps
22
+ }) => {
23
+ return /* @__PURE__ */React.createElement(Flex, {
24
+ ...flexProps,
25
+ sx: {
26
+ flexDirection: "row",
27
+ gap: "2",
28
+ justifyContent: alignMap[align],
29
+ ...(sticky && {
30
+ position: "sticky",
31
+ bottom: 0,
32
+ backgroundColor: "background",
33
+ paddingY: "3",
34
+ zIndex: "sticky"
35
+ }),
36
+ ...sx
37
+ }
38
+ }, children);
39
+ }, "FormActions");
40
+
41
+ // src/FormGroup.tsx
42
+ import { Box, Flex as Flex2, Text } from "@ttoss/ui";
43
+ import * as React2 from "react";
44
+ var FormGroupContext = /* @__PURE__ */React2.createContext({});
45
+ var useFormGroup = /* @__PURE__ */__name(() => {
46
+ const {
47
+ parentLevel
48
+ } = React2.useContext(FormGroupContext);
49
+ return {
50
+ level: parentLevel,
51
+ /**
52
+ * @deprecated `levelsLength` has been removed from `FormGroup` internals.
53
+ * This field always returns `undefined`. Use `level` to determine nesting depth.
54
+ */
55
+ levelsLength: void 0
56
+ };
57
+ }, "useFormGroup");
58
+ var FormGroupWrapper = /* @__PURE__ */__name(({
59
+ title,
60
+ description,
61
+ direction,
62
+ children,
63
+ name,
64
+ ...boxProps
65
+ }) => {
66
+ const {
67
+ level
68
+ } = useFormGroup();
69
+ const childrenContainerSx = {
70
+ flexDirection: direction || "column",
71
+ gap: "4",
72
+ width: "100%"
73
+ };
74
+ return /* @__PURE__ */React2.createElement(Box, {
75
+ "data-level": level,
76
+ ...boxProps,
77
+ sx: {
78
+ marginTop: level === 0 ? "none" : "4",
79
+ ...boxProps.sx
80
+ }
81
+ }, (title || description) && /* @__PURE__ */React2.createElement(Box, {
82
+ sx: {
83
+ marginBottom: "2"
84
+ }
85
+ }, title && /* @__PURE__ */React2.createElement(Text, {
86
+ sx: {
87
+ fontSize: "2xl",
88
+ fontWeight: "bold"
89
+ }
90
+ }, title), description && /* @__PURE__ */React2.createElement(Text, {
91
+ sx: {
92
+ color: "text.secondary"
93
+ }
94
+ }, description)), /* @__PURE__ */React2.createElement(Flex2, {
95
+ sx: childrenContainerSx
96
+ }, children), name && /* @__PURE__ */React2.createElement(FormErrorMessage, {
97
+ name
98
+ }));
99
+ }, "FormGroupWrapper");
100
+ var FormGroup = /* @__PURE__ */__name(props => {
101
+ const {
102
+ level
103
+ } = useFormGroup();
104
+ const currentLevel = level === void 0 ? 0 : level + 1;
105
+ return /* @__PURE__ */React2.createElement(FormGroupContext.Provider, {
106
+ value: {
107
+ parentLevel: currentLevel
108
+ }
109
+ }, /* @__PURE__ */React2.createElement(FormGroupWrapper, props));
110
+ }, "FormGroup");
111
+
112
+ // src/Form.tsx
113
+ var FormBase = /* @__PURE__ */__name(({
10
114
  children,
11
115
  onSubmit,
12
116
  sx,
13
117
  ...formMethods
14
118
  }) => {
15
- return /* @__PURE__ */React2.createElement(FormProvider, formMethods, /* @__PURE__ */React2.createElement(Box, {
119
+ return /* @__PURE__ */React.createElement(FormProvider, formMethods, /* @__PURE__ */React.createElement(Box2, {
16
120
  as: "form",
17
121
  variant: "forms.form",
18
122
  onSubmit: formMethods.handleSubmit(data => {
19
123
  return onSubmit?.(data);
20
124
  }),
21
- sx
125
+ sx: {
126
+ display: "flex",
127
+ flexDirection: "column",
128
+ gap: "4",
129
+ ...sx
130
+ }
22
131
  }, children));
23
- }, "Form");
132
+ }, "FormBase");
133
+ var Form = Object.assign(FormBase, {
134
+ Group: FormGroup,
135
+ Actions: FormActions
136
+ });
24
137
 
25
138
  // src/FormFieldCheckbox.tsx
26
139
  import { Checkbox } from "@ttoss/ui";
@@ -363,7 +476,7 @@ var FormFieldPassword = /* @__PURE__ */__name(({
363
476
  }, "FormFieldPassword");
364
477
 
365
478
  // src/FormFieldRadio.tsx
366
- import { Flex, Label, Radio } from "@ttoss/ui";
479
+ import { Flex as Flex3, Label, Radio } from "@ttoss/ui";
367
480
  var FormFieldRadio = /* @__PURE__ */__name(({
368
481
  disabled,
369
482
  options,
@@ -397,7 +510,7 @@ var FormFieldRadio = /* @__PURE__ */__name(({
397
510
  render: /* @__PURE__ */__name(({
398
511
  field
399
512
  }) => {
400
- return /* @__PURE__ */React.createElement(Flex, {
513
+ return /* @__PURE__ */React.createElement(Flex3, {
401
514
  sx: {
402
515
  flexDirection: "column",
403
516
  gap: "1"
@@ -431,7 +544,7 @@ var FormFieldRadio = /* @__PURE__ */__name(({
431
544
  }, "FormFieldRadio");
432
545
 
433
546
  // src/FormFieldRadioCard.tsx
434
- import { Box as Box2, Flex as Flex2, Label as Label2, Radio as Radio2, Text } from "@ttoss/ui";
547
+ import { Box as Box3, Flex as Flex4, Label as Label2, Radio as Radio2, Text as Text2 } from "@ttoss/ui";
435
548
  var FormFieldRadioCard = /* @__PURE__ */__name(({
436
549
  disabled,
437
550
  ...props
@@ -467,7 +580,7 @@ var FormFieldRadioCard = /* @__PURE__ */__name(({
467
580
  render: /* @__PURE__ */__name(({
468
581
  field
469
582
  }) => {
470
- return /* @__PURE__ */React.createElement(Flex2, {
583
+ return /* @__PURE__ */React.createElement(Flex4, {
471
584
  sx: {
472
585
  flexDirection: direction,
473
586
  gap: "4"
@@ -475,7 +588,7 @@ var FormFieldRadioCard = /* @__PURE__ */__name(({
475
588
  }, options.map(option => {
476
589
  const key = `form-field-radio-${name}-${option.value}`;
477
590
  const isSelected = field.value === option.value;
478
- return /* @__PURE__ */React.createElement(Box2, {
591
+ return /* @__PURE__ */React.createElement(Box3, {
479
592
  key,
480
593
  sx: {
481
594
  gap: "2",
@@ -506,12 +619,12 @@ var FormFieldRadioCard = /* @__PURE__ */__name(({
506
619
  checked: field.value === option.value,
507
620
  name,
508
621
  disabled: disabled ?? field.disabled
509
- }), /* @__PURE__ */React.createElement(Flex2, {
622
+ }), /* @__PURE__ */React.createElement(Flex4, {
510
623
  sx: {
511
624
  flexDirection: "column",
512
625
  gap: "1"
513
626
  }
514
- }, option.label && /* @__PURE__ */React.createElement(Text, null, option.label), option.description && /* @__PURE__ */React.createElement(Text, {
627
+ }, option.label && /* @__PURE__ */React.createElement(Text2, null, option.label), option.description && /* @__PURE__ */React.createElement(Text2, {
515
628
  sx: {
516
629
  fontSize: "sm"
517
630
  }
@@ -522,7 +635,7 @@ var FormFieldRadioCard = /* @__PURE__ */__name(({
522
635
  }, "FormFieldRadioCard");
523
636
 
524
637
  // src/FormFieldRadioCardIcony.tsx
525
- import { Box as Box3, Flex as Flex3, Tag, Text as Text2 } from "@ttoss/ui";
638
+ import { Box as Box4, Flex as Flex5, Tag, Text as Text3 } from "@ttoss/ui";
526
639
  import * as React3 from "react";
527
640
  var FormFieldRadioCardIcony = /* @__PURE__ */__name(({
528
641
  disabled,
@@ -563,7 +676,7 @@ var FormFieldRadioCardIcony = /* @__PURE__ */__name(({
563
676
  field.onBlur();
564
677
  }
565
678
  }, "handleOptionClick");
566
- return /* @__PURE__ */React3.createElement(Flex3, {
679
+ return /* @__PURE__ */React3.createElement(Flex5, {
567
680
  sx: {
568
681
  flexDirection: direction,
569
682
  gap: "4"
@@ -573,7 +686,7 @@ var FormFieldRadioCardIcony = /* @__PURE__ */__name(({
573
686
  const isSelected = field.value === option.value;
574
687
  const IconComponent = option.icon;
575
688
  const tag = option.tag;
576
- return /* @__PURE__ */React3.createElement(Box3, {
689
+ return /* @__PURE__ */React3.createElement(Box4, {
577
690
  key,
578
691
  onClick: /* @__PURE__ */__name(() => {
579
692
  return handleOptionClick(option.value);
@@ -594,25 +707,25 @@ var FormFieldRadioCardIcony = /* @__PURE__ */__name(({
594
707
  opacity: isDisabled ? 0.5 : 1,
595
708
  transition: "all 0.2s ease-in-out"
596
709
  }
597
- }, IconComponent && /* @__PURE__ */React3.createElement(Box3, {
710
+ }, IconComponent && /* @__PURE__ */React3.createElement(Box4, {
598
711
  sx: {
599
712
  marginBottom: "2",
600
713
  color: "text.primary"
601
714
  }
602
715
  }, /* @__PURE__ */React3.createElement(IconComponent, {
603
716
  size: 24
604
- })), /* @__PURE__ */React3.createElement(Flex3, {
717
+ })), /* @__PURE__ */React3.createElement(Flex5, {
605
718
  sx: {
606
719
  flexDirection: "column",
607
720
  gap: "1"
608
721
  }
609
- }, option.label && /* @__PURE__ */React3.createElement(Text2, {
722
+ }, option.label && /* @__PURE__ */React3.createElement(Text3, {
610
723
  sx: {
611
724
  fontSize: "lg",
612
725
  fontWeight: "semibold",
613
726
  color: "text.primary"
614
727
  }
615
- }, option.label), option.description && /* @__PURE__ */React3.createElement(Text2, {
728
+ }, option.label), option.description && /* @__PURE__ */React3.createElement(Text3, {
616
729
  sx: {
617
730
  fontSize: "md",
618
731
  color: "text.secondary"
@@ -845,78 +958,6 @@ var FormFieldTextarea = /* @__PURE__ */__name(({
845
958
  });
846
959
  }, "FormFieldTextarea");
847
960
 
848
- // src/FormGroup.tsx
849
- import { Box as Box4, Flex as Flex4, Text as Text3 } from "@ttoss/ui";
850
- import * as React4 from "react";
851
- var FormGroupContext = /* @__PURE__ */React4.createContext({});
852
- var useFormGroup = /* @__PURE__ */__name(() => {
853
- const {
854
- parentLevel
855
- } = React4.useContext(FormGroupContext);
856
- return {
857
- level: parentLevel,
858
- /**
859
- * @deprecated `levelsLength` has been removed from `FormGroup` internals.
860
- * This field always returns `undefined`. Use `level` to determine nesting depth.
861
- */
862
- levelsLength: void 0
863
- };
864
- }, "useFormGroup");
865
- var FormGroupWrapper = /* @__PURE__ */__name(({
866
- title,
867
- description,
868
- direction,
869
- children,
870
- name,
871
- ...boxProps
872
- }) => {
873
- const {
874
- level
875
- } = useFormGroup();
876
- const childrenContainerSx = {
877
- flexDirection: direction || "column",
878
- gap: "1",
879
- width: "100%"
880
- };
881
- return /* @__PURE__ */React4.createElement(Box4, {
882
- "data-level": level,
883
- ...boxProps,
884
- sx: {
885
- marginTop: level === 0 ? "none" : "4",
886
- marginBottom: "4",
887
- ...boxProps.sx
888
- }
889
- }, (title || description) && /* @__PURE__ */React4.createElement(Box4, {
890
- sx: {
891
- marginBottom: "2"
892
- }
893
- }, title && /* @__PURE__ */React4.createElement(Text3, {
894
- sx: {
895
- fontSize: "2xl",
896
- fontWeight: "bold"
897
- }
898
- }, title), description && /* @__PURE__ */React4.createElement(Text3, {
899
- sx: {
900
- color: "text.secondary"
901
- }
902
- }, description)), /* @__PURE__ */React4.createElement(Flex4, {
903
- sx: childrenContainerSx
904
- }, children), name && /* @__PURE__ */React4.createElement(FormErrorMessage, {
905
- name
906
- }));
907
- }, "FormGroupWrapper");
908
- var FormGroup = /* @__PURE__ */__name(props => {
909
- const {
910
- level
911
- } = useFormGroup();
912
- const currentLevel = level === void 0 ? 0 : level + 1;
913
- return /* @__PURE__ */React4.createElement(FormGroupContext.Provider, {
914
- value: {
915
- parentLevel: currentLevel
916
- }
917
- }, /* @__PURE__ */React4.createElement(FormGroupWrapper, props));
918
- }, "FormGroup");
919
-
920
961
  // src/yup/i18n.ts
921
962
  import { defineMessage } from "@ttoss/react-i18n";
922
963
  import { setLocale } from "yup";
@@ -1083,4 +1124,4 @@ import { yupResolver } from "@hookform/resolvers/yup";
1083
1124
  import { zodResolver } from "@hookform/resolvers/zod";
1084
1125
  import { Controller, FormProvider as FormProvider2, useController, useFieldArray, useForm, useFormContext, useFormState, useWatch } from "react-hook-form";
1085
1126
  export * from "react-hook-form";
1086
- export { Form, FormFieldCheckbox, FormFieldCreditCardNumber, FormFieldNumericFormat, FormFieldCurrencyInput, FormFieldDatePicker, FormFieldInput, FormFieldPassword, FormFieldRadio, FormFieldRadioCard, FormFieldRadioCardIcony, FormFieldSegmentedControl, FormFieldSelect, FormFieldSwitch, FormFieldTextarea, useFormGroup, FormGroup, yup2 as yup, z3 as z, yupResolver, zodResolver, Controller, FormProvider2 as FormProvider, useController, useFieldArray, useForm, useFormContext, useFormState, useWatch };
1127
+ export { FormActions, useFormGroup, FormGroup, Form, FormFieldCheckbox, FormFieldCreditCardNumber, FormFieldNumericFormat, FormFieldCurrencyInput, FormFieldDatePicker, FormFieldInput, FormFieldPassword, FormFieldRadio, FormFieldRadioCard, FormFieldRadioCardIcony, FormFieldSegmentedControl, FormFieldSelect, FormFieldSwitch, FormFieldTextarea, yup2 as yup, z3 as z, yupResolver, zodResolver, Controller, FormProvider2 as FormProvider, useController, useFieldArray, useForm, useFormContext, useFormState, useWatch };
@@ -377,7 +377,7 @@ var PhoneDropdownWrapper = /* @__PURE__ */__name(({
377
377
  }, "PhoneDropdownWrapper");
378
378
  var FormFieldPhone = /* @__PURE__ */__name(({
379
379
  disabled,
380
- countryCode: countryCodeProp,
380
+ defaultCountryCode: countryCodeProp,
381
381
  format,
382
382
  countryCodeOptions = COMMON_PHONE_COUNTRY_CODES,
383
383
  onCountryCodeChange,
@@ -399,7 +399,11 @@ var FormFieldPhone = /* @__PURE__ */__name(({
399
399
  placeholder,
400
400
  ...patternFormatProps
401
401
  } = props;
402
- const countryCode = countryCodeProp ?? countryCodeOptions[0]?.value ?? void 0;
402
+ const [countryCode, setCountryCode] = React4.useState(countryCodeProp ?? countryCodeOptions[0]?.value ?? void 0);
403
+ const handleCountryCodeChange = /* @__PURE__ */__name(code => {
404
+ setCountryCode(code);
405
+ onCountryCodeChange?.(code);
406
+ }, "handleCountryCodeChange");
403
407
  const isManual = countryCode === MANUAL_PHONE_COUNTRY_CODE;
404
408
  const getFormat = /* @__PURE__ */__name(value => {
405
409
  const selectedOption = countryCodeOptions.find(opt => {
@@ -472,7 +476,7 @@ var FormFieldPhone = /* @__PURE__ */__name(({
472
476
  countryCode,
473
477
  countryCodeOptions,
474
478
  disabled: disabled ?? field.disabled,
475
- onCountryCodeChange,
479
+ onCountryCodeChange: handleCountryCodeChange,
476
480
  onPhoneReset: /* @__PURE__ */__name(() => {
477
481
  return field.onChange("");
478
482
  }, "onPhoneReset"),
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, FormFieldDatePicker, FormFieldInput, FormFieldNumericFormat, FormFieldPassword, FormFieldRadio, FormFieldRadioCard, FormFieldRadioCardIcony, FormFieldSegmentedControl, FormFieldSelect, FormFieldSwitch, FormFieldTextarea, FormGroup, FormProvider, useController, useFieldArray, useForm, useFormContext, useFormGroup, useFormState, useWatch, yup, yupResolver, z, zodResolver } from "./chunk-YCW3GPTZ.js";
3
- import { COMMON_PHONE_COUNTRY_CODES, FormErrorMessage, FormField, FormFieldPatternFormat, FormFieldPhone, MANUAL_PHONE_COUNTRY_CODE } from "./chunk-ZORKZWD7.js";
4
- export { COMMON_PHONE_COUNTRY_CODES, Controller, Form, FormErrorMessage, FormField, FormFieldCheckbox, FormFieldCreditCardNumber, FormFieldCurrencyInput, FormFieldDatePicker, FormFieldInput, FormFieldNumericFormat, FormFieldPassword, FormFieldPatternFormat, FormFieldPhone, FormFieldRadio, FormFieldRadioCard, FormFieldRadioCardIcony, FormFieldSegmentedControl, FormFieldSelect, FormFieldSwitch, FormFieldTextarea, FormGroup, FormProvider, MANUAL_PHONE_COUNTRY_CODE, useController, useFieldArray, useForm, useFormContext, useFormGroup, useFormState, useWatch, yup, yupResolver, z, zodResolver };
2
+ import { Controller, Form, FormActions, FormFieldCheckbox, FormFieldCreditCardNumber, FormFieldCurrencyInput, FormFieldDatePicker, FormFieldInput, FormFieldNumericFormat, FormFieldPassword, FormFieldRadio, FormFieldRadioCard, FormFieldRadioCardIcony, FormFieldSegmentedControl, FormFieldSelect, FormFieldSwitch, FormFieldTextarea, FormGroup, FormProvider, useController, useFieldArray, useForm, useFormContext, useFormGroup, useFormState, useWatch, yup, yupResolver, z, zodResolver } from "./chunk-3M2OR5NH.js";
3
+ import { COMMON_PHONE_COUNTRY_CODES, FormErrorMessage, FormField, FormFieldPatternFormat, FormFieldPhone, MANUAL_PHONE_COUNTRY_CODE } from "./chunk-U7QBQHMU.js";
4
+ export { COMMON_PHONE_COUNTRY_CODES, Controller, Form, FormActions, FormErrorMessage, FormField, FormFieldCheckbox, FormFieldCreditCardNumber, FormFieldCurrencyInput, FormFieldDatePicker, FormFieldInput, FormFieldNumericFormat, FormFieldPassword, FormFieldPatternFormat, FormFieldPhone, FormFieldRadio, FormFieldRadioCard, FormFieldRadioCardIcony, FormFieldSegmentedControl, FormFieldSelect, FormFieldSwitch, FormFieldTextarea, FormGroup, FormProvider, MANUAL_PHONE_COUNTRY_CODE, useController, useFieldArray, useForm, useFormContext, useFormGroup, useFormState, useWatch, yup, yupResolver, z, zodResolver };
package/dist/index.d.ts CHANGED
@@ -1,11 +1,11 @@
1
1
  import * as react_jsx_runtime from 'react/jsx-runtime';
2
- import { BoxProps, CheckboxProps, InputProps, InputPasswordProps, RadioProps, ThemeUIStyleObject, SegmentedControlProps, SelectProps, SwitchProps, TextareaProps } from '@ttoss/ui';
2
+ import { FlexProps, BoxProps, CheckboxProps, InputProps, InputPasswordProps, RadioProps, ThemeUIStyleObject, SegmentedControlProps, SelectProps, SwitchProps, TextareaProps } from '@ttoss/ui';
3
3
  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 './FormFieldPhone-BKeOS0i6.js';
8
- export { f as COMMON_PHONE_COUNTRY_CODES, C as CountryCodeOption, b as FormField, c as FormFieldPatternFormat, e as FormFieldPhone, d as FormFieldPhoneProps, M as MANUAL_PHONE_COUNTRY_CODE } from './FormFieldPhone-BKeOS0i6.js';
7
+ import { F as FormFieldProps, a as FormFieldPatternFormatProps } from './FormFieldPhone--tC0Hlcf.js';
8
+ export { f as COMMON_PHONE_COUNTRY_CODES, C as CountryCodeOption, b as FormField, c as FormFieldPatternFormat, e as FormFieldPhone, d as FormFieldPhoneProps, M as MANUAL_PHONE_COUNTRY_CODE } from './FormFieldPhone--tC0Hlcf.js';
9
9
  import { NumericFormatProps } from 'react-number-format';
10
10
  import { DateRange } from '@ttoss/components/DatePicker';
11
11
  import './typings.d-BzNUo1mD.js';
@@ -16,11 +16,141 @@ export { z };
16
16
  export { yupResolver } from '@hookform/resolvers/yup';
17
17
  export { zodResolver } from '@hookform/resolvers/zod';
18
18
 
19
- declare const Form: <TFieldValues extends FieldValues, TContext = any, TTransformedValues = TFieldValues>({ children, onSubmit, sx, ...formMethods }: {
19
+ declare const alignMap: {
20
+ readonly left: "flex-start";
21
+ readonly center: "center";
22
+ readonly right: "flex-end";
23
+ };
24
+ /**
25
+ * Props for the FormActions component.
26
+ */
27
+ type FormActionsProps = {
28
+ /** Action buttons (Submit, Cancel, Reset, etc.). */
29
+ children: React.ReactNode;
30
+ /**
31
+ * Horizontal alignment of the action buttons.
32
+ * @default 'right'
33
+ */
34
+ align?: keyof typeof alignMap;
35
+ /**
36
+ * When `true`, the action bar sticks to the bottom of the viewport so it
37
+ * remains visible while the user scrolls through a long form.
38
+ * @default false
39
+ */
40
+ sticky?: boolean;
41
+ } & Omit<FlexProps, 'children'>;
42
+ /**
43
+ * FormActions is a layout container for form action buttons such as Submit,
44
+ * Cancel, and Reset. It renders a flex row with consistent spacing.
45
+ *
46
+ * Use `align` to control horizontal button placement (`'left'`, `'center'`,
47
+ * or `'right'`). Use `sticky` to keep the action bar visible while the user
48
+ * scrolls through a long form.
49
+ *
50
+ * @example
51
+ * ```tsx
52
+ * <Form.Actions align="right" sticky>
53
+ * <Button variant="secondary" onClick={onCancel}>Cancel</Button>
54
+ * <Button type="submit">Save</Button>
55
+ * </Form.Actions>
56
+ * ```
57
+ */
58
+ declare const FormActions: ({ align, children, sticky, sx, ...flexProps }: FormActionsProps) => react_jsx_runtime.JSX.Element;
59
+
60
+ declare const useFormGroup: () => {
61
+ level: number | undefined;
62
+ /**
63
+ * @deprecated `levelsLength` has been removed from `FormGroup` internals.
64
+ * This field always returns `undefined`. Use `level` to determine nesting depth.
65
+ */
66
+ levelsLength: number | undefined;
67
+ };
68
+ /**
69
+ * Props for the FormGroup component.
70
+ */
71
+ type FormGroupProps = {
72
+ /**
73
+ * The field name used to display a validation error message below the group.
74
+ * Useful when the group maps to an array or object field in the form schema.
75
+ */
76
+ name?: string;
77
+ /**
78
+ * Optional heading displayed above the group's children.
79
+ */
80
+ title?: string;
81
+ /**
82
+ * Optional description displayed below the title.
83
+ */
84
+ description?: string;
85
+ /**
86
+ * Layout direction for the group's children.
87
+ * @default 'column'
88
+ */
89
+ direction?: 'column' | 'row';
90
+ } & BoxProps;
91
+ /**
92
+ * FormGroup is a layout container that organises form fields into labelled,
93
+ * optionally nested sections. Each nested `FormGroup` increments an internal
94
+ * `level` counter exposed via `useFormGroup`, which drives a `data-level`
95
+ * attribute and top-margin spacing so deeper groups are visually indented.
96
+ *
97
+ * @example
98
+ * ```tsx
99
+ * <FormGroup title="Personal details">
100
+ * <FormFieldInput name="firstName" label="First name" />
101
+ * <FormFieldInput name="lastName" label="Last name" />
102
+ *
103
+ * <FormGroup title="Address" direction="row">
104
+ * <FormFieldInput name="city" label="City" />
105
+ * <FormFieldInput name="zip" label="ZIP" />
106
+ * </FormGroup>
107
+ * </FormGroup>
108
+ * ```
109
+ *
110
+ * @example
111
+ * // Show a group-level validation error (e.g. for an array field)
112
+ * ```tsx
113
+ * <FormGroup name="items" title="Items">
114
+ * {fields.map((field, i) => (
115
+ * <FormFieldInput key={field.id} name={`items[${i}].value`} label="Value" />
116
+ * ))}
117
+ * </FormGroup>
118
+ * ```
119
+ */
120
+ declare const FormGroup: (props: FormGroupProps) => react_jsx_runtime.JSX.Element;
121
+
122
+ /**
123
+ * Form is the root component for all form compositions. It wraps
124
+ * `react-hook-form`'s `FormProvider` and an HTML `<form>` element,
125
+ * forwarding submission handling.
126
+ *
127
+ * Use the compound sub-components for structure:
128
+ * - `Form.Group` – groups related fields with optional title/description
129
+ * - `Form.Actions` – footer bar for Submit / Cancel / Reset buttons
130
+ *
131
+ * @example
132
+ * ```tsx
133
+ * const methods = useForm<FormValues>();
134
+ *
135
+ * <Form {...methods} onSubmit={handleSubmit}>
136
+ * <Form.Group title="Personal details">
137
+ * <FormFieldInput name="firstName" label="First name" />
138
+ * </Form.Group>
139
+ * <Form.Actions>
140
+ * <Button variant="secondary" onClick={onCancel}>Cancel</Button>
141
+ * <Button type="submit">Save</Button>
142
+ * </Form.Actions>
143
+ * </Form>
144
+ * ```
145
+ */
146
+ declare const Form: (<TFieldValues extends FieldValues, TContext = any, TTransformedValues = TFieldValues>({ children, onSubmit, sx, ...formMethods }: {
20
147
  children?: React.ReactNode;
21
148
  onSubmit?: (data: TTransformedValues) => Promise<void> | void;
22
149
  sx?: BoxProps["sx"];
23
- } & FormProviderProps<TFieldValues, TContext, TTransformedValues>) => react_jsx_runtime.JSX.Element;
150
+ } & FormProviderProps<TFieldValues, TContext, TTransformedValues>) => react_jsx_runtime.JSX.Element) & {
151
+ Group: (props: FormGroupProps) => react_jsx_runtime.JSX.Element;
152
+ Actions: ({ align, children, sticky, sx, ...flexProps }: FormActionsProps) => react_jsx_runtime.JSX.Element;
153
+ };
24
154
 
25
155
  declare const FormErrorMessage: <TFieldValues extends FieldValues = FieldValues>({ name, disabled, }: {
26
156
  name: FieldName<TFieldValues>;
@@ -112,66 +242,4 @@ declare const FormFieldSwitch: <TFieldValues extends FieldValues = FieldValues,
112
242
  type FormFieldTextareaProps<TFieldValues extends FieldValues = FieldValues, TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>> = FormFieldProps<TFieldValues, TName> & Omit<TextareaProps, 'name'>;
113
243
  declare const FormFieldTextarea: <TFieldValues extends FieldValues = FieldValues, TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>>({ defaultValue, disabled, ...props }: FormFieldTextareaProps<TFieldValues, TName>) => react_jsx_runtime.JSX.Element;
114
244
 
115
- declare const useFormGroup: () => {
116
- level: number | undefined;
117
- /**
118
- * @deprecated `levelsLength` has been removed from `FormGroup` internals.
119
- * This field always returns `undefined`. Use `level` to determine nesting depth.
120
- */
121
- levelsLength: number | undefined;
122
- };
123
- /**
124
- * Props for the FormGroup component.
125
- */
126
- type FormGroupProps = {
127
- /**
128
- * The field name used to display a validation error message below the group.
129
- * Useful when the group maps to an array or object field in the form schema.
130
- */
131
- name?: string;
132
- /**
133
- * Optional heading displayed above the group's children.
134
- */
135
- title?: string;
136
- /**
137
- * Optional description displayed below the title.
138
- */
139
- description?: string;
140
- /**
141
- * Layout direction for the group's children.
142
- * @default 'column'
143
- */
144
- direction?: 'column' | 'row';
145
- } & BoxProps;
146
- /**
147
- * FormGroup is a layout container that organises form fields into labelled,
148
- * optionally nested sections. Each nested `FormGroup` increments an internal
149
- * `level` counter exposed via `useFormGroup`, which drives a `data-level`
150
- * attribute and top-margin spacing so deeper groups are visually indented.
151
- *
152
- * @example
153
- * ```tsx
154
- * <FormGroup title="Personal details">
155
- * <FormFieldInput name="firstName" label="First name" />
156
- * <FormFieldInput name="lastName" label="Last name" />
157
- *
158
- * <FormGroup title="Address" direction="row">
159
- * <FormFieldInput name="city" label="City" />
160
- * <FormFieldInput name="zip" label="ZIP" />
161
- * </FormGroup>
162
- * </FormGroup>
163
- * ```
164
- *
165
- * @example
166
- * // Show a group-level validation error (e.g. for an array field)
167
- * ```tsx
168
- * <FormGroup name="items" title="Items">
169
- * {fields.map((field, i) => (
170
- * <FormFieldInput key={field.id} name={`items[${i}].value`} label="Value" />
171
- * ))}
172
- * </FormGroup>
173
- * ```
174
- */
175
- declare const FormGroup: (props: FormGroupProps) => react_jsx_runtime.JSX.Element;
176
-
177
- export { type DateRangePreset, Form, FormErrorMessage, FormFieldCheckbox, FormFieldCreditCardNumber, FormFieldCurrencyInput, FormFieldDatePicker, FormFieldInput, FormFieldNumericFormat, FormFieldPassword, FormFieldProps, FormFieldRadio, FormFieldRadioCard, FormFieldRadioCardIcony, FormFieldSegmentedControl, FormFieldSelect, FormFieldSwitch, FormFieldTextarea, FormGroup, type FormRadioOption, useFormGroup };
245
+ export { type DateRangePreset, Form, FormActions, type FormActionsProps, FormErrorMessage, FormFieldCheckbox, FormFieldCreditCardNumber, FormFieldCurrencyInput, FormFieldDatePicker, FormFieldInput, FormFieldNumericFormat, FormFieldPassword, FormFieldProps, FormFieldRadio, FormFieldRadioCard, FormFieldRadioCardIcony, FormFieldSegmentedControl, FormFieldSelect, FormFieldSwitch, FormFieldTextarea, FormGroup, type FormRadioOption, useFormGroup };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ttoss/forms",
3
- "version": "0.42.0",
3
+ "version": "0.43.1",
4
4
  "license": "MIT",
5
5
  "author": "ttoss",
6
6
  "contributors": [
@@ -52,13 +52,13 @@
52
52
  "react-error-boundary": "^6.1.0",
53
53
  "tsup": "^8.5.1",
54
54
  "yup": "^1.7.1",
55
+ "@ttoss/config": "^1.36.0",
55
56
  "@ttoss/components": "^2.13.2",
56
57
  "@ttoss/i18n-cli": "^0.7.39",
57
- "@ttoss/react-i18n": "^2.1.0",
58
58
  "@ttoss/react-icons": "^0.6.0",
59
59
  "@ttoss/test-utils": "^4.1.0",
60
- "@ttoss/ui": "^6.8.0",
61
- "@ttoss/config": "^1.36.0"
60
+ "@ttoss/react-i18n": "^2.1.0",
61
+ "@ttoss/ui": "^6.8.0"
62
62
  },
63
63
  "publishConfig": {
64
64
  "access": "public",