@whatsaaaa/form-builder-affects 0.1.0 → 0.1.2

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.
Files changed (2) hide show
  1. package/README.md +29 -136
  2. package/package.json +3 -3
package/README.md CHANGED
@@ -1,6 +1,16 @@
1
1
  # @whatsaaaa/form-builder-affects
2
2
 
3
- Conditions for JSON form definitions of [`@whatsaaaa/form-builder-schema`](https://github.com/whatsaaaa/form-builder/tree/main/packages/schema): show, hide and clear fields, sections and steps by the values of other fields. A hidden field isn't validated, in every Formisch package and in a plain `v.parse`.
3
+ Show, hide and clear the fields, sections and steps of JSON form definitions by conditions.
4
+
5
+ [![npm version](https://img.shields.io/npm/v/@whatsaaaa/form-builder-affects)](https://www.npmjs.com/package/@whatsaaaa/form-builder-affects)
6
+ [![npm downloads](https://img.shields.io/npm/dm/@whatsaaaa/form-builder-affects)](https://www.npmjs.com/package/@whatsaaaa/form-builder-affects)
7
+ [![CI](https://img.shields.io/github/actions/workflow/status/whatsaaaa/form-builder/ci.yml?branch=main&label=CI)](https://github.com/whatsaaaa/form-builder/actions/workflows/ci.yml)
8
+ [![Coverage](https://codecov.io/gh/whatsaaaa/form-builder/graph/badge.svg?flag=affects)](https://codecov.io/gh/whatsaaaa/form-builder)
9
+ [![Bundle size](https://deno.bundlejs.com/badge?q=@whatsaaaa/form-builder-affects&treeshake=%5B%7Baffects%7D%5D&config=%7B%22esbuild%22:%7B%22external%22:%5B%22valibot%22,%22@whatsaaaa/form-builder-schema%22%5D%7D%7D)](https://bundlejs.com/?q=@whatsaaaa/form-builder-affects&treeshake=%5B%7Baffects%7D%5D&config=%7B%22esbuild%22:%7B%22external%22:%5B%22valibot%22,%22@whatsaaaa/form-builder-schema%22%5D%7D%7D)
10
+ [![Types](https://img.shields.io/npm/types/@whatsaaaa/form-builder-affects)](https://www.npmjs.com/package/@whatsaaaa/form-builder-affects)
11
+ [![License](https://img.shields.io/npm/l/@whatsaaaa/form-builder-affects)](https://github.com/whatsaaaa/form-builder/blob/main/LICENSE)
12
+
13
+ This package is an extension for [`@whatsaaaa/form-builder-schema`](https://github.com/whatsaaaa/form-builder/tree/main/packages/schema). It adds `affects` to definitions: conditions on the values of other fields that show a field, hide it, or hide it and clear its value. A hidden field isn't validated, in every Formisch package and in a plain `v.parse`.
4
14
 
5
15
  ## Install
6
16
 
@@ -8,7 +18,7 @@ Conditions for JSON form definitions of [`@whatsaaaa/form-builder-schema`](https
8
18
  pnpm add @whatsaaaa/form-builder-affects @whatsaaaa/form-builder-schema valibot
9
19
  ```
10
20
 
11
- ## Usage
21
+ ## Example
12
22
 
13
23
  Give the schema builder the extension, and write `affects` on the fields:
14
24
 
@@ -42,146 +52,29 @@ const json: JsonForm = {
42
52
  const schema = builder.build(json) // vatId is required only for a business account
43
53
  ```
44
54
 
45
- With the [Vue](https://github.com/whatsaaaa/form-builder/tree/main/frameworks/vue) or [React](https://github.com/whatsaaaa/form-builder/tree/main/frameworks/react) adapter, pass the builder, and the hidden fields, sections and steps leave the form:
46
-
47
- ```ts
48
- const jsonForm = useJsonForm(json, { builder, components })
49
- ```
50
-
51
- Without the extension, `affects` is an unknown key: `validate()` reports it, and `build()` throws. A definition never runs without its conditions by mistake.
52
-
53
- ## Affects
54
-
55
- `affects` is a list of effects, each with the condition that applies it:
56
-
57
- | Effect | While its condition is met |
58
- | -------------- | ------------------------------------------------------- |
59
- | `show` | the field shows. Otherwise it is hidden, like `hide` |
60
- | `hide` | the field is hidden, and keeps its value |
61
- | `hideAndClear` | the field is hidden, and its value cleared: it is blank |
62
-
63
- - **Hide always wins.** A field is hidden when its `show` condition isn't met, or its `hide` or `hideAndClear` condition is. It is cleared only when its `hideAndClear` condition is met.
64
- - **Each effect goes once on a field.** Join conditions with `all` or `any`. A field can have `show` with `hide` or `hideAndClear`.
65
- - **Where:** any field in `fields`, at any depth, display elements, groups and arrays included, and the layout's steps, sections and display elements. Not on a row, nor on the `of` field of a list, which is the item itself: put them on the list.
66
-
67
- ## Conditions
68
-
69
- ```text
70
- Condition = { "field": Path, Operator: Value }
71
- | { "all": [Condition, ...] }
72
- | { "any": [Condition, ...] }
73
- ```
74
-
75
- A test reads one field, by its dot path, with exactly one operator. `all` and `any` take at least one condition, and nest to any depth.
76
-
77
- | Operator | Value | True when the field |
78
- | ----------- | --------------------------- | --------------------------------------------------------- |
79
- | `equals` | a string, number or boolean | holds the value |
80
- | `notEquals` | a string, number or boolean | doesn't hold the value, or is empty |
81
- | `in` | a list of at least one | holds one of the values |
82
- | `includes` | a string, number or boolean | is a multiselect or a list of values, and holds the value |
83
- | `empty` | `true` | is empty |
84
- | `notEmpty` | `true` | isn't empty |
85
-
86
- Paths are the paths of form rules: `isBusiness`, `billing.zip`, or `lines.*.kind` for the field of the **same item** of `lines`, from a field, section or display element inside its items. A path with an index, like `lines.0.kind`, or to an item's field from outside the items, is an issue. Nested lists bind a `*` to each item the field is in: `orders.*.lines.*.sku`.
87
-
88
- ### Operators by kind
89
-
90
- | Kind | `equals`, `notEquals`, `in` | `includes` | `empty`, `notEmpty` |
91
- | ---------------------------------------------------------------------- | --------------------------- | ---------- | ------------------- |
92
- | string, number, boolean, date (text, select, radio, checkbox, date...) | yes | no | yes |
93
- | array: a multiselect, or a list of values with `of` | no | yes | yes |
94
- | array: a list of objects with `fields`, or of files | no | no | yes |
95
- | file | no | no | yes |
96
- | object: a group, which is never empty | no | no | no |
97
- | your own kinds | no | no | yes |
98
-
99
- A value must be one the field can hold: its schema, rules included, takes it, like a `default`. So `{ "field": "country", "equals": "Germny" }` on a select is an issue, not a condition that is never met. A date is the ISO string the field takes: `"2026-11-09"`, `"14:30"`, `"2026-11-09T14:30"`.
100
-
101
- - **Conditions read the input**, what the form holds now, turned the way a renderer sets it: `"5"` is `5` for a number. Normalize rules like `trim` aren't applied.
102
- - **Empty** is the kind's empty, the one `required` uses: `undefined`, `''`, `[]`. An empty field never `equals` anything, and `notEquals` is true for it.
103
- - **A checkbox nobody touched holds nothing**, not `false`. To show something while it isn't ticked, write `{ "notEquals": true }`, or give the checkbox `"default": false`. A required checkbox only takes `true`, so `{ "equals": false }` on one is an issue.
104
-
105
- ## What a hidden field does
106
-
107
- - **It isn't validated:** no `required`, no rules, and no form rule that reads it, or is on it. A hidden list skips its own `required`, `minLength` and `maxLength` too. Formisch shows no error on it, and an old one goes at the next validation.
108
- - **It counts as empty** in every other condition, so a chain follows: when `companyType` hides with the business account, a `vatId` shown for `companyType: "ltd"` hides too, though `companyType` still holds `"ltd"`.
109
- - **A field `hideAndClear` hides is blank**: it submits `undefined`, a list `[]`, and each field of a group is blank. The adapters set it blank in the form too, with Formisch's `setInput`, as soon as it is cleared, so it is blank when it shows again. A field that starts hidden this way starts blank, without its `default`.
110
- - **A field hidden otherwise keeps its value**: by `hide`, by a `show` that isn't met, or by a group, list, section or step that `hide` hides. It submits its output when its value is valid, and nothing when it isn't, so its errors, which nobody can see, never block the form.
55
+ With the Vue or React adapter, pass the builder to `useJsonForm`, and hidden fields, sections and steps leave the form. Without the extension, `affects` is an unknown key, so a definition never runs without its conditions by mistake.
111
56
 
112
- So **plain hiding submits old values**. A product line that was a service line still submits its hours, and a hidden "Repeat the shipping ZIP" still submits what it held. Use `hideAndClear` for data that only makes sense while the field shows, and `hide` or `show` to keep what the user typed for when the field comes back. To clear a field a `show` hides, give it `hideAndClear` with the opposite condition.
57
+ ## Highlights
113
58
 
114
- Typing the form, mark every field that can hide as optional in the output type: a required field submits nothing while it is hidden.
59
+ - Three effects: `show`, `hide`, and `hideAndClear`, which also blanks the value
60
+ - Conditions on any field by its path, combined with `all` and `any`
61
+ - Works on fields at any depth, display elements, and the layout's sections and steps
62
+ - Hidden fields skip validation, so a required field only counts while it shows
63
+ - Checks every condition against the fields it reads, and reports loops
64
+ - `getVisibility` tells a renderer in any framework what to hide and clear
65
+ - Ships a `definition.schema.json` with `affects` for editor autocompletion
115
66
 
116
- ## Groups, lists, sections and steps
117
-
118
- - **A hidden group, list, section or step hides everything in it**, and clears it when `hideAndClear` hides it. A field inside is hidden even when its own `show` is met.
119
- - **A group whose fields are all hidden hides too.** The fields that count are those with a value, nested groups and lists included, and the display elements with `affects` of their own: a heading without conditions doesn't keep a group on show, but a paragraph shown by a condition does. A list never hides this way: it has a value of its own, and its add button.
120
- - **A section, a row or a step of the layout whose fields are all hidden hides**, by the same rule. A field hidden in a row drops out of it, and the row's other cells keep their spans: no field moves to another row. Next and Back skip a hidden step, and the adapters leave it out of `steps.list`, unless the user is on it.
121
- - **A step or a section with `affects`** hides and clears the fields placed in it. Where the layout splits a group, only the fields of the group placed in the section go with it.
122
-
123
- ## Loops
124
-
125
- A condition can't depend on itself. These are all `cycle` issues, which name the loop:
126
-
127
- - `a` hides with `b`, and `b` with `a`;
128
- - a field that reads itself;
129
- - a group that reads a field inside it, like "show `company` when `company.name` is filled": it would have two answers;
130
- - a step or a section that reads a field placed in it.
131
-
132
- The fields of a list's items are checked once, by their `*` paths.
133
-
134
- ## Checks
135
-
136
- `validate()` checks every `affects` along with the rest of the definition:
137
-
138
- | Problem | Code |
139
- | ------------------------------------------------------------------------------------------------------------- | --------------- |
140
- | `affects` isn't a list, an affect isn't an object, an unknown `effect`, no `when`, an effect twice | `invalid_shape` |
141
- | A test with no operator, or with two; `all` or `any` with `field`; an empty `all`, `any` or `in`; a bad value | `invalid_shape` |
142
- | `affects` on the `of` field of a list | `invalid_shape` |
143
- | A key no affect or condition takes, like a misspelled operator `equal` | `unknown_key` |
144
- | A path to no field with a value: a typo, a display element, an index, an item's field from outside its item | `unknown_field` |
145
- | An operator the field's kind doesn't take, like `equals` on a file | `kind_mismatch` |
146
- | A value the field can't hold, or an empty one | `invalid_value` |
147
- | Conditions that depend on each other | `cycle` |
148
-
149
- Each issue has its full path, such as `['fields', 3, 'affects', 0, 'when', 'all', 1, 'field']`. A field a condition reads that has issues of its own adds none.
150
-
151
- ## Rendering
152
-
153
- The schema skips hidden fields and gives their output either way, so validation and submitting need nothing more. To render, `builder.getVisibility(json)` tells what to hide, in any framework:
154
-
155
- ```ts
156
- const visibility = builder.getVisibility(json)
157
- const state = visibility.of(getInput(form)) // the same object until what is hidden changes
158
-
159
- state.isHidden(fieldNode, indexes) // a field of getFieldTree or getLayout
160
- state.isStepHidden(step)
161
- state.shownNodes(nodes, indexes) // the layout nodes to render
162
- for (const { path, input } of state.toClear) setInput(form, { path, input }) // in an effect
163
- ```
164
-
165
- The Vue and React adapters do all of this. [Formisch in each framework](https://github.com/whatsaaaa/form-builder/blob/main/packages/schema/docs/frameworks.md) has a renderer for Preact, Solid, Svelte, Qwik and Angular that does it too. Without the clearing effect, the output is still right, but a cleared field shows its old value when it comes back.
166
-
167
- ## JSON Schema
168
-
169
- The package ships `definition.schema.json`, the JSON Schema of definitions for a builder with `affects()`: every key of the schema package's own, with `affects` on fields, steps and sections. Point a definition at it with `$schema`, and editors suggest effects and operators:
170
-
171
- ```json
172
- {
173
- "$schema": "./node_modules/@whatsaaaa/form-builder-affects/definition.schema.json",
174
- "version": "1",
175
- "fields": []
176
- }
177
- ```
67
+ ## Documentation
178
68
 
179
- It can't check paths, kinds, values or loops: `validate()` does. For a builder with your own registries too, make it with the schema package's `toJsonSchema(builder)`, which takes the extension's keys.
69
+ - [Guide](https://whatsaaaa.github.io/form-builder/guide/conditions): effects, conditions and their operators, what a hidden field does, groups, sections and steps, loops and rendering
70
+ - [API reference](https://whatsaaaa.github.io/form-builder/api/affects/)
180
71
 
181
- ## TypeScript
72
+ ## Related packages
182
73
 
183
- `builder.defineForm()` types `affects` on fields, steps, sections and display elements, with the types `Affect`, `Condition`, `Effect` and `Scalar` this package exports.
74
+ - [`@whatsaaaa/form-builder-schema`](https://github.com/whatsaaaa/form-builder/tree/main/packages/schema): build Formisch-ready Valibot schemas from JSON definitions
75
+ - [`@whatsaaaa/form-builder-vue`](https://github.com/whatsaaaa/form-builder/tree/main/frameworks/vue): render definitions with your own Vue components
76
+ - [`@whatsaaaa/form-builder-react`](https://github.com/whatsaaaa/form-builder/tree/main/frameworks/react): render definitions with your own React components
184
77
 
185
78
  ## License
186
79
 
187
- MIT
80
+ [MIT](https://github.com/whatsaaaa/form-builder/blob/main/LICENSE)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@whatsaaaa/form-builder-affects",
3
- "version": "0.1.0",
3
+ "version": "0.1.2",
4
4
  "description": "Show, hide and clear the fields, sections and steps of JSON form definitions by conditions.",
5
5
  "keywords": [
6
6
  "conditional",
@@ -39,7 +39,7 @@
39
39
  "@size-limit/preset-small-lib": "^14.3.0",
40
40
  "@valibot/to-json-schema": "^1.8.0",
41
41
  "@vitest/coverage-v8": "^5.0.5",
42
- "@whatsaaaa/form-builder-schema": "^0.15.0",
42
+ "@whatsaaaa/form-builder-schema": "^0.15.2",
43
43
  "ajv": "^8.18.0",
44
44
  "jiti": "^2.7.0",
45
45
  "publint": "^0.3.25",
@@ -50,7 +50,7 @@
50
50
  "vitest": "^5.0.5"
51
51
  },
52
52
  "peerDependencies": {
53
- "@whatsaaaa/form-builder-schema": "^0.15.0",
53
+ "@whatsaaaa/form-builder-schema": "^0.15.2",
54
54
  "valibot": "^1.3.0"
55
55
  },
56
56
  "scripts": {