wip add entity-create-option-action extension point
This commit is contained in:
committed by
Niels Lyngsø
parent
59790431c4
commit
42fe0c3819
@@ -0,0 +1,87 @@
|
||||
import type { UmbEntityCreateOptionAction } from '../entity-create-option-action.interface.js';
|
||||
import type { UmbEntityCreateOptionActionElement } from '../entity-create-option-action-element.interface.js';
|
||||
import type { ManifestEntityCreateOptionAction } from '../entity-create-option-action.extension.js';
|
||||
import type { MetaEntityCreateOptionActionDefaultKind } from './types.js';
|
||||
import { UmbActionExecutedEvent } from '@umbraco-cms/backoffice/event';
|
||||
import { html, nothing, ifDefined, customElement, property, state } from '@umbraco-cms/backoffice/external/lit';
|
||||
import type { UUIMenuItemEvent } from '@umbraco-cms/backoffice/external/uui';
|
||||
import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element';
|
||||
|
||||
const elementName = 'umb-entity-create-option-action';
|
||||
@customElement(elementName)
|
||||
export class UmbEntityCreateOptionActionDefaultElement<
|
||||
MetaType extends MetaEntityCreateOptionActionDefaultKind = MetaEntityCreateOptionActionDefaultKind,
|
||||
ApiType extends UmbEntityCreateOptionAction<MetaType> = UmbEntityCreateOptionAction<MetaType>,
|
||||
>
|
||||
extends UmbLitElement
|
||||
implements UmbEntityCreateOptionActionElement
|
||||
{
|
||||
#api?: ApiType;
|
||||
|
||||
// TODO: Do these need to be properties? [NL]
|
||||
@property({ type: String })
|
||||
entityType?: string | null;
|
||||
|
||||
// TODO: Do these need to be properties? [NL]
|
||||
@property({ type: String })
|
||||
public unique?: string | null;
|
||||
|
||||
@property({ attribute: false })
|
||||
public manifest?: ManifestEntityCreateOptionAction<MetaType>;
|
||||
|
||||
public set api(api: ApiType | undefined) {
|
||||
this.#api = api;
|
||||
|
||||
// TODO: Fix so when we use a HREF it does not refresh the page?
|
||||
this.#api?.getHref?.().then((href) => {
|
||||
this._href = href;
|
||||
// TODO: Do we need to update the component here? [NL]
|
||||
});
|
||||
}
|
||||
|
||||
@state()
|
||||
_href?: string;
|
||||
|
||||
override async focus() {
|
||||
await this.updateComplete;
|
||||
this.shadowRoot?.querySelector('uui-menu-item')?.focus();
|
||||
}
|
||||
|
||||
async #onClickLabel(event: UUIMenuItemEvent) {
|
||||
if (!this._href) {
|
||||
event.stopPropagation();
|
||||
await this.#api?.execute();
|
||||
}
|
||||
this.dispatchEvent(new UmbActionExecutedEvent());
|
||||
}
|
||||
|
||||
// TODO: we need to stop the regular click event from bubbling up to the table so it doesn't select the row.
|
||||
// This should probably be handled in the UUI Menu item component. so we don't dispatch a label-click event and click event at the same time.
|
||||
#onClick(event: PointerEvent) {
|
||||
event.stopPropagation();
|
||||
}
|
||||
|
||||
override render() {
|
||||
const label = this.manifest?.meta.label ? this.localize.string(this.manifest.meta.label) : this.manifest?.name;
|
||||
|
||||
return html`
|
||||
<uui-menu-item
|
||||
label=${ifDefined(this.manifest?.meta.additionalOptions ? label + '...' : label)}
|
||||
href=${ifDefined(this._href)}
|
||||
@click-label=${this.#onClickLabel}
|
||||
@click=${this.#onClick}>
|
||||
${this.manifest?.meta.icon
|
||||
? html`<umb-icon slot="icon" name="${this.manifest?.meta.icon}"></umb-icon>`
|
||||
: nothing}
|
||||
</uui-menu-item>
|
||||
`;
|
||||
}
|
||||
}
|
||||
|
||||
export { UmbEntityCreateOptionActionDefaultElement as element };
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
[elementName]: UmbEntityCreateOptionActionDefaultElement;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
export * from './types.js';
|
||||
@@ -0,0 +1,20 @@
|
||||
import type { UmbExtensionManifestKind } from '@umbraco-cms/backoffice/extension-registry';
|
||||
|
||||
export const manifests: Array<UmbExtensionManifest | UmbExtensionManifestKind> = [
|
||||
{
|
||||
type: 'kind',
|
||||
alias: 'Umb.Kind.EntityCreateOptionAction.Default',
|
||||
matchKind: 'default',
|
||||
matchType: 'entityCreateOptionAction',
|
||||
manifest: {
|
||||
type: 'entityCreateOptionAction',
|
||||
kind: 'default',
|
||||
weight: 1000,
|
||||
element: () => import('./entity-create-option-action.element.js'),
|
||||
meta: {
|
||||
icon: '',
|
||||
label: 'Default Entity Create Option Action',
|
||||
},
|
||||
},
|
||||
},
|
||||
];
|
||||
@@ -0,0 +1,44 @@
|
||||
import type {
|
||||
ManifestEntityCreateOptionAction,
|
||||
MetaEntityCreateOptionAction,
|
||||
} from '../entity-create-option-action.extension.js';
|
||||
|
||||
export interface ManifestEntityCreateOptionActionDefaultKind
|
||||
extends ManifestEntityCreateOptionAction<MetaEntityCreateOptionActionDefaultKind> {
|
||||
type: 'entityCreateOptionAction';
|
||||
kind: 'default';
|
||||
}
|
||||
|
||||
export interface MetaEntityCreateOptionActionDefaultKind extends MetaEntityCreateOptionAction {
|
||||
/**
|
||||
* 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 action requires additional input from the user.
|
||||
* A dialog will prompt the user for more information or to make a choice.
|
||||
* @type {boolean}
|
||||
* @memberof MetaEntityCreateOptionActionDefaultKind
|
||||
*/
|
||||
additionalOptions?: boolean;
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface UmbExtensionManifestMap {
|
||||
umbDefaultEntityCreateOptionActionKind: ManifestEntityCreateOptionActionDefaultKind;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
import type { UmbEntityCreateOptionActionArgs } from './types.js';
|
||||
import type { UmbEntityCreateOptionAction } from './entity-create-option-action.interface.js';
|
||||
import { UmbActionBase } from '@umbraco-cms/backoffice/action';
|
||||
|
||||
export abstract class UmbEntityCreateOptionActionBase<ArgsMetaType>
|
||||
extends UmbActionBase<UmbEntityCreateOptionActionArgs<ArgsMetaType>>
|
||||
implements UmbEntityCreateOptionAction<ArgsMetaType>
|
||||
{
|
||||
/**
|
||||
* By specifying the href, the action will act as a link.
|
||||
* The `execute` method will not be called.
|
||||
* @abstract
|
||||
* @returns {string | undefined}
|
||||
*/
|
||||
public getHref(): Promise<string | undefined> {
|
||||
return Promise.resolve(undefined);
|
||||
}
|
||||
|
||||
/**
|
||||
* By specifying the `execute` method, the action will act as a button.
|
||||
* @abstract
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
public execute(): Promise<void> {
|
||||
return Promise.resolve();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
import type { UmbControllerHostElement } from '@umbraco-cms/backoffice/controller-api';
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-empty-object-type
|
||||
export interface UmbEntityCreateOptionActionElement extends UmbControllerHostElement {}
|
||||
@@ -0,0 +1,108 @@
|
||||
import type { UmbEntityCreateOptionActionArgs } from './types.js';
|
||||
import type {
|
||||
ManifestEntityCreateOptionAction,
|
||||
MetaEntityCreateOptionAction,
|
||||
} from './entity-create-option-action.extension.js';
|
||||
import { UmbEntityContext } from '@umbraco-cms/backoffice/entity';
|
||||
import { html, customElement, property, state, css } from '@umbraco-cms/backoffice/external/lit';
|
||||
import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element';
|
||||
import type { UmbApiConstructorArgumentsMethodType } from '@umbraco-cms/backoffice/extension-api';
|
||||
|
||||
const elementName = 'umb-entity-create-option-action-list';
|
||||
@customElement(elementName)
|
||||
export class UmbEntityCreateOptionActionListElement extends UmbLitElement {
|
||||
@property({ type: String, attribute: 'entity-type' })
|
||||
public get entityType(): string | undefined {
|
||||
return this._props.entityType;
|
||||
}
|
||||
public set entityType(value: string | undefined) {
|
||||
if (value === undefined || value === this._props.entityType) return;
|
||||
this._props.entityType = value;
|
||||
this.#generateApiArgs();
|
||||
this.requestUpdate('_props');
|
||||
// Update filter:
|
||||
//const oldValue = this._filter;
|
||||
this._filter = (extension: ManifestEntityCreateOptionAction<MetaEntityCreateOptionAction>) =>
|
||||
extension.forEntityTypes.includes(value);
|
||||
//this.requestUpdate('_filter', oldValue);
|
||||
}
|
||||
|
||||
@state()
|
||||
_filter?: (extension: ManifestEntityCreateOptionAction<MetaEntityCreateOptionAction>) => boolean;
|
||||
|
||||
@property({ type: String })
|
||||
public get unique(): string | null | undefined {
|
||||
return this._props.unique;
|
||||
}
|
||||
public set unique(value: string | null | undefined) {
|
||||
if (value === this._props.unique) return;
|
||||
this._props.unique = value;
|
||||
this.#generateApiArgs();
|
||||
this.requestUpdate('_props');
|
||||
}
|
||||
|
||||
@state()
|
||||
_props: Partial<UmbEntityCreateOptionActionArgs<unknown>> = {};
|
||||
|
||||
@state()
|
||||
_apiArgs?: UmbApiConstructorArgumentsMethodType<
|
||||
ManifestEntityCreateOptionAction<MetaEntityCreateOptionAction>,
|
||||
[UmbEntityCreateOptionActionArgs<MetaEntityCreateOptionAction>]
|
||||
>;
|
||||
|
||||
#entityContext = new UmbEntityContext(this);
|
||||
|
||||
#generateApiArgs() {
|
||||
if (!this._props.entityType || this._props.unique === undefined) return;
|
||||
|
||||
this.#entityContext.setEntityType(this._props.entityType);
|
||||
this.#entityContext.setUnique(this._props.unique);
|
||||
this.#hasRenderedOnce = false;
|
||||
|
||||
this._apiArgs = (manifest: ManifestEntityCreateOptionAction<MetaEntityCreateOptionAction>) => {
|
||||
return [{ entityType: this._props.entityType!, unique: this._props.unique!, meta: manifest.meta }];
|
||||
};
|
||||
}
|
||||
|
||||
#hasRenderedOnce?: boolean;
|
||||
override render() {
|
||||
return this._filter
|
||||
? html`
|
||||
<umb-extension-with-api-slot
|
||||
type="entityAction"
|
||||
.filter=${this._filter}
|
||||
.elementProps=${this._props}
|
||||
.apiArgs=${this._apiArgs}
|
||||
.renderMethod=${(ext: any, i: number) => {
|
||||
if (!this.#hasRenderedOnce && i === 0) {
|
||||
// TODO: Replace this block:
|
||||
ext.component?.updateComplete.then(async () => {
|
||||
const menuitem = ext.component?.shadowRoot?.querySelector('uui-menu-item');
|
||||
menuitem?.updateComplete.then(async () => {
|
||||
menuitem?.shadowRoot?.querySelector('#label-button')?.focus?.();
|
||||
});
|
||||
});
|
||||
// end of block, with this, when this PR is part of UI Lib: https://github.com/umbraco/Umbraco.UI/pull/789
|
||||
// ext.component?.focus();
|
||||
this.#hasRenderedOnce = true;
|
||||
}
|
||||
return ext.component;
|
||||
}}></umb-extension-with-api-slot>
|
||||
`
|
||||
: '';
|
||||
}
|
||||
|
||||
static override styles = [
|
||||
css`
|
||||
:host {
|
||||
--uui-menu-item-flat-structure: 1;
|
||||
}
|
||||
`,
|
||||
];
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
[elementName]: UmbEntityCreateOptionActionListElement;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
import type { ManifestElementAndApi, ManifestWithDynamicConditions } from '@umbraco-cms/backoffice/extension-api';
|
||||
|
||||
export interface ManifestEntityCreateOptionAction<
|
||||
MetaType extends MetaEntityCreateOptionAction = MetaEntityCreateOptionAction,
|
||||
> extends ManifestElementAndApi,
|
||||
ManifestWithDynamicConditions<UmbExtensionConditionConfig> {
|
||||
type: 'entityCreateOptionAction';
|
||||
forEntityTypes: Array<string>;
|
||||
meta: MetaType;
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-empty-object-type
|
||||
export interface MetaEntityCreateOptionAction {}
|
||||
@@ -0,0 +1,17 @@
|
||||
import type { UmbEntityCreateOptionActionArgs } from './types.js';
|
||||
import type { UmbAction } from '@umbraco-cms/backoffice/action';
|
||||
|
||||
export interface UmbEntityCreateOptionAction<ArgsMetaType>
|
||||
extends UmbAction<UmbEntityCreateOptionActionArgs<ArgsMetaType>> {
|
||||
/**
|
||||
* The href location, the action will act as a link.
|
||||
* @returns {Promise<string | undefined>}
|
||||
*/
|
||||
getHref(): Promise<string | undefined>;
|
||||
|
||||
/**
|
||||
* The `execute` method, the action will act as a button.
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
execute(): Promise<void>;
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
export * from './default/index.js';
|
||||
export * from './entity-create-option-action-base.js';
|
||||
export * from './entity-create-option-action-list.element.js';
|
||||
export * from './entity-create-option-action.extension.js';
|
||||
export * from './entity-create-option-action.interface.js';
|
||||
export * from './types.js';
|
||||
export type * from './entity-create-option-action-element.interface.js';
|
||||
@@ -0,0 +1,4 @@
|
||||
import { manifests as defaultEntityActionManifests } from './default/manifests.js';
|
||||
import type { UmbExtensionManifestKind } from '@umbraco-cms/backoffice/extension-registry';
|
||||
|
||||
export const manifests: Array<UmbExtensionManifest | UmbExtensionManifestKind> = [...defaultEntityActionManifests];
|
||||
@@ -0,0 +1,5 @@
|
||||
import type { UmbEntityModel } from '@umbraco-cms/backoffice/entity';
|
||||
|
||||
export interface UmbEntityCreateOptionActionArgs<MetaArgsType> extends UmbEntityModel {
|
||||
meta: MetaArgsType;
|
||||
}
|
||||
Reference in New Issue
Block a user