@byline/admin 4.19.0 → 5.0.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/dist/fields/field-services-types.d.ts +5 -2
- package/dist/forms/available-locales-widget.d.ts +2 -1
- package/dist/forms/available-locales-widget.js +4 -2
- package/dist/forms/document-actions.d.ts +2 -1
- package/dist/forms/document-actions.js +32 -14
- package/dist/forms/form-renderer.d.ts +9 -0
- package/dist/forms/form-renderer.js +98 -8
- package/dist/forms/form-renderer.module.js +1 -0
- package/dist/forms/form-renderer_module.css +12 -0
- package/dist/forms/form-status-display.d.ts +2 -1
- package/dist/forms/form-status-display.js +5 -2
- package/dist/forms/path-widget.d.ts +2 -1
- package/dist/forms/path-widget.js +7 -2
- package/dist/forms/scheduled-publication-control.d.ts +2 -1
- package/dist/forms/scheduled-publication-control.js +12 -8
- package/dist/forms/tree-placement-widget.d.ts +5 -1
- package/dist/forms/tree-placement-widget.js +14 -7
- package/package.json +7 -7
- package/src/fields/field-services-types.ts +10 -2
- package/src/forms/available-locales-widget.tsx +5 -2
- package/src/forms/document-actions.tsx +55 -20
- package/src/forms/form-renderer-submit.test.tsx +131 -2
- package/src/forms/form-renderer.module.css +20 -0
- package/src/forms/form-renderer.tsx +122 -7
- package/src/forms/form-status-display.tsx +6 -1
- package/src/forms/path-widget.tsx +8 -3
- package/src/forms/scheduled-publication-control.tsx +16 -7
- package/src/forms/tree-placement-widget.tsx +34 -7
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { SlugifierFn } from '@byline/core';
|
|
2
2
|
export interface PathWidgetProps {
|
|
3
|
+
disabled?: boolean;
|
|
3
4
|
/** The collection's `useAsPath` source field name, when configured. */
|
|
4
5
|
useAsPath: string | undefined;
|
|
5
6
|
/** Collection path, forwarded to the slugifier as context. */
|
|
@@ -51,4 +52,4 @@ export interface PathWidgetProps {
|
|
|
51
52
|
* Stable override handles: `.byline-form-path`, `.byline-form-path-header`,
|
|
52
53
|
* `.byline-form-path-regenerate`.
|
|
53
54
|
*/
|
|
54
|
-
export declare const PathWidget: ({ useAsPath, collectionPath, defaultLocale, activeLocale, mode, slugifier, sourceLocked, }: PathWidgetProps) => import("react").JSX.Element;
|
|
55
|
+
export declare const PathWidget: ({ useAsPath, collectionPath, defaultLocale, activeLocale, mode, slugifier, sourceLocked, disabled, }: PathWidgetProps) => import("react").JSX.Element;
|
|
@@ -12,7 +12,7 @@ function coerceToString(value) {
|
|
|
12
12
|
if (value instanceof Date) return value.toISOString();
|
|
13
13
|
return String(value);
|
|
14
14
|
}
|
|
15
|
-
const PathWidget = ({ useAsPath, collectionPath, defaultLocale, activeLocale, mode, slugifier, sourceLocked = false })=>{
|
|
15
|
+
const PathWidget = ({ useAsPath, collectionPath, defaultLocale, activeLocale, mode, slugifier, sourceLocked = false, disabled = false })=>{
|
|
16
16
|
const { setSystemPath } = useFormContext();
|
|
17
17
|
const { t } = useTranslation('byline-admin');
|
|
18
18
|
const systemPath = useSystemPath();
|
|
@@ -37,13 +37,16 @@ const PathWidget = ({ useAsPath, collectionPath, defaultLocale, activeLocale, mo
|
|
|
37
37
|
]);
|
|
38
38
|
const inputValue = systemPath ?? '';
|
|
39
39
|
const handleChange = useCallback((next)=>{
|
|
40
|
+
if (disabled) return;
|
|
40
41
|
setSystemPath(0 === next.length ? null : next);
|
|
41
42
|
}, [
|
|
43
|
+
disabled,
|
|
42
44
|
setSystemPath
|
|
43
45
|
]);
|
|
44
46
|
const handleRegenerate = useCallback(()=>{
|
|
45
|
-
if (livePreview.length > 0) setSystemPath(livePreview);
|
|
47
|
+
if (!disabled && livePreview.length > 0) setSystemPath(livePreview);
|
|
46
48
|
}, [
|
|
49
|
+
disabled,
|
|
47
50
|
livePreview,
|
|
48
51
|
setSystemPath
|
|
49
52
|
]);
|
|
@@ -90,6 +93,7 @@ const PathWidget = ({ useAsPath, collectionPath, defaultLocale, activeLocale, mo
|
|
|
90
93
|
type: "button",
|
|
91
94
|
onClick: handleRegenerate,
|
|
92
95
|
className: clsx('byline-form-path-regenerate', path_widget_module.regenerate),
|
|
96
|
+
disabled: disabled,
|
|
93
97
|
"aria-label": t('pathWidget.regenerateAriaLabel', {
|
|
94
98
|
field: useAsPath
|
|
95
99
|
}),
|
|
@@ -100,6 +104,7 @@ const PathWidget = ({ useAsPath, collectionPath, defaultLocale, activeLocale, mo
|
|
|
100
104
|
]
|
|
101
105
|
}),
|
|
102
106
|
/*#__PURE__*/ jsx(Input, {
|
|
107
|
+
disabled: disabled,
|
|
103
108
|
id: "system-path",
|
|
104
109
|
name: "__systemPath__",
|
|
105
110
|
value: inputValue,
|
|
@@ -5,6 +5,7 @@ export interface SchedulePublicationInput {
|
|
|
5
5
|
publishAt: string;
|
|
6
6
|
}
|
|
7
7
|
export interface UseScheduledPublicationArgs {
|
|
8
|
+
disabled?: boolean;
|
|
8
9
|
schedule: ScheduledPublicationInfo | null;
|
|
9
10
|
onSchedule?: (input: SchedulePublicationInput) => Promise<void>;
|
|
10
11
|
onConfirm?: () => Promise<void>;
|
|
@@ -24,7 +25,7 @@ export interface UseScheduledPublicationReturn {
|
|
|
24
25
|
/** The schedule / reschedule modal. Render once, anywhere in the form. */
|
|
25
26
|
modal: React.ReactNode;
|
|
26
27
|
}
|
|
27
|
-
export declare function useScheduledPublication({ schedule, onSchedule, onConfirm, onCancel, hasUnsavedChanges, onUnsavedChanges, }: UseScheduledPublicationArgs): UseScheduledPublicationReturn;
|
|
28
|
+
export declare function useScheduledPublication({ disabled, schedule, onSchedule, onConfirm, onCancel, hasUnsavedChanges, onUnsavedChanges, }: UseScheduledPublicationArgs): UseScheduledPublicationReturn;
|
|
28
29
|
/**
|
|
29
30
|
* One metadata cell for the form's status bar, matching the Status /
|
|
30
31
|
* Last modified / Created cells in scale, sitting immediately after the status
|
|
@@ -28,7 +28,7 @@ function seedScheduleInstant(schedule) {
|
|
|
28
28
|
seed.setSeconds(0, 0);
|
|
29
29
|
return seed;
|
|
30
30
|
}
|
|
31
|
-
function useScheduledPublication({ schedule, onSchedule, onConfirm, onCancel, hasUnsavedChanges, onUnsavedChanges }) {
|
|
31
|
+
function useScheduledPublication({ disabled = false, schedule, onSchedule, onConfirm, onCancel, hasUnsavedChanges, onUnsavedChanges }) {
|
|
32
32
|
const timeZone = useMemo(browserTimeZone, []);
|
|
33
33
|
const [now, setNow] = useState(()=>Date.now());
|
|
34
34
|
const [showSchedule, setShowSchedule] = useState(false);
|
|
@@ -55,52 +55,56 @@ function useScheduledPublication({ schedule, onSchedule, onConfirm, onCancel, ha
|
|
|
55
55
|
now
|
|
56
56
|
]);
|
|
57
57
|
const openSchedule = useCallback(()=>{
|
|
58
|
+
if (disabled) return;
|
|
58
59
|
if (hasUnsavedChanges) return void onUnsavedChanges();
|
|
59
60
|
setShowSchedule(true);
|
|
60
61
|
}, [
|
|
62
|
+
disabled,
|
|
61
63
|
hasUnsavedChanges,
|
|
62
64
|
onUnsavedChanges
|
|
63
65
|
]);
|
|
64
66
|
const confirm = useCallback(async ()=>{
|
|
65
|
-
if (null == onConfirm) return;
|
|
67
|
+
if (disabled || null == onConfirm) return;
|
|
66
68
|
if (hasUnsavedChanges) return void onUnsavedChanges();
|
|
67
69
|
setBusy(true);
|
|
68
70
|
try {
|
|
69
71
|
await onConfirm();
|
|
70
|
-
} finally{
|
|
72
|
+
} catch {} finally{
|
|
71
73
|
setBusy(false);
|
|
72
74
|
}
|
|
73
75
|
}, [
|
|
76
|
+
disabled,
|
|
74
77
|
onConfirm,
|
|
75
78
|
hasUnsavedChanges,
|
|
76
79
|
onUnsavedChanges
|
|
77
80
|
]);
|
|
78
81
|
const cancel = useCallback(async ()=>{
|
|
79
|
-
if (null == onCancel) return;
|
|
82
|
+
if (disabled || null == onCancel) return;
|
|
80
83
|
setBusy(true);
|
|
81
84
|
try {
|
|
82
85
|
await onCancel();
|
|
83
|
-
} finally{
|
|
86
|
+
} catch {} finally{
|
|
84
87
|
setBusy(false);
|
|
85
88
|
}
|
|
86
89
|
}, [
|
|
90
|
+
disabled,
|
|
87
91
|
onCancel
|
|
88
92
|
]);
|
|
89
93
|
const modal = showSchedule ? /*#__PURE__*/ jsx(ScheduleModal, {
|
|
90
94
|
schedule: schedule,
|
|
91
95
|
timeZone: timeZone,
|
|
92
96
|
onSubmit: async (input)=>{
|
|
93
|
-
if (null == onSchedule) return;
|
|
97
|
+
if (disabled || null == onSchedule) return;
|
|
94
98
|
setBusy(true);
|
|
95
99
|
try {
|
|
96
100
|
await onSchedule(input);
|
|
97
101
|
setShowSchedule(false);
|
|
98
|
-
} finally{
|
|
102
|
+
} catch {} finally{
|
|
99
103
|
setBusy(false);
|
|
100
104
|
}
|
|
101
105
|
},
|
|
102
106
|
onDismiss: ()=>setShowSchedule(false),
|
|
103
|
-
busy: busy
|
|
107
|
+
busy: disabled || busy
|
|
104
108
|
}) : null;
|
|
105
109
|
return {
|
|
106
110
|
state,
|
|
@@ -1,4 +1,8 @@
|
|
|
1
1
|
export interface TreePlacementWidgetProps {
|
|
2
|
+
disabled?: boolean;
|
|
3
|
+
onMutationError?: (error: unknown) => 'blocked' | 'committed' | null | void;
|
|
4
|
+
onCommitted?: (receipt: import('@byline/core').StructuralMutationReceipt) => void;
|
|
5
|
+
expectedRevision: number;
|
|
2
6
|
/** The collection path (`tree: true`). */
|
|
3
7
|
collectionPath: string;
|
|
4
8
|
/** The logical id of the document being edited. */
|
|
@@ -20,4 +24,4 @@ export interface TreePlacementWidgetProps {
|
|
|
20
24
|
* Renders only in edit mode (placement needs a persisted document) and only when
|
|
21
25
|
* the host wires the tree services. Stable override handle: `.byline-form-tree`.
|
|
22
26
|
*/
|
|
23
|
-
export declare const TreePlacementWidget: ({ collectionPath, documentId, useAsTitle, }: TreePlacementWidgetProps) => import("react").JSX.Element | null;
|
|
27
|
+
export declare const TreePlacementWidget: ({ collectionPath, disabled, onMutationError, onCommitted, expectedRevision, documentId, useAsTitle, }: TreePlacementWidgetProps) => import("react").JSX.Element | null;
|
|
@@ -8,7 +8,7 @@ import clsx from "clsx";
|
|
|
8
8
|
import { useBylineFieldServices } from "../fields/field-services-context.js";
|
|
9
9
|
import { RelationPicker } from "../fields/relation/relation-picker.js";
|
|
10
10
|
import tree_placement_widget_module from "./tree-placement-widget.module.js";
|
|
11
|
-
const TreePlacementWidget = ({ collectionPath, documentId, useAsTitle })=>{
|
|
11
|
+
const TreePlacementWidget = ({ collectionPath, disabled = false, onMutationError, onCommitted, expectedRevision, documentId, useAsTitle })=>{
|
|
12
12
|
const { t } = useTranslation('byline-admin');
|
|
13
13
|
const { getTreeAncestors, getTreeParent, placeTreeNode } = useBylineFieldServices();
|
|
14
14
|
const definition = getCollectionDefinition(collectionPath);
|
|
@@ -60,7 +60,7 @@ const TreePlacementWidget = ({ collectionPath, documentId, useAsTitle })=>{
|
|
|
60
60
|
t
|
|
61
61
|
]);
|
|
62
62
|
const place = useCallback(async (parentDocumentId, optimistic)=>{
|
|
63
|
-
if (null == placeTreeNode || busy) return;
|
|
63
|
+
if (disabled || null == placeTreeNode || busy) return;
|
|
64
64
|
const previousParent = parent;
|
|
65
65
|
const previousPlaced = placed;
|
|
66
66
|
setError(null);
|
|
@@ -68,12 +68,15 @@ const TreePlacementWidget = ({ collectionPath, documentId, useAsTitle })=>{
|
|
|
68
68
|
setParent(optimistic);
|
|
69
69
|
setPlaced(true);
|
|
70
70
|
try {
|
|
71
|
-
await placeTreeNode({
|
|
71
|
+
const result = await placeTreeNode({
|
|
72
|
+
expectedRevision,
|
|
72
73
|
collection: collectionPath,
|
|
73
74
|
documentId,
|
|
74
75
|
parentDocumentId
|
|
75
76
|
});
|
|
76
|
-
|
|
77
|
+
onCommitted?.(result);
|
|
78
|
+
} catch (error) {
|
|
79
|
+
if (onMutationError?.(error) === 'committed') return;
|
|
77
80
|
setParent(previousParent);
|
|
78
81
|
setPlaced(previousPlaced);
|
|
79
82
|
setError(t('treeWidget.error'));
|
|
@@ -81,6 +84,10 @@ const TreePlacementWidget = ({ collectionPath, documentId, useAsTitle })=>{
|
|
|
81
84
|
setBusy(false);
|
|
82
85
|
}
|
|
83
86
|
}, [
|
|
87
|
+
disabled,
|
|
88
|
+
onMutationError,
|
|
89
|
+
onCommitted,
|
|
90
|
+
expectedRevision,
|
|
84
91
|
placeTreeNode,
|
|
85
92
|
busy,
|
|
86
93
|
parent,
|
|
@@ -136,20 +143,20 @@ const TreePlacementWidget = ({ collectionPath, documentId, useAsTitle })=>{
|
|
|
136
143
|
size: "xs",
|
|
137
144
|
variant: "outlined",
|
|
138
145
|
intent: "noeffect",
|
|
139
|
-
disabled: loading || busy,
|
|
146
|
+
disabled: disabled || loading || busy,
|
|
140
147
|
onClick: ()=>setPickerOpen(true),
|
|
141
148
|
children: t('treeWidget.choose')
|
|
142
149
|
}),
|
|
143
150
|
placed ? null != parent && /*#__PURE__*/ jsx("button", {
|
|
144
151
|
type: "button",
|
|
145
152
|
className: clsx('byline-form-tree-link', tree_placement_widget_module.link),
|
|
146
|
-
disabled: busy,
|
|
153
|
+
disabled: disabled || busy,
|
|
147
154
|
onClick: ()=>place(null, null),
|
|
148
155
|
children: t('treeWidget.makeRoot')
|
|
149
156
|
}) : /*#__PURE__*/ jsx("button", {
|
|
150
157
|
type: "button",
|
|
151
158
|
className: clsx('byline-form-tree-link', tree_placement_widget_module.link),
|
|
152
|
-
disabled: loading || busy,
|
|
159
|
+
disabled: disabled || loading || busy,
|
|
153
160
|
onClick: ()=>place(null, null),
|
|
154
161
|
children: t('treeWidget.addToTree')
|
|
155
162
|
})
|
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "@byline/admin",
|
|
3
3
|
"private": false,
|
|
4
4
|
"license": "MPL-2.0",
|
|
5
|
-
"version": "
|
|
5
|
+
"version": "5.0.0",
|
|
6
6
|
"engines": {
|
|
7
7
|
"node": ">=20.9.0"
|
|
8
8
|
},
|
|
@@ -183,12 +183,12 @@
|
|
|
183
183
|
"react-diff-viewer-continued": "^4.4.0",
|
|
184
184
|
"uuid": "^14.0.2",
|
|
185
185
|
"zod": "^4.4.3",
|
|
186
|
-
"@byline/
|
|
187
|
-
"@byline/analytics
|
|
188
|
-
"@byline/
|
|
189
|
-
"@byline/ui": "
|
|
190
|
-
"@byline/
|
|
191
|
-
"@byline/
|
|
186
|
+
"@byline/core": "5.0.0",
|
|
187
|
+
"@byline/analytics": "5.0.0",
|
|
188
|
+
"@byline/i18n": "5.0.0",
|
|
189
|
+
"@byline/ui": "5.0.0",
|
|
190
|
+
"@byline/auth": "5.0.0",
|
|
191
|
+
"@byline/analytics-agent": "5.0.0"
|
|
192
192
|
},
|
|
193
193
|
"peerDependencies": {
|
|
194
194
|
"react": "^19.0.0",
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { StructuralMutationReceipt } from '@byline/core'
|
|
1
2
|
/**
|
|
2
3
|
* This Source Code is subject to the terms of the Mozilla Public
|
|
3
4
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
@@ -72,6 +73,7 @@ export interface TreeAncestor {
|
|
|
72
73
|
}
|
|
73
74
|
|
|
74
75
|
export interface PlaceTreeNodeInput {
|
|
76
|
+
expectedRevision: number
|
|
75
77
|
collection: string
|
|
76
78
|
documentId: string
|
|
77
79
|
/** The new parent; `null` makes the document a root node. */
|
|
@@ -82,10 +84,16 @@ export interface PlaceTreeNodeInput {
|
|
|
82
84
|
}
|
|
83
85
|
|
|
84
86
|
/** Place / move a document within its collection's tree. */
|
|
85
|
-
export type PlaceTreeNodeFn = (
|
|
87
|
+
export type PlaceTreeNodeFn = (
|
|
88
|
+
input: PlaceTreeNodeInput
|
|
89
|
+
) => Promise<{ orderKey: string } & StructuralMutationReceipt>
|
|
86
90
|
|
|
87
91
|
/** Remove a document from the tree (back to the unplaced state). */
|
|
88
|
-
export type RemoveFromTreeFn = (input: {
|
|
92
|
+
export type RemoveFromTreeFn = (input: {
|
|
93
|
+
collection: string
|
|
94
|
+
documentId: string
|
|
95
|
+
expectedRevision: number
|
|
96
|
+
}) => Promise<StructuralMutationReceipt>
|
|
89
97
|
|
|
90
98
|
/** Resolve a document's ancestor chain, root-first, hydrated with titles. */
|
|
91
99
|
export type GetTreeAncestorsFn = (input: {
|
|
@@ -27,6 +27,7 @@ export interface AvailableLocalesWidgetLocale {
|
|
|
27
27
|
}
|
|
28
28
|
|
|
29
29
|
export interface AvailableLocalesWidgetProps {
|
|
30
|
+
disabled?: boolean
|
|
30
31
|
/** All configured content locales — one checkbox each (code + display label). */
|
|
31
32
|
contentLocales: ReadonlyArray<AvailableLocalesWidgetLocale>
|
|
32
33
|
/**
|
|
@@ -50,6 +51,7 @@ export interface AvailableLocalesWidgetProps {
|
|
|
50
51
|
* `.byline-form-available-locales-list`.
|
|
51
52
|
*/
|
|
52
53
|
export const AvailableLocalesWidget = ({
|
|
54
|
+
disabled: mutationsBlocked = false,
|
|
53
55
|
contentLocales,
|
|
54
56
|
availableVersionLocales,
|
|
55
57
|
}: AvailableLocalesWidgetProps) => {
|
|
@@ -62,6 +64,7 @@ export const AvailableLocalesWidget = ({
|
|
|
62
64
|
|
|
63
65
|
const toggle = useCallback(
|
|
64
66
|
(code: string, checked: boolean) => {
|
|
67
|
+
if (mutationsBlocked) return
|
|
65
68
|
const next = new Set(advertised)
|
|
66
69
|
if (checked) {
|
|
67
70
|
next.add(code)
|
|
@@ -70,7 +73,7 @@ export const AvailableLocalesWidget = ({
|
|
|
70
73
|
}
|
|
71
74
|
setSystemAvailableLocales([...next])
|
|
72
75
|
},
|
|
73
|
-
[advertised, setSystemAvailableLocales]
|
|
76
|
+
[mutationsBlocked, advertised, setSystemAvailableLocales]
|
|
74
77
|
)
|
|
75
78
|
|
|
76
79
|
if (contentLocales.length === 0) {
|
|
@@ -102,7 +105,7 @@ export const AvailableLocalesWidget = ({
|
|
|
102
105
|
label={label}
|
|
103
106
|
intent={intent}
|
|
104
107
|
checked={checked}
|
|
105
|
-
disabled={disabled}
|
|
108
|
+
disabled={mutationsBlocked || disabled}
|
|
106
109
|
onCheckedChange={(value) => toggle(code, value === true)}
|
|
107
110
|
/>
|
|
108
111
|
)
|
|
@@ -41,6 +41,7 @@ export interface DocumentActionsLocaleOption {
|
|
|
41
41
|
}
|
|
42
42
|
|
|
43
43
|
export function DocumentActions({
|
|
44
|
+
disabled = false,
|
|
44
45
|
publishedVersion,
|
|
45
46
|
onUnpublish,
|
|
46
47
|
onDelete,
|
|
@@ -59,6 +60,7 @@ export function DocumentActions({
|
|
|
59
60
|
onConfirmScheduledPublication,
|
|
60
61
|
onCancelScheduledPublication,
|
|
61
62
|
}: {
|
|
63
|
+
disabled?: boolean
|
|
62
64
|
publishedVersion?: PublishedVersionInfo | null
|
|
63
65
|
onUnpublish?: () => Promise<void>
|
|
64
66
|
onDelete?: () => Promise<void>
|
|
@@ -221,24 +223,29 @@ export function DocumentActions({
|
|
|
221
223
|
onDelete != null
|
|
222
224
|
|
|
223
225
|
const handleOnDelete = () => {
|
|
226
|
+
if (disabled) return
|
|
224
227
|
setShowDeleteConfirm(false)
|
|
225
228
|
if (onDelete) {
|
|
226
|
-
onDelete()
|
|
229
|
+
void onDelete().catch(() => {})
|
|
227
230
|
}
|
|
228
231
|
}
|
|
229
232
|
|
|
230
233
|
const handleOnDuplicate = async () => {
|
|
234
|
+
if (disabled) return
|
|
231
235
|
if (!onDuplicate) return
|
|
232
236
|
setDuplicateBusy(true)
|
|
233
237
|
try {
|
|
234
238
|
await onDuplicate()
|
|
235
239
|
setShowDuplicateConfirm(false)
|
|
240
|
+
} catch {
|
|
241
|
+
// The host reports the failure and retains the editor observation.
|
|
236
242
|
} finally {
|
|
237
243
|
setDuplicateBusy(false)
|
|
238
244
|
}
|
|
239
245
|
}
|
|
240
246
|
|
|
241
247
|
const handleOpenDuplicate = () => {
|
|
248
|
+
if (disabled) return
|
|
242
249
|
// Duplicate copies the saved version — block when the form is dirty so
|
|
243
250
|
// unsaved edits are not silently dropped from the copy.
|
|
244
251
|
if (hasUnsavedChanges) {
|
|
@@ -249,6 +256,7 @@ export function DocumentActions({
|
|
|
249
256
|
}
|
|
250
257
|
|
|
251
258
|
const handleOpenCopyToLocale = () => {
|
|
259
|
+
if (disabled) return
|
|
252
260
|
// Copy-to-Locale reads the saved version — block when the form is dirty.
|
|
253
261
|
if (hasUnsavedChanges) {
|
|
254
262
|
onUnsavedChanges?.()
|
|
@@ -263,17 +271,21 @@ export function DocumentActions({
|
|
|
263
271
|
}
|
|
264
272
|
|
|
265
273
|
const handleOnCopyToLocale = async () => {
|
|
274
|
+
if (disabled) return
|
|
266
275
|
if (!onCopyToLocale || !copyTargetLocale) return
|
|
267
276
|
setCopyToLocaleBusy(true)
|
|
268
277
|
try {
|
|
269
278
|
await onCopyToLocale({ targetLocale: copyTargetLocale, overwrite: copyOverwrite })
|
|
270
279
|
setShowCopyToLocaleConfirm(false)
|
|
280
|
+
} catch {
|
|
281
|
+
// The host reports the failure and retains the editor observation.
|
|
271
282
|
} finally {
|
|
272
283
|
setCopyToLocaleBusy(false)
|
|
273
284
|
}
|
|
274
285
|
}
|
|
275
286
|
|
|
276
287
|
const handleOpenDeleteLocale = () => {
|
|
288
|
+
if (disabled) return
|
|
277
289
|
// Delete-Locale removes the saved version's locale content — block when
|
|
278
290
|
// the form is dirty so the editor saves (or discards) first.
|
|
279
291
|
if (hasUnsavedChanges) {
|
|
@@ -288,11 +300,14 @@ export function DocumentActions({
|
|
|
288
300
|
}
|
|
289
301
|
|
|
290
302
|
const handleOnDeleteLocale = async () => {
|
|
303
|
+
if (disabled) return
|
|
291
304
|
if (!onDeleteLocale || !deleteTargetLocale) return
|
|
292
305
|
setDeleteLocaleBusy(true)
|
|
293
306
|
try {
|
|
294
307
|
await onDeleteLocale({ targetLocale: deleteTargetLocale })
|
|
295
308
|
setShowDeleteLocaleConfirm(false)
|
|
309
|
+
} catch {
|
|
310
|
+
// The host reports the failure and retains the editor observation.
|
|
296
311
|
} finally {
|
|
297
312
|
setDeleteLocaleBusy(false)
|
|
298
313
|
}
|
|
@@ -311,6 +326,7 @@ export function DocumentActions({
|
|
|
311
326
|
{hasAnyAction && (
|
|
312
327
|
<DropdownComponent.Root>
|
|
313
328
|
<DropdownComponent.Trigger
|
|
329
|
+
disabled={disabled}
|
|
314
330
|
render={<IconButton variant="text" intent="noeffect" size="sm" />}
|
|
315
331
|
>
|
|
316
332
|
<EllipsisIcon
|
|
@@ -329,7 +345,7 @@ export function DocumentActions({
|
|
|
329
345
|
>
|
|
330
346
|
{/*{publishedVersion && (
|
|
331
347
|
<>
|
|
332
|
-
<DropdownComponent.Item onClick={onUnpublish}>
|
|
348
|
+
<DropdownComponent.Item disabled={disabled} onClick={onUnpublish}>
|
|
333
349
|
<div className={cx('byline-form-actions-item', styles.item)}>
|
|
334
350
|
<span className={cx('byline-form-actions-item-icon', styles['item-icon'])} />
|
|
335
351
|
<span className={cx('byline-form-actions-item-text', styles['item-text'])}>
|
|
@@ -343,10 +359,16 @@ export function DocumentActions({
|
|
|
343
359
|
{schedulingActions.length > 0 && (
|
|
344
360
|
<>
|
|
345
361
|
{schedulingActions.map((action) => (
|
|
346
|
-
<DropdownComponent.Item
|
|
362
|
+
<DropdownComponent.Item
|
|
363
|
+
disabled={disabled}
|
|
364
|
+
key={action.key}
|
|
365
|
+
onClick={action.onSelect}
|
|
366
|
+
>
|
|
347
367
|
<div className={cx('byline-form-actions-item', styles.item)}>
|
|
348
368
|
<span className={cx('byline-form-actions-item-text', styles['item-text'])}>
|
|
349
|
-
<button type="button"
|
|
369
|
+
<button type="button" disabled={disabled}>
|
|
370
|
+
{action.label}
|
|
371
|
+
</button>
|
|
350
372
|
</span>
|
|
351
373
|
</div>
|
|
352
374
|
</DropdownComponent.Item>
|
|
@@ -355,28 +377,34 @@ export function DocumentActions({
|
|
|
355
377
|
</>
|
|
356
378
|
)}
|
|
357
379
|
{copyToLocaleAvailable && (
|
|
358
|
-
<DropdownComponent.Item onClick={handleOpenCopyToLocale}>
|
|
380
|
+
<DropdownComponent.Item disabled={disabled} onClick={handleOpenCopyToLocale}>
|
|
359
381
|
<div className={cx('byline-form-actions-item', styles.item)}>
|
|
360
382
|
<span className={cx('byline-form-actions-item-text', styles['item-text'])}>
|
|
361
|
-
<button type="button"
|
|
383
|
+
<button type="button" disabled={disabled}>
|
|
384
|
+
{t('documentActions.copyToLocaleMenuItem')}
|
|
385
|
+
</button>
|
|
362
386
|
</span>
|
|
363
387
|
</div>
|
|
364
388
|
</DropdownComponent.Item>
|
|
365
389
|
)}
|
|
366
390
|
{deleteLocaleAvailable && (
|
|
367
|
-
<DropdownComponent.Item onClick={handleOpenDeleteLocale}>
|
|
391
|
+
<DropdownComponent.Item disabled={disabled} onClick={handleOpenDeleteLocale}>
|
|
368
392
|
<div className={cx('byline-form-actions-item', styles.item)}>
|
|
369
393
|
<span className={cx('byline-form-actions-item-text', styles['item-text'])}>
|
|
370
|
-
<button type="button"
|
|
394
|
+
<button type="button" disabled={disabled}>
|
|
395
|
+
{t('documentActions.deleteLocale.menuItem')}
|
|
396
|
+
</button>
|
|
371
397
|
</span>
|
|
372
398
|
</div>
|
|
373
399
|
</DropdownComponent.Item>
|
|
374
400
|
)}
|
|
375
401
|
{onDuplicate && (
|
|
376
|
-
<DropdownComponent.Item onClick={handleOpenDuplicate}>
|
|
402
|
+
<DropdownComponent.Item disabled={disabled} onClick={handleOpenDuplicate}>
|
|
377
403
|
<div className={cx('byline-form-actions-item', styles.item)}>
|
|
378
404
|
<span className={cx('byline-form-actions-item-text', styles['item-text'])}>
|
|
379
|
-
<button type="button"
|
|
405
|
+
<button type="button" disabled={disabled}>
|
|
406
|
+
{t('common.actions.duplicate')}
|
|
407
|
+
</button>
|
|
380
408
|
</span>
|
|
381
409
|
</div>
|
|
382
410
|
</DropdownComponent.Item>
|
|
@@ -385,6 +413,7 @@ export function DocumentActions({
|
|
|
385
413
|
<>
|
|
386
414
|
<DropdownComponent.Separator />
|
|
387
415
|
<DropdownComponent.Item
|
|
416
|
+
disabled={disabled}
|
|
388
417
|
onClick={() => {
|
|
389
418
|
setShowDeleteConfirm(true)
|
|
390
419
|
}}
|
|
@@ -454,7 +483,13 @@ export function DocumentActions({
|
|
|
454
483
|
>
|
|
455
484
|
{t('common.actions.cancel')}
|
|
456
485
|
</Button>
|
|
457
|
-
<Button
|
|
486
|
+
<Button
|
|
487
|
+
size="sm"
|
|
488
|
+
style={{ minWidth: '80px' }}
|
|
489
|
+
intent="danger"
|
|
490
|
+
disabled={disabled}
|
|
491
|
+
onClick={handleOnDelete}
|
|
492
|
+
>
|
|
458
493
|
{t('common.actions.delete')}
|
|
459
494
|
</Button>
|
|
460
495
|
</Modal.Actions>
|
|
@@ -533,7 +568,7 @@ export function DocumentActions({
|
|
|
533
568
|
onClick={() => {
|
|
534
569
|
if (!duplicateBusy) setShowDuplicateConfirm(false)
|
|
535
570
|
}}
|
|
536
|
-
disabled={duplicateBusy}
|
|
571
|
+
disabled={disabled || duplicateBusy}
|
|
537
572
|
>
|
|
538
573
|
{t('common.actions.cancel')}
|
|
539
574
|
</Button>
|
|
@@ -542,7 +577,7 @@ export function DocumentActions({
|
|
|
542
577
|
style={{ minWidth: '80px' }}
|
|
543
578
|
intent="primary"
|
|
544
579
|
onClick={handleOnDuplicate}
|
|
545
|
-
disabled={duplicateBusy}
|
|
580
|
+
disabled={disabled || duplicateBusy}
|
|
546
581
|
>
|
|
547
582
|
{duplicateBusy
|
|
548
583
|
? t('documentActions.duplicate.busyButton')
|
|
@@ -611,7 +646,7 @@ export function DocumentActions({
|
|
|
611
646
|
onValueChange={(value) => {
|
|
612
647
|
if (value != null) setCopyTargetLocale(value)
|
|
613
648
|
}}
|
|
614
|
-
disabled={copyToLocaleBusy}
|
|
649
|
+
disabled={disabled || copyToLocaleBusy}
|
|
615
650
|
/>
|
|
616
651
|
</div>
|
|
617
652
|
<div
|
|
@@ -623,7 +658,7 @@ export function DocumentActions({
|
|
|
623
658
|
name="overwrite"
|
|
624
659
|
label={t('documentActions.copyToLocale.overwriteLabel')}
|
|
625
660
|
checked={copyOverwrite}
|
|
626
|
-
disabled={copyToLocaleBusy}
|
|
661
|
+
disabled={disabled || copyToLocaleBusy}
|
|
627
662
|
helpText={t('documentActions.copyToLocale.overwriteHelp')}
|
|
628
663
|
onCheckedChange={(value) => {
|
|
629
664
|
setCopyOverwrite(value === true)
|
|
@@ -647,7 +682,7 @@ export function DocumentActions({
|
|
|
647
682
|
onClick={() => {
|
|
648
683
|
if (!copyToLocaleBusy) setShowCopyToLocaleConfirm(false)
|
|
649
684
|
}}
|
|
650
|
-
disabled={copyToLocaleBusy}
|
|
685
|
+
disabled={disabled || copyToLocaleBusy}
|
|
651
686
|
>
|
|
652
687
|
{t('common.actions.cancel')}
|
|
653
688
|
</Button>
|
|
@@ -656,7 +691,7 @@ export function DocumentActions({
|
|
|
656
691
|
style={{ minWidth: '80px' }}
|
|
657
692
|
intent="primary"
|
|
658
693
|
onClick={handleOnCopyToLocale}
|
|
659
|
-
disabled={copyToLocaleBusy || !copyTargetLocale}
|
|
694
|
+
disabled={disabled || copyToLocaleBusy || !copyTargetLocale}
|
|
660
695
|
>
|
|
661
696
|
{copyToLocaleBusy
|
|
662
697
|
? t('documentActions.copyToLocale.busyButton')
|
|
@@ -711,7 +746,7 @@ export function DocumentActions({
|
|
|
711
746
|
onValueChange={(value) => {
|
|
712
747
|
if (value != null) setDeleteTargetLocale(value)
|
|
713
748
|
}}
|
|
714
|
-
disabled={deleteLocaleBusy}
|
|
749
|
+
disabled={disabled || deleteLocaleBusy}
|
|
715
750
|
/>
|
|
716
751
|
</div>
|
|
717
752
|
<p style={{ marginTop: 'var(--spacing-12)' }}>
|
|
@@ -734,7 +769,7 @@ export function DocumentActions({
|
|
|
734
769
|
onClick={() => {
|
|
735
770
|
if (!deleteLocaleBusy) setShowDeleteLocaleConfirm(false)
|
|
736
771
|
}}
|
|
737
|
-
disabled={deleteLocaleBusy}
|
|
772
|
+
disabled={disabled || deleteLocaleBusy}
|
|
738
773
|
>
|
|
739
774
|
{t('common.actions.cancel')}
|
|
740
775
|
</Button>
|
|
@@ -743,7 +778,7 @@ export function DocumentActions({
|
|
|
743
778
|
style={{ minWidth: '80px' }}
|
|
744
779
|
intent="danger"
|
|
745
780
|
onClick={handleOnDeleteLocale}
|
|
746
|
-
disabled={deleteLocaleBusy || !deleteTargetLocale}
|
|
781
|
+
disabled={disabled || deleteLocaleBusy || !deleteTargetLocale}
|
|
747
782
|
>
|
|
748
783
|
{deleteLocaleBusy
|
|
749
784
|
? t('documentActions.deleteLocale.busyButton')
|