@fleetbase/ember-core 0.3.12 → 0.3.14
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import BaseContract from './base-contract';
|
|
2
2
|
import ExtensionComponent from './extension-component';
|
|
3
3
|
import { dasherize } from '@ember/string';
|
|
4
|
+
import { isArray } from '@ember/array';
|
|
4
5
|
import isObject from '../utils/is-object';
|
|
5
6
|
|
|
6
7
|
/**
|
|
@@ -35,6 +36,16 @@ import isObject from '../utils/is-object';
|
|
|
35
36
|
* .onClick((menuItem, router) => {
|
|
36
37
|
* router.transitionTo('virtual', menuItem.slug);
|
|
37
38
|
* })
|
|
39
|
+
*
|
|
40
|
+
* @example
|
|
41
|
+
* // Menu item with description and shortcuts (Phase 2)
|
|
42
|
+
* new MenuItem('Fleet-Ops', 'console.fleet-ops')
|
|
43
|
+
* .withIcon('route')
|
|
44
|
+
* .withDescription('Manage fleets, drivers, and live orders')
|
|
45
|
+
* .withShortcuts([
|
|
46
|
+
* { title: 'Scheduler', route: 'console.fleet-ops.scheduler', icon: 'calendar' },
|
|
47
|
+
* { title: 'Order Config', route: 'console.fleet-ops.order-configs', icon: 'gear' },
|
|
48
|
+
* ])
|
|
38
49
|
*/
|
|
39
50
|
export default class MenuItem extends BaseContract {
|
|
40
51
|
/**
|
|
@@ -102,6 +113,22 @@ export default class MenuItem extends BaseContract {
|
|
|
102
113
|
|
|
103
114
|
// Nested items
|
|
104
115
|
this.items = definition.items || null;
|
|
116
|
+
|
|
117
|
+
// ── Phase 2 additions ──────────────────────────────────────────
|
|
118
|
+
// A short human-readable description of the extension shown in the
|
|
119
|
+
// overflow dropdown. Optional – defaults to null.
|
|
120
|
+
this.description = definition.description || null;
|
|
121
|
+
|
|
122
|
+
// An array of shortcut items (sub-menu links) displayed beneath the
|
|
123
|
+
// extension name in the multi-column dropdown. Each shortcut is a
|
|
124
|
+
// plain object with at minimum { title, route } and optionally
|
|
125
|
+
// { icon, iconPrefix, id }. Optional – defaults to null.
|
|
126
|
+
this.shortcuts = definition.shortcuts || null;
|
|
127
|
+
|
|
128
|
+
// An array of string tags used to improve search discoverability in
|
|
129
|
+
// the overflow dropdown. e.g. ['logistics', 'tracking', 'fleet'].
|
|
130
|
+
// Optional – defaults to null.
|
|
131
|
+
this.tags = isArray(definition.tags) ? definition.tags : definition.tags ? [definition.tags] : null;
|
|
105
132
|
} else {
|
|
106
133
|
// Handle string title with optional route (chaining pattern)
|
|
107
134
|
this.title = titleOrDefinition;
|
|
@@ -153,6 +180,11 @@ export default class MenuItem extends BaseContract {
|
|
|
153
180
|
|
|
154
181
|
// Nested items
|
|
155
182
|
this.items = null;
|
|
183
|
+
|
|
184
|
+
// ── Phase 2 additions ──────────────────────────────────────────
|
|
185
|
+
this.description = null;
|
|
186
|
+
this.shortcuts = null;
|
|
187
|
+
this.tags = null;
|
|
156
188
|
}
|
|
157
189
|
|
|
158
190
|
// Call setup() to trigger validation after properties are set
|
|
@@ -342,6 +374,132 @@ export default class MenuItem extends BaseContract {
|
|
|
342
374
|
return this;
|
|
343
375
|
}
|
|
344
376
|
|
|
377
|
+
// ── Phase 2 builder methods ────────────────────────────────────────────
|
|
378
|
+
|
|
379
|
+
/**
|
|
380
|
+
* Set a short human-readable description for the extension.
|
|
381
|
+
* Displayed beneath the extension title in the overflow dropdown.
|
|
382
|
+
*
|
|
383
|
+
* @method withDescription
|
|
384
|
+
* @param {String} description Short description text
|
|
385
|
+
* @returns {MenuItem} This instance for chaining
|
|
386
|
+
*
|
|
387
|
+
* @example
|
|
388
|
+
* new MenuItem('Fleet-Ops', 'console.fleet-ops')
|
|
389
|
+
* .withDescription('Manage fleets, drivers, and live orders')
|
|
390
|
+
*/
|
|
391
|
+
withDescription(description) {
|
|
392
|
+
this.description = description;
|
|
393
|
+
this._options.description = description;
|
|
394
|
+
return this;
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
/**
|
|
398
|
+
* Set an array of shortcut items displayed as independent sibling cards in
|
|
399
|
+
* the multi-column overflow dropdown (AWS Console style). Each shortcut
|
|
400
|
+
* supports the full MenuItem property surface:
|
|
401
|
+
*
|
|
402
|
+
* Required:
|
|
403
|
+
* title {String} Display label
|
|
404
|
+
*
|
|
405
|
+
* Routing:
|
|
406
|
+
* route {String} Ember route name
|
|
407
|
+
* queryParams {Object}
|
|
408
|
+
* routeParams {Array}
|
|
409
|
+
*
|
|
410
|
+
* Identity:
|
|
411
|
+
* id {String} Explicit id (auto-dasherized from title if omitted)
|
|
412
|
+
* slug {String} URL slug (falls back to id)
|
|
413
|
+
*
|
|
414
|
+
* Icons:
|
|
415
|
+
* icon {String} FontAwesome icon name
|
|
416
|
+
* iconPrefix {String} FA prefix (e.g. 'far', 'fab')
|
|
417
|
+
* iconSize {String} FA size string
|
|
418
|
+
* iconClass {String} Extra CSS class on the icon element
|
|
419
|
+
* iconComponent {String} Lazy-loaded engine component path
|
|
420
|
+
* iconComponentOptions {Object}
|
|
421
|
+
*
|
|
422
|
+
* Metadata:
|
|
423
|
+
* description {String} Short description shown in the card
|
|
424
|
+
* tags {String[]} Search tags
|
|
425
|
+
*
|
|
426
|
+
* Behaviour:
|
|
427
|
+
* onClick {Function} Click handler (receives the shortcut item)
|
|
428
|
+
* disabled {Boolean}
|
|
429
|
+
*
|
|
430
|
+
* Shortcuts are registered as first-class header menu items at boot time
|
|
431
|
+
* and can be individually pinned to the navigation bar.
|
|
432
|
+
*
|
|
433
|
+
* @method withShortcuts
|
|
434
|
+
* @param {Array<Object>} shortcuts Array of shortcut definition objects
|
|
435
|
+
* @returns {MenuItem} This instance for chaining
|
|
436
|
+
*
|
|
437
|
+
* @example
|
|
438
|
+
* new MenuItem('Fleet-Ops', 'console.fleet-ops')
|
|
439
|
+
* .withShortcuts([
|
|
440
|
+
* {
|
|
441
|
+
* title: 'Scheduler',
|
|
442
|
+
* route: 'console.fleet-ops.scheduler',
|
|
443
|
+
* icon: 'calendar',
|
|
444
|
+
* description: 'Plan and visualise driver schedules',
|
|
445
|
+
* tags: ['schedule', 'calendar'],
|
|
446
|
+
* },
|
|
447
|
+
* {
|
|
448
|
+
* title: 'Live Map',
|
|
449
|
+
* route: 'console.fleet-ops',
|
|
450
|
+
* iconComponent: 'fleet-ops@components/live-map-icon',
|
|
451
|
+
* description: 'Real-time vehicle tracking',
|
|
452
|
+
* },
|
|
453
|
+
* ])
|
|
454
|
+
*/
|
|
455
|
+
withShortcuts(shortcuts) {
|
|
456
|
+
this.shortcuts = isArray(shortcuts) ? shortcuts : null;
|
|
457
|
+
this._options.shortcuts = this.shortcuts;
|
|
458
|
+
return this;
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
/**
|
|
462
|
+
* Set an array of string tags for this menu item.
|
|
463
|
+
* Tags are matched against the search query in the overflow dropdown,
|
|
464
|
+
* making items discoverable even when the query doesn't match the title
|
|
465
|
+
* or description.
|
|
466
|
+
*
|
|
467
|
+
* @method withTags
|
|
468
|
+
* @param {String|String[]} tags One tag string or an array of tag strings
|
|
469
|
+
* @returns {MenuItem} This instance for chaining
|
|
470
|
+
*
|
|
471
|
+
* @example
|
|
472
|
+
* new MenuItem('Fleet-Ops', 'console.fleet-ops')
|
|
473
|
+
* .withTags(['logistics', 'tracking', 'fleet', 'drivers'])
|
|
474
|
+
*/
|
|
475
|
+
withTags(tags) {
|
|
476
|
+
this.tags = isArray(tags) ? tags : tags ? [tags] : null;
|
|
477
|
+
this._options.tags = this.tags;
|
|
478
|
+
return this;
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
/**
|
|
482
|
+
* Add a single shortcut to the existing shortcuts array.
|
|
483
|
+
* Creates the array if it does not yet exist.
|
|
484
|
+
*
|
|
485
|
+
* @method addShortcut
|
|
486
|
+
* @param {Object} shortcut Shortcut definition object { title, route, icon?, iconPrefix?, id? }
|
|
487
|
+
* @returns {MenuItem} This instance for chaining
|
|
488
|
+
*
|
|
489
|
+
* @example
|
|
490
|
+
* new MenuItem('Fleet-Ops', 'console.fleet-ops')
|
|
491
|
+
* .addShortcut({ title: 'Scheduler', route: 'console.fleet-ops.scheduler' })
|
|
492
|
+
* .addShortcut({ title: 'Order Config', route: 'console.fleet-ops.order-configs' })
|
|
493
|
+
*/
|
|
494
|
+
addShortcut(shortcut) {
|
|
495
|
+
if (!isArray(this.shortcuts)) {
|
|
496
|
+
this.shortcuts = [];
|
|
497
|
+
}
|
|
498
|
+
this.shortcuts = [...this.shortcuts, shortcut];
|
|
499
|
+
this._options.shortcuts = this.shortcuts;
|
|
500
|
+
return this;
|
|
501
|
+
}
|
|
502
|
+
|
|
345
503
|
/**
|
|
346
504
|
* Get the plain object representation
|
|
347
505
|
*
|
|
@@ -401,6 +559,16 @@ export default class MenuItem extends BaseContract {
|
|
|
401
559
|
// Nested items
|
|
402
560
|
items: this.items,
|
|
403
561
|
|
|
562
|
+
// ── Phase 2 additions ──────────────────────────────────────────
|
|
563
|
+
// Optional short description shown in the overflow dropdown card
|
|
564
|
+
description: this.description,
|
|
565
|
+
|
|
566
|
+
// Optional array of shortcut sub-links shown inside the extension card
|
|
567
|
+
shortcuts: this.shortcuts,
|
|
568
|
+
|
|
569
|
+
// Optional array of string tags for search discoverability
|
|
570
|
+
tags: this.tags,
|
|
571
|
+
|
|
404
572
|
// Indicator flag
|
|
405
573
|
_isMenuItem: true,
|
|
406
574
|
|
|
@@ -3,7 +3,7 @@ import Evented from '@ember/object/evented';
|
|
|
3
3
|
import { tracked } from '@glimmer/tracking';
|
|
4
4
|
import { inject as service } from '@ember/service';
|
|
5
5
|
import { dasherize } from '@ember/string';
|
|
6
|
-
import { A } from '@ember/array';
|
|
6
|
+
import { A, isArray } from '@ember/array';
|
|
7
7
|
import MenuItem from '../../contracts/menu-item';
|
|
8
8
|
import MenuPanel from '../../contracts/menu-panel';
|
|
9
9
|
|
|
@@ -153,6 +153,70 @@ export default class MenuService extends Service.extend(Evented) {
|
|
|
153
153
|
const menuItem = this.#normalizeMenuItem(itemOrTitle, route, options);
|
|
154
154
|
this.registry.register('header', 'menu-item', menuItem.slug, menuItem);
|
|
155
155
|
|
|
156
|
+
// Auto-register each shortcut as a first-class header menu item so that
|
|
157
|
+
// they appear in the customiser's "All Extensions" list and can be found
|
|
158
|
+
// by id in allItems when pinned to the bar.
|
|
159
|
+
if (isArray(menuItem.shortcuts)) {
|
|
160
|
+
for (const sc of menuItem.shortcuts) {
|
|
161
|
+
const scId = sc.id ?? dasherize(menuItem.id + '-sc-' + sc.title);
|
|
162
|
+
const scSlug = sc.slug ?? scId;
|
|
163
|
+
|
|
164
|
+
// Build a first-class item that supports the full MenuItem
|
|
165
|
+
// property surface. Each property falls back to the parent's
|
|
166
|
+
// value so shortcuts inherit sensible defaults without the
|
|
167
|
+
// consumer having to repeat them.
|
|
168
|
+
const scItem = {
|
|
169
|
+
// ── Identity ──────────────────────────────────────────────
|
|
170
|
+
id: scId,
|
|
171
|
+
slug: scSlug,
|
|
172
|
+
title: sc.title,
|
|
173
|
+
text: sc.text ?? sc.title,
|
|
174
|
+
label: sc.label ?? sc.title,
|
|
175
|
+
view: sc.view ?? scId,
|
|
176
|
+
|
|
177
|
+
// ── Routing ───────────────────────────────────────────────
|
|
178
|
+
route: sc.route ?? menuItem.route,
|
|
179
|
+
section: sc.section ?? null,
|
|
180
|
+
queryParams: sc.queryParams ?? {},
|
|
181
|
+
routeParams: sc.routeParams ?? [],
|
|
182
|
+
|
|
183
|
+
// ── Icons (full surface) ──────────────────────────────────
|
|
184
|
+
icon: sc.icon ?? menuItem.icon,
|
|
185
|
+
iconPrefix: sc.iconPrefix ?? menuItem.iconPrefix,
|
|
186
|
+
iconSize: sc.iconSize ?? menuItem.iconSize ?? null,
|
|
187
|
+
iconClass: sc.iconClass ?? menuItem.iconClass ?? null,
|
|
188
|
+
iconComponent: sc.iconComponent ?? null,
|
|
189
|
+
iconComponentOptions: sc.iconComponentOptions ?? {},
|
|
190
|
+
|
|
191
|
+
// ── Metadata ──────────────────────────────────────────────
|
|
192
|
+
description: sc.description ?? null,
|
|
193
|
+
// Shortcuts inherit parent tags so they surface under the
|
|
194
|
+
// same search terms; shortcut-specific tags take precedence.
|
|
195
|
+
tags: isArray(sc.tags) ? sc.tags : isArray(menuItem.tags) ? menuItem.tags : null,
|
|
196
|
+
|
|
197
|
+
// ── Behaviour ─────────────────────────────────────────────
|
|
198
|
+
onClick: sc.onClick ?? null,
|
|
199
|
+
disabled: sc.disabled ?? false,
|
|
200
|
+
type: sc.type ?? 'default',
|
|
201
|
+
buttonType: sc.buttonType ?? null,
|
|
202
|
+
|
|
203
|
+
// ── Styling ───────────────────────────────────────────────
|
|
204
|
+
class: sc.class ?? null,
|
|
205
|
+
inlineClass: sc.inlineClass ?? null,
|
|
206
|
+
wrapperClass: sc.wrapperClass ?? null,
|
|
207
|
+
|
|
208
|
+
// ── Internal flags ────────────────────────────────────────
|
|
209
|
+
_isShortcut: true,
|
|
210
|
+
_parentTitle: menuItem.title,
|
|
211
|
+
_parentId: menuItem.id,
|
|
212
|
+
priority: (menuItem.priority ?? 0) + 1,
|
|
213
|
+
_isMenuItem: true,
|
|
214
|
+
};
|
|
215
|
+
this.registry.register('header', 'menu-item', scSlug, scItem);
|
|
216
|
+
this.trigger('menuItem.registered', scItem, 'header');
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
|
|
156
220
|
// Trigger event for backward compatibility
|
|
157
221
|
this.trigger('menuItem.registered', menuItem, 'header');
|
|
158
222
|
}
|
package/package.json
CHANGED