@famgia/omnify-react 0.0.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/dist/index.js ADDED
@@ -0,0 +1,947 @@
1
+ // src/components/JapaneseNameField.tsx
2
+ import { Form, Input, Row, Col } from "antd";
3
+
4
+ // src/lib/zod-i18n.ts
5
+ var DEFAULT_MESSAGES = {
6
+ required: {
7
+ en: "${displayName} is required",
8
+ ja: "${displayName}\u306F\u5FC5\u9808\u3067\u3059",
9
+ vi: "${displayName} l\xE0 b\u1EAFt bu\u1ED9c",
10
+ ko: "${displayName}\uC740(\uB294) \uD544\uC218\uC785\uB2C8\uB2E4",
11
+ "zh-CN": "${displayName}\u662F\u5FC5\u586B\u9879",
12
+ "zh-TW": "${displayName}\u70BA\u5FC5\u586B\u6B04\u4F4D",
13
+ th: "${displayName} \u0E08\u0E33\u0E40\u0E1B\u0E47\u0E19\u0E15\u0E49\u0E2D\u0E07\u0E01\u0E23\u0E2D\u0E01",
14
+ es: "${displayName} es obligatorio"
15
+ },
16
+ minLength: {
17
+ en: "${displayName} must be at least ${min} characters",
18
+ ja: "${displayName}\u306F${min}\u6587\u5B57\u4EE5\u4E0A\u3067\u5165\u529B\u3057\u3066\u304F\u3060\u3055\u3044",
19
+ vi: "${displayName} ph\u1EA3i c\xF3 \xEDt nh\u1EA5t ${min} k\xFD t\u1EF1",
20
+ ko: "${displayName}\uC740(\uB294) ${min}\uC790 \uC774\uC0C1\uC774\uC5B4\uC57C \uD569\uB2C8\uB2E4",
21
+ "zh-CN": "${displayName}\u81F3\u5C11\u9700\u8981${min}\u4E2A\u5B57\u7B26",
22
+ "zh-TW": "${displayName}\u81F3\u5C11\u9700\u8981${min}\u500B\u5B57\u5143",
23
+ th: "${displayName} \u0E15\u0E49\u0E2D\u0E07\u0E21\u0E35\u0E2D\u0E22\u0E48\u0E32\u0E07\u0E19\u0E49\u0E2D\u0E22 ${min} \u0E15\u0E31\u0E27\u0E2D\u0E31\u0E01\u0E29\u0E23",
24
+ es: "${displayName} debe tener al menos ${min} caracteres"
25
+ },
26
+ maxLength: {
27
+ en: "${displayName} must be at most ${max} characters",
28
+ ja: "${displayName}\u306F${max}\u6587\u5B57\u4EE5\u5185\u3067\u5165\u529B\u3057\u3066\u304F\u3060\u3055\u3044",
29
+ vi: "${displayName} kh\xF4ng \u0111\u01B0\u1EE3c qu\xE1 ${max} k\xFD t\u1EF1",
30
+ ko: "${displayName}\uC740(\uB294) ${max}\uC790 \uC774\uD558\uC5EC\uC57C \uD569\uB2C8\uB2E4",
31
+ "zh-CN": "${displayName}\u6700\u591A${max}\u4E2A\u5B57\u7B26",
32
+ "zh-TW": "${displayName}\u6700\u591A${max}\u500B\u5B57\u5143",
33
+ th: "${displayName} \u0E15\u0E49\u0E2D\u0E07\u0E44\u0E21\u0E48\u0E40\u0E01\u0E34\u0E19 ${max} \u0E15\u0E31\u0E27\u0E2D\u0E31\u0E01\u0E29\u0E23",
34
+ es: "${displayName} debe tener como m\xE1ximo ${max} caracteres"
35
+ },
36
+ min: {
37
+ en: "${displayName} must be at least ${min}",
38
+ ja: "${displayName}\u306F${min}\u4EE5\u4E0A\u3067\u5165\u529B\u3057\u3066\u304F\u3060\u3055\u3044",
39
+ vi: "${displayName} ph\u1EA3i l\u1EDBn h\u01A1n ho\u1EB7c b\u1EB1ng ${min}",
40
+ ko: "${displayName}\uC740(\uB294) ${min} \uC774\uC0C1\uC774\uC5B4\uC57C \uD569\uB2C8\uB2E4",
41
+ "zh-CN": "${displayName}\u5FC5\u987B\u5927\u4E8E\u7B49\u4E8E${min}",
42
+ "zh-TW": "${displayName}\u5FC5\u9808\u5927\u65BC\u7B49\u65BC${min}",
43
+ th: "${displayName} \u0E15\u0E49\u0E2D\u0E07\u0E21\u0E32\u0E01\u0E01\u0E27\u0E48\u0E32\u0E2B\u0E23\u0E37\u0E2D\u0E40\u0E17\u0E48\u0E32\u0E01\u0E31\u0E1A ${min}",
44
+ es: "${displayName} debe ser al menos ${min}"
45
+ },
46
+ max: {
47
+ en: "${displayName} must be at most ${max}",
48
+ ja: "${displayName}\u306F${max}\u4EE5\u4E0B\u3067\u5165\u529B\u3057\u3066\u304F\u3060\u3055\u3044",
49
+ vi: "${displayName} ph\u1EA3i nh\u1ECF h\u01A1n ho\u1EB7c b\u1EB1ng ${max}",
50
+ ko: "${displayName}\uC740(\uB294) ${max} \uC774\uD558\uC5EC\uC57C \uD569\uB2C8\uB2E4",
51
+ "zh-CN": "${displayName}\u5FC5\u987B\u5C0F\u4E8E\u7B49\u4E8E${max}",
52
+ "zh-TW": "${displayName}\u5FC5\u9808\u5C0F\u65BC\u7B49\u65BC${max}",
53
+ th: "${displayName} \u0E15\u0E49\u0E2D\u0E07\u0E19\u0E49\u0E2D\u0E22\u0E01\u0E27\u0E48\u0E32\u0E2B\u0E23\u0E37\u0E2D\u0E40\u0E17\u0E48\u0E32\u0E01\u0E31\u0E1A ${max}",
54
+ es: "${displayName} debe ser como m\xE1ximo ${max}"
55
+ },
56
+ email: {
57
+ en: "Please enter a valid email address",
58
+ ja: "\u6709\u52B9\u306A\u30E1\u30FC\u30EB\u30A2\u30C9\u30EC\u30B9\u3092\u5165\u529B\u3057\u3066\u304F\u3060\u3055\u3044",
59
+ vi: "Vui l\xF2ng nh\u1EADp \u0111\u1ECBa ch\u1EC9 email h\u1EE3p l\u1EC7",
60
+ ko: "\uC720\uD6A8\uD55C \uC774\uBA54\uC77C \uC8FC\uC18C\uB97C \uC785\uB825\uD558\uC138\uC694",
61
+ "zh-CN": "\u8BF7\u8F93\u5165\u6709\u6548\u7684\u7535\u5B50\u90AE\u4EF6\u5730\u5740",
62
+ "zh-TW": "\u8ACB\u8F38\u5165\u6709\u6548\u7684\u96FB\u5B50\u90F5\u4EF6\u5730\u5740",
63
+ th: "\u0E01\u0E23\u0E38\u0E13\u0E32\u0E01\u0E23\u0E2D\u0E01\u0E2D\u0E35\u0E40\u0E21\u0E25\u0E17\u0E35\u0E48\u0E16\u0E39\u0E01\u0E15\u0E49\u0E2D\u0E07",
64
+ es: "Por favor, introduce una direcci\xF3n de correo electr\xF3nico v\xE1lida"
65
+ },
66
+ url: {
67
+ en: "Please enter a valid URL",
68
+ ja: "\u6709\u52B9\u306AURL\u3092\u5165\u529B\u3057\u3066\u304F\u3060\u3055\u3044",
69
+ vi: "Vui l\xF2ng nh\u1EADp URL h\u1EE3p l\u1EC7",
70
+ ko: "\uC720\uD6A8\uD55C URL\uC744 \uC785\uB825\uD558\uC138\uC694",
71
+ "zh-CN": "\u8BF7\u8F93\u5165\u6709\u6548\u7684URL",
72
+ "zh-TW": "\u8ACB\u8F38\u5165\u6709\u6548\u7684\u7DB2\u5740",
73
+ th: "\u0E01\u0E23\u0E38\u0E13\u0E32\u0E01\u0E23\u0E2D\u0E01 URL \u0E17\u0E35\u0E48\u0E16\u0E39\u0E01\u0E15\u0E49\u0E2D\u0E07",
74
+ es: "Por favor, introduce una URL v\xE1lida"
75
+ },
76
+ pattern: {
77
+ en: "${displayName} format is invalid",
78
+ ja: "${displayName}\u306E\u5F62\u5F0F\u304C\u6B63\u3057\u304F\u3042\u308A\u307E\u305B\u3093",
79
+ vi: "${displayName} kh\xF4ng \u0111\xFAng \u0111\u1ECBnh d\u1EA1ng",
80
+ ko: "${displayName} \uD615\uC2DD\uC774 \uC62C\uBC14\uB974\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4",
81
+ "zh-CN": "${displayName}\u683C\u5F0F\u4E0D\u6B63\u786E",
82
+ "zh-TW": "${displayName}\u683C\u5F0F\u4E0D\u6B63\u78BA",
83
+ th: "\u0E23\u0E39\u0E1B\u0E41\u0E1A\u0E1A${displayName}\u0E44\u0E21\u0E48\u0E16\u0E39\u0E01\u0E15\u0E49\u0E2D\u0E07",
84
+ es: "El formato de ${displayName} no es v\xE1lido"
85
+ }
86
+ };
87
+ var currentLocale = "ja";
88
+ var fallbackLocale = "en";
89
+ var customMessages = {};
90
+ function setZodLocale(locale) {
91
+ currentLocale = locale;
92
+ }
93
+ function getZodLocale() {
94
+ return currentLocale;
95
+ }
96
+ function setZodFallbackLocale(locale) {
97
+ fallbackLocale = locale;
98
+ }
99
+ function getZodFallbackLocale() {
100
+ return fallbackLocale;
101
+ }
102
+ function addZodMessages(messages) {
103
+ for (const [key, locales] of Object.entries(messages)) {
104
+ customMessages[key] = { ...customMessages[key], ...locales };
105
+ }
106
+ }
107
+ function getZodMessage(key, params = {}) {
108
+ const messages = customMessages[key] ?? DEFAULT_MESSAGES[key];
109
+ if (!messages) return key;
110
+ let message2 = messages[currentLocale] ?? messages[fallbackLocale] ?? messages["en"] ?? key;
111
+ for (const [param, value] of Object.entries(params)) {
112
+ message2 = message2.replace(new RegExp(`\\$\\{${param}\\}`, "g"), String(value));
113
+ }
114
+ return message2;
115
+ }
116
+ function getZodMessages(locale) {
117
+ const targetLocale = locale ?? currentLocale;
118
+ const result = {};
119
+ for (const [key, locales] of Object.entries(DEFAULT_MESSAGES)) {
120
+ result[key] = locales[targetLocale] ?? locales[fallbackLocale] ?? locales["en"] ?? key;
121
+ }
122
+ for (const [key, locales] of Object.entries(customMessages)) {
123
+ if (locales[targetLocale]) {
124
+ result[key] = locales[targetLocale];
125
+ }
126
+ }
127
+ return result;
128
+ }
129
+
130
+ // src/lib/form-validation.ts
131
+ function zodRule(schema, displayName) {
132
+ const field = displayName ?? "\u3053\u306E\u9805\u76EE";
133
+ return {
134
+ validator: async (_, value) => {
135
+ if (value === void 0 || value === null || value === "") {
136
+ if (schema.safeParse(void 0).success) return;
137
+ throw new Error(getZodMessage("required", { displayName: field }));
138
+ }
139
+ const result = schema.safeParse(value);
140
+ if (result.success) return;
141
+ const issue = result.error.issues[0];
142
+ if (!issue) {
143
+ throw new Error(getZodMessage("required", { displayName: field }));
144
+ }
145
+ const issueAny = issue;
146
+ switch (issue.code) {
147
+ case "too_small": {
148
+ const origin = issueAny.origin;
149
+ const minimum = issueAny.minimum;
150
+ if (origin === "string" && minimum === 1) {
151
+ throw new Error(getZodMessage("required", { displayName: field }));
152
+ }
153
+ if (origin === "string") {
154
+ throw new Error(getZodMessage("minLength", { displayName: field, min: minimum }));
155
+ }
156
+ throw new Error(getZodMessage("min", { displayName: field, min: minimum }));
157
+ }
158
+ case "too_big": {
159
+ const origin = issueAny.origin;
160
+ const maximum = issueAny.maximum;
161
+ if (origin === "string") {
162
+ throw new Error(getZodMessage("maxLength", { displayName: field, max: maximum }));
163
+ }
164
+ throw new Error(getZodMessage("max", { displayName: field, max: maximum }));
165
+ }
166
+ // Zod v4: 'invalid_string' → 'invalid_format'
167
+ case "invalid_format": {
168
+ const format = issueAny.format;
169
+ if (format === "email") {
170
+ throw new Error(getZodMessage("email", { displayName: field }));
171
+ }
172
+ if (format === "url") {
173
+ throw new Error(getZodMessage("url", { displayName: field }));
174
+ }
175
+ if (format === "regex") {
176
+ throw new Error(getZodMessage("pattern", { displayName: field }));
177
+ }
178
+ break;
179
+ }
180
+ // Zod v3: 'invalid_string' (handled via default case for forward compatibility)
181
+ default: {
182
+ if (issue.code === "invalid_string") {
183
+ const validation = issueAny.validation;
184
+ if (validation === "email") {
185
+ throw new Error(getZodMessage("email", { displayName: field }));
186
+ }
187
+ if (validation === "url") {
188
+ throw new Error(getZodMessage("url", { displayName: field }));
189
+ }
190
+ if (validation === "regex") {
191
+ throw new Error(getZodMessage("pattern", { displayName: field }));
192
+ }
193
+ }
194
+ break;
195
+ }
196
+ case "invalid_type": {
197
+ const expected = issueAny.expected;
198
+ if (expected && value === void 0) {
199
+ throw new Error(getZodMessage("required", { displayName: field }));
200
+ }
201
+ break;
202
+ }
203
+ }
204
+ throw new Error(issue.message);
205
+ }
206
+ };
207
+ }
208
+ function requiredRule(displayName) {
209
+ return {
210
+ required: true,
211
+ message: getZodMessage("required", { displayName })
212
+ };
213
+ }
214
+ function isZodRequired(schema) {
215
+ const schemaDesc = schema?._def?.typeName;
216
+ const inner = schema?._def?.innerType;
217
+ if (schemaDesc === "ZodOptional" || schemaDesc === "ZodNullable") return false;
218
+ if (inner?._def?.typeName === "ZodOptional" || inner?._def?.typeName === "ZodNullable") return false;
219
+ return true;
220
+ }
221
+
222
+ // src/components/JapaneseNameField.tsx
223
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
224
+ function getLabel(i18n, field, locale) {
225
+ return i18n.fields[field]?.label?.[locale] ?? i18n.fields[field]?.label?.["en"] ?? field;
226
+ }
227
+ function getCompoundLabel(i18n, prefix, locale) {
228
+ return i18n.fields[prefix]?.label?.[locale] ?? i18n.fields[prefix]?.label?.["en"];
229
+ }
230
+ function getPlaceholder(i18n, field, locale) {
231
+ return i18n.fields[field]?.placeholder?.[locale] ?? i18n.fields[field]?.placeholder?.["en"] ?? "";
232
+ }
233
+ function JapaneseNameField({
234
+ schemas,
235
+ i18n,
236
+ prefix = "name",
237
+ required = false,
238
+ showKana = true,
239
+ label,
240
+ kanaLabel
241
+ }) {
242
+ const locale = getZodLocale();
243
+ const lastnameField = `${prefix}_lastname`;
244
+ const firstnameField = `${prefix}_firstname`;
245
+ const kanaLastnameField = `${prefix}_kana_lastname`;
246
+ const kanaFirstnameField = `${prefix}_kana_firstname`;
247
+ const getRule = (field) => {
248
+ const schema = schemas[field];
249
+ if (!schema) return [];
250
+ return [zodRule(schema, getLabel(i18n, field, locale))];
251
+ };
252
+ const isFieldRequired = (field) => {
253
+ const schema = schemas[field];
254
+ if (!schema) return false;
255
+ return isZodRequired(schema);
256
+ };
257
+ const nameRequired = isFieldRequired(lastnameField) || isFieldRequired(firstnameField) || required;
258
+ const kanaRequired = isFieldRequired(kanaLastnameField) || isFieldRequired(kanaFirstnameField);
259
+ const nameLabel = label ?? getCompoundLabel(i18n, prefix, locale) ?? getLabel(i18n, lastnameField, locale);
260
+ const nameKanaLabel = kanaLabel ?? `${getCompoundLabel(i18n, prefix, locale) ?? getLabel(i18n, kanaLastnameField, locale)}\uFF08\u30AB\u30CA\uFF09`;
261
+ const lastnameShortLabel = locale === "ja" ? "\u59D3" : "Last";
262
+ const firstnameShortLabel = locale === "ja" ? "\u540D" : "First";
263
+ const prefixStyle = {
264
+ color: "rgba(0, 0, 0, 0.88)",
265
+ fontWeight: 500,
266
+ borderRight: "1px solid #d9d9d9",
267
+ paddingRight: 8,
268
+ marginRight: 4
269
+ };
270
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
271
+ /* @__PURE__ */ jsx(Form.Item, { label: nameLabel, required: nameRequired, style: { marginBottom: 0 }, children: /* @__PURE__ */ jsxs(Row, { gutter: 8, children: [
272
+ /* @__PURE__ */ jsx(Col, { span: 12, children: /* @__PURE__ */ jsx(Form.Item, { name: lastnameField, rules: getRule(lastnameField), style: { marginBottom: 16 }, children: /* @__PURE__ */ jsx(
273
+ Input,
274
+ {
275
+ prefix: /* @__PURE__ */ jsx("span", { style: prefixStyle, children: lastnameShortLabel }),
276
+ placeholder: getPlaceholder(i18n, lastnameField, locale)
277
+ }
278
+ ) }) }),
279
+ /* @__PURE__ */ jsx(Col, { span: 12, children: /* @__PURE__ */ jsx(Form.Item, { name: firstnameField, rules: getRule(firstnameField), style: { marginBottom: 16 }, children: /* @__PURE__ */ jsx(
280
+ Input,
281
+ {
282
+ prefix: /* @__PURE__ */ jsx("span", { style: prefixStyle, children: firstnameShortLabel }),
283
+ placeholder: getPlaceholder(i18n, firstnameField, locale)
284
+ }
285
+ ) }) })
286
+ ] }) }),
287
+ showKana && /* @__PURE__ */ jsx(Form.Item, { label: nameKanaLabel, required: kanaRequired, style: { marginBottom: 0 }, children: /* @__PURE__ */ jsxs(Row, { gutter: 8, children: [
288
+ /* @__PURE__ */ jsx(Col, { span: 12, children: /* @__PURE__ */ jsx(
289
+ Form.Item,
290
+ {
291
+ name: kanaLastnameField,
292
+ rules: getRule(kanaLastnameField),
293
+ style: { marginBottom: 16 },
294
+ children: /* @__PURE__ */ jsx(
295
+ Input,
296
+ {
297
+ prefix: /* @__PURE__ */ jsx("span", { style: prefixStyle, children: lastnameShortLabel }),
298
+ placeholder: getPlaceholder(i18n, kanaLastnameField, locale)
299
+ }
300
+ )
301
+ }
302
+ ) }),
303
+ /* @__PURE__ */ jsx(Col, { span: 12, children: /* @__PURE__ */ jsx(
304
+ Form.Item,
305
+ {
306
+ name: kanaFirstnameField,
307
+ rules: getRule(kanaFirstnameField),
308
+ style: { marginBottom: 16 },
309
+ children: /* @__PURE__ */ jsx(
310
+ Input,
311
+ {
312
+ prefix: /* @__PURE__ */ jsx("span", { style: prefixStyle, children: firstnameShortLabel }),
313
+ placeholder: getPlaceholder(i18n, kanaFirstnameField, locale)
314
+ }
315
+ )
316
+ }
317
+ ) })
318
+ ] }) })
319
+ ] });
320
+ }
321
+
322
+ // src/components/JapaneseAddressField.tsx
323
+ import { useState, useCallback } from "react";
324
+ import { Form as Form2, Input as Input2, Select, Button, message } from "antd";
325
+ import { SearchOutlined, LoadingOutlined } from "@ant-design/icons";
326
+ import { Fragment as Fragment2, jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
327
+ function getLabel2(i18n, field, locale) {
328
+ return i18n.fields[field]?.label?.[locale] ?? i18n.fields[field]?.label?.["en"] ?? field;
329
+ }
330
+ function getPlaceholder2(i18n, field, locale) {
331
+ return i18n.fields[field]?.placeholder?.[locale] ?? i18n.fields[field]?.placeholder?.["en"] ?? "";
332
+ }
333
+ async function lookupPostalCode(postalCode) {
334
+ try {
335
+ const response = await fetch(`https://zipcloud.ibsnet.co.jp/api/search?zipcode=${postalCode}`);
336
+ const data = await response.json();
337
+ if (data.results && data.results.length > 0) {
338
+ const result = data.results[0];
339
+ return {
340
+ prefectureCode: result.prefcode,
341
+ prefectureName: result.address1,
342
+ address1: result.address2,
343
+ address2: result.address3
344
+ };
345
+ }
346
+ return null;
347
+ } catch (error) {
348
+ console.error("Postal code lookup failed:", error);
349
+ return null;
350
+ }
351
+ }
352
+ var MESSAGES = {
353
+ ja: {
354
+ searchAddress: "\u4F4F\u6240\u691C\u7D22",
355
+ searching: "\u691C\u7D22\u4E2D...",
356
+ notFound: "\u90F5\u4FBF\u756A\u53F7\u304C\u898B\u3064\u304B\u308A\u307E\u305B\u3093\u3067\u3057\u305F",
357
+ error: "\u4F4F\u6240\u691C\u7D22\u306B\u5931\u6557\u3057\u307E\u3057\u305F",
358
+ invalidFormat: "\u90F5\u4FBF\u756A\u53F7\u306E\u5F62\u5F0F\u304C\u6B63\u3057\u304F\u3042\u308A\u307E\u305B\u3093\uFF08\u4F8B\uFF1A123-4567\uFF09"
359
+ },
360
+ en: {
361
+ searchAddress: "Search Address",
362
+ searching: "Searching...",
363
+ notFound: "Postal code not found",
364
+ error: "Address lookup failed",
365
+ invalidFormat: "Invalid postal code format (e.g., 123-4567)"
366
+ },
367
+ vi: {
368
+ searchAddress: "T\xECm \u0111\u1ECBa ch\u1EC9",
369
+ searching: "\u0110ang t\xECm...",
370
+ notFound: "Kh\xF4ng t\xECm th\u1EA5y m\xE3 b\u01B0u \u0111i\u1EC7n",
371
+ error: "T\xECm \u0111\u1ECBa ch\u1EC9 th\u1EA5t b\u1EA1i",
372
+ invalidFormat: "\u0110\u1ECBnh d\u1EA1ng m\xE3 b\u01B0u \u0111i\u1EC7n kh\xF4ng h\u1EE3p l\u1EC7 (VD: 123-4567)"
373
+ }
374
+ };
375
+ function getMessage(key, locale) {
376
+ return MESSAGES[locale]?.[key] ?? MESSAGES.ja[key];
377
+ }
378
+ function JapaneseAddressField({
379
+ form,
380
+ schemas,
381
+ i18n,
382
+ prefix = "address",
383
+ usePrefectureId = false,
384
+ prefectureOptions,
385
+ enablePostalLookup = true,
386
+ showSearchButton = true,
387
+ autoSearch = true,
388
+ onPostalLookup
389
+ }) {
390
+ const locale = getZodLocale();
391
+ const [isSearching, setIsSearching] = useState(false);
392
+ const postalCodeField = `${prefix}_postal_code`;
393
+ const prefectureField = usePrefectureId ? `${prefix}_prefecture_id` : `${prefix}_prefecture`;
394
+ const address1Field = `${prefix}_address1`;
395
+ const address2Field = `${prefix}_address2`;
396
+ const address3Field = `${prefix}_address3`;
397
+ const getRule = (field) => {
398
+ const schema = schemas[field];
399
+ if (!schema) return [];
400
+ return [zodRule(schema, getLabel2(i18n, field, locale))];
401
+ };
402
+ const isRequired = (field) => {
403
+ const schema = schemas[field];
404
+ if (!schema) return false;
405
+ return isZodRequired(schema);
406
+ };
407
+ const doLookup = useCallback(
408
+ async (postalCode) => {
409
+ const digits = postalCode.replace(/[^0-9]/g, "");
410
+ if (digits.length !== 7) {
411
+ message.warning(getMessage("invalidFormat", locale));
412
+ return;
413
+ }
414
+ setIsSearching(true);
415
+ try {
416
+ const lookupFn = onPostalLookup ?? lookupPostalCode;
417
+ const result = await lookupFn(postalCode);
418
+ if (result) {
419
+ const prefectureValue = usePrefectureId ? result.prefectureCode : result.prefecture;
420
+ form.setFieldsValue({
421
+ [prefectureField]: prefectureValue,
422
+ [address1Field]: result.address1,
423
+ [address2Field]: result.address2
424
+ });
425
+ } else {
426
+ message.info(getMessage("notFound", locale));
427
+ }
428
+ } catch (error) {
429
+ message.error(getMessage("error", locale));
430
+ console.error("Postal code lookup failed:", error);
431
+ } finally {
432
+ setIsSearching(false);
433
+ }
434
+ },
435
+ [form, locale, onPostalLookup, prefectureField, address1Field, address2Field, usePrefectureId]
436
+ );
437
+ const handlePostalCodeChange = async (e) => {
438
+ const postalCode = e.target.value.replace(/[^0-9]/g, "");
439
+ if (autoSearch && enablePostalLookup && postalCode.length === 7) {
440
+ await doLookup(postalCode);
441
+ }
442
+ };
443
+ const handleSearchClick = async () => {
444
+ const postalCode = form.getFieldValue(postalCodeField);
445
+ if (postalCode) {
446
+ await doLookup(postalCode);
447
+ }
448
+ };
449
+ return /* @__PURE__ */ jsxs2(Fragment2, { children: [
450
+ /* @__PURE__ */ jsx2(
451
+ Form2.Item,
452
+ {
453
+ name: postalCodeField,
454
+ label: getLabel2(i18n, postalCodeField, locale),
455
+ rules: getRule(postalCodeField),
456
+ required: isRequired(postalCodeField),
457
+ children: /* @__PURE__ */ jsx2(
458
+ Input2,
459
+ {
460
+ placeholder: getPlaceholder2(i18n, postalCodeField, locale),
461
+ style: { width: enablePostalLookup && showSearchButton ? "calc(100% - 110px)" : 150 },
462
+ onChange: handlePostalCodeChange,
463
+ addonAfter: enablePostalLookup && showSearchButton && /* @__PURE__ */ jsx2(
464
+ Button,
465
+ {
466
+ type: "text",
467
+ size: "small",
468
+ icon: isSearching ? /* @__PURE__ */ jsx2(LoadingOutlined, {}) : /* @__PURE__ */ jsx2(SearchOutlined, {}),
469
+ onClick: handleSearchClick,
470
+ disabled: isSearching,
471
+ children: getMessage("searchAddress", locale)
472
+ }
473
+ )
474
+ }
475
+ )
476
+ }
477
+ ),
478
+ /* @__PURE__ */ jsx2(
479
+ Form2.Item,
480
+ {
481
+ name: prefectureField,
482
+ label: getLabel2(i18n, prefectureField, locale),
483
+ rules: getRule(prefectureField),
484
+ required: isRequired(prefectureField),
485
+ children: prefectureOptions ? /* @__PURE__ */ jsx2(
486
+ Select,
487
+ {
488
+ placeholder: getPlaceholder2(i18n, prefectureField, locale),
489
+ options: prefectureOptions,
490
+ style: { width: 200 },
491
+ showSearch: true,
492
+ optionFilterProp: "label"
493
+ }
494
+ ) : /* @__PURE__ */ jsx2(
495
+ Input2,
496
+ {
497
+ placeholder: getPlaceholder2(i18n, prefectureField, locale),
498
+ style: { width: 200 }
499
+ }
500
+ )
501
+ }
502
+ ),
503
+ /* @__PURE__ */ jsx2(
504
+ Form2.Item,
505
+ {
506
+ name: address1Field,
507
+ label: getLabel2(i18n, address1Field, locale),
508
+ rules: getRule(address1Field),
509
+ required: isRequired(address1Field),
510
+ children: /* @__PURE__ */ jsx2(Input2, { placeholder: getPlaceholder2(i18n, address1Field, locale) })
511
+ }
512
+ ),
513
+ /* @__PURE__ */ jsx2(
514
+ Form2.Item,
515
+ {
516
+ name: address2Field,
517
+ label: getLabel2(i18n, address2Field, locale),
518
+ rules: getRule(address2Field),
519
+ required: isRequired(address2Field),
520
+ children: /* @__PURE__ */ jsx2(Input2, { placeholder: getPlaceholder2(i18n, address2Field, locale) })
521
+ }
522
+ ),
523
+ /* @__PURE__ */ jsx2(
524
+ Form2.Item,
525
+ {
526
+ name: address3Field,
527
+ label: getLabel2(i18n, address3Field, locale),
528
+ rules: getRule(address3Field),
529
+ required: isRequired(address3Field),
530
+ children: /* @__PURE__ */ jsx2(Input2, { placeholder: getPlaceholder2(i18n, address3Field, locale) })
531
+ }
532
+ )
533
+ ] });
534
+ }
535
+
536
+ // src/components/JapaneseBankField.tsx
537
+ import { Form as Form3, Input as Input3, Select as Select2, Row as Row2, Col as Col2 } from "antd";
538
+ import { Fragment as Fragment3, jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
539
+ function getLabel3(i18n, field, locale) {
540
+ return i18n.fields[field]?.label?.[locale] ?? i18n.fields[field]?.label?.["en"] ?? field;
541
+ }
542
+ function getPlaceholder3(i18n, field, locale) {
543
+ return i18n.fields[field]?.placeholder?.[locale] ?? i18n.fields[field]?.placeholder?.["en"] ?? "";
544
+ }
545
+ function JapaneseBankField({
546
+ schemas,
547
+ i18n,
548
+ prefix = "bank",
549
+ accountTypeOptions
550
+ }) {
551
+ const locale = getZodLocale();
552
+ const bankCodeField = `${prefix}_bank_code`;
553
+ const bankNameField = `${prefix}_bank_name`;
554
+ const branchCodeField = `${prefix}_branch_code`;
555
+ const branchNameField = `${prefix}_branch_name`;
556
+ const accountTypeField = `${prefix}_account_type`;
557
+ const accountNumberField = `${prefix}_account_number`;
558
+ const accountHolderField = `${prefix}_account_holder`;
559
+ const getRule = (field) => {
560
+ const schema = schemas[field];
561
+ if (!schema) return [];
562
+ return [zodRule(schema, getLabel3(i18n, field, locale))];
563
+ };
564
+ const isRequired = (field) => {
565
+ const schema = schemas[field];
566
+ if (!schema) return false;
567
+ return isZodRequired(schema);
568
+ };
569
+ const bankRequired = isRequired(bankCodeField) || isRequired(bankNameField);
570
+ const branchRequired = isRequired(branchCodeField) || isRequired(branchNameField);
571
+ const codeShortLabel = locale === "ja" ? "\u30B3\u30FC\u30C9" : "Code";
572
+ const nameShortLabel = locale === "ja" ? "\u540D\u79F0" : "Name";
573
+ const bankLabel = locale === "ja" ? "\u9280\u884C" : "Bank";
574
+ const branchLabel = locale === "ja" ? "\u652F\u5E97" : "Branch";
575
+ const prefixStyle = {
576
+ color: "rgba(0, 0, 0, 0.88)",
577
+ fontWeight: 500,
578
+ borderRight: "1px solid #d9d9d9",
579
+ paddingRight: 8,
580
+ marginRight: 4
581
+ };
582
+ return /* @__PURE__ */ jsxs3(Fragment3, { children: [
583
+ /* @__PURE__ */ jsx3(Form3.Item, { label: bankLabel, required: bankRequired, style: { marginBottom: 0 }, children: /* @__PURE__ */ jsxs3(Row2, { gutter: 8, children: [
584
+ /* @__PURE__ */ jsx3(Col2, { span: 8, children: /* @__PURE__ */ jsx3(Form3.Item, { name: bankCodeField, rules: getRule(bankCodeField), style: { marginBottom: 16 }, children: /* @__PURE__ */ jsx3(
585
+ Input3,
586
+ {
587
+ prefix: /* @__PURE__ */ jsx3("span", { style: prefixStyle, children: codeShortLabel }),
588
+ placeholder: getPlaceholder3(i18n, bankCodeField, locale),
589
+ maxLength: 4
590
+ }
591
+ ) }) }),
592
+ /* @__PURE__ */ jsx3(Col2, { span: 16, children: /* @__PURE__ */ jsx3(Form3.Item, { name: bankNameField, rules: getRule(bankNameField), style: { marginBottom: 16 }, children: /* @__PURE__ */ jsx3(
593
+ Input3,
594
+ {
595
+ prefix: /* @__PURE__ */ jsx3("span", { style: prefixStyle, children: nameShortLabel }),
596
+ placeholder: getPlaceholder3(i18n, bankNameField, locale)
597
+ }
598
+ ) }) })
599
+ ] }) }),
600
+ /* @__PURE__ */ jsx3(Form3.Item, { label: branchLabel, required: branchRequired, style: { marginBottom: 0 }, children: /* @__PURE__ */ jsxs3(Row2, { gutter: 8, children: [
601
+ /* @__PURE__ */ jsx3(Col2, { span: 8, children: /* @__PURE__ */ jsx3(
602
+ Form3.Item,
603
+ {
604
+ name: branchCodeField,
605
+ rules: getRule(branchCodeField),
606
+ style: { marginBottom: 16 },
607
+ children: /* @__PURE__ */ jsx3(
608
+ Input3,
609
+ {
610
+ prefix: /* @__PURE__ */ jsx3("span", { style: prefixStyle, children: codeShortLabel }),
611
+ placeholder: getPlaceholder3(i18n, branchCodeField, locale),
612
+ maxLength: 3
613
+ }
614
+ )
615
+ }
616
+ ) }),
617
+ /* @__PURE__ */ jsx3(Col2, { span: 16, children: /* @__PURE__ */ jsx3(
618
+ Form3.Item,
619
+ {
620
+ name: branchNameField,
621
+ rules: getRule(branchNameField),
622
+ style: { marginBottom: 16 },
623
+ children: /* @__PURE__ */ jsx3(
624
+ Input3,
625
+ {
626
+ prefix: /* @__PURE__ */ jsx3("span", { style: prefixStyle, children: nameShortLabel }),
627
+ placeholder: getPlaceholder3(i18n, branchNameField, locale)
628
+ }
629
+ )
630
+ }
631
+ ) })
632
+ ] }) }),
633
+ /* @__PURE__ */ jsx3(
634
+ Form3.Item,
635
+ {
636
+ name: accountTypeField,
637
+ label: getLabel3(i18n, accountTypeField, locale),
638
+ rules: getRule(accountTypeField),
639
+ required: isRequired(accountTypeField),
640
+ children: accountTypeOptions ? /* @__PURE__ */ jsx3(
641
+ Select2,
642
+ {
643
+ placeholder: getPlaceholder3(i18n, accountTypeField, locale),
644
+ options: accountTypeOptions,
645
+ style: { width: 150 }
646
+ }
647
+ ) : /* @__PURE__ */ jsx3(
648
+ Input3,
649
+ {
650
+ placeholder: getPlaceholder3(i18n, accountTypeField, locale),
651
+ style: { width: 150 }
652
+ }
653
+ )
654
+ }
655
+ ),
656
+ /* @__PURE__ */ jsx3(
657
+ Form3.Item,
658
+ {
659
+ name: accountNumberField,
660
+ label: getLabel3(i18n, accountNumberField, locale),
661
+ rules: getRule(accountNumberField),
662
+ required: isRequired(accountNumberField),
663
+ children: /* @__PURE__ */ jsx3(
664
+ Input3,
665
+ {
666
+ placeholder: getPlaceholder3(i18n, accountNumberField, locale),
667
+ maxLength: 7,
668
+ style: { width: 150 }
669
+ }
670
+ )
671
+ }
672
+ ),
673
+ /* @__PURE__ */ jsx3(
674
+ Form3.Item,
675
+ {
676
+ name: accountHolderField,
677
+ label: getLabel3(i18n, accountHolderField, locale),
678
+ rules: getRule(accountHolderField),
679
+ required: isRequired(accountHolderField),
680
+ children: /* @__PURE__ */ jsx3(Input3, { placeholder: getPlaceholder3(i18n, accountHolderField, locale) })
681
+ }
682
+ )
683
+ ] });
684
+ }
685
+
686
+ // src/hooks/use-form-mutation.ts
687
+ import { useMutation, useQueryClient } from "@tanstack/react-query";
688
+ import { App } from "antd";
689
+ function getFormErrors(error) {
690
+ const data = error?.response?.data;
691
+ const errors = data?.errors;
692
+ if (!errors || typeof errors !== "object") return [];
693
+ return Object.entries(errors).map(([name, messages]) => ({
694
+ name,
695
+ errors: Array.isArray(messages) ? messages : [String(messages)]
696
+ }));
697
+ }
698
+ function getValidationMessage(error) {
699
+ const data = error?.response?.data;
700
+ return data?.message || null;
701
+ }
702
+ function useFormMutation({
703
+ form,
704
+ mutationFn,
705
+ invalidateKeys = [],
706
+ successMessage,
707
+ onSuccess,
708
+ onError
709
+ }) {
710
+ const queryClient = useQueryClient();
711
+ const { message: message2 } = App.useApp();
712
+ return useMutation({
713
+ mutationFn,
714
+ onSuccess: (data) => {
715
+ invalidateKeys.forEach((key) => {
716
+ queryClient.invalidateQueries({ queryKey: [...key] });
717
+ });
718
+ if (successMessage) {
719
+ message2.success(successMessage);
720
+ }
721
+ onSuccess?.(data);
722
+ },
723
+ onError: (error) => {
724
+ const formErrors = getFormErrors(error);
725
+ if (formErrors.length > 0) {
726
+ form.setFields(formErrors);
727
+ }
728
+ const validationMessage = getValidationMessage(error);
729
+ if (validationMessage) {
730
+ message2.error(validationMessage);
731
+ }
732
+ onError?.(error);
733
+ }
734
+ });
735
+ }
736
+
737
+ // src/lib/rules/kana.ts
738
+ import { z } from "zod";
739
+ var CHAR_RANGES = {
740
+ // Full-width Katakana: ァ-ヶ (U+30A1-U+30F6) + ー (U+30FC)
741
+ fullWidthKatakana: "\u30A1-\u30F6\u30FC",
742
+ // Half-width Katakana: ヲ-゚ (U+FF66-U+FF9F)
743
+ halfWidthKatakana: "\uFF66-\uFF9F",
744
+ // Hiragana: ぁ-ゖ (U+3041-U+3096)
745
+ hiragana: "\u3041-\u3096",
746
+ // Full-width numbers: 0-9
747
+ fullWidthNumbers: "\uFF10-\uFF19",
748
+ // Half-width numbers: 0-9
749
+ halfWidthNumbers: "0-9",
750
+ // Full-width space:  (U+3000)
751
+ fullWidthSpace: "\u3000",
752
+ // Half-width space
753
+ halfWidthSpace: " ",
754
+ // Common special chars for names
755
+ defaultSpecialChars: ["\u30FC", "\u30FB"]
756
+ };
757
+ var DEFAULT_OPTIONS = {
758
+ fullWidthKatakana: true,
759
+ halfWidthKatakana: false,
760
+ hiragana: false,
761
+ allowNumbers: false,
762
+ fullWidthNumbers: false,
763
+ halfWidthNumbers: false,
764
+ allowSpaces: true,
765
+ allowSpecialChars: ["\u30FC", "\u30FB"],
766
+ message: ""
767
+ };
768
+ function buildKanaPattern(options = {}) {
769
+ const opts = { ...DEFAULT_OPTIONS, ...options };
770
+ const parts = [];
771
+ if (opts.fullWidthKatakana) {
772
+ parts.push(CHAR_RANGES.fullWidthKatakana);
773
+ }
774
+ if (opts.halfWidthKatakana) {
775
+ parts.push(CHAR_RANGES.halfWidthKatakana);
776
+ }
777
+ if (opts.hiragana) {
778
+ parts.push(CHAR_RANGES.hiragana);
779
+ }
780
+ if (opts.allowNumbers || opts.fullWidthNumbers) {
781
+ parts.push(CHAR_RANGES.fullWidthNumbers);
782
+ }
783
+ if (opts.allowNumbers || opts.halfWidthNumbers) {
784
+ parts.push(CHAR_RANGES.halfWidthNumbers);
785
+ }
786
+ if (opts.allowSpaces) {
787
+ parts.push(CHAR_RANGES.fullWidthSpace);
788
+ parts.push(CHAR_RANGES.halfWidthSpace);
789
+ }
790
+ if (opts.allowSpecialChars && opts.allowSpecialChars.length > 0) {
791
+ const escaped = opts.allowSpecialChars.map((c) => c.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")).join("");
792
+ parts.push(escaped);
793
+ }
794
+ return `^[${parts.join("")}]*$`;
795
+ }
796
+ function getDefaultMessage(options = {}, locale = "ja") {
797
+ const opts = { ...DEFAULT_OPTIONS, ...options };
798
+ const messages = {
799
+ ja: {
800
+ fullWidthKatakana: "\u5168\u89D2\u30AB\u30BF\u30AB\u30CA",
801
+ halfWidthKatakana: "\u534A\u89D2\u30AB\u30BF\u30AB\u30CA",
802
+ hiragana: "\u3072\u3089\u304C\u306A",
803
+ mixed: "\u30AB\u30CA\u6587\u5B57"
804
+ },
805
+ en: {
806
+ fullWidthKatakana: "full-width katakana",
807
+ halfWidthKatakana: "half-width katakana",
808
+ hiragana: "hiragana",
809
+ mixed: "kana characters"
810
+ }
811
+ };
812
+ const msg = messages[locale] ?? messages["ja"];
813
+ let type = msg.mixed;
814
+ if (opts.fullWidthKatakana && !opts.halfWidthKatakana && !opts.hiragana) {
815
+ type = msg.fullWidthKatakana;
816
+ } else if (opts.halfWidthKatakana && !opts.fullWidthKatakana && !opts.hiragana) {
817
+ type = msg.halfWidthKatakana;
818
+ } else if (opts.hiragana && !opts.fullWidthKatakana && !opts.halfWidthKatakana) {
819
+ type = msg.hiragana;
820
+ }
821
+ if (locale === "ja") {
822
+ return `${type}\u3067\u5165\u529B\u3057\u3066\u304F\u3060\u3055\u3044`;
823
+ }
824
+ return `Please enter in ${type}`;
825
+ }
826
+ function createKanaRegex(options = {}) {
827
+ return new RegExp(buildKanaPattern(options));
828
+ }
829
+ function validateKana(value, options = {}) {
830
+ if (!value) return true;
831
+ const regex = createKanaRegex(options);
832
+ return regex.test(value);
833
+ }
834
+ function getKanaPattern(options = {}) {
835
+ return buildKanaPattern(options);
836
+ }
837
+ function getKanaErrorMessage(options = {}, locale = "ja") {
838
+ return options.message ?? getDefaultMessage(options, locale);
839
+ }
840
+ var KATAKANA_FULL_WIDTH = {
841
+ fullWidthKatakana: true,
842
+ halfWidthKatakana: false,
843
+ hiragana: false,
844
+ allowSpaces: true,
845
+ allowSpecialChars: ["\u30FC", "\u30FB"]
846
+ };
847
+ var KATAKANA_HALF_WIDTH = {
848
+ fullWidthKatakana: false,
849
+ halfWidthKatakana: true,
850
+ hiragana: false,
851
+ allowSpaces: true,
852
+ allowSpecialChars: ["\uFF70"]
853
+ // Half-width prolonged sound mark
854
+ };
855
+ var HIRAGANA = {
856
+ fullWidthKatakana: false,
857
+ halfWidthKatakana: false,
858
+ hiragana: true,
859
+ allowSpaces: true,
860
+ allowSpecialChars: ["\u30FC"]
861
+ };
862
+ var KANA_ANY = {
863
+ fullWidthKatakana: true,
864
+ halfWidthKatakana: true,
865
+ hiragana: true,
866
+ allowSpaces: true,
867
+ allowSpecialChars: ["\u30FC", "\u30FB", "\uFF70"]
868
+ };
869
+ var KATAKANA_WITH_NUMBERS = {
870
+ fullWidthKatakana: true,
871
+ halfWidthKatakana: false,
872
+ hiragana: false,
873
+ allowNumbers: true,
874
+ allowSpaces: true,
875
+ allowSpecialChars: ["\u30FC", "\u30FB"]
876
+ };
877
+ var KATAKANA_PATTERN = /^[ァ-ヶー・  ]*$/;
878
+ var KATAKANA_HALF_PATTERN = /^[ヲ-゚ー ]*$/;
879
+ var HIRAGANA_PATTERN = /^[ぁ-ゖー  ]*$/;
880
+ var KANA_ANY_PATTERN = /^[ァ-ヶぁ-ゖヲ-゚ー・ー  ]*$/;
881
+ function kanaString(options = {}) {
882
+ const opts = { ...KATAKANA_FULL_WIDTH, ...options };
883
+ const regex = createKanaRegex(opts);
884
+ const message2 = getKanaErrorMessage(opts);
885
+ return z.string().regex(regex, { message: message2 });
886
+ }
887
+ function withKana(options = {}) {
888
+ return kanaString(options);
889
+ }
890
+ var kanaRules = {
891
+ createRegex: createKanaRegex,
892
+ validate: validateKana,
893
+ getPattern: getKanaPattern,
894
+ getMessage: getKanaErrorMessage,
895
+ // Zod helpers
896
+ string: kanaString,
897
+ // Presets
898
+ presets: {
899
+ fullWidthKatakana: KATAKANA_FULL_WIDTH,
900
+ halfWidthKatakana: KATAKANA_HALF_WIDTH,
901
+ hiragana: HIRAGANA,
902
+ any: KANA_ANY,
903
+ withNumbers: KATAKANA_WITH_NUMBERS
904
+ },
905
+ // Pattern constants for direct use
906
+ patterns: {
907
+ katakana: KATAKANA_PATTERN,
908
+ katakanaHalf: KATAKANA_HALF_PATTERN,
909
+ hiragana: HIRAGANA_PATTERN,
910
+ any: KANA_ANY_PATTERN
911
+ }
912
+ };
913
+ export {
914
+ HIRAGANA,
915
+ HIRAGANA_PATTERN,
916
+ JapaneseAddressField,
917
+ JapaneseBankField,
918
+ JapaneseNameField,
919
+ KANA_ANY,
920
+ KANA_ANY_PATTERN,
921
+ KATAKANA_FULL_WIDTH,
922
+ KATAKANA_HALF_PATTERN,
923
+ KATAKANA_HALF_WIDTH,
924
+ KATAKANA_PATTERN,
925
+ KATAKANA_WITH_NUMBERS,
926
+ addZodMessages,
927
+ createKanaRegex,
928
+ getKanaErrorMessage,
929
+ getKanaPattern,
930
+ getZodFallbackLocale,
931
+ getZodLocale,
932
+ getZodMessage,
933
+ getZodMessages,
934
+ isZodRequired,
935
+ getKanaPattern as kanaPattern,
936
+ createKanaRegex as kanaRegex,
937
+ kanaRules,
938
+ kanaString,
939
+ requiredRule,
940
+ setZodFallbackLocale,
941
+ setZodLocale,
942
+ useFormMutation,
943
+ validateKana,
944
+ withKana,
945
+ zodRule
946
+ };
947
+ //# sourceMappingURL=index.js.map