@elytracms/studio 0.0.70 → 0.0.71
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/components/account/self-account-view-source.js +1 -0
- package/dist/components/account/self-account-view-source.js.map +1 -1
- package/dist/components/assets/assets-collection-view-source.js +1 -0
- package/dist/components/assets/assets-collection-view-source.js.map +1 -1
- package/dist/components/cms/collection-table.js +19 -2
- package/dist/components/cms/collection-table.js.map +1 -1
- package/dist/components/cms/detail-editor.js +73 -51
- package/dist/components/cms/detail-editor.js.map +1 -1
- package/dist/components/cms/duplicate-document.d.ts +45 -0
- package/dist/components/cms/duplicate-document.js +141 -0
- package/dist/components/cms/duplicate-document.js.map +1 -0
- package/dist/components/cms/field-anchors.d.ts +67 -0
- package/dist/components/cms/field-anchors.js +91 -0
- package/dist/components/cms/field-anchors.js.map +1 -0
- package/dist/components/cms/issue-copy.d.ts +45 -0
- package/dist/components/cms/issue-copy.js +89 -0
- package/dist/components/cms/issue-copy.js.map +1 -0
- package/dist/components/cms/issues-pane.d.ts +11 -2
- package/dist/components/cms/issues-pane.js +22 -4
- package/dist/components/cms/issues-pane.js.map +1 -1
- package/dist/components/cms/object-array-field.d.ts +2 -0
- package/dist/components/cms/object-array-field.js +75 -21
- package/dist/components/cms/object-array-field.js.map +1 -1
- package/dist/components/cms/row-actions.d.ts +10 -3
- package/dist/components/cms/row-actions.js +8 -0
- package/dist/components/cms/row-actions.js.map +1 -1
- package/dist/components/cms/use-field-focus.d.ts +25 -9
- package/dist/components/cms/use-field-focus.js +80 -15
- package/dist/components/cms/use-field-focus.js.map +1 -1
- package/dist/components/collection-view/cms-collection-view-source.js +1 -0
- package/dist/components/collection-view/cms-collection-view-source.js.map +1 -1
- package/dist/components/redirects/redirects-collection-view-source.js +1 -0
- package/dist/components/redirects/redirects-collection-view-source.js.map +1 -1
- package/dist/components/users/users-collection-view-source.js +1 -0
- package/dist/components/users/users-collection-view-source.js.map +1 -1
- package/dist/customization.d.ts +9 -0
- package/dist/customization.js +8 -0
- package/dist/customization.js.map +1 -1
- package/dist/lib/collection-view/collection-view-source.d.ts +8 -0
- package/dist/lib/collection-view/collection-view-source.js.map +1 -1
- package/dist/lib/i18n/messages.de.js +38 -1
- package/dist/lib/i18n/messages.de.js.map +1 -1
- package/dist/lib/i18n/messages.en.d.ts +34 -0
- package/dist/lib/i18n/messages.en.js +41 -1
- package/dist/lib/i18n/messages.en.js.map +1 -1
- package/dist/lib/persona/labels.d.ts +5 -1
- package/dist/lib/persona/labels.js +43 -30
- package/dist/lib/persona/labels.js.map +1 -1
- package/dist/previews/enumerate.d.ts +68 -0
- package/dist/previews/enumerate.js +78 -0
- package/dist/previews/enumerate.js.map +1 -0
- package/dist/previews/harness.d.ts +50 -0
- package/dist/previews/harness.js +123 -0
- package/dist/previews/harness.js.map +1 -0
- package/dist/previews/manifest-format.d.ts +57 -0
- package/dist/previews/manifest-format.js +59 -0
- package/dist/previews/manifest-format.js.map +1 -0
- package/dist/ui/field.d.ts +24 -1
- package/dist/ui/field.js +23 -2
- package/dist/ui/field.js.map +1 -1
- package/package.json +10 -6
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
2
|
import { Link, STUDIO_PROJECT_ROUTE, studioPath, useNavigate } from '@elytracms/studio/router';
|
|
3
|
-
import { Check, ChevronDown, CircleAlert, CircleCheck, Eye, EyeOff, GitCompareArrows, History, Loader2, Lock, MoreHorizontal, RotateCcw, Save, Tag, Trash2, X, } from 'lucide-react';
|
|
3
|
+
import { Check, ChevronDown, CircleAlert, CircleCheck, Copy, Eye, EyeOff, GitCompareArrows, History, Loader2, Lock, MoreHorizontal, RotateCcw, Save, Tag, Trash2, X, } from 'lucide-react';
|
|
4
4
|
import { useCallback, useMemo, useState } from 'react';
|
|
5
5
|
import { FIELD_WIDTH_SPANS, SIDEBAR_FIELD_GROUP, documentKey, groupOrder, groupSectionOf, hasAliases, hiddenGroupNames, isBlocksField, isFieldHidden, isFieldLocalized, isSingleton, resolveDocumentForLocale, resolveDocumentTitle, resolveField, sidebarFields, tabDescriptorOf, tabOrder, titleFieldOf, staticFieldHidden, } from '@elytracms/core/cms-core';
|
|
6
6
|
import { blockRootsOf } from '@elytracms/core/project-graph';
|
|
@@ -13,6 +13,7 @@ import { LocaleSwitcher, activeLocaleValues, displayedFieldFallback, fieldConnec
|
|
|
13
13
|
import { FallbackChip } from './cell.js';
|
|
14
14
|
import { FieldConnectionControl } from './field-connection.js';
|
|
15
15
|
import { FieldControl } from './field-control.js';
|
|
16
|
+
import { duplicateFromSource } from './duplicate-document.js';
|
|
16
17
|
// EC-322: the lazy-wrapped page builder — the heavy canvas subtree code-splits out
|
|
17
18
|
// of the detail-view critical path and downloads only when a `blocks` field opens.
|
|
18
19
|
import { CompositionFieldEditor } from '../builder/composition-field-editor-lazy.js';
|
|
@@ -44,6 +45,7 @@ import { usePublishedPerspective } from './use-published-perspective.js';
|
|
|
44
45
|
import { useSaveMode } from './use-save-mode.js';
|
|
45
46
|
import { useDiscardController } from './use-discard-controller.js';
|
|
46
47
|
import { usePublishController, publishGate } from './use-publish-controller.js';
|
|
48
|
+
import { FieldFocusRequestContext, FieldPathContext } from './field-anchors.js';
|
|
47
49
|
import { useFieldFocus } from './use-field-focus.js';
|
|
48
50
|
import { useCompareSelection } from './use-compare-selection.js';
|
|
49
51
|
import { useLiveUrl } from './use-live-url.js';
|
|
@@ -221,10 +223,12 @@ function EditorBody({ projectId, collection, doc, focusPath, formAppendix, }) {
|
|
|
221
223
|
// not a field-row — it takes over its tab with the full page-builder canvas.
|
|
222
224
|
// Convention: such a field lives on its own dedicated fieldset tab.
|
|
223
225
|
const canvasField = tabFields.find(isBlocksField);
|
|
224
|
-
// EC-172 / EC-283 — the field-focus/highlight controller (jump-to-field
|
|
225
|
-
// reference deep-link `focusPath` and the Issues pane)
|
|
226
|
-
//
|
|
227
|
-
|
|
226
|
+
// EC-172 / EC-283 / EC-443 — the field-focus/highlight controller (jump-to-field
|
|
227
|
+
// from the reference deep-link `focusPath` and the Issues pane), now path-aware:
|
|
228
|
+
// `focusRequest` is provided below via `FieldFocusRequestContext` so nested
|
|
229
|
+
// array/object controls can auto-expand and ring the exact target. Owns the
|
|
230
|
+
// highlight + the controlled inspector-pane state and the scroll/clear timers.
|
|
231
|
+
const { highlightedField, focusRequest, inspectorPaneId, setInspectorPaneId, focusField } = useFieldFocus({
|
|
228
232
|
collection,
|
|
229
233
|
source,
|
|
230
234
|
focusPath,
|
|
@@ -314,7 +318,7 @@ function EditorBody({ projectId, collection, doc, focusPath, formAppendix, }) {
|
|
|
314
318
|
const connection = localized
|
|
315
319
|
? fieldConnection(collection, draft, field.name, locale, source.localeConfig)
|
|
316
320
|
: null;
|
|
317
|
-
return (_jsx(Field, { className: cn('rounded-md transition-shadow', highlightedField === field.name && 'bg-primary/5 p-2 ring-2 ring-primary/70'), label: _jsxs(Row, { gap: "2", children: [_jsx("span", { children: fieldLabel(field) }), field.validation?.required ? _jsx("span", { className: "text-destructive", children: "*" }) : null, localized ? _jsx(Badge, { variant: "outline", children: locale }) : null, fieldLevelReadOnly ? (_jsxs(Badge, { variant: "outline", children: [_jsx(Lock, { className: "size-3" }), " ", translate('cms.editor.readOnlyBadge')] })) : null, _jsx(Spacer, {}), connection && !fieldReadOnly ? (_jsx(FieldConnectionControl, { connection: connection, fieldName: field.name, locale: locale, onFillFromBase: () => setDraft((d) => fillFieldFromBase(collection, d, field.name, locale, source.localeConfig)), onResetToBase: () => setDraft((d) => resetFieldToBase(collection, d, field.name, locale)) })) : null] }), htmlFor: `f-${field.name}`, hint: localizedLabel(field.form?.description) || undefined, error: fieldReadOnly ? undefined : errorsByField.get(field.name), children: _jsx(FieldControl, { id: `f-${field.name}`, field: field, value: getValue(field), onChange: (v) => setValue(field, v), readOnly: fieldReadOnly }) }, field.name));
|
|
321
|
+
return (_jsx(Field, { className: cn('rounded-md transition-shadow', highlightedField === field.name && 'bg-primary/5 p-2 ring-2 ring-primary/70'), label: _jsxs(Row, { gap: "2", children: [_jsx("span", { children: fieldLabel(field) }), field.validation?.required ? _jsx("span", { className: "text-destructive", children: "*" }) : null, localized ? _jsx(Badge, { variant: "outline", children: locale }) : null, fieldLevelReadOnly ? (_jsxs(Badge, { variant: "outline", children: [_jsx(Lock, { className: "size-3" }), " ", translate('cms.editor.readOnlyBadge')] })) : null, _jsx(Spacer, {}), connection && !fieldReadOnly ? (_jsx(FieldConnectionControl, { connection: connection, fieldName: field.name, locale: locale, onFillFromBase: () => setDraft((d) => fillFieldFromBase(collection, d, field.name, locale, source.localeConfig)), onResetToBase: () => setDraft((d) => resetFieldToBase(collection, d, field.name, locale)) })) : null] }), htmlFor: `f-${field.name}`, fieldPath: field.name, hint: localizedLabel(field.form?.description) || undefined, error: fieldReadOnly ? undefined : errorsByField.get(field.name), children: _jsx(FieldPathContext.Provider, { value: field.name, children: _jsx(FieldControl, { id: `f-${field.name}`, field: field, value: getValue(field), onChange: (v) => setValue(field, v), readOnly: fieldReadOnly }) }) }, field.name));
|
|
318
322
|
}
|
|
319
323
|
// The frame-owned `Versions` pane (EC-224) — appended after any view-supplied
|
|
320
324
|
// pane (the canvas's `Properties`). One instance feeds both the form view's
|
|
@@ -390,6 +394,24 @@ function EditorBody({ projectId, collection, doc, focusPath, formAppendix, }) {
|
|
|
390
394
|
dirty,
|
|
391
395
|
hasUnpublishedRefs,
|
|
392
396
|
});
|
|
397
|
+
// EC-442 (amended): Duplicate in the header `…` menu — gated on the explicit
|
|
398
|
+
// `duplicate` capability (CMS collections only, owner call 2026-07-17) + a
|
|
399
|
+
// detail surface to land on, and never a singleton (its shape IS "exactly one
|
|
400
|
+
// document"; a copy would be EC-274 drift by construction, mirroring the
|
|
401
|
+
// hidden Delete).
|
|
402
|
+
const canDuplicate = !singleton && source.capabilities.duplicate && source.detailPath !== undefined;
|
|
403
|
+
function handleDuplicate() {
|
|
404
|
+
// Copy the CURRENT draft buffer — what the editor sees — not the possibly
|
|
405
|
+
// autosave-lagged stored row. The duplicate is a fresh DRAFT, never
|
|
406
|
+
// auto-published (live-is-published-only), created through the seam's
|
|
407
|
+
// existing create+save write paths (`duplicateFromSource`).
|
|
408
|
+
const newRef = duplicateFromSource(source, draft);
|
|
409
|
+
toast.success(translate('cms.editor.duplicateCreated'));
|
|
410
|
+
const splat = source.detailPath?.(newRef.id);
|
|
411
|
+
if (splat === undefined)
|
|
412
|
+
return;
|
|
413
|
+
void navigate({ to: STUDIO_PROJECT_ROUTE, params: { projectId, _splat: splat } });
|
|
414
|
+
}
|
|
393
415
|
function handleConfirmedAction() {
|
|
394
416
|
if (confirmAction === 'unpublish') {
|
|
395
417
|
setConfirmAction(null);
|
|
@@ -483,51 +505,51 @@ function EditorBody({ projectId, collection, doc, focusPath, formAppendix, }) {
|
|
|
483
505
|
setCompareBase,
|
|
484
506
|
setCompareTarget,
|
|
485
507
|
exitCompare,
|
|
486
|
-
}, children: _jsxs("div", { className: cn('flex h-full min-h-0 flex-col', viewReadOnly && 'bg-info/[0.04]'), children: [_jsxs("div", { className: "shrink-0 border-b border-border px-4 py-3 sm:px-6", children: [_jsxs(Row, { gap: "3", align: "start", children: [_jsxs(Stack, { gap: "1", className: "min-w-0", children: [_jsxs(Row, { gap: "2", align: "center", className: "min-w-0", children: [_jsx(Heading, { level: 1, size: "h2", className: "truncate", children: singleton ? localizedLabel(collection.form?.label, collection.id) : title || doc.id }), singleton ? null : _jsx(FallbackChip, { fallback: titleFallback, testId: "doc-title-fallback" })] }), routableCollection ? (_jsx(DocumentUrlPath, { projectId: projectId, collection: collection, doc: draft, siblings: source.documents(), routes: resolveDeploymentRoutes(), localeConfig: source.localeConfig, locale: locale, live: headerLive })) : null] }), _jsx(Spacer, {}), _jsxs(Stack, { gap: "none", align: "end", className: "min-w-0", children: [_jsx(Text, { size: "xs", tone: "muted", className: "whitespace-nowrap", "data-testid": "autosave-status", children: source.lastError
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
508
|
+
}, children: _jsx(FieldFocusRequestContext.Provider, { value: focusRequest, children: _jsxs("div", { className: cn('flex h-full min-h-0 flex-col', viewReadOnly && 'bg-info/[0.04]'), children: [_jsxs("div", { className: "shrink-0 border-b border-border px-4 py-3 sm:px-6", children: [_jsxs(Row, { gap: "3", align: "start", children: [_jsxs(Stack, { gap: "1", className: "min-w-0", children: [_jsxs(Row, { gap: "2", align: "center", className: "min-w-0", children: [_jsx(Heading, { level: 1, size: "h2", className: "truncate", children: singleton ? localizedLabel(collection.form?.label, collection.id) : title || doc.id }), singleton ? null : _jsx(FallbackChip, { fallback: titleFallback, testId: "doc-title-fallback" })] }), routableCollection ? (_jsx(DocumentUrlPath, { projectId: projectId, collection: collection, doc: draft, siblings: source.documents(), routes: resolveDeploymentRoutes(), localeConfig: source.localeConfig, locale: locale, live: headerLive })) : null] }), _jsx(Spacer, {}), _jsxs(Stack, { gap: "none", align: "end", className: "min-w-0", children: [_jsx(Text, { size: "xs", tone: "muted", className: "whitespace-nowrap", "data-testid": "autosave-status", children: source.lastError
|
|
509
|
+
? translate('cms.editor.saveFailed')
|
|
510
|
+
: explicitSave
|
|
511
|
+
? dirty
|
|
512
|
+
? translate('cms.editor.unsavedChanges')
|
|
513
|
+
: translate('cms.editor.saved')
|
|
514
|
+
: dirty
|
|
515
|
+
? translate('cms.editor.saving')
|
|
516
|
+
: translate('cms.editor.saved') }), source.lastError?.message ? (_jsx(Text, { size: "xs", className: "max-w-64 truncate text-destructive", title: source.lastError.message, "data-testid": "save-error-message", children: source.lastError.message })) : null] }), canPublish ? _jsx(DocStateBadge, { published: published }) : null, canPublish ? (_jsxs(Row, { gap: "px", align: "center", children: [_jsx(Button, { size: "sm", onClick: handlePublish, disabled: publishDisabled, title: publishTitle, className: canPublishClose ? 'rounded-r-none' : undefined, "data-testid": "doc-publish", children: publishButtonLabel }), canPublishClose ? (_jsxs(DropdownMenu, { children: [_jsx(DropdownMenuTrigger, { asChild: true, children: _jsx(Button, { size: "sm", disabled: publishDisabled, "aria-label": translate('cms.editor.publishActions'), "data-testid": "doc-publish-actions", className: "rounded-l-none border-l border-primary-foreground/25 px-1.5", children: _jsx(ChevronDown, { className: "size-3.5" }) }) }), _jsx(DropdownMenuContent, { align: "end", children: _jsxs(DropdownMenuItem, { onClick: handlePublishAndClose, "data-testid": "doc-publish-and-close", children: [_jsx(Save, {}), " ", translate('cms.editor.publishAndClose')] }) })] })) : null] })) : null, explicitSave ? (_jsxs(Button, { size: "sm", onClick: save, disabled: !dirty, "data-testid": "doc-save", children: [_jsx(Check, {}), " ", translate('common.save')] })) : null, !viewReadOnly ? (_jsx(TranslateDocumentAction, { projectId: projectId, collection: collection, docRef: ref, document: draft, localeConfig: source.localeConfig, localesEnabled: canLocales, onApplied: (next) => {
|
|
517
|
+
source.save(next);
|
|
518
|
+
replaceDraft(next);
|
|
519
|
+
} })) : null, published || !singleton ? (_jsxs(DropdownMenu, { children: [_jsx(DropdownMenuTrigger, { asChild: true, children: _jsx(Button, { variant: "outline", size: "icon-sm", "aria-label": translate('cms.editor.moreActions'), children: _jsx(MoreHorizontal, {}) }) }), _jsxs(DropdownMenuContent, { align: "end", children: [canDuplicate ? (_jsxs(DropdownMenuItem, { "data-testid": "doc-duplicate", onClick: handleDuplicate, children: [_jsx(Copy, {}), " ", translate('common.duplicate')] })) : null, discardStatus !== 'hidden' ? (_jsxs(DropdownMenuItem, { "data-testid": "doc-discard-draft", disabled: discardStatus !== 'ready', title: discardDisabledReason, onClick: () => setDiscardConfirm(true), children: [_jsx(RotateCcw, {}), " ", translate('cms.editor.discardDraft')] })) : null, published ? (_jsxs(DropdownMenuItem, { onClick: () => setConfirmAction('unpublish'), children: [_jsx(EyeOff, {}), " ", translate('cms.editor.unpublish')] })) : null, singleton ? null : (_jsxs(DropdownMenuItem, { "data-testid": "doc-delete", className: "text-destructive", onClick: () => setConfirmAction('delete'), children: [_jsx(Trash2, {}), " ", translate('common.delete')] }))] })] })) : null] }), showLensRow ? (_jsxs(Row, { gap: "3", className: "mt-3", wrap: true, children: [_jsxs(Row, { gap: "1", align: "center", children: [showContentTabs ? tabs.map((tab) => renderContentTab(tab)) : null, showReferences ? (_jsxs(_Fragment, { children: [_jsx("div", { className: "mx-1 h-4 w-px bg-border", "aria-hidden": true }), renderContentTab(REFERENCES_TAB)] })) : null, compare ? (_jsxs("span", { className: cn('inline-flex items-center gap-1 rounded-md px-2.5 py-1 text-xs font-medium transition-colors', activeTab === COMPARE_TAB
|
|
520
|
+
? 'bg-accent text-accent-foreground'
|
|
521
|
+
: 'text-muted-foreground hover:bg-muted'), children: [_jsxs("button", { type: "button", onClick: () => setActiveTab(COMPARE_TAB), "aria-pressed": activeTab === COMPARE_TAB, className: "inline-flex items-center gap-1", children: [_jsx(GitCompareArrows, { className: "size-3", "aria-hidden": true }), " ", translate('cms.version.compare')] }), _jsx("button", { type: "button", onClick: exitCompare, "aria-label": translate('cms.version.closeCompare'), className: "ml-0.5 rounded p-0.5 hover:bg-background/40", children: _jsx(X, { className: "size-3" }) })] })) : null] }), _jsx(Spacer, {}), canLocales ? _jsx(LocaleSwitcher, {}) : null, canPublish ? (_jsxs(Row, { gap: "px", className: "rounded-md bg-muted/60 p-0.5", children: [_jsx(PerspectiveButton, { label: translate('cms.editor.draftState'), active: perspective === 'draft', onClick: () => setPerspective('draft') }), _jsx(PerspectiveButton, { label: translate('cms.editor.publishedState'), active: perspective === 'published', accent: "info", disabled: !published, title: published ? undefined : translate('cms.editor.publishToViewPublished'), onClick: () => setPerspective('published') })] })) : null] })) : null] }), viewReadOnly ? (_jsxs(Row, { gap: "2", className: "shrink-0 border-b border-info/30 bg-info/10 px-4 py-1.5 text-info sm:px-6", "data-testid": "published-view-banner", children: [_jsx(Lock, { className: "size-3.5 shrink-0", "aria-hidden": true }), _jsx(Text, { size: "xs", className: "text-info", children: translate('cms.editor.publishedReadOnlyBanner') }), _jsx(Spacer, {}), _jsx("button", { type: "button", onClick: () => setPerspective('draft'), className: "text-xs font-medium underline-offset-2 hover:underline", children: translate('cms.editor.switchToDraft') })] })) : null, publishedPending ? (_jsx("div", { className: "grid min-h-0 flex-1 place-items-center p-8", children: _jsx(Text, { size: "sm", tone: "muted", children: publishedSnapshot.status === 'loading'
|
|
522
|
+
? translate('cms.editor.loadingPublishedVersion')
|
|
523
|
+
: translate('cms.editor.noPublishedVersion') }) })) : activeTab === COMPARE_TAB && compare ? (_jsxs("div", { className: "flex min-h-0 flex-1 overflow-hidden", children: [_jsx("div", { className: "min-h-0 flex-1 overflow-auto", children: _jsx(VersionCompare, {}) }), _jsx(InspectorSidebar, { panes: trailingPanes, activeId: inspectorPaneId, onActiveChange: setInspectorPaneId, className: "w-[320px] shrink-0 border-l border-border" })] })) : canvasField ? (_jsx("div", { className: "min-h-0 flex-1 overflow-hidden", children: _jsx(CompositionFieldEditor, { field: canvasField, value: getValue(canvasField), onChange: (v) => setValue(canvasField, v), readOnly: viewReadOnly || source.isFieldReadOnly(canvasField), trailingPanes: trailingPanes }, `${canvasField.name}:${restoreNonce}:${perspective}`) })) : activeTab === REFERENCES_TAB ? (_jsx("div", { className: "flex min-h-0 flex-1 overflow-hidden", children: _jsx("div", { className: "min-h-0 flex-1 overflow-auto p-4 sm:p-6", children: _jsx("div", { className: "mx-auto w-full max-w-2xl", children: _jsx(ReferencesPanel, { projectId: projectId, source: { type: 'document', id: documentKey(ref) }, target: { type: 'document', id: documentKey(ref) } }) }) }) })) : (_jsxs("div", { className: "flex min-h-0 flex-1 overflow-hidden", children: [_jsx("div", { className: "min-h-0 flex-1 overflow-auto p-4 sm:p-6", children: _jsx("div", { className: "mx-auto w-full max-w-2xl", children: _jsxs(Stack, { gap: "6", children: [groups.map((group) => {
|
|
524
|
+
// EC-306: a conditionally/statically hidden group section is skipped
|
|
525
|
+
// entirely (its fields are also exempt from validation).
|
|
526
|
+
if (hiddenGroups.has(group.name))
|
|
527
|
+
return null;
|
|
528
|
+
// EC-273/EC-240: drop a fieldset whose every field is hidden — whether
|
|
529
|
+
// content-conditionally or for this principal — so the form never
|
|
530
|
+
// shows an empty section header.
|
|
531
|
+
const visible = group.fields.filter(isFieldShown);
|
|
532
|
+
if (visible.length === 0)
|
|
533
|
+
return null;
|
|
534
|
+
const section = group.section;
|
|
535
|
+
// EC-241: a section `title` is a LocalizedString ({ en, de } or a plain
|
|
536
|
+
// string) — resolve it against the active UI locale before rendering.
|
|
537
|
+
const sectionTitle = section?.title !== undefined ? localizedLabel(section.title, group.name) : undefined;
|
|
538
|
+
return (_jsx(CollapsibleSection, { collapsible: section?.collapsible, defaultOpen: section?.defaultOpen, title: _jsx(Text, { size: "sm", weight: "semibold", className: sectionTitle || group.name === DEFAULT_FIELD_GROUP ? undefined : 'capitalize', children: sectionTitle ??
|
|
539
|
+
(group.name === DEFAULT_FIELD_GROUP
|
|
540
|
+
? translate('cms.editor.defaultGroup')
|
|
541
|
+
: group.name) }), children: _jsx("div", { className: "grid grid-cols-1 gap-4 p-4 md:grid-cols-12", children: visible.map((field) => (_jsx("div", { className: cn('col-span-1', fieldWidthColSpan(field.form?.width)), children: renderFieldRow(field) }, field.name))) }) }, group.name));
|
|
542
|
+
}), formAppendix] }) }) }), _jsx(InspectorSidebar, { panes: trailingPanes, activeId: inspectorPaneId, onActiveChange: setInspectorPaneId, className: "w-[320px] shrink-0 border-l border-border" })] })), explicitSave ? (_jsx(UnsavedChangesGuard, { when: dirty, onSave: () => {
|
|
543
|
+
save();
|
|
544
|
+
return true;
|
|
545
|
+
}, description: translate('cms.editor.unsavedChangesGuard') })) : null, confirmAction !== null ? (_jsx(ReferenceConfirmDialog, { projectId: projectId, action: confirmAction, targets: confirmTargets, open: true, onOpenChange: (open) => {
|
|
546
|
+
if (!open)
|
|
547
|
+
setConfirmAction(null);
|
|
548
|
+
}, onConfirm: handleConfirmedAction })) : null, _jsx(Dialog, { open: discardConfirm, onOpenChange: (open) => { if (!open)
|
|
549
|
+
setDiscardConfirm(false); }, children: _jsxs(DialogContent, { children: [_jsxs(DialogHeader, { children: [_jsx(DialogTitle, { children: translate('cms.editor.discardDraftTitle') }), _jsx(DialogDescription, { children: translate('cms.editor.discardDraftDescription') })] }), _jsxs(DialogFooter, { className: "mt-4", children: [_jsx(Button, { variant: "ghost", size: "sm", onClick: () => setDiscardConfirm(false), disabled: discardBusy, children: translate('common.cancel') }), _jsx(Button, { variant: "destructive", size: "sm", "data-testid": "discard-draft-confirm", disabled: discardBusy, onClick: () => void handleDiscardDraft(), children: discardBusy ? (_jsxs(_Fragment, { children: [_jsx(Loader2, { className: "animate-spin" }), " ", translate('cms.editor.discardingDraft')] })) : (_jsxs(_Fragment, { children: [_jsx(RotateCcw, {}), " ", translate('cms.editor.discardDraftConfirm')] })) })] })] }) }), _jsx(Dialog, { open: publishConfirm, onOpenChange: (open) => { if (!open)
|
|
550
|
+
cancelPublishConfirm(); }, children: _jsxs(DialogContent, { children: [_jsxs(DialogHeader, { children: [_jsx(DialogTitle, { children: translate('cms.editor.publishUnpublishedRefsTitle') }), _jsx(DialogDescription, { children: translate('cms.editor.publishUnpublishedRefsDescription') })] }), _jsxs(Stack, { gap: "1", children: [referenceIssues.map((issue, i) => (_jsxs(Text, { size: "xs", tone: "muted", children: ["\u2022 ", issue.message] }, `${issue.meta?.id ?? i}`))), schemaIssues.length > 0 ? (_jsx(Text, { size: "xs", tone: "muted", className: "mt-1", children: translate(schemaIssues.length === 1
|
|
551
|
+
? 'cms.editor.publishAlsoHasIssues.one'
|
|
552
|
+
: 'cms.editor.publishAlsoHasIssues.other', { count: schemaIssues.length }) })) : null] }), _jsxs(DialogFooter, { className: "mt-4", children: [_jsx(Button, { variant: "ghost", size: "sm", onClick: cancelPublishConfirm, children: translate('common.cancel') }), _jsx(Button, { size: "sm", "data-testid": "publish-anyway", onClick: confirmPublish, children: translate('cms.editor.publishAnyway') })] })] }) })] }) }) }));
|
|
531
553
|
}
|
|
532
554
|
/**
|
|
533
555
|
* EC-300/AD-16 — errors for composition embeds (hard references) whose target isn't
|