@panneau/forms 5.0.0-alpha.2 → 5.0.0-alpha.4

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.
@@ -1,6 +1,6 @@
1
1
  import { type ReactNode } from 'react';
2
2
  import { type Form } from '@panneau/core';
3
- import { type FieldEntry, type RelationSource } from '@panneau/fields';
3
+ import { type FieldEntry, type RelationSource, type UploadSource } from '@panneau/fields';
4
4
  import type { Field, LayoutNode } from '@panneau/protocol';
5
5
  export interface FormViewProps {
6
6
  /** The form state, from `useForm()` or `createForm()` of `@panneau/core`. */
@@ -13,6 +13,11 @@ export interface FormViewProps {
13
13
  locales?: readonly string[];
14
14
  /** Where relation fields search. Defaults to the one of a parent `FieldsProvider`. */
15
15
  relationSource?: RelationSource | null;
16
+ /**
17
+ * Where fields that take files upload them directly to storage. Defaults to the one of a
18
+ * parent `FieldsProvider`. Without one, values hold the files themselves.
19
+ */
20
+ uploadSource?: UploadSource | null;
16
21
  /** Field entries by type, over the built-in ones and those of the plugins. */
17
22
  components?: Readonly<Record<string, FieldEntry>>;
18
23
  /** Label of the submit button. Defaults to "Save". `null` hides the button. */
@@ -44,5 +49,5 @@ export interface FormViewProps {
44
49
  * unsaved changes and the dialog of version conflicts. Saving is the job of the form's
45
50
  * `onSubmit`.
46
51
  */
47
- export declare function FormView({ form, fields, layout, locales, relationSource, components, submitLabel, actions, onSubmitted, onReload, guardUnsavedChanges, showSavedToast, id, 'aria-label': ariaLabel, className, layoutClassName, sidebarClassName, footerClassName, summaryClassName, }: FormViewProps): import("react").JSX.Element;
52
+ export declare function FormView({ form, fields, layout, locales, relationSource, uploadSource, components, submitLabel, actions, onSubmitted, onReload, guardUnsavedChanges, showSavedToast, id, 'aria-label': ariaLabel, className, layoutClassName, sidebarClassName, footerClassName, summaryClassName, }: FormViewProps): import("react").JSX.Element;
48
53
  //# sourceMappingURL=FormView.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"FormView.d.ts","sourceRoot":"","sources":["../src/FormView.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,SAAS,EAAoB,MAAM,OAAO,CAAC;AAEzD,OAAO,EACH,KAAK,IAAI,EAQZ,MAAM,eAAe,CAAC;AACvB,OAAO,EACH,KAAK,UAAU,EAEf,KAAK,cAAc,EAEtB,MAAM,iBAAiB,CAAC;AACzB,OAAO,KAAK,EAAE,KAAK,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAa3D,MAAM,WAAW,aAAa;IAC1B,6EAA6E;IAC7E,IAAI,EAAE,IAAI,CAAC;IACX,8BAA8B;IAC9B,MAAM,EAAE,SAAS,KAAK,EAAE,CAAC;IACzB,4EAA4E;IAC5E,MAAM,CAAC,EAAE,SAAS,UAAU,EAAE,CAAC;IAC/B,yFAAyF;IACzF,OAAO,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAC5B,sFAAsF;IACtF,cAAc,CAAC,EAAE,cAAc,GAAG,IAAI,CAAC;IACvC,8EAA8E;IAC9E,UAAU,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC,CAAC;IAClD,+EAA+E;IAC/E,WAAW,CAAC,EAAE,SAAS,CAAC;IACxB,gDAAgD;IAChD,OAAO,CAAC,EAAE,SAAS,CAAC;IACpB,uEAAuE;IACvE,WAAW,CAAC,EAAE,CAAC,MAAM,EAAE,OAAO,KAAK,IAAI,CAAC;IACxC;;;OAGG;IACH,QAAQ,CAAC,EAAE,MAAM,IAAI,CAAC;IACtB,4FAA4F;IAC5F,mBAAmB,CAAC,EAAE,OAAO,CAAC;IAC9B,iEAAiE;IACjE,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC7B;AAqCD;;;;;GAKG;AACH,wBAAgB,QAAQ,CAAC,EACrB,IAAI,EACJ,MAAM,EACN,MAAW,EACX,OAAO,EACP,cAAc,EACd,UAAU,EACV,WAAW,EACX,OAAO,EACP,WAAW,EACX,QAAQ,EACR,mBAA0B,EAC1B,cAAqB,EACrB,EAAE,EACF,YAAY,EAAE,SAAS,EACvB,SAAS,EACT,eAAe,EACf,gBAAgB,EAChB,eAAe,EACf,gBAAgB,GACnB,EAAE,aAAa,+BAgMf"}
1
+ {"version":3,"file":"FormView.d.ts","sourceRoot":"","sources":["../src/FormView.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,SAAS,EAAoB,MAAM,OAAO,CAAC;AAEzD,OAAO,EACH,KAAK,IAAI,EAQZ,MAAM,eAAe,CAAC;AACvB,OAAO,EACH,KAAK,UAAU,EAEf,KAAK,cAAc,EACnB,KAAK,YAAY,EAEpB,MAAM,iBAAiB,CAAC;AACzB,OAAO,KAAK,EAAE,KAAK,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAa3D,MAAM,WAAW,aAAa;IAC1B,6EAA6E;IAC7E,IAAI,EAAE,IAAI,CAAC;IACX,8BAA8B;IAC9B,MAAM,EAAE,SAAS,KAAK,EAAE,CAAC;IACzB,4EAA4E;IAC5E,MAAM,CAAC,EAAE,SAAS,UAAU,EAAE,CAAC;IAC/B,yFAAyF;IACzF,OAAO,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAC5B,sFAAsF;IACtF,cAAc,CAAC,EAAE,cAAc,GAAG,IAAI,CAAC;IACvC;;;OAGG;IACH,YAAY,CAAC,EAAE,YAAY,GAAG,IAAI,CAAC;IACnC,8EAA8E;IAC9E,UAAU,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC,CAAC;IAClD,+EAA+E;IAC/E,WAAW,CAAC,EAAE,SAAS,CAAC;IACxB,gDAAgD;IAChD,OAAO,CAAC,EAAE,SAAS,CAAC;IACpB,uEAAuE;IACvE,WAAW,CAAC,EAAE,CAAC,MAAM,EAAE,OAAO,KAAK,IAAI,CAAC;IACxC;;;OAGG;IACH,QAAQ,CAAC,EAAE,MAAM,IAAI,CAAC;IACtB,4FAA4F;IAC5F,mBAAmB,CAAC,EAAE,OAAO,CAAC;IAC9B,iEAAiE;IACjE,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC7B;AAqCD;;;;;GAKG;AACH,wBAAgB,QAAQ,CAAC,EACrB,IAAI,EACJ,MAAM,EACN,MAAW,EACX,OAAO,EACP,cAAc,EACd,YAAY,EACZ,UAAU,EACV,WAAW,EACX,OAAO,EACP,WAAW,EACX,QAAQ,EACR,mBAA0B,EAC1B,cAAqB,EACrB,EAAE,EACF,YAAY,EAAE,SAAS,EACvB,SAAS,EACT,eAAe,EACf,gBAAgB,EAChB,eAAe,EACf,gBAAgB,GACnB,EAAE,aAAa,+BAiMf"}
package/dist/FormView.js CHANGED
@@ -42,7 +42,7 @@ function orderErrors(names, errors) {
42
42
  * unsaved changes and the dialog of version conflicts. Saving is the job of the form's
43
43
  * `onSubmit`.
44
44
  */
45
- function FormView({ form, fields, layout = [], locales, relationSource, components, submitLabel, actions, onSubmitted, onReload, guardUnsavedChanges = true, showSavedToast = true, id, "aria-label": ariaLabel, className, layoutClassName, sidebarClassName, footerClassName, summaryClassName }) {
45
+ function FormView({ form, fields, layout = [], locales, relationSource, uploadSource, components, submitLabel, actions, onSubmitted, onReload, guardUnsavedChanges = true, showSavedToast = true, id, "aria-label": ariaLabel, className, layoutClassName, sidebarClassName, footerClassName, summaryClassName }) {
46
46
  const intl = usePanneauIntl();
47
47
  const toasts = useToast();
48
48
  const { definition } = usePanneau();
@@ -118,6 +118,7 @@ function FormView({ form, fields, layout = [], locales, relationSource, componen
118
118
  components: entries,
119
119
  locales: locales ?? definition?.locales ?? [],
120
120
  ...relationSource !== void 0 ? { relationSource } : {},
121
+ ...uploadSource !== void 0 ? { uploadSource } : {},
121
122
  binding: FormBinding,
122
123
  children: [/* @__PURE__ */ jsxs("form", {
123
124
  ref: root,
@@ -1 +1 @@
1
- {"version":3,"file":"FormView.js","names":["node","styles"],"sources":["../src/FormView.tsx"],"sourcesContent":["import { type ReactNode, useRef, useState } from 'react';\n\nimport {\n type Form,\n type SubmitResult,\n getLayoutFieldNames,\n getProblem,\n resolveFields,\n selectVisible,\n usePanneau,\n usePlugins,\n} from '@panneau/core';\nimport {\n type FieldEntry,\n FieldsProvider,\n type RelationSource,\n builtInFields,\n} from '@panneau/fields';\nimport type { Field, LayoutNode } from '@panneau/protocol';\nimport { Alert, Button, usePanneauIntl, useToast } from '@panneau/ui';\n\nimport { ConflictDialog } from './ConflictDialog';\nimport { ErrorSummary } from './ErrorSummary';\nimport styles from './Form.module.css';\nimport { FormBinding } from './FormBinding';\nimport { FormContext, RevealContext, createRevealRegistry, useFormState } from './context';\nimport { cx } from './cx';\nimport { LayoutNodes } from './layout/Layout';\nimport { messages } from './messages';\nimport { useBeforeUnloadGuard } from './unsaved';\n\nexport interface FormViewProps {\n /** The form state, from `useForm()` or `createForm()` of `@panneau/core`. */\n form: Form;\n /** The fields of the form. */\n fields: readonly Field[];\n /** The layout tree. Fields that no node references are added at the end. */\n layout?: readonly LayoutNode[];\n /** Locales of localized fields. Defaults to the definition's, from `PanneauProvider`. */\n locales?: readonly string[];\n /** Where relation fields search. Defaults to the one of a parent `FieldsProvider`. */\n relationSource?: RelationSource | null;\n /** Field entries by type, over the built-in ones and those of the plugins. */\n components?: Readonly<Record<string, FieldEntry>>;\n /** Label of the submit button. Defaults to \"Save\". `null` hides the button. */\n submitLabel?: ReactNode;\n /** Other buttons, next to the submit button. */\n actions?: ReactNode;\n /** Called after a successful submit, with what `onSubmit` returned. */\n onSubmitted?: (result: unknown) => void;\n /**\n * Called by \"Load the current version\" in the conflict dialog when the backend didn't send the\n * current item: fetch it, and `form.reset()` with it.\n */\n onReload?: () => void;\n /** Asks for confirmation before the page is left with unsaved changes. Defaults to true. */\n guardUnsavedChanges?: boolean;\n /** Shows a toast after a successful submit. Defaults to true. */\n showSavedToast?: boolean;\n id?: string;\n 'aria-label'?: string;\n className?: string;\n layoutClassName?: string;\n sidebarClassName?: string;\n footerClassName?: string;\n summaryClassName?: string;\n}\n\nconst FOCUSABLE =\n 'input:not([type=\"hidden\"]):not([disabled]), textarea:not([disabled]), select:not([disabled]), button:not([disabled]), [tabindex]:not([tabindex=\"-1\"])';\n\n/** The element of the field at a path, or of the closest field that holds it. */\nfunction findFieldElement(root: HTMLElement, path: string): HTMLElement | null {\n let current = path;\n while (current !== '') {\n const element = root.querySelector<HTMLElement>(\n `[data-panneau-path=\"${CSS.escape(current)}\"]`,\n );\n if (element !== null) {\n return element;\n }\n current = current.includes('.') ? current.slice(0, current.lastIndexOf('.')) : '';\n }\n return null;\n}\n\n/**\n * Error paths in the order of their fields: the order of the layout for top-level fields, then\n * the order of the paths inside them (`blocks.2.body` before `blocks.10.body`).\n */\nfunction orderErrors(\n names: readonly string[],\n errors: Record<string, string[]>,\n): Record<string, string[]> {\n const rank = (path: string) => {\n const index = names.indexOf(path.split('.')[0] ?? '');\n return index === -1 ? names.length : index;\n };\n const compare = (a: string, b: string) =>\n rank(a) - rank(b) || a.localeCompare(b, undefined, { numeric: true });\n return Object.fromEntries(Object.entries(errors).sort(([a], [b]) => compare(a, b)));\n}\n\n/**\n * Renders a form: its fields in their layout, bound to the form state of `@panneau/core`, with\n * an error summary, a scroll to the first error, error counts on tabs and sections, a guard for\n * unsaved changes and the dialog of version conflicts. Saving is the job of the form's\n * `onSubmit`.\n */\nexport function FormView({\n form,\n fields,\n layout = [],\n locales,\n relationSource,\n components,\n submitLabel,\n actions,\n onSubmitted,\n onReload,\n guardUnsavedChanges = true,\n showSavedToast = true,\n id,\n 'aria-label': ariaLabel,\n className,\n layoutClassName,\n sidebarClassName,\n footerClassName,\n summaryClassName,\n}: FormViewProps) {\n const intl = usePanneauIntl();\n const toasts = useToast();\n const { definition } = usePanneau();\n const plugins = usePlugins();\n const state = useFormState(form);\n const [registry] = useState(createRevealRegistry);\n const [resolving, setResolving] = useState(false);\n const root = useRef<HTMLFormElement>(null);\n useBeforeUnloadGuard(guardUnsavedChanges && state.dirty);\n\n const resolved = resolveFields(fields, layout);\n const main = resolved.layout.filter((node) => node.type !== 'sidebar');\n const sidebar = resolved.layout.filter(\n (node) => node.type === 'sidebar' && selectVisible(state, node),\n );\n const order = [\n ...getLayoutFieldNames(main),\n ...getLayoutFieldNames(sidebar.flatMap((node) => node.children ?? [])),\n ];\n const entries: Record<string, FieldEntry> = {\n ...builtInFields,\n ...(Object.fromEntries(plugins.fields) as Record<string, FieldEntry>),\n ...components,\n };\n\n /** Opens the tab or section of a path, then scrolls to its field and focuses it. */\n const goTo = (path: string) => {\n registry.reveal(path);\n // Wait for the tab or section to open before scrolling.\n requestAnimationFrame(() => {\n const element = root.current !== null ? findFieldElement(root.current, path) : null;\n if (element === null) {\n return;\n }\n const reduced = window.matchMedia?.('(prefers-reduced-motion: reduce)').matches;\n element.scrollIntoView?.({ block: 'center', behavior: reduced ? 'auto' : 'smooth' });\n const target =\n element.querySelector<HTMLElement>('[aria-invalid=\"true\"]') ??\n element.querySelector<HTMLElement>(FOCUSABLE);\n target?.focus({ preventScroll: true });\n });\n };\n\n const handle = (result: SubmitResult | null) => {\n if (result === null) {\n return;\n }\n if (result.status === 'invalid') {\n const first = Object.keys(orderErrors(order, result.errors))[0];\n if (first !== undefined) {\n goTo(first);\n }\n } else if (result.status === 'submitted') {\n if (showSavedToast) {\n toasts.show({ title: intl.formatMessage(messages.saved), tone: 'success' });\n }\n onSubmitted?.(result.result);\n } else if (result.status === 'error') {\n toasts.show(\n {\n title: intl.formatMessage(messages.saveFailed),\n tone: 'danger',\n action: {\n label: intl.formatMessage(messages.retry),\n onAction: () => void form.submit().then(handle),\n },\n },\n { timeout: 8000 },\n );\n }\n };\n\n const resolve = async (choice: 'reload' | 'overwrite') => {\n if (choice === 'reload' && state.conflict?.current === null && onReload !== undefined) {\n onReload();\n return;\n }\n setResolving(true);\n try {\n handle(await form.resolveConflict(choice));\n } finally {\n setResolving(false);\n }\n };\n\n const problem = state.status === 'error' ? getProblem(state.error) : null;\n const errorDetail =\n problem?.detail ??\n (problem === null && state.error instanceof Error ? state.error.message : undefined);\n const showSummary = state.status === 'invalid' && state.submitCount > 0;\n\n return (\n <FormContext value={form}>\n <RevealContext value={registry}>\n <FieldsProvider\n components={entries}\n locales={locales ?? definition?.locales ?? []}\n {...(relationSource !== undefined ? { relationSource } : {})}\n binding={FormBinding}\n >\n <form\n ref={root}\n id={id}\n aria-label={ariaLabel}\n noValidate\n className={cx(styles.form, className)}\n onSubmit={(event) => {\n event.preventDefault();\n void form.submit().then(handle);\n }}\n >\n {showSummary ? (\n <ErrorSummary\n errors={orderErrors(order, state.errors)}\n fields={resolved.fields}\n onSelect={goTo}\n {...(summaryClassName !== undefined\n ? { className: summaryClassName }\n : {})}\n />\n ) : null}\n {state.status === 'error' ? (\n <Alert\n tone=\"danger\"\n role=\"alert\"\n title={intl.formatMessage(messages.saveFailed)}\n actions={\n <Button size=\"sm\" type=\"submit\">\n {intl.formatMessage(messages.retry)}\n </Button>\n }\n >\n {errorDetail}\n </Alert>\n ) : null}\n <div\n className={cx(\n styles.layout,\n sidebar.length > 0 && styles.withSidebar,\n layoutClassName,\n )}\n >\n <div className={styles.stack}>\n <LayoutNodes nodes={main} fields={resolved.fieldsByName} />\n </div>\n {sidebar.length > 0 ? (\n <aside className={cx(styles.sidebar, sidebarClassName)}>\n {sidebar.map((node, index) => (\n <div key={index} className={styles.stack}>\n <LayoutNodes\n nodes={node.children ?? []}\n fields={resolved.fieldsByName}\n />\n </div>\n ))}\n </aside>\n ) : null}\n </div>\n {submitLabel !== null || actions !== undefined ? (\n <div className={cx(styles.footer, footerClassName)}>\n {state.dirty ? (\n <span className={styles.dirty} role=\"status\">\n {intl.formatMessage(messages.unsaved)}\n </span>\n ) : null}\n {actions}\n {submitLabel !== null ? (\n <Button\n type=\"submit\"\n variant=\"primary\"\n isPending={state.status === 'submitting'}\n >\n {submitLabel ?? intl.formatMessage(messages.save)}\n </Button>\n ) : null}\n </div>\n ) : null}\n </form>\n <ConflictDialog\n isOpen={state.status === 'conflict' || resolving}\n fields={resolved.fields}\n values={state.values}\n current={state.conflict?.current ?? null}\n isPending={resolving}\n onReload={() => void resolve('reload')}\n onOverwrite={() => void resolve('overwrite')}\n />\n </FieldsProvider>\n </RevealContext>\n </FormContext>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"FormView.js","names":["node","styles"],"sources":["../src/FormView.tsx"],"sourcesContent":["import { type ReactNode, useRef, useState } from 'react';\n\nimport {\n type Form,\n type SubmitResult,\n getLayoutFieldNames,\n getProblem,\n resolveFields,\n selectVisible,\n usePanneau,\n usePlugins,\n} from '@panneau/core';\nimport {\n type FieldEntry,\n FieldsProvider,\n type RelationSource,\n type UploadSource,\n builtInFields,\n} from '@panneau/fields';\nimport type { Field, LayoutNode } from '@panneau/protocol';\nimport { Alert, Button, usePanneauIntl, useToast } from '@panneau/ui';\n\nimport { ConflictDialog } from './ConflictDialog';\nimport { ErrorSummary } from './ErrorSummary';\nimport styles from './Form.module.css';\nimport { FormBinding } from './FormBinding';\nimport { FormContext, RevealContext, createRevealRegistry, useFormState } from './context';\nimport { cx } from './cx';\nimport { LayoutNodes } from './layout/Layout';\nimport { messages } from './messages';\nimport { useBeforeUnloadGuard } from './unsaved';\n\nexport interface FormViewProps {\n /** The form state, from `useForm()` or `createForm()` of `@panneau/core`. */\n form: Form;\n /** The fields of the form. */\n fields: readonly Field[];\n /** The layout tree. Fields that no node references are added at the end. */\n layout?: readonly LayoutNode[];\n /** Locales of localized fields. Defaults to the definition's, from `PanneauProvider`. */\n locales?: readonly string[];\n /** Where relation fields search. Defaults to the one of a parent `FieldsProvider`. */\n relationSource?: RelationSource | null;\n /**\n * Where fields that take files upload them directly to storage. Defaults to the one of a\n * parent `FieldsProvider`. Without one, values hold the files themselves.\n */\n uploadSource?: UploadSource | null;\n /** Field entries by type, over the built-in ones and those of the plugins. */\n components?: Readonly<Record<string, FieldEntry>>;\n /** Label of the submit button. Defaults to \"Save\". `null` hides the button. */\n submitLabel?: ReactNode;\n /** Other buttons, next to the submit button. */\n actions?: ReactNode;\n /** Called after a successful submit, with what `onSubmit` returned. */\n onSubmitted?: (result: unknown) => void;\n /**\n * Called by \"Load the current version\" in the conflict dialog when the backend didn't send the\n * current item: fetch it, and `form.reset()` with it.\n */\n onReload?: () => void;\n /** Asks for confirmation before the page is left with unsaved changes. Defaults to true. */\n guardUnsavedChanges?: boolean;\n /** Shows a toast after a successful submit. Defaults to true. */\n showSavedToast?: boolean;\n id?: string;\n 'aria-label'?: string;\n className?: string;\n layoutClassName?: string;\n sidebarClassName?: string;\n footerClassName?: string;\n summaryClassName?: string;\n}\n\nconst FOCUSABLE =\n 'input:not([type=\"hidden\"]):not([disabled]), textarea:not([disabled]), select:not([disabled]), button:not([disabled]), [tabindex]:not([tabindex=\"-1\"])';\n\n/** The element of the field at a path, or of the closest field that holds it. */\nfunction findFieldElement(root: HTMLElement, path: string): HTMLElement | null {\n let current = path;\n while (current !== '') {\n const element = root.querySelector<HTMLElement>(\n `[data-panneau-path=\"${CSS.escape(current)}\"]`,\n );\n if (element !== null) {\n return element;\n }\n current = current.includes('.') ? current.slice(0, current.lastIndexOf('.')) : '';\n }\n return null;\n}\n\n/**\n * Error paths in the order of their fields: the order of the layout for top-level fields, then\n * the order of the paths inside them (`blocks.2.body` before `blocks.10.body`).\n */\nfunction orderErrors(\n names: readonly string[],\n errors: Record<string, string[]>,\n): Record<string, string[]> {\n const rank = (path: string) => {\n const index = names.indexOf(path.split('.')[0] ?? '');\n return index === -1 ? names.length : index;\n };\n const compare = (a: string, b: string) =>\n rank(a) - rank(b) || a.localeCompare(b, undefined, { numeric: true });\n return Object.fromEntries(Object.entries(errors).sort(([a], [b]) => compare(a, b)));\n}\n\n/**\n * Renders a form: its fields in their layout, bound to the form state of `@panneau/core`, with\n * an error summary, a scroll to the first error, error counts on tabs and sections, a guard for\n * unsaved changes and the dialog of version conflicts. Saving is the job of the form's\n * `onSubmit`.\n */\nexport function FormView({\n form,\n fields,\n layout = [],\n locales,\n relationSource,\n uploadSource,\n components,\n submitLabel,\n actions,\n onSubmitted,\n onReload,\n guardUnsavedChanges = true,\n showSavedToast = true,\n id,\n 'aria-label': ariaLabel,\n className,\n layoutClassName,\n sidebarClassName,\n footerClassName,\n summaryClassName,\n}: FormViewProps) {\n const intl = usePanneauIntl();\n const toasts = useToast();\n const { definition } = usePanneau();\n const plugins = usePlugins();\n const state = useFormState(form);\n const [registry] = useState(createRevealRegistry);\n const [resolving, setResolving] = useState(false);\n const root = useRef<HTMLFormElement>(null);\n useBeforeUnloadGuard(guardUnsavedChanges && state.dirty);\n\n const resolved = resolveFields(fields, layout);\n const main = resolved.layout.filter((node) => node.type !== 'sidebar');\n const sidebar = resolved.layout.filter(\n (node) => node.type === 'sidebar' && selectVisible(state, node),\n );\n const order = [\n ...getLayoutFieldNames(main),\n ...getLayoutFieldNames(sidebar.flatMap((node) => node.children ?? [])),\n ];\n const entries: Record<string, FieldEntry> = {\n ...builtInFields,\n ...(Object.fromEntries(plugins.fields) as Record<string, FieldEntry>),\n ...components,\n };\n\n /** Opens the tab or section of a path, then scrolls to its field and focuses it. */\n const goTo = (path: string) => {\n registry.reveal(path);\n // Wait for the tab or section to open before scrolling.\n requestAnimationFrame(() => {\n const element = root.current !== null ? findFieldElement(root.current, path) : null;\n if (element === null) {\n return;\n }\n const reduced = window.matchMedia?.('(prefers-reduced-motion: reduce)').matches;\n element.scrollIntoView?.({ block: 'center', behavior: reduced ? 'auto' : 'smooth' });\n const target =\n element.querySelector<HTMLElement>('[aria-invalid=\"true\"]') ??\n element.querySelector<HTMLElement>(FOCUSABLE);\n target?.focus({ preventScroll: true });\n });\n };\n\n const handle = (result: SubmitResult | null) => {\n if (result === null) {\n return;\n }\n if (result.status === 'invalid') {\n const first = Object.keys(orderErrors(order, result.errors))[0];\n if (first !== undefined) {\n goTo(first);\n }\n } else if (result.status === 'submitted') {\n if (showSavedToast) {\n toasts.show({ title: intl.formatMessage(messages.saved), tone: 'success' });\n }\n onSubmitted?.(result.result);\n } else if (result.status === 'error') {\n toasts.show(\n {\n title: intl.formatMessage(messages.saveFailed),\n tone: 'danger',\n action: {\n label: intl.formatMessage(messages.retry),\n onAction: () => void form.submit().then(handle),\n },\n },\n { timeout: 8000 },\n );\n }\n };\n\n const resolve = async (choice: 'reload' | 'overwrite') => {\n if (choice === 'reload' && state.conflict?.current === null && onReload !== undefined) {\n onReload();\n return;\n }\n setResolving(true);\n try {\n handle(await form.resolveConflict(choice));\n } finally {\n setResolving(false);\n }\n };\n\n const problem = state.status === 'error' ? getProblem(state.error) : null;\n const errorDetail =\n problem?.detail ??\n (problem === null && state.error instanceof Error ? state.error.message : undefined);\n const showSummary = state.status === 'invalid' && state.submitCount > 0;\n\n return (\n <FormContext value={form}>\n <RevealContext value={registry}>\n <FieldsProvider\n components={entries}\n locales={locales ?? definition?.locales ?? []}\n {...(relationSource !== undefined ? { relationSource } : {})}\n {...(uploadSource !== undefined ? { uploadSource } : {})}\n binding={FormBinding}\n >\n <form\n ref={root}\n id={id}\n aria-label={ariaLabel}\n noValidate\n className={cx(styles.form, className)}\n onSubmit={(event) => {\n event.preventDefault();\n void form.submit().then(handle);\n }}\n >\n {showSummary ? (\n <ErrorSummary\n errors={orderErrors(order, state.errors)}\n fields={resolved.fields}\n onSelect={goTo}\n {...(summaryClassName !== undefined\n ? { className: summaryClassName }\n : {})}\n />\n ) : null}\n {state.status === 'error' ? (\n <Alert\n tone=\"danger\"\n role=\"alert\"\n title={intl.formatMessage(messages.saveFailed)}\n actions={\n <Button size=\"sm\" type=\"submit\">\n {intl.formatMessage(messages.retry)}\n </Button>\n }\n >\n {errorDetail}\n </Alert>\n ) : null}\n <div\n className={cx(\n styles.layout,\n sidebar.length > 0 && styles.withSidebar,\n layoutClassName,\n )}\n >\n <div className={styles.stack}>\n <LayoutNodes nodes={main} fields={resolved.fieldsByName} />\n </div>\n {sidebar.length > 0 ? (\n <aside className={cx(styles.sidebar, sidebarClassName)}>\n {sidebar.map((node, index) => (\n <div key={index} className={styles.stack}>\n <LayoutNodes\n nodes={node.children ?? []}\n fields={resolved.fieldsByName}\n />\n </div>\n ))}\n </aside>\n ) : null}\n </div>\n {submitLabel !== null || actions !== undefined ? (\n <div className={cx(styles.footer, footerClassName)}>\n {state.dirty ? (\n <span className={styles.dirty} role=\"status\">\n {intl.formatMessage(messages.unsaved)}\n </span>\n ) : null}\n {actions}\n {submitLabel !== null ? (\n <Button\n type=\"submit\"\n variant=\"primary\"\n isPending={state.status === 'submitting'}\n >\n {submitLabel ?? intl.formatMessage(messages.save)}\n </Button>\n ) : null}\n </div>\n ) : null}\n </form>\n <ConflictDialog\n isOpen={state.status === 'conflict' || resolving}\n fields={resolved.fields}\n values={state.values}\n current={state.conflict?.current ?? null}\n isPending={resolving}\n onReload={() => void resolve('reload')}\n onOverwrite={() => void resolve('overwrite')}\n />\n </FieldsProvider>\n </RevealContext>\n </FormContext>\n );\n}\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@panneau/forms",
3
- "version": "5.0.0-alpha.2",
3
+ "version": "5.0.0-alpha.4",
4
4
  "description": "Forms of Panneau: fields in a layout, driven by the form state of @panneau/core, with errors, unsaved changes and version conflicts.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -41,13 +41,13 @@
41
41
  "react-intl": "^12.0.0"
42
42
  },
43
43
  "dependencies": {
44
- "@panneau/core": "5.0.0-alpha.2",
45
- "@panneau/fields": "5.0.0-alpha.2",
46
- "@panneau/intl": "5.0.0-alpha.2",
47
- "@panneau/protocol": "5.0.0-alpha.2",
48
- "@panneau/ui": "5.0.0-alpha.2"
44
+ "@panneau/core": "5.0.0-alpha.4",
45
+ "@panneau/fields": "5.0.0-alpha.4",
46
+ "@panneau/intl": "5.0.0-alpha.4",
47
+ "@panneau/protocol": "5.0.0-alpha.4",
48
+ "@panneau/ui": "5.0.0-alpha.4"
49
49
  },
50
50
  "devDependencies": {
51
- "@panneau/data": "5.0.0-alpha.2"
51
+ "@panneau/data": "5.0.0-alpha.4"
52
52
  }
53
53
  }