@ticketmaster/tm-global-address 0.6.0 → 0.8.0
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/dist/components/AddressCard/AddressCard.d.ts +5 -0
- package/dist/components/AddressCard/AddressCard.js +45 -46
- package/dist/components/AddressForm/AddressForm.d.ts +14 -2
- package/dist/components/AddressForm/AddressForm.js +216 -212
- package/dist/components/AddressList/AddressList.d.ts +7 -2
- package/dist/components/AddressList/AddressList.js +41 -37
- package/dist/hooks/index.d.ts +10 -0
- package/dist/hooks/useAddressForm.d.ts +76 -0
- package/dist/hooks/useAddressForm.js +174 -0
- package/dist/hooks/useAddressFormRHF.d.ts +8 -0
- package/dist/hooks/useAddressFormRHF.js +174 -0
- package/dist/i18n/defaultLabels.d.ts +2 -0
- package/dist/i18n/defaultLabels.js +66 -0
- package/dist/i18n/index.d.ts +14 -0
- package/dist/i18n/index.js +45 -0
- package/dist/i18n/labelResolver.d.ts +24 -0
- package/dist/i18n/labelResolver.js +98 -0
- package/dist/i18n/locales/ar-sa.json.d.ts +159 -0
- package/dist/i18n/locales/ar-sa.json.js +163 -0
- package/dist/i18n/locales/ca-es.json.d.ts +159 -0
- package/dist/i18n/locales/ca-es.json.js +163 -0
- package/dist/i18n/locales/cs-cz.json.d.ts +159 -0
- package/dist/i18n/locales/cs-cz.json.js +163 -0
- package/dist/i18n/locales/da-dk.json.d.ts +159 -0
- package/dist/i18n/locales/da-dk.json.js +163 -0
- package/dist/i18n/locales/de-at.json.d.ts +159 -0
- package/dist/i18n/locales/de-at.json.js +163 -0
- package/dist/i18n/locales/de-ch.json.d.ts +159 -0
- package/dist/i18n/locales/de-ch.json.js +163 -0
- package/dist/i18n/locales/de-de.json.d.ts +159 -0
- package/dist/i18n/locales/de-de.json.js +163 -0
- package/dist/i18n/locales/en-au.json.d.ts +159 -0
- package/dist/i18n/locales/en-au.json.js +163 -0
- package/dist/i18n/locales/en-ca.json.d.ts +159 -0
- package/dist/i18n/locales/en-ca.json.js +163 -0
- package/dist/i18n/locales/en-gb.json.d.ts +159 -0
- package/dist/i18n/locales/en-gb.json.js +163 -0
- package/dist/i18n/locales/en-nz.json.d.ts +159 -0
- package/dist/i18n/locales/en-nz.json.js +163 -0
- package/dist/i18n/locales/en-us.json.d.ts +159 -0
- package/dist/i18n/locales/en-us.json.js +163 -0
- package/dist/i18n/locales/es-es.json.d.ts +159 -0
- package/dist/i18n/locales/es-es.json.js +163 -0
- package/dist/i18n/locales/es-mx.json.d.ts +159 -0
- package/dist/i18n/locales/es-mx.json.js +163 -0
- package/dist/i18n/locales/fi-fi.json.d.ts +159 -0
- package/dist/i18n/locales/fi-fi.json.js +163 -0
- package/dist/i18n/locales/fr-be.json.d.ts +159 -0
- package/dist/i18n/locales/fr-be.json.js +163 -0
- package/dist/i18n/locales/fr-ca.json.d.ts +159 -0
- package/dist/i18n/locales/fr-ca.json.js +163 -0
- package/dist/i18n/locales/fr-ch.json.d.ts +159 -0
- package/dist/i18n/locales/fr-ch.json.js +163 -0
- package/dist/i18n/locales/fr-fr.json.d.ts +159 -0
- package/dist/i18n/locales/fr-fr.json.js +163 -0
- package/dist/i18n/locales/it-ch.json.d.ts +159 -0
- package/dist/i18n/locales/it-ch.json.js +163 -0
- package/dist/i18n/locales/ja-jp.json.d.ts +159 -0
- package/dist/i18n/locales/ja-jp.json.js +163 -0
- package/dist/i18n/locales/nl-be.json.d.ts +159 -0
- package/dist/i18n/locales/nl-be.json.js +163 -0
- package/dist/i18n/locales/nl-nl.json.d.ts +159 -0
- package/dist/i18n/locales/nl-nl.json.js +163 -0
- package/dist/i18n/locales/no-no.json.d.ts +159 -0
- package/dist/i18n/locales/no-no.json.js +163 -0
- package/dist/i18n/locales/pl-pl.json.d.ts +159 -0
- package/dist/i18n/locales/pl-pl.json.js +163 -0
- package/dist/i18n/locales/sv-se.json.d.ts +159 -0
- package/dist/i18n/locales/sv-se.json.js +163 -0
- package/dist/i18n/types.d.ts +67 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +50 -38
- package/package.json +2 -1
|
@@ -3,8 +3,8 @@ import { AddressData } from '../../types/AddressData';
|
|
|
3
3
|
export interface AddressListProps {
|
|
4
4
|
addresses: AddressData[];
|
|
5
5
|
selectedAddressId?: number;
|
|
6
|
-
/** Section label above the list */
|
|
7
|
-
addressLabel
|
|
6
|
+
/** Section label above the list (overrides i18n when provided) */
|
|
7
|
+
addressLabel?: string;
|
|
8
8
|
maxAddresses: number;
|
|
9
9
|
/** Called when user selects an address from the list */
|
|
10
10
|
onSelect: (address: AddressData) => void;
|
|
@@ -19,6 +19,11 @@ export interface AddressListProps {
|
|
|
19
19
|
disabled?: boolean;
|
|
20
20
|
/** Per-card error messages, keyed by addressId */
|
|
21
21
|
cardErrors?: Record<number, string>;
|
|
22
|
+
/**
|
|
23
|
+
* BCP-47 locale tag. Controls button labels.
|
|
24
|
+
* Defaults to "en-us".
|
|
25
|
+
*/
|
|
26
|
+
locale?: string;
|
|
22
27
|
}
|
|
23
28
|
/**
|
|
24
29
|
* AddressList — the LIST (expanded dropdown) state.
|
|
@@ -1,22 +1,23 @@
|
|
|
1
1
|
import { jsxs as n, jsx as o } from "react/jsx-runtime";
|
|
2
2
|
import r from "styled-components";
|
|
3
3
|
import { tokens as e } from "../../styles/tokens.js";
|
|
4
|
-
import { AddressCard as
|
|
5
|
-
|
|
4
|
+
import { AddressCard as y } from "../AddressCard/AddressCard.js";
|
|
5
|
+
import { useLabels as v } from "../../i18n/index.js";
|
|
6
|
+
const L = r.div`
|
|
6
7
|
display: flex;
|
|
7
8
|
flex-direction: column;
|
|
8
9
|
gap: ${e.space2};
|
|
9
|
-
`,
|
|
10
|
+
`, k = r.div`
|
|
10
11
|
display: flex;
|
|
11
12
|
align-items: center;
|
|
12
13
|
justify-content: space-between;
|
|
13
14
|
margin-block-end: ${e.space2};
|
|
14
|
-
`,
|
|
15
|
+
`, I = r.p`
|
|
15
16
|
margin: 0;
|
|
16
17
|
font-size: ${e.fontSizeLabel};
|
|
17
18
|
font-weight: ${e.fontWeightBold};
|
|
18
19
|
color: ${e.colorTextPrimary};
|
|
19
|
-
`,
|
|
20
|
+
`, S = r.button`
|
|
20
21
|
appearance: none;
|
|
21
22
|
background: none;
|
|
22
23
|
border: none;
|
|
@@ -36,16 +37,16 @@ const h = r.div`
|
|
|
36
37
|
outline-offset: 2px;
|
|
37
38
|
border-radius: 2px;
|
|
38
39
|
}
|
|
39
|
-
`,
|
|
40
|
+
`, B = r.ul`
|
|
40
41
|
list-style: none;
|
|
41
42
|
margin: 0;
|
|
42
43
|
padding: 0;
|
|
43
44
|
display: flex;
|
|
44
45
|
flex-direction: column;
|
|
45
46
|
gap: ${e.space3};
|
|
46
|
-
`,
|
|
47
|
+
`, z = r.li``, C = r.div`
|
|
47
48
|
margin-block-start: ${e.space3};
|
|
48
|
-
`,
|
|
49
|
+
`, w = r.button`
|
|
49
50
|
appearance: none;
|
|
50
51
|
background: none;
|
|
51
52
|
border: none;
|
|
@@ -75,7 +76,7 @@ const h = r.div`
|
|
|
75
76
|
cursor: not-allowed;
|
|
76
77
|
text-decoration: none;
|
|
77
78
|
}
|
|
78
|
-
`,
|
|
79
|
+
`, P = r.div`
|
|
79
80
|
display: flex;
|
|
80
81
|
align-items: flex-start;
|
|
81
82
|
gap: ${e.space2};
|
|
@@ -86,64 +87,67 @@ const h = r.div`
|
|
|
86
87
|
font-size: ${e.fontSizeBody};
|
|
87
88
|
color: ${e.colorTextSecondary};
|
|
88
89
|
margin-block-start: ${e.space3};
|
|
89
|
-
`,
|
|
90
|
+
`, M = ({
|
|
90
91
|
addresses: a,
|
|
91
|
-
selectedAddressId:
|
|
92
|
-
addressLabel:
|
|
93
|
-
maxAddresses:
|
|
94
|
-
onSelect:
|
|
95
|
-
onEdit:
|
|
96
|
-
onDelete:
|
|
97
|
-
onAdd:
|
|
98
|
-
onCollapse:
|
|
92
|
+
selectedAddressId: c,
|
|
93
|
+
addressLabel: p,
|
|
94
|
+
maxAddresses: u,
|
|
95
|
+
onSelect: f,
|
|
96
|
+
onEdit: m,
|
|
97
|
+
onDelete: b,
|
|
98
|
+
onAdd: x,
|
|
99
|
+
onCollapse: $,
|
|
99
100
|
disabled: i = !1,
|
|
100
|
-
cardErrors:
|
|
101
|
+
cardErrors: g = {},
|
|
102
|
+
locale: d = "en-us"
|
|
101
103
|
}) => {
|
|
102
|
-
const
|
|
103
|
-
return /* @__PURE__ */ n(
|
|
104
|
-
/* @__PURE__ */ n(
|
|
105
|
-
/* @__PURE__ */ o(
|
|
104
|
+
const { form: s } = v(d, ""), l = p ?? s.addressHeading, h = a.length >= u;
|
|
105
|
+
return /* @__PURE__ */ n(L, { children: [
|
|
106
|
+
/* @__PURE__ */ n(k, { children: [
|
|
107
|
+
/* @__PURE__ */ o(I, { children: l }),
|
|
106
108
|
/* @__PURE__ */ o(
|
|
107
|
-
|
|
109
|
+
S,
|
|
108
110
|
{
|
|
109
111
|
type: "button",
|
|
110
|
-
onClick:
|
|
112
|
+
onClick: $,
|
|
111
113
|
disabled: i,
|
|
112
114
|
"aria-label": "Collapse address list",
|
|
113
115
|
children: "▲"
|
|
114
116
|
}
|
|
115
117
|
)
|
|
116
118
|
] }),
|
|
117
|
-
/* @__PURE__ */ o(
|
|
118
|
-
|
|
119
|
+
/* @__PURE__ */ o(B, { role: "listbox", "aria-label": l, children: a.map((t) => /* @__PURE__ */ o(z, { children: /* @__PURE__ */ o(
|
|
120
|
+
y,
|
|
119
121
|
{
|
|
120
122
|
address: t,
|
|
121
|
-
isSelected: t.addressId ===
|
|
122
|
-
onSelect:
|
|
123
|
-
onEdit:
|
|
124
|
-
onDelete:
|
|
123
|
+
isSelected: t.addressId === c,
|
|
124
|
+
onSelect: f,
|
|
125
|
+
onEdit: m,
|
|
126
|
+
onDelete: b,
|
|
125
127
|
disabled: i,
|
|
126
|
-
errorMessage:
|
|
128
|
+
errorMessage: g[t.addressId],
|
|
129
|
+
locale: d
|
|
127
130
|
}
|
|
128
131
|
) }, t.addressId)) }),
|
|
129
|
-
/* @__PURE__ */ o(
|
|
132
|
+
/* @__PURE__ */ o(C, { children: h ? /* @__PURE__ */ n(P, { role: "status", children: [
|
|
130
133
|
/* @__PURE__ */ o("span", { "aria-hidden": "true", children: "ℹ" }),
|
|
131
134
|
/* @__PURE__ */ o("span", { children: "You've reached the maximum number of addresses. Please edit or remove one to add another." })
|
|
132
135
|
] }) : /* @__PURE__ */ n(
|
|
133
|
-
|
|
136
|
+
w,
|
|
134
137
|
{
|
|
135
138
|
type: "button",
|
|
136
139
|
disabled: i,
|
|
137
|
-
onClick:
|
|
140
|
+
onClick: x,
|
|
138
141
|
"data-testid": "add-new-address",
|
|
139
142
|
children: [
|
|
140
143
|
/* @__PURE__ */ o("span", { "aria-hidden": "true", children: "+" }),
|
|
141
|
-
"
|
|
144
|
+
" ",
|
|
145
|
+
s.addNewAddress
|
|
142
146
|
]
|
|
143
147
|
}
|
|
144
148
|
) })
|
|
145
149
|
] });
|
|
146
150
|
};
|
|
147
151
|
export {
|
|
148
|
-
|
|
152
|
+
M as AddressList
|
|
149
153
|
};
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Phase 7: Form State & Hooks
|
|
3
|
+
*
|
|
4
|
+
* Exports:
|
|
5
|
+
* - useAddressForm: Standalone form state hook (no external dependencies)
|
|
6
|
+
* - useAddressFormRHF: react-hook-form adapter (same interface, different engine)
|
|
7
|
+
*/
|
|
8
|
+
export { useAddressForm } from './useAddressForm';
|
|
9
|
+
export type { UseAddressFormConfig, UseAddressFormReturn, AddressFormValues, } from './useAddressForm';
|
|
10
|
+
export { useAddressFormRHF } from './useAddressFormRHF';
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { SaveAddressPayload, FieldError } from '../types/AddressData';
|
|
2
|
+
import { CountryValidationConfig } from '../types/CountryConfig';
|
|
3
|
+
import { ValidationError, FormValidationResult } from '../validation/types';
|
|
4
|
+
/** Configuration for the useAddressForm hook */
|
|
5
|
+
export interface UseAddressFormConfig {
|
|
6
|
+
/** Initial values to pre-populate the form (e.g. when editing an existing address) */
|
|
7
|
+
initialValues?: Partial<SaveAddressPayload>;
|
|
8
|
+
/** Country validation config. If omitted, derived from initialValues.country or defaults to empty US-fallback. */
|
|
9
|
+
countryConfig?: CountryValidationConfig;
|
|
10
|
+
/** Show phone number field */
|
|
11
|
+
showPhoneNumber?: boolean;
|
|
12
|
+
/** Called on successful form submission with the validated payload */
|
|
13
|
+
onSubmit?: (values: SaveAddressPayload) => void;
|
|
14
|
+
/** Called whenever a field value changes */
|
|
15
|
+
onChange?: (field: string, value: string | boolean) => void;
|
|
16
|
+
/** Called whenever validation runs (onBlur or onSubmit) */
|
|
17
|
+
onValidate?: (errors: ValidationError[]) => void;
|
|
18
|
+
/** Called when the country field changes (before state reset) */
|
|
19
|
+
onCountryChange?: (country: string) => void;
|
|
20
|
+
}
|
|
21
|
+
/** Internal form value map — all strings for text fields, boolean for defaultAddress */
|
|
22
|
+
export interface AddressFormValues {
|
|
23
|
+
firstName: string;
|
|
24
|
+
lastName: string;
|
|
25
|
+
country: string;
|
|
26
|
+
address1: string;
|
|
27
|
+
address2: string;
|
|
28
|
+
city: string;
|
|
29
|
+
postal: string;
|
|
30
|
+
state: string;
|
|
31
|
+
phoneNumber: string;
|
|
32
|
+
defaultAddress: boolean;
|
|
33
|
+
}
|
|
34
|
+
/** The full return value of useAddressForm */
|
|
35
|
+
export interface UseAddressFormReturn {
|
|
36
|
+
/** Current form values */
|
|
37
|
+
values: AddressFormValues;
|
|
38
|
+
/** Field-level validation errors (inline display) */
|
|
39
|
+
errors: Record<string, ValidationError>;
|
|
40
|
+
/** Non-field server errors (AlertBox banner display) */
|
|
41
|
+
nonFieldErrors: ValidationError[];
|
|
42
|
+
/** Which fields have been touched (focused then blurred) */
|
|
43
|
+
touched: Record<string, boolean>;
|
|
44
|
+
/** Whether the form passes full validation */
|
|
45
|
+
isValid: boolean;
|
|
46
|
+
/** Whether any field differs from initialValues */
|
|
47
|
+
isDirty: boolean;
|
|
48
|
+
/** Update a single field value */
|
|
49
|
+
setValue: (field: string, value: string | boolean) => void;
|
|
50
|
+
/** Update multiple field values at once (e.g. pre-populate from API) */
|
|
51
|
+
setValues: (values: Partial<AddressFormValues>) => void;
|
|
52
|
+
/** Mark a field as touched and run its blur validation */
|
|
53
|
+
handleBlur: (field: string) => void;
|
|
54
|
+
/** Run full form validation and return the result */
|
|
55
|
+
handleSubmit: () => FormValidationResult;
|
|
56
|
+
/** Reset form to initial values (or provided values) */
|
|
57
|
+
reset: (values?: Partial<AddressFormValues>) => void;
|
|
58
|
+
/** Inject server errors from a failed onSave response */
|
|
59
|
+
setServerErrors: (errors: FieldError[]) => void;
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* useAddressForm — standalone address form state management hook.
|
|
63
|
+
*
|
|
64
|
+
* @example
|
|
65
|
+
* ```tsx
|
|
66
|
+
* const form = useAddressForm({
|
|
67
|
+
* initialValues: { country: 'US' },
|
|
68
|
+
* showPhoneNumber: true,
|
|
69
|
+
* onChange: (field, value) => console.log(field, value),
|
|
70
|
+
* });
|
|
71
|
+
*
|
|
72
|
+
* // In your form:
|
|
73
|
+
* <input value={form.values.firstName} onChange={e => form.setValue('firstName', e.target.value)} />
|
|
74
|
+
* ```
|
|
75
|
+
*/
|
|
76
|
+
export declare function useAddressForm(config?: UseAddressFormConfig): UseAddressFormReturn;
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
import { useRef as Q, useState as F, useCallback as N } from "react";
|
|
2
|
+
import { getCountryConfig as U } from "../data/countryData.js";
|
|
3
|
+
import { FIELD_CONSTRAINTS as g } from "../validation/constants.js";
|
|
4
|
+
import { validateForm as q } from "../validation/validateForm.js";
|
|
5
|
+
import { validatePhoneNumber as B } from "../validation/validatePhoneNumber.js";
|
|
6
|
+
import { validateState as V } from "../validation/validateState.js";
|
|
7
|
+
import { validatePostalCode as z } from "../validation/validatePostalCode.js";
|
|
8
|
+
import { validateTextField as b } from "../validation/validateTextField.js";
|
|
9
|
+
function I(e) {
|
|
10
|
+
return {
|
|
11
|
+
firstName: (e == null ? void 0 : e.firstName) ?? "",
|
|
12
|
+
lastName: (e == null ? void 0 : e.lastName) ?? "",
|
|
13
|
+
country: (e == null ? void 0 : e.country) ?? "",
|
|
14
|
+
address1: (e == null ? void 0 : e.address1) ?? "",
|
|
15
|
+
address2: (e == null ? void 0 : e.address2) ?? "",
|
|
16
|
+
city: (e == null ? void 0 : e.city) ?? "",
|
|
17
|
+
postal: (e == null ? void 0 : e.postal) ?? "",
|
|
18
|
+
state: (e == null ? void 0 : e.state) ?? "",
|
|
19
|
+
phoneNumber: (e == null ? void 0 : e.phoneNumber) ?? "",
|
|
20
|
+
defaultAddress: (e == null ? void 0 : e.defaultAddress) ?? !1
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
function G(e, c) {
|
|
24
|
+
return Object.keys(e).some(
|
|
25
|
+
(n) => e[n] !== c[n]
|
|
26
|
+
);
|
|
27
|
+
}
|
|
28
|
+
function H(e, c, n, y) {
|
|
29
|
+
const s = c[e];
|
|
30
|
+
switch (e) {
|
|
31
|
+
case "firstName":
|
|
32
|
+
return b("firstName", String(s), g.firstName);
|
|
33
|
+
case "lastName":
|
|
34
|
+
return b("lastName", String(s), g.lastName);
|
|
35
|
+
case "address1":
|
|
36
|
+
return b("address1", String(s), g.address1);
|
|
37
|
+
case "address2":
|
|
38
|
+
return !s || String(s) === "" ? null : b("address2", String(s), g.address2);
|
|
39
|
+
case "city":
|
|
40
|
+
return b("city", String(s), g.city);
|
|
41
|
+
case "postal":
|
|
42
|
+
return !s || String(s) === "" ? null : z(String(s), g.postal, n);
|
|
43
|
+
case "state":
|
|
44
|
+
return V(s ? String(s) : void 0, n);
|
|
45
|
+
case "phoneNumber":
|
|
46
|
+
return !y || !s || String(s) === "" ? null : B(String(s), n);
|
|
47
|
+
case "country":
|
|
48
|
+
return !s || String(s) === "" ? {
|
|
49
|
+
field: "country",
|
|
50
|
+
code: "FIELD_REQUIRED",
|
|
51
|
+
message: "country is required",
|
|
52
|
+
metadata: { field: "country" }
|
|
53
|
+
} : null;
|
|
54
|
+
default:
|
|
55
|
+
return null;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
function i(e = {}) {
|
|
59
|
+
const {
|
|
60
|
+
initialValues: c,
|
|
61
|
+
showPhoneNumber: n = !1,
|
|
62
|
+
onSubmit: y,
|
|
63
|
+
onChange: s,
|
|
64
|
+
onValidate: m,
|
|
65
|
+
onCountryChange: h
|
|
66
|
+
} = e, A = Q(I(c)), [o, l] = F(
|
|
67
|
+
() => I(c)
|
|
68
|
+
), [S, f] = F({}), [D, O] = F([]), [R, E] = F({}), v = e.countryConfig ?? U(o.country), _ = N(
|
|
69
|
+
(t, d) => {
|
|
70
|
+
if (t === "country") {
|
|
71
|
+
const u = String(d);
|
|
72
|
+
h == null || h(u), l((r) => ({
|
|
73
|
+
...r,
|
|
74
|
+
country: u,
|
|
75
|
+
state: ""
|
|
76
|
+
})), f((r) => {
|
|
77
|
+
const { country: a, state: p, ...x } = r;
|
|
78
|
+
return x;
|
|
79
|
+
});
|
|
80
|
+
} else
|
|
81
|
+
l((u) => ({ ...u, [t]: d })), f((u) => {
|
|
82
|
+
const { [t]: r, ...a } = u;
|
|
83
|
+
return a;
|
|
84
|
+
});
|
|
85
|
+
s == null || s(t, d);
|
|
86
|
+
},
|
|
87
|
+
[s, h]
|
|
88
|
+
), j = N((t) => {
|
|
89
|
+
l((d) => ({ ...d, ...t }));
|
|
90
|
+
}, []), T = N(
|
|
91
|
+
(t) => {
|
|
92
|
+
if (E((r) => ({ ...r, [t]: !0 })), typeof o[t] == "boolean") return;
|
|
93
|
+
const u = H(t, o, v, n);
|
|
94
|
+
if (f((r) => {
|
|
95
|
+
if (u) return { ...r, [t]: u };
|
|
96
|
+
const { [t]: a, ...p } = r;
|
|
97
|
+
return p;
|
|
98
|
+
}), m) {
|
|
99
|
+
const r = u ? { ...S, [t]: u } : Object.fromEntries(Object.entries(S).filter(([a]) => a !== t));
|
|
100
|
+
m(Object.values(r));
|
|
101
|
+
}
|
|
102
|
+
},
|
|
103
|
+
[o, v, n, S, m]
|
|
104
|
+
), C = N(() => {
|
|
105
|
+
const t = {
|
|
106
|
+
mode: (c == null ? void 0 : c.mode) ?? "create",
|
|
107
|
+
...(c == null ? void 0 : c.mode) === "edit" && (c == null ? void 0 : c.addressId) !== void 0 ? { addressId: c.addressId } : {},
|
|
108
|
+
defaultAddress: o.defaultAddress,
|
|
109
|
+
firstName: o.firstName.trim(),
|
|
110
|
+
lastName: o.lastName.trim(),
|
|
111
|
+
address1: o.address1.trim(),
|
|
112
|
+
...o.address2.trim() ? { address2: o.address2.trim() } : {},
|
|
113
|
+
city: o.city.trim(),
|
|
114
|
+
postal: o.postal.trim(),
|
|
115
|
+
country: o.country,
|
|
116
|
+
...o.state ? { state: o.state } : {},
|
|
117
|
+
...n && o.phoneNumber ? { phoneNumber: o.phoneNumber.trim() } : {}
|
|
118
|
+
}, d = q(t, v, { showPhoneNumber: n });
|
|
119
|
+
if (d.valid)
|
|
120
|
+
f({}), y == null || y(t);
|
|
121
|
+
else {
|
|
122
|
+
const u = {};
|
|
123
|
+
for (const r of d.errors)
|
|
124
|
+
u[r.field] = r;
|
|
125
|
+
f(u), E((r) => {
|
|
126
|
+
const a = { ...r };
|
|
127
|
+
for (const p of d.errors)
|
|
128
|
+
a[p.field] = !0;
|
|
129
|
+
return a;
|
|
130
|
+
}), m == null || m(d.errors);
|
|
131
|
+
}
|
|
132
|
+
return d;
|
|
133
|
+
}, [o, v, n, c, y, m]), k = N((t) => {
|
|
134
|
+
const d = t ? I(t) : { ...A.current };
|
|
135
|
+
l(d), f({}), O([]), E({});
|
|
136
|
+
}, []), w = N((t) => {
|
|
137
|
+
const d = {}, u = [];
|
|
138
|
+
for (const r of t)
|
|
139
|
+
r.field && r.field !== "" ? d[r.field] = {
|
|
140
|
+
field: r.field,
|
|
141
|
+
code: r.code,
|
|
142
|
+
message: r.message,
|
|
143
|
+
metadata: r.metadata
|
|
144
|
+
} : u.push({
|
|
145
|
+
field: "",
|
|
146
|
+
code: r.code || "FIELD_REQUIRED",
|
|
147
|
+
message: r.message,
|
|
148
|
+
metadata: r.metadata
|
|
149
|
+
});
|
|
150
|
+
f((r) => ({ ...r, ...d })), O(u), E((r) => {
|
|
151
|
+
const a = { ...r };
|
|
152
|
+
for (const p of Object.keys(d))
|
|
153
|
+
a[p] = !0;
|
|
154
|
+
return a;
|
|
155
|
+
});
|
|
156
|
+
}, []), L = Object.keys(S).length === 0 && D.length === 0, P = G(o, A.current);
|
|
157
|
+
return {
|
|
158
|
+
values: o,
|
|
159
|
+
errors: S,
|
|
160
|
+
nonFieldErrors: D,
|
|
161
|
+
touched: R,
|
|
162
|
+
isValid: L,
|
|
163
|
+
isDirty: P,
|
|
164
|
+
setValue: _,
|
|
165
|
+
setValues: j,
|
|
166
|
+
handleBlur: T,
|
|
167
|
+
handleSubmit: C,
|
|
168
|
+
reset: k,
|
|
169
|
+
setServerErrors: w
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
export {
|
|
173
|
+
i as useAddressForm
|
|
174
|
+
};
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { UseAddressFormConfig, UseAddressFormReturn } from './useAddressForm';
|
|
2
|
+
/**
|
|
3
|
+
* useAddressFormRHF — react-hook-form-backed address form state hook.
|
|
4
|
+
*
|
|
5
|
+
* Produces the same UseAddressFormReturn as useAddressForm so consuming
|
|
6
|
+
* components are agnostic to which hook is used.
|
|
7
|
+
*/
|
|
8
|
+
export declare function useAddressFormRHF(config?: UseAddressFormConfig): UseAddressFormReturn;
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
import { useCallback as N } from "react";
|
|
2
|
+
import { useForm as Q } from "react-hook-form";
|
|
3
|
+
import { getCountryConfig as C } from "../data/countryData.js";
|
|
4
|
+
import { FIELD_CONSTRAINTS as S } from "../validation/constants.js";
|
|
5
|
+
import { validateForm as U } from "../validation/validateForm.js";
|
|
6
|
+
import { validatePhoneNumber as k } from "../validation/validatePhoneNumber.js";
|
|
7
|
+
import { validateState as x } from "../validation/validateState.js";
|
|
8
|
+
import { validatePostalCode as B } from "../validation/validatePostalCode.js";
|
|
9
|
+
import { validateTextField as l } from "../validation/validateTextField.js";
|
|
10
|
+
function q(f, r, o, u) {
|
|
11
|
+
const t = r[f];
|
|
12
|
+
switch (f) {
|
|
13
|
+
case "firstName":
|
|
14
|
+
return l("firstName", String(t), S.firstName);
|
|
15
|
+
case "lastName":
|
|
16
|
+
return l("lastName", String(t), S.lastName);
|
|
17
|
+
case "address1":
|
|
18
|
+
return l("address1", String(t), S.address1);
|
|
19
|
+
case "address2":
|
|
20
|
+
return !t || String(t) === "" ? null : l("address2", String(t), S.address2);
|
|
21
|
+
case "city":
|
|
22
|
+
return l("city", String(t), S.city);
|
|
23
|
+
case "postal":
|
|
24
|
+
return !t || String(t) === "" ? null : B(String(t), S.postal, o);
|
|
25
|
+
case "state":
|
|
26
|
+
return x(t ? String(t) : void 0, o);
|
|
27
|
+
case "phoneNumber":
|
|
28
|
+
return !u || !t || String(t) === "" ? null : k(String(t), o);
|
|
29
|
+
case "country":
|
|
30
|
+
return !t || String(t) === "" ? {
|
|
31
|
+
field: "country",
|
|
32
|
+
code: "FIELD_REQUIRED",
|
|
33
|
+
message: "country is required",
|
|
34
|
+
metadata: { field: "country" }
|
|
35
|
+
} : null;
|
|
36
|
+
default:
|
|
37
|
+
return null;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
function Z(f = {}) {
|
|
41
|
+
const {
|
|
42
|
+
initialValues: r,
|
|
43
|
+
showPhoneNumber: o = !1,
|
|
44
|
+
onSubmit: u,
|
|
45
|
+
onChange: t,
|
|
46
|
+
onValidate: c,
|
|
47
|
+
onCountryChange: h
|
|
48
|
+
} = f, b = {
|
|
49
|
+
firstName: (r == null ? void 0 : r.firstName) ?? "",
|
|
50
|
+
lastName: (r == null ? void 0 : r.lastName) ?? "",
|
|
51
|
+
country: (r == null ? void 0 : r.country) ?? "",
|
|
52
|
+
address1: (r == null ? void 0 : r.address1) ?? "",
|
|
53
|
+
address2: (r == null ? void 0 : r.address2) ?? "",
|
|
54
|
+
city: (r == null ? void 0 : r.city) ?? "",
|
|
55
|
+
postal: (r == null ? void 0 : r.postal) ?? "",
|
|
56
|
+
state: (r == null ? void 0 : r.state) ?? "",
|
|
57
|
+
phoneNumber: (r == null ? void 0 : r.phoneNumber) ?? "",
|
|
58
|
+
defaultAddress: (r == null ? void 0 : r.defaultAddress) ?? !1
|
|
59
|
+
}, {
|
|
60
|
+
watch: D,
|
|
61
|
+
setValue: g,
|
|
62
|
+
setError: y,
|
|
63
|
+
clearErrors: m,
|
|
64
|
+
reset: F,
|
|
65
|
+
formState: p,
|
|
66
|
+
getValues: i
|
|
67
|
+
} = Q({
|
|
68
|
+
defaultValues: b,
|
|
69
|
+
mode: "onBlur"
|
|
70
|
+
}), R = D(), v = {};
|
|
71
|
+
for (const [e, s] of Object.entries(p.errors))
|
|
72
|
+
s && typeof s.message == "string" && (v[e] = {
|
|
73
|
+
field: e,
|
|
74
|
+
code: "FIELD_REQUIRED",
|
|
75
|
+
// RHF default; overridden by setServerErrors
|
|
76
|
+
message: s.message
|
|
77
|
+
});
|
|
78
|
+
const A = N(
|
|
79
|
+
(e, s) => {
|
|
80
|
+
if (e === "country") {
|
|
81
|
+
const d = String(s);
|
|
82
|
+
h == null || h(d), g("country", d), g("state", ""), m(["country", "state"]);
|
|
83
|
+
} else
|
|
84
|
+
g(e, s), m(e);
|
|
85
|
+
t == null || t(e, s);
|
|
86
|
+
},
|
|
87
|
+
[g, m, t, h]
|
|
88
|
+
), O = N(
|
|
89
|
+
(e) => {
|
|
90
|
+
for (const [s, d] of Object.entries(e))
|
|
91
|
+
g(s, d);
|
|
92
|
+
},
|
|
93
|
+
[g]
|
|
94
|
+
), j = N(
|
|
95
|
+
(e) => {
|
|
96
|
+
const s = i(), d = C(s.country), n = q(e, s, d, o);
|
|
97
|
+
if (n) {
|
|
98
|
+
y(e, { message: n.message });
|
|
99
|
+
const E = Object.entries(p.errors).filter(([, a]) => a && typeof a.message == "string").map(([a, P]) => ({
|
|
100
|
+
field: a,
|
|
101
|
+
code: "FIELD_REQUIRED",
|
|
102
|
+
message: P.message
|
|
103
|
+
}));
|
|
104
|
+
c == null || c([...E, n]);
|
|
105
|
+
} else
|
|
106
|
+
m(e);
|
|
107
|
+
},
|
|
108
|
+
[i, o, y, m, p.errors, c]
|
|
109
|
+
), L = N(() => {
|
|
110
|
+
var E;
|
|
111
|
+
const e = i(), s = f.countryConfig ?? C(e.country), d = {
|
|
112
|
+
mode: (r == null ? void 0 : r.mode) ?? "create",
|
|
113
|
+
...(r == null ? void 0 : r.mode) === "edit" && (r == null ? void 0 : r.addressId) !== void 0 ? { addressId: r.addressId } : {},
|
|
114
|
+
defaultAddress: e.defaultAddress,
|
|
115
|
+
firstName: e.firstName.trim(),
|
|
116
|
+
lastName: e.lastName.trim(),
|
|
117
|
+
address1: e.address1.trim(),
|
|
118
|
+
...(E = e.address2) != null && E.trim() ? { address2: e.address2.trim() } : {},
|
|
119
|
+
city: e.city.trim(),
|
|
120
|
+
postal: e.postal.trim(),
|
|
121
|
+
country: e.country,
|
|
122
|
+
...e.state ? { state: e.state } : {},
|
|
123
|
+
...o && e.phoneNumber ? { phoneNumber: e.phoneNumber.trim() } : {}
|
|
124
|
+
}, n = U(d, s, { showPhoneNumber: o });
|
|
125
|
+
if (n.valid)
|
|
126
|
+
m(), u == null || u(d);
|
|
127
|
+
else {
|
|
128
|
+
for (const a of n.errors)
|
|
129
|
+
y(a.field, { message: a.message });
|
|
130
|
+
c == null || c(n.errors);
|
|
131
|
+
}
|
|
132
|
+
return n;
|
|
133
|
+
}, [
|
|
134
|
+
i,
|
|
135
|
+
f.countryConfig,
|
|
136
|
+
o,
|
|
137
|
+
r,
|
|
138
|
+
y,
|
|
139
|
+
m,
|
|
140
|
+
u,
|
|
141
|
+
c
|
|
142
|
+
]), T = N(
|
|
143
|
+
(e) => {
|
|
144
|
+
F(e ? { ...b, ...e } : b);
|
|
145
|
+
},
|
|
146
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
147
|
+
[F]
|
|
148
|
+
), _ = [], w = N(
|
|
149
|
+
(e) => {
|
|
150
|
+
for (const s of e)
|
|
151
|
+
s.field && s.field !== "" && y(s.field, { message: s.message });
|
|
152
|
+
},
|
|
153
|
+
[y]
|
|
154
|
+
), I = {};
|
|
155
|
+
for (const [e, s] of Object.entries(p.touchedFields))
|
|
156
|
+
s && (I[e] = !0);
|
|
157
|
+
return {
|
|
158
|
+
values: R,
|
|
159
|
+
errors: v,
|
|
160
|
+
nonFieldErrors: _,
|
|
161
|
+
touched: I,
|
|
162
|
+
isValid: p.isValid && Object.keys(v).length === 0,
|
|
163
|
+
isDirty: p.isDirty,
|
|
164
|
+
setValue: A,
|
|
165
|
+
setValues: O,
|
|
166
|
+
handleBlur: j,
|
|
167
|
+
handleSubmit: L,
|
|
168
|
+
reset: T,
|
|
169
|
+
setServerErrors: w
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
export {
|
|
173
|
+
Z as useAddressFormRHF
|
|
174
|
+
};
|