Adding Custom Menu Item
In Univer, both the top toolbar menu (Ribbon) and the context menu can be extended by writing plugins. This section will introduce how to register menu items using the IMenuManagerService
in the dependency injection system.
1. Plugin Environment
Make sure you have a basic understanding of the plugin mechanism.
First, construct a controller
class to register menu item commands, menu item icons, and menu item configurations.
import { Disposable, ICommandService, Inject, Injector } from '@univerjs/core'
import { ComponentManager, IMenuManagerService } from '@univerjs/ui'
export class CustomMenuController extends Disposable {
constructor(
@Inject(Injector) private readonly _injector: Injector,
@ICommandService private readonly _commandService: ICommandService,
@IMenuManagerService private readonly _menuManagerService: IMenuManagerService,
@Inject(ComponentManager) private readonly _componentManager: ComponentManager,
) {
super()
this._initCommands()
this._registerComponents()
this._initMenus()
}
/**
* register commands
*/
private _initCommands(): void { }
/**
* register icon components
*/
private _registerComponents(): void { }
/**
* register menu items
*/
private _initMenus(): void { }
}
Register this controller
in the plugin.
import type { Dependency } from '@univerjs/core'
import { Inject, Injector, Plugin, touchDependencies, UniverInstanceType } from '@univerjs/core'
import { CustomMenuController } from './controllers/custom-menu.controller'
const SHEET_CUSTOM_MENU_PLUGIN = 'SHEET_CUSTOM_MENU_PLUGIN'
export class UniverSheetsCustomMenuPlugin extends Plugin {
static override type = UniverInstanceType.UNIVER_SHEET
static override pluginName = SHEET_CUSTOM_MENU_PLUGIN
constructor(
@Inject(Injector) protected readonly _injector: Injector,
) {
super()
}
override onStarting(): void {
([
[CustomMenuController],
] as Dependency[]).forEach(d => this._injector.add(d))
}
override onRendered(): void {
touchDependencies(this._injector, [
[CustomMenuController],
])
}
}
2. Menu Item Commands
Before registering the menu, you need to construct a Command
, which will be executed when the menu is clicked.
import type { IAccessor, ICommand } from '@univerjs/core'
import { CommandType } from '@univerjs/core'
export const SingleButtonOperation: ICommand = {
id: 'custom-menu.operation.single-button',
type: CommandType.OPERATION,
handler: async (accessor: IAccessor) => {
console.log('Single button operation')
return true
},
}
Register this Command
with ICommandService
.
import { SingleButtonOperation } from '../commands/operations/single-button.operation'
export class CustomMenuController extends Plugin {
private _initCommands(): void {
[
SingleButtonOperation,
].forEach((c) => {
this.disposeWithMe(this._commandService.registerCommand(c))
})
}
}
3. Menu Item Icons
If your menu item needs an icon, you also need to register the icon in advance.
First, construct an icon tsx component.
export function ButtonIcon() {
return (
<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 24 24">
<path fill="currentColor" d="M12 2c5.523 0 10 4.477 10 10s-4.477 10-10 10S2 17.523 2 12S6.477 2 12 2m.16 14a6.981 6.981 0 0 0-5.147 2.256A7.966 7.966 0 0 0 12 20a7.97 7.97 0 0 0 5.167-1.892A6.979 6.979 0 0 0 12.16 16M12 4a8 8 0 0 0-6.384 12.821A8.975 8.975 0 0 1 12.16 14a8.972 8.972 0 0 1 6.362 2.634A8 8 0 0 0 12 4m0 1a4 4 0 1 1 0 8a4 4 0 0 1 0-8m0 2a2 2 0 1 0 0 4a2 2 0 0 0 0-4" />
</svg>
)
};
Register this icon with ComponentManager
.
import { ButtonIcon } from '../components/button-icon/ButtonIcon'
export class CustomMenuController extends Plugin {
private _registerComponents(): void {
this.disposeWithMe(this._componentManager.register('ButtonIcon', ButtonIcon))
}
}
4. Menu Item Internationalization
If your menu item needs internationalization, you need to add internationalization resources in advance.
export default {
customMenu: {
button: '按钮',
singleButton: '单个按钮',
},
}
export default {
customMenu: {
button: 'Button',
singleButton: 'Single button',
},
}
Register this internationalized resource to ILocaleService
import { LocaleService } from '@univerjs/core'
import enUS from './locale/en-US'
import zhCN from './locale/zh-CN'
export class UniverSheetsCustomMenuPlugin extends Plugin {
static override type = UniverInstanceType.UNIVER_SHEET
static override pluginName = SHEET_CUSTOM_MENU_PLUGIN
constructor(
@Inject(Injector) protected readonly _injector: Injector,
@Inject(LocaleService) private readonly _localeService: LocaleService,
) {
super()
this._localeService.load({
enUS,
zhCN,
})
}
}
5. Menu Item Configuration
Define a menu item configuration factory function that returns a menu item configuration object.
import type { IMenuButtonItem } from '@univerjs/ui'
import { MenuItemType } from '@univerjs/ui'
import { SingleButtonOperation } from '../commands/operations/single-button.operation'
export function CustomMenuItemSingleButtonFactory(): IMenuButtonItem<string> {
return {
// Bind the command id, clicking the button will trigger this command
id: SingleButtonOperation.id,
// The type of the menu item, in this case, it is a button
type: MenuItemType.BUTTON,
// The icon of the button, which needs to be registered in ComponentManager
icon: 'ButtonIcon',
// The tooltip of the button. Prioritize matching internationalization. If no match is found, the original string will be displayed
tooltip: 'customMenu.singleButton',
// The title of the button. Prioritize matching internationalization. If no match is found, the original string will be displayed
title: 'customMenu.button',
}
}
Construct these menu items into a Schema and merge them into the menu through IMenuManagerService
.
import { ContextMenuGroup, ContextMenuPosition, RibbonStartGroup } from '@univerjs/ui'
import { SingleButtonOperation } from '../commands/operations/single-button.operation'
import { CustomMenuItemSingleButtonFactory } from './menu'
export class CustomMenuController extends Disposable {
private _initMenus(): void {
this._menuManagerService.mergeMenu({
[RibbonStartGroup.OTHERS]: {
[SingleButtonOperation.id]: {
order: 10,
menuItemFactory: CustomMenuItemSingleButtonFactory,
},
},
[ContextMenuPosition.MAIN_AREA]: {
[ContextMenuGroup.OTHERS]: {
[SingleButtonOperation.id]: {
order: 12,
menuItemFactory: CustomMenuItemSingleButtonFactory,
},
},
},
})
}
}
6. Dropdown List
In addition to adding a single button menu item, you can also add a dropdown menu item. The specific implementation is similar, except for the difference in constructing the menu item configuration:
- Replace menu item configuration return type
IMenuButtonItem<string>
withIMenuSelectorItem<string>
- Replace menu item type
MenuItemType.BUTTON
withMenuItemType.SUBITEMS
- The main button of the dropdown list needs to customize an id, which is used as the unique identifier of the dropdown list and is used to associate the sub-menu items of the dropdown list.
import type { IMenuButtonItem, IMenuSelectorItem } from '@univerjs/ui'
import { MenuItemType } from '@univerjs/ui'
import { DropdownListFirstItemOperation, DropdownListSecondItemOperation } from '../../commands/operations/dropdown-list.operation'
const CUSTOM_MENU_DROPDOWN_LIST_OPERATION_ID = 'custom-menu.operation.dropdown-list'
export function CustomMenuItemDropdownListMainButtonFactory(): IMenuSelectorItem<string> {
return {
// When type is MenuItemType.SUBITEMS, this factory serves as a container for the drop-down list, and you can set any unique id
id: CUSTOM_MENU_DROPDOWN_LIST_OPERATION_ID,
// The type of the menu item, in this case, it is a subitems
type: MenuItemType.SUBITEMS,
icon: 'MainButtonIcon',
tooltip: 'customMenu.dropdownList',
title: 'customMenu.dropdown',
}
}
export function CustomMenuItemDropdownListFirstItemFactory(): IMenuButtonItem<string> {
return {
id: DropdownListFirstItemOperation.id,
type: MenuItemType.BUTTON,
title: 'customMenu.itemOne',
icon: 'ItemIcon',
}
}
export function CustomMenuItemDropdownListSecondItemFactory(): IMenuButtonItem<string> {
return {
id: DropdownListSecondItemOperation.id,
type: MenuItemType.BUTTON,
title: 'customMenu.itemTwo',
icon: 'ItemIcon',
}
}
Then construct these menu items into a Schema and merge them into the menu through IMenuManagerService
.
import { DropdownListFirstItemOperation, DropdownListSecondItemOperation } from '../commands/operations/dropdown-list.operation'
import { CUSTOM_MENU_DROPDOWN_LIST_OPERATION_ID, CustomMenuItemDropdownListFirstItemFactory, CustomMenuItemDropdownListMainButtonFactory, CustomMenuItemDropdownListSecondItemFactory } from './menu/dropdown-list.menu'
export class CustomMenuController extends Disposable {
private _initMenus(): void {
this._menuManagerService.mergeMenu({
[RibbonStartGroup.OTHERS]: {
[CUSTOM_MENU_DROPDOWN_LIST_OPERATION_ID]: {
order: 11,
menuItemFactory: CustomMenuItemDropdownListMainButtonFactory,
[DropdownListFirstItemOperation.id]: {
order: 0,
menuItemFactory: CustomMenuItemDropdownListFirstItemFactory,
},
[DropdownListSecondItemOperation.id]: {
order: 1,
menuItemFactory: CustomMenuItemDropdownListSecondItemFactory,
},
},
},
[ContextMenuPosition.MAIN_AREA]: {
[ContextMenuGroup.OTHERS]: {
[CUSTOM_MENU_DROPDOWN_LIST_OPERATION_ID]: {
order: 9,
menuItemFactory: CustomMenuItemDropdownListMainButtonFactory,
[DropdownListFirstItemOperation.id]: {
order: 0,
menuItemFactory: CustomMenuItemDropdownListFirstItemFactory,
},
[DropdownListSecondItemOperation.id]: {
order: 1,
menuItemFactory: CustomMenuItemDropdownListSecondItemFactory,
},
},
},
},
})
}
}
Export the plugin and register it with Univer instance.
export { UniverSheetsCustomMenuPlugin } from './plugin'
import { UniverSheetsCustomMenuPlugin } from './plugin'
univer.registerPlugin(UniverSheetsCustomMenuPlugin)
Now you have successfully added a custom menu item to Univer. You can see the menu item in the top toolbar and context menu.
Menu Item ID List
You can find the menu item ID through the data-u-command
attribute on the menu item DOM element.
Toolbar Menu Items
Menu Item ID | Menu Item Name |
---|---|
univer.command.undo | Undo |
univer.command.redo | Redo |
sheet.command.set-once-format-painter | Paint format |
sheet.command.set-range-bold | Bold |
sheet.command.set-range-italic | Italic |
sheet.command.set-range-underline | Underline |
sheet.command.set-range-stroke | Strikethrough |
sheet.command.set-range-font-family | Font |
sheet.command.set-range-fontsize | Font size |
sheet.command.set-range-text-color | Text color |
sheet.command.set-background-color | Fill color |
sheet.command.set-border-basic | Border |
sheet.command.set-horizontal-text-align | Horizontal align |
sheet.command.set-vertical-text-align | Vertical align |
sheet.command.set-text-wrap | Text wrap |
sheet.command.set-text-rotation | Text rotate |
sheet.command.add-worksheet-merge | Merge cells |
sheet.command.add-worksheet-merge-all | Merge cells - Merge all |
sheet.command.add-worksheet-merge-vertical | Merge cells - Vertical merge |
sheet.command.add-worksheet-merge-horizontal | Merge cells - Horizontal merge |
sheet.command.remove-worksheet-merge | Merge cells - Cancel merge |
sheet.operation.open.conditional.formatting.panel | Conditional Formatting |
formula-ui.operation.insert-function | Functions |
formula-ui.operation.more-functions | Functions - More Functions |
sheet.menu.sheets-sort | Sort |
sheet.command.sort-range-asc | Sort - Ascending |
sheet.command.sort-range-asc-ext | Sort - Expand Ascending |
sheet.command.sort-range-desc | Sort - Descending |
sheet.command.sort-range-desc-ext | Sort - Expand Descending |
sheet.command.sort-range-custom | Sort - Custom Sort |
sheet.menu.image | Image |
sheet.command.insert-float-image | Image - Float Image |
sheet.command.insert-cell-image | Image - Cell Image |
sheet.command.numfmt.set.currency | Currency |
sheet.command.numfmt.add.decimal.command | Increase decimal places |
sheet.command.numfmt.subtract.decimal.command | Decreasing decimal places |
sheet.command.numfmt.set.percent | Percentage |
sheet.operation.open.numfmt.panel | Number format |
sheet.menu.data-validation | Data validation |
data-validation.operation.open-validation-panel | Data validation - Data validation management |
data-validation.command.addRuleAndOpen | Data validation - Add Rule |
sheet.command.smart-toggle-filter | Toggle Filter |
sheet.command.clear-filter-criteria | Toggle Filter - Clear Filter Conditions |
sheet.command.re-calc-filter | Toggle Filter - Re-calc Filter Conditions |
sheet.operation.open-pivot-table-range-selector-panel | Pivot Table |
sheet.operation.print-open | |
data-connector.operation.sidebar | Data Connector |
sheets-exchange-client.operation.exchange | File |
exchange-client.operation.import-xlsx | File - Open(File) |
exchange-client.operation.export-xlsx | File - Save As |
sheet.command.menu-insert-chart | Insert chart |
sheet.command.add-range-protection-from-toolbar | Protection |
univer.operation.toggle-edit-history | Edit History |
sheet.operation.open-sparkline-selector | Sparkline |
thread-comment-ui.operation.toggle-panel | Comment Management |
sheet.operation.insert-hyper-link-toolbar | Insert Link |
ui.operation.open-find-dialog | Find & Replace |
base-ui.operation.toggle-shortcut-panel | Toggle Shortcut Panel |
Context Menu Items
Menu Item ID | Menu Item Name |
---|---|
sheet.command.copy | Copy |
sheet.command.paste | Paste |
sheet.menu.paste-special | Paste Special |
sheet.command.paste-values | Paste Special - Paste Value |
sheet.command.paste-format | Paste Special - Paste Format |
sheet.command.paste-col-width | Paste Special - Paste Column Width |
sheet.command.paste-besides-border | Paste Special - Paste Besides Border Styles |
sheet.command.paste-formula | Paste Special - Paste Formula |
sheet.menu.clear-selection | Clear |
sheet.command.clear-selection-content | Clear - Clear Contents |
sheet.command.clear-selection-format | Clear - Clear Formats |
sheet.command.clear-selection-all | Clear - Clear All |
sheet.menu.cell-insert | Insert |
sheet.command.insert-row-before | Insert - Insert Row Before |
sheet.command.insert-col-before | Insert - Insert Column Before |
sheet.command.insert-range-move-right-confirm | Insert - Move Right |
sheet.command.insert-range-move-down-confirm | Insert - Move Down |
sheet.menu.delete | Delete |
sheet.command.remove-row-confirm | Delete Selected Row |
sheet.command.remove-col-confirm | Delete Selected Column |
sheet.command.delete-range-move-left-confirm | Delete - Move Left |
sheet.command.delete-range-move-up-confirm | Delete - Move Up |
sheet.menu.sheet-frozen | Freeze |
sheet.header-menu.sheet-frozen | Freeze (Row & Column Header Right Click Menu Item) |
sheet.command.set-selection-frozen | Freeze - Freeze |
sheet.command.set-row-frozen | Freeze - Freeze to this row |
sheet.command.set-col-frozen | Freeze - Freeze to this column |
sheet.command.cancel-frozen | Freeze - Cancel freeze |
sheet.contextMenu.permission | Protect Rows And Columns |
sheet.command.add-range-protection-from-context-menu | Protect Rows And Columns - Add Protection Range |
sheet.command.set-range-protection-from-context-menu | Protect Rows And Columns - Set Protection Range |
sheet.command.delete-range-protection-from-context-menu | Protect Rows And Columns - Remove Protection Range |
sheet.command.view-sheet-permission-from-context-menu | Protect Rows And Columns - View All Protection Ranges |
sheet.menu.sheets-sort-ctx | Sort |
sheet.command.sort-range-asc-ctx | Sort - Ascending |
sheet.command.sort-range-asc-ext-ctx | Sort - Expand Ascending |
sheet.command.sort-range-desc-ctx | Sort - Descending |
sheet.command.sort-range-desc-ext-ctx | Sort - Expand Descending |
sheet.command.sort-range-custom-ctx | Sort - Custom Sort |
sheets.operation.show-comment-modal | Add Comment |
sheet.operation.insert-hyper-link | Insert Link |
zen-editor.command.open-zen-editor | Full Screen Editor |
sheet.operation.screenshot | Copy as picture |
Context Menu Items - Row Header
Menu Item ID | Menu Item Name |
---|---|
sheet.command.insert-multi-rows-above | Insert Row Before |
sheet.command.insert-multi-rows-after | Insert Row After |
sheet.command.hide-row-confirm | Hide Selected Row |
sheet.command.set-selected-rows-visible | Show Hide Row |
sheet.command.set-row-height | Row Height |
sheet.command.set-row-is-auto-height | Fit for data |
Context Menu Items - Column Header
Menu Item ID | Menu Item Name |
---|---|
sheet.command.insert-multi-cols-before | Insert Column Before |
sheet.command.insert-multi-cols-right | Insert Column After |
sheet.command.hide-col-confirm | Hide Selected Column |
sheet.command.set-selected-cols-visible | Show Hide Column |
sheet.command.set-worksheet-col-width | Column Width |
sheet.command.set-col-auto-width | Fit for data |