@krizaka/ui 3.0.0 → 3.1.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/README.md +1 -1
- package/dist/form/server.d.ts +96 -0
- package/dist/form/server.js +82 -0
- package/dist/form/server.js.map +1 -0
- package/dist/form.d.ts +503 -0
- package/dist/form.js +760 -0
- package/dist/form.js.map +1 -0
- package/dist/native/form.d.ts +376 -0
- package/dist/native/form.js +650 -0
- package/dist/native/form.js.map +1 -0
- package/dist/native.d.ts +113 -2
- package/dist/native.js +618 -388
- package/dist/native.js.map +1 -1
- package/package.json +48 -5
- package/registry/achievement.json +1 -1
- package/registry/action-bar.json +1 -1
- package/registry/alert.json +1 -1
- package/registry/app-shell.json +1 -1
- package/registry/argument-card.json +1 -1
- package/registry/audio-player.json +1 -1
- package/registry/auth-layout.json +1 -1
- package/registry/avatar.json +1 -1
- package/registry/badge.json +1 -1
- package/registry/button.json +1 -1
- package/registry/card-grid.json +1 -1
- package/registry/card.json +1 -1
- package/registry/catalogue-page.json +1 -1
- package/registry/changelog-page.json +1 -1
- package/registry/checkbox.json +89 -4
- package/registry/chip-link.json +1 -1
- package/registry/chip.json +1 -1
- package/registry/choice-grid.json +1 -1
- package/registry/code-block.json +1 -1
- package/registry/combobox.json +1 -1
- package/registry/command.json +1 -1
- package/registry/component-page.json +1 -1
- package/registry/confirm-button.json +1 -1
- package/registry/contact-page.json +1 -1
- package/registry/continue-prompt.json +1 -1
- package/registry/countdown.json +1 -1
- package/registry/cta-panel.json +1 -1
- package/registry/dashboard-layout.json +1 -1
- package/registry/data-table.json +1 -1
- package/registry/date-picker.json +142 -4
- package/registry/decision-list.json +1 -1
- package/registry/detail-layout.json +1 -1
- package/registry/dialog.json +1 -1
- package/registry/docs-article-page.json +1 -1
- package/registry/docs-hub-page.json +1 -1
- package/registry/docs-layout.json +1 -1
- package/registry/dropdown-menu.json +1 -1
- package/registry/empty-state.json +1 -1
- package/registry/error-page.json +1 -1
- package/registry/examples/checkbox/native/terms.d.ts +4 -0
- package/registry/examples/checkbox/native/terms.dark.png +0 -0
- package/registry/examples/checkbox/native/terms.js +26 -0
- package/registry/examples/checkbox/native/terms.light.png +0 -0
- package/registry/examples/checkbox/native/terms.tsx +28 -0
- package/registry/examples/date-picker/native/birth-date.d.ts +4 -0
- package/registry/examples/date-picker/native/birth-date.dark.png +0 -0
- package/registry/examples/date-picker/native/birth-date.js +30 -0
- package/registry/examples/date-picker/native/birth-date.light.png +0 -0
- package/registry/examples/date-picker/native/birth-date.tsx +27 -0
- package/registry/examples/field/native/default.d.ts +4 -0
- package/registry/examples/field/native/default.dark.png +0 -0
- package/registry/examples/field/native/default.js +29 -0
- package/registry/examples/field/native/default.light.png +0 -0
- package/registry/examples/field/native/default.tsx +23 -0
- package/registry/examples/form/amount.d.ts +4 -0
- package/registry/examples/form/amount.js +30 -0
- package/registry/examples/form/amount.tsx +36 -0
- package/registry/examples/form/audience.d.ts +4 -0
- package/registry/examples/form/audience.js +29 -0
- package/registry/examples/form/audience.tsx +31 -0
- package/registry/examples/form/native/amount.d.ts +4 -0
- package/registry/examples/form/native/amount.dark.png +0 -0
- package/registry/examples/form/native/amount.js +26 -0
- package/registry/examples/form/native/amount.light.png +0 -0
- package/registry/examples/form/native/amount.tsx +30 -0
- package/registry/examples/form/native/audience.d.ts +4 -0
- package/registry/examples/form/native/audience.dark.png +0 -0
- package/registry/examples/form/native/audience.js +24 -0
- package/registry/examples/form/native/audience.light.png +0 -0
- package/registry/examples/form/native/audience.tsx +24 -0
- package/registry/examples/form/native/profile.d.ts +4 -0
- package/registry/examples/form/native/profile.dark.png +0 -0
- package/registry/examples/form/native/profile.js +33 -0
- package/registry/examples/form/native/profile.light.png +0 -0
- package/registry/examples/form/native/profile.tsx +36 -0
- package/registry/examples/form/native/settings.d.ts +4 -0
- package/registry/examples/form/native/settings.dark.png +0 -0
- package/registry/examples/form/native/settings.js +25 -0
- package/registry/examples/form/native/settings.light.png +0 -0
- package/registry/examples/form/native/settings.tsx +23 -0
- package/registry/examples/form/native/sign-up.d.ts +4 -0
- package/registry/examples/form/native/sign-up.dark.png +0 -0
- package/registry/examples/form/native/sign-up.js +38 -0
- package/registry/examples/form/native/sign-up.light.png +0 -0
- package/registry/examples/form/native/sign-up.tsx +34 -0
- package/registry/examples/form/native/upload.d.ts +4 -0
- package/registry/examples/form/native/upload.dark.png +0 -0
- package/registry/examples/form/native/upload.js +23 -0
- package/registry/examples/form/native/upload.light.png +0 -0
- package/registry/examples/form/native/upload.tsx +22 -0
- package/registry/examples/form/profile.d.ts +4 -0
- package/registry/examples/form/profile.js +35 -0
- package/registry/examples/form/profile.tsx +40 -0
- package/registry/examples/form/report.d.ts +4 -0
- package/registry/examples/form/report.js +30 -0
- package/registry/examples/form/report.tsx +30 -0
- package/registry/examples/form/settings.d.ts +4 -0
- package/registry/examples/form/settings.js +30 -0
- package/registry/examples/form/settings.tsx +31 -0
- package/registry/examples/form/sign-up.d.ts +4 -0
- package/registry/examples/form/sign-up.js +38 -0
- package/registry/examples/form/sign-up.tsx +37 -0
- package/registry/examples/form/upload.d.ts +4 -0
- package/registry/examples/form/upload.js +32 -0
- package/registry/examples/form/upload.tsx +32 -0
- package/registry/field.json +104 -4
- package/registry/file-upload.json +1 -1
- package/registry/filter-bar.json +1 -1
- package/registry/form.json +2940 -0
- package/registry/gallery-page.json +1 -1
- package/registry/glow-card.json +1 -1
- package/registry/goal-gauge.json +1 -1
- package/registry/how-it-works-page.json +1 -1
- package/registry/image-cropper.json +1 -1
- package/registry/index.json +39 -7
- package/registry/iso.json +1 -1
- package/registry/kbd.json +1 -1
- package/registry/landing-page.json +1 -1
- package/registry/landscape.json +1 -1
- package/registry/legal-page.json +1 -1
- package/registry/link-card.json +1 -1
- package/registry/loader.json +1 -1
- package/registry/longform-page.json +1 -1
- package/registry/marketing-layout.json +1 -1
- package/registry/marquee.json +1 -1
- package/registry/mascot-quote.json +1 -1
- package/registry/mascot-state.json +2 -2
- package/registry/mascots.json +2 -2
- package/registry/media-capture.json +1 -1
- package/registry/meter.json +1 -1
- package/registry/nav-bar.json +1 -1
- package/registry/number-ticker.json +1 -1
- package/registry/page-header.json +1 -1
- package/registry/page-hero.json +1 -1
- package/registry/page.json +1 -1
- package/registry/pagination.json +1 -1
- package/registry/pointer-light.json +1 -1
- package/registry/popover.json +1 -1
- package/registry/pricing-page.json +1 -1
- package/registry/product-page.json +1 -1
- package/registry/profile-card.json +1 -1
- package/registry/progress.json +1 -1
- package/registry/promise-card.json +1 -1
- package/registry/radio-group.json +1 -1
- package/registry/scroll-stage.json +1 -1
- package/registry/search-field.json +1 -1
- package/registry/section-backdrop.json +1 -1
- package/registry/separator.json +1 -1
- package/registry/settings-layout.json +1 -1
- package/registry/skeleton.json +1 -1
- package/registry/slider.json +1 -1
- package/registry/slot.json +1 -1
- package/registry/spinner.json +1 -1
- package/registry/split-view.json +1 -1
- package/registry/stage-banner.json +1 -1
- package/registry/stat.json +1 -1
- package/registry/status-page.json +1 -1
- package/registry/stepper.json +1 -1
- package/registry/switch.json +1 -1
- package/registry/tabs.json +1 -1
- package/registry/tag-input.json +1 -1
- package/registry/terminal-replay.json +1 -1
- package/registry/theme.json +1 -1
- package/registry/toast.json +1 -1
- package/registry/toc.json +1 -1
- package/registry/tooltip.json +1 -1
- package/registry/video-player.json +1 -1
- package/registry/video-trimmer.json +1 -1
- package/registry/virtual.json +1 -1
- package/skills/form/SKILL.md +44 -0
|
@@ -0,0 +1,650 @@
|
|
|
1
|
+
// src/native/form.tsx
|
|
2
|
+
import { Button, Checkbox as CheckboxControl, Chip, DateField as DateFieldControl, ProductMark, Spinner, TextField as TextFieldControl, Txt, useTheme } from "@krizaka/ui/native";
|
|
3
|
+
import * as React2 from "react";
|
|
4
|
+
import { AccessibilityInfo, KeyboardAvoidingView, Platform, Pressable, ScrollView, StyleSheet, Switch as SwitchControl, View } from "react-native";
|
|
5
|
+
|
|
6
|
+
// src/form/core.tsx
|
|
7
|
+
import { useField, useForm, useStore } from "@tanstack/react-form";
|
|
8
|
+
import * as React from "react";
|
|
9
|
+
|
|
10
|
+
// src/form/errors.ts
|
|
11
|
+
var formMessages = {
|
|
12
|
+
required: "This field is required.",
|
|
13
|
+
tooShort: "Use at least {minimum} characters.",
|
|
14
|
+
tooLong: "Use at most {maximum} characters.",
|
|
15
|
+
tooSmall: "Enter {minimum} or more.",
|
|
16
|
+
tooBig: "Enter {maximum} or less.",
|
|
17
|
+
tooFew: "Choose at least {minimum}.",
|
|
18
|
+
tooMany: "Choose at most {maximum}.",
|
|
19
|
+
invalidEmail: "Enter an e-mail address, like name@example.com.",
|
|
20
|
+
invalidUrl: "Enter a web address, like https://example.com.",
|
|
21
|
+
invalidDate: "Enter a real date.",
|
|
22
|
+
invalidNumber: "Enter a number.",
|
|
23
|
+
invalidFormat: "Check the format of this field.",
|
|
24
|
+
invalidChoice: "Choose one of the options.",
|
|
25
|
+
invalid: "Check this field.",
|
|
26
|
+
// The whole form (a server's answer without fields): the codes of @krizaka/query's KrizakaApiError.
|
|
27
|
+
network: "No connection. Check your network and try again.",
|
|
28
|
+
"rate-limited": "Too many tries. Wait a moment and try again.",
|
|
29
|
+
unauthorized: "Your session has ended. Sign in again.",
|
|
30
|
+
forbidden: "You are not allowed to do this.",
|
|
31
|
+
conflict: "This was changed meanwhile. Reload and try again.",
|
|
32
|
+
"validation-failed": "Some fields need your attention.",
|
|
33
|
+
unavailable: "The service is unavailable for a moment. Try again shortly.",
|
|
34
|
+
server: "Something went wrong on our side. Try again."
|
|
35
|
+
};
|
|
36
|
+
var KEY = /^[\w-]+(\.[\w-]+)+$/;
|
|
37
|
+
var fill = (text, values) => values ? text.replace(/\{(\w+)\}/g, (all, name) => values[name] === void 0 ? all : String(values[name])) : text;
|
|
38
|
+
function formMessage(error, i18n = {}) {
|
|
39
|
+
const e = typeof error === "string" ? { key: error } : error;
|
|
40
|
+
const translated = i18n.t?.(e.key, e.values);
|
|
41
|
+
if (translated && translated !== e.key) return translated;
|
|
42
|
+
const code = e.key.slice(e.key.lastIndexOf(".") + 1);
|
|
43
|
+
const fallback = formMessages[code];
|
|
44
|
+
if (e.message && (code === "invalid" || !fallback)) return e.message;
|
|
45
|
+
if (fallback && (e.key.startsWith(`${i18n.prefix ?? "form.errors"}.`) || e.key.startsWith(`${i18n.apiPrefix ?? "apiErrors"}.`))) return fill(fallback, e.values);
|
|
46
|
+
return e.message ?? (KEY.test(e.key) ? fill(fallback ?? formMessages.invalid, e.values) : e.key);
|
|
47
|
+
}
|
|
48
|
+
function pathName(path) {
|
|
49
|
+
let name = "";
|
|
50
|
+
for (const part of path ?? []) {
|
|
51
|
+
const key = typeof part === "object" && part !== null ? part.key : part;
|
|
52
|
+
if (typeof key === "number") name += `[${key}]`;
|
|
53
|
+
else name += name ? `.${String(key)}` : String(key);
|
|
54
|
+
}
|
|
55
|
+
return name;
|
|
56
|
+
}
|
|
57
|
+
var num = (value) => value === void 0 ? void 0 : Number(value);
|
|
58
|
+
function issueError(issue, prefix = "form.errors") {
|
|
59
|
+
if (KEY.test(issue.message)) return { key: issue.message };
|
|
60
|
+
const key = (code, values) => ({ key: `${prefix}.${code}`, ...values ? { values } : {} });
|
|
61
|
+
const collection = issue.origin === "array" || issue.origin === "set";
|
|
62
|
+
switch (issue.code) {
|
|
63
|
+
case "invalid_type":
|
|
64
|
+
return issue.input === void 0 || issue.input === null || issue.input === "" ? key("required") : issue.expected === "number" ? key("invalidNumber") : issue.expected === "date" ? key("invalidDate") : key("invalid");
|
|
65
|
+
case "too_small": {
|
|
66
|
+
const minimum = num(issue.minimum);
|
|
67
|
+
if (issue.origin === "string") return minimum <= 1 ? key("required") : key("tooShort", { minimum });
|
|
68
|
+
if (collection) return minimum <= 1 ? key("required") : key("tooFew", { minimum });
|
|
69
|
+
return key("tooSmall", { minimum });
|
|
70
|
+
}
|
|
71
|
+
case "too_big": {
|
|
72
|
+
const maximum = num(issue.maximum);
|
|
73
|
+
return issue.origin === "string" ? key("tooLong", { maximum }) : collection ? key("tooMany", { maximum }) : key("tooBig", { maximum });
|
|
74
|
+
}
|
|
75
|
+
case "invalid_format":
|
|
76
|
+
if (issue.format === "email") return key("invalidEmail");
|
|
77
|
+
if (issue.format === "url") return key("invalidUrl");
|
|
78
|
+
if (issue.format === "date" || issue.format === "datetime") return key("invalidDate");
|
|
79
|
+
return key("invalidFormat");
|
|
80
|
+
case "invalid_value":
|
|
81
|
+
return issue.input === false || issue.input === void 0 ? key("required") : key("invalidChoice");
|
|
82
|
+
case "invalid_union":
|
|
83
|
+
case "invalid_key":
|
|
84
|
+
case "invalid_element":
|
|
85
|
+
return key("invalidChoice");
|
|
86
|
+
default:
|
|
87
|
+
return key("invalid");
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
function issuesToFields(issues, prefix) {
|
|
91
|
+
const fields2 = {};
|
|
92
|
+
for (const issue of issues) {
|
|
93
|
+
const name = pathName(issue.path);
|
|
94
|
+
if (!(name in fields2)) fields2[name] = { ...issueError(issue, prefix), source: "schema" };
|
|
95
|
+
}
|
|
96
|
+
return fields2;
|
|
97
|
+
}
|
|
98
|
+
function isFormProblem(value) {
|
|
99
|
+
return typeof value === "object" && value !== null && ("fields" in value || "code" in value || "status" in value);
|
|
100
|
+
}
|
|
101
|
+
function problemToErrors(problem, i18n = {}, known, codeFields = {}) {
|
|
102
|
+
const prefix = i18n.prefix ?? "form.errors";
|
|
103
|
+
const fields2 = {};
|
|
104
|
+
let unplaced = false;
|
|
105
|
+
const owner = problem.code ? codeFields[problem.code] : void 0;
|
|
106
|
+
if (owner && (!known || known(owner)) && !problem.fields?.[owner]) {
|
|
107
|
+
fields2[owner] = { key: `${prefix}.${problem.code}`, ...problem.detail ? { message: problem.detail } : {}, source: "server" };
|
|
108
|
+
if (!problem.fields || Object.keys(problem.fields).length === 0) return { fields: fields2, form: null };
|
|
109
|
+
}
|
|
110
|
+
for (const [name, refusal] of Object.entries(problem.fields ?? {})) {
|
|
111
|
+
if (known && !known(name)) {
|
|
112
|
+
unplaced = true;
|
|
113
|
+
continue;
|
|
114
|
+
}
|
|
115
|
+
const error = typeof refusal === "string" ? { key: refusal } : "key" in refusal ? refusal : { key: refusal.code ? KEY.test(refusal.code) ? refusal.code : `${prefix}.${refusal.code}` : `${prefix}.invalid`, message: refusal.message };
|
|
116
|
+
fields2[name] = { ...error, source: "server" };
|
|
117
|
+
}
|
|
118
|
+
const placed = Object.keys(fields2).length > 0;
|
|
119
|
+
if (placed && !unplaced) return { fields: fields2, form: null };
|
|
120
|
+
const code = problem.code ?? (problem.status === 0 ? "network" : problem.status && problem.status >= 500 ? "server" : "invalid");
|
|
121
|
+
const retryIn = problem.retryAt ? Math.max(1, Math.ceil((problem.retryAt - Date.now()) / 1e3)) : void 0;
|
|
122
|
+
return {
|
|
123
|
+
fields: fields2,
|
|
124
|
+
form: { key: KEY.test(code) ? code : `${i18n.apiPrefix ?? "apiErrors"}.${code}`, ...problem.detail ? { message: problem.detail } : {}, ...retryIn ? { values: { seconds: retryIn } } : {}, source: "server" }
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
// src/form/core.tsx
|
|
129
|
+
import { jsx } from "react/jsx-runtime";
|
|
130
|
+
var FormContext = React.createContext(null);
|
|
131
|
+
function useFormContext() {
|
|
132
|
+
const ctx = React.useContext(FormContext);
|
|
133
|
+
if (!ctx) throw new Error("A Krizaka form field must be inside its form's `form.Form`.");
|
|
134
|
+
return ctx;
|
|
135
|
+
}
|
|
136
|
+
var run = (schema, value) => schema["~standard"].validate(value);
|
|
137
|
+
function useKrizakaFormCore(definition, options = {}) {
|
|
138
|
+
const i18n = { ...definition.i18n, ...options.i18n };
|
|
139
|
+
const latest = React.useRef({ options, i18n });
|
|
140
|
+
React.useLayoutEffect(() => {
|
|
141
|
+
latest.current = { options, i18n };
|
|
142
|
+
});
|
|
143
|
+
const [error, setError] = React.useState(null);
|
|
144
|
+
const refused = React.useRef(false);
|
|
145
|
+
const api = useForm({
|
|
146
|
+
defaultValues: options.defaultValues ?? definition.defaultValues,
|
|
147
|
+
validators: {
|
|
148
|
+
// Every change: the schema, synchronously (an async refinement waits for the submit). Errors are computed for
|
|
149
|
+
// every field; each field shows its own once the person left it or tried to send.
|
|
150
|
+
onChange: ({ value }) => {
|
|
151
|
+
const result = run(definition.schema, value);
|
|
152
|
+
if (result instanceof Promise || !result.issues?.length) return void 0;
|
|
153
|
+
const fields2 = issuesToFields(result.issues, latest.current.i18n.prefix);
|
|
154
|
+
delete fields2[""];
|
|
155
|
+
return { fields: fields2 };
|
|
156
|
+
}
|
|
157
|
+
},
|
|
158
|
+
onSubmit: async ({ value }) => {
|
|
159
|
+
const parsed = await run(definition.schema, value);
|
|
160
|
+
if (parsed.issues?.length) {
|
|
161
|
+
apply({ fields: issuesToFields(parsed.issues, latest.current.i18n.prefix) });
|
|
162
|
+
return;
|
|
163
|
+
}
|
|
164
|
+
let result;
|
|
165
|
+
try {
|
|
166
|
+
result = await latest.current.options.onSubmit?.(parsed.value);
|
|
167
|
+
} catch (thrown) {
|
|
168
|
+
apply(isFormProblem(thrown) ? thrown : { code: "server" });
|
|
169
|
+
return;
|
|
170
|
+
}
|
|
171
|
+
if (result && typeof result === "object" && "ok" in result && result.ok === false) {
|
|
172
|
+
apply(result.problem);
|
|
173
|
+
return;
|
|
174
|
+
}
|
|
175
|
+
const data = result && typeof result === "object" && "ok" in result ? result.data : result;
|
|
176
|
+
latest.current.options.onSuccess?.(data);
|
|
177
|
+
}
|
|
178
|
+
});
|
|
179
|
+
const known = (name) => name in api.fieldInfo;
|
|
180
|
+
function apply(problem) {
|
|
181
|
+
refused.current = true;
|
|
182
|
+
const { fields: fields2, form } = problemToErrors(problem, latest.current.i18n, known, latest.current.options.codeFields);
|
|
183
|
+
for (const [name, fieldError] of Object.entries(fields2)) {
|
|
184
|
+
api.setFieldMeta(name, (meta) => ({ ...meta, isBlurred: true, errorMap: { ...meta.errorMap, onServer: fieldError } }));
|
|
185
|
+
}
|
|
186
|
+
setError(form);
|
|
187
|
+
}
|
|
188
|
+
const submit = React.useCallback(async () => {
|
|
189
|
+
setError(null);
|
|
190
|
+
refused.current = false;
|
|
191
|
+
for (const name of Object.keys(api.fieldInfo)) {
|
|
192
|
+
if (api.getFieldMeta(name)?.errorMap?.onServer) api.setFieldMeta(name, (meta) => ({ ...meta, errorMap: { ...meta.errorMap, onServer: void 0 } }));
|
|
193
|
+
}
|
|
194
|
+
await api.handleSubmit();
|
|
195
|
+
const state = api.state;
|
|
196
|
+
return !refused.current && state.isSubmitSuccessful && state.isValid && !Object.values(state.fieldMeta).some((meta) => meta?.errorMap?.onServer);
|
|
197
|
+
}, [api]);
|
|
198
|
+
return React.useMemo(() => ({ api, i18n, error, submit }), [api, error, submit, i18n.t, i18n.prefix, i18n.apiPrefix, i18n.locale]);
|
|
199
|
+
}
|
|
200
|
+
var UNCHECKED = /* @__PURE__ */ Symbol("unchecked");
|
|
201
|
+
function useKrizakaField(name, options = {}) {
|
|
202
|
+
const ctx = useFormContext();
|
|
203
|
+
const id = React.useId();
|
|
204
|
+
const [checked, setChecked] = React.useState(UNCHECKED);
|
|
205
|
+
const check = options.validateAsync;
|
|
206
|
+
const field = useField({
|
|
207
|
+
form: ctx.api,
|
|
208
|
+
name,
|
|
209
|
+
...check ? {
|
|
210
|
+
asyncDebounceMs: options.asyncDebounceMs ?? 350,
|
|
211
|
+
validators: {
|
|
212
|
+
onChangeAsyncDebounceMs: options.asyncDebounceMs ?? 350,
|
|
213
|
+
onChangeAsync: async ({ value: value2, signal }) => {
|
|
214
|
+
const answer = await check(value2, signal);
|
|
215
|
+
if (signal.aborted) return void 0;
|
|
216
|
+
if (!answer) {
|
|
217
|
+
setChecked(value2);
|
|
218
|
+
return void 0;
|
|
219
|
+
}
|
|
220
|
+
setChecked(UNCHECKED);
|
|
221
|
+
const error = typeof answer === "string" ? { key: answer } : answer;
|
|
222
|
+
return { ...error, immediate: true, source: "check" };
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
} : {}
|
|
226
|
+
});
|
|
227
|
+
const attempts = useStore(ctx.api.store, (state) => state.submissionAttempts);
|
|
228
|
+
const meta = field.state.meta;
|
|
229
|
+
const map = meta.errorMap;
|
|
230
|
+
const all = [map.onServer, map.onChange, map.onBlur, map.onSubmit, map.onMount].filter(Boolean);
|
|
231
|
+
const first = all[0];
|
|
232
|
+
const detail = typeof first === "string" ? { key: first } : first;
|
|
233
|
+
const visible = detail && (detail.immediate || detail.source === "server" || meta.isBlurred || attempts > 0) ? detail : void 0;
|
|
234
|
+
const value = field.state.value;
|
|
235
|
+
return {
|
|
236
|
+
id,
|
|
237
|
+
name,
|
|
238
|
+
value,
|
|
239
|
+
setValue: (next) => {
|
|
240
|
+
if (map.onServer) ctx.api.setFieldMeta(name, (m) => ({ ...m, errorMap: { ...m.errorMap, onServer: void 0 } }));
|
|
241
|
+
field.handleChange(next);
|
|
242
|
+
},
|
|
243
|
+
onBlur: () => field.handleBlur(),
|
|
244
|
+
error: visible ? formMessage(visible, ctx.i18n) : void 0,
|
|
245
|
+
errorDetail: visible,
|
|
246
|
+
validating: Boolean(meta.isValidating),
|
|
247
|
+
available: Boolean(check) && checked !== UNCHECKED && Object.is(checked, value) && !meta.isValidating && all.length === 0,
|
|
248
|
+
hintId: `${id}-hint`,
|
|
249
|
+
errorId: `${id}-error`,
|
|
250
|
+
locale: ctx.i18n.locale ?? "en",
|
|
251
|
+
api: field
|
|
252
|
+
};
|
|
253
|
+
}
|
|
254
|
+
function useKrizakaFieldArray(name) {
|
|
255
|
+
const ctx = useFormContext();
|
|
256
|
+
const field = useField({ form: ctx.api, name, mode: "array" });
|
|
257
|
+
const attempts = useStore(ctx.api.store, (state) => state.submissionAttempts);
|
|
258
|
+
const map = field.state.meta.errorMap;
|
|
259
|
+
const first = map.onServer ?? map.onChange ?? map.onSubmit;
|
|
260
|
+
return {
|
|
261
|
+
name,
|
|
262
|
+
items: field.state.value ?? [],
|
|
263
|
+
push: (item) => field.pushValue(item),
|
|
264
|
+
remove: (index) => void field.removeValue(index),
|
|
265
|
+
move: (from, to) => field.moveValue(from, to),
|
|
266
|
+
fieldName: (index, key) => `${name}[${index}]${key ? `.${key}` : ""}`,
|
|
267
|
+
error: first && (attempts > 0 || first.source === "server" || field.state.meta.isDirty) ? formMessage(first, ctx.i18n) : void 0
|
|
268
|
+
};
|
|
269
|
+
}
|
|
270
|
+
function useFormValue(name) {
|
|
271
|
+
const ctx = useFormContext();
|
|
272
|
+
return useStore(ctx.api.store, (state) => getPath(state.values, name));
|
|
273
|
+
}
|
|
274
|
+
function useFormStatus() {
|
|
275
|
+
const ctx = useFormContext();
|
|
276
|
+
const submitting = useStore(ctx.api.store, (s) => s.isSubmitting);
|
|
277
|
+
const succeeded = useStore(ctx.api.store, (s) => s.isSubmitSuccessful);
|
|
278
|
+
const attempts = useStore(ctx.api.store, (s) => s.submissionAttempts);
|
|
279
|
+
return { submitting, succeeded: succeeded && !ctx.error, attempts };
|
|
280
|
+
}
|
|
281
|
+
function getPath(source, path) {
|
|
282
|
+
let current = source;
|
|
283
|
+
for (const part of path.replace(/\[(\d+)\]/g, ".$1").split(".")) {
|
|
284
|
+
if (current === null || typeof current !== "object") return void 0;
|
|
285
|
+
current = current[part];
|
|
286
|
+
}
|
|
287
|
+
return current;
|
|
288
|
+
}
|
|
289
|
+
function firstVisibleError(ctx) {
|
|
290
|
+
if (ctx.error) return formMessage(ctx.error, ctx.i18n);
|
|
291
|
+
for (const meta of Object.values(ctx.api.state.fieldMeta)) {
|
|
292
|
+
const error = meta?.errorMap && Object.values(meta.errorMap).find(Boolean);
|
|
293
|
+
if (error) return formMessage(error, ctx.i18n);
|
|
294
|
+
}
|
|
295
|
+
return void 0;
|
|
296
|
+
}
|
|
297
|
+
function makeCreateKrizakaForm(components, Root) {
|
|
298
|
+
return function createKrizakaForm2(definition) {
|
|
299
|
+
function useForm2(options) {
|
|
300
|
+
const ctx = useKrizakaFormCore(definition, options);
|
|
301
|
+
const [store] = React.useState(() => createStore(ctx));
|
|
302
|
+
React.useLayoutEffect(() => store.set(ctx), [store, ctx]);
|
|
303
|
+
const Form = React.useCallback((props) => /* @__PURE__ */ jsx(Bound, { store, Root, props }), [store]);
|
|
304
|
+
return React.useMemo(
|
|
305
|
+
() => ({
|
|
306
|
+
...components,
|
|
307
|
+
Form,
|
|
308
|
+
submit: ctx.submit,
|
|
309
|
+
reset: (values) => ctx.api.reset(values),
|
|
310
|
+
setValue: (name, value) => ctx.api.setFieldValue(name, value),
|
|
311
|
+
getValues: () => ctx.api.state.values,
|
|
312
|
+
error: ctx.error,
|
|
313
|
+
api: ctx.api
|
|
314
|
+
}),
|
|
315
|
+
[ctx, Form]
|
|
316
|
+
);
|
|
317
|
+
}
|
|
318
|
+
return {
|
|
319
|
+
schema: definition.schema,
|
|
320
|
+
defaultValues: definition.defaultValues,
|
|
321
|
+
useForm: useForm2,
|
|
322
|
+
useFormWithAction: (action, options) => useForm2({ ...options, onSubmit: action })
|
|
323
|
+
};
|
|
324
|
+
};
|
|
325
|
+
}
|
|
326
|
+
function createStore(initial) {
|
|
327
|
+
let value = initial;
|
|
328
|
+
const listeners = /* @__PURE__ */ new Set();
|
|
329
|
+
return {
|
|
330
|
+
get: () => value,
|
|
331
|
+
set: (next) => {
|
|
332
|
+
if (Object.is(next, value)) return;
|
|
333
|
+
value = next;
|
|
334
|
+
listeners.forEach((listener) => listener());
|
|
335
|
+
},
|
|
336
|
+
subscribe: (listener) => {
|
|
337
|
+
listeners.add(listener);
|
|
338
|
+
return () => listeners.delete(listener);
|
|
339
|
+
}
|
|
340
|
+
};
|
|
341
|
+
}
|
|
342
|
+
function Bound({ store, Root, props }) {
|
|
343
|
+
const value = React.useSyncExternalStore(store.subscribe, store.get, store.get);
|
|
344
|
+
return /* @__PURE__ */ jsx(Root, { ...props, value });
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
// src/form/money.ts
|
|
348
|
+
import { currencyDigits } from "@krizaka/intl";
|
|
349
|
+
function decimalSeparator(locale) {
|
|
350
|
+
return new Intl.NumberFormat(locale).formatToParts(1.5).find((p) => p.type === "decimal")?.value ?? ".";
|
|
351
|
+
}
|
|
352
|
+
function currencySymbol(currency, locale) {
|
|
353
|
+
return new Intl.NumberFormat(locale, { style: "currency", currency }).formatToParts(0).find((p) => p.type === "currency")?.value ?? currency;
|
|
354
|
+
}
|
|
355
|
+
function parseMoney(text, currency, locale = "en") {
|
|
356
|
+
const digits = currencyDigits(currency);
|
|
357
|
+
const t = text.replace(/[\s\u00a0\u202f']/g, "");
|
|
358
|
+
if (!t) return void 0;
|
|
359
|
+
const negative = t.startsWith("-");
|
|
360
|
+
const body = negative ? t.slice(1) : t;
|
|
361
|
+
if (!/^[\d.,]+$/.test(body)) return null;
|
|
362
|
+
let int = body;
|
|
363
|
+
let frac = "";
|
|
364
|
+
const last = Math.max(body.lastIndexOf("."), body.lastIndexOf(","));
|
|
365
|
+
if (last >= 0) {
|
|
366
|
+
const tail = body.slice(last + 1);
|
|
367
|
+
if (body[last] === decimalSeparator(locale) || tail.length !== 3) {
|
|
368
|
+
if (tail.length > digits || !/^\d*$/.test(tail)) return null;
|
|
369
|
+
int = body.slice(0, last);
|
|
370
|
+
frac = tail;
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
if (!/^(\d*|\d{1,3}([.,]\d{3})+)$/.test(int)) return null;
|
|
374
|
+
int = int.replace(/[.,]/g, "");
|
|
375
|
+
if (!int && !frac) return null;
|
|
376
|
+
const units = BigInt(int || "0") * 10n ** BigInt(digits) + BigInt((frac || "").padEnd(digits, "0") || "0");
|
|
377
|
+
const value = Number(negative ? -units : units);
|
|
378
|
+
return Number.isSafeInteger(value) ? value : null;
|
|
379
|
+
}
|
|
380
|
+
function moneyInputText(minorUnits, currency, locale = "en") {
|
|
381
|
+
if (minorUnits === null || minorUnits === void 0 || !Number.isFinite(minorUnits)) return "";
|
|
382
|
+
const digits = currencyDigits(currency);
|
|
383
|
+
const sign = minorUnits < 0 ? "-" : "";
|
|
384
|
+
const abs = String(Math.abs(Math.trunc(minorUnits))).padStart(digits + 1, "0");
|
|
385
|
+
return digits === 0 ? sign + abs : `${sign}${abs.slice(0, -digits)}${decimalSeparator(locale)}${abs.slice(-digits)}`;
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
// src/date-picker/parts.ts
|
|
389
|
+
function isRealDate(value) {
|
|
390
|
+
if (!/^\d{4}-\d{2}-\d{2}$/.test(value)) return false;
|
|
391
|
+
const date = /* @__PURE__ */ new Date(`${value}T00:00:00Z`);
|
|
392
|
+
return !Number.isNaN(date.getTime()) && date.toISOString().slice(0, 10) === value;
|
|
393
|
+
}
|
|
394
|
+
function latestBirthDate(age, today = /* @__PURE__ */ new Date()) {
|
|
395
|
+
return new Date(Date.UTC(today.getFullYear() - age, today.getMonth(), today.getDate())).toISOString().slice(0, 10);
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
// src/native/form.tsx
|
|
399
|
+
import { Fragment, jsx as jsx2, jsxs } from "react/jsx-runtime";
|
|
400
|
+
var ChainContext = React2.createContext(null);
|
|
401
|
+
function useChain(ctx) {
|
|
402
|
+
const entries = React2.useRef([]);
|
|
403
|
+
return React2.useMemo(() => {
|
|
404
|
+
const mark = () => entries.current.forEach((e, i) => e.setLast(i === entries.current.length - 1));
|
|
405
|
+
return {
|
|
406
|
+
register: (entry) => {
|
|
407
|
+
entries.current = [...entries.current.filter((e) => e.key !== entry.key), entry];
|
|
408
|
+
mark();
|
|
409
|
+
return () => {
|
|
410
|
+
entries.current = entries.current.filter((e) => e.key !== entry.key);
|
|
411
|
+
mark();
|
|
412
|
+
};
|
|
413
|
+
},
|
|
414
|
+
next: (key) => {
|
|
415
|
+
const i = entries.current.findIndex((e) => e.key === key);
|
|
416
|
+
const following = entries.current[i + 1];
|
|
417
|
+
if (following) following.focus();
|
|
418
|
+
else void submitAndAnnounce(ctx);
|
|
419
|
+
}
|
|
420
|
+
};
|
|
421
|
+
}, [ctx]);
|
|
422
|
+
}
|
|
423
|
+
function useChained(override) {
|
|
424
|
+
const chain = React2.useContext(ChainContext);
|
|
425
|
+
const key = React2.useId();
|
|
426
|
+
const ref = React2.useRef(null);
|
|
427
|
+
const [last, setLast] = React2.useState(false);
|
|
428
|
+
React2.useLayoutEffect(() => chain?.register({ key, focus: () => ref.current?.focus(), setLast }), [chain, key]);
|
|
429
|
+
if (!chain || override.returnKeyType !== void 0 || override.onSubmitEditing !== void 0) return { inputRef: ref };
|
|
430
|
+
return {
|
|
431
|
+
inputRef: ref,
|
|
432
|
+
returnKeyType: last ? "go" : "next",
|
|
433
|
+
submitBehavior: last ? "blurAndSubmit" : "submit",
|
|
434
|
+
onSubmitEditing: () => chain.next(key)
|
|
435
|
+
};
|
|
436
|
+
}
|
|
437
|
+
async function submitAndAnnounce(ctx) {
|
|
438
|
+
if (await ctx.submit()) return;
|
|
439
|
+
const message = firstVisibleError(ctx);
|
|
440
|
+
if (message) AccessibilityInfo.announceForAccessibility(message);
|
|
441
|
+
}
|
|
442
|
+
function FormRoot({ value, children, style, scroll, "aria-label": label }) {
|
|
443
|
+
const chain = useChain(value);
|
|
444
|
+
const body = scroll ? /* @__PURE__ */ jsx2(KeyboardAvoidingView, { behavior: Platform.OS === "ios" ? "padding" : void 0, style: styles.screen, children: /* @__PURE__ */ jsx2(ScrollView, { role: "form", "aria-label": label, keyboardShouldPersistTaps: "handled", automaticallyAdjustKeyboardInsets: true, keyboardDismissMode: "interactive", contentContainerStyle: [styles.form, style], children }) }) : /* @__PURE__ */ jsx2(View, { role: "form", "aria-label": label, style: [styles.form, style], children });
|
|
445
|
+
return /* @__PURE__ */ jsx2(FormContext.Provider, { value, children: /* @__PURE__ */ jsx2(ChainContext.Provider, { value: chain, children: body }) });
|
|
446
|
+
}
|
|
447
|
+
function Suggestion({ field }) {
|
|
448
|
+
const suggestion = field.errorDetail?.suggestion;
|
|
449
|
+
if (!field.error || !suggestion) return null;
|
|
450
|
+
return /* @__PURE__ */ jsx2(Pressable, { role: "button", onPress: () => field.setValue(suggestion.value), hitSlop: 8, style: styles.suggestion, children: /* @__PURE__ */ jsx2(Txt, { variant: "label", tone: "accent", children: suggestion.label }) });
|
|
451
|
+
}
|
|
452
|
+
function Submit({ label, variant = "primary", brand, style }) {
|
|
453
|
+
const ctx = useFormContext();
|
|
454
|
+
const { submitting } = useFormStatus();
|
|
455
|
+
const mark = submitting && brand ? /* @__PURE__ */ jsx2(ProductMark, { id: brand, size: 18, animated: true }) : void 0;
|
|
456
|
+
return /* @__PURE__ */ jsx2(Button, { label, variant, loading: submitting && !brand, disabled: submitting, "aria-busy": submitting, icon: mark, style, onPress: () => void submitAndAnnounce(ctx) });
|
|
457
|
+
}
|
|
458
|
+
function FormError({ title, style }) {
|
|
459
|
+
const ctx = useFormContext();
|
|
460
|
+
const { theme, radius } = useTheme();
|
|
461
|
+
if (!ctx.error) return null;
|
|
462
|
+
return /* @__PURE__ */ jsxs(View, { role: "alert", "aria-live": "assertive", style: [styles.alert, { borderRadius: radius.lg, borderColor: theme.danger, backgroundColor: theme.dangerSoft }, style], children: [
|
|
463
|
+
title ? /* @__PURE__ */ jsx2(Txt, { variant: "label", children: title }) : null,
|
|
464
|
+
/* @__PURE__ */ jsx2(Txt, { tone: "secondary", children: formMessage(ctx.error, ctx.i18n) })
|
|
465
|
+
] });
|
|
466
|
+
}
|
|
467
|
+
function TextField({ name, label, hint, disabled, style, transform, validateAsync, asyncDebounceMs, availableHint, checkingLabel = "Checking", ...input }) {
|
|
468
|
+
const f = useKrizakaField(name, { validateAsync, asyncDebounceMs });
|
|
469
|
+
const chained = useChained(input);
|
|
470
|
+
const { theme } = useTheme();
|
|
471
|
+
const end = f.validating ? /* @__PURE__ */ jsx2(Spinner, { size: "sm", label: checkingLabel }) : f.available ? /* @__PURE__ */ jsx2(Txt, { "aria-hidden": true, style: { color: theme.successText }, children: "\u2713" }) : null;
|
|
472
|
+
return /* @__PURE__ */ jsxs(View, { style: [styles.field, style], children: [
|
|
473
|
+
/* @__PURE__ */ jsx2(
|
|
474
|
+
TextFieldControl,
|
|
475
|
+
{
|
|
476
|
+
label,
|
|
477
|
+
hint: f.available && availableHint ? hint ? `${availableHint} \xB7 ${hint}` : availableHint : hint,
|
|
478
|
+
error: f.error,
|
|
479
|
+
disabled,
|
|
480
|
+
value: f.value ?? "",
|
|
481
|
+
onValueChange: (text) => f.setValue(transform ? transform(text) : text),
|
|
482
|
+
onBlur: f.onBlur,
|
|
483
|
+
end,
|
|
484
|
+
...chained,
|
|
485
|
+
...input
|
|
486
|
+
}
|
|
487
|
+
),
|
|
488
|
+
/* @__PURE__ */ jsx2(Suggestion, { field: f })
|
|
489
|
+
] });
|
|
490
|
+
}
|
|
491
|
+
function TextArea({ name, label, hint, disabled, style, ...input }) {
|
|
492
|
+
const f = useKrizakaField(name);
|
|
493
|
+
return /* @__PURE__ */ jsx2(TextFieldControl, { multiline: true, label, hint, error: f.error, disabled, value: f.value ?? "", onValueChange: (t) => f.setValue(t), onBlur: f.onBlur, style, ...input });
|
|
494
|
+
}
|
|
495
|
+
function Checkbox({ name, label, accessibleLabel, disabled, style }) {
|
|
496
|
+
const f = useKrizakaField(name);
|
|
497
|
+
return /* @__PURE__ */ jsx2(
|
|
498
|
+
CheckboxControl,
|
|
499
|
+
{
|
|
500
|
+
label: accessibleLabel,
|
|
501
|
+
checked: f.value === true,
|
|
502
|
+
onCheckedChange: (c) => {
|
|
503
|
+
f.setValue(c);
|
|
504
|
+
f.onBlur();
|
|
505
|
+
},
|
|
506
|
+
error: f.error,
|
|
507
|
+
disabled,
|
|
508
|
+
style,
|
|
509
|
+
children: label
|
|
510
|
+
}
|
|
511
|
+
);
|
|
512
|
+
}
|
|
513
|
+
function Switch({ name, label, hint, disabled, style }) {
|
|
514
|
+
const f = useKrizakaField(name);
|
|
515
|
+
const { theme } = useTheme();
|
|
516
|
+
return /* @__PURE__ */ jsxs(View, { style: [styles.switchRow, style], children: [
|
|
517
|
+
/* @__PURE__ */ jsxs(View, { style: styles.switchText, children: [
|
|
518
|
+
/* @__PURE__ */ jsx2(Txt, { variant: "label", children: label }),
|
|
519
|
+
f.error ? /* @__PURE__ */ jsx2(Txt, { variant: "caption", tone: "danger", role: "alert", children: f.error }) : hint ? /* @__PURE__ */ jsx2(Txt, { variant: "caption", tone: "secondary", children: hint }) : null
|
|
520
|
+
] }),
|
|
521
|
+
/* @__PURE__ */ jsx2(
|
|
522
|
+
SwitchControl,
|
|
523
|
+
{
|
|
524
|
+
"aria-label": label,
|
|
525
|
+
accessibilityHint: f.error ?? hint,
|
|
526
|
+
value: f.value === true,
|
|
527
|
+
disabled,
|
|
528
|
+
onValueChange: (on) => {
|
|
529
|
+
f.setValue(on);
|
|
530
|
+
f.onBlur();
|
|
531
|
+
},
|
|
532
|
+
trackColor: { false: theme.surface3, true: theme.accent },
|
|
533
|
+
thumbColor: theme.textOnMedia,
|
|
534
|
+
...{ activeThumbColor: theme.textOnMedia },
|
|
535
|
+
ios_backgroundColor: theme.surface3
|
|
536
|
+
}
|
|
537
|
+
)
|
|
538
|
+
] });
|
|
539
|
+
}
|
|
540
|
+
function RadioGroup({ name, label, hint, disabled, style, options, scrollable }) {
|
|
541
|
+
const f = useKrizakaField(name);
|
|
542
|
+
return /* @__PURE__ */ jsxs(View, { style: [styles.field, style], children: [
|
|
543
|
+
/* @__PURE__ */ jsx2(Txt, { variant: "caption", tone: "secondary", style: styles.groupLabel, children: label }),
|
|
544
|
+
/* @__PURE__ */ jsx2(
|
|
545
|
+
Chip.Group,
|
|
546
|
+
{
|
|
547
|
+
type: "single",
|
|
548
|
+
"aria-label": label,
|
|
549
|
+
value: f.value ?? "",
|
|
550
|
+
disabled,
|
|
551
|
+
scrollable,
|
|
552
|
+
onValueChange: (v) => {
|
|
553
|
+
f.setValue(v);
|
|
554
|
+
f.onBlur();
|
|
555
|
+
},
|
|
556
|
+
children: options.map((o) => /* @__PURE__ */ jsx2(Chip, { value: o.value, disabled: o.disabled, children: o.label }, o.value))
|
|
557
|
+
}
|
|
558
|
+
),
|
|
559
|
+
f.error ? /* @__PURE__ */ jsx2(Txt, { variant: "caption", tone: "danger", role: "alert", children: f.error }) : hint ? /* @__PURE__ */ jsx2(Txt, { variant: "caption", tone: "secondary", children: hint }) : null
|
|
560
|
+
] });
|
|
561
|
+
}
|
|
562
|
+
function Select(props) {
|
|
563
|
+
return /* @__PURE__ */ jsx2(RadioGroup, { scrollable: props.options.length > 4, ...props });
|
|
564
|
+
}
|
|
565
|
+
function DateField({ name, label, hint, disabled, style, labels, autoComplete }) {
|
|
566
|
+
const f = useKrizakaField(name);
|
|
567
|
+
const chained = useChained({});
|
|
568
|
+
return /* @__PURE__ */ jsx2(DateFieldControl, { ...chained, returnKeyType: chained.returnKeyType, label, hint, error: f.error, disabled, style, labels, autoComplete, locale: f.locale, value: f.value ?? "", onValueChange: (v) => f.setValue(v), onBlur: f.onBlur });
|
|
569
|
+
}
|
|
570
|
+
function Money({ name, label, hint, disabled, style, currency, placeholder }) {
|
|
571
|
+
const f = useKrizakaField(name);
|
|
572
|
+
const [text, setText] = React2.useState(() => moneyInputText(f.value, currency, f.locale));
|
|
573
|
+
const [editing, setEditing] = React2.useState(false);
|
|
574
|
+
const chained = useChained({});
|
|
575
|
+
return /* @__PURE__ */ jsx2(
|
|
576
|
+
TextFieldControl,
|
|
577
|
+
{
|
|
578
|
+
...chained,
|
|
579
|
+
label,
|
|
580
|
+
hint,
|
|
581
|
+
error: f.error,
|
|
582
|
+
disabled,
|
|
583
|
+
style,
|
|
584
|
+
placeholder,
|
|
585
|
+
keyboardType: "decimal-pad",
|
|
586
|
+
value: editing ? text : moneyInputText(f.value, currency, f.locale),
|
|
587
|
+
end: /* @__PURE__ */ jsx2(Txt, { tone: "secondary", children: currencySymbol(currency, f.locale) }),
|
|
588
|
+
onFocus: () => {
|
|
589
|
+
setText(moneyInputText(f.value, currency, f.locale));
|
|
590
|
+
setEditing(true);
|
|
591
|
+
},
|
|
592
|
+
onValueChange: (next) => {
|
|
593
|
+
setText(next);
|
|
594
|
+
const units = parseMoney(next, currency, f.locale);
|
|
595
|
+
f.setValue(units === void 0 ? null : units === null ? Number.NaN : units);
|
|
596
|
+
},
|
|
597
|
+
onBlur: () => {
|
|
598
|
+
setEditing(false);
|
|
599
|
+
f.onBlur();
|
|
600
|
+
}
|
|
601
|
+
}
|
|
602
|
+
);
|
|
603
|
+
}
|
|
604
|
+
function FormField({ name, validateAsync, asyncDebounceMs, children }) {
|
|
605
|
+
return /* @__PURE__ */ jsx2(Fragment, { children: children(useKrizakaField(name, { validateAsync, asyncDebounceMs })) });
|
|
606
|
+
}
|
|
607
|
+
function FieldArray({ name, children }) {
|
|
608
|
+
return /* @__PURE__ */ jsx2(Fragment, { children: children(useKrizakaFieldArray(name)) });
|
|
609
|
+
}
|
|
610
|
+
function Watch({ name, children }) {
|
|
611
|
+
return /* @__PURE__ */ jsx2(Fragment, { children: children(useFormValue(name)) });
|
|
612
|
+
}
|
|
613
|
+
var fields = { TextField, TextArea, Checkbox, Switch, RadioGroup, Select, DateField, Money, Field: FormField, FieldArray, Watch, Submit, FormError };
|
|
614
|
+
var createKrizakaForm = makeCreateKrizakaForm(fields, FormRoot);
|
|
615
|
+
var styles = StyleSheet.create({
|
|
616
|
+
form: { gap: 16, alignSelf: "stretch" },
|
|
617
|
+
screen: { flex: 1, alignSelf: "stretch" },
|
|
618
|
+
field: { gap: 6, alignSelf: "stretch" },
|
|
619
|
+
suggestion: { alignSelf: "flex-start", minHeight: 32, justifyContent: "center" },
|
|
620
|
+
alert: { borderWidth: 1, padding: 12, gap: 4 },
|
|
621
|
+
switchRow: { flexDirection: "row", alignItems: "center", justifyContent: "space-between", gap: 16, minHeight: 44 },
|
|
622
|
+
switchText: { flex: 1, gap: 2 },
|
|
623
|
+
groupLabel: { fontWeight: "600" }
|
|
624
|
+
});
|
|
625
|
+
export {
|
|
626
|
+
Checkbox,
|
|
627
|
+
DateField,
|
|
628
|
+
FieldArray,
|
|
629
|
+
FormError,
|
|
630
|
+
FormField,
|
|
631
|
+
Money,
|
|
632
|
+
RadioGroup,
|
|
633
|
+
Select,
|
|
634
|
+
Submit,
|
|
635
|
+
Switch,
|
|
636
|
+
TextArea,
|
|
637
|
+
TextField,
|
|
638
|
+
Watch,
|
|
639
|
+
createKrizakaForm,
|
|
640
|
+
formMessage,
|
|
641
|
+
formMessages,
|
|
642
|
+
isRealDate,
|
|
643
|
+
latestBirthDate,
|
|
644
|
+
useFormContext,
|
|
645
|
+
useFormStatus,
|
|
646
|
+
useFormValue,
|
|
647
|
+
useKrizakaField,
|
|
648
|
+
useKrizakaFieldArray
|
|
649
|
+
};
|
|
650
|
+
//# sourceMappingURL=form.js.map
|