@fleetbase/ember-core
Version:
Provides all the core services, decorators and utilities for building a Fleetbase extension for the Console.
170 lines (158 loc) • 4.7 kB
JavaScript
import BaseContract from './base-contract';
import MenuItem from './menu-item';
import { dasherize } from '@ember/string';
import isObject from '../utils/is-object';
/**
* Represents a menu panel containing multiple menu items
*
* Menu panels are used in admin settings and other sections to group related menu items.
*
* @class MenuPanel
* @extends BaseContract
*
* @example
* // With chaining
* new MenuPanel('Fleet-Ops Config')
* .withSlug('fleet-ops')
* .withIcon('truck')
* .addItem(new MenuItem('Navigator App').withIcon('location-arrow'))
* .addItem(new MenuItem('Avatar Management').withIcon('images'))
*
* @example
* // Full definition object (first-class)
* new MenuPanel({
* title: 'Fleet-Ops Config',
* slug: 'fleet-ops',
* icon: 'truck',
* priority: 5,
* items: [
* { title: 'Navigator App', icon: 'location-arrow' },
* { title: 'Avatar Management', icon: 'images' }
* ]
* })
*/
export default class MenuPanel extends BaseContract {
/**
* Create a new MenuPanel
*
* @constructor
* @param {String|Object} titleOrDefinition The panel title or full definition object
* @param {Array} items Optional array of menu items (only used if first param is string)
*/
constructor(titleOrDefinition, items = []) {
// Call super FIRST (JavaScript requirement)
super(isObject(titleOrDefinition) && titleOrDefinition.title ? titleOrDefinition : { title: titleOrDefinition });
// THEN set properties
if (isObject(titleOrDefinition) && titleOrDefinition.title) {
const definition = titleOrDefinition;
this.title = definition.title;
this.items = definition.items || [];
this.slug = definition.slug || dasherize(this.title);
this.icon = definition.icon || null;
this.open = definition.open !== undefined ? definition.open : true;
this.priority = definition.priority !== undefined ? definition.priority : 9;
} else {
// Handle string title (chaining pattern)
this.title = titleOrDefinition;
this.items = items;
this.slug = dasherize(titleOrDefinition);
this.icon = null;
this.open = true;
this.priority = 9;
}
// Call setup() to trigger validation after properties are set
super.setup();
}
/**
* Validate the menu panel
*
* @method validate
* @throws {Error} If title is missing
*/
validate() {
if (!this.title) {
throw new Error('MenuPanel requires a title');
}
}
/**
* Set the panel slug
*
* @method withSlug
* @param {String} slug URL-friendly slug
* @returns {MenuPanel} This instance for chaining
*/
withSlug(slug) {
this.slug = slug;
this._options.slug = slug;
return this;
}
/**
* Set the panel icon
*
* @method withIcon
* @param {String} icon Icon name
* @returns {MenuPanel} This instance for chaining
*/
withIcon(icon) {
this.icon = icon;
this._options.icon = icon;
return this;
}
/**
* Set the panel priority
*
* @method withPriority
* @param {Number} priority Priority value
* @returns {MenuPanel} This instance for chaining
*/
withPriority(priority) {
this.priority = priority;
this._options.priority = priority;
return this;
}
/**
* Add a menu item to the panel
*
* @method addItem
* @param {MenuItem|Object} item Menu item to add
* @returns {MenuPanel} This instance for chaining
*/
addItem(item) {
if (item instanceof MenuItem) {
this.items.push(item.toObject());
} else {
this.items.push(item);
}
return this;
}
/**
* Add multiple menu items to the panel
*
* @method addItems
* @param {Array} items Array of menu items
* @returns {MenuPanel} This instance for chaining
*/
addItems(items) {
items.forEach((item) => this.addItem(item));
return this;
}
/**
* Get the plain object representation
*
* @method toObject
* @returns {Object} Plain object with all panel properties
*/
toObject() {
return {
title: this.title,
slug: this.slug,
icon: this.icon,
open: this.open,
priority: this.priority,
items: this.items,
// Indicator flag
_isMenuPanel: true,
...this._options,
};
}
}