Манифест WebPart
Манифест WebPart
Файл manifest.json в корне пакета .portalpart:
{ "id": "contoso.tasks-widget", "title": "Задачи отдела", "version": "1.0.0", "category": "Списки", "icon": "fa-list-check", "runtime": "dotnet", "entry": "dist/MyWebPart.dll", "entryType": "Contoso.TasksWidget.TasksWidgetWebPart", "styles": ["dist/main.css"], "scripts": ["dist/main.js"], "properties": { "listId": { "type": "listPicker", "label": "Список", "required": true }, "viewId": { "type": "viewPicker", "label": "Представление", "dependsOn": "listId" }, "pageSize": { "type": "number", "label": "Записей", "default": 10 } }}Поля манифеста
| Поле | Описание |
|---|---|
id | Уникальный ключ vendor.name (латиница, точки) |
title | Название в каталоге |
version | Semver (1.0.0) |
category | Группа в каталоге |
icon | Font Awesome класс |
entry | Путь к .dll внутри ZIP |
entryType | Полное имя C#-класса (IWebPart) |
runtime | Всегда dotnet |
styles | CSS-файлы (пути внутри ZIP, см. ниже) |
scripts | JS-файлы для клиентской логики после SSR (опционально) |
properties | Схема Property Pane |
CSS и JavaScript в пакете
WebPart — гибрид SSR: HTML генерирует C# (IWebPart.RenderAsync), а стили и клиентский код поставляются вместе с пакетом, а не через frontend/ платформы.
Структура файлов
my-widget/├── manifest.json├── MyWidgetWebPart.cs├── dist/│ ├── main.css ← styles в manifest.json│ ├── main.js ← scripts в manifest.json (опционально)│ └── MyWidget.dll ← entry в manifest.json└── build.shВ manifest.json укажите пути относительно корня ZIP (в исходниках — без версии в имени):
{ "styles": ["dist/main.css"], "scripts": ["dist/main.js"]}При сборке build.sh файлы переименовываются в dist/main.v{version}.css / .js, а manifest в ZIP обновляется автоматически.
Можно подключить несколько файлов — они загружаются в порядке массива.
Как платформа подключает ассеты
- При монтировании WebPart на странице фронтенд читает
stylesheetsиscriptsиз каталога (заполняются при установке пакета). - CSS и JS загружаются с API, например:
/api/v1/webparts/assets/{manifestKey}/dist/main.v1.0.0.css?v=1.0.0(требуется авторизация). - Стили вставляются в
<head>, скрипты выполняются один раз за версию пакета; при сменеversionстарые теги удаляются. - После каждого SSR-рендера и action вызывается
PortalWebPartClients[id].bind(container).
CSS обязателен для оформления SSR-разметки. JS нужен, если после серверного HTML требуется интерактив (модалки, обработчики кликов, синхронизация формы).
CSS: рекомендации
- Используйте префикс классов по корневому контейнеру (например
.webpart-room-booking), чтобы не конфликтовать с глобальными стилями портала. - CSS-переменные портала (
--color-border,--color-surface) можно использовать как fallback:var(--color-border, #e5e7eb). - Файлы кладите в
dist/и включайте в ZIP при сборке (build.shкопируетdist/main.cssв пакет).
JavaScript: хук bind
Если WebPart требует интерактива после серверного рендера (модалки, обработчики DOM), добавьте scripts в манифест и зарегистрируйте хук в dist/main.js:
(function () { const MANIFEST_KEY = 'contoso.tasks-widget';
function bind(root) { if (!root || root.dataset.uiBound === '1') return; root.dataset.uiBound = '1'; // root — корневой элемент WebPart (.webpart-… или id из SSR) root.querySelectorAll('[data-wp-action]').forEach(/* … */); }
window.PortalWebPartClients = window.PortalWebPartClients || {}; window.PortalWebPartClients[MANIFEST_KEY] = { bind };})();- Ключ в
PortalWebPartClientsдолжен совпадать сidвmanifest.json. bindвызывается после каждого SSR-рендера и action — защищайтесь от повторной привязки (dataset.uiBound, делегирование).- Действия с сервером из разметки: атрибут
data-wp-action="имя"— платформа отправляет POST/webparts/invoke/{key}/action(см. SDK).
Полный пример с CSS + JS: meeting-room-booking.
Подробнее: Интерфейс, контролы и API — разметка контролов, три паттерна взаимодействия, серверный и клиентский API.
Типы свойств (Property Pane)
| type | Описание |
|---|---|
text | Строка |
multiline | Текстовая область |
number | Число |
boolean | Флажок |
choice | Select (choices: [{value, label}]) |
listPicker | Список узла; с dependsOn — списки выбранного узла |
libraryPicker | Библиотека текущего узла |
nodePicker | Дочерний узел текущей страницы; scope: "subtree" — все узлы портала с путём «Родитель / Узел» |
viewPicker | Представление списка (dependsOn: listId) |
linksEditor | Редактор списка ссылок (title, url, icon) |
color | Цвет (input type=color) |
Модель страницы (zones_content)
{ "layoutTemplate": "2-equal", "zones": [ [ { "type": "richtext", "id": "uuid", "html": "<p>Текст</p>" }, { "type": "webpart", "instanceId": "uuid", "definitionKey": "portal.list-view", "properties": { "listId": "..." } } ] ]}Quill редактирует только блоки richtext (тип richtext в zones, не WebPart). WebPart — отдельные острова с собственной настройкой.
Встроенные WebPart
| key | Назначение |
|---|---|
portal.hero | Баннер с заголовком и кнопкой |
portal.announcement | Информационное объявление |
portal.list-view | Таблица списка |
portal.document-list | Файлы библиотеки |
portal.quick-links | Быстрые ссылки |
portal.child-nodes | Подразделы узла |