Запасные концы
Соединение, чьего блока нет в setBlocks(), пропускается. Это проблема, когда блок отсутствует лишь временно: строка свёрнутого узла дерева или строка, которую виртуализированный список ещё не отрисовал. Соединение не должно исчезать — оно должно идти к ближайшему видимому блоку, например к свёрнутому родителю.
Такие блоки конец перечисляет в fallback:
{
id: 'first>fullName',
from: { blockId: 'user.name.first', fallback: ['user.name', 'user'] },
to: { blockId: 'person.fullName' },
}Правила
- Если собственного блока конца нет среди зарегистрированных, вместо него берётся первый зарегистрированный блок из
fallback. Элемент запасного списка — id блока или{ blockId, portId? }. - Если собственный блок зарегистрирован, но его порт прокручен за пределы видимости (и
clipToScrollParentsравно'pin'или'hide'), берётся первый запасной блок, который виден, вместо прижатия линии к краю. Если не виден и никто из запасных, действует обычное прижатие или скрытие. - Если в цепочке нет ни одного зарегистрированного блока, соединение пропускается, как и раньше.
- Конец, которому запасной не понадобился, рисуется точно так же, как без этого поля.
Состояние proxied
Соединение, использующее запасной конец, находится в состоянии proxied — чтобы вы могли показать его иначе: пунктиром, бледнее. Оно ложится поверх базового вида и под selected, highlight, hover и focus:
lines: { proxied: { dashed: true, opacity: 0.55 } },
// или у одного соединения
style: { proxied: { color: '#9aa0b8' } },proxied — «ведро» каждой группы, знающей о состояниях (lines, markers, ports, labels), как и hover.
Что сообщает движок
- Событие
layoutкладёт в каждыйConnectionLayoutполеproxied: { from?: boolean; to?: boolean }, если конец был перенесён. getStats().proxiedсчитает такие соединения.- Подписи и маркеры подчиняются обычным правилам; для запасного конца ничего отдельного не рисуется.
В Vue
Поле проходит через проп connections без изменений. Дерево, свёрнутые строки которого удаляются из DOM:
function endpoint(side: 'source' | 'target', id: string) {
return {
blockId: `${side}:${id}`,
fallback: ancestorsOf(id).map((parent) => `${side}:${parent}`),
}
}См. демо-страницу «Mapping» в playground репозитория.