Skip to content

Блоки и порты ​

Блок — это любой DOM-элемент, который уже находится под вашим контролем — движок только измеряет его (и его порты) и никогда не трогает содержимое или раскладку, кроме опциональной трансформации при перетаскивании.

Определение блока ​

BlockDescriptor

id ​

string

Уникальный id блока, на который ссылаются from.blockId/to.blockId соединения.

el ​

HTMLElement

Собственный элемент блока.

ports ​

PortDescriptor[] · по умолчанию: — (не задано)

Явные порты — смотрите «Определение порта» ниже. Если не задано, соединения крепятся прямо к границе блока, с side: 'auto'.

draggable ​

boolean · по умолчанию: берётся из blocks.draggable

Переопределяет дефолт уровня инстанса для этого конкретного блока.

dragHandle ​

string | HTMLElement · по умолчанию: — (перетаскивание начинается с любой точки блока)

CSS-селектор или элемент внутри el, с которого начинается перетаскивание вместо всего блока.

dragBounds ​

DragBounds · по умолчанию: берётся из blocks.drag.bounds

Переопределяет дефолт уровня инстанса для этого конкретного блока. Три допустимые формы — смотрите «Перетаскивание» ниже.

portSpread ​

boolean | { gap?: number; padding?: number } · по умолчанию: берётся из ports.spread

Разводит соединения, делящие сторону порта этого блока, по отдельным виртуальным портам. spread самого порта переопределяет его. См. Разведение соединений по порту.

highlightable ​

boolean · по умолчанию: берётся из interaction.highlight

Включает или выключает состояние highlight соединений этого блока, пока курсор над блоком; локальное значение сильнее глобального. См. Визуальные состояния.

Пример — все поля сразу:

ts
linker.setBlocks([
  {
    id: 'task-1', // уникальный id — соединения ссылаются на него через from.blockId/to.blockId
    el: taskCardEl, // собственный элемент блока
    ports: [{ id: 'output', side: 'right', offset: 0.5 }], // опционально, см. «Определение порта» ниже — можно не задавать совсем, тогда соединения крепятся к границе блока
    draggable: true, // этот блок можно перетаскивать, даже если дефолт уровня инстанса — false
    dragHandle: '.card-header', // перетаскивание начинается только с этой части блока — без этого поля начинает весь блок
    dragBounds: 'container', // этот блок нельзя перетащить за пределы собственного контейнера диаграммы
    portSpread: { gap: 20 }, // соединения, делящие сторону этого блока, получают каждое свой виртуальный порт на расстоянии 20px
    highlightable: true, // наведение на этот блок подсвечивает его соединения, даже если interaction.highlight равен false
  },
])

Определение порта ​

PortDescriptor

Массив ports у блока позволяет соединению крепиться к чему-то более конкретному, чем граница блока — к определённой строке списка, к конкретному полю формы.

id ​

string

Id порта, на который ссылается portId конечной точки соединения.

target ​

string | HTMLElement · по умолчанию: — (собственный элемент блока)

CSS-селектор (резолвится относительно el блока) или прямой элемент — куда на самом деле садится точка подключения порта.

side ​

PortSide | FixedSide[] · по умолчанию: 'auto'

'auto' определяет сторону входа/выхода из положения второго конца соединения при каждом рендере, выбирая ту сторону, что лучше всего смотрит на неё. FixedSide[] (подмножество 'top' | 'right' | 'bottom' | 'left') сужает этот автоматический выбор — например, ['left', 'right'], чтобы полностью исключить выход сверху/снизу. Одна фиксированная сторона закрепляет её жёстко.

offset ​

number · по умолчанию: 0.5

Позиция вдоль выбранной стороны, 0..1 (0.5 — по центру). Игнорируется, если задан anchorBlockId/anchorEl.

anchorBlockId ​

string · по умолчанию: — (не задано)

