Refactor menu building code to support sub-menus of any depth

This commit is contained in:
Aditya Toshniwal 2024-11-14 13:36:42 +05:30
parent 64399901ad
commit 00d3aaa1fd
14 changed files with 261 additions and 237 deletions

View File

@ -64,7 +64,7 @@ define('pgadmin.node.schema', [
},{
name: 'generate_erd', node: 'schema', module: this,
applies: ['object', 'context'], callback: 'generate_erd',
category: 'erd', priority: 5, label: gettext('ERD For Schema')
priority: 5, label: gettext('ERD For Schema')
}]);
},
can_create_schema: function(node) {

View File

@ -82,12 +82,12 @@ define('pgadmin.node.compound_trigger', [
},{
name: 'enable_compound_trigger', node: 'compound_trigger', module: this,
applies: ['object', 'context'], callback: 'enable_compound_trigger',
category: 'connect', priority: 3, label: gettext('Enable compound trigger'),
priority: 3, label: gettext('Enable compound trigger'),
enable : 'canCreate_with_compound_trigger_enable',
},{
name: 'disable_compound_trigger', node: 'compound_trigger', module: this,
applies: ['object', 'context'], callback: 'disable_compound_trigger',
category: 'drop', priority: 3, label: gettext('Disable compound trigger'),
priority: 3, label: gettext('Disable compound trigger'),
enable : 'canCreate_with_compound_trigger_disable',
},{
name: 'create_compound_trigger_onView', node: 'view', module: this,

View File

@ -47,7 +47,7 @@ define('pgadmin.node.foreign_key', [
},{
name: 'validate_foreign_key', node: 'foreign_key', module: this,
applies: ['object', 'context'], callback: 'validate_foreign_key',
category: 'validate', priority: 4, label: gettext('Validate foreign key'),
priority: 4, label: gettext('Validate foreign key'),
enable : 'is_not_valid',
},
]);

View File

@ -90,7 +90,7 @@ function(
},{
name: 'reset_table_stats', node: 'partition', module: this,
applies: ['object', 'context'], callback: 'reset_table_stats',
category: 'Reset', priority: 4, label: gettext('Reset Statistics'),
priority: 4, label: gettext('Reset Statistics'),
enable : 'canCreate',
},{
name: 'detach_partition', node: 'partition', module: this,
@ -121,7 +121,7 @@ function(
},{
name: 'count_table_rows', node: 'partition', module: pgBrowser.Nodes['table'],
applies: ['object', 'context'], callback: 'count_table_rows',
category: 'Count', priority: 2, label: gettext('Count Rows'),
priority: 2, label: gettext('Count Rows'),
enable: true,
}]);
},

View File

