feat(editor): support dragging tables into queries
This commit is contained in:
1 parent
13d9fd14b4
commit
e324f35f28
6 files changed
+229
-1
No files matched your search
@@ -24,6 +24,11 @@ import {
|
||||
} from "@/lib/sqlCompletion";
|
||||
import { extractIdentifierAt, isSqlKeyword, matchTable } from "@/lib/sqlNavigation";
|
||||
import { lineColumnToOffset, parseSqlErrorLocation } from "@/lib/sqlDiagnostics";
|
||||
import {
|
||||
DBX_TABLE_REFERENCE_MIME,
|
||||
parseTableReferencePayload,
|
||||
tableReferenceInsertText,
|
||||
} from "@/lib/queryEditorTableDrop";
|
||||
import {
|
||||
EDITOR_FONT_FAMILY_CSS_VAR,
|
||||
EDITOR_FONT_SIZE_CSS_VAR,
|
||||
@@ -47,13 +52,14 @@ import {
|
||||
type SqlSemanticDiagnostic,
|
||||
} from "@/lib/sqlSemanticDiagnostics";
|
||||
import type { SqlCompletionColumn } from "@/lib/sqlCompletion";
|
||||
import type { SqlReferenceAnalysis, SqlTableReference, SqlTextSpan } from "@/types/database";
|
||||
import type { DatabaseType, SqlReferenceAnalysis, SqlTableReference, SqlTextSpan } from "@/types/database";
|
||||
import { vscodeSelectionLayer } from "@/lib/codemirrorVscodeSelectionLayer";
|
||||
|
||||
const props = defineProps<{
|
||||
modelValue: string;
|
||||
connectionId?: string;
|
||||
database?: string;
|
||||
databaseType?: DatabaseType;
|
||||
dialect?: "mysql" | "postgres" | "sqlserver";
|
||||
formatDialect?: SqlFormatDialect;
|
||||
formatRequestId?: number;
|
||||
@@ -715,6 +721,33 @@ async function formatCurrentSql() {
|
||||
}
|
||||
}
|
||||
|
||||
function droppedTableReference(event: DragEvent) {
|
||||
return parseTableReferencePayload(event.dataTransfer?.getData(DBX_TABLE_REFERENCE_MIME));
|
||||
}
|
||||
|
||||
function insertDroppedTableReference(currentView: EditorViewType, event: DragEvent): boolean {
|
||||
if (props.readOnly) return false;
|
||||
const payload = droppedTableReference(event);
|
||||
if (!payload) return false;
|
||||
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
|
||||
const insertText = tableReferenceInsertText(payload, props.databaseType);
|
||||
const dropPos = currentView.posAtCoords({ x: event.clientX, y: event.clientY });
|
||||
const selection = currentView.state.selection.main;
|
||||
const from = dropPos ?? selection.from;
|
||||
const to = dropPos == null && !selection.empty ? selection.to : from;
|
||||
currentView.dispatch({
|
||||
changes: { from, to, insert: insertText },
|
||||
selection: { anchor: from + insertText.length },
|
||||
scrollIntoView: true,
|
||||
userEvent: "input.drop",
|
||||
});
|
||||
currentView.focus();
|
||||
return true;
|
||||
}
|
||||
|
||||
let completionEpoch = 0;
|
||||
let completionDebounceTimer: ReturnType<typeof setTimeout> | null = null;
|
||||
|
||||
@@ -1249,6 +1282,15 @@ onMounted(async () => {
|
||||
}),
|
||||
),
|
||||
EditorView.domEventHandlers({
|
||||
dragover(event) {
|
||||
if (props.readOnly || !droppedTableReference(event)) return false;
|
||||
event.preventDefault();
|
||||
if (event.dataTransfer) event.dataTransfer.dropEffect = "copy";
|
||||
return true;
|
||||
},
|
||||
drop(event, currentView) {
|
||||
return insertDroppedTableReference(currentView, event);
|
||||
},
|
||||
wheel(event) {
|
||||
if (!event.metaKey && !event.ctrlKey) return false;
|
||||
event.preventDefault();
|
||||
|
||||
@@ -293,6 +293,7 @@ defineExpose({ focusSearch, refreshData, handleModRTarget });
|
||||
:model-value="activeTab.sql"
|
||||
:connection-id="activeTab.connectionId"
|
||||
:database="activeTab.database"
|
||||
:database-type="activeConnection?.db_type"
|
||||
:dialect="editorDialect"
|
||||
:format-dialect="activeSqlFormatDialect"
|
||||
:format-request-id="formatSqlRequestId"
|
||||
|
||||
@@ -1391,6 +1391,7 @@ function getObjectBrowserMenuItems(item: ObjectBrowserRow): ContextMenuItem[] {
|
||||
class="min-h-0 flex-1"
|
||||
:connection-id="props.connection.id"
|
||||
:database="props.database"
|
||||
:database-type="props.connection.db_type"
|
||||
:dialect="sourceDialect"
|
||||
:format-dialect="sourceFormatDialect"
|
||||
force-word-wrap
|
||||
@@ -1407,6 +1408,7 @@ function getObjectBrowserMenuItems(item: ObjectBrowserRow): ContextMenuItem[] {
|
||||
class="min-h-0 flex-1"
|
||||
:connection-id="props.connection.id"
|
||||
:database="props.database"
|
||||
:database-type="props.connection.db_type"
|
||||
:dialect="sourceDialect"
|
||||
:format-dialect="sourceFormatDialect"
|
||||
force-word-wrap
|
||||
|
||||
@@ -58,6 +58,12 @@ import { uuid } from "@/lib/utils";
|
||||
import { resolveDefaultDatabase } from "@/lib/defaultDatabase";
|
||||
import { canTreeNodeShowExpander, treeItemPaddingLeft } from "@/lib/sidebarTreeItemLayout";
|
||||
import { buildTableSelectSql } from "@/lib/tableSelectSql";
|
||||
import {
|
||||
DBX_TABLE_REFERENCE_MIME,
|
||||
createTableReferencePayload,
|
||||
serializeTableReferencePayload,
|
||||
tableReferenceInsertText,
|
||||
} from "@/lib/queryEditorTableDrop";
|
||||
import { editablePrimaryKeys, usesSyntheticRowIdKey } from "@/lib/tableEditing";
|
||||
import {
|
||||
supportsDatabaseCreation,
|
||||
@@ -1792,6 +1798,29 @@ const showDropInside = computed(
|
||||
() => dragState.active && dragState.targetId === props.node.id && dragState.dropPosition === "inside",
|
||||
);
|
||||
const isDragging = computed(() => dragState.active && dragState.draggedId === props.node.id);
|
||||
const canDragTableReference = computed(
|
||||
() =>
|
||||
!props.dragDisabled &&
|
||||
(props.node.type === "table" || props.node.type === "view") &&
|
||||
!!props.node.connectionId &&
|
||||
!!props.node.database,
|
||||
);
|
||||
|
||||
function onTableReferenceDragStart(event: DragEvent) {
|
||||
if (!canDragTableReference.value) return;
|
||||
const payload = createTableReferencePayload({
|
||||
connectionId: props.node.connectionId,
|
||||
database: props.node.database,
|
||||
schema: props.node.schema,
|
||||
tableName: props.node.label,
|
||||
databaseType: currentDatabaseType(),
|
||||
});
|
||||
if (!payload || !event.dataTransfer) return;
|
||||
const insertText = tableReferenceInsertText(payload, currentDatabaseType());
|
||||
event.dataTransfer.effectAllowed = "copy";
|
||||
event.dataTransfer.setData(DBX_TABLE_REFERENCE_MIME, serializeTableReferencePayload(payload));
|
||||
event.dataTransfer.setData("text/plain", insertText);
|
||||
}
|
||||
|
||||
// ---- CustomContextMenu ----
|
||||
|
||||
@@ -2110,12 +2139,14 @@ function treeItemMenuItems(): ContextMenuItem[] {
|
||||
}"
|
||||
:tabindex="isSelected ? 0 : -1"
|
||||
:style="rowStyle"
|
||||
:draggable="canDragTableReference"
|
||||
@click="onClick"
|
||||
@dblclick="onDoubleClick"
|
||||
@keydown="onKeydown"
|
||||
@mousedown="isDraggable ? startDrag($event, node.id, node.type) : undefined"
|
||||
@mousemove="isDropTarget ? updateTarget($event, node.id, node.type) : undefined"
|
||||
@mouseleave="clearTarget(node.id)"
|
||||
@dragstart="onTableReferenceDragStart"
|
||||
>
|
||||
<div
|
||||
v-if="showDropBefore"
|
||||
|
||||
Reference in new issue
Block a user