diff --git a/apps/desktop/src/components/grid/DataGridCellDetailPanel.vue b/apps/desktop/src/components/grid/DataGridCellDetailPanel.vue index fd3ca930b..f673ce6ea 100644 --- a/apps/desktop/src/components/grid/DataGridCellDetailPanel.vue +++ b/apps/desktop/src/components/grid/DataGridCellDetailPanel.vue @@ -55,6 +55,18 @@ void geometryCanvas; void detailsEditorContainer; void sideJsonPreviewContainer; +function startJsonEditFromBlankArea(event: MouseEvent) { + const target = event.target as { closest?: (selector: string) => Element | null } | null; + const line = target?.closest?.(".cm-line"); + if (line) { + const range = line.ownerDocument.createRange(); + range.selectNodeContents(line); + const textHit = Array.from(range.getClientRects()).some((rect) => event.clientX >= rect.left && event.clientX <= rect.right && event.clientY >= rect.top && event.clientY <= rect.bottom); + if (textHit) return; + } + emit("startEdit"); +} + defineExpose({ openSearch }); @@ -159,9 +171,10 @@ defineExpose({ openSearch }); v-else-if="detail.formattedJson" ref="sideJsonPreviewContainer" data-cell-detail-editor-root + data-cell-detail-json-preview class="overflow-hidden rounded border bg-muted/20 p-2" :class="[{ 'cursor-text': detail.isEditable }, valueFillsHeight ? 'min-h-0 flex-1' : 'h-72 max-h-[42vh]']" - @dblclick.capture="emit('startEdit')" + @dblclick.capture="startJsonEditFromBlankArea" />
{
expect(updateOpen).toHaveBeenCalledWith(false);
});
- it("forwards panel edit/copy/cancel actions and exposes search", () => {
+ it("forwards panel actions and only starts JSON editing from preview whitespace", async () => {
const startEdit = vi.fn();
const copyValue = vi.fn();
const cancel = vi.fn();
@@ -959,6 +959,26 @@ describe("cell detail surfaces", () => {
expect(cancel).toHaveBeenCalledOnce();
mounted.exposed.value.openSearch();
expect(mocks.panelOpenSearch).toHaveBeenCalledOnce();
+
+ await mounted.setProps({ detail: detail() });
+ const jsonPreview = findOne(mounted.root, (node) => node.props["data-cell-detail-json-preview"] === "");
+ const doubleClickCapture = jsonPreview.props.onDblclickCapture;
+ const textLine = {
+ ownerDocument: {
+ createRange: () => ({
+ selectNodeContents: vi.fn(),
+ getClientRects: () => [{ left: 10, right: 110, top: 20, bottom: 40 }],
+ }),
+ },
+ };
+ const lineTarget = { closest: (selector: string) => (selector === ".cm-line" ? textLine : null) };
+
+ doubleClickCapture({ target: lineTarget, clientX: 60, clientY: 30 });
+ expect(startEdit).toHaveBeenCalledTimes(2);
+
+ doubleClickCapture({ target: lineTarget, clientX: 160, clientY: 30 });
+ doubleClickCapture({ target: { closest: () => null }, clientX: 60, clientY: 80 });
+ expect(startEdit).toHaveBeenCalledTimes(4);
});
});