@elytracms/studio 0.0.40 → 0.0.42
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/builder/canvas.js +16 -2
- package/dist/components/builder/canvas.js.map +1 -1
- package/dist/components/cms/detail-editor.js +95 -73
- package/dist/components/cms/detail-editor.js.map +1 -1
- package/dist/components/cms/details-pane.d.ts +11 -21
- package/dist/components/cms/details-pane.js +14 -31
- package/dist/components/cms/details-pane.js.map +1 -1
- package/dist/components/cms/document-url-path.d.ts +39 -0
- package/dist/components/cms/document-url-path.js +60 -0
- package/dist/components/cms/document-url-path.js.map +1 -0
- package/dist/components/cms/field-control.js +18 -12
- package/dist/components/cms/field-control.js.map +1 -1
- package/dist/components/cms/rich-text/component-embed.d.ts +7 -2
- package/dist/components/cms/rich-text/component-embed.js +60 -5
- package/dist/components/cms/rich-text/component-embed.js.map +1 -1
- package/dist/components/cms/rich-text/editor.js +24 -5
- package/dist/components/cms/rich-text/editor.js.map +1 -1
- package/dist/components/cms/rich-text/embed-inspector-context.d.ts +50 -0
- package/dist/components/cms/rich-text/embed-inspector-context.js +12 -0
- package/dist/components/cms/rich-text/embed-inspector-context.js.map +1 -0
- package/dist/components/cms/rich-text/embed-inspector.d.ts +25 -0
- package/dist/components/cms/rich-text/embed-inspector.js +48 -0
- package/dist/components/cms/rich-text/embed-inspector.js.map +1 -0
- package/dist/components/cms/rich-text/embed-picker.d.ts +0 -2
- package/dist/components/cms/rich-text/embed-picker.js +0 -4
- package/dist/components/cms/rich-text/embed-picker.js.map +1 -1
- package/dist/components/cms/rich-text/embed-props.d.ts +7 -0
- package/dist/components/cms/rich-text/embed-props.js +9 -0
- package/dist/components/cms/rich-text/embed-props.js.map +1 -1
- package/dist/components/cms/rich-text/insert.d.ts +8 -2
- package/dist/components/cms/rich-text/insert.js +32 -3
- package/dist/components/cms/rich-text/insert.js.map +1 -1
- package/dist/components/cms/rich-text/slash-command.d.ts +23 -6
- package/dist/components/cms/rich-text/slash-command.js +31 -23
- package/dist/components/cms/rich-text/slash-command.js.map +1 -1
- package/dist/components/cms/version-compare.js +4 -3
- package/dist/components/cms/version-compare.js.map +1 -1
- package/dist/components/components/component-preview.js +62 -9
- package/dist/components/components/component-preview.js.map +1 -1
- package/dist/components/inspector/inspector-sidebar.js +20 -3
- package/dist/components/inspector/inspector-sidebar.js.map +1 -1
- package/dist/lib/i18n/messages.en.js +9 -5
- package/dist/lib/i18n/messages.en.js.map +1 -1
- package/package.json +2 -2
|
@@ -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,
|
|
3
|
+
import { Check, ChevronDown, CircleAlert, CircleCheck, Eye, EyeOff, GitCompareArrows, History, Loader2, Lock, MoreHorizontal, RotateCcw, Save, Tag, Trash2, X, } from 'lucide-react';
|
|
4
4
|
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
|
5
5
|
import { FIELD_WIDTH_SPANS, SIDEBAR_FIELD_GROUP, clearHiddenValues, documentKey, groupOrder, groupSectionOf, hasAliases, hiddenGroupNames, isBlocksField, isFieldHidden, isFieldLocalized, isSingleton, resolveDocumentForLocale, resolveDocumentTitle, resolveField, sidebarFields, tabDescriptorOf, tabOrder, titleFieldOf, staticFieldHidden, urlForDocument, } from '@elytracms/core/cms-core';
|
|
6
6
|
import { blockRootsOf } from '@elytracms/core/project-graph';
|
|
@@ -36,6 +36,11 @@ import { useDocumentVersionsQuery } from './use-document-versions';
|
|
|
36
36
|
import { discardDraftStatus } from './discard-draft';
|
|
37
37
|
import { IssuesPane } from './issues-pane';
|
|
38
38
|
import { DetailsPane } from './details-pane';
|
|
39
|
+
import { DocumentUrlPath } from './document-url-path';
|
|
40
|
+
// 0.0.41: selection-driven embed inspector. Both modules are deliberately TipTap-free
|
|
41
|
+
// so the editor runtime stays behind rich-text/lazy (EC-322) — see their doc comments.
|
|
42
|
+
import { EmbedInspectorProvider } from './rich-text/embed-inspector-context';
|
|
43
|
+
import { EmbedInspector } from './rich-text/embed-inspector';
|
|
39
44
|
import { useSetBreadcrumb } from '../breadcrumb-context';
|
|
40
45
|
import { resolveDeploymentFrontendUrl, resolveDeploymentRoutes } from '@elytracms/studio/lib/workspace';
|
|
41
46
|
import { usePublishedDocuments } from '@elytracms/studio/lib/cms/published-documents';
|
|
@@ -291,6 +296,15 @@ function EditorBody({ projectId, collection, doc, focusPath, }) {
|
|
|
291
296
|
// `sidebar`-group field can open the Details pane (where it renders). Undefined
|
|
292
297
|
// = uncontrolled (the sidebar defaults to its first pane).
|
|
293
298
|
const [inspectorPaneId, setInspectorPaneId] = useState();
|
|
299
|
+
// 0.0.41: the selected rich-text component embed, if any — a `componentEmbed` node
|
|
300
|
+
// selection swaps the inspector sidebar for the embed's prop form. Fed by the
|
|
301
|
+
// rich-text field through the EmbedInspector context; `clearActiveEmbed` keys on the
|
|
302
|
+
// node id so a fast A→B reselect can't let A's deselect wipe B (selection race).
|
|
303
|
+
const [activeEmbed, setActiveEmbed] = useState(null);
|
|
304
|
+
const embedInspectorApi = useMemo(() => ({
|
|
305
|
+
setActiveEmbed: (payload) => setActiveEmbed(payload),
|
|
306
|
+
clearActiveEmbed: (nodeId) => setActiveEmbed((prev) => (prev?.nodeId === nodeId ? null : prev)),
|
|
307
|
+
}), []);
|
|
294
308
|
// EC-298: the open version comparison. The Compare tab is STICKY — opening it
|
|
295
309
|
// sets the selection and focuses the tab; tabbing to the real fields keeps it
|
|
296
310
|
// open (the selection persists) so an editor can flip between diff and fields;
|
|
@@ -473,25 +487,23 @@ function EditorBody({ projectId, collection, doc, focusPath, }) {
|
|
|
473
487
|
// memoized: its render closes over per-render edit state (draft, highlight,
|
|
474
488
|
// field errors) via `renderFieldRow`, and the inspector re-renders with the rest
|
|
475
489
|
// of the editor anyway, so a stable pane identity would buy nothing here.
|
|
476
|
-
// 0.0.
|
|
477
|
-
//
|
|
478
|
-
// A cheap synchronous accessor, resolved on every render (NOT frozen in a
|
|
479
|
-
// with a static dep — the same first-paint-empty-routes trap the SlugField
|
|
480
|
-
// removed: routes arrive after the workspace/config loads).
|
|
490
|
+
// 0.0.42 (round 3): whether a deployment route addresses this collection — gates the
|
|
491
|
+
// header's composed-URL row (which carries the live-page affordance, EC-315 hybrid
|
|
492
|
+
// split). A cheap synchronous accessor, resolved on every render (NOT frozen in a
|
|
493
|
+
// `useMemo` with a static dep — the same first-paint-empty-routes trap the SlugField
|
|
494
|
+
// fix removed: routes arrive after the workspace/config loads).
|
|
481
495
|
const routableCollection = resolveDeploymentRoutes().some((route) => route.document?.collection === collection.id);
|
|
482
|
-
|
|
496
|
+
// The composed URL moved OUT of this pane and up to the header (round 3): a lone URL
|
|
497
|
+
// row on an otherwise-empty pane looked odd. So the Details pane exists only for slots
|
|
498
|
+
// and/or sidebar fields — never merely because the collection is routable (that would
|
|
499
|
+
// resurrect the empty-pane problem). A routable page keeps its pane via its slug
|
|
500
|
+
// sidebar field; a routable collection with neither has no pane (URL is in the header).
|
|
501
|
+
const detailsPane = hasAliases(collection) || visibleSidebarFields.length > 0
|
|
483
502
|
? {
|
|
484
503
|
id: 'details',
|
|
485
504
|
label: translate('cms.editor.detailsPane'),
|
|
486
505
|
icon: _jsx(Tag, { className: "size-3.5" }),
|
|
487
|
-
render: () => (_jsx(DetailsPane, { collection: collection, doc: draft, siblings: source.documents(), readOnly: viewReadOnly, fields: visibleSidebarFields, renderField: renderFieldRow,
|
|
488
|
-
? {
|
|
489
|
-
projectId,
|
|
490
|
-
routes: resolveDeploymentRoutes(),
|
|
491
|
-
localeConfig: source.localeConfig,
|
|
492
|
-
locale,
|
|
493
|
-
}
|
|
494
|
-
: undefined, onAliasChange: (alias) => setDraft((d) => {
|
|
506
|
+
render: () => (_jsx(DetailsPane, { collection: collection, doc: draft, siblings: source.documents(), readOnly: viewReadOnly, fields: visibleSidebarFields, renderField: renderFieldRow, onAliasChange: (alias) => setDraft((d) => {
|
|
495
507
|
const next = { ...d };
|
|
496
508
|
if (alias === undefined)
|
|
497
509
|
delete next.alias;
|
|
@@ -642,10 +654,24 @@ function EditorBody({ projectId, collection, doc, focusPath, }) {
|
|
|
642
654
|
localeConfig: source.localeConfig,
|
|
643
655
|
}, collection, published, { siblings: [...publishedDocs.byId.values()], locale });
|
|
644
656
|
}, [publishedDocs, collection, doc.id, locale, source.localeConfig]);
|
|
645
|
-
//
|
|
646
|
-
//
|
|
647
|
-
//
|
|
648
|
-
//
|
|
657
|
+
// 0.0.42 (round 3): the composed URL row lives in the header under the title; its
|
|
658
|
+
// LEAF segment carries the open-live affordance, mirroring the retired "View live"
|
|
659
|
+
// button's gating EXACTLY. `href` present (published + frontendUrl) → the leaf opens
|
|
660
|
+
// the live page in a new tab; absent → a muted, non-interactive icon with the same
|
|
661
|
+
// reason tooltip (loading / publish-first / set-frontendUrl). The draft-derived path
|
|
662
|
+
// itself still updates as you type — only the OPEN-live action is published-gated, so
|
|
663
|
+
// the draft/live distinction is preserved.
|
|
664
|
+
const headerLive = routableCollection
|
|
665
|
+
? liveUrl?.href
|
|
666
|
+
? { href: liveUrl.href, title: translate('cms.editor.openLiveUrl', { path: liveUrl.path }) }
|
|
667
|
+
: {
|
|
668
|
+
title: publishedDocs.status === 'loading'
|
|
669
|
+
? translate('cms.editor.loadingLiveUrl')
|
|
670
|
+
: !published
|
|
671
|
+
? translate('cms.editor.publishToViewLive')
|
|
672
|
+
: translate('cms.editor.setFrontendUrlHint'),
|
|
673
|
+
}
|
|
674
|
+
: undefined;
|
|
649
675
|
// EC-240: hide the tab bar when there is nothing to navigate — a single content
|
|
650
676
|
// tab with no References/Compare (the users rail). When framework tabs ARE
|
|
651
677
|
// present we KEEP the content tab(s) so there is always a way back to the form;
|
|
@@ -683,60 +709,56 @@ function EditorBody({ projectId, collection, doc, focusPath, }) {
|
|
|
683
709
|
setCompareBase,
|
|
684
710
|
setCompareTarget,
|
|
685
711
|
exitCompare,
|
|
686
|
-
}, 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", 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" })] }), _jsx(Spacer, {}), _jsx(Text, { size: "xs", tone: "muted", className: "whitespace-nowrap", "data-testid": "autosave-status", children: source.lastError
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
}, children: translate('common.cancel') }), _jsx(Button, { size: "sm", "data-testid": "publish-anyway", onClick: () => {
|
|
737
|
-
setPublishConfirm(false);
|
|
738
|
-
void doPublish();
|
|
739
|
-
}, children: translate('cms.editor.publishAnyway') })] })] }) })] }) }));
|
|
712
|
+
}, children: _jsx(EmbedInspectorProvider, { value: embedInspectorApi, 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, {}), _jsx(Text, { size: "xs", tone: "muted", className: "whitespace-nowrap", "data-testid": "autosave-status", children: source.lastError
|
|
713
|
+
? translate('cms.editor.saveFailed')
|
|
714
|
+
: explicitSave
|
|
715
|
+
? dirty
|
|
716
|
+
? translate('cms.editor.unsavedChanges')
|
|
717
|
+
: translate('cms.editor.saved')
|
|
718
|
+
: dirty
|
|
719
|
+
? translate('cms.editor.saving')
|
|
720
|
+
: translate('cms.editor.saved') }), 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, 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: [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
|
|
721
|
+
? 'bg-accent text-accent-foreground'
|
|
722
|
+
: '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'
|
|
723
|
+
? translate('cms.editor.loadingPublishedVersion')
|
|
724
|
+
: 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: _jsx(Stack, { gap: "6", children: groups.map((group) => {
|
|
725
|
+
// EC-306: a conditionally/statically hidden group section is skipped
|
|
726
|
+
// entirely (its fields are also exempt from validation).
|
|
727
|
+
if (hiddenGroups.has(group.name))
|
|
728
|
+
return null;
|
|
729
|
+
// EC-273/EC-240: drop a fieldset whose every field is hidden — whether
|
|
730
|
+
// content-conditionally or for this principal — so the form never
|
|
731
|
+
// shows an empty section header.
|
|
732
|
+
const visible = group.fields.filter(isFieldShown);
|
|
733
|
+
if (visible.length === 0)
|
|
734
|
+
return null;
|
|
735
|
+
const section = group.section;
|
|
736
|
+
// EC-241: a section `title` is a LocalizedString ({ en, de } or a plain
|
|
737
|
+
// string) — resolve it against the active UI locale before rendering.
|
|
738
|
+
const sectionTitle = section?.title !== undefined ? localizedLabel(section.title, group.name) : undefined;
|
|
739
|
+
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 ??
|
|
740
|
+
(group.name === DEFAULT_FIELD_GROUP
|
|
741
|
+
? translate('cms.editor.defaultGroup')
|
|
742
|
+
: 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));
|
|
743
|
+
}) }) }) }), activeEmbed ? (_jsx(EmbedInspector, { payload: activeEmbed, className: "w-[320px] shrink-0 border-l border-border" })) : (_jsx(InspectorSidebar, { panes: trailingPanes, activeId: inspectorPaneId, onActiveChange: setInspectorPaneId, className: "w-[320px] shrink-0 border-l border-border" }))] })), explicitSave ? (_jsx(UnsavedChangesGuard, { when: dirty, onSave: () => {
|
|
744
|
+
save();
|
|
745
|
+
return true;
|
|
746
|
+
}, description: translate('cms.editor.unsavedChangesGuard') })) : null, confirmAction !== null ? (_jsx(ReferenceConfirmDialog, { projectId: projectId, action: confirmAction, targets: confirmTargets, open: true, onOpenChange: (open) => {
|
|
747
|
+
if (!open)
|
|
748
|
+
setConfirmAction(null);
|
|
749
|
+
}, onConfirm: handleConfirmedAction })) : null, _jsx(Dialog, { open: discardConfirm, onOpenChange: (open) => { if (!open)
|
|
750
|
+
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) {
|
|
751
|
+
setPublishConfirm(false);
|
|
752
|
+
closeAfterPublishRef.current = false;
|
|
753
|
+
} }, 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
|
|
754
|
+
? 'cms.editor.publishAlsoHasIssues.one'
|
|
755
|
+
: 'cms.editor.publishAlsoHasIssues.other', { count: schemaIssues.length }) })) : null] }), _jsxs(DialogFooter, { className: "mt-4", children: [_jsx(Button, { variant: "ghost", size: "sm", onClick: () => {
|
|
756
|
+
setPublishConfirm(false);
|
|
757
|
+
closeAfterPublishRef.current = false;
|
|
758
|
+
}, children: translate('common.cancel') }), _jsx(Button, { size: "sm", "data-testid": "publish-anyway", onClick: () => {
|
|
759
|
+
setPublishConfirm(false);
|
|
760
|
+
void doPublish();
|
|
761
|
+
}, children: translate('cms.editor.publishAnyway') })] })] }) })] }) }) }));
|
|
740
762
|
}
|
|
741
763
|
/**
|
|
742
764
|
* EC-300/AD-16 — errors for composition embeds (hard references) whose target isn't
|