@namroodinc/liquid-editorial 0.1.11 → 0.2.0
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/AGENTS.md +1 -1
- package/README.md +21 -15
- package/UPGRADING.md +30 -0
- package/dist/chunks/FormEditor-8bz3pOdP.cjs +2 -0
- package/dist/chunks/FormEditor-8bz3pOdP.cjs.map +1 -0
- package/dist/chunks/FormEditor-DuW8x78Y.js +330 -0
- package/dist/chunks/FormEditor-DuW8x78Y.js.map +1 -0
- package/dist/components/EntryPicker/index.cjs +1 -1
- package/dist/components/EntryPicker/index.cjs.map +1 -1
- package/dist/components/EntryPicker/index.d.cts +3 -1
- package/dist/components/EntryPicker/index.d.cts.map +1 -1
- package/dist/components/EntryPicker/index.d.ts +3 -1
- package/dist/components/EntryPicker/index.d.ts.map +1 -1
- package/dist/components/EntryPicker/index.js +45 -43
- package/dist/components/EntryPicker/index.js.map +1 -1
- package/dist/components/FormEditor/control.d.cts +15 -0
- package/dist/components/FormEditor/control.d.cts.map +1 -0
- package/dist/components/FormEditor/control.d.ts +15 -0
- package/dist/components/FormEditor/control.d.ts.map +1 -0
- package/dist/components/FormEditor/index.cjs +1 -0
- package/dist/components/FormEditor/index.d.cts +28 -0
- package/dist/components/FormEditor/index.d.cts.map +1 -0
- package/dist/components/FormEditor/index.d.ts +28 -0
- package/dist/components/FormEditor/index.d.ts.map +1 -0
- package/dist/components/FormEditor/index.js +2 -0
- package/dist/components/FormEditor/model.d.cts +86 -0
- package/dist/components/FormEditor/model.d.cts.map +1 -0
- package/dist/components/FormEditor/model.d.ts +86 -0
- package/dist/components/FormEditor/model.d.ts.map +1 -0
- package/dist/components/FormEditor/style.css +25 -0
- package/dist/components/NumberField/index.cjs +1 -1
- package/dist/components/NumberField/index.cjs.map +1 -1
- package/dist/components/NumberField/index.d.cts +3 -0
- package/dist/components/NumberField/index.d.cts.map +1 -1
- package/dist/components/NumberField/index.d.ts +3 -0
- package/dist/components/NumberField/index.d.ts.map +1 -1
- package/dist/components/NumberField/index.js +20 -20
- package/dist/components/NumberField/index.js.map +1 -1
- package/dist/components/RichTextEditor/index.cjs +1 -1
- package/dist/components/RichTextEditor/index.js +0 -1
- package/dist/index.cjs +1 -1
- package/dist/index.d.cts +2 -2
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +20 -20
- package/dist/styles.css +1 -1
- package/package.json +1 -1
- package/dist/chunks/EntryEditor-CcyYtH74.js +0 -191
- package/dist/chunks/EntryEditor-CcyYtH74.js.map +0 -1
- package/dist/chunks/EntryEditor-DIpc1iPF.cjs +0 -2
- package/dist/chunks/EntryEditor-DIpc1iPF.cjs.map +0 -1
- package/dist/components/EntryEditor/index.cjs +0 -1
- package/dist/components/EntryEditor/index.d.cts +0 -21
- package/dist/components/EntryEditor/index.d.cts.map +0 -1
- package/dist/components/EntryEditor/index.d.ts +0 -21
- package/dist/components/EntryEditor/index.d.ts.map +0 -1
- package/dist/components/EntryEditor/index.js +0 -6
- package/dist/components/EntryEditor/model.d.cts +0 -33
- package/dist/components/EntryEditor/model.d.cts.map +0 -1
- package/dist/components/EntryEditor/model.d.ts +0 -33
- package/dist/components/EntryEditor/model.d.ts.map +0 -1
- package/dist/components/EntryEditor/style.css +0 -8
package/AGENTS.md
CHANGED
|
@@ -8,7 +8,7 @@ End every completed change handoff with a suggested Git commit subject and descr
|
|
|
8
8
|
|
|
9
9
|
Render button controls through the shared `Button` component, including controls inside other components and examples. Native button markup belongs only in `src/components/Button` (isolated test fixtures may use native controls). Use Button's standard variants, sizes, icons, focus, and disabled styling instead of copying them into a component stylesheet. Specialized widgets may retain their semantic layout, shared Field skin, or switch track/knob geometry.
|
|
10
10
|
|
|
11
|
-
Every releasable change set must update the `Unreleased` section in `UPGRADING.md` with changes, adoption steps, compatibility notes, and verification steps for agents in consuming projects. Related edits share that section. Leave version increments to `.github/workflows/release.yml`: every push to `main` automatically bumps the patch
|
|
11
|
+
Every releasable change set must update the `Unreleased` section in `UPGRADING.md` with changes, adoption steps, compatibility notes, and verification steps for agents in consuming projects. A change set that removes or renames public API also lists it under a `### Breaking changes` heading there. Related edits share that section. Leave version increments to `.github/workflows/release.yml`: every push to `main` automatically bumps the version (patch, or minor below 1.0 and major from 1.0 when `Unreleased` has `### Breaking changes`), versions the upgrade notes, validates, commits the release files, and publishes to npm. Keep the manifest and lockfile versions aligned and include this file and `UPGRADING.md` in the package.
|
|
12
12
|
|
|
13
13
|
## Using the installed package
|
|
14
14
|
|
package/README.md
CHANGED
|
@@ -55,20 +55,20 @@ If an application only needs the design tokens, import `@namroodinc/liquid-edito
|
|
|
55
55
|
|
|
56
56
|
Use `appearance="outline"` (the default) on board and selection cards, `appearance="soft"` in dense tables and toolbar counts, and `appearance="solid"` for the one state a row or tile emphasizes, such as a Cover asset badge. Soft badges have no visible stroke. Tags inside sentences also display uppercase, including path labels such as `/news`.
|
|
57
57
|
|
|
58
|
-
##
|
|
58
|
+
## Forms and content models
|
|
59
59
|
|
|
60
|
-
`
|
|
60
|
+
`FormEditor` renders standard fields in schema order. Applications own state, choice loading, permissions, submission, and persistence:
|
|
61
61
|
|
|
62
62
|
```tsx
|
|
63
63
|
import { useState } from "react";
|
|
64
64
|
import {
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
type
|
|
68
|
-
type
|
|
65
|
+
FormEditor,
|
|
66
|
+
validateFormValues,
|
|
67
|
+
type FormSchema,
|
|
68
|
+
type FormValues,
|
|
69
69
|
} from "@namroodinc/liquid-editorial";
|
|
70
70
|
|
|
71
|
-
const
|
|
71
|
+
const schema: FormSchema = {
|
|
72
72
|
fields: [
|
|
73
73
|
{ id: "title", type: "shortText", label: "Title", required: true },
|
|
74
74
|
{ id: "gallery", type: "asset", label: "Gallery", multiple: true },
|
|
@@ -76,10 +76,10 @@ const model: ContentModel = {
|
|
|
76
76
|
};
|
|
77
77
|
|
|
78
78
|
export function ContentEditor() {
|
|
79
|
-
const [value, setValue] = useState<
|
|
79
|
+
const [value, setValue] = useState<FormValues>({});
|
|
80
80
|
return (
|
|
81
|
-
<
|
|
82
|
-
|
|
81
|
+
<FormEditor
|
|
82
|
+
schema={schema}
|
|
83
83
|
value={value}
|
|
84
84
|
onValueChange={setValue}
|
|
85
85
|
pickerItems={{ gallery: [{ id: "image-1", title: "Cover.jpg" }] }}
|
|
@@ -87,12 +87,18 @@ export function ContentEditor() {
|
|
|
87
87
|
);
|
|
88
88
|
}
|
|
89
89
|
|
|
90
|
-
// Before saving:
|
|
90
|
+
// Before saving: validateFormValues(schema, value) returns errors keyed by field ID.
|
|
91
91
|
```
|
|
92
92
|
|
|
93
|
-
Supported types are `shortText`, `longText`, `richText`, `number`, `boolean`, `dateTime`, `asset`, and `
|
|
93
|
+
Supported types are `shortText`, `longText`, `richText`, `number`, `boolean`, `dateTime`, `asset`, `reference`, and string-valued `select`. Rich text stores a TipTap JSON document rooted at `doc`, using the editor's existing StarterKit and Link schema. Dates are edited in UTC and emitted as ISO-8601 strings ending in `Z`. Assets store `{ assetId }`; references store `{ entryId }`. Set `multiple: true` for an ordered array of these objects.
|
|
94
94
|
|
|
95
|
-
Use `defaultValue` for an uncontrolled editor, `errors` for app-specific messages, `showAllErrors` after a submit attempt, and `isDisabled` to disable editing. Required errors appear after a field changes; `false` and `0` are populated values. Missing keys render empty controls without emitting changes. Clearing a number, date, or single selection removes its key; empty text, rich-text documents, and lists keep their typed values. Unrelated entry keys are preserved.
|
|
95
|
+
Use `defaultValue` for an uncontrolled editor, `errors` for app-specific messages, `showAllErrors` after a submit attempt, and `isDisabled` to disable editing. Required and format errors appear after a field changes; an out-of-range number shows its message straight away, because the control already reads invalid. `false` and `0` are populated values. Missing keys render empty controls without emitting changes. Clearing a number, date, select, or single selection removes its key; empty text, rich-text documents, and lists keep their typed values. Unrelated entry keys are preserved. Validation runs per field and is reused until that field or its value changes, so define schemas outside render or memoize them.
|
|
96
|
+
|
|
97
|
+
General forms can use select fields with `options: [{ value, label }]`, per-field `isDisabled`, and React-node hints. Optional selects lead with an empty choice, labelled with the field's `placeholder` or "None", that clears the value. Pass `options: []` while choices load; a stored value stays valid until they arrive. Text fields support `maxLength` and `readOnly`; short text also supports `inputType: "email"`, long text supports `rows`, and numeric fields support `min`, `max`, `step`, and `hasSteppers`. Steppers are named after their field, such as "Increase Width". `validateFormValues` enforces `maxLength`, numeric limits, and `picker.max`. `autoFocus` applies to text, number, select, boolean, and date fields.
|
|
98
|
+
|
|
99
|
+
`columns` sets the most columns and defaults to one. The form drops a column whenever a field would be narrower than `minColumnWidth` (default `16rem`), measured against the form's own width, so it steps down inside drawers and modals. Use `columns="auto"` to fit as many columns as the width allows. FormEditor is not a size container, so it keeps its content width in flex rows and popovers.
|
|
100
|
+
|
|
101
|
+
Changes emit `onValueChange(nextValues, { fieldId })`. Use the changed field ID when adapting to APIs that accept patches, generated IDs, or dependent values. Runtime form schemas can hold JSX and live choices, so keep them separate from persisted content definitions. Picker field options such as `picker: { itemLabel: "image", itemsLabel: "images" }` provide contextual search and selection copy without putting data access in the library. Omitted or `undefined` options keep the asset or entry wording.
|
|
96
102
|
|
|
97
103
|
`RichTextEditor` also supports `format="json"` with `value`/`defaultValue` and an `onChange` callback receiving the document. HTML remains its default format. Unsupported JSON content produces an error and cannot be edited until corrected.
|
|
98
104
|
|
|
@@ -256,7 +262,7 @@ Storybook is the component catalogue and documentation surface. Stories and test
|
|
|
256
262
|
|
|
257
263
|
## Publishing
|
|
258
264
|
|
|
259
|
-
Merge a PR into `main` to publish a release. Every push to `main` runs [the release workflow](.github/workflows/release.yml), which installs dependencies, bumps the
|
|
265
|
+
Merge a PR into `main` to publish a release. Every push to `main` runs [the release workflow](.github/workflows/release.yml), which installs dependencies, bumps the version, moves the `Unreleased` upgrade notes under that version, and runs `npm run validate`. It then commits the version files and guide back to `main` and publishes `@namroodinc/liquid-editorial` to npm under `latest`. All commands run in GitHub Actions; contributors do not run local release commands or create GitHub Releases or tags. Other branches and pull requests do not publish.
|
|
260
266
|
|
|
261
267
|
### One-time npm setup
|
|
262
268
|
|
|
@@ -278,7 +284,7 @@ npm requires two-factor verification for this setup. The workflow subsequently a
|
|
|
278
284
|
|
|
279
285
|
### Release a change
|
|
280
286
|
|
|
281
|
-
Add the change's adoption, compatibility, and verification notes under `Unreleased` in `UPGRADING.md`, then merge the PR to `main`. The workflow handles the version bump; for example, `0.1.4` becomes `0.1.5`. Pull the generated version commit before continuing local work.
|
|
287
|
+
Add the change's adoption, compatibility, and verification notes under `Unreleased` in `UPGRADING.md`, then merge the PR to `main`. The workflow handles the version bump; for example, `0.1.4` becomes `0.1.5`. When a change removes or renames public API, add a `### Breaking changes` section under `Unreleased` that lists it. The workflow then bumps the minor version below 1.0 (`0.1.11` becomes `0.2.0`) and the major version from 1.0, so caret ranges do not pick it up. Pull the generated version commit before continuing local work.
|
|
282
288
|
|
|
283
289
|
Follow **Publish to npm** in GitHub Actions to check the result. Runs are serialized, and the bot's version commit uses `GITHUB_TOKEN`, which does not trigger another workflow run.
|
|
284
290
|
|
package/UPGRADING.md
CHANGED
|
@@ -2,6 +2,36 @@
|
|
|
2
2
|
|
|
3
3
|
## Unreleased
|
|
4
4
|
|
|
5
|
+
## 0.2.0
|
|
6
|
+
|
|
7
|
+
### Changes
|
|
8
|
+
|
|
9
|
+
- Replaced EntryEditor with FormEditor for content and general forms. Added select fields, per-field disabled state, read-only text, autofocus, email inputs, text limits, textarea rows, numeric limits/steps/steppers, React-node hints, responsive columns, and contextual asset/image picker wording.
|
|
10
|
+
- FormEditor reports each edit as `onValueChange(values, { fieldId })`. Asset/reference value shapes and existing scalar/rich-text/date behavior are unchanged. Persistence, choice loading, permissions, and dependent-field rules remain in the consuming application.
|
|
11
|
+
- Optional selects lead with an empty choice (the field's `placeholder`, or "None") that removes the key. Required selects set `aria-required`. Number steppers are named after their field, such as "Increase Width", and out-of-range numbers show their message on load.
|
|
12
|
+
- `validateFormValues` enforces text `maxLength` and multiple-picker `picker.max`. A non-string select value reports "Choose an available option.", and a select with an empty `options` array keeps its stored value valid while choices load. A select field without an `options` array throws a schema error.
|
|
13
|
+
- NumberField accepts `decreaseLabel` and `increaseLabel` for its stepper names. The defaults remain "Decrease" and "Increase".
|
|
14
|
+
|
|
15
|
+
### Breaking changes
|
|
16
|
+
|
|
17
|
+
- Removed `EntryEditor`, `EntryEditorProps`, `ContentModel`, `ContentField`, `EntryFieldValue`, `EntryValues`, `EntryErrors`, and `validateEntryValues` from the package root, and the `components/EntryEditor` subpath. There are no aliases.
|
|
18
|
+
- The editor's `model` prop is now `schema`, and its CSS hook is `.ds-form-editor` instead of `.ds-entry-editor`.
|
|
19
|
+
- This release is published as 0.2.0, so `^0.1.x` ranges do not install it automatically.
|
|
20
|
+
|
|
21
|
+
### Adoption
|
|
22
|
+
|
|
23
|
+
Import `FormEditor`, `FormEditorProps`, `FormSchema`, `FormField`, `FormFieldValue`, `FormValues`, `FormErrors`, `FormChange`, and `validateFormValues` from the package root or `components/FormEditor`. Replace EntryEditor, ContentModel, ContentField, EntryFieldValue, EntryValues, EntryErrors, and validateEntryValues accordingly. Change the component's `model` prop to `schema` and CSS hooks from `.ds-entry-editor` to `.ds-form-editor`. `columns` sets the most columns, defaulting to one; the form drops a column whenever a field would be narrower than `minColumnWidth` (default `16rem`). Use `columns="auto"` to fit as many as the width allows. Existing one-argument callbacks may continue to ignore change metadata; use `fieldId` when only edited fields should be sent to an API.
|
|
24
|
+
|
|
25
|
+
For selects, use `{ type: "select", options: [{ value, label }] }` and store strings. Pass `options: []` while choices load, and a placeholder such as "Loading roles…". Field definitions accept `isDisabled`; short/long text support `readOnly` and `maxLength`; short text supports `inputType: "email"`; long text supports `rows`; numbers support `min`, `max`, `step`, and `hasSteppers`. `autoFocus` applies to text, number, select, boolean, and date fields. Pass asset/reference choices through `pickerItems` and picker copy through the field's `picker` options (`itemLabel`, `itemsLabel`, `label`, `addLabel`, `emptyLabel`); omitted or `undefined` options keep the asset or entry wording. Rich hints and loading messages are React values, so keep runtime form schemas separate from persisted content-model DTOs. Schema definitions do not fetch data. Define schemas outside render, or memoize them, so per-field validation is reused between keystrokes.
|
|
26
|
+
|
|
27
|
+
### Compatibility
|
|
28
|
+
|
|
29
|
+
AssetValue and ReferenceValue remain available. Select fields are UI configuration; consuming CMS APIs do not automatically gain new content field types. FormEditor is not a Grid: it carries no `ds-grid*` classes or container query and sizes its columns against its own width, so it also keeps its content width in flex rows and popovers. `autoFocus` is a type error on rich-text, asset, and reference fields. Loaded text over `maxLength` or lists over `picker.max` now fail `validateFormValues`. No dependencies are added. The release workflow owns the version increment; a `### Breaking changes` section makes it a minor bump below 1.0.
|
|
30
|
+
|
|
31
|
+
### Verification
|
|
32
|
+
|
|
33
|
+
Run consuming-project typechecks, tests, and builds against the packed build. Check every migrated form's save rules, read-only access, explicit clears, generated IDs, dependent fields, and nested pickers. Confirm touched-field patches use change metadata and do not overwrite concurrent changes. Check labels/hints/errors, select keyboard controls and clearing, single/multiple asset selection, numeric bounds and stepper names, rich text and UTC dates, columns in drawers and narrow containers, and disabled/read-only controls. Re-run pre-save validation against stored data for text and picker limits. Update tests that query FormEditor steppers as "Increase" or "Decrease". The FormEditor stories demonstrate entry and general forms. Run library validation and package checks for React 18 and 19 before publishing.
|
|
34
|
+
|
|
5
35
|
## 0.1.11
|
|
6
36
|
|
|
7
37
|
### Changes
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
const e=require("../components/Field/index.cjs"),t=require("../components/TextField/index.cjs"),n=require("./RichTextEditor-BxtlbjXj.cjs"),r=require("../components/EntryPicker/index.cjs"),i=require("../components/NumberField/index.cjs"),a=require("../components/Select/index.cjs"),o=require("../components/Switch/index.cjs");let s=require("react"),c=require("react/jsx-runtime");var l=new Set([`shortText`,`longText`,`richText`,`number`,`boolean`,`dateTime`,`asset`,`reference`,`select`]);function u(e){let t=new Set;for(let n of e.fields){if(!n.id?.trim()||t.has(n.id))throw Error(`Form field IDs must be nonempty and unique.`);if(!l.has(n.type))throw Error(`Unsupported form field type: ${n.type}`);if(n.type===`select`&&!Array.isArray(n.options))throw Error(`Select field ${n.id} needs an options array.`);t.add(n.id)}}var d={asset:{key:`assetId`,label:`Assets`,itemLabel:`asset`,itemsLabel:`assets`},reference:{key:`entryId`,label:`Entries`,itemLabel:`entry`,itemsLabel:`entries`}};function f(e){let t=d[e.type],{label:n=t.label,addLabel:r=`Choose ${e.label}`,itemLabel:i=t.itemLabel,itemsLabel:a=t.itemsLabel,...o}=e.picker??{};return{...o,label:n,addLabel:r,itemLabel:i,itemsLabel:a}}function p(e){if(typeof e!=`string`)return;let t=/^(\d{4}-\d{2}-\d{2}T\d{2}:\d{2})(?::(\d{2})(?:\.(\d{1,3}))?)?(?:Z|\+00:00)$/.exec(e);if(!t)return;let n=`${t[1]}:${t[2]??`00`}.${(t[3]??``).padEnd(3,`0`)}Z`,r=new Date(n);if(Number.isFinite(r.getTime())&&r.toISOString()===n)return n}function m(e,t){return!!e&&typeof e==`object`&&t in e&&typeof e[t]==`string`&&e[t].trim().length>0}function h(e){return!!e.text?.trim()||e.type===`horizontalRule`||!!e.content?.some(h)}function g(e){return`${e.label} is required.`}function _(e,t){if(typeof t!=`string`)return`Enter text.`;if(e.required&&!t.trim())return g(e);if((e.type===`shortText`||e.type===`longText`)&&e.maxLength!==void 0&&t.length>e.maxLength)return`Enter no more than ${e.maxLength} characters.`}function v(e,t){let r=n.n(t);if(r)return r;if(e.required&&!h(t))return g(e)}function y(e,t){if(typeof t!=`string`)return`Choose an available option.`;if(t===``)return e.required?g(e):void 0;if(e.type===`select`&&e.options.length>0&&!e.options.some(e=>e.value===t))return`Choose an available option.`}function b(e,t){if(e.type!==`asset`&&e.type!==`reference`)return;let{key:n,itemLabel:r,itemsLabel:i}=d[e.type];if(!e.multiple)return m(t,n)?void 0:`Choose an ${r}.`;if(!Array.isArray(t)||!t.every(e=>m(e,n)))return`Choose a list of ${i}.`;if(e.required&&t.length===0)return g(e);let a=e.picker?.max;if(a!==void 0&&t.length>a)return`Choose no more than ${a} ${f(e).itemsLabel}.`}var x={shortText:_,longText:_,richText:v,number:(e,t)=>{if(typeof t!=`number`||!Number.isFinite(t))return`Enter a finite number.`;if(e.type===`number`){if(e.min!==void 0&&t<e.min)return`Enter a number of at least ${e.min}.`;if(e.max!==void 0&&t>e.max)return`Enter a number no greater than ${e.max}.`}},boolean:(e,t)=>typeof t==`boolean`?void 0:`Choose a boolean value.`,dateTime:(e,t)=>p(t)?void 0:`Enter a valid UTC date and time.`,select:y,asset:b,reference:b};function S(e,t){return t===void 0?e.required?g(e):void 0:x[e.type](e,t)}function C(e,t){return u(e),Object.fromEntries(e.fields.flatMap(e=>{let n=S(e,Object.hasOwn(t,e.id)?t[e.id]:void 0);return n?[[e.id,n]]:[]}))}function w(e,t){let n=e.type===`asset`?`assetId`:`entryId`;return(Array.isArray(t)?t:t===void 0?[]:[t]).flatMap(e=>{if(!e||typeof e!=`object`||!(n in e))return[];let t=e[n];return typeof t==`string`?[t]:[]})}function T(e,t){if(e.type===`asset`){let n=t.map(e=>({assetId:e}));return e.multiple?n:n[0]}let n=t.map(e=>({entryId:e}));return e.multiple?n:n[0]}function E({field:e,stored:t,isDisabled:n,required:r,accessibility:a,onChange:o}){return(0,c.jsx)(i.NumberField,{...a,required:r,isDisabled:n,placeholder:e.placeholder,min:e.min,max:e.max,step:e.step??`any`,hasSteppers:e.hasSteppers??!1,autoFocus:e.autoFocus,decreaseLabel:`Decrease ${e.label}`,increaseLabel:`Increase ${e.label}`,value:typeof t==`number`&&Number.isFinite(t)?t:``,onValueChange:e=>{(e===``||Number.isFinite(e))&&o(e===``?void 0:e)}})}function D({field:e,stored:t,isDisabled:n,required:r,accessibility:i,onChange:o}){let s=!r&&!e.options.some(e=>e.value===``);return(0,c.jsx)(a.Select,{...i,"aria-required":r||void 0,isDisabled:n,isInvalid:i[`aria-invalid`],options:s?[{value:``,label:e.placeholder??`None`},...e.options]:e.options,placeholder:e.placeholder,autoFocus:e.autoFocus,value:typeof t==`string`?t:``,onValueChange:e=>o(e===``?void 0:e)})}function O({field:e,stored:t,items:n,isDisabled:i,accessibility:a,onChange:o}){return(0,c.jsx)(`div`,{...a,role:`group`,children:(0,c.jsx)(r.EntryPicker,{items:n,value:w(e,t),...f(e),mode:e.multiple?`multiple`:`single`,isDisabled:i,onValueChange:t=>o(T(e,t))})})}function k({field:e,stored:r,items:i,isDisabled:a,accessibility:{required:s,...l},onChange:u}){let d={stored:r,isDisabled:a,required:s,accessibility:l,onChange:u};switch(e.type){case`shortText`:return(0,c.jsx)(t.TextField,{...l,required:s,disabled:a,placeholder:e.placeholder,type:e.inputType,maxLength:e.maxLength,readOnly:e.readOnly,autoFocus:e.autoFocus,value:typeof r==`string`?r:``,onChange:e=>u(e.target.value)});case`longText`:return(0,c.jsx)(t.TextArea,{...l,required:s,disabled:a,placeholder:e.placeholder,rows:e.rows,maxLength:e.maxLength,readOnly:e.readOnly,autoFocus:e.autoFocus,value:typeof r==`string`?r:``,onChange:e=>u(e.target.value)});case`number`:return(0,c.jsx)(E,{field:e,...d});case`boolean`:return(0,c.jsx)(o.Switch,{...l,isDisabled:a,autoFocus:e.autoFocus,isSelected:r===!0,onSelectedChange:u});case`dateTime`:return(0,c.jsx)(t.TextField,{...l,required:s,disabled:a,type:`datetime-local`,step:`0.001`,autoFocus:e.autoFocus,value:p(r)?.slice(0,-1)??``,onChange:e=>u(p(`${e.target.value}Z`))});case`richText`:return(0,c.jsx)(n.t,{...l,label:e.label,isRequired:s,isDisabled:a,placeholder:e.placeholder,format:`json`,value:r===void 0?n.r:r,onChange:u});case`select`:return(0,c.jsx)(D,{field:e,...d});case`asset`:case`reference`:return(0,c.jsx)(O,{field:e,items:i,...d})}}function A({field:t,stored:n,items:r,isDisabled:i,error:a,isErrorVisible:o,onChange:l}){let u=(0,s.useMemo)(()=>a===void 0&&o?S(t,n):void 0,[a,o,t,n]),d=t.type===`dateTime`?`${t.label} (UTC)`:t.label;return(0,c.jsx)(e.Field,{label:d,hint:t.hint,error:a??u,isRequired:t.required,isGroup:t.type===`asset`||t.type===`reference`,children:e=>(0,c.jsx)(k,{field:t,stored:n,items:r,isDisabled:i,accessibility:e,onChange:l})})}function j({schema:e,value:t,defaultValue:n={},onValueChange:r,pickerItems:i={},errors:a={},isDisabled:o=!1,showAllErrors:l=!1,columns:d=1,minColumnWidth:f=`16rem`,className:p,style:m,...h}){let[g,_]=(0,s.useState)(n),[v,y]=(0,s.useState)(()=>new Set),b=t??g;(0,s.useMemo)(()=>u(e),[e]);let x=(e,n)=>{let i={...b};n===void 0?delete i[e]:i[e]=n,t===void 0&&_(i),y(t=>new Set(t).add(e)),r?.(i,{fieldId:e})},S=d===`auto`?{"--form-editor-min-column":f}:{"--form-editor-columns":d,"--form-editor-min-column":f};return(0,c.jsx)(`div`,{className:[`ds-form-editor`,d===`auto`&&`ds-form-editor--auto`,p].filter(Boolean).join(` `),style:{...S,...m},...h,children:e.fields.map(e=>{let t=Object.hasOwn(b,e.id)?b[e.id]:void 0,n=o||!!e.isDisabled,r=n||`readOnly`in e&&!!e.readOnly,s=l||v.has(e.id)||e.type===`number`&&typeof t==`number`;return(0,c.jsx)(A,{field:e,stored:t,items:i[e.id]??[],isDisabled:n,error:a[e.id],isErrorVisible:s,onChange:t=>{r||x(e.id,t)}},e.id)})})}Object.defineProperty(exports,"n",{enumerable:!0,get:function(){return C}}),Object.defineProperty(exports,"t",{enumerable:!0,get:function(){return j}});
|
|
2
|
+
//# sourceMappingURL=FormEditor-8bz3pOdP.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"FormEditor-8bz3pOdP.cjs","names":["documentError","NumberField","Select","EntryPicker","TextField","TextArea","Switch","RichTextEditor","emptyDocument","useMemo","Field","useState","require_components_Field_index","require_components_TextField_index","require_RichTextEditor","require_components_EntryPicker_index","require_components_NumberField_index","require_components_Select_index","require_components_Switch_index","react","react_jsx_runtime","fieldTypes","assertModel","ids","field","model","linkCopy","pickerCopy","defaults","rest","parseUtcDateTime","value","match","canonical","date","isLink","key","hasRichText","node","requiredError","textError","richTextError","error","choiceError","option","linkError","itemLabel","item","itemsLabel","max","validators","fieldError","validateFormValues","values","selectedIds","link","id","pickerValue","links","NumberControl","accessibility","stored","next","onChange","SelectControl","isClearable","required","PickerControl","FormControl","shared","isDisabled","event","FormEditorField","validation","isErrorVisible","label","FormEditor","internal","setInternal","defaultValue","changed","setChanged","current","schema","commit","fieldId","previous","onValueChange","layout","columns","minColumnWidth","className","style","fieldDisabled","isLocked","showAllErrors","pickerItems","errors"],"sources":["../../src/components/FormEditor/model.ts","../../src/components/FormEditor/control.tsx","../../src/components/FormEditor/index.tsx"],"sourcesContent":["import type { ReactNode } from \"react\";\nimport type { SelectOption } from \"../Select\";\nimport type { EntryPickerProps } from \"../EntryPicker\";\nimport type { JSONContent } from \"@tiptap/react\";\nimport { documentError } from \"../RichTextEditor/document\";\nimport type { RichTextDocument } from \"../RichTextEditor\";\n\ninterface FormFieldBase {\n id: string;\n label: string;\n hint?: ReactNode;\n placeholder?: string;\n required?: boolean;\n isDisabled?: boolean;\n}\n\nexport type FormField = FormFieldBase & (\n | { type: \"shortText\"; inputType?: \"text\" | \"email\"; maxLength?: number; readOnly?: boolean; autoFocus?: boolean; multiple?: never }\n | { type: \"longText\"; rows?: number; maxLength?: number; readOnly?: boolean; autoFocus?: boolean; multiple?: never }\n | { type: \"boolean\" | \"dateTime\"; autoFocus?: boolean; multiple?: never }\n | { type: \"richText\"; autoFocus?: never; multiple?: never }\n | { type: \"number\"; min?: number; max?: number; step?: number | \"any\"; hasSteppers?: boolean; autoFocus?: boolean; multiple?: never }\n | { type: \"select\"; options: SelectOption[]; autoFocus?: boolean; multiple?: never }\n | { type: \"asset\" | \"reference\"; multiple?: boolean; autoFocus?: never; picker?: Pick<EntryPickerProps, \"label\" | \"addLabel\" | \"emptyLabel\" | \"itemLabel\" | \"itemsLabel\" | \"max\" | \"isReorderable\"> }\n);\n\nexport type FieldOf<T extends FormField[\"type\"]> = Extract<FormField, { type: T }>;\n\nexport interface FormSchema {\n fields: readonly FormField[];\n}\n\nexport interface AssetValue { assetId: string }\nexport interface ReferenceValue { entryId: string }\nexport type FormFieldValue = string | number | boolean | RichTextDocument | AssetValue | ReferenceValue | AssetValue[] | ReferenceValue[];\nexport type FormValues = Record<string, FormFieldValue | undefined>;\nexport type FormErrors = Partial<Record<string, string>>;\n\nconst fieldTypes = new Set([\"shortText\", \"longText\", \"richText\", \"number\", \"boolean\", \"dateTime\", \"asset\", \"reference\", \"select\"]);\n\nexport function assertModel(model: FormSchema) {\n const ids = new Set<string>();\n for (const field of model.fields) {\n if (!field.id?.trim() || ids.has(field.id)) throw new Error(\"Form field IDs must be nonempty and unique.\");\n if (!fieldTypes.has(field.type)) throw new Error(`Unsupported form field type: ${field.type}`);\n if (field.type === \"select\" && !Array.isArray(field.options)) throw new Error(`Select field ${field.id} needs an options array.`);\n ids.add(field.id);\n }\n}\n\nconst linkCopy = {\n asset: { key: \"assetId\", label: \"Assets\", itemLabel: \"asset\", itemsLabel: \"assets\" },\n reference: { key: \"entryId\", label: \"Entries\", itemLabel: \"entry\", itemsLabel: \"entries\" },\n} as const;\n\n/** Picker copy, with asset or entry wording for any option the field leaves out or sets to undefined. */\nexport function pickerCopy(field: FieldOf<\"asset\" | \"reference\">) {\n const defaults = linkCopy[field.type];\n const {\n label = defaults.label,\n addLabel = `Choose ${field.label}`,\n itemLabel = defaults.itemLabel,\n itemsLabel = defaults.itemsLabel,\n ...rest\n } = field.picker ?? {};\n return { ...rest, label, addLabel, itemLabel, itemsLabel };\n}\n\n/** Accept UTC ISO strings without rolling invalid dates into the next month. */\nexport function parseUtcDateTime(value: unknown): string | undefined {\n if (typeof value !== \"string\") return;\n const match = /^(\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2})(?::(\\d{2})(?:\\.(\\d{1,3}))?)?(?:Z|\\+00:00)$/.exec(value);\n if (!match) return;\n const canonical = `${match[1]}:${match[2] ?? \"00\"}.${(match[3] ?? \"\").padEnd(3, \"0\")}Z`;\n const date = new Date(canonical);\n if (Number.isFinite(date.getTime()) && date.toISOString() === canonical) return canonical;\n}\n\nfunction isLink(value: unknown, key: \"assetId\" | \"entryId\"): boolean {\n return !!value && typeof value === \"object\" && key in value &&\n typeof (value as Record<string, unknown>)[key] === \"string\" &&\n ((value as Record<string, string>)[key]).trim().length > 0;\n}\n\nfunction hasRichText(node: JSONContent): boolean {\n return !!node.text?.trim() || node.type === \"horizontalRule\" || !!node.content?.some(hasRichText);\n}\n\nfunction requiredError(field: FormField): string {\n return `${field.label} is required.`;\n}\n\nfunction textError(field: FormField, value: unknown): string | undefined {\n if (typeof value !== \"string\") return \"Enter text.\";\n if (field.required && !value.trim()) return requiredError(field);\n if ((field.type === \"shortText\" || field.type === \"longText\") && field.maxLength !== undefined && value.length > field.maxLength) {\n return `Enter no more than ${field.maxLength} characters.`;\n }\n}\n\nfunction richTextError(field: FormField, value: unknown): string | undefined {\n const error = documentError(value);\n if (error) return error;\n if (field.required && !hasRichText(value as RichTextDocument)) return requiredError(field);\n}\n\nfunction choiceError(field: FormField, value: unknown): string | undefined {\n if (typeof value !== \"string\") return \"Choose an available option.\";\n if (value === \"\") return field.required ? requiredError(field) : undefined;\n // An empty list means the choices are still loading, so a stored value stays valid until they arrive.\n if (field.type === \"select\" && field.options.length > 0 && !field.options.some((option) => option.value === value)) {\n return \"Choose an available option.\";\n }\n}\n\nfunction linkError(field: FormField, value: unknown): string | undefined {\n if (field.type !== \"asset\" && field.type !== \"reference\") return;\n const { key, itemLabel, itemsLabel } = linkCopy[field.type];\n if (!field.multiple) return isLink(value, key) ? undefined : `Choose an ${itemLabel}.`;\n if (!Array.isArray(value) || !value.every((item) => isLink(item, key))) return `Choose a list of ${itemsLabel}.`;\n if (field.required && value.length === 0) return requiredError(field);\n const max = field.picker?.max;\n if (max !== undefined && value.length > max) return `Choose no more than ${max} ${pickerCopy(field).itemsLabel}.`;\n}\n\nconst validators: Record<FormField[\"type\"], (field: FormField, value: unknown) => string | undefined> = {\n shortText: textError,\n longText: textError,\n richText: richTextError,\n number: (field, value) => {\n if (typeof value !== \"number\" || !Number.isFinite(value)) return \"Enter a finite number.\";\n if (field.type !== \"number\") return;\n if (field.min !== undefined && value < field.min) return `Enter a number of at least ${field.min}.`;\n if (field.max !== undefined && value > field.max) return `Enter a number no greater than ${field.max}.`;\n },\n boolean: (_field, value) => typeof value === \"boolean\" ? undefined : \"Choose a boolean value.\",\n dateTime: (_field, value) => parseUtcDateTime(value) ? undefined : \"Enter a valid UTC date and time.\",\n select: choiceError,\n asset: linkError,\n reference: linkError,\n};\n\nexport function fieldError(field: FormField, value: unknown): string | undefined {\n if (value === undefined) return field.required ? requiredError(field) : undefined;\n return validators[field.type](field, value);\n}\n\n/** Validate data loaded from JSON as well as values emitted by FormEditor. */\nexport function validateFormValues(model: FormSchema, values: Record<string, unknown>): FormErrors {\n assertModel(model);\n return Object.fromEntries(model.fields.flatMap((field): [string, string][] => {\n const error = fieldError(field, Object.hasOwn(values, field.id) ? values[field.id] : undefined);\n return error ? [[field.id, error]] : [];\n }));\n}\n","import { EntryPicker, type EntryItem } from \"../EntryPicker\";\nimport type { FieldProps } from \"../Field\";\nimport { NumberField } from \"../NumberField\";\nimport { RichTextEditor, type RichTextDocument } from \"../RichTextEditor\";\nimport { emptyDocument } from \"../RichTextEditor/document\";\nimport { Select } from \"../Select\";\nimport { Switch } from \"../Switch\";\nimport { TextArea, TextField } from \"../TextField\";\nimport { parseUtcDateTime, pickerCopy, type FieldOf, type FormField, type FormFieldValue } from \"./model\";\n\nfunction selectedIds(field: FormField, value: FormFieldValue | undefined): string[] {\n const key = field.type === \"asset\" ? \"assetId\" : \"entryId\";\n const links = Array.isArray(value) ? value : value === undefined ? [] : [value];\n return links.flatMap((link) => {\n if (!link || typeof link !== \"object\" || !(key in link)) return [];\n const id = (link as unknown as Record<string, unknown>)[key];\n return typeof id === \"string\" ? [id] : [];\n });\n}\n\nfunction pickerValue(field: FormField, ids: string[]): FormFieldValue | undefined {\n if (field.type === \"asset\") {\n const links = ids.map((assetId) => ({ assetId }));\n return field.multiple ? links : links[0];\n }\n const links = ids.map((entryId) => ({ entryId }));\n return field.multiple ? links : links[0];\n}\n\ntype Accessibility = Parameters<FieldProps[\"children\"]>[0];\n\ninterface ControlProps<F extends FormField> {\n field: F;\n stored: FormFieldValue | undefined;\n isDisabled: boolean;\n required: boolean | undefined;\n accessibility: Omit<Accessibility, \"required\">;\n onChange: (value: FormFieldValue | undefined) => void;\n}\n\nfunction NumberControl({ field, stored, isDisabled, required, accessibility, onChange }: ControlProps<FieldOf<\"number\">>) {\n return <NumberField {...accessibility} required={required} isDisabled={isDisabled} placeholder={field.placeholder}\n min={field.min} max={field.max} step={field.step ?? \"any\"} hasSteppers={field.hasSteppers ?? false} autoFocus={field.autoFocus}\n decreaseLabel={`Decrease ${field.label}`} increaseLabel={`Increase ${field.label}`}\n value={typeof stored === \"number\" && Number.isFinite(stored) ? stored : \"\"}\n onValueChange={(next) => { if (next === \"\" || Number.isFinite(next)) onChange(next === \"\" ? undefined : next); }} />;\n}\n\nfunction SelectControl({ field, stored, isDisabled, required, accessibility, onChange }: ControlProps<FieldOf<\"select\">>) {\n // Optional selects lead with an empty choice; picking it removes the key.\n const isClearable = !required && !field.options.some((option) => option.value === \"\");\n return <Select {...accessibility} aria-required={required || undefined} isDisabled={isDisabled} isInvalid={accessibility[\"aria-invalid\"]}\n options={isClearable ? [{ value: \"\", label: field.placeholder ?? \"None\" }, ...field.options] : field.options}\n placeholder={field.placeholder} autoFocus={field.autoFocus}\n value={typeof stored === \"string\" ? stored : \"\"} onValueChange={(next) => onChange(next === \"\" ? undefined : next)} />;\n}\n\nfunction PickerControl({ field, stored, items, isDisabled, accessibility, onChange }: ControlProps<FieldOf<\"asset\" | \"reference\">> & { items: EntryItem[] }) {\n return <div {...accessibility} role=\"group\">\n <EntryPicker items={items} value={selectedIds(field, stored)}\n {...pickerCopy(field)} mode={field.multiple ? \"multiple\" : \"single\"} isDisabled={isDisabled}\n onValueChange={(ids) => onChange(pickerValue(field, ids))} />\n </div>;\n}\n\ninterface FormControlProps {\n field: FormField;\n stored: FormFieldValue | undefined;\n items: EntryItem[];\n isDisabled: boolean;\n accessibility: Accessibility;\n onChange: (value: FormFieldValue | undefined) => void;\n}\n\nexport function FormControl({ field, stored, items, isDisabled, accessibility: { required, ...accessibility }, onChange }: FormControlProps) {\n const shared = { stored, isDisabled, required, accessibility, onChange };\n switch (field.type) {\n case \"shortText\":\n return <TextField {...accessibility} required={required} disabled={isDisabled} placeholder={field.placeholder}\n type={field.inputType} maxLength={field.maxLength} readOnly={field.readOnly} autoFocus={field.autoFocus}\n value={typeof stored === \"string\" ? stored : \"\"} onChange={(event) => onChange(event.target.value)} />;\n case \"longText\":\n return <TextArea {...accessibility} required={required} disabled={isDisabled} placeholder={field.placeholder}\n rows={field.rows} maxLength={field.maxLength} readOnly={field.readOnly} autoFocus={field.autoFocus}\n value={typeof stored === \"string\" ? stored : \"\"} onChange={(event) => onChange(event.target.value)} />;\n case \"number\":\n return <NumberControl field={field} {...shared} />;\n case \"boolean\":\n return <Switch {...accessibility} isDisabled={isDisabled} autoFocus={field.autoFocus} isSelected={stored === true} onSelectedChange={onChange} />;\n case \"dateTime\":\n return <TextField {...accessibility} required={required} disabled={isDisabled} type=\"datetime-local\" step=\"0.001\" autoFocus={field.autoFocus}\n value={parseUtcDateTime(stored)?.slice(0, -1) ?? \"\"}\n onChange={(event) => onChange(parseUtcDateTime(`${event.target.value}Z`))} />;\n case \"richText\":\n return <RichTextEditor {...accessibility} label={field.label} isRequired={required} isDisabled={isDisabled}\n placeholder={field.placeholder} format=\"json\" value={stored === undefined ? emptyDocument : stored as RichTextDocument}\n onChange={onChange} />;\n case \"select\":\n return <SelectControl field={field} {...shared} />;\n case \"asset\":\n case \"reference\":\n return <PickerControl field={field} items={items} {...shared} />;\n }\n}\n","import { useMemo, useState, type CSSProperties, type HTMLAttributes } from \"react\";\nimport type { EntryItem } from \"../EntryPicker\";\nimport { Field } from \"../Field\";\nimport type { GridColumns } from \"../Grid\";\nimport { FormControl } from \"./control\";\nimport { assertModel, fieldError, type FormField, type FormSchema, type FormErrors, type FormFieldValue, type FormValues } from \"./model\";\nimport \"./style.css\";\n\nexport { validateFormValues } from \"./model\";\nexport type { AssetValue, FormField, FormSchema, FormErrors, FormFieldValue, FormValues, ReferenceValue } from \"./model\";\n\nexport interface FormChange { fieldId: string }\n\nexport interface FormEditorProps extends Omit<HTMLAttributes<HTMLDivElement>, \"defaultValue\" | \"onChange\"> {\n schema: FormSchema;\n value?: FormValues;\n defaultValue?: FormValues;\n onValueChange?: (values: FormValues, change: FormChange) => void;\n /** Available assets or entries, keyed by field ID. Loading belongs to the app. */\n pickerItems?: Record<string, EntryItem[]>;\n /** App-provided messages take precedence over built-in validation. */\n errors?: FormErrors;\n isDisabled?: boolean;\n showAllErrors?: boolean;\n /** Columns at the widest. The form drops one whenever a field would be narrower than `minColumnWidth`. */\n columns?: GridColumns;\n /** Narrowest a field may get before the form drops a column. */\n minColumnWidth?: string;\n}\n\ninterface FormEditorFieldProps {\n field: FormField;\n stored: FormFieldValue | undefined;\n items: EntryItem[];\n isDisabled: boolean;\n error: string | undefined;\n isErrorVisible: boolean;\n onChange: (value: FormFieldValue | undefined) => void;\n}\n\n/** Validates only when its message can show, and only when its own field or value changes. */\nfunction FormEditorField({ field, stored, items, isDisabled, error, isErrorVisible, onChange }: FormEditorFieldProps) {\n const validation = useMemo(() => error === undefined && isErrorVisible ? fieldError(field, stored) : undefined,\n [error, isErrorVisible, field, stored]);\n const label = field.type === \"dateTime\" ? `${field.label} (UTC)` : field.label;\n return (\n <Field label={label} hint={field.hint} error={error ?? validation} isRequired={field.required}\n isGroup={field.type === \"asset\" || field.type === \"reference\"}>\n {(accessibility) => <FormControl field={field} stored={stored} items={items}\n isDisabled={isDisabled} accessibility={accessibility} onChange={onChange} />}\n </Field>\n );\n}\n\n/** Ordered form controls. Loading, submission, and persistence belong to the app. */\nexport function FormEditor({ schema, value, defaultValue = {}, onValueChange, pickerItems = {}, errors = {},\n isDisabled = false, showAllErrors = false, columns = 1, minColumnWidth = \"16rem\", className, style, ...rest }: FormEditorProps) {\n const [internal, setInternal] = useState<FormValues>(defaultValue);\n const [changed, setChanged] = useState<Set<string>>(() => new Set());\n const current = value ?? internal;\n useMemo(() => assertModel(schema), [schema]);\n\n const commit = (fieldId: string, next: FormFieldValue | undefined) => {\n const values = { ...current };\n if (next === undefined) delete values[fieldId];\n else values[fieldId] = next;\n if (value === undefined) setInternal(values);\n setChanged((previous) => new Set(previous).add(fieldId));\n onValueChange?.(values, { fieldId });\n };\n\n const layout = columns === \"auto\"\n ? { \"--form-editor-min-column\": minColumnWidth }\n : { \"--form-editor-columns\": columns, \"--form-editor-min-column\": minColumnWidth };\n\n return (\n <div className={[\"ds-form-editor\", columns === \"auto\" && \"ds-form-editor--auto\", className].filter(Boolean).join(\" \")}\n style={{ ...layout, ...style } as CSSProperties} {...rest}>\n {schema.fields.map((field) => {\n const stored = Object.hasOwn(current, field.id) ? current[field.id] : undefined;\n const fieldDisabled = isDisabled || Boolean(field.isDisabled);\n const isLocked = fieldDisabled || (\"readOnly\" in field && Boolean(field.readOnly));\n // NumberField marks an out-of-range number invalid itself, so its message shows straight away.\n const isErrorVisible = showAllErrors || changed.has(field.id) || (field.type === \"number\" && typeof stored === \"number\");\n return <FormEditorField key={field.id} field={field} stored={stored} items={pickerItems[field.id] ?? []}\n isDisabled={fieldDisabled} error={errors[field.id]} isErrorVisible={isErrorVisible}\n onChange={(next) => { if (!isLocked) commit(field.id, next); }} />;\n })}\n </div>\n );\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,330 @@
|
|
|
1
|
+
import { Field as e } from "../components/Field/index.js";
|
|
2
|
+
import { TextArea as t, TextField as n } from "../components/TextField/index.js";
|
|
3
|
+
import { n as r, r as i, t as a } from "./RichTextEditor-Dnx4i7bQ.js";
|
|
4
|
+
import { EntryPicker as o } from "../components/EntryPicker/index.js";
|
|
5
|
+
import { NumberField as s } from "../components/NumberField/index.js";
|
|
6
|
+
import { Select as c } from "../components/Select/index.js";
|
|
7
|
+
import { Switch as l } from "../components/Switch/index.js";
|
|
8
|
+
import { useMemo as u, useState as d } from "react";
|
|
9
|
+
import { jsx as f } from "react/jsx-runtime";
|
|
10
|
+
//#region src/components/FormEditor/model.ts
|
|
11
|
+
var p = /* @__PURE__ */ new Set([
|
|
12
|
+
"shortText",
|
|
13
|
+
"longText",
|
|
14
|
+
"richText",
|
|
15
|
+
"number",
|
|
16
|
+
"boolean",
|
|
17
|
+
"dateTime",
|
|
18
|
+
"asset",
|
|
19
|
+
"reference",
|
|
20
|
+
"select"
|
|
21
|
+
]);
|
|
22
|
+
function m(e) {
|
|
23
|
+
let t = /* @__PURE__ */ new Set();
|
|
24
|
+
for (let n of e.fields) {
|
|
25
|
+
if (!n.id?.trim() || t.has(n.id)) throw Error("Form field IDs must be nonempty and unique.");
|
|
26
|
+
if (!p.has(n.type)) throw Error(`Unsupported form field type: ${n.type}`);
|
|
27
|
+
if (n.type === "select" && !Array.isArray(n.options)) throw Error(`Select field ${n.id} needs an options array.`);
|
|
28
|
+
t.add(n.id);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
var h = {
|
|
32
|
+
asset: {
|
|
33
|
+
key: "assetId",
|
|
34
|
+
label: "Assets",
|
|
35
|
+
itemLabel: "asset",
|
|
36
|
+
itemsLabel: "assets"
|
|
37
|
+
},
|
|
38
|
+
reference: {
|
|
39
|
+
key: "entryId",
|
|
40
|
+
label: "Entries",
|
|
41
|
+
itemLabel: "entry",
|
|
42
|
+
itemsLabel: "entries"
|
|
43
|
+
}
|
|
44
|
+
};
|
|
45
|
+
function g(e) {
|
|
46
|
+
let t = h[e.type], { label: n = t.label, addLabel: r = `Choose ${e.label}`, itemLabel: i = t.itemLabel, itemsLabel: a = t.itemsLabel, ...o } = e.picker ?? {};
|
|
47
|
+
return {
|
|
48
|
+
...o,
|
|
49
|
+
label: n,
|
|
50
|
+
addLabel: r,
|
|
51
|
+
itemLabel: i,
|
|
52
|
+
itemsLabel: a
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
function _(e) {
|
|
56
|
+
if (typeof e != "string") return;
|
|
57
|
+
let t = /^(\d{4}-\d{2}-\d{2}T\d{2}:\d{2})(?::(\d{2})(?:\.(\d{1,3}))?)?(?:Z|\+00:00)$/.exec(e);
|
|
58
|
+
if (!t) return;
|
|
59
|
+
let n = `${t[1]}:${t[2] ?? "00"}.${(t[3] ?? "").padEnd(3, "0")}Z`, r = new Date(n);
|
|
60
|
+
if (Number.isFinite(r.getTime()) && r.toISOString() === n) return n;
|
|
61
|
+
}
|
|
62
|
+
function v(e, t) {
|
|
63
|
+
return !!e && typeof e == "object" && t in e && typeof e[t] == "string" && e[t].trim().length > 0;
|
|
64
|
+
}
|
|
65
|
+
function y(e) {
|
|
66
|
+
return !!e.text?.trim() || e.type === "horizontalRule" || !!e.content?.some(y);
|
|
67
|
+
}
|
|
68
|
+
function b(e) {
|
|
69
|
+
return `${e.label} is required.`;
|
|
70
|
+
}
|
|
71
|
+
function x(e, t) {
|
|
72
|
+
if (typeof t != "string") return "Enter text.";
|
|
73
|
+
if (e.required && !t.trim()) return b(e);
|
|
74
|
+
if ((e.type === "shortText" || e.type === "longText") && e.maxLength !== void 0 && t.length > e.maxLength) return `Enter no more than ${e.maxLength} characters.`;
|
|
75
|
+
}
|
|
76
|
+
function S(e, t) {
|
|
77
|
+
let n = r(t);
|
|
78
|
+
if (n) return n;
|
|
79
|
+
if (e.required && !y(t)) return b(e);
|
|
80
|
+
}
|
|
81
|
+
function C(e, t) {
|
|
82
|
+
if (typeof t != "string") return "Choose an available option.";
|
|
83
|
+
if (t === "") return e.required ? b(e) : void 0;
|
|
84
|
+
if (e.type === "select" && e.options.length > 0 && !e.options.some((e) => e.value === t)) return "Choose an available option.";
|
|
85
|
+
}
|
|
86
|
+
function w(e, t) {
|
|
87
|
+
if (e.type !== "asset" && e.type !== "reference") return;
|
|
88
|
+
let { key: n, itemLabel: r, itemsLabel: i } = h[e.type];
|
|
89
|
+
if (!e.multiple) return v(t, n) ? void 0 : `Choose an ${r}.`;
|
|
90
|
+
if (!Array.isArray(t) || !t.every((e) => v(e, n))) return `Choose a list of ${i}.`;
|
|
91
|
+
if (e.required && t.length === 0) return b(e);
|
|
92
|
+
let a = e.picker?.max;
|
|
93
|
+
if (a !== void 0 && t.length > a) return `Choose no more than ${a} ${g(e).itemsLabel}.`;
|
|
94
|
+
}
|
|
95
|
+
var T = {
|
|
96
|
+
shortText: x,
|
|
97
|
+
longText: x,
|
|
98
|
+
richText: S,
|
|
99
|
+
number: (e, t) => {
|
|
100
|
+
if (typeof t != "number" || !Number.isFinite(t)) return "Enter a finite number.";
|
|
101
|
+
if (e.type === "number") {
|
|
102
|
+
if (e.min !== void 0 && t < e.min) return `Enter a number of at least ${e.min}.`;
|
|
103
|
+
if (e.max !== void 0 && t > e.max) return `Enter a number no greater than ${e.max}.`;
|
|
104
|
+
}
|
|
105
|
+
},
|
|
106
|
+
boolean: (e, t) => typeof t == "boolean" ? void 0 : "Choose a boolean value.",
|
|
107
|
+
dateTime: (e, t) => _(t) ? void 0 : "Enter a valid UTC date and time.",
|
|
108
|
+
select: C,
|
|
109
|
+
asset: w,
|
|
110
|
+
reference: w
|
|
111
|
+
};
|
|
112
|
+
function E(e, t) {
|
|
113
|
+
return t === void 0 ? e.required ? b(e) : void 0 : T[e.type](e, t);
|
|
114
|
+
}
|
|
115
|
+
function D(e, t) {
|
|
116
|
+
return m(e), Object.fromEntries(e.fields.flatMap((e) => {
|
|
117
|
+
let n = E(e, Object.hasOwn(t, e.id) ? t[e.id] : void 0);
|
|
118
|
+
return n ? [[e.id, n]] : [];
|
|
119
|
+
}));
|
|
120
|
+
}
|
|
121
|
+
//#endregion
|
|
122
|
+
//#region src/components/FormEditor/control.tsx
|
|
123
|
+
function O(e, t) {
|
|
124
|
+
let n = e.type === "asset" ? "assetId" : "entryId";
|
|
125
|
+
return (Array.isArray(t) ? t : t === void 0 ? [] : [t]).flatMap((e) => {
|
|
126
|
+
if (!e || typeof e != "object" || !(n in e)) return [];
|
|
127
|
+
let t = e[n];
|
|
128
|
+
return typeof t == "string" ? [t] : [];
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
function k(e, t) {
|
|
132
|
+
if (e.type === "asset") {
|
|
133
|
+
let n = t.map((e) => ({ assetId: e }));
|
|
134
|
+
return e.multiple ? n : n[0];
|
|
135
|
+
}
|
|
136
|
+
let n = t.map((e) => ({ entryId: e }));
|
|
137
|
+
return e.multiple ? n : n[0];
|
|
138
|
+
}
|
|
139
|
+
function A({ field: e, stored: t, isDisabled: n, required: r, accessibility: i, onChange: a }) {
|
|
140
|
+
return /* @__PURE__ */ f(s, {
|
|
141
|
+
...i,
|
|
142
|
+
required: r,
|
|
143
|
+
isDisabled: n,
|
|
144
|
+
placeholder: e.placeholder,
|
|
145
|
+
min: e.min,
|
|
146
|
+
max: e.max,
|
|
147
|
+
step: e.step ?? "any",
|
|
148
|
+
hasSteppers: e.hasSteppers ?? !1,
|
|
149
|
+
autoFocus: e.autoFocus,
|
|
150
|
+
decreaseLabel: `Decrease ${e.label}`,
|
|
151
|
+
increaseLabel: `Increase ${e.label}`,
|
|
152
|
+
value: typeof t == "number" && Number.isFinite(t) ? t : "",
|
|
153
|
+
onValueChange: (e) => {
|
|
154
|
+
(e === "" || Number.isFinite(e)) && a(e === "" ? void 0 : e);
|
|
155
|
+
}
|
|
156
|
+
});
|
|
157
|
+
}
|
|
158
|
+
function j({ field: e, stored: t, isDisabled: n, required: r, accessibility: i, onChange: a }) {
|
|
159
|
+
let o = !r && !e.options.some((e) => e.value === "");
|
|
160
|
+
return /* @__PURE__ */ f(c, {
|
|
161
|
+
...i,
|
|
162
|
+
"aria-required": r || void 0,
|
|
163
|
+
isDisabled: n,
|
|
164
|
+
isInvalid: i["aria-invalid"],
|
|
165
|
+
options: o ? [{
|
|
166
|
+
value: "",
|
|
167
|
+
label: e.placeholder ?? "None"
|
|
168
|
+
}, ...e.options] : e.options,
|
|
169
|
+
placeholder: e.placeholder,
|
|
170
|
+
autoFocus: e.autoFocus,
|
|
171
|
+
value: typeof t == "string" ? t : "",
|
|
172
|
+
onValueChange: (e) => a(e === "" ? void 0 : e)
|
|
173
|
+
});
|
|
174
|
+
}
|
|
175
|
+
function M({ field: e, stored: t, items: n, isDisabled: r, accessibility: i, onChange: a }) {
|
|
176
|
+
return /* @__PURE__ */ f("div", {
|
|
177
|
+
...i,
|
|
178
|
+
role: "group",
|
|
179
|
+
children: /* @__PURE__ */ f(o, {
|
|
180
|
+
items: n,
|
|
181
|
+
value: O(e, t),
|
|
182
|
+
...g(e),
|
|
183
|
+
mode: e.multiple ? "multiple" : "single",
|
|
184
|
+
isDisabled: r,
|
|
185
|
+
onValueChange: (t) => a(k(e, t))
|
|
186
|
+
})
|
|
187
|
+
});
|
|
188
|
+
}
|
|
189
|
+
function N({ field: e, stored: r, items: o, isDisabled: s, accessibility: { required: c, ...u }, onChange: d }) {
|
|
190
|
+
let p = {
|
|
191
|
+
stored: r,
|
|
192
|
+
isDisabled: s,
|
|
193
|
+
required: c,
|
|
194
|
+
accessibility: u,
|
|
195
|
+
onChange: d
|
|
196
|
+
};
|
|
197
|
+
switch (e.type) {
|
|
198
|
+
case "shortText": return /* @__PURE__ */ f(n, {
|
|
199
|
+
...u,
|
|
200
|
+
required: c,
|
|
201
|
+
disabled: s,
|
|
202
|
+
placeholder: e.placeholder,
|
|
203
|
+
type: e.inputType,
|
|
204
|
+
maxLength: e.maxLength,
|
|
205
|
+
readOnly: e.readOnly,
|
|
206
|
+
autoFocus: e.autoFocus,
|
|
207
|
+
value: typeof r == "string" ? r : "",
|
|
208
|
+
onChange: (e) => d(e.target.value)
|
|
209
|
+
});
|
|
210
|
+
case "longText": return /* @__PURE__ */ f(t, {
|
|
211
|
+
...u,
|
|
212
|
+
required: c,
|
|
213
|
+
disabled: s,
|
|
214
|
+
placeholder: e.placeholder,
|
|
215
|
+
rows: e.rows,
|
|
216
|
+
maxLength: e.maxLength,
|
|
217
|
+
readOnly: e.readOnly,
|
|
218
|
+
autoFocus: e.autoFocus,
|
|
219
|
+
value: typeof r == "string" ? r : "",
|
|
220
|
+
onChange: (e) => d(e.target.value)
|
|
221
|
+
});
|
|
222
|
+
case "number": return /* @__PURE__ */ f(A, {
|
|
223
|
+
field: e,
|
|
224
|
+
...p
|
|
225
|
+
});
|
|
226
|
+
case "boolean": return /* @__PURE__ */ f(l, {
|
|
227
|
+
...u,
|
|
228
|
+
isDisabled: s,
|
|
229
|
+
autoFocus: e.autoFocus,
|
|
230
|
+
isSelected: r === !0,
|
|
231
|
+
onSelectedChange: d
|
|
232
|
+
});
|
|
233
|
+
case "dateTime": return /* @__PURE__ */ f(n, {
|
|
234
|
+
...u,
|
|
235
|
+
required: c,
|
|
236
|
+
disabled: s,
|
|
237
|
+
type: "datetime-local",
|
|
238
|
+
step: "0.001",
|
|
239
|
+
autoFocus: e.autoFocus,
|
|
240
|
+
value: _(r)?.slice(0, -1) ?? "",
|
|
241
|
+
onChange: (e) => d(_(`${e.target.value}Z`))
|
|
242
|
+
});
|
|
243
|
+
case "richText": return /* @__PURE__ */ f(a, {
|
|
244
|
+
...u,
|
|
245
|
+
label: e.label,
|
|
246
|
+
isRequired: c,
|
|
247
|
+
isDisabled: s,
|
|
248
|
+
placeholder: e.placeholder,
|
|
249
|
+
format: "json",
|
|
250
|
+
value: r === void 0 ? i : r,
|
|
251
|
+
onChange: d
|
|
252
|
+
});
|
|
253
|
+
case "select": return /* @__PURE__ */ f(j, {
|
|
254
|
+
field: e,
|
|
255
|
+
...p
|
|
256
|
+
});
|
|
257
|
+
case "asset":
|
|
258
|
+
case "reference": return /* @__PURE__ */ f(M, {
|
|
259
|
+
field: e,
|
|
260
|
+
items: o,
|
|
261
|
+
...p
|
|
262
|
+
});
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
//#endregion
|
|
266
|
+
//#region src/components/FormEditor/index.tsx
|
|
267
|
+
function P({ field: t, stored: n, items: r, isDisabled: i, error: a, isErrorVisible: o, onChange: s }) {
|
|
268
|
+
let c = u(() => a === void 0 && o ? E(t, n) : void 0, [
|
|
269
|
+
a,
|
|
270
|
+
o,
|
|
271
|
+
t,
|
|
272
|
+
n
|
|
273
|
+
]), l = t.type === "dateTime" ? `${t.label} (UTC)` : t.label;
|
|
274
|
+
return /* @__PURE__ */ f(e, {
|
|
275
|
+
label: l,
|
|
276
|
+
hint: t.hint,
|
|
277
|
+
error: a ?? c,
|
|
278
|
+
isRequired: t.required,
|
|
279
|
+
isGroup: t.type === "asset" || t.type === "reference",
|
|
280
|
+
children: (e) => /* @__PURE__ */ f(N, {
|
|
281
|
+
field: t,
|
|
282
|
+
stored: n,
|
|
283
|
+
items: r,
|
|
284
|
+
isDisabled: i,
|
|
285
|
+
accessibility: e,
|
|
286
|
+
onChange: s
|
|
287
|
+
})
|
|
288
|
+
});
|
|
289
|
+
}
|
|
290
|
+
function F({ schema: e, value: t, defaultValue: n = {}, onValueChange: r, pickerItems: i = {}, errors: a = {}, isDisabled: o = !1, showAllErrors: s = !1, columns: c = 1, minColumnWidth: l = "16rem", className: p, style: h, ...g }) {
|
|
291
|
+
let [_, v] = d(n), [y, b] = d(() => /* @__PURE__ */ new Set()), x = t ?? _;
|
|
292
|
+
u(() => m(e), [e]);
|
|
293
|
+
let S = (e, n) => {
|
|
294
|
+
let i = { ...x };
|
|
295
|
+
n === void 0 ? delete i[e] : i[e] = n, t === void 0 && v(i), b((t) => new Set(t).add(e)), r?.(i, { fieldId: e });
|
|
296
|
+
}, C = c === "auto" ? { "--form-editor-min-column": l } : {
|
|
297
|
+
"--form-editor-columns": c,
|
|
298
|
+
"--form-editor-min-column": l
|
|
299
|
+
};
|
|
300
|
+
return /* @__PURE__ */ f("div", {
|
|
301
|
+
className: [
|
|
302
|
+
"ds-form-editor",
|
|
303
|
+
c === "auto" && "ds-form-editor--auto",
|
|
304
|
+
p
|
|
305
|
+
].filter(Boolean).join(" "),
|
|
306
|
+
style: {
|
|
307
|
+
...C,
|
|
308
|
+
...h
|
|
309
|
+
},
|
|
310
|
+
...g,
|
|
311
|
+
children: e.fields.map((e) => {
|
|
312
|
+
let t = Object.hasOwn(x, e.id) ? x[e.id] : void 0, n = o || !!e.isDisabled, r = n || "readOnly" in e && !!e.readOnly, c = s || y.has(e.id) || e.type === "number" && typeof t == "number";
|
|
313
|
+
return /* @__PURE__ */ f(P, {
|
|
314
|
+
field: e,
|
|
315
|
+
stored: t,
|
|
316
|
+
items: i[e.id] ?? [],
|
|
317
|
+
isDisabled: n,
|
|
318
|
+
error: a[e.id],
|
|
319
|
+
isErrorVisible: c,
|
|
320
|
+
onChange: (t) => {
|
|
321
|
+
r || S(e.id, t);
|
|
322
|
+
}
|
|
323
|
+
}, e.id);
|
|
324
|
+
})
|
|
325
|
+
});
|
|
326
|
+
}
|
|
327
|
+
//#endregion
|
|
328
|
+
export { D as n, F as t };
|
|
329
|
+
|
|
330
|
+
//# sourceMappingURL=FormEditor-DuW8x78Y.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"FormEditor-DuW8x78Y.js","names":["documentError","emptyDocument","RichTextEditor","fieldTypes","assertModel","ids","field","model","linkCopy","pickerCopy","defaults","rest","parseUtcDateTime","value","match","canonical","date","isLink","key","hasRichText","node","requiredError","textError","richTextError","error","choiceError","option","linkError","itemLabel","item","itemsLabel","max","validators","fieldError","validateFormValues","values","selectedIds","link","id","pickerValue","links","NumberControl","NumberField","accessibility","stored","next","onChange","SelectControl","isClearable","required","Select","PickerControl","EntryPicker","FormControl","shared","TextField","isDisabled","event","TextArea","Switch","FormEditorField","validation","useMemo","isErrorVisible","label","Field","FormEditor","internal","setInternal","useState","defaultValue","changed","setChanged","current","schema","commit","fieldId","previous","onValueChange","layout","columns","minColumnWidth","className","style","fieldDisabled","isLocked","showAllErrors","pickerItems","errors"],"sources":["../../src/components/FormEditor/model.ts","../../src/components/FormEditor/control.tsx","../../src/components/FormEditor/index.tsx"],"sourcesContent":["import type { ReactNode } from \"react\";\nimport type { SelectOption } from \"../Select\";\nimport type { EntryPickerProps } from \"../EntryPicker\";\nimport type { JSONContent } from \"@tiptap/react\";\nimport { documentError } from \"../RichTextEditor/document\";\nimport type { RichTextDocument } from \"../RichTextEditor\";\n\ninterface FormFieldBase {\n id: string;\n label: string;\n hint?: ReactNode;\n placeholder?: string;\n required?: boolean;\n isDisabled?: boolean;\n}\n\nexport type FormField = FormFieldBase & (\n | { type: \"shortText\"; inputType?: \"text\" | \"email\"; maxLength?: number; readOnly?: boolean; autoFocus?: boolean; multiple?: never }\n | { type: \"longText\"; rows?: number; maxLength?: number; readOnly?: boolean; autoFocus?: boolean; multiple?: never }\n | { type: \"boolean\" | \"dateTime\"; autoFocus?: boolean; multiple?: never }\n | { type: \"richText\"; autoFocus?: never; multiple?: never }\n | { type: \"number\"; min?: number; max?: number; step?: number | \"any\"; hasSteppers?: boolean; autoFocus?: boolean; multiple?: never }\n | { type: \"select\"; options: SelectOption[]; autoFocus?: boolean; multiple?: never }\n | { type: \"asset\" | \"reference\"; multiple?: boolean; autoFocus?: never; picker?: Pick<EntryPickerProps, \"label\" | \"addLabel\" | \"emptyLabel\" | \"itemLabel\" | \"itemsLabel\" | \"max\" | \"isReorderable\"> }\n);\n\nexport type FieldOf<T extends FormField[\"type\"]> = Extract<FormField, { type: T }>;\n\nexport interface FormSchema {\n fields: readonly FormField[];\n}\n\nexport interface AssetValue { assetId: string }\nexport interface ReferenceValue { entryId: string }\nexport type FormFieldValue = string | number | boolean | RichTextDocument | AssetValue | ReferenceValue | AssetValue[] | ReferenceValue[];\nexport type FormValues = Record<string, FormFieldValue | undefined>;\nexport type FormErrors = Partial<Record<string, string>>;\n\nconst fieldTypes = new Set([\"shortText\", \"longText\", \"richText\", \"number\", \"boolean\", \"dateTime\", \"asset\", \"reference\", \"select\"]);\n\nexport function assertModel(model: FormSchema) {\n const ids = new Set<string>();\n for (const field of model.fields) {\n if (!field.id?.trim() || ids.has(field.id)) throw new Error(\"Form field IDs must be nonempty and unique.\");\n if (!fieldTypes.has(field.type)) throw new Error(`Unsupported form field type: ${field.type}`);\n if (field.type === \"select\" && !Array.isArray(field.options)) throw new Error(`Select field ${field.id} needs an options array.`);\n ids.add(field.id);\n }\n}\n\nconst linkCopy = {\n asset: { key: \"assetId\", label: \"Assets\", itemLabel: \"asset\", itemsLabel: \"assets\" },\n reference: { key: \"entryId\", label: \"Entries\", itemLabel: \"entry\", itemsLabel: \"entries\" },\n} as const;\n\n/** Picker copy, with asset or entry wording for any option the field leaves out or sets to undefined. */\nexport function pickerCopy(field: FieldOf<\"asset\" | \"reference\">) {\n const defaults = linkCopy[field.type];\n const {\n label = defaults.label,\n addLabel = `Choose ${field.label}`,\n itemLabel = defaults.itemLabel,\n itemsLabel = defaults.itemsLabel,\n ...rest\n } = field.picker ?? {};\n return { ...rest, label, addLabel, itemLabel, itemsLabel };\n}\n\n/** Accept UTC ISO strings without rolling invalid dates into the next month. */\nexport function parseUtcDateTime(value: unknown): string | undefined {\n if (typeof value !== \"string\") return;\n const match = /^(\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2})(?::(\\d{2})(?:\\.(\\d{1,3}))?)?(?:Z|\\+00:00)$/.exec(value);\n if (!match) return;\n const canonical = `${match[1]}:${match[2] ?? \"00\"}.${(match[3] ?? \"\").padEnd(3, \"0\")}Z`;\n const date = new Date(canonical);\n if (Number.isFinite(date.getTime()) && date.toISOString() === canonical) return canonical;\n}\n\nfunction isLink(value: unknown, key: \"assetId\" | \"entryId\"): boolean {\n return !!value && typeof value === \"object\" && key in value &&\n typeof (value as Record<string, unknown>)[key] === \"string\" &&\n ((value as Record<string, string>)[key]).trim().length > 0;\n}\n\nfunction hasRichText(node: JSONContent): boolean {\n return !!node.text?.trim() || node.type === \"horizontalRule\" || !!node.content?.some(hasRichText);\n}\n\nfunction requiredError(field: FormField): string {\n return `${field.label} is required.`;\n}\n\nfunction textError(field: FormField, value: unknown): string | undefined {\n if (typeof value !== \"string\") return \"Enter text.\";\n if (field.required && !value.trim()) return requiredError(field);\n if ((field.type === \"shortText\" || field.type === \"longText\") && field.maxLength !== undefined && value.length > field.maxLength) {\n return `Enter no more than ${field.maxLength} characters.`;\n }\n}\n\nfunction richTextError(field: FormField, value: unknown): string | undefined {\n const error = documentError(value);\n if (error) return error;\n if (field.required && !hasRichText(value as RichTextDocument)) return requiredError(field);\n}\n\nfunction choiceError(field: FormField, value: unknown): string | undefined {\n if (typeof value !== \"string\") return \"Choose an available option.\";\n if (value === \"\") return field.required ? requiredError(field) : undefined;\n // An empty list means the choices are still loading, so a stored value stays valid until they arrive.\n if (field.type === \"select\" && field.options.length > 0 && !field.options.some((option) => option.value === value)) {\n return \"Choose an available option.\";\n }\n}\n\nfunction linkError(field: FormField, value: unknown): string | undefined {\n if (field.type !== \"asset\" && field.type !== \"reference\") return;\n const { key, itemLabel, itemsLabel } = linkCopy[field.type];\n if (!field.multiple) return isLink(value, key) ? undefined : `Choose an ${itemLabel}.`;\n if (!Array.isArray(value) || !value.every((item) => isLink(item, key))) return `Choose a list of ${itemsLabel}.`;\n if (field.required && value.length === 0) return requiredError(field);\n const max = field.picker?.max;\n if (max !== undefined && value.length > max) return `Choose no more than ${max} ${pickerCopy(field).itemsLabel}.`;\n}\n\nconst validators: Record<FormField[\"type\"], (field: FormField, value: unknown) => string | undefined> = {\n shortText: textError,\n longText: textError,\n richText: richTextError,\n number: (field, value) => {\n if (typeof value !== \"number\" || !Number.isFinite(value)) return \"Enter a finite number.\";\n if (field.type !== \"number\") return;\n if (field.min !== undefined && value < field.min) return `Enter a number of at least ${field.min}.`;\n if (field.max !== undefined && value > field.max) return `Enter a number no greater than ${field.max}.`;\n },\n boolean: (_field, value) => typeof value === \"boolean\" ? undefined : \"Choose a boolean value.\",\n dateTime: (_field, value) => parseUtcDateTime(value) ? undefined : \"Enter a valid UTC date and time.\",\n select: choiceError,\n asset: linkError,\n reference: linkError,\n};\n\nexport function fieldError(field: FormField, value: unknown): string | undefined {\n if (value === undefined) return field.required ? requiredError(field) : undefined;\n return validators[field.type](field, value);\n}\n\n/** Validate data loaded from JSON as well as values emitted by FormEditor. */\nexport function validateFormValues(model: FormSchema, values: Record<string, unknown>): FormErrors {\n assertModel(model);\n return Object.fromEntries(model.fields.flatMap((field): [string, string][] => {\n const error = fieldError(field, Object.hasOwn(values, field.id) ? values[field.id] : undefined);\n return error ? [[field.id, error]] : [];\n }));\n}\n","import { EntryPicker, type EntryItem } from \"../EntryPicker\";\nimport type { FieldProps } from \"../Field\";\nimport { NumberField } from \"../NumberField\";\nimport { RichTextEditor, type RichTextDocument } from \"../RichTextEditor\";\nimport { emptyDocument } from \"../RichTextEditor/document\";\nimport { Select } from \"../Select\";\nimport { Switch } from \"../Switch\";\nimport { TextArea, TextField } from \"../TextField\";\nimport { parseUtcDateTime, pickerCopy, type FieldOf, type FormField, type FormFieldValue } from \"./model\";\n\nfunction selectedIds(field: FormField, value: FormFieldValue | undefined): string[] {\n const key = field.type === \"asset\" ? \"assetId\" : \"entryId\";\n const links = Array.isArray(value) ? value : value === undefined ? [] : [value];\n return links.flatMap((link) => {\n if (!link || typeof link !== \"object\" || !(key in link)) return [];\n const id = (link as unknown as Record<string, unknown>)[key];\n return typeof id === \"string\" ? [id] : [];\n });\n}\n\nfunction pickerValue(field: FormField, ids: string[]): FormFieldValue | undefined {\n if (field.type === \"asset\") {\n const links = ids.map((assetId) => ({ assetId }));\n return field.multiple ? links : links[0];\n }\n const links = ids.map((entryId) => ({ entryId }));\n return field.multiple ? links : links[0];\n}\n\ntype Accessibility = Parameters<FieldProps[\"children\"]>[0];\n\ninterface ControlProps<F extends FormField> {\n field: F;\n stored: FormFieldValue | undefined;\n isDisabled: boolean;\n required: boolean | undefined;\n accessibility: Omit<Accessibility, \"required\">;\n onChange: (value: FormFieldValue | undefined) => void;\n}\n\nfunction NumberControl({ field, stored, isDisabled, required, accessibility, onChange }: ControlProps<FieldOf<\"number\">>) {\n return <NumberField {...accessibility} required={required} isDisabled={isDisabled} placeholder={field.placeholder}\n min={field.min} max={field.max} step={field.step ?? \"any\"} hasSteppers={field.hasSteppers ?? false} autoFocus={field.autoFocus}\n decreaseLabel={`Decrease ${field.label}`} increaseLabel={`Increase ${field.label}`}\n value={typeof stored === \"number\" && Number.isFinite(stored) ? stored : \"\"}\n onValueChange={(next) => { if (next === \"\" || Number.isFinite(next)) onChange(next === \"\" ? undefined : next); }} />;\n}\n\nfunction SelectControl({ field, stored, isDisabled, required, accessibility, onChange }: ControlProps<FieldOf<\"select\">>) {\n // Optional selects lead with an empty choice; picking it removes the key.\n const isClearable = !required && !field.options.some((option) => option.value === \"\");\n return <Select {...accessibility} aria-required={required || undefined} isDisabled={isDisabled} isInvalid={accessibility[\"aria-invalid\"]}\n options={isClearable ? [{ value: \"\", label: field.placeholder ?? \"None\" }, ...field.options] : field.options}\n placeholder={field.placeholder} autoFocus={field.autoFocus}\n value={typeof stored === \"string\" ? stored : \"\"} onValueChange={(next) => onChange(next === \"\" ? undefined : next)} />;\n}\n\nfunction PickerControl({ field, stored, items, isDisabled, accessibility, onChange }: ControlProps<FieldOf<\"asset\" | \"reference\">> & { items: EntryItem[] }) {\n return <div {...accessibility} role=\"group\">\n <EntryPicker items={items} value={selectedIds(field, stored)}\n {...pickerCopy(field)} mode={field.multiple ? \"multiple\" : \"single\"} isDisabled={isDisabled}\n onValueChange={(ids) => onChange(pickerValue(field, ids))} />\n </div>;\n}\n\ninterface FormControlProps {\n field: FormField;\n stored: FormFieldValue | undefined;\n items: EntryItem[];\n isDisabled: boolean;\n accessibility: Accessibility;\n onChange: (value: FormFieldValue | undefined) => void;\n}\n\nexport function FormControl({ field, stored, items, isDisabled, accessibility: { required, ...accessibility }, onChange }: FormControlProps) {\n const shared = { stored, isDisabled, required, accessibility, onChange };\n switch (field.type) {\n case \"shortText\":\n return <TextField {...accessibility} required={required} disabled={isDisabled} placeholder={field.placeholder}\n type={field.inputType} maxLength={field.maxLength} readOnly={field.readOnly} autoFocus={field.autoFocus}\n value={typeof stored === \"string\" ? stored : \"\"} onChange={(event) => onChange(event.target.value)} />;\n case \"longText\":\n return <TextArea {...accessibility} required={required} disabled={isDisabled} placeholder={field.placeholder}\n rows={field.rows} maxLength={field.maxLength} readOnly={field.readOnly} autoFocus={field.autoFocus}\n value={typeof stored === \"string\" ? stored : \"\"} onChange={(event) => onChange(event.target.value)} />;\n case \"number\":\n return <NumberControl field={field} {...shared} />;\n case \"boolean\":\n return <Switch {...accessibility} isDisabled={isDisabled} autoFocus={field.autoFocus} isSelected={stored === true} onSelectedChange={onChange} />;\n case \"dateTime\":\n return <TextField {...accessibility} required={required} disabled={isDisabled} type=\"datetime-local\" step=\"0.001\" autoFocus={field.autoFocus}\n value={parseUtcDateTime(stored)?.slice(0, -1) ?? \"\"}\n onChange={(event) => onChange(parseUtcDateTime(`${event.target.value}Z`))} />;\n case \"richText\":\n return <RichTextEditor {...accessibility} label={field.label} isRequired={required} isDisabled={isDisabled}\n placeholder={field.placeholder} format=\"json\" value={stored === undefined ? emptyDocument : stored as RichTextDocument}\n onChange={onChange} />;\n case \"select\":\n return <SelectControl field={field} {...shared} />;\n case \"asset\":\n case \"reference\":\n return <PickerControl field={field} items={items} {...shared} />;\n }\n}\n","import { useMemo, useState, type CSSProperties, type HTMLAttributes } from \"react\";\nimport type { EntryItem } from \"../EntryPicker\";\nimport { Field } from \"../Field\";\nimport type { GridColumns } from \"../Grid\";\nimport { FormControl } from \"./control\";\nimport { assertModel, fieldError, type FormField, type FormSchema, type FormErrors, type FormFieldValue, type FormValues } from \"./model\";\nimport \"./style.css\";\n\nexport { validateFormValues } from \"./model\";\nexport type { AssetValue, FormField, FormSchema, FormErrors, FormFieldValue, FormValues, ReferenceValue } from \"./model\";\n\nexport interface FormChange { fieldId: string }\n\nexport interface FormEditorProps extends Omit<HTMLAttributes<HTMLDivElement>, \"defaultValue\" | \"onChange\"> {\n schema: FormSchema;\n value?: FormValues;\n defaultValue?: FormValues;\n onValueChange?: (values: FormValues, change: FormChange) => void;\n /** Available assets or entries, keyed by field ID. Loading belongs to the app. */\n pickerItems?: Record<string, EntryItem[]>;\n /** App-provided messages take precedence over built-in validation. */\n errors?: FormErrors;\n isDisabled?: boolean;\n showAllErrors?: boolean;\n /** Columns at the widest. The form drops one whenever a field would be narrower than `minColumnWidth`. */\n columns?: GridColumns;\n /** Narrowest a field may get before the form drops a column. */\n minColumnWidth?: string;\n}\n\ninterface FormEditorFieldProps {\n field: FormField;\n stored: FormFieldValue | undefined;\n items: EntryItem[];\n isDisabled: boolean;\n error: string | undefined;\n isErrorVisible: boolean;\n onChange: (value: FormFieldValue | undefined) => void;\n}\n\n/** Validates only when its message can show, and only when its own field or value changes. */\nfunction FormEditorField({ field, stored, items, isDisabled, error, isErrorVisible, onChange }: FormEditorFieldProps) {\n const validation = useMemo(() => error === undefined && isErrorVisible ? fieldError(field, stored) : undefined,\n [error, isErrorVisible, field, stored]);\n const label = field.type === \"dateTime\" ? `${field.label} (UTC)` : field.label;\n return (\n <Field label={label} hint={field.hint} error={error ?? validation} isRequired={field.required}\n isGroup={field.type === \"asset\" || field.type === \"reference\"}>\n {(accessibility) => <FormControl field={field} stored={stored} items={items}\n isDisabled={isDisabled} accessibility={accessibility} onChange={onChange} />}\n </Field>\n );\n}\n\n/** Ordered form controls. Loading, submission, and persistence belong to the app. */\nexport function FormEditor({ schema, value, defaultValue = {}, onValueChange, pickerItems = {}, errors = {},\n isDisabled = false, showAllErrors = false, columns = 1, minColumnWidth = \"16rem\", className, style, ...rest }: FormEditorProps) {\n const [internal, setInternal] = useState<FormValues>(defaultValue);\n const [changed, setChanged] = useState<Set<string>>(() => new Set());\n const current = value ?? internal;\n useMemo(() => assertModel(schema), [schema]);\n\n const commit = (fieldId: string, next: FormFieldValue | undefined) => {\n const values = { ...current };\n if (next === undefined) delete values[fieldId];\n else values[fieldId] = next;\n if (value === undefined) setInternal(values);\n setChanged((previous) => new Set(previous).add(fieldId));\n onValueChange?.(values, { fieldId });\n };\n\n const layout = columns === \"auto\"\n ? { \"--form-editor-min-column\": minColumnWidth }\n : { \"--form-editor-columns\": columns, \"--form-editor-min-column\": minColumnWidth };\n\n return (\n <div className={[\"ds-form-editor\", columns === \"auto\" && \"ds-form-editor--auto\", className].filter(Boolean).join(\" \")}\n style={{ ...layout, ...style } as CSSProperties} {...rest}>\n {schema.fields.map((field) => {\n const stored = Object.hasOwn(current, field.id) ? current[field.id] : undefined;\n const fieldDisabled = isDisabled || Boolean(field.isDisabled);\n const isLocked = fieldDisabled || (\"readOnly\" in field && Boolean(field.readOnly));\n // NumberField marks an out-of-range number invalid itself, so its message shows straight away.\n const isErrorVisible = showAllErrors || changed.has(field.id) || (field.type === \"number\" && typeof stored === \"number\");\n return <FormEditorField key={field.id} field={field} stored={stored} items={pickerItems[field.id] ?? []}\n isDisabled={fieldDisabled} error={errors[field.id]} isErrorVisible={isErrorVisible}\n onChange={(next) => { if (!isLocked) commit(field.id, next); }} />;\n })}\n </div>\n );\n}\n"],"mappings":"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"}
|