fix(app): align variable-enabled panel fields
- Keep Apply variable actions visible with restrained hover treatment - Preserve the shared 26px NumberField height in column layouts - Anchor variable pickers to their trigger instead of the zero-size contents wrapper - Cover control dimensions, action visibility, and popover placement end to end
This commit is contained in:
parent
86d6a63d0f
commit
fc5cc2d4b8
|
|
@ -18,6 +18,7 @@
|
|||
- Add saved per-node export settings for repeat exports.
|
||||
- Add Design panel controls for layer blend modes and alpha, vector, and luminance masks.
|
||||
- Refine Design panel foundations with 26px controls, consistently aligned action rails, shared Tailwind themes, and Storybook component states.
|
||||
- Fix variable binding action visibility, picker anchoring, hover treatment, and Design panel number-field heights.
|
||||
- Scale the Layers panel to 5,000-node documents with virtualized rows, indexed updates, scroll-to-selection, range selection, and focus-aware themed states.
|
||||
- Add Figma-style horizontal and vertical constraint controls with pin interactions, mixed-selection editing, undo, and responsive frame resizing.
|
||||
- Add mixed-selection stroke cap, join, and miter-limit controls with CanvasKit rendering and `.fig` roundtrip support.
|
||||
|
|
|
|||
|
|
@ -99,7 +99,7 @@ defineOptions({ inheritAttrs: false })
|
|||
|
||||
<template>
|
||||
<BindableValuePicker v-slot="picker">
|
||||
<ComboboxAnchor class="contents" data-slot="anchor">
|
||||
<ComboboxAnchor class="inline-flex shrink-0 items-center" data-slot="anchor">
|
||||
<Tip :label="triggerLabel">
|
||||
<ComboboxTrigger as-child>
|
||||
<BindingTrigger
|
||||
|
|
|
|||
|
|
@ -1,14 +1,10 @@
|
|||
import { panelIconButtonBase } from './panel/field'
|
||||
|
||||
const bindingFieldTheme = {
|
||||
slots: {
|
||||
root: 'group/binding min-w-0',
|
||||
root: 'min-w-0',
|
||||
pill: 'flex min-w-0 flex-1 items-center overflow-hidden rounded-sm px-1 text-component outline-none',
|
||||
pillLabel: 'min-w-0 flex-1 truncate text-[11px] font-medium',
|
||||
trigger: [
|
||||
panelIconButtonBase,
|
||||
'size-5 rounded-sm transition-opacity data-[state=unbound]:opacity-0 data-[state=mixed]:opacity-0 group-hover/binding:opacity-100 group-focus-within/binding:opacity-100 data-[open]:bg-hover data-[open]:text-component disabled:opacity-0 data-[disabled]:opacity-0'
|
||||
],
|
||||
trigger:
|
||||
'flex size-5 shrink-0 cursor-pointer items-center justify-center rounded-sm border border-transparent bg-transparent text-muted outline-none transition-colors hover:text-surface focus-visible:border-panel-focus data-[open]:bg-hover data-[open]:text-component disabled:opacity-0 data-[disabled]:opacity-0',
|
||||
pickerContent:
|
||||
'z-[100] w-56 overflow-hidden rounded-lg border border-border bg-panel text-surface shadow-xl',
|
||||
pickerSearch:
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ const numberFieldTheme = {
|
|||
slots: {
|
||||
root: [
|
||||
panelFieldBase,
|
||||
'group flex flex-1 cursor-ew-resize items-center tabular-nums data-[disabled]:cursor-auto data-[editing]:cursor-auto'
|
||||
'group flex flex-auto cursor-ew-resize items-center tabular-nums data-[disabled]:cursor-auto data-[editing]:cursor-auto'
|
||||
],
|
||||
leading:
|
||||
'flex shrink-0 items-center justify-center self-stretch px-[5px] text-muted select-none [&>*]:pointer-events-none',
|
||||
|
|
|
|||
|
|
@ -20,6 +20,43 @@ test('property sections collapse and reopen from their title', async () => {
|
|||
await expect(blendMode).toBeVisible()
|
||||
})
|
||||
|
||||
test('appearance fields share control height and show variable actions', async () => {
|
||||
await editor.canvas.clearCanvas()
|
||||
await editor.canvas.drawRect(200, 200, 80, 80)
|
||||
|
||||
const section = propertySection(editor.page, 'Appearance')
|
||||
const controls = [
|
||||
section.getByRole('combobox', { name: 'Blend mode' }),
|
||||
section.getByRole('spinbutton', { name: 'Opacity' }),
|
||||
section.getByRole('spinbutton', { name: 'Radius' }),
|
||||
section.getByRole('spinbutton', { name: 'Corner smoothing' })
|
||||
]
|
||||
for (const control of controls) {
|
||||
await expect(control).toHaveCSS('height', '26px')
|
||||
}
|
||||
|
||||
const applyVariable = section.getByRole('button', { name: 'Apply variable' }).first()
|
||||
await expect(applyVariable).toBeVisible()
|
||||
await expect(applyVariable).toHaveCSS('opacity', '1')
|
||||
await expect(applyVariable).toHaveCSS('width', '20px')
|
||||
await expect(applyVariable).toHaveCSS('height', '20px')
|
||||
|
||||
await applyVariable.hover()
|
||||
await expect(applyVariable).toHaveCSS('background-color', 'rgba(0, 0, 0, 0)')
|
||||
|
||||
await applyVariable.click()
|
||||
const search = editor.page.getByPlaceholder('Search')
|
||||
const picker = editor.page.locator('[data-slot=content]').filter({ has: search })
|
||||
await expect(picker).toBeVisible()
|
||||
const triggerBox = expectDefined(await applyVariable.boundingBox(), 'variable trigger bounds')
|
||||
const pickerBox = expectDefined(await picker.boundingBox(), 'variable picker bounds')
|
||||
expect(pickerBox.x + pickerBox.width).toBeLessThan(triggerBox.x)
|
||||
expect(
|
||||
Math.abs(pickerBox.y + pickerBox.height / 2 - (triggerBox.y + triggerBox.height / 2))
|
||||
).toBeLessThan(4)
|
||||
await search.press('Escape')
|
||||
})
|
||||
|
||||
test('NumberField drag changes X position', async () => {
|
||||
await editor.canvas.clearCanvas()
|
||||
await editor.canvas.drawRect(100, 100, 80, 80)
|
||||
|
|
|
|||
Loading…
Reference in a new issue