diff --git a/src/packages/core/extension-registry/models/current-user-action.model.ts b/src/packages/core/extension-registry/models/current-user-action.model.ts new file mode 100644 index 0000000000..7af684a83a --- /dev/null +++ b/src/packages/core/extension-registry/models/current-user-action.model.ts @@ -0,0 +1,73 @@ +import type { ConditionTypes } from '../conditions/types.js'; +import type { UmbAction } from '../../action/action.interface.js'; +import type { UmbControllerHostElement } from '@umbraco-cms/backoffice/controller-api'; +import type { ManifestElementAndApi, ManifestWithDynamicConditions } from '@umbraco-cms/backoffice/extension-api'; +import type { UUIInterfaceColor, UUIInterfaceLook } from '@umbraco-cms/backoffice/external/uui'; + +export interface UmbCurrentUserActionArgs { + meta: MetaArgsType; +} + +export interface UmbCurrentUserAction extends UmbAction> { + /** + * The href location, the action will act as a link. + * @returns {Promise} + */ + getHref(): Promise; + + /** + * The `execute` method, the action will act as a button. + * @returns {Promise} + */ + execute(): Promise; +} + +export interface ManifestCurrentUserAction + extends ManifestElementAndApi>, + ManifestWithDynamicConditions { + type: 'currentUserAction'; + meta: MetaType; +} + +export interface MetaCurrentUserAction {} + +export interface ManifestCurrentUserActionDefaultKind< + MetaType extends MetaCurrentUserActionDefaultKind = MetaCurrentUserActionDefaultKind, +> extends ManifestCurrentUserAction { + type: 'currentUserAction'; + kind: 'default'; +} + +export interface MetaCurrentUserActionDefaultKind extends MetaCurrentUserAction { + /** + * An icon to represent the action to be performed + * + * @examples [ + * "icon-box", + * "icon-grid" + * ] + */ + icon?: string; + + /** + * The friendly name of the action to perform + * + * @examples [ + * "Create", + * "Create Content Template" + * ] + */ + label: string; + + /** + * The look of the button + * @default primary + */ + look?: UUIInterfaceLook; + + /** + * The color of the button + * @default default + */ + color?: UUIInterfaceColor; +} diff --git a/src/packages/core/extension-registry/models/index.ts b/src/packages/core/extension-registry/models/index.ts index a997488da7..5729a909c2 100644 --- a/src/packages/core/extension-registry/models/index.ts +++ b/src/packages/core/extension-registry/models/index.ts @@ -2,6 +2,7 @@ import type { ManifestAuthProvider } from './auth-provider.model.js'; import type { ManifestBlockEditorCustomView } from './block-editor-custom-view.model.js'; import type { ManifestCollection } from './collection.models.js'; import type { ManifestCollectionView } from './collection-view.model.js'; +import type { ManifestCurrentUserAction, ManifestCurrentUserActionDefaultKind } from './current-user-action.model.js'; import type { ManifestDashboard } from './dashboard.model.js'; import type { ManifestDashboardCollection } from './dashboard-collection.model.js'; import type { @@ -72,6 +73,7 @@ export type * from './block-editor-custom-view.model.js'; export type * from './collection.models.js'; export type * from './collection-action.model.js'; export type * from './collection-view.model.js'; +export type * from './current-user-action.model.js'; export type * from './dashboard-collection.model.js'; export type * from './dashboard.model.js'; export type * from './dynamic-root.model.js'; @@ -143,6 +145,8 @@ export type ManifestTypes = | ManifestCollection | ManifestCollectionView | ManifestCollectionAction + | ManifestCurrentUserAction + | ManifestCurrentUserActionDefaultKind | ManifestCondition | ManifestDashboard | ManifestDashboardCollection diff --git a/src/packages/user/current-user/action/current-user-app-button.element.ts b/src/packages/user/current-user/action/current-user-app-button.element.ts new file mode 100644 index 0000000000..fd6887215a --- /dev/null +++ b/src/packages/user/current-user/action/current-user-app-button.element.ts @@ -0,0 +1,66 @@ +import { UmbTextStyles } from '@umbraco-cms/backoffice/style'; +import { html, customElement, ifDefined, state, property } from '@umbraco-cms/backoffice/external/lit'; +import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; +import type { + ManifestCurrentUserActionDefaultKind, + MetaCurrentUserActionDefaultKind, + UmbCurrentUserAction, +} from '@umbraco-cms/backoffice/extension-registry'; +import { UmbActionExecutedEvent } from '@umbraco-cms/backoffice/event'; + +@customElement('umb-current-user-app-button') +export class UmbCurrentUserAppButtonElement< + MetaType extends MetaCurrentUserActionDefaultKind = MetaCurrentUserActionDefaultKind, + ApiType extends UmbCurrentUserAction = UmbCurrentUserAction, +> extends UmbLitElement { + #api?: ApiType; + + @state() + _href?: string; + + @property({ attribute: false }) + public manifest?: ManifestCurrentUserActionDefaultKind; + + public set api(api: ApiType | undefined) { + this.#api = api; + + this.#api?.getHref?.().then((href) => { + this._href = href; + }); + } + + async #onClick(event: Event) { + if (!this._href) { + event.stopPropagation(); + await this.#api?.execute(); + } + this.dispatchEvent(new UmbActionExecutedEvent()); + } + + get label(): string | undefined { + return this.manifest?.meta.label ? this.localize.string(this.manifest.meta.label) : undefined; + } + + render() { + return html` + + ${this.manifest?.meta.icon ? html`` : ''} ${this.label} + + `; + } + + static styles = [UmbTextStyles]; +} + +export default UmbCurrentUserAppButtonElement; + +declare global { + interface HTMLElementTagNameMap { + 'umb-current-user-app-button': UmbCurrentUserAppButtonElement; + } +} diff --git a/src/packages/user/current-user/action/default.kind.ts b/src/packages/user/current-user/action/default.kind.ts new file mode 100644 index 0000000000..900bd8ad36 --- /dev/null +++ b/src/packages/user/current-user/action/default.kind.ts @@ -0,0 +1,13 @@ +import type { UmbBackofficeManifestKind } from '@umbraco-cms/backoffice/extension-registry'; + +export const manifest: UmbBackofficeManifestKind = { + type: 'kind', + alias: 'Umb.Kind.CurrentUserAction.Default', + matchKind: 'default', + matchType: 'currentUserAction', + manifest: { + type: 'currentUserAction', + kind: 'default', + elementName: 'umb-current-user-app-button', + }, +}; diff --git a/src/packages/user/current-user/action/index.ts b/src/packages/user/current-user/action/index.ts new file mode 100644 index 0000000000..76e9e5eab0 --- /dev/null +++ b/src/packages/user/current-user/action/index.ts @@ -0,0 +1 @@ +export * from './current-user-app-button.element.js'; diff --git a/src/packages/user/current-user/index.ts b/src/packages/user/current-user/index.ts index 16c179e8dc..def7620871 100644 --- a/src/packages/user/current-user/index.ts +++ b/src/packages/user/current-user/index.ts @@ -1,3 +1,4 @@ +export * from './action/index.js'; export * from './components/index.js'; export * from './history/current-user-history.store.js'; export * from './utils/index.js'; diff --git a/src/packages/user/current-user/manifests.ts b/src/packages/user/current-user/manifests.ts index 8b2d9a3751..65509321ec 100644 --- a/src/packages/user/current-user/manifests.ts +++ b/src/packages/user/current-user/manifests.ts @@ -1,3 +1,4 @@ +import { manifest as actionDefaultKindManifest } from './action/default.kind.js'; import { manifests as modalManifests } from './modals/manifests.js'; import { manifests as externalLoginProviderManifests } from './external-login/manifests.js'; import { manifests as historyManifests } from './history/manifests.js'; @@ -29,6 +30,7 @@ export const headerApps: Array = [ ]; export const manifests = [ + actionDefaultKindManifest, ...externalLoginProviderManifests, ...headerApps, ...historyManifests, diff --git a/src/packages/user/current-user/profile/current-user-profile-user-profile-app.element.ts b/src/packages/user/current-user/profile/current-user-profile-user-profile-app.element.ts index 0916731b84..5cc0d8e8f4 100644 --- a/src/packages/user/current-user/profile/current-user-profile-user-profile-app.element.ts +++ b/src/packages/user/current-user/profile/current-user-profile-user-profile-app.element.ts @@ -1,7 +1,8 @@ -import { html, customElement, state } from '@umbraco-cms/backoffice/external/lit'; +import { html, customElement, state, css } from '@umbraco-cms/backoffice/external/lit'; import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; import { UMB_CHANGE_PASSWORD_MODAL, UMB_MODAL_MANAGER_CONTEXT } from '@umbraco-cms/backoffice/modal'; import { UMB_CURRENT_USER_CONTEXT, type UmbCurrentUserModel } from '@umbraco-cms/backoffice/current-user'; +import { UmbTextStyles } from '@umbraco-cms/backoffice/style'; @customElement('umb-current-user-profile-user-profile-app') export class UmbCurrentUserProfileUserProfileAppElement extends UmbLitElement { @@ -59,9 +60,22 @@ export class UmbCurrentUserProfileUserProfileAppElement extends UmbLitElement { ${this.localize.term('general_changePassword')} + `; } + + static styles = [ + UmbTextStyles, + css` + #actions { + display: flex; + flex-wrap: wrap; + flex-direction: row; + gap: var(--uui-size-space-2); + } + `, + ]; } export default UmbCurrentUserProfileUserProfileAppElement;