Перейти к основному содержимому

ErdViewerElement

Начиная с версии 5.0.0 пакет содержит также <erd-viewer> — представление документа только для чтения: читатель перемещает холст, меняет масштаб, ищет и подсвечивает связи, но ничто в нем не редактирует. Он рисует один лишь холст ERD с его полосами прокрутки, мини-картой, панелью управления и палитрой, которая ищет по документу, и в нем нет ни панели инструментов, ни вкладок, ни Обозревателя, ни контекстного меню.

import '@dineug/erd-editor/erd-viewer.js';
<erd-viewer src="/diagrams/shop.erd.json" style="display: block; height: 480px"></erd-viewer>

Фрейм выше — это данный элемент в том виде, в каком его показывает erd-editor.io/embed, здесь с образцом bookstore: щелкните по нему, чтобы дать ему фокус, затем меняйте масштаб и ищите клавишами из раздела Фокус и клавиши, а о том, как встроить собственный документ без кода, см. Встраивание.

Просмотрщик — это обычный HTMLElement.
Его определение типа приведено ниже.

interface ErdViewerElement extends HTMLElement {
src: string;
nameView: 'physical' | 'logical' | 'both' | null;
minimap: boolean;
controls: boolean;
value: string;
setPresetTheme: (themeOptions: Partial<ThemeOptions>) => void;
setSystemAppearance: (appearance: 'dark' | 'light' | null) => void;
setTheme: (theme: Partial<Theme>) => void;
setLocale: (locale: LocaleOption) => Promise<void>;
setSystemLocale: (locale: string | null) => Promise<void>;
destroy: () => void;
}

Импорт @dineug/erd-editor/erd-viewer.js определяет <erd-viewer> и оставляет <erd-editor> неопределенным. Импорт корня пакета не определяет ни одного из них: он экспортирует класс ErdViewerElement и defineErdViewer(), которая определяет тег там, где вы ее вызываете, поэтому страница может определить этот класс и под собственным тегом — см. Регистрация элементов.
Каждой точке входа, кроме engine.js и peer.js, нужен DOM, и этой тоже, поэтому страница, отрисованная на сервере, импортирует ее на клиенте — см. Серверный рендеринг.
Импорт точки входа добавляет erd-viewer в HTMLElementTagNameMap, поэтому document.createElement('erd-viewer') и document.querySelector('erd-viewer') типизируются как ErdViewerElement без приведения типа.

Как и редактор, просмотрщик отрисовывается в закрытом теневом корне, поэтому viewer.shadowRoot равен null, а стили страницы не проникают ни внутрь, ни наружу; оформляйте его через setTheme и пользовательские свойства --erd-editor-*. Несколько просмотрщиков на одной странице хранят каждый свой документ, вид, тему и язык.
У <erd-viewer> нет собственного размера. Пользовательский элемент строчный, поэтому задайте ему display: block и высоту, как выше. Вид заполняет блок элемента и следует за ним при изменении размера, сохраняя масштаб.

src, name-view, minimap и controls — его единственные атрибуты, у каждого есть свойство с тем же именем, nameView для name-view. value — только свойство.
Значения value, src, nameView, minimap или controls, заданные элементу до определения его тега, например фреймворком, который отрисовывает элемент до загрузки точки входа, учитываются, когда тег определен, поверх атрибутов, заданных разметкой. Они учитываются в том порядке, в каком страница записала каждое впервые, поэтому из value и src побеждает более поздний. value, не содержащий документа, тогда уже некому выбросить исключение: о нем сообщается как о неперехваченной ошибке, а просмотрщик остается пустым.

Сборка для тега script, dist/erd-viewer.umd.js, содержит один лишь просмотрщик со всеми зависимостями. Она определяет <erd-viewer> и помещает ErdViewerElement и defineErdViewer в window.ErdViewer. URL CDN без пути отдает вместо нее сборку редактора, поэтому называйте файл — см. Тег script.

<erd-viewer src="diagram.erd.json" style="display: block; height: 480px"></erd-viewer>
<script src="https://cdn.jsdelivr.net/npm/@dineug/erd-editor@5.0.0/dist/erd-viewer.umd.js"></script>

src​

