mirror of
https://github.com/t8y2/dbx.git
synced 2026-10-20 00:17:44 +00:00
feat(document): copy document json content
This commit is contained in:
@@ -2,7 +2,7 @@
|
||||
import { computed, ref, shallowRef, nextTick, watch, onMounted, onBeforeUnmount } from "vue";
|
||||
import { uuid } from "@/lib/common/utils";
|
||||
import { useI18n } from "vue-i18n";
|
||||
import { RefreshCw, Trash2, Plus, Save, ChevronDown, ChevronLeft, ChevronRight, Table2, Braces, X, Search, Wrench, Filter, Columns3Cog, SquareDashed, Minus, Rows3, AlignLeft, AlignRight, EyeOff, Palette } from "@lucide/vue";
|
||||
import { RefreshCw, Trash2, Plus, Save, ChevronDown, ChevronLeft, ChevronRight, Table2, Braces, X, Search, Wrench, Filter, Columns3Cog, SquareDashed, Minus, Rows3, AlignLeft, AlignRight, EyeOff, Palette, Copy } from "@lucide/vue";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Badge } from "@/components/ui/badge";
|
||||
import { Input } from "@/components/ui/input";
|
||||
@@ -91,6 +91,7 @@ import { documentGridColumnVisibilityScopeKey, migrateDocumentGridColumnVisibili
|
||||
import { matchesElasticsearchIndexPattern, subscribeElasticsearchIndexCleared, type ElasticsearchIndexClearedDetail } from "@/lib/sidebar/elasticsearchIndexActions";
|
||||
import { TABLE_FONT_SIZE_MAX, TABLE_FONT_SIZE_MIN, useSettingsStore } from "@/stores/settingsStore";
|
||||
import { useToast } from "@/composables/useToast";
|
||||
import { copyToClipboard } from "@/lib/common/clipboard";
|
||||
import JsonEditNode from "./JsonEditNode.vue";
|
||||
import type { EditNode } from "@/types/editor";
|
||||
import type { ColumnInfo, DatabaseType, QueryResult, QueryTab } from "@/types/database";
|
||||
@@ -2026,6 +2027,15 @@ function docPreview(doc: JsonRecord): string {
|
||||
return `${id} - ${preview}`;
|
||||
}
|
||||
|
||||
async function copyDocument() {
|
||||
try {
|
||||
await copyToClipboard(editJson.value);
|
||||
toast(t("grid.copied"), 1500);
|
||||
} catch (error: unknown) {
|
||||
toast(t("grid.copyFailed", { message: error instanceof Error ? error.message : String(error) }), 3000);
|
||||
}
|
||||
}
|
||||
|
||||
function handleDocumentViewerDoubleClick(event: MouseEvent) {
|
||||
const target = event.target;
|
||||
if (!(target instanceof Element)) return;
|
||||
@@ -2689,6 +2699,9 @@ defineExpose({ focusSearch });
|
||||
<input class="min-w-0 w-full cursor-text select-text appearance-none border-0 bg-transparent p-0 text-inherit outline-none focus:ring-0" :value="selectedDocumentIdLabel" :aria-label="`_id: ${selectedDocumentIdLabel}`" readonly spellcheck="false" />
|
||||
</Badge>
|
||||
<span class="flex-1" />
|
||||
<Button v-if="!isEditing" variant="ghost" size="icon" class="h-6 w-7" :title="t('grid.copy')" @click="copyDocument">
|
||||
<Copy class="h-3.5 w-3.5" />
|
||||
</Button>
|
||||
<Button v-if="!isEditing" variant="ghost" size="sm" class="h-6 text-xs" :disabled="!documentStoreEditable" :title="documentStoreEditDisabledReason" @click="startEdit">{{ t("mongo.edit") }}</Button>
|
||||
<template v-if="isEditing">
|
||||
<div class="flex items-center border rounded-md overflow-hidden mr-1">
|
||||
@@ -2722,7 +2735,7 @@ defineExpose({ focusSearch });
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-else data-document-json-viewer class="flex-1 min-h-0 bg-muted/10 outline-none" @dblclick="handleDocumentViewerDoubleClick">
|
||||
<div v-else data-document-json-viewer class="flex-1 min-h-0 select-text bg-muted/10 outline-none" @dblclick="handleDocumentViewerDoubleClick">
|
||||
<RedisJsonEditor ref="documentJsonEditorRef" :model-value="editJson" read-only :line-numbers="false" presentation="viewer" class="h-full" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
+15
@@ -21,6 +21,10 @@ const documentJsonEditor = vi.hoisted(() => ({
|
||||
openSearch: vi.fn().mockReturnValue(true),
|
||||
}));
|
||||
|
||||
const clipboard = vi.hoisted(() => ({
|
||||
copyToClipboard: vi.fn(),
|
||||
}));
|
||||
|
||||
const dataGrid = vi.hoisted(() => ({
|
||||
fullExportResult: undefined as
|
||||
| ((onProgress?: (info: { rowsExported: number; totalRows: number | null }) => void) => Promise<
|
||||
@@ -95,6 +99,10 @@ vi.mock("@/stores/settingsStore", () => ({
|
||||
useSettingsStore: () => settings,
|
||||
}));
|
||||
|
||||
vi.mock("@/lib/common/clipboard", () => ({
|
||||
copyToClipboard: clipboard.copyToClipboard,
|
||||
}));
|
||||
|
||||
vi.mock("@/components/grid/DataGrid.vue", () => {
|
||||
return {
|
||||
default: defineComponent({
|
||||
@@ -324,6 +332,8 @@ beforeEach(async () => {
|
||||
dataGrid.paginate = undefined;
|
||||
dataGrid.editable = true;
|
||||
documentJsonEditor.openSearch.mockClear();
|
||||
clipboard.copyToClipboard.mockReset();
|
||||
clipboard.copyToClipboard.mockResolvedValue(undefined);
|
||||
backend.documentDeleteDocument.mockResolvedValue(undefined);
|
||||
backend.documentInsertDocument.mockResolvedValue("created");
|
||||
backend.documentUpdateDocument.mockResolvedValue(1);
|
||||
@@ -1086,6 +1096,7 @@ describe("DocumentBrowser MongoDB filter value types", () => {
|
||||
const jsonText = viewer.querySelector<HTMLElement>(".cm-line .json-string")!;
|
||||
const documentId = root!.querySelector<HTMLInputElement>('input[aria-label^="_id:"]')!;
|
||||
expect(root!.firstElementChild?.classList.contains("select-none")).toBe(true);
|
||||
expect(viewer.classList.contains("select-text")).toBe(true);
|
||||
expect(documentId.readOnly).toBe(true);
|
||||
expect(documentId.value).toBe("document-1");
|
||||
expect(documentId.classList.contains("select-text")).toBe(true);
|
||||
@@ -1097,6 +1108,10 @@ describe("DocumentBrowser MongoDB filter value types", () => {
|
||||
expect(viewer.querySelector<HTMLElement>("[data-redis-json-editor-stub]")?.dataset.lineNumbers).toBe("false");
|
||||
expect(viewer.querySelector<HTMLElement>("[data-redis-json-editor-stub]")?.dataset.presentation).toBe("viewer");
|
||||
|
||||
buttonWithTitle("grid.copy").click();
|
||||
await flushUi();
|
||||
expect(clipboard.copyToClipboard).toHaveBeenCalledWith(expect.stringContaining('"_id": "document-1"'));
|
||||
|
||||
documentId.setSelectionRange(0, documentId.value.length);
|
||||
expect(documentId.selectionStart).toBe(0);
|
||||
expect(documentId.selectionEnd).toBe(documentId.value.length);
|
||||
|
||||
Reference in New Issue
Block a user