register and render property actions

This commit is contained in:
Mads Rasmussen
2022-06-21 10:38:36 +02:00
parent 06426c28ef
commit 11ac9538d1
6 changed files with 205 additions and 1 deletions

View File

@@ -0,0 +1,38 @@
import { UUITextStyles } from '@umbraco-ui/uui';
import { CSSResultGroup, html, LitElement } from 'lit';
import { customElement, property, state } from 'lit/decorators.js';
import { createExtensionElement, UmbExtensionManifestPropertyAction } from '../../core/extension';
@customElement('umb-node-property-action')
export class UmbNodePropertyAction extends LitElement {
static styles: CSSResultGroup = [
UUITextStyles
];
private _propertyAction?: UmbExtensionManifestPropertyAction;
@property({ type: Object })
public get propertyAction(): UmbExtensionManifestPropertyAction | undefined {
return this._propertyAction;
}
public set propertyAction(value: UmbExtensionManifestPropertyAction | undefined) {
this._propertyAction = value;
this._createElement();
}
@state()
private _element?: HTMLElement;
private async _createElement () {
if (!this.propertyAction) return;
try {
this._element = await createExtensionElement(this.propertyAction);
} catch (error) {
// TODO: loading JS failed so we should do some nice UI. (This does only happen if extension has a js prop, otherwise we concluded that no source was needed resolved the load.)
}
}
render () {
return html`${this._element}`;
}
}

View File

@@ -0,0 +1,93 @@
import { UUITextStyles } from '@umbraco-ui/uui';
import { css, CSSResultGroup, html, LitElement } from 'lit';
import { customElement, property, state } from 'lit/decorators.js';
import { Subscription, map } from 'rxjs';
import { UmbContextConsumerMixin } from '../../core/context';
import { UmbExtensionManifestPropertyAction, UmbExtensionRegistry } from '../../core/extension';
import './node-property-action.element';
@customElement('umb-node-property-actions')
export class UmbNodePropertyActions extends UmbContextConsumerMixin(LitElement) {
static styles: CSSResultGroup = [
UUITextStyles,
css`
#dropdown {
background-color: white;
border-radius: var(--uui-border-radius);
width: 100%;
height: 100%;
box-sizing: border-box;
box-shadow: var(--uui-shadow-depth-3);
min-width: 200px;
color: black; /* Change to variable */
}
`,
];
@property()
public propertyEditorUIAlias = '';
@state()
private _actions: Array<UmbExtensionManifestPropertyAction> = [];
@state()
private _open = false;
private _extensionRegistry?: UmbExtensionRegistry;
private _subscription?: Subscription;
constructor () {
super();
this.consumeContext('umbExtensionRegistry', (extensionRegistry: UmbExtensionRegistry) => {
this._extensionRegistry = extensionRegistry;
this._usePropertyActions();
});
}
private _usePropertyActions () {
this._subscription?.unsubscribe();
this._extensionRegistry?.extensionsOfType('propertyAction')
.pipe(
map(propertyActions => propertyActions.filter(propertyAction => propertyAction.meta.propertyEditors.includes(this.propertyEditorUIAlias))))
.subscribe(extensions => {
this._actions = extensions;
});
}
disconnectedCallback () {
super.disconnectedCallback();
this._subscription?.unsubscribe();
}
render () {
return html`
${ this._actions?.length > 0 ? html`
<uui-popover
.open=${this._open}
placement="bottom-start"
@close="${() => this._open = false}">
<uui-button
slot="trigger"
look="secondary"
label="More"
@click="${() => this._open = true}"
compact>
<uui-symbol-more></uui-symbol-more>
</uui-button>
<div slot="popover" id="dropdown">
${this._actions.map(
action => html`
<umb-node-property-action .propertyAction=${action}></umb-node-property-action>
`
)}
</div>
</uui-popover>
` : '' }
`;
}
}

View File