URL документа, который просмотрщик загружает и показывает. По умолчанию это '', то есть ничего.
Загрузка — обычный fetch(), запрос GET без собственных параметров, поэтому ею управляют правила CORS браузера: документу на другом источнике нужен сервер, который разрешает источник страницы, а cookie отправляются только на источник самой страницы. Относительный URL разрешается относительно страницы.
Ответ должен содержать документ в том виде, в каком его хранит файл .erd.json, — форму хранения, которую записывают value редактора и все официальные хосты. Никакой другой формат не читается: файл SQL, GraphQL, DBML или AML завершается сбоем как текст, который не разбирается.
Документ, который разобран, ложится вписанным в вид и генерирует load. Запрос, отклоненный сетью или CORS, ответ с признаком не ok или текст, который не разбирается, генерирует error и оставляет пустую диаграмму — см. Загрузка.

Учитывается только последний запрос. Новый src прерывает еще выполняющуюся загрузку, поэтому медленный ответ никогда не ложится поверх более позднего документа. Присваивание value тоже ее прерывает, как и удаление src или его замена на '', которая перестает следовать за ним и оставляет показанный документ как был.
Побеждает то из value и src, что задано последним, будь то через свойство или через атрибут. src, записанный после value, загружается, даже если называет тот же URL, что и раньше. Записанный повторно поверх документа, который он уже показывает, он ничего не загружает.
Просмотрщик загружает только пока находится на странице. Удаление его со страницы прерывает еще выполняющуюся загрузку, а возвращение загружает src, который он не показал, в том числе завершившийся сбоем, но никогда тот, который он уже показывает.
src — единственный fetch(), который делает пакет, поэтому строгая Content Security Policy перечисляет источник документа в connect-src.

viewer.src = 'https://example.com/diagrams/shop.erd.json';
// or
viewer.setAttribute('src', 'https://example.com/diagrams/shop.erd.json');
<erd-viewer src="https://example.com/diagrams/shop.erd.json"></erd-viewer>

value​

getter​

Возвращает показанный документ, пришел ли он из src или из value, в виде строки JSON в форме хранения, которую записывает value редактора. Пока ничего не показано, это новый пустой документ, у которого doc равен {}.
Пока блокировка Viewport документа включена, как в новом документе, он записывает масштаб и прокрутку, на которых документ заблокирован, куда бы читатель ни переместил вид. Только при выключенной блокировке он записывает то место, где стоит вид, в том числе вписывание, которое делает загрузка.
Он записывает режим отображения имен, который хранит документ, и никогда тот, что nameView или панель управления показывают поверх него, даже в документе, у которого блокировка Name View выключена.
Он по-прежнему возвращает документ и после destroy.

const data = viewer.value;

setter​

Показывает документ по его тексту, заменяя показанный, и вписывает его в вид — см. Загрузка. Он принимает текст, который содержит файл или value редактора.
Текст, который не разбирается, вызывает исключение до каких-либо изменений, и показанный документ остается: SyntaxError для текста, не являющегося JSON, и TypeError для JSON, не содержащего документа, например 7. Пустая строка или любое значение, не являющееся строкой, показывает пустую диаграмму.
Присваивание прерывает еще выполняющуюся загрузку src и не генерирует ни load, ни error.

viewer.value = localStorage.getItem('my-diagram') ?? '';

nameView​

Показывает диаграмму в режиме отображения имен поверх того, который хранит документ: 'physical' для Physical Names, 'logical' для Logical Names или 'both' для Both Names. null, значение по умолчанию, показывает собственный режим документа, а значение атрибута, отличное от этих трех, читается как null.
Он остается в силе для каждого документа, загруженного после него, а value никогда его не записывает. Удаление атрибута возвращает собственный вид документа.
Список Name View на панели управления тоже переключает вид, до тех пор пока не ляжет следующий документ или nameView не изменится снова. Переключение там тоже не записывается в value.

viewer.nameView = 'both';
// or
viewer.setAttribute('name-view', 'both');
<erd-viewer name-view="logical"></erd-viewer>

minimap​

Показывает мини-карту в правом верхнем углу, пока документ содержит таблицу, показанную заметку или показанную группу таблиц. По умолчанию она включена.
Она работает так же, как мини-карта редактора: щелкните по ней, чтобы переместить туда вид, или перетащите ее прямоугольник области просмотра, чтобы переместить холст, — см. Навигация по холсту. Палец на ней принадлежит странице, пока просмотрщик не получит фокус.
Отключает ее только minimap="false". Атрибут без значения, любое другое значение и отсутствие атрибута оставляют ее включенной.

viewer.minimap = false;
// or
viewer.setAttribute('minimap', 'false');
<erd-viewer minimap="false"></erd-viewer>

controls​

