cms-renderer 0.6.2 → 0.6.4
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/lib/block-renderer.js +2 -1
- package/dist/lib/block-renderer.js.map +1 -1
- package/dist/lib/block-toolbar.d.ts +4 -4
- package/dist/lib/block-toolbar.js +62 -102
- package/dist/lib/block-toolbar.js.map +1 -1
- package/dist/lib/client-editable-block.js +157 -118
- package/dist/lib/client-editable-block.js.map +1 -1
- package/dist/lib/custom-schemas.js.map +1 -1
- package/dist/lib/renderer.js +6 -3
- package/dist/lib/renderer.js.map +1 -1
- package/dist/lib/schema.d.ts +8 -0
- package/dist/lib/schema.js +8 -2
- package/dist/lib/schema.js.map +1 -1
- package/dist/lib/types.d.ts +2 -0
- package/package.json +1 -1
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"use client";
|
|
3
3
|
|
|
4
4
|
// lib/client-editable-block.tsx
|
|
5
|
-
import { useCallback, useLayoutEffect as
|
|
5
|
+
import { useCallback, useEffect, useLayoutEffect as useLayoutEffect2, useRef as useRef2, useState } from "react";
|
|
6
6
|
import { createPortal } from "react-dom";
|
|
7
7
|
|
|
8
8
|
// lib/block-outline.tsx
|
|
@@ -45,126 +45,99 @@ function BlockOutline({ blockRect }) {
|
|
|
45
45
|
|
|
46
46
|
// lib/block-toolbar.tsx
|
|
47
47
|
import { ChevronDown, ChevronUp, Plus, Trash2 } from "lucide-react";
|
|
48
|
-
import {
|
|
48
|
+
import { forwardRef } from "react";
|
|
49
49
|
import { jsx as jsx2, jsxs } from "react/jsx-runtime";
|
|
50
|
-
|
|
51
|
-
const toolbarRef = useRef2(null);
|
|
52
|
-
useLayoutEffect2(() => {
|
|
53
|
-
const el = toolbarRef.current;
|
|
54
|
-
if (!el) return;
|
|
55
|
-
const vw = window.innerWidth;
|
|
56
|
-
const vh = window.innerHeight;
|
|
57
|
-
const { width: tw, height: th } = el.getBoundingClientRect();
|
|
58
|
-
const b = blockRect;
|
|
59
|
-
const candidates = [
|
|
60
|
-
{ top: b.bottom + 8, left: b.left + b.width / 2 - tw / 2 },
|
|
61
|
-
// below, centered
|
|
62
|
-
{ top: b.top - th - 8, left: b.left + b.width / 2 - tw / 2 },
|
|
63
|
-
// above, centered
|
|
64
|
-
{ top: b.bottom + 8, left: b.right - tw },
|
|
65
|
-
// below, right-aligned
|
|
66
|
-
{ top: b.bottom + 8, left: b.left },
|
|
67
|
-
// below, left-aligned
|
|
68
|
-
{ top: b.top - th - 8, left: b.right - tw }
|
|
69
|
-
// above, right-aligned
|
|
70
|
-
];
|
|
71
|
-
let chosen = candidates[0] ?? { top: b.bottom + 8, left: b.left + b.width / 2 - tw / 2 };
|
|
72
|
-
for (const c of candidates) {
|
|
73
|
-
if (c.top >= 0 && c.left >= 0 && c.top + th <= vh && c.left + tw <= vw) {
|
|
74
|
-
chosen = c;
|
|
75
|
-
break;
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
el.style.top = `${Math.min(Math.max(0, chosen.top), vh - th)}px`;
|
|
79
|
-
el.style.left = `${Math.min(Math.max(0, chosen.left), vw - tw)}px`;
|
|
80
|
-
el.style.visibility = "visible";
|
|
81
|
-
}, [blockRect]);
|
|
50
|
+
var BlockToolbar = forwardRef(function BlockToolbar2({ blockId }, ref) {
|
|
82
51
|
const handleAction = (action) => {
|
|
83
52
|
if (typeof window !== "undefined" && window.parent && window.parent !== window) {
|
|
84
53
|
window.parent.postMessage({ type: "cms-block-action", action, blockId }, "*");
|
|
85
54
|
}
|
|
86
55
|
};
|
|
87
|
-
return /* @__PURE__ */ jsxs(
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
handleAction("delete");
|
|
148
|
-
},
|
|
149
|
-
children: /* @__PURE__ */ jsx2(Trash2, {})
|
|
150
|
-
}
|
|
151
|
-
)
|
|
152
|
-
]
|
|
153
|
-
}
|
|
154
|
-
);
|
|
155
|
-
}
|
|
56
|
+
return /* @__PURE__ */ jsxs("div", { ref, className: "cms-block-toolbar", "data-cms-toolbar": "true", children: [
|
|
57
|
+
/* @__PURE__ */ jsx2(
|
|
58
|
+
"button",
|
|
59
|
+
{
|
|
60
|
+
type: "button",
|
|
61
|
+
title: "Move up",
|
|
62
|
+
"aria-label": "Move up",
|
|
63
|
+
"data-action": "move-up",
|
|
64
|
+
onClick: (e) => {
|
|
65
|
+
e.stopPropagation();
|
|
66
|
+
handleAction("move-up");
|
|
67
|
+
},
|
|
68
|
+
children: /* @__PURE__ */ jsx2(ChevronUp, {})
|
|
69
|
+
}
|
|
70
|
+
),
|
|
71
|
+
/* @__PURE__ */ jsx2(
|
|
72
|
+
"button",
|
|
73
|
+
{
|
|
74
|
+
type: "button",
|
|
75
|
+
title: "Move down",
|
|
76
|
+
"aria-label": "Move down",
|
|
77
|
+
"data-action": "move-down",
|
|
78
|
+
onClick: (e) => {
|
|
79
|
+
e.stopPropagation();
|
|
80
|
+
handleAction("move-down");
|
|
81
|
+
},
|
|
82
|
+
children: /* @__PURE__ */ jsx2(ChevronDown, {})
|
|
83
|
+
}
|
|
84
|
+
),
|
|
85
|
+
/* @__PURE__ */ jsx2(
|
|
86
|
+
"button",
|
|
87
|
+
{
|
|
88
|
+
type: "button",
|
|
89
|
+
title: "Add block",
|
|
90
|
+
"aria-label": "Add block",
|
|
91
|
+
"data-action": "add-block",
|
|
92
|
+
onClick: (e) => {
|
|
93
|
+
e.stopPropagation();
|
|
94
|
+
handleAction("add-block");
|
|
95
|
+
},
|
|
96
|
+
children: /* @__PURE__ */ jsx2(Plus, {})
|
|
97
|
+
}
|
|
98
|
+
),
|
|
99
|
+
/* @__PURE__ */ jsx2(
|
|
100
|
+
"button",
|
|
101
|
+
{
|
|
102
|
+
type: "button",
|
|
103
|
+
className: "delete",
|
|
104
|
+
title: "Delete block",
|
|
105
|
+
"aria-label": "Delete block",
|
|
106
|
+
"data-action": "delete",
|
|
107
|
+
onClick: (e) => {
|
|
108
|
+
e.stopPropagation();
|
|
109
|
+
handleAction("delete");
|
|
110
|
+
},
|
|
111
|
+
children: /* @__PURE__ */ jsx2(Trash2, {})
|
|
112
|
+
}
|
|
113
|
+
)
|
|
114
|
+
] });
|
|
115
|
+
});
|
|
156
116
|
|
|
157
117
|
// lib/client-editable-block.tsx
|
|
158
118
|
import { Fragment, jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
159
119
|
var CMS_STYLES = `
|
|
160
120
|
[data-cms-editable] { cursor: pointer; border-radius: 2px; }
|
|
161
121
|
[data-cms-editable]:hover { outline: 2px solid #3b82f6; outline-offset: 2px; }
|
|
122
|
+
.cms-block-cursor {
|
|
123
|
+
position: fixed; width: 22px; height: 22px;
|
|
124
|
+
background: radial-gradient(circle, #fff 5px, #000 5px);
|
|
125
|
+
border-radius: 50%;
|
|
126
|
+
pointer-events: none; z-index: 99999;
|
|
127
|
+
transform: translate(-50%, -50%);
|
|
128
|
+
opacity: 0; transition: opacity 0.1s ease;
|
|
129
|
+
}
|
|
130
|
+
.cms-block-cursor.cms-cursor-visible { opacity: 1; }
|
|
162
131
|
.cms-block-toolbar {
|
|
163
132
|
position: fixed;
|
|
164
133
|
display: flex; gap: 4px; background: #1f2937; border-radius: 6px; padding: 4px;
|
|
165
|
-
box-shadow: 0
|
|
166
|
-
|
|
167
|
-
|
|
134
|
+
box-shadow: 0 4px 12px rgba(0,0,0,0.25);
|
|
135
|
+
z-index: 99999; pointer-events: none;
|
|
136
|
+
opacity: 0; transform: scale(0.9) translateY(4px);
|
|
137
|
+
transition: opacity 0.15s ease, transform 0.15s ease;
|
|
138
|
+
}
|
|
139
|
+
.cms-block-toolbar.cms-toolbar-visible {
|
|
140
|
+
opacity: 1; transform: scale(1) translateY(0); pointer-events: auto;
|
|
168
141
|
}
|
|
169
142
|
.cms-block-toolbar button {
|
|
170
143
|
display: flex; align-items: center; justify-content: center;
|
|
@@ -221,9 +194,13 @@ function ClientEditableBlock({
|
|
|
221
194
|
contentEntries,
|
|
222
195
|
children
|
|
223
196
|
}) {
|
|
224
|
-
const sentinelRef =
|
|
225
|
-
const observerRef =
|
|
226
|
-
const isInjectingRef =
|
|
197
|
+
const sentinelRef = useRef2(null);
|
|
198
|
+
const observerRef = useRef2(null);
|
|
199
|
+
const isInjectingRef = useRef2(false);
|
|
200
|
+
const cursorElRef = useRef2(null);
|
|
201
|
+
const toolbarElRef = useRef2(null);
|
|
202
|
+
const toolbarVisibleRef = useRef2(false);
|
|
203
|
+
const [mounted, setMounted] = useState(false);
|
|
227
204
|
const [outlineRect, setOutlineRect] = useState(null);
|
|
228
205
|
const getBlockRoot = useCallback(() => {
|
|
229
206
|
return sentinelRef.current?.nextElementSibling ?? null;
|
|
@@ -286,17 +263,74 @@ function ClientEditableBlock({
|
|
|
286
263
|
}
|
|
287
264
|
}
|
|
288
265
|
}, [blockId, blockType, contentEntries, getBlockRoot]);
|
|
289
|
-
|
|
266
|
+
useEffect(() => {
|
|
267
|
+
setMounted(true);
|
|
268
|
+
}, []);
|
|
269
|
+
useLayoutEffect2(() => {
|
|
290
270
|
ensureCmsGlobals();
|
|
291
271
|
const blockRoot = getBlockRoot();
|
|
292
272
|
if (!blockRoot) return;
|
|
293
273
|
blockRoot.setAttribute("data-cms-block", "");
|
|
294
274
|
blockRoot.setAttribute("data-block-id", blockId);
|
|
295
275
|
blockRoot.setAttribute("data-block-type", blockType);
|
|
296
|
-
const
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
276
|
+
const positionToolbar = (x, y) => {
|
|
277
|
+
const el = toolbarElRef.current;
|
|
278
|
+
if (!el) return;
|
|
279
|
+
const { width: tw, height: th } = el.getBoundingClientRect();
|
|
280
|
+
const top = Math.max(4, y - th - 12);
|
|
281
|
+
const left = Math.max(4, Math.min(x - tw / 2, window.innerWidth - tw - 4));
|
|
282
|
+
el.style.transition = "opacity 0.15s ease, transform 0.15s ease";
|
|
283
|
+
el.style.top = `${top}px`;
|
|
284
|
+
el.style.left = `${left}px`;
|
|
285
|
+
};
|
|
286
|
+
const showCursor = (e) => {
|
|
287
|
+
if (toolbarVisibleRef.current) return;
|
|
288
|
+
setOutlineRect(blockRoot.getBoundingClientRect());
|
|
289
|
+
const el = cursorElRef.current;
|
|
290
|
+
if (!el) return;
|
|
291
|
+
el.style.top = `${e.clientY}px`;
|
|
292
|
+
el.style.left = `${e.clientX}px`;
|
|
293
|
+
el.classList.add("cms-cursor-visible");
|
|
294
|
+
};
|
|
295
|
+
const moveCursor = (e) => {
|
|
296
|
+
if (toolbarVisibleRef.current) return;
|
|
297
|
+
const el = cursorElRef.current;
|
|
298
|
+
if (!el) return;
|
|
299
|
+
el.style.top = `${e.clientY}px`;
|
|
300
|
+
el.style.left = `${e.clientX}px`;
|
|
301
|
+
};
|
|
302
|
+
const hideCursor = () => {
|
|
303
|
+
cursorElRef.current?.classList.remove("cms-cursor-visible");
|
|
304
|
+
setOutlineRect(null);
|
|
305
|
+
};
|
|
306
|
+
const handleContextMenu = (e) => {
|
|
307
|
+
if (e.target.closest("[data-cms-toolbar]")) return;
|
|
308
|
+
if (toolbarVisibleRef.current) return;
|
|
309
|
+
e.preventDefault();
|
|
310
|
+
hideCursor();
|
|
311
|
+
positionToolbar(e.clientX, e.clientY);
|
|
312
|
+
toolbarVisibleRef.current = true;
|
|
313
|
+
toolbarElRef.current?.classList.add("cms-toolbar-visible");
|
|
314
|
+
};
|
|
315
|
+
const handleClickOutside = (e) => {
|
|
316
|
+
if (!toolbarVisibleRef.current) return;
|
|
317
|
+
const target = e.target;
|
|
318
|
+
if (target.closest("[data-cms-toolbar]")) return;
|
|
319
|
+
if (blockRoot.contains(target)) return;
|
|
320
|
+
toolbarElRef.current?.classList.remove("cms-toolbar-visible");
|
|
321
|
+
toolbarVisibleRef.current = false;
|
|
322
|
+
};
|
|
323
|
+
const hideOnScroll = () => {
|
|
324
|
+
hideCursor();
|
|
325
|
+
toolbarElRef.current?.classList.remove("cms-toolbar-visible");
|
|
326
|
+
toolbarVisibleRef.current = false;
|
|
327
|
+
};
|
|
328
|
+
blockRoot.addEventListener("mouseenter", showCursor);
|
|
329
|
+
blockRoot.addEventListener("mousemove", moveCursor);
|
|
330
|
+
blockRoot.addEventListener("mouseleave", hideCursor);
|
|
331
|
+
blockRoot.addEventListener("contextmenu", handleContextMenu);
|
|
332
|
+
document.addEventListener("click", handleClickOutside);
|
|
333
|
+
window.addEventListener("scroll", hideOnScroll, { passive: true, capture: true });
|
|
300
334
|
injectSpans();
|
|
301
335
|
const observer = new MutationObserver(injectSpans);
|
|
302
336
|
observerRef.current = observer;
|
|
@@ -308,15 +342,20 @@ function ClientEditableBlock({
|
|
|
308
342
|
return () => {
|
|
309
343
|
observer.disconnect();
|
|
310
344
|
observerRef.current = null;
|
|
311
|
-
blockRoot.removeEventListener("mouseenter",
|
|
312
|
-
blockRoot.removeEventListener("
|
|
345
|
+
blockRoot.removeEventListener("mouseenter", showCursor);
|
|
346
|
+
blockRoot.removeEventListener("mousemove", moveCursor);
|
|
347
|
+
blockRoot.removeEventListener("mouseleave", hideCursor);
|
|
348
|
+
blockRoot.removeEventListener("contextmenu", handleContextMenu);
|
|
349
|
+
document.removeEventListener("click", handleClickOutside);
|
|
350
|
+
window.removeEventListener("scroll", hideOnScroll, { capture: true });
|
|
313
351
|
};
|
|
314
352
|
}, [injectSpans, blockId, blockType, getBlockRoot]);
|
|
315
353
|
return /* @__PURE__ */ jsxs2(Fragment, { children: [
|
|
316
354
|
/* @__PURE__ */ jsx3("span", { ref: sentinelRef, style: { display: "none" }, "aria-hidden": true }),
|
|
317
355
|
children,
|
|
318
|
-
outlineRect && createPortal(/* @__PURE__ */ jsx3(BlockOutline, { blockRect: outlineRect }), document.body),
|
|
319
|
-
|
|
356
|
+
mounted && outlineRect && createPortal(/* @__PURE__ */ jsx3(BlockOutline, { blockRect: outlineRect }), document.body),
|
|
357
|
+
mounted && createPortal(/* @__PURE__ */ jsx3("div", { ref: cursorElRef, className: "cms-block-cursor" }), document.body),
|
|
358
|
+
mounted && createPortal(/* @__PURE__ */ jsx3(BlockToolbar, { ref: toolbarElRef, blockId }), document.body)
|
|
320
359
|
] });
|
|
321
360
|
}
|
|
322
361
|
export {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../lib/client-editable-block.tsx","../../lib/block-outline.tsx","../../lib/block-toolbar.tsx"],"sourcesContent":["'use client';\n\nimport { useCallback, useLayoutEffect, useRef, useState } from 'react';\nimport { createPortal } from 'react-dom';\nimport { BlockOutline } from './block-outline';\nimport { BlockToolbar } from './block-toolbar';\n\n// ---------------------------------------------------------------------------\n// Global styles + click-routing — injected once into <head> on first mount.\n// We do this programmatically because Next.js RSC marks body-level <style> and\n// <script> tags as inactive (they appear greyed-out in DevTools and are not\n// applied by the browser).\n// ---------------------------------------------------------------------------\n\nconst CMS_STYLES = `\n [data-cms-editable] { cursor: pointer; border-radius: 2px; }\n [data-cms-editable]:hover { outline: 2px solid #3b82f6; outline-offset: 2px; }\n .cms-block-toolbar {\n position: fixed;\n display: flex; gap: 4px; background: #1f2937; border-radius: 6px; padding: 4px;\n box-shadow: 0 2px 8px rgba(0,0,0,0.15);\n transition: opacity 0.15s ease; z-index: 99999;\n pointer-events: auto;\n }\n .cms-block-toolbar button {\n display: flex; align-items: center; justify-content: center;\n width: 28px; height: 28px; border: none; background: transparent;\n color: #9ca3af; border-radius: 4px; cursor: pointer;\n transition: background 0.15s ease, color 0.15s ease;\n }\n .cms-block-toolbar button:hover { background: #374151; color: #fff; }\n .cms-block-toolbar button.delete:hover { background: #dc2626; color: #fff; }\n .cms-block-toolbar button:disabled { opacity: 0.4; cursor: not-allowed; }\n .cms-block-toolbar button:disabled:hover { background: transparent; color: #9ca3af; }\n .cms-block-toolbar svg { width: 16px; height: 16px; }\n`;\n\nlet cmsGlobalInjected = false;\n\nfunction ensureCmsGlobals() {\n if (cmsGlobalInjected) return;\n cmsGlobalInjected = true;\n\n const style = document.createElement('style');\n style.setAttribute('data-cms', '');\n style.textContent = CMS_STYLES;\n document.head.appendChild(style);\n\n if (!(window as Window & { __cmsEditableInitialized?: boolean }).__cmsEditableInitialized) {\n (window as Window & { __cmsEditableInitialized?: boolean }).__cmsEditableInitialized = true;\n document.addEventListener('click', (e) => {\n const target = e.target as Element;\n if (target.closest('.cms-block-toolbar')) return;\n\n const editable = target.closest('[data-cms-editable]');\n if (editable) {\n const msg = {\n type: 'cms-editable-click',\n blockId: editable.getAttribute('data-block-id'),\n blockType: editable.getAttribute('data-block-type'),\n contentPath: editable.getAttribute('data-content-path'),\n };\n if (window.parent && window.parent !== window) window.parent.postMessage(msg, '*');\n return;\n }\n\n const block = target.closest('[data-cms-block]');\n if (block) {\n const msg = {\n type: 'cms-editable-click',\n blockId: block.getAttribute('data-block-id'),\n blockType: block.getAttribute('data-block-type'),\n contentPath: null,\n };\n if (window.parent && window.parent !== window) window.parent.postMessage(msg, '*');\n }\n });\n }\n}\n\ninterface ContentEntry {\n v: string;\n p: string;\n}\n\ninterface ClientEditableBlockProps {\n blockId: string;\n blockType: string;\n contentEntries: ContentEntry[];\n children: React.ReactNode;\n}\n\n/**\n * Client-side block editable wrapper.\n *\n * Renders a hidden sentinel span followed by the block's children (no wrapper div).\n * On mount, finds the component's root element via nextElementSibling and:\n * - Stamps data-cms-block, data-block-id, data-block-type directly on it\n * - Injects data-cms-editable spans around matching text nodes\n * - Portals the BlockToolbar into document.body with position:fixed so it is\n * never clipped by overflow:hidden or stacking contexts on the block itself\n *\n * Uses a MutationObserver to re-inject spans after every DOM mutation\n * (e.g. animated state changes that swap visible elements).\n */\nexport function ClientEditableBlock({\n blockId,\n blockType,\n contentEntries,\n children,\n}: ClientEditableBlockProps) {\n const sentinelRef = useRef<HTMLSpanElement>(null);\n const observerRef = useRef<MutationObserver | null>(null);\n const isInjectingRef = useRef(false);\n const [outlineRect, setOutlineRect] = useState<DOMRect | null>(null);\n\n const getBlockRoot = useCallback((): HTMLElement | null => {\n return (sentinelRef.current?.nextElementSibling as HTMLElement) ?? null;\n }, []);\n\n const injectSpans = useCallback(() => {\n if (isInjectingRef.current) return;\n const blockRoot = getBlockRoot();\n if (!blockRoot) return;\n\n isInjectingRef.current = true;\n // Disconnect observer while we mutate the DOM to avoid re-entrant calls.\n observerRef.current?.disconnect();\n\n try {\n // Remove previously injected text-level spans, restoring the original text nodes.\n const existing = Array.from(\n blockRoot.querySelectorAll<HTMLElement>('span[data-cms-editable][data-content-path]')\n );\n for (const span of existing) {\n const parent = span.parentNode;\n if (!parent) continue;\n while (span.firstChild) parent.insertBefore(span.firstChild, span);\n parent.removeChild(span);\n }\n\n // Walk all visible text nodes and wrap the ones that match content values.\n const used = new Set<string>();\n const walker = document.createTreeWalker(blockRoot, NodeFilter.SHOW_TEXT, null);\n const textNodes: Text[] = [];\n let n = walker.nextNode();\n while (n !== null) {\n const textNode = n as Text;\n if (\n textNode.parentElement?.closest('.cms-block-toolbar') == null &&\n textNode.nodeValue?.trim()\n ) {\n textNodes.push(textNode);\n }\n n = walker.nextNode();\n }\n\n for (const textNode of textNodes) {\n // Never inject spans inside SVG — <span> is not valid SVG content\n if (textNode.parentElement?.closest('svg') != null) continue;\n\n const text = textNode.nodeValue;\n if (!text) continue;\n for (const entry of contentEntries) {\n if (used.has(entry.p)) continue;\n if (text.indexOf(entry.v) !== -1 && text.trim() === entry.v.trim()) {\n used.add(entry.p);\n const span = document.createElement('span');\n span.setAttribute('data-cms-editable', '');\n span.setAttribute('data-block-id', blockId);\n span.setAttribute('data-block-type', blockType);\n span.setAttribute('data-content-path', entry.p);\n textNode.parentNode?.insertBefore(span, textNode);\n span.appendChild(textNode);\n break;\n }\n }\n }\n } finally {\n isInjectingRef.current = false;\n // Reconnect after our mutations are done.\n const root = getBlockRoot();\n if (root && observerRef.current) {\n observerRef.current.observe(root, {\n childList: true,\n subtree: true,\n characterData: true,\n });\n }\n }\n }, [blockId, blockType, contentEntries, getBlockRoot]);\n\n useLayoutEffect(() => {\n ensureCmsGlobals();\n\n const blockRoot = getBlockRoot();\n if (!blockRoot) return;\n\n // Stamp CMS attributes directly on the component's root element.\n blockRoot.setAttribute('data-cms-block', '');\n blockRoot.setAttribute('data-block-id', blockId);\n blockRoot.setAttribute('data-block-type', blockType);\n\n // Toolbar: portal to document.body with position:fixed so it escapes\n // any overflow:hidden or stacking context on the block or its ancestors.\n const showToolbar = () => setOutlineRect(blockRoot.getBoundingClientRect());\n const hideToolbar = () => setOutlineRect(null);\n\n blockRoot.addEventListener('mouseenter', showToolbar);\n blockRoot.addEventListener('mouseleave', hideToolbar);\n\n // Initial span injection after React's first commit.\n injectSpans();\n\n // Re-inject whenever the child component mutates the DOM.\n const observer = new MutationObserver(injectSpans);\n observerRef.current = observer;\n observer.observe(blockRoot, {\n childList: true,\n subtree: true,\n characterData: true,\n });\n\n return () => {\n observer.disconnect();\n observerRef.current = null;\n blockRoot.removeEventListener('mouseenter', showToolbar);\n blockRoot.removeEventListener('mouseleave', hideToolbar);\n };\n }, [injectSpans, blockId, blockType, getBlockRoot]);\n\n return (\n <>\n <span ref={sentinelRef} style={{ display: 'none' }} aria-hidden />\n {children}\n {outlineRect && createPortal(<BlockOutline blockRect={outlineRect} />, document.body)}\n {outlineRect &&\n createPortal(<BlockToolbar blockId={blockId} blockRect={outlineRect} />, document.body)}\n </>\n );\n}\n","'use client';\n\nimport { useLayoutEffect, useRef } from 'react';\n\n/**\n * Block Outline Component\n *\n * Renders a position:fixed border overlay around a hovered block.\n * Tries up to 3 offset values (4px → 0px → -2px inset) to keep every\n * edge of the border visible inside the viewport before giving up.\n */\nexport function BlockOutline({ blockRect }: { blockRect: DOMRect }) {\n const outlineRef = useRef<HTMLDivElement>(null);\n\n useLayoutEffect(() => {\n const el = outlineRef.current;\n if (!el) return;\n\n const vw = window.innerWidth;\n const vh = window.innerHeight;\n\n // Attempt progressively smaller offsets until the border is fully visible.\n // offset=4 → normal outline-offset feel\n // offset=0 → border flush with block edge\n // offset=-2 → border drawn inside the block (inset)\n const offsets = [4, 0, -2];\n\n for (const offset of offsets) {\n const pad = offset + 2; // 2px border + offset\n el.style.top = `${blockRect.top - pad}px`;\n el.style.left = `${blockRect.left - pad}px`;\n el.style.width = `${blockRect.width + pad * 2}px`;\n el.style.height = `${blockRect.height + pad * 2}px`;\n\n const rect = el.getBoundingClientRect();\n if (rect.top >= 0 && rect.left >= 0 && rect.right <= vw && rect.bottom <= vh) break;\n }\n }, [blockRect]);\n\n return (\n <div\n ref={outlineRef}\n data-cms-outline=\"true\"\n style={{\n position: 'fixed',\n border: '2px solid #3b82f6',\n borderRadius: '2px',\n pointerEvents: 'none',\n zIndex: 99998,\n boxSizing: 'border-box',\n }}\n />\n );\n}\n","'use client';\n\nimport { ChevronDown, ChevronUp, Plus, Trash2 } from 'lucide-react';\nimport { useLayoutEffect, useRef } from 'react';\n\n/**\n * Block Toolbar Component\n *\n * Provides move up/down and delete controls for blocks in edit mode.\n * This is a Client Component because it requires onClick handlers.\n */\n\nexport function BlockToolbar({ blockId, blockRect }: { blockId: string; blockRect: DOMRect }) {\n const toolbarRef = useRef<HTMLDivElement>(null);\n\n useLayoutEffect(() => {\n const el = toolbarRef.current;\n if (!el) return;\n\n const vw = window.innerWidth;\n const vh = window.innerHeight;\n const { width: tw, height: th } = el.getBoundingClientRect();\n const b = blockRect;\n\n // Up to 5 candidate placements tried in order; first one fully in-viewport wins.\n const candidates = [\n { top: b.bottom + 8, left: b.left + b.width / 2 - tw / 2 }, // below, centered\n { top: b.top - th - 8, left: b.left + b.width / 2 - tw / 2 }, // above, centered\n { top: b.bottom + 8, left: b.right - tw }, // below, right-aligned\n { top: b.bottom + 8, left: b.left }, // below, left-aligned\n { top: b.top - th - 8, left: b.right - tw }, // above, right-aligned\n ];\n\n let chosen = candidates[0] ?? { top: b.bottom + 8, left: b.left + b.width / 2 - tw / 2 };\n for (const c of candidates) {\n if (c.top >= 0 && c.left >= 0 && c.top + th <= vh && c.left + tw <= vw) {\n chosen = c;\n break;\n }\n }\n\n el.style.top = `${Math.min(Math.max(0, chosen.top), vh - th)}px`;\n el.style.left = `${Math.min(Math.max(0, chosen.left), vw - tw)}px`;\n el.style.visibility = 'visible';\n }, [blockRect]);\n\n const handleAction = (action: string) => {\n if (typeof window !== 'undefined' && window.parent && window.parent !== window) {\n window.parent.postMessage({ type: 'cms-block-action', action, blockId }, '*');\n }\n };\n\n return (\n <div\n ref={toolbarRef}\n className=\"cms-block-toolbar\"\n data-cms-toolbar=\"true\"\n style={{ visibility: 'hidden' }}\n >\n <button\n type=\"button\"\n title=\"Move up\"\n aria-label=\"Move up\"\n data-action=\"move-up\"\n onClick={(e) => {\n e.stopPropagation();\n handleAction('move-up');\n }}\n >\n <ChevronUp />\n </button>\n <button\n type=\"button\"\n title=\"Move down\"\n aria-label=\"Move down\"\n data-action=\"move-down\"\n onClick={(e) => {\n e.stopPropagation();\n handleAction('move-down');\n }}\n >\n <ChevronDown />\n </button>\n <button\n type=\"button\"\n title=\"Add block\"\n aria-label=\"Add block\"\n data-action=\"add-block\"\n onClick={(e) => {\n e.stopPropagation();\n handleAction('add-block');\n }}\n >\n <Plus />\n </button>\n <button\n type=\"button\"\n className=\"delete\"\n title=\"Delete block\"\n aria-label=\"Delete block\"\n data-action=\"delete\"\n onClick={(e) => {\n e.stopPropagation();\n handleAction('delete');\n }}\n >\n <Trash2 />\n </button>\n </div>\n );\n}\n"],"mappings":"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","names":["useLayoutEffect","useRef","useLayoutEffect","useRef","jsx","jsx","jsxs","useRef","useLayoutEffect"]}
|
|
1
|
+
{"version":3,"sources":["../../lib/client-editable-block.tsx","../../lib/block-outline.tsx","../../lib/block-toolbar.tsx"],"sourcesContent":["'use client';\n\nimport { useCallback, useEffect, useLayoutEffect, useRef, useState } from 'react';\nimport { createPortal } from 'react-dom';\nimport { BlockOutline } from './block-outline';\nimport { BlockToolbar } from './block-toolbar';\n\n// ---------------------------------------------------------------------------\n// Global styles + click-routing — injected once into <head> on first mount.\n// We do this programmatically because Next.js RSC marks body-level <style> and\n// <script> tags as inactive (they appear greyed-out in DevTools and are not\n// applied by the browser).\n// ---------------------------------------------------------------------------\n\nconst CMS_STYLES = `\n [data-cms-editable] { cursor: pointer; border-radius: 2px; }\n [data-cms-editable]:hover { outline: 2px solid #3b82f6; outline-offset: 2px; }\n .cms-block-cursor {\n position: fixed; width: 22px; height: 22px;\n background: radial-gradient(circle, #fff 5px, #000 5px);\n border-radius: 50%;\n pointer-events: none; z-index: 99999;\n transform: translate(-50%, -50%);\n opacity: 0; transition: opacity 0.1s ease;\n }\n .cms-block-cursor.cms-cursor-visible { opacity: 1; }\n .cms-block-toolbar {\n position: fixed;\n display: flex; gap: 4px; background: #1f2937; border-radius: 6px; padding: 4px;\n box-shadow: 0 4px 12px rgba(0,0,0,0.25);\n z-index: 99999; pointer-events: none;\n opacity: 0; transform: scale(0.9) translateY(4px);\n transition: opacity 0.15s ease, transform 0.15s ease;\n }\n .cms-block-toolbar.cms-toolbar-visible {\n opacity: 1; transform: scale(1) translateY(0); pointer-events: auto;\n }\n .cms-block-toolbar button {\n display: flex; align-items: center; justify-content: center;\n width: 28px; height: 28px; border: none; background: transparent;\n color: #9ca3af; border-radius: 4px; cursor: pointer;\n transition: background 0.15s ease, color 0.15s ease;\n }\n .cms-block-toolbar button:hover { background: #374151; color: #fff; }\n .cms-block-toolbar button.delete:hover { background: #dc2626; color: #fff; }\n .cms-block-toolbar button:disabled { opacity: 0.4; cursor: not-allowed; }\n .cms-block-toolbar button:disabled:hover { background: transparent; color: #9ca3af; }\n .cms-block-toolbar svg { width: 16px; height: 16px; }\n`;\n\nlet cmsGlobalInjected = false;\n\nfunction ensureCmsGlobals() {\n if (cmsGlobalInjected) return;\n cmsGlobalInjected = true;\n\n const style = document.createElement('style');\n style.setAttribute('data-cms', '');\n style.textContent = CMS_STYLES;\n document.head.appendChild(style);\n\n if (!(window as Window & { __cmsEditableInitialized?: boolean }).__cmsEditableInitialized) {\n (window as Window & { __cmsEditableInitialized?: boolean }).__cmsEditableInitialized = true;\n document.addEventListener('click', (e) => {\n const target = e.target as Element;\n if (target.closest('.cms-block-toolbar')) return;\n\n const editable = target.closest('[data-cms-editable]');\n if (editable) {\n const msg = {\n type: 'cms-editable-click',\n blockId: editable.getAttribute('data-block-id'),\n blockType: editable.getAttribute('data-block-type'),\n contentPath: editable.getAttribute('data-content-path'),\n };\n if (window.parent && window.parent !== window) window.parent.postMessage(msg, '*');\n return;\n }\n\n const block = target.closest('[data-cms-block]');\n if (block) {\n const msg = {\n type: 'cms-editable-click',\n blockId: block.getAttribute('data-block-id'),\n blockType: block.getAttribute('data-block-type'),\n contentPath: null,\n };\n if (window.parent && window.parent !== window) window.parent.postMessage(msg, '*');\n }\n });\n }\n}\n\ninterface ContentEntry {\n v: string;\n p: string;\n}\n\ninterface ClientEditableBlockProps {\n blockId: string;\n blockType: string;\n contentEntries: ContentEntry[];\n children: React.ReactNode;\n}\n\n/**\n * Client-side block editable wrapper.\n *\n * Renders a hidden sentinel span followed by the block's children (no wrapper div).\n * On mount, finds the component's root element via nextElementSibling and:\n * - Stamps data-cms-block, data-block-id, data-block-type directly on it\n * - Injects data-cms-editable spans around matching text nodes\n * - Portals the BlockToolbar into document.body with position:fixed so it is\n * never clipped by overflow:hidden or stacking contexts on the block itself\n *\n * Uses a MutationObserver to re-inject spans after every DOM mutation\n * (e.g. animated state changes that swap visible elements).\n */\nexport function ClientEditableBlock({\n blockId,\n blockType,\n contentEntries,\n children,\n}: ClientEditableBlockProps) {\n const sentinelRef = useRef<HTMLSpanElement>(null);\n const observerRef = useRef<MutationObserver | null>(null);\n const isInjectingRef = useRef(false);\n const cursorElRef = useRef<HTMLDivElement>(null);\n const toolbarElRef = useRef<HTMLDivElement>(null);\n const toolbarVisibleRef = useRef(false);\n const [mounted, setMounted] = useState(false);\n const [outlineRect, setOutlineRect] = useState<DOMRect | null>(null);\n\n const getBlockRoot = useCallback((): HTMLElement | null => {\n return (sentinelRef.current?.nextElementSibling as HTMLElement) ?? null;\n }, []);\n\n const injectSpans = useCallback(() => {\n if (isInjectingRef.current) return;\n const blockRoot = getBlockRoot();\n if (!blockRoot) return;\n\n isInjectingRef.current = true;\n // Disconnect observer while we mutate the DOM to avoid re-entrant calls.\n observerRef.current?.disconnect();\n\n try {\n // Remove previously injected text-level spans, restoring the original text nodes.\n const existing = Array.from(\n blockRoot.querySelectorAll<HTMLElement>('span[data-cms-editable][data-content-path]')\n );\n for (const span of existing) {\n const parent = span.parentNode;\n if (!parent) continue;\n while (span.firstChild) parent.insertBefore(span.firstChild, span);\n parent.removeChild(span);\n }\n\n // Walk all visible text nodes and wrap the ones that match content values.\n const used = new Set<string>();\n const walker = document.createTreeWalker(blockRoot, NodeFilter.SHOW_TEXT, null);\n const textNodes: Text[] = [];\n let n = walker.nextNode();\n while (n !== null) {\n const textNode = n as Text;\n if (\n textNode.parentElement?.closest('.cms-block-toolbar') == null &&\n textNode.nodeValue?.trim()\n ) {\n textNodes.push(textNode);\n }\n n = walker.nextNode();\n }\n\n for (const textNode of textNodes) {\n // Never inject spans inside SVG — <span> is not valid SVG content\n if (textNode.parentElement?.closest('svg') != null) continue;\n\n const text = textNode.nodeValue;\n if (!text) continue;\n for (const entry of contentEntries) {\n if (used.has(entry.p)) continue;\n if (text.indexOf(entry.v) !== -1 && text.trim() === entry.v.trim()) {\n used.add(entry.p);\n const span = document.createElement('span');\n span.setAttribute('data-cms-editable', '');\n span.setAttribute('data-block-id', blockId);\n span.setAttribute('data-block-type', blockType);\n span.setAttribute('data-content-path', entry.p);\n textNode.parentNode?.insertBefore(span, textNode);\n span.appendChild(textNode);\n break;\n }\n }\n }\n } finally {\n isInjectingRef.current = false;\n // Reconnect after our mutations are done.\n const root = getBlockRoot();\n if (root && observerRef.current) {\n observerRef.current.observe(root, {\n childList: true,\n subtree: true,\n characterData: true,\n });\n }\n }\n }, [blockId, blockType, contentEntries, getBlockRoot]);\n\n useEffect(() => {\n setMounted(true);\n }, []);\n\n useLayoutEffect(() => {\n ensureCmsGlobals();\n\n const blockRoot = getBlockRoot();\n if (!blockRoot) return;\n\n // Stamp CMS attributes directly on the component's root element.\n blockRoot.setAttribute('data-cms-block', '');\n blockRoot.setAttribute('data-block-id', blockId);\n blockRoot.setAttribute('data-block-type', blockType);\n\n // Position toolbar at (x, y) without animating the placement itself —\n // suppress top/left transitions for the instant snap, then restore.\n const positionToolbar = (x: number, y: number) => {\n const el = toolbarElRef.current;\n if (!el) return;\n const { width: tw, height: th } = el.getBoundingClientRect();\n const top = Math.max(4, y - th - 12);\n const left = Math.max(4, Math.min(x - tw / 2, window.innerWidth - tw - 4));\n el.style.transition = 'opacity 0.15s ease, transform 0.15s ease';\n el.style.top = `${top}px`;\n el.style.left = `${left}px`;\n };\n\n // Cursor circle follows the mouse to indicate the block is interactive.\n const showCursor = (e: MouseEvent) => {\n if (toolbarVisibleRef.current) return;\n setOutlineRect(blockRoot.getBoundingClientRect());\n const el = cursorElRef.current;\n if (!el) return;\n el.style.top = `${e.clientY}px`;\n el.style.left = `${e.clientX}px`;\n el.classList.add('cms-cursor-visible');\n };\n const moveCursor = (e: MouseEvent) => {\n if (toolbarVisibleRef.current) return;\n const el = cursorElRef.current;\n if (!el) return;\n el.style.top = `${e.clientY}px`;\n el.style.left = `${e.clientX}px`;\n };\n const hideCursor = () => {\n cursorElRef.current?.classList.remove('cms-cursor-visible');\n setOutlineRect(null);\n };\n\n // On right-click: first time suppresses the browser context menu and shows\n // the CMS toolbar instead. If the toolbar is already visible the event is\n // not prevented so the browser's native context menu opens as a fallback.\n const handleContextMenu = (e: MouseEvent) => {\n if ((e.target as Element).closest('[data-cms-toolbar]')) return;\n if (toolbarVisibleRef.current) return; // fall through to browser context menu\n e.preventDefault();\n hideCursor();\n positionToolbar(e.clientX, e.clientY);\n toolbarVisibleRef.current = true;\n toolbarElRef.current?.classList.add('cms-toolbar-visible');\n };\n\n // Dismiss toolbar when clicking outside this block or its toolbar.\n const handleClickOutside = (e: MouseEvent) => {\n if (!toolbarVisibleRef.current) return;\n const target = e.target as Element;\n if (target.closest('[data-cms-toolbar]')) return;\n if (blockRoot.contains(target)) return;\n toolbarElRef.current?.classList.remove('cms-toolbar-visible');\n toolbarVisibleRef.current = false;\n };\n\n const hideOnScroll = () => {\n hideCursor();\n toolbarElRef.current?.classList.remove('cms-toolbar-visible');\n toolbarVisibleRef.current = false;\n };\n\n blockRoot.addEventListener('mouseenter', showCursor);\n blockRoot.addEventListener('mousemove', moveCursor);\n blockRoot.addEventListener('mouseleave', hideCursor);\n blockRoot.addEventListener('contextmenu', handleContextMenu);\n document.addEventListener('click', handleClickOutside);\n window.addEventListener('scroll', hideOnScroll, { passive: true, capture: true });\n\n // Initial span injection after React's first commit.\n injectSpans();\n\n // Re-inject whenever the child component mutates the DOM.\n const observer = new MutationObserver(injectSpans);\n observerRef.current = observer;\n observer.observe(blockRoot, {\n childList: true,\n subtree: true,\n characterData: true,\n });\n\n return () => {\n observer.disconnect();\n observerRef.current = null;\n blockRoot.removeEventListener('mouseenter', showCursor);\n blockRoot.removeEventListener('mousemove', moveCursor);\n blockRoot.removeEventListener('mouseleave', hideCursor);\n blockRoot.removeEventListener('contextmenu', handleContextMenu);\n document.removeEventListener('click', handleClickOutside);\n window.removeEventListener('scroll', hideOnScroll, { capture: true });\n };\n }, [injectSpans, blockId, blockType, getBlockRoot]);\n\n return (\n <>\n <span ref={sentinelRef} style={{ display: 'none' }} aria-hidden />\n {children}\n {mounted &&\n outlineRect &&\n createPortal(<BlockOutline blockRect={outlineRect} />, document.body)}\n {mounted &&\n createPortal(<div ref={cursorElRef} className=\"cms-block-cursor\" />, document.body)}\n {mounted &&\n createPortal(<BlockToolbar ref={toolbarElRef} blockId={blockId} />, document.body)}\n </>\n );\n}\n","'use client';\n\nimport { useLayoutEffect, useRef } from 'react';\n\n/**\n * Block Outline Component\n *\n * Renders a position:fixed border overlay around a hovered block.\n * Tries up to 3 offset values (4px → 0px → -2px inset) to keep every\n * edge of the border visible inside the viewport before giving up.\n */\nexport function BlockOutline({ blockRect }: { blockRect: DOMRect }) {\n const outlineRef = useRef<HTMLDivElement>(null);\n\n useLayoutEffect(() => {\n const el = outlineRef.current;\n if (!el) return;\n\n const vw = window.innerWidth;\n const vh = window.innerHeight;\n\n // Attempt progressively smaller offsets until the border is fully visible.\n // offset=4 → normal outline-offset feel\n // offset=0 → border flush with block edge\n // offset=-2 → border drawn inside the block (inset)\n const offsets = [4, 0, -2];\n\n for (const offset of offsets) {\n const pad = offset + 2; // 2px border + offset\n el.style.top = `${blockRect.top - pad}px`;\n el.style.left = `${blockRect.left - pad}px`;\n el.style.width = `${blockRect.width + pad * 2}px`;\n el.style.height = `${blockRect.height + pad * 2}px`;\n\n const rect = el.getBoundingClientRect();\n if (rect.top >= 0 && rect.left >= 0 && rect.right <= vw && rect.bottom <= vh) break;\n }\n }, [blockRect]);\n\n return (\n <div\n ref={outlineRef}\n data-cms-outline=\"true\"\n style={{\n position: 'fixed',\n border: '2px solid #3b82f6',\n borderRadius: '2px',\n pointerEvents: 'none',\n zIndex: 99998,\n boxSizing: 'border-box',\n }}\n />\n );\n}\n","'use client';\n\nimport { ChevronDown, ChevronUp, Plus, Trash2 } from 'lucide-react';\nimport { forwardRef } from 'react';\n\n/**\n * Block Toolbar Component\n *\n * Provides move up/down and delete controls for blocks in edit mode.\n * Position is managed imperatively by the parent via a forwarded ref —\n * the toolbar follows the mouse cursor and has no internal layout logic.\n */\n\nexport const BlockToolbar = forwardRef<HTMLDivElement, { blockId: string }>(function BlockToolbar(\n { blockId },\n ref\n) {\n const handleAction = (action: string) => {\n if (typeof window !== 'undefined' && window.parent && window.parent !== window) {\n window.parent.postMessage({ type: 'cms-block-action', action, blockId }, '*');\n }\n };\n\n return (\n <div ref={ref} className=\"cms-block-toolbar\" data-cms-toolbar=\"true\">\n <button\n type=\"button\"\n title=\"Move up\"\n aria-label=\"Move up\"\n data-action=\"move-up\"\n onClick={(e) => {\n e.stopPropagation();\n handleAction('move-up');\n }}\n >\n <ChevronUp />\n </button>\n <button\n type=\"button\"\n title=\"Move down\"\n aria-label=\"Move down\"\n data-action=\"move-down\"\n onClick={(e) => {\n e.stopPropagation();\n handleAction('move-down');\n }}\n >\n <ChevronDown />\n </button>\n <button\n type=\"button\"\n title=\"Add block\"\n aria-label=\"Add block\"\n data-action=\"add-block\"\n onClick={(e) => {\n e.stopPropagation();\n handleAction('add-block');\n }}\n >\n <Plus />\n </button>\n <button\n type=\"button\"\n className=\"delete\"\n title=\"Delete block\"\n aria-label=\"Delete block\"\n data-action=\"delete\"\n onClick={(e) => {\n e.stopPropagation();\n handleAction('delete');\n }}\n >\n <Trash2 />\n </button>\n </div>\n );\n});\n"],"mappings":"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","names":["useLayoutEffect","useRef","jsx","BlockToolbar","jsx","jsxs","useRef","useLayoutEffect"]}
|