@simmalugnt-se/payload-visual-editing 0.1.2 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,12 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.2.0
4
+
5
+ - A click in the preview dispatches `visual-editing:select` on Admin's `window` with the form path,
6
+ so other Admin components can follow the selection, and with `path: null` when the editor clears
7
+ it in the preview (empty space, Escape).
8
+ - Clicks inside `data-visual-editing-keep-selection` keep the preview outline.
9
+
3
10
  ## 0.1.2
4
11
 
5
12
  - A click in the preview no longer blocks the page's own click handlers: accordions, tabs and
package/README.md CHANGED
@@ -78,3 +78,23 @@ Requires Payload `>=3.85.2 <4` and React 19.
78
78
  The outline shows the same names as Admin: the editor's block name, otherwise the block's
79
79
  `labels.singular`, then the field's `label`, e.g. "CTA › Button label". Array rows get their row
80
80
  header, e.g. "Item 02 › Title". Admin sends the labels to the preview, so nothing needs configuring.
81
+
82
+ ## Following the selection in other Admin components
83
+
84
+ A click in the preview dispatches `visual-editing:select` on Admin's `window`, with the form path
85
+ in `detail.path` (`layout.2` for a block, `layout.2.headline` for a field), and with `path: null`
86
+ when the editor clears the selection in the preview (empty space, Escape). Other components can
87
+ follow the editor's selection without depending on this package;
88
+ `@simmalugnt-se/payload-editor-assistant` uses it so "this" in a message means the clicked block.
89
+
90
+ ```ts
91
+ window.addEventListener("visual-editing:select", (event) => {
92
+ const { path } = (event as CustomEvent<{ path: string | null }>).detail;
93
+ });
94
+ ```
95
+
96
+ Clicks in Admin clear the preview outline. Put `data-visual-editing-keep-selection` on a panel
97
+ whose clicks should keep it, such as a chat next to the form.
98
+
99
+ The event name, its `detail` and the attribute are public API: renaming or removing them is a
100
+ breaking change and needs a major version.
@@ -4,7 +4,7 @@ import { useConfig, useDocumentForm, useDocumentInfo, useLivePreviewContext, use
4
4
  import { useEffect, useRef } from "react";
5
5
  import "../styles/admin.css";
6
6
  import { collectBlockLabels, collectRowLabels } from "../labels.js";
7
- import { fieldDomId, focusTargets, isReadyMessage, isSelectMessage, MESSAGE_TYPE, matchNodeIndex, pathFromDomId, pathFromRowDomId, resolveBlockTarget, rowDomId, } from "../protocol.js";
7
+ import { ATTR_KEEP_SELECTION, fieldDomId, focusTargets, isClearMessage, isReadyMessage, isSelectMessage, MESSAGE_TYPE, matchNodeIndex, pathFromDomId, pathFromRowDomId, resolveBlockTarget, rowDomId, SELECT_EVENT, } from "../protocol.js";
8
8
  import { planReveal } from "../reveal-plan.js";
9
9
  import { HIGHLIGHT_TINT } from "../theme.js";
10
10
  const HIGHLIGHT_CLASS = "pve-highlight";
@@ -50,6 +50,11 @@ export function VisualEditingAdminBridge() {
50
50
  sendLabels(event.source, event.origin);
51
51
  return;
52
52
  }
53
+ if (isClearMessage(event.data)) {
54
+ run += 1;
55
+ window.dispatchEvent(new CustomEvent(SELECT_EVENT, { detail: { path: null } }));
56
+ return;
57
+ }
53
58
  if (!isSelectMessage(event.data)) {
54
59
  return;
55
60
  }
@@ -85,6 +90,9 @@ export function VisualEditingAdminBridge() {
85
90
  const fieldPath = message.field ? `${target.rowPath}.${message.field}` : undefined;
86
91
  const fieldPlan = fieldPath ? planReveal(rootFields, fieldPath, lookup) : null;
87
92
  const plan = fieldPlan ?? planReveal(rootFields, target.rowPath, lookup) ?? [];
93
+ window.dispatchEvent(new CustomEvent(SELECT_EVENT, {
94
+ detail: { path: fieldPlan && fieldPath ? fieldPath : target.rowPath },
95
+ }));
88
96
  const root = formRoot(anchorRef.current);
89
97
  const scope = await openSteps(plan, root, () => id === run);
90
98
  if (!scope || id !== run) {
@@ -117,6 +125,9 @@ export function VisualEditingAdminBridge() {
117
125
  // Focus moving into the preview iframe is the preview's own click; it already selected.
118
126
  return;
119
127
  }
128
+ if (event.target.closest(`[${ATTR_KEEP_SELECTION}]`)) {
129
+ return;
130
+ }
120
131
  const root = formRoot(anchorRef.current);
121
132
  const path = root.contains(event.target) ? pathOf(event.target) : undefined;
122
133
  const fields = latest.current.form.getFields();
@@ -75,11 +75,20 @@ export function VisualEditingPreview({ adminOrigin }) {
75
75
  redraw();
76
76
  }
77
77
  };
78
+ /** The editor dropped the selection here; Admin tells components following it. */
79
+ const dropSelection = () => {
80
+ if (!current) {
81
+ return;
82
+ }
83
+ clearSelection();
84
+ const message = { type: MESSAGE_TYPE, action: "clear" };
85
+ admin.postMessage(message, targetOrigin);
86
+ };
78
87
  const onClick = (event) => {
79
88
  const hit = hitFromTarget(event.target, labels);
80
89
  if (!hit) {
81
90
  // Outside any editable element the page behaves normally; only the selection is dropped.
82
- clearSelection();
91
+ dropSelection();
83
92
  return;
84
93
  }
85
94
  // A click selects, and must not leave the page: links and submits are cancelled. Anything
@@ -134,7 +143,7 @@ export function VisualEditingPreview({ adminOrigin }) {
134
143
  };
135
144
  const onKeyDown = (event) => {
136
145
  if (event.key === "Escape") {
137
- clearSelection();
146
+ dropSelection();
138
147
  }
139
148
  };
140
149
  document.addEventListener("pointermove", onPointerMove, { passive: true });
@@ -9,6 +9,15 @@ export declare const ATTR_BLOCK_NAME = "data-payload-block-name";
9
9
  export declare const ATTR_FIELD = "data-payload-field";
10
10
  /** On the rich text container whose direct children are the top-level editor nodes. */
11
11
  export declare const ATTR_RICHTEXT = "data-payload-richtext";
12
+ /**
13
+ * Dispatched on Admin's `window` when a preview click selects something, with the form path as
14
+ * `detail.path` (`layout.2` for a block, `layout.2.headline` for a field), and with `path: null`
15
+ * when the editor clears the selection in the preview. Other Admin components, such as an
16
+ * assistant, can follow the editor's selection without depending on this package.
17
+ */
18
+ export declare const SELECT_EVENT = "visual-editing:select";
19
+ /** Clicks and focus inside an element with this attribute keep the preview outline. */
20
+ export declare const ATTR_KEEP_SELECTION = "data-visual-editing-keep-selection";
12
21
  export type SelectMessage = {
13
22
  type: typeof MESSAGE_TYPE;
14
23
  action: "select";
@@ -21,6 +30,12 @@ export type SelectMessage = {
21
30
  text?: string;
22
31
  };
23
32
  };
33
+ /** Preview → Admin: the editor dropped the selection in the preview (empty space, Escape). */
34
+ export type ClearMessage = {
35
+ type: typeof MESSAGE_TYPE;
36
+ action: "clear";
37
+ };
38
+ export declare function isClearMessage(value: unknown): value is ClearMessage;
24
39
  export declare function isSelectMessage(value: unknown): value is SelectMessage;
25
40
  /**
26
41
  * Pick the editor node matching the clicked preview node. Text wins over index because
package/dist/protocol.js CHANGED
@@ -8,7 +8,23 @@ export const ATTR_BLOCK_NAME = "data-payload-block-name";
8
8
  export const ATTR_FIELD = "data-payload-field";
9
9
  /** On the rich text container whose direct children are the top-level editor nodes. */
10
10
  export const ATTR_RICHTEXT = "data-payload-richtext";
11
+ /**
12
+ * Dispatched on Admin's `window` when a preview click selects something, with the form path as
13
+ * `detail.path` (`layout.2` for a block, `layout.2.headline` for a field), and with `path: null`
14
+ * when the editor clears the selection in the preview. Other Admin components, such as an
15
+ * assistant, can follow the editor's selection without depending on this package.
16
+ */
17
+ export const SELECT_EVENT = "visual-editing:select";
18
+ /** Clicks and focus inside an element with this attribute keep the preview outline. */
19
+ export const ATTR_KEEP_SELECTION = "data-visual-editing-keep-selection";
11
20
  const MAX_NODE_TEXT = 200;
21
+ export function isClearMessage(value) {
22
+ if (!value || typeof value !== "object") {
23
+ return false;
24
+ }
25
+ const message = value;
26
+ return message.type === MESSAGE_TYPE && message.action === "clear";
27
+ }
12
28
  export function isSelectMessage(value) {
13
29
  if (!value || typeof value !== "object") {
14
30
  return false;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@simmalugnt-se/payload-visual-editing",
3
- "version": "0.1.2",
3
+ "version": "0.2.0",
4
4
  "description": "Click a block in Payload Live Preview to open its fields in the edit form",
5
5
  "keywords": [
6
6
  "payload",