Skip to content

Commit

Permalink
Merge pull request #2261 from umbraco/v15/feature/help-header-app
Browse files Browse the repository at this point in the history
Feature: Help Header App + Menu
  • Loading branch information
nielslyngsoe authored Sep 13, 2024
2 parents 4a75f92 + e88103f commit 4b1ade7
Show file tree
Hide file tree
Showing 30 changed files with 312 additions and 14 deletions.
5 changes: 5 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
"./entity": "./dist-cms/packages/core/entity/index.js",
"./event": "./dist-cms/packages/core/event/index.js",
"./extension-registry": "./dist-cms/packages/core/extension-registry/index.js",
"./help": "./dist-cms/packages/help/index.js",
"./icon": "./dist-cms/packages/core/icon-registry/index.js",
"./id": "./dist-cms/packages/core/id/index.js",
"./imaging": "./dist-cms/packages/media/imaging/index.js",
Expand Down
1 change: 1 addition & 0 deletions src/apps/backoffice/backoffice.element.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ const CORE_PACKAGES = [
import('../../packages/dictionary/umbraco-package.js'),
import('../../packages/documents/umbraco-package.js'),
import('../../packages/health-check/umbraco-package.js'),
import('../../packages/help/umbraco-package.js'),
import('../../packages/language/umbraco-package.js'),
import('../../packages/log-viewer/umbraco-package.js'),
import('../../packages/markdown-editor/umbraco-package.js'),
Expand Down
3 changes: 2 additions & 1 deletion src/packages/core/extension-registry/models/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ import type { ManifestHealthCheck } from './health-check.model.js';
import type { ManifestIcons } from './icons.model.js';
import type { ManifestLocalization } from './localization.model.js';
import type { ManifestMenu } from './menu.model.js';
import type { ManifestMenuItem, ManifestMenuItemTreeKind } from './menu-item.model.js';
import type { ManifestMenuItem, ManifestMenuItemLinkKind, ManifestMenuItemTreeKind } from './menu-item.model.js';
import type { ManifestModal } from './modal.model.js';
import type { ManifestPackageView } from './package-view.model.js';
import type { ManifestPreviewAppProvider } from './preview-app.model.js';
Expand Down Expand Up @@ -188,6 +188,7 @@ export type ManifestTypes =
| ManifestMenu
| ManifestMenuItem
| ManifestMenuItemTreeKind
| ManifestMenuItemLinkKind
| ManifestMfaLoginProvider
| ManifestModal
| ManifestMonacoMarkdownEditorAction
Expand Down
10 changes: 10 additions & 0 deletions src/packages/core/extension-registry/models/menu-item.model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,3 +26,13 @@ export interface MetaMenuItemTreeKind extends MetaMenuItem {
treeAlias: string;
hideTreeRoot?: boolean;
}

export interface ManifestMenuItemLinkKind extends ManifestMenuItem {
type: 'menuItem';
kind: 'link';
meta: MetaMenuItemLinkKind;
}

export interface MetaMenuItemLinkKind extends MetaMenuItem {
href: string;
}
2 changes: 2 additions & 0 deletions src/packages/core/manifests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { manifests as entityBulkActionManifests } from './entity-bulk-action/man
import { manifests as extensionManifests } from './extension-registry/manifests.js';
import { manifests as iconRegistryManifests } from './icon-registry/manifests.js';
import { manifests as localizationManifests } from './localization/manifests.js';
import { manifests as menuManifests } from './menu/manifests.js';
import { manifests as modalManifests } from './modal/common/manifests.js';
import { manifests as pickerManifests } from './picker/manifests.js';
import { manifests as propertyActionManifests } from './property-action/manifests.js';
Expand All @@ -35,6 +36,7 @@ export const manifests: Array<ManifestTypes | UmbBackofficeManifestKind> = [
...extensionManifests,
...iconRegistryManifests,
...localizationManifests,
...menuManifests,
...modalManifests,
...pickerManifests,
...propertyActionManifests,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,15 @@ export class UmbMenuItemLayoutElement extends UmbLitElement {
@property({ type: String })
public href?: string;

/**
* Set an anchor tag target, only used when using href.
* @type {string}
* @attr
* @default undefined
*/
@property({ type: String })
public target?: '_blank' | '_parent' | '_self' | '_top';

@state()
private _isActive = false;

Expand All @@ -63,7 +72,8 @@ export class UmbMenuItemLayoutElement extends UmbLitElement {
label=${this.label}
.caretLabel=${this.localize.term('visuallyHiddenTexts_expandChildItems') + ' ' + this.label}
?active=${this._isActive}
?has-children=${this.hasChildren}>
?has-children=${this.hasChildren}
target=${ifDefined(this.href && this.target ? this.target : undefined)}>
<umb-icon slot="icon" name=${this.iconName}></umb-icon>
${this.entityType
? html`<umb-entity-actions-bundle
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import { customElement, html, property } from '@umbraco-cms/backoffice/external/lit';
import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element';
import type { ManifestMenuItemLinkKind, UmbMenuItemElement } from '@umbraco-cms/backoffice/extension-registry';

const elementName = 'umb-link-menu-item';
@customElement(elementName)
export class UmbLinkMenuItemElement extends UmbLitElement implements UmbMenuItemElement {
@property({ type: Object, attribute: false })
manifest?: ManifestMenuItemLinkKind;

#getTarget() {
const href = this.manifest?.meta.href;

if (href && href.startsWith('http')) {
return '_blank';
}

return '_self';
}

override render() {
return html`
<umb-menu-item-layout
.href=${this.manifest?.meta.href}
target=${this.#getTarget()}
.iconName=${this.manifest?.meta.icon ?? ''}
.label=${this.localize.string(this.manifest?.meta.label ?? this.manifest?.name ?? '')}>
</umb-menu-item-layout>
`;
}
}

export { UmbLinkMenuItemElement as element };

declare global {
interface HTMLElementTagNameMap {
[elementName]: UmbLinkMenuItemElement;
}
}
14 changes: 14 additions & 0 deletions src/packages/core/menu/components/menu-item/link/manifests.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import type { ManifestTypes, UmbBackofficeManifestKind } from '@umbraco-cms/backoffice/extension-registry';

export const manifests: Array<ManifestTypes | UmbBackofficeManifestKind> = [
{
type: 'kind',
alias: 'Umb.Kind.MenuItem.Link',
matchKind: 'link',
matchType: 'menuItem',
manifest: {
type: 'menuItem',
element: () => import('./link-menu-item.element.js'),
},
},
];
4 changes: 4 additions & 0 deletions src/packages/core/menu/components/menu-item/manifests.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import { manifests as linkManifests } from './link/manifests.js';
import type { ManifestTypes, UmbBackofficeManifestKind } from '@umbraco-cms/backoffice/extension-registry';

export const manifests: Array<ManifestTypes | UmbBackofficeManifestKind> = [...linkManifests];
4 changes: 4 additions & 0 deletions src/packages/core/menu/manifests.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import { manifests as menuItemManifests } from './components/menu-item/manifests.js';
import type { ManifestTypes, UmbBackofficeManifestKind } from '@umbraco-cms/backoffice/extension-registry';

export const manifests: Array<ManifestTypes | UmbBackofficeManifestKind> = [...menuItemManifests];
77 changes: 77 additions & 0 deletions src/packages/help/header-app/help-header-app.element.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
import { UMB_HELP_MENU_ALIAS } from '../menu/index.js';
import type { CSSResultGroup } from '@umbraco-cms/backoffice/external/lit';
import { css, html, customElement, state, nothing } from '@umbraco-cms/backoffice/external/lit';
import { UmbHeaderAppButtonElement } from '@umbraco-cms/backoffice/components';
import { umbExtensionsRegistry, type ManifestMenu } from '@umbraco-cms/backoffice/extension-registry';
import { UmbExtensionsManifestInitializer } from '@umbraco-cms/backoffice/extension-api';

const elementName = 'umb-help-header-app';
@customElement(elementName)
export class UmbHelpHeaderAppElement extends UmbHeaderAppButtonElement {
@state()
private _popoverOpen = false;

@state()
private _helpMenuHasMenuItems = false;

constructor() {
super();

new UmbExtensionsManifestInitializer(
this,
umbExtensionsRegistry,
'menuItem',
(manifest) => manifest.meta.menus.includes(UMB_HELP_MENU_ALIAS),
(menuItems) => {
const manifests = menuItems.map((menuItem) => menuItem.manifest);
this._helpMenuHasMenuItems = manifests.length > 0;
},
);
}

#onPopoverToggle(event: ToggleEvent) {
// TODO: This ignorer is just neede for JSON SCHEMA TO WORK, As its not updated with latest TS jet.
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
this._popoverOpen = event.newState === 'open';
}

override render() {
return html` ${this.#renderButton()} ${this.#renderPopover()} `;
}

#renderButton() {
if (!this._helpMenuHasMenuItems) return nothing;

return html`
<uui-button popovertarget="help-menu-popover" look="primary" label="help" compact>
<uui-icon name="icon-help-alt"></uui-icon>
</uui-button>
`;
}

#renderPopover() {
return html`
<uui-popover-container id="help-menu-popover" @toggle=${this.#onPopoverToggle}>
<umb-popover-layout>
<uui-scroll-container>
<umb-extension-slot
type="menu"
.filter="${(menu: ManifestMenu) => menu.alias === UMB_HELP_MENU_ALIAS}"
default-element="umb-menu"></umb-extension-slot>
</uui-scroll-container>
</umb-popover-layout>
</uui-popover-container>
`;
}

static override styles: CSSResultGroup = [UmbHeaderAppButtonElement.styles, css``];
}

export { UmbHelpHeaderAppElement as element };

declare global {
interface HTMLElementTagNameMap {
[elementName]: UmbHelpHeaderAppElement;
}
}
11 changes: 11 additions & 0 deletions src/packages/help/header-app/manifests.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import type { ManifestTypes, UmbBackofficeManifestKind } from '@umbraco-cms/backoffice/extension-registry';

export const manifests: Array<ManifestTypes | UmbBackofficeManifestKind> = [
{
type: 'headerApp',
alias: 'Umb.HeaderApp.Help',
name: 'Help Header App',
element: () => import('./help-header-app.element.js'),
weight: 500,
},
];
1 change: 1 addition & 0 deletions src/packages/help/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './menu/index.js';
5 changes: 5 additions & 0 deletions src/packages/help/manifests.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { manifests as headerAppManifests } from './header-app/manifests.js';
import { manifests as menuManifests } from './menu/manifests.js';
import type { ManifestTypes, UmbBackofficeManifestKind } from '@umbraco-cms/backoffice/extension-registry';

export const manifests: Array<ManifestTypes | UmbBackofficeManifestKind> = [...menuManifests, ...headerAppManifests];
1 change: 1 addition & 0 deletions src/packages/help/menu/constants.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export const UMB_HELP_MENU_ALIAS = 'Umb.Menu.Help';
1 change: 1 addition & 0 deletions src/packages/help/menu/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './constants.js';
47 changes: 47 additions & 0 deletions src/packages/help/menu/manifests.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import { UMB_HELP_MENU_ALIAS } from './constants.js';
import { UMB_CURRENT_USER_IS_ADMIN_CONDITION_ALIAS } from '@umbraco-cms/backoffice/current-user';
import type { ManifestTypes, UmbBackofficeManifestKind } from '@umbraco-cms/backoffice/extension-registry';

export const manifests: Array<ManifestTypes | UmbBackofficeManifestKind> = [
{
type: 'menu',
alias: UMB_HELP_MENU_ALIAS,
name: 'Help Menu',
},
{
type: 'menuItem',
kind: 'link',
alias: 'Umb.MenuItem.Help.LearningBase',
name: 'Learning Base Help Menu Item',
weight: 200,
meta: {
menus: [UMB_HELP_MENU_ALIAS],
label: 'Umbraco Learning Base',
icon: 'icon-movie-alt',
href: 'https://umbra.co/ulb',
},
conditions: [
{
alias: UMB_CURRENT_USER_IS_ADMIN_CONDITION_ALIAS,
},
],
},
{
type: 'menuItem',
kind: 'link',
alias: 'Umb.MenuItem.Help.CommunityWebsite',
name: 'Community Website Help Menu Item',
weight: 100,
meta: {
menus: [UMB_HELP_MENU_ALIAS],
label: 'Community Website',
icon: 'icon-hearts',
href: 'https://our.umbraco.com?utm_source=core&amp;utm_medium=help&amp;utm_content=link&amp;utm_campaign=our',
},
conditions: [
{
alias: UMB_CURRENT_USER_IS_ADMIN_CONDITION_ALIAS,
},
],
},
];
8 changes: 8 additions & 0 deletions src/packages/help/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"name": "@umbraco-backoffice/help",
"private": true,
"type": "module",
"scripts": {
"build": "vite build"
}
}
9 changes: 9 additions & 0 deletions src/packages/help/umbraco-package.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
export const name = 'Umbraco.Core.Help';
export const extensions = [
{
name: 'Help Bundle',
alias: 'Umb.Bundle.Help',
type: 'bundle',
js: () => import('./manifests.js'),
},
];
12 changes: 12 additions & 0 deletions src/packages/help/vite.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { defineConfig } from 'vite';
import { rmSync } from 'fs';
import { getDefaultConfig } from '../../vite-config-base';

const dist = '../../../dist-cms/packages/help';

// delete the unbundled dist folder
rmSync(dist, { recursive: true, force: true });

export default defineConfig({
...getDefaultConfig({ dist }),
});
1 change: 1 addition & 0 deletions src/packages/user/current-user/conditions/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './is-admin/index.js';
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export const UMB_CURRENT_USER_IS_ADMIN_CONDITION_ALIAS = 'Umb.Condition.CurrentUser.IsAdmin';
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './constants.js';
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { UMB_CURRENT_USER_IS_ADMIN_CONDITION_ALIAS } from './constants.js';
import type { ManifestCondition } from '@umbraco-cms/backoffice/extension-api';

export const manifest: ManifestCondition = {
type: 'condition',
name: 'Current user is admin Condition',
alias: UMB_CURRENT_USER_IS_ADMIN_CONDITION_ALIAS,
api: () => import('./is-admin.condition.js'),
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { isCurrentUserAnAdmin } from '../../utils/is-current-user.function.js';
import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api';
import type {
UmbConditionConfigBase,
UmbConditionControllerArguments,
UmbExtensionCondition,
} from '@umbraco-cms/backoffice/extension-api';
import { UmbConditionBase } from '@umbraco-cms/backoffice/extension-registry';

export class UmbContentHasPropertiesWorkspaceCondition
extends UmbConditionBase<UmbConditionConfigBase>
implements UmbExtensionCondition
{
constructor(host: UmbControllerHost, args: UmbConditionControllerArguments<UmbConditionConfigBase>) {
super(host, args);
isCurrentUserAnAdmin(host).then((isAdmin) => (this.permitted = isAdmin));
}
}

export { UmbContentHasPropertiesWorkspaceCondition as api };
3 changes: 3 additions & 0 deletions src/packages/user/current-user/conditions/manifests.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import { manifest as isAdminManifests } from './is-admin/is-admin.condition.manifest.js';

export const manifests = [isAdminManifests];
Loading

0 comments on commit 4b1ade7

Please sign in to comment.