react-luna-form 0.0.85 → 0.0.87
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 +15 -10
- package/dist/client/cjs/index.js +2 -1
- package/dist/client/esm/index.js +2 -1
- package/dist/config/cjs/index.js +1 -1
- package/dist/config/esm/index.js +1 -1
- package/dist/schema/cjs/index.js +1 -1
- package/dist/schema/esm/index.js +1 -1
- package/dist/server/cjs/index.js +1 -1
- package/dist/server/esm/index.js +1 -1
- package/dist/types/luna-core/type.d.ts +30 -16
- package/dist/types/luna-core/util/build.d.ts +3 -1
- package/dist/types/luna-core/util/extract.d.ts +22 -0
- package/dist/types/luna-core/util/is-input.d.ts +13 -0
- package/dist/types/luna-core/util/prepare.d.ts +1 -1
- package/dist/types/luna-core/util/schema.d.ts +5 -5
- package/dist/types/luna-react/src/client/component/field/field-preview.d.ts +4 -2
- package/dist/types/luna-react/src/{component → client/component}/field/field-set-advanced.d.ts +1 -1
- package/dist/types/luna-react/src/client/component/field/field-set.d.ts +11 -0
- package/dist/types/luna-react/src/client/component/field/field.d.ts +37 -9
- package/dist/types/luna-react/src/client/component/field-description.d.ts +2 -0
- package/dist/types/luna-react/src/{component → client/component}/formatted-description.d.ts +2 -2
- package/dist/types/luna-react/src/client/component/input-strategies.d.ts +1 -1
- package/dist/types/luna-react/src/client/context/failed-submit-context.d.ts +3 -0
- package/dist/types/luna-react/src/client/context/reveal-context.d.ts +1 -0
- package/dist/types/luna-react/src/client/hook/use-disclosure.d.ts +1 -0
- package/dist/types/luna-react/src/client/hook/use-entry-atom.d.ts +18 -0
- package/dist/types/luna-react/src/client/hook/use-form-action.d.ts +2 -1
- package/dist/types/luna-react/src/client/hook/use-host-entry.d.ts +17 -0
- package/dist/types/luna-react/src/client/hook/use-input-core.d.ts +6 -8
- package/dist/types/luna-react/src/client/hook/use-input.d.ts +1 -1
- package/dist/types/luna-react/src/client/hook/use-latest.d.ts +22 -0
- package/dist/types/luna-react/src/client/hook/use-live-item-value.d.ts +17 -3
- package/dist/types/luna-react/src/client/hook/use-schema.d.ts +4 -2
- package/dist/types/luna-react/src/client/hook/use-value.d.ts +3 -3
- package/dist/types/luna-react/src/client/lib/compose-list-value.d.ts +29 -2
- package/dist/types/luna-react/src/client/lib/error-store.d.ts +3 -1
- package/dist/types/luna-react/src/client/lib/host-value-store.d.ts +28 -0
- package/dist/types/luna-react/src/client/lib/list-store.d.ts +6 -4
- package/dist/types/luna-react/src/client/lib/resolve-value.d.ts +0 -5
- package/dist/types/luna-react/src/client/lib/source-store.d.ts +7 -1
- package/dist/types/luna-react/src/client/lib/state-store.d.ts +0 -1
- package/dist/types/luna-react/src/client/lib/store-helper.d.ts +5 -7
- package/dist/types/luna-react/src/client/lib/value-store.d.ts +0 -1
- package/dist/types/luna-react/src/component/collapsible.d.ts +2 -1
- package/dist/types/luna-react/src/component/column.d.ts +2 -0
- package/dist/types/luna-react/src/component/description.d.ts +9 -0
- package/dist/types/luna-react/src/component/field/{field-set.d.ts → field-set-static.d.ts} +1 -1
- package/dist/types/luna-react/src/component/field/field.d.ts +1 -1
- package/dist/types/luna-react/src/component/field-description.d.ts +3 -2
- package/dist/types/luna-react/src/component/form.d.ts +11 -0
- package/dist/types/luna-react/src/component/input/input-base.d.ts +1 -0
- package/dist/types/luna-react/src/component/input-group.d.ts +2 -0
- package/dist/types/luna-react/src/component/input-label.d.ts +2 -0
- package/dist/types/luna-react/src/component/slot/slot-base.d.ts +2 -0
- package/dist/types/luna-react/src/lib/visibility.d.ts +3 -0
- package/dist/types/luna-react/src/server/component/field.d.ts +2 -0
- package/dist/types/luna-react/src/server/component/list-slot.d.ts +2 -0
- package/dist/types/luna-react/src/server/component/slot.d.ts +15 -0
- package/dist/types/luna-react/src/type.d.ts +1 -0
- package/docs/events/change.md +21 -7
- package/docs/fields/custom-inputs.md +4 -0
- package/docs/fields/input.md +9 -3
- package/docs/fields/list.md +2 -0
- package/docs/fields/specialized-selectors.md +4 -0
- package/docs/forms/submit.md +14 -8
- package/docs/setup.md +39 -19
- package/docs/structure/sections.md +3 -1
- package/docs/validation/overview.md +10 -1
- package/package.json +7 -7
- package/dist/types/luna-react/src/client/component/field/field-with-state.d.ts +0 -13
package/docs/fields/input.md
CHANGED
|
@@ -28,10 +28,9 @@ Luna Form's input fields inherit the standard logical properties available to al
|
|
|
28
28
|
- Or an object payload: `{ title: string, message: string, collapsed?: boolean }`.
|
|
29
29
|
- **`placeholder`** _(string, optional)_: The temporary placeholder text shown when the input is empty.
|
|
30
30
|
- **`required`** _(boolean, optional)_: If `true`, standard browser/HTML5 validation makes the field mandatory.
|
|
31
|
-
- **`disabled`** _(boolean, optional)_: If `true`, the field is non-interactive and blocked from events.
|
|
32
|
-
- **`readonly`** _(boolean, optional)_: If `true`, the field's value is locked and cannot be modified by the user.
|
|
31
|
+
- **`disabled`** _(boolean, optional)_: If `true`, the field is non-interactive and blocked from events, and its value is not submitted.
|
|
32
|
+
- **`readonly`** _(boolean, optional)_: If `true`, the field's value is locked and cannot be modified by the user, but it is still submitted. The control renders disabled, the one lock every input component understands, and the form sends the value itself. A field that is both `readonly` and `disabled` is not submitted.
|
|
33
33
|
- **`hidden`** _(boolean, optional)_: If `true`, the field is hidden from the user interface.
|
|
34
|
-
- **`keepValue`** _(boolean, optional)_: If `true`, hiding the field leaves its value behind instead of taking it. See [What clearing a hidden target means](../events/change.md#what-clearing-a-hidden-target-means).
|
|
35
34
|
- **`order`** _(number, optional)_: A numeric order determining the field's position relative to adjacent fields.
|
|
36
35
|
|
|
37
36
|
---
|
|
@@ -48,6 +47,7 @@ The `advanced` property dictates finer HTML details, interactive structures, and
|
|
|
48
47
|
- **`reverse`** _(boolean)_: `checkbox` only, and it exists to turn a default **off**. A checkbox renders its box first and its label after it; `reverse: false` swaps them, `true` changes nothing, and on any other field type the key is not read. See [Layout](../structure/layout.md#advancedreverse-checkbox-only).
|
|
49
48
|
- **`aria`** _(object)_: Map of specific ARIA attributes for screen readers (e.g., `{ "aria-label": "Custom label" }`).
|
|
50
49
|
- **`data`** _(object)_: Map of specific HTML data attributes (e.g., `{ "data-test-id": "my-field" }`).
|
|
50
|
+
- **`keepValue`** _(boolean)_: If `true`, hiding the field leaves its value behind instead of taking it — the field is put away rather than dropped. See [Keeping a value through a hide](../events/change.md#keeping-a-value-through-a-hide).
|
|
51
51
|
- **`transient`** _(boolean)_: If `true`, the field fires its change events and keeps nothing of its own — it is neither stored nor reported to the consumer. For a control whose whole job is to write to _another_ field. See [Transient controls](../events/change.md#transient-controls).
|
|
52
52
|
|
|
53
53
|
### Text & Numeric Options (`input/text`, `input/email`, `input/tel`, `input/password`, `input/number`)
|
|
@@ -55,12 +55,17 @@ The `advanced` property dictates finer HTML details, interactive structures, and
|
|
|
55
55
|
These basic field types support extra manipulation properties inside the `advanced` block:
|
|
56
56
|
|
|
57
57
|
- **`length`** _({ min?: number, max?: number })_: Applies HTML structural limits (`minlength` / `maxlength`, or `min` / `max` depending on the input type).
|
|
58
|
+
- **`step`** _(number)_ (`input/number` only): A number is whole unless it declares a step, which is the browser's own default of 1. The step is rendered on the input, so its arrows move by it, and validation accepts only values on it, counted from `length.min` when there is one, as the browser does: `0.01` for a price, `0.5` for halves, `0.001` for three decimals. A step has to be a number above 0. The browser ignores 0 and below, and so does the form; text such as `"any"` is no step either, and it is never rendered. Either way the number stays whole.
|
|
58
59
|
- **`transform`** _(string | string[])_: Safely intercepts user inputs and manipulates content dynamically. Options include:
|
|
59
60
|
- `"lowercase"`
|
|
60
61
|
- `"uppercase"`
|
|
61
62
|
- `"remove-space"`
|
|
62
63
|
- `"remove-accent"`
|
|
63
64
|
|
|
65
|
+
### Empty and required numbers
|
|
66
|
+
|
|
67
|
+
A required `input/number` accepts `0` and negative numbers: required means a value is present, not that it is at least 1. An optional one left empty is not submitted at all, rather than submitted as `0`, and its `length` bounds only apply to a value that is there. `select/year` and `select/month` read an empty selection the same way.
|
|
68
|
+
|
|
64
69
|
### Temporal Options (`input/date`, `input/time`)
|
|
65
70
|
|
|
66
71
|
Date and time inputs omit transformations and instead expose a `format` property directly under `advanced` for displaying values properly:
|
|
@@ -77,6 +82,7 @@ The `validation` object resolves form errors overriding generic defaults, mappin
|
|
|
77
82
|
- **`required`** _(string)_: Specifies the error message exposed when the element is marked exactly as `required: true` and the field is empty.
|
|
78
83
|
- **`email`** _(string)_: Error message specifically asserting an invalid email format.
|
|
79
84
|
- **`length`** _({ min?: string, max?: string })_: Specific string messages shown when input lengths are breached.
|
|
85
|
+
- **`step`** _(string)_: The message shown when an `input/number` is off its step: a decimal on a number that declares no step, or a value off the `advanced.step` it declares. The form's dictionary translates it, as it does every other message.
|
|
80
86
|
- **`custom`** _(CustomValidation | CustomValidation[])_: Powerful conditional-based logic blocks. An array specifying:
|
|
81
87
|
- `field`: Optional target reference string.
|
|
82
88
|
- `operator`: Logical evaluation operations (e.g., `eq`, `neq`, `gt`, `lt`).
|
package/docs/fields/list.md
CHANGED
|
@@ -26,6 +26,8 @@ The `advanced` property customizes structural interaction rules and layout rende
|
|
|
26
26
|
- **`length`** _({ min?: number, max?: number })_: Configures restrictions on how many items can be generated. Enforces boundary conditions where minimum instances ensure permanent default items and maximum instances automatically disable insertion toggles. The matching error messages are declared under the list's `validation.length` — see the [Validation reference](../validation/overview.md#list-length).
|
|
27
27
|
Both bounds apply to the rows a list opens with, not only to the ones added by hand: an initial `value` shorter than `min` leaves the remainder standing and empty, and one longer than `max` stops at `max`. A [`value` event](../events/change.md#targeting-a-list) assigning rows is clamped the same way.
|
|
28
28
|
- **`collapsed`** _(boolean)_: When enabled, list items default to a visually collapsed representation upon mounting or adding.
|
|
29
|
+
Collapsing only hides a row: its fields keep their values, still validate and are still submitted, and a submit that fails on one of them opens the row so the error can be seen.
|
|
30
|
+
- **`keepValue`** _(boolean)_: If `true`, hiding the list leaves the values behind instead of taking them. A list holds nothing under its own name, so declaring it here is a statement about the rows: it covers every value they hold. See [Keeping a value through a hide](../events/change.md#keeping-a-value-through-a-hide).
|
|
29
31
|
|
|
30
32
|
### Collapsed Previews (`preview` object)
|
|
31
33
|
|
|
@@ -17,6 +17,8 @@ Renders a dropdown with the 12 months of the year.
|
|
|
17
17
|
}
|
|
18
18
|
```
|
|
19
19
|
|
|
20
|
+
An optional month nobody picked is not submitted at all, and a required one asks for a value, the way an [`input/number`](input.md#empty-and-required-numbers) does.
|
|
21
|
+
|
|
20
22
|
### 2. Year Selector (`select/year`)
|
|
21
23
|
|
|
22
24
|
Renders a dropdown with a range of years.
|
|
@@ -38,6 +40,8 @@ Renders a dropdown with a range of years.
|
|
|
38
40
|
}
|
|
39
41
|
```
|
|
40
42
|
|
|
43
|
+
An optional year nobody picked is not submitted at all, and a required one asks for a value, the way an [`input/number`](input.md#empty-and-required-numbers) does.
|
|
44
|
+
|
|
41
45
|
### 3. Day Selector (`select/day`)
|
|
42
46
|
|
|
43
47
|
Renders a dropdown with days 1 through 31.
|
package/docs/forms/submit.md
CHANGED
|
@@ -103,18 +103,24 @@ If the values have to survive, hold them outside the form: mirror
|
|
|
103
103
|
people, which is that a key you stop sending is not the same as a key you send
|
|
104
104
|
empty.
|
|
105
105
|
|
|
106
|
-
A **failed**
|
|
107
|
-
|
|
106
|
+
A **failed** submit is the opposite: nothing is cleared or reset, so what the
|
|
107
|
+
user entered stays on screen next to the errors — typed text, ticked boxes and
|
|
108
|
+
picked options alike. An error inside a collapsed
|
|
109
|
+
[section](../structure/sections.md#advancedcollapsible) or list row is brought
|
|
110
|
+
on screen too: the section or row holding it opens. The form does not take over
|
|
111
|
+
`value` either. What you pass is still what the fields read, so you can change
|
|
112
|
+
it, or put it back, while the errors are showing.
|
|
108
113
|
|
|
109
114
|
## Turning submit validation off
|
|
110
115
|
|
|
111
|
-
`defineConfig({ validation: { submit: false
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
116
|
+
`defineConfig({ validation: { submit: false } })` skips the parse entirely. The
|
|
117
|
+
action is called with the raw `FormData` and the schema, and nothing is checked,
|
|
118
|
+
reported or cleared on the way — for a form whose action does its own parsing.
|
|
119
|
+
With no action at all, submitting does nothing.
|
|
115
120
|
|
|
116
|
-
|
|
117
|
-
|
|
121
|
+
The other three keys are untouched by that: this object
|
|
122
|
+
[merges into the defaults](../setup.md#validation), so a form that stops
|
|
123
|
+
validating on submit goes on validating as the user types.
|
|
118
124
|
|
|
119
125
|
## The error summary
|
|
120
126
|
|
package/docs/setup.md
CHANGED
|
@@ -13,15 +13,15 @@ pnpm add react-luna-form
|
|
|
13
13
|
The package carries no dependencies of its own: everything it needs is a peer
|
|
14
14
|
dependency, so your application resolves one copy of React, Zod and the rest.
|
|
15
15
|
|
|
16
|
-
| Peer
|
|
17
|
-
|
|
|
18
|
-
| `react`, `react-dom`
|
|
19
|
-
| `zod`
|
|
20
|
-
| `jotai
|
|
21
|
-
| `swr`
|
|
22
|
-
| `date-fns`
|
|
23
|
-
| `fast-equals`
|
|
24
|
-
| `tailwind-merge`
|
|
16
|
+
| Peer | Version | Used for |
|
|
17
|
+
| -------------------- | --------- | ------------------------------------------- |
|
|
18
|
+
| `react`, `react-dom` | `^19.0.0` | rendering, and `useActionState` on submit |
|
|
19
|
+
| `zod` | `^4.0.0` | the schema derived from `sections` |
|
|
20
|
+
| `jotai` | `^2.0.0` | the value, error and source stores |
|
|
21
|
+
| `swr` | `^2.0.0` | fetching a field's remote `source` |
|
|
22
|
+
| `date-fns` | `^4.0.0` | date and time formatting, `duration` filter |
|
|
23
|
+
| `fast-equals` | `^6.0.0` | change detection in the stores |
|
|
24
|
+
| `tailwind-merge` | `^3.7.0` | merging the classes the components render |
|
|
25
25
|
|
|
26
26
|
None of them is optional, and none is bundled. A package manager that installs
|
|
27
27
|
peers automatically (npm 7+, or pnpm with `auto-install-peers=true`) brings
|
|
@@ -48,20 +48,26 @@ CSS.
|
|
|
48
48
|
|
|
49
49
|
Every element also carries a `data-slot` attribute — `field`, `field-label`,
|
|
50
50
|
`field-control`, `field-set`, `field-set-content`, `field-content`,
|
|
51
|
-
`field-group`, `field-separator`, `
|
|
52
|
-
system can restyle the structure without
|
|
53
|
-
you register yourself are yours to style;
|
|
54
|
-
around them.
|
|
51
|
+
`field-group`, `field-separator`, `collapsible-content`, `column`,
|
|
52
|
+
`list-item-card` — so a design system can restyle the structure without
|
|
53
|
+
patching the library. The components you register yourself are yours to style;
|
|
54
|
+
these slots cover the scaffolding around them.
|
|
55
55
|
|
|
56
56
|
## Entry points
|
|
57
57
|
|
|
58
58
|
| Import | Contains |
|
|
59
59
|
| ------------------------ | ----------------------------------------------------------------------------------------- |
|
|
60
60
|
| `react-luna-form` | `Form` (client), `withDeclaredFields`, and the `Sections`, `Source` and `ZodSchema` types |
|
|
61
|
-
| `react-luna-form/server` | `Form` (server) and `Fallback`
|
|
61
|
+
| `react-luna-form/server` | `Form` (server) and `Fallback`, usable under the `react-server` condition |
|
|
62
62
|
| `react-luna-form/config` | `defineConfig` and the `define*` registration helpers |
|
|
63
63
|
| `react-luna-form/schema` | `buildFormSchema` and `collectIssues` |
|
|
64
64
|
|
|
65
|
+
The client bundle is published carrying a `'use client'` directive, so an App
|
|
66
|
+
Router project imports `Form` straight from a Server Component and needs no
|
|
67
|
+
wrapper module of its own to say it. The server bundle carries none and
|
|
68
|
+
resolves under the `react-server` condition, which is what lets a Server
|
|
69
|
+
Component render the form itself.
|
|
70
|
+
|
|
65
71
|
## Configuring
|
|
66
72
|
|
|
67
73
|
`defineConfig` is called once, usually in a `luna.config.ts` at the root of the
|
|
@@ -99,10 +105,9 @@ When to validate and whether to summarise errors. All four default to `true`.
|
|
|
99
105
|
validation: { blur: true, change: true, showError: true, submit: true }
|
|
100
106
|
```
|
|
101
107
|
|
|
102
|
-
|
|
103
|
-
`{ change: false }`
|
|
104
|
-
|
|
105
|
-
Pass every key you still want.
|
|
108
|
+
This object merges into the defaults, so pass only what you want to change.
|
|
109
|
+
`{ change: false }` stops the form validating as the user types and leaves the
|
|
110
|
+
other three alone.
|
|
106
111
|
|
|
107
112
|
### `fetcher.remotePatterns`
|
|
108
113
|
|
|
@@ -127,7 +132,8 @@ fetcher: {
|
|
|
127
132
|
A blocked URL is not an error. The library logs
|
|
128
133
|
`URL blocked by remotePatterns: <url>` and the field renders with no options at
|
|
129
134
|
all, which on screen looks like an empty select rather than a rejected request.
|
|
130
|
-
Check the console when a remote source comes back empty
|
|
135
|
+
Check the console when a remote source comes back empty — see
|
|
136
|
+
[Warnings](#warnings) for when that line is written.
|
|
131
137
|
|
|
132
138
|
### `style`, `alert`, `env`
|
|
133
139
|
|
|
@@ -137,6 +143,20 @@ Check the console when a remote source comes back empty.
|
|
|
137
143
|
- `env` is a flat record of values available to interpolation, in labels and in
|
|
138
144
|
descriptions. Its `locale` key is also what those two read to format values.
|
|
139
145
|
|
|
146
|
+
## Warnings
|
|
147
|
+
|
|
148
|
+
Some of what can go wrong is nobody's to fix but the author of the form, and
|
|
149
|
+
the library says so on the console rather than on screen: a `pattern` that does
|
|
150
|
+
not compile, a field name that reaches a prototype and is left out of the
|
|
151
|
+
submit, the error an `action` threw, a remote `source` that failed or was
|
|
152
|
+
blocked. Every one of those lines is prefixed `[Luna Form]`.
|
|
153
|
+
|
|
154
|
+
They are written outside production and silent inside it, and which one you are
|
|
155
|
+
in is your build's answer to give: the published bundle leaves
|
|
156
|
+
`process.env.NODE_ENV` for your bundler to resolve instead of deciding while it
|
|
157
|
+
is being packaged. A development build of your application shows them and a
|
|
158
|
+
production build does not. Where nothing defines it at all, they are shown.
|
|
159
|
+
|
|
140
160
|
## Rendering
|
|
141
161
|
|
|
142
162
|
### `Form` from `react-luna-form`
|
|
@@ -95,7 +95,7 @@ Renders the section closed, with its title as the toggle:
|
|
|
95
95
|
}
|
|
96
96
|
```
|
|
97
97
|
|
|
98
|
-
|
|
98
|
+
Four things to know before using it:
|
|
99
99
|
|
|
100
100
|
- **It always starts closed.** There is no key that opens it, and reopening the
|
|
101
101
|
form does not remember that the user had expanded it.
|
|
@@ -103,6 +103,8 @@ Three things to know before using it:
|
|
|
103
103
|
them. They keep their values, they still validate, and they are still
|
|
104
104
|
submitted. That is the opposite of what a [`state` action](../events/change.md#what-clearing-a-hidden-target-means)
|
|
105
105
|
does when it hides a field, which unmounts it and clears the value.
|
|
106
|
+
- **A failed submit opens it.** When a field inside fails validation, the
|
|
107
|
+
section opens so the error can be seen, and it stays open.
|
|
106
108
|
- **The description moves inside.** It renders with the fields rather than under
|
|
107
109
|
the title, so it is not visible while the section is closed.
|
|
108
110
|
|
|
@@ -18,6 +18,7 @@ This page documents every key of the `validation` object plus the shared operato
|
|
|
18
18
|
- **`required`** _(string)_: Message shown when a field marked `required: true` is empty.
|
|
19
19
|
- **`email`** _(string)_: Message for an invalid email format (`input/email`).
|
|
20
20
|
- **`length`** _({ min?: string, max?: string })_: Messages when the value's length breaches `advanced.length`.
|
|
21
|
+
- **`step`** _(string)_: Message when an `input/number` is off its step: a decimal on a whole number, or a value off `advanced.step`. See [Input](../fields/input.md).
|
|
21
22
|
- **`custom`** _(CustomValidation | CustomValidation[])_: Cross-field comparison. See [Custom (cross-field)](#custom-cross-field).
|
|
22
23
|
- **`requiredWhen`** _(WhenRule | WhenRule[])_: Conditionally required. See [requiredWhen](#requiredwhen).
|
|
23
24
|
- **`pattern`** _(PatternRule)_: Regex/format check. See [pattern](#pattern).
|
|
@@ -110,6 +111,14 @@ Validates a string value against a regular expression.
|
|
|
110
111
|
|
|
111
112
|
`pattern` never fires on an empty value — pair it with `required`/`requiredWhen` when the field is mandatory.
|
|
112
113
|
|
|
114
|
+
A `regex` or `flags` that does not compile is a mistake in the definition, not in the value. The rule then holds back every value it checks with its `message`, as it does a value that does not match, and the form keeps working. The same goes for a `pattern` among [`rules`](#rules). Since the `message` is written for whoever fills the form in, the expression that failed to compile is named on the console instead — see [Warnings](../setup.md#warnings).
|
|
115
|
+
|
|
116
|
+
A `regex` that compiles runs as written, on what the user types: in the browser when the form is submitted, and on the server when it validates with `buildFormSchema`. Nothing inspects it first, so it is part of the code the form ships with. Accept one only from someone you would let write that code.
|
|
117
|
+
|
|
118
|
+
The shape to avoid is a pattern that can split the same text between its repetitions in more than one way, such as `^(a+)+$`, `^(\w+\s?)+$` or `^(a|a)+$`. On a value that almost matches, it backtracks exponentially, and about thirty characters can hold the page, or the server, for seconds. A repetition that opens with a separator the one before cannot close with is not ambiguous and runs in linear time, as in `^[a-z]+(-[a-z]+)*$` or `^\d+(,\d+)*$`.
|
|
119
|
+
|
|
120
|
+
If definitions reach you from other people, check their patterns with a ReDoS analyzer before you accept them.
|
|
121
|
+
|
|
113
122
|
---
|
|
114
123
|
|
|
115
124
|
## `rules`
|
|
@@ -258,7 +267,7 @@ if (!result.success) {
|
|
|
258
267
|
|
|
259
268
|
| Rule | Rendered `<Form>` submit | `buildFormSchema` (headless) |
|
|
260
269
|
| --------------------------------------------- | ------------------------ | ---------------------------- |
|
|
261
|
-
| `required`, `email`, `length` (field)
|
|
270
|
+
| `required`, `email`, `length`, `step` (field) | ✅ | ✅ |
|
|
262
271
|
| `custom` | ✅ | ✅ |
|
|
263
272
|
| `requiredWhen` (top-level) | ✅ | ✅ |
|
|
264
273
|
| `pattern` | ✅ | ✅ |
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "react-luna-form",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.87",
|
|
4
4
|
"description": "A React library for building forms based on JSON",
|
|
5
5
|
"main": "./dist/client/cjs/index.js",
|
|
6
6
|
"module": "./dist/client/esm/index.js",
|
|
@@ -22,20 +22,20 @@
|
|
|
22
22
|
"date-fns": "^4.0.0",
|
|
23
23
|
"fast-equals": "^6.0.0",
|
|
24
24
|
"jotai": "^2.0.0",
|
|
25
|
-
"jotai-family": "^1.0.0",
|
|
26
25
|
"react": "^19.0.0",
|
|
27
26
|
"react-dom": "^19.0.0",
|
|
28
27
|
"swr": "^2.0.0",
|
|
29
|
-
"tailwind-merge": "^3.
|
|
28
|
+
"tailwind-merge": "^3.7.0",
|
|
30
29
|
"zod": "^4.0.0"
|
|
31
30
|
},
|
|
32
31
|
"devDependencies": {
|
|
33
|
-
"@
|
|
34
|
-
"@types/react
|
|
32
|
+
"@luna-form/core": "0.0.87",
|
|
33
|
+
"@types/react": "19.3.0",
|
|
34
|
+
"@types/react-dom": "19.3.0",
|
|
35
35
|
"eslint-plugin-react": "7.37.5",
|
|
36
36
|
"eslint-plugin-react-hooks": "7.1.1",
|
|
37
|
-
"eslint-plugin-react-refresh": "0.5.
|
|
38
|
-
"
|
|
37
|
+
"eslint-plugin-react-refresh": "0.5.7",
|
|
38
|
+
"jotai-family": "1.1.0"
|
|
39
39
|
},
|
|
40
40
|
"license": "Apache-2.0",
|
|
41
41
|
"repository": {
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
export declare function withError<P extends {
|
|
2
|
-
errors?: Record<string, string[]>;
|
|
3
|
-
field: {
|
|
4
|
-
name: string;
|
|
5
|
-
};
|
|
6
|
-
}>(Component: React.ComponentType<P>): (props: Readonly<P>) => import("react").JSX.Element | null;
|
|
7
|
-
export declare function withState<P extends {
|
|
8
|
-
disabled?: boolean;
|
|
9
|
-
field: {
|
|
10
|
-
name: string;
|
|
11
|
-
hidden?: boolean;
|
|
12
|
-
};
|
|
13
|
-
}>(Component: React.ComponentType<P>): (props: Readonly<P>) => import("react").JSX.Element | null;
|