fix(editor): close padding editor on canvas click

This commit is contained in:
Danila Poyarkov 2026-05-15 11:49:18 +03:00
parent 9537f35a27
commit b46295a7bb
8 changed files with 81 additions and 19 deletions

View file

@ -201,6 +201,35 @@ const noVueStyleBlocks = {
}
}
const noDocumentQuerySelectorInVue = {
meta: {
docs: {
description: 'Disallow document.querySelector in Vue components — use template refs or composables'
}
},
create(context) {
const file = normalizedFilename(context)
if (!file.endsWith('.vue')) return {}
return {
CallExpression(node) {
const callee = node.callee
if (callee?.type !== 'MemberExpression') return
if (callee.object?.type !== 'Identifier' || callee.object.name !== 'document') return
if (callee.property?.type !== 'Identifier') return
if (callee.property.name !== 'querySelector' && callee.property.name !== 'querySelectorAll') {
return
}
context.report({
node,
message:
'Do not query the document from Vue components. Use template refs, component APIs, or a composable.'
})
}
}
}
}
const noDirectSelectionToolStateMutation = {
meta: {
docs: {
@ -1257,6 +1286,7 @@ const plugin = {
'no-inline-named-types': noInlineNamedTypes,
'no-structuredclone-scene-arrays': noStructuredCloneSceneArrays,
'no-vue-style-blocks': noVueStyleBlocks,
'no-document-query-selector-in-vue': noDocumentQuerySelectorInVue,
'no-direct-selection-tool-state-mutation': noDirectSelectionToolStateMutation,
'no-math-random': noMathRandom,
'no-hand-rolled-color': noHandRolledColor,

View file

@ -109,6 +109,7 @@
],
"open-pencil/no-structuredclone-scene-arrays": "error",
"open-pencil/no-vue-style-blocks": "error",
"open-pencil/no-document-query-selector-in-vue": "error",
"open-pencil/no-direct-selection-tool-state-mutation": "error",
"open-pencil/no-math-random": "error",
"open-pencil/no-hand-rolled-color": "error",

View file

@ -148,6 +148,10 @@ export function useCanvasInput(
}
function onMouseDown(e: MouseEvent) {
const paddingEdit = autoLayoutPaddingEdit.value
if (paddingEdit) {
commitAutoLayoutPaddingEdit(paddingEdit.value)
}
if (!editor.state.editingTextId) canvasRef.value?.focus()
editor.setHoveredNode(null)
const { sx, sy, cx, cy } = getCoords(e)

View file

@ -10,10 +10,19 @@ export function createProfilerActions(editor: Editor) {
return { x: window.innerWidth / 2, y: window.innerHeight / 2 }
}
function viewportCanvasCenter() {
const canvas = document.querySelector<HTMLCanvasElement>('[data-test-id="canvas-element"]')
if (canvas) {
const rect = canvas.getBoundingClientRect()
return { x: rect.width / 2, y: rect.height / 2 }
}
return { x: window.innerWidth / 2, y: window.innerHeight / 2 }
}
function toggleProfiler() {
editor.renderer?.profiler.toggle()
editor.requestRepaint()
}
return { viewportScreenCenter, toggleProfiler }
return { viewportScreenCenter, viewportCanvasCenter, toggleProfiler }
}

View file

@ -144,12 +144,8 @@ function openDetails(asset: LocalAsset) {
}
function insertionPoint(component: SceneNode, parentId: string) {
const canvas = document.querySelector<HTMLElement>('[data-test-id="canvas-area"]')
const rect = canvas?.getBoundingClientRect()
const center = editor.screenToCanvas(
(rect?.width ?? window.innerWidth) / 2,
(rect?.height ?? window.innerHeight) / 2
)
const canvasCenter = editor.viewportCanvasCenter()
const center = editor.screenToCanvas(canvasCenter.x, canvasCenter.y)
const parentOffset =
parentId === editor.state.currentPageId
? { x: 0, y: 0 }

View file

@ -1,5 +1,5 @@
<script setup lang="ts">
import { computed, nextTick, ref, watch, type Component } from 'vue'
import { computed, ref, type Component } from 'vue'
import {
AUTO_LAYOUT_PADDING_EDITOR_OFFSET_X,
AUTO_LAYOUT_PADDING_EDITOR_OFFSET_Y
@ -97,16 +97,6 @@ const paddingEditorIcon = computed(() => {
return edit ? paddingSideIcons[edit.side] : IconLucidePanelTop
})
watch(autoLayoutPaddingEdit, async (edit) => {
if (!edit) return
await nextTick()
const input = document.querySelector<HTMLInputElement>(
'[data-test-id="auto-layout-padding-editor"] input'
)
input?.focus()
input?.select()
})
const cursor = computed(() => toolCursor(store.state.activeTool, cursorOverride.value))
</script>

View file

@ -21,6 +21,7 @@ const { modelValue, min, max, step, icon, label, suffix, sensitivity, placeholde
const emit = defineEmits<{
'update:modelValue': [value: number]
'editing-change': [editing: boolean]
commit: [value: number, previous: number]
}>()
@ -38,7 +39,12 @@ defineOptions({ inheritAttrs: false })
:placeholder="placeholder"
@update:model-value="emit('update:modelValue', $event)"
@commit="(val: number, prev: number) => emit('commit', val, prev)"
@editing-change="store.state.scrubInputFocused = $event"
@editing-change="
(editing: boolean) => {
store.state.scrubInputFocused = editing
emit('editing-change', editing)
}
"
>
<div
v-bind="attrs"

View file

@ -2,6 +2,13 @@ import { expect, test, type Page } from '@playwright/test'
import { CanvasHelper } from '#tests/helpers/canvas'
async function clickCanvas(page: Page, x: number, y: number) {
const canvas = page.locator('[data-test-id="canvas-element"]')
const box = await canvas.boundingBox()
if (!box) throw new Error('Canvas has no bounding box')
await page.mouse.click(box.x + x, box.y + y)
}
async function dblclickCanvas(page: Page, x: number, y: number) {
const canvas = page.locator('[data-test-id="canvas-element"]')
const box = await canvas.boundingBox()
@ -85,6 +92,25 @@ test('double-clicking an auto-layout padding handle opens a scrub editor', async
canvas.assertNoErrors()
})
test('clicking the canvas closes the auto-layout padding editor', async ({ page }) => {
await page.goto('/')
const canvas = new CanvasHelper(page)
await canvas.waitForInit()
await canvas.clearCanvas()
await setupAutoLayoutFrame(page)
await canvas.waitForRender()
await canvas.hover(300, 130)
await dblclickCanvas(page, 300, 130)
const editor = page.locator('[data-test-id="auto-layout-padding-editor"]')
await expect(editor).toBeVisible()
await clickCanvas(page, 80, 80)
await expect(editor).toHaveCount(0)
canvas.assertNoErrors()
})
test('auto-layout padding editor follows canvas pan while open', async ({ page }) => {
await page.goto('/')
const canvas = new CanvasHelper(page)