Add dropdown control

This commit is contained in:
Sipke Schoorstra 2021-03-04 13:53:10 +01:00
parent 9c6c141516
commit 3b7e5aff57
14 changed files with 145 additions and 56 deletions

View file

@ -19,7 +19,7 @@ namespace Elsa.Activities.Entity
public string? EntityName { get; set; }
[ActivityProperty(
UIHint = ActivityPropertyUIHints.DropdownList,
UIHint = ActivityPropertyUIHints.Dropdown,
Hint = "The Entity Changed Action to observe. Matches any action if no value is specified.",
Options = new[] { "Added", "Updated", "Deleted" }
)]

View file

@ -47,7 +47,7 @@ namespace Elsa.Activities.Http
/// The HTTP method to use.
/// </summary>
[ActivityProperty(
UIHint = ActivityPropertyUIHints.DropdownList,
UIHint = ActivityPropertyUIHints.Dropdown,
Hint = "The HTTP method to use when making the request.",
Options = new[] { "GET", "POST", "PUT", "DELETE", "PATCH", "OPTIONS", "HEAD" }
)]
@ -63,7 +63,7 @@ namespace Elsa.Activities.Http
/// The Content Type header to send along with the request body.
/// </summary>
[ActivityProperty(
UIHint = ActivityPropertyUIHints.DropdownList,
UIHint = ActivityPropertyUIHints.Dropdown,
Hint = "The content type to send with the request.",
Options = new[] { "text/plain", "text/html", "application/json", "application/xml" }
)]

View file

@ -14,7 +14,7 @@ namespace Elsa.Activities.Http
{
[Action(
Category = "HTTP",
DisplayName = "Write HTTP Response",
DisplayName = "HTTP Response",
Description = "Write an HTTP response.",
Outcomes = new[] { OutcomeNames.Done }
)]
@ -34,7 +34,7 @@ namespace Elsa.Activities.Http
/// The HTTP status code to return.
/// </summary>
[ActivityProperty(
UIHint = ActivityPropertyUIHints.DropdownList,
UIHint = ActivityPropertyUIHints.Dropdown,
Hint = "The HTTP status code to write.",
Options = new[] { HttpStatusCode.OK, HttpStatusCode.Created, HttpStatusCode.Accepted, HttpStatusCode.NoContent, HttpStatusCode.Redirect, HttpStatusCode.BadRequest, HttpStatusCode.NotFound, HttpStatusCode.Conflict }
)]
@ -50,7 +50,7 @@ namespace Elsa.Activities.Http
/// The Content-Type header to send along with the response.
/// </summary>
[ActivityProperty(
UIHint = ActivityPropertyUIHints.DropdownList,
UIHint = ActivityPropertyUIHints.Dropdown,
Hint = "The HTTP content type header to write.",
Options = new[]{ "text/plain", "text/html", "application/json", "application/xml" }
)]

View file

@ -7,7 +7,7 @@ namespace Elsa.Design
public const string Checkbox = "checkbox";
public const string CheckList = "check-list";
public const string RadioList = "radio-list";
public const string DropdownList = "dropdown-list";
public const string Dropdown = "dropdown";
/// <summary>
/// A list that allows the user to enter zero or more values.

View file

@ -37,7 +37,7 @@ namespace Elsa.Activities.ControlFlow
}
[ActivityProperty(
UIHint = ActivityPropertyUIHints.DropdownList,
UIHint = ActivityPropertyUIHints.Dropdown,
Hint = "WaitAll: wait for all incoming activities to have executed. WaitAny: continue execution as soon as any of the incoming activity has executed.",
Options = new[] { "WaitAll", "WaitAny" }
)]

View file

@ -79,7 +79,7 @@ namespace Elsa.Metadata
return ActivityPropertyUIHints.SingleLine;
if (typeof(IEnumerable).IsAssignableFrom(type))
return ActivityPropertyUIHints.DropdownList;
return ActivityPropertyUIHints.Dropdown;
return ActivityPropertyUIHints.SingleLine;
}

