@avocadostudio-ai/preview-adapter 0.5.1 → 0.6.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/dist/bridge-functions.d.ts +23 -0
- package/dist/bridge-functions.js +56 -7
- package/package.json +2 -2
|
@@ -52,6 +52,29 @@ export declare function scrollBlockIntoView(el: HTMLElement, options?: {
|
|
|
52
52
|
behavior?: ScrollBehavior;
|
|
53
53
|
block?: ScrollLogicalPosition;
|
|
54
54
|
}): void;
|
|
55
|
+
/**
|
|
56
|
+
* The full path a marked node stands for, composing any enclosing scopes.
|
|
57
|
+
*
|
|
58
|
+
* `data-editable-target` is scoped from the block down — `items[3].question` —
|
|
59
|
+
* which a renderer can only write if it knows where it sits. That is fine
|
|
60
|
+
* while one component draws the whole block, and false the moment a list row
|
|
61
|
+
* is rendered by a component of its own: the child knows it has a `question`
|
|
62
|
+
* and cannot know it is `items[3]`. So every renderer that can appear inside a
|
|
63
|
+
* list needed a prefix threaded in from its parent, and every parent needed to
|
|
64
|
+
* remember to pass it.
|
|
65
|
+
*
|
|
66
|
+
* Forgetting is silent and *wrong* rather than silent and absent. The child
|
|
67
|
+
* marks a bare `question`, the overlay resolves it against the enclosing
|
|
68
|
+
* block, and an edit to a headline inside a column patches a prop the section
|
|
69
|
+
* does not have.
|
|
70
|
+
*
|
|
71
|
+
* `data-editable-scope="items[3]"` on any ancestor removes the threading:
|
|
72
|
+
* children emit bare field names and composition happens here, at the one
|
|
73
|
+
* place that knows the whole ancestry. Scopes nest, outermost first.
|
|
74
|
+
*/
|
|
75
|
+
export declare function editablePathOf(node: HTMLElement): string;
|
|
76
|
+
/** `items[3]` + `question` → `items[3].question`; `cards` + `[0].title` → `cards[0].title`. */
|
|
77
|
+
export declare function joinEditablePath(scope: string, path: string): string;
|
|
55
78
|
export declare function findEditableNode(parent: HTMLElement, editablePath: string): HTMLElement | null;
|
|
56
79
|
/**
|
|
57
80
|
* The node to point at for a field, which is not always the field's own node.
|
package/dist/bridge-functions.js
CHANGED
|
@@ -243,10 +243,59 @@ export function scrollBlockIntoView(el, options) {
|
|
|
243
243
|
lastY = window.scrollY;
|
|
244
244
|
}, SCROLL_SETTLE_INTERVAL_MS);
|
|
245
245
|
}
|
|
246
|
+
/**
|
|
247
|
+
* The full path a marked node stands for, composing any enclosing scopes.
|
|
248
|
+
*
|
|
249
|
+
* `data-editable-target` is scoped from the block down — `items[3].question` —
|
|
250
|
+
* which a renderer can only write if it knows where it sits. That is fine
|
|
251
|
+
* while one component draws the whole block, and false the moment a list row
|
|
252
|
+
* is rendered by a component of its own: the child knows it has a `question`
|
|
253
|
+
* and cannot know it is `items[3]`. So every renderer that can appear inside a
|
|
254
|
+
* list needed a prefix threaded in from its parent, and every parent needed to
|
|
255
|
+
* remember to pass it.
|
|
256
|
+
*
|
|
257
|
+
* Forgetting is silent and *wrong* rather than silent and absent. The child
|
|
258
|
+
* marks a bare `question`, the overlay resolves it against the enclosing
|
|
259
|
+
* block, and an edit to a headline inside a column patches a prop the section
|
|
260
|
+
* does not have.
|
|
261
|
+
*
|
|
262
|
+
* `data-editable-scope="items[3]"` on any ancestor removes the threading:
|
|
263
|
+
* children emit bare field names and composition happens here, at the one
|
|
264
|
+
* place that knows the whole ancestry. Scopes nest, outermost first.
|
|
265
|
+
*/
|
|
266
|
+
export function editablePathOf(node) {
|
|
267
|
+
const own = node.getAttribute("data-editable-target") ?? "";
|
|
268
|
+
const scopes = [];
|
|
269
|
+
// The node's own scope does not apply to itself — a component that both
|
|
270
|
+
// scopes its children and marks a field of its own would otherwise prefix
|
|
271
|
+
// its own path with the scope it sets for them.
|
|
272
|
+
let current = node.parentElement;
|
|
273
|
+
while (current) {
|
|
274
|
+
const scope = current.getAttribute("data-editable-scope");
|
|
275
|
+
if (scope)
|
|
276
|
+
scopes.unshift(scope);
|
|
277
|
+
// A block boundary ends the search: a path is scoped from the block down,
|
|
278
|
+
// and a stray scope outside one must not reach into it.
|
|
279
|
+
if (current.hasAttribute("data-block-id"))
|
|
280
|
+
break;
|
|
281
|
+
current = current.parentElement;
|
|
282
|
+
}
|
|
283
|
+
if (scopes.length === 0)
|
|
284
|
+
return own;
|
|
285
|
+
return joinEditablePath(scopes.join("."), own);
|
|
286
|
+
}
|
|
287
|
+
/** `items[3]` + `question` → `items[3].question`; `cards` + `[0].title` → `cards[0].title`. */
|
|
288
|
+
export function joinEditablePath(scope, path) {
|
|
289
|
+
if (scope === "")
|
|
290
|
+
return path;
|
|
291
|
+
if (path === "")
|
|
292
|
+
return scope;
|
|
293
|
+
return path.startsWith("[") ? `${scope}${path}` : `${scope}.${path}`;
|
|
294
|
+
}
|
|
246
295
|
export function findEditableNode(parent, editablePath) {
|
|
247
296
|
const nodes = parent.querySelectorAll("[data-editable-target]");
|
|
248
297
|
for (const node of nodes) {
|
|
249
|
-
if ((node
|
|
298
|
+
if (editablePathOf(node) === editablePath)
|
|
250
299
|
return node;
|
|
251
300
|
}
|
|
252
301
|
return null;
|
|
@@ -475,7 +524,7 @@ export function computeInsertBefore(blockId, preferredNode) {
|
|
|
475
524
|
export function groupListItemNodes(block) {
|
|
476
525
|
const groups = new Map();
|
|
477
526
|
block.querySelectorAll("[data-editable-target]").forEach((node) => {
|
|
478
|
-
const path =
|
|
527
|
+
const path = editablePathOf(node);
|
|
479
528
|
const match = /^([A-Za-z_][A-Za-z0-9_]*)\[([0-9]+)\](?:\.|$)/.exec(path);
|
|
480
529
|
if (!match)
|
|
481
530
|
return;
|
|
@@ -839,7 +888,7 @@ export function createBridgeFunctions(state, callbacks, config) {
|
|
|
839
888
|
const applyLiveDraftFields = (block, fields) => {
|
|
840
889
|
const nextPaths = new Set(Object.keys(fields));
|
|
841
890
|
for (const [node, html] of [...state.liveDraftOriginals.entries()]) {
|
|
842
|
-
const path = node
|
|
891
|
+
const path = editablePathOf(node);
|
|
843
892
|
if (!nextPaths.has(path)) {
|
|
844
893
|
node.innerHTML = html;
|
|
845
894
|
node.classList.remove("editor-live-typing");
|
|
@@ -1350,7 +1399,7 @@ export function createBridgeFunctions(state, callbacks, config) {
|
|
|
1350
1399
|
return;
|
|
1351
1400
|
}
|
|
1352
1401
|
document.querySelectorAll(".editor-selectable [data-editable-target]").forEach((el) => {
|
|
1353
|
-
const path = el
|
|
1402
|
+
const path = editablePathOf(el);
|
|
1354
1403
|
if (!isImageEditable(el, path))
|
|
1355
1404
|
return;
|
|
1356
1405
|
const block = el.closest("[data-block-id]");
|
|
@@ -1741,12 +1790,12 @@ export function createBridgeFunctions(state, callbacks, config) {
|
|
|
1741
1790
|
const blockType = node.getAttribute("data-block-type");
|
|
1742
1791
|
if (!blockId || !blockType)
|
|
1743
1792
|
return;
|
|
1744
|
-
let editablePath = childNode && node.contains(childNode) ?
|
|
1793
|
+
let editablePath = childNode && node.contains(childNode) ? editablePathOf(childNode) || undefined : undefined;
|
|
1745
1794
|
if (!editablePath) {
|
|
1746
1795
|
const itemRoot = target?.closest(".editor-item-has-delete");
|
|
1747
1796
|
if (itemRoot && node.contains(itemRoot)) {
|
|
1748
1797
|
const firstEditable = itemRoot.querySelector("[data-editable-target]");
|
|
1749
|
-
editablePath =
|
|
1798
|
+
editablePath = (firstEditable ? editablePathOf(firstEditable) : "") || undefined;
|
|
1750
1799
|
}
|
|
1751
1800
|
}
|
|
1752
1801
|
if (state.selectedBlockId === blockId) {
|
|
@@ -1806,7 +1855,7 @@ export function createBridgeFunctions(state, callbacks, config) {
|
|
|
1806
1855
|
return;
|
|
1807
1856
|
const blockId = node.getAttribute("data-block-id");
|
|
1808
1857
|
const blockType = node.getAttribute("data-block-type");
|
|
1809
|
-
const editablePath =
|
|
1858
|
+
const editablePath = editablePathOf(childNode);
|
|
1810
1859
|
if (!blockId || !blockType || !editablePath)
|
|
1811
1860
|
return;
|
|
1812
1861
|
if (!supportsInlineEditablePath(editablePath))
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@avocadostudio-ai/preview-adapter",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -32,7 +32,7 @@
|
|
|
32
32
|
"src/styles.css"
|
|
33
33
|
],
|
|
34
34
|
"dependencies": {
|
|
35
|
-
"@avocadostudio-ai/shared": "^0.
|
|
35
|
+
"@avocadostudio-ai/shared": "^0.6.0"
|
|
36
36
|
},
|
|
37
37
|
"peerDependencies": {
|
|
38
38
|
"next": ">=15.0.0",
|