Рисует точку подключения этого порта на границе другого зарегистрированного блока вместо границы target — полезно, когда target — это дочерний элемент, вложенный глубоко внутрь контейнера (например, строка внутри блока-группы), но соединение визуально должно выходить с края самого контейнера. Точка при этом всё равно отслеживает реальное положение target, спроецированное на границу блока-якоря, так что соседние порты, закреплённые на одном и том же блоке, сохраняют свой относительный порядок, а не схлопываются в одну точку. Определение стороны (включая 'auto') идёт по блоку-якорю, а не по target. Игнорируется, если также задан anchorEl.

anchorEl ​

HTMLElement · по умолчанию: — (не задано)

Как anchorBlockId, но крепится прямо к заданному элементу вместо el зарегистрированного блока — для привязки к элементу, который ещё не (или вообще не) является одним из зарегистрированных блоков движка. Имеет приоритет над anchorBlockId, если заданы оба.

spread ​

boolean | { gap?: number; padding?: number } · по умолчанию: берётся из portSpread блока

Разводит соединения, делящие сторону этого порта. false исключает порт, даже если его блок разводит соединения. См. Разведение соединений по порту.

Пример — все поля сразу:

ts
{
  id: 'output', // на него ссылается from.portId/to.portId соединения
  target: '.output-row', // CSS-селектор внутри блока — без него используется собственный элемент блока
  side: ['right', 'bottom'], // 'auto' здесь может выбрать только right или bottom, никогда top/left
  offset: 0.25, // в этом примере игнорируется, так как anchorBlockId ниже переопределяет, где на самом деле сидит точка
  anchorBlockId: 'group-1', // точка рисуется на границе блока "group-1", но всё равно отслеживает реальное положение target
  spread: true, // соединения, делящие сторону этого порта, получают виртуальные порты
}

Если вместо anchorBlockId задать anchorEl: someElement, порт крепится прямо к этому элементу, а не к зарегистрированному блоку — и побеждает, если заданы оба поля одновременно.

Перетаскивание ​

Любой блок можно сделать перетаскиваемым — задайте blocks.draggable: true в конфигурации для всех блоков либо draggable у одного блока (BlockDescriptor.draggable, побеждает, если задано). Перетаскивание управляется указателем, двигает блок через отдельное CSS-свойство translate — поэтому сдвиг складывается с уже имеющейся у блока transform, а не затирает её, — а каждое связанное соединение перестраивается в реальном времени по мере движения блока — без ручного вызова повторного рендера.

ts
const linker = createVisualLinker(diagramEl, {
  blocks: {
    draggable: true, // любой блок можно перетаскивать, если сам не задаёт draggable: false
    drag: {
      grid: 20, // перетаскиваемые блоки привязываются к общей сетке 20px, так что независимо передвинутые блоки всё равно совпадают по линиям
      bounds: 'container', // ни один блок нельзя перетащить за пределы собственного контейнера диаграммы
    },
  },
})

linker.setBlocks([
  {
    id: 'task-1', // id этого блока
    el: taskCardEl, // собственный элемент этого блока
    dragHandle: '.card-header', // перетаскивание начинает только заголовок — остальная часть карточки инертна
  },
  {
    id: 'task-2', // id этого блока
    el: otherCardEl, // собственный элемент этого блока
    draggable: false, // этот блок исключён, даже если дефолт инстанса выше — true
  },
])

dragBounds (для всех блоков через blocks.drag.bounds или для конкретного блока через BlockDescriptor.dragBounds) ограничивает позицию перетаскиваемого блока так, что его собственный прямоугольник никогда не выходит за границы:

  • 'container' — собственный элемент-контейнер движка, как в примере выше.
  • HTMLElement — произвольная область элемента (например, отдельная drop-зона в другом месте раскладки, не обязательно сам контейнер).
  • объект DragBoundsInset ({ top?, right?, bottom?, left? }, в px, незаданные стороны по умолчанию 0) — собственная область контейнера, уменьшенная на эти отступы, например { top: 16, bottom: 16 }, чтобы перетаскиваемые блоки не заходили под перекрывающие контейнер шапку/подвал.

Не задано ни на одном из уровней — без ограничений, поведение по умолчанию.

События перетаскивания (block:dragstart/block:drag/block:dragend) описаны на странице API движка.