diff --git a/lint/plugin.js b/lint/plugin.js index 69280a271..38faf6dfe 100644 --- a/lint/plugin.js +++ b/lint/plugin.js @@ -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, diff --git a/oxlint.json b/oxlint.json index 648d7b074..24b0946ae 100644 --- a/oxlint.json +++ b/oxlint.json @@ -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", diff --git a/packages/vue/src/canvas/useCanvasInput.ts b/packages/vue/src/canvas/useCanvasInput.ts index 56d271748..e287a2c75 100644 --- a/packages/vue/src/canvas/useCanvasInput.ts +++ b/packages/vue/src/canvas/useCanvasInput.ts @@ -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) diff --git a/src/app/editor/profiler/index.ts b/src/app/editor/profiler/index.ts index 722bfcb09..813d551c5 100644 --- a/src/app/editor/profiler/index.ts +++ b/src/app/editor/profiler/index.ts @@ -10,10 +10,19 @@ export function createProfilerActions(editor: Editor) { return { x: window.innerWidth / 2, y: window.innerHeight / 2 } } + function viewportCanvasCenter() { + const canvas = document.querySelector('[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 } } diff --git a/src/components/AssetsPanel.vue b/src/components/AssetsPanel.vue index 39b34d64e..5648a005b 100644 --- a/src/components/AssetsPanel.vue +++ b/src/components/AssetsPanel.vue @@ -144,12 +144,8 @@ function openDetails(asset: LocalAsset) { } function insertionPoint(component: SceneNode, parentId: string) { - const canvas = document.querySelector('[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 } diff --git a/src/components/EditorCanvas.vue b/src/components/EditorCanvas.vue index 323058509..ed1eed2df 100644 --- a/src/components/EditorCanvas.vue +++ b/src/components/EditorCanvas.vue @@ -1,5 +1,5 @@ diff --git a/src/components/ScrubInput.vue b/src/components/ScrubInput.vue index e87c00add..7aaaafc47 100644 --- a/src/components/ScrubInput.vue +++ b/src/components/ScrubInput.vue @@ -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) + } + " >
{ + 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)