Files
dbx/apps/desktop/src/lib/editor/tableReferenceDragFeedback.ts
T

71 lines
3.1 KiB
TypeScript

/**
* 表引用拖拽(侧边栏表/列、查询结果列头)共用的指针模拟拖拽反馈:
* 跟随鼠标的 chip 浮层 + body copy 光标。落点探测与插入见 queryEditorTableDrop.ts。
*/
export const QUERY_EDITOR_DROP_TARGET_SELECTOR = "[data-query-editor-root]";
/** 拖拽期间加在 body 上,禁用文本选择(样式见 globals.css)。 */
export const TABLE_REFERENCE_DRAGGING_CLASS = "dbx-table-reference-dragging";
const CHIP_OFFSET_X = 14;
const CHIP_OFFSET_Y = 18;
const CHIP_VIEWPORT_MARGIN = 8;
/**
* elementFromPoint 命中 root 内部才算命中;elementFromPoint 可能被透明覆盖层
* 拦截(面板层等),此时回退为 root 包围盒的几何包含判定。
*/
export function isPointOverElementRoot(clientX: number, clientY: number, root: Element | null | undefined, doc: Document = document): boolean {
if (!root) return false;
const target = doc.elementFromPoint(clientX, clientY);
if (target instanceof Element && root.contains(target)) return true;
const rect = root.getBoundingClientRect();
return clientX >= rect.left && clientX <= rect.right && clientY >= rect.top && clientY <= rect.bottom;
}
export function isOverSqlEditorTarget(clientX: number, clientY: number, doc: Document = document): boolean {
for (const root of doc.querySelectorAll(QUERY_EDITOR_DROP_TARGET_SELECTOR)) {
if (isPointOverElementRoot(clientX, clientY, root, doc)) return true;
}
return false;
}
/** 多列摘要由 i18n 插值完成:调用方以 { names: 前两个列名, count: 总数 } 调 t("grid.columnDragChipMany", ...)。 */
export interface TableReferenceDragFeedback {
update(clientX: number, clientY: number): void;
end(): void;
}
export function beginTableReferenceDragFeedback(label: string, doc: Document = document): TableReferenceDragFeedback {
const chip = doc.createElement("div");
chip.dataset.tableReferenceDragChip = "";
chip.setAttribute("aria-hidden", "true");
chip.className = "pointer-events-none fixed z-[9999] max-w-72 truncate rounded-md border border-border bg-popover px-2 py-1 font-mono text-xs leading-4 text-popover-foreground shadow-lg";
chip.textContent = label;
chip.style.visibility = "hidden";
doc.body.appendChild(chip);
doc.body.classList.add(TABLE_REFERENCE_DRAGGING_CLASS);
doc.body.style.cursor = "copy";
const viewport = doc.defaultView ?? window;
const update = (clientX: number, clientY: number) => {
if (chip.style.visibility === "hidden") chip.style.visibility = "visible";
const rect = chip.getBoundingClientRect();
const x = Math.min(clientX + CHIP_OFFSET_X, viewport.innerWidth - rect.width - CHIP_VIEWPORT_MARGIN);
const y = Math.min(clientY + CHIP_OFFSET_Y, viewport.innerHeight - rect.height - CHIP_VIEWPORT_MARGIN);
chip.style.left = `${Math.max(CHIP_VIEWPORT_MARGIN, x)}px`;
chip.style.top = `${Math.max(CHIP_VIEWPORT_MARGIN, y)}px`;
};
return {
update,
end() {
chip.remove();
doc.body.classList.remove(TABLE_REFERENCE_DRAGGING_CLASS);
if (doc.body.style.cursor === "copy") doc.body.style.cursor = "";
},
};
}