payload-better-editor 1.1.0 → 1.2.1
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/admin/LiveEditorOverlay.js +1 -3
- package/dist/admin/LiveEditorOverlay.js.map +1 -1
- package/dist/admin/blocks/schema.d.ts +8 -0
- package/dist/admin/blocks/schema.js +13 -2
- package/dist/admin/blocks/schema.js.map +1 -1
- package/dist/admin/sidebar/BlockSettingsTab.js +7 -4
- package/dist/admin/sidebar/BlockSettingsTab.js.map +1 -1
- package/dist/hooks/useOverlayKeyboard.d.ts +1 -2
- package/dist/hooks/useOverlayKeyboard.js +1 -15
- package/dist/hooks/useOverlayKeyboard.js.map +1 -1
- package/dist/index.js +18 -15
- package/dist/index.js.map +1 -1
- package/dist/internal/entities.d.ts +6 -0
- package/dist/internal/entities.js +15 -0
- package/dist/internal/entities.js.map +1 -0
- package/dist/preview/hover-css.d.ts +1 -1
- package/dist/preview/hover-css.js +2 -2
- package/dist/preview/hover-css.js.map +1 -1
- package/dist/types.d.ts +19 -11
- package/dist/types.js.map +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/dist/version.js.map +1 -1
- package/package.json +1 -1
|
@@ -31,14 +31,13 @@ export const LiveEditorOverlay = ({ onClose, blocksField, storageNamespace, admi
|
|
|
31
31
|
storageNamespace: storageNamespace,
|
|
32
32
|
adminPortalSelector: adminPortalSelector,
|
|
33
33
|
children: /*#__PURE__*/ _jsx(LiveEditorOverlayInner, {
|
|
34
|
-
onClose: onClose,
|
|
35
34
|
blocksField: blocksField,
|
|
36
35
|
selectedBlockPath: selectedBlockPath,
|
|
37
36
|
setSelectedBlockPath: setSelectedBlockPath
|
|
38
37
|
})
|
|
39
38
|
});
|
|
40
39
|
};
|
|
41
|
-
const LiveEditorOverlayInner = ({
|
|
40
|
+
const LiveEditorOverlayInner = ({ blocksField, selectedBlockPath, setSelectedBlockPath })=>{
|
|
42
41
|
const settings = useBetterEditorSettings();
|
|
43
42
|
const history = useEditorHistory();
|
|
44
43
|
const { previewURL } = useLivePreviewContext();
|
|
@@ -59,7 +58,6 @@ const LiveEditorOverlayInner = ({ onClose, blocksField, selectedBlockPath, setSe
|
|
|
59
58
|
setSelectedBlockPath
|
|
60
59
|
});
|
|
61
60
|
useOverlayKeyboard({
|
|
62
|
-
onClose,
|
|
63
61
|
history
|
|
64
62
|
});
|
|
65
63
|
const [sidebarCollapsed, setSidebarCollapsed] = useState(false);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/admin/LiveEditorOverlay.tsx"],"sourcesContent":["'use client'\n\nimport React, { useCallback, useRef, useState } from 'react'\nimport { useLivePreviewContext } from '@payloadcms/ui'\nimport { PreviewFrame } from './PreviewFrame'\nimport { PreviewToolbar } from './PreviewToolbar'\nimport { Sidebar } from './sidebar/Sidebar'\nimport { useBetterEditorSettings } from '../state/useBetterEditorSettings'\nimport { useEditorHistory } from '../state/useEditorHistory'\nimport { useSidebarResize } from '../hooks/useSidebarResize'\nimport { useViewportState } from '../hooks/useViewportState'\nimport { useFullscreenOverlay } from '../hooks/useFullscreenOverlay'\nimport { useBlockActionMessages } from '../hooks/useBlockActionMessages'\nimport { useOverlayKeyboard } from '../hooks/useOverlayKeyboard'\nimport { useFocusTrap } from '../hooks/useFocusTrap'\nimport { OverlayProviders } from '../providers/OverlayProviders'\nimport '../styles/overlay.css'\nimport '../styles/preview.css'\nimport '../styles/sidebar.css'\nimport '../styles/blocks-tab.css'\n\nexport type LiveEditorOverlayProps = {\n onClose: () => void\n blocksField: string\n storageNamespace?: string\n adminPortalSelector?: string\n}\n\nconst RESIZE_HANDLE_PX = 6\n\nconst classes = (...parts: Array<string | false | null | undefined>): string =>\n parts.filter(Boolean).join(' ')\n\nexport const LiveEditorOverlay: React.FC<LiveEditorOverlayProps> = ({\n onClose,\n blocksField,\n storageNamespace,\n adminPortalSelector,\n}) => {\n // Selection state lives outside OverlayProviders so the error boundary's\n // onReset can clear it without remounting providers.\n const [selectedBlockPath, setSelectedBlockPath] = useState<string | null>(null)\n const clearSelection = useCallback(() => setSelectedBlockPath(null), [])\n\n return (\n <OverlayProviders\n onClose={onClose}\n onReset={clearSelection}\n storageNamespace={storageNamespace}\n adminPortalSelector={adminPortalSelector}\n >\n <LiveEditorOverlayInner\n onClose={onClose}\n blocksField={blocksField}\n selectedBlockPath={selectedBlockPath}\n setSelectedBlockPath={setSelectedBlockPath}\n />\n </OverlayProviders>\n )\n}\n\ntype InnerProps = LiveEditorOverlayProps & {\n selectedBlockPath: string | null\n setSelectedBlockPath: React.Dispatch<React.SetStateAction<string | null>>\n}\n\nconst LiveEditorOverlayInner: React.FC<InnerProps> = ({\n onClose,\n blocksField,\n selectedBlockPath,\n setSelectedBlockPath,\n}) => {\n const settings = useBetterEditorSettings()\n const history = useEditorHistory()\n const { previewURL } = useLivePreviewContext()\n\n const { sidebarWidth, isResizing, onResizeStart, onResizeKeyDown } = useSidebarResize(\n settings.sidebarPosition,\n )\n const {\n viewport,\n setViewport,\n setResponsiveWidth,\n viewportWidth,\n } = useViewportState(settings)\n // Shared with the toolbar's width chip, which measures the iframe directly.\n const iframeRef = useRef<HTMLIFrameElement | null>(null)\n\n const [isFullscreen, setIsFullscreen] = useState(false)\n const toggleFullscreen = useCallback(() => setIsFullscreen((v) => !v), [])\n const exitFullscreen = useCallback(() => setIsFullscreen(false), [])\n const overlayRef = useFullscreenOverlay(isFullscreen, exitFullscreen)\n useFocusTrap(overlayRef)\n\n const clearSelection = useCallback(\n () => setSelectedBlockPath(null),\n [setSelectedBlockPath],\n )\n\n const { addBelowRequestId } = useBlockActionMessages({\n selectedBlockPath,\n setSelectedBlockPath,\n })\n\n useOverlayKeyboard({ onClose, history })\n\n const [sidebarCollapsed, setSidebarCollapsed] = useState(false)\n const toggleSidebar = useCallback(() => setSidebarCollapsed((v) => !v), [])\n\n const [interactMode, setInteractMode] = useState(false)\n const toggleInteractMode = useCallback(() => setInteractMode((v) => !v), [])\n\n const isLeft = settings.sidebarPosition === 'left'\n const showSidebar = !sidebarCollapsed\n const gridTemplateColumns = !showSidebar\n ? '1fr'\n : isLeft\n ? `${sidebarWidth}px ${RESIZE_HANDLE_PX}px 1fr`\n : `1fr ${RESIZE_HANDLE_PX}px ${sidebarWidth}px`\n\n return (\n <div\n ref={overlayRef}\n className={classes(\n 'better-editor',\n isResizing && 'better-editor--resizing',\n isFullscreen && 'better-editor--fullscreen',\n )}\n role=\"dialog\"\n aria-modal=\"true\"\n aria-label=\"Better Editor\"\n tabIndex={-1}\n >\n <div className=\"better-editor__body\" style={{ gridTemplateColumns }}>\n <div className=\"better-editor__preview\" style={{ order: isLeft ? 2 : 0 }}>\n <PreviewToolbar\n history={history}\n viewport={viewport}\n onViewportChange={setViewport}\n iframeRef={iframeRef}\n isFullscreen={isFullscreen}\n onFullscreenToggle={toggleFullscreen}\n interactMode={interactMode}\n onInteractToggle={toggleInteractMode}\n sidebarCollapsed={sidebarCollapsed}\n onSidebarToggle={toggleSidebar}\n />\n <div className=\"better-editor__preview-stage\">\n <PreviewFrame\n iframeRef={iframeRef}\n previewURL={previewURL}\n hoverColorTopLevel={settings.hoverColorTopLevel}\n hoverColorNested={settings.hoverColorNested}\n hoverOutlineWidth={settings.hoverOutlineWidth}\n showHoverToolbar={settings.showHoverToolbar}\n hoverToolbarPosition={settings.hoverToolbarPosition}\n selectedBlockPath={selectedBlockPath}\n interactMode={interactMode}\n viewport={viewport}\n viewportWidth={viewportWidth}\n resizable={viewport === 'responsive'}\n onResize={setResponsiveWidth}\n />\n </div>\n </div>\n {showSidebar ? (\n <>\n <div\n className=\"better-editor__resize-handle\"\n style={{ order: 1 }}\n role=\"separator\"\n aria-orientation=\"vertical\"\n aria-label=\"Resize sidebar (use ← / → arrow keys)\"\n aria-valuenow={sidebarWidth}\n tabIndex={0}\n onMouseDown={onResizeStart}\n onKeyDown={onResizeKeyDown}\n />\n <aside\n className=\"better-editor__sidebar\"\n style={{ order: isLeft ? 0 : 2 }}\n >\n <Sidebar\n selectedBlockPath={selectedBlockPath}\n onClearSelection={clearSelection}\n onSelectPath={setSelectedBlockPath}\n forceFullWidthFields={settings.forceFullWidthFields}\n blocksField={blocksField}\n addBelowRequestId={addBelowRequestId}\n />\n </aside>\n </>\n ) : null}\n </div>\n </div>\n )\n}\n\n"],"names":["React","useCallback","useRef","useState","useLivePreviewContext","PreviewFrame","PreviewToolbar","Sidebar","useBetterEditorSettings","useEditorHistory","useSidebarResize","useViewportState","useFullscreenOverlay","useBlockActionMessages","useOverlayKeyboard","useFocusTrap","OverlayProviders","RESIZE_HANDLE_PX","classes","parts","filter","Boolean","join","LiveEditorOverlay","onClose","blocksField","storageNamespace","adminPortalSelector","selectedBlockPath","setSelectedBlockPath","clearSelection","onReset","LiveEditorOverlayInner","settings","history","previewURL","sidebarWidth","isResizing","onResizeStart","onResizeKeyDown","sidebarPosition","viewport","setViewport","setResponsiveWidth","viewportWidth","iframeRef","isFullscreen","setIsFullscreen","toggleFullscreen","v","exitFullscreen","overlayRef","addBelowRequestId","sidebarCollapsed","setSidebarCollapsed","toggleSidebar","interactMode","setInteractMode","toggleInteractMode","isLeft","showSidebar","gridTemplateColumns","div","ref","className","role","aria-modal","aria-label","tabIndex","style","order","onViewportChange","onFullscreenToggle","onInteractToggle","onSidebarToggle","hoverColorTopLevel","hoverColorNested","hoverOutlineWidth","showHoverToolbar","hoverToolbarPosition","resizable","onResize","aria-orientation","aria-valuenow","onMouseDown","onKeyDown","aside","onClearSelection","onSelectPath","forceFullWidthFields"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../src/admin/LiveEditorOverlay.tsx"],"sourcesContent":["'use client'\n\nimport React, { useCallback, useRef, useState } from 'react'\nimport { useLivePreviewContext } from '@payloadcms/ui'\nimport { PreviewFrame } from './PreviewFrame'\nimport { PreviewToolbar } from './PreviewToolbar'\nimport { Sidebar } from './sidebar/Sidebar'\nimport { useBetterEditorSettings } from '../state/useBetterEditorSettings'\nimport { useEditorHistory } from '../state/useEditorHistory'\nimport { useSidebarResize } from '../hooks/useSidebarResize'\nimport { useViewportState } from '../hooks/useViewportState'\nimport { useFullscreenOverlay } from '../hooks/useFullscreenOverlay'\nimport { useBlockActionMessages } from '../hooks/useBlockActionMessages'\nimport { useOverlayKeyboard } from '../hooks/useOverlayKeyboard'\nimport { useFocusTrap } from '../hooks/useFocusTrap'\nimport { OverlayProviders } from '../providers/OverlayProviders'\nimport '../styles/overlay.css'\nimport '../styles/preview.css'\nimport '../styles/sidebar.css'\nimport '../styles/blocks-tab.css'\n\nexport type LiveEditorOverlayProps = {\n onClose: () => void\n blocksField: string\n storageNamespace?: string\n adminPortalSelector?: string\n}\n\nconst RESIZE_HANDLE_PX = 6\n\nconst classes = (...parts: Array<string | false | null | undefined>): string =>\n parts.filter(Boolean).join(' ')\n\nexport const LiveEditorOverlay: React.FC<LiveEditorOverlayProps> = ({\n onClose,\n blocksField,\n storageNamespace,\n adminPortalSelector,\n}) => {\n // Selection state lives outside OverlayProviders so the error boundary's\n // onReset can clear it without remounting providers.\n const [selectedBlockPath, setSelectedBlockPath] = useState<string | null>(null)\n const clearSelection = useCallback(() => setSelectedBlockPath(null), [])\n\n return (\n <OverlayProviders\n onClose={onClose}\n onReset={clearSelection}\n storageNamespace={storageNamespace}\n adminPortalSelector={adminPortalSelector}\n >\n <LiveEditorOverlayInner\n blocksField={blocksField}\n selectedBlockPath={selectedBlockPath}\n setSelectedBlockPath={setSelectedBlockPath}\n />\n </OverlayProviders>\n )\n}\n\ntype InnerProps = Omit<LiveEditorOverlayProps, 'onClose'> & {\n selectedBlockPath: string | null\n setSelectedBlockPath: React.Dispatch<React.SetStateAction<string | null>>\n}\n\nconst LiveEditorOverlayInner: React.FC<InnerProps> = ({\n blocksField,\n selectedBlockPath,\n setSelectedBlockPath,\n}) => {\n const settings = useBetterEditorSettings()\n const history = useEditorHistory()\n const { previewURL } = useLivePreviewContext()\n\n const { sidebarWidth, isResizing, onResizeStart, onResizeKeyDown } = useSidebarResize(\n settings.sidebarPosition,\n )\n const {\n viewport,\n setViewport,\n setResponsiveWidth,\n viewportWidth,\n } = useViewportState(settings)\n // Shared with the toolbar's width chip, which measures the iframe directly.\n const iframeRef = useRef<HTMLIFrameElement | null>(null)\n\n const [isFullscreen, setIsFullscreen] = useState(false)\n const toggleFullscreen = useCallback(() => setIsFullscreen((v) => !v), [])\n const exitFullscreen = useCallback(() => setIsFullscreen(false), [])\n const overlayRef = useFullscreenOverlay(isFullscreen, exitFullscreen)\n useFocusTrap(overlayRef)\n\n const clearSelection = useCallback(\n () => setSelectedBlockPath(null),\n [setSelectedBlockPath],\n )\n\n const { addBelowRequestId } = useBlockActionMessages({\n selectedBlockPath,\n setSelectedBlockPath,\n })\n\n useOverlayKeyboard({ history })\n\n const [sidebarCollapsed, setSidebarCollapsed] = useState(false)\n const toggleSidebar = useCallback(() => setSidebarCollapsed((v) => !v), [])\n\n const [interactMode, setInteractMode] = useState(false)\n const toggleInteractMode = useCallback(() => setInteractMode((v) => !v), [])\n\n const isLeft = settings.sidebarPosition === 'left'\n const showSidebar = !sidebarCollapsed\n const gridTemplateColumns = !showSidebar\n ? '1fr'\n : isLeft\n ? `${sidebarWidth}px ${RESIZE_HANDLE_PX}px 1fr`\n : `1fr ${RESIZE_HANDLE_PX}px ${sidebarWidth}px`\n\n return (\n <div\n ref={overlayRef}\n className={classes(\n 'better-editor',\n isResizing && 'better-editor--resizing',\n isFullscreen && 'better-editor--fullscreen',\n )}\n role=\"dialog\"\n aria-modal=\"true\"\n aria-label=\"Better Editor\"\n tabIndex={-1}\n >\n <div className=\"better-editor__body\" style={{ gridTemplateColumns }}>\n <div className=\"better-editor__preview\" style={{ order: isLeft ? 2 : 0 }}>\n <PreviewToolbar\n history={history}\n viewport={viewport}\n onViewportChange={setViewport}\n iframeRef={iframeRef}\n isFullscreen={isFullscreen}\n onFullscreenToggle={toggleFullscreen}\n interactMode={interactMode}\n onInteractToggle={toggleInteractMode}\n sidebarCollapsed={sidebarCollapsed}\n onSidebarToggle={toggleSidebar}\n />\n <div className=\"better-editor__preview-stage\">\n <PreviewFrame\n iframeRef={iframeRef}\n previewURL={previewURL}\n hoverColorTopLevel={settings.hoverColorTopLevel}\n hoverColorNested={settings.hoverColorNested}\n hoverOutlineWidth={settings.hoverOutlineWidth}\n showHoverToolbar={settings.showHoverToolbar}\n hoverToolbarPosition={settings.hoverToolbarPosition}\n selectedBlockPath={selectedBlockPath}\n interactMode={interactMode}\n viewport={viewport}\n viewportWidth={viewportWidth}\n resizable={viewport === 'responsive'}\n onResize={setResponsiveWidth}\n />\n </div>\n </div>\n {showSidebar ? (\n <>\n <div\n className=\"better-editor__resize-handle\"\n style={{ order: 1 }}\n role=\"separator\"\n aria-orientation=\"vertical\"\n aria-label=\"Resize sidebar (use ← / → arrow keys)\"\n aria-valuenow={sidebarWidth}\n tabIndex={0}\n onMouseDown={onResizeStart}\n onKeyDown={onResizeKeyDown}\n />\n <aside\n className=\"better-editor__sidebar\"\n style={{ order: isLeft ? 0 : 2 }}\n >\n <Sidebar\n selectedBlockPath={selectedBlockPath}\n onClearSelection={clearSelection}\n onSelectPath={setSelectedBlockPath}\n forceFullWidthFields={settings.forceFullWidthFields}\n blocksField={blocksField}\n addBelowRequestId={addBelowRequestId}\n />\n </aside>\n </>\n ) : null}\n </div>\n </div>\n )\n}\n\n"],"names":["React","useCallback","useRef","useState","useLivePreviewContext","PreviewFrame","PreviewToolbar","Sidebar","useBetterEditorSettings","useEditorHistory","useSidebarResize","useViewportState","useFullscreenOverlay","useBlockActionMessages","useOverlayKeyboard","useFocusTrap","OverlayProviders","RESIZE_HANDLE_PX","classes","parts","filter","Boolean","join","LiveEditorOverlay","onClose","blocksField","storageNamespace","adminPortalSelector","selectedBlockPath","setSelectedBlockPath","clearSelection","onReset","LiveEditorOverlayInner","settings","history","previewURL","sidebarWidth","isResizing","onResizeStart","onResizeKeyDown","sidebarPosition","viewport","setViewport","setResponsiveWidth","viewportWidth","iframeRef","isFullscreen","setIsFullscreen","toggleFullscreen","v","exitFullscreen","overlayRef","addBelowRequestId","sidebarCollapsed","setSidebarCollapsed","toggleSidebar","interactMode","setInteractMode","toggleInteractMode","isLeft","showSidebar","gridTemplateColumns","div","ref","className","role","aria-modal","aria-label","tabIndex","style","order","onViewportChange","onFullscreenToggle","onInteractToggle","onSidebarToggle","hoverColorTopLevel","hoverColorNested","hoverOutlineWidth","showHoverToolbar","hoverToolbarPosition","resizable","onResize","aria-orientation","aria-valuenow","onMouseDown","onKeyDown","aside","onClearSelection","onSelectPath","forceFullWidthFields"],"mappings":"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"}
|
|
@@ -11,9 +11,17 @@ export type BlockSchemaContext = {
|
|
|
11
11
|
docFields: ClientField[];
|
|
12
12
|
docSlug: string;
|
|
13
13
|
formFields: FormState;
|
|
14
|
+
/** Client config block registry, used to resolve `blockReferences` slugs. */
|
|
15
|
+
blocksMap?: Record<string, ClientBlock>;
|
|
14
16
|
};
|
|
17
|
+
type BlocksLikeField = {
|
|
18
|
+
blocks?: ClientBlock[];
|
|
19
|
+
blockReferences?: (ClientBlock | string)[];
|
|
20
|
+
};
|
|
21
|
+
export declare const resolveFieldBlocks: (field: BlocksLikeField, blocksMap: Record<string, ClientBlock> | undefined) => ClientBlock[];
|
|
15
22
|
export declare const findNamedField: (fields: ClientField[], name: string, schemaPath: string) => {
|
|
16
23
|
field: ClientField;
|
|
17
24
|
schemaPath: string;
|
|
18
25
|
} | null;
|
|
19
26
|
export declare const resolveBlockSchema: (context: BlockSchemaContext, blockPath: string) => Resolved | null;
|
|
27
|
+
export {};
|
|
@@ -1,3 +1,14 @@
|
|
|
1
|
+
// Resolves a blocks field's block configs, handling `blockReferences` (slugs
|
|
2
|
+
// looked up in the config block registry) where the field's own `blocks` is empty.
|
|
3
|
+
export const resolveFieldBlocks = (field, blocksMap)=>{
|
|
4
|
+
if (!field.blockReferences) return field.blocks ?? [];
|
|
5
|
+
const out = [];
|
|
6
|
+
for (const ref of field.blockReferences){
|
|
7
|
+
const block = typeof ref === 'string' ? blocksMap?.[ref] : ref;
|
|
8
|
+
if (block) out.push(block);
|
|
9
|
+
}
|
|
10
|
+
return out;
|
|
11
|
+
};
|
|
1
12
|
const tabHasName = (tab)=>typeof tab.name === 'string';
|
|
2
13
|
export const findNamedField = (fields, name, schemaPath)=>{
|
|
3
14
|
for (const field of fields){
|
|
@@ -24,7 +35,7 @@ export const findNamedField = (fields, name, schemaPath)=>{
|
|
|
24
35
|
return null;
|
|
25
36
|
};
|
|
26
37
|
export const resolveBlockSchema = (context, blockPath)=>{
|
|
27
|
-
const { docFields, docSlug, formFields } = context;
|
|
38
|
+
const { docFields, docSlug, formFields, blocksMap } = context;
|
|
28
39
|
const segments = blockPath.split('.');
|
|
29
40
|
let currentFields = docFields;
|
|
30
41
|
let currentSchemaPath = docSlug;
|
|
@@ -50,7 +61,7 @@ export const resolveBlockSchema = (context, blockPath)=>{
|
|
|
50
61
|
const row = rows[index];
|
|
51
62
|
if (!row?.blockType) return null;
|
|
52
63
|
blockType = row.blockType;
|
|
53
|
-
const blocks = field
|
|
64
|
+
const blocks = resolveFieldBlocks(field, blocksMap);
|
|
54
65
|
blockConfig = blocks.find((b)=>b.slug === blockType) ?? null;
|
|
55
66
|
if (!blockConfig) return null;
|
|
56
67
|
// Capture parent before descending — used by "add sibling block".
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/admin/blocks/schema.ts"],"sourcesContent":["import type { ClientTab } from 'payload'\nimport type { ClientBlock, ClientField, FormState } from 'payload'\n\nexport type Resolved = {\n blockType: string\n blockFields: ClientField[]\n schemaPath: string\n parentPath: string\n blocksFieldSchemaPath: string\n blocksFieldBlocks: ClientBlock[]\n}\n\nexport type BlockSchemaContext = {\n docFields: ClientField[]\n docSlug: string\n formFields: FormState\n}\n\nconst tabHasName = (tab: ClientTab): tab is ClientTab & { name: string } =>\n typeof (tab as { name?: unknown }).name === 'string'\n\nexport const findNamedField = (\n fields: ClientField[],\n name: string,\n schemaPath: string,\n): { field: ClientField; schemaPath: string } | null => {\n for (const field of fields) {\n if (!field || typeof field !== 'object') continue\n\n if ('name' in field && typeof field.name === 'string' && field.name === name) {\n return { field, schemaPath: `${schemaPath}.${name}` }\n }\n\n if (field.type === 'tabs') {\n for (const tab of field.tabs) {\n const tabSchemaPath = tabHasName(tab) ? `${schemaPath}.${tab.name}` : schemaPath\n const found = findNamedField(tab.fields, name, tabSchemaPath)\n if (found) return found\n }\n } else if (field.type === 'collapsible' || field.type === 'row') {\n const found = findNamedField(field.fields, name, schemaPath)\n if (found) return found\n }\n // Groups are not transparent: their inner fields are reached via the\n // group's own name as the next path segment.\n }\n return null\n}\n\nexport const resolveBlockSchema = (\n context: BlockSchemaContext,\n blockPath: string,\n): Resolved | null => {\n const { docFields, docSlug, formFields } = context\n const segments = blockPath.split('.')\n let currentFields: ClientField[] = docFields\n let currentSchemaPath = docSlug\n let currentPath = ''\n let blockType: string | null = null\n let blockConfig: ClientBlock | null = null\n let blocksFieldSchemaPath = ''\n let blocksFieldBlocks: ClientBlock[] = []\n\n for (let i = 0; i < segments.length; i += 2) {\n const fieldName = segments[i]\n const indexStr = segments[i + 1]\n if (indexStr === undefined) break\n const index = Number(indexStr)\n if (!Number.isInteger(index) || index < 0) return null\n\n const found = findNamedField(currentFields, fieldName, currentSchemaPath)\n if (!found) return null\n const field = found.field\n currentSchemaPath = found.schemaPath\n currentPath = currentPath ? `${currentPath}.${fieldName}` : fieldName\n\n if (field.type === 'blocks') {\n const rows = formFields[currentPath]?.rows\n if (!Array.isArray(rows) || index >= rows.length) return null\n const row = rows[index] as { blockType?: string } | undefined\n if (!row?.blockType) return null\n blockType = row.blockType\n const blocks = field
|
|
1
|
+
{"version":3,"sources":["../../../src/admin/blocks/schema.ts"],"sourcesContent":["import type { ClientTab } from 'payload'\nimport type { ClientBlock, ClientField, FormState } from 'payload'\n\nexport type Resolved = {\n blockType: string\n blockFields: ClientField[]\n schemaPath: string\n parentPath: string\n blocksFieldSchemaPath: string\n blocksFieldBlocks: ClientBlock[]\n}\n\nexport type BlockSchemaContext = {\n docFields: ClientField[]\n docSlug: string\n formFields: FormState\n /** Client config block registry, used to resolve `blockReferences` slugs. */\n blocksMap?: Record<string, ClientBlock>\n}\n\ntype BlocksLikeField = {\n blocks?: ClientBlock[]\n blockReferences?: (ClientBlock | string)[]\n}\n\n// Resolves a blocks field's block configs, handling `blockReferences` (slugs\n// looked up in the config block registry) where the field's own `blocks` is empty.\nexport const resolveFieldBlocks = (\n field: BlocksLikeField,\n blocksMap: Record<string, ClientBlock> | undefined,\n): ClientBlock[] => {\n if (!field.blockReferences) return field.blocks ?? []\n const out: ClientBlock[] = []\n for (const ref of field.blockReferences) {\n const block = typeof ref === 'string' ? blocksMap?.[ref] : ref\n if (block) out.push(block)\n }\n return out\n}\n\nconst tabHasName = (tab: ClientTab): tab is ClientTab & { name: string } =>\n typeof (tab as { name?: unknown }).name === 'string'\n\nexport const findNamedField = (\n fields: ClientField[],\n name: string,\n schemaPath: string,\n): { field: ClientField; schemaPath: string } | null => {\n for (const field of fields) {\n if (!field || typeof field !== 'object') continue\n\n if ('name' in field && typeof field.name === 'string' && field.name === name) {\n return { field, schemaPath: `${schemaPath}.${name}` }\n }\n\n if (field.type === 'tabs') {\n for (const tab of field.tabs) {\n const tabSchemaPath = tabHasName(tab) ? `${schemaPath}.${tab.name}` : schemaPath\n const found = findNamedField(tab.fields, name, tabSchemaPath)\n if (found) return found\n }\n } else if (field.type === 'collapsible' || field.type === 'row') {\n const found = findNamedField(field.fields, name, schemaPath)\n if (found) return found\n }\n // Groups are not transparent: their inner fields are reached via the\n // group's own name as the next path segment.\n }\n return null\n}\n\nexport const resolveBlockSchema = (\n context: BlockSchemaContext,\n blockPath: string,\n): Resolved | null => {\n const { docFields, docSlug, formFields, blocksMap } = context\n const segments = blockPath.split('.')\n let currentFields: ClientField[] = docFields\n let currentSchemaPath = docSlug\n let currentPath = ''\n let blockType: string | null = null\n let blockConfig: ClientBlock | null = null\n let blocksFieldSchemaPath = ''\n let blocksFieldBlocks: ClientBlock[] = []\n\n for (let i = 0; i < segments.length; i += 2) {\n const fieldName = segments[i]\n const indexStr = segments[i + 1]\n if (indexStr === undefined) break\n const index = Number(indexStr)\n if (!Number.isInteger(index) || index < 0) return null\n\n const found = findNamedField(currentFields, fieldName, currentSchemaPath)\n if (!found) return null\n const field = found.field\n currentSchemaPath = found.schemaPath\n currentPath = currentPath ? `${currentPath}.${fieldName}` : fieldName\n\n if (field.type === 'blocks') {\n const rows = formFields[currentPath]?.rows\n if (!Array.isArray(rows) || index >= rows.length) return null\n const row = rows[index] as { blockType?: string } | undefined\n if (!row?.blockType) return null\n blockType = row.blockType\n const blocks = resolveFieldBlocks(field, blocksMap)\n blockConfig = blocks.find((b) => b.slug === blockType) ?? null\n if (!blockConfig) return null\n // Capture parent before descending — used by \"add sibling block\".\n blocksFieldSchemaPath = currentSchemaPath\n blocksFieldBlocks = blocks\n currentFields = blockConfig.fields\n currentSchemaPath = `${currentSchemaPath}.${blockType}`\n currentPath = `${currentPath}.${index}`\n } else if (field.type === 'array') {\n currentFields = field.fields\n currentPath = `${currentPath}.${index}`\n } else {\n return null\n }\n }\n\n if (!blockType || !blockConfig) return null\n\n return {\n blockType,\n blockFields: currentFields,\n schemaPath: currentSchemaPath,\n parentPath: currentPath,\n blocksFieldSchemaPath,\n blocksFieldBlocks,\n }\n}\n"],"names":["resolveFieldBlocks","field","blocksMap","blockReferences","blocks","out","ref","block","push","tabHasName","tab","name","findNamedField","fields","schemaPath","type","tabs","tabSchemaPath","found","resolveBlockSchema","context","blockPath","docFields","docSlug","formFields","segments","split","currentFields","currentSchemaPath","currentPath","blockType","blockConfig","blocksFieldSchemaPath","blocksFieldBlocks","i","length","fieldName","indexStr","undefined","index","Number","isInteger","rows","Array","isArray","row","find","b","slug","blockFields","parentPath"],"mappings":"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"}
|
|
@@ -1,18 +1,20 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
3
3
|
import React, { useMemo } from 'react';
|
|
4
|
-
import { RenderFields, useAllFormFields, useDrawerSlug, useField, useModal } from '@payloadcms/ui';
|
|
4
|
+
import { RenderFields, useAllFormFields, useConfig, useDrawerSlug, useField, useModal } from '@payloadcms/ui';
|
|
5
5
|
import { useDocConfig } from '../../hooks/useDocConfig';
|
|
6
6
|
import { useAddBlockDrawer } from '../../hooks/useAddBlockDrawer';
|
|
7
7
|
import { AddBlockDrawer } from '../blocks/AddBlockDrawer';
|
|
8
8
|
import { BlockActionsToolbar } from '../blocks/BlockActionsToolbar';
|
|
9
9
|
import { useBlockActions } from '../blocks/useBlockActions';
|
|
10
|
-
import { findNamedField, resolveBlockSchema } from '../blocks/schema';
|
|
10
|
+
import { findNamedField, resolveBlockSchema, resolveFieldBlocks } from '../blocks/schema';
|
|
11
11
|
import { BlockEmptyState } from '../blocks/BlockEmptyState';
|
|
12
12
|
import { BlockHeader } from '../blocks/BlockHeader';
|
|
13
13
|
export const BlockSettingsTab = ({ selectedBlockPath, onClearSelection, onSelectPath, blocksField, addBelowRequestId = 0 })=>{
|
|
14
14
|
const { fields: docFields, slug: docSlug } = useDocConfig();
|
|
15
15
|
const [fields] = useAllFormFields();
|
|
16
|
+
const { config } = useConfig();
|
|
17
|
+
const blocksMap = config.blocksMap;
|
|
16
18
|
const { toggleModal } = useModal();
|
|
17
19
|
const addBlockDrawerSlug = useDrawerSlug('better-editor-add-block');
|
|
18
20
|
const addAfterDrawerSlug = useDrawerSlug('better-editor-add-after');
|
|
@@ -35,7 +37,7 @@ export const BlockSettingsTab = ({ selectedBlockPath, onClearSelection, onSelect
|
|
|
35
37
|
docSlug
|
|
36
38
|
]);
|
|
37
39
|
const blocksFieldField = blocksFieldInfo?.field;
|
|
38
|
-
const availableBlocks = blocksFieldField && blocksFieldField.type === 'blocks' ? blocksFieldField
|
|
40
|
+
const availableBlocks = blocksFieldField && blocksFieldField.type === 'blocks' ? resolveFieldBlocks(blocksFieldField, blocksMap) : [];
|
|
39
41
|
const blocksSchemaPath = blocksFieldInfo?.schemaPath || '';
|
|
40
42
|
const topLevelRows = fields[blocksField]?.rows;
|
|
41
43
|
const addRowIndex = Array.isArray(topLevelRows) ? topLevelRows.length : 0;
|
|
@@ -63,7 +65,8 @@ export const BlockSettingsTab = ({ selectedBlockPath, onClearSelection, onSelect
|
|
|
63
65
|
const resolved = docFields ? resolveBlockSchema({
|
|
64
66
|
docFields,
|
|
65
67
|
docSlug: docSlug || '',
|
|
66
|
-
formFields: fields
|
|
68
|
+
formFields: fields,
|
|
69
|
+
blocksMap
|
|
67
70
|
}, selectedBlockPath) : null;
|
|
68
71
|
return /*#__PURE__*/ _jsxs("div", {
|
|
69
72
|
className: "better-editor-tab better-editor-tab--native",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/admin/sidebar/BlockSettingsTab.tsx"],"sourcesContent":["'use client'\n\nimport React, { useMemo } from 'react'\nimport {\n RenderFields,\n useAllFormFields,\n useDrawerSlug,\n useField,\n useModal,\n} from '@payloadcms/ui'\nimport { useDocConfig } from '../../hooks/useDocConfig'\nimport { useAddBlockDrawer } from '../../hooks/useAddBlockDrawer'\nimport { AddBlockDrawer } from '../blocks/AddBlockDrawer'\nimport { BlockActionsToolbar } from '../blocks/BlockActionsToolbar'\nimport { useBlockActions } from '../blocks/useBlockActions'\nimport { findNamedField, resolveBlockSchema } from '../blocks/schema'\nimport type { ClientBlock } from 'payload'\nimport { BlockEmptyState } from '../blocks/BlockEmptyState'\nimport { BlockHeader } from '../blocks/BlockHeader'\n\nexport type BlockSettingsTabProps = {\n selectedBlockPath: string | null\n onClearSelection: () => void\n onSelectPath: (path: string | null) => void\n blocksField: string\n /** Bump to open the add-after drawer externally (iframe `+` button). */\n addBelowRequestId?: number\n}\n\nexport const BlockSettingsTab: React.FC<BlockSettingsTabProps> = ({\n selectedBlockPath,\n onClearSelection,\n onSelectPath,\n blocksField,\n addBelowRequestId = 0,\n}) => {\n const { fields: docFields, slug: docSlug } = useDocConfig()\n const [fields] = useAllFormFields()\n const { toggleModal } = useModal()\n const addBlockDrawerSlug = useDrawerSlug('better-editor-add-block')\n const addAfterDrawerSlug = useDrawerSlug('better-editor-add-after')\n\n const actions = useBlockActions({\n selectedBlockPath,\n onSelectPath,\n onClearSelection,\n })\n\n useAddBlockDrawer({\n drawerSlug: addAfterDrawerSlug,\n requestId: addBelowRequestId,\n selectedBlockPath,\n })\n\n const blocksFieldInfo = useMemo(() => {\n if (!docFields) return null\n return findNamedField(docFields, blocksField, docSlug || '')\n }, [docFields, blocksField, docSlug])\n\n const blocksFieldField = blocksFieldInfo?.field\n const availableBlocks: ClientBlock[] =\n blocksFieldField && blocksFieldField.type === 'blocks' ? blocksFieldField.blocks : []\n const blocksSchemaPath = blocksFieldInfo?.schemaPath || ''\n const topLevelRows = fields[blocksField]?.rows\n const addRowIndex = Array.isArray(topLevelRows) ? topLevelRows.length : 0\n\n if (!selectedBlockPath) {\n return (\n <>\n <BlockEmptyState\n canAdd={availableBlocks.length > 0}\n onAddClick={() => toggleModal(addBlockDrawerSlug)}\n />\n {availableBlocks.length > 0 ? (\n <AddBlockDrawer\n slug={addBlockDrawerSlug}\n blocks={availableBlocks}\n addRow={(index, blockType) =>\n actions.addAfter({\n blockType,\n schemaPath: blocksSchemaPath,\n containerPath: blocksField,\n index,\n })\n }\n addRowIndex={addRowIndex}\n />\n ) : null}\n </>\n )\n }\n\n const resolved = docFields\n ? resolveBlockSchema({ docFields, docSlug: docSlug || '', formFields: fields }, selectedBlockPath)\n : null\n\n return (\n <div className=\"better-editor-tab better-editor-tab--native\">\n <BlockHeader\n blockType={resolved?.blockType || 'unknown'}\n path={selectedBlockPath}\n onClearSelection={onClearSelection}\n />\n\n <hr className=\"better-editor-tab__divider\" aria-hidden=\"true\" />\n\n <BlockActionsToolbar\n canMoveUp={actions.canMoveUp}\n canMoveDown={actions.canMoveDown}\n canMutate={actions.canMutate}\n canAddBelow={!!resolved}\n onMoveUp={actions.moveUp}\n onMoveDown={actions.moveDown}\n onDuplicate={actions.duplicate}\n onAddBelow={() => toggleModal(addAfterDrawerSlug)}\n onDelete={actions.remove}\n />\n\n {actions.canMutate && resolved && resolved.blocksFieldBlocks.length > 0 ? (\n <AddBlockDrawer\n slug={addAfterDrawerSlug}\n blocks={resolved.blocksFieldBlocks}\n addRow={(index, blockType) =>\n actions.addAfter({\n blockType,\n schemaPath: resolved.blocksFieldSchemaPath,\n containerPath: actions.parentPath,\n index,\n })\n }\n addRowIndex={actions.rowIndex + 1}\n />\n ) : null}\n\n {!resolved ? (\n <div className=\"better-editor-tab__empty\">\n Could not resolve block schema for this path.\n </div>\n ) : (\n <>\n <BlockNameInput path={`${selectedBlockPath}.blockName`} />\n <hr className=\"better-editor-tab__divider\" aria-hidden=\"true\" />\n <RenderFields\n fields={resolved.blockFields}\n parentPath={resolved.parentPath}\n parentIndexPath=\"\"\n parentSchemaPath={resolved.schemaPath}\n // RenderFields' client read-gate is bypassed; the server-side\n // write check still runs on save.\n permissions={true}\n />\n </>\n )}\n </div>\n )\n}\n\nconst BlockNameInput: React.FC<{ path: string }> = ({ path }) => {\n const { value, setValue } = useField<string>({ path })\n const inputId = `be-blockname-${path}`\n return (\n <div className=\"better-editor-tab__block-name\">\n <label className=\"better-editor-tab__block-name-label\" htmlFor={inputId}>\n Block Name\n </label>\n <input\n id={inputId}\n className=\"better-editor-tab__block-name-input\"\n type=\"text\"\n value={value || ''}\n onChange={(e) => setValue(e.target.value)}\n placeholder=\"Optional label for this block\"\n />\n </div>\n )\n}\n"],"names":["React","useMemo","RenderFields","useAllFormFields","useDrawerSlug","useField","useModal","useDocConfig","useAddBlockDrawer","AddBlockDrawer","BlockActionsToolbar","useBlockActions","findNamedField","resolveBlockSchema","BlockEmptyState","BlockHeader","BlockSettingsTab","selectedBlockPath","onClearSelection","onSelectPath","blocksField","addBelowRequestId","fields","docFields","slug","docSlug","toggleModal","addBlockDrawerSlug","addAfterDrawerSlug","actions","drawerSlug","requestId","blocksFieldInfo","blocksFieldField","field","availableBlocks","type","blocks","blocksSchemaPath","schemaPath","topLevelRows","rows","addRowIndex","Array","isArray","length","canAdd","onAddClick","addRow","index","blockType","addAfter","containerPath","resolved","formFields","div","className","path","hr","aria-hidden","canMoveUp","canMoveDown","canMutate","canAddBelow","onMoveUp","moveUp","onMoveDown","moveDown","onDuplicate","duplicate","onAddBelow","onDelete","remove","blocksFieldBlocks","blocksFieldSchemaPath","parentPath","rowIndex","BlockNameInput","blockFields","parentIndexPath","parentSchemaPath","permissions","value","setValue","inputId","label","htmlFor","input","id","onChange","e","target","placeholder"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../../src/admin/sidebar/BlockSettingsTab.tsx"],"sourcesContent":["'use client'\n\nimport React, { useMemo } from 'react'\nimport {\n RenderFields,\n useAllFormFields,\n useConfig,\n useDrawerSlug,\n useField,\n useModal,\n} from '@payloadcms/ui'\nimport { useDocConfig } from '../../hooks/useDocConfig'\nimport { useAddBlockDrawer } from '../../hooks/useAddBlockDrawer'\nimport { AddBlockDrawer } from '../blocks/AddBlockDrawer'\nimport { BlockActionsToolbar } from '../blocks/BlockActionsToolbar'\nimport { useBlockActions } from '../blocks/useBlockActions'\nimport { findNamedField, resolveBlockSchema, resolveFieldBlocks } from '../blocks/schema'\nimport type { ClientBlock } from 'payload'\nimport { BlockEmptyState } from '../blocks/BlockEmptyState'\nimport { BlockHeader } from '../blocks/BlockHeader'\n\nexport type BlockSettingsTabProps = {\n selectedBlockPath: string | null\n onClearSelection: () => void\n onSelectPath: (path: string | null) => void\n blocksField: string\n /** Bump to open the add-after drawer externally (iframe `+` button). */\n addBelowRequestId?: number\n}\n\nexport const BlockSettingsTab: React.FC<BlockSettingsTabProps> = ({\n selectedBlockPath,\n onClearSelection,\n onSelectPath,\n blocksField,\n addBelowRequestId = 0,\n}) => {\n const { fields: docFields, slug: docSlug } = useDocConfig()\n const [fields] = useAllFormFields()\n const { config } = useConfig()\n const blocksMap = config.blocksMap\n const { toggleModal } = useModal()\n const addBlockDrawerSlug = useDrawerSlug('better-editor-add-block')\n const addAfterDrawerSlug = useDrawerSlug('better-editor-add-after')\n\n const actions = useBlockActions({\n selectedBlockPath,\n onSelectPath,\n onClearSelection,\n })\n\n useAddBlockDrawer({\n drawerSlug: addAfterDrawerSlug,\n requestId: addBelowRequestId,\n selectedBlockPath,\n })\n\n const blocksFieldInfo = useMemo(() => {\n if (!docFields) return null\n return findNamedField(docFields, blocksField, docSlug || '')\n }, [docFields, blocksField, docSlug])\n\n const blocksFieldField = blocksFieldInfo?.field\n const availableBlocks: ClientBlock[] =\n blocksFieldField && blocksFieldField.type === 'blocks'\n ? resolveFieldBlocks(blocksFieldField, blocksMap)\n : []\n const blocksSchemaPath = blocksFieldInfo?.schemaPath || ''\n const topLevelRows = fields[blocksField]?.rows\n const addRowIndex = Array.isArray(topLevelRows) ? topLevelRows.length : 0\n\n if (!selectedBlockPath) {\n return (\n <>\n <BlockEmptyState\n canAdd={availableBlocks.length > 0}\n onAddClick={() => toggleModal(addBlockDrawerSlug)}\n />\n {availableBlocks.length > 0 ? (\n <AddBlockDrawer\n slug={addBlockDrawerSlug}\n blocks={availableBlocks}\n addRow={(index, blockType) =>\n actions.addAfter({\n blockType,\n schemaPath: blocksSchemaPath,\n containerPath: blocksField,\n index,\n })\n }\n addRowIndex={addRowIndex}\n />\n ) : null}\n </>\n )\n }\n\n const resolved = docFields\n ? resolveBlockSchema(\n { docFields, docSlug: docSlug || '', formFields: fields, blocksMap },\n selectedBlockPath,\n )\n : null\n\n return (\n <div className=\"better-editor-tab better-editor-tab--native\">\n <BlockHeader\n blockType={resolved?.blockType || 'unknown'}\n path={selectedBlockPath}\n onClearSelection={onClearSelection}\n />\n\n <hr className=\"better-editor-tab__divider\" aria-hidden=\"true\" />\n\n <BlockActionsToolbar\n canMoveUp={actions.canMoveUp}\n canMoveDown={actions.canMoveDown}\n canMutate={actions.canMutate}\n canAddBelow={!!resolved}\n onMoveUp={actions.moveUp}\n onMoveDown={actions.moveDown}\n onDuplicate={actions.duplicate}\n onAddBelow={() => toggleModal(addAfterDrawerSlug)}\n onDelete={actions.remove}\n />\n\n {actions.canMutate && resolved && resolved.blocksFieldBlocks.length > 0 ? (\n <AddBlockDrawer\n slug={addAfterDrawerSlug}\n blocks={resolved.blocksFieldBlocks}\n addRow={(index, blockType) =>\n actions.addAfter({\n blockType,\n schemaPath: resolved.blocksFieldSchemaPath,\n containerPath: actions.parentPath,\n index,\n })\n }\n addRowIndex={actions.rowIndex + 1}\n />\n ) : null}\n\n {!resolved ? (\n <div className=\"better-editor-tab__empty\">\n Could not resolve block schema for this path.\n </div>\n ) : (\n <>\n <BlockNameInput path={`${selectedBlockPath}.blockName`} />\n <hr className=\"better-editor-tab__divider\" aria-hidden=\"true\" />\n <RenderFields\n fields={resolved.blockFields}\n parentPath={resolved.parentPath}\n parentIndexPath=\"\"\n parentSchemaPath={resolved.schemaPath}\n // RenderFields' client read-gate is bypassed; the server-side\n // write check still runs on save.\n permissions={true}\n />\n </>\n )}\n </div>\n )\n}\n\nconst BlockNameInput: React.FC<{ path: string }> = ({ path }) => {\n const { value, setValue } = useField<string>({ path })\n const inputId = `be-blockname-${path}`\n return (\n <div className=\"better-editor-tab__block-name\">\n <label className=\"better-editor-tab__block-name-label\" htmlFor={inputId}>\n Block Name\n </label>\n <input\n id={inputId}\n className=\"better-editor-tab__block-name-input\"\n type=\"text\"\n value={value || ''}\n onChange={(e) => setValue(e.target.value)}\n placeholder=\"Optional label for this block\"\n />\n </div>\n )\n}\n"],"names":["React","useMemo","RenderFields","useAllFormFields","useConfig","useDrawerSlug","useField","useModal","useDocConfig","useAddBlockDrawer","AddBlockDrawer","BlockActionsToolbar","useBlockActions","findNamedField","resolveBlockSchema","resolveFieldBlocks","BlockEmptyState","BlockHeader","BlockSettingsTab","selectedBlockPath","onClearSelection","onSelectPath","blocksField","addBelowRequestId","fields","docFields","slug","docSlug","config","blocksMap","toggleModal","addBlockDrawerSlug","addAfterDrawerSlug","actions","drawerSlug","requestId","blocksFieldInfo","blocksFieldField","field","availableBlocks","type","blocksSchemaPath","schemaPath","topLevelRows","rows","addRowIndex","Array","isArray","length","canAdd","onAddClick","blocks","addRow","index","blockType","addAfter","containerPath","resolved","formFields","div","className","path","hr","aria-hidden","canMoveUp","canMoveDown","canMutate","canAddBelow","onMoveUp","moveUp","onMoveDown","moveDown","onDuplicate","duplicate","onAddBelow","onDelete","remove","blocksFieldBlocks","blocksFieldSchemaPath","parentPath","rowIndex","BlockNameInput","blockFields","parentIndexPath","parentSchemaPath","permissions","value","setValue","inputId","label","htmlFor","input","id","onChange","e","target","placeholder"],"mappings":"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"}
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { useEditorHistory } from '../state/useEditorHistory';
|
|
2
2
|
export type UseOverlayKeyboardArgs = {
|
|
3
|
-
onClose: () => void;
|
|
4
3
|
history: ReturnType<typeof useEditorHistory>;
|
|
5
4
|
};
|
|
6
|
-
export declare const useOverlayKeyboard: ({
|
|
5
|
+
export declare const useOverlayKeyboard: ({ history }: UseOverlayKeyboardArgs) => void;
|
|
@@ -1,26 +1,12 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
import { useEffect } from 'react';
|
|
3
3
|
import { useLatestRef } from './useLatestRef';
|
|
4
|
-
const
|
|
5
|
-
'INPUT',
|
|
6
|
-
'TEXTAREA',
|
|
7
|
-
'SELECT'
|
|
8
|
-
]);
|
|
9
|
-
// Don't hijack Escape away from text inputs / native dropdowns / rich text
|
|
10
|
-
// editors — users expect it to clear/blur the field first.
|
|
11
|
-
const isEditableTarget = (el)=>!!el && (EDITABLE_TAGS.has(el.tagName) || el.isContentEditable === true);
|
|
12
|
-
export const useOverlayKeyboard = ({ onClose, history })=>{
|
|
4
|
+
export const useOverlayKeyboard = ({ history })=>{
|
|
13
5
|
const handlersRef = useLatestRef({
|
|
14
|
-
onClose,
|
|
15
6
|
history
|
|
16
7
|
});
|
|
17
8
|
useEffect(()=>{
|
|
18
9
|
const onKey = (e)=>{
|
|
19
|
-
if (e.key === 'Escape') {
|
|
20
|
-
if (isEditableTarget(document.activeElement)) return;
|
|
21
|
-
handlersRef.current.onClose();
|
|
22
|
-
return;
|
|
23
|
-
}
|
|
24
10
|
if (!(e.metaKey || e.ctrlKey)) return;
|
|
25
11
|
const k = e.key.toLowerCase();
|
|
26
12
|
if (k === 'z') {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/hooks/useOverlayKeyboard.ts"],"sourcesContent":["'use client'\n\nimport { useEffect } from 'react'\nimport { useEditorHistory } from '../state/useEditorHistory'\nimport { useLatestRef } from './useLatestRef'\n\nexport type UseOverlayKeyboardArgs = {\n
|
|
1
|
+
{"version":3,"sources":["../../src/hooks/useOverlayKeyboard.ts"],"sourcesContent":["'use client'\n\nimport { useEffect } from 'react'\nimport { useEditorHistory } from '../state/useEditorHistory'\nimport { useLatestRef } from './useLatestRef'\n\nexport type UseOverlayKeyboardArgs = {\n history: ReturnType<typeof useEditorHistory>\n}\n\nexport const useOverlayKeyboard = ({ history }: UseOverlayKeyboardArgs): void => {\n const handlersRef = useLatestRef({ history })\n\n useEffect(() => {\n const onKey = (e: KeyboardEvent) => {\n if (!(e.metaKey || e.ctrlKey)) return\n const k = e.key.toLowerCase()\n if (k === 'z') {\n e.preventDefault()\n const { history: h } = handlersRef.current\n if (e.shiftKey) h.redo()\n else h.undo()\n } else if (k === 'y') {\n e.preventDefault()\n handlersRef.current.history.redo()\n }\n }\n window.addEventListener('keydown', onKey)\n return () => window.removeEventListener('keydown', onKey)\n }, [handlersRef])\n}\n"],"names":["useEffect","useLatestRef","useOverlayKeyboard","history","handlersRef","onKey","e","metaKey","ctrlKey","k","key","toLowerCase","preventDefault","h","current","shiftKey","redo","undo","window","addEventListener","removeEventListener"],"mappings":"AAAA;AAEA,SAASA,SAAS,QAAQ,QAAO;AAEjC,SAASC,YAAY,QAAQ,iBAAgB;AAM7C,OAAO,MAAMC,qBAAqB,CAAC,EAAEC,OAAO,EAA0B;IACpE,MAAMC,cAAcH,aAAa;QAAEE;IAAQ;IAE3CH,UAAU;QACR,MAAMK,QAAQ,CAACC;YACb,IAAI,CAAEA,CAAAA,EAAEC,OAAO,IAAID,EAAEE,OAAO,AAAD,GAAI;YAC/B,MAAMC,IAAIH,EAAEI,GAAG,CAACC,WAAW;YAC3B,IAAIF,MAAM,KAAK;gBACbH,EAAEM,cAAc;gBAChB,MAAM,EAAET,SAASU,CAAC,EAAE,GAAGT,YAAYU,OAAO;gBAC1C,IAAIR,EAAES,QAAQ,EAAEF,EAAEG,IAAI;qBACjBH,EAAEI,IAAI;YACb,OAAO,IAAIR,MAAM,KAAK;gBACpBH,EAAEM,cAAc;gBAChBR,YAAYU,OAAO,CAACX,OAAO,CAACa,IAAI;YAClC;QACF;QACAE,OAAOC,gBAAgB,CAAC,WAAWd;QACnC,OAAO,IAAMa,OAAOE,mBAAmB,CAAC,WAAWf;IACrD,GAAG;QAACD;KAAY;AAClB,EAAC"}
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { BETTER_EDITOR_SETTINGS_BANNER_FIELD, betterEditorSettingsGlobal } from './global';
|
|
2
|
+
import { normalizeEntities } from './internal/entities';
|
|
2
3
|
export { BETTER_EDITOR_SETTINGS_SLUG } from './global';
|
|
3
4
|
export { VERSION } from './version';
|
|
4
5
|
const DEFAULT_BLOCKS_FIELD = 'layout';
|
|
@@ -72,14 +73,14 @@ const warnMissingBlocksField = (kind, slug, blocksField)=>{
|
|
|
72
73
|
* @see {@link BetterEditorConfig} for all options.
|
|
73
74
|
*/ export const betterEditor = (pluginOptions)=>(config)=>{
|
|
74
75
|
if (pluginOptions?.disabled) return config;
|
|
75
|
-
const
|
|
76
|
-
const
|
|
77
|
-
const
|
|
78
|
-
const
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
76
|
+
const defaultBlocksField = pluginOptions?.blocksField || DEFAULT_BLOCKS_FIELD;
|
|
77
|
+
const collectionMap = normalizeEntities(pluginOptions?.collections, defaultBlocksField);
|
|
78
|
+
const globalMap = normalizeEntities(pluginOptions?.globals, defaultBlocksField);
|
|
79
|
+
const toggleClientProps = (blocksField)=>({
|
|
80
|
+
blocksField,
|
|
81
|
+
adminPortalSelector: pluginOptions?.adminPortalSelector,
|
|
82
|
+
storageNamespace: pluginOptions?.storageNamespace
|
|
83
|
+
});
|
|
83
84
|
const showBanner = pluginOptions?.showSettingsBanner !== false;
|
|
84
85
|
const settingsGlobal = showBanner ? betterEditorSettingsGlobal : {
|
|
85
86
|
...betterEditorSettingsGlobal,
|
|
@@ -91,28 +92,30 @@ const warnMissingBlocksField = (kind, slug, blocksField)=>{
|
|
|
91
92
|
...existingGlobals,
|
|
92
93
|
settingsGlobal
|
|
93
94
|
];
|
|
94
|
-
if (
|
|
95
|
+
if (collectionMap.size === 0 && globalMap.size === 0) {
|
|
95
96
|
if (isDev) {
|
|
96
97
|
console.warn('[better-editor] plugin loaded with empty `collections` and `globals` — toggle button will not appear anywhere. Pass `collections: ["pages"]` (or similar) to BetterEditorConfig.');
|
|
97
98
|
}
|
|
98
99
|
return config;
|
|
99
100
|
}
|
|
100
|
-
if (
|
|
101
|
+
if (collectionMap.size > 0 && config.collections) {
|
|
101
102
|
config.collections = config.collections.map((collection)=>{
|
|
102
|
-
|
|
103
|
+
const blocksField = collectionMap.get(collection.slug);
|
|
104
|
+
if (blocksField === undefined) return collection;
|
|
103
105
|
if (isDev && !hasBlocksField(collection.fields, blocksField)) {
|
|
104
106
|
warnMissingBlocksField('collection', collection.slug, blocksField);
|
|
105
107
|
}
|
|
106
|
-
return withToggleInjected(collection, 'edit',
|
|
108
|
+
return withToggleInjected(collection, 'edit', toggleClientProps(blocksField));
|
|
107
109
|
});
|
|
108
110
|
}
|
|
109
|
-
if (
|
|
111
|
+
if (globalMap.size > 0) {
|
|
110
112
|
config.globals = (config.globals ?? []).map((global)=>{
|
|
111
|
-
|
|
113
|
+
const blocksField = globalMap.get(global.slug);
|
|
114
|
+
if (blocksField === undefined) return global;
|
|
112
115
|
if (isDev && !hasBlocksField(global.fields, blocksField)) {
|
|
113
116
|
warnMissingBlocksField('global', global.slug, blocksField);
|
|
114
117
|
}
|
|
115
|
-
return withToggleInjected(global, 'elements',
|
|
118
|
+
return withToggleInjected(global, 'elements', toggleClientProps(blocksField));
|
|
116
119
|
});
|
|
117
120
|
}
|
|
118
121
|
return config;
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["import type { CollectionConfig, Config, Field, GlobalConfig } from 'payload'\nimport type { BetterEditorConfig } from './types'\nimport { BETTER_EDITOR_SETTINGS_BANNER_FIELD, betterEditorSettingsGlobal } from './global'\n\nexport type { BetterEditorConfig }\nexport type { BetterEditorSettings, HoverToolbarPosition } from './state/useBetterEditorSettings'\nexport type { SidebarPosition } from './internal/constants'\nexport { BETTER_EDITOR_SETTINGS_SLUG } from './global'\n\n/** Plugin signature — handy for typing plugin lists in consumer code. */\nexport type BetterEditorPlugin = (config: Config) => Config\n\nexport { VERSION } from './version'\n\nconst DEFAULT_BLOCKS_FIELD = 'layout'\nconst TOGGLE_COMPONENT_PATH = 'payload-better-editor/client#LiveEditorToggle'\nconst isDev = process.env.NODE_ENV !== 'production'\n\n/**\n * Checks whether a field with the given `name` exists at the document's\n * top-level data path. Recurses into presentational containers that do\n * not introduce a path segment (`tabs` without a name, `row`, `collapsible`)\n * but stops at `group` and named tabs, since those namespace their children.\n */\nconst hasBlocksField = (fields: Field[] | undefined, name: string): boolean => {\n if (!Array.isArray(fields)) return false\n return fields.some((field) => {\n if ('name' in field && field.name === name) return true\n if (field.type === 'row' || field.type === 'collapsible') {\n return hasBlocksField(field.fields, name)\n }\n if (field.type === 'tabs') {\n return field.tabs.some((tab) =>\n 'name' in tab && tab.name ? false : hasBlocksField(tab.fields, name),\n )\n }\n return false\n })\n}\n\ntype ToggleSlot = 'edit' | 'elements'\n\ntype ToggleClientProps = {\n blocksField: string\n adminPortalSelector?: string\n storageNamespace?: string\n}\n\nconst withToggleInjected = <T extends CollectionConfig | GlobalConfig>(\n entity: T,\n slot: ToggleSlot,\n clientProps: ToggleClientProps,\n): T => {\n const admin = { ...(entity.admin ?? {}) } as NonNullable<T['admin']>\n const components = { ...(admin.components ?? {}) } as Record<string, unknown>\n const target = { ...((components[slot] as Record<string, unknown>) ?? {}) }\n const before = (target.beforeDocumentControls as unknown[]) ?? []\n return {\n ...entity,\n admin: {\n ...admin,\n components: {\n ...components,\n [slot]: {\n ...target,\n beforeDocumentControls: [\n ...before,\n { path: TOGGLE_COMPONENT_PATH, clientProps },\n ],\n },\n },\n },\n }\n}\n\nconst warnMissingBlocksField = (kind: 'collection' | 'global', slug: string, blocksField: string) => {\n\n console.warn(\n `[better-editor] ${kind} \"${slug}\" has no top-level field named \"${blocksField}\" — the sidebar Blocks tab will be empty. Set \\`blocksField\\` to the actual blocks field name.`,\n )\n}\n\n/**\n * Payload CMS plugin factory for the Better Editor overlay. Adds an\n * \"Open Better Editor\" toggle to the configured collections / globals\n * and registers a `BetterEditorSettings` global with editor-wide options.\n *\n * @example\n * import { betterEditor } from 'payload-better-editor'\n *\n * export default buildConfig({\n * plugins: [betterEditor({ collections: ['pages'] })],\n * // ...\n * })\n *\n * @see {@link BetterEditorConfig} for all options.\n */\nexport const betterEditor =\n (pluginOptions?: BetterEditorConfig): BetterEditorPlugin =>\n (config: Config): Config => {\n if (pluginOptions?.disabled) return config\n\n const
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["import type { CollectionConfig, Config, Field, GlobalConfig } from 'payload'\nimport type { BetterEditorConfig } from './types'\nimport { BETTER_EDITOR_SETTINGS_BANNER_FIELD, betterEditorSettingsGlobal } from './global'\nimport { normalizeEntities } from './internal/entities'\n\nexport type { BetterEditorConfig }\nexport type { BetterEditorSettings, HoverToolbarPosition } from './state/useBetterEditorSettings'\nexport type { SidebarPosition } from './internal/constants'\nexport { BETTER_EDITOR_SETTINGS_SLUG } from './global'\n\n/** Plugin signature — handy for typing plugin lists in consumer code. */\nexport type BetterEditorPlugin = (config: Config) => Config\n\nexport { VERSION } from './version'\n\nconst DEFAULT_BLOCKS_FIELD = 'layout'\nconst TOGGLE_COMPONENT_PATH = 'payload-better-editor/client#LiveEditorToggle'\nconst isDev = process.env.NODE_ENV !== 'production'\n\n/**\n * Checks whether a field with the given `name` exists at the document's\n * top-level data path. Recurses into presentational containers that do\n * not introduce a path segment (`tabs` without a name, `row`, `collapsible`)\n * but stops at `group` and named tabs, since those namespace their children.\n */\nconst hasBlocksField = (fields: Field[] | undefined, name: string): boolean => {\n if (!Array.isArray(fields)) return false\n return fields.some((field) => {\n if ('name' in field && field.name === name) return true\n if (field.type === 'row' || field.type === 'collapsible') {\n return hasBlocksField(field.fields, name)\n }\n if (field.type === 'tabs') {\n return field.tabs.some((tab) =>\n 'name' in tab && tab.name ? false : hasBlocksField(tab.fields, name),\n )\n }\n return false\n })\n}\n\ntype ToggleSlot = 'edit' | 'elements'\n\ntype ToggleClientProps = {\n blocksField: string\n adminPortalSelector?: string\n storageNamespace?: string\n}\n\nconst withToggleInjected = <T extends CollectionConfig | GlobalConfig>(\n entity: T,\n slot: ToggleSlot,\n clientProps: ToggleClientProps,\n): T => {\n const admin = { ...(entity.admin ?? {}) } as NonNullable<T['admin']>\n const components = { ...(admin.components ?? {}) } as Record<string, unknown>\n const target = { ...((components[slot] as Record<string, unknown>) ?? {}) }\n const before = (target.beforeDocumentControls as unknown[]) ?? []\n return {\n ...entity,\n admin: {\n ...admin,\n components: {\n ...components,\n [slot]: {\n ...target,\n beforeDocumentControls: [\n ...before,\n { path: TOGGLE_COMPONENT_PATH, clientProps },\n ],\n },\n },\n },\n }\n}\n\nconst warnMissingBlocksField = (kind: 'collection' | 'global', slug: string, blocksField: string) => {\n\n console.warn(\n `[better-editor] ${kind} \"${slug}\" has no top-level field named \"${blocksField}\" — the sidebar Blocks tab will be empty. Set \\`blocksField\\` to the actual blocks field name.`,\n )\n}\n\n/**\n * Payload CMS plugin factory for the Better Editor overlay. Adds an\n * \"Open Better Editor\" toggle to the configured collections / globals\n * and registers a `BetterEditorSettings` global with editor-wide options.\n *\n * @example\n * import { betterEditor } from 'payload-better-editor'\n *\n * export default buildConfig({\n * plugins: [betterEditor({ collections: ['pages'] })],\n * // ...\n * })\n *\n * @see {@link BetterEditorConfig} for all options.\n */\nexport const betterEditor =\n (pluginOptions?: BetterEditorConfig): BetterEditorPlugin =>\n (config: Config): Config => {\n if (pluginOptions?.disabled) return config\n\n const defaultBlocksField = pluginOptions?.blocksField || DEFAULT_BLOCKS_FIELD\n const collectionMap = normalizeEntities(pluginOptions?.collections, defaultBlocksField)\n const globalMap = normalizeEntities(pluginOptions?.globals, defaultBlocksField)\n const toggleClientProps = (blocksField: string): ToggleClientProps => ({\n blocksField,\n adminPortalSelector: pluginOptions?.adminPortalSelector,\n storageNamespace: pluginOptions?.storageNamespace,\n })\n\n const showBanner = pluginOptions?.showSettingsBanner !== false\n const settingsGlobal: GlobalConfig = showBanner\n ? betterEditorSettingsGlobal\n : {\n ...betterEditorSettingsGlobal,\n fields: betterEditorSettingsGlobal.fields.filter(\n (f) => !('name' in f && f.name === BETTER_EDITOR_SETTINGS_BANNER_FIELD),\n ),\n }\n\n const existingGlobals = config.globals ?? []\n const hasSettingsGlobal = existingGlobals.some((g) => g.slug === settingsGlobal.slug)\n config.globals = hasSettingsGlobal\n ? existingGlobals\n : [...existingGlobals, settingsGlobal]\n\n if (collectionMap.size === 0 && globalMap.size === 0) {\n if (isDev) {\n\n console.warn(\n '[better-editor] plugin loaded with empty `collections` and `globals` — toggle button will not appear anywhere. Pass `collections: [\"pages\"]` (or similar) to BetterEditorConfig.',\n )\n }\n return config\n }\n\n if (collectionMap.size > 0 && config.collections) {\n config.collections = config.collections.map((collection) => {\n const blocksField = collectionMap.get(collection.slug)\n if (blocksField === undefined) return collection\n if (isDev && !hasBlocksField(collection.fields, blocksField)) {\n warnMissingBlocksField('collection', collection.slug, blocksField)\n }\n return withToggleInjected(collection, 'edit', toggleClientProps(blocksField))\n })\n }\n\n if (globalMap.size > 0) {\n config.globals = (config.globals ?? []).map((global) => {\n const blocksField = globalMap.get(global.slug)\n if (blocksField === undefined) return global\n if (isDev && !hasBlocksField(global.fields, blocksField)) {\n warnMissingBlocksField('global', global.slug, blocksField)\n }\n return withToggleInjected(global, 'elements', toggleClientProps(blocksField))\n })\n }\n\n return config\n }\n"],"names":["BETTER_EDITOR_SETTINGS_BANNER_FIELD","betterEditorSettingsGlobal","normalizeEntities","BETTER_EDITOR_SETTINGS_SLUG","VERSION","DEFAULT_BLOCKS_FIELD","TOGGLE_COMPONENT_PATH","isDev","process","env","NODE_ENV","hasBlocksField","fields","name","Array","isArray","some","field","type","tabs","tab","withToggleInjected","entity","slot","clientProps","admin","components","target","before","beforeDocumentControls","path","warnMissingBlocksField","kind","slug","blocksField","console","warn","betterEditor","pluginOptions","config","disabled","defaultBlocksField","collectionMap","collections","globalMap","globals","toggleClientProps","adminPortalSelector","storageNamespace","showBanner","showSettingsBanner","settingsGlobal","filter","f","existingGlobals","hasSettingsGlobal","g","size","map","collection","get","undefined","global"],"mappings":"AAEA,SAASA,mCAAmC,EAAEC,0BAA0B,QAAQ,WAAU;AAC1F,SAASC,iBAAiB,QAAQ,sBAAqB;AAKvD,SAASC,2BAA2B,QAAQ,WAAU;AAKtD,SAASC,OAAO,QAAQ,YAAW;AAEnC,MAAMC,uBAAuB;AAC7B,MAAMC,wBAAwB;AAC9B,MAAMC,QAAQC,QAAQC,GAAG,CAACC,QAAQ,KAAK;AAEvC;;;;;CAKC,GACD,MAAMC,iBAAiB,CAACC,QAA6BC;IACnD,IAAI,CAACC,MAAMC,OAAO,CAACH,SAAS,OAAO;IACnC,OAAOA,OAAOI,IAAI,CAAC,CAACC;QAClB,IAAI,UAAUA,SAASA,MAAMJ,IAAI,KAAKA,MAAM,OAAO;QACnD,IAAII,MAAMC,IAAI,KAAK,SAASD,MAAMC,IAAI,KAAK,eAAe;YACxD,OAAOP,eAAeM,MAAML,MAAM,EAAEC;QACtC;QACA,IAAII,MAAMC,IAAI,KAAK,QAAQ;YACzB,OAAOD,MAAME,IAAI,CAACH,IAAI,CAAC,CAACI,MACtB,UAAUA,OAAOA,IAAIP,IAAI,GAAG,QAAQF,eAAeS,IAAIR,MAAM,EAAEC;QAEnE;QACA,OAAO;IACT;AACF;AAUA,MAAMQ,qBAAqB,CACzBC,QACAC,MACAC;IAEA,MAAMC,QAAQ;QAAE,GAAIH,OAAOG,KAAK,IAAI,CAAC,CAAC;IAAE;IACxC,MAAMC,aAAa;QAAE,GAAID,MAAMC,UAAU,IAAI,CAAC,CAAC;IAAE;IACjD,MAAMC,SAAS;QAAE,GAAI,AAACD,UAAU,CAACH,KAAK,IAAgC,CAAC,CAAC;IAAE;IAC1E,MAAMK,SAAS,AAACD,OAAOE,sBAAsB,IAAkB,EAAE;IACjE,OAAO;QACL,GAAGP,MAAM;QACTG,OAAO;YACL,GAAGA,KAAK;YACRC,YAAY;gBACV,GAAGA,UAAU;gBACb,CAACH,KAAK,EAAE;oBACN,GAAGI,MAAM;oBACTE,wBAAwB;2BACnBD;wBACH;4BAAEE,MAAMxB;4BAAuBkB;wBAAY;qBAC5C;gBACH;YACF;QACF;IACF;AACF;AAEA,MAAMO,yBAAyB,CAACC,MAA+BC,MAAcC;IAE3EC,QAAQC,IAAI,CACV,CAAC,gBAAgB,EAAEJ,KAAK,EAAE,EAAEC,KAAK,gCAAgC,EAAEC,YAAY,8FAA8F,CAAC;AAElL;AAEA;;;;;;;;;;;;;;CAcC,GACD,OAAO,MAAMG,eACX,CAACC,gBACD,CAACC;QACC,IAAID,eAAeE,UAAU,OAAOD;QAEpC,MAAME,qBAAqBH,eAAeJ,eAAe7B;QACzD,MAAMqC,gBAAgBxC,kBAAkBoC,eAAeK,aAAaF;QACpE,MAAMG,YAAY1C,kBAAkBoC,eAAeO,SAASJ;QAC5D,MAAMK,oBAAoB,CAACZ,cAA4C,CAAA;gBACrEA;gBACAa,qBAAqBT,eAAeS;gBACpCC,kBAAkBV,eAAeU;YACnC,CAAA;QAEA,MAAMC,aAAaX,eAAeY,uBAAuB;QACzD,MAAMC,iBAA+BF,aACjChD,6BACA;YACE,GAAGA,0BAA0B;YAC7BW,QAAQX,2BAA2BW,MAAM,CAACwC,MAAM,CAC9C,CAACC,IAAM,CAAE,CAAA,UAAUA,KAAKA,EAAExC,IAAI,KAAKb,mCAAkC;QAEzE;QAEJ,MAAMsD,kBAAkBf,OAAOM,OAAO,IAAI,EAAE;QAC5C,MAAMU,oBAAoBD,gBAAgBtC,IAAI,CAAC,CAACwC,IAAMA,EAAEvB,IAAI,KAAKkB,eAAelB,IAAI;QACpFM,OAAOM,OAAO,GAAGU,oBACbD,kBACA;eAAIA;YAAiBH;SAAe;QAExC,IAAIT,cAAce,IAAI,KAAK,KAAKb,UAAUa,IAAI,KAAK,GAAG;YACpD,IAAIlD,OAAO;gBAET4B,QAAQC,IAAI,CACV;YAEJ;YACA,OAAOG;QACT;QAEA,IAAIG,cAAce,IAAI,GAAG,KAAKlB,OAAOI,WAAW,EAAE;YAChDJ,OAAOI,WAAW,GAAGJ,OAAOI,WAAW,CAACe,GAAG,CAAC,CAACC;gBAC3C,MAAMzB,cAAcQ,cAAckB,GAAG,CAACD,WAAW1B,IAAI;gBACrD,IAAIC,gBAAgB2B,WAAW,OAAOF;gBACtC,IAAIpD,SAAS,CAACI,eAAegD,WAAW/C,MAAM,EAAEsB,cAAc;oBAC5DH,uBAAuB,cAAc4B,WAAW1B,IAAI,EAAEC;gBACxD;gBACA,OAAOb,mBAAmBsC,YAAY,QAAQb,kBAAkBZ;YAClE;QACF;QAEA,IAAIU,UAAUa,IAAI,GAAG,GAAG;YACtBlB,OAAOM,OAAO,GAAG,AAACN,CAAAA,OAAOM,OAAO,IAAI,EAAE,AAAD,EAAGa,GAAG,CAAC,CAACI;gBAC3C,MAAM5B,cAAcU,UAAUgB,GAAG,CAACE,OAAO7B,IAAI;gBAC7C,IAAIC,gBAAgB2B,WAAW,OAAOC;gBACtC,IAAIvD,SAAS,CAACI,eAAemD,OAAOlD,MAAM,EAAEsB,cAAc;oBACxDH,uBAAuB,UAAU+B,OAAO7B,IAAI,EAAEC;gBAChD;gBACA,OAAOb,mBAAmByC,QAAQ,YAAYhB,kBAAkBZ;YAClE;QACF;QAEA,OAAOK;IACT,EAAC"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/** Normalizes the `collections`/`globals` plugin option (a slug list OR a
|
|
2
|
+
* slug → options record) into a slug → blocksField map, applying the default
|
|
3
|
+
* where unset. */
|
|
4
|
+
export declare const normalizeEntities: (option: string[] | Partial<Record<string, {
|
|
5
|
+
blocksField?: string;
|
|
6
|
+
}>> | undefined, defaultBlocksField: string) => Map<string, string>;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/** Normalizes the `collections`/`globals` plugin option (a slug list OR a
|
|
2
|
+
* slug → options record) into a slug → blocksField map, applying the default
|
|
3
|
+
* where unset. */ export const normalizeEntities = (option, defaultBlocksField)=>{
|
|
4
|
+
const map = new Map();
|
|
5
|
+
if (Array.isArray(option)) {
|
|
6
|
+
for (const slug of option)map.set(slug, defaultBlocksField);
|
|
7
|
+
} else if (option) {
|
|
8
|
+
for (const [slug, cfg] of Object.entries(option)){
|
|
9
|
+
map.set(slug, cfg?.blocksField || defaultBlocksField);
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
return map;
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
//# sourceMappingURL=entities.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/internal/entities.ts"],"sourcesContent":["/** Normalizes the `collections`/`globals` plugin option (a slug list OR a\n * slug → options record) into a slug → blocksField map, applying the default\n * where unset. */\nexport const normalizeEntities = (\n option: string[] | Partial<Record<string, { blocksField?: string }>> | undefined,\n defaultBlocksField: string,\n): Map<string, string> => {\n const map = new Map<string, string>()\n if (Array.isArray(option)) {\n for (const slug of option) map.set(slug, defaultBlocksField)\n } else if (option) {\n for (const [slug, cfg] of Object.entries(option)) {\n map.set(slug, cfg?.blocksField || defaultBlocksField)\n }\n }\n return map\n}\n"],"names":["normalizeEntities","option","defaultBlocksField","map","Map","Array","isArray","slug","set","cfg","Object","entries","blocksField"],"mappings":"AAAA;;gBAEgB,GAChB,OAAO,MAAMA,oBAAoB,CAC/BC,QACAC;IAEA,MAAMC,MAAM,IAAIC;IAChB,IAAIC,MAAMC,OAAO,CAACL,SAAS;QACzB,KAAK,MAAMM,QAAQN,OAAQE,IAAIK,GAAG,CAACD,MAAML;IAC3C,OAAO,IAAID,QAAQ;QACjB,KAAK,MAAM,CAACM,MAAME,IAAI,IAAIC,OAAOC,OAAO,CAACV,QAAS;YAChDE,IAAIK,GAAG,CAACD,MAAME,KAAKG,eAAeV;QACpC;IACF;IACA,OAAOC;AACT,EAAC"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
export declare const HOVER_STYLE_ID = "better-editor-hover-style";
|
|
2
2
|
export declare const TOOLBAR_ID = "better-editor-block-toolbar";
|
|
3
3
|
export declare const INTERACT_BODY_ATTR = "data-bee-interact";
|
|
4
|
-
export declare const HOVER_CSS = "\n body:not([data-bee-interact]) [data-better-editor-id] { cursor: pointer; }\n body:not([data-bee-interact]) [data-better-editor-id]:hover,\n body:not([data-bee-interact]) [data-better-editor-id].better-editor-active {\n outline: var(--bee-outline-width) solid var(--bee-top);\n outline-offset: calc(-1 * var(--bee-outline-width) - 1px);\n
|
|
4
|
+
export declare const HOVER_CSS = "\n body:not([data-bee-interact]) [data-better-editor-id] { cursor: pointer; }\n body:not([data-bee-interact]) [data-better-editor-id]:hover,\n body:not([data-bee-interact]) [data-better-editor-id].better-editor-active {\n outline: var(--bee-outline-width) solid var(--bee-top);\n outline-offset: calc(-1 * var(--bee-outline-width) - 1px);\n box-shadow: inset 0 0 0 100vmax color-mix(in srgb, var(--bee-top) 10%, transparent);\n }\n body:not([data-bee-interact]) [data-better-editor-id] [data-better-editor-id]:hover,\n body:not([data-bee-interact]) [data-better-editor-id] [data-better-editor-id].better-editor-active {\n outline-color: var(--bee-nested);\n box-shadow: inset 0 0 0 100vmax color-mix(in srgb, var(--bee-nested) 10%, transparent);\n }\n body[data-bee-interact] #better-editor-block-toolbar { display: none; }\n\n #better-editor-block-toolbar {\n position: absolute;\n z-index: var(--better-editor-z-toolbar, 2147483647);\n display: none;\n gap: 2px;\n padding: 3px;\n border-radius: 4px;\n background: var(--bee-top);\n color: #fff;\n box-shadow: 0 4px 12px rgba(0, 0, 0, 0.18);\n font-family: system-ui, sans-serif;\n }\n #better-editor-block-toolbar[data-nested=\"1\"] { background: var(--bee-nested); }\n #better-editor-block-toolbar.is-visible { display: inline-flex; }\n #better-editor-block-toolbar button {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 26px;\n height: 26px;\n padding: 0;\n border: 0;\n border-radius: 3px;\n background: transparent;\n color: inherit;\n cursor: pointer;\n }\n #better-editor-block-toolbar button:hover { background: rgba(255, 255, 255, 0.18); }\n #better-editor-block-toolbar button[data-action=\"delete\"]:hover { background: rgba(0, 0, 0, 0.25); }\n";
|
|
5
5
|
export type HoverVars = {
|
|
6
6
|
topColor: string;
|
|
7
7
|
nestedColor: string;
|
|
@@ -14,12 +14,12 @@ export const HOVER_CSS = `
|
|
|
14
14
|
body:not([${INTERACT_BODY_ATTR}]) [${BLOCK_ID_ATTR}].${ACTIVE_CLASS} {
|
|
15
15
|
outline: var(${VAR_OUTLINE_WIDTH}) solid var(${VAR_TOP});
|
|
16
16
|
outline-offset: calc(-1 * var(${VAR_OUTLINE_WIDTH}) - 1px);
|
|
17
|
-
|
|
17
|
+
box-shadow: inset 0 0 0 100vmax color-mix(in srgb, var(${VAR_TOP}) 10%, transparent);
|
|
18
18
|
}
|
|
19
19
|
body:not([${INTERACT_BODY_ATTR}]) [${BLOCK_ID_ATTR}] [${BLOCK_ID_ATTR}]:hover,
|
|
20
20
|
body:not([${INTERACT_BODY_ATTR}]) [${BLOCK_ID_ATTR}] [${BLOCK_ID_ATTR}].${ACTIVE_CLASS} {
|
|
21
21
|
outline-color: var(${VAR_NESTED});
|
|
22
|
-
|
|
22
|
+
box-shadow: inset 0 0 0 100vmax color-mix(in srgb, var(${VAR_NESTED}) 10%, transparent);
|
|
23
23
|
}
|
|
24
24
|
body[${INTERACT_BODY_ATTR}] #${TOOLBAR_ID} { display: none; }
|
|
25
25
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/preview/hover-css.ts"],"sourcesContent":["import { ACTIVE_CLASS, BLOCK_ID_ATTR } from '../internal/dom'\n\nexport const HOVER_STYLE_ID = 'better-editor-hover-style'\nexport const TOOLBAR_ID = 'better-editor-block-toolbar'\n// Set on doc.body to disable hover affordances + click-to-focus while\n// the user wants to interact with the consumer page (forms, accordions,\n// links). All hover/active rules below are gated on its absence.\nexport const INTERACT_BODY_ATTR = 'data-bee-interact'\n\nconst VAR_TOP = '--bee-top'\nconst VAR_NESTED = '--bee-nested'\nconst VAR_OUTLINE_WIDTH = '--bee-outline-width'\n\nexport const HOVER_CSS = `\n body:not([${INTERACT_BODY_ATTR}]) [${BLOCK_ID_ATTR}] { cursor: pointer; }\n body:not([${INTERACT_BODY_ATTR}]) [${BLOCK_ID_ATTR}]:hover,\n body:not([${INTERACT_BODY_ATTR}]) [${BLOCK_ID_ATTR}].${ACTIVE_CLASS} {\n outline: var(${VAR_OUTLINE_WIDTH}) solid var(${VAR_TOP});\n outline-offset: calc(-1 * var(${VAR_OUTLINE_WIDTH}) - 1px);\n
|
|
1
|
+
{"version":3,"sources":["../../src/preview/hover-css.ts"],"sourcesContent":["import { ACTIVE_CLASS, BLOCK_ID_ATTR } from '../internal/dom'\n\nexport const HOVER_STYLE_ID = 'better-editor-hover-style'\nexport const TOOLBAR_ID = 'better-editor-block-toolbar'\n// Set on doc.body to disable hover affordances + click-to-focus while\n// the user wants to interact with the consumer page (forms, accordions,\n// links). All hover/active rules below are gated on its absence.\nexport const INTERACT_BODY_ATTR = 'data-bee-interact'\n\nconst VAR_TOP = '--bee-top'\nconst VAR_NESTED = '--bee-nested'\nconst VAR_OUTLINE_WIDTH = '--bee-outline-width'\n\nexport const HOVER_CSS = `\n body:not([${INTERACT_BODY_ATTR}]) [${BLOCK_ID_ATTR}] { cursor: pointer; }\n body:not([${INTERACT_BODY_ATTR}]) [${BLOCK_ID_ATTR}]:hover,\n body:not([${INTERACT_BODY_ATTR}]) [${BLOCK_ID_ATTR}].${ACTIVE_CLASS} {\n outline: var(${VAR_OUTLINE_WIDTH}) solid var(${VAR_TOP});\n outline-offset: calc(-1 * var(${VAR_OUTLINE_WIDTH}) - 1px);\n box-shadow: inset 0 0 0 100vmax color-mix(in srgb, var(${VAR_TOP}) 10%, transparent);\n }\n body:not([${INTERACT_BODY_ATTR}]) [${BLOCK_ID_ATTR}] [${BLOCK_ID_ATTR}]:hover,\n body:not([${INTERACT_BODY_ATTR}]) [${BLOCK_ID_ATTR}] [${BLOCK_ID_ATTR}].${ACTIVE_CLASS} {\n outline-color: var(${VAR_NESTED});\n box-shadow: inset 0 0 0 100vmax color-mix(in srgb, var(${VAR_NESTED}) 10%, transparent);\n }\n body[${INTERACT_BODY_ATTR}] #${TOOLBAR_ID} { display: none; }\n\n #${TOOLBAR_ID} {\n position: absolute;\n z-index: var(--better-editor-z-toolbar, 2147483647);\n display: none;\n gap: 2px;\n padding: 3px;\n border-radius: 4px;\n background: var(${VAR_TOP});\n color: #fff;\n box-shadow: 0 4px 12px rgba(0, 0, 0, 0.18);\n font-family: system-ui, sans-serif;\n }\n #${TOOLBAR_ID}[data-nested=\"1\"] { background: var(${VAR_NESTED}); }\n #${TOOLBAR_ID}.is-visible { display: inline-flex; }\n #${TOOLBAR_ID} button {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 26px;\n height: 26px;\n padding: 0;\n border: 0;\n border-radius: 3px;\n background: transparent;\n color: inherit;\n cursor: pointer;\n }\n #${TOOLBAR_ID} button:hover { background: rgba(255, 255, 255, 0.18); }\n #${TOOLBAR_ID} button[data-action=\"delete\"]:hover { background: rgba(0, 0, 0, 0.25); }\n`\n\nexport type HoverVars = {\n topColor: string\n nestedColor: string\n outlineWidth: number\n}\n\n// Restrictive on purpose: these values are written verbatim into a CSS\n// custom property on the preview iframe, so anything that survives the\n// regex still lands inside `outline:` / `background-color:`. The regex\n// rejects newlines and unmatched parens so an injected value can't escape\n// into a separate declaration.\nconst COLOR_RE = /^(?:#[0-9a-fA-F]{3,8}|rgba?\\([^()\\n\\r]*\\))$/i\n\nconst isValidColor = (v: unknown): v is string =>\n typeof v === 'string' && COLOR_RE.test(v.trim())\n\nconst isValidOutline = (v: unknown): v is number =>\n typeof v === 'number' && Number.isFinite(v) && v >= 0 && v <= 50\n\nconst warnInvalid = (kind: string, value: unknown): void => {\n if (process.env.NODE_ENV !== 'production') {\n console.warn(`[better-editor] ignoring invalid ${kind}:`, value)\n }\n}\n\nexport const setHoverVars = (doc: Document, vars: HoverVars): void => {\n const root = doc.documentElement\n if (isValidColor(vars.topColor)) {\n root.style.setProperty(VAR_TOP, vars.topColor)\n } else {\n warnInvalid('topColor', vars.topColor)\n }\n if (isValidColor(vars.nestedColor)) {\n root.style.setProperty(VAR_NESTED, vars.nestedColor)\n } else {\n warnInvalid('nestedColor', vars.nestedColor)\n }\n if (isValidOutline(vars.outlineWidth)) {\n root.style.setProperty(VAR_OUTLINE_WIDTH, `${vars.outlineWidth}px`)\n } else {\n warnInvalid('outlineWidth', vars.outlineWidth)\n }\n}\n\nexport const clearHoverVars = (doc: Document): void => {\n const root = doc.documentElement\n root.style.removeProperty(VAR_TOP)\n root.style.removeProperty(VAR_NESTED)\n root.style.removeProperty(VAR_OUTLINE_WIDTH)\n}\n"],"names":["ACTIVE_CLASS","BLOCK_ID_ATTR","HOVER_STYLE_ID","TOOLBAR_ID","INTERACT_BODY_ATTR","VAR_TOP","VAR_NESTED","VAR_OUTLINE_WIDTH","HOVER_CSS","COLOR_RE","isValidColor","v","test","trim","isValidOutline","Number","isFinite","warnInvalid","kind","value","process","env","NODE_ENV","console","warn","setHoverVars","doc","vars","root","documentElement","topColor","style","setProperty","nestedColor","outlineWidth","clearHoverVars","removeProperty"],"mappings":"AAAA,SAASA,YAAY,EAAEC,aAAa,QAAQ,kBAAiB;AAE7D,OAAO,MAAMC,iBAAiB,4BAA2B;AACzD,OAAO,MAAMC,aAAa,8BAA6B;AACvD,sEAAsE;AACtE,wEAAwE;AACxE,iEAAiE;AACjE,OAAO,MAAMC,qBAAqB,oBAAmB;AAErD,MAAMC,UAAU;AAChB,MAAMC,aAAa;AACnB,MAAMC,oBAAoB;AAE1B,OAAO,MAAMC,YAAY,CAAC;YACd,EAAEJ,mBAAmB,IAAI,EAAEH,cAAc;YACzC,EAAEG,mBAAmB,IAAI,EAAEH,cAAc;YACzC,EAAEG,mBAAmB,IAAI,EAAEH,cAAc,EAAE,EAAED,aAAa;iBACrD,EAAEO,kBAAkB,YAAY,EAAEF,QAAQ;kCACzB,EAAEE,kBAAkB;2DACK,EAAEF,QAAQ;;YAEzD,EAAED,mBAAmB,IAAI,EAAEH,cAAc,GAAG,EAAEA,cAAc;YAC5D,EAAEG,mBAAmB,IAAI,EAAEH,cAAc,GAAG,EAAEA,cAAc,EAAE,EAAED,aAAa;uBAClE,EAAEM,WAAW;2DACuB,EAAEA,WAAW;;OAEjE,EAAEF,mBAAmB,GAAG,EAAED,WAAW;;GAEzC,EAAEA,WAAW;;;;;;;oBAOI,EAAEE,QAAQ;;;;;GAK3B,EAAEF,WAAW,oCAAoC,EAAEG,WAAW;GAC9D,EAAEH,WAAW;GACb,EAAEA,WAAW;;;;;;;;;;;;;GAab,EAAEA,WAAW;GACb,EAAEA,WAAW;AAChB,CAAC,CAAA;AAQD,uEAAuE;AACvE,uEAAuE;AACvE,uEAAuE;AACvE,0EAA0E;AAC1E,+BAA+B;AAC/B,MAAMM,WAAW;AAEjB,MAAMC,eAAe,CAACC,IACpB,OAAOA,MAAM,YAAYF,SAASG,IAAI,CAACD,EAAEE,IAAI;AAE/C,MAAMC,iBAAiB,CAACH,IACtB,OAAOA,MAAM,YAAYI,OAAOC,QAAQ,CAACL,MAAMA,KAAK,KAAKA,KAAK;AAEhE,MAAMM,cAAc,CAACC,MAAcC;IACjC,IAAIC,QAAQC,GAAG,CAACC,QAAQ,KAAK,cAAc;QACzCC,QAAQC,IAAI,CAAC,CAAC,iCAAiC,EAAEN,KAAK,CAAC,CAAC,EAAEC;IAC5D;AACF;AAEA,OAAO,MAAMM,eAAe,CAACC,KAAeC;IAC1C,MAAMC,OAAOF,IAAIG,eAAe;IAChC,IAAInB,aAAaiB,KAAKG,QAAQ,GAAG;QAC/BF,KAAKG,KAAK,CAACC,WAAW,CAAC3B,SAASsB,KAAKG,QAAQ;IAC/C,OAAO;QACLb,YAAY,YAAYU,KAAKG,QAAQ;IACvC;IACA,IAAIpB,aAAaiB,KAAKM,WAAW,GAAG;QAClCL,KAAKG,KAAK,CAACC,WAAW,CAAC1B,YAAYqB,KAAKM,WAAW;IACrD,OAAO;QACLhB,YAAY,eAAeU,KAAKM,WAAW;IAC7C;IACA,IAAInB,eAAea,KAAKO,YAAY,GAAG;QACrCN,KAAKG,KAAK,CAACC,WAAW,CAACzB,mBAAmB,GAAGoB,KAAKO,YAAY,CAAC,EAAE,CAAC;IACpE,OAAO;QACLjB,YAAY,gBAAgBU,KAAKO,YAAY;IAC/C;AACF,EAAC;AAED,OAAO,MAAMC,iBAAiB,CAACT;IAC7B,MAAME,OAAOF,IAAIG,eAAe;IAChCD,KAAKG,KAAK,CAACK,cAAc,CAAC/B;IAC1BuB,KAAKG,KAAK,CAACK,cAAc,CAAC9B;IAC1BsB,KAAKG,KAAK,CAACK,cAAc,CAAC7B;AAC5B,EAAC"}
|
package/dist/types.d.ts
CHANGED
|
@@ -1,22 +1,30 @@
|
|
|
1
|
+
import type { CollectionSlug, GlobalSlug } from 'payload';
|
|
2
|
+
/** Per-entity overrides for a collection/global. */
|
|
3
|
+
export type BetterEditorEntityOptions = {
|
|
4
|
+
/** Blocks-field name for this entity; falls back to the top-level `blocksField`. */
|
|
5
|
+
blocksField?: string;
|
|
6
|
+
};
|
|
1
7
|
export type BetterEditorConfig = {
|
|
2
8
|
/** Skip plugin installation entirely (e.g. behind a feature flag). */
|
|
3
9
|
disabled?: boolean;
|
|
4
10
|
/**
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
* `
|
|
11
|
+
* Collections that get the "Open Better Editor" toggle. Either a list of
|
|
12
|
+
* slugs, or a slug → options record for per-collection settings (e.g. a
|
|
13
|
+
* different `blocksField`). Each collection needs `admin.preview` configured
|
|
14
|
+
* for the preview to render and the toggle to appear.
|
|
8
15
|
*/
|
|
9
|
-
collections?: string[]
|
|
16
|
+
collections?: string[] | Partial<Record<CollectionSlug, BetterEditorEntityOptions>>;
|
|
10
17
|
/**
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
18
|
+
* Globals that get the "Open Better Editor" toggle — a list of slugs or a
|
|
19
|
+
* slug → options record. Each global needs `admin.preview` configured for the
|
|
20
|
+
* toggle to appear.
|
|
14
21
|
*/
|
|
15
|
-
globals?: string[]
|
|
22
|
+
globals?: string[] | Partial<Record<GlobalSlug, BetterEditorEntityOptions>>;
|
|
16
23
|
/**
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
24
|
+
* Default name of the `blocks` field the sidebar targets. Top-level only;
|
|
25
|
+
* nested paths (e.g. `content.layout`) are not supported. Per-collection
|
|
26
|
+
* overrides via the `collections`/`globals` record take precedence.
|
|
27
|
+
* Defaults to `'layout'`.
|
|
20
28
|
*/
|
|
21
29
|
blocksField?: string;
|
|
22
30
|
/**
|
package/dist/types.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/types.ts"],"sourcesContent":["
|
|
1
|
+
{"version":3,"sources":["../src/types.ts"],"sourcesContent":["import type { CollectionSlug, GlobalSlug } from 'payload'\n\n/** Per-entity overrides for a collection/global. */\nexport type BetterEditorEntityOptions = {\n /** Blocks-field name for this entity; falls back to the top-level `blocksField`. */\n blocksField?: string\n}\n\nexport type BetterEditorConfig = {\n /** Skip plugin installation entirely (e.g. behind a feature flag). */\n disabled?: boolean\n /**\n * Collections that get the \"Open Better Editor\" toggle. Either a list of\n * slugs, or a slug → options record for per-collection settings (e.g. a\n * different `blocksField`). Each collection needs `admin.preview` configured\n * for the preview to render and the toggle to appear.\n */\n collections?: string[] | Partial<Record<CollectionSlug, BetterEditorEntityOptions>>\n /**\n * Globals that get the \"Open Better Editor\" toggle — a list of slugs or a\n * slug → options record. Each global needs `admin.preview` configured for the\n * toggle to appear.\n */\n globals?: string[] | Partial<Record<GlobalSlug, BetterEditorEntityOptions>>\n /**\n * Default name of the `blocks` field the sidebar targets. Top-level only;\n * nested paths (e.g. `content.layout`) are not supported. Per-collection\n * overrides via the `collections`/`globals` record take precedence.\n * Defaults to `'layout'`.\n */\n blocksField?: string\n /**\n * CSS selector for the Payload admin element the overlay portals into.\n * Override only if the default selector breaks against a future Payload\n * version. The plugin falls back to `<main>` and finally `<body>`.\n */\n adminPortalSelector?: string\n /**\n * Prefix for all `localStorage` keys the editor writes (sidebar width,\n * responsive viewport width, …). Set this if multiple Better Editor\n * instances share the same origin and would otherwise collide.\n * Defaults to `'better-editor'`.\n */\n storageNamespace?: string\n /**\n * Show the plugin info banner (version, GitHub links, \"Report a bug\")\n * at the top of the `BetterEditorSettings` global. Set to `false` to\n * hide it for end users. Defaults to `true`.\n */\n showSettingsBanner?: boolean\n}\n"],"names":[],"mappings":"AAQA,WA0CC"}
|
package/dist/version.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const VERSION = "1.1
|
|
1
|
+
export declare const VERSION = "1.2.1";
|
package/dist/version.js
CHANGED
package/dist/version.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/version.ts"],"sourcesContent":["// Package version, re-exported as VERSION; read by the settings banner without\n// pulling the server entry into the client bundle.\nexport const VERSION = '1.1
|
|
1
|
+
{"version":3,"sources":["../src/version.ts"],"sourcesContent":["// Package version, re-exported as VERSION; read by the settings banner without\n// pulling the server entry into the client bundle.\nexport const VERSION = '1.2.1'\n"],"names":["VERSION"],"mappings":"AAAA,+EAA+E;AAC/E,mDAAmD;AACnD,OAAO,MAAMA,UAAU,QAAO"}
|
package/package.json
CHANGED