View file

@ -27,6 +27,10 @@ export namespace Components {
"activityModel": ActivityModel;
"icon": string;
}
interface ElsaDropdownProperty {
"propertyDescriptor": ActivityPropertyDescriptor;
"propertyModel": ActivityDefinitionProperty;
}
interface ElsaModalDialog {
"hide": (animate: boolean) => Promise<void>;
"show": (animate: boolean) => Promise<void>;
@ -95,6 +99,12 @@ declare global {
prototype: HTMLElsaDesignerTreeActivityElement;
new (): HTMLElsaDesignerTreeActivityElement;
};
interface HTMLElsaDropdownPropertyElement extends Components.ElsaDropdownProperty, HTMLStencilElement {
}
var HTMLElsaDropdownPropertyElement: {
prototype: HTMLElsaDropdownPropertyElement;
new (): HTMLElsaDropdownPropertyElement;
};
interface HTMLElsaModalDialogElement extends Components.ElsaModalDialog, HTMLStencilElement {
}
var HTMLElsaModalDialogElement: {
@ -138,6 +148,7 @@ declare global {
"elsa-checkbox-property": HTMLElsaCheckboxPropertyElement;
"elsa-designer-tree": HTMLElsaDesignerTreeElement;
"elsa-designer-tree-activity": HTMLElsaDesignerTreeActivityElement;
"elsa-dropdown-property": HTMLElsaDropdownPropertyElement;
"elsa-modal-dialog": HTMLElsaModalDialogElement;
"elsa-monaco": HTMLElsaMonacoElement;
"elsa-text-property": HTMLElsaTextPropertyElement;
@ -169,6 +180,10 @@ declare namespace LocalJSX {
"onEdit-activity"?: (event: CustomEvent<ActivityModel>) => void;
"onRemove-activity"?: (event: CustomEvent<ActivityModel>) => void;
}
interface ElsaDropdownProperty {
"propertyDescriptor"?: ActivityPropertyDescriptor;
"propertyModel"?: ActivityDefinitionProperty;
}
interface ElsaModalDialog {
}
interface ElsaMonaco {
@ -203,6 +218,7 @@ declare namespace LocalJSX {
"elsa-checkbox-property": ElsaCheckboxProperty;
"elsa-designer-tree": ElsaDesignerTree;
"elsa-designer-tree-activity": ElsaDesignerTreeActivity;
"elsa-dropdown-property": ElsaDropdownProperty;
"elsa-modal-dialog": ElsaModalDialog;
"elsa-monaco": ElsaMonaco;
"elsa-text-property": ElsaTextProperty;
@ -221,6 +237,7 @@ declare module "@stencil/core" {
"elsa-checkbox-property": LocalJSX.ElsaCheckboxProperty & JSXBase.HTMLAttributes<HTMLElsaCheckboxPropertyElement>;
"elsa-designer-tree": LocalJSX.ElsaDesignerTree & JSXBase.HTMLAttributes<HTMLElsaDesignerTreeElement>;
"elsa-designer-tree-activity": LocalJSX.ElsaDesignerTreeActivity & JSXBase.HTMLAttributes<HTMLElsaDesignerTreeActivityElement>;
"elsa-dropdown-property": LocalJSX.ElsaDropdownProperty & JSXBase.HTMLAttributes<HTMLElsaDropdownPropertyElement>;
"elsa-modal-dialog": LocalJSX.ElsaModalDialog & JSXBase.HTMLAttributes<HTMLElsaModalDialogElement>;
"elsa-monaco": LocalJSX.ElsaMonaco & JSXBase.HTMLAttributes<HTMLElsaMonacoElement>;
"elsa-text-property": LocalJSX.ElsaTextProperty & JSXBase.HTMLAttributes<HTMLElsaTextPropertyElement>;

View file

@ -57,7 +57,7 @@ export class ElsaCheckListProperty {
<div class="flex items-center h-5">
<input id={inputId} type="checkbox" checked={isSelected} value={option} onChange={e => this.onCheckChanged(e)} class="focus:ring-blue-500 h-4 w-4 text-blue-600 border-gray-300 rounded"/>
</div>
<div class="ml-3 text-sm">
<div class="ml-3 mt-1 text-sm">
<label htmlFor={inputId} class="font-medium text-gray-700">{option}</label>
</div>
</div>

View file

@ -0,0 +1,48 @@
import {Component, h, Prop, State} from '@stencil/core';
import {ActivityDefinitionProperty, ActivityPropertyDescriptor} from "../../../../models";
@Component({
tag: 'elsa-dropdown-property',
styleUrl: 'elsa-dropdown-property.css',
shadow: false,
})
export class ElsaDropdownProperty {
@Prop() propertyDescriptor: ActivityPropertyDescriptor;
@Prop() propertyModel: ActivityDefinitionProperty;
@State() currentValue?: string;
async componentWillLoad() {
this.currentValue = this.propertyModel.expression || '';
}
onChanged(e: Event) {
const select = (e.target as HTMLSelectElement);
this.currentValue = select.value;
}
render() {
const propertyDescriptor = this.propertyDescriptor;
const propertyName = propertyDescriptor.name;
const fieldId = propertyName;
const fieldName = propertyName;
const fieldLabel = propertyDescriptor.label || propertyName;
const fieldHint = propertyDescriptor.hint;
const options = propertyDescriptor.options as Array<any>;
const value = this.currentValue;
return (
<div>
<label htmlFor={fieldId} class="block text-sm font-medium text-gray-700">
{fieldLabel}
</label>
<select id={fieldId} name={fieldName} class="mt-1 block focus:ring-blue-500 focus:border-blue-500 w-full shadow-sm sm:max-w-xs sm:text-sm border-gray-300 rounded-md">
{options.map((option, index) => <option value={option} selected={option === value}>{option}</option>)}
</select>
{fieldHint ? <p class="mt-2 text-sm text-gray-500">{fieldHint}</p> : undefined}
</div>
)
}
}

View file

@ -73,55 +73,51 @@ export class ElsaTextProperty {
const property = this.propertyModel;
const value = this.currentValue;
return (
<div>
<label htmlFor={fieldId} class="block text-sm font-medium text-gray-700">
{fieldLabel}
</label>
<div class="mb-2">
<div class="sm:hidden">
<label htmlFor="tabs" class="sr-only">Select a tab</label>
<select id="tabs" name="tabs" onChange={e => {
this.onSyntaxListChange(e)
}} class="block w-full pl-3 pr-10 py-2 text-base border-gray-300 focus:outline-none focus:ring-blue-500 focus:border-blue-500 sm:text-sm rounded-md">
{syntaxes.map(syntax => {
const isSelected = syntax == selectedSyntax;
return (
<option selected={isSelected}>{syntax}</option>
);
})}
</select>
</div>
<div class="hidden sm:block">
<nav class="flex flex-row-reverse" aria-label="Tabs">
{syntaxes.map(syntax => {
const isSelected = syntax == selectedSyntax;
const className = isSelected ? 'bg-blue-100 text-blue-700' : 'text-gray-500 hover:text-gray-700';
return <div>
return (
<a href="#" onClick={e => {
this.onSyntaxSelect(e, syntax)
}} data-syntax={syntax} class={`${className} px-3 py-2 font-medium text-sm rounded-md`}>
<div class="flex">
<div class="">
<label htmlFor={fieldId} class="block text-sm font-medium text-gray-700">
{fieldLabel}
</label>
</div>
<div class="flex-1">
<div class="">
<div class="sm:hidden">
<label htmlFor="tabs" class="sr-only">Select a tab</label>
<select id="tabs" name="tabs" onChange={e => this.onSyntaxListChange(e)} class="block w-full pl-3 pr-10 py-2 text-base border-gray-300 focus:outline-none focus:ring-blue-500 focus:border-blue-500 sm:text-sm rounded-md">
{syntaxes.map(syntax => {
return <option selected={syntax == selectedSyntax}>{syntax}</option>;
})}
</select>
</div>
<div class="hidden sm:block">
<nav class="flex flex-row-reverse" aria-label="Tabs">
{syntaxes.map(syntax => {
const isSelected = syntax == selectedSyntax;
const className = isSelected ? 'bg-blue-100 text-blue-700' : 'text-gray-500 hover:text-gray-700';
return <a href="#" onClick={e => this.onSyntaxSelect(e, syntax)} data-syntax={syntax} class={`${className} px-3 py-2 font-medium text-sm rounded-md`}>
{syntax}
</a>
);
})}
</nav>
</a>;
})}
</nav>
</div>
</div>
</div>
<div class="">
<elsa-monaco value={value}
language={monacoLanguage}
editor-height={this.editorHeight}
single-line={this.singleLineMode}
onValueChanged={e => this.onMonacoValueChanged(e.detail)}
ref={el => this.monacoEditor = el}/>
</div>
{fieldHint ? <p class="mt-2 text-sm text-gray-500">{fieldHint}</p> : undefined}
<input type="hidden" name={fieldName} value={value}/>
<input type="hidden" name={syntaxFieldName} value={selectedSyntax}/>
</div>
)
<div class="mt-1">
<elsa-monaco value={value}
language={monacoLanguage}
editor-height={this.editorHeight}
single-line={this.singleLineMode}
onValueChanged={e => this.onMonacoValueChanged(e.detail)}
ref={el => this.monacoEditor = el}/>
</div>
{fieldHint ? <p class="mt-2 text-sm text-gray-500">{fieldHint}</p> : undefined}
<input type="hidden" name={fieldName} value={value}/>
<input type="hidden" name={syntaxFieldName} value={selectedSyntax}/>
</div>
}
}

View file

@ -0,0 +1,23 @@
import {PropertyDisplayDriver} from "../services/property-display-driver";
import {ActivityModel, ActivityPropertyDescriptor} from "../models";
import {h} from "@stencil/core";
import {getProperty, setActivityModelProperty} from "../utils/utils";
export class DropdownDriver implements PropertyDisplayDriver {
display(activity: ActivityModel, property: ActivityPropertyDescriptor) {
const key = `${activity.activityId}:${property.name}`;
const prop = getProperty(activity.properties, property.name);
return (
<div class="sm:col-span-6">
<elsa-dropdown-property propertyDescriptor={property} propertyModel={prop}/>
</div>
)
}
update(activity: ActivityModel, property: ActivityPropertyDescriptor, form: FormData) {
const value = form.get(property.name) as string;
setActivityModelProperty(activity, property.name, value, 'Literal');
}
}

View file

@ -3,3 +3,4 @@ export * from './single-line-driver';
export * from './multiline-driver';
export * from './check-list-driver';
export * from './checkbox-driver';
export * from './dropdown-driver';

View file

@ -1,6 +1,6 @@
import {ActivityModel, ActivityPropertyDescriptor} from "../models/";
import {PropertyDisplayDriver} from "./property-display-driver";
import {CheckBoxDriver, CheckListDriver, MultilineDriver, NullPropertyDriver, SingleLineDriver} from "../property-display-drivers";
import {CheckBoxDriver, CheckListDriver, DropdownDriver, MultilineDriver, NullPropertyDriver, SingleLineDriver} from "../property-display-drivers";
import {Map} from '../utils/utils';
export type PropertyDisplayDriverMap = Map<() => PropertyDisplayDriver>;
@ -14,7 +14,8 @@ export class PropertyDisplayManager {
'single-line': () => new SingleLineDriver(),
'multi-line': () => new MultilineDriver(),
'check-list': () => new CheckListDriver(),
'checkbox': () => new CheckBoxDriver()
'checkbox': () => new CheckBoxDriver(),
'dropdown': () => new DropdownDriver()
};
}