feat(builder): add dynamic blocks email and dnd packages
This commit is contained in:
1 parent
06433e4ac4
commit
0bd2a6860d
37 files changed
+3060
-1
No files matched your search
@@ -0,0 +1,76 @@
|
||||
import { createDndManager, type DndAnnouncement, type DndManager } from "@wrnexus/dnd";
|
||||
|
||||
export interface MountedEditor {
|
||||
destroy(): void;
|
||||
refresh(): void;
|
||||
}
|
||||
export interface MountEditorOptions {
|
||||
direction?: "ltr" | "rtl";
|
||||
announce?(announcement: DndAnnouncement): string;
|
||||
onMove(intent: {
|
||||
nodeIds: string[];
|
||||
to: { parentId: string; slot: string; index: number };
|
||||
}): boolean | void | Promise<boolean | void>;
|
||||
}
|
||||
|
||||
export function mountBlockEditor(root: HTMLElement, options: MountEditorOptions): MountedEditor {
|
||||
let manager: DndManager | undefined;
|
||||
let cleanups: Array<() => void> = [];
|
||||
const live = document.createElement("div");
|
||||
live.setAttribute("role", "status");
|
||||
live.setAttribute("aria-live", "polite");
|
||||
live.setAttribute("aria-atomic", "true");
|
||||
live.style.cssText =
|
||||
"position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0";
|
||||
root.append(live);
|
||||
const refresh = () => {
|
||||
cleanups.forEach((cleanup) => cleanup());
|
||||
cleanups = [];
|
||||
manager?.destroy();
|
||||
manager = createDndManager({
|
||||
direction: options.direction ?? (getComputedStyle(root).direction === "rtl" ? "rtl" : "ltr"),
|
||||
reducedMotion: matchMedia("(prefers-reduced-motion: reduce)").matches,
|
||||
announce(announcement) {
|
||||
live.textContent = options.announce?.(announcement) ?? announcement.type;
|
||||
},
|
||||
async onMoveIntent(intent) {
|
||||
const [parentId, slot] = String(intent.to.group).split(":", 2);
|
||||
if (!parentId || !slot) return false;
|
||||
return options.onMove({
|
||||
nodeIds: intent.ids.map(String),
|
||||
to: { parentId, slot, index: intent.to.index },
|
||||
});
|
||||
},
|
||||
});
|
||||
for (const element of root.querySelectorAll<HTMLElement>(
|
||||
"[data-block-id][data-block-parent][data-block-slot]",
|
||||
)) {
|
||||
const id = element.dataset.blockId!;
|
||||
const parent = element.dataset.blockParent!;
|
||||
const slot = element.dataset.blockSlot!;
|
||||
const siblings = [
|
||||
...element.parentElement!.querySelectorAll<HTMLElement>(":scope > [data-block-id]"),
|
||||
];
|
||||
const handle = element.querySelector<HTMLElement>("[data-block-handle]") ?? undefined;
|
||||
cleanups.push(
|
||||
manager.registerSortable({
|
||||
id,
|
||||
group: `${parent}:${slot}`,
|
||||
index: siblings.indexOf(element),
|
||||
element,
|
||||
handle,
|
||||
type: element.dataset.blockType,
|
||||
}),
|
||||
);
|
||||
}
|
||||
};
|
||||
refresh();
|
||||
return {
|
||||
refresh,
|
||||
destroy() {
|
||||
cleanups.forEach((cleanup) => cleanup());
|
||||
manager?.destroy();
|
||||
live.remove();
|
||||
},
|
||||
};
|
||||
}
|
||||
Reference in new issue
Block a user