@ticketmaster/tm-global-address 0.7.0 → 0.9.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/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/index.d.ts +2 -2
- package/dist/index.js +40 -38
- package/dist/styles/darkTokens.d.ts +63 -0
- package/package.json +2 -1
- package/dist/components/HelloWorld/HelloWorld.d.ts +0 -14
- package/dist/components/HelloWorld/HelloWorld.js +0 -11
|
@@ -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
|
+
};
|
package/dist/index.d.ts
CHANGED
|
@@ -26,5 +26,5 @@ export { sanitize, validateTextField, validatePostalCode, validateState, validat
|
|
|
26
26
|
export type { ValidateFormOptions } from './validation/index';
|
|
27
27
|
export { useLabels, resolveLabels, loadBundle, isRTLLocale, interpolate, SUPPORTED_LOCALES, } from './i18n/index';
|
|
28
28
|
export type { ResolvedLabels, LocaleBundle, FormLabels, ValidationLabels, CountryOverrides, InterpolationVars, SupportedLocale, } from './i18n/index';
|
|
29
|
-
export {
|
|
30
|
-
export type {
|
|
29
|
+
export { useAddressForm, useAddressFormRHF } from './hooks/index';
|
|
30
|
+
export type { UseAddressFormConfig, UseAddressFormReturn, AddressFormValues } from './hooks/index';
|
package/dist/index.js
CHANGED
|
@@ -1,55 +1,57 @@
|
|
|
1
1
|
import { GlobalAddress as e } from "./components/GlobalAddress/GlobalAddress.js";
|
|
2
2
|
import { AddressForm as m } from "./components/AddressForm/AddressForm.js";
|
|
3
|
-
import { PhoneField as
|
|
4
|
-
import { AddressCard as
|
|
3
|
+
import { PhoneField as s } from "./components/PhoneField/PhoneField.js";
|
|
4
|
+
import { AddressCard as f, formatAddressSummary as p } from "./components/AddressCard/AddressCard.js";
|
|
5
5
|
import { AddressSummary as l } from "./components/AddressSummary/AddressSummary.js";
|
|
6
6
|
import { AddressList as i } from "./components/AddressList/AddressList.js";
|
|
7
|
-
import { COUNTRIES as T, getCountryByCode as
|
|
8
|
-
import { SUBDIVISIONS as
|
|
9
|
-
import { POSTAL_PATTERNS as
|
|
10
|
-
import { STATE_REQUIRED_CODES as
|
|
11
|
-
import { COUNTRY_CONFIGS as
|
|
7
|
+
import { COUNTRIES as T, getCountryByCode as A } from "./data/countries.js";
|
|
8
|
+
import { SUBDIVISIONS as n, getSubdivisions as u } from "./data/states.js";
|
|
9
|
+
import { POSTAL_PATTERNS as P, getPostalPattern as R } from "./data/postalPatterns.js";
|
|
10
|
+
import { STATE_REQUIRED_CODES as F } from "./data/countryConfig.js";
|
|
11
|
+
import { COUNTRY_CONFIGS as L, COUNTRY_OPTIONS as v, getCountryConfig as _, getCountryName as g } from "./data/countryData.js";
|
|
12
12
|
import { FIELD_CONSTRAINTS as U } from "./validation/constants.js";
|
|
13
13
|
import { SUPPORTED_LOCALES as D, useLabels as B } from "./i18n/index.js";
|
|
14
|
-
import {
|
|
15
|
-
import {
|
|
16
|
-
import {
|
|
17
|
-
import {
|
|
18
|
-
import {
|
|
19
|
-
import {
|
|
20
|
-
import {
|
|
21
|
-
import {
|
|
14
|
+
import { interpolate as G, isRTLLocale as Y, loadBundle as c, resolveLabels as z } from "./i18n/labelResolver.js";
|
|
15
|
+
import { sanitize as Q } from "./validation/sanitize.js";
|
|
16
|
+
import { useAddressForm as j } from "./hooks/useAddressForm.js";
|
|
17
|
+
import { useAddressFormRHF as q } from "./hooks/useAddressFormRHF.js";
|
|
18
|
+
import { validateForm as J } from "./validation/validateForm.js";
|
|
19
|
+
import { validatePhoneNumber as M } from "./validation/validatePhoneNumber.js";
|
|
20
|
+
import { validatePostalCode as X } from "./validation/validatePostalCode.js";
|
|
21
|
+
import { validateState as $ } from "./validation/validateState.js";
|
|
22
|
+
import { validateTextField as or } from "./validation/validateTextField.js";
|
|
22
23
|
export {
|
|
23
|
-
|
|
24
|
+
f as AddressCard,
|
|
24
25
|
m as AddressForm,
|
|
25
26
|
i as AddressList,
|
|
26
27
|
l as AddressSummary,
|
|
27
28
|
T as COUNTRIES,
|
|
28
|
-
|
|
29
|
-
|
|
29
|
+
L as COUNTRY_CONFIGS,
|
|
30
|
+
v as COUNTRY_OPTIONS,
|
|
30
31
|
U as FIELD_CONSTRAINTS,
|
|
31
32
|
e as GlobalAddress,
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
A as SUBDIVISIONS,
|
|
33
|
+
P as POSTAL_PATTERNS,
|
|
34
|
+
s as PhoneField,
|
|
35
|
+
F as STATE_REQUIRED_CODES,
|
|
36
|
+
n as SUBDIVISIONS,
|
|
37
37
|
D as SUPPORTED_LOCALES,
|
|
38
38
|
p as formatAddressSummary,
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
39
|
+
A as getCountryByCode,
|
|
40
|
+
_ as getCountryConfig,
|
|
41
|
+
g as getCountryName,
|
|
42
|
+
R as getPostalPattern,
|
|
43
|
+
u as getSubdivisions,
|
|
44
|
+
G as interpolate,
|
|
45
|
+
Y as isRTLLocale,
|
|
46
|
+
c as loadBundle,
|
|
47
|
+
z as resolveLabels,
|
|
48
|
+
Q as sanitize,
|
|
49
|
+
j as useAddressForm,
|
|
50
|
+
q as useAddressFormRHF,
|
|
49
51
|
B as useLabels,
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
52
|
+
J as validateForm,
|
|
53
|
+
M as validatePhoneNumber,
|
|
54
|
+
X as validatePostalCode,
|
|
55
|
+
$ as validateState,
|
|
56
|
+
or as validateTextField
|
|
55
57
|
};
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Dark theme design tokens for tm-global-address.
|
|
3
|
+
*
|
|
4
|
+
* Intentionally designed dark palette — NOT a simple color inversion.
|
|
5
|
+
* Surfaces use deep navy/charcoal rather than black to reduce eye strain.
|
|
6
|
+
* Interactive colors maintain sufficient contrast ratios (WCAG AA minimum 4.5:1).
|
|
7
|
+
*
|
|
8
|
+
* These tokens mirror the GDS token naming conventions, matching `tokens.ts`.
|
|
9
|
+
*
|
|
10
|
+
* TODO(Phase 2 GDS swap): Replace with GDS dark theme token references.
|
|
11
|
+
*/
|
|
12
|
+
export declare const darkTokens: {
|
|
13
|
+
readonly colorInteractivePrimary: "#4D9FEC";
|
|
14
|
+
readonly colorInteractivePrimaryHover: "#6DB5F5";
|
|
15
|
+
readonly colorInteractivePrimaryText: "#0D1117";
|
|
16
|
+
readonly colorInteractiveGhost: "transparent";
|
|
17
|
+
readonly colorInteractiveGhostBorder: "#4D9FEC";
|
|
18
|
+
readonly colorInteractiveGhostText: "#4D9FEC";
|
|
19
|
+
readonly colorInteractiveLink: "#6DB5F5";
|
|
20
|
+
readonly colorFeedbackError: "#F28B82";
|
|
21
|
+
readonly colorFeedbackErrorSurface: "#2D1A1A";
|
|
22
|
+
readonly colorFeedbackErrorBorder: "#F28B82";
|
|
23
|
+
readonly colorBorderInput: "#6E7681";
|
|
24
|
+
readonly colorBorderInputFocus: "#4D9FEC";
|
|
25
|
+
readonly colorBorderInputError: "#F28B82";
|
|
26
|
+
readonly colorBorderInputDisabled: "#3D4451";
|
|
27
|
+
readonly colorSurfacePrimary: "#161B22";
|
|
28
|
+
readonly colorSurfaceSecondary: "#21262D";
|
|
29
|
+
readonly colorSurfaceDisabled: "#1C2128";
|
|
30
|
+
readonly colorTextPrimary: "#E6EDF3";
|
|
31
|
+
readonly colorTextSecondary: "#8B949E";
|
|
32
|
+
readonly colorTextPlaceholder: "#6E7681";
|
|
33
|
+
readonly colorTextDisabled: "#484F58";
|
|
34
|
+
readonly colorTextError: "#F28B82";
|
|
35
|
+
readonly colorTextInverse: "#0D1117";
|
|
36
|
+
readonly colorBadgeBorder: "#6E7681";
|
|
37
|
+
readonly colorBadgeText: "#8B949E";
|
|
38
|
+
readonly space1: "4px";
|
|
39
|
+
readonly space2: "8px";
|
|
40
|
+
readonly space3: "12px";
|
|
41
|
+
readonly space4: "16px";
|
|
42
|
+
readonly space5: "20px";
|
|
43
|
+
readonly space6: "24px";
|
|
44
|
+
readonly space8: "32px";
|
|
45
|
+
readonly fontSizeBody: "14px";
|
|
46
|
+
readonly fontSizeInput: "16px";
|
|
47
|
+
readonly fontSizeLabel: "14px";
|
|
48
|
+
readonly fontSizeError: "12px";
|
|
49
|
+
readonly fontSizeHeading: "16px";
|
|
50
|
+
readonly fontWeightNormal: "400";
|
|
51
|
+
readonly fontWeightBold: "600";
|
|
52
|
+
readonly fontFamily: "-apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif";
|
|
53
|
+
readonly lineHeightBody: "1.5";
|
|
54
|
+
readonly borderRadiusInput: "4px";
|
|
55
|
+
readonly borderRadiusButton: "4px";
|
|
56
|
+
readonly borderRadiusBadge: "4px";
|
|
57
|
+
readonly borderWidthInput: "1px";
|
|
58
|
+
readonly borderWidthInputFocus: "2px";
|
|
59
|
+
readonly breakpointMobile: "768px";
|
|
60
|
+
readonly transitionFast: "150ms ease";
|
|
61
|
+
readonly transitionNormal: "250ms ease";
|
|
62
|
+
readonly zIndexDropdown: 100;
|
|
63
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ticketmaster/tm-global-address",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.9.0",
|
|
4
4
|
"description": "Shared React UI Library for Global Address Management",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"module": "dist/index.js",
|
|
@@ -52,6 +52,7 @@
|
|
|
52
52
|
"prettier": "3.0.3",
|
|
53
53
|
"react": "^18.3.1",
|
|
54
54
|
"react-dom": "^18.3.1",
|
|
55
|
+
"react-hook-form": "^7.72.1",
|
|
55
56
|
"storybook": "^8.2.4",
|
|
56
57
|
"styled-components": "^6.4.0",
|
|
57
58
|
"tsconfig-paths-webpack-plugin": "^4.1.0",
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
import { default as React } from 'react';
|
|
2
|
-
/**
|
|
3
|
-
* HelloWorld — placeholder component.
|
|
4
|
-
*
|
|
5
|
-
* This component exists solely to validate the build pipeline,
|
|
6
|
-
* test setup, and Storybook configuration in Phase 1.
|
|
7
|
-
* It will be replaced by the actual address form components in Phase 2.
|
|
8
|
-
*/
|
|
9
|
-
export interface HelloWorldProps {
|
|
10
|
-
/** Optional message to display alongside the package name */
|
|
11
|
-
message?: string;
|
|
12
|
-
}
|
|
13
|
-
export declare const HelloWorld: React.FC<HelloWorldProps>;
|
|
14
|
-
export default HelloWorld;
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
import { jsxs as r, jsx as d } from "react/jsx-runtime";
|
|
2
|
-
const t = ({ message: l }) => /* @__PURE__ */ r("div", { "data-testid": "hello-world", children: [
|
|
3
|
-
/* @__PURE__ */ d("span", { children: "tm-global-address" }),
|
|
4
|
-
l && /* @__PURE__ */ r("span", { children: [
|
|
5
|
-
" — ",
|
|
6
|
-
l
|
|
7
|
-
] })
|
|
8
|
-
] });
|
|
9
|
-
export {
|
|
10
|
-
t as HelloWorld
|
|
11
|
-
};
|