Показывает панель управления над серединой нижнего края холста. По умолчанию она включена и содержит, слева направо:

  • Масштаб: Zoom out, текущий масштаб в процентах и Zoom in. Две кнопки меняют масштаб шагами, как клавиши масштаба, а щелчок по процентам, Reset zoom, возвращает 100%. Всплывающая подсказка каждой называет ее клавишу.
  • Fit, который меняет масштаб и прокручивает так, чтобы вся диаграмма оказалась в виде по центру.
  • Name View — кнопка, которая называет текущий режим и открывает над панелью список из Physical Names, Logical Names и Both Names — см. nameView.
  • Компас Go to content, который присоединяется к концу панели, только пока на экране нет ни одной части диаграммы, и переводит вид к ближайшей из них.

Панель идет слева направо на любом языке.
Отключает ее только controls="false", вместе с ее компасом и списком Name View. Атрибут без значения, любое другое значение и отсутствие атрибута оставляют ее включенной. Без панели колесико, щипок и клавиши по-прежнему меняют масштаб, как только просмотрщик получает фокус.

viewer.controls = false;
// or
viewer.setAttribute('controls', 'false');
<erd-viewer controls="false"></erd-viewer>

События​

load и error — единственные два события, и оба сообщают о src. Ни одно не всплывает, поэтому слушайте на самом элементе.

load​

Генерируется, когда документ, названный в src, загружен и показан. event.detail равен null.
Присваивание value не генерирует ни одного, как и загрузка, которую прервали более поздний src, value, удаление элемента со страницы или destroy.

viewer.addEventListener('load', () => {
console.log('shown', viewer.src);
});

error​

Генерируется, когда загрузка src завершается сбоем: сеть или CORS отклонили запрос, ответ был не ok или его текст не разобрался. Тогда просмотрщик показывает пустую диаграмму и уведомление Couldn't load the diagram — см. Загрузка.
event.detail — это причина: TypeError, которым отклоняется fetch(), Error с названием статуса, например GET /missing.erd.json answered 404 Not Found, или SyntaxError либо TypeError разбора.
Загрузка, которую что-то прервало, не генерирует ничего.

viewer.addEventListener('error', event => {
console.warn('could not show', viewer.src, event.detail);
});

setPresetTheme​

Задает предустановленную тему, принимая ThemeOptions редактора — см. setPresetTheme.
Просмотрщик начинает с appearance: 'system', grayColor: 'slate' и accentColor: 'indigo', поэтому с первого кадра показывает светлое или темное оформление, о котором сообщает операционная система, и переключается, когда оно меняется. Редактор, напротив, начинает с темного.
Каждое поле применяется само по себе, поэтому частичный вызов оставляет два других как есть. Значение, не входящее в число имен, игнорируется, а вызов никогда не выбрасывает исключение.
В просмотрщике нет конструктора темы и systemDarkMode, и он не генерирует события для темы.

viewer.setPresetTheme({ appearance: 'light', accentColor: 'teal' });

setSystemAppearance​

Называет светлое или темное оформление, которое показывает оформление 'system', для страницы с собственным светлым и темным оформлением. Пока одно из них названо, изменения операционной системы игнорируются.
null или любое значение, кроме 'dark' и 'light', возвращает слежение за операционной системой.
Пока оформление равно 'light' или 'dark', на экране оно ничего не меняет, но значение сохраняется и применяется, как только оформление становится 'system'. Оно ничего не генерирует.

viewer.setSystemAppearance('dark'); // null follows the OS again

setTheme​

Переопределяет токены темы поверх предустановки, принимая токены Theme редактора — см. setTheme.
Каждый вызов заменяет весь слой целиком, поэтому передайте {}, чтобы вернуться к предустановке. Сохраняются только имена токенов со строковыми значениями, а более поздний setPresetTheme подменяет предустановку под ними, и переопределения остаются.
Пользовательские свойства CSS --erd-editor-* доходят и до просмотрщика, если задать их на элементе или выше него — см. Переменные CSS. Холст, до которого не доходит ни одна таблица стилей, читает их снова, когда меняется таблица стилей в head страницы либо атрибут class или style элемента или элемента выше него. Правило, добавленное через insertRule(), не меняет ничего из этого, поэтому холст подхватывает его при следующем таком изменении.

erd-viewer {
--erd-editor-canvas-background: #ffffff;
}

setLocale​

Задает язык интерфейса: 'system' (по умолчанию) или один из 25 кодов, которые перечисляет Язык интерфейса редактора. Все остальное, например голое 'ko' или значение, не являющееся строкой, игнорируется.
Язык меняет всплывающие подсказки панели управления, список Name View, палитру, уведомления о загрузке и слова, которые диаграмма рисует сама, например unnamed. Имена, комментарии и остальное содержимое документа никогда не переводятся.

