feat: dock buttons stretch window to zone (edge/corner) + reposition; center keeps size

This commit is contained in:
Hermes Agent committed 2026-09-09 18:44:30 +02:00
1 parent 5984df1692
commit a38f7188ee
1 file changed
+33 -20
+33 -20
View File
@@ -9,8 +9,8 @@ export default function () {
let lastSize = { width: 320, height: 200 }
// Текущий режим докинга. null = окно свободно (не прижато).
// Пока не null — позиция пересчитывается при каждом изменении viewport
// (через поллинг, т.к. события viewportchange в API нет), иначе
// Пока не null — состояние (размер+позиция) пересчитывается при каждом изменении
// viewport (через поллинг, т.к. события viewportchange в API нет), иначе
// canvas-space-точка "уплывает" на экране при смене зума/пана.
let dockedPosition: DockPosition | null = null
@@ -25,36 +25,52 @@ export default function () {
// верх шапки, поэтому полная высота окна = (PLUGIN_HEADER_PX + height) / zoom.
const PLUGIN_HEADER_PX = 38
// Прижать окно к краю видимой области canvas (bounds) в canvas-space.
// ВАЖНО: reposition()/getPosition() и bounds — в canvas-space (единицы документа),
// а figma.ui.resize(width,height) — в ЭКРАННЫХ пикселях. Размер окна в canvas-units =\n // width / zoom (по ширине) и (PLUGIN_HEADER_PX + height) / zoom (по высоте, т.к. окно\n // включает шапку). Без учёта шапки прижатый к низу/центру окно висит на 38px выше нужного.
function applyDock (position: DockPosition, width: number, height: number) {
// Вычислить целевое состояние окна для позиции докинга:
// имитация настоящего дока — края/углы РАСТЯГИВАЮТ окно на всю соответствующую
// сторону viewport (по ширине тянутся лево/право/углы, по высоте — верх/низ/углы),
// центр НЕ растягивает (оставляет текущий размер). Возвращает размер (экранные px)
// и позицию (canvas-space, прижатую к нужному краю).
function computeDock (position: DockPosition, currentWidth: number, currentHeight: number) {
const b = figma.viewport.bounds
const zoom = figma.viewport.zoom || 1
const wCanvas = width / zoom
const hCanvas = (PLUGIN_HEADER_PX + height) / zoom
// Растягивать ли по осям:
const stretchX = position !== 'center' // всё кроме центра тянется по ширине
const stretchY =
position === 'top-left' || position === 'top-center' || position === 'top-right' ||
position === 'bottom-left' || position === 'bottom-center' || position === 'bottom-right'
const targetW = stretchX ? Math.round(b.width * zoom) : currentWidth
const targetH = stretchY ? Math.round(b.height * zoom) - PLUGIN_HEADER_PX : currentHeight
// canvas-units от целевого размера
const wCanvas = targetW / zoom
const hCanvas = (PLUGIN_HEADER_PX + targetH) / 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
}
// По вертикали (hCanvas учитывает шапку окна)
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
}
figma.ui.reposition(x, y)
return { width: targetW, height: targetH, x, y }
}
// Поллинг viewport: держим окно прижатым к краю при любом зуме/панорамировании.
// Применить состояние докинга: ресайзим окно (растяжение) + 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 }
return state
}
// Поллинг viewport: держим окно в док-состоянии (размер+позиция) при любом зуме/панораме.
// События viewportchange в Figma API НЕТ (нельзя подписаться через figma.on),
// поэтому опрашиваем bounds и репозиционируем только когда он реально изменился.
// Это предотвращает "уплывание" окна на экране: reposition() работает в canvas-space,
// а bounds при зуме сжимается/растягивается — без пересчёта точка уезжает.
// поэтому опрашиваем bounds и пере-применяем док только когда он реально изменился.
let lastBoundsKey = ''
const dockInterval = setInterval(function () {
if (dockedPosition === null) return
@@ -92,15 +108,12 @@ export default function () {
}
})
// Эмуляция докинга: прижать окно к краям canvas (9 позиций).
// Эмуляция докинга: растянуть окно на зону и прижать к её краю (9 позиций).
on('DOCK_WINDOW', function (payload: { position: DockPosition; width: number; height: number }) {
const { position } = payload
const { width, height } = clampWindowSize(payload)
lastSize = { width, height }
figma.ui.resize(width, height)
dockedPosition = position
lastBoundsKey = '' // форсируем пересчёт на ближайшем тике
applyDock(position, width, height)
applyDock(position, payload.width, payload.height)
})
// Останавливаем поллинг при закрытии плагина, чтобы не висел в памяти.