Разметка блоков и портов
<VisualLinker> находит свои блоки и порты в вашей собственной разметке. Пометить их можно тремя способами — директивами v-vl-block/v-vl-port, обычными data-vl-* атрибутами и пропом blocks компонента — и все три наполняют один и тот же реестр, так что их можно свободно смешивать в одной диаграмме.
Регистрация
Глобальная регистрация компонента и обеих директив:
import { createApp } from 'vue'
import { VisualLinkerPlugin } from '@macrulez/visual-linker-vue'
createApp(App).use(VisualLinkerPlugin).mount('#app')…или импорт в каждом компоненте — в <script setup> vVlBlock/vVlPort автоматически становятся v-vl-block/v-vl-port:
import { VisualLinker, vVlBlock, vVlPort } from '@macrulez/visual-linker-vue'С модулем Nuxt обе директивы регистрируются за вас, в том числе на сервере.
v-vl-block
Помечает любой элемент — на любой глубине, внутри любого компонента-обёртки — как блок.
<div v-vl-block="'b13'">…</div>
<div
v-vl-block="{ id: 'b13', draggable: true, dragHandle: '.title', dragBounds: 'container' }"
>…</div>Просто строка — сокращение для { id }.
Опции
id
string · обязательна
Id блока, на который ссылаются соединения через from.blockId/to.blockId.
linker
string · по умолчанию: — (ближайший объемлющий <VisualLinker>)
Закрепляет блок за <VisualLinker name="…"> с этим именем, где бы тот ни находился на странице.
draggable
boolean · по умолчанию: берётся из config.blocks.draggable
dragHandle
string | Ref | геттер | HTMLElement · по умолчанию: — (весь блок)
CSS-селектор внутри блока или template ref/геттер/элемент.
dragBounds
'container' | string | Ref | геттер | HTMLElement | DragBoundsInset · по умолчанию: берётся из config.blocks.drag.bounds
Смотрите Перетаскивание. Строка-селектор ищется по всему документу.
portSpread
boolean | { gap?: number; padding?: number } · по умолчанию: берётся из config.ports.spread
Разводит соединения, делящие сторону порта этого блока — смотрите Разведение соединений по порту.
highlightable
boolean · по умолчанию: берётся из config.interaction.highlight
Наведение на этот блок подсвечивает его соединения. См. Визуальные состояния.
v-vl-port
Помечает элемент как порт соединения. Точка порта измеряется на этом элементе; порт принадлежит ближайшему предку, зарегистрированному как блок, если только block не указывает блок явно.
<div v-vl-block="'b13'">
<div v-vl-port="{ id: 'row1', side: ['left', 'right'], anchorBlockId: 'b13' }">Строка 1</div>
</div>
<!-- явная привязка к блоку, из любого места в области -->
<span v-vl-port="{ id: 'out', block: 'b13' }" />Просто строка — сокращение для { id }.
Опции
id
string · обязательна
Id порта, на который ссылаются соединения через from.portId/to.portId.
block
string · по умолчанию: — (ближайший блок-предок)
Id блока-владельца.
linker
string · по умолчанию: — (ближайший объемлющий <VisualLinker>)
То же, что linker у v-vl-block.
side
'top' | 'right' | 'bottom' | 'left' | 'auto' | FixedSide[] · по умолчанию: 'auto'
offset
number · по умолчанию: 0.5
Положение вдоль стороны, 0..1.
anchorBlockId
string · по умолчанию: — (не задано)
anchorEl
Ref | геттер | HTMLElement · по умолчанию: — (не задано)
side, offset, anchorBlockId и anchorEl значат ровно то же, что у PortDescriptor ядра — смотрите Блоки и порты.
spread
boolean | { gap?: number; padding?: number } · по умолчанию: берётся из portSpread блока
Собственное разведение порта, как у PortDescriptor ядра — смотрите Разведение соединений по порту.
Data-атрибуты
Вообще без JavaScript — удобно для серверной или сторонней разметки:
| Атрибут | На элементе | Значение |
|---|---|---|
data-vl-block="id" | блок | регистрирует элемент как блок |
data-vl-draggable | блок | ""/"true" → перетаскиваемый, "false" → нет |
data-vl-highlightable | block | ""/"true" → подсвечивает свои соединения, "false" → нет |
data-vl-drag-handle=".sel" | блок | ручка перетаскивания, селектор внутри блока |
data-vl-drag-bounds="container" | блок | container или CSS-селектор элемента-ограничителя |
data-vl-port="id" | порт | регистрирует порт на ближайшем блоке вокруг него |
data-vl-port-block="id" | порт | …или явно на этом блоке |
data-vl-side="left right" | порт | одна сторона или список кандидатов через пробел/запятую |
data-vl-offset="0.3" | порт | положение вдоль стороны, 0..1 |
data-vl-anchor="id" | порт | anchorBlockId — точка рисуется на границе этого блока |
data-vl-port-spread="24 8" | блок | portSpread: "" включено, "false" выключено или gap [padding] |
data-vl-spread="false" | порт | собственный spread порта, те же значения |
data-vl-linker="name" | блок/порт | закрепляет элемент за <VisualLinker name="…"> с этим именем |
Директивы сами записывают на элемент data-vl-block/data-vl-port (плюс data-vl-port-block/data-vl-linker, если заданы), так что один проход по DOM находит и элементы с директивами, и элементы с атрибутами. Все имена атрибутов также экспортируются константой VL_ATTR.
Проп blocks
Для рефов, которые хранятся в <script>, или элементов, на которые нельзя добавить атрибуты:
const cardRef = useTemplateRef('card')
const blocks = [
{ id: 'a', el: cardRef, ports: [{ id: 'p', target: rowRef }] }, // template ref
{ id: 'b', el: '#legacy-widget' }, // CSS-селектор
{ id: 'c', draggable: false }, // без el: доп. конфигурация для блока, помеченного в шаблоне
]Все поля — в описании пропа blocks.
Приоритет
Для одного и того же id блока поле из пропа blocks побеждает опции директивы, а те — data-атрибуты. Порт, заданный в пропе blocks, сохраняет приоритет над помеченным портом с тем же id на этом блоке.
Какому <VisualLinker> принадлежит элемент
- явное имя
data-vl-linker/linkerпобеждает; - иначе элемент принадлежит ближайшему объемлющему
<VisualLinker>— так вложенные инстансы никогда не забирают блоки друг у друга; - элемент вне всех
<VisualLinker>принадлежит только инстансам соscope="page".
Если на странице несколько инстансов со scope="page", дайте каждому name и закрепляйте элементы явно.
Живое обнаружение
Блоки и порты, добавленные, удалённые или перепомеченные позже — v-if, v-for, сторонняя разметка, выставляющая атрибуты, — подхватываются автоматически: компонент следит за изменениями data-vl-* в своей области (во всём <body> при scope="page"), а директивы сами уведомляют его об изменении опций, которые не отражаются в атрибутах. Повторный проход, не нашедший ничего нового, движок не трогает.
Перетаскивание и инлайн-стили
Сдвиг при перетаскивании применяется через отдельное CSS-свойство translate, поэтому он складывается с любой transform, которая уже есть у блока. Избегайте строковой привязки :style на перетаскиваемом блоке — Vue заменяет весь инлайн-стиль при каждом изменении этой строки и вместе с ним стирает сдвиг. Привязка :style объектом безопасна.
SSR
Директивы выводят свои data-vl-* атрибуты и при серверном рендеринге, так что разметка находится ещё до гидратации. Движок и его слой отрисовки существуют только на клиенте, после монтирования.