@elytracms/studio 0.0.41 → 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 +34 -25
- 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/inspector/inspector-sidebar.js +20 -3
- package/dist/components/inspector/inspector-sidebar.js.map +1 -1
- package/dist/lib/i18n/messages.en.js +6 -5
- package/dist/lib/i18n/messages.en.js.map +1 -1
- package/package.json +2 -2
|
@@ -1,7 +1,18 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { PanelRight } from 'lucide-react';
|
|
2
3
|
import { useState } from 'react';
|
|
3
4
|
import { Row, Text } from '@elytracms/studio/ui';
|
|
4
5
|
import { cn } from '@elytracms/studio/lib/utils';
|
|
6
|
+
/**
|
|
7
|
+
* When the sidebar carries this many panes or more, inactive tabs collapse to their
|
|
8
|
+
* icon alone (label in a tooltip + `aria-label`) so the tab strip stops overflowing
|
|
9
|
+
* (0.0.42, round 3). 1–2 panes keep their full labels.
|
|
10
|
+
*/
|
|
11
|
+
const ICON_COLLAPSE_THRESHOLD = 3;
|
|
12
|
+
/** Fallback icon for a pane that declares none — so an icon-collapsed tab is never blank. */
|
|
13
|
+
function paneIcon(pane) {
|
|
14
|
+
return pane.icon ?? _jsx(PanelRight, { className: "size-3.5" });
|
|
15
|
+
}
|
|
5
16
|
export function InspectorSidebar({ panes, className, activeId: controlledId, onActiveChange, }) {
|
|
6
17
|
const [internalId, setInternalId] = useState(() => panes[0]?.id);
|
|
7
18
|
const activeId = controlledId ?? internalId;
|
|
@@ -15,9 +26,15 @@ export function InspectorSidebar({ panes, className, activeId: controlledId, onA
|
|
|
15
26
|
const active = panes.find((pane) => pane.id === activeId) ?? panes[0];
|
|
16
27
|
if (!active)
|
|
17
28
|
return null;
|
|
18
|
-
|
|
29
|
+
// 0.0.42: with a crowded tab strip (3+ panes) the inactive tabs collapse to icons so
|
|
30
|
+
// the labels stop overflowing; the active tab always keeps its icon + label.
|
|
31
|
+
const collapseInactive = panes.length >= ICON_COLLAPSE_THRESHOLD;
|
|
32
|
+
return (_jsxs("div", { className: cn('flex h-full min-h-0 flex-col bg-card', className), children: [_jsx(Row, { gap: "1", className: "shrink-0 border-b border-border p-1.5", children: panes.map((pane) => {
|
|
33
|
+
const isActive = pane.id === active.id;
|
|
34
|
+
return (_jsx(PaneTab, { pane: pane, active: isActive, showLabel: isActive || !collapseInactive, onClick: () => select(pane.id) }, pane.id));
|
|
35
|
+
}) }), _jsx("div", { className: "min-h-0 flex-1 overflow-auto", children: active.render() })] }));
|
|
19
36
|
}
|
|
20
|
-
function PaneTab({ pane, active, onClick, }) {
|
|
21
|
-
return (_jsxs("button", { type: "button", "aria-pressed": active, onClick: onClick, className: cn('inline-flex items-center gap-1.5 rounded-md px-2.5 py-1 transition-colors', active ? 'bg-accent text-accent-foreground' : 'text-muted-foreground hover:bg-muted'), children: [pane
|
|
37
|
+
function PaneTab({ pane, active, showLabel, onClick, }) {
|
|
38
|
+
return (_jsxs("button", { type: "button", "aria-pressed": active, "aria-label": showLabel ? undefined : pane.label, title: showLabel ? undefined : pane.label, onClick: onClick, className: cn('inline-flex items-center gap-1.5 rounded-md px-2.5 py-1 transition-colors', active ? 'bg-accent text-accent-foreground' : 'text-muted-foreground hover:bg-muted'), children: [paneIcon(pane), showLabel ? (_jsx(Text, { as: "span", size: "xs", weight: "medium", className: "leading-none", children: pane.label })) : null, pane.badge] }));
|
|
22
39
|
}
|
|
23
40
|
//# sourceMappingURL=inspector-sidebar.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"inspector-sidebar.js","sourceRoot":"","sources":["../../../src/components/inspector/inspector-sidebar.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAA;AAEhC,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,sBAAsB,CAAA;AAChD,OAAO,EAAE,EAAE,EAAE,MAAM,6BAA6B,CAAA;
|
|
1
|
+
{"version":3,"file":"inspector-sidebar.js","sourceRoot":"","sources":["../../../src/components/inspector/inspector-sidebar.tsx"],"names":[],"mappings":"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","sourcesContent":["import { PanelRight } from 'lucide-react'\nimport { useState } from 'react'\nimport type { ReactNode } from 'react'\nimport { Row, Text } from '@elytracms/studio/ui'\nimport { cn } from '@elytracms/studio/lib/utils'\n\n/**\n * When the sidebar carries this many panes or more, inactive tabs collapse to their\n * icon alone (label in a tooltip + `aria-label`) so the tab strip stops overflowing\n * (0.0.42, round 3). 1–2 panes keep their full labels.\n */\nconst ICON_COLLAPSE_THRESHOLD = 3\n\n/** Fallback icon for a pane that declares none — so an icon-collapsed tab is never blank. */\nfunction paneIcon(pane: InspectorPane): ReactNode {\n return pane.icon ?? <PanelRight className=\"size-3.5\" />\n}\n\n/**\n * EC-224 — the shared, view-supplied inspector sidebar.\n *\n * A generic tabbed frame: it renders whatever {@link InspectorPane}s it is given\n * and owns nothing domain-specific. The OWNER composes the pane list — by\n * convention `panes = [...view.suppliedPanes, versionsPane]`, so a view (the\n * canvas) contributes its context pane (`Properties`) and the frame-owner (the\n * document editor) appends the document-level `Versions` pane. A future rich\n * field editor contributes its own pane the same way, without touching this\n * frame (AD-12 — the canvas is itself just a field editor).\n *\n * Each pane's `render` runs where the frame is mounted, so a pane that needs a\n * scoped context (e.g. the canvas `BuilderProvider` behind `PropertyInspector`)\n * must be supplied by a view that mounts the frame inside that provider.\n */\nexport interface InspectorPane {\n id: string\n label: string\n icon?: ReactNode\n /** Optional trailing adornment on the tab (e.g. a count badge for `Issues`). */\n badge?: ReactNode\n render: () => ReactNode\n}\n\nexport function InspectorSidebar({\n panes,\n className,\n activeId: controlledId,\n onActiveChange,\n}: {\n panes: readonly InspectorPane[]\n className?: string\n /**\n * Optionally control which pane is shown (EC-285): the document editor drives\n * this to open the `Details` pane when a jump-to-field targets a sidebar field.\n * Uncontrolled (internal state) when omitted.\n */\n activeId?: string\n onActiveChange?: (id: string) => void\n}) {\n const [internalId, setInternalId] = useState<string | undefined>(() => panes[0]?.id)\n const activeId = controlledId ?? internalId\n const select = (id: string) => {\n if (controlledId === undefined) setInternalId(id)\n onActiveChange?.(id)\n }\n // Derive the active pane defensively so a changed pane list (switching views)\n // can never render a stale/absent tab — fall back to the first pane.\n const active = panes.find((pane) => pane.id === activeId) ?? panes[0]\n if (!active) return null\n\n // 0.0.42: with a crowded tab strip (3+ panes) the inactive tabs collapse to icons so\n // the labels stop overflowing; the active tab always keeps its icon + label.\n const collapseInactive = panes.length >= ICON_COLLAPSE_THRESHOLD\n\n return (\n <div className={cn('flex h-full min-h-0 flex-col bg-card', className)}>\n <Row gap=\"1\" className=\"shrink-0 border-b border-border p-1.5\">\n {panes.map((pane) => {\n const isActive = pane.id === active.id\n return (\n <PaneTab\n key={pane.id}\n pane={pane}\n active={isActive}\n showLabel={isActive || !collapseInactive}\n onClick={() => select(pane.id)}\n />\n )\n })}\n </Row>\n <div className=\"min-h-0 flex-1 overflow-auto\">{active.render()}</div>\n </div>\n )\n}\n\nfunction PaneTab({\n pane,\n active,\n showLabel,\n onClick,\n}: {\n pane: InspectorPane\n active: boolean\n /** When false, the tab renders icon-only — the label moves to the tooltip + aria-label. */\n showLabel: boolean\n onClick: () => void\n}) {\n return (\n <button\n type=\"button\"\n aria-pressed={active}\n aria-label={showLabel ? undefined : pane.label}\n title={showLabel ? undefined : pane.label}\n onClick={onClick}\n className={cn(\n 'inline-flex items-center gap-1.5 rounded-md px-2.5 py-1 transition-colors',\n active ? 'bg-accent text-accent-foreground' : 'text-muted-foreground hover:bg-muted',\n )}\n >\n {paneIcon(pane)}\n {showLabel ? (\n <Text as=\"span\" size=\"xs\" weight=\"medium\" className=\"leading-none\">\n {pane.label}\n </Text>\n ) : null}\n {pane.badge}\n </button>\n )\n}\n"]}
|
|
@@ -227,8 +227,10 @@ export const EN_MESSAGES = {
|
|
|
227
227
|
'cms.editor.unsavedChanges': msg('Unsaved changes'),
|
|
228
228
|
'cms.editor.saved': msg('Saved'),
|
|
229
229
|
'cms.editor.saving': msg('Saving…'),
|
|
230
|
+
// 0.0.42 (round 3): the standalone "View live" button is retired — opening the live
|
|
231
|
+
// page now hangs off the header URL row's leaf segment. These strings still drive the
|
|
232
|
+
// leaf's tooltip (open-live target + the published-gating reasons).
|
|
230
233
|
'cms.editor.openLiveUrl': msg('Open {path}'),
|
|
231
|
-
'cms.editor.viewLive': msg('View live'),
|
|
232
234
|
'cms.editor.loadingLiveUrl': msg('Loading the live URL…'),
|
|
233
235
|
'cms.editor.publishToViewLive': msg('Publish to view the live page'),
|
|
234
236
|
'cms.editor.setFrontendUrlHint': msg('Set frontendUrl in elytra.config.ts to enable'),
|
|
@@ -299,10 +301,9 @@ export const EN_MESSAGES = {
|
|
|
299
301
|
'cms.details.slotInUse': msg(' — in use', 'Suffix on a slot option already taken by another document.'),
|
|
300
302
|
'cms.details.slotHint': msg('A stable name the site uses to find this document (e.g. the main menu), instead of which exact entry it is. One document per slot.'),
|
|
301
303
|
'cms.details.empty': msg('No document properties to set.'),
|
|
302
|
-
//
|
|
303
|
-
//
|
|
304
|
-
// their editors
|
|
305
|
-
'cms.details.url': msg('URL', 'Details pane section: the document’s composed URL path.'),
|
|
304
|
+
// The composed-URL affordance (EC-315 "hybrid split") — moved to the detail-editor
|
|
305
|
+
// HEADER under the title in 0.0.42 (round 3); ancestor-document segments link to
|
|
306
|
+
// their editors, static route segments are plain, and the leaf carries the live link.
|
|
306
307
|
'cms.details.urlCurrent': msg('this page', 'Tooltip on the final URL segment — the document being edited.'),
|
|
307
308
|
// ─────────────────────────────────────────────────────────────────────────
|
|
308
309
|
// CMS — import dialog
|