|
4 | 4 | This page is a work in progress and may undergo further revisions, updates, or amendments. The information contained herein is subject to change without notice. |
5 | 5 | {% endhint %} |
6 | 6 |
|
7 | | -## Menu |
8 | | - |
9 | 7 | <figure><img src="../../../.gitbook/assets/menu.png" alt="" width="250"><figcaption><p>Menu</p></figcaption></figure> |
10 | 8 |
|
11 | | -```typescript |
| 9 | +**JSON Manifest:** |
| 10 | +```json |
12 | 11 | { |
13 | 12 | "type": "menu", |
14 | 13 | "alias": "My.Menu", |
15 | 14 | "name": "My Menu" |
16 | 15 | } |
17 | 16 | ``` |
18 | 17 |
|
19 | | -### Menu Item <a href="#menu-item" id="menu-item"></a> |
| 18 | +**Typescript Manifest:** |
| 19 | +```typescript |
| 20 | +import { ManifestMenu } from "@umbraco-cms/backoffice/extension-registry"; |
| 21 | + |
| 22 | +const menuManifest: Array<ManifestMenu> = [ |
| 23 | + { |
| 24 | + type: 'menu', |
| 25 | + alias: 'My.Menu', |
| 26 | + name: 'My Menu' |
| 27 | + } |
| 28 | +]; |
| 29 | +``` |
| 30 | + |
| 31 | + |
| 32 | +## Menu Item <a href="#menu-item" id="menu-item"></a> |
20 | 33 |
|
21 | 34 | <figure><img src="../../../.gitbook/assets/menu-item.png" alt="" width="250"><figcaption><p>Menu Item</p></figcaption></figure> |
22 | 35 |
|
23 | | -```typescript |
| 36 | +### What is a Menu Item? |
| 37 | + |
| 38 | +Menu items are the items that appear in the menu. |
| 39 | + |
| 40 | +For adding custom menu items we can define a single MenuItem manifest and link an element to it. In this element we can fetch the data and render as many menu items as we want based on that data. |
| 41 | + |
| 42 | +### JSON Manifest |
| 43 | + |
| 44 | +```json |
24 | 45 | { |
25 | 46 | "type": "menuItem", |
26 | 47 | "alias": "My.MenuItem", |
27 | 48 | "name": "My Menu Item", |
| 49 | + "element": "./menu-items.ts", |
28 | 50 | "meta": { |
29 | 51 | "label": "My Menu Item", |
30 | 52 | "menus": ["My.Menu"] |
31 | 53 | } |
32 | 54 | } |
33 | 55 | ``` |
34 | 56 |
|
35 | | -### **Tree Menu Item** |
| 57 | +### Typescript Manifest |
| 58 | +```typescript |
| 59 | +const menuItemManifest: Array<ManifestMenuItem> = [ |
| 60 | + { |
| 61 | + type: 'menuItem', |
| 62 | + alias: 'My.MenuItem', |
| 63 | + name: 'My Menu Item', |
| 64 | + meta: { |
| 65 | + label: 'My Menu Item', |
| 66 | + menus: ["My.Menu"] |
| 67 | + }, |
| 68 | + element: () => import('./menu-items.ts') |
| 69 | + } |
| 70 | +]; |
| 71 | +``` |
| 72 | + |
| 73 | +### The Lit Element |
| 74 | + |
| 75 | +#### Rendering menu items with Umbraco's UI menu item component |
| 76 | + |
| 77 | +To render your menu items in Umbraco, you can make use of the powerful [Umbraco UI Menu Item component](https://uui.umbraco.com/?path=/docs/uui-menu-item--docs). This component allows you to easily create nested menu structures with just a few lines of code. |
| 78 | + |
| 79 | +To display the caret icon indicating nested items, you can set the `has-children` attribute dynamically like this: `?has-children=${bool}`. |
| 80 | + |
| 81 | +**Example:** |
| 82 | + |
| 83 | +```tsx |
| 84 | +<uui-menu-item label="Menu Item 1" has-children> |
| 85 | + <uui-menu-item label="Nested Menu Item 1"></uui-menu-item> |
| 86 | + <uui-menu-item label="Nested Menu Item 2"></uui-menu-item> |
| 87 | +</uui-menu-item> |
| 88 | +``` |
| 89 | + |
| 90 | +#### Custom menu item element example |
| 91 | + |
| 92 | +Using this Lit element we can fetch the data and render the menu items. By putting the result of the fetch in a `@state()`, we can trigger a re-render of the component when the data is fetched. |
| 93 | + |
| 94 | +**menu-items.ts:** |
| 95 | +```typescript |
| 96 | +import { UmbMenuItemElement } from '@umbraco-cms/backoffice/extension-registry'; |
| 97 | +import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; |
| 98 | +import { html, TemplateResult } from 'lit'; |
| 99 | +import { customElement, state } from 'lit/decorators.js'; |
| 100 | +import { MyMenuItemResponseModel, MyMenuResource } from '../../../api'; |
| 101 | + |
| 102 | +const elementName = 'my-menu-item'; |
| 103 | + |
| 104 | +@customElement(elementName) |
| 105 | +class MyMenuItems extends UmbLitElement implements UmbMenuItemElement { |
| 106 | + @state() |
| 107 | + private _items: MyMenuItemResponseModel[] = []; // Store fetched items |
| 108 | + @state() |
| 109 | + private _loading: boolean = true; // Track loading state |
| 110 | + @state() |
| 111 | + private _error: string | null = null; // Track any errors |
| 112 | + |
| 113 | + constructor() { |
| 114 | + super(); |
| 115 | + this.fetchInitialItems(); // Start fetching on component load |
| 116 | + } |
| 117 | + |
| 118 | + // Fetch initial items |
| 119 | + async fetchInitialItems() { |
| 120 | + try { |
| 121 | + this._loading = true; |
| 122 | + this._items = ((await MyMenuResource.getMenuApiV1()).items); // Fetch root-level items |
| 123 | + } catch (e) { |
| 124 | + this._error = 'Error fetching items'; |
| 125 | + } finally { |
| 126 | + this._loading = false; |
| 127 | + } |
| 128 | + } |
| 129 | + |
| 130 | + // Render items |
| 131 | + renderItems(items: MyMenuItemResponseModel[]): TemplateResult { |
| 132 | + return html` |
| 133 | + ${items.map(element => html` |
| 134 | + <uui-menu-item label="${element.name}" ?has-children=${element.hasChildren}> |
| 135 | + ${element.type === 1 |
| 136 | + ? html`<uui-icon slot="icon" name="icon-folder"></uui-icon>` |
| 137 | + : html`<uui-icon slot="icon" name="icon-autofill"></uui-icon>`} |
| 138 | + <!-- recursively render children --> |
| 139 | + ${element.hasChildren ? this.renderItems(element.children) : ''} |
| 140 | + </uui-menu-item> |
| 141 | + `)} |
| 142 | + `; |
| 143 | + } |
| 144 | + |
| 145 | + // Main render function |
| 146 | + render() { |
| 147 | + if (this._loading) { |
| 148 | + return html`<uui-loader></uui-loader>`; |
| 149 | + } |
| 150 | + |
| 151 | + if (this._error) { |
| 152 | + return html`<uui-menu-item active disabled label="Could not load form tree!"> |
| 153 | + </uui-menu-item>`; |
| 154 | + } |
| 155 | + |
| 156 | + // Render items if loading is done and no error occurred |
| 157 | + return html`${this.renderItems(this._items)}`; |
| 158 | + } |
| 159 | +} |
| 160 | + |
| 161 | +export { MyMenuItems as element }; |
| 162 | + |
| 163 | +declare global { |
| 164 | + interface HTMLElementTagNameMap { |
| 165 | + [elementName]: MyMenuItems; |
| 166 | + } |
| 167 | +} |
| 168 | + |
| 169 | +``` |
| 170 | + |
| 171 | +## **Tree Menu Item** |
36 | 172 |
|
37 | | -#### **Manifest** |
| 173 | +### **Manifest** |
38 | 174 |
|
39 | 175 | ```typescript |
40 | 176 | // it will be something like this |
|
0 commit comments