cms-renderer 0.6.2 → 0.6.3

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.
@@ -4,11 +4,11 @@ import * as react from 'react';
4
4
  * Block Toolbar Component
5
5
  *
6
6
  * Provides move up/down and delete controls for blocks in edit mode.
7
- * This is a Client Component because it requires onClick handlers.
7
+ * Position is managed imperatively by the parent via a forwarded ref —
8
+ * the toolbar follows the mouse cursor and has no internal layout logic.
8
9
  */
9
- declare function BlockToolbar({ blockId, blockRect }: {
10
+ declare const BlockToolbar: react.ForwardRefExoticComponent<{
10
11
  blockId: string;
11
- blockRect: DOMRect;
12
- }): react.JSX.Element;
12
+ } & react.RefAttributes<HTMLDivElement>>;
13
13
 
14
14
  export { BlockToolbar };
@@ -3,114 +3,84 @@
3
3
 
4
4
  // lib/block-toolbar.tsx
5
5
  import { ChevronDown, ChevronUp, Plus, Trash2 } from "lucide-react";
6
- import { useLayoutEffect, useRef } from "react";
6
+ import { forwardRef } from "react";
7
7
  import { jsx, jsxs } from "react/jsx-runtime";
8
- function BlockToolbar({ blockId, blockRect }) {
9
- const toolbarRef = useRef(null);
10
- useLayoutEffect(() => {
11
- const el = toolbarRef.current;
12
- if (!el) return;
13
- const vw = window.innerWidth;
14
- const vh = window.innerHeight;
15
- const { width: tw, height: th } = el.getBoundingClientRect();
16
- const b = blockRect;
17
- const candidates = [
18
- { top: b.bottom + 8, left: b.left + b.width / 2 - tw / 2 },
19
- // below, centered
20
- { top: b.top - th - 8, left: b.left + b.width / 2 - tw / 2 },
21
- // above, centered
22
- { top: b.bottom + 8, left: b.right - tw },
23
- // below, right-aligned
24
- { top: b.bottom + 8, left: b.left },
25
- // below, left-aligned
26
- { top: b.top - th - 8, left: b.right - tw }
27
- // above, right-aligned
28
- ];
29
- let chosen = candidates[0] ?? { top: b.bottom + 8, left: b.left + b.width / 2 - tw / 2 };
30
- for (const c of candidates) {
31
- if (c.top >= 0 && c.left >= 0 && c.top + th <= vh && c.left + tw <= vw) {
32
- chosen = c;
33
- break;
8
+ var BlockToolbar = forwardRef(
9
+ function BlockToolbar2({ blockId }, ref) {
10
+ const handleAction = (action) => {
11
+ if (typeof window !== "undefined" && window.parent && window.parent !== window) {
12
+ window.parent.postMessage({ type: "cms-block-action", action, blockId }, "*");
34
13
  }
35
- }
36
- el.style.top = `${Math.min(Math.max(0, chosen.top), vh - th)}px`;
37
- el.style.left = `${Math.min(Math.max(0, chosen.left), vw - tw)}px`;
38
- el.style.visibility = "visible";
39
- }, [blockRect]);
40
- const handleAction = (action) => {
41
- if (typeof window !== "undefined" && window.parent && window.parent !== window) {
42
- window.parent.postMessage({ type: "cms-block-action", action, blockId }, "*");
43
- }
44
- };
45
- return /* @__PURE__ */ jsxs(
46
- "div",
47
- {
48
- ref: toolbarRef,
49
- className: "cms-block-toolbar",
50
- "data-cms-toolbar": "true",
51
- style: { visibility: "hidden" },
52
- children: [
53
- /* @__PURE__ */ jsx(
54
- "button",
55
- {
56
- type: "button",
57
- title: "Move up",
58
- "aria-label": "Move up",
59
- "data-action": "move-up",
60
- onClick: (e) => {
61
- e.stopPropagation();
62
- handleAction("move-up");
63
- },
64
- children: /* @__PURE__ */ jsx(ChevronUp, {})
65
- }
66
- ),
67
- /* @__PURE__ */ jsx(
68
- "button",
69
- {
70
- type: "button",
71
- title: "Move down",
72
- "aria-label": "Move down",
73
- "data-action": "move-down",
74
- onClick: (e) => {
75
- e.stopPropagation();
76
- handleAction("move-down");
77
- },
78
- children: /* @__PURE__ */ jsx(ChevronDown, {})
79
- }
80
- ),
81
- /* @__PURE__ */ jsx(
82
- "button",
83
- {
84
- type: "button",
85
- title: "Add block",
86
- "aria-label": "Add block",
87
- "data-action": "add-block",
88
- onClick: (e) => {
89
- e.stopPropagation();
90
- handleAction("add-block");
91
- },
92
- children: /* @__PURE__ */ jsx(Plus, {})
93
- }
94
- ),
95
- /* @__PURE__ */ jsx(
96
- "button",
97
- {
98
- type: "button",
99
- className: "delete",
100
- title: "Delete block",
101
- "aria-label": "Delete block",
102
- "data-action": "delete",
103
- onClick: (e) => {
104
- e.stopPropagation();
105
- handleAction("delete");
106
- },
107
- children: /* @__PURE__ */ jsx(Trash2, {})
108
- }
109
- )
110
- ]
111
- }
112
- );
113
- }
14
+ };
15
+ return /* @__PURE__ */ jsxs(
16
+ "div",
17
+ {
18
+ ref,
19
+ className: "cms-block-toolbar",
20
+ "data-cms-toolbar": "true",
21
+ children: [
22
+ /* @__PURE__ */ jsx(
23
+ "button",
24
+ {
25
+ type: "button",
26
+ title: "Move up",
27
+ "aria-label": "Move up",
28
+ "data-action": "move-up",
29
+ onClick: (e) => {
30
+ e.stopPropagation();
31
+ handleAction("move-up");
32
+ },
33
+ children: /* @__PURE__ */ jsx(ChevronUp, {})
34
+ }
35
+ ),
36
+ /* @__PURE__ */ jsx(
37
+ "button",
38
+ {
39
+ type: "button",
40
+ title: "Move down",
41
+ "aria-label": "Move down",
42
+ "data-action": "move-down",
43
+ onClick: (e) => {
44
+ e.stopPropagation();
45
+ handleAction("move-down");
46
+ },
47
+ children: /* @__PURE__ */ jsx(ChevronDown, {})
48
+ }
49
+ ),
50
+ /* @__PURE__ */ jsx(
51
+ "button",
52
+ {
53
+ type: "button",
54
+ title: "Add block",
55
+ "aria-label": "Add block",
56
+ "data-action": "add-block",
57
+ onClick: (e) => {
58
+ e.stopPropagation();
59
+ handleAction("add-block");
60
+ },
61
+ children: /* @__PURE__ */ jsx(Plus, {})
62
+ }
63
+ ),
64
+ /* @__PURE__ */ jsx(
65
+ "button",
66
+ {
67
+ type: "button",
68
+ className: "delete",
69
+ title: "Delete block",
70
+ "aria-label": "Delete block",
71
+ "data-action": "delete",
72
+ onClick: (e) => {
73
+ e.stopPropagation();
74
+ handleAction("delete");
75
+ },
76
+ children: /* @__PURE__ */ jsx(Trash2, {})
77
+ }
78
+ )
79
+ ]
80
+ }
81
+ );
82
+ }
83
+ );
114
84
  export {
115
85
  BlockToolbar
116
86
  };
@@ -1 +1 @@
1
- {"version":3,"sources":["../../lib/block-toolbar.tsx"],"sourcesContent":["'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":[]}
1
+ {"version":3,"sources":["../../lib/block-toolbar.tsx"],"sourcesContent":["'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 }>(\n function BlockToolbar({ blockId }, ref) {\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={ref}\n className=\"cms-block-toolbar\"\n data-cms-toolbar=\"true\"\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":";;;;AAEA,SAAS,aAAa,WAAW,MAAM,cAAc;AACrD,SAAS,kBAAkB;AAmBvB,SAeI,KAfJ;AATG,IAAM,eAAe;AAAA,EAC1B,SAASA,cAAa,EAAE,QAAQ,GAAG,KAAK;AACxC,UAAM,eAAe,CAAC,WAAmB;AACvC,UAAI,OAAO,WAAW,eAAe,OAAO,UAAU,OAAO,WAAW,QAAQ;AAC9E,eAAO,OAAO,YAAY,EAAE,MAAM,oBAAoB,QAAQ,QAAQ,GAAG,GAAG;AAAA,MAC9E;AAAA,IACF;AAEA,WACE;AAAA,MAAC;AAAA;AAAA,QACC;AAAA,QACA,WAAU;AAAA,QACV,oBAAiB;AAAA,QAEjB;AAAA;AAAA,YAAC;AAAA;AAAA,cACC,MAAK;AAAA,cACL,OAAM;AAAA,cACN,cAAW;AAAA,cACX,eAAY;AAAA,cACZ,SAAS,CAAC,MAAM;AACd,kBAAE,gBAAgB;AAClB,6BAAa,SAAS;AAAA,cACxB;AAAA,cAEA,8BAAC,aAAU;AAAA;AAAA,UACb;AAAA,UACA;AAAA,YAAC;AAAA;AAAA,cACC,MAAK;AAAA,cACL,OAAM;AAAA,cACN,cAAW;AAAA,cACX,eAAY;AAAA,cACZ,SAAS,CAAC,MAAM;AACd,kBAAE,gBAAgB;AAClB,6BAAa,WAAW;AAAA,cAC1B;AAAA,cAEA,8BAAC,eAAY;AAAA;AAAA,UACf;AAAA,UACA;AAAA,YAAC;AAAA;AAAA,cACC,MAAK;AAAA,cACL,OAAM;AAAA,cACN,cAAW;AAAA,cACX,eAAY;AAAA,cACZ,SAAS,CAAC,MAAM;AACd,kBAAE,gBAAgB;AAClB,6BAAa,WAAW;AAAA,cAC1B;AAAA,cAEA,8BAAC,QAAK;AAAA;AAAA,UACR;AAAA,UACA;AAAA,YAAC;AAAA;AAAA,cACC,MAAK;AAAA,cACL,WAAU;AAAA,cACV,OAAM;AAAA,cACN,cAAW;AAAA,cACX,eAAY;AAAA,cACZ,SAAS,CAAC,MAAM;AACd,kBAAE,gBAAgB;AAClB,6BAAa,QAAQ;AAAA,cACvB;AAAA,cAEA,8BAAC,UAAO;AAAA;AAAA,UACV;AAAA;AAAA;AAAA,IACF;AAAA,EAEJ;AAAC;","names":["BlockToolbar"]}
@@ -2,7 +2,7 @@
2
2
  "use client";
3
3
 
4
4
  // lib/client-editable-block.tsx
5
- import { useCallback, useLayoutEffect as useLayoutEffect3, useRef as useRef3, useState } from "react";
5
+ import { useCallback, 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,109 @@ function BlockOutline({ blockRect }) {
45
45
 
46
46
  // lib/block-toolbar.tsx
47
47
  import { ChevronDown, ChevronUp, Plus, Trash2 } from "lucide-react";
48
- import { useLayoutEffect as useLayoutEffect2, useRef as useRef2 } from "react";
48
+ import { forwardRef } from "react";
49
49
  import { jsx as jsx2, jsxs } from "react/jsx-runtime";
50
- function BlockToolbar({ blockId, blockRect }) {
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;
50
+ var BlockToolbar = forwardRef(
51
+ function BlockToolbar2({ blockId }, ref) {
52
+ const handleAction = (action) => {
53
+ if (typeof window !== "undefined" && window.parent && window.parent !== window) {
54
+ window.parent.postMessage({ type: "cms-block-action", action, blockId }, "*");
76
55
  }
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]);
82
- const handleAction = (action) => {
83
- if (typeof window !== "undefined" && window.parent && window.parent !== window) {
84
- window.parent.postMessage({ type: "cms-block-action", action, blockId }, "*");
85
- }
86
- };
87
- return /* @__PURE__ */ jsxs(
88
- "div",
89
- {
90
- ref: toolbarRef,
91
- className: "cms-block-toolbar",
92
- "data-cms-toolbar": "true",
93
- style: { visibility: "hidden" },
94
- children: [
95
- /* @__PURE__ */ jsx2(
96
- "button",
97
- {
98
- type: "button",
99
- title: "Move up",
100
- "aria-label": "Move up",
101
- "data-action": "move-up",
102
- onClick: (e) => {
103
- e.stopPropagation();
104
- handleAction("move-up");
105
- },
106
- children: /* @__PURE__ */ jsx2(ChevronUp, {})
107
- }
108
- ),
109
- /* @__PURE__ */ jsx2(
110
- "button",
111
- {
112
- type: "button",
113
- title: "Move down",
114
- "aria-label": "Move down",
115
- "data-action": "move-down",
116
- onClick: (e) => {
117
- e.stopPropagation();
118
- handleAction("move-down");
119
- },
120
- children: /* @__PURE__ */ jsx2(ChevronDown, {})
121
- }
122
- ),
123
- /* @__PURE__ */ jsx2(
124
- "button",
125
- {
126
- type: "button",
127
- title: "Add block",
128
- "aria-label": "Add block",
129
- "data-action": "add-block",
130
- onClick: (e) => {
131
- e.stopPropagation();
132
- handleAction("add-block");
133
- },
134
- children: /* @__PURE__ */ jsx2(Plus, {})
135
- }
136
- ),
137
- /* @__PURE__ */ jsx2(
138
- "button",
139
- {
140
- type: "button",
141
- className: "delete",
142
- title: "Delete block",
143
- "aria-label": "Delete block",
144
- "data-action": "delete",
145
- onClick: (e) => {
146
- e.stopPropagation();
147
- handleAction("delete");
148
- },
149
- children: /* @__PURE__ */ jsx2(Trash2, {})
150
- }
151
- )
152
- ]
153
- }
154
- );
155
- }
56
+ };
57
+ return /* @__PURE__ */ jsxs(
58
+ "div",
59
+ {
60
+ ref,
61
+ className: "cms-block-toolbar",
62
+ "data-cms-toolbar": "true",
63
+ children: [
64
+ /* @__PURE__ */ jsx2(
65
+ "button",
66
+ {
67
+ type: "button",
68
+ title: "Move up",
69
+ "aria-label": "Move up",
70
+ "data-action": "move-up",
71
+ onClick: (e) => {
72
+ e.stopPropagation();
73
+ handleAction("move-up");
74
+ },
75
+ children: /* @__PURE__ */ jsx2(ChevronUp, {})
76
+ }
77
+ ),
78
+ /* @__PURE__ */ jsx2(
79
+ "button",
80
+ {
81
+ type: "button",
82
+ title: "Move down",
83
+ "aria-label": "Move down",
84
+ "data-action": "move-down",
85
+ onClick: (e) => {
86
+ e.stopPropagation();
87
+ handleAction("move-down");
88
+ },
89
+ children: /* @__PURE__ */ jsx2(ChevronDown, {})
90
+ }
91
+ ),
92
+ /* @__PURE__ */ jsx2(
93
+ "button",
94
+ {
95
+ type: "button",
96
+ title: "Add block",
97
+ "aria-label": "Add block",
98
+ "data-action": "add-block",
99
+ onClick: (e) => {
100
+ e.stopPropagation();
101
+ handleAction("add-block");
102
+ },
103
+ children: /* @__PURE__ */ jsx2(Plus, {})
104
+ }
105
+ ),
106
+ /* @__PURE__ */ jsx2(
107
+ "button",
108
+ {
109
+ type: "button",
110
+ className: "delete",
111
+ title: "Delete block",
112
+ "aria-label": "Delete block",
113
+ "data-action": "delete",
114
+ onClick: (e) => {
115
+ e.stopPropagation();
116
+ handleAction("delete");
117
+ },
118
+ children: /* @__PURE__ */ jsx2(Trash2, {})
119
+ }
120
+ )
121
+ ]
122
+ }
123
+ );
124
+ }
125
+ );
156
126
 
157
127
  // lib/client-editable-block.tsx
158
128
  import { Fragment, jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
159
129
  var CMS_STYLES = `
160
130
  [data-cms-editable] { cursor: pointer; border-radius: 2px; }
161
131
  [data-cms-editable]:hover { outline: 2px solid #3b82f6; outline-offset: 2px; }
132
+ .cms-block-cursor {
133
+ position: fixed; width: 22px; height: 22px;
134
+ background: radial-gradient(circle, #fff 5px, #000 5px);
135
+ border-radius: 50%;
136
+ pointer-events: none; z-index: 99999;
137
+ transform: translate(-50%, -50%);
138
+ opacity: 0; transition: opacity 0.1s ease;
139
+ }
140
+ .cms-block-cursor.cms-cursor-visible { opacity: 1; }
162
141
  .cms-block-toolbar {
163
142
  position: fixed;
164
143
  display: flex; gap: 4px; background: #1f2937; border-radius: 6px; padding: 4px;
165
- box-shadow: 0 2px 8px rgba(0,0,0,0.15);
166
- transition: opacity 0.15s ease; z-index: 99999;
167
- pointer-events: auto;
144
+ box-shadow: 0 4px 12px rgba(0,0,0,0.25);
145
+ z-index: 99999; pointer-events: none;
146
+ opacity: 0; transform: scale(0.9) translateY(4px);
147
+ transition: opacity 0.15s ease, transform 0.15s ease;
148
+ }
149
+ .cms-block-toolbar.cms-toolbar-visible {
150
+ opacity: 1; transform: scale(1) translateY(0); pointer-events: auto;
168
151
  }
169
152
  .cms-block-toolbar button {
170
153
  display: flex; align-items: center; justify-content: center;
@@ -221,9 +204,12 @@ function ClientEditableBlock({
221
204
  contentEntries,
222
205
  children
223
206
  }) {
224
- const sentinelRef = useRef3(null);
225
- const observerRef = useRef3(null);
226
- const isInjectingRef = useRef3(false);
207
+ const sentinelRef = useRef2(null);
208
+ const observerRef = useRef2(null);
209
+ const isInjectingRef = useRef2(false);
210
+ const cursorElRef = useRef2(null);
211
+ const toolbarElRef = useRef2(null);
212
+ const toolbarVisibleRef = useRef2(false);
227
213
  const [outlineRect, setOutlineRect] = useState(null);
228
214
  const getBlockRoot = useCallback(() => {
229
215
  return sentinelRef.current?.nextElementSibling ?? null;
@@ -286,17 +272,71 @@ function ClientEditableBlock({
286
272
  }
287
273
  }
288
274
  }, [blockId, blockType, contentEntries, getBlockRoot]);
289
- useLayoutEffect3(() => {
275
+ useLayoutEffect2(() => {
290
276
  ensureCmsGlobals();
291
277
  const blockRoot = getBlockRoot();
292
278
  if (!blockRoot) return;
293
279
  blockRoot.setAttribute("data-cms-block", "");
294
280
  blockRoot.setAttribute("data-block-id", blockId);
295
281
  blockRoot.setAttribute("data-block-type", blockType);
296
- const showToolbar = () => setOutlineRect(blockRoot.getBoundingClientRect());
297
- const hideToolbar = () => setOutlineRect(null);
298
- blockRoot.addEventListener("mouseenter", showToolbar);
299
- blockRoot.addEventListener("mouseleave", hideToolbar);
282
+ const positionToolbar = (x, y) => {
283
+ const el = toolbarElRef.current;
284
+ if (!el) return;
285
+ const { width: tw, height: th } = el.getBoundingClientRect();
286
+ const top = Math.max(4, y - th - 12);
287
+ const left = Math.max(4, Math.min(x - tw / 2, window.innerWidth - tw - 4));
288
+ el.style.transition = "opacity 0.15s ease, transform 0.15s ease";
289
+ el.style.top = `${top}px`;
290
+ el.style.left = `${left}px`;
291
+ };
292
+ const showCursor = (e) => {
293
+ if (toolbarVisibleRef.current) return;
294
+ setOutlineRect(blockRoot.getBoundingClientRect());
295
+ const el = cursorElRef.current;
296
+ if (!el) return;
297
+ el.style.top = `${e.clientY}px`;
298
+ el.style.left = `${e.clientX}px`;
299
+ el.classList.add("cms-cursor-visible");
300
+ };
301
+ const moveCursor = (e) => {
302
+ if (toolbarVisibleRef.current) return;
303
+ const el = cursorElRef.current;
304
+ if (!el) return;
305
+ el.style.top = `${e.clientY}px`;
306
+ el.style.left = `${e.clientX}px`;
307
+ };
308
+ const hideCursor = () => {
309
+ cursorElRef.current?.classList.remove("cms-cursor-visible");
310
+ setOutlineRect(null);
311
+ };
312
+ const handleContextMenu = (e) => {
313
+ if (e.target.closest("[data-cms-toolbar]")) return;
314
+ if (toolbarVisibleRef.current) return;
315
+ e.preventDefault();
316
+ hideCursor();
317
+ positionToolbar(e.clientX, e.clientY);
318
+ toolbarVisibleRef.current = true;
319
+ toolbarElRef.current?.classList.add("cms-toolbar-visible");
320
+ };
321
+ const handleClickOutside = (e) => {
322
+ if (!toolbarVisibleRef.current) return;
323
+ const target = e.target;
324
+ if (target.closest("[data-cms-toolbar]")) return;
325
+ if (blockRoot.contains(target)) return;
326
+ toolbarElRef.current?.classList.remove("cms-toolbar-visible");
327
+ toolbarVisibleRef.current = false;
328
+ };
329
+ const hideOnScroll = () => {
330
+ hideCursor();
331
+ toolbarElRef.current?.classList.remove("cms-toolbar-visible");
332
+ toolbarVisibleRef.current = false;
333
+ };
334
+ blockRoot.addEventListener("mouseenter", showCursor);
335
+ blockRoot.addEventListener("mousemove", moveCursor);
336
+ blockRoot.addEventListener("mouseleave", hideCursor);
337
+ blockRoot.addEventListener("contextmenu", handleContextMenu);
338
+ document.addEventListener("click", handleClickOutside);
339
+ window.addEventListener("scroll", hideOnScroll, { passive: true, capture: true });
300
340
  injectSpans();
301
341
  const observer = new MutationObserver(injectSpans);
302
342
  observerRef.current = observer;
@@ -308,15 +348,20 @@ function ClientEditableBlock({
308
348
  return () => {
309
349
  observer.disconnect();
310
350
  observerRef.current = null;
311
- blockRoot.removeEventListener("mouseenter", showToolbar);
312
- blockRoot.removeEventListener("mouseleave", hideToolbar);
351
+ blockRoot.removeEventListener("mouseenter", showCursor);
352
+ blockRoot.removeEventListener("mousemove", moveCursor);
353
+ blockRoot.removeEventListener("mouseleave", hideCursor);
354
+ blockRoot.removeEventListener("contextmenu", handleContextMenu);
355
+ document.removeEventListener("click", handleClickOutside);
356
+ window.removeEventListener("scroll", hideOnScroll, { capture: true });
313
357
  };
314
358
  }, [injectSpans, blockId, blockType, getBlockRoot]);
315
359
  return /* @__PURE__ */ jsxs2(Fragment, { children: [
316
360
  /* @__PURE__ */ jsx3("span", { ref: sentinelRef, style: { display: "none" }, "aria-hidden": true }),
317
361
  children,
318
362
  outlineRect && createPortal(/* @__PURE__ */ jsx3(BlockOutline, { blockRect: outlineRect }), document.body),
319
- outlineRect && createPortal(/* @__PURE__ */ jsx3(BlockToolbar, { blockId, blockRect: outlineRect }), document.body)
363
+ createPortal(/* @__PURE__ */ jsx3("div", { ref: cursorElRef, className: "cms-block-cursor" }), document.body),
364
+ createPortal(/* @__PURE__ */ jsx3(BlockToolbar, { ref: toolbarElRef, blockId }), document.body)
320
365
  ] });
321
366
  }
322
367
  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, 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 [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 // 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 {outlineRect && createPortal(<BlockOutline blockRect={outlineRect} />, document.body)}\n {createPortal(<div ref={cursorElRef} className=\"cms-block-cursor\" />, document.body)}\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 }>(\n function BlockToolbar({ blockId }, ref) {\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={ref}\n className=\"cms-block-toolbar\"\n data-cms-toolbar=\"true\"\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","jsx","BlockToolbar","jsx","jsxs","useRef","useLayoutEffect"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cms-renderer",
3
- "version": "0.6.2",
3
+ "version": "0.6.3",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "exports": {