1 |
|
2 |
|
3 |
|
4 |
|
5 |
|
6 | import { Text } from '@jupyterlab/coreutils';
|
7 | import { ISettingRegistry } from '@jupyterlab/settingregistry';
|
8 | import { LabIcon } from '@jupyterlab/ui-components';
|
9 | import { JSONExt } from '@lumino/coreutils';
|
10 | import { ContextMenu, Menu } from '@lumino/widgets';
|
11 |
|
12 |
|
13 |
|
14 |
|
15 | export namespace MenuFactory {
|
16 | |
17 |
|
18 |
|
19 | export interface IMenuOptions {
|
20 | |
21 |
|
22 |
|
23 | id: string;
|
24 |
|
25 | |
26 |
|
27 |
|
28 | label?: string;
|
29 |
|
30 | |
31 |
|
32 |
|
33 | rank?: number;
|
34 | }
|
35 |
|
36 | |
37 |
|
38 |
|
39 |
|
40 |
|
41 |
|
42 | export function createMenus(
|
43 | data: ISettingRegistry.IMenu[],
|
44 | menuFactory: (options: IMenuOptions) => Menu
|
45 | ): Menu[] {
|
46 | return data
|
47 | .filter(item => !item.disabled)
|
48 | .sort((a, b) => (a.rank ?? Infinity) - (b.rank ?? Infinity))
|
49 | .map(menuItem => {
|
50 | return dataToMenu(menuItem, menuFactory);
|
51 | });
|
52 | }
|
53 |
|
54 | |
55 |
|
56 |
|
57 |
|
58 |
|
59 |
|
60 |
|
61 | function dataToMenu(
|
62 | item: ISettingRegistry.IMenu,
|
63 | menuFactory: (options: IMenuOptions) => Menu
|
64 | ): Menu {
|
65 | const menu = menuFactory(item);
|
66 | menu.id = item.id;
|
67 |
|
68 |
|
69 | if (!menu.title.label) {
|
70 | menu.title.label = item.label ?? Text.titleCase(menu.id.trim());
|
71 | }
|
72 |
|
73 | if (item.icon) {
|
74 | menu.title.icon = LabIcon.resolve({ icon: item.icon });
|
75 | }
|
76 | if (item.mnemonic !== undefined) {
|
77 | menu.title.mnemonic = item.mnemonic;
|
78 | }
|
79 |
|
80 | item.items
|
81 | ?.filter(item => !item.disabled)
|
82 | .sort((a, b) => (a.rank ?? Infinity) - (b.rank ?? Infinity))
|
83 | .map(item => {
|
84 | addItem(item, menu, menuFactory);
|
85 | });
|
86 | return menu;
|
87 | }
|
88 |
|
89 | |
90 |
|
91 |
|
92 |
|
93 |
|
94 |
|
95 |
|
96 | export function addContextItem(
|
97 | item: ISettingRegistry.IContextMenuItem,
|
98 | menu: ContextMenu,
|
99 | menuFactory: (options: IMenuOptions) => Menu
|
100 | ): void {
|
101 | const { submenu, ...newItem } = item;
|
102 |
|
103 | menu.addItem({
|
104 | ...newItem,
|
105 | submenu: submenu ? dataToMenu(submenu, menuFactory) : null
|
106 | } as any);
|
107 | }
|
108 |
|
109 | |
110 |
|
111 |
|
112 |
|
113 |
|
114 |
|
115 |
|
116 | function addItem(
|
117 | item: ISettingRegistry.IMenuItem,
|
118 | menu: Menu,
|
119 | menuFactory: (options: IMenuOptions) => Menu
|
120 | ): void {
|
121 | const { submenu, ...newItem } = item;
|
122 |
|
123 | menu.addItem({
|
124 | ...newItem,
|
125 | submenu: submenu ? dataToMenu(submenu, menuFactory) : null
|
126 | } as any);
|
127 | }
|
128 |
|
129 | |
130 |
|
131 |
|
132 |
|
133 |
|
134 |
|
135 |
|
136 |
|
137 |
|
138 |
|
139 |
|
140 |
|
141 | export function updateMenus(
|
142 | menus: Menu[],
|
143 | data: ISettingRegistry.IMenu[],
|
144 | menuFactory: (options: IMenuOptions) => Menu
|
145 | ): Menu[] {
|
146 | const newMenus: Menu[] = [];
|
147 | data.forEach(item => {
|
148 | const menu = menus.find(menu => menu.id === item.id);
|
149 | if (menu) {
|
150 | mergeMenus(item, menu, menuFactory);
|
151 | } else {
|
152 | if (!item.disabled) {
|
153 | newMenus.push(dataToMenu(item, menuFactory));
|
154 | }
|
155 | }
|
156 | });
|
157 | menus.push(...newMenus);
|
158 | return newMenus;
|
159 | }
|
160 |
|
161 | function mergeMenus(
|
162 | item: ISettingRegistry.IMenu,
|
163 | menu: Menu,
|
164 | menuFactory: (options: IMenuOptions) => Menu
|
165 | ) {
|
166 | if (item.disabled) {
|
167 | menu.dispose();
|
168 | } else {
|
169 | item.items?.forEach(entry => {
|
170 | const existingItem = menu?.items.find(
|
171 | (i, idx) =>
|
172 | i.type === entry.type &&
|
173 | i.command === (entry.command ?? '') &&
|
174 | i.submenu?.id === entry.submenu?.id
|
175 | );
|
176 |
|
177 | if (existingItem && entry.type !== 'separator') {
|
178 | if (entry.disabled) {
|
179 | menu.removeItem(existingItem);
|
180 | } else {
|
181 | switch (entry.type ?? 'command') {
|
182 | case 'command':
|
183 | if (entry.command) {
|
184 | if (!JSONExt.deepEqual(existingItem.args, entry.args ?? {})) {
|
185 | addItem(entry, menu, menuFactory);
|
186 | }
|
187 | }
|
188 | break;
|
189 | case 'submenu':
|
190 | if (entry.submenu) {
|
191 | mergeMenus(entry.submenu, existingItem.submenu!, menuFactory);
|
192 | }
|
193 | }
|
194 | }
|
195 | } else {
|
196 | addItem(entry, menu, menuFactory);
|
197 | }
|
198 | });
|
199 | }
|
200 | }
|
201 | }
|