@byline/admin 6.2.0 → 6.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,8 @@
1
+ /**
2
+ * This Source Code is subject to the terms of the Mozilla Public
3
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
4
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
5
+ *
6
+ * Copyright (c) Infonomic Company Limited
7
+ */
8
+ export {};
@@ -0,0 +1,39 @@
1
+ /**
2
+ * This Source Code is subject to the terms of the Mozilla Public
3
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
4
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
5
+ *
6
+ * Copyright (c) Infonomic Company Limited
7
+ */
8
+ import { type ReactNode } from 'react';
9
+ import type { AdminResourceConfig, Field } from '@byline/core';
10
+ export interface FormLayoutProps {
11
+ fields: Field[];
12
+ adminConfig?: AdminResourceConfig;
13
+ /** Which regions to render, in order. Default: both. */
14
+ regions?: ReadonlyArray<'main' | 'sidebar'>;
15
+ /** 'split' = two columns (full page); 'stacked' = one column (embedded). */
16
+ variant?: 'split' | 'stacked';
17
+ collectionPath?: string;
18
+ /** Content locale passed through to every field renderer. */
19
+ activeLocale?: string;
20
+ /** Initial document, for field default values. */
21
+ initialData?: any;
22
+ /**
23
+ * Controlled active-tab state. Owned above the keyed `FormProvider` so tab
24
+ * choices survive the remount a locale change triggers — see `useFormTabs`.
25
+ */
26
+ activeTabBySet: Record<string, string>;
27
+ onTabChange: (tabSetName: string, tabName: string) => void;
28
+ /**
29
+ * Page-level sidebar widgets (path, tree placement, available locales),
30
+ * rendered in the same sidebar column as the sidebar schema fields. The
31
+ * embedded creation view passes nothing.
32
+ */
33
+ sidebarSlot?: ReactNode;
34
+ }
35
+ /**
36
+ * The layout walk: recursively dispatches each name in a region to the
37
+ * appropriate primitive renderer or to `<FieldRenderer>`.
38
+ */
39
+ export declare const FormLayout: ({ fields, adminConfig, regions, variant, activeLocale, initialData, activeTabBySet, onTabChange, sidebarSlot, }: FormLayoutProps) => ReactNode;
@@ -0,0 +1,100 @@
1
+ "use client";
2
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
3
+ import { Fragment as external_react_Fragment } from "react";
4
+ import clsx from "clsx";
5
+ import { sliceFieldAdmin } from "../fields/field-admin.js";
6
+ import { FieldRenderer } from "../fields/field-renderer.js";
7
+ import { AdminGroup } from "../presentation/group.js";
8
+ import { AdminRow } from "../presentation/row.js";
9
+ import { AdminTabs, tabPanelId, tabTriggerId } from "../presentation/tabs.js";
10
+ import form_renderer_module from "./form-renderer.module.js";
11
+ import { useFormLayout } from "./use-form-layout.js";
12
+ import { useFormTabs } from "./use-form-tabs.js";
13
+ const BOTH_REGIONS = [
14
+ 'main',
15
+ 'sidebar'
16
+ ];
17
+ const FormLayout = ({ fields, adminConfig, regions = BOTH_REGIONS, variant = 'split', activeLocale, initialData, activeTabBySet, onTabChange, sidebarSlot })=>{
18
+ const { fieldByName, tabSetByName, rowByName, groupByName, layout, fieldToTabPath } = useFormLayout(adminConfig, fields);
19
+ const tabs = useFormTabs(fieldToTabPath, activeTabBySet);
20
+ const renderField = (fieldName)=>{
21
+ const field = fieldByName.get(fieldName);
22
+ if (!field) return null;
23
+ return /*#__PURE__*/ jsx(FieldRenderer, {
24
+ field: field,
25
+ defaultValue: initialData?.fields?.[field.name],
26
+ contentLocale: activeLocale,
27
+ components: adminConfig?.fields?.[field.name]?.components,
28
+ editor: adminConfig?.fields?.[field.name]?.editor,
29
+ fieldAdmin: sliceFieldAdmin(adminConfig?.fields, field.name)
30
+ }, field.name);
31
+ };
32
+ const renderItem = (name)=>{
33
+ const tabSet = tabSetByName.get(name);
34
+ if (tabSet) return renderTabSet(tabSet);
35
+ const group = groupByName.get(name);
36
+ if (group) return renderGroup(group);
37
+ const row = rowByName.get(name);
38
+ if (row) return renderRow(row);
39
+ return renderField(name);
40
+ };
41
+ const renderRow = (row)=>/*#__PURE__*/ jsx(AdminRow, {
42
+ children: row.fields.map((name)=>renderField(name))
43
+ }, `row:${row.name}`);
44
+ const renderGroup = (group)=>/*#__PURE__*/ jsx(AdminGroup, {
45
+ label: group.label,
46
+ children: group.fields.map((name)=>renderItem(name))
47
+ }, `group:${group.name}`);
48
+ const renderTabSet = (set)=>{
49
+ const { visibleTabs, activeTabName, activeTab, errorCounts } = tabs.resolve(set);
50
+ const idBase = `tabset:${set.name}`;
51
+ return /*#__PURE__*/ jsxs("div", {
52
+ className: clsx('byline-form-tabset', form_renderer_module.tabset),
53
+ children: [
54
+ visibleTabs.length > 0 && /*#__PURE__*/ jsx(AdminTabs, {
55
+ idBase: idBase,
56
+ tabs: visibleTabs,
57
+ activeTab: activeTabName,
58
+ onChange: (tabName)=>onTabChange(set.name, tabName),
59
+ errorCounts: errorCounts,
60
+ className: clsx('byline-form-tabset-tabs', form_renderer_module["tabset-tabs"])
61
+ }),
62
+ activeTab && /*#__PURE__*/ jsx("div", {
63
+ role: "tabpanel",
64
+ id: tabPanelId(idBase, activeTab.name),
65
+ "aria-labelledby": tabTriggerId(idBase, activeTab.name),
66
+ className: clsx('byline-form-tabset-fields', form_renderer_module["tabset-fields"]),
67
+ children: activeTab.fields.map((name)=>renderItem(name))
68
+ })
69
+ ]
70
+ }, idBase);
71
+ };
72
+ const sidebarNames = layout.sidebar ?? [];
73
+ const sidebarContents = /*#__PURE__*/ jsxs(Fragment, {
74
+ children: [
75
+ sidebarSlot,
76
+ sidebarNames.map((name)=>renderItem(name))
77
+ ]
78
+ });
79
+ const renderRegion = (region)=>'main' === region ? /*#__PURE__*/ jsx("div", {
80
+ className: clsx('byline-form-content', form_renderer_module.content),
81
+ children: layout.main.map((name)=>renderItem(name))
82
+ }, "main") : /*#__PURE__*/ jsx("div", {
83
+ className: clsx('byline-form-sidebar', form_renderer_module.sidebar),
84
+ children: sidebarContents
85
+ }, "sidebar");
86
+ if ('stacked' === variant) return /*#__PURE__*/ jsx("div", {
87
+ className: clsx('byline-form-layout-stacked', form_renderer_module["layout-stacked"]),
88
+ children: /*#__PURE__*/ jsx("div", {
89
+ className: clsx('byline-form-content', form_renderer_module.content),
90
+ children: regions.map((region)=>/*#__PURE__*/ jsx(external_react_Fragment, {
91
+ children: 'main' === region ? layout.main.map((name)=>renderItem(name)) : sidebarContents
92
+ }, region))
93
+ })
94
+ });
95
+ return /*#__PURE__*/ jsx("div", {
96
+ className: clsx('byline-form-layout', form_renderer_module.layout),
97
+ children: regions.map((region)=>renderRegion(region))
98
+ });
99
+ };
100
+ export { FormLayout };
@@ -0,0 +1,110 @@
1
+ /**
2
+ * This Source Code is subject to the terms of the Mozilla Public
3
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
4
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
5
+ *
6
+ * Copyright (c) Infonomic Company Limited
7
+ */
8
+ /**
9
+ * Page-level chrome for the full-page document editor: the parts that frame a
10
+ * form rather than render it. The embedded creation view renders none of this.
11
+ *
12
+ * Each component takes only the props it needs — deliberately not a
13
+ * `FormRendererProps` pass-through, so what a region actually depends on stays
14
+ * visible at its call site.
15
+ */
16
+ import type { ReactNode, RefObject } from 'react';
17
+ import type { SlugifierFn, StructuralMutationReceipt, WorkflowStatus } from '@byline/core';
18
+ import { type DocumentActionsLocaleOption } from './document-actions.js';
19
+ import { type UseScheduledPublicationReturn } from './scheduled-publication-control.js';
20
+ import type { PublishedVersionInfo } from './form-renderer.js';
21
+ import type { StatusTransitions } from './status-transitions.js';
22
+ export interface FormHeadingRowProps {
23
+ heading: ReactNode;
24
+ /** Host-supplied controls rendered beside the heading (locale switcher, …). */
25
+ headerSlot?: ReactNode;
26
+ }
27
+ export declare const FormHeadingRow: ({ heading, headerSlot }: FormHeadingRowProps) => ReactNode;
28
+ export interface FormStatusBarProps {
29
+ /** Mutations blocked or a discard in flight. */
30
+ disabled: boolean;
31
+ initialData?: any;
32
+ workflowStatuses?: WorkflowStatus[];
33
+ publishedVersion?: PublishedVersionInfo | null;
34
+ scheduling: UseScheduledPublicationReturn;
35
+ hasChanges: boolean;
36
+ isUploading: boolean;
37
+ isSubmitting: boolean;
38
+ onCancel: () => void;
39
+ /** Status transitions, already computed for the current workflow position. */
40
+ transitions: StatusTransitions;
41
+ statusBusy: boolean;
42
+ onStatusBusyChange: (busy: boolean) => void;
43
+ onStatusChange?: (nextStatus: string) => Promise<void>;
44
+ onMutationError?: (error: unknown) => 'blocked' | 'committed' | null | void;
45
+ /** Read synchronously at click time, not at render time. */
46
+ isBlocked: () => boolean;
47
+ /** A guarded action was attempted while the form is dirty. */
48
+ onUnsavedChanges: () => void;
49
+ onUnpublish?: () => Promise<void>;
50
+ onDelete?: () => Promise<void>;
51
+ onDuplicate?: () => Promise<void>;
52
+ onCopyToLocale?: (args: {
53
+ targetLocale: string;
54
+ overwrite: boolean;
55
+ }) => Promise<void>;
56
+ onDeleteLocale?: (args: {
57
+ targetLocale: string;
58
+ }) => Promise<void>;
59
+ useAsTitle?: string;
60
+ contentLocale: string;
61
+ contentLocales?: ReadonlyArray<DocumentActionsLocaleOption>;
62
+ defaultLocale: string;
63
+ }
64
+ export declare const FormStatusBar: ({ disabled, initialData, workflowStatuses, publishedVersion, scheduling, hasChanges, isUploading, isSubmitting, onCancel, transitions, statusBusy, onStatusBusyChange, onStatusChange, onMutationError, isBlocked, onUnsavedChanges, onUnpublish, onDelete, onDuplicate, onCopyToLocale, onDeleteLocale, useAsTitle, contentLocale, contentLocales, defaultLocale, }: FormStatusBarProps) => ReactNode;
65
+ export interface FormConcurrencyNoticesProps {
66
+ /** Mutations blocked or a discard in flight. */
67
+ disabled: boolean;
68
+ mutationIssue?: 'stale' | 'reload' | 'lock' | 'unavailable' | 'committed' | null;
69
+ scheduledPublicationsNeedReconfirmation?: boolean;
70
+ scheduledPublicationsHref?: string;
71
+ /**
72
+ * Focus target when a mutation issue appears. The ref belongs to the form
73
+ * component, which restores focus after a save; this region only attaches it.
74
+ */
75
+ warningRef: RefObject<HTMLDivElement | null>;
76
+ discarding: boolean;
77
+ reloadFailed: boolean;
78
+ onDiscardRequested: () => void;
79
+ scheduling: UseScheduledPublicationReturn;
80
+ restoreWarnings?: string[];
81
+ }
82
+ export declare const FormConcurrencyNotices: ({ disabled, mutationIssue, scheduledPublicationsNeedReconfirmation, scheduledPublicationsHref, warningRef, discarding, reloadFailed, onDiscardRequested, scheduling, restoreWarnings, }: FormConcurrencyNoticesProps) => ReactNode;
83
+ export interface FormSidebarWidgetsProps {
84
+ /** Mutations blocked or a discard in flight. */
85
+ disabled: boolean;
86
+ mode: 'create' | 'edit';
87
+ initialData?: any;
88
+ collectionPath?: string;
89
+ defaultLocale: string;
90
+ contentLocale: string;
91
+ contentLocales?: ReadonlyArray<DocumentActionsLocaleOption>;
92
+ showPath: boolean;
93
+ useAsPath?: string;
94
+ lockPath?: boolean;
95
+ pathSlugifier?: SlugifierFn;
96
+ pathSourceLocked?: boolean;
97
+ tree?: boolean;
98
+ useAsTitle?: string;
99
+ advertiseLocales?: boolean;
100
+ observedRevision?: number;
101
+ onMutationError?: (error: unknown) => 'blocked' | 'committed' | null | void;
102
+ onTreeMutationCommitted?: (receipt: StructuralMutationReceipt) => void;
103
+ }
104
+ /**
105
+ * The page-level widgets in the editor's sidebar: path, tree placement and
106
+ * advertised locales. They are page concerns rather than schema fields, so they
107
+ * reach `FormLayout` through its `sidebarSlot` and the embedded creation view
108
+ * passes none of them.
109
+ */
110
+ export declare const FormSidebarWidgets: ({ disabled, mode, initialData, collectionPath, defaultLocale, contentLocale, contentLocales, showPath, useAsPath, lockPath, pathSlugifier, pathSourceLocked, tree, useAsTitle, advertiseLocales, observedRevision, onMutationError, onTreeMutationCommitted, }: FormSidebarWidgetsProps) => ReactNode;
@@ -0,0 +1,253 @@
1
+ "use client";
2
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
3
+ import { useTranslation } from "@byline/i18n/react";
4
+ import { Alert, Button, ComboButton, LoaderEllipsis } from "@byline/ui/react";
5
+ import clsx from "clsx";
6
+ import { AvailableLocalesWidget } from "./available-locales-widget.js";
7
+ import { DocumentActions } from "./document-actions.js";
8
+ import form_renderer_module from "./form-renderer.module.js";
9
+ import { FormStatusDisplay } from "./form-status-display.js";
10
+ import { PathWidget } from "./path-widget.js";
11
+ import { ScheduledPublicationCell, ScheduledPublicationNotice } from "./scheduled-publication-control.js";
12
+ import { TreePlacementWidget } from "./tree-placement-widget.js";
13
+ const FormHeadingRow = ({ heading, headerSlot })=>/*#__PURE__*/ jsxs("div", {
14
+ className: clsx('byline-form-heading-row', form_renderer_module["heading-row"]),
15
+ children: [
16
+ /*#__PURE__*/ jsx("h1", {
17
+ className: clsx('byline-form-heading', form_renderer_module.heading),
18
+ children: heading
19
+ }),
20
+ headerSlot
21
+ ]
22
+ });
23
+ const FormStatusBar = ({ disabled, initialData, workflowStatuses, publishedVersion, scheduling, hasChanges, isUploading, isSubmitting, onCancel, transitions, statusBusy, onStatusBusyChange, onStatusChange, onMutationError, isBlocked, onUnsavedChanges, onUnpublish, onDelete, onDuplicate, onCopyToLocale, onDeleteLocale, useAsTitle, contentLocale, contentLocales, defaultLocale })=>{
24
+ const { t } = useTranslation('byline-admin');
25
+ const { primaryStatus, secondaryStatuses, isTerminal } = transitions;
26
+ return /*#__PURE__*/ jsxs("div", {
27
+ className: clsx('byline-form-status-bar', form_renderer_module["status-bar"]),
28
+ children: [
29
+ /*#__PURE__*/ jsx("div", {
30
+ className: clsx('byline-form-status-details', form_renderer_module["status-details"]),
31
+ children: /*#__PURE__*/ jsx(FormStatusDisplay, {
32
+ disabled: disabled,
33
+ initialData: initialData,
34
+ workflowStatuses: workflowStatuses,
35
+ publishedVersion: publishedVersion,
36
+ onUnpublish: onUnpublish,
37
+ afterStatusCells: /*#__PURE__*/ jsx(ScheduledPublicationCell, {
38
+ state: scheduling.state,
39
+ timeZone: scheduling.timeZone
40
+ })
41
+ })
42
+ }),
43
+ /*#__PURE__*/ jsxs("div", {
44
+ className: clsx('byline-form-actions', form_renderer_module.actions),
45
+ children: [
46
+ /*#__PURE__*/ jsx(Button, {
47
+ className: clsx('byline-form-actions-button', form_renderer_module["actions-button"]),
48
+ size: "sm",
49
+ intent: "noeffect",
50
+ type: "button",
51
+ onClick: onCancel,
52
+ children: false === hasChanges ? t('common.actions.close') : t('common.actions.cancel')
53
+ }),
54
+ /*#__PURE__*/ jsx(Button, {
55
+ className: clsx('byline-form-actions-button', form_renderer_module["actions-button"]),
56
+ size: "sm",
57
+ type: "submit",
58
+ disabled: disabled || false === hasChanges || isUploading || isSubmitting,
59
+ "aria-label": isSubmitting ? t('common.actions.save') : void 0,
60
+ children: isUploading ? t('forms.actions.uploading') : /*#__PURE__*/ jsxs("span", {
61
+ className: clsx('byline-form-save-content', form_renderer_module["save-content"]),
62
+ children: [
63
+ /*#__PURE__*/ jsx("span", {
64
+ className: clsx('byline-form-save-label', form_renderer_module["save-label"], isSubmitting && form_renderer_module["save-label-hidden"]),
65
+ children: t('common.actions.save')
66
+ }),
67
+ isSubmitting ? /*#__PURE__*/ jsx("span", {
68
+ className: clsx('byline-form-save-loader', form_renderer_module["save-loader"]),
69
+ children: /*#__PURE__*/ jsx(LoaderEllipsis, {
70
+ size: 28,
71
+ "aria-hidden": "true"
72
+ })
73
+ }) : null
74
+ ]
75
+ })
76
+ }),
77
+ primaryStatus && onStatusChange && /*#__PURE__*/ jsx("div", {
78
+ className: clsx('byline-form-actions-status-wrap', form_renderer_module["actions-status-wrap"]),
79
+ children: /*#__PURE__*/ jsx(ComboButton, {
80
+ buttonClassName: clsx('byline-form-actions-combo-button', form_renderer_module["actions-combo-button"]),
81
+ triggerClassName: clsx('byline-form-actions-combo-trigger', form_renderer_module["actions-combo-trigger"]),
82
+ options: secondaryStatuses.map((s)=>({
83
+ label: isTerminal ? t('forms.actions.revertTo', {
84
+ label: s.label ?? s.name
85
+ }) : s.verb ?? s.label ?? s.name,
86
+ value: s.name
87
+ })),
88
+ sideOffset: 5,
89
+ size: "sm",
90
+ type: "button",
91
+ intent: isTerminal ? 'info' : 'success',
92
+ disabled: disabled || statusBusy,
93
+ onOptionSelect: async (value)=>{
94
+ if (isBlocked()) return;
95
+ if (hasChanges) return void onUnsavedChanges();
96
+ onStatusBusyChange(true);
97
+ try {
98
+ await onStatusChange(value);
99
+ } catch (error) {
100
+ onMutationError?.(error);
101
+ } finally{
102
+ onStatusBusyChange(false);
103
+ }
104
+ },
105
+ onButtonClick: isTerminal ? void 0 : async ()=>{
106
+ if (isBlocked()) return;
107
+ if (hasChanges) return void onUnsavedChanges();
108
+ onStatusBusyChange(true);
109
+ try {
110
+ await onStatusChange(primaryStatus.name);
111
+ } catch (error) {
112
+ onMutationError?.(error);
113
+ } finally{
114
+ onStatusBusyChange(false);
115
+ }
116
+ },
117
+ children: statusBusy ? '...' : isTerminal ? primaryStatus.label ?? primaryStatus.name : primaryStatus.verb ?? primaryStatus.label ?? primaryStatus.name
118
+ })
119
+ }),
120
+ /*#__PURE__*/ jsx(DocumentActions, {
121
+ disabled: disabled,
122
+ publishedVersion: publishedVersion,
123
+ onUnpublish: onUnpublish,
124
+ onDelete: onDelete,
125
+ onDuplicate: onDuplicate,
126
+ sourceTitle: null != useAsTitle && null != initialData ? initialData[useAsTitle] : null,
127
+ onCopyToLocale: onCopyToLocale,
128
+ sourceLocale: contentLocale,
129
+ contentLocales: contentLocales,
130
+ hasUnsavedChanges: hasChanges,
131
+ onUnsavedChanges: ()=>onUnsavedChanges(),
132
+ onDeleteLocale: onDeleteLocale,
133
+ defaultLocale: defaultLocale,
134
+ availableLocales: initialData?._availableVersionLocales,
135
+ scheduledPublicationState: scheduling.state,
136
+ onSchedulePublication: scheduling.openSchedule,
137
+ onConfirmScheduledPublication: scheduling.confirm,
138
+ onCancelScheduledPublication: scheduling.cancel
139
+ })
140
+ ]
141
+ })
142
+ ]
143
+ });
144
+ };
145
+ const FormConcurrencyNotices = ({ disabled, mutationIssue, scheduledPublicationsNeedReconfirmation, scheduledPublicationsHref, warningRef, discarding, reloadFailed, onDiscardRequested, scheduling, restoreWarnings })=>{
146
+ const { t } = useTranslation('byline-admin');
147
+ return /*#__PURE__*/ jsxs(Fragment, {
148
+ children: [
149
+ (mutationIssue || scheduledPublicationsNeedReconfirmation) && /*#__PURE__*/ jsxs("div", {
150
+ ref: warningRef,
151
+ tabIndex: -1,
152
+ role: "alert",
153
+ "aria-live": "assertive",
154
+ className: clsx('byline-document-concurrency', form_renderer_module.concurrency),
155
+ children: [
156
+ mutationIssue && /*#__PURE__*/ jsxs(Alert, {
157
+ intent: "warning",
158
+ icon: true,
159
+ close: false,
160
+ title: t(`documentConcurrency.${mutationIssue}Title`),
161
+ children: [
162
+ /*#__PURE__*/ jsx("p", {
163
+ children: t(`documentConcurrency.${mutationIssue}`)
164
+ }),
165
+ 'committed' !== mutationIssue && /*#__PURE__*/ jsx(Button, {
166
+ type: "button",
167
+ disabled: discarding,
168
+ onClick: onDiscardRequested,
169
+ children: t('documentConcurrency.reloadAction')
170
+ }),
171
+ reloadFailed && /*#__PURE__*/ jsx("p", {
172
+ children: t('documentConcurrency.reloadFailed')
173
+ })
174
+ ]
175
+ }),
176
+ scheduledPublicationsNeedReconfirmation && /*#__PURE__*/ jsxs(Alert, {
177
+ intent: "warning",
178
+ icon: true,
179
+ close: false,
180
+ title: t('documentConcurrency.schedulesTitle'),
181
+ children: [
182
+ /*#__PURE__*/ jsx("p", {
183
+ children: t('documentConcurrency.schedules')
184
+ }),
185
+ scheduledPublicationsHref && /*#__PURE__*/ jsx("a", {
186
+ href: scheduledPublicationsHref,
187
+ children: t('documentConcurrency.reviewSchedules')
188
+ })
189
+ ]
190
+ })
191
+ ]
192
+ }),
193
+ /*#__PURE__*/ jsx(ScheduledPublicationNotice, {
194
+ state: scheduling.state,
195
+ timeZone: scheduling.timeZone,
196
+ busy: disabled || scheduling.busy,
197
+ onConfirm: scheduling.confirm,
198
+ onReschedule: scheduling.openSchedule,
199
+ onCancel: scheduling.cancel
200
+ }),
201
+ scheduling.modal,
202
+ restoreWarnings && restoreWarnings.length > 0 && /*#__PURE__*/ jsxs(Alert, {
203
+ className: "m-0 mt-4",
204
+ intent: "warning",
205
+ icon: true,
206
+ close: false,
207
+ title: t('forms.restoreWarnings.title'),
208
+ children: [
209
+ /*#__PURE__*/ jsx("p", {
210
+ children: t('forms.restoreWarnings.body', {
211
+ count: restoreWarnings.length
212
+ })
213
+ }),
214
+ /*#__PURE__*/ jsx("ul", {
215
+ children: restoreWarnings.map((w)=>/*#__PURE__*/ jsx("li", {
216
+ children: w
217
+ }, w))
218
+ })
219
+ ]
220
+ })
221
+ ]
222
+ });
223
+ };
224
+ const FormSidebarWidgets = ({ disabled, mode, initialData, collectionPath, defaultLocale, contentLocale, contentLocales, showPath, useAsPath, lockPath, pathSlugifier, pathSourceLocked, tree, useAsTitle, advertiseLocales, observedRevision, onMutationError, onTreeMutationCommitted })=>/*#__PURE__*/ jsxs(Fragment, {
225
+ children: [
226
+ showPath && (useAsPath || true === lockPath || 'string' == typeof initialData?.path && initialData.path.length > 0) && /*#__PURE__*/ jsx(PathWidget, {
227
+ disabled: disabled,
228
+ useAsPath: useAsPath,
229
+ collectionPath: collectionPath ?? '',
230
+ defaultLocale: defaultLocale,
231
+ activeLocale: contentLocale,
232
+ mode: mode,
233
+ slugifier: pathSlugifier,
234
+ sourceLocked: pathSourceLocked,
235
+ lockPath: lockPath
236
+ }),
237
+ tree && 'edit' === mode && 'string' == typeof initialData?.id && /*#__PURE__*/ jsx(TreePlacementWidget, {
238
+ disabled: disabled,
239
+ onMutationError: onMutationError,
240
+ onCommitted: onTreeMutationCommitted,
241
+ expectedRevision: observedRevision ?? initialData.revision,
242
+ collectionPath: collectionPath ?? '',
243
+ documentId: initialData.id,
244
+ useAsTitle: useAsTitle
245
+ }),
246
+ advertiseLocales && /*#__PURE__*/ jsx(AvailableLocalesWidget, {
247
+ disabled: disabled,
248
+ contentLocales: contentLocales ?? [],
249
+ availableVersionLocales: initialData?._availableVersionLocales ?? []
250
+ })
251
+ ]
252
+ });
253
+ export { FormConcurrencyNotices, FormHeadingRow, FormSidebarWidgets, FormStatusBar };
@@ -7,10 +7,10 @@
7
7
  */
