diff --git a/src/main.ts b/src/main.ts index d1528f1..89200d4 100644 --- a/src/main.ts +++ b/src/main.ts @@ -38,7 +38,12 @@ export default function () { // Возвращает размер (экранные px) и позицию (canvas-space, прижатую к нужному краю). const DEFAULT_DOCK_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 zoom = figma.viewport.zoom || 1 const maxW = Math.round(b.width * zoom) @@ -52,13 +57,13 @@ export default function () { let targetH = currentHeight if (isLeftRight) { targetW = dockEdgeSize - targetH = maxH + targetH = canonical ? maxH : Math.min(currentHeight, maxH) } else if (isTopBottom) { - targetW = maxW + targetW = canonical ? maxW : Math.min(currentWidth, maxW) targetH = dockEdgeSize } else if (isCenter) { - targetW = Math.round(maxW / 2) - targetH = Math.round(maxH / 2) + targetW = canonical ? Math.round(maxW / 2) : currentWidth + targetH = canonical ? Math.round(maxH / 2) : currentHeight } else if (isCorner) { // размер редактируется вручную, только клампим в пределы viewport targetW = Math.min(currentWidth, maxW) @@ -82,13 +87,38 @@ export default function () { return { width: targetW, height: targetH, x, y } } - // Применить состояние докинга: ресайзим окно (растяжение) + reposition (прижатие). - // Сначала resize, иначе reposition считает позицию для старого размера. - function applyDock (position: DockPosition, width: number, height: number) { - const state = computeDock(position, width, height) - figma.ui.resize(state.width, state.height) - figma.ui.reposition(state.x, state.y) - lastSize = { width: state.width, height: state.height } + // Только позиция (без изменения размера) для данного размера — используется при + // ручном ресайзе, чтобы переприжать окно к док-краю/центру, сохранив ручной размер. + function computeDockPos (position: DockPosition, width: number, height: number) { + const b = figma.viewport.bounds + const zoom = figma.viewport.zoom || 1 + const wCanvas = width / zoom + 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 } @@ -102,7 +132,7 @@ export default function () { const key = `${b.x}|${b.y}|${b.width}|${b.height}` if (key !== lastBoundsKey) { lastBoundsKey = key - applyDock(dockedPosition, lastSize.width, lastSize.height) + applyDock(dockedPosition, lastSize.width, lastSize.height, false) } }, 250) @@ -172,18 +202,13 @@ export default function () { const { width, height } = clampWindowSize(windowSize) lastSize = { width, height } figma.ui.resize(width, height) - // Если окно задокено — держим прижатым к краю при смене размера. - // При этом фикс-сторона edge-позиции (dockEdgeSize) ОБНОВЛЯЕТСЯ из ручного - // ресайза, чтобы пользователь мог менять её (а не была заблокирована на 320). + // Если окно задокено/в центре — переприжимаем ПОЗИЦИЮ к краю/центру, но размер + // НЕ трогаем (canonical=false), чтобы ручной ресайз не сбрасывался на отпускании. if (dockedPosition !== null) { - const isLeftRight = dockedPosition === 'center-left' || dockedPosition === 'center-right' - const isTopBottom = dockedPosition === 'top-center' || dockedPosition === 'bottom-center' - if (isLeftRight) dockEdgeSize = width - else if (isTopBottom) dockEdgeSize = height - applyDock(dockedPosition, width, height) + applyDock(dockedPosition, width, height, false) } else if (centerLocked) { // Центр: позиция удерживается по центру при ресайзе (но не поллит за зумом/паном). - applyDock('center', width, height) + applyDock('center', width, height, false) } }) @@ -195,14 +220,14 @@ export default function () { if (position === 'center') { dockedPosition = null centerLocked = true - applyDock('center', width, height) + applyDock('center', width, height, true) return } // Остальные позиции — удерживаем прижатым (поллинг пересчитывает при зуме/пане). centerLocked = false dockedPosition = position lastBoundsKey = '' // форсируем пересчёт на ближайшем тике - applyDock(position, width, height) + applyDock(position, width, height, true) }) // Останавливаем поллинг при закрытии плагина, чтобы не висел в памяти.