Пользовательские компоненты
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 } регистрирует стандартный компонент.
Регистрируйте переопределение до первого открытия представления. Изменение регистрации не перерисовывает уже смонтированные представления автоматически.
Например, встроенный компонент 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
Насколько полезно это руководство?