Skip to content

Справочник ​

Архитектура ​

Три пакета, построенные на одном движке: @macrulez/visual-linker-core делает всю реальную работу вообще без зависимости от фреймворка — единственная фабрика createVisualLinker(container, config), которая измеряет блоки/порты через getBoundingClientRect(), батчит пересчёт через наблюдатель на ResizeObserver плюс requestAnimationFrame, и рисует пути в обычный SVG-слой, который сама создаёт и которым владеет внутри container. @macrulez/visual-linker-vue — тонкий адаптер: компонент <VisualLinker> оборачивает вашу собственную разметку, находит помеченные в ней блоки и порты (директивы v-vl-block/v-vl-port, data-vl-* атрибуты или проп blocks) сканированием по сигналам MutationObserver, запускает движок внутри собственного слоя отрисовки — в боксе компонента или, при scope="page", в слое position: fixed, телепортированном в <body>, — и прокидывает событие layout движка в три опциональных HTML-слота-оверлея (#connection-label/#port/#marker); useVisualLinker() полностью пропускает обнаружение и просто подключает движок к контейнеру и уже существующим у вас blocks/connections. Vue-пакет реэкспортирует всю поверхность ядра, так что установки только @macrulez/visual-linker-vue достаточно — отдельно ставить ядро не нужно. @macrulez/visual-linker-nuxt не добавляет ничего своего, кроме автоимпортов, универсального плагина, регистрирующего две директивы, и общей конфигурации приложения, которая берётся из nuxt.config.ts.

Совместимость с SSR ​

<VisualLinker> и useVisualLinker() оба безопасны для SSR — движок создаётся только на клиенте, после монтирования, и слой отрисовки <VisualLinker> до этого тоже не рендерится, поэтому серверный вывод и проход гидратации совпадают. Директивы v-vl-block/v-vl-port выводят свои data-vl-* атрибуты и при серверном рендеринге. Обёртка <ClientOnly> нигде не нужна, включая модуль Nuxt.

Доступность ​

По умолчанию соединения — это чисто визуальный SVG-оверлей: каждая линия несёт role="img" и aria-label (ariaLabel соединения или «Connection: a → b»), а её невидимая область наведения скрыта от вспомогательных технологий. Содержимое блоков — это ваша собственная разметка, она сохраняет ту доступность, которую вы сами ей задали; пометка элемента как блока или порта лишь добавляет ему data-vl-* атрибуты. Если соединение несёт информацию, важную для пользователя скринридера (например, «шаг 2 зависит от шага 1»), выразите эту связь ещё и в окружающем тексте или содержимом блоков, а не полагайтесь только на нарисованную линию.

С опцией interaction.selectable каждое соединение становится фокусируемой кнопкой, которую можно выделить с клавиатуры, — см. Выделение и доступность. Анимированный поток отключается для пользователей, которые предпочитают сниженное движение.

Размер бандла и peer-зависимости ​

У @macrulez/visual-linker-core вообще нет peer-зависимостей — пригоден для самостоятельного использования в любом окружении. @macrulez/visual-linker-vue зависит от @macrulez/visual-linker-core (workspace:*) и указывает peer на vue: ^3.3.0. @macrulez/visual-linker-nuxt зависит от @macrulez/visual-linker-vue и @nuxt/kit, и указывает peer на nuxt: ^3.9.0 || ^4.0.0. Все три поставляются с sideEffects: false.

Разработка ​

Монорепо на pnpm workspaces (packages/core, packages/vue, packages/nuxt, плюс playground на Vue).

bash
pnpm install
pnpm build       # собирает каждый пакет
pnpm test        # запускает тесты каждого пакета
pnpm typecheck
pnpm dev         # запускает Vue playground
pnpm lint        # eslint по всему монорепо
pnpm format      # prettier --write по всему монорепо

Лицензия ​

MIT.