8
8
  import { type ReactNode } from 'react';
9
9
  import type { AdminResourceConfig, Field, WorkflowStatus } from '@byline/core';
10
- import type { DocumentPatch } from '@byline/core/patches';
11
- import { type DocumentActionsLocaleOption } from './document-actions.js';
12
10
  import { type ScheduledPublicationInfo, type SchedulePublicationInput } from './scheduled-publication-control.js';
11
+ import type { DocumentActionsLocaleOption } from './document-actions.js';
13
12
  import type { UseNavigationGuard } from './navigation-guard.js';
13
+ export type { SystemFieldsSubmitPayload } from './use-form-submission.js';
14
14
  /** Metadata about a previously published version that is still live. */
15
15
  export interface PublishedVersionInfo {
16
16
  id: string;
@@ -19,22 +19,6 @@ export interface PublishedVersionInfo {
19
19
  createdAt: string | Date;
20
20
  updatedAt: string | Date;
21
21
  }
22
- /**
23
- * Payload emitted by the form on Save. Carries the content (field data +
24
- * patches) alongside the document-grain system fields (path / advertised
25
- * locales) and per-bucket dirty flags so the host can route each piece to the
26
- * right write path — versioned for content, immediate/non-versioned for the
27
- * system fields. See docs/08-internationalization/index.md.
28
- */
29
- export interface SystemFieldsSubmitPayload {
30
- data: any;
31
- patches: DocumentPatch[];
32
- contentDirty: boolean;
33
- pathDirty: boolean;
34
- systemPath?: string | null;
35
- availableLocalesDirty: boolean;
36
- systemAvailableLocales?: string[];
37
- }
38
22
  /** Props shared by both the public FormRenderer and its internal FormContent component. */
39
23
  export interface FormRendererProps {
40
24
  mutationIssue?: 'stale' | 'reload' | 'lock' | 'unavailable' | 'committed' | null;