Рутовое приложение
Плагины

Плагины оболочки

Плагин — веб-компонент продукта, который рутовое приложение показывает не во вкладке, а рядом с ней: кнопкой в шапке или на боковой панели и раскрывающейся по клику панелью. Так подключаются уведомления, обратная связь, ассистент — то, что должно быть под рукой на любом экране. Плагин объявляется в конфигурации рутового приложения (config.json), собственного кода в оболочке для него не пишут.

Со стороны продукта плагин — обычный веб-компонент на Q.Palette; всё, что ему нужно от оболочки, он получает через QPluginService из @diasoft/qpalette-visual.

С чего начать

Добавьте запись в массив plugins конфигурации рутового приложения:

{
  "plugins": [
    {
      "endpoint": { "service": "qnotify", "component": "qnotify" },
      "slot": "header",
      "icon": "pi-bell-outlined",
      "caption": "Уведомления"
    }
  ]
}

В правой части шапки появится кнопка с колокольчиком и подсказкой «Уведомления». Клик по ней загружает веб-компонент qnotify и открывает его в панели под кнопкой; повторный клик, клик вне панели или Escape — закрывает. Веб-компонент загружается при первом открытии, а не при старте приложения.

Как это устроено

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

slotКнопкаПанель
headerправая часть шапкивыпадающая под кнопкой, размеры задаёт плагин
toolbarверх боковой панели (по умолчанию)боковая панель во всю высоту экрана
bottomниз боковой панелибоковая панель во всю высоту экрана

Открытые панели не мешают друг другу между группами: одновременно могут быть открыты один плагин шапки и один плагин боковой панели. Внутри группы открытие одного плагина закрывает другой.

Разделение ответственности:

  • оболочка рисует кнопку (иконка, подсказка, индикатор уведомлений, статус), панель и её геометрию, загружает веб-компонент и передаёт панели высоту;
  • плагин — своё содержимое и раскладку внутри панели; закрывает себя, меняет статус и счётчик на кнопке через QPluginService.

Настройка

Что загружать: endpoint и bundleUrl

endpoint называет веб-компонент так же, как в остальных местах конфигурации — сервис, компонент и, при необходимости, маршрут внутри него:

{
  "endpoint": { "service": "qdemo", "component": "qdemo", "route": "plugins/assistant" }
}

route открывает в панели конкретный экран веб-компонента: без него — маршрут по умолчанию.

bundleUrl нужен, когда веб-компонент лежит не за шлюзом продукта, а отдаётся как файл — например библиотечный плагин из ассетов:

{
  "endpoint": { "service": "qfeedback", "component": "qfeedback" },
  "bundleUrl": "/assets/qfeedback/main.js"
}

Кнопка: icon, caption, slot

icon — имя иконки из наборов pi-* (PrimeIcons) или qi-* (иконки Q.Palette). Короткая форма — строка; расширенная — объект, если кнопке нужен свой цвет:

{
  "icon": { "name": "qi-feedback", "color": "#ffffff", "background": "var(--q-primary)" }
}

color и background принимают любое CSS-значение цвета, включая токены темы (var(--q-primary)). Кнопка с заданным background сохраняет отклик на наведение и показывает, что панель открыта — цвет отклика подбирается от текущего цвета иконки, поэтому одинаково читается на светлом и тёмном фоне.

caption — подпись плагина: показывается подсказкой при наведении на кнопку. Заголовка у панели нет — его, если нужен, рисует сам плагин.

slot — где стоит кнопка (см. «Как это устроено»). Не задан — toolbar.

Загрузка при старте: initWithApp

По умолчанию веб-компонент загружается при первом открытии панели. initWithApp: true загружает его вместе с приложением — для плагинов, которые должны работать в фоне ещё до того, как пользователь их открыл: например, считать уведомления и показывать счётчик на кнопке.

{
  "endpoint": { "service": "qnotify", "component": "qnotify" },
  "slot": "header",
  "icon": "pi-bell-outlined",
  "caption": "Уведомления",
  "initWithApp": true
}

Цена — загрузка бандла при старте у каждого пользователя, в том числе у тех, кто плагин не откроет. Не включайте, если фоновой работы у плагина нет.

Размеры панели: panel (только slot: "header")

Панель плагина шапки по умолчанию шириной 20rem и высотой по содержимому. Настройка panel меняет это:

{
  "panel": { "width": "28rem", "height": "32rem" }
}
  • width — ширина; любое CSS-значение (28rem, 480px).
  • height — высота; без неё панель растёт по содержимому. Заданная высота не выпустит панель за экран: сверху она ограничена расстоянием от кнопки до края окна.
  • bottom — отступ панели от дальнего края окна. Панель растягивается от кнопки до этого отступа; height при этом не учитывается. Так делается панель «на всю высоту экрана» без подсчёта высоты шапки:
{
  "panel": { "width": "24rem", "bottom": "1rem" }
}

На узком экране кнопки шапки уезжают вниз, и панель раскрывается вверх — bottom тогда отсчитывается от верхнего края. Настройка одна, поведение зеркальное.

Панели боковых слотов (toolbar, bottom) всегда занимают всю высоту экрана; panel на них не действует.

Как решать типовые задачи

Занять панель целиком

Когда у панели есть определённая высота (panel.height или panel.bottom, а также любой боковой слот), оболочка передаёт её корневому элементу веб-компонента. Дальше высоту нужно забрать самому — объявите у корневого компонента плагина:

:host {
  display: block;
  height: 100%;
}

