payload-better-editor 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +57 -0
- package/dist/admin/ErrorBoundary.d.ts +17 -0
- package/dist/admin/ErrorBoundary.js +62 -0
- package/dist/admin/ErrorBoundary.js.map +1 -0
- package/dist/admin/LiveEditorOverlay.d.ts +12 -0
- package/dist/admin/LiveEditorOverlay.js +160 -0
- package/dist/admin/LiveEditorOverlay.js.map +1 -0
- package/dist/admin/LiveEditorToggle.d.ts +7 -0
- package/dist/admin/LiveEditorToggle.js +84 -0
- package/dist/admin/LiveEditorToggle.js.map +1 -0
- package/dist/admin/PreviewFrame.d.ts +22 -0
- package/dist/admin/PreviewFrame.js +137 -0
- package/dist/admin/PreviewFrame.js.map +1 -0
- package/dist/admin/PreviewToolbar.d.ts +16 -0
- package/dist/admin/PreviewToolbar.js +90 -0
- package/dist/admin/PreviewToolbar.js.map +1 -0
- package/dist/admin/SettingsBanner.d.ts +3 -0
- package/dist/admin/SettingsBanner.js +105 -0
- package/dist/admin/SettingsBanner.js.map +1 -0
- package/dist/admin/ViewportToggle.d.ts +7 -0
- package/dist/admin/ViewportToggle.js +79 -0
- package/dist/admin/ViewportToggle.js.map +1 -0
- package/dist/admin/blocks/AddBlockDrawer.d.ts +9 -0
- package/dist/admin/blocks/AddBlockDrawer.js +16 -0
- package/dist/admin/blocks/AddBlockDrawer.js.map +1 -0
- package/dist/admin/blocks/BlockActionsToolbar.d.ts +15 -0
- package/dist/admin/blocks/BlockActionsToolbar.js +102 -0
- package/dist/admin/blocks/BlockActionsToolbar.js.map +1 -0
- package/dist/admin/blocks/BlockEmptyState.d.ts +6 -0
- package/dist/admin/blocks/BlockEmptyState.js +26 -0
- package/dist/admin/blocks/BlockEmptyState.js.map +1 -0
- package/dist/admin/blocks/BlockHeader.d.ts +7 -0
- package/dist/admin/blocks/BlockHeader.js +32 -0
- package/dist/admin/blocks/BlockHeader.js.map +1 -0
- package/dist/admin/blocks/schema.d.ts +19 -0
- package/dist/admin/blocks/schema.js +80 -0
- package/dist/admin/blocks/schema.js.map +1 -0
- package/dist/admin/blocks/useBlockActions.d.ts +24 -0
- package/dist/admin/blocks/useBlockActions.js +100 -0
- package/dist/admin/blocks/useBlockActions.js.map +1 -0
- package/dist/admin/icons.d.ts +24 -0
- package/dist/admin/icons.js +36 -0
- package/dist/admin/icons.js.map +1 -0
- package/dist/admin/sidebar/BlockSettingsTab.d.ts +10 -0
- package/dist/admin/sidebar/BlockSettingsTab.js +153 -0
- package/dist/admin/sidebar/BlockSettingsTab.js.map +1 -0
- package/dist/admin/sidebar/DocumentFieldsTab.d.ts +8 -0
- package/dist/admin/sidebar/DocumentFieldsTab.js +38 -0
- package/dist/admin/sidebar/DocumentFieldsTab.js.map +1 -0
- package/dist/admin/sidebar/DocumentMetaTab.d.ts +2 -0
- package/dist/admin/sidebar/DocumentMetaTab.js +11 -0
- package/dist/admin/sidebar/DocumentMetaTab.js.map +1 -0
- package/dist/admin/sidebar/DocumentSettingsTab.d.ts +2 -0
- package/dist/admin/sidebar/DocumentSettingsTab.js +48 -0
- package/dist/admin/sidebar/DocumentSettingsTab.js.map +1 -0
- package/dist/admin/sidebar/Sidebar.d.ts +10 -0
- package/dist/admin/sidebar/Sidebar.js +92 -0
- package/dist/admin/sidebar/Sidebar.js.map +1 -0
- package/dist/client.d.ts +34 -0
- package/dist/client.js +30 -0
- package/dist/client.js.map +1 -0
- package/dist/global.d.ts +4 -0
- package/dist/global.js +200 -0
- package/dist/global.js.map +1 -0
- package/dist/hooks/useAddBlockDrawer.d.ts +14 -0
- package/dist/hooks/useAddBlockDrawer.js +26 -0
- package/dist/hooks/useAddBlockDrawer.js.map +1 -0
- package/dist/hooks/useBlockActionMessages.d.ts +8 -0
- package/dist/hooks/useBlockActionMessages.js +107 -0
- package/dist/hooks/useBlockActionMessages.js.map +1 -0
- package/dist/hooks/useDocConfig.d.ts +6 -0
- package/dist/hooks/useDocConfig.js +18 -0
- package/dist/hooks/useDocConfig.js.map +1 -0
- package/dist/hooks/useFocusTrap.d.ts +2 -0
- package/dist/hooks/useFocusTrap.js +84 -0
- package/dist/hooks/useFocusTrap.js.map +1 -0
- package/dist/hooks/useFullscreenOverlay.d.ts +2 -0
- package/dist/hooks/useFullscreenOverlay.js +30 -0
- package/dist/hooks/useFullscreenOverlay.js.map +1 -0
- package/dist/hooks/useIframeResizeObserver.d.ts +2 -0
- package/dist/hooks/useIframeResizeObserver.js +20 -0
- package/dist/hooks/useIframeResizeObserver.js.map +1 -0
- package/dist/hooks/useLatestRef.d.ts +6 -0
- package/dist/hooks/useLatestRef.js +12 -0
- package/dist/hooks/useLatestRef.js.map +1 -0
- package/dist/hooks/useMainWrapperPortal.d.ts +1 -0
- package/dist/hooks/useMainWrapperPortal.js +64 -0
- package/dist/hooks/useMainWrapperPortal.js.map +1 -0
- package/dist/hooks/useOverlayKeyboard.d.ts +6 -0
- package/dist/hooks/useOverlayKeyboard.js +43 -0
- package/dist/hooks/useOverlayKeyboard.js.map +1 -0
- package/dist/hooks/usePreviewBinding.d.ts +28 -0
- package/dist/hooks/usePreviewBinding.js +108 -0
- package/dist/hooks/usePreviewBinding.js.map +1 -0
- package/dist/hooks/usePreviewHandleDrag.d.ts +11 -0
- package/dist/hooks/usePreviewHandleDrag.js +53 -0
- package/dist/hooks/usePreviewHandleDrag.js.map +1 -0
- package/dist/hooks/usePreviewSelectionSync.d.ts +15 -0
- package/dist/hooks/usePreviewSelectionSync.js +80 -0
- package/dist/hooks/usePreviewSelectionSync.js.map +1 -0
- package/dist/hooks/usePreviewSettingsSync.d.ts +17 -0
- package/dist/hooks/usePreviewSettingsSync.js +55 -0
- package/dist/hooks/usePreviewSettingsSync.js.map +1 -0
- package/dist/hooks/useSidebarResize.d.ts +8 -0
- package/dist/hooks/useSidebarResize.js +101 -0
- package/dist/hooks/useSidebarResize.js.map +1 -0
- package/dist/hooks/useViewportState.d.ts +10 -0
- package/dist/hooks/useViewportState.js +44 -0
- package/dist/hooks/useViewportState.js.map +1 -0
- package/dist/index.d.ts +25 -0
- package/dist/index.js +104 -0
- package/dist/index.js.map +1 -0
- package/dist/internal/constants.d.ts +22 -0
- package/dist/internal/constants.js +38 -0
- package/dist/internal/constants.js.map +1 -0
- package/dist/internal/dom.d.ts +4 -0
- package/dist/internal/dom.js +6 -0
- package/dist/internal/dom.js.map +1 -0
- package/dist/internal/iframe.d.ts +5 -0
- package/dist/internal/iframe.js +12 -0
- package/dist/internal/iframe.js.map +1 -0
- package/dist/internal/limits.d.ts +9 -0
- package/dist/internal/limits.js +11 -0
- package/dist/internal/limits.js.map +1 -0
- package/dist/internal/path.d.ts +5 -0
- package/dist/internal/path.js +12 -0
- package/dist/internal/path.js.map +1 -0
- package/dist/internal/postmessage.d.ts +3 -0
- package/dist/internal/postmessage.js +21 -0
- package/dist/internal/postmessage.js.map +1 -0
- package/dist/internal/storage-keys.d.ts +8 -0
- package/dist/internal/storage-keys.js +9 -0
- package/dist/internal/storage-keys.js.map +1 -0
- package/dist/internal/storage.d.ts +2 -0
- package/dist/internal/storage.js +20 -0
- package/dist/internal/storage.js.map +1 -0
- package/dist/preview/HoverToolbar.d.ts +8 -0
- package/dist/preview/HoverToolbar.js +48 -0
- package/dist/preview/HoverToolbar.js.map +1 -0
- package/dist/preview/HoverToolbarController.d.ts +31 -0
- package/dist/preview/HoverToolbarController.js +160 -0
- package/dist/preview/HoverToolbarController.js.map +1 -0
- package/dist/preview/hover-css.d.ts +11 -0
- package/dist/preview/hover-css.js +94 -0
- package/dist/preview/hover-css.js.map +1 -0
- package/dist/preview/installClickToFocus.d.ts +6 -0
- package/dist/preview/installClickToFocus.js +21 -0
- package/dist/preview/installClickToFocus.js.map +1 -0
- package/dist/preview/installHoverStyles.d.ts +2 -0
- package/dist/preview/installHoverStyles.js +15 -0
- package/dist/preview/installHoverStyles.js.map +1 -0
- package/dist/preview/protocol.d.ts +11 -0
- package/dist/preview/protocol.js +19 -0
- package/dist/preview/protocol.js.map +1 -0
- package/dist/preview/toolbar-position.d.ts +20 -0
- package/dist/preview/toolbar-position.js +22 -0
- package/dist/preview/toolbar-position.js.map +1 -0
- package/dist/providers/BetterEditorConfigProvider.d.ts +14 -0
- package/dist/providers/BetterEditorConfigProvider.js +26 -0
- package/dist/providers/BetterEditorConfigProvider.js.map +1 -0
- package/dist/providers/OverlayProviders.d.ts +8 -0
- package/dist/providers/OverlayProviders.js +22 -0
- package/dist/providers/OverlayProviders.js.map +1 -0
- package/dist/state/useBetterEditorSettings.d.ts +18 -0
- package/dist/state/useBetterEditorSettings.js +65 -0
- package/dist/state/useBetterEditorSettings.js.map +1 -0
- package/dist/state/useEditorHistory.d.ts +16 -0
- package/dist/state/useEditorHistory.js +157 -0
- package/dist/state/useEditorHistory.js.map +1 -0
- package/dist/styles/blocks-tab.css +163 -0
- package/dist/styles/overlay.css +133 -0
- package/dist/styles/preview.css +211 -0
- package/dist/styles/settings-banner.css +73 -0
- package/dist/styles/sidebar.css +88 -0
- package/dist/types.d.ts +41 -0
- package/dist/types.js +3 -0
- package/dist/types.js.map +1 -0
- package/dist/version.d.ts +1 -0
- package/dist/version.js +6 -0
- package/dist/version.js.map +1 -0
- package/package.json +117 -0
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
3
|
+
import React from 'react';
|
|
4
|
+
import { DocumentFieldsTab } from './DocumentFieldsTab';
|
|
5
|
+
const isNotSidebar = (f)=>!('admin' in f) || f.admin?.position !== 'sidebar';
|
|
6
|
+
// The blocks tab owns layout-block editing; stripping `blocks` fields here
|
|
7
|
+
// avoids rendering them twice and keeps this tab focused on document meta.
|
|
8
|
+
const stripBlocks = (fields)=>{
|
|
9
|
+
const result = [];
|
|
10
|
+
for (const field of fields){
|
|
11
|
+
const type = field.type;
|
|
12
|
+
if (type === 'blocks') continue;
|
|
13
|
+
if (type === 'tabs') {
|
|
14
|
+
const newTabs = [];
|
|
15
|
+
for (const tab of field.tabs){
|
|
16
|
+
const inner = stripBlocks(tab.fields ?? []);
|
|
17
|
+
if (inner.length > 0) newTabs.push({
|
|
18
|
+
...tab,
|
|
19
|
+
fields: inner
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
if (newTabs.length === 0) continue;
|
|
23
|
+
result.push({
|
|
24
|
+
...field,
|
|
25
|
+
tabs: newTabs
|
|
26
|
+
});
|
|
27
|
+
continue;
|
|
28
|
+
}
|
|
29
|
+
if (type === 'collapsible' || type === 'row' || type === 'group') {
|
|
30
|
+
const inner = stripBlocks(field.fields);
|
|
31
|
+
if (inner.length === 0) continue;
|
|
32
|
+
result.push({
|
|
33
|
+
...field,
|
|
34
|
+
fields: inner
|
|
35
|
+
});
|
|
36
|
+
continue;
|
|
37
|
+
}
|
|
38
|
+
result.push(field);
|
|
39
|
+
}
|
|
40
|
+
return result;
|
|
41
|
+
};
|
|
42
|
+
export const DocumentSettingsTab = ()=>/*#__PURE__*/ _jsx(DocumentFieldsTab, {
|
|
43
|
+
filter: isNotSidebar,
|
|
44
|
+
transform: stripBlocks,
|
|
45
|
+
emptyText: "No document-level fields found."
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
//# sourceMappingURL=DocumentSettingsTab.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/admin/sidebar/DocumentSettingsTab.tsx"],"sourcesContent":["'use client'\n\nimport React from 'react'\nimport type { ClientField, ClientTab } from 'payload'\nimport { DocumentFieldsTab } from './DocumentFieldsTab'\n\nconst isNotSidebar = (f: ClientField): boolean =>\n !('admin' in f) || f.admin?.position !== 'sidebar'\n\n// The blocks tab owns layout-block editing; stripping `blocks` fields here\n// avoids rendering them twice and keeps this tab focused on document meta.\nconst stripBlocks = (fields: ClientField[]): ClientField[] => {\n const result: ClientField[] = []\n for (const field of fields) {\n const type = field.type\n\n if (type === 'blocks') continue\n\n if (type === 'tabs') {\n const newTabs: ClientTab[] = []\n for (const tab of field.tabs) {\n const inner = stripBlocks(tab.fields ?? [])\n if (inner.length > 0) newTabs.push({ ...tab, fields: inner })\n }\n if (newTabs.length === 0) continue\n result.push({ ...field, tabs: newTabs })\n continue\n }\n\n if (type === 'collapsible' || type === 'row' || type === 'group') {\n const inner = stripBlocks(field.fields)\n if (inner.length === 0) continue\n result.push({ ...field, fields: inner })\n continue\n }\n\n result.push(field)\n }\n return result\n}\n\nexport const DocumentSettingsTab: React.FC = () => (\n <DocumentFieldsTab\n filter={isNotSidebar}\n transform={stripBlocks}\n emptyText=\"No document-level fields found.\"\n />\n)\n"],"names":["React","DocumentFieldsTab","isNotSidebar","f","admin","position","stripBlocks","fields","result","field","type","newTabs","tab","tabs","inner","length","push","DocumentSettingsTab","filter","transform","emptyText"],"mappings":"AAAA;;AAEA,OAAOA,WAAW,QAAO;AAEzB,SAASC,iBAAiB,QAAQ,sBAAqB;AAEvD,MAAMC,eAAe,CAACC,IACpB,CAAE,CAAA,WAAWA,CAAAA,KAAMA,EAAEC,KAAK,EAAEC,aAAa;AAE3C,2EAA2E;AAC3E,2EAA2E;AAC3E,MAAMC,cAAc,CAACC;IACnB,MAAMC,SAAwB,EAAE;IAChC,KAAK,MAAMC,SAASF,OAAQ;QAC1B,MAAMG,OAAOD,MAAMC,IAAI;QAEvB,IAAIA,SAAS,UAAU;QAEvB,IAAIA,SAAS,QAAQ;YACnB,MAAMC,UAAuB,EAAE;YAC/B,KAAK,MAAMC,OAAOH,MAAMI,IAAI,CAAE;gBAC5B,MAAMC,QAAQR,YAAYM,IAAIL,MAAM,IAAI,EAAE;gBAC1C,IAAIO,MAAMC,MAAM,GAAG,GAAGJ,QAAQK,IAAI,CAAC;oBAAE,GAAGJ,GAAG;oBAAEL,QAAQO;gBAAM;YAC7D;YACA,IAAIH,QAAQI,MAAM,KAAK,GAAG;YAC1BP,OAAOQ,IAAI,CAAC;gBAAE,GAAGP,KAAK;gBAAEI,MAAMF;YAAQ;YACtC;QACF;QAEA,IAAID,SAAS,iBAAiBA,SAAS,SAASA,SAAS,SAAS;YAChE,MAAMI,QAAQR,YAAYG,MAAMF,MAAM;YACtC,IAAIO,MAAMC,MAAM,KAAK,GAAG;YACxBP,OAAOQ,IAAI,CAAC;gBAAE,GAAGP,KAAK;gBAAEF,QAAQO;YAAM;YACtC;QACF;QAEAN,OAAOQ,IAAI,CAACP;IACd;IACA,OAAOD;AACT;AAEA,OAAO,MAAMS,sBAAgC,kBAC3C,KAAChB;QACCiB,QAAQhB;QACRiB,WAAWb;QACXc,WAAU;OAEb"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
export type SidebarProps = {
|
|
3
|
+
selectedBlockPath: string | null;
|
|
4
|
+
onClearSelection: () => void;
|
|
5
|
+
onSelectPath: (path: string | null) => void;
|
|
6
|
+
forceFullWidthFields: boolean;
|
|
7
|
+
blocksField: string;
|
|
8
|
+
addBelowRequestId?: number;
|
|
9
|
+
};
|
|
10
|
+
export declare const Sidebar: React.FC<SidebarProps>;
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import React, { useEffect, useState } from 'react';
|
|
4
|
+
import { DocumentSettingsTab } from './DocumentSettingsTab';
|
|
5
|
+
import { DocumentMetaTab } from './DocumentMetaTab';
|
|
6
|
+
import { BlockSettingsTab } from './BlockSettingsTab';
|
|
7
|
+
const TABS = [
|
|
8
|
+
{
|
|
9
|
+
key: 'page',
|
|
10
|
+
label: 'Page'
|
|
11
|
+
},
|
|
12
|
+
{
|
|
13
|
+
key: 'block',
|
|
14
|
+
label: 'Blocks'
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
key: 'settings',
|
|
18
|
+
label: 'Settings'
|
|
19
|
+
}
|
|
20
|
+
];
|
|
21
|
+
const ROOT_CLASS = 'better-editor-sidebar';
|
|
22
|
+
const TAB_CLASS = `${ROOT_CLASS}__tab`;
|
|
23
|
+
const tabId = (key)=>`better-editor-tab-${key}`;
|
|
24
|
+
const panelId = (key)=>`better-editor-panel-${key}`;
|
|
25
|
+
const SidebarTab = ({ tabKey, label, active, onSelect })=>/*#__PURE__*/ _jsx("button", {
|
|
26
|
+
type: "button",
|
|
27
|
+
role: "tab",
|
|
28
|
+
id: tabId(tabKey),
|
|
29
|
+
"aria-selected": active,
|
|
30
|
+
"aria-controls": panelId(tabKey),
|
|
31
|
+
tabIndex: active ? 0 : -1,
|
|
32
|
+
className: active ? `${TAB_CLASS} ${TAB_CLASS}--active` : TAB_CLASS,
|
|
33
|
+
onClick: ()=>onSelect(tabKey),
|
|
34
|
+
children: label
|
|
35
|
+
});
|
|
36
|
+
export const Sidebar = ({ selectedBlockPath, onClearSelection, onSelectPath, forceFullWidthFields, blocksField, addBelowRequestId = 0 })=>{
|
|
37
|
+
const [tab, setTab] = useState('page');
|
|
38
|
+
// Auto-jump to the block tab when the iframe selects a new block.
|
|
39
|
+
useEffect(()=>{
|
|
40
|
+
if (selectedBlockPath) setTab('block');
|
|
41
|
+
}, [
|
|
42
|
+
selectedBlockPath
|
|
43
|
+
]);
|
|
44
|
+
const className = forceFullWidthFields ? `${ROOT_CLASS} ${ROOT_CLASS}--force-full-width` : ROOT_CLASS;
|
|
45
|
+
return /*#__PURE__*/ _jsxs("div", {
|
|
46
|
+
className: className,
|
|
47
|
+
children: [
|
|
48
|
+
/*#__PURE__*/ _jsx("div", {
|
|
49
|
+
role: "tablist",
|
|
50
|
+
className: `${ROOT_CLASS}__tabs`,
|
|
51
|
+
children: TABS.map((t)=>/*#__PURE__*/ _jsx(SidebarTab, {
|
|
52
|
+
tabKey: t.key,
|
|
53
|
+
label: t.label,
|
|
54
|
+
active: tab === t.key,
|
|
55
|
+
onSelect: setTab
|
|
56
|
+
}, t.key))
|
|
57
|
+
}),
|
|
58
|
+
/*#__PURE__*/ _jsxs("div", {
|
|
59
|
+
className: `${ROOT_CLASS}__content`,
|
|
60
|
+
role: "tabpanel",
|
|
61
|
+
id: panelId(tab),
|
|
62
|
+
"aria-labelledby": tabId(tab),
|
|
63
|
+
tabIndex: 0,
|
|
64
|
+
children: [
|
|
65
|
+
tab === 'page' && /*#__PURE__*/ _jsx(DocumentSettingsTab, {}),
|
|
66
|
+
tab === 'block' && /*#__PURE__*/ _jsx(BlockSettingsTab, {
|
|
67
|
+
selectedBlockPath: selectedBlockPath,
|
|
68
|
+
onClearSelection: onClearSelection,
|
|
69
|
+
onSelectPath: onSelectPath,
|
|
70
|
+
blocksField: blocksField,
|
|
71
|
+
addBelowRequestId: addBelowRequestId
|
|
72
|
+
}),
|
|
73
|
+
tab === 'settings' && /*#__PURE__*/ _jsx(DocumentMetaTab, {})
|
|
74
|
+
]
|
|
75
|
+
}),
|
|
76
|
+
/*#__PURE__*/ _jsx(SelectionAnnouncer, {
|
|
77
|
+
selectedBlockPath: selectedBlockPath
|
|
78
|
+
})
|
|
79
|
+
]
|
|
80
|
+
});
|
|
81
|
+
};
|
|
82
|
+
// Off-screen live region announces block-selection changes to screen
|
|
83
|
+
// readers; visible UI updates handle sighted users via the tab switch.
|
|
84
|
+
const SelectionAnnouncer = ({ selectedBlockPath })=>/*#__PURE__*/ _jsx("div", {
|
|
85
|
+
role: "status",
|
|
86
|
+
"aria-live": "polite",
|
|
87
|
+
"aria-atomic": "true",
|
|
88
|
+
className: "better-editor-sr-only",
|
|
89
|
+
children: selectedBlockPath ? `Block selected: ${selectedBlockPath}` : 'No block selected'
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
//# sourceMappingURL=Sidebar.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/admin/sidebar/Sidebar.tsx"],"sourcesContent":["'use client'\n\nimport React, { useEffect, useState } from 'react'\nimport { DocumentSettingsTab } from './DocumentSettingsTab'\nimport { DocumentMetaTab } from './DocumentMetaTab'\nimport { BlockSettingsTab } from './BlockSettingsTab'\n\nexport type SidebarProps = {\n selectedBlockPath: string | null\n onClearSelection: () => void\n onSelectPath: (path: string | null) => void\n forceFullWidthFields: boolean\n blocksField: string\n addBelowRequestId?: number\n}\n\ntype TabKey = 'page' | 'block' | 'settings'\n\nconst TABS: ReadonlyArray<{ key: TabKey; label: string }> = [\n { key: 'page', label: 'Page' },\n { key: 'block', label: 'Blocks' },\n { key: 'settings', label: 'Settings' },\n]\n\nconst ROOT_CLASS = 'better-editor-sidebar'\nconst TAB_CLASS = `${ROOT_CLASS}__tab`\n\nconst tabId = (key: TabKey) => `better-editor-tab-${key}`\nconst panelId = (key: TabKey) => `better-editor-panel-${key}`\n\ntype SidebarTabProps = {\n tabKey: TabKey\n label: string\n active: boolean\n onSelect: (key: TabKey) => void\n}\n\nconst SidebarTab: React.FC<SidebarTabProps> = ({ tabKey, label, active, onSelect }) => (\n <button\n type=\"button\"\n role=\"tab\"\n id={tabId(tabKey)}\n aria-selected={active}\n aria-controls={panelId(tabKey)}\n tabIndex={active ? 0 : -1}\n className={active ? `${TAB_CLASS} ${TAB_CLASS}--active` : TAB_CLASS}\n onClick={() => onSelect(tabKey)}\n >\n {label}\n </button>\n)\n\nexport const Sidebar: React.FC<SidebarProps> = ({\n selectedBlockPath,\n onClearSelection,\n onSelectPath,\n forceFullWidthFields,\n blocksField,\n addBelowRequestId = 0,\n}) => {\n const [tab, setTab] = useState<TabKey>('page')\n\n // Auto-jump to the block tab when the iframe selects a new block.\n useEffect(() => {\n if (selectedBlockPath) setTab('block')\n }, [selectedBlockPath])\n\n const className = forceFullWidthFields\n ? `${ROOT_CLASS} ${ROOT_CLASS}--force-full-width`\n : ROOT_CLASS\n\n return (\n <div className={className}>\n <div role=\"tablist\" className={`${ROOT_CLASS}__tabs`}>\n {TABS.map((t) => (\n <SidebarTab\n key={t.key}\n tabKey={t.key}\n label={t.label}\n active={tab === t.key}\n onSelect={setTab}\n />\n ))}\n </div>\n\n <div\n className={`${ROOT_CLASS}__content`}\n role=\"tabpanel\"\n id={panelId(tab)}\n aria-labelledby={tabId(tab)}\n tabIndex={0}\n >\n {tab === 'page' && <DocumentSettingsTab />}\n {tab === 'block' && (\n <BlockSettingsTab\n selectedBlockPath={selectedBlockPath}\n onClearSelection={onClearSelection}\n onSelectPath={onSelectPath}\n blocksField={blocksField}\n addBelowRequestId={addBelowRequestId}\n />\n )}\n {tab === 'settings' && <DocumentMetaTab />}\n </div>\n\n <SelectionAnnouncer selectedBlockPath={selectedBlockPath} />\n </div>\n )\n}\n\n// Off-screen live region announces block-selection changes to screen\n// readers; visible UI updates handle sighted users via the tab switch.\nconst SelectionAnnouncer: React.FC<{ selectedBlockPath: string | null }> = ({\n selectedBlockPath,\n}) => (\n <div role=\"status\" aria-live=\"polite\" aria-atomic=\"true\" className=\"better-editor-sr-only\">\n {selectedBlockPath ? `Block selected: ${selectedBlockPath}` : 'No block selected'}\n </div>\n)\n"],"names":["React","useEffect","useState","DocumentSettingsTab","DocumentMetaTab","BlockSettingsTab","TABS","key","label","ROOT_CLASS","TAB_CLASS","tabId","panelId","SidebarTab","tabKey","active","onSelect","button","type","role","id","aria-selected","aria-controls","tabIndex","className","onClick","Sidebar","selectedBlockPath","onClearSelection","onSelectPath","forceFullWidthFields","blocksField","addBelowRequestId","tab","setTab","div","map","t","aria-labelledby","SelectionAnnouncer","aria-live","aria-atomic"],"mappings":"AAAA;;AAEA,OAAOA,SAASC,SAAS,EAAEC,QAAQ,QAAQ,QAAO;AAClD,SAASC,mBAAmB,QAAQ,wBAAuB;AAC3D,SAASC,eAAe,QAAQ,oBAAmB;AACnD,SAASC,gBAAgB,QAAQ,qBAAoB;AAarD,MAAMC,OAAsD;IAC1D;QAAEC,KAAK;QAAQC,OAAO;IAAO;IAC7B;QAAED,KAAK;QAASC,OAAO;IAAS;IAChC;QAAED,KAAK;QAAYC,OAAO;IAAW;CACtC;AAED,MAAMC,aAAa;AACnB,MAAMC,YAAY,GAAGD,WAAW,KAAK,CAAC;AAEtC,MAAME,QAAQ,CAACJ,MAAgB,CAAC,kBAAkB,EAAEA,KAAK;AACzD,MAAMK,UAAU,CAACL,MAAgB,CAAC,oBAAoB,EAAEA,KAAK;AAS7D,MAAMM,aAAwC,CAAC,EAAEC,MAAM,EAAEN,KAAK,EAAEO,MAAM,EAAEC,QAAQ,EAAE,iBAChF,KAACC;QACCC,MAAK;QACLC,MAAK;QACLC,IAAIT,MAAMG;QACVO,iBAAeN;QACfO,iBAAeV,QAAQE;QACvBS,UAAUR,SAAS,IAAI,CAAC;QACxBS,WAAWT,SAAS,GAAGL,UAAU,CAAC,EAAEA,UAAU,QAAQ,CAAC,GAAGA;QAC1De,SAAS,IAAMT,SAASF;kBAEvBN;;AAIL,OAAO,MAAMkB,UAAkC,CAAC,EAC9CC,iBAAiB,EACjBC,gBAAgB,EAChBC,YAAY,EACZC,oBAAoB,EACpBC,WAAW,EACXC,oBAAoB,CAAC,EACtB;IACC,MAAM,CAACC,KAAKC,OAAO,GAAGhC,SAAiB;IAEvC,kEAAkE;IAClED,UAAU;QACR,IAAI0B,mBAAmBO,OAAO;IAChC,GAAG;QAACP;KAAkB;IAEtB,MAAMH,YAAYM,uBACd,GAAGrB,WAAW,CAAC,EAAEA,WAAW,kBAAkB,CAAC,GAC/CA;IAEJ,qBACE,MAAC0B;QAAIX,WAAWA;;0BACd,KAACW;gBAAIhB,MAAK;gBAAUK,WAAW,GAAGf,WAAW,MAAM,CAAC;0BACjDH,KAAK8B,GAAG,CAAC,CAACC,kBACT,KAACxB;wBAECC,QAAQuB,EAAE9B,GAAG;wBACbC,OAAO6B,EAAE7B,KAAK;wBACdO,QAAQkB,QAAQI,EAAE9B,GAAG;wBACrBS,UAAUkB;uBAJLG,EAAE9B,GAAG;;0BAShB,MAAC4B;gBACCX,WAAW,GAAGf,WAAW,SAAS,CAAC;gBACnCU,MAAK;gBACLC,IAAIR,QAAQqB;gBACZK,mBAAiB3B,MAAMsB;gBACvBV,UAAU;;oBAETU,QAAQ,wBAAU,KAAC9B;oBACnB8B,QAAQ,yBACP,KAAC5B;wBACCsB,mBAAmBA;wBACnBC,kBAAkBA;wBAClBC,cAAcA;wBACdE,aAAaA;wBACbC,mBAAmBA;;oBAGtBC,QAAQ,4BAAc,KAAC7B;;;0BAG1B,KAACmC;gBAAmBZ,mBAAmBA;;;;AAG7C,EAAC;AAED,qEAAqE;AACrE,uEAAuE;AACvE,MAAMY,qBAAqE,CAAC,EAC1EZ,iBAAiB,EAClB,iBACC,KAACQ;QAAIhB,MAAK;QAASqB,aAAU;QAASC,eAAY;QAAOjB,WAAU;kBAChEG,oBAAoB,CAAC,gBAAgB,EAAEA,mBAAmB,GAAG"}
|
package/dist/client.d.ts
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The button rendered in `beforeDocumentControls` that toggles the
|
|
3
|
+
* Better Editor overlay. Auto-injected by the plugin factory; consumers
|
|
4
|
+
* usually don't render it directly.
|
|
5
|
+
*/
|
|
6
|
+
export { LiveEditorToggle } from './admin/LiveEditorToggle';
|
|
7
|
+
export type { LiveEditorToggleProps } from './admin/LiveEditorToggle';
|
|
8
|
+
/**
|
|
9
|
+
* The full editor overlay (preview iframe + 3-tab sidebar). Mounted into
|
|
10
|
+
* the Payload admin shell when the toggle opens it. Exported so consumers
|
|
11
|
+
* can build a custom toggle / wrapper if they need to bypass the
|
|
12
|
+
* auto-injected button.
|
|
13
|
+
*/
|
|
14
|
+
export { LiveEditorOverlay } from './admin/LiveEditorOverlay';
|
|
15
|
+
export type { LiveEditorOverlayProps } from './admin/LiveEditorOverlay';
|
|
16
|
+
/**
|
|
17
|
+
* Banner shown at the top of the `BetterEditorSettings` global. Wired up
|
|
18
|
+
* automatically; hide it via `betterEditor({ showSettingsBanner: false })`.
|
|
19
|
+
*/
|
|
20
|
+
export { SettingsBanner } from './admin/SettingsBanner';
|
|
21
|
+
/**
|
|
22
|
+
* Spread these props on every block wrapper in your frontend so the
|
|
23
|
+
* Better Editor can target it. The plugin uses the resulting
|
|
24
|
+
* `data-better-editor-id` attribute for hover outlines, click-to-focus,
|
|
25
|
+
* the in-iframe action toolbar, and selection sync.
|
|
26
|
+
*
|
|
27
|
+
* @example
|
|
28
|
+
* import { getBlockProps } from 'payload-better-editor/client'
|
|
29
|
+
*
|
|
30
|
+
* <section {...getBlockProps(block)}>...</section>
|
|
31
|
+
*/
|
|
32
|
+
export declare const getBlockProps: (block: {
|
|
33
|
+
id?: string | null;
|
|
34
|
+
}) => Record<string, string>;
|
package/dist/client.js
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The button rendered in `beforeDocumentControls` that toggles the
|
|
3
|
+
* Better Editor overlay. Auto-injected by the plugin factory; consumers
|
|
4
|
+
* usually don't render it directly.
|
|
5
|
+
*/ export { LiveEditorToggle } from './admin/LiveEditorToggle';
|
|
6
|
+
/**
|
|
7
|
+
* The full editor overlay (preview iframe + 3-tab sidebar). Mounted into
|
|
8
|
+
* the Payload admin shell when the toggle opens it. Exported so consumers
|
|
9
|
+
* can build a custom toggle / wrapper if they need to bypass the
|
|
10
|
+
* auto-injected button.
|
|
11
|
+
*/ export { LiveEditorOverlay } from './admin/LiveEditorOverlay';
|
|
12
|
+
/**
|
|
13
|
+
* Banner shown at the top of the `BetterEditorSettings` global. Wired up
|
|
14
|
+
* automatically; hide it via `betterEditor({ showSettingsBanner: false })`.
|
|
15
|
+
*/ export { SettingsBanner } from './admin/SettingsBanner';
|
|
16
|
+
/**
|
|
17
|
+
* Spread these props on every block wrapper in your frontend so the
|
|
18
|
+
* Better Editor can target it. The plugin uses the resulting
|
|
19
|
+
* `data-better-editor-id` attribute for hover outlines, click-to-focus,
|
|
20
|
+
* the in-iframe action toolbar, and selection sync.
|
|
21
|
+
*
|
|
22
|
+
* @example
|
|
23
|
+
* import { getBlockProps } from 'payload-better-editor/client'
|
|
24
|
+
*
|
|
25
|
+
* <section {...getBlockProps(block)}>...</section>
|
|
26
|
+
*/ export const getBlockProps = (block)=>block.id ? {
|
|
27
|
+
'data-better-editor-id': block.id
|
|
28
|
+
} : {};
|
|
29
|
+
|
|
30
|
+
//# sourceMappingURL=client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/client.ts"],"sourcesContent":["/**\n * The button rendered in `beforeDocumentControls` that toggles the\n * Better Editor overlay. Auto-injected by the plugin factory; consumers\n * usually don't render it directly.\n */\nexport { LiveEditorToggle } from './admin/LiveEditorToggle'\nexport type { LiveEditorToggleProps } from './admin/LiveEditorToggle'\n\n/**\n * The full editor overlay (preview iframe + 3-tab sidebar). Mounted into\n * the Payload admin shell when the toggle opens it. Exported so consumers\n * can build a custom toggle / wrapper if they need to bypass the\n * auto-injected button.\n */\nexport { LiveEditorOverlay } from './admin/LiveEditorOverlay'\nexport type { LiveEditorOverlayProps } from './admin/LiveEditorOverlay'\n\n/**\n * Banner shown at the top of the `BetterEditorSettings` global. Wired up\n * automatically; hide it via `betterEditor({ showSettingsBanner: false })`.\n */\nexport { SettingsBanner } from './admin/SettingsBanner'\n\n/**\n * Spread these props on every block wrapper in your frontend so the\n * Better Editor can target it. The plugin uses the resulting\n * `data-better-editor-id` attribute for hover outlines, click-to-focus,\n * the in-iframe action toolbar, and selection sync.\n *\n * @example\n * import { getBlockProps } from 'payload-better-editor/client'\n *\n * <section {...getBlockProps(block)}>...</section>\n */\nexport const getBlockProps = (\n block: { id?: string | null },\n): Record<string, string> =>\n block.id ? { 'data-better-editor-id': block.id } : {}\n"],"names":["LiveEditorToggle","LiveEditorOverlay","SettingsBanner","getBlockProps","block","id"],"mappings":"AAAA;;;;CAIC,GACD,SAASA,gBAAgB,QAAQ,2BAA0B;AAG3D;;;;;CAKC,GACD,SAASC,iBAAiB,QAAQ,4BAA2B;AAG7D;;;CAGC,GACD,SAASC,cAAc,QAAQ,yBAAwB;AAEvD;;;;;;;;;;CAUC,GACD,OAAO,MAAMC,gBAAgB,CAC3BC,QAEAA,MAAMC,EAAE,GAAG;QAAE,yBAAyBD,MAAMC,EAAE;IAAC,IAAI,CAAC,EAAC"}
|
package/dist/global.d.ts
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { GlobalConfig } from 'payload';
|
|
2
|
+
export declare const BETTER_EDITOR_SETTINGS_SLUG = "better-editor-settings";
|
|
3
|
+
export declare const BETTER_EDITOR_SETTINGS_BANNER_FIELD = "betterEditorSettingsBanner";
|
|
4
|
+
export declare const betterEditorSettingsGlobal: GlobalConfig;
|
package/dist/global.js
ADDED
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
import { DEFAULT_BETTER_EDITOR_SETTINGS as D } from './internal/constants';
|
|
2
|
+
import { HOVER_OUTLINE_MAX, HOVER_OUTLINE_MIN, MOBILE_WIDTH_MAX, MOBILE_WIDTH_MIN, TABLET_WIDTH_MAX, TABLET_WIDTH_MIN } from './internal/limits';
|
|
3
|
+
export const BETTER_EDITOR_SETTINGS_SLUG = 'better-editor-settings';
|
|
4
|
+
export const BETTER_EDITOR_SETTINGS_BANNER_FIELD = 'betterEditorSettingsBanner';
|
|
5
|
+
// Mirrors the runtime regex in `preview/hover-css.ts` so the admin UI rejects
|
|
6
|
+
// values that would silently be skipped at render time.
|
|
7
|
+
const HOVER_COLOR_RE = /^(?:#[0-9a-fA-F]{3,8}|rgba?\([^()\n\r]*\))$/i;
|
|
8
|
+
const validateHoverColor = (value)=>{
|
|
9
|
+
if (typeof value !== 'string' || value.length === 0) return 'Color is required';
|
|
10
|
+
if (!HOVER_COLOR_RE.test(value.trim())) {
|
|
11
|
+
return 'Must be a hex color (e.g. #3b82f6) or rgb()/rgba()';
|
|
12
|
+
}
|
|
13
|
+
return true;
|
|
14
|
+
};
|
|
15
|
+
const validateHoverOutline = (value)=>{
|
|
16
|
+
if (typeof value !== 'number' || !Number.isFinite(value)) return 'Must be a number';
|
|
17
|
+
if (value < HOVER_OUTLINE_MIN || value > HOVER_OUTLINE_MAX) {
|
|
18
|
+
return `Must be between ${HOVER_OUTLINE_MIN} and ${HOVER_OUTLINE_MAX}`;
|
|
19
|
+
}
|
|
20
|
+
return true;
|
|
21
|
+
};
|
|
22
|
+
export const betterEditorSettingsGlobal = {
|
|
23
|
+
slug: BETTER_EDITOR_SETTINGS_SLUG,
|
|
24
|
+
label: 'Settings',
|
|
25
|
+
access: {
|
|
26
|
+
read: ()=>true
|
|
27
|
+
},
|
|
28
|
+
admin: {
|
|
29
|
+
group: 'Better Editor',
|
|
30
|
+
description: 'Editor-wide preferences for the Better Editor overlay.'
|
|
31
|
+
},
|
|
32
|
+
fields: [
|
|
33
|
+
{
|
|
34
|
+
name: 'betterEditorSettingsBanner',
|
|
35
|
+
type: 'ui',
|
|
36
|
+
admin: {
|
|
37
|
+
components: {
|
|
38
|
+
Field: 'payload-better-editor/client#SettingsBanner'
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
type: 'tabs',
|
|
44
|
+
tabs: [
|
|
45
|
+
{
|
|
46
|
+
label: 'Sidebar',
|
|
47
|
+
description: 'Where the sidebar sits and how its fields are stacked.',
|
|
48
|
+
fields: [
|
|
49
|
+
{
|
|
50
|
+
name: 'sidebarPosition',
|
|
51
|
+
type: 'select',
|
|
52
|
+
label: 'Position',
|
|
53
|
+
defaultValue: D.sidebarPosition,
|
|
54
|
+
options: [
|
|
55
|
+
{
|
|
56
|
+
label: 'Right',
|
|
57
|
+
value: 'right'
|
|
58
|
+
},
|
|
59
|
+
{
|
|
60
|
+
label: 'Left',
|
|
61
|
+
value: 'left'
|
|
62
|
+
}
|
|
63
|
+
]
|
|
64
|
+
},
|
|
65
|
+
{
|
|
66
|
+
name: 'forceFullWidthFields',
|
|
67
|
+
type: 'checkbox',
|
|
68
|
+
label: 'Stack fields full-width',
|
|
69
|
+
defaultValue: D.forceFullWidthFields,
|
|
70
|
+
admin: {
|
|
71
|
+
description: 'Override admin.width on sidebar fields so they always span the full row.'
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
]
|
|
75
|
+
},
|
|
76
|
+
{
|
|
77
|
+
label: 'Viewport',
|
|
78
|
+
description: 'Pixel widths for the Tablet and Mobile preview modes.',
|
|
79
|
+
fields: [
|
|
80
|
+
{
|
|
81
|
+
type: 'row',
|
|
82
|
+
fields: [
|
|
83
|
+
{
|
|
84
|
+
name: 'tabletWidth',
|
|
85
|
+
type: 'number',
|
|
86
|
+
label: 'Tablet (px)',
|
|
87
|
+
defaultValue: D.tabletWidth,
|
|
88
|
+
min: TABLET_WIDTH_MIN,
|
|
89
|
+
max: TABLET_WIDTH_MAX,
|
|
90
|
+
admin: {
|
|
91
|
+
width: '50%',
|
|
92
|
+
placeholder: String(D.tabletWidth)
|
|
93
|
+
}
|
|
94
|
+
},
|
|
95
|
+
{
|
|
96
|
+
name: 'mobileWidth',
|
|
97
|
+
type: 'number',
|
|
98
|
+
label: 'Mobile (px)',
|
|
99
|
+
defaultValue: D.mobileWidth,
|
|
100
|
+
min: MOBILE_WIDTH_MIN,
|
|
101
|
+
max: MOBILE_WIDTH_MAX,
|
|
102
|
+
admin: {
|
|
103
|
+
width: '50%',
|
|
104
|
+
placeholder: String(D.mobileWidth)
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
]
|
|
108
|
+
}
|
|
109
|
+
]
|
|
110
|
+
},
|
|
111
|
+
{
|
|
112
|
+
label: 'Outline',
|
|
113
|
+
description: 'Outline + tint shown on the hovered or selected block.',
|
|
114
|
+
fields: [
|
|
115
|
+
{
|
|
116
|
+
type: 'row',
|
|
117
|
+
fields: [
|
|
118
|
+
{
|
|
119
|
+
name: 'hoverColorTopLevel',
|
|
120
|
+
type: 'text',
|
|
121
|
+
label: 'Top-level color',
|
|
122
|
+
defaultValue: D.hoverColorTopLevel,
|
|
123
|
+
validate: validateHoverColor,
|
|
124
|
+
admin: {
|
|
125
|
+
width: '50%',
|
|
126
|
+
placeholder: D.hoverColorTopLevel,
|
|
127
|
+
description: 'Hex color (e.g. `#3b82f6`).'
|
|
128
|
+
}
|
|
129
|
+
},
|
|
130
|
+
{
|
|
131
|
+
name: 'hoverColorNested',
|
|
132
|
+
type: 'text',
|
|
133
|
+
label: 'Nested color',
|
|
134
|
+
defaultValue: D.hoverColorNested,
|
|
135
|
+
validate: validateHoverColor,
|
|
136
|
+
admin: {
|
|
137
|
+
width: '50%',
|
|
138
|
+
placeholder: D.hoverColorNested,
|
|
139
|
+
description: 'Hex color for blocks nested inside another block.'
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
]
|
|
143
|
+
},
|
|
144
|
+
{
|
|
145
|
+
name: 'hoverOutlineWidth',
|
|
146
|
+
type: 'number',
|
|
147
|
+
label: 'Outline width (px)',
|
|
148
|
+
defaultValue: D.hoverOutlineWidth,
|
|
149
|
+
min: HOVER_OUTLINE_MIN,
|
|
150
|
+
max: HOVER_OUTLINE_MAX,
|
|
151
|
+
validate: validateHoverOutline,
|
|
152
|
+
admin: {
|
|
153
|
+
placeholder: String(D.hoverOutlineWidth),
|
|
154
|
+
description: `Outline thickness in pixels (${HOVER_OUTLINE_MIN}–${HOVER_OUTLINE_MAX}).`
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
]
|
|
158
|
+
},
|
|
159
|
+
{
|
|
160
|
+
label: 'Toolbar',
|
|
161
|
+
description: 'Floating Move / Duplicate / Delete toolbar that appears on the selected block.',
|
|
162
|
+
fields: [
|
|
163
|
+
{
|
|
164
|
+
name: 'showHoverToolbar',
|
|
165
|
+
type: 'checkbox',
|
|
166
|
+
label: 'Enabled',
|
|
167
|
+
defaultValue: D.showHoverToolbar
|
|
168
|
+
},
|
|
169
|
+
{
|
|
170
|
+
name: 'hoverToolbarPosition',
|
|
171
|
+
type: 'select',
|
|
172
|
+
label: 'Anchor corner',
|
|
173
|
+
defaultValue: D.hoverToolbarPosition,
|
|
174
|
+
options: [
|
|
175
|
+
{
|
|
176
|
+
label: 'Top right',
|
|
177
|
+
value: 'top-right'
|
|
178
|
+
},
|
|
179
|
+
{
|
|
180
|
+
label: 'Top left',
|
|
181
|
+
value: 'top-left'
|
|
182
|
+
},
|
|
183
|
+
{
|
|
184
|
+
label: 'Bottom right',
|
|
185
|
+
value: 'bottom-right'
|
|
186
|
+
},
|
|
187
|
+
{
|
|
188
|
+
label: 'Bottom left',
|
|
189
|
+
value: 'bottom-left'
|
|
190
|
+
}
|
|
191
|
+
]
|
|
192
|
+
}
|
|
193
|
+
]
|
|
194
|
+
}
|
|
195
|
+
]
|
|
196
|
+
}
|
|
197
|
+
]
|
|
198
|
+
};
|
|
199
|
+
|
|
200
|
+
//# sourceMappingURL=global.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/global.ts"],"sourcesContent":["import type { GlobalConfig } from 'payload'\nimport { DEFAULT_BETTER_EDITOR_SETTINGS as D } from './internal/constants'\nimport {\n HOVER_OUTLINE_MAX,\n HOVER_OUTLINE_MIN,\n MOBILE_WIDTH_MAX,\n MOBILE_WIDTH_MIN,\n TABLET_WIDTH_MAX,\n TABLET_WIDTH_MIN,\n} from './internal/limits'\n\nexport const BETTER_EDITOR_SETTINGS_SLUG = 'better-editor-settings'\n\nexport const BETTER_EDITOR_SETTINGS_BANNER_FIELD = 'betterEditorSettingsBanner'\n\n// Mirrors the runtime regex in `preview/hover-css.ts` so the admin UI rejects\n// values that would silently be skipped at render time.\nconst HOVER_COLOR_RE = /^(?:#[0-9a-fA-F]{3,8}|rgba?\\([^()\\n\\r]*\\))$/i\n\nconst validateHoverColor = (value: unknown): string | true => {\n if (typeof value !== 'string' || value.length === 0) return 'Color is required'\n if (!HOVER_COLOR_RE.test(value.trim())) {\n return 'Must be a hex color (e.g. #3b82f6) or rgb()/rgba()'\n }\n return true\n}\n\nconst validateHoverOutline = (value: unknown): string | true => {\n if (typeof value !== 'number' || !Number.isFinite(value)) return 'Must be a number'\n if (value < HOVER_OUTLINE_MIN || value > HOVER_OUTLINE_MAX) {\n return `Must be between ${HOVER_OUTLINE_MIN} and ${HOVER_OUTLINE_MAX}`\n }\n return true\n}\n\nexport const betterEditorSettingsGlobal: GlobalConfig = {\n slug: BETTER_EDITOR_SETTINGS_SLUG,\n label: 'Settings',\n access: {\n read: () => true,\n },\n admin: {\n group: 'Better Editor',\n description: 'Editor-wide preferences for the Better Editor overlay.',\n },\n\n fields: [\n {\n name: 'betterEditorSettingsBanner',\n type: 'ui',\n admin: {\n components: {\n Field: 'payload-better-editor/client#SettingsBanner',\n },\n },\n },\n {\n type: 'tabs',\n tabs: [\n {\n label: 'Sidebar',\n description: 'Where the sidebar sits and how its fields are stacked.',\n fields: [\n {\n name: 'sidebarPosition',\n type: 'select',\n label: 'Position',\n defaultValue: D.sidebarPosition,\n options: [\n { label: 'Right', value: 'right' },\n { label: 'Left', value: 'left' },\n ],\n },\n {\n name: 'forceFullWidthFields',\n type: 'checkbox',\n label: 'Stack fields full-width',\n defaultValue: D.forceFullWidthFields,\n admin: {\n description:\n 'Override admin.width on sidebar fields so they always span the full row.',\n },\n },\n ],\n },\n {\n label: 'Viewport',\n description: 'Pixel widths for the Tablet and Mobile preview modes.',\n fields: [\n {\n type: 'row',\n fields: [\n {\n name: 'tabletWidth',\n type: 'number',\n label: 'Tablet (px)',\n defaultValue: D.tabletWidth,\n min: TABLET_WIDTH_MIN,\n max: TABLET_WIDTH_MAX,\n admin: { width: '50%', placeholder: String(D.tabletWidth) },\n },\n {\n name: 'mobileWidth',\n type: 'number',\n label: 'Mobile (px)',\n defaultValue: D.mobileWidth,\n min: MOBILE_WIDTH_MIN,\n max: MOBILE_WIDTH_MAX,\n admin: { width: '50%', placeholder: String(D.mobileWidth) },\n },\n ],\n },\n ],\n },\n {\n label: 'Outline',\n description: 'Outline + tint shown on the hovered or selected block.',\n fields: [\n {\n type: 'row',\n fields: [\n {\n name: 'hoverColorTopLevel',\n type: 'text',\n label: 'Top-level color',\n defaultValue: D.hoverColorTopLevel,\n validate: validateHoverColor,\n admin: {\n width: '50%',\n placeholder: D.hoverColorTopLevel,\n description: 'Hex color (e.g. `#3b82f6`).',\n },\n },\n {\n name: 'hoverColorNested',\n type: 'text',\n label: 'Nested color',\n defaultValue: D.hoverColorNested,\n validate: validateHoverColor,\n admin: {\n width: '50%',\n placeholder: D.hoverColorNested,\n description: 'Hex color for blocks nested inside another block.',\n },\n },\n ],\n },\n {\n name: 'hoverOutlineWidth',\n type: 'number',\n label: 'Outline width (px)',\n defaultValue: D.hoverOutlineWidth,\n min: HOVER_OUTLINE_MIN,\n max: HOVER_OUTLINE_MAX,\n validate: validateHoverOutline,\n admin: {\n placeholder: String(D.hoverOutlineWidth),\n description: `Outline thickness in pixels (${HOVER_OUTLINE_MIN}–${HOVER_OUTLINE_MAX}).`,\n },\n },\n ],\n },\n {\n label: 'Toolbar',\n description:\n 'Floating Move / Duplicate / Delete toolbar that appears on the selected block.',\n fields: [\n {\n name: 'showHoverToolbar',\n type: 'checkbox',\n label: 'Enabled',\n defaultValue: D.showHoverToolbar,\n },\n {\n name: 'hoverToolbarPosition',\n type: 'select',\n label: 'Anchor corner',\n defaultValue: D.hoverToolbarPosition,\n options: [\n { label: 'Top right', value: 'top-right' },\n { label: 'Top left', value: 'top-left' },\n { label: 'Bottom right', value: 'bottom-right' },\n { label: 'Bottom left', value: 'bottom-left' },\n ],\n },\n ],\n },\n ],\n },\n ],\n}\n"],"names":["DEFAULT_BETTER_EDITOR_SETTINGS","D","HOVER_OUTLINE_MAX","HOVER_OUTLINE_MIN","MOBILE_WIDTH_MAX","MOBILE_WIDTH_MIN","TABLET_WIDTH_MAX","TABLET_WIDTH_MIN","BETTER_EDITOR_SETTINGS_SLUG","BETTER_EDITOR_SETTINGS_BANNER_FIELD","HOVER_COLOR_RE","validateHoverColor","value","length","test","trim","validateHoverOutline","Number","isFinite","betterEditorSettingsGlobal","slug","label","access","read","admin","group","description","fields","name","type","components","Field","tabs","defaultValue","sidebarPosition","options","forceFullWidthFields","tabletWidth","min","max","width","placeholder","String","mobileWidth","hoverColorTopLevel","validate","hoverColorNested","hoverOutlineWidth","showHoverToolbar","hoverToolbarPosition"],"mappings":"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"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export type UseAddBlockDrawerArgs = {
|
|
2
|
+
drawerSlug: string;
|
|
3
|
+
/** Bumped externally to request the drawer to open. */
|
|
4
|
+
requestId: number;
|
|
5
|
+
/** Drawer should only open when a block is currently selected. */
|
|
6
|
+
selectedBlockPath: string | null;
|
|
7
|
+
};
|
|
8
|
+
/**
|
|
9
|
+
* Defers `toggleModal(drawerSlug)` to the next paint so the drawer is
|
|
10
|
+
* mounted before we open it (the sidebar may have just auto-switched to
|
|
11
|
+
* the Blocks tab). De-duplicates by `requestId` so a stale id can't
|
|
12
|
+
* re-trigger after the drawer has been closed.
|
|
13
|
+
*/
|
|
14
|
+
export declare const useAddBlockDrawer: ({ drawerSlug, requestId, selectedBlockPath, }: UseAddBlockDrawerArgs) => void;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { useEffect, useRef } from 'react';
|
|
3
|
+
import { useModal } from '@payloadcms/ui';
|
|
4
|
+
/**
|
|
5
|
+
* Defers `toggleModal(drawerSlug)` to the next paint so the drawer is
|
|
6
|
+
* mounted before we open it (the sidebar may have just auto-switched to
|
|
7
|
+
* the Blocks tab). De-duplicates by `requestId` so a stale id can't
|
|
8
|
+
* re-trigger after the drawer has been closed.
|
|
9
|
+
*/ export const useAddBlockDrawer = ({ drawerSlug, requestId, selectedBlockPath })=>{
|
|
10
|
+
const { toggleModal } = useModal();
|
|
11
|
+
const lastHandledRef = useRef(0);
|
|
12
|
+
useEffect(()=>{
|
|
13
|
+
if (!requestId || requestId === lastHandledRef.current) return;
|
|
14
|
+
if (!selectedBlockPath) return;
|
|
15
|
+
lastHandledRef.current = requestId;
|
|
16
|
+
const raf = requestAnimationFrame(()=>toggleModal(drawerSlug));
|
|
17
|
+
return ()=>cancelAnimationFrame(raf);
|
|
18
|
+
}, [
|
|
19
|
+
requestId,
|
|
20
|
+
selectedBlockPath,
|
|
21
|
+
toggleModal,
|
|
22
|
+
drawerSlug
|
|
23
|
+
]);
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
//# sourceMappingURL=useAddBlockDrawer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/hooks/useAddBlockDrawer.ts"],"sourcesContent":["'use client'\n\nimport { useEffect, useRef } from 'react'\nimport { useModal } from '@payloadcms/ui'\n\nexport type UseAddBlockDrawerArgs = {\n drawerSlug: string\n /** Bumped externally to request the drawer to open. */\n requestId: number\n /** Drawer should only open when a block is currently selected. */\n selectedBlockPath: string | null\n}\n\n/**\n * Defers `toggleModal(drawerSlug)` to the next paint so the drawer is\n * mounted before we open it (the sidebar may have just auto-switched to\n * the Blocks tab). De-duplicates by `requestId` so a stale id can't\n * re-trigger after the drawer has been closed.\n */\nexport const useAddBlockDrawer = ({\n drawerSlug,\n requestId,\n selectedBlockPath,\n}: UseAddBlockDrawerArgs): void => {\n const { toggleModal } = useModal()\n const lastHandledRef = useRef(0)\n\n useEffect(() => {\n if (!requestId || requestId === lastHandledRef.current) return\n if (!selectedBlockPath) return\n lastHandledRef.current = requestId\n const raf = requestAnimationFrame(() => toggleModal(drawerSlug))\n return () => cancelAnimationFrame(raf)\n }, [requestId, selectedBlockPath, toggleModal, drawerSlug])\n}\n"],"names":["useEffect","useRef","useModal","useAddBlockDrawer","drawerSlug","requestId","selectedBlockPath","toggleModal","lastHandledRef","current","raf","requestAnimationFrame","cancelAnimationFrame"],"mappings":"AAAA;AAEA,SAASA,SAAS,EAAEC,MAAM,QAAQ,QAAO;AACzC,SAASC,QAAQ,QAAQ,iBAAgB;AAUzC;;;;;CAKC,GACD,OAAO,MAAMC,oBAAoB,CAAC,EAChCC,UAAU,EACVC,SAAS,EACTC,iBAAiB,EACK;IACtB,MAAM,EAAEC,WAAW,EAAE,GAAGL;IACxB,MAAMM,iBAAiBP,OAAO;IAE9BD,UAAU;QACR,IAAI,CAACK,aAAaA,cAAcG,eAAeC,OAAO,EAAE;QACxD,IAAI,CAACH,mBAAmB;QACxBE,eAAeC,OAAO,GAAGJ;QACzB,MAAMK,MAAMC,sBAAsB,IAAMJ,YAAYH;QACpD,OAAO,IAAMQ,qBAAqBF;IACpC,GAAG;QAACL;QAAWC;QAAmBC;QAAaH;KAAW;AAC5D,EAAC"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export type UseBlockActionMessagesArgs = {
|
|
2
|
+
selectedBlockPath: string | null;
|
|
3
|
+
setSelectedBlockPath: (path: string | null) => void;
|
|
4
|
+
};
|
|
5
|
+
export type UseBlockActionMessagesReturn = {
|
|
6
|
+
addBelowRequestId: number;
|
|
7
|
+
};
|
|
8
|
+
export declare const useBlockActionMessages: ({ setSelectedBlockPath, }: UseBlockActionMessagesArgs) => UseBlockActionMessagesReturn;
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { useEffect, useMemo, useState } from 'react';
|
|
3
|
+
import { useAllFormFields, useForm } from '@payloadcms/ui';
|
|
4
|
+
import { listenForParentInbound } from '../internal/postmessage';
|
|
5
|
+
import { splitFieldPath } from '../internal/path';
|
|
6
|
+
import { useEditorHistory } from '../state/useEditorHistory';
|
|
7
|
+
import { useLatestRef } from './useLatestRef';
|
|
8
|
+
const ID_SUFFIX = '.id';
|
|
9
|
+
const buildIdIndex = (fields)=>{
|
|
10
|
+
const map = new Map();
|
|
11
|
+
for (const key of Object.keys(fields)){
|
|
12
|
+
if (!key.endsWith(ID_SUFFIX)) continue;
|
|
13
|
+
const value = fields[key]?.value;
|
|
14
|
+
if (typeof value === 'string' && value.length > 0) {
|
|
15
|
+
map.set(value, key.slice(0, -ID_SUFFIX.length));
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
return map;
|
|
19
|
+
};
|
|
20
|
+
export const useBlockActionMessages = ({ setSelectedBlockPath })=>{
|
|
21
|
+
const [addBelowRequestId, setAddBelowRequestId] = useState(0);
|
|
22
|
+
const [allFields] = useAllFormFields();
|
|
23
|
+
const idIndex = useMemo(()=>buildIdIndex(allFields), [
|
|
24
|
+
allFields
|
|
25
|
+
]);
|
|
26
|
+
const { dispatchFields } = useForm();
|
|
27
|
+
const history = useEditorHistory();
|
|
28
|
+
// Refs let the postMessage listener stay bound across renders without
|
|
29
|
+
// missing the latest form state, dispatcher, or history commit.
|
|
30
|
+
const allFieldsRef = useLatestRef(allFields);
|
|
31
|
+
const idIndexRef = useLatestRef(idIndex);
|
|
32
|
+
const dispatchFieldsRef = useLatestRef(dispatchFields);
|
|
33
|
+
const historyRef = useLatestRef(history);
|
|
34
|
+
const setSelectedBlockPathRef = useLatestRef(setSelectedBlockPath);
|
|
35
|
+
// Bind the postMessage listener exactly once. All four state inputs flow
|
|
36
|
+
// through stable refs (above), so a re-bind is never needed.
|
|
37
|
+
useEffect(()=>listenForParentInbound((data)=>{
|
|
38
|
+
const fields = allFieldsRef.current;
|
|
39
|
+
const path = idIndexRef.current.get(data.id) ?? null;
|
|
40
|
+
if (!path) return;
|
|
41
|
+
const select = setSelectedBlockPathRef.current;
|
|
42
|
+
const dispatch = dispatchFieldsRef.current;
|
|
43
|
+
const { commit } = historyRef.current;
|
|
44
|
+
if (data.type === 'focus-block') {
|
|
45
|
+
select(path);
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
if (data.action === 'add') {
|
|
49
|
+
// Monotonic counter — BlockSettingsTab compares the latest id against
|
|
50
|
+
// its lastHandledRequestRef. Date.now() risked collisions on
|
|
51
|
+
// double-clicks landing in the same millisecond.
|
|
52
|
+
select(path);
|
|
53
|
+
setAddBelowRequestId((id)=>id + 1);
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
const split = splitFieldPath(path);
|
|
57
|
+
if (!split) return;
|
|
58
|
+
const { parent: parentPath, index: rowIndex } = split;
|
|
59
|
+
const rows = fields[parentPath]?.rows;
|
|
60
|
+
const rowCount = Array.isArray(rows) ? rows.length : 0;
|
|
61
|
+
if (rowIndex < 0 || rowIndex >= rowCount) return;
|
|
62
|
+
switch(data.action){
|
|
63
|
+
case 'move-up':
|
|
64
|
+
if (rowIndex === 0) return;
|
|
65
|
+
commit(()=>dispatch({
|
|
66
|
+
type: 'MOVE_ROW',
|
|
67
|
+
path: parentPath,
|
|
68
|
+
moveFromIndex: rowIndex,
|
|
69
|
+
moveToIndex: rowIndex - 1
|
|
70
|
+
}));
|
|
71
|
+
select(`${parentPath}.${rowIndex - 1}`);
|
|
72
|
+
break;
|
|
73
|
+
case 'move-down':
|
|
74
|
+
if (rowIndex >= rowCount - 1) return;
|
|
75
|
+
commit(()=>dispatch({
|
|
76
|
+
type: 'MOVE_ROW',
|
|
77
|
+
path: parentPath,
|
|
78
|
+
moveFromIndex: rowIndex,
|
|
79
|
+
moveToIndex: rowIndex + 1
|
|
80
|
+
}));
|
|
81
|
+
select(`${parentPath}.${rowIndex + 1}`);
|
|
82
|
+
break;
|
|
83
|
+
case 'duplicate':
|
|
84
|
+
commit(()=>dispatch({
|
|
85
|
+
type: 'DUPLICATE_ROW',
|
|
86
|
+
path: parentPath,
|
|
87
|
+
rowIndex
|
|
88
|
+
}));
|
|
89
|
+
select(`${parentPath}.${rowIndex + 1}`);
|
|
90
|
+
break;
|
|
91
|
+
case 'delete':
|
|
92
|
+
commit(()=>dispatch({
|
|
93
|
+
type: 'REMOVE_ROW',
|
|
94
|
+
path: parentPath,
|
|
95
|
+
rowIndex
|
|
96
|
+
}));
|
|
97
|
+
select(null);
|
|
98
|
+
break;
|
|
99
|
+
}
|
|
100
|
+
}), // eslint-disable-next-line react-hooks/exhaustive-deps -- refs are stable
|
|
101
|
+
[]);
|
|
102
|
+
return {
|
|
103
|
+
addBelowRequestId
|
|
104
|
+
};
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
//# sourceMappingURL=useBlockActionMessages.js.map
|