71 lines
3.1 KiB
TypeScript
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 = "";
|
|
},
|
|
};
|
|
}
|