Implement multi-text input control
This commit is contained in:
parent
d172470c00
commit
a949fa2c14
|
|
@ -5,7 +5,7 @@
|
|||
* It contains typing information for all components that exist in this project.
|
||||
*/
|
||||
import { HTMLStencilElement, JSXBase } from "@stencil/core/internal";
|
||||
import { ActionDefinition, ActionInvokedArgs, Activity, ActivityDescriptor, ActivitySelectedArgs, ContainerSelectedArgs, GraphUpdatedArgs, TabChangedArgs, TabDefinition, Trigger, TriggerDescriptor, Workflow, WorkflowInstance, WorkflowInstanceSummary, WorkflowSummary } from "./models";
|
||||
import { ActionDefinition, ActionInvokedArgs, Activity, ActivityDescriptor, ActivitySelectedArgs, ContainerSelectedArgs, GraphUpdatedArgs, SelectListItem, TabChangedArgs, TabDefinition, Trigger, TriggerDescriptor, Workflow, WorkflowInstance, WorkflowInstanceSummary, WorkflowSummary } from "./models";
|
||||
import { ActivityUpdatedArgs, DeleteActivityRequestedArgs } from "./components/designer/activity-properties-editor/activity-properties-editor";
|
||||
import { AddActivityArgs } from "./components/designer/canvas/canvas";
|
||||
import { NodeInputContext } from "./services/node-input-driver";
|
||||
|
|
@ -65,6 +65,18 @@ export namespace Components {
|
|||
"selectedTabIndex"?: number;
|
||||
"tabs": Array<TabDefinition>;
|
||||
}
|
||||
interface ElsaInputTags {
|
||||
"fieldId"?: string;
|
||||
"placeHolder"?: string;
|
||||
"values"?: Array<string>;
|
||||
}
|
||||
interface ElsaInputTagsDropdown {
|
||||
"dropdownValues"?: Array<SelectListItem>;
|
||||
"fieldId"?: string;
|
||||
"fieldName"?: string;
|
||||
"placeHolder"?: string;
|
||||
"values"?: Array<string | SelectListItem>;
|
||||
}
|
||||
interface ElsaModalDialog {
|
||||
"actions": Array<ActionDefinition>;
|
||||
"hide": (animate?: boolean) => Promise<void>;
|
||||
|
|
@ -74,6 +86,9 @@ export namespace Components {
|
|||
interface ElsaMultiLineInput {
|
||||
"inputContext": NodeInputContext;
|
||||
}
|
||||
interface ElsaMultiTextInput {
|
||||
"inputContext": NodeInputContext;
|
||||
}
|
||||
interface ElsaPager {
|
||||
"page": number;
|
||||
"pageSize": number;
|
||||
|
|
@ -203,6 +218,18 @@ declare global {
|
|||
prototype: HTMLElsaFormPanelElement;
|
||||
new (): HTMLElsaFormPanelElement;
|
||||
};
|
||||
interface HTMLElsaInputTagsElement extends Components.ElsaInputTags, HTMLStencilElement {
|
||||
}
|
||||
var HTMLElsaInputTagsElement: {
|
||||
prototype: HTMLElsaInputTagsElement;
|
||||
new (): HTMLElsaInputTagsElement;
|
||||
};
|
||||
interface HTMLElsaInputTagsDropdownElement extends Components.ElsaInputTagsDropdown, HTMLStencilElement {
|
||||
}
|
||||
var HTMLElsaInputTagsDropdownElement: {
|
||||
prototype: HTMLElsaInputTagsDropdownElement;
|
||||
new (): HTMLElsaInputTagsDropdownElement;
|
||||
};
|
||||
interface HTMLElsaModalDialogElement extends Components.ElsaModalDialog, HTMLStencilElement {
|
||||
}
|
||||
var HTMLElsaModalDialogElement: {
|
||||
|
|
@ -215,6 +242,12 @@ declare global {
|
|||
prototype: HTMLElsaMultiLineInputElement;
|
||||
new (): HTMLElsaMultiLineInputElement;
|
||||
};
|
||||
interface HTMLElsaMultiTextInputElement extends Components.ElsaMultiTextInput, HTMLStencilElement {
|
||||
}
|
||||
var HTMLElsaMultiTextInputElement: {
|
||||
prototype: HTMLElsaMultiTextInputElement;
|
||||
new (): HTMLElsaMultiTextInputElement;
|
||||
};
|
||||
interface HTMLElsaPagerElement extends Components.ElsaPager, HTMLStencilElement {
|
||||
}
|
||||
var HTMLElsaPagerElement: {
|
||||
|
|
@ -332,8 +365,11 @@ declare global {
|
|||
"elsa-dropdown-input": HTMLElsaDropdownInputElement;
|
||||
"elsa-flowchart": HTMLElsaFlowchartElement;
|
||||
"elsa-form-panel": HTMLElsaFormPanelElement;
|
||||
"elsa-input-tags": HTMLElsaInputTagsElement;
|
||||
"elsa-input-tags-dropdown": HTMLElsaInputTagsDropdownElement;
|
||||
"elsa-modal-dialog": HTMLElsaModalDialogElement;
|
||||
"elsa-multi-line-input": HTMLElsaMultiLineInputElement;
|
||||
"elsa-multi-text-input": HTMLElsaMultiTextInputElement;
|
||||
"elsa-pager": HTMLElsaPagerElement;
|
||||
"elsa-panel": HTMLElsaPanelElement;
|
||||
"elsa-radio-list-input": HTMLElsaRadioListInputElement;
|
||||
|
|
@ -397,6 +433,20 @@ declare namespace LocalJSX {
|
|||
"selectedTabIndex"?: number;
|
||||
"tabs"?: Array<TabDefinition>;
|
||||
}
|
||||
interface ElsaInputTags {
|
||||
"fieldId"?: string;
|
||||
"onValueChanged"?: (event: CustomEvent<Array<string>>) => void;
|
||||
"placeHolder"?: string;
|
||||
"values"?: Array<string>;
|
||||
}
|
||||
interface ElsaInputTagsDropdown {
|
||||
"dropdownValues"?: Array<SelectListItem>;
|
||||
"fieldId"?: string;
|
||||
"fieldName"?: string;
|
||||
"onValueChanged"?: (event: CustomEvent<Array<string | SelectListItem>>) => void;
|
||||
"placeHolder"?: string;
|
||||
"values"?: Array<string | SelectListItem>;
|
||||
}
|
||||
interface ElsaModalDialog {
|
||||
"actions"?: Array<ActionDefinition>;
|
||||
"onActionInvoked"?: (event: CustomEvent<ActionInvokedArgs>) => void;
|
||||
|
|
@ -407,6 +457,9 @@ declare namespace LocalJSX {
|
|||
interface ElsaMultiLineInput {
|
||||
"inputContext"?: NodeInputContext;
|
||||
}
|
||||
interface ElsaMultiTextInput {
|
||||
"inputContext"?: NodeInputContext;
|
||||
}
|
||||
interface ElsaPager {
|
||||
"onPaginated"?: (event: CustomEvent<PagerData>) => void;
|
||||
"page"?: number;
|
||||
|
|
@ -498,8 +551,11 @@ declare namespace LocalJSX {
|
|||
"elsa-dropdown-input": ElsaDropdownInput;
|
||||
"elsa-flowchart": ElsaFlowchart;
|
||||
"elsa-form-panel": ElsaFormPanel;
|
||||
"elsa-input-tags": ElsaInputTags;
|
||||
"elsa-input-tags-dropdown": ElsaInputTagsDropdown;
|
||||
"elsa-modal-dialog": ElsaModalDialog;
|
||||
"elsa-multi-line-input": ElsaMultiLineInput;
|
||||
"elsa-multi-text-input": ElsaMultiTextInput;
|
||||
"elsa-pager": ElsaPager;
|
||||
"elsa-panel": ElsaPanel;
|
||||
"elsa-radio-list-input": ElsaRadioListInput;
|
||||
|
|
@ -532,8 +588,11 @@ declare module "@stencil/core" {
|
|||
"elsa-dropdown-input": LocalJSX.ElsaDropdownInput & JSXBase.HTMLAttributes<HTMLElsaDropdownInputElement>;
|
||||
"elsa-flowchart": LocalJSX.ElsaFlowchart & JSXBase.HTMLAttributes<HTMLElsaFlowchartElement>;
|
||||
"elsa-form-panel": LocalJSX.ElsaFormPanel & JSXBase.HTMLAttributes<HTMLElsaFormPanelElement>;
|
||||
"elsa-input-tags": LocalJSX.ElsaInputTags & JSXBase.HTMLAttributes<HTMLElsaInputTagsElement>;
|
||||
"elsa-input-tags-dropdown": LocalJSX.ElsaInputTagsDropdown & JSXBase.HTMLAttributes<HTMLElsaInputTagsDropdownElement>;
|
||||
"elsa-modal-dialog": LocalJSX.ElsaModalDialog & JSXBase.HTMLAttributes<HTMLElsaModalDialogElement>;
|
||||
"elsa-multi-line-input": LocalJSX.ElsaMultiLineInput & JSXBase.HTMLAttributes<HTMLElsaMultiLineInputElement>;
|
||||
"elsa-multi-text-input": LocalJSX.ElsaMultiTextInput & JSXBase.HTMLAttributes<HTMLElsaMultiTextInputElement>;
|
||||
"elsa-pager": LocalJSX.ElsaPager & JSXBase.HTMLAttributes<HTMLElsaPagerElement>;
|
||||
"elsa-panel": LocalJSX.ElsaPanel & JSXBase.HTMLAttributes<HTMLElsaPanelElement>;
|
||||
"elsa-radio-list-input": LocalJSX.ElsaRadioListInput & JSXBase.HTMLAttributes<HTMLElsaRadioListInputElement>;
|
||||
|
|
|
|||
|
|
@ -23,7 +23,6 @@ export class MultiLineInput {
|
|||
}
|
||||
|
||||
private getEditorHeight = (options: any) => {
|
||||
debugger;
|
||||
const editorHeightName = options.editorHeight || 'Default';
|
||||
|
||||
switch (editorHeightName) {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,27 @@
|
|||
import {Component, Prop, h} from '@stencil/core';
|
||||
import {JsonExpression, LiteralExpression, SyntaxNames} from "../../models";
|
||||
import {NodeInputContext} from "../../services/node-input-driver";
|
||||
import {getInputPropertyValue, parseJson} from "../../utils";
|
||||
|
||||
@Component({
|
||||
tag: 'elsa-multi-text-input',
|
||||
shadow: false
|
||||
})
|
||||
export class MultiTextInput {
|
||||
@Prop() inputContext: NodeInputContext;
|
||||
|
||||
public render() {
|
||||
const inputContext = this.inputContext;
|
||||
const inputProperty = inputContext.inputDescriptor;
|
||||
const fieldId = inputProperty.name;
|
||||
const input = getInputPropertyValue(inputContext);
|
||||
const json = (input?.expression as JsonExpression)?.value;
|
||||
const values = parseJson(json);
|
||||
return <elsa-input-tags fieldId={fieldId} values={values} onValueChanged={this.onPropertyEditorChanged}/>
|
||||
}
|
||||
|
||||
private onPropertyEditorChanged = (e: CustomEvent<Array<string>>) => {
|
||||
const json = JSON.stringify(e.detail);
|
||||
this.inputContext.inputChanged(json, SyntaxNames.Json);
|
||||
};
|
||||
}
|
||||
|
|
@ -0,0 +1,99 @@
|
|||
import {Component, h, Prop, Event, EventEmitter, State, Watch} from '@stencil/core';
|
||||
import {uniq} from 'lodash';
|
||||
import {SelectListItem} from '../../../models';
|
||||
|
||||
@Component({
|
||||
tag: 'elsa-input-tags-dropdown',
|
||||
shadow: false,
|
||||
})
|
||||
export class InputTagsDropdown {
|
||||
|
||||
@Prop() fieldName?: string;
|
||||
@Prop() fieldId?: string;
|
||||
@Prop() placeHolder?: string = 'Add tag';
|
||||
@Prop() values?: Array<string | SelectListItem> = [];
|
||||
@Prop() dropdownValues?: Array<SelectListItem> = [];
|
||||
@Event({bubbles: true}) valueChanged: EventEmitter<Array<string | SelectListItem>>;
|
||||
@State() currentValues?: Array<SelectListItem> = [];
|
||||
|
||||
@Watch('values')
|
||||
private valuesChangedHandler(newValue: Array<string | SelectListItem>) {
|
||||
this.updateCurrentValues(newValue);
|
||||
}
|
||||
|
||||
public componentWillLoad() {
|
||||
this.updateCurrentValues(this.values);
|
||||
}
|
||||
|
||||
private updateCurrentValues = (newValue: Array<string | SelectListItem>) => {
|
||||
const dropdownValues = this.dropdownValues || [];
|
||||
let values: Array<SelectListItem> = [];
|
||||
|
||||
if (!!newValue) {
|
||||
newValue.forEach(value => {
|
||||
const valueKey = typeof (value) == 'string' ? value as string : (value as SelectListItem).value;
|
||||
const tag = dropdownValues.find(x => x.value == valueKey);
|
||||
|
||||
if (!!tag)
|
||||
values.push(tag);
|
||||
})
|
||||
}
|
||||
|
||||
this.currentValues = values;
|
||||
};
|
||||
|
||||
private async onTagSelected(e: any) {
|
||||
e.preventDefault();
|
||||
|
||||
const input = e.target as HTMLSelectElement;
|
||||
const currentTag: SelectListItem = {
|
||||
text: input.options[input.selectedIndex].text.trim(),
|
||||
value: input.value
|
||||
}
|
||||
|
||||
if (currentTag.value.length == 0)
|
||||
return;
|
||||
|
||||
const values: Array<SelectListItem> = uniq([...this.currentValues, currentTag]);
|
||||
input.value = "Add";
|
||||
await this.valueChanged.emit(values);
|
||||
}
|
||||
|
||||
async onDeleteTagClick(e: any, currentTag: SelectListItem) {
|
||||
e.preventDefault();
|
||||
|
||||
this.currentValues = this.currentValues.filter(tag => tag.value !== currentTag.value);
|
||||
await this.valueChanged.emit(this.currentValues);
|
||||
}
|
||||
|
||||
render() {
|
||||
let values: Array<SelectListItem> = this.currentValues || [];
|
||||
let dropdownItems = this.dropdownValues.filter(x => values.findIndex(y => y.value === x.value) < 0);
|
||||
|
||||
if (!Array.isArray(values))
|
||||
values = [];
|
||||
|
||||
const valuesJson = JSON.stringify(values.map(tag => tag.value));
|
||||
|
||||
return (
|
||||
<div class="py-2 px-3 bg-white shadow-sm border border-gray-300 rounded-md">
|
||||
{values.map(tag => (
|
||||
<a href="#" onClick={e => this.onDeleteTagClick(e, tag)} class="inline-block text-xs bg-blue-400 text-white py-2 px-3 mr-1 mb-1 rounded">
|
||||
<input type="hidden" value={tag.value}/>
|
||||
<span>{tag.text}</span>
|
||||
<span class="text-white hover:text-white ml-1">×</span>
|
||||
</a>
|
||||
))}
|
||||
|
||||
<select
|
||||
id={this.fieldId}
|
||||
class="inline-block text-xs py-2 px-3 mr-1 mb-1 pr-8 border-gray-300 focus:outline-none focus:ring-blue-500 focus:border-blue-500 rounded"
|
||||
onChange={(e) => this.onTagSelected(e)}>
|
||||
<option value="Add" disabled selected>{this.placeHolder}</option>
|
||||
{dropdownItems.map(tag => <option value={tag.value}>{tag.text}</option>)}
|
||||
</select>
|
||||
<input type="hidden" name={this.fieldName} value={valuesJson}/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
.tag-input {
|
||||
outline: none !important;
|
||||
box-shadow: none !important;
|
||||
max-width: max-content !important;
|
||||
display: inline-block !important;
|
||||
}
|
||||
|
|
@ -0,0 +1,81 @@
|
|||
import {Component, h, Prop, Event, EventEmitter, State, Watch} from '@stencil/core';
|
||||
import {uniq} from 'lodash';
|
||||
|
||||
@Component({
|
||||
tag: 'elsa-input-tags',
|
||||
styleUrl: 'input-tags.css',
|
||||
shadow: false,
|
||||
})
|
||||
export class InputTags {
|
||||
|
||||
@Prop() fieldId?: string;
|
||||
@Prop() placeHolder?: string = 'Add tag';
|
||||
@Prop({mutable: true}) values?: Array<string> = [];
|
||||
@Event({bubbles: true}) valueChanged: EventEmitter<Array<string>>;
|
||||
|
||||
private addItem = async (item: string) => {
|
||||
const values = uniq([...this.values || [], item]);
|
||||
this.values = values;
|
||||
await this.valueChanged.emit(values);
|
||||
};
|
||||
|
||||
private async onInputKeyDown(e: KeyboardEvent) {
|
||||
if (e.key != "Enter")
|
||||
return;
|
||||
|
||||
e.preventDefault();
|
||||
|
||||
const input = e.target as HTMLInputElement;
|
||||
const value = input.value.trim();
|
||||
|
||||
if (value.length == 0)
|
||||
return;
|
||||
|
||||
await this.addItem(value);
|
||||
input.value = '';
|
||||
}
|
||||
|
||||
async onInputBlur(e: Event) {
|
||||
const input = e.target as HTMLInputElement;
|
||||
const value = input.value.trim();
|
||||
|
||||
if (value.length == 0)
|
||||
return;
|
||||
|
||||
await this.addItem(value);
|
||||
input.value = '';
|
||||
}
|
||||
|
||||
async onDeleteTagClick(e: Event, tag: string) {
|
||||
e.preventDefault();
|
||||
|
||||
this.values = this.values.filter(x => x !== tag);
|
||||
await this.valueChanged.emit(this.values);
|
||||
}
|
||||
|
||||
render() {
|
||||
let values = this.values || [];
|
||||
|
||||
if (!Array.isArray(values))
|
||||
values = [];
|
||||
|
||||
// const valuesJson = JSON.stringify(values);
|
||||
|
||||
return (
|
||||
<div class="py-2 px-3 bg-white shadow-sm border border-gray-300 rounded-md">
|
||||
{values.map(value => (
|
||||
<a href="#" onClick={e => this.onDeleteTagClick(e, value)} class="inline-block text-xs bg-blue-400 text-white py-2 px-3 mr-1 mb-1 rounded">
|
||||
<span>{value}</span>
|
||||
<span class="text-white hover:text-white ml-1">×</span>
|
||||
</a>
|
||||
))}
|
||||
<input type="text" id={this.fieldId}
|
||||
onKeyDown={e => this.onInputKeyDown(e)}
|
||||
onBlur={e => this.onInputBlur(e)}
|
||||
class="tag-input inline-block text-sm outline-none focus:outline-none border-none shadow:none focus:border-none focus:border-transparent focus:shadow-none"
|
||||
placeholder={this.placeHolder}/>
|
||||
{/*<input type="hidden" name={this.fieldName} value={valuesJson}/>*/}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -15,6 +15,7 @@ export class InputControlRegistry {
|
|||
this.add('dropdown', c => <elsa-dropdown-input inputContext={c}/>);
|
||||
this.add('check-list', c => <elsa-check-list-input inputContext={c}/>);
|
||||
this.add('radio-list', c => <elsa-radio-list-input inputContext={c}/>);
|
||||
this.add('multi-text', c => <elsa-multi-text-input inputContext={c}/>);
|
||||
}
|
||||
|
||||
public add(uiHint: UIHint, control: RenderActivityPropInputControl) {
|
||||
|
|
|
|||
|
|
@ -41,6 +41,9 @@ public class WriteLines : Activity, IActivityPropertyOptionsProvider
|
|||
|
||||
[Input(OptionsProvider = typeof(WriteLines), UIHint = InputUIHints.MultiLine)]
|
||||
public Input<string> Body { get; set; } = default!;
|
||||
|
||||
[Input(UIHint = InputUIHints.MultiText)]
|
||||
public Input<ICollection<string>> Tags { get; set; } = default!;
|
||||
|
||||
protected override void Execute(ActivityExecutionContext context)
|
||||
{
|
||||
|
|
|
|||
Loading…
Reference in a new issue