Без этого содержимое прижмётся к верху, а низ панели останется пустым. Внутренняя раскладка — дело плагина: например, display: grid; grid-template-rows: auto 1fr auto для шапки, прокручиваемой середины и подвала.

Закрыть панель из плагина

Плагин не знает, в каком слоте он открыт, и не должен искать панель в DOM. Вместо этого он просит оболочку закрыть себя:

import { inject } from '@angular/core';
import { QPluginService } from '@diasoft/qpalette-visual';
 
export class AssistantComponent {
  private readonly plugin = inject(QPluginService);
 
  close(): void {
    this.plugin.close();
  }
}

Оболочка закроет панель того плагина, из которого пришло событие.

Показать счётчик на кнопке

this.plugin.notification(3);   // индикатор с числом 3
this.plugin.notification(1);   // индикатор без числа — точка
this.plugin.notification(0);   // индикатор скрыт

Счётчик виден и при закрытой панели, поэтому такому плагину обычно нужен initWithApp: true — иначе считать уведомления некому, пока пользователь не откроет панель.

Пометить кнопку статусом

this.plugin.changeStatus('error');   // QStatus: 'success' | 'info' | 'warn' | 'error'

Кнопка окрашивается по статусу (классы p-button-success, p-button-info, p-button-warning, p-button-danger темы) — например, чтобы показать, что соединение с сервисом потеряно. Статус виден только на кнопках боковой панели (toolbar, bottom); кнопка в шапке его не показывает.

Открыть в панели конкретный экран

Задайте route в endpoint — маршрут внутри веб-компонента, тот же, что открыл бы QUrlService продукта:

{
  "endpoint": { "service": "qdemo", "component": "qdemo", "route": "plugins/assistant" },
  "slot": "bottom",
  "icon": "pi-comments-outlined",
  "caption": "Ассистент"
}

Так один веб-компонент отдаёт и полноценные экраны во вкладках, и облегчённый экран для панели плагина.

Ограничения и типичные ошибки

  • На кнопке звезда вместо иконки. У записи нет icon — оболочка подставляет pi-star-o-outlined. Задайте icon.
  • Плагин не загружается, в панели сообщение об ошибке. Оболочка не нашла веб-компонент по endpoint (ответ 403/404 шлюза) либо bundleUrl указывает на отсутствующий файл. Проверьте, что сервис и компонент называются так же, как в реестре, и что бандл лежит по указанному адресу.
  • Панель пустая снизу. У панели задана высота, а корневой компонент плагина её не забрал — см. «Занять панель целиком».
  • Всплывающие элементы плагина обрезаются. Панель с определённой высотой прокручивает содержимое и обрезает то, что выходит за её границы. Выпадающие списки и подсказки выводите через appendTo="body".
  • panel не действует. Настройка учитывается только в слоте header.

Справочник

Запись в plugins[]

ПолеТипПо умолчаниюОписание
endpoint{ service, component, route? }—Веб-компонент плагина; route — маршрут внутри него
bundleUrlstring—Адрес файла бандла, если веб-компонент отдаётся не через шлюз продукта
slot'toolbar' | 'bottom' | 'header''toolbar'Где стоит кнопка и в какой панели открывается содержимое
iconstring | { name, color?, background? }pi-star-o-outlinedИконка кнопки: имя из pi-*/qi-*; в объектной форме — с цветом иконки и фона
captionstring—Подсказка при наведении на кнопку
initWithAppbooleanfalseЗагружать веб-компонент при старте приложения, а не при первом открытии
panel{ width?, height?, bottom? }width: '20rem', высота по содержимомуРазмеры панели; только для slot: 'header'
service, component, routestring—(устаревшее) То же, что endpoint, полями верхнего уровня. Если задан endpoint, приоритет у него

Порядок записей в массиве — порядок кнопок в слоте.

QPluginService (@diasoft/qpalette-visual)

МетодЧто делает
close(): voidЗакрывает панель плагина, из которого вызван
notification(value: number | boolean): voidИндикатор на кнопке: число больше 1 — с числом, 1 или true — точка, 0 или false — скрыт
changeStatus(status: QStatus): voidОкрашивает кнопку по статусу 'success' | 'info' | 'warn' | 'error'; только в слотах toolbar и bottom

Сервис отправляет события через QEventsService того же бандла — работает из любого компонента плагина, регистрировать ничего не нужно.

Совместимость версий

ВерсияЧто изменилось
8.2.0icon в объектной форме (цвет иконки и фона); panel — размеры панели плагина шапки, в том числе растяжение до края экрана через bottom
8.0.27Слот header с выпадающей панелью и слот bottom; endpoint вместо полей service/component/route верхнего уровня; bundleUrl; секции feedback и notifications конфигурации объявлены устаревшими

Устаревшее

  • service, component, route на верхнем уровне записи — заменены объектом endpoint. Старые поля читаются, пока не задан endpoint.
  • opened — раньше открывал панель при старте. Не учитывается: панель открывается только действием пользователя; фоновая работа плагина — через initWithApp.
  • Манифест плагина src/assets/manifest/main.js — не читается; всё, что описывал манифест (icon, initWithApp, description), задаётся в записи plugins[].
  • Секции feedback и notifications конфигурации — прежний способ подключить обратную связь и уведомления. Ещё читаются и превращаются в плагины слота header с иконками qi-feedback и pi-bell-outlined; новые продукты объявляют их в plugins[]:
{
  "plugins": [
    {
      "endpoint": { "service": "qfeedback", "component": "qfeedback" },
      "bundleUrl": "/assets/qfeedback/main.js",
      "slot": "header",
      "icon": "qi-feedback",
      "caption": "Обратная связь"
    }
  ]
}