haze-ui 1.11.1 → 1.12.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/README.md
CHANGED
|
@@ -70,9 +70,16 @@ function NameField({ form }) {
|
|
|
70
70
|
```
|
|
71
71
|
|
|
72
72
|
The handle is a real `Control`: reads subscribe to the field (sibling
|
|
73
|
-
fields stay isolated), writes go through
|
|
74
|
-
|
|
75
|
-
|
|
73
|
+
fields stay isolated), and writes go through react-f0rm's user-change
|
|
74
|
+
channel (`changeValueByPath`, ≥ 0.7) — a control write fires exactly the
|
|
75
|
+
validation a user typing into the field would fire: the field's effective
|
|
76
|
+
`mode` (a `FormItem`/`useField` per-field override included) and the
|
|
77
|
+
form's `reValidateMode`. With the default `mode: 'onSubmit'` +
|
|
78
|
+
`reValidateMode: 'onChange'`, typing through a bridged control after a
|
|
79
|
+
failed submit re-validates per keystroke and clears the error as soon as
|
|
80
|
+
the value is valid — no blur, no resubmit. Functional updates evaluate
|
|
81
|
+
against the live form value. `reset(form, newValues)` re-seeds every
|
|
82
|
+
bridged control with no remounting.
|
|
76
83
|
|
|
77
84
|
### FormItem: label, errors and aria wiring
|
|
78
85
|
|
|
@@ -137,6 +144,45 @@ submit needed. `mode` accepts `'onSubmit'`, `'onBlur'`, `'onChange'`,
|
|
|
137
144
|
`'onTouched'` or `'all'`; only this field's schedule changes, the rest
|
|
138
145
|
of the form keeps its own `mode`.
|
|
139
146
|
|
|
147
|
+
#### `validateDebounce` / `delayError` / `rules` (react-f0rm ≥ 0.6)
|
|
148
|
+
|
|
149
|
+
`FormItem` passes these field-level options straight through to
|
|
150
|
+
react-f0rm's `useField`:
|
|
151
|
+
|
|
152
|
+
- `validateDebounce={300}` — debounce this field's validation kicks:
|
|
153
|
+
only the last kick inside the window runs the validator (e.g. keeps a
|
|
154
|
+
per-keystroke async validator from firing while the user types fast).
|
|
155
|
+
While the timer is pending the field counts as validating, so
|
|
156
|
+
`trigger`/submit wait it out.
|
|
157
|
+
- `delayError={500}` — delay *showing* a newly appearing error in the
|
|
158
|
+
rendered error span (and the binding's `invalid`/`errors`). The form's
|
|
159
|
+
error state stays immediate — submit and `getError` still gate on it.
|
|
160
|
+
An error that clears inside the window never shows.
|
|
161
|
+
- `rules={{ required: 'Email is required', minLength: 4, pattern: { value: /@/, message: 'Must be an email' } }}`
|
|
162
|
+
— declarative constraints (a subset of react-hook-form's `register`
|
|
163
|
+
rules) compiled into a validator that runs *before* `validate`; both
|
|
164
|
+
sources' errors merge into the field's error list, rules errors ahead.
|
|
165
|
+
|
|
166
|
+
```jsx
|
|
167
|
+
<FormItem
|
|
168
|
+
form={form}
|
|
169
|
+
name="email"
|
|
170
|
+
label="Email"
|
|
171
|
+
validateDebounce={300}
|
|
172
|
+
delayError={500}
|
|
173
|
+
rules={{ required: 'Email is required' }}
|
|
174
|
+
validate={(v) => (v.includes('@') ? undefined : 'must be an email')}
|
|
175
|
+
>
|
|
176
|
+
{({ id, errorId, invalid, control }) => (
|
|
177
|
+
<Input id={id} value={control} aria-invalid={invalid} aria-describedby={errorId} />
|
|
178
|
+
)}
|
|
179
|
+
</FormItem>
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
All three are optional; omit them and the field behaves exactly as
|
|
183
|
+
before (immediate validation per the form's `mode`, immediate error
|
|
184
|
+
display, `validate`-only).
|
|
185
|
+
|
|
140
186
|
## Related Projects
|
|
141
187
|
|
|
142
188
|
- [react-use-control](https://github.com/wmzy/react-use-control)
|
package/dist/form/FormItem.js
CHANGED
|
@@ -4,40 +4,43 @@ import { useFormControl as t } from "./useFormControl.js";
|
|
|
4
4
|
/* empty css */
|
|
5
5
|
import { jsx as n, jsxs as r } from "react/jsx-runtime";
|
|
6
6
|
import { useId as i } from "react";
|
|
7
|
-
import { useField as a
|
|
7
|
+
import { useField as a } from "react-f0rm";
|
|
8
8
|
//#region src/lib/form/FormItem.tsx
|
|
9
|
-
var
|
|
10
|
-
function
|
|
11
|
-
let
|
|
12
|
-
form:
|
|
13
|
-
name:
|
|
14
|
-
validate:
|
|
15
|
-
mode:
|
|
16
|
-
|
|
9
|
+
var o = "haze-FormItem__item", s = "haze-FormItem__labelText", c = "haze-FormItem__errorText";
|
|
10
|
+
function l({ form: l, name: u, label: d, validate: f, mode: p, validateDebounce: m, delayError: h, rules: g, className: _, children: v }) {
|
|
11
|
+
let y = `haze-field-${i()}`, b = `${y}-error`, { onBlur: x, errors: S } = a({
|
|
12
|
+
form: l,
|
|
13
|
+
name: u,
|
|
14
|
+
validate: f,
|
|
15
|
+
mode: p,
|
|
16
|
+
validateDebounce: m,
|
|
17
|
+
delayError: h,
|
|
18
|
+
rules: g
|
|
19
|
+
}), C = t(l, u), w = S.length > 0;
|
|
17
20
|
return /* @__PURE__ */ r("div", {
|
|
18
|
-
className: e([
|
|
21
|
+
className: e([o, _]),
|
|
19
22
|
children: [
|
|
20
|
-
|
|
21
|
-
htmlFor:
|
|
22
|
-
className: e(
|
|
23
|
-
children:
|
|
23
|
+
d !== void 0 && /* @__PURE__ */ n("label", {
|
|
24
|
+
htmlFor: y,
|
|
25
|
+
className: e(s),
|
|
26
|
+
children: d
|
|
24
27
|
}),
|
|
25
|
-
|
|
26
|
-
id:
|
|
27
|
-
errorId:
|
|
28
|
-
invalid:
|
|
29
|
-
errors:
|
|
30
|
-
onBlur:
|
|
31
|
-
control:
|
|
28
|
+
v({
|
|
29
|
+
id: y,
|
|
30
|
+
errorId: b,
|
|
31
|
+
invalid: w,
|
|
32
|
+
errors: S,
|
|
33
|
+
onBlur: x,
|
|
34
|
+
control: C
|
|
32
35
|
}),
|
|
33
|
-
|
|
34
|
-
id:
|
|
36
|
+
w && /* @__PURE__ */ n("span", {
|
|
37
|
+
id: b,
|
|
35
38
|
role: "alert",
|
|
36
|
-
className: e(
|
|
37
|
-
children:
|
|
39
|
+
className: e(c),
|
|
40
|
+
children: S[0].message
|
|
38
41
|
})
|
|
39
42
|
]
|
|
40
43
|
});
|
|
41
44
|
}
|
|
42
45
|
//#endregion
|
|
43
|
-
export {
|
|
46
|
+
export { l as default };
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { useControl as e, useThru as t } from "react-use-control";
|
|
2
2
|
import { useMemo as n } from "react";
|
|
3
|
-
import {
|
|
3
|
+
import { changeValueByPath as r, getValueByPath as i, useValueByPath as a } from "react-f0rm";
|
|
4
4
|
//#region src/lib/form/useFormControl.ts
|
|
5
5
|
var o = /* @__PURE__ */ new Map();
|
|
6
6
|
function s(e) {
|
|
@@ -46,8 +46,8 @@ function l(e) {
|
|
|
46
46
|
function u(n, o) {
|
|
47
47
|
let s = l(o), c = a(n, s), [, , u] = e();
|
|
48
48
|
return t(u, () => [c, (e) => {
|
|
49
|
-
let t =
|
|
50
|
-
|
|
49
|
+
let t = i(n, s), a = typeof e == "function" ? e(t) : e;
|
|
50
|
+
r(n, s, a);
|
|
51
51
|
}]);
|
|
52
52
|
}
|
|
53
53
|
//#endregion
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { ReactNode } from 'react';
|
|
2
|
-
import type { FieldError, FieldPath, Name, ValidationMode } from 'react-f0rm';
|
|
2
|
+
import type { FieldError, FieldPath, FieldRules, Name, ValidationMode } from 'react-f0rm';
|
|
3
3
|
import type { Control } from 'react-use-control';
|
|
4
4
|
import type { FormInstance, PathValueOf } from './useFormControl';
|
|
5
5
|
/**
|
|
@@ -41,6 +41,23 @@ export type FormItemProps<TValues extends Record<string, any> = any, P extends F
|
|
|
41
41
|
* of the form's `mode`; other fields are unaffected. Omit to keep the
|
|
42
42
|
* form-wide behavior. */
|
|
43
43
|
mode?: ValidationMode;
|
|
44
|
+
/** Milliseconds to debounce this field's validation kicks (react-f0rm
|
|
45
|
+
* ≥0.6): only the last kick inside the window runs the validator — e.g.
|
|
46
|
+
* `300` keeps a fast typist from firing a per-keystroke async validator.
|
|
47
|
+
* While the timer is pending the field counts as validating. Omit for
|
|
48
|
+
* immediate validation (react-f0rm's default). */
|
|
49
|
+
validateDebounce?: number;
|
|
50
|
+
/** Milliseconds to delay *showing* a newly appearing error in the render
|
|
51
|
+
* layer (react-f0rm ≥0.6): the form's error state stays immediate —
|
|
52
|
+
* submit/trigger still gate on it — only the rendered error span and
|
|
53
|
+
* `invalid` wait out the window. An error that clears inside the window
|
|
54
|
+
* never shows. Omit for immediate display. */
|
|
55
|
+
delayError?: number;
|
|
56
|
+
/** Declarative rules (required/min/max/minLength/maxLength/pattern;
|
|
57
|
+
* react-f0rm ≥0.6), compiled into a validator that runs *before*
|
|
58
|
+
* `validate` — both sources' errors merge, rules errors ahead. Omit for
|
|
59
|
+
* `validate`-only validation. */
|
|
60
|
+
rules?: FieldRules;
|
|
44
61
|
className?: string;
|
|
45
62
|
children: (binding: FormItemBinding<TValues, P>) => ReactNode;
|
|
46
63
|
};
|
|
@@ -61,4 +78,4 @@ export type FormItemProps<TValues extends Record<string, any> = any, P extends F
|
|
|
61
78
|
* `<span id={errorId} role='alert'>` next to the children; with no errors
|
|
62
79
|
* no extra element is rendered.
|
|
63
80
|
*/
|
|
64
|
-
export default function FormItem<TValues extends Record<string, any> = any, P extends FieldPath<TValues> | Name = Name>({ form, name, label, validate, mode, className, children }: FormItemProps<TValues, P>): import("react").JSX.Element;
|
|
81
|
+
export default function FormItem<TValues extends Record<string, any> = any, P extends FieldPath<TValues> | Name = Name>({ form, name, label, validate, mode, validateDebounce, delayError, rules, className, children }: FormItemProps<TValues, P>): import("react").JSX.Element;
|
|
@@ -18,8 +18,11 @@ export type { FormInstance, PathValueOf };
|
|
|
18
18
|
* `getValueByPath` at render time (kept fresh by a `useValueByPath`
|
|
19
19
|
* subscription that re-renders this component on own-field changes — and
|
|
20
20
|
* only those, so sibling fields stay isolated); writes go through
|
|
21
|
-
* `
|
|
22
|
-
*
|
|
21
|
+
* `changeValueByPath` — react-f0rm's user-change channel — so a control
|
|
22
|
+
* write fires exactly the validation a user typing into the field would
|
|
23
|
+
* fire (the field's effective `mode`, per-field override included, and the
|
|
24
|
+
* form's `reValidateMode`). The setter accepts either a plain value or a
|
|
25
|
+
* functional updater that is evaluated against the live form value.
|
|
23
26
|
*
|
|
24
27
|
* The handle's identity is stable across re-renders, including own-field
|
|
25
28
|
* value changes.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "haze-ui",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.12.1",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "A React UI component library powered by react-use-control and Linaria",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -49,7 +49,7 @@
|
|
|
49
49
|
"peerDependencies": {
|
|
50
50
|
"@linaria/core": "^7.0.0 || ^8.0.0",
|
|
51
51
|
"react": "^19.0.0",
|
|
52
|
-
"react-f0rm": ">=0.
|
|
52
|
+
"react-f0rm": ">=0.7.0"
|
|
53
53
|
},
|
|
54
54
|
"dependencies": {
|
|
55
55
|
"react-toolroom": "^0.11.0",
|
|
@@ -95,7 +95,7 @@
|
|
|
95
95
|
"ramda": "^0.32.0",
|
|
96
96
|
"react": "^19.2.8",
|
|
97
97
|
"react-dom": "^19.2.8",
|
|
98
|
-
"react-f0rm": "^0.
|
|
98
|
+
"react-f0rm": "^0.7.0",
|
|
99
99
|
"react-icons": "^5.7.0",
|
|
100
100
|
"rollup-plugin-type-as-json-schema": "^0.2.6",
|
|
101
101
|
"semantic-release": "^25.0.9",
|