@byline/admin 4.18.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 +338 -191
- package/dist/forms/form-renderer.module.js +11 -0
- package/dist/forms/form-renderer_module.css +46 -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 +231 -8
- package/src/forms/form-renderer.module.css +62 -0
- package/src/forms/form-renderer.tsx +393 -200
- 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
|
@@ -141,6 +141,40 @@
|
|
|
141
141
|
min-height: 28px;
|
|
142
142
|
}
|
|
143
143
|
|
|
144
|
+
:is(.busy-status-kzSMVH, .byline-form-busy-status) {
|
|
145
|
+
clip: rect(0 0 0 0);
|
|
146
|
+
clip-path: inset(50%);
|
|
147
|
+
white-space: nowrap;
|
|
148
|
+
border: 0;
|
|
149
|
+
width: 1px;
|
|
150
|
+
height: 1px;
|
|
151
|
+
margin: -1px;
|
|
152
|
+
padding: 0;
|
|
153
|
+
position: absolute;
|
|
154
|
+
overflow: hidden;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
:is(.save-content-bc8XeC, .byline-form-save-content) {
|
|
158
|
+
display: inline-block;
|
|
159
|
+
position: relative;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
:is(.save-label-iJw9DA, .byline-form-save-label) {
|
|
163
|
+
display: inline-block;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
.save-label-hidden-Wct46I {
|
|
167
|
+
visibility: hidden;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
:is(.save-loader-g2wcnV, .byline-form-save-loader) {
|
|
171
|
+
display: inline-flex;
|
|
172
|
+
position: absolute;
|
|
173
|
+
top: 50%;
|
|
174
|
+
left: 50%;
|
|
175
|
+
transform: translate(-50%, -50%);
|
|
176
|
+
}
|
|
177
|
+
|
|
144
178
|
:is(.actions-status-wrap-hSvlps, .byline-form-actions-status-wrap) {
|
|
145
179
|
z-index: 10;
|
|
146
180
|
position: relative;
|
|
@@ -155,6 +189,18 @@
|
|
|
155
189
|
min-height: 28px;
|
|
156
190
|
}
|
|
157
191
|
|
|
192
|
+
:is(.concurrency-UT6bX5, .byline-document-concurrency) {
|
|
193
|
+
margin-top: var(--spacing-16);
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
:is(.concurrency-UT6bX5 button, .byline-document-concurrency button) {
|
|
197
|
+
margin-top: var(--spacing-12);
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
:is(.concurrency-UT6bX5 ~ .layout-WTbLYr, .byline-document-concurrency ~ .byline-form-layout) {
|
|
201
|
+
padding-top: var(--spacing-16);
|
|
202
|
+
}
|
|
203
|
+
|
|
158
204
|
:is(.layout-WTbLYr, .byline-form-layout) {
|
|
159
205
|
gap: var(--spacing-12);
|
|
160
206
|
padding-top: var(--spacing-32);
|
|
@@ -13,7 +13,8 @@ import type { PublishedVersionInfo } from './form-renderer';
|
|
|
13
13
|
* created timestamps, and a "published live" indicator with an inline
|
|
14
14
|
* Unpublish action when a previously-published version is still live.
|
|
15
15
|
*/
|
|
16
|
-
export declare const FormStatusDisplay: ({ initialData, workflowStatuses, publishedVersion, onUnpublish, afterStatusCells, }: {
|
|
16
|
+
export declare const FormStatusDisplay: ({ disabled, initialData, workflowStatuses, publishedVersion, onUnpublish, afterStatusCells, }: {
|
|
17
|
+
disabled?: boolean;
|
|
17
18
|
initialData?: Record<string, any>;
|
|
18
19
|
workflowStatuses?: WorkflowStatus[];
|
|
19
20
|
publishedVersion?: PublishedVersionInfo | null;
|
|
@@ -4,7 +4,7 @@ import { useTranslation } from "@byline/i18n/react";
|
|
|
4
4
|
import clsx from "clsx";
|
|
5
5
|
import { LocalDateTime } from "../fields/local-date-time.js";
|
|
6
6
|
import form_renderer_module from "./form-renderer.module.js";
|
|
7
|
-
const FormStatusDisplay = ({ initialData, workflowStatuses, publishedVersion, onUnpublish, afterStatusCells })=>{
|
|
7
|
+
const FormStatusDisplay = ({ disabled = false, initialData, workflowStatuses, publishedVersion, onUnpublish, afterStatusCells })=>{
|
|
8
8
|
const { t } = useTranslation('byline-admin');
|
|
9
9
|
const statusCode = initialData?.status;
|
|
10
10
|
const statusLabel = workflowStatuses?.find((s)=>s.name === statusCode)?.label ?? statusCode;
|
|
@@ -82,7 +82,10 @@ const FormStatusDisplay = ({ initialData, workflowStatuses, publishedVersion, on
|
|
|
82
82
|
' ',
|
|
83
83
|
/*#__PURE__*/ jsx("button", {
|
|
84
84
|
type: "button",
|
|
85
|
-
|
|
85
|
+
disabled: disabled,
|
|
86
|
+
onClick: ()=>{
|
|
87
|
+
if (!disabled) onUnpublish().catch(()=>{});
|
|
88
|
+
},
|
|
86
89
|
className: clsx('byline-form-status-unpublish', form_renderer_module["status-unpublish"]),
|
|
87
90
|
children: t('common.actions.unpublish')
|
|
88
91
|
})
|
|
@@ -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/
|
|
188
|
-
"@byline/
|
|
189
|
-
"@byline/
|
|
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
|
)
|