Английский входит в точку входа просмотрщика и применяется до возврата из вызова. Каждый другой язык — это отдельный модуль, который ваш сборщик выводит чанком рядом с просмотрщиком, а просмотрщик загружает из вашего бандла, когда страница впервые запрашивает этот язык, один раз для всех просмотрщиков страницы. Пока он не загрузится, просмотрщик продолжает показывать язык, который показывал, на первом кадре — английский. Если загрузка завершается сбоем, просмотрщик показывает английский, а повторный запрос этого языка пробует снова.
Сборка для тега script содержит все 25 языков, поэтому не загружает ни одного. Бандл, в котором есть и редактор, загружает каждый язык вместе с чанком редактора — см. Регистрация элементов.
Возвращаемый им Promise разрешается, когда этот язык загружен или загрузка не удалась, и никогда не отклоняется. После этого действует этот язык или, после сбоя, английский, если только более поздний вызов не запросил другой: побеждает более поздний вызов, в каком бы порядке ни загрузились языки.

Иврит, арабский и персидский располагают палитру и уведомления о загрузке справа налево. Холст, мини-карта, полосы прокрутки и панель управления с ее компасом остаются слева направо, как холст редактора, — см. Языки с письмом справа налево.
Просмотрщик задает lang и dir на собственном корне внутри закрытого теневого корня, а не на элементе-хосте и не на странице.
Он ничего не генерирует, в нем нет окна выбора языка, и он ничего не сохраняет.

await viewer.setLocale('ko-KR');

setSystemLocale​

Называет язык, который обозначает 'system', в виде тега BCP 47, для страницы с собственной настройкой языка.
Тег обрезается по краям. null, пустая строка или значение, не являющееся строкой, возвращает языки страницы.
Это <html lang> страницы, затем navigator.languages браузера, прочитанные один раз, когда просмотрщик впервые подключается. 'system' берет первый из них, для которого у просмотрщика есть язык, по правилу редактора из setSystemLocale, и английский, если нет ни одного. Поэтому страница, у которой <html lang> называет язык, известный просмотрщику, показывает этот язык, чего бы ни предпочитал браузер, а последующее изменение lang или языков браузера не сдвигает ни один просмотрщик.
Это имеет значение, только пока действует вариант 'system', а его Promise разрешается так же, как у setLocale.

viewer.setSystemLocale('ja-JP'); // null goes back to the page's languages

destroy​

Завершает работу просмотрщика навсегда. Он снимает вид до возврата из вызова, находится ли элемент еще на странице или был удален раньше, прерывает еще выполняющуюся загрузку src и перестает следить за цветовой схемой операционной системы и стилями страницы.
Уничтоженный просмотрщик больше ничего не рисует, ничего не загружает и не запрашивает никаких языков, даже если его снова добавить на страницу. Чтение value по-прежнему возвращает документ, который он держал.
Удаление элемента со страницы не уничтожает его: возвращенный, он продолжает работу и загружает src, который еще не показал, — см. Загрузка.

viewer.destroy();

Фокус и клавиши​

Просмотрщик не берет фокус при монтировании, при загрузке документа и при закрытии палитры, поэтому он никогда не прокручивает страницу к себе и не перехватывает клавиши страницы.
Щелчок или касание его холста дает ему фокус без прокрутки страницы, и он является одной остановкой клавиши Tab. В отличие от редактора, у него нет собственных focus() и blur(). Нажатие в любом другом месте страницы снова отнимает фокус.

Пока просмотрщик не получил фокус, страница сохраняет колесико, которое прокручивает страницу, и щипок на трекпаде. Палец на холсте прокручивает страницу и ничего не перемещает, а каждая клавиша доходит до страницы без отмены действия по умолчанию.
Когда он получил фокус:

  • Колесико перемещает вид, а Shift + Wheel перемещает его вбок. Над заметкой, текст которой не помещается, колесико прокручивает заметку.
  • Ctrl + Wheel (Windows/Linux) или ⌘ + Wheel (Mac), щипок на трекпаде или два пальца на сенсорном экране меняют масштаб.
  • Палец перемещает вид.
  • Ctrl + Plus и Ctrl + Minus (Windows/Linux) или ⌘ + Plus и ⌘ + Minus (Mac) меняют масштаб шагами, как кнопки Zoom in и Zoom out, а Ctrl + 0 (Windows/Linux) или ⌘ + 0 (Mac) сразу возвращает 100%, оставляя середину экрана на месте.
  • Ctrl + K (Windows/Linux) или ⌘ + K (Mac) открывает палитру, а Esc ее закрывает.

