Introduce generic component for rendering either HTML string, JSX node or HTML node

This commit is contained in:
Sipke Schoorstra 2021-07-27 09:18:29 +02:00
parent 9182af3d1f
commit 969c78531d
3 changed files with 50 additions and 24 deletions

View file

@ -8,6 +8,7 @@ import { HTMLStencilElement, JSXBase } from "@stencil/core/internal";
import { ActivityDefinitionProperty, ActivityDescriptor, ActivityModel, ActivityPropertyDescriptor, ElsaStudio, OrderBy, SelectListItem, VersionOptions, WorkflowBlueprint, WorkflowDefinition, WorkflowExecutionLogRecord, WorkflowModel, WorkflowStatus } from "./models";
import { LocationSegments, MatchResults, RouterHistory } from "@stencil/router";
import { MenuItem } from "./components/controls/elsa-context-menu/models";
import { VNode } from "@stencil/core";
import { ActivityContextMenuState, LayoutDirection, WorkflowDesignerMode } from "./components/designers/tree/elsa-designer-tree/models";
import { DropdownButtonItem, DropdownButtonOrigin } from "./components/controls/elsa-dropdown-button/models";
import { MonacoValueChangedArgs } from "./components/controls/elsa-monaco/elsa-monaco";
@ -39,6 +40,9 @@ export namespace Components {
"history": RouterHistory;
"menuItems": Array<MenuItem>;
}
interface ElsaControl {
"content": VNode | string | Element;
}
interface ElsaDesignerTree {
"activityBorderColor"?: (activity: ActivityModel) => string;
"activityContextMenu"?: ActivityContextMenuState;
@ -331,6 +335,12 @@ declare global {
prototype: HTMLElsaContextMenuElement;
new (): HTMLElsaContextMenuElement;
};
interface HTMLElsaControlElement extends Components.ElsaControl, HTMLStencilElement {
}
var HTMLElsaControlElement: {
prototype: HTMLElsaControlElement;
new (): HTMLElsaControlElement;
};
interface HTMLElsaDesignerTreeElement extends Components.ElsaDesignerTree, HTMLStencilElement {
}
var HTMLElsaDesignerTreeElement: {
@ -596,6 +606,7 @@ declare global {
"elsa-checkbox-property": HTMLElsaCheckboxPropertyElement;
"elsa-confirm-dialog": HTMLElsaConfirmDialogElement;
"elsa-context-menu": HTMLElsaContextMenuElement;
"elsa-control": HTMLElsaControlElement;
"elsa-designer-tree": HTMLElsaDesignerTreeElement;
"elsa-dropdown-button": HTMLElsaDropdownButtonElement;
"elsa-dropdown-property": HTMLElsaDropdownPropertyElement;
@ -663,6 +674,9 @@ declare namespace LocalJSX {
"history"?: RouterHistory;
"menuItems"?: Array<MenuItem>;
}
interface ElsaControl {
"content"?: VNode | string | Element;
}
interface ElsaDesignerTree {
"activityBorderColor"?: (activity: ActivityModel) => string;
"activityContextMenu"?: ActivityContextMenuState;
@ -920,6 +934,7 @@ declare namespace LocalJSX {
"elsa-checkbox-property": ElsaCheckboxProperty;
"elsa-confirm-dialog": ElsaConfirmDialog;
"elsa-context-menu": ElsaContextMenu;
"elsa-control": ElsaControl;
"elsa-designer-tree": ElsaDesignerTree;
"elsa-dropdown-button": ElsaDropdownButton;
"elsa-dropdown-property": ElsaDropdownProperty;
@ -975,6 +990,7 @@ declare module "@stencil/core" {
"elsa-checkbox-property": LocalJSX.ElsaCheckboxProperty & JSXBase.HTMLAttributes<HTMLElsaCheckboxPropertyElement>;
"elsa-confirm-dialog": LocalJSX.ElsaConfirmDialog & JSXBase.HTMLAttributes<HTMLElsaConfirmDialogElement>;
"elsa-context-menu": LocalJSX.ElsaContextMenu & JSXBase.HTMLAttributes<HTMLElsaContextMenuElement>;
"elsa-control": LocalJSX.ElsaControl & JSXBase.HTMLAttributes<HTMLElsaControlElement>;
"elsa-designer-tree": LocalJSX.ElsaDesignerTree & JSXBase.HTMLAttributes<HTMLElsaDesignerTreeElement>;
"elsa-dropdown-button": LocalJSX.ElsaDropdownButton & JSXBase.HTMLAttributes<HTMLElsaDropdownButtonElement>;
"elsa-dropdown-property": LocalJSX.ElsaDropdownProperty & JSXBase.HTMLAttributes<HTMLElsaDropdownPropertyElement>;

View file

@ -0,0 +1,32 @@
import {Component, h, Host, Prop, VNode} from '@stencil/core';
@Component({
tag: 'elsa-control',
shadow: false,
})
export class ElsaControl {
@Prop() content: VNode | string | Element;
el: HTMLElement;
render() {
const content: any = this.content;
if(typeof content === 'string')
return <Host innerHTML={content}/>;
if(!!content.tagName)
return <Host ref={el => this.el = el}/>;
return (
<Host>{content}</Host>
);
}
componentDidRender(){
if(!this.el)
return;
const content = this.content as HTMLElement;
this.el.append(content);
}
}

View file

@ -36,14 +36,12 @@ export class ElsaActivityEditorModal {
form: HTMLFormElement;
formContext: FormContext;
renderProps: ActivityEditorRenderProps = {};
propertyElements: Array<any> = [];
// Force a new key every time we show the editor to make sure Stencil creates new components.
// This prevents the issue where the designer has e.g. one activity where the user edits the properties, cancels out, then opens the editor again, seeing the entered value still there.
timestamp: Date = new Date();
connectedCallback() {
this.propertyElements = [];
eventBus.on(EventTypes.ShowActivityEditor, this.onShowActivityEditor);
}
@ -116,8 +114,6 @@ export class ElsaActivityEditorModal {
};
eventBus.emit(EventTypes.ActivityEditorDisplaying, this, this.renderProps);
this.propertyElements = [];
}
async onCancelClick() {
@ -148,16 +144,7 @@ export class ElsaActivityEditorModal {
this.timestamp = new Date();
await this.dialog.show(animate);
};
componentDidRender() {
for (const item of this.propertyElements) {
const container: HTMLDivElement = item.host;
const input: HTMLElement = item.input;
container.append(input);
}
}
render() {
const renderProps = this.renderProps;
const activityDescriptor: ActivityDescriptor = renderProps.activityDescriptor;
@ -168,7 +155,6 @@ export class ElsaActivityEditorModal {
const t = this.t;
return (
<Host class="elsa-block">
<elsa-modal-dialog ref={el => this.dialog = el}>
<div slot="content" class="elsa-py-8 elsa-pb-0">
@ -310,15 +296,7 @@ export class ElsaActivityEditorModal {
renderPropertyEditor(activity: ActivityModel, property: ActivityPropertyDescriptor) {
const key = `activity-property-input:${activity.activityId}:${property.name}`;
const display = propertyDisplayManager.display(activity, property);
const html = typeof (display) == 'string' ? display as string : null;
const element = !!display.tagName ? display as HTMLElement : null;
const jsx = !html && !element ? display : null;
return <div key={key} class="sm:elsa-col-span-6" innerHTML={html} ref={el => {
if (!!element && !!el) {
this.propertyElements.push({host: el, input: element});
}
}}>{jsx}</div>;
return <elsa-control key={key} class="sm:elsa-col-span-6" content={display} />;
}
getHiddenClass(tab: string) {