@whatsaaaa/form-builder-vue 0.0.0-stage → 0.1.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Whatsaaaa
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,3 +1,248 @@
1
- # Temporary Holding Version
1
+ # @whatsaaaa/form-builder-vue
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ [![npm](https://img.shields.io/npm/v/@whatsaaaa/form-builder-vue)](https://www.npmjs.com/package/@whatsaaaa/form-builder-vue)
4
+
5
+ Render JSON form definitions with [Formisch](https://formisch.dev) and your own Vue components.
6
+
7
+ It's headless: it reads the definition, connects every field to Formisch and turns input into the values the schema expects, but it renders nothing of its own. You give it a component per field type, so it works with plain HTML, Nuxt UI or any other UI library.
8
+
9
+ ## Install
10
+
11
+ ```sh
12
+ pnpm add @whatsaaaa/form-builder-vue @formisch/vue valibot
13
+ ```
14
+
15
+ Vue 3.5, `@formisch/vue` 1.1 and Valibot 1.3 or later are required. `@whatsaaaa/form-builder-schema` comes with it.
16
+
17
+ ## Usage
18
+
19
+ ```vue
20
+ <script setup lang="ts">
21
+ import { Form } from '@formisch/vue'
22
+ import type { JsonForm } from '@whatsaaaa/form-builder-schema'
23
+ import { JsonFields, useJsonForm } from '@whatsaaaa/form-builder-vue'
24
+ import { components } from './fields'
25
+
26
+ const { definition } = defineProps<{ definition: JsonForm }>()
27
+
28
+ const jsonForm = useJsonForm(definition, { components })
29
+
30
+ function save(output: unknown) {
31
+ // The output of the schema: trimmed, numbers as numbers, empty optional fields left out.
32
+ }
33
+ </script>
34
+
35
+ <template>
36
+ <Form :of="jsonForm.form" @submit="save">
37
+ <JsonFields :of="jsonForm" />
38
+ <button type="submit">Save</button>
39
+ </Form>
40
+ </template>
41
+ ```
42
+
43
+ - `useJsonForm(definition, options?)` builds the schema and the initial input, and creates the Formisch form. `jsonForm.form` is a normal Formisch form, so every Formisch method works on it.
44
+ - `<JsonFields>` renders the fields inside your `<Form>`. The form element, the submit button and anything around them are yours.
45
+ - A definition with issues throws the builder's `DefinitionError`. Run `validate()` first when the definition comes from outside your code.
46
+ - The definition is read once. To show another one, give the component that calls `useJsonForm` a new `:key`.
47
+
48
+ ### Options
49
+
50
+ ```ts
51
+ useJsonForm(definition, {
52
+ builder: createSchemaBuilder({ fieldTypes: { money } }), // your own types, rules and messages
53
+ components, // your component per field type, see below
54
+ validate: 'blur', // when a field is validated the first time, as in Formisch
55
+ revalidate: 'input', // when it is validated again
56
+ })
57
+ ```
58
+
59
+ ## Components
60
+
61
+ `components` has one component per field type, by type name. Pass it to `useJsonForm`, or to `<JsonFields>` and `<JsonField>` as a prop, which wins. Several types can share one:
62
+
63
+ ```ts
64
+ import type { FieldComponents } from '@whatsaaaa/form-builder-vue'
65
+
66
+ export const components: FieldComponents = {
67
+ text: TextInput,
68
+ email: TextInput,
69
+ number: TextInput,
70
+ select: SelectInput,
71
+ group: Fieldset,
72
+ array: Repeater,
73
+ heading: Heading,
74
+ // …one for every type your definitions use
75
+ }
76
+ ```
77
+
78
+ A type that a definition uses but `components` doesn't have throws a `TypeError` that names every missing type, when `<JsonFields>` or `<JsonField>` sets up. Only the fields it renders count.
79
+
80
+ What a component gets depends on the field's role:
81
+
82
+ | Role | Which fields | Props | Slot |
83
+ | ------- | ------------------------------ | -------------------------------------------------------------------------------- | -------------------------------------------- |
84
+ | value | every field with a value | `field`, `path`, `kind`, `required`, `input`, `errors`, `inputProps`, `setInput` | — |
85
+ | group | a container of kind `object` | `field`, `path` | default: the group's fields |
86
+ | array | a container of kind `array` | `field`, `path`, `items`, `errors`, `add`, `remove`, `move` | default, with `{ index }`: one item's fields |
87
+ | display | a display element, kind `none` | `field` | — |
88
+
89
+ The props are typed as `ValueFieldProps`, `GroupFieldProps`, `ArrayFieldProps` and `DisplayFieldProps`, and the slots as `GroupFieldSlots` and `ArrayFieldSlots`. `defineProps<ValueFieldProps>()` works in `<script setup>`.
90
+
91
+ Your own container types render through the group or array role too, by their kind.
92
+
93
+ ### A value field
94
+
95
+ ```vue
96
+ <script setup lang="ts">
97
+ import type { ValueFieldProps } from '@whatsaaaa/form-builder-vue'
98
+
99
+ defineProps<ValueFieldProps>()
100
+ </script>
101
+
102
+ <template>
103
+ <label>
104
+ {{ field.label }}
105
+ <input
106
+ v-bind="inputProps"
107
+ :type="kind === 'number' ? 'number' : 'text'"
108
+ :value="input ?? ''"
109
+ :required="required"
110
+ @input="setInput(($event.target as HTMLInputElement).value)"
111
+ />
112
+ <small v-if="errors">{{ errors[0] }}</small>
113
+ </label>
114
+ </template>
115
+ ```
116
+
117
+ - `field` is the field from the definition, with its `label`, `placeholder`, `options` and `ui`.
118
+ - `path` is its Formisch path with real indexes, like `['lines', 0, 'quantity']`.
119
+ - `inputProps` are Formisch's element props: the field's name, a ref, and focus, change and blur handlers. Bind them to the input element, so Formisch knows when the field is touched and can focus it after a failed submit.
120
+ - `required` is true when the definition makes the field required.
121
+
122
+ ### setInput
123
+
124
+ `setInput` sets the field's input after turning what an input element gives into what the schema expects, by the field's kind:
125
+
126
+ | Kind | Input | Value set |
127
+ | ------------------- | ----------------------- | ------------------------------ |
128
+ | `number` | `''` or only spaces | `undefined` |
129
+ | `number` | a numeric string, `'5'` | `5` |
130
+ | `file` | a `FileList` | its first file, or `undefined` |
131
+ | `array` of `file` | a `FileList` | an array of its files |
132
+ | `array` of `number` | numeric strings | numbers |
133
+
134
+ Anything else is set as it is, and the schema reports it. For a choice, like a radio or a select, set the option's value from `field.options` rather than the element's value: that keeps `2` a number and `true` a boolean.
135
+
136
+ ### A group
137
+
138
+ ```vue
139
+ <script setup lang="ts">
140
+ import type { GroupFieldProps, GroupFieldSlots } from '@whatsaaaa/form-builder-vue'
141
+
142
+ defineProps<GroupFieldProps>()
143
+ defineSlots<GroupFieldSlots>()
144
+ </script>
145
+
146
+ <template>
147
+ <fieldset>
148
+ <legend>{{ field.label }}</legend>
149
+ <slot />
150
+ </fieldset>
151
+ </template>
152
+ ```
153
+
154
+ ### An array
155
+
156
+ ```vue
157
+ <script setup lang="ts">
158
+ import type { ArrayFieldProps, ArrayFieldSlots } from '@whatsaaaa/form-builder-vue'
159
+
160
+ defineProps<ArrayFieldProps>()
161
+ defineSlots<ArrayFieldSlots>()
162
+ </script>
163
+
164
+ <template>
165
+ <fieldset>
166
+ <legend>{{ field.label }}</legend>
167
+ <div v-for="(item, index) in items" :key="item">
168
+ <slot :index="index" />
169
+ <button type="button" @click="remove(index)">Remove</button>
170
+ </div>
171
+ <button type="button" @click="add()">Add</button>
172
+ <small v-if="errors">{{ errors[0] }}</small>
173
+ </fieldset>
174
+ </template>
175
+ ```
176
+
177
+ - `items` has one id per item. Use it as the `key`, so each item keeps its inputs when one is removed or moved.
178
+ - `add()` adds an item at the end, with the defaults its fields have in the definition.
179
+ - `errors` are the array's own messages, like a required array's or `maxLength`'s. Its items' fields show their own.
180
+
181
+ ### A display element
182
+
183
+ ```vue
184
+ <script setup lang="ts">
185
+ import type { DisplayFieldProps } from '@whatsaaaa/form-builder-vue'
186
+
187
+ defineProps<DisplayFieldProps>()
188
+ </script>
189
+
190
+ <template>
191
+ <h2>{{ field.text }}</h2>
192
+ </template>
193
+ ```
194
+
195
+ Declare the props even when a component doesn't use them, like a divider, or Vue adds `field` to its element as an attribute.
196
+
197
+ ## Placing fields yourself
198
+
199
+ `<JsonFields>` renders every field in the order of the definition. To put some of them somewhere else, place them with `<JsonField>` and leave them out of `<JsonFields>` with `except`:
200
+
201
+ ```vue
202
+ <template>
203
+ <Form :of="jsonForm.form" @submit="save">
204
+ <main>
205
+ <JsonFields :of="jsonForm" :except="['coupon', 'acceptTerms']" />
206
+ </main>
207
+ <aside>
208
+ <JsonField :of="jsonForm" path="coupon" />
209
+ <JsonField :of="jsonForm" path="acceptTerms" />
210
+ <button type="submit">Place order</button>
211
+ </aside>
212
+ </Form>
213
+ </template>
214
+ ```
215
+
216
+ - A path is the field's names joined with dots: `'email'`, or `'address.city'` for a field of a group. A placed field validates, focuses and submits like any other, because it is still part of the one Formisch form.
217
+ - A group or an array comes with all its fields. A field of an array's items can't be placed on its own, because it has no index outside its item, so place the array. To leave such a field out of every item, use `'*'` for the index: `except: ['lines.*.note']`.
218
+ - `only` renders just the fields it names, in the order of the definition: `<JsonFields :of="jsonForm" :only="['name', 'email']" />`. Display elements have no name, so `only` leaves them out.
219
+ - A path that names no field throws a `TypeError`, so a typo shows up at once. `only` and `except` are read when the component sets up.
220
+
221
+ ## Typing the form
222
+
223
+ The definition is data, so TypeScript doesn't know its fields, and `jsonForm.form` is typed loosely: Formisch's own functions accept only top-level paths on it. Give `useJsonForm` your input type, and your output type when it differs, and they check paths and values as in any Formisch form:
224
+
225
+ ```ts
226
+ import { getInput, useFieldArray } from '@formisch/vue'
227
+
228
+ interface CheckoutInput {
229
+ name: string
230
+ address: { city: string }
231
+ lines: { product: string; quantity: number }[]
232
+ }
233
+
234
+ const jsonForm = useJsonForm<CheckoutInput>(definition, { components })
235
+
236
+ getInput(jsonForm.form, { path: ['address', 'city'] }) // string | undefined
237
+ useFieldArray(jsonForm.form, { path: ['lines'] })
238
+ ```
239
+
240
+ - Formisch's `<Field>`, `<FieldArray>`, `useField`, `getInput` and `setInput` work on `jsonForm.form` either way. With a type, nested paths type-check, their values are typed, and so is the output your submit handler gets.
241
+ - The type is a promise you make: nothing checks it against the definition. Keep the two in step.
242
+ - A field you render with Formisch's `<Field>` gets none of the adapter's help, like `setInput` turning `'5'` into `5`, or the defaults of a new array item. Leave it out of `<JsonFields>` with `except`.
243
+
244
+ ## Not included
245
+
246
+ - Labels and display text reach your components as written in the definition. The builder's `resolveMessage` only changes what goes into the schema, so translate `field.label` in your components.
247
+ - `ui.widget` doesn't pick a component. A component can read `field.ui` itself, e.g. to show a multiselect as checkboxes.
248
+ - Showing and hiding fields, or making one required because of another, belongs to a future affects package.
@@ -0,0 +1,204 @@
1
+ import { Component, PropType, VNode } from "vue";
2
+ import { FieldElementProps, FormSchema, FormStore, ValidationMode } from "@formisch/vue";
3
+ import { JsonField as JsonField$1, JsonForm, SchemaBuilder, ValueKind } from "@whatsaaaa/form-builder-schema";
4
+ import { GenericSchema } from "valibot";
5
+ /** Your component for each field type, by type name: `{ text: TextInput, group: Fieldset }`. */
6
+ type FieldComponents = Record<string, Component>;
7
+ /** A field's Formisch path, with real indexes: `['lines', 0, 'quantity']`. */
8
+ type FieldPath = readonly (string | number)[];
9
+ /** The props of a component for a display element, like a heading. */
10
+ interface DisplayFieldProps {
11
+ field: JsonField$1;
12
+ }
13
+ /** The props of a component for a field with a value. */
14
+ interface ValueFieldProps {
15
+ field: JsonField$1;
16
+ path: FieldPath;
17
+ /** The kind of the value, e.g. `'number'`. */
18
+ kind: ValueKind;
19
+ /** True when the definition makes the field required. */
20
+ required: boolean;
21
+ /** The field's current input. */
22
+ input: unknown;
23
+ /** The field's error messages, or null when it has none. */
24
+ errors: [string, ...string[]] | null;
25
+ /** Bind these to the input element: its name, ref, and focus, change and blur handlers. */
26
+ inputProps: FieldElementProps;
27
+ /** Sets the input, after turning `'5'` into `5` for a number, or a `FileList` into files. */
28
+ setInput: (input: unknown) => void;
29
+ }
30
+ /** The props of a component for a container of kind `object`, like the built-in group. */
31
+ interface GroupFieldProps {
32
+ field: JsonField$1;
33
+ path: FieldPath;
34
+ }
35
+ /** The slots of a group component: its default slot renders the group's fields. */
36
+ interface GroupFieldSlots {
37
+ default(): VNode[];
38
+ }
39
+ /** The props of a component for a container of kind `array`, like the built-in array. */
40
+ interface ArrayFieldProps {
41
+ field: JsonField$1;
42
+ path: FieldPath;
43
+ /** One id per item, to use as its `key`. */
44
+ items: string[];
45
+ /** The array's own error messages, e.g. from `maxLength`, or null. */
46
+ errors: [string, ...string[]] | null;
47
+ /** Adds an item at the end. */
48
+ add: () => void;
49
+ remove: (index: number) => void;
50
+ move: (from: number, to: number) => void;
51
+ }
52
+ /** The slots of an array component: its default slot renders the fields of the item at `index`. */
53
+ interface ArrayFieldSlots {
54
+ default(props: {
55
+ index: number;
56
+ }): VNode[];
57
+ }
58
+ /**
59
+ * How `<JsonFields>` renders a field: a `value` field has an input, a `group` or an `array` holds
60
+ * fields, and a `display` element, like a heading, has no value.
61
+ */
62
+ type FieldRole = 'value' | 'group' | 'array' | 'display';
63
+ /** One field of a definition, with the fields inside it when it is a container. */
64
+ interface FieldNode {
65
+ readonly field: JsonField$1;
66
+ /** The field's type, e.g. `'text'`. */
67
+ readonly type: string;
68
+ readonly role: FieldRole;
69
+ /** The kind of the field's value, e.g. `'number'`, or `'none'` for a display element. */
70
+ readonly kind: ValueKind;
71
+ /** The kind of the items, for a field of kind `array` whose type has one. */
72
+ readonly itemKind: ValueKind | undefined;
73
+ /** Where the value is, with `'*'` for each array index: `['lines', '*', 'quantity']`. */
74
+ readonly dataPath: readonly (string | number)[];
75
+ /** The fields of a group, or of one item of an array. */
76
+ readonly children: readonly FieldNode[];
77
+ }
78
+ interface UseJsonFormOptions {
79
+ /** Builds the schema and knows your own types and rules. Defaults to `createSchemaBuilder()`. */
80
+ builder?: SchemaBuilder;
81
+ /** Your component for each field type, so `<JsonFields>` and `<JsonField>` don't need them. */
82
+ components?: FieldComponents;
83
+ /** When a field is validated the first time, as in Formisch. */
84
+ validate?: ValidationMode;
85
+ /** When a field is validated again after that, as in Formisch. */
86
+ revalidate?: Exclude<ValidationMode, 'initial'>;
87
+ }
88
+ /** The schema of a definition, as the builder builds it. */
89
+ type JsonFormSchema = ReturnType<SchemaBuilder['build']>;
90
+ /**
91
+ * The schema type of `useJsonForm<TInput, TOutput>`: your types, so Formisch's paths and values
92
+ * are checked. An interface works too, which Formisch's own constraint wouldn't take as it is.
93
+ */
94
+ type TypedJsonFormSchema<TInput extends object, TOutput extends object = TInput> = GenericSchema<Plain<TInput>, Plain<TOutput>>;
95
+ /** The same type as an object literal type, which has the index signature Formisch requires. */
96
+ type Plain<T> = { [K in keyof T]: T[K]; };
97
+ /** What `useJsonForm` returns. Pass it to `<JsonFields>`. */
98
+ interface JsonFormInstance<TSchema extends FormSchema = JsonFormSchema> {
99
+ /** The Formisch form: pass it to Formisch's `<Form :of>`, or to any of its methods. */
100
+ readonly form: FormStore<TSchema>;
101
+ readonly definition: JsonForm;
102
+ readonly builder: SchemaBuilder;
103
+ /** The components from the options, if any. */
104
+ readonly components: FieldComponents | undefined;
105
+ /** The definition's fields as a tree, the way `<JsonFields>` renders them. */
106
+ readonly fields: readonly FieldNode[];
107
+ }
108
+ /** Any form `useJsonForm` returns, typed or not: what the components take. */
109
+ type AnyJsonFormInstance = JsonFormInstance<FormSchema>;
110
+ /**
111
+ * A Formisch form for a definition, with its schema and initial input from the builder. A
112
+ * definition with issues throws the builder's `DefinitionError`, so run `validate()` first when it
113
+ * comes from outside your code. The definition is read once: give the form a new `:key` to use
114
+ * another one.
115
+ */
116
+ export declare function useJsonForm(definition: JsonForm, options?: UseJsonFormOptions): JsonFormInstance;
117
+ /**
118
+ * The same, with the form typed by your input type, and output type when it differs, so Formisch's
119
+ * paths and values are checked. The types are yours to keep in step with the definition.
120
+ */
121
+ export declare function useJsonForm<TInput extends object, TOutput extends object = TInput>(definition: JsonForm, options?: UseJsonFormOptions): JsonFormInstance<TypedJsonFormSchema<TInput, TOutput>>;
122
+ /**
123
+ * Renders one field of a `useJsonForm` form, wherever you put it inside Formisch's `<Form>`. A
124
+ * group or an array comes with its fields. Leave the field out of `<JsonFields>` with `except`.
125
+ */
126
+ export declare const JsonField: import("vue").DefineComponent<import("vue").ExtractPropTypes<{
127
+ /** What `useJsonForm` returned. */
128
+ of: {
129
+ type: PropType<AnyJsonFormInstance>;
130
+ required: true;
131
+ };
132
+ /** The field's path: `'email'`, or `'address.city'` for a field of a group. */
133
+ path: {
134
+ type: StringConstructor;
135
+ required: true;
136
+ };
137
+ /** Defaults to the components `useJsonForm` got. */
138
+ components: {
139
+ type: PropType<FieldComponents>;
140
+ };
141
+ }>, () => import("vue").VNode<import("vue").RendererNode, import("vue").RendererElement, {
142
+ [key: string]: any;
143
+ }>, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<import("vue").ExtractPropTypes<{
144
+ /** What `useJsonForm` returned. */
145
+ of: {
146
+ type: PropType<AnyJsonFormInstance>;
147
+ required: true;
148
+ };
149
+ /** The field's path: `'email'`, or `'address.city'` for a field of a group. */
150
+ path: {
151
+ type: StringConstructor;
152
+ required: true;
153
+ };
154
+ /** Defaults to the components `useJsonForm` got. */
155
+ components: {
156
+ type: PropType<FieldComponents>;
157
+ };
158
+ }>> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
159
+ /**
160
+ * Renders the fields of a `useJsonForm` form with your components, one per field type. It renders
161
+ * nothing of its own, so put it inside Formisch's `<Form>` with your own submit button. `only` and
162
+ * `except` are read when it sets up.
163
+ */
164
+ export declare const JsonFields: import("vue").DefineComponent<import("vue").ExtractPropTypes<{
165
+ /** What `useJsonForm` returned. */
166
+ of: {
167
+ type: PropType<AnyJsonFormInstance>;
168
+ required: true;
169
+ };
170
+ /** Defaults to the components `useJsonForm` got. */
171
+ components: {
172
+ type: PropType<FieldComponents>;
173
+ };
174
+ /** Renders only these fields, by path, in the order of the definition. */
175
+ only: {
176
+ type: PropType<readonly string[]>;
177
+ };
178
+ /** Leaves out these fields, by path, e.g. the ones you place with `<JsonField>`. */
179
+ except: {
180
+ type: PropType<readonly string[]>;
181
+ };
182
+ }>, () => import("vue").VNode<import("vue").RendererNode, import("vue").RendererElement, {
183
+ [key: string]: any;
184
+ }>[], {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<import("vue").ExtractPropTypes<{
185
+ /** What `useJsonForm` returned. */
186
+ of: {
187
+ type: PropType<AnyJsonFormInstance>;
188
+ required: true;
189
+ };
190
+ /** Defaults to the components `useJsonForm` got. */
191
+ components: {
192
+ type: PropType<FieldComponents>;
193
+ };
194
+ /** Renders only these fields, by path, in the order of the definition. */
195
+ only: {
196
+ type: PropType<readonly string[]>;
197
+ };
198
+ /** Leaves out these fields, by path, e.g. the ones you place with `<JsonField>`. */
199
+ except: {
200
+ type: PropType<readonly string[]>;
201
+ };
202
+ }>> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
203
+ export type { AnyJsonFormInstance, ArrayFieldProps, ArrayFieldSlots, DisplayFieldProps, FieldComponents, FieldNode, FieldPath, FieldRole, GroupFieldProps, GroupFieldSlots, JsonFormInstance, JsonFormSchema, TypedJsonFormSchema, UseJsonFormOptions, ValueFieldProps };
204
+ //# sourceMappingURL=index.d.ts.map
package/dist/index.js ADDED
@@ -0,0 +1,368 @@
1
+ import { defineComponent, h } from "vue";
2
+ import { insert, move, remove, useField, useFieldArray, useForm } from "@formisch/vue";
3
+ import { createSchemaBuilder } from "@whatsaaaa/form-builder-schema";
4
+ // src/field-paths.ts
5
+ /**
6
+ * A field's path as `only`, `except` and `<JsonField>` name it: `'address.city'`, or
7
+ * `'lines.*.quantity'` for a field of an array's items. Undefined for a field without a name.
8
+ */
9
+ const pathOf = (node) => node.field.name === void 0 ? void 0 : node.dataPath.join(".");
10
+ /** Every field of the tree, in document order, a container before its fields. */
11
+ function* allFields(nodes) {
12
+ for (const node of nodes) {
13
+ yield node;
14
+ yield* allFields(node.children);
15
+ }
16
+ }
17
+ /**
18
+ * The field at `path`. A path that names no field is a mistake in the code, so it throws. With
19
+ * `placed`, the field is rendered on its own, which a field of an array's items can't be: it has no
20
+ * index outside its item.
21
+ */
22
+ function findField(nodes, path, placed) {
23
+ const node = [...allFields(nodes)].find((candidate) => pathOf(candidate) === path);
24
+ if (!node) throw new TypeError(`No field at "${path}" in the definition.`);
25
+ if (placed && node.dataPath.includes("*")) {
26
+ const array = path.slice(0, path.indexOf(".*"));
27
+ throw new TypeError(`"${path}" is in an array's items, so it can't be placed on its own. Place "${array}" instead.`);
28
+ }
29
+ return node;
30
+ }
31
+ // src/item-input.ts
32
+ /**
33
+ * The input of a new item of an array, from the defaults in the definition: an object of its
34
+ * fields' input, or the input of the item field for a list of single values.
35
+ */
36
+ function itemInput(builder, array) {
37
+ const fields = array.children.map((child) => child.field);
38
+ const [only] = array.children;
39
+ if (array.children.length === 1 && only?.role !== "display" && only?.dataPath.at(-1) === "*") return initialInput(builder, [{
40
+ ...only.field,
41
+ name: "item"
42
+ }]).item;
43
+ return initialInput(builder, fields);
44
+ }
45
+ const initialInput = (builder, fields) => builder.getInitialInput({
46
+ version: "1",
47
+ fields
48
+ });
49
+ // src/array-field.ts
50
+ /** Connects a container of kind `array` to Formisch, and renders your component for it. */
51
+ const ArrayField = defineComponent({
52
+ name: "ArrayField",
53
+ props: {
54
+ of: {
55
+ type: Object,
56
+ required: true
57
+ },
58
+ node: {
59
+ type: Object,
60
+ required: true
61
+ },
62
+ path: {
63
+ type: Array,
64
+ required: true
65
+ },
66
+ component: {
67
+ type: [Object, Function],
68
+ required: true
69
+ },
70
+ /** Renders the fields of the item at `index`. */
71
+ renderItem: {
72
+ type: Function,
73
+ required: true
74
+ }
75
+ },
76
+ setup(props) {
77
+ const path = () => props.path;
78
+ const array = useFieldArray(() => props.of.form, () => ({ path: path() }));
79
+ const methods = {
80
+ add: () => insert(props.of.form, {
81
+ path: path(),
82
+ initialInput: itemInput(props.of.builder, props.node)
83
+ }),
84
+ remove: (index) => remove(props.of.form, {
85
+ path: path(),
86
+ at: index
87
+ }),
88
+ move: (from, to) => move(props.of.form, {
89
+ path: path(),
90
+ from,
91
+ to
92
+ })
93
+ };
94
+ return () => {
95
+ const fieldProps = {
96
+ field: props.node.field,
97
+ path: props.path,
98
+ items: array.items,
99
+ errors: array.errors,
100
+ ...methods
101
+ };
102
+ return h(props.component, fieldProps, { default: ({ index }) => props.renderItem(index) });
103
+ };
104
+ }
105
+ });
106
+ // src/coerce-input.ts
107
+ /**
108
+ * Turns what an input element gives into what the field's schema expects: `'5'` into `5` for a
109
+ * number, and a `FileList` into a file, or an array of files for a field of kind `array`. Anything
110
+ * else is passed through, and the schema reports it.
111
+ */
112
+ function coerceInput(input, kind, itemKind) {
113
+ if (kind === "array") {
114
+ const items = isFileList(input) ? Array.from(input) : input;
115
+ if (!Array.isArray(items) || itemKind === void 0) return items;
116
+ return items.map((item) => coerceInput(item, itemKind));
117
+ }
118
+ if (isFileList(input)) return kind === "file" ? input[0] ?? void 0 : input;
119
+ if (kind === "number" && typeof input === "string") return toNumber(input);
120
+ return input;
121
+ }
122
+ /** An empty string means no value. One that isn't a number stays a string, for the schema. */
123
+ function toNumber(input) {
124
+ if (input.trim() === "") return void 0;
125
+ const number = Number(input);
126
+ return Number.isNaN(number) ? input : number;
127
+ }
128
+ /**
129
+ * A `FileList`, or anything shaped like one: an array-like whose items are all files. Checked by
130
+ * shape rather than with `instanceof FileList`, because a `File[]` from a component that spread the
131
+ * list, or from a DOM shim like happy-dom whose `DataTransfer.files` is an array, should count too.
132
+ * `File` only exists in a browser, so check for it before using it, e.g. during SSR.
133
+ */
134
+ function isFileList(value) {
135
+ if (typeof File === "undefined" || typeof value !== "object" || value === null) return false;
136
+ if (value instanceof File || typeof value.length !== "number") return false;
137
+ return Array.from(value).every((item) => item instanceof File);
138
+ }
139
+ // src/value-field.ts
140
+ /** Connects a field with a value to Formisch, and renders your component for it. */
141
+ const ValueField = defineComponent({
142
+ name: "ValueField",
143
+ props: {
144
+ of: {
145
+ type: Object,
146
+ required: true
147
+ },
148
+ node: {
149
+ type: Object,
150
+ required: true
151
+ },
152
+ path: {
153
+ type: Array,
154
+ required: true
155
+ },
156
+ component: {
157
+ type: [Object, Function],
158
+ required: true
159
+ }
160
+ },
161
+ setup(props) {
162
+ const field = useField(() => props.of.form, () => ({ path: props.path }));
163
+ const setInput = (input) => {
164
+ field.input = coerceInput(input, props.node.kind, props.node.itemKind);
165
+ };
166
+ return () => {
167
+ const fieldProps = {
168
+ field: props.node.field,
169
+ path: props.path,
170
+ kind: props.node.kind,
171
+ required: isRequired(props.node.field),
172
+ input: field.input,
173
+ errors: field.errors,
174
+ inputProps: field.props,
175
+ setInput
176
+ };
177
+ return h(props.component, fieldProps);
178
+ };
179
+ }
180
+ });
181
+ const isRequired = (field) => field.required === true || typeof field.required === "object" && field.required !== null;
182
+ // src/render-fields.ts
183
+ /** The components from the props, or else the ones `useJsonForm` got. */
184
+ function resolveComponents(components, of) {
185
+ const resolved = components ?? of.components;
186
+ if (!resolved) throw new TypeError("No field components. Pass them to useJsonForm, or to the component.");
187
+ return resolved;
188
+ }
189
+ /** Throws for the field types that would render without a component, naming each once. */
190
+ function checkComponents(ctx, nodes) {
191
+ const missing = /* @__PURE__ */ new Set();
192
+ const visit = (node) => {
193
+ if (ctx.skip(node)) return;
194
+ if (!Object.hasOwn(ctx.components, node.type)) missing.add(node.type);
195
+ node.children.forEach(visit);
196
+ };
197
+ nodes.forEach(visit);
198
+ if (!missing.size) return;
199
+ const names = [...missing].map((type) => `"${type}"`).join(", ");
200
+ const [noun, pronoun] = missing.size > 1 ? ["types", "them"] : ["type", "it"];
201
+ throw new TypeError(`No component for the field ${noun} ${names}. Add ${pronoun} to the components.`);
202
+ }
203
+ /** `indexes` are the indexes of the array items the fields are in, outermost first. */
204
+ function renderFields(ctx, nodes, indexes) {
205
+ return nodes.filter((node) => !ctx.skip(node)).map((node, position) => renderField(ctx, node, indexes, position));
206
+ }
207
+ function renderField(ctx, node, indexes, position) {
208
+ const component = ctx.components[node.type];
209
+ const path = toPath(node.dataPath, indexes);
210
+ const key = node.field.name ?? `#${position}`;
211
+ switch (node.role) {
212
+ case "display": return h(component, {
213
+ key,
214
+ field: node.field
215
+ });
216
+ case "group": return h(component, {
217
+ key,
218
+ field: node.field,
219
+ path
220
+ }, { default: () => renderFields(ctx, node.children, indexes) });
221
+ case "array": return h(ArrayField, {
222
+ key,
223
+ of: ctx.of,
224
+ node,
225
+ path,
226
+ component,
227
+ renderItem: (index) => renderFields(ctx, node.children, [...indexes, index])
228
+ });
229
+ case "value": return h(ValueField, {
230
+ key,
231
+ of: ctx.of,
232
+ node,
233
+ path,
234
+ component
235
+ });
236
+ }
237
+ }
238
+ /** The field's data path, with each `'*'` replaced by the index of the item it is in. */
239
+ function toPath(dataPath, indexes) {
240
+ let next = 0;
241
+ return dataPath.map((key) => key === "*" ? indexes[next++] ?? key : key);
242
+ }
243
+ // src/json-field.ts
244
+ /**
245
+ * Renders one field of a `useJsonForm` form, wherever you put it inside Formisch's `<Form>`. A
246
+ * group or an array comes with its fields. Leave the field out of `<JsonFields>` with `except`.
247
+ */
248
+ const JsonField = defineComponent({
249
+ name: "JsonField",
250
+ props: {
251
+ /** What `useJsonForm` returned. */
252
+ of: {
253
+ type: Object,
254
+ required: true
255
+ },
256
+ /** The field's path: `'email'`, or `'address.city'` for a field of a group. */
257
+ path: {
258
+ type: String,
259
+ required: true
260
+ },
261
+ /** Defaults to the components `useJsonForm` got. */
262
+ components: { type: Object }
263
+ },
264
+ setup(props) {
265
+ const node = findField(props.of.fields, props.path, true);
266
+ const ctx = {
267
+ of: props.of,
268
+ components: resolveComponents(props.components, props.of),
269
+ skip: () => false
270
+ };
271
+ checkComponents(ctx, [node]);
272
+ return () => renderField(ctx, node, [], 0);
273
+ }
274
+ });
275
+ // src/json-fields.ts
276
+ /**
277
+ * Renders the fields of a `useJsonForm` form with your components, one per field type. It renders
278
+ * nothing of its own, so put it inside Formisch's `<Form>` with your own submit button. `only` and
279
+ * `except` are read when it sets up.
280
+ */
281
+ const JsonFields = defineComponent({
282
+ name: "JsonFields",
283
+ props: {
284
+ /** What `useJsonForm` returned. */
285
+ of: {
286
+ type: Object,
287
+ required: true
288
+ },
289
+ /** Defaults to the components `useJsonForm` got. */
290
+ components: { type: Object },
291
+ /** Renders only these fields, by path, in the order of the definition. */
292
+ only: { type: Array },
293
+ /** Leaves out these fields, by path, e.g. the ones you place with `<JsonField>`. */
294
+ except: { type: Array }
295
+ },
296
+ setup(props) {
297
+ const fields = props.of.fields;
298
+ const except = new Set(props.except);
299
+ for (const path of except) findField(fields, path, false);
300
+ const ctx = {
301
+ of: props.of,
302
+ components: resolveComponents(props.components, props.of),
303
+ skip: (node) => except.has(pathOf(node) ?? "")
304
+ };
305
+ const nodes = props.only ? onlyFields(fields, props.only) : fields;
306
+ checkComponents(ctx, nodes);
307
+ return () => renderFields(ctx, nodes, []);
308
+ }
309
+ });
310
+ /** The fields `only` names, in the order of the definition, each once. */
311
+ function onlyFields(fields, only) {
312
+ const named = new Set(only.map((path) => findField(fields, path, true)));
313
+ return [...allFields(fields)].filter((node) => named.has(node));
314
+ }
315
+ // src/field-tree.ts
316
+ /**
317
+ * The fields of a definition that builds, as a tree. It comes from the builder's `walkFields`, so
318
+ * your own container types are in it too.
319
+ */
320
+ function fieldTree(builder, definition) {
321
+ const roots = [];
322
+ const lists = [roots];
323
+ for (const walked of builder.walkFields(definition)) {
324
+ const children = [];
325
+ lists.length = walked.depth + 1;
326
+ lists.at(-1)?.push(toNode(walked, builder, children));
327
+ lists.push(children);
328
+ }
329
+ return roots;
330
+ }
331
+ function toNode(walked, builder, children) {
332
+ const kind = walked.kind;
333
+ return {
334
+ field: walked.field,
335
+ type: walked.type,
336
+ role: roleOf(kind, builder.fieldTypes[walked.type]?.container === true),
337
+ kind,
338
+ itemKind: walked.itemKind,
339
+ dataPath: walked.dataPath,
340
+ children
341
+ };
342
+ }
343
+ function roleOf(kind, container) {
344
+ if (kind === "none") return "display";
345
+ if (!container) return "value";
346
+ return kind === "array" ? "array" : "group";
347
+ }
348
+ // src/use-json-form.ts
349
+ function useJsonForm(definition, options = {}) {
350
+ const builder = options.builder ?? createSchemaBuilder();
351
+ const form = useForm({
352
+ schema: builder.build(definition),
353
+ initialInput: builder.getInitialInput(definition),
354
+ validate: options.validate,
355
+ revalidate: options.revalidate
356
+ });
357
+ const { components } = options;
358
+ return {
359
+ form,
360
+ definition,
361
+ builder,
362
+ components,
363
+ fields: fieldTree(builder, definition)
364
+ };
365
+ }
366
+ export { JsonField, JsonFields, useJsonForm };
367
+
368
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","names":[],"sources":["../src/field-paths.ts","../src/item-input.ts","../src/array-field.ts","../src/coerce-input.ts","../src/value-field.ts","../src/render-fields.ts","../src/json-field.ts","../src/json-fields.ts","../src/field-tree.ts","../src/use-json-form.ts"],"sourcesContent":["import type { FieldNode } from './field-tree'\n\n/**\n * A field's path as `only`, `except` and `<JsonField>` name it: `'address.city'`, or\n * `'lines.*.quantity'` for a field of an array's items. Undefined for a field without a name.\n */\nexport const pathOf = (node: FieldNode): string | undefined =>\n node.field.name === undefined ? undefined : node.dataPath.join('.')\n\n/** Every field of the tree, in document order, a container before its fields. */\nexport function* allFields(nodes: readonly FieldNode[]): Generator<FieldNode, void, undefined> {\n for (const node of nodes) {\n yield node\n yield* allFields(node.children)\n }\n}\n\n/**\n * The field at `path`. A path that names no field is a mistake in the code, so it throws. With\n * `placed`, the field is rendered on its own, which a field of an array's items can't be: it has no\n * index outside its item.\n */\nexport function findField(nodes: readonly FieldNode[], path: string, placed: boolean): FieldNode {\n const node = [...allFields(nodes)].find((candidate) => pathOf(candidate) === path)\n if (!node) throw new TypeError(`No field at \"${path}\" in the definition.`)\n if (placed && node.dataPath.includes('*')) {\n const array = path.slice(0, path.indexOf('.*'))\n throw new TypeError(\n `\"${path}\" is in an array's items, so it can't be placed on its own. Place \"${array}\" instead.`,\n )\n }\n return node\n}\n","import type { JsonField, SchemaBuilder } from '@whatsaaaa/form-builder-schema'\nimport type { FieldNode } from './field-tree'\n\n/**\n * The input of a new item of an array, from the defaults in the definition: an object of its\n * fields' input, or the input of the item field for a list of single values.\n */\nexport function itemInput(builder: SchemaBuilder, array: FieldNode): unknown {\n const fields = array.children.map((child) => child.field)\n const [only] = array.children\n // The field of a list of single values has no name: its value is the item itself.\n if (array.children.length === 1 && only?.role !== 'display' && only?.dataPath.at(-1) === '*') {\n return initialInput(builder, [{ ...only.field, name: 'item' }]).item\n }\n return initialInput(builder, fields)\n}\n\nconst initialInput = (builder: SchemaBuilder, fields: JsonField[]) =>\n builder.getInitialInput({ version: '1', fields })\n","import { insert, move, remove, useFieldArray } from '@formisch/vue'\nimport { type Component, defineComponent, h, type PropType, type VNode } from 'vue'\nimport type { ArrayFieldProps, FieldPath } from './field-components'\nimport type { FieldNode } from './field-tree'\nimport { itemInput } from './item-input'\nimport type { AnyJsonFormInstance } from './use-json-form'\n\n/** Connects a container of kind `array` to Formisch, and renders your component for it. */\nexport const ArrayField = defineComponent({\n name: 'ArrayField',\n props: {\n of: { type: Object as PropType<AnyJsonFormInstance>, required: true },\n node: { type: Object as PropType<FieldNode>, required: true },\n path: { type: Array as PropType<FieldPath>, required: true },\n component: { type: [Object, Function] as PropType<Component>, required: true },\n /** Renders the fields of the item at `index`. */\n renderItem: { type: Function as PropType<(index: number) => VNode[]>, required: true },\n },\n setup(props) {\n // The path comes from the definition at runtime, so the schema's types can't check it.\n const path = () => props.path as never\n const array = useFieldArray(\n () => props.of.form,\n () => ({ path: path() }),\n )\n\n const methods = {\n // A new item starts with the defaults of its fields, like the items of a new form.\n add: () =>\n insert(props.of.form, {\n path: path(),\n initialInput: itemInput(props.of.builder, props.node) as never,\n }),\n remove: (index: number) => remove(props.of.form, { path: path(), at: index }),\n move: (from: number, to: number) => move(props.of.form, { path: path(), from, to }),\n }\n\n return () => {\n const fieldProps: ArrayFieldProps = {\n field: props.node.field,\n path: props.path,\n items: array.items,\n errors: array.errors,\n ...methods,\n }\n return h(props.component, fieldProps, {\n default: ({ index }: { index: number }) => props.renderItem(index),\n })\n }\n },\n})\n","import type { ValueKind } from '@whatsaaaa/form-builder-schema'\n\n/**\n * Turns what an input element gives into what the field's schema expects: `'5'` into `5` for a\n * number, and a `FileList` into a file, or an array of files for a field of kind `array`. Anything\n * else is passed through, and the schema reports it.\n */\nexport function coerceInput(input: unknown, kind: ValueKind, itemKind?: ValueKind): unknown {\n if (kind === 'array') {\n const items = isFileList(input) ? Array.from(input) : input\n if (!Array.isArray(items) || itemKind === undefined) return items\n return items.map((item: unknown) => coerceInput(item, itemKind))\n }\n if (isFileList(input)) return kind === 'file' ? (input[0] ?? undefined) : input\n if (kind === 'number' && typeof input === 'string') return toNumber(input)\n return input\n}\n\n/** An empty string means no value. One that isn't a number stays a string, for the schema. */\nfunction toNumber(input: string): number | string | undefined {\n if (input.trim() === '') return undefined\n const number = Number(input)\n return Number.isNaN(number) ? input : number\n}\n\n/**\n * A `FileList`, or anything shaped like one: an array-like whose items are all files. Checked by\n * shape rather than with `instanceof FileList`, because a `File[]` from a component that spread the\n * list, or from a DOM shim like happy-dom whose `DataTransfer.files` is an array, should count too.\n * `File` only exists in a browser, so check for it before using it, e.g. during SSR.\n */\nfunction isFileList(value: unknown): value is ArrayLike<File> {\n if (typeof File === 'undefined' || typeof value !== 'object' || value === null) return false\n if (value instanceof File || typeof (value as ArrayLike<unknown>).length !== 'number')\n return false\n return Array.from(value as ArrayLike<unknown>).every((item) => item instanceof File)\n}\n","import { useField } from '@formisch/vue'\nimport type { JsonField } from '@whatsaaaa/form-builder-schema'\nimport { type Component, defineComponent, h, type PropType } from 'vue'\nimport { coerceInput } from './coerce-input'\nimport type { FieldPath, ValueFieldProps } from './field-components'\nimport type { FieldNode } from './field-tree'\nimport type { AnyJsonFormInstance } from './use-json-form'\n\n/** Connects a field with a value to Formisch, and renders your component for it. */\nexport const ValueField = defineComponent({\n name: 'ValueField',\n props: {\n of: { type: Object as PropType<AnyJsonFormInstance>, required: true },\n node: { type: Object as PropType<FieldNode>, required: true },\n path: { type: Array as PropType<FieldPath>, required: true },\n component: { type: [Object, Function] as PropType<Component>, required: true },\n },\n setup(props) {\n // The path comes from the definition at runtime, so the schema's types can't check it.\n const field = useField(\n () => props.of.form,\n () => ({ path: props.path as never }),\n )\n\n const setInput = (input: unknown) => {\n field.input = coerceInput(input, props.node.kind, props.node.itemKind) as never\n }\n\n return () => {\n const fieldProps: ValueFieldProps = {\n field: props.node.field,\n path: props.path,\n kind: props.node.kind,\n required: isRequired(props.node.field),\n input: field.input,\n errors: field.errors,\n inputProps: field.props,\n setInput,\n }\n return h(props.component, fieldProps)\n }\n },\n})\n\nconst isRequired = (field: JsonField) =>\n field.required === true || (typeof field.required === 'object' && field.required !== null)\n","import { type Component, h, type VNode } from 'vue'\nimport { ArrayField } from './array-field'\nimport type { FieldComponents, FieldPath } from './field-components'\nimport type { FieldNode } from './field-tree'\nimport type { AnyJsonFormInstance } from './use-json-form'\nimport { ValueField } from './value-field'\n\n/** What `<JsonFields>` and `<JsonField>` render with. */\nexport interface RenderContext {\n of: AnyJsonFormInstance\n components: FieldComponents\n /** True for a field that isn't rendered, and so neither are the fields inside it. */\n skip: (node: FieldNode) => boolean\n}\n\n/** The components from the props, or else the ones `useJsonForm` got. */\nexport function resolveComponents(\n components: FieldComponents | undefined,\n of: AnyJsonFormInstance,\n): FieldComponents {\n const resolved = components ?? of.components\n if (!resolved) {\n throw new TypeError('No field components. Pass them to useJsonForm, or to the component.')\n }\n return resolved\n}\n\n/** Throws for the field types that would render without a component, naming each once. */\nexport function checkComponents(ctx: RenderContext, nodes: readonly FieldNode[]): void {\n const missing = new Set<string>()\n const visit = (node: FieldNode) => {\n if (ctx.skip(node)) return\n if (!Object.hasOwn(ctx.components, node.type)) missing.add(node.type)\n node.children.forEach(visit)\n }\n nodes.forEach(visit)\n if (!missing.size) return\n\n // A missing component is a mistake in the code, not in the definition, so it throws early.\n const names = [...missing].map((type) => `\"${type}\"`).join(', ')\n const [noun, pronoun] = missing.size > 1 ? ['types', 'them'] : ['type', 'it']\n throw new TypeError(\n `No component for the field ${noun} ${names}. Add ${pronoun} to the components.`,\n )\n}\n\n/** `indexes` are the indexes of the array items the fields are in, outermost first. */\nexport function renderFields(\n ctx: RenderContext,\n nodes: readonly FieldNode[],\n indexes: readonly number[],\n): VNode[] {\n return nodes\n .filter((node) => !ctx.skip(node))\n .map((node, position) => renderField(ctx, node, indexes, position))\n}\n\nexport function renderField(\n ctx: RenderContext,\n node: FieldNode,\n indexes: readonly number[],\n position: number,\n): VNode {\n const component = ctx.components[node.type] as Component\n const path = toPath(node.dataPath, indexes)\n const key = node.field.name ?? `#${position}`\n switch (node.role) {\n case 'display':\n return h(component, { key, field: node.field })\n case 'group':\n return h(\n component,\n { key, field: node.field, path },\n { default: () => renderFields(ctx, node.children, indexes) },\n )\n case 'array':\n return h(ArrayField, {\n key,\n of: ctx.of,\n node,\n path,\n component,\n renderItem: (index: number) => renderFields(ctx, node.children, [...indexes, index]),\n })\n case 'value':\n return h(ValueField, { key, of: ctx.of, node, path, component })\n }\n}\n\n/** The field's data path, with each `'*'` replaced by the index of the item it is in. */\nfunction toPath(dataPath: FieldNode['dataPath'], indexes: readonly number[]): FieldPath {\n let next = 0\n return dataPath.map((key) => (key === '*' ? (indexes[next++] ?? key) : key))\n}\n","import { defineComponent, type PropType } from 'vue'\nimport type { FieldComponents } from './field-components'\nimport { findField } from './field-paths'\nimport {\n checkComponents,\n type RenderContext,\n renderField,\n resolveComponents,\n} from './render-fields'\nimport type { AnyJsonFormInstance } from './use-json-form'\n\n/**\n * Renders one field of a `useJsonForm` form, wherever you put it inside Formisch's `<Form>`. A\n * group or an array comes with its fields. Leave the field out of `<JsonFields>` with `except`.\n */\nexport const JsonField = defineComponent({\n name: 'JsonField',\n props: {\n /** What `useJsonForm` returned. */\n of: { type: Object as PropType<AnyJsonFormInstance>, required: true },\n /** The field's path: `'email'`, or `'address.city'` for a field of a group. */\n path: { type: String, required: true },\n /** Defaults to the components `useJsonForm` got. */\n components: { type: Object as PropType<FieldComponents> },\n },\n setup(props) {\n const node = findField(props.of.fields, props.path, true)\n const ctx: RenderContext = {\n of: props.of,\n components: resolveComponents(props.components, props.of),\n skip: () => false,\n }\n checkComponents(ctx, [node])\n\n return () => renderField(ctx, node, [], 0)\n },\n})\n","import { defineComponent, type PropType } from 'vue'\nimport type { FieldComponents } from './field-components'\nimport { allFields, findField, pathOf } from './field-paths'\nimport type { FieldNode } from './field-tree'\nimport {\n checkComponents,\n type RenderContext,\n renderFields,\n resolveComponents,\n} from './render-fields'\nimport type { AnyJsonFormInstance } from './use-json-form'\n\n/**\n * Renders the fields of a `useJsonForm` form with your components, one per field type. It renders\n * nothing of its own, so put it inside Formisch's `<Form>` with your own submit button. `only` and\n * `except` are read when it sets up.\n */\nexport const JsonFields = defineComponent({\n name: 'JsonFields',\n props: {\n /** What `useJsonForm` returned. */\n of: { type: Object as PropType<AnyJsonFormInstance>, required: true },\n /** Defaults to the components `useJsonForm` got. */\n components: { type: Object as PropType<FieldComponents> },\n /** Renders only these fields, by path, in the order of the definition. */\n only: { type: Array as PropType<readonly string[]> },\n /** Leaves out these fields, by path, e.g. the ones you place with `<JsonField>`. */\n except: { type: Array as PropType<readonly string[]> },\n },\n setup(props) {\n const fields = props.of.fields\n const except = new Set(props.except)\n // A path that names no field is a typo, so check them even though nothing uses the result.\n for (const path of except) findField(fields, path, false)\n\n const ctx: RenderContext = {\n of: props.of,\n components: resolveComponents(props.components, props.of),\n skip: (node) => except.has(pathOf(node) ?? ''),\n }\n const nodes = props.only ? onlyFields(fields, props.only) : fields\n checkComponents(ctx, nodes)\n\n return () => renderFields(ctx, nodes, [])\n },\n})\n\n/** The fields `only` names, in the order of the definition, each once. */\nfunction onlyFields(fields: readonly FieldNode[], only: readonly string[]): FieldNode[] {\n const named = new Set(only.map((path) => findField(fields, path, true)))\n return [...allFields(fields)].filter((node) => named.has(node))\n}\n","import type {\n JsonField,\n JsonForm,\n SchemaBuilder,\n ValueKind,\n WalkedField,\n} from '@whatsaaaa/form-builder-schema'\n\n/**\n * How `<JsonFields>` renders a field: a `value` field has an input, a `group` or an `array` holds\n * fields, and a `display` element, like a heading, has no value.\n */\nexport type FieldRole = 'value' | 'group' | 'array' | 'display'\n\n/** One field of a definition, with the fields inside it when it is a container. */\nexport interface FieldNode {\n readonly field: JsonField\n /** The field's type, e.g. `'text'`. */\n readonly type: string\n readonly role: FieldRole\n /** The kind of the field's value, e.g. `'number'`, or `'none'` for a display element. */\n readonly kind: ValueKind\n /** The kind of the items, for a field of kind `array` whose type has one. */\n readonly itemKind: ValueKind | undefined\n /** Where the value is, with `'*'` for each array index: `['lines', '*', 'quantity']`. */\n readonly dataPath: readonly (string | number)[]\n /** The fields of a group, or of one item of an array. */\n readonly children: readonly FieldNode[]\n}\n\n/**\n * The fields of a definition that builds, as a tree. It comes from the builder's `walkFields`, so\n * your own container types are in it too.\n */\nexport function fieldTree(builder: SchemaBuilder, definition: JsonForm): FieldNode[] {\n const roots: FieldNode[] = []\n // The list each depth adds its fields to. A container comes before its fields, so the list at a\n // field's depth is the one its parent opened.\n const lists: FieldNode[][] = [roots]\n for (const walked of builder.walkFields(definition)) {\n const children: FieldNode[] = []\n lists.length = walked.depth + 1\n lists.at(-1)?.push(toNode(walked, builder, children))\n lists.push(children)\n }\n return roots\n}\n\nfunction toNode(walked: WalkedField, builder: SchemaBuilder, children: FieldNode[]): FieldNode {\n // The definition builds, so the builder gave every field its kind.\n const kind = walked.kind as ValueKind\n return {\n field: walked.field,\n type: walked.type,\n role: roleOf(kind, builder.fieldTypes[walked.type]?.container === true),\n kind,\n itemKind: walked.itemKind,\n dataPath: walked.dataPath,\n children,\n }\n}\n\nfunction roleOf(kind: ValueKind, container: boolean): FieldRole {\n if (kind === 'none') return 'display'\n if (!container) return 'value'\n return kind === 'array' ? 'array' : 'group'\n}\n","import { type FormSchema, type FormStore, useForm, type ValidationMode } from '@formisch/vue'\nimport {\n createSchemaBuilder,\n type JsonForm,\n type SchemaBuilder,\n} from '@whatsaaaa/form-builder-schema'\nimport type { GenericSchema } from 'valibot'\nimport type { FieldComponents } from './field-components'\nimport { type FieldNode, fieldTree } from './field-tree'\n\nexport interface UseJsonFormOptions {\n /** Builds the schema and knows your own types and rules. Defaults to `createSchemaBuilder()`. */\n builder?: SchemaBuilder\n /** Your component for each field type, so `<JsonFields>` and `<JsonField>` don't need them. */\n components?: FieldComponents\n /** When a field is validated the first time, as in Formisch. */\n validate?: ValidationMode\n /** When a field is validated again after that, as in Formisch. */\n revalidate?: Exclude<ValidationMode, 'initial'>\n}\n\n/** The schema of a definition, as the builder builds it. */\nexport type JsonFormSchema = ReturnType<SchemaBuilder['build']>\n\n/**\n * The schema type of `useJsonForm<TInput, TOutput>`: your types, so Formisch's paths and values\n * are checked. An interface works too, which Formisch's own constraint wouldn't take as it is.\n */\nexport type TypedJsonFormSchema<\n TInput extends object,\n TOutput extends object = TInput,\n> = GenericSchema<Plain<TInput>, Plain<TOutput>>\n\n/** The same type as an object literal type, which has the index signature Formisch requires. */\ntype Plain<T> = { [K in keyof T]: T[K] }\n\n/** What `useJsonForm` returns. Pass it to `<JsonFields>`. */\nexport interface JsonFormInstance<TSchema extends FormSchema = JsonFormSchema> {\n /** The Formisch form: pass it to Formisch's `<Form :of>`, or to any of its methods. */\n readonly form: FormStore<TSchema>\n readonly definition: JsonForm\n readonly builder: SchemaBuilder\n /** The components from the options, if any. */\n readonly components: FieldComponents | undefined\n /** The definition's fields as a tree, the way `<JsonFields>` renders them. */\n readonly fields: readonly FieldNode[]\n}\n\n/** Any form `useJsonForm` returns, typed or not: what the components take. */\nexport type AnyJsonFormInstance = JsonFormInstance<FormSchema>\n\n/**\n * A Formisch form for a definition, with its schema and initial input from the builder. A\n * definition with issues throws the builder's `DefinitionError`, so run `validate()` first when it\n * comes from outside your code. The definition is read once: give the form a new `:key` to use\n * another one.\n */\nexport function useJsonForm(definition: JsonForm, options?: UseJsonFormOptions): JsonFormInstance\n/**\n * The same, with the form typed by your input type, and output type when it differs, so Formisch's\n * paths and values are checked. The types are yours to keep in step with the definition.\n */\nexport function useJsonForm<TInput extends object, TOutput extends object = TInput>(\n definition: JsonForm,\n options?: UseJsonFormOptions,\n): JsonFormInstance<TypedJsonFormSchema<TInput, TOutput>>\nexport function useJsonForm(\n definition: JsonForm,\n options: UseJsonFormOptions = {},\n): JsonFormInstance {\n const builder = options.builder ?? createSchemaBuilder()\n const form = useForm({\n schema: builder.build(definition),\n initialInput: builder.getInitialInput(definition),\n validate: options.validate,\n revalidate: options.revalidate,\n })\n const { components } = options\n return { form, definition, builder, components, fields: fieldTree(builder, definition) }\n}\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,60 @@
1
1
  {
2
2
  "name": "@whatsaaaa/form-builder-vue",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
3
+ "version": "0.1.1",
4
+ "description": "Render JSON form definitions with Formisch and your own Vue components.",
5
+ "keywords": [
6
+ "form",
7
+ "formisch",
8
+ "headless",
9
+ "json",
10
+ "vue"
11
+ ],
12
+ "homepage": "https://github.com/whatsaaaa/form-builder",
13
+ "bugs": "https://github.com/whatsaaaa/form-builder/issues",
14
+ "license": "MIT",
15
+ "author": "Whatsaaaa",
16
+ "repository": {
17
+ "type": "git",
18
+ "url": "git+https://github.com/whatsaaaa/form-builder.git",
19
+ "directory": "frameworks/vue"
20
+ },
21
+ "files": [
22
+ "dist"
23
+ ],
24
+ "type": "module",
25
+ "sideEffects": false,
26
+ "exports": {
27
+ ".": {
28
+ "types": "./dist/index.d.ts",
29
+ "default": "./dist/index.js"
30
+ }
31
+ },
32
+ "publishConfig": {
33
+ "access": "public"
34
+ },
35
+ "dependencies": {
36
+ "@whatsaaaa/form-builder-schema": "^0.9.0"
37
+ },
38
+ "devDependencies": {
39
+ "@arethetypeswrong/cli": "^0.18.5",
40
+ "@formisch/vue": "^1.1.1",
41
+ "happy-dom": "^20.0.0",
42
+ "publint": "^0.3.25",
43
+ "tsdown": "^0.23.2",
44
+ "typescript": "~6.0.3",
45
+ "valibot": "^1.5.0",
46
+ "vitest": "^5.0.5",
47
+ "vue": "^3.5.42"
48
+ },
49
+ "peerDependencies": {
50
+ "@formisch/vue": "^1.1.1",
51
+ "valibot": "^1.3.0",
52
+ "vue": "^3.5.0"
53
+ },
54
+ "scripts": {
55
+ "build": "tsdown",
56
+ "test": "vitest run",
57
+ "type-check": "tsc --noEmit",
58
+ "lint:pkg": "publint && pnpm pack --pack-destination .attw && attw .attw/*.tgz --profile esm-only"
59
+ }
6
60
  }