Skip to content

Компонент VisualLinker ​

Рисует соединения между элементами, которые вы размещаете сами. Поместите любой шаблон в default-слот — блоки могут находиться на любой глубине, внутри любых компонентов-обёрток — и пометьте их директивами v-vl-block/v-vl-port, обычными data-vl-* атрибутами или передайте через проп blocks. Компонент не добавляет обёрток на блоки и не трогает вашу разметку или раскладку: он только измеряет помеченные элементы и рисует линии в собственном слое.

vue
<script setup lang="ts">
import { VisualLinker, vVlBlock } from '@macrulez/visual-linker-vue'

const connections = [{ id: 'a-b', from: { blockId: 'a' }, to: { blockId: 'b' } }]
</script>

<template>
  <VisualLinker :connections="connections">
    <MyLayout>
      <MyCard v-vl-block="'a'" />
      <SidePanel>
        <div data-vl-block="b">Обычный HTML тоже работает</div>
      </SidePanel>
    </MyLayout>
  </VisualLinker>
</template>

Как помечать блоки и порты и какому <VisualLinker> принадлежит элемент — смотрите на странице Разметка блоков и портов.

Пропсы ​

connections ​

ConnectionDescriptor[] · обязателен

Смотрите Соединения. Отслеживается глубоко — изменение style существующего соединения на месте тоже вызывает перерисовку.

blocks ​

VisualLinkerBlock[] · по умолчанию: []

Явно переданные блоки — для элементов, которые хранятся рефами в <script>, или элементов, на которые нельзя добавить атрибуты. Та же форма, что у BlockDescriptor ядра (смотрите Блоки и порты), причём каждое поле-элемент принимает также Vue template ref или геттер:

  • el — template ref, геттер, обычный элемент или CSS-селектор. Селектор ищется внутри области компонента, а при scope="page" — по всему документу. Без el запись только добавляет конфигурацию (порты, настройки перетаскивания) блоку, уже помеченному в шаблоне с тем же id.
  • ports — target/anchorEl порта тоже принимают template ref.
  • draggable, dragHandle, dragBounds — как в ядре.

Для одного и того же id поле, заданное здесь, побеждает опции директивы, а те — data-атрибуты.

config ​

VisualLinkerConfig · по умолчанию: {}

Конфигурация диаграммы — см. Структура конфигурации. Она реактивна: замените объект или измените реактивный, и диаграмма перерисуется с новыми значениями; смена темы или включение lines.jumps не требуют повторного монтирования. Незаданные поля берутся из общей конфигурации приложения (её предоставляют модуль Nuxt и VisualLinkerPlugin), которая сливается с этим config по полям, причём побеждает он, а затем из встроенных значений.

selected ​

string[] · по умолчанию: — (не задано, выделением владеет движок)

Id выделенных соединений, для v-model:selected — нужен config.interaction.selectable. См. Выделение и доступность.

scope ​

'container' | 'page' · по умолчанию: 'container'

  • 'container' — блоки находятся где угодно внутри default-слота компонента; линии рисуются в слое внутри собственного бокса компонента.
  • 'page' — блоки могут быть в любом месте документа; линии рисуются в слое размером с viewport с position: fixed, телепортированном в <body>, поэтому overflow: hidden или transform у предков не обрезают и не сдвигают его. dragBounds: 'container' в этом случае означает viewport.

name ​

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

Позволяет элементам в других местах страницы закрепиться за этим инстансом через data-vl-linker="<name>" или опцию linker у директив. Если на странице несколько инстансов с scope="page", дайте каждому свой name.

zIndex ​

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

z-index слоя, в котором рисуются линии (и слоты-оверлеи).

Любые другие атрибуты (class, style, id, …) попадают на корневой элемент компонента.

События ​

block-dragstart ​

Payload: { blockId: string }

block-drag ​

Payload: { blockId: string; x: number; y: number }

block-dragend ​

Payload: { blockId: string; x: number; y: number }

