|
|
|
@@ -1,7 +1,7 @@
|
|
|
|
|
<script setup lang="ts">
|
|
|
|
|
import { computed, nextTick, onBeforeUnmount, reactive, ref } from "vue";
|
|
|
|
|
import { useI18n } from "vue-i18n";
|
|
|
|
|
import { Download, FileInput, FilePlus, FileText, FolderCog, FolderClosed, FolderOpen, FolderPlus, Library, LocateFixed, Pencil, Search, Trash2, Upload, X } from "@lucide/vue";
|
|
|
|
|
import { Download, FileInput, FilePlus, FileText, FolderCog, FolderClosed, FolderOpen, FolderPlus, Library, LocateFixed, Pencil, Search, Trash2, Upload, X, ArrowDownWideNarrow } from "@lucide/vue";
|
|
|
|
|
import { Button } from "@/components/ui/button";
|
|
|
|
|
import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle } from "@/components/ui/dialog";
|
|
|
|
|
import CustomContextMenu, { type ContextMenuItem as CtxMenuItem } from "@/components/ui/CustomContextMenu.vue";
|
|
|
|
@@ -37,6 +37,9 @@ const searchText = ref("");
|
|
|
|
|
const searchQuery = computed(() => searchText.value.trim().toLowerCase());
|
|
|
|
|
const orphanedIds = computed(() => savedSqlStore.orphanedFileIds(activeConnectionIds.value));
|
|
|
|
|
|
|
|
|
|
// Sort mode: "folder" (default tree structure) or "date" (flat list by update date)
|
|
|
|
|
const sortMode = ref<"folder" | "date">("folder");
|
|
|
|
|
|
|
|
|
|
function isConnectionVisible(connectionId: string) {
|
|
|
|
|
return activeConnectionIds.value.has(connectionId);
|
|
|
|
|
}
|
|
|
|
@@ -368,6 +371,18 @@ const visibleFiles = computed(() =>
|
|
|
|
|
.filter((file) => fileMatchesQuery(file)),
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
// Flat list sorted by updatedAt (descending) - combines all folders and files
|
|
|
|
|
const itemsByDate = computed(() => {
|
|
|
|
|
const allFolders = savedSqlStore.allFolders
|
|
|
|
|
.filter((folder) => isConnectionVisible(folder.connectionId))
|
|
|
|
|
.filter((folder) => folderBranchMatchesQuery(folder))
|
|
|
|
|
.map((folder) => ({ type: "folder" as const, item: folder, updatedAt: folder.updatedAt }));
|
|
|
|
|
|
|
|
|
|
const allFiles = [...savedSqlStore.allFolders.flatMap((folder) => filesInFolder(folder.id)), ...visibleFiles.value].map((file) => ({ type: "file" as const, item: file, updatedAt: file.updatedAt }));
|
|
|
|
|
|
|
|
|
|
return [...allFolders, ...allFiles].sort((a, b) => b.updatedAt.localeCompare(a.updatedAt));
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
const hasAnyVisibleItem = computed(() => visibleFolderRows.value.length > 0 || visibleFiles.value.length > 0);
|
|
|
|
|
|
|
|
|
|
const collapsedFolders = ref<Set<string>>(new Set());
|
|
|
|
@@ -983,6 +998,9 @@ function showDropInside(targetId: string) {
|
|
|
|
|
<span class="text-[13px] font-medium">{{ t("sqlLibrary.title") }}</span>
|
|
|
|
|
<span v-if="hasSelection" class="text-[12px] text-muted-foreground ml-1">({{ selectedCount }})</span>
|
|
|
|
|
<span class="flex-1" />
|
|
|
|
|
<Button variant="ghost" size="icon" class="h-5 w-5" :title="sortMode === 'folder' ? t('sqlLibrary.sortByDate') : t('sqlLibrary.sortByFolder')" @click="sortMode = sortMode === 'folder' ? 'date' : 'folder'">
|
|
|
|
|
<ArrowDownWideNarrow :class="['h-3 w-3', sortMode === 'date' ? 'text-primary' : '']" />
|
|
|
|
|
</Button>
|
|
|
|
|
<Button variant="ghost" size="icon" class="h-5 w-5" :title="t('savedSql.newFolder')" @click="openNewFolderInput">
|
|
|
|
|
<FolderPlus class="h-3 w-3" />
|
|
|
|
|
</Button>
|
|
|
|
@@ -1018,121 +1036,167 @@ function showDropInside(targetId: string) {
|
|
|
|
|
onContextMenu($event);
|
|
|
|
|
"
|
|
|
|
|
>
|
|
|
|
|
<div v-for="row in visibleFolderRows" :key="row.type === 'folder' ? row.folder.id : row.file.id">
|
|
|
|
|
<div
|
|
|
|
|
v-if="row.type === 'folder'"
|
|
|
|
|
class="relative flex items-center gap-1 rounded py-1.5 pr-2 text-[13px] cursor-pointer group"
|
|
|
|
|
:style="{ paddingLeft: `${8 + row.depth * 16}px` }"
|
|
|
|
|
:class="[showDropInside(row.folder.id) ? 'ring-1 ring-primary/50 bg-primary/5' : isFolderSelected(row.folder.id) ? 'bg-primary/10' : isFolderActive(row.folder.id) ? 'bg-accent' : 'hover:bg-accent', isDraggingItem(row.folder.id) ? 'opacity-50' : '']"
|
|
|
|
|
@mousedown="handleDragMouseDown($event, row.folder.id, 'folder')"
|
|
|
|
|
@mousemove="updateDropTarget($event, row.folder.id, 'folder')"
|
|
|
|
|
@mouseleave="clearDropTarget(row.folder.id)"
|
|
|
|
|
@click="handleFolderClick(row.folder, $event)"
|
|
|
|
|
@contextmenu.capture="contextTarget = row.folder"
|
|
|
|
|
@contextmenu.prevent="
|
|
|
|
|
contextTarget = row.folder;
|
|
|
|
|
onContextMenu($event);
|
|
|
|
|
"
|
|
|
|
|
>
|
|
|
|
|
<div v-if="showDropBefore(row.folder.id)" class="absolute left-2 right-2 top-0 border-t-2 border-primary" />
|
|
|
|
|
<div v-if="showDropAfter(row.folder.id)" class="absolute left-2 right-2 bottom-0 border-b-2 border-primary" />
|
|
|
|
|
<component :is="isFolderExpanded(row.folder.id) ? FolderOpen : FolderClosed" class="h-4 w-4 text-amber-500 shrink-0" />
|
|
|
|
|
<template v-if="renamingTarget?.type === 'folder' && renamingTarget.id === row.folder.id">
|
|
|
|
|
<input
|
|
|
|
|
:ref="setRenameInputRef"
|
|
|
|
|
v-model="renameValue"
|
|
|
|
|
data-no-drag="true"
|
|
|
|
|
class="min-w-0 flex-1 rounded border border-primary/50 bg-transparent px-1 text-[13px] outline-none"
|
|
|
|
|
@keydown.enter.prevent="confirmRename"
|
|
|
|
|
@keydown.escape.prevent="cancelRename"
|
|
|
|
|
@blur="confirmRename"
|
|
|
|
|
@click.stop
|
|
|
|
|
/>
|
|
|
|
|
</template>
|
|
|
|
|
<span v-else class="dbx-sql-library-drag-label min-w-0 flex-1 truncate">
|
|
|
|
|
{{ row.folder.name }}
|
|
|
|
|
<span class="ml-1 text-muted-foreground">({{ filesInFolder(row.folder.id).length }})</span>
|
|
|
|
|
</span>
|
|
|
|
|
</div>
|
|
|
|
|
<!-- Flat list sorted by date -->
|
|
|
|
|
<div v-if="sortMode === 'date'" class="space-y-0">
|
|
|
|
|
<div v-for="item in itemsByDate" :key="item.type + '-' + item.item.id">
|
|
|
|
|
<div
|
|
|
|
|
v-if="item.type === 'folder'"
|
|
|
|
|
class="relative flex items-center gap-1 rounded px-2 py-1.5 text-[13px] cursor-pointer group"
|
|
|
|
|
:class="[isFolderSelected(item.item.id) ? 'bg-primary/10' : isFolderActive(item.item.id) ? 'bg-accent' : 'hover:bg-accent', isDraggingItem(item.item.id) ? 'opacity-50' : '']"
|
|
|
|
|
@mousedown="handleDragMouseDown($event, item.item.id, 'folder')"
|
|
|
|
|
@click="handleFolderClick(item.item, $event)"
|
|
|
|
|
@contextmenu.capture="contextTarget = item.item"
|
|
|
|
|
@contextmenu.prevent="
|
|
|
|
|
contextTarget = item.item;
|
|
|
|
|
onContextMenu($event);
|
|
|
|
|
"
|
|
|
|
|
>
|
|
|
|
|
<FolderClosed class="h-4 w-4 text-amber-500 shrink-0" />
|
|
|
|
|
<span class="dbx-sql-library-drag-label min-w-0 flex-1 truncate">
|
|
|
|
|
{{ item.item.name }}
|
|
|
|
|
<span class="ml-1 text-muted-foreground">({{ filesInFolder(item.item.id).length }})</span>
|
|
|
|
|
</span>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<div
|
|
|
|
|
v-else
|
|
|
|
|
class="relative flex items-center gap-1 rounded py-1.5 pr-2 text-[13px] cursor-pointer group"
|
|
|
|
|
:style="{ paddingLeft: `${8 + row.depth * 16}px` }"
|
|
|
|
|
:class="[isDraggingItem(row.file.id) ? 'opacity-50' : isFileSelected(row.file.id) ? 'bg-primary/10' : isFileActive(row.file.id) ? 'bg-accent' : 'hover:bg-accent']"
|
|
|
|
|
@mousedown="handleDragMouseDown($event, row.file.id, 'file')"
|
|
|
|
|
@mousemove="updateDropTarget($event, row.file.id, 'file')"
|
|
|
|
|
@mouseleave="clearDropTarget(row.file.id)"
|
|
|
|
|
@click="handleFileClick(row.file, $event)"
|
|
|
|
|
@contextmenu.capture="contextTarget = row.file"
|
|
|
|
|
@contextmenu.prevent="
|
|
|
|
|
contextTarget = row.file;
|
|
|
|
|
onContextMenu($event);
|
|
|
|
|
"
|
|
|
|
|
>
|
|
|
|
|
<div v-if="showDropBefore(row.file.id)" class="absolute left-2 right-2 top-0 border-t-2 border-primary" />
|
|
|
|
|
<div v-if="showDropAfter(row.file.id)" class="absolute left-2 right-2 bottom-0 border-b-2 border-primary" />
|
|
|
|
|
<FileText class="h-3.5 w-3.5 text-blue-400 shrink-0" />
|
|
|
|
|
<template v-if="renamingTarget?.type === 'file' && renamingTarget.id === row.file.id">
|
|
|
|
|
<input
|
|
|
|
|
:ref="setRenameInputRef"
|
|
|
|
|
v-model="renameValue"
|
|
|
|
|
data-no-drag="true"
|
|
|
|
|
class="min-w-0 flex-1 rounded border border-primary/50 bg-transparent px-1 text-[13px] outline-none"
|
|
|
|
|
@keydown.enter.prevent="confirmRename"
|
|
|
|
|
@keydown.escape.prevent="cancelRename"
|
|
|
|
|
@blur="confirmRename"
|
|
|
|
|
@click.stop
|
|
|
|
|
/>
|
|
|
|
|
</template>
|
|
|
|
|
<span v-else class="dbx-sql-library-drag-label min-w-0 flex-1 truncate">{{ row.file.name }}</span>
|
|
|
|
|
<span class="shrink-0 text-[13px] text-muted-foreground"> [{{ getConnectionLabel(row.file.connectionId) }}] </span>
|
|
|
|
|
<div
|
|
|
|
|
v-else
|
|
|
|
|
class="relative flex items-center gap-1 rounded px-2 py-1.5 text-[13px] cursor-pointer group"
|
|
|
|
|
:class="[isDraggingItem(item.item.id) ? 'opacity-50' : isFileSelected(item.item.id) ? 'bg-primary/10' : isFileActive(item.item.id) ? 'bg-accent' : 'hover:bg-accent']"
|
|
|
|
|
@mousedown="handleDragMouseDown($event, item.item.id, 'file')"
|
|
|
|
|
@click="handleFileClick(item.item, $event)"
|
|
|
|
|
@contextmenu.capture="contextTarget = item.item"
|
|
|
|
|
@contextmenu.prevent="
|
|
|
|
|
contextTarget = item.item;
|
|
|
|
|
onContextMenu($event);
|
|
|
|
|
"
|
|
|
|
|
>
|
|
|
|
|
<FileText class="h-3.5 w-3.5 text-blue-400 shrink-0" />
|
|
|
|
|
<span class="dbx-sql-library-drag-label min-w-0 flex-1 truncate">{{ item.item.name }}</span>
|
|
|
|
|
<span class="shrink-0 text-[13px] text-muted-foreground"> [{{ getConnectionLabel(item.item.connectionId) }}] </span>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<div v-if="visibleFiles.length > 0 || dragState.draggedType === 'file'" class="mt-2">
|
|
|
|
|
<div
|
|
|
|
|
class="relative rounded px-2 py-1 text-[10px] font-medium uppercase text-muted-foreground"
|
|
|
|
|
:class="showDropInside(UNFILED_DROP_TARGET_ID) ? 'ring-1 ring-primary/50 bg-primary/5' : ''"
|
|
|
|
|
@mousemove="updateDropTarget($event, UNFILED_DROP_TARGET_ID, 'unfiled')"
|
|
|
|
|
@mouseleave="clearDropTarget(UNFILED_DROP_TARGET_ID)"
|
|
|
|
|
>
|
|
|
|
|
{{ t("sqlLibrary.unfiled") }}
|
|
|
|
|
<!-- Tree structure sorted by folder -->
|
|
|
|
|
<div v-else>
|
|
|
|
|
<div v-for="row in visibleFolderRows" :key="row.type === 'folder' ? row.folder.id : row.file.id">
|
|
|
|
|
<div
|
|
|
|
|
v-if="row.type === 'folder'"
|
|
|
|
|
class="relative flex items-center gap-1 rounded py-1.5 pr-2 text-[13px] cursor-pointer group"
|
|
|
|
|
:style="{ paddingLeft: `${8 + row.depth * 16}px` }"
|
|
|
|
|
:class="[showDropInside(row.folder.id) ? 'ring-1 ring-primary/50 bg-primary/5' : isFolderSelected(row.folder.id) ? 'bg-primary/10' : isFolderActive(row.folder.id) ? 'bg-accent' : 'hover:bg-accent', isDraggingItem(row.folder.id) ? 'opacity-50' : '']"
|
|
|
|
|
@mousedown="handleDragMouseDown($event, row.folder.id, 'folder')"
|
|
|
|
|
@mousemove="updateDropTarget($event, row.folder.id, 'folder')"
|
|
|
|
|
@mouseleave="clearDropTarget(row.folder.id)"
|
|
|
|
|
@click="handleFolderClick(row.folder, $event)"
|
|
|
|
|
@contextmenu.capture="contextTarget = row.folder"
|
|
|
|
|
@contextmenu.prevent="
|
|
|
|
|
contextTarget = row.folder;
|
|
|
|
|
onContextMenu($event);
|
|
|
|
|
"
|
|
|
|
|
>
|
|
|
|
|
<div v-if="showDropBefore(row.folder.id)" class="absolute left-2 right-2 top-0 border-t-2 border-primary" />
|
|
|
|
|
<div v-if="showDropAfter(row.folder.id)" class="absolute left-2 right-2 bottom-0 border-b-2 border-primary" />
|
|
|
|
|
<component :is="isFolderExpanded(row.folder.id) ? FolderOpen : FolderClosed" class="h-4 w-4 text-amber-500 shrink-0" />
|
|
|
|
|
<template v-if="renamingTarget?.type === 'folder' && renamingTarget.id === row.folder.id">
|
|
|
|
|
<input
|
|
|
|
|
:ref="setRenameInputRef"
|
|
|
|
|
v-model="renameValue"
|
|
|
|
|
data-no-drag="true"
|
|
|
|
|
class="min-w-0 flex-1 rounded border border-primary/50 bg-transparent px-1 text-[13px] outline-none"
|
|
|
|
|
@keydown.enter.prevent="confirmRename"
|
|
|
|
|
@keydown.escape.prevent="cancelRename"
|
|
|
|
|
@blur="confirmRename"
|
|
|
|
|
@click.stop
|
|
|
|
|
/>
|
|
|
|
|
</template>
|
|
|
|
|
<span v-else class="dbx-sql-library-drag-label min-w-0 flex-1 truncate">
|
|
|
|
|
{{ row.folder.name }}
|
|
|
|
|
<span class="ml-1 text-muted-foreground">({{ filesInFolder(row.folder.id).length }})</span>
|
|
|
|
|
</span>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<div
|
|
|
|
|
v-else
|
|
|
|
|
class="relative flex items-center gap-1 rounded py-1.5 pr-2 text-[13px] cursor-pointer group"
|
|
|
|
|
:style="{ paddingLeft: `${8 + row.depth * 16}px` }"
|
|
|
|
|
:class="[isDraggingItem(row.file.id) ? 'opacity-50' : isFileSelected(row.file.id) ? 'bg-primary/10' : isFileActive(row.file.id) ? 'bg-accent' : 'hover:bg-accent']"
|
|
|
|
|
@mousedown="handleDragMouseDown($event, row.file.id, 'file')"
|
|
|
|
|
@mousemove="updateDropTarget($event, row.file.id, 'file')"
|
|
|
|
|
@mouseleave="clearDropTarget(row.file.id)"
|
|
|
|
|
@click="handleFileClick(row.file, $event)"
|
|
|
|
|
@contextmenu.capture="contextTarget = row.file"
|
|
|
|
|
@contextmenu.prevent="
|
|
|
|
|
contextTarget = row.file;
|
|
|
|
|
onContextMenu($event);
|
|
|
|
|
"
|
|
|
|
|
>
|
|
|
|
|
<div v-if="showDropBefore(row.file.id)" class="absolute left-2 right-2 top-0 border-t-2 border-primary" />
|
|
|
|
|
<div v-if="showDropAfter(row.file.id)" class="absolute left-2 right-2 bottom-0 border-b-2 border-primary" />
|
|
|
|
|
<FileText class="h-3.5 w-3.5 text-blue-400 shrink-0" />
|
|
|
|
|
<template v-if="renamingTarget?.type === 'file' && renamingTarget.id === row.file.id">
|
|
|
|
|
<input
|
|
|
|
|
:ref="setRenameInputRef"
|
|
|
|
|
v-model="renameValue"
|
|
|
|
|
data-no-drag="true"
|
|
|
|
|
class="min-w-0 flex-1 rounded border border-primary/50 bg-transparent px-1 text-[13px] outline-none"
|
|
|
|
|
@keydown.enter.prevent="confirmRename"
|
|
|
|
|
@keydown.escape.prevent="cancelRename"
|
|
|
|
|
@blur="confirmRename"
|
|
|
|
|
@click.stop
|
|
|
|
|
/>
|
|
|
|
|
</template>
|
|
|
|
|
<span v-else class="dbx-sql-library-drag-label min-w-0 flex-1 truncate">{{ row.file.name }}</span>
|
|
|
|
|
<span class="shrink-0 text-[13px] text-muted-foreground"> [{{ getConnectionLabel(row.file.connectionId) }}] </span>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
<div
|
|
|
|
|
v-for="file in visibleFiles"
|
|
|
|
|
:key="file.id"
|
|
|
|
|
class="relative flex items-center gap-1 rounded px-2 py-1.5 text-[13px] cursor-pointer group"
|
|
|
|
|
:class="[isDraggingItem(file.id) ? 'opacity-50' : isFileSelected(file.id) ? 'bg-primary/10' : 'hover:bg-accent']"
|
|
|
|
|
@mousedown="handleDragMouseDown($event, file.id, 'file')"
|
|
|
|
|
@mousemove="updateDropTarget($event, file.id, 'file')"
|
|
|
|
|
@mouseleave="clearDropTarget(file.id)"
|
|
|
|
|
@click="handleFileClick(file, $event)"
|
|
|
|
|
@contextmenu.capture="contextTarget = file"
|
|
|
|
|
@contextmenu.prevent="
|
|
|
|
|
contextTarget = file;
|
|
|
|
|
onContextMenu($event);
|
|
|
|
|
"
|
|
|
|
|
>
|
|
|
|
|
<div v-if="showDropBefore(file.id)" class="absolute left-2 right-2 top-0 border-t-2 border-primary" />
|
|
|
|
|
<div v-if="showDropAfter(file.id)" class="absolute left-2 right-2 bottom-0 border-b-2 border-primary" />
|
|
|
|
|
<FileText class="h-3.5 w-3.5 text-blue-400 shrink-0" />
|
|
|
|
|
<template v-if="renamingTarget?.type === 'file' && renamingTarget.id === file.id">
|
|
|
|
|
<input
|
|
|
|
|
:ref="setRenameInputRef"
|
|
|
|
|
v-model="renameValue"
|
|
|
|
|
data-no-drag="true"
|
|
|
|
|
class="min-w-0 flex-1 rounded border border-primary/50 bg-transparent px-1 text-[13px] outline-none"
|
|
|
|
|
@keydown.enter.prevent="confirmRename"
|
|
|
|
|
@keydown.escape.prevent="cancelRename"
|
|
|
|
|
@blur="confirmRename"
|
|
|
|
|
@click.stop
|
|
|
|
|
/>
|
|
|
|
|
</template>
|
|
|
|
|
<span v-else class="dbx-sql-library-drag-label min-w-0 flex-1 truncate">{{ file.name }}</span>
|
|
|
|
|
<span class="shrink-0 text-[13px] text-muted-foreground"> [{{ getConnectionLabel(file.connectionId) }}] </span>
|
|
|
|
|
|
|
|
|
|
<div v-if="visibleFiles.length > 0 || dragState.draggedType === 'file'">
|
|
|
|
|
<div
|
|
|
|
|
v-if="dragState.draggedType === 'file'"
|
|
|
|
|
class="relative rounded px-2 py-1 text-[10px] font-medium uppercase text-muted-foreground"
|
|
|
|
|
:class="showDropInside(UNFILED_DROP_TARGET_ID) ? 'ring-1 ring-primary/50 bg-primary/5' : ''"
|
|
|
|
|
@mousemove="updateDropTarget($event, UNFILED_DROP_TARGET_ID, 'unfiled')"
|
|
|
|
|
@mouseleave="clearDropTarget(UNFILED_DROP_TARGET_ID)"
|
|
|
|
|
>
|
|
|
|
|
{{ t("sqlLibrary.unfiled") }}
|
|
|
|
|
</div>
|
|
|
|
|
<div
|
|
|
|
|
v-for="file in visibleFiles"
|
|
|
|
|
:key="file.id"
|
|
|
|
|
class="relative flex items-center gap-1 rounded px-2 py-1.5 text-[13px] cursor-pointer group"
|
|
|
|
|
:class="[isDraggingItem(file.id) ? 'opacity-50' : isFileSelected(file.id) ? 'bg-primary/10' : 'hover:bg-accent']"
|
|
|
|
|
@mousedown="handleDragMouseDown($event, file.id, 'file')"
|
|
|
|
|
@mousemove="updateDropTarget($event, file.id, 'file')"
|
|
|
|
|
@mouseleave="clearDropTarget(file.id)"
|
|
|
|
|
@click="handleFileClick(file, $event)"
|
|
|
|
|
@contextmenu.capture="contextTarget = file"
|
|
|
|
|
@contextmenu.prevent="
|
|
|
|
|
contextTarget = file;
|
|
|
|
|
onContextMenu($event);
|
|
|
|
|
"
|
|
|
|
|
>
|
|
|
|
|
<div v-if="showDropBefore(file.id)" class="absolute left-2 right-2 top-0 border-t-2 border-primary" />
|
|
|
|
|
<div v-if="showDropAfter(file.id)" class="absolute left-2 right-2 bottom-0 border-b-2 border-primary" />
|
|
|
|
|
<FileText class="h-3.5 w-3.5 text-blue-400 shrink-0" />
|
|
|
|
|
<template v-if="renamingTarget?.type === 'file' && renamingTarget.id === file.id">
|
|
|
|
|
<input
|
|
|
|
|
:ref="setRenameInputRef"
|
|
|
|
|
v-model="renameValue"
|
|
|
|
|
data-no-drag="true"
|
|
|
|
|
class="min-w-0 flex-1 rounded border border-primary/50 bg-transparent px-1 text-[13px] outline-none"
|
|
|
|
|
@keydown.enter.prevent="confirmRename"
|
|
|
|
|
@keydown.escape.prevent="cancelRename"
|
|
|
|
|
@blur="confirmRename"
|
|
|
|
|
@click.stop
|
|
|
|
|
/>
|
|
|
|
|
</template>
|
|
|
|
|
<span v-else class="dbx-sql-library-drag-label min-w-0 flex-1 truncate">{{ file.name }}</span>
|
|
|
|
|
<span class="shrink-0 text-[13px] text-muted-foreground"> [{{ getConnectionLabel(file.connectionId) }}] </span>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
<!-- End tree structure -->
|
|
|
|
|
|
|
|
|
|
<div v-if="!hasAnyVisibleItem" class="flex h-full flex-col items-center justify-center gap-2 text-muted-foreground">
|
|
|
|
|
<Library class="h-8 w-8 opacity-30" />
|
|
|
|
|