Это его единственные клавиши, и их нельзя переназначить. Все остальные клавиши доходят до страницы без отмены действия по умолчанию, с фокусом или без: Delete, Backspace, Space, Enter, Tab, Ctrl + A, Ctrl + F и каждое сочетание, которое что-то редактирует в редакторе.

Палитра ищет только по документу и не перечисляет ни одной команды. Пустое поле ввода показывает префиксы: # ищет таблицы, @ — столбцы, : — комментарии и заметки, а ? перечисляет префиксы, как в редакторе, — см. Поиск по диаграмме. Слово без префикса показывает No commands match над строками, которые ищут его по диаграмме.
Выбор строки перемещает вид по правилу редактора, Куда ведет переход, оставляя масштаб как есть, и обводит то, до чего он дошел, выводя это на передний план: таблицу, таблицу, содержащую столбец или комментарий, или заметку.
Выбор строки или нажатие Esc закрывает палитру и оставляет фокус без элемента, поэтому следующее колесико прокручивает страницу; щелкните по виду, чтобы снова отдать ему колесико и клавиши. Пока палитра открыта, колесико прокручивает ее список, а клавиши масштаба ничего не делают.

С фокусом или без него перетаскивание основной кнопкой мыши перемещает вид из любого места холста, в том числе над таблицей. Щелчок или касание без движения подсвечивает связи: на таблице — связи этой таблицы, на линии связи — эту линию, а в любом другом месте, включая заметку, гасит подсветку. Такой щелчок снимает и обводку, оставленную переходом из палитры. Указатель, остановившийся на таблице, подсвечивает ее связи, пока находится на ней.
Никакое нажатие ничего не выделяет, не перемещает и не редактирует. Нет никакого выделения, кроме обводки перехода, нет рамки выделения, и ни двойной щелчок, ни Enter не открывают ячейку.

Загрузка​

Пока src загружается, уведомление над серединой холста гласит Loading diagram рядом с вращающимся индикатором. Если загрузка завершается сбоем, оно гласит Couldn't load the diagram рядом со знаком предупреждения, пока его не заменит новый src или value либо пока не будет удален src. Нажатия проходят сквозь уведомление к холсту, а читается оно на языке интерфейса, справа налево — на языке с письмом справа налево.

Каждый документ, который ложится, из src или из value, вписывается в блок просмотрщика: масштаб и прокрутка подбираются так, чтобы вся диаграмма оказалась в виде по центру, как это делает Fit, а масштаб и прокрутка, сохраненные в файле, откладываются в сторону. Документ, переданный до того, как у просмотрщика появился размер, заданный до помещения на страницу или пока его блок пуст, вписывается, как только размер появится. Последующее изменение размера сохраняет масштаб; Fit вписывает снова.

Просмотрщик загружает src только пока находится на странице. Удаление его со страницы прерывает еще выполняющуюся загрузку, а возвращение загружает src, который он не показал, в том числе завершившийся сбоем, но никогда тот, который он уже показывает. Просмотрщик, который больше не нужен, завершается через destroy.

Что он добавляет на страницу​

  • Никаких воркеров. Он не запускает ни одного, поэтому для него Content Security Policy не нужна директива worker-src.
  • Никакого хранилища. Он не записывает ни cookie, ни localStorage, ни sessionStorage, ни IndexedDB.
  • Запросы двух видов: загрузка src, источник которой строгая политика перечисляет в connect-src, и модуль языка интерфейса, отличного от английского, из вашего собственного бандла — см. setLocale.
  • Стили в сконструированных таблицах стилей, которые пропускает строгая директива style-src. Браузер без сконструированных таблиц стилей, например Firefox до версии 101, получает вместо них элементы <style>, которым нужна директива style-src 'unsafe-inline'.
  • Обработчики на window. Импорт точки входа добавляет три на все время жизни страницы: для mousedown, touchstart и собственного события пакета @dineug/erd-editor/internal-forward-move-start. Konva, которая рисует холст, задает window.Konva и вместе с первой сценой добавляет обработчики мыши и касаний на window и обработчик visibilitychange на document, которые никогда не удаляет. Точка входа редактора добавляет те же.
  • До destroy каждый просмотрщик следует за цветовой схемой операционной системы и следит за стилями страницы на предмет пользовательских свойств, которые описывает setTheme.