@byline/admin 6.0.0 → 6.0.2

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.
Files changed (92) hide show
  1. package/dist/modules/admin-preferences/index.d.ts +7 -3
  2. package/dist/modules/admin-preferences/index.js +1 -1
  3. package/dist/modules/admin-preferences/schemas.d.ts +19 -16
  4. package/dist/modules/admin-preferences/schemas.js +46 -2
  5. package/dist/modules/analytics/components/timeseries.d.ts +28 -0
  6. package/dist/modules/analytics/components/timeseries.js +107 -35
  7. package/dist/modules/analytics/components/timeseries.module.js +15 -0
  8. package/dist/modules/analytics/components/timeseries_module.css +110 -3
  9. package/package.json +7 -7
  10. package/src/modules/admin-preferences/index.ts +7 -2
  11. package/src/modules/admin-preferences/schemas.test.node.ts +74 -0
  12. package/src/modules/admin-preferences/schemas.ts +85 -8
  13. package/src/modules/analytics/components/dashboard.test.node.ts +51 -0
  14. package/src/modules/analytics/components/timeseries.module.css +176 -3
  15. package/src/modules/analytics/components/timeseries.tsx +153 -40
  16. package/dist/exports-parity.test.node.js +0 -37
  17. package/dist/fields/array/array-field.jsx +0 -209
  18. package/dist/fields/blocks/blocks-field.jsx +0 -248
  19. package/dist/fields/checkbox/checkbox-field.jsx +0 -24
  20. package/dist/fields/code/code-editor.jsx +0 -211
  21. package/dist/fields/code/code-field.jsx +0 -109
  22. package/dist/fields/column-formatter.jsx +0 -25
  23. package/dist/fields/date-time-formatter.jsx +0 -18
  24. package/dist/fields/datetime/datetime-field.jsx +0 -22
  25. package/dist/fields/draggable-context-menu.jsx +0 -50
  26. package/dist/fields/field-admin.test.node.js +0 -40
  27. package/dist/fields/field-renderer.jsx +0 -136
  28. package/dist/fields/field-services-context.jsx +0 -17
  29. package/dist/fields/file/file-field.jsx +0 -176
  30. package/dist/fields/file/file-upload-field.jsx +0 -89
  31. package/dist/fields/group/group-field.jsx +0 -46
  32. package/dist/fields/image/image-field.jsx +0 -183
  33. package/dist/fields/image/image-upload-field.jsx +0 -157
  34. package/dist/fields/local-date-time.jsx +0 -69
  35. package/dist/fields/locale-badge.jsx +0 -19
  36. package/dist/fields/numerical/numerical-field.jsx +0 -93
  37. package/dist/fields/relation/relation-column-formatter.jsx +0 -74
  38. package/dist/fields/relation/relation-display.jsx +0 -118
  39. package/dist/fields/relation/relation-field.jsx +0 -108
  40. package/dist/fields/relation/relation-many-field.jsx +0 -142
  41. package/dist/fields/relation/relation-picker.jsx +0 -240
  42. package/dist/fields/relation/relation-summary.jsx +0 -45
  43. package/dist/fields/select/select-field.jsx +0 -27
  44. package/dist/fields/sortable-item.jsx +0 -54
  45. package/dist/fields/text/text-field.jsx +0 -79
  46. package/dist/fields/text-area/text-area-field.jsx +0 -79
  47. package/dist/forms/available-locales-reconcile.test.node.js +0 -43
  48. package/dist/forms/available-locales-widget.jsx +0 -63
  49. package/dist/forms/document-actions.jsx +0 -486
  50. package/dist/forms/form-context.jsx +0 -598
  51. package/dist/forms/form-modals.jsx +0 -128
  52. package/dist/forms/form-renderer.jsx +0 -532
  53. package/dist/forms/form-status-display.jsx +0 -69
  54. package/dist/forms/navigation-guard.jsx +0 -68
  55. package/dist/forms/nested-path.test.node.js +0 -115
  56. package/dist/forms/path-widget.jsx +0 -125
  57. package/dist/forms/pending-uploads.test.node.js +0 -19
  58. package/dist/forms/repeating-items.test.node.js +0 -29
  59. package/dist/forms/scheduled-publication-control.jsx +0 -389
  60. package/dist/forms/scheduled-publication-state.test.node.js +0 -121
  61. package/dist/forms/scheduled-publication-time.test.node.js +0 -70
  62. package/dist/forms/status-transitions.test.node.js +0 -76
  63. package/dist/forms/tree-placement-widget.jsx +0 -160
  64. package/dist/forms/upload-executor.test.node.js +0 -368
  65. package/dist/forms/use-form-layout.test.node.js +0 -69
  66. package/dist/modules/admin-account/components/change-password.jsx +0 -169
  67. package/dist/modules/admin-account/components/container.jsx +0 -164
  68. package/dist/modules/admin-account/components/preferences.jsx +0 -148
  69. package/dist/modules/admin-account/components/theme-switch.jsx +0 -52
  70. package/dist/modules/admin-account/components/update.jsx +0 -186
  71. package/dist/modules/admin-permissions/components/inspector.jsx +0 -200
  72. package/dist/modules/admin-preferences/schemas.test.node.js +0 -48
  73. package/dist/modules/admin-roles/components/create.jsx +0 -155
  74. package/dist/modules/admin-roles/components/permissions.jsx +0 -239
  75. package/dist/modules/admin-roles/components/update.jsx +0 -148
  76. package/dist/modules/admin-users/components/create.jsx +0 -206
  77. package/dist/modules/admin-users/components/roles.jsx +0 -126
  78. package/dist/modules/admin-users/components/set-password.jsx +0 -133
  79. package/dist/modules/admin-users/components/update.jsx +0 -215
  80. package/dist/modules/analytics/components/dashboard.jsx +0 -208
  81. package/dist/modules/analytics/components/dashboard.test.node.js +0 -162
  82. package/dist/modules/analytics/components/timeseries.jsx +0 -193
  83. package/dist/modules/auth/components/sign-in-form.jsx +0 -89
  84. package/dist/modules/auth/safe-redirect.test.node.js +0 -40
  85. package/dist/modules/auth/sign-in-form-props.test.node.js +0 -7
  86. package/dist/presentation/group.jsx +0 -26
  87. package/dist/presentation/row.jsx +0 -23
  88. package/dist/presentation/tabs.jsx +0 -39
  89. package/dist/services/admin-services-context.jsx +0 -17
  90. package/dist/widgets/diff-viewer/diff-modal.jsx +0 -110
  91. package/dist/widgets/source-locale-badge/source-locale-badge.jsx +0 -28
  92. package/dist/widgets/status-badge/status-badge.jsx +0 -46
