asma-ui-core 3.90.1 → 3.91.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/forms/createAsmaFieldController.js +70 -0
- package/dist/forms/createAsmaForms.js +12 -0
- package/dist/forms/focusFormField.js +14 -0
- package/dist/forms/getDirtyValues.js +24 -0
- package/dist/forms/index.d.ts +2 -0
- package/dist/forms/index.js +12 -0
- package/dist/forms/submitChanged.js +11 -0
- package/dist/forms/touchInvalidFields.js +12 -0
- package/dist/forms/useAsmaForm.js +16 -0
- package/dist/src/forms/createAsmaFieldController.d.ts +28 -0
- package/dist/src/forms/createAsmaForms.d.ts +9 -0
- package/dist/src/forms/focusFormField.d.ts +1 -0
- package/dist/src/forms/getDirtyValues.d.ts +1 -0
- package/dist/src/forms/index.d.ts +8 -0
- package/dist/src/forms/submitChanged.d.ts +6 -0
- package/dist/src/forms/touchInvalidFields.d.ts +2 -0
- package/dist/src/forms/types.d.ts +6 -0
- package/dist/src/forms/useAsmaForm.d.ts +2 -0
- package/package.json +20 -2
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { useCallback as y, useEffect as u, useRef as F } from "react";
|
|
2
|
+
import { useController as S, useFormContext as x } from "react-hook-form";
|
|
3
|
+
var B = 150, P = 20, U = "onAutoFillStart";
|
|
4
|
+
function h(r) {
|
|
5
|
+
return r == null ? !0 : typeof r == "string" ? r.trim().length === 0 : typeof r == "number" ? Number.isNaN(r) : !1;
|
|
6
|
+
}
|
|
7
|
+
function V(r) {
|
|
8
|
+
return !(!r || r.required === !1 || r.required == null);
|
|
9
|
+
}
|
|
10
|
+
function X({ getLanguageCode: r, browserAutofill: l = !1 }) {
|
|
11
|
+
return function({ name: n, control: p, rules: f, helperText: I, requestFocus: s, render: L }) {
|
|
12
|
+
const d = x(), { field: t, fieldState: i } = S({
|
|
13
|
+
name: n,
|
|
14
|
+
control: p ?? d.control,
|
|
15
|
+
rules: f
|
|
16
|
+
}), { trigger: a, setValue: A } = d, { ref: o } = t, C = V(f), N = r(), v = F(null), g = (e) => {
|
|
17
|
+
e != null && e !== t.value && t.onChange(e), a(n);
|
|
18
|
+
};
|
|
19
|
+
u(() => {
|
|
20
|
+
s && o({ focus: s });
|
|
21
|
+
}, [o, s]), u(() => {
|
|
22
|
+
i.invalid && a(n);
|
|
23
|
+
}, [N]), u(() => {
|
|
24
|
+
l && (h(t.value) || a(n));
|
|
25
|
+
}, []), u(() => {
|
|
26
|
+
if (!l) return;
|
|
27
|
+
let e = 0;
|
|
28
|
+
const c = setInterval(() => {
|
|
29
|
+
e += 1;
|
|
30
|
+
const T = v.current;
|
|
31
|
+
T?.matches(":-webkit-autofill") ? (g(T.value), clearInterval(c)) : e >= P && clearInterval(c);
|
|
32
|
+
}, B);
|
|
33
|
+
return () => clearInterval(c);
|
|
34
|
+
}, []);
|
|
35
|
+
const _ = () => {
|
|
36
|
+
if (!C && h(t.value)) {
|
|
37
|
+
A(n, t.value, {
|
|
38
|
+
shouldTouch: !0,
|
|
39
|
+
shouldValidate: !1
|
|
40
|
+
});
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
t.onBlur(), a(n);
|
|
44
|
+
};
|
|
45
|
+
u(() => {
|
|
46
|
+
i.error && a(n);
|
|
47
|
+
}, [t.value]);
|
|
48
|
+
const R = (...e) => {
|
|
49
|
+
t.onChange(...e);
|
|
50
|
+
}, E = (e) => {
|
|
51
|
+
l && e.animationName === U && g(e.target?.value);
|
|
52
|
+
}, M = y((e) => {
|
|
53
|
+
v.current = e, typeof o == "function" && o(e);
|
|
54
|
+
}, [o]), O = i.error?.message, m = !!i.error && (!l || i.isTouched);
|
|
55
|
+
return L({
|
|
56
|
+
value: t.value,
|
|
57
|
+
onChange: R,
|
|
58
|
+
onBlur: _,
|
|
59
|
+
onAnimationStart: E,
|
|
60
|
+
name: t.name,
|
|
61
|
+
ref: l ? M : o,
|
|
62
|
+
error: m,
|
|
63
|
+
helperText: m ? O : I,
|
|
64
|
+
reserveHelperText: f != null
|
|
65
|
+
});
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
export {
|
|
69
|
+
X as createAsmaFieldController
|
|
70
|
+
};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { createAsmaFieldController as m } from "./createAsmaFieldController.js";
|
|
2
|
+
import { submitChanged as n } from "./submitChanged.js";
|
|
3
|
+
function u(t) {
|
|
4
|
+
const e = t.browserAutofill === !0, r = (o, i, l) => n(o, i, l, { touchInvalidFields: e });
|
|
5
|
+
return {
|
|
6
|
+
AsmaFieldController: m(t),
|
|
7
|
+
submitChanged: r
|
|
8
|
+
};
|
|
9
|
+
}
|
|
10
|
+
export {
|
|
11
|
+
u as createAsmaForms
|
|
12
|
+
};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
var n = 'input, textarea, select, button, [contenteditable="true"], [contenteditable=""], [tabindex]';
|
|
2
|
+
function o(t) {
|
|
3
|
+
return t.matches(n);
|
|
4
|
+
}
|
|
5
|
+
function c(t) {
|
|
6
|
+
const e = document.getElementById(t) ?? Array.from(document.querySelectorAll("[data-testid]")).find((r) => r.dataset.testid === t);
|
|
7
|
+
return e ? ((o(e) ? e : e.querySelector(n) ?? e).focus({ preventScroll: !0 }), e.scrollIntoView({
|
|
8
|
+
behavior: "instant",
|
|
9
|
+
block: "center"
|
|
10
|
+
}), !0) : !1;
|
|
11
|
+
}
|
|
12
|
+
export {
|
|
13
|
+
c as focusFormField
|
|
14
|
+
};
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
function u(e, f) {
|
|
2
|
+
if (e === !0) return { ...f };
|
|
3
|
+
if (!e || typeof e != "object") return {};
|
|
4
|
+
const c = {};
|
|
5
|
+
for (const n of Object.keys(e)) {
|
|
6
|
+
const o = e[n], t = f[n];
|
|
7
|
+
if (o === !0) {
|
|
8
|
+
c[n] = t;
|
|
9
|
+
continue;
|
|
10
|
+
}
|
|
11
|
+
if (Array.isArray(t)) {
|
|
12
|
+
o && (c[n] = t);
|
|
13
|
+
continue;
|
|
14
|
+
}
|
|
15
|
+
if (o && typeof o == "object" && t != null && typeof t == "object") {
|
|
16
|
+
const r = u(o, t);
|
|
17
|
+
Object.keys(r).length > 0 && (c[n] = r);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
return c;
|
|
21
|
+
}
|
|
22
|
+
export {
|
|
23
|
+
u as getDirtyValues
|
|
24
|
+
};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { getDirtyValues as r } from "./getDirtyValues.js";
|
|
2
|
+
import { touchInvalidFields as t } from "./touchInvalidFields.js";
|
|
3
|
+
import { createAsmaForms as e } from "./createAsmaForms.js";
|
|
4
|
+
import { focusFormField as f } from "./focusFormField.js";
|
|
5
|
+
import { useAsmaForm as a } from "./useAsmaForm.js";
|
|
6
|
+
export {
|
|
7
|
+
e as createAsmaForms,
|
|
8
|
+
f as focusFormField,
|
|
9
|
+
r as getDirtyValues,
|
|
10
|
+
t as touchInvalidFields,
|
|
11
|
+
a as useAsmaForm
|
|
12
|
+
};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { getDirtyValues as o } from "./getDirtyValues.js";
|
|
2
|
+
import { touchInvalidFields as n } from "./touchInvalidFields.js";
|
|
3
|
+
async function f(t, a, i, e = {}) {
|
|
4
|
+
if (!(i ? await t.trigger() : await t.trigger(void 0, { shouldFocus: !0 })))
|
|
5
|
+
return e.touchInvalidFields && n(t), i?.(), !1;
|
|
6
|
+
const r = t.getValues(), { dirtyFields: u } = t.control._formState, l = await a(o(u, r), r);
|
|
7
|
+
return t.reset(l ?? r), !0;
|
|
8
|
+
}
|
|
9
|
+
export {
|
|
10
|
+
f as submitChanged
|
|
11
|
+
};
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { useForm as o } from "react-hook-form";
|
|
2
|
+
var e = {
|
|
3
|
+
mode: "onBlur",
|
|
4
|
+
reValidateMode: "onSubmit",
|
|
5
|
+
criteriaMode: "all",
|
|
6
|
+
shouldFocusError: !0
|
|
7
|
+
};
|
|
8
|
+
function t(r) {
|
|
9
|
+
return o({
|
|
10
|
+
...r,
|
|
11
|
+
...e
|
|
12
|
+
});
|
|
13
|
+
}
|
|
14
|
+
export {
|
|
15
|
+
t as useAsmaForm
|
|
16
|
+
};
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { ReactElement, ReactNode } from 'react';
|
|
2
|
+
import { Control, ControllerRenderProps, FieldPath, FieldPathValue, FieldValues, RegisterOptions } from 'react-hook-form';
|
|
3
|
+
import { AsmaFormsOptions } from './types';
|
|
4
|
+
export interface AutofillAnimationEvent {
|
|
5
|
+
animationName: string;
|
|
6
|
+
target: EventTarget | null;
|
|
7
|
+
}
|
|
8
|
+
export interface AsmaFieldRenderProps<TValue = unknown> {
|
|
9
|
+
value: TValue;
|
|
10
|
+
onChange: (...event: unknown[]) => void;
|
|
11
|
+
onBlur: () => void;
|
|
12
|
+
onAnimationStart: (event: AutofillAnimationEvent) => void;
|
|
13
|
+
name: string;
|
|
14
|
+
ref: ControllerRenderProps['ref'];
|
|
15
|
+
error: boolean;
|
|
16
|
+
helperText: ReactNode;
|
|
17
|
+
reserveHelperText: boolean;
|
|
18
|
+
}
|
|
19
|
+
export interface AsmaFieldControllerProps<TFieldValues extends FieldValues = FieldValues, TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>> {
|
|
20
|
+
name: TName;
|
|
21
|
+
control?: Control<TFieldValues>;
|
|
22
|
+
rules?: Omit<RegisterOptions<TFieldValues, TName>, 'valueAsNumber' | 'valueAsDate' | 'setValueAs' | 'disabled'>;
|
|
23
|
+
helperText?: ReactNode;
|
|
24
|
+
/** For controls that own their input ref (comboboxes, rich editors): RHF focuses the field through this instead of a DOM node. */
|
|
25
|
+
requestFocus?: () => void;
|
|
26
|
+
render: (props: AsmaFieldRenderProps<FieldPathValue<TFieldValues, TName>>) => ReactElement;
|
|
27
|
+
}
|
|
28
|
+
export declare function createAsmaFieldController({ getLanguageCode, browserAutofill, }: AsmaFormsOptions): <TFieldValues extends FieldValues = FieldValues, TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>>({ name, control, rules, helperText, requestFocus, render, }: AsmaFieldControllerProps<TFieldValues, TName>) => ReactElement;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { FieldValues, UseFormReturn } from 'react-hook-form';
|
|
2
|
+
import { createAsmaFieldController } from './createAsmaFieldController';
|
|
3
|
+
import { SubmitChangedPatch } from './submitChanged';
|
|
4
|
+
import { AsmaFormsOptions } from './types';
|
|
5
|
+
export interface AsmaFormsApi {
|
|
6
|
+
AsmaFieldController: ReturnType<typeof createAsmaFieldController>;
|
|
7
|
+
submitChanged: <TFieldValues extends FieldValues>(methods: UseFormReturn<TFieldValues>, onPatch: SubmitChangedPatch<TFieldValues>, onInvalid?: () => void) => Promise<boolean>;
|
|
8
|
+
}
|
|
9
|
+
export declare function createAsmaForms(options: AsmaFormsOptions): AsmaFormsApi;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function focusFormField(fieldDomId: string): boolean;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function getDirtyValues<T extends Record<string, unknown>>(dirtyFields: Partial<Record<keyof T, unknown>> | boolean | undefined, values: T): Partial<T>;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export { createAsmaForms } from './createAsmaForms';
|
|
2
|
+
export { focusFormField } from './focusFormField';
|
|
3
|
+
export { getDirtyValues } from './getDirtyValues';
|
|
4
|
+
export { touchInvalidFields } from './touchInvalidFields';
|
|
5
|
+
export { useAsmaForm } from './useAsmaForm';
|
|
6
|
+
export type { AsmaFieldControllerProps, AsmaFieldRenderProps, AutofillAnimationEvent } from './createAsmaFieldController';
|
|
7
|
+
export type { SubmitChangedPatch } from './submitChanged';
|
|
8
|
+
export type { AsmaFormsOptions } from './types';
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { FieldValues, UseFormReturn } from 'react-hook-form';
|
|
2
|
+
export type SubmitChangedPatch<TFieldValues extends FieldValues> = (dirty: Partial<TFieldValues>, all: TFieldValues) => Promise<TFieldValues | void>;
|
|
3
|
+
export interface SubmitChangedOptions {
|
|
4
|
+
touchInvalidFields?: boolean;
|
|
5
|
+
}
|
|
6
|
+
export declare function submitChanged<TFieldValues extends FieldValues>(methods: UseFormReturn<TFieldValues>, onPatch: SubmitChangedPatch<TFieldValues>, onInvalid?: () => void, options?: SubmitChangedOptions): Promise<boolean>;
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export interface AsmaFormsOptions {
|
|
2
|
+
/** Read during every render; a change re-validates already-invalid fields so their message re-renders in the new language. */
|
|
3
|
+
getLanguageCode: () => string;
|
|
4
|
+
/** Login/auth forms: mount validation, autofill sync, merged ref, touched-gated errors, touch-on-submit. */
|
|
5
|
+
browserAutofill?: boolean;
|
|
6
|
+
}
|
package/package.json
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"publishConfig": {
|
|
4
4
|
"access": "public"
|
|
5
5
|
},
|
|
6
|
-
"version": "3.
|
|
6
|
+
"version": "3.91.0",
|
|
7
7
|
"type": "module",
|
|
8
8
|
"sideEffects": [
|
|
9
9
|
"**/*.css",
|
|
@@ -26,6 +26,9 @@
|
|
|
26
26
|
],
|
|
27
27
|
"icons/*": [
|
|
28
28
|
"dist/src/components/icons/*/index.d.ts"
|
|
29
|
+
],
|
|
30
|
+
"forms": [
|
|
31
|
+
"dist/src/forms/index.d.ts"
|
|
29
32
|
]
|
|
30
33
|
}
|
|
31
34
|
},
|
|
@@ -45,6 +48,11 @@
|
|
|
45
48
|
"types": "./dist/src/components/icons/*/index.d.ts",
|
|
46
49
|
"default": "./dist/components/icons/*/index.js"
|
|
47
50
|
},
|
|
51
|
+
"./forms": {
|
|
52
|
+
"types": "./dist/src/forms/index.d.ts",
|
|
53
|
+
"import": "./dist/forms/index.js",
|
|
54
|
+
"default": "./dist/forms/index.js"
|
|
55
|
+
},
|
|
48
56
|
"./dist/style.css": "./dist/style.css",
|
|
49
57
|
"./tw-configs/twConfigs.json": "./tw-configs/twConfigs.json",
|
|
50
58
|
"./src/styles/breakpoints": "./src/styles/_breakpoints.scss"
|
|
@@ -102,6 +110,8 @@
|
|
|
102
110
|
"@storybook/addon-themes": "^10.3.3",
|
|
103
111
|
"@storybook/addon-vitest": "10.3.3",
|
|
104
112
|
"@storybook/react-vite": "10.3.3",
|
|
113
|
+
"@testing-library/dom": "^10.4.1",
|
|
114
|
+
"@testing-library/react": "^16.3.2",
|
|
105
115
|
"@tippyjs/react": "^4.2.6",
|
|
106
116
|
"@types/node": "^24.12.4",
|
|
107
117
|
"@types/react": "^18.3.23",
|
|
@@ -120,10 +130,12 @@
|
|
|
120
130
|
"eslint-plugin-react-you-might-not-need-an-effect": "^0.7.0",
|
|
121
131
|
"eslint-plugin-regexp": "^2.10.0",
|
|
122
132
|
"eslint-plugin-storybook": "10.3.3",
|
|
133
|
+
"jsdom": "^30.0.1",
|
|
123
134
|
"playwright": "^1.57.0",
|
|
124
135
|
"react": "^18.3.1",
|
|
125
136
|
"react-copy-to-clipboard": "^5.1.0",
|
|
126
137
|
"react-dom": "^18.3.1",
|
|
138
|
+
"react-hook-form": "^7.89.0",
|
|
127
139
|
"react-hot-toast": "^2.4.1",
|
|
128
140
|
"sass": "^1.98.0",
|
|
129
141
|
"storybook": "10.3.3",
|
|
@@ -142,7 +154,13 @@
|
|
|
142
154
|
},
|
|
143
155
|
"peerDependencies": {
|
|
144
156
|
"react": "^18.0.0 || ^19.0.0",
|
|
145
|
-
"react-dom": "^18.0.0 || ^19.0.0"
|
|
157
|
+
"react-dom": "^18.0.0 || ^19.0.0",
|
|
158
|
+
"react-hook-form": "^7.85.0"
|
|
159
|
+
},
|
|
160
|
+
"peerDependenciesMeta": {
|
|
161
|
+
"react-hook-form": {
|
|
162
|
+
"optional": true
|
|
163
|
+
}
|
|
146
164
|
},
|
|
147
165
|
"repository": {
|
|
148
166
|
"type": "git",
|