Support explicit empty slot drop zones in shared editor
This commit is contained in:
1 parent
7110e1a7f1
commit
0f8d10eb91
7 files changed
+100
-11
No files matched your search
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/block-editor",
|
||||
"version": "0.1.4",
|
||||
"version": "0.1.5",
|
||||
"type": "module",
|
||||
"description": "Hydrated accessible editor runtime for WRNexus block documents.",
|
||||
"exports": {
|
||||
|
||||
@@ -107,6 +107,16 @@ export function mountBlockEditor(root: HTMLElement, options: MountEditorOptions)
|
||||
});
|
||||
},
|
||||
});
|
||||
for (const element of root.querySelectorAll<HTMLElement>("[data-block-slot-target][data-block-parent]")) {
|
||||
const parentId = element.dataset.blockParent!;
|
||||
const slot = element.dataset.blockSlotTarget!;
|
||||
const hasChildren = [...root.querySelectorAll<HTMLElement>("[data-block-id][data-block-parent][data-block-slot]")]
|
||||
.some(child => child.dataset.blockParent === parentId && child.dataset.blockSlot === slot);
|
||||
if (hasChildren) continue;
|
||||
const group = JSON.stringify([parentId, slot]);
|
||||
groups.set(group, { parentId, slot });
|
||||
cleanups.push(manager.registerDropZone({ id: `wrn-slot:${group}`, group, index: 0, element }));
|
||||
}
|
||||
for (const element of root.querySelectorAll<HTMLElement>(
|
||||
"[data-block-id][data-block-parent][data-block-slot]",
|
||||
)) {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { expect, test } from "bun:test";
|
||||
import { readFileSync } from "node:fs";
|
||||
import { Window } from "happy-dom";
|
||||
import type { DndManagerOptions, SortableRegistration } from "@wrnexus/dnd";
|
||||
import type { DndManagerOptions, SortableRegistration, DropZoneRegistration } from "@wrnexus/dnd";
|
||||
import type { MountedEditor, MountEditorOptions } from "../src/runtime.ts";
|
||||
|
||||
const code = new Bun.Transpiler({ loader: "ts" })
|
||||
@@ -19,13 +19,18 @@ function fixture(onMove: MountEditorOptions["onMove"] = () => false) {
|
||||
const backends: Array<{
|
||||
options: DndManagerOptions;
|
||||
registrations: SortableRegistration[];
|
||||
zones: DropZoneRegistration[];
|
||||
destroyed: number;
|
||||
}> = [];
|
||||
const mount = new Function("createDndManager", code + ";return mountBlockEditor;")(
|
||||
(options: DndManagerOptions) => {
|
||||
const backend = { options, registrations: [] as SortableRegistration[], destroyed: 0 };
|
||||
const backend = { options, registrations: [] as SortableRegistration[], zones: [] as DropZoneRegistration[], destroyed: 0 };
|
||||
backends.push(backend);
|
||||
return {
|
||||
registerDropZone: (registration: DropZoneRegistration) => {
|
||||
backend.zones.push(registration);
|
||||
return () => {};
|
||||
},
|
||||
registerSortable: (registration: SortableRegistration) => {
|
||||
backend.registrations.push(registration);
|
||||
return () => {};
|
||||
@@ -66,6 +71,28 @@ test("cancelled optimistic moves restore original DOM nodes across containers",
|
||||
f.editor.destroy();
|
||||
});
|
||||
|
||||
test("empty named slot targets produce validated editor move intents and refresh away when populated", async () => {
|
||||
const moves: unknown[] = [];
|
||||
const f = fixture(intent => { moves.push(intent); return true; });
|
||||
const target = f.document.createElement("div");
|
||||
target.dataset.blockParent = "parent:empty";
|
||||
target.dataset.blockSlotTarget = "content:empty";
|
||||
f.root.append(target);
|
||||
f.editor.refresh();
|
||||
const backend = f.backends.at(-1)!;
|
||||
expect(backend.zones).toHaveLength(1);
|
||||
const zone = backend.zones[0]!;
|
||||
expect(backend.options.accept!(["a"], { group: zone.group, index: 0 })).toBe(true);
|
||||
await backend.options.onMoveIntent({ ids: ["a"], from: [], to: { group: zone.group, index: 0 } });
|
||||
expect(moves).toEqual([{ nodeIds: ["a"], to: { parentId: "parent:empty", slot: "content:empty", index: 0 } }]);
|
||||
const child = f.root.querySelector('[data-block-id="a"]')! as any;
|
||||
child.dataset.blockParent = "parent:empty"; child.dataset.blockSlot = "content:empty";
|
||||
target.append(child);
|
||||
f.editor.refresh();
|
||||
expect(f.backends.at(-1)!.zones).toHaveLength(0);
|
||||
f.editor.destroy();
|
||||
});
|
||||
|
||||
test("slot and parent identifiers containing colons round-trip without collisions", async () => {
|
||||
const moves: unknown[] = [];
|
||||
const f = fixture((intent) => {
|
||||
|
||||
Reference in new issue
Block a user