block-mouseenter ​

Payload: { blockId: string }

block-mouseleave ​

Payload: { blockId: string }

connection-click ​

Payload: ConnectionDescriptor

connection-mouseenter ​

Payload: ConnectionDescriptor

connection-mouseleave ​

Payload: ConnectionDescriptor

connection-selectionchange ​

Payload: string[] — id выделенных соединений.

connection-delete-request ​

Payload: ConnectionDescriptor[] — соединения для удаления. Ничего не удаляется, решаете вы.

update:selected ​

Payload: string[] — для v-model:selected.

Первые восемь один в один повторяют собственные события движка (смотрите API движка), только с именами событий в kebab-case вместо префиксов движка block:/connection:.

Слоты ​

default ​

Scope: без scope

Ваша собственная разметка — помеченные в ней блоки и порты находятся на любой глубине. Элементы, добавленные, удалённые или перепомеченные позже (v-if, v-for, сторонняя разметка, выставляющая data-vl-* атрибуты), подхватываются автоматически.

connection-label ​

Scope: { connection: ConnectionDescriptor; label?: ConnectionLabel; point: Point; angle?: number; rotation?: number; from: Point; to: Point }

Рендерится по одному разу на каждое соединение, абсолютно позиционирован по фактической середине соединения (с учётом формы кривой, а не просто по середине между конечными точками). Для соединения с labels он рендерится вместо этого по одному разу на каждую подпись без text — в её point, повёрнутый на rotation; подписи с text рисует движок — HTML-оверлей внутри слоя отрисовки, так как произвольное содержимое Vue не может рендериться внутри <svg> без особенностей кроссбраузерности <foreignObject>. Строится, только если этот слот реально используется.

port ​

Scope: { blockId: string; portId?: string; point: Point }

Рендерится по одному разу на каждый посчитанный порт — для кастомного содержимого порта вместо (или, вместе с ports.show, дополнительно к) встроенной точки. Строится, только если этот слот реально используется.

marker ​

Scope: { connection: ConnectionDescriptor; position: 'start' | 'end'; point: Point; angle: number }

Рендерится для каждой конечной точки соединения, у которой не настроен маркер — ни в config.markers, ни в style.markers соединения — и которая не прижата к краю прокручиваемого контейнера (см. Прокручиваемые контейнеры) — настроенный маркер всё равно побеждает и рендерится как нативный SVG-маркер. Строится, только если этот слот реально используется.

Пример — кастомный маркер и подпись соединения:

vue
<VisualLinker :connections="connections">
  <div v-for="item in items" :key="item.id" v-vl-block="item.id" class="card">{{ item.id }}</div>
  <template #connection-label="{ connection }">
    <span class="badge">{{ connection.id }}</span>
  </template>
  <template #marker="{ position }">
    <span v-if="position === 'end'" class="dot" />
  </template>
</VisualLinker>

Область страницы ​

Пример — связь двух списков, отрендеренных разными компонентами:

vue
<template>
  <VisualLinker scope="page" name="assign" :connections="connections" :z-index="10" />

  <TaskList>
    <li v-vl-block="{ id: 't1', linker: 'assign' }">Задача 1</li>
  </TaskList>
  <OwnerList>
    <li data-vl-block="ann" data-vl-linker="assign">Анна</li>
  </OwnerList>
</template>

Сам <VisualLinker> здесь может оставаться пустым — со scope="page" ему не нужно оборачивать блоки, которые он соединяет.

Итоговая разметка ​

Компонент рендерит один корневой <div class="vl-container"> вокруг содержимого слота (с position: relative при scope="container"). Слой отрисовки — <div class="vl-layer">, плюс vl-layer--page в режиме страницы — появляется только на клиенте после монтирования, поэтому серверный рендер и проход гидратации совпадают. При scope="container" слой — последний дочерний элемент корня, так что он рисуется поверх содержимого слота без всякой возни с z-index.