@byline/admin 5.1.3 → 5.2.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.
Files changed (93) hide show
  1. package/dist/modules/analytics/components/dashboard.d.ts +13 -3
  2. package/dist/modules/analytics/components/dashboard.js +86 -68
  3. package/dist/modules/analytics/components/dashboard.module.js +10 -1
  4. package/dist/modules/analytics/components/dashboard_module.css +57 -0
  5. package/dist/modules/analytics/components/ranked-list-modal.d.ts +32 -0
  6. package/dist/modules/analytics/components/ranked-list-modal.js +145 -0
  7. package/dist/modules/analytics/components/ranking.d.ts +45 -0
  8. package/dist/modules/analytics/components/ranking.js +71 -0
  9. package/dist/modules/analytics/components/ranking.test.node.d.ts +8 -0
  10. package/package.json +14 -14
  11. package/src/modules/analytics/components/dashboard.module.css +69 -0
  12. package/src/modules/analytics/components/dashboard.tsx +105 -83
  13. package/src/modules/analytics/components/ranked-list-modal.test.tsx +154 -0
  14. package/src/modules/analytics/components/ranked-list-modal.tsx +159 -0
  15. package/src/modules/analytics/components/ranking.test.node.ts +43 -0
  16. package/src/modules/analytics/components/ranking.tsx +122 -0
  17. package/dist/exports-parity.test.node.js +0 -37
  18. package/dist/fields/array/array-field.jsx +0 -209
  19. package/dist/fields/blocks/blocks-field.jsx +0 -248
  20. package/dist/fields/checkbox/checkbox-field.jsx +0 -24
  21. package/dist/fields/code/code-editor.jsx +0 -211
  22. package/dist/fields/code/code-field.jsx +0 -109
  23. package/dist/fields/column-formatter.jsx +0 -25
  24. package/dist/fields/date-time-formatter.jsx +0 -18
  25. package/dist/fields/datetime/datetime-field.jsx +0 -22
  26. package/dist/fields/draggable-context-menu.jsx +0 -50
  27. package/dist/fields/field-admin.test.node.js +0 -40
  28. package/dist/fields/field-renderer.jsx +0 -136
  29. package/dist/fields/field-services-context.jsx +0 -17
  30. package/dist/fields/file/file-field.jsx +0 -176
  31. package/dist/fields/file/file-upload-field.jsx +0 -89
  32. package/dist/fields/group/group-field.jsx +0 -46
  33. package/dist/fields/image/image-field.jsx +0 -183
  34. package/dist/fields/image/image-upload-field.jsx +0 -157
  35. package/dist/fields/local-date-time.jsx +0 -69
  36. package/dist/fields/locale-badge.jsx +0 -19
  37. package/dist/fields/numerical/numerical-field.jsx +0 -93
  38. package/dist/fields/relation/relation-column-formatter.jsx +0 -74
  39. package/dist/fields/relation/relation-display.jsx +0 -118
  40. package/dist/fields/relation/relation-field.jsx +0 -108
  41. package/dist/fields/relation/relation-many-field.jsx +0 -142
  42. package/dist/fields/relation/relation-picker.jsx +0 -240
  43. package/dist/fields/relation/relation-summary.jsx +0 -45
  44. package/dist/fields/select/select-field.jsx +0 -27
  45. package/dist/fields/sortable-item.jsx +0 -54
  46. package/dist/fields/text/text-field.jsx +0 -79
  47. package/dist/fields/text-area/text-area-field.jsx +0 -79
  48. package/dist/forms/available-locales-reconcile.test.node.js +0 -43
  49. package/dist/forms/available-locales-widget.jsx +0 -63
  50. package/dist/forms/document-actions.jsx +0 -486
  51. package/dist/forms/form-context.jsx +0 -598
  52. package/dist/forms/form-modals.jsx +0 -128
  53. package/dist/forms/form-renderer.jsx +0 -532
  54. package/dist/forms/form-status-display.jsx +0 -69
  55. package/dist/forms/navigation-guard.jsx +0 -68
  56. package/dist/forms/nested-path.test.node.js +0 -115
  57. package/dist/forms/path-widget.jsx +0 -125
  58. package/dist/forms/pending-uploads.test.node.js +0 -19
  59. package/dist/forms/repeating-items.test.node.js +0 -29
  60. package/dist/forms/scheduled-publication-control.jsx +0 -389
  61. package/dist/forms/scheduled-publication-state.test.node.js +0 -121
  62. package/dist/forms/scheduled-publication-time.test.node.js +0 -70
  63. package/dist/forms/status-transitions.test.node.js +0 -76
  64. package/dist/forms/tree-placement-widget.jsx +0 -160
  65. package/dist/forms/upload-executor.test.node.js +0 -368
  66. package/dist/forms/use-form-layout.test.node.js +0 -69
  67. package/dist/modules/admin-account/components/change-password.jsx +0 -169
  68. package/dist/modules/admin-account/components/container.jsx +0 -164
  69. package/dist/modules/admin-account/components/preferences.jsx +0 -148
  70. package/dist/modules/admin-account/components/theme-switch.jsx +0 -52
  71. package/dist/modules/admin-account/components/update.jsx +0 -186
  72. package/dist/modules/admin-permissions/components/inspector.jsx +0 -200
  73. package/dist/modules/admin-preferences/schemas.test.node.js +0 -48
  74. package/dist/modules/admin-roles/components/create.jsx +0 -155
  75. package/dist/modules/admin-roles/components/permissions.jsx +0 -239
  76. package/dist/modules/admin-roles/components/update.jsx +0 -148
  77. package/dist/modules/admin-users/components/create.jsx +0 -206
  78. package/dist/modules/admin-users/components/roles.jsx +0 -126
  79. package/dist/modules/admin-users/components/set-password.jsx +0 -133
  80. package/dist/modules/admin-users/components/update.jsx +0 -215
  81. package/dist/modules/analytics/components/dashboard.jsx +0 -208
  82. package/dist/modules/analytics/components/dashboard.test.node.js +0 -162
  83. package/dist/modules/analytics/components/timeseries.jsx +0 -193
  84. package/dist/modules/auth/components/sign-in-form.jsx +0 -89
  85. package/dist/modules/auth/safe-redirect.test.node.js +0 -40
  86. package/dist/modules/auth/sign-in-form-props.test.node.js +0 -7
  87. package/dist/presentation/group.jsx +0 -26
  88. package/dist/presentation/row.jsx +0 -23
  89. package/dist/presentation/tabs.jsx +0 -39
  90. package/dist/services/admin-services-context.jsx +0 -17
  91. package/dist/widgets/diff-viewer/diff-modal.jsx +0 -110
  92. package/dist/widgets/source-locale-badge/source-locale-badge.jsx +0 -28
  93. 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
- }