@ -119,12 +119,12 @@ define('pgadmin.node.table', [
},{
name: 'count_table_rows', node: 'table', module: this,
applies: ['object', 'context'], callback: 'count_table_rows',
category: 'Count', priority: 2, label: gettext('Count Rows'),
priority: 2, label: gettext('Count Rows'),
enable: true,
},{
name: 'generate_erd', node: 'table', module: this,
applies: ['object', 'context'], callback: 'generate_erd',
category: 'erd', priority: 5, label: gettext('ERD For Table'),
priority: 5, label: gettext('ERD For Table'),
enable: (_, item) => {
return !('catalog' in pgAdmin.Browser.tree.getTreeNodeHierarchy(item));
}

View File

@ -19,7 +19,7 @@ from pgadmin.tools.schema_diff.node_registry import SchemaDiffRegistry
class SchemaDiffViewCompare(SchemaDiffObjectCompare):
view_keys_to_ignore = ['oid', 'schema', 'xmin', 'oid-2', 'setting',
view_keys_to_ignore = ['oid', 'schema', 'xmin', 'oid-2', 'setting',
'indrelid']
trigger_keys_to_ignore = ['xmin', 'tgrelid', 'tgfoid', 'tfunction',

View File

@ -87,8 +87,10 @@ define('pgadmin.node.mview', [
@property {data} - Allow create view option on schema node or
system view nodes.
*/
pgAdmin.Browser.add_menu_category(
'refresh_mview', gettext('Refresh View'), 18, '');
pgAdmin.Browser.add_menu_category({
name: 'refresh_mview', label: gettext('Refresh View'), priority: 18
});
pgBrowser.add_menus([{
name: 'create_mview_on_coll', node: 'coll-mview', module: this,
applies: ['object', 'context'], callback: 'show_obj_properties',

View File

@ -119,7 +119,7 @@ define('pgadmin.node.database', [
},{
name: 'generate_erd', node: 'database', module: this,
applies: ['object', 'context'], callback: 'generate_erd',
category: 'erd', priority: 5, label: gettext('ERD For Database'),
priority: 5, label: gettext('ERD For Database'),
enable: (node) => {
return node.allowConn;
}

View File

@ -11,42 +11,14 @@ import pgAdmin from 'sources/pgadmin';
import Menu, { MenuItem } from '../../../static/js/helpers/Menu';
import getApiInstance from '../../../static/js/api_instance';
import url_for from 'sources/url_for';
import { getBrowser } from '../../../static/js/utils';
import { isMac } from '../../../static/js/keyboard_shortcuts';
const MAIN_MENUS = [
{ label: gettext('File'), name: 'file', id: 'mnu_file', index: 0, addSepratior: true },
{ label: gettext('Object'), name: 'object', id: 'mnu_obj', index: 1, addSepratior: true },
{ label: gettext('Tools'), name: 'tools', id: 'mnu_tools', index: 2, addSepratior: true },
{ label: gettext('Help'), name: 'help', id: 'mnu_help', index: 5, addSepratior: false }
{ label: gettext('File'), name: 'file', id: 'mnu_file', index: 0, addSeprator: true, hasDynamicMenuItems: false },
{ label: gettext('Object'), name: 'object', id: 'mnu_obj', index: 1, addSeprator: true, hasDynamicMenuItems: true },
{ label: gettext('Tools'), name: 'tools', id: 'mnu_tools', index: 2, addSeprator: true, hasDynamicMenuItems: false },
{ label: gettext('Help'), name: 'help', id: 'mnu_help', index: 5, addSeprator: false, hasDynamicMenuItems: false }
];
let { name: browser } = getBrowser();
if (browser == 'Electron') {
let controlKey = isMac() ? 'cmd' : 'ctrl';
let fullScreenKey = isMac() ? 'F' : 'F10';
const RUNTIME_MENUS_OPTIONS = {
runtime : {
label: gettext('Runtime'),
name: 'runtime',
priority: 999,
submenus: {
configure: { label: gettext('Configure...'), name: 'configure', priority: 0, enable: true},
view_log: { label: gettext('View log...'), name: 'view_log', priority: 1, enable: true},
enter_full_screen: { label: gettext('Enter Full Screen'), name: 'enter_full_screen', enable: true, priority: 2, key: fullScreenKey, modifiers: isMac() ?`${controlKey}+ctrl` : controlKey},
exit_full_screen: { label: gettext('Exit Full Screen'), name: 'exit_full_screen', enable: true, priority: 2, key: fullScreenKey, modifiers: isMac() ?`${controlKey}+ctrl` : controlKey},
actual_size: { label: gettext('Actual Size'), name: 'actual_size', priority: 3, enable: true, key: '0', modifiers: controlKey},
zoom_in: { label: gettext('Zoom In'), name: 'zoom_in', priority: 4, enable: true, key: '+', modifiers: controlKey},
zoom_out: { label: gettext('Zoom Out'), name: 'zoom_out', enable: true, priority: 5, key: '-', modifiers: controlKey},
}
}
};
pgAdmin.Browser.RUNTIME_MENUS_OPTIONS = RUNTIME_MENUS_OPTIONS;
}
export default class MainMenuFactory {
static electronCallbacks = {};
@ -75,23 +47,17 @@ export default class MainMenuFactory {
static createMainMenus() {
pgAdmin.Browser.MainMenus = [];
MAIN_MENUS.forEach((_menu) => {
let menuObj = Menu.create(_menu.name, _menu.label, _menu.id, _menu.index, _menu.addSepratior);
let menuObj = Menu.create(_menu.name, _menu.label, _menu.id, _menu.index, _menu.addSeprator, _menu.hasDynamicMenuItems);
pgAdmin.Browser.MainMenus.push(menuObj);
// Don't add menuItems for Object menu as it's menuItems get changed on tree selection.
if(_menu.name !== 'object') {
menuObj.addMenuItems(Object.values(pgAdmin.Browser.all_menus_cache[_menu.name]));
menuObj.getMenuItems().forEach((menuItem, index)=> {
menuItem?.getMenuItems()?.forEach((item, indx)=> {
item.below && menuItem?.getMenuItems().splice(indx+1, 0, MainMenuFactory.getSeparator());
});
if(menuItem.below) {
menuObj.addMenuItem(MainMenuFactory.getSeparator(), index+1);
}
});
menuObj.clearMenuItems();
menuObj.addMenuItems(MainMenuFactory.createMenuItems(pgAdmin.Browser.all_menus_cache[_menu.name]));
}
});
pgAdmin.Browser.enable_disable_menus();
// enable disable will take care of dynamic menus.
MainMenuFactory.enableDisableMenus();
window.electronUI?.onMenuClick((menuName)=>{
MainMenuFactory.electronCallbacks[menuName]?.();
@ -135,20 +101,165 @@ export default class MainMenuFactory {
});
}
static getContextMenu(menuList) {
Menu.sortMenus(menuList);
return menuList;
static enableDisableMenus(item) {
let itemData = item ? pgAdmin.Browser.tree.itemData(item) : undefined;
const checkForItems = (items)=>{
items.forEach((mitem) => {
const subItems = mitem.getMenuItems() ?? [];
if(subItems.length > 0) {
checkForItems(subItems);
} else {
mitem.checkAndSetDisabled(itemData, item);
}
});
};
// Non dynamic menus will be required to check whether enabled/disabled.
pgAdmin.Browser.MainMenus.filter((m)=>(!m.hasDynamicMenuItems)).forEach((menu) => {
checkForItems(menu.getMenuItems());
});
pgAdmin.Browser.MainMenus.filter((m)=>(m.hasDynamicMenuItems)).forEach((menu) => {
let menuItemList = MainMenuFactory.getDynamicMenu(menu.name, item, itemData);
menu.setMenuItems(menuItemList);
});
// set the context menu as well
pgAdmin.Browser.BrowserContextMenu = MainMenuFactory.getDynamicMenu('context', item, itemData, true);
pgAdmin.Browser.Events.trigger('pgadmin:refresh-app-menu');
}
static checkNoMenuOptionForNode(d){
let selectedNodeFromNodes=pgAdmin.Browser.Nodes[d._type];
static checkNoMenuOptionForNode(itemData){
if(!itemData) {
return true;
}
let selectedNodeFromNodes=pgAdmin.Browser.Nodes[itemData._type];
let selectedNode=pgAdmin.Browser.tree.selected();
let flag=!_.isUndefined(selectedNodeFromNodes.showMenu);
if(flag){
let showMenu = selectedNodeFromNodes.showMenu(d, selectedNode);
return {flag:showMenu?false:flag,showMenu};
} else{
return {flag,showMenu:undefined};
return selectedNodeFromNodes.showMenu?.(itemData, selectedNode) ?? true;
}
static createMenuItems(items, skipDisabled=false, checkAndSetDisabled=()=>true) {
let retVal = [];
let categories = {};
const getNewMenuItem = (i)=>{
const mi = MainMenuFactory.createMenuItem({...i});
checkAndSetDisabled?.(mi);
if(skipDisabled && mi.isDisabled) {
return null;
}
return mi;
};
const getMenuCategory = (catName)=>{
let category = pgAdmin.Browser.menu_categories[catName];
if(!category) {
// generate category on the fly.
category = {
name: catName,
label: catName,
priority: 10,
};
}
let cmi = categories[category.name];
if(!cmi) {
cmi = getNewMenuItem({...category});
// for easily finding again, note down.
categories[category.name] = cmi;
}
return cmi;
};
const applySeparators = (mi)=>{
const newItems = [];
if(mi.above) {
newItems.push(MainMenuFactory.getSeparator(mi.label, mi.priority));
}
newItems.push(mi);
if(mi.below) {
newItems.push(MainMenuFactory.getSeparator(mi.label, mi.priority));
}
return newItems;
};
Object.entries(items).forEach(([k, i])=>{
if('name' in i) {
const mi = getNewMenuItem(i);
if(!mi) return;
if(i.category??'common' != 'common') {
const cmi = getMenuCategory(i.category);
if(cmi) {
cmi.addMenuItems([...applySeparators(mi)]);
} else {
retVal.push(...applySeparators(mi));
}
} else {
retVal.push(...applySeparators(getNewMenuItem(i)));
}
} else {
// Can be a category
const cmi = getMenuCategory(k);
if(cmi) {
cmi.addMenuItems(MainMenuFactory.createMenuItems(i, skipDisabled, checkAndSetDisabled));
}
}
});
// Push the category menus
Object.values(categories).forEach((cmi)=>{
const items = cmi.getMenuItems();
// if there is only one menu in the category, then no need of the category.
if(items.length <= 1 && !cmi.single) {
retVal = retVal.concat(items);
return;
}
retVal.push(...applySeparators(cmi));
});
Menu.sortMenus(retVal ?? []);
return retVal;
}
static getDynamicMenu(name, item, itemData, skipDisabled=false) {
if(!item) {
return [MainMenuFactory.createMenuItem({
name: '',
label: gettext('No object selected'),
category: 'create',
priority: 1,
enable: false,
})];
}
const showMenu = MainMenuFactory.checkNoMenuOptionForNode(itemData);
if(!showMenu){
return [MainMenuFactory.createMenuItem({
enable : false,
label: gettext('No menu available for this object.'),
name:'',
priority: 1,
category: 'create',
})];
} else {
const nodeTypeMenus = pgAdmin.Browser.all_menus_cache[name]?.[itemData._type] ?? [];
const menuItemList = MainMenuFactory.createMenuItems(nodeTypeMenus, skipDisabled, (mi)=>{
return mi.checkAndSetDisabled(itemData, item);
});
if(menuItemList.length == 0) {
return [MainMenuFactory.createMenuItem({
enable : false,
label: gettext('No menu available for this object.'),
name:'',
priority: 1,
category: 'create',
})];
}
return menuItemList;
}
}
}

View File

@ -122,6 +122,7 @@ define('pgadmin.browser', [
menu_categories: {
/* name, label (pair) */
'register': {
name: 'register',
label: gettext('Register'),
priority: 1,
/* separator above this menu */
@ -131,6 +132,7 @@ define('pgadmin.browser', [
single: true,
},
'create': {
name: 'create',
label: gettext('Create'),
priority: 2,
/* separator above this menu */
@ -147,113 +149,9 @@ define('pgadmin.browser', [
scripts[n].push({'name': m, 'path': p, loaded: false});
},
masterpass_callback_queue: [],
getMenuList: function(name, item, d, skipDisabled=false) {
let obj = this;
//This 'checkNoMenuOptionForNode' function will check if showMenu flag is present or not for selected node
let {flag,showMenu}=MainMenuFactory.checkNoMenuOptionForNode(d);
if(flag){
if(showMenu===false){
return [MainMenuFactory.createMenuItem({
enable : false,
label: gettext('No menu available for this object.'),
name:'',
priority: 1,
category: 'create',
})];
}
}else{
let category = {
'common': []
};
const nodeTypeMenus = obj.all_menus_cache[name][d._type];
for(let key of Object.keys(nodeTypeMenus)) {
let menuItem = nodeTypeMenus[key];
let menuCategory = menuItem.category ?? 'common';
category[menuCategory] = category[menuCategory] ?? [];
category[menuCategory].push(menuItem);
}
let menuItemList = [];
for(let c in category) {
if((c in obj.menu_categories || category[c].length > 1) && c != 'common' ) {
let allMenuItemsDisabled = true;
category[c].forEach((mi)=> {
mi.checkAndSetDisabled(d, item);
if(allMenuItemsDisabled) {
allMenuItemsDisabled = mi.isDisabled;
}
});
const categoryMenuOptions = obj.menu_categories[c];
let label = categoryMenuOptions?.label ?? c;
let priority = categoryMenuOptions?.priority ?? 10;
if(categoryMenuOptions?.above) {
menuItemList.push(MainMenuFactory.getSeparator(label, priority));
}
if((!allMenuItemsDisabled && skipDisabled) || !skipDisabled) {
let _menuItem = MainMenuFactory.createMenuItem({
name: c,
label: label,
module: c,
category: c,
menu_items: category[c],
priority: priority
});
menuItemList.push(_menuItem);
}
if(categoryMenuOptions?.below) {
menuItemList.push(MainMenuFactory.getSeparator(label, priority));
}
} else {
category[c].forEach((c)=> {
c.checkAndSetDisabled(d, item);
});
category[c].forEach((m)=> {
if(!skipDisabled || (skipDisabled && !m.isDisabled)) {
menuItemList.push(m);
}
});
}
}
return menuItemList;
}
},
// Enable/disable menu options
enable_disable_menus: function(item) {
let obj = this;
let d = item ? obj.tree.itemData(item) : undefined;
// All menus (except for the object menus) are already present.
// They will just require to check, whether they are
// enabled/disabled.
pgBrowser.MainMenus.filter((m)=>m.name != 'object').forEach((menu) => {
menu.menuItems.forEach((mitem) => {
mitem.checkAndSetDisabled(d, item);
});
});
// Create the object menu dynamically
let objectMenu = pgBrowser.MainMenus.find((menu) => menu.name == 'object');
if (item && obj.all_menus_cache['object']?.[d._type]) {
let menuItemList = obj.getMenuList('object', item, d);
objectMenu && MainMenuFactory.refreshMainMenuItems(objectMenu, menuItemList);
let ctxMenuList = obj.getMenuList('context', item, d, true);
obj.BrowserContextMenu = MainMenuFactory.getContextMenu(ctxMenuList);
} else {
objectMenu && MainMenuFactory.refreshMainMenuItems(objectMenu, [
MainMenuFactory.createMenuItem({
name: '',
label: gettext('No object selected'),
category: 'create',
priority: 1,
enable: false,
})
]);
}
MainMenuFactory.enableDisableMenus(item);
},
init: function() {
let obj=this;
@ -414,29 +312,28 @@ define('pgadmin.browser', [
});
},
add_menu_category: function(
id, label, priority, icon, above_separator, below_separator, single
) {
this.menu_categories[id] = {
label: label,
priority: priority,
icon: icon,
above: (above_separator === true),
below: (below_separator === true),
single: single,
add_menu_category: function({name, ...options}) {
this.menu_categories[name] = {
label: '(No Label)',
priority: 10,
icon: '',
above: false,
below: false,
parent: null,
isCategory: true,
...options,
};
},
// Add menus of module/extension at appropriate menu
add_menus: function(menus) {
let pgMenu = this.all_menus_cache;
const self = this;
let allMenus = this.all_menus_cache;
_.each(menus, function(m) {
_.each(m.applies, function(a) {
/* We do support menu type only from this list */
if(['context', 'file', 'edit', 'object','management', 'tools', 'help'].indexOf(a) > -1){
let _menus;
// If current node is not visible in browser tree
// then return from here
if(!checkNodeVisibility(m.node)) {
@ -448,16 +345,19 @@ define('pgadmin.browser', [
return;
}
}
pgMenu[a] = pgMenu[a] || {};
if (_.isString(m.node)) {
_menus = pgMenu[a][m.node] = pgMenu[a][m.node] || {};
} else if (_.isString(m.category)) {
_menus = pgMenu[a][m.category] = pgMenu[a][m.category] || {};
}
else {
_menus = pgMenu[a];
}
const getFullPath = (currPath, currMenu)=>{
if(currMenu.node) {
return currPath.concat([currMenu.node]);
} else if(currMenu.category??'common' != 'common') {
const currCat = self.menu_categories[currMenu.category];
if(currCat?.category) {
return getFullPath(currPath.concat([currMenu.category]), currCat);
}
return [currMenu.category].concat(currPath);
} else {
return currPath;
}
};
let get_menuitem_obj = function(_m) {
let enable = _m.enable;
@ -474,7 +374,7 @@ define('pgadmin.browser', [
};
}
return MainMenuFactory.createMenuItem({
return {
name: _m.name,
label: _m.label,
module: _m.module,
@ -490,20 +390,19 @@ define('pgadmin.browser', [
checked: _m.checked,
below: _m.below,
applies: _m.applies,
});
};
};
if (!_.has(_menus, m.name)) {
_menus[m.name] = get_menuitem_obj(m);
const menuPath = [a].concat(getFullPath([], m)).concat([m.name]);
const _menus = _.set(allMenus, menuPath, get_menuitem_obj(m));
if(m.menu_items) {
let sub_menu_items = [];
if(m.menu_items) {
let sub_menu_items = [];
for(let mnu_val of m.menu_items) {
sub_menu_items.push(get_menuitem_obj(mnu_val));
}
_menus[m.name]['menu_items'] = sub_menu_items;
for(let mnu_val of m.menu_items) {
sub_menu_items.push(get_menuitem_obj(mnu_val));
}
_menus[m.name]['menu_items'] = sub_menu_items;
}
} else {
console.warn(

View File

@ -136,7 +136,8 @@ export function getNodeAjaxOptions(url, nodeObj, treeNodeInfo, itemNodeData, par
}
/* Get the nodes list based on current selected node id */
export function getNodeListById(nodeObj, treeNodeInfo, itemNodeData, params={}, filter=()=>true) {
export function getNodeListById(nodeObj, treeNodeInfo, itemNodeData, params={}, filter=()=>true, postTransform=(res)=>res) {
nodeObj = typeof(nodeObj) == 'string' ? pgAdmin.Browser.Nodes[nodeObj] : nodeObj;
/* Transform the result to add image details */
const transform = (rows) => {
let res = [];
@ -158,7 +159,7 @@ export function getNodeListById(nodeObj, treeNodeInfo, itemNodeData, params={},
}
});
return res;
return postTransform(res);
};
return getNodeAjaxOptions('nodes', nodeObj, treeNodeInfo, itemNodeData, params, transform);

View File

@ -67,7 +67,7 @@ export default function AppMenuBar() {
pgAdmin.Browser.Events.on('pgadmin:enable-disable-menu-items', _.debounce(()=>{
forceUpdate();
}, 100));
pgAdmin.Browser.Events.on('pgadmin:refresh-menu-item', _.debounce(()=>{
pgAdmin.Browser.Events.on('pgadmin:refresh-app-menu', _.debounce(()=>{
forceUpdate();
}, 100));
}, []);
@ -95,6 +95,18 @@ export default function AppMenuBar() {
const userMenuInfo = pgAdmin.Browser.utils.userMenuInfo;
const getPgMenu = (menu)=>{
return menu.getMenuItems()?.map((menuItem, i)=>{
const submenus = menuItem.getMenuItems();
if(submenus) {
return <PgSubMenu key={menuItem.label} label={menuItem.label}>
{getPgMenu(menuItem)}
</PgSubMenu>;
}
return getPgMenuItem(menuItem, i);
});
};
return (
<StyledBox data-test="app-menu-bar">
<div className='AppMenuBar-logo' />
@ -106,17 +118,7 @@ export default function AppMenuBar() {
label={menu.label}
key={menu.name}
>
{menu.getMenuItems().map((menuItem, i)=>{
const submenus = menuItem.getMenuItems();
if(submenus) {
return <PgSubMenu key={menuItem.label} label={menuItem.label}>
{submenus.map((submenuItem, si)=>{
return getPgMenuItem(submenuItem, si);
})}
</PgSubMenu>;
}
return getPgMenuItem(menuItem, i);
})}
{getPgMenu(menu)}
</PgMenu>
);
})}

View File

@ -10,17 +10,18 @@ import _ from 'lodash';
import gettext from 'sources/gettext';
export default class Menu {
constructor(name, label, id, index, addSepratior) {
constructor(name, label, id, index, addSeprator, hasDynamicMenuItems) {
this.label = label;
this.name = name;
this.id = id;
this.index = index || 1;
this.menuItems = [];
this.addSepratior = addSepratior || false;
this.addSeprator = addSeprator || false;
this.hasDynamicMenuItems = hasDynamicMenuItems;
}
static create(name, label, id, index, addSepratior) {
let menuObj = new Menu(name, label, id, index, addSepratior);
static create(name, label, id, index, addSeprator, hasDynamicMenuItems) {
let menuObj = new Menu(name, label, id, index, addSeprator, hasDynamicMenuItems);
return menuObj;
}
@ -30,7 +31,7 @@ export default class Menu {
label: this.label,
name: this.name,
index: this.index,
addSepratior: this.addSepratior,
addSeprator: this.addSeprator,
};
}
@ -41,13 +42,10 @@ export default class Menu {
this.menuItems.splice(index, 0, menuItem);
} else {
this.menuItems.push(menuItem);
Menu.sortMenus(this.menuItems);
}
} else {
throw new Error(gettext('Invalid MenuItem instance'));
}
}
addMenuItems(menuItems) {
@ -59,7 +57,6 @@ export default class Menu {
item.menu_items.forEach((i)=> {
i.parentMenu = item;
});
Menu.sortMenus(item.menu_items);
}
} else {
let subItems = Object.values(item);
@ -88,16 +85,16 @@ export default class Menu {
setMenuItems(menuItems) {
this.menuItems = menuItems;
Menu.sortMenus(this.menuItems);
}
this.menuItems.forEach((item)=> {
if(item?.menu_items?.length > 0) {
Menu.sortMenus(item.menu_items);
}
});
clearMenuItems() {
this.menuItems = [];
}
static sortMenus(menuItems) {
if(!menuItems || menuItems.length <=0) {
return;
}
// Sort by alphanumeric ordered first
menuItems.sort(function (a, b) {
return a.label.localeCompare(b.label);
@ -107,6 +104,10 @@ export default class Menu {
menuItems.sort(function (a, b) {
return a.priority - b.priority;
});
menuItems.forEach((mi)=>{
Menu.sortMenus(mi.getMenuItems());
});
}
getMenuItems() {
@ -117,9 +118,9 @@ export default class Menu {
export class MenuItem {
constructor(options, onDisableChange) {
let menu_opts = [
let allowedOptions = [
'name', 'label', 'priority', 'module', 'callback', 'data', 'enable',
'category', 'target', 'url', 'node',
'category', 'target', 'url', 'node', 'single',
'checked', 'below', 'menu_items', 'is_checkbox', 'action', 'applies', 'is_native_only', 'type',
];
let defaults = {
@ -127,7 +128,7 @@ export class MenuItem {
target: '_self',
enable: true,
};
_.extend(this, defaults, _.pick(options, menu_opts));
_.extend(this, defaults, _.pick(options, allowedOptions));
if (!this.callback) {
this.callback = (item) => {
if (item.url != '#') {
@ -159,6 +160,11 @@ export class MenuItem {
this.checked = isChecked;
}
addMenuItems(items) {
this.menu_items = this.menu_items ?? [];
this.menu_items = this.menu_items.concat(items);
}
getMenuItems() {
return this.menu_items;
}

View File

@ -163,7 +163,10 @@ export default class SQLEditor {
});
}
pgBrowser.add_menu_category('view_data', gettext('View/Edit Data'), 100, '');
pgBrowser.add_menu_category({
name: 'view_data', label: gettext('View/Edit Data'), priority: 100
});
pgBrowser.add_menus(menus);
}