mirror of
https://github.com/dream-num/univer.git
synced 2026-10-02 04:24:34 +08:00
feat(ui): expose runtime menu configuration updates (#7718)
This commit is contained in:
@@ -14,6 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import type { IMenuSchema } from '@univerjs/ui';
|
||||
import {
|
||||
CommandType,
|
||||
ICommandService,
|
||||
@@ -33,6 +34,7 @@ import {
|
||||
CopyCommand,
|
||||
DesktopDialogService,
|
||||
DesktopMessageService,
|
||||
DesktopRibbonService,
|
||||
DesktopSidebarService,
|
||||
FontService,
|
||||
IDialogService,
|
||||
@@ -40,6 +42,7 @@ import {
|
||||
IMenuManagerService,
|
||||
IMessageService,
|
||||
IPlatformService,
|
||||
IRibbonService,
|
||||
IShortcutService,
|
||||
ISidebarService,
|
||||
IUIPartsService,
|
||||
@@ -48,6 +51,7 @@ import {
|
||||
MenuManagerService,
|
||||
PasteCommand,
|
||||
PlatformService,
|
||||
RibbonStartGroup,
|
||||
ShortcutService,
|
||||
UI_PLUGIN_CONFIG_KEY,
|
||||
UIPartsService,
|
||||
@@ -153,6 +157,53 @@ describe('ui facade', () => {
|
||||
expect(menuKeys).toContain('second-action');
|
||||
});
|
||||
|
||||
it('updates ribbon menu order and grid layout while preserving menu actions and earlier overrides', async () => {
|
||||
const injector = univer.__getInjector();
|
||||
injector.add([IRibbonService, { useClass: DesktopRibbonService }]);
|
||||
const ribbonService = injector.get(IRibbonService);
|
||||
const actionCalls: string[] = [];
|
||||
let ribbon: IMenuSchema[] = [];
|
||||
const subscription = ribbonService.ribbon$.subscribe((value) => {
|
||||
ribbon = value;
|
||||
});
|
||||
|
||||
univerAPI.updateMenuConfig({
|
||||
first: { title: 'Renamed', gridLayout: { row: 1, column: 1, width: 80 } },
|
||||
});
|
||||
univerAPI.createMenu({
|
||||
id: 'first',
|
||||
title: 'First',
|
||||
action: () => actionCalls.push('clicked'),
|
||||
order: 0,
|
||||
gridLayout: { row: 1, column: 1, showLabel: true },
|
||||
}).appendTo(RibbonStartGroup.FORMAT);
|
||||
univerAPI.createSubmenu({
|
||||
id: 'second',
|
||||
title: 'Second',
|
||||
order: 1,
|
||||
gridLayout: { row: 2, column: 1 },
|
||||
}).addSubmenu(univerAPI.createMenu({ id: 'child', title: 'Child', action: 'child.command' })).appendTo(RibbonStartGroup.FORMAT);
|
||||
|
||||
expect(ribbon[0].children?.[0].children?.map((item) => [item.key, item.gridLayout?.row])).toEqual([
|
||||
['first', 1],
|
||||
['second', 2],
|
||||
]);
|
||||
expect(univerAPI.updateMenuConfig({
|
||||
first: { order: 2, gridLayout: { row: 2, column: 1 } },
|
||||
second: { order: 0, gridLayout: { row: 1, column: 1 } },
|
||||
})).toBe(univerAPI);
|
||||
|
||||
const items = ribbon[0].children![0].children!;
|
||||
expect(items.map((item) => item.key)).toEqual(['second', 'first']);
|
||||
expect(items[0].gridLayout).toEqual({ row: 1, column: 1 });
|
||||
expect(items[1].gridLayout).toEqual({ row: 2, column: 1, showLabel: true, width: 80 });
|
||||
expect(items[1].item?.title).toBe('Renamed');
|
||||
await injector.get(ICommandService).executeCommand(items[1].item!.commandId!);
|
||||
expect(actionCalls).toEqual(['clicked']);
|
||||
|
||||
subscription.unsubscribe();
|
||||
});
|
||||
|
||||
it('registers UI parts, toggles visibility, and exposes UI enums from the facade', () => {
|
||||
const component = () => null;
|
||||
const disposable = univerAPI.registerUIPart(BuiltInUIPart.CUSTOM_HEADER, component);
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
*/
|
||||
|
||||
import type { IAccessor } from '@univerjs/core';
|
||||
import type { IMenuButtonItem, IMenuItem, MenuSchemaType } from '@univerjs/ui';
|
||||
import type { IMenuButtonItem, IMenuItem, IRibbonGridLayout, MenuSchemaType } from '@univerjs/ui';
|
||||
import { CommandType, generateRandomId, ICommandService, Inject, Injector } from '@univerjs/core';
|
||||
import { FBase } from '@univerjs/core/facade';
|
||||
import { IMenuManagerService, MenuItemType, MenuManagerPosition, RibbonPosition, RibbonStartGroup } from '@univerjs/ui';
|
||||
@@ -49,6 +49,8 @@ export interface IFacadeMenuItem {
|
||||
* The order of the menu item in the submenu.
|
||||
*/
|
||||
order?: number;
|
||||
/** Placement within a ribbon group when ribbonType is 'grid'. */
|
||||
gridLayout?: IRibbonGridLayout;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -75,6 +77,8 @@ export interface IFacadeSubmenuItem {
|
||||
* The order of the menu item in the submenu.
|
||||
*/
|
||||
order?: number;
|
||||
/** Placement within a ribbon group when ribbonType is 'grid'. */
|
||||
gridLayout?: IRibbonGridLayout;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -156,6 +160,7 @@ export class FMenu extends FMenuBase {
|
||||
private _commandToRegister = new Map<string, (accessor: IAccessor) => void>();
|
||||
private _buildingSchema: {
|
||||
order?: number;
|
||||
gridLayout?: IRibbonGridLayout;
|
||||
menuItemFactory?: (accessor: IAccessor) => IMenuItem;
|
||||
};
|
||||
|
||||
@@ -186,6 +191,10 @@ export class FMenu extends FMenuBase {
|
||||
if (typeof _item.order !== 'undefined') {
|
||||
this._buildingSchema.order = _item.order;
|
||||
}
|
||||
|
||||
if (_item.gridLayout) {
|
||||
this._buildingSchema.gridLayout = _item.gridLayout;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -222,6 +231,7 @@ export class FSubmenu extends FMenuBase {
|
||||
|
||||
private _buildingSchema: {
|
||||
order?: number;
|
||||
gridLayout?: IRibbonGridLayout;
|
||||
menuItemFactory?: (accessor: IAccessor) => IMenuItem;
|
||||
};
|
||||
|
||||
@@ -245,6 +255,10 @@ export class FSubmenu extends FMenuBase {
|
||||
if (typeof _item.order !== 'undefined') {
|
||||
this._buildingSchema.order = _item.order;
|
||||
}
|
||||
|
||||
if (_item.gridLayout) {
|
||||
this._buildingSchema.gridLayout = _item.gridLayout;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -16,13 +16,33 @@
|
||||
|
||||
import type { IDisposable } from '@univerjs/core';
|
||||
import type { IMessageProps } from '@univerjs/design';
|
||||
import type { BuiltInUIPart, ComponentType, IComponentOptions, IDialogPartMethodOptions, IFontConfig, ISidebarMethodOptions, RibbonType } from '@univerjs/ui';
|
||||
import type {
|
||||
BuiltInUIPart,
|
||||
ComponentType,
|
||||
IComponentOptions,
|
||||
IDialogPartMethodOptions,
|
||||
IFontConfig,
|
||||
ISidebarMethodOptions,
|
||||
MenuConfig,
|
||||
RibbonType,
|
||||
} from '@univerjs/ui';
|
||||
import type { IFacadeMenuItem, IFacadeSubmenuItem } from './f-menu-builder';
|
||||
import { IConfigService } from '@univerjs/core';
|
||||
import { FUniver } from '@univerjs/core/facade';
|
||||
import { IRenderManagerService } from '@univerjs/engine-render';
|
||||
|
||||
import { ComponentManager, connectInjector, CopyCommand, IDialogService, IFontService, IMessageService, ISidebarService, IUIPartsService, PasteCommand, UI_PLUGIN_CONFIG_KEY } from '@univerjs/ui';
|
||||
import {
|
||||
ComponentManager,
|
||||
connectInjector,
|
||||
CopyCommand,
|
||||
IDialogService,
|
||||
IFontService,
|
||||
IMenuManagerService,
|
||||
IMessageService,
|
||||
ISidebarService,
|
||||
IUIPartsService,
|
||||
PasteCommand,
|
||||
UI_PLUGIN_CONFIG_KEY,
|
||||
} from '@univerjs/ui';
|
||||
import { FMenu, FSubmenu } from './f-menu-builder';
|
||||
import { FShortcut } from './f-shortcut';
|
||||
|
||||
@@ -225,6 +245,30 @@ export interface IFUniverUIMixin {
|
||||
*/
|
||||
createSubmenu(submenuItem: IFacadeSubmenuItem): FSubmenu;
|
||||
|
||||
/**
|
||||
* Merge menu overrides and refresh the UI immediately. Overrides also apply to menus registered later.
|
||||
* Use the same configuration as the UI plugin or preset's `menu` option.
|
||||
* @param {MenuConfig} config Overrides keyed by menu item ID, ribbon tab key, or ribbon group key.
|
||||
* Unspecified properties keep their current values. Lower `order` values come first among siblings.
|
||||
* `gridLayout` applies only to the grid ribbon and uses 1-based positions within a two-row group.
|
||||
* @returns the {@link FUniver} instance for chaining
|
||||
* @example
|
||||
* ```ts
|
||||
* univerAPI.updateMenuConfig({
|
||||
* 'ribbon.insert': { order: -1 },
|
||||
* 'ribbon.start.format': { order: -1 },
|
||||
* 'sheet.command.set-range-bold': {
|
||||
* order: 0,
|
||||
* gridLayout: { row: 2, column: 2, showLabel: true, width: 80 },
|
||||
* },
|
||||
* 'sheet.command.set-range-italic': {
|
||||
* gridLayout: { row: 2, column: 1 },
|
||||
* },
|
||||
* });
|
||||
* ```
|
||||
*/
|
||||
updateMenuConfig(config: MenuConfig): FUniver;
|
||||
|
||||
/**
|
||||
* Open a sidebar.
|
||||
* @param {ISidebarMethodOptions} params the sidebar options
|
||||
@@ -479,6 +523,11 @@ export class FUniverUIMixin extends FUniver implements IFUniverUIMixin {
|
||||
return this._injector.createInstance(FSubmenu, submenuItem);
|
||||
}
|
||||
|
||||
override updateMenuConfig(config: MenuConfig): FUniver {
|
||||
this._injector.get(IMenuManagerService).updateMenuConfig(config);
|
||||
return this;
|
||||
}
|
||||
|
||||
override openSidebar(params: ISidebarMethodOptions): IDisposable {
|
||||
const sideBarService = this._injector.get(ISidebarService);
|
||||
return sideBarService.open(params);
|
||||
|
||||
@@ -14,42 +14,43 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import type { MenuSchemaType } from '../menu-manager.service';
|
||||
import type { MenuConfig } from '../menu';
|
||||
import { ConfigService, IConfigService, Injector } from '@univerjs/core';
|
||||
|
||||
import { describe, expect, it } from 'vitest';
|
||||
import { isMenuButtonSelectorItem, isMenuSelectorItem, MenuItemType } from '../menu';
|
||||
import { IMenuManagerService, MenuManagerService } from '../menu-manager.service';
|
||||
import { FloatingObjectToolbarPosition } from '../types';
|
||||
import { FloatingObjectToolbarPosition, MenuManagerPosition, RibbonPosition, RibbonStartGroup } from '../types';
|
||||
|
||||
function createService(): IMenuManagerService {
|
||||
function createInjector(): Injector {
|
||||
const injector = new Injector();
|
||||
injector.add([IConfigService, { useClass: ConfigService }]);
|
||||
injector.add([IMenuManagerService, { useClass: MenuManagerService }]);
|
||||
return injector.get(IMenuManagerService);
|
||||
return injector;
|
||||
}
|
||||
|
||||
describe('MenuManagerService', () => {
|
||||
it('appends command menus and returns them in display order', () => {
|
||||
const service = createService();
|
||||
const service = createInjector().get(IMenuManagerService);
|
||||
|
||||
service.appendRootMenu({
|
||||
testPosition: {
|
||||
second: { order: 2, menuItemFactory: () => ({ id: 'second' }) },
|
||||
first: { order: 1, menuItemFactory: () => ({ id: 'first' }) },
|
||||
second: { order: 2, menuItemFactory: () => ({ id: 'second', type: MenuItemType.BUTTON }) },
|
||||
first: { order: 1, menuItemFactory: () => ({ id: 'first', type: MenuItemType.BUTTON }) },
|
||||
},
|
||||
} as MenuSchemaType);
|
||||
});
|
||||
|
||||
expect(service.getMenuByPositionKey('testPosition').map((item) => item.key)).toEqual(['first', 'second']);
|
||||
expect(service.getFlatMenuByPositionKey('testPosition').map((item) => item.item?.id)).toEqual(['first', 'second']);
|
||||
});
|
||||
|
||||
it('merges menu contributions into existing positions and emits a change event', () => {
|
||||
const service = createService();
|
||||
const service = createInjector().get(IMenuManagerService);
|
||||
const changes: void[] = [];
|
||||
const sub = service.menuChanged$.subscribe((value) => changes.push(value));
|
||||
|
||||
service.appendRootMenu({ testMerge: { group: { order: 1 } } } as MenuSchemaType);
|
||||
service.mergeMenu({ group: { command: { order: 1, menuItemFactory: () => ({ id: 'command' }) } } } as MenuSchemaType);
|
||||
service.appendRootMenu({ testMerge: { group: { order: 1 } } });
|
||||
service.mergeMenu({ group: { command: { order: 1, menuItemFactory: () => ({ id: 'command', type: MenuItemType.BUTTON }) } } });
|
||||
|
||||
expect(service.getFlatMenuByPositionKey('testMerge').map((item) => item.key)).toEqual(['group', 'command']);
|
||||
expect(changes.length).toBeGreaterThanOrEqual(2);
|
||||
@@ -57,11 +58,11 @@ describe('MenuManagerService', () => {
|
||||
});
|
||||
|
||||
it.each(Object.values(FloatingObjectToolbarPosition))('accepts contributions for %s', (position) => {
|
||||
const service = createService();
|
||||
const service = createInjector().get(IMenuManagerService);
|
||||
|
||||
service.mergeMenu({
|
||||
[position]: {
|
||||
comment: { order: 1, menuItemFactory: () => ({ id: 'comment' }) },
|
||||
comment: { order: 1, menuItemFactory: () => ({ id: 'comment', type: MenuItemType.BUTTON }) },
|
||||
},
|
||||
});
|
||||
|
||||
@@ -69,7 +70,7 @@ describe('MenuManagerService', () => {
|
||||
});
|
||||
|
||||
it('returns an empty array for missing menu positions', () => {
|
||||
const service = createService();
|
||||
const service = createInjector().get(IMenuManagerService);
|
||||
|
||||
expect(service.getMenuByPositionKey('missing-position')).toEqual([]);
|
||||
expect(service.getFlatMenuByPositionKey('missing-position')).toEqual([]);
|
||||
@@ -81,4 +82,48 @@ describe('MenuManagerService', () => {
|
||||
expect(isMenuSelectorItem({ id: 'copy', type: MenuItemType.BUTTON })).toBe(false);
|
||||
expect(isMenuButtonSelectorItem({ id: 'fill-color', type: MenuItemType.BUTTON_SELECTOR })).toBe(true);
|
||||
});
|
||||
|
||||
it('applies configured tab, group, and item order to menus registered later', () => {
|
||||
const injector = createInjector();
|
||||
const service = injector.get(IMenuManagerService);
|
||||
injector.get(IConfigService).setConfig('menu', {
|
||||
[RibbonPosition.INSERT]: { order: -1 },
|
||||
[RibbonStartGroup.FORMAT]: { order: -1 },
|
||||
font: { order: 2, gridLayout: { row: 2, column: 1, width: 120 } },
|
||||
} satisfies MenuConfig);
|
||||
|
||||
service.mergeMenu({
|
||||
[RibbonPosition.START]: {
|
||||
[RibbonStartGroup.HISTORY]: {
|
||||
undo: { order: 0, menuItemFactory: () => ({ id: 'undo', type: MenuItemType.BUTTON }) },
|
||||
},
|
||||
[RibbonStartGroup.FORMAT]: {
|
||||
fontMenu: {
|
||||
order: 0,
|
||||
gridLayout: { row: 1, column: 1, columnSpan: 2, showLabel: true, width: 180 },
|
||||
menuItemFactory: () => ({ id: 'font', type: MenuItemType.SELECTOR }),
|
||||
},
|
||||
bold: { order: 1, menuItemFactory: () => ({ id: 'bold', type: MenuItemType.BUTTON }) },
|
||||
},
|
||||
},
|
||||
[RibbonPosition.INSERT]: {
|
||||
custom: {
|
||||
order: 0,
|
||||
image: { order: 0, menuItemFactory: () => ({ id: 'image', type: MenuItemType.BUTTON }) },
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
expect(service.getMenuByPositionKey(MenuManagerPosition.RIBBON).map((tab) => tab.key)).toEqual([
|
||||
RibbonPosition.INSERT,
|
||||
RibbonPosition.START,
|
||||
]);
|
||||
expect(service.getMenuByPositionKey(RibbonPosition.START).map((group) => group.key)).toEqual([
|
||||
RibbonStartGroup.FORMAT,
|
||||
RibbonStartGroup.HISTORY,
|
||||
]);
|
||||
const items = service.getMenuByPositionKey(RibbonStartGroup.FORMAT);
|
||||
expect(items.map((item) => item.key)).toEqual(['bold', 'fontMenu']);
|
||||
expect(items[1].gridLayout).toEqual({ row: 2, column: 1, columnSpan: 2, showLabel: true, width: 120 });
|
||||
});
|
||||
});
|
||||
|
||||
@@ -14,9 +14,9 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import type { IAccessor, Nullable } from '@univerjs/core';
|
||||
import type { IAccessor } from '@univerjs/core';
|
||||
import type { Observable } from 'rxjs';
|
||||
import type { IMenuItem } from './menu';
|
||||
import type { IMenuItem, MenuConfig } from './menu';
|
||||
import { createIdentifier, Disposable, IConfigService, Inject, Injector, merge } from '@univerjs/core';
|
||||
import { Subject } from 'rxjs';
|
||||
import { mergeMenuConfigs } from '../../common/menu-merge-configs';
|
||||
@@ -41,12 +41,19 @@ export type ContextMenuQuickLayoutVariant = 'default' | 'compact';
|
||||
export type MobileMenuLayout = 'grid' | 'list';
|
||||
|
||||
export interface IRibbonGridLayout {
|
||||
/** 1-based row within the ribbon group's two rows. */
|
||||
row: number;
|
||||
/** 1-based column within the ribbon group. */
|
||||
column: number;
|
||||
/** Number of rows occupied. Defaults to 1; use 2 for a large button. */
|
||||
rowSpan?: number;
|
||||
/** Number of columns occupied. Defaults to 1. */
|
||||
columnSpan?: number;
|
||||
/** Whether to display the menu title alongside its icon. */
|
||||
showLabel?: boolean;
|
||||
/** Width in pixels. */
|
||||
width?: number;
|
||||
/** Icon size in pixels. */
|
||||
iconSize?: number;
|
||||
}
|
||||
|
||||
@@ -73,6 +80,8 @@ export interface IMenuManagerService {
|
||||
|
||||
appendRootMenu(source: MenuSchemaType): void;
|
||||
|
||||
updateMenuConfig(config: MenuConfig): void;
|
||||
|
||||
getMenuByPositionKey(position: string): IMenuSchema[];
|
||||
|
||||
getFlatMenuByPositionKey(position: string): IMenuSchema[];
|
||||
@@ -302,6 +311,7 @@ export class MenuManagerService extends Disposable implements IMenuManagerServic
|
||||
|
||||
override dispose(): void {
|
||||
this.menuChanged$.complete();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -330,18 +340,23 @@ export class MenuManagerService extends Disposable implements IMenuManagerServic
|
||||
this.menuChanged$.next();
|
||||
}
|
||||
|
||||
updateMenuConfig(config: MenuConfig): void {
|
||||
this._configService.setConfig('menu', config, { merge: true });
|
||||
this.menuChanged$.next();
|
||||
}
|
||||
|
||||
createScoped(injector: Injector): IMenuManagerService {
|
||||
const root = this;
|
||||
const createScopedBuilder = () => {
|
||||
const service = new MenuManagerService(injector, root._configService);
|
||||
service._menu = root._menu;
|
||||
const service = new MenuManagerService(injector, this._configService);
|
||||
service._menu = this._menu;
|
||||
return service;
|
||||
};
|
||||
|
||||
return {
|
||||
menuChanged$: root.menuChanged$,
|
||||
mergeMenu: (source: MenuSchemaType, target?: MenuSchemaType) => root.mergeMenu(source, target),
|
||||
appendRootMenu: (source: MenuSchemaType) => root.appendRootMenu(source),
|
||||
menuChanged$: this.menuChanged$,
|
||||
mergeMenu: (source: MenuSchemaType, target?: MenuSchemaType) => this.mergeMenu(source, target),
|
||||
appendRootMenu: (source: MenuSchemaType) => this.appendRootMenu(source),
|
||||
updateMenuConfig: (config: MenuConfig) => this.updateMenuConfig(config),
|
||||
getMenuByPositionKey: (position: string) => createScopedBuilder().getMenuByPositionKey(position),
|
||||
getFlatMenuByPositionKey: (position: string) => createScopedBuilder().getFlatMenuByPositionKey(position),
|
||||
};
|
||||
@@ -349,15 +364,18 @@ export class MenuManagerService extends Disposable implements IMenuManagerServic
|
||||
|
||||
private _buildMenuSchema(data: MenuSchemaType): IMenuSchema[] {
|
||||
const result: IMenuSchema[] = [];
|
||||
const menuConfig = this._configService.getConfig<MenuConfig>('menu');
|
||||
|
||||
for (const [key, value] of Object.entries(data)) {
|
||||
if (key === 'replace') {
|
||||
continue;
|
||||
}
|
||||
|
||||
const item: IMenuItem | undefined = value.menuItemFactory ? this._injector.invoke(value.menuItemFactory) : undefined;
|
||||
const config = menuConfig?.[item?.id ?? key];
|
||||
const menuItem: Partial<IMenuSchema> = {
|
||||
key,
|
||||
order: value.order,
|
||||
order: config?.order ?? value.order,
|
||||
title: value.title,
|
||||
contextual: value.contextual,
|
||||
quickLayout: value.quickLayout,
|
||||
@@ -365,22 +383,11 @@ export class MenuManagerService extends Disposable implements IMenuManagerServic
|
||||
quickLayoutVariant: value.quickLayoutVariant,
|
||||
mobileLayout: value.mobileLayout,
|
||||
tiny: value.tiny,
|
||||
gridLayout: value.gridLayout,
|
||||
gridLayout: config?.gridLayout ? { ...value.gridLayout, ...config.gridLayout } : value.gridLayout,
|
||||
};
|
||||
|
||||
if (value.menuItemFactory) {
|
||||
const item: IMenuItem = this._injector.invoke(value.menuItemFactory);
|
||||
|
||||
if (item) {
|
||||
const menuItemConfig: Nullable<IMenuItem> = this._configService.getConfig<IMenuItem>('menu');
|
||||
|
||||
if (menuItemConfig && item.id in menuItemConfig) {
|
||||
const _key = item.id as keyof IMenuItem;
|
||||
menuItem.item = mergeMenuConfigs(item, menuItemConfig[_key] as any);
|
||||
} else {
|
||||
menuItem.item = item;
|
||||
}
|
||||
}
|
||||
if (item) {
|
||||
menuItem.item = config ? mergeMenuConfigs(item, config) : item;
|
||||
}
|
||||
if (value.headerActionMenuItemFactory) {
|
||||
menuItem.headerActionItem = this._injector.invoke(value.headerActionMenuItemFactory);
|
||||
|
||||
@@ -17,6 +17,8 @@
|
||||
import type { IAccessor } from '@univerjs/core';
|
||||
import type { Observable } from 'rxjs';
|
||||
|
||||
import type { IRibbonGridLayout } from './menu-manager.service';
|
||||
|
||||
export type OneOrMany<T> = T | T[];
|
||||
|
||||
export enum MenuItemType {
|
||||
@@ -144,6 +146,11 @@ export type MenuItemConfig<TLocaleKey extends string = string> = Partial<Omit<IM
|
||||
hidden?: boolean;
|
||||
disabled?: boolean;
|
||||
activated?: boolean;
|
||||
/** Display order among sibling menu items, ribbon tabs, or groups. Lower values come first. */
|
||||
order?: number;
|
||||
/** Placement within a ribbon group when ribbonType is 'grid'. Rows and columns start at 1. */
|
||||
gridLayout?: IRibbonGridLayout;
|
||||
}>;
|
||||
/** Overrides keyed by menu item ID, ribbon tab key, or ribbon group key. */
|
||||
export type MenuConfig<TLocaleKey extends string = string> = Record<string, MenuItemConfig<TLocaleKey>>;
|
||||
export type IMenuItemFactory<TLocaleKey extends string = string> = (accessor: IAccessor, menuConfig?: MenuConfig<TLocaleKey>) => IMenuItem<TLocaleKey>;
|
||||
|
||||
Reference in New Issue
Block a user