@@ -8,6 +8,8 @@ import { createExtensionElement, UmbExtensionManifest, UmbExtensionRegistry } fr
import { UmbDataTypeStore } from '../../core/stores/data-type.store';
import { DataTypeEntity } from '../../mocks/data/content.data';
import './node-property-actions.element';
@customElement('umb-node-property')
class UmbNodeProperty extends UmbContextConsumerMixin(LitElement) {
static styles = [
@@ -141,11 +143,16 @@ class UmbNodeProperty extends UmbContextConsumerMixin(LitElement) {
this._dataTypeSubscription?.unsubscribe();
}
private _renderPropertyActions () {
return html`${ this._dataType ? html`<umb-node-property-actions .propertyEditorUIAlias="${this._dataType.propertyEditorUIAlias}"></umb-node-property-actions>`: '' }`;
}
render() {
return html`
<umb-editor-property-layout>
<div slot="header">
<uui-label>${this.property.label}</uui-label>
${ this._renderPropertyActions() }
<p>${this.property.description}</p>
</div>
<div slot="editor">${this._element}</div>

View File

@@ -0,0 +1,44 @@
import { html, LitElement } from 'lit';
import { customElement, property } from 'lit/decorators.js';
import { UmbContextConsumerMixin } from '../../core/context';
import { UmbNotificationService } from '../../core/services/notification.service';
// eslint-disable-next-line @typescript-eslint/no-empty-interface
interface UmbPropertyActionElement {
value: string;
}
@customElement('umb-property-action-copy')
export default class UmbPropertyActionCopy extends UmbContextConsumerMixin(LitElement) implements UmbPropertyActionElement {
@property()
value = '';
private _notificationService?: UmbNotificationService;
constructor () {
super();
this.consumeContext('umbProperty', (property) => {
console.log('PROPERTY', property);
});
this.consumeContext('umbNotificationService', (notificationService: UmbNotificationService) => {
this._notificationService = notificationService;
});
}
private _handleLabelClick () {
this._notificationService?.peek('Copied to clipboard');
}
render() {
return html`<uui-menu-item label="Copy" @click-label="${this._handleLabelClick}"></uui-menu-item>`;
}
}
declare global {
interface HTMLElementTagNameMap {
'umb-property-action-copy': UmbPropertyActionCopy;
}
}

View File

@@ -1,7 +1,7 @@
import { BehaviorSubject, map, Observable } from 'rxjs';
// TODO: how do we want to type extensions?
export type UmbExtensionType = 'startUp' | 'section' | 'propertyEditorUI' | 'dashboard';
export type UmbExtensionType = 'startUp' | 'section' | 'propertyEditorUI' | 'propertyAction' | 'dashboard';
export type UmbExtensionManifestJSModel = {
elementName?: string;
@@ -41,6 +41,16 @@ export type UmbExtensionManifestPropertyEditor = {
meta: UmbManifestPropertyEditorMeta;
} & UmbExtensionManifestBase;
// Property Actions
export type UmbExtensionManifestPropertyAction = {
type: 'propertyAction';
meta: UmbManifestPropertyActionMeta;
} & UmbExtensionManifestBase;
export type UmbManifestPropertyActionMeta = {
propertyEditors: Array<string>;
};
// Dashboard:
export type UmbManifestDashboardMeta = {
sections: Array<string>;
@@ -67,6 +77,7 @@ export type UmbExtensionManifestCore =
| UmbExtensionManifestSection
| UmbExtensionManifestDashboard
| UmbExtensionManifestPropertyEditor
| UmbExtensionManifestPropertyAction
| UmbExtensionManifestEditorView;
// the 'Other' manifest type:
@@ -107,6 +118,7 @@ export class UmbExtensionRegistry {
extensionsOfType(type: 'section'): Observable<Array<UmbExtensionManifestSection>>;
extensionsOfType(type: 'dashboard'): Observable<Array<UmbExtensionManifestDashboard>>;
extensionsOfType(type: 'propertyEditor'): Observable<Array<UmbExtensionManifestPropertyEditor>>;
extensionsOfType(type: 'propertyAction'): Observable<Array<UmbExtensionManifestPropertyAction>>;
extensionsOfType(type: UmbExtensionManifestCoreTypes): Observable<Array<UmbExtensionManifestCore>>;
extensionsOfType(type: string): Observable<Array<UmbExtensionManifestOther>>;
extensionsOfType<T extends UmbExtensionManifestBase>(type: string): Observable<Array<T>>;

View File

@@ -111,5 +111,15 @@ export const internalManifests: Array<UmbExtensionManifestCore> = [
weight: 90,
icon: 'info',
}
},
{
type: 'propertyAction',
alias: 'Umb.PropertyAction.Copy',
name: 'Copy',
elementName: 'umb-property-action-copy',
js: () => import('./backoffice/property-actions/property-action-copy.element'),
meta: {
propertyEditors: ['Umb.PropertyEditorUI.Text'],
}
}
];