@@ -1,486 +0,0 @@
1
- 'use client';
2
- /**
3
- * This Source Code is subject to the terms of the Mozilla Public
4
- * License, v. 2.0. If a copy of the MPL was not distributed with this
5
- * file, You can obtain one at http://mozilla.org/MPL/2.0/.
6
- *
7
- * Copyright (c) Infonomic Company Limited
8
- */
9
- import { useState } from 'react';
10
- import { useTranslation } from '@byline/i18n/react';
11
- import { Button, Checkbox, CloseIcon, DeleteIcon, Dropdown as DropdownComponent, EllipsisIcon, IconButton, Modal, Select, } from '@byline/ui/react';
12
- import cx from 'clsx';
13
- import styles from './document-actions.module.css';
14
- const DUPLICATE_TITLE_SUFFIX = ' (copy)';
15
- export function DocumentActions({ disabled = false, publishedVersion, onUnpublish, onDelete, onDuplicate, sourceTitle, onCopyToLocale, sourceLocale, contentLocales, hasUnsavedChanges, onUnsavedChanges, onDeleteLocale, defaultLocale, availableLocales, scheduledPublicationState, onSchedulePublication, onConfirmScheduledPublication, onCancelScheduledPublication, }) {
16
- const { t } = useTranslation('byline-admin');
17
- const [showDeleteConfirm, setShowDeleteConfirm] = useState(false);
18
- const [showDuplicateConfirm, setShowDuplicateConfirm] = useState(false);
19
- const [duplicateBusy, setDuplicateBusy] = useState(false);
20
- // Copy-to-Locale modal state. The menu item is hidden entirely unless
21
- // the host has supplied a handler AND there is at least one *other*
22
- // locale to copy into.
23
- const availableTargetLocales = (contentLocales ?? []).filter((loc) => loc.code !== sourceLocale);
24
- const copyToLocaleAvailable = onCopyToLocale != null && availableTargetLocales.length > 0;
25
- const [showCopyToLocaleConfirm, setShowCopyToLocaleConfirm] = useState(false);
26
- const [copyToLocaleBusy, setCopyToLocaleBusy] = useState(false);
27
- const [copyTargetLocale, setCopyTargetLocale] = useState(availableTargetLocales[0]?.code ?? '');
28
- const [copyOverwrite, setCopyOverwrite] = useState(false);
29
- // Delete-Locale modal state. The menu item is hidden unless the host
30
- // supplied a handler AND the document has at least one non-default locale
31
- // with content. The default locale is the document's anchor and is never
32
- // listed (it cannot be removed).
33
- const deletableLocales = (availableLocales ?? [])
34
- .filter((code) => code !== defaultLocale && code !== 'all')
35
- .map((code) => ({
36
- code,
37
- label: contentLocales?.find((loc) => loc.code === code)?.label ?? code,
38
- }));
39
- const deleteLocaleAvailable = onDeleteLocale != null && deletableLocales.length > 0;
40
- const [showDeleteLocaleConfirm, setShowDeleteLocaleConfirm] = useState(false);
41
- const [deleteLocaleBusy, setDeleteLocaleBusy] = useState(false);
42
- const [deleteTargetLocale, setDeleteTargetLocale] = useState('');
43
- // Scheduled-publication menu items. The derived state already accounts for
44
- // eligibility and for the abilities the actor holds, so the only thing left
45
- // here is to map the permitted operations onto entries. The group renders
46
- // above the locale and duplicate items because scheduling acts on the
47
- // document's lifecycle rather than on its copies.
48
- const scheduling = scheduledPublicationState;
49
- const schedulingActions = [];
50
- if (scheduling != null) {
51
- if (scheduling.actions.schedule && onSchedulePublication != null) {
52
- schedulingActions.push({
53
- key: 'schedule',
54
- // Short form: the menu supplies the "this document" context that the
55
- // modal's title and submit button have to spell out for themselves.
56
- label: t('scheduledPublication.actions.scheduleMenuItem'),
57
- onSelect: onSchedulePublication,
58
- });
59
- }
60
- if (scheduling.actions.confirm && onConfirmScheduledPublication != null) {
61
- schedulingActions.push({
62
- key: 'confirm',
63
- label: t('scheduledPublication.actions.confirm'),
64
- onSelect: () => void onConfirmScheduledPublication(),
65
- });
66
- }
67
- if (scheduling.actions.reschedule && onSchedulePublication != null) {
68
- schedulingActions.push({
69
- key: 'reschedule',
70
- label: t('scheduledPublication.actions.reschedule'),
71
- onSelect: onSchedulePublication,
72
- });
73
- }
74
- if (scheduling.actions.cancel && onCancelScheduledPublication != null) {
75
- schedulingActions.push({
76
- key: 'cancel',
77
- label: t('scheduledPublication.actions.cancel'),
78
- onSelect: () => void onCancelScheduledPublication(),
79
- });
80
- }
81
- }
82
- // Whether the ellipsis menu has anything to show. Every entry below is
83
- // conditional, so with no available action the trigger would open an empty
84
- // menu.
85
- const hasAnyAction = schedulingActions.length > 0 ||
86
- copyToLocaleAvailable ||
87
- deleteLocaleAvailable ||
88
- onDuplicate != null ||
89
- onDelete != null;
90
- const handleOnDelete = () => {
91
- if (disabled)
92
- return;
93
- setShowDeleteConfirm(false);
94
- if (onDelete) {
95
- void onDelete().catch(() => { });
96
- }
97
- };
98
- const handleOnDuplicate = async () => {
99
- if (disabled)
100
- return;
101
- if (!onDuplicate)
102
- return;
103
- setDuplicateBusy(true);
104
- try {
105
- await onDuplicate();
106
- setShowDuplicateConfirm(false);
107
- }
108
- catch {
109
- // The host reports the failure and retains the editor observation.
110
- }
111
- finally {
112
- setDuplicateBusy(false);
113
- }
114
- };
115
- const handleOpenDuplicate = () => {
116
- if (disabled)
117
- return;
118
- // Duplicate copies the saved version — block when the form is dirty so
119
- // unsaved edits are not silently dropped from the copy.
120
- if (hasUnsavedChanges) {
121
- onUnsavedChanges?.();
122
- return;
123
- }
124
- setShowDuplicateConfirm(true);
125
- };
126
- const handleOpenCopyToLocale = () => {
127
- if (disabled)
128
- return;
129
- // Copy-to-Locale reads the saved version — block when the form is dirty.
130
- if (hasUnsavedChanges) {
131
- onUnsavedChanges?.();
132
- return;
133
- }
134
- // Reset on open: pick the first available target and clear the
135
- // overwrite checkbox so a previous-session "overwrite=true" choice
136
- // is not silently sticky.
137
- setCopyTargetLocale(availableTargetLocales[0]?.code ?? '');
138
- setCopyOverwrite(false);
139
- setShowCopyToLocaleConfirm(true);
140
- };
141
- const handleOnCopyToLocale = async () => {
142
- if (disabled)
143
- return;
144
- if (!onCopyToLocale || !copyTargetLocale)
145
- return;
146
- setCopyToLocaleBusy(true);
147
- try {
148
- await onCopyToLocale({ targetLocale: copyTargetLocale, overwrite: copyOverwrite });
149
- setShowCopyToLocaleConfirm(false);
150
- }
151
- catch {
152
- // The host reports the failure and retains the editor observation.
153
- }
154
- finally {
155
- setCopyToLocaleBusy(false);
156
- }
157
- };
158
- const handleOpenDeleteLocale = () => {
159
- if (disabled)
160
- return;
161
- // Delete-Locale removes the saved version's locale content — block when
162
- // the form is dirty so the editor saves (or discards) first.
163
- if (hasUnsavedChanges) {
164
- onUnsavedChanges?.();
165
- return;
166
- }
167
- // Default to the currently-viewed locale when it is deletable, otherwise
168
- // the first available target.
169
- const preferred = deletableLocales.find((loc) => loc.code === sourceLocale)?.code;
170
- setDeleteTargetLocale(preferred ?? deletableLocales[0]?.code ?? '');
171
- setShowDeleteLocaleConfirm(true);
172
- };
173
- const handleOnDeleteLocale = async () => {
174
- if (disabled)
175
- return;
176
- if (!onDeleteLocale || !deleteTargetLocale)
177
- return;
178
- setDeleteLocaleBusy(true);
179
- try {
180
- await onDeleteLocale({ targetLocale: deleteTargetLocale });
181
- setShowDeleteLocaleConfirm(false);
182
- }
183
- catch {
184
- // The host reports the failure and retains the editor observation.
185
- }
186
- finally {
187
- setDeleteLocaleBusy(false);
188
- }
189
- };
190
- // Preview text shown inside the modal. Falls back to the literal suffix
191
- // when no source title is supplied (collections without `useAsTitle`).
192
- const duplicatePreviewBefore = sourceTitle ?? '';
193
- const duplicatePreviewAfter = (sourceTitle ?? '') + DUPLICATE_TITLE_SUFFIX;
194
- const sourceLocaleLabel = contentLocales?.find((loc) => loc.code === sourceLocale)?.label ?? sourceLocale ?? '';
195
- return (<>
196
- {hasAnyAction && (<DropdownComponent.Root>
197
- <DropdownComponent.Trigger disabled={disabled} render={<IconButton variant="text" intent="noeffect" size="sm"/>}>
198
- <EllipsisIcon className={cx('byline-form-actions-icon', styles.icon)} width="15px" height="15px"/>
199
- </DropdownComponent.Trigger>
200
-
201
- <DropdownComponent.Portal>
202
- <DropdownComponent.Content className={cx('byline-form-actions-menu', styles.menu)} align="end" data-side="top" sideOffset={10}>
203
- {/*{publishedVersion && (
204
- <>
205
- <DropdownComponent.Item disabled={disabled} onClick={onUnpublish}>
206
- <div className={cx('byline-form-actions-item', styles.item)}>
207
- <span className={cx('byline-form-actions-item-icon', styles['item-icon'])} />
208
- <span className={cx('byline-form-actions-item-text', styles['item-text'])}>
209
- Unpublish
210
- </span>
211
- </div>
212
- </DropdownComponent.Item>
213
- <DropdownComponent.Separator />
214
- </>
215
- )}*/}
216
- {schedulingActions.length > 0 && (<>
217
- {schedulingActions.map((action) => (<DropdownComponent.Item disabled={disabled} key={action.key} onClick={action.onSelect}>
218
- <div className={cx('byline-form-actions-item', styles.item)}>
219
- <span className={cx('byline-form-actions-item-text', styles['item-text'])}>
220
- <button type="button" disabled={disabled}>
221
- {action.label}
222
- </button>
223
- </span>
224
- </div>
225
- </DropdownComponent.Item>))}
226
- <DropdownComponent.Separator />
227
- </>)}
228
- {copyToLocaleAvailable && (<DropdownComponent.Item disabled={disabled} onClick={handleOpenCopyToLocale}>
229
- <div className={cx('byline-form-actions-item', styles.item)}>
230
- <span className={cx('byline-form-actions-item-text', styles['item-text'])}>
231
- <button type="button" disabled={disabled}>
232
- {t('documentActions.copyToLocaleMenuItem')}
233
- </button>
234
- </span>
235
- </div>
236
- </DropdownComponent.Item>)}
237
- {deleteLocaleAvailable && (<DropdownComponent.Item disabled={disabled} onClick={handleOpenDeleteLocale}>
238
- <div className={cx('byline-form-actions-item', styles.item)}>
239
- <span className={cx('byline-form-actions-item-text', styles['item-text'])}>
240
- <button type="button" disabled={disabled}>
241
- {t('documentActions.deleteLocale.menuItem')}
242
- </button>
243
- </span>
244
- </div>
245
- </DropdownComponent.Item>)}
246
- {onDuplicate && (<DropdownComponent.Item disabled={disabled} onClick={handleOpenDuplicate}>
247
- <div className={cx('byline-form-actions-item', styles.item)}>
248
- <span className={cx('byline-form-actions-item-text', styles['item-text'])}>
249
- <button type="button" disabled={disabled}>
250
- {t('common.actions.duplicate')}
251
- </button>
252
- </span>
253
- </div>
254
- </DropdownComponent.Item>)}
255
- {onDelete && (<>
256
- <DropdownComponent.Separator />
257
- <DropdownComponent.Item disabled={disabled} onClick={() => {
258
- setShowDeleteConfirm(true);
259
- }}>
260
- <div className={cx('byline-form-actions-item', styles.item)}>
261
- <span className={cx('byline-form-actions-item-icon', styles['item-icon'])}>
262
- <DeleteIcon width="16px" height="16px"/>
263
- </span>
264
- <span className={cx('byline-form-actions-item-text', styles['item-text'])}>
265
- <button type="button" className={cx('byline-form-actions-delete', styles.delete)}>
266
- {t('common.actions.delete')}
267
- </button>
268
- </span>
269
- </div>
270
- </DropdownComponent.Item>
271
- </>)}
272
- </DropdownComponent.Content>
273
- </DropdownComponent.Portal>
274
- </DropdownComponent.Root>)}
275
-
276
- <Modal isOpen={showDeleteConfirm} closeOnOverlayClick={true} onDismiss={() => {
277
- setShowDeleteConfirm(false);
278
- }}>
279
- <Modal.Container style={{ maxWidth: '500px' }}>
280
- <Modal.Header className={cx('byline-form-actions-modal-head', styles['modal-head'])}>
281
- <h3 className={cx('byline-form-actions-modal-title', styles['modal-title'])}>
282
- {t('documentActions.delete.title')}
283
- </h3>
284
- <IconButton arial-label={t('common.actions.close')} size="xs" onClick={() => {
285
- setShowDeleteConfirm(false);
286
- }}>
287
- <CloseIcon width="16px" height="16px" svgClassName="white-icon"/>
288
- </IconButton>
289
- </Modal.Header>
290
- <Modal.Content>
291
- <p>{t('documentActions.delete.warning')}</p>
292
- </Modal.Content>
293
- <Modal.Actions>
294
- <button data-autofocus type="button" tabIndex={0} className={cx('byline-form-actions-sr-only', styles['sr-only'])}>
295
- no action
296
- </button>
297
- <Button size="sm" style={{ minWidth: '80px' }} intent="noeffect" onClick={() => {
298
- setShowDeleteConfirm(false);
299
- }}>
300
- {t('common.actions.cancel')}
301
- </Button>
302
- <Button size="sm" style={{ minWidth: '80px' }} intent="danger" disabled={disabled} onClick={handleOnDelete}>
303
- {t('common.actions.delete')}
304
- </Button>
305
- </Modal.Actions>
306
- </Modal.Container>
307
- </Modal>
308
-
309
- <Modal isOpen={showDuplicateConfirm} closeOnOverlayClick={!duplicateBusy} onDismiss={() => {
310
- if (!duplicateBusy)
311
- setShowDuplicateConfirm(false);
312
- }}>
313
- <Modal.Container style={{ maxWidth: '560px' }}>
314
- <Modal.Header className={cx('byline-form-actions-modal-head', styles['modal-head'])}>
315
- <h3 className={cx('byline-form-actions-modal-title', styles['modal-title'])}>
316
- {t('documentActions.duplicate.title')}
317
- </h3>
318
- <IconButton arial-label={t('common.actions.close')} size="xs" onClick={() => {
319
- if (!duplicateBusy)
320
- setShowDuplicateConfirm(false);
321
- }}>
322
- <CloseIcon width="16px" height="16px" svgClassName="white-icon"/>
323
- </IconButton>
324
- </Modal.Header>
325
- <Modal.Content className="prose">
326
- <p className="m-0">{t('documentActions.duplicate.intro')}</p>
327
- <ul className={cx('byline-form-actions-list', styles.list)}>
328
- <li>
329
- {t('documentActions.duplicate.bulletTitle')}{' '}
330
- <code>{DUPLICATE_TITLE_SUFFIX.trim()}</code>.
331
- </li>
332
- <li>{t('documentActions.duplicate.bulletPath')}</li>
333
- </ul>
334
- {sourceTitle != null && sourceTitle.length > 0 && (<div className={cx('byline-form-actions-preview', styles.preview)}>
335
- <div className={cx('byline-form-actions-preview-label', styles['preview-label'])}>
336
- {t('documentActions.duplicate.previewLabel')}
337
- </div>
338
- <div className={cx('byline-form-actions-preview-row', styles['preview-row'])}>
339
- <span className={cx('byline-form-actions-preview-before', styles['preview-before'])}>
340
- {duplicatePreviewBefore}
341
- </span>
342
- <span className={cx('byline-form-actions-preview-arrow', styles['preview-arrow'])}>
343
- →
344
- </span>
345
- <span className={cx('byline-form-actions-preview-after', styles['preview-after'])}>
346
- {duplicatePreviewAfter}
347
- </span>
348
- </div>
349
- </div>)}
350
- </Modal.Content>
351
- <Modal.Actions>
352
- <button data-autofocus type="button" tabIndex={0} className={cx('byline-form-actions-sr-only', styles['sr-only'])}>
353
- no action
354
- </button>
355
- <Button size="sm" style={{ minWidth: '80px' }} intent="noeffect" onClick={() => {
356
- if (!duplicateBusy)
357
- setShowDuplicateConfirm(false);
358
- }} disabled={disabled || duplicateBusy}>
359
- {t('common.actions.cancel')}
360
- </Button>
361
- <Button size="sm" style={{ minWidth: '80px' }} intent="primary" onClick={handleOnDuplicate} disabled={disabled || duplicateBusy}>
362
- {duplicateBusy
363
- ? t('documentActions.duplicate.busyButton')
364
- : t('common.actions.duplicate')}
365
- </Button>
366
- </Modal.Actions>
367
- </Modal.Container>
368
- </Modal>
369
-
370
- <Modal isOpen={showCopyToLocaleConfirm} closeOnOverlayClick={!copyToLocaleBusy} onDismiss={() => {
371
- if (!copyToLocaleBusy)
372
- setShowCopyToLocaleConfirm(false);
373
- }}>
374
- <Modal.Container style={{ maxWidth: '560px' }}>
375
- <Modal.Header className={cx('byline-form-actions-modal-head', styles['modal-head'])}>
376
- <h3 className={cx('byline-form-actions-modal-title', styles['modal-title'])}>
377
- {t('documentActions.copyToLocale.title')}
378
- </h3>
379
- <IconButton arial-label={t('common.actions.close')} size="xs" onClick={() => {
380
- if (!copyToLocaleBusy)
381
- setShowCopyToLocaleConfirm(false);
382
- }}>
383
- <CloseIcon width="16px" height="16px" svgClassName="white-icon"/>
384
- </IconButton>
385
- </Modal.Header>
386
- <Modal.Content>
387
- <p>{t('documentActions.copyToLocale.intro')}</p>
388
- <div className={cx('byline-form-actions-copy-row', styles['copy-row'])} style={{ marginTop: 'var(--spacing-12)' }}>
389
- <span className={cx('byline-form-actions-copy-label', styles['copy-label'])} style={{ fontWeight: 500 }}>
390
- {t('documentActions.copyToLocale.fromLabel')}&nbsp;
391
- </span>
392
- <span className={cx('byline-form-actions-copy-source', styles['copy-source'])}>
393
- {sourceLocaleLabel}
394
- </span>
395
- </div>
396
- <div className={cx('byline-form-actions-copy-row', styles['copy-row'])} style={{ marginTop: 'var(--spacing-12)' }}>
397
- <span className={cx('byline-form-actions-copy-label', styles['copy-label'])} style={{ fontWeight: 500, marginRight: 'var(--spacing-8)' }}>
398
- {t('documentActions.copyToLocale.toLabel')}
399
- </span>
400
- <Select size="sm" ariaLabel={t('documentActions.copyToLocale.targetAriaLabel')} value={copyTargetLocale} items={availableTargetLocales.map((loc) => ({
401
- value: loc.code,
402
- label: loc.label,
403
- }))} onValueChange={(value) => {
404
- if (value != null)
405
- setCopyTargetLocale(value);
406
- }} disabled={disabled || copyToLocaleBusy}/>
407
- </div>
408
- <div className={cx('byline-form-actions-copy-row', styles['copy-row'])} style={{ marginTop: 'var(--spacing-16)' }}>
409
- <Checkbox id="copy-to-locale-overwrite" name="overwrite" label={t('documentActions.copyToLocale.overwriteLabel')} checked={copyOverwrite} disabled={disabled || copyToLocaleBusy} helpText={t('documentActions.copyToLocale.overwriteHelp')} onCheckedChange={(value) => {
410
- setCopyOverwrite(value === true);
411
- }}/>
412
- </div>
413
- </Modal.Content>
414
- <Modal.Actions>
415
- <button data-autofocus type="button" tabIndex={0} className={cx('byline-form-actions-sr-only', styles['sr-only'])}>
416
- no action
417
- </button>
418
- <Button size="sm" style={{ minWidth: '80px' }} intent="noeffect" onClick={() => {
419
- if (!copyToLocaleBusy)
420
- setShowCopyToLocaleConfirm(false);
421
- }} disabled={disabled || copyToLocaleBusy}>
422
- {t('common.actions.cancel')}
423
- </Button>
424
- <Button size="sm" style={{ minWidth: '80px' }} intent="primary" onClick={handleOnCopyToLocale} disabled={disabled || copyToLocaleBusy || !copyTargetLocale}>
425
- {copyToLocaleBusy
426
- ? t('documentActions.copyToLocale.busyButton')
427
- : t('documentActions.copyToLocale.confirmButton')}
428
- </Button>
429
- </Modal.Actions>
430
- </Modal.Container>
431
- </Modal>
432
-
433
- <Modal isOpen={showDeleteLocaleConfirm} closeOnOverlayClick={!deleteLocaleBusy} onDismiss={() => {
434
- if (!deleteLocaleBusy)
435
- setShowDeleteLocaleConfirm(false);
436
- }}>
437
- <Modal.Container style={{ maxWidth: '560px' }}>
438
- <Modal.Header className={cx('byline-form-actions-modal-head', styles['modal-head'])}>
439
- <h3 className={cx('byline-form-actions-modal-title', styles['modal-title'])}>
440
- {t('documentActions.deleteLocale.title')}
441
- </h3>
442
- <IconButton arial-label={t('common.actions.close')} size="xs" onClick={() => {
443
- if (!deleteLocaleBusy)
444
- setShowDeleteLocaleConfirm(false);
445
- }}>
446
- <CloseIcon width="16px" height="16px" svgClassName="white-icon"/>
447
- </IconButton>
448
- </Modal.Header>
449
- <Modal.Content>
450
- <p>{t('documentActions.deleteLocale.intro')}</p>
451
- <div className={cx('byline-form-actions-copy-row', styles['copy-row'])} style={{ marginTop: 'var(--spacing-12)' }}>
452
- <span className={cx('byline-form-actions-copy-label', styles['copy-label'])} style={{ fontWeight: 500, marginRight: 'var(--spacing-8)' }}>
453
- {t('documentActions.deleteLocale.localeLabel')}
454
- </span>
455
- <Select size="sm" ariaLabel={t('documentActions.deleteLocale.targetAriaLabel')} value={deleteTargetLocale} items={deletableLocales.map((loc) => ({
456
- value: loc.code,
457
- label: loc.label,
458
- }))} onValueChange={(value) => {
459
- if (value != null)
460
- setDeleteTargetLocale(value);
461
- }} disabled={disabled || deleteLocaleBusy}/>
462
- </div>
463
- <p style={{ marginTop: 'var(--spacing-12)' }}>
464
- {t('documentActions.deleteLocale.warning')}
465
- </p>
466
- </Modal.Content>
467
- <Modal.Actions>
468
- <button data-autofocus type="button" tabIndex={0} className={cx('byline-form-actions-sr-only', styles['sr-only'])}>
469
- no action
470
- </button>
471
- <Button size="sm" style={{ minWidth: '80px' }} intent="noeffect" onClick={() => {
472
- if (!deleteLocaleBusy)
473
- setShowDeleteLocaleConfirm(false);
474
- }} disabled={disabled || deleteLocaleBusy}>
475
- {t('common.actions.cancel')}
476
- </Button>
477
- <Button size="sm" style={{ minWidth: '80px' }} intent="danger" onClick={handleOnDeleteLocale} disabled={disabled || deleteLocaleBusy || !deleteTargetLocale}>
478
- {deleteLocaleBusy
479
- ? t('documentActions.deleteLocale.busyButton')
480
- : t('documentActions.deleteLocale.confirmButton')}
481
- </Button>
482
- </Modal.Actions>
483
- </Modal.Container>
484
- </Modal>
485
- </>);
486
- }