Пользовательские компоненты
Web SDK предлагает несколько способов интеграции пользовательских компонентов, с помощью которых можно расширять и настраивать его функциональность. В этом руководстве рассматриваются наиболее распространённые способы.
Univer не передаёт компоненты напрямую в функции отрисовки в качестве параметров. Чтобы использовать компоненты в различных точках монтирования, сначала зарегистрируйте их в Univer через Facade API.
Если вы не знаете, как получить экземпляр Facade API, обратитесь к разделу Facade API.
Регистрация пользовательских компонентов
univerAPI.registerComponent(componentKey, CustomComponent, options)Для регистрации пользовательских компонентов используйте метод univerAPI.registerComponent. Он принимает три параметра:
componentKey: уникальный идентификатор компонента, по которому к нему можно обращаться в Univer.CustomComponent: реализация компонента — это может быть компонент React, Vue или Web Component.options: необязательные параметры конфигурации, с помощью которых можно указать фреймворк, от которого зависит компонент, и другие связанные настройки.
Компоненты React
Для регистрации компонента React дополнительная настройка не требуется — достаточно убедиться, что это корректный компонент React. Простой пример:
function ReactComponent(props: Record<string, any>) { return <div>Hello Univer!</div>}univerAPI.registerComponent('MyReactComponent', ReactComponent)Компоненты Vue
Vue 3.x
Чтобы зарегистрировать компоненты Vue, установите и зарегистрируйте плагин UniverVue3AdapterPlugin из пакета @univerjs/ui-adapter-vue3:
pnpm add @univerjs/ui-adapter-vue3npm install @univerjs/ui-adapter-vue3yarn add @univerjs/ui-adapter-vue3bun add @univerjs/ui-adapter-vue3import { UniverVue3AdapterPlugin } from '@univerjs/ui-adapter-vue3'univer.registerPlugin(UniverVue3AdapterPlugin)При регистрации компонентов Vue необходимо задать для параметра framework значение 'vue3':
const Vue3Component = defineComponent({ setup(props) { return () => <div>Hello Univer!</div> },})univerAPI.registerComponent('MyVue3Component', Vue3Component, { framework: 'vue3',})Vue 2.x
Поскольку поддержка Vue 2.x прекращена (EOL) и эта версия больше не сопровождается, сейчас Univer не предоставляет для нее плагин UI-адаптера. Однако вы можете реализовать адаптер Vue 2.x с помощью пользовательского плагина. Ниже приведен пример такого плагина:
import { DependentOn, Inject, Injector, Plugin } from '@univerjs/core'import { ComponentManager, UniverUIPlugin } from '@univerjs/ui'import Vue from 'vue'/** * The plugin that allows Univer to use Vue 2 components as UI components. */@DependentOn(UniverUIPlugin)export class UniverVue2AdapterPlugin extends Plugin { static override pluginName = 'UNIVER_UI_VUE2_ADAPTER_PLUGIN' constructor( private readonly _config = {}, @Inject(Injector) protected readonly _injector: Injector, @Inject(ComponentManager) protected readonly _componentManager: ComponentManager, ) { super() } override onStarting(): void { const { createElement, useEffect, useRef } = this._componentManager.reactUtils this._componentManager.setHandler('vue2', (component: any) => { return (props: Record<string, any>) => createElement(VueComponentWrapper, { component, props: Object.keys(props).reduce<Record<string, any>>((acc, key) => { if (key !== 'key') { acc[key] = props[key] } return acc }, {}), reactUtils: { createElement, useEffect, useRef }, }) }) }}export function VueComponentWrapper(options: { component: any props: Record<string, any> reactUtils: typeof ComponentManager.prototype.reactUtils}) { const { component, props, reactUtils } = options const { createElement, useEffect, useRef } = reactUtils const domRef = useRef<HTMLDivElement>(null) useEffect(() => { if (!domRef.current) return const Constructor = Vue.extend(component) const instance = new Constructor({ data: props, }) instance.$mount() domRef.current.appendChild(instance.$el) return () => { instance.$destroy() } }, [props]) return createElement('div', { ref: domRef })}Затем зарегистрируйте его:
import { UniverVue2AdapterPlugin } from './ui-adapter-vue2'univer.registerPlugin(UniverUIPlugin)univer.registerPlugin(UniverVue2AdapterPlugin)При регистрации компонентов Vue 2.x необходимо задать для параметра framework значение 'vue2':
const Vue2Component = Vue.component('MyVue2Component', { template: '<div>Hello, Univer!</div>',})univerAPI.registerComponent('MyVue2Component', Vue2Component, { framework: 'vue2',})Web Components
Чтобы зарегистрировать Web Components, убедитесь, что компонент соответствует стандарту Web Components, а затем установите и зарегистрируйте плагин UniverWebComponentAdapterPlugin из пакета @univerjs/ui-adapter-web-component:
pnpm add @univerjs/ui-adapter-web-componentnpm install @univerjs/ui-adapter-web-componentyarn add @univerjs/ui-adapter-web-componentbun add @univerjs/ui-adapter-web-componentimport { UniverWebComponentAdapterPlugin } from '@univerjs/ui-adapter-web-component'univer.registerPlugin(UniverWebComponentAdapterPlugin)При регистрации Web Components необходимо задать для параметра framework значение 'web-component':
class WebComponent extends HTMLElement { constructor() { super() const shadow = this.attachShadow({ mode: 'open' }) const div = document.createElement('div') div.textContent = 'Hello Univer!' shadow.appendChild(div) }}univerAPI.registerComponent('my-web-component', WebComponent, { framework: 'web-component',})Angular
Преобразуйте компонент Angular в пользовательский элемент через createCustomElement из @angular/elements, затем зарегистрируйте его через адаптер Web Component в Univer. Компонент можно использовать в боковой панели или диалоге.
Добавьте @angular/elements той же версии, что и @angular/core, и @univerjs/ui-adapter-web-component той же версии, что и остальные пакеты Univer.
Вызовите эту функцию после инициализации Univer. В компоненте-хозяине получите Angular-инжектор через private readonly injector = inject(Injector) и вызовите registerAngularPanel(univer, univerAPI, this.injector). Импортируйте inject и Injector из @angular/core в этом компоненте.
import type { Injector } from '@angular/core'import type { FUniver, Univer } from '@univerjs/presets'import { Component } from '@angular/core'import { createCustomElement } from '@angular/elements'import { UniverWebComponentAdapterPlugin } from '@univerjs/ui-adapter-web-component'@Component({ selector: 'app-angular-panel', standalone: true, template: '<p>Hello from Angular!</p>',})export class AngularPanelComponent {}export function registerAngularPanel(univer: Univer, univerAPI: FUniver, injector: Injector) { const element = createCustomElement(AngularPanelComponent, { injector }) univer.registerPlugin(UniverWebComponentAdapterPlugin) return univerAPI.registerComponent('univer-angular-panel', element, { framework: 'web-component', })}После отрисовки редактора используйте зарегистрированный ключ как содержимое боковой панели:
const sidebar = univerAPI.openSidebar({ header: { title: 'Angular' }, children: { label: 'univer-angular-panel' }, width: 360,})Сохраните дескрипторы регистрации и боковой панели и вызовите их методы dispose(), когда они больше не нужны. Регистрируйте элемент один раз на приложение. Имя должно содержать дефис и отличаться от selector компонента Angular.
Angular Elements преобразует входы в свойства элемента, а выходы — в события DOM CustomEvent. Адаптер Univer передаёт свойства, но не подписывается автоматически на выходы Angular: подключите нужные события явно.
Использование пользовательских компонентов
С помощью следующих методов можно гибко интегрировать в Univer различные пользовательские компоненты, расширяя и настраивая его возможности.
Внимание
- Перед использованием этих методов убедитесь, что Univer завершил отрисовку. 2. Если компонент требуется
зарегистрировать, перед использованием убедитесь, что регистрация выполнена правильно. 3. Используйте метод
dispose(), чтобы очищать и удалять добавленные компоненты и тем самым избегать утечек памяти.
Добавление пользовательских пунктов меню
Пользовательские компоненты можно добавлять как на верхнюю панель меню (Ribbon), так и в контекстное меню (Context Menu). Для этого необходимо создать пользовательский плагин. Чтобы вам было проще начать, мы подготовили рекомендации по добавлению пользовательских пунктов меню.
Замена встроенных компонентов
Предупреждение
Замена встроенных компонентов может нарушить работу некоторых функций. Прежде чем заменять компоненты, внимательно изучите исходный код и документацию и действуйте осторожно.
univerAPI.registerComponent по умолчанию регистрирует переопределение (override: true). Оно имеет приоритет независимо от порядка регистрации стандартного компонента плагином. Для каждого componentKey допустимо только одно активное переопределение; перед повторной регистрацией освободите предыдущее. Метод dispose() возвращённого объекта восстанавливает стандартный компонент, если он зарегистрирован. Параметр { override: false } регистрирует стандартный компонент.
Регистрируйте переопределение до первого открытия представления. Изменение регистрации не перерисовывает уже смонтированные представления автоматически.
Публичные точки входа пакетов Sheets экспортируют ключи для переопределения встроенных компонентов:
| Компонент | Экспортируемый ключ | Пакет |
|---|---|---|
| Определённые имена | DEFINED_NAME_CONTAINER | @univerjs/sheets-ui |
| Другие функции | MORE_FUNCTIONS_COMPONENT | @univerjs/sheets-formula-ui |
| Проверка данных | DATA_VALIDATION_PANEL | @univerjs/sheets-data-validation-ui |
| Условное форматирование | CF_PANEL_KEY | @univerjs/sheets-conditional-formatting-ui |
Например, встроенный компонент ColorPicker можно заменить следующим образом:
// Following code only replaces the UI of the built-in ColorPicker component, not its functionalityuniverAPI.registerComponent('UI_COLOR_PICKER_COMPONENT', () => <input type="color" />)Использование в качестве компонентов содержимого
На боковой панели
Метод univerAPI.openSidebar позволяет открыть в интерфейсе Univer боковую панель с пользовательским компонентом.
// You should register the component at an appropriate time (e.g., after Univer has loaded)univerAPI.registerComponent('MyCustomSidebarComponent', () => <div>Hello Univer!</div>)const sidebar = univerAPI.openSidebar({ header: { title: 'My Sidebar' }, children: { label: 'MyCustomSidebarComponent' }, onClose: () => { console.log('close') }, width: 360,})// Later close the sidebarsidebar.dispose()Справочник: univerAPI.openSidebar
В диалоговом окне
Метод univerAPI.openDialog позволяет открыть диалоговое окно с пользовательским компонентом.
// You should register the component at an appropriate time (e.g., after Univer has loaded)univerAPI.registerComponent('MyCustomDialogComponent', () => <div>Hello Univer!</div>)const dialog = univerAPI.openDialog({ id: 'unique-dialog-id', // The unique identifier for the dialog draggable: true, width: 300, title: { title: 'My Dialog' }, children: { label: 'MyCustomDialogComponent', }, destroyOnClose: true, preservePositionOnDestroy: true, onClose: () => {},})// Later close the dialogdialog.dispose()Справочник: univerAPI.openDialog
Добавление всплывающего окна к ячейкам
Метод FRange.attachPopup позволяет добавить пользовательское всплывающее окно к определённому диапазону ячеек.
- Всплывающее окно — это временный DOM-элемент, прикреплённый к ячейке. Обычно он служит для отображения временной информации о состоянии и не поддерживает постоянное хранение данных.
- Метод
FRange.attachPopup(popup: IFCanvasPopup)позволяет добавить пользовательское всплывающее окно к определённому диапазону ячеек. - Всплывающее окно прикрепляется к краям ячейки и автоматически меняет положение и ориентацию, если оказывается перекрыто.
// You should register the component at an appropriate time (e.g., after Univer has loaded)univerAPI.registerComponent( 'MyCustomPopupComponent', () => <div>Hello Univer!</div>,)const fWorkbook = univerAPI.getActiveWorkbook()const fWorksheet = fWorkbook.getActiveSheet()const fRange = fWorksheet.getRange('A1:J10')// Attach the popup to the first cell of the range// If `disposable` is null, it indicates that the popup was not added successfullyconst disposable = fRange.attachPopup({ componentKey: 'MyCustomPopupComponent',})// Remove the popupdisposable.dispose()Добавление предупреждения
Метод FRange.attachAlertPopup позволяет добавить всплывающее предупреждение к начальной ячейке указанного диапазона.
const fWorkbook = univerAPI.getActiveWorkbook()const fWorksheet = fWorkbook.getActiveSheet()const fRange = fWorksheet.getRange('A1:B2')const disposable = fRange.attachAlertPopup({ key: 'unique-alert-key', // Unique identifier title: '这是一个警告!', message: '这是一个警告!', width: 300, height: 200, // 0: Information // 1: Warning // 2: Error type: 0,})// Remove the alert laterdisposable.dispose()Добавление плавающего DOM-элемента на лист
Метод FWorksheet.addFloatDomToPosition позволяет добавить на лист плавающий DOM-элемент.
- Перед использованием этого метода установите плагин
@univerjs/sheets-drawing-uiили пресет@univerjs/preset-sheets-drawing. - Плавающий DOM-элемент — это компонент, который располагается поверх листа, поддерживает перетаскивание и постоянное хранение данных.
- Вызывайте этот метод после завершения отрисовки Univer.
componentKey: уникальный идентификатор компонента, по которому на него можно ссылаться в Univer.- Полное описание параметров
// You should register the component at an appropriate time (e.g., after Univer has loaded)univerAPI.registerComponent( 'MyCustomFloatDOMComponent', ({ data }) => ( <div> Hello {data?.label} ! </div> ),)const fWorkbook = univerAPI.getActiveWorkbook()const fWorksheet = fWorkbook.getActiveSheet()// Add a floating DOM to the worksheet// If `disposable` is null, it indicates that the floating DOM was not added successfullyconst disposable = fWorksheet.addFloatDomToPosition({ componentKey: 'MyCustomFloatDOMComponent', initPosition: { startX: 100, endX: 200, startY: 100, endY: 200, }, // The data for the component data: { label: 'Univer', },})// Remove the floating DOMdisposable.dispose()Насколько полезно это руководство?