|
1 | | -# Menu |
| 1 | +--- |
| 2 | +description: >- |
| 3 | + Create menus that appear throughout the backoffice, including in sidebars and button flyouts. |
| 4 | +--- |
2 | 5 |
|
3 | | -<figure><img src="../../../.gitbook/assets/menu.png" alt="" width="250"><figcaption><p>Menu</p></figcaption></figure> |
| 6 | +# Menus |
4 | 7 |
|
5 | | -## Creating a custom menu |
| 8 | +Menu extensions contain one or more [menu item extensions](menu-item.md) and can appear throughout the backoffice, such as in sidebars and flyouts. |
6 | 9 |
|
7 | | -In this section, you can learn how to register and create a custom Menu for the Umbraco backoffice. |
| 10 | +<figure><img src="../../../.gitbook/assets/menu.png" alt="" width="250"><figcaption><p>Menu</p></figcaption></figure> |
8 | 11 |
|
9 | | -### Manifest |
| 12 | +## Creating a custom menu |
10 | 13 |
|
11 | | -The manifest file can be created using either JSON or TypeScript. Both methods are shown below. |
| 14 | +Menu extensions can be created using either JSON or TypeScript. Both approaches are shown below. |
12 | 15 |
|
13 | 16 | {% tabs %} |
14 | | - |
15 | 17 | {% tab title="JSON" %} |
16 | | - |
17 | | -We can create the manifest using JSON in the `umbraco-package.json`. |
18 | | - |
| 18 | +{% code title="umbraco-package.json" %} |
19 | 19 | ```json |
20 | 20 | { |
21 | | - "type": "menu", |
22 | | - "alias": "My.Menu", |
23 | | - "name": "My Menu" |
| 21 | + "$schema": "../../umbraco-package-schema.json", |
| 22 | + "name": "My Package", |
| 23 | + "version": "0.1.0", |
| 24 | + "extensions": [ |
| 25 | + { |
| 26 | + "type": "menu", |
| 27 | + "alias": "My.Menu", |
| 28 | + "name": "My Menu" |
| 29 | + } |
| 30 | + ] |
24 | 31 | } |
25 | 32 | ``` |
| 33 | +{% endcode %} |
26 | 34 | {% endtab %} |
27 | | - |
28 | 35 | {% tab title="TypeScript" %} |
| 36 | +Extension authors define the menu manifest, then register it dynamically/during runtime using a [Backoffice Entry Point](../../extending-overview/extension-types/backoffice-entry-point.md) extension. |
29 | 37 |
|
30 | | -The manifest can also be written in TypeScript. |
31 | | - |
32 | | -For this TypeScript example we used a [Backoffice Entry Point](../../extending-overview/extension-types/backoffice-entry-point.md) extension to register the manifests. |
33 | | - |
| 38 | +{% code title="my-menu/manifests.ts" %} |
34 | 39 | ```typescript |
35 | 40 | import type { ManifestMenu } from '@umbraco-cms/backoffice/menu'; |
36 | 41 |
|
37 | | -const menuManifest: Array<ManifestMenu> = [ |
38 | | - { |
39 | | - type: 'menu', |
40 | | - alias: 'My.Menu', |
41 | | - name: 'My Menu' |
42 | | - } |
43 | | -]; |
44 | | -``` |
45 | | - |
46 | | -{% endtab %} |
47 | | - |
48 | | -{% endtabs %} |
49 | | - |
50 | | -# Menu Item |
51 | | - |
52 | | -<figure><img src="../../../.gitbook/assets/menu-item.png" alt="" width="250"><figcaption><p>Menu Item</p></figcaption></figure> |
53 | | - |
54 | | -Menu items are the items that appear in the menu. |
55 | | - |
56 | | -## Creating a custom menu items |
57 | | - |
58 | | -In this section, you can learn how to add custom Menu Items to your Umbraco backoffice Menu. |
59 | | - |
60 | | -### Manifest |
61 | | - |
62 | | -To add custom menu items, you can define a single MenuItem manifest and link an element to it. In this element, you can fetch the data and render as many menu items as you want based on that data. |
63 | | - |
64 | | -The code snippets below show how to declare a new menu item using JSON or TypeScript. |
65 | | - |
66 | | -{% tabs %} |
67 | | - |
68 | | -{% tab title="JSON" %} |
69 | | - |
70 | | -We can create the manifest using JSON in the `umbraco-package.json`. |
71 | | - |
72 | | -```json |
73 | | -{ |
74 | | - "type": "menuItem", |
75 | | - "alias": "My.MenuItem", |
76 | | - "name": "My Menu Item", |
77 | | - "element": "./menu-items.ts", |
78 | | - "meta": { |
79 | | - "label": "My Menu Item", |
80 | | - "menus": ["My.Menu"] |
81 | | - } |
82 | | -} |
83 | | -``` |
84 | | - |
85 | | -{% endtab %} |
86 | | - |
87 | | -{% tab title="TypeScript" %} |
88 | | - |
89 | | -The manifest can also be written in TypeScript. |
90 | | - |
91 | | -For this TypeScript example we used a [Backoffice Entry Point](../../extending-overview/extension-types/backoffice-entry-point.md) extension to register the manifests. |
92 | | - |
93 | | -{% code title="manifest.ts" overflow="wrap" lineNumbers="true" %} |
94 | | -```typescript |
95 | | -const menuItemManifest: Array<ManifestMenuItem> = [ |
96 | | - { |
97 | | - type: 'menuItem', |
98 | | - alias: 'My.MenuItem', |
99 | | - name: 'My Menu Item', |
100 | | - meta: { |
101 | | - label: 'My Menu Item', |
102 | | - menus: ["My.Menu"] |
103 | | - }, |
104 | | - element: () => import('./menu-items.ts') |
105 | | - } |
106 | | -]; |
| 42 | +export const menuManifest: ManifestMenu = { |
| 43 | + type: 'menu', |
| 44 | + alias: 'My.Menu', |
| 45 | + name: 'My Menu' |
| 46 | +}; |
107 | 47 | ``` |
108 | 48 | {% endcode %} |
109 | 49 |
|
110 | | - |
111 | | -{% endtab %} |
112 | | - |
113 | | -{% endtabs %} |
114 | | - |
115 | | -### The UI Element |
116 | | - |
117 | | -#### Rendering menu items with Umbraco's UI menu item component |
118 | | - |
119 | | -To render your menu items in Umbraco, you can use the [Umbraco UI Menu Item component](https://uui.umbraco.com/?path=/docs/uui-menu-item--docs). This component allows you to create nested menu structures with a few lines of code. |
120 | | - |
121 | | -By default, you can set the `has-children` attribute to display the caret icon indicating nested items. It will look like this: `?has-children=${bool}`. |
122 | | - |
123 | | -**Example:** |
124 | | - |
125 | | -```tsx |
126 | | -<uui-menu-item label="Menu Item 1" has-children> |
127 | | - <uui-menu-item label="Nested Menu Item 1"></uui-menu-item> |
128 | | - <uui-menu-item label="Nested Menu Item 2"></uui-menu-item> |
129 | | -</uui-menu-item> |
130 | | -``` |
131 | | - |
132 | | -#### Custom menu item element example |
133 | | - |
134 | | -You can fetch the data and render the menu items using the Lit element above. By putting the result of the fetch in a `@state()`, we can trigger a re-render of the component when the data is fetched. |
135 | | - |
136 | | -{% code title="menu-items.ts" overflow="wrap" lineNumbers="true" %} |
| 50 | +{% code title="entrypoints/entrypoints.ts" %} |
137 | 51 | ```typescript |
138 | | -import type { UmbMenuItemElement } from '@umbraco-cms/backoffice/menu'; |
139 | | -import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; |
140 | | -import { html, TemplateResult, customElement, state } from '@umbraco-cms/backoffice/external/lit'; |
141 | | -import { MyMenuItemResponseModel, MyMenuResource } from '../../../api'; |
142 | | - |
143 | | -const elementName = 'my-menu-item'; |
144 | | - |
145 | | -@customElement(elementName) |
146 | | -class MyMenuItems extends UmbLitElement implements UmbMenuItemElement { |
147 | | - @state() |
148 | | - private _items: MyMenuItemResponseModel[] = []; // Store fetched items |
149 | | - |
150 | | - @state() |
151 | | - private _loading: boolean = true; // Track loading state |
152 | | - |
153 | | - @state() |
154 | | - private _error: string | null = null; // Track any errors |
155 | | - |
156 | | - override firstUpdated() { |
157 | | - this.fetchInitialItems(); // Start fetching on component load |
158 | | - } |
159 | | - |
160 | | - // Fetch initial items |
161 | | - async fetchInitialItems() { |
162 | | - try { |
163 | | - this._loading = true; |
164 | | - this._items = ((await MyMenuResource.getMenuApiV1()).items); // Fetch root-level items |
165 | | - } catch (e) { |
166 | | - this._error = 'Error fetching items'; |
167 | | - } finally { |
168 | | - this._loading = false; |
169 | | - } |
170 | | - } |
171 | | - |
172 | | - // Render items |
173 | | - renderItems(items: MyMenuItemResponseModel[]): TemplateResult { |
174 | | - return html` |
175 | | - ${items.map(element => html` |
176 | | - <uui-menu-item label="${element.name}" ?has-children=${element.hasChildren}> |
177 | | - ${element.type === 1 |
178 | | - ? html`<uui-icon slot="icon" name="icon-folder"></uui-icon>` |
179 | | - : html`<uui-icon slot="icon" name="icon-autofill"></uui-icon>`} |
180 | | - <!-- recursively render children --> |
181 | | - ${element.hasChildren ? this.renderItems(element.children) : ''} |
182 | | - </uui-menu-item> |
183 | | - `)} |
184 | | - `; |
185 | | - } |
186 | | - |
187 | | - // Main render function |
188 | | - override render() { |
189 | | - if (this._loading) { |
190 | | - return html`<uui-loader></uui-loader>`; |
191 | | - } |
192 | | - |
193 | | - if (this._error) { |
194 | | - return html`<uui-menu-item active disabled label="Could not load form tree!"> |
195 | | - </uui-menu-item>`; |
196 | | - } |
197 | | - |
198 | | - // Render items if loading is done and no error occurred |
199 | | - return this.renderItems(this._items); |
200 | | - } |
201 | | -} |
202 | | - |
203 | | -export { MyMenuItems as element }; |
| 52 | +import type { |
| 53 | + UmbEntryPointOnInit, |
| 54 | +} from "@umbraco-cms/backoffice/extension-api"; |
| 55 | +import { umbExtensionsRegistry } from "@umbraco-cms/backoffice/extension-registry"; |
| 56 | +import { menuManifest } from "./../my-menu/manifests.ts"; |
204 | 57 |
|
205 | | -declare global { |
206 | | - interface HTMLElementTagNameMap { |
207 | | - [elementName]: MyMenuItems; |
208 | | - } |
209 | | -} |
| 58 | +export const onInit: UmbEntryPointOnInit = (_host, _extensionRegistry) => { |
| 59 | + console.log("Hello from my extension 🎉"); |
210 | 60 |
|
| 61 | + umbExtensionsRegistry.register(menuManifest); |
| 62 | +}; |
211 | 63 | ``` |
212 | 64 | {% endcode %} |
| 65 | +{% endtab %} |
| 66 | +{% endtabs %} |
213 | 67 |
|
214 | | - |
215 | | -## Tree Menu Item |
216 | | - |
217 | | -### Manifest |
218 | | - |
219 | | -```json |
220 | | -// it will be something like this |
221 | | -{ |
222 | | - "type": "menuItem", |
223 | | - "kind": "tree", |
224 | | - "alias": "My.TreeMenuItem", |
225 | | - "name": "My Tree Menu Item", |
226 | | - "meta": { |
227 | | - "label": "My Tree Menu Item", |
228 | | - "menus": ["My.Menu"] |
229 | | - } |
230 | | -} |
231 | | -``` |
232 | | - |
233 | | -#### Default Element |
234 | | - |
235 | | -The default element supports rendering a subtree of menu items. |
236 | | - |
237 | | -```typescript |
238 | | -class UmbMenuItemTreeDefaultElement {} |
239 | | -``` |
240 | | - |
241 | | -### Adding menu items to an existing menu |
242 | | - |
243 | | -The backoffice comes with a couple of menus. |
244 | | - |
245 | | -* Content, Media, Settings, Templating, Dictionary, etc. |
246 | | - |
247 | | -To add a menu item to an existing menu, you can use the `meta.menus` property. |
248 | | - |
249 | | -```typescript |
250 | | -{ |
251 | | - "type": "menuItem", |
252 | | - "alias": "My.MenuItem", |
253 | | - "name": "My Menu Item", |
254 | | - "meta": { |
255 | | - "label": "My Menu Item", |
256 | | - "menus": ["Umb.Menu.Content"] |
257 | | - } |
258 | | -} |
259 | | -``` |
| 68 | +## See Also |
| 69 | +* [Section Sidebar](sections/section-sidebar.md) for information on creating menus for navigation within section extensions. |
| 70 | +* [Menu Item](menu-item.md) for information on creating menu items. |
0 commit comments