react-luna-form 0.0.83 → 0.0.84

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.
@@ -0,0 +1,187 @@
1
+ # Sections
2
+
3
+ `sections` is the top level of a form. Every other page describes what goes
4
+ inside one; this page describes the section itself.
5
+
6
+ A section is a **visual grouping and nothing else**. It draws a legend, sets the
7
+ spacing around its fields and can collapse them out of the way, but it does not
8
+ scope names: every field in the form shares one flat namespace, whichever
9
+ section it was declared in. Two fields called `email` in two different sections
10
+ are one field with two renderings, and
11
+ [`buildFormSchema`](../validation/overview.md#headless-validation) produces one
12
+ key for them.
13
+
14
+ ## The shape
15
+
16
+ | Key | Type | Does |
17
+ | ---------------------- | ------------------ | --------------------------------------------- |
18
+ | `fields` | `Fields` | what the section renders |
19
+ | `title` | `string` | the legend |
20
+ | `description` | `string` | help text under the legend |
21
+ | `id` | `string \| number` | the `id` attribute of the rendered `fieldset` |
22
+ | `order` | `number` | position among the other sections |
23
+ | `advanced.compact` | `boolean` | tighter spacing between this section's fields |
24
+ | `advanced.collapsible` | `boolean` | render the section closed, behind a toggle |
25
+ | `advanced.separator` | `boolean` | draw a rule after the section |
26
+
27
+ ```json
28
+ {
29
+ "sections": [
30
+ {
31
+ "id": "payment",
32
+ "title": "Payment Method",
33
+ "description": "All transactions are secure and encrypted",
34
+ "advanced": { "separator": true },
35
+ "fields": [{ "name": "name", "type": "input/text", "label": "Name" }]
36
+ }
37
+ ]
38
+ }
39
+ ```
40
+
41
+ ## A section with no title and no description draws nothing
42
+
43
+ There is no empty legend and no wrapper to style around it: the section's fields
44
+ join the flow of the form as if they had been declared without one. This is the
45
+ form to reach for when you want the grouping only for `compact` spacing or for
46
+ `order`.
47
+
48
+ Declaring `advanced.collapsible` on such a section does nothing either — with no
49
+ title there is nothing to click, so the fields render open.
50
+
51
+ ## Title and description are translated, not interpolated
52
+
53
+ Both are resolved against the [`translations`](../fields/list.md#localization)
54
+ dictionary and both render `[text](url)` markdown links. Neither is passed
55
+ through interpolation: a `{context.name}` in a section title stays on screen as
56
+ the literal text `{context.name}`.
57
+
58
+ This is the one place the rule differs. A field's `label` and `description`
59
+ _are_ interpolated, and so is a [column's description](layout.md#description).
60
+ If a section heading has to carry a value, put the value in a field description
61
+ or in a column instead.
62
+
63
+ ## `advanced.compact`
64
+
65
+ Spacing between the fields of this section, tight instead of loose. It is
66
+ declared per section:
67
+
68
+ ```json
69
+ {
70
+ "title": "Connection",
71
+ "advanced": { "compact": true },
72
+ "fields": [{ "name": "host", "type": "input/text" }]
73
+ }
74
+ ```
75
+
76
+ A section that declares nothing inherits `style.compact` from
77
+ [`defineConfig`](../setup.md#style-alert-env), so the form-wide default holds
78
+ and a section only has to speak up to differ from it — including to opt out,
79
+ with `"compact": false` under a compact form.
80
+
81
+ ## `advanced.separator`
82
+
83
+ Draws a horizontal rule **after** the section, between it and whatever follows.
84
+ On the last section it draws a trailing rule, which is rarely what you want.
85
+
86
+ ## `advanced.collapsible`
87
+
88
+ Renders the section closed, with its title as the toggle:
89
+
90
+ ```json
91
+ {
92
+ "title": "Advanced options",
93
+ "advanced": { "collapsible": true },
94
+ "fields": [{ "name": "timeout", "type": "input/number" }]
95
+ }
96
+ ```
97
+
98
+ Three things to know before using it:
99
+
100
+ - **It always starts closed.** There is no key that opens it, and reopening the
101
+ form does not remember that the user had expanded it.
102
+ - **The fields inside stay mounted.** Collapsing hides them; it does not unmount
103
+ them. They keep their values, they still validate, and they are still
104
+ submitted. That is the opposite of what a [`state` action](../events/change.md#what-clearing-a-hidden-target-means)
105
+ does when it hides a field, which unmounts it and clears the value.
106
+ - **The description moves inside.** It renders with the fields rather than under
107
+ the title, so it is not visible while the section is closed.
108
+
109
+ A collapsible section also renders no step number, whatever `advanced.step` says.
110
+
111
+ ## `advanced.step` numbers the sections
112
+
113
+ Declared on the **form**, not on a section, it puts a numbered badge beside each
114
+ section's legend, counting the sections in the order they render:
115
+
116
+ ```tsx
117
+ <Form advanced={{ step: true }} sections={sections} config={config} />
118
+ ```
119
+
120
+ It is a numbering, not a wizard: every section is on screen at once, and the
121
+ library ships nothing that shows them one at a time. The badge appears only on
122
+ sections that draw a legend — a section with no title and no description has
123
+ nowhere to put it, and a collapsible one does not take it.
124
+
125
+ ## `order`, and what disappears
126
+
127
+ `sections` renders in array order unless a section declares `order`. Sections
128
+ carrying one sort ascending; sections without one keep their relative order and
129
+ follow the rest. The same rule applies to the entries inside a section, so a
130
+ field and a column can be interleaved by `order` — see
131
+ [layout.md](layout.md#ordering-fields-and-columns).
132
+
133
+ Two kinds of section never render at all:
134
+
135
+ - **One with no fields.** `"fields": []` is dropped before rendering, legend
136
+ included.
137
+ - **One whose fields are all hidden.** Hidden by `hidden: true` in the JSON or
138
+ by a [`state` action](../events/change.md#2-state-action-stateevent) at run
139
+ time, it makes no difference: when nothing inside a section is visible the
140
+ section goes with it, and it comes back when one of them is shown again. A
141
+ column counts as hidden when every field in it is.
142
+
143
+ A section has **no visibility flag of its own**. Writing `"hidden": true` on a
144
+ section is accepted and ignored; hide its fields instead, or leave the section
145
+ out of the array.
146
+
147
+ ## `id`
148
+
149
+ Lands on the rendered `<fieldset>` as its `id` attribute, for an anchor link or
150
+ a test selector. It is not a name and nothing resolves against it: fields are
151
+ addressed by their own `name` from anywhere in the form.
152
+
153
+ ## Complete example
154
+
155
+ ```json
156
+ {
157
+ "advanced": { "step": true },
158
+ "sections": [
159
+ {
160
+ "id": "contact",
161
+ "title": "Contact",
162
+ "description": "How we reach you about this order",
163
+ "advanced": { "separator": true },
164
+ "fields": [
165
+ { "name": "email", "type": "input/email", "label": "Email" },
166
+ { "name": "phone", "type": "input/tel", "label": "Phone" }
167
+ ]
168
+ },
169
+ {
170
+ "title": "Delivery",
171
+ "advanced": { "compact": true },
172
+ "fields": [
173
+ { "name": "street", "type": "input/text", "label": "Street" },
174
+ { "name": "city", "type": "input/text", "label": "City" }
175
+ ]
176
+ },
177
+ {
178
+ "title": "Advanced options",
179
+ "advanced": { "collapsible": true },
180
+ "order": 99,
181
+ "fields": [
182
+ { "name": "notes", "type": "textarea", "label": "Delivery notes" }
183
+ ]
184
+ }
185
+ ]
186
+ }
187
+ ```
@@ -251,7 +251,7 @@ if (!result.success) {
251
251
  }
252
252
  ```
253
253
 
254
- - **`buildFormSchema(sections, translations?, definition?)`** — walks the form tree (sections → columns → lists → fields) and returns a Zod schema. Lists become arrays of objects, so nested `requiredWhen`/`rules` resolve in **item scope**. `translations` maps messages to localized strings; `definition` resolves `$ref` sources.
254
+ - **`buildFormSchema(sections, translations?, definition?)`** — walks the form tree (sections → columns → lists → fields) and returns a Zod schema. Lists become arrays of objects, so nested `requiredWhen`/`rules` resolve in **item scope**. `translations` maps messages to localized strings; `definition` resolves [`$ref` entries](../structure/definition.md), and has to be the same one the form renders with.
255
255
  - **`collectIssues(error)`** — flattens a `ZodError` into `{ path, message }` entries with **dotted paths** (e.g. `rules.0.value`), including nested list issues.
256
256
 
257
257
  ### What each path validates
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-luna-form",
3
- "version": "0.0.83",
3
+ "version": "0.0.84",
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",
@@ -31,11 +31,11 @@
31
31
  },
32
32
  "devDependencies": {
33
33
  "@types/react": "19.2.18",
34
- "@types/react-dom": "19.2.4",
34
+ "@types/react-dom": "19.2.5",
35
35
  "eslint-plugin-react": "7.37.5",
36
36
  "eslint-plugin-react-hooks": "7.1.1",
37
- "eslint-plugin-react-refresh": "0.5.4",
38
- "@luna-form/core": "0.0.83"
37
+ "eslint-plugin-react-refresh": "0.5.5",
38
+ "@luna-form/core": "0.0.84"
39
39
  },
40
40
  "license": "Apache-2.0",
41
41
  "repository": {