fix: manual resize no longer resets on pointerup (canonical vs hold mode)

This commit is contained in:
Hermes Agent committed 2026-09-09 20:43:46 +02:00
1 parent 20777fb0de
commit 6901c5e0cd
1 file changed
+49 -24
+49 -24
View File
@@ -38,7 +38,12 @@ export default function () {
// Возвращает размер (экранные px) и позицию (canvas-space, прижатую к нужному краю). // Возвращает размер (экранные px) и позицию (canvas-space, прижатую к нужному краю).
const DEFAULT_DOCK_EDGE = 320 const DEFAULT_DOCK_EDGE = 320
let dockEdgeSize = DEFAULT_DOCK_EDGE // редактируемая фикс-сторона для edge-позиций (320 по умолч.) let dockEdgeSize = DEFAULT_DOCK_EDGE // редактируемая фикс-сторона для edge-позиций (320 по умолч.)
function computeDock (position: DockPosition, currentWidth: number, currentHeight: number) { // computeDock считает целевой размер+позицию для док-позиции.
// canonical=true — НАЖАТИЕ кнопки дока: задаём канонический размер
// (edge: фикс-сторона dockEdgeSize, другая = max viewport; center: 50%).
// canonical=false — удержание/ресайз: сохраняем РУЧНОЙ размер (currentW/H),
// только клампим в пределы viewport. Так ручной ресайз "прилипает" и не сбрасывается.
function computeDock (position: DockPosition, currentWidth: number, currentHeight: number, canonical: boolean = false) {
const b = figma.viewport.bounds const b = figma.viewport.bounds
const zoom = figma.viewport.zoom || 1 const zoom = figma.viewport.zoom || 1
const maxW = Math.round(b.width * zoom) const maxW = Math.round(b.width * zoom)
@@ -52,13 +57,13 @@ export default function () {
let targetH = currentHeight let targetH = currentHeight
if (isLeftRight) { if (isLeftRight) {
targetW = dockEdgeSize targetW = dockEdgeSize
targetH = maxH targetH = canonical ? maxH : Math.min(currentHeight, maxH)
} else if (isTopBottom) { } else if (isTopBottom) {
targetW = maxW targetW = canonical ? maxW : Math.min(currentWidth, maxW)
targetH = dockEdgeSize targetH = dockEdgeSize
} else if (isCenter) { } else if (isCenter) {
targetW = Math.round(maxW / 2) targetW = canonical ? Math.round(maxW / 2) : currentWidth
targetH = Math.round(maxH / 2) targetH = canonical ? Math.round(maxH / 2) : currentHeight
} else if (isCorner) { } else if (isCorner) {
// размер редактируется вручную, только клампим в пределы viewport // размер редактируется вручную, только клампим в пределы viewport
targetW = Math.min(currentWidth, maxW) targetW = Math.min(currentWidth, maxW)
@@ -82,13 +87,38 @@ export default function () {
return { width: targetW, height: targetH, x, y } return { width: targetW, height: targetH, x, y }
} }
// Применить состояние докинга: ресайзим окно (растяжение) + reposition (прижатие). // Только позиция (без изменения размера) для данного размера — используется при
// Сначала resize, иначе reposition считает позицию для старого размера. // ручном ресайзе, чтобы переприжать окно к док-краю/центру, сохранив ручной размер.
function applyDock (position: DockPosition, width: number, height: number) { function computeDockPos (position: DockPosition, width: number, height: number) {
const state = computeDock(position, width, height) const b = figma.viewport.bounds
figma.ui.resize(state.width, state.height) const zoom = figma.viewport.zoom || 1
figma.ui.reposition(state.x, state.y) const wCanvas = width / zoom
lastSize = { width: state.width, height: state.height } const hCanvas = (PLUGIN_HEADER_PX + height) / zoom
let x = b.x
let y = b.y
if (position === 'top-center' || position === 'center' || position === 'bottom-center') {
x = b.x + (b.width - wCanvas) / 2
} else if (position === 'top-right' || position === 'center-right' || position === 'bottom-right') {
x = b.x + b.width - wCanvas
}
if (position === 'center-left' || position === 'center' || position === 'center-right') {
y = b.y + (b.height - hCanvas) / 2
} else if (position === 'bottom-left' || position === 'bottom-center' || position === 'bottom-right') {
y = b.y + b.height - hCanvas
}
return { x, y }
}
// Применить состояние докинга.
// canonical=true (нажатие кнопки): computeDock задаёт канонический размер.
// canonical=false (удержание/ресайз): сохраняем РУЧНОЙ размер, только переприжимаем позицию.
function applyDock (position: DockPosition, width: number, height: number, canonical: boolean = false) {
const state = computeDock(position, width, height, canonical)
// Размер меняем, только если канон (иначе ручной ресайз сбросится на отпускании).
if (canonical) figma.ui.resize(state.width, state.height)
const pos = canonical ? { x: state.x, y: state.y } : computeDockPos(position, width, height)
figma.ui.reposition(pos.x, pos.y)
lastSize = { width: canonical ? state.width : width, height: canonical ? state.height : height }
return state return state
} }
@@ -102,7 +132,7 @@ export default function () {
const key = `${b.x}|${b.y}|${b.width}|${b.height}` const key = `${b.x}|${b.y}|${b.width}|${b.height}`
if (key !== lastBoundsKey) { if (key !== lastBoundsKey) {
lastBoundsKey = key lastBoundsKey = key
applyDock(dockedPosition, lastSize.width, lastSize.height) applyDock(dockedPosition, lastSize.width, lastSize.height, false)
} }
}, 250) }, 250)
@@ -172,18 +202,13 @@ export default function () {
const { width, height } = clampWindowSize(windowSize) const { width, height } = clampWindowSize(windowSize)
lastSize = { width, height } lastSize = { width, height }
figma.ui.resize(width, height) figma.ui.resize(width, height)
// Если окно задокено — держим прижатым к краю при смене размера. // Если окно задокено/в центре — переприжимаем ПОЗИЦИЮ к краю/центру, но размер
// При этом фикс-сторона edge-позиции (dockEdgeSize) ОБНОВЛЯЕТСЯ из ручного // НЕ трогаем (canonical=false), чтобы ручной ресайз не сбрасывался на отпускании.
// ресайза, чтобы пользователь мог менять её (а не была заблокирована на 320).
if (dockedPosition !== null) { if (dockedPosition !== null) {
const isLeftRight = dockedPosition === 'center-left' || dockedPosition === 'center-right' applyDock(dockedPosition, width, height, false)
const isTopBottom = dockedPosition === 'top-center' || dockedPosition === 'bottom-center'
if (isLeftRight) dockEdgeSize = width
else if (isTopBottom) dockEdgeSize = height
applyDock(dockedPosition, width, height)
} else if (centerLocked) { } else if (centerLocked) {
// Центр: позиция удерживается по центру при ресайзе (но не поллит за зумом/паном). // Центр: позиция удерживается по центру при ресайзе (но не поллит за зумом/паном).
applyDock('center', width, height) applyDock('center', width, height, false)
} }
}) })
@@ -195,14 +220,14 @@ export default function () {
if (position === 'center') { if (position === 'center') {
dockedPosition = null dockedPosition = null
centerLocked = true centerLocked = true
applyDock('center', width, height) applyDock('center', width, height, true)
return return
} }
// Остальные позиции — удерживаем прижатым (поллинг пересчитывает при зуме/пане). // Остальные позиции — удерживаем прижатым (поллинг пересчитывает при зуме/пане).
centerLocked = false centerLocked = false
dockedPosition = position dockedPosition = position
lastBoundsKey = '' // форсируем пересчёт на ближайшем тике lastBoundsKey = '' // форсируем пересчёт на ближайшем тике
applyDock(position, width, height) applyDock(position, width, height, true)
}) })
// Останавливаем поллинг при закрытии плагина, чтобы не висел в памяти. // Останавливаем поллинг при закрытии плагина, чтобы не висел в памяти.