sequential-workflow-editor
Version:

1,366 lines (1,325 loc) • 62 kB
JavaScript
(function (global, factory) {
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('sequential-workflow-editor-model')) :
typeof define === 'function' && define.amd ? define(['exports', 'sequential-workflow-editor-model'], factory) :
(global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.sequentialWorkflowEditor = {}, global.sequentialWorkflowEditorModel));
})(this, (function (exports, sequentialWorkflowEditorModel) { 'use strict';
class Html {
static attrs(element, attributes) {
Object.keys(attributes).forEach(name => {
const value = attributes[name];
element.setAttribute(name, typeof value === 'string' ? value : value.toString());
});
}
static element(name, attributes) {
const element = document.createElement(name);
if (attributes) {
Html.attrs(element, attributes);
}
return element;
}
static toggleClass(element, isEnabled, className) {
if (isEnabled) {
element.classList.add(className);
}
else {
element.classList.remove(className);
}
}
}
const ns = 'http://www.w3.org/2000/svg';
class Icons {
static createSvg(icon, cls) {
const svg = document.createElementNS(ns, 'svg');
svg.setAttribute('viewBox', '0 -960 960 960');
svg.classList.add(cls);
const path = document.createElementNS(ns, 'path');
path.setAttribute('d', icon);
svg.appendChild(path);
return svg;
}
}
Icons.help = 'M419-334q1-87 20.5-129t65.5-76q39-31 57.5-61.109T581-666q0-39-25.5-64.5T486-756q-46 0-75 26t-43 67l-120-52q27-74 87-120.5T485.756-882q109.228 0 168.236 62.148Q713-757.703 713-669q0 60-21 105.5T625-478q-46 40-57 65.5T557-334H419Zm66.788 282Q447-52 420-79t-27-65.496q0-38.495 26.92-65.5Q446.841-237 485.92-237 525-237 552-209.996q27 27.005 27 65.5Q579-106 551.788-79q-27.213 27-66 27Z';
Icons.close = 'm249-183-66-66 231-231-231-231 66-66 231 231 231-231 66 66-231 231 231 231-66 66-231-231-231 231Z';
Icons.add = 'M433-183v-250H183v-94h250v-250h94v250h250v94H527v250h-94Z';
function buttonComponent(label, configuration) {
function onClicked(e) {
e.preventDefault();
onClick.forward();
}
function setIcon(d) {
if (icon) {
icon.getElementsByTagName('path')[0].setAttribute('d', d);
}
else {
throw new Error('This button does not have icon');
}
}
function setLabel(label) {
if (configuration === null || configuration === void 0 ? void 0 : configuration.icon) {
throw new Error('Cannot change label on button with icon');
}
else {
view.innerText = label;
}
}
const onClick = new sequentialWorkflowEditorModel.SimpleEvent();
let className = 'swe-button';
if (configuration === null || configuration === void 0 ? void 0 : configuration.size) {
className += ` swe-button-${configuration.size}`;
}
if (configuration === null || configuration === void 0 ? void 0 : configuration.theme) {
className += ` swe-button-${configuration.theme}`;
}
const view = Html.element('button', {
class: className,
title: label,
'aria-label': label
});
let icon;
if (configuration === null || configuration === void 0 ? void 0 : configuration.icon) {
icon = Icons.createSvg(configuration.icon, 'swe-button-icon');
view.appendChild(icon);
}
else {
view.innerText = label;
}
view.addEventListener('click', onClicked, false);
return {
view,
onClick,
setIcon,
setLabel
};
}
function validationErrorComponent() {
const view = Html.element('div', {
class: 'swe-validation-error'
});
const onIsHiddenChanged = new sequentialWorkflowEditorModel.SimpleEvent();
let child = null;
function isHidden() {
return child === null;
}
function setError(error) {
const oldState = isHidden();
if (child) {
view.removeChild(child);
child = null;
}
if (error) {
child = Html.element('div', {
class: 'swe-validation-error-text'
});
child.textContent = error;
view.appendChild(child);
}
const newState = isHidden();
if (oldState !== newState) {
onIsHiddenChanged.forward(newState);
}
}
function setDefaultError(result) {
setError(result && result['$']);
}
return {
onIsHiddenChanged,
view,
isHidden,
setError,
setDefaultError
};
}
function dynamicListComponent(initialItems, itemComponentFactory, context, configuration) {
const onChanged = new sequentialWorkflowEditorModel.SimpleEvent();
const items = [...initialItems];
function forward() {
onChanged.forward([...items]);
}
function onItemChanged(newItem, index) {
items[index] = newItem;
forward();
validateList();
}
function onItemDeleted(index) {
if (configuration && configuration.canDelete) {
const error = configuration.canDelete(items[index]);
if (error) {
window.alert(error);
return;
}
}
items.splice(index, 1);
forward();
reloadList();
}
function add(item) {
items.push(item);
forward();
reloadList();
}
function forEach(callback) {
components.forEach(callback);
}
function reloadList() {
if (emptyRow) {
view.removeChild(emptyRow);
emptyRow = null;
}
components.forEach(component => view.removeChild(component.view));
components.length = 0;
if (items.length > 0) {
items.forEach((item, index) => {
const component = itemComponentFactory(item, context.i18n, index);
component.onItemChanged.subscribe(item => onItemChanged(item, index));
component.onDeleteClicked.subscribe(() => onItemDeleted(index));
view.insertBefore(component.view, validation.view);
components.push(component);
});
}
else if (configuration === null || configuration === void 0 ? void 0 : configuration.emptyMessage) {
emptyRow = Html.element('div', {
class: 'swe-dynamic-list-empty-row'
});
emptyRow.innerText = configuration.emptyMessage;
view.insertBefore(emptyRow, validation.view);
}
validateList();
}
function validateList() {
const result = context.validate();
for (let i = 0; i < components.length; i++) {
components[i].validate(result ? result[i] : null);
}
validation.setError(result && result.$ ? result.$ : null);
}
let emptyRow = null;
const view = Html.element('div', {
class: 'swe-dynamic-list'
});
const validation = validationErrorComponent();
view.appendChild(validation.view);
const components = [];
reloadList();
return {
onChanged,
view,
add,
forEach
};
}
function appendMultilineText(target, text) {
const lines = text.split(/\r?\n/g);
for (let i = 0; i < lines.length; i++) {
if (i > 0) {
target.appendChild(document.createElement('br'));
}
const line = document.createTextNode(lines[i]);
target.appendChild(line);
}
}
function filterValueTypes(types, allowedTypes) {
if (!allowedTypes) {
return types;
}
const result = [];
for (const type of types) {
if (allowedTypes.includes(type)) {
result.push(type);
}
}
return result;
}
class StackedSimpleEvent {
constructor() {
this.event = new sequentialWorkflowEditorModel.SimpleEvent();
this.stack = [];
this.to = null;
}
push(value) {
this.stack.push(value);
if (this.to) {
return;
}
this.to = setTimeout(() => {
this.to = null;
this.event.forward(this.stack);
this.stack.length = 0;
});
}
subscribe(listener) {
this.event.subscribe(listener);
}
}
function formatVariableName(name) {
return `$${name}`;
}
function formatVariableNameWithType(name, type) {
return `${formatVariableName(name)} (${type})`;
}
function inputComponent(startValue, configuration) {
var _a;
const onChanged = new sequentialWorkflowEditorModel.SimpleEvent();
function setValue(value) {
view.value = value;
}
function getValue() {
return view.value;
}
function setReadonly(readonly) {
if (readonly) {
view.setAttribute('readonly', 'readonly');
}
else {
view.removeAttribute('readonly');
}
}
const view = Html.element('input', {
class: 'swe-input swe-stretched',
type: (_a = configuration === null || configuration === void 0 ? void 0 : configuration.type) !== null && _a !== void 0 ? _a : 'text'
});
if (configuration === null || configuration === void 0 ? void 0 : configuration.placeholder) {
view.setAttribute('placeholder', configuration.placeholder);
}
if (configuration === null || configuration === void 0 ? void 0 : configuration.isReadonly) {
setReadonly(true);
}
view.value = startValue;
view.addEventListener('input', () => {
onChanged.forward(view.value);
});
return {
view,
onChanged,
setValue,
getValue,
setReadonly
};
}
function prependedInputComponent(prefix, component) {
const view = Html.element('div', {
class: 'swe-prepended-input'
});
const pref = Html.element('span', {
class: 'swe-prepended-input-prefix'
});
pref.innerText = prefix;
view.appendChild(pref);
view.appendChild(component.view);
return Object.assign(Object.assign({}, component), { view });
}
function rowComponent(elements, configuration) {
let viewClass = 'swe-row';
if (configuration && configuration.class) {
viewClass += ' ' + configuration.class;
}
const view = Html.element('div', {
class: viewClass
});
elements.forEach((element, index) => {
const grow = configuration && configuration.cols ? configuration.cols[index] : 1;
let className = 'swe-col';
if (grow) {
className += ` swe-col-${grow}`;
}
const col = Html.element('div', {
class: className
});
col.appendChild(element);
view.appendChild(col);
});
return {
view
};
}
function selectComponent(configuration) {
function setValues(values) {
options.forEach(option => view.removeChild(option));
options.length = 0;
for (let i = 0; i < values.length; i++) {
const option = document.createElement('option');
option.value = values[i];
option.innerText = values[i];
view.appendChild(option);
options.push(option);
}
}
function getSelectedIndex() {
return view.selectedIndex;
}
function selectIndex(index) {
view.selectedIndex = index;
}
function onSelectChanged() {
onSelected.forward(getSelectedIndex());
}
const onSelected = new sequentialWorkflowEditorModel.SimpleEvent();
let className = 'swe-select';
if (configuration === null || configuration === void 0 ? void 0 : configuration.size) {
className += ` swe-select-${configuration.size}`;
}
if (configuration === null || configuration === void 0 ? void 0 : configuration.stretched) {
className += ' swe-stretched';
}
const view = Html.element('select', {
class: className
});
const options = [];
view.addEventListener('change', onSelectChanged, false);
return {
view,
setValues,
getSelectedIndex,
selectIndex,
onSelected
};
}
function textareaComponent(startValue, configuration) {
var _a, _b;
const onChanged = new sequentialWorkflowEditorModel.SimpleEvent();
function setValue(value) {
view.value = value;
}
function getValue() {
return view.value;
}
const view = Html.element('textarea', {
class: 'swe-textarea swe-stretched',
rows: (_b = (_a = configuration === null || configuration === void 0 ? void 0 : configuration.rows) === null || _a === void 0 ? void 0 : _a.toString()) !== null && _b !== void 0 ? _b : '4'
});
if (configuration === null || configuration === void 0 ? void 0 : configuration.placeholder) {
view.setAttribute('placeholder', configuration.placeholder);
}
view.value = startValue;
view.addEventListener('input', () => {
onChanged.forward(view.value);
});
return {
view,
onChanged,
setValue,
getValue
};
}
function valueEditorContainerComponent(elements) {
const view = document.createElement('div');
view.className = 'swe-value-editor-container';
elements.forEach(element => view.appendChild(element));
return { view };
}
const stringValueEditorId = 'string';
const defaultMultiline = 4;
function createStringValueEditor(configuration) {
return (context) => {
function validate() {
validation.setDefaultError(context.validate());
}
const startValue = context.getValue();
const multiline = context.model.configuration.multiline;
const input = multiline
? textareaComponent(startValue, {
rows: multiline === true ? defaultMultiline : multiline
})
: inputComponent(startValue);
input.onChanged.subscribe(value => {
context.setValue(value);
validate();
});
const row = rowComponent([input.view], {
class: configuration === null || configuration === void 0 ? void 0 : configuration.class
});
const validation = validationErrorComponent();
const container = valueEditorContainerComponent([row.view, validation.view]);
validate();
return {
view: container.view
};
};
}
class StringValueEditorEditorExtension {
static create(configuration) {
return new StringValueEditorEditorExtension(configuration);
}
constructor(configuration) {
this.configuration = configuration;
this.valueEditors = [
{
editorId: this.configuration.editorId,
factory: createStringValueEditor(this.configuration)
}
];
}
}
const numberValueEditorId = 'number';
function numberValueEditor(context) {
function validate() {
validation.setDefaultError(context.validate());
}
const startValue = String(context.getValue());
const input = inputComponent(startValue, {
type: 'number'
});
input.onChanged.subscribe(value => {
const num = value.length > 0 ? Number(value) : NaN;
context.setValue(num);
validate();
});
const row = rowComponent([input.view]);
const validation = validationErrorComponent();
const container = valueEditorContainerComponent([row.view, validation.view]);
validate();
return {
view: container.view
};
}
function variableDefinitionItemComponent(variable, context) {
function validate(error) {
validation.setError(error);
}
function onTypeChanged(index) {
const type = valueTypes[index];
variable.type = type;
onItemChanged.forward(variable);
}
function onNameChanged(value) {
variable.name = value;
onItemChanged.forward(variable);
}
const onItemChanged = new sequentialWorkflowEditorModel.SimpleEvent();
const onDeleteClicked = new sequentialWorkflowEditorModel.SimpleEvent();
const view = Html.element('div', {
class: 'swe-variable-definition-item'
});
const input = prependedInputComponent('$', inputComponent(variable.name, {
placeholder: context.i18n('variableDefinitions.namePlaceholder', 'Variable name')
}));
input.onChanged.subscribe(onNameChanged);
const valueTypes = filterValueTypes(context.getValueTypes(), context.model.configuration.valueTypes);
const typeSelect = selectComponent({
stretched: true
});
typeSelect.setValues(valueTypes);
typeSelect.selectIndex(valueTypes.findIndex(type => type === variable.type));
typeSelect.onSelected.subscribe(onTypeChanged);
const deleteButton = buttonComponent(context.i18n('variableDefinitions.delete', 'Delete'), {
size: 'small',
theme: 'secondary',
icon: Icons.close
});
deleteButton.onClick.subscribe(() => onDeleteClicked.forward());
const validation = validationErrorComponent();
const row = rowComponent([input.view, typeSelect.view, deleteButton.view], {
cols: [2, 1, null]
});
view.appendChild(row.view);
view.appendChild(validation.view);
return {
view,
onItemChanged,
onDeleteClicked,
validate
};
}
const variableDefinitionsValueEditorId = 'variableDefinitions';
function variableDefinitionsValueEditor(context) {
function onChanged(variables) {
context.setValue({
variables
});
}
function onAddClicked() {
list.add({
name: '',
type: context.getValueTypes()[0]
});
}
const list = dynamicListComponent(context.getValue().variables, item => variableDefinitionItemComponent(item, context), context, {
emptyMessage: context.i18n('variableDefinitions.noVariablesDefined', 'No variables defined')
});
list.onChanged.subscribe(onChanged);
const addButton = buttonComponent(context.i18n('variableDefinitions.newVariable', 'New variable'), {
size: 'small',
icon: Icons.add
});
addButton.onClick.subscribe(onAddClicked);
const container = valueEditorContainerComponent([list.view]);
return {
view: container.view,
controlView: addButton.view
};
}
function filterVariablesByType(variables, valueType) {
if (!valueType) {
return variables;
}
const filter = Array.isArray(valueType)
? (variable) => valueType.includes(variable.type)
: (variable) => variable.type === valueType;
return variables.filter(filter);
}
const nullableVariableValueEditorId = 'nullableVariable';
function nullableVariableValueEditor(context) {
function validate() {
validation.setDefaultError(context.validate());
}
function onChanged(selectedIndex) {
if (selectedIndex === 0) {
context.setValue(null);
}
else {
context.setValue({
name: variables[selectedIndex - 1].name
});
}
validate();
}
const startValue = context.getValue();
const variables = filterVariablesByType(context.getVariables(), context.model.configuration.valueType);
const select = selectComponent({
stretched: true
});
select.setValues([
context.i18n('nullableVariable.selectType', '- Select: :type -', {
type: context.model.configuration.valueType
}),
...variables.map(variable => formatVariableNameWithType(variable.name, variable.type))
]);
if (startValue) {
select.selectIndex(variables.findIndex(variable => variable.name === startValue.name) + 1);
}
else {
select.selectIndex(0);
}
select.onSelected.subscribe(index => onChanged(index));
const row = rowComponent([select.view]);
const validation = validationErrorComponent();
const container = valueEditorContainerComponent([row.view, validation.view]);
validate();
return {
view: container.view
};
}
const nullableVariableDefinitionValueEditorId = 'nullableVariableDefinition';
function nullableVariableDefinitionValueEditor(context) {
var _a;
function validate() {
validation.setDefaultError(context.validate());
}
const startValue = ((_a = context.getValue()) === null || _a === void 0 ? void 0 : _a.name) || '';
const input = prependedInputComponent('$', inputComponent(startValue));
input.onChanged.subscribe(value => {
context.setValue(value
? {
name: value,
type: context.model.configuration.valueType
}
: null);
validate();
});
const row = rowComponent([input.view]);
const validation = validationErrorComponent();
const container = valueEditorContainerComponent([row.view, validation.view]);
validate();
return {
view: container.view
};
}
function anyVariableItemComponent(variable, i18n) {
function validate(error) {
validation.setError(error);
}
const onDeleteClicked = new sequentialWorkflowEditorModel.SimpleEvent();
const view = Html.element('div');
const name = Html.element('span');
name.innerText = formatVariableNameWithType(variable.name, variable.type);
const deleteButton = buttonComponent(i18n('anyVariable.delete', 'Delete'), {
size: 'small',
theme: 'secondary',
icon: Icons.close
});
deleteButton.onClick.subscribe(() => onDeleteClicked.forward());
const validation = validationErrorComponent();
const row = rowComponent([name, deleteButton.view], {
cols: [1, null]
});
view.appendChild(row.view);
view.appendChild(validation.view);
return {
view,
onDeleteClicked,
onItemChanged: new sequentialWorkflowEditorModel.SimpleEvent(),
validate
};
}
function anyVariableSelectorComponent(context) {
var _a;
function getSelectedValueType() {
return valueTypes[typeSelect.getSelectedIndex()];
}
function getSelectedVariableName() {
const index = variableSelect.getSelectedIndex();
return variables && index > 0 ? variables[index - 1].name : null;
}
function reloadVariableSelector() {
variables = filterVariablesByType(context.getVariables(), getSelectedValueType());
const variableNames = variables.map(variable => formatVariableName(variable.name));
variableSelect.setValues([context.i18n('anyVariable.select', '- Select -'), ...variableNames]);
}
function onAddClicked() {
const type = getSelectedValueType();
const name = getSelectedVariableName();
if (name) {
onAdded.forward({ type, name });
variableSelect.selectIndex(0);
}
}
const onAdded = new sequentialWorkflowEditorModel.SimpleEvent();
const valueTypes = (_a = context.model.configuration.valueTypes) !== null && _a !== void 0 ? _a : context.getValueTypes();
const typeSelect = selectComponent({
stretched: true
});
typeSelect.setValues(valueTypes);
typeSelect.onSelected.subscribe(reloadVariableSelector);
const variableSelect = selectComponent({
stretched: true
});
let variables = null;
const addButton = buttonComponent(context.i18n('anyVariable.addVariable', 'Add variable'), {
icon: Icons.add
});
addButton.onClick.subscribe(onAddClicked);
const row = rowComponent([typeSelect.view, variableSelect.view, addButton.view], {
cols: [1, 1, null]
});
reloadVariableSelector();
return {
view: row.view,
onAdded
};
}
const anyVariablesValueEditorId = 'anyVariables';
function anyVariablesValueEditor(context) {
function onChanged(variables) {
context.setValue({
variables
});
}
function onNewAdded(newVariable) {
if (context.getValue().variables.some(v => v.name === newVariable.name)) {
// TODO: variable is already added, some message?
return;
}
list.add(newVariable);
}
const selector = anyVariableSelectorComponent(context);
selector.onAdded.subscribe(onNewAdded);
const list = dynamicListComponent(context.getValue().variables, anyVariableItemComponent, context, {
emptyMessage: context.i18n('anyVariables.noVariablesSelected', 'No variables selected')
});
list.onChanged.subscribe(onChanged);
const container = valueEditorContainerComponent([selector.view, list.view]);
return {
view: container.view
};
}
const dynamicValueEditorId = 'dynamic';
function dynamicValueEditor(context, services) {
if (!context.model.subModels) {
throw new Error('subModels is required');
}
const subModels = context.model.subModels;
function reloadDependencies() {
if (editor && editor.reloadDependencies) {
editor.reloadDependencies();
}
}
function reloadEditor() {
if (editor) {
placeholder.removeChild(editor.view);
if (subControl) {
control.removeChild(subControl);
subControl = null;
}
}
const value = context.getValue();
const model = subModels.find(model => model.id === value.modelId);
if (!model || !model.id) {
throw new Error(`Model not found: ${value.modelId}`);
}
const childContext = context.createChildContext(model);
editor = services.valueEditorFactoryResolver.resolve(model.id, model.editorId)(childContext, services);
placeholder.appendChild(editor.view);
if (editor.controlView) {
subControl = Html.element('span', {
class: 'swe-dynamic-sub-control'
});
subControl.appendChild(editor.controlView);
control.appendChild(subControl);
}
}
function onTypeChanged() {
const newModel = subModels[subModelSelect.getSelectedIndex()];
const defaultValueContext = sequentialWorkflowEditorModel.DefaultValueContext.create(services.activator, context.scopedPropertyContext.propertyContext);
const defaultValue = {
modelId: newModel.id,
value: newModel.getDefaultValue(defaultValueContext)
};
context.setValue(defaultValue);
reloadEditor();
}
const startValue = context.getValue();
const control = Html.element('div', {
class: 'swe-dynamic-control'
});
const subModelSelect = selectComponent({
size: 'small'
});
subModelSelect.setValues(context.model.subModels.map(model => {
return context.i18n(`dynamic.${model.id}.label`, model.label);
}));
subModelSelect.selectIndex(context.model.subModels.findIndex(model => model.id === startValue.modelId));
subModelSelect.onSelected.subscribe(onTypeChanged);
control.appendChild(subModelSelect.view);
const placeholder = Html.element('div', {
class: 'swe-dynamic-placeholder'
});
const container = valueEditorContainerComponent([placeholder]);
let editor = null;
let subControl = null;
reloadEditor();
return {
view: container.view,
controlView: control,
reloadDependencies
};
}
function createStepI18nPrefix(stepType) {
return stepType ? `step.${stepType}.property:` : 'root.property:';
}
const choiceValueEditorId = 'choice';
function choiceValueEditor(context) {
function validate() {
validation.setDefaultError(context.validate());
}
function onSelected(index) {
const value = choices[index];
context.setValue(value);
validate();
}
const select = selectComponent({
stretched: true
});
const stepType = context.tryGetStepType();
const i18nPrefix = createStepI18nPrefix(stepType);
const choices = context.model.configuration.choices;
const translatedChoices = choices.map(choice => {
const pathStr = context.model.path.toString();
const key = `${i18nPrefix}${pathStr}:choice:${choice}`;
return context.i18n(key, choice);
});
select.setValues(translatedChoices);
const startIndex = choices.indexOf(context.getValue());
select.selectIndex(startIndex);
select.onSelected.subscribe(onSelected);
const row = rowComponent([select.view]);
const validation = validationErrorComponent();
const container = valueEditorContainerComponent([row.view, validation.view]);
validate();
return {
view: container.view
};
}
const nullableAnyVariableValueEditorId = 'nullableAnyVariable';
function nullableAnyVariableValueEditor(context) {
function validate() {
validation.setDefaultError(context.validate());
}
function onChanged(selectedIndex) {
if (selectedIndex === 0) {
context.setValue(null);
}
else {
const variable = variables[selectedIndex - 1];
context.setValue({
name: variable.name,
type: variable.type
});
}
validate();
}
const startValue = context.getValue();
const variables = filterVariablesByType(context.getVariables(), context.model.configuration.valueTypes);
const select = selectComponent({
stretched: true
});
const expectedTypes = context.model.configuration.valueTypes ? context.model.configuration.valueTypes.join(', ') : null;
const actionText = expectedTypes
? context.i18n('nullableAnyVariable.selectTypes', '- Select: :types -', {
types: expectedTypes
})
: context.i18n('nullableAnyVariable.select', '- Select -');
select.setValues([actionText, ...variables.map(variable => formatVariableNameWithType(variable.name, variable.type))]);
if (startValue) {
select.selectIndex(variables.findIndex(variable => variable.name === startValue.name) + 1);
}
else {
select.selectIndex(0);
}
select.onSelected.subscribe(index => onChanged(index));
const row = rowComponent([select.view]);
const validation = validationErrorComponent();
const container = valueEditorContainerComponent([row.view, validation.view]);
validate();
return {
view: container.view
};
}
const booleanValueEditorId = 'boolean';
function booleanValueEditor(context) {
function validate() {
validation.setDefaultError(context.validate());
}
function onSelected(index) {
context.setValue(index === 1);
validate();
}
const select = selectComponent({
stretched: true
});
select.setValues([context.i18n('boolean.false', 'False'), context.i18n('boolean.true', 'True')]);
select.selectIndex(context.getValue() ? 1 : 0);
select.onSelected.subscribe(onSelected);
const row = rowComponent([select.view]);
const validation = validationErrorComponent();
const container = valueEditorContainerComponent([row.view, validation.view]);
validate();
return {
view: container.view
};
}
const generatedStringValueEditorId = 'generatedString';
function generatedStringValueEditor(context) {
const generatedContext = sequentialWorkflowEditorModel.GeneratedStringContext.create(context);
function validate() {
validation.setDefaultError(context.validate());
}
function reloadDependencies() {
generate();
validate();
}
function generate() {
const generated = context.model.configuration.generator(generatedContext);
if (input.getValue() !== generated) {
input.setValue(generated);
context.setValue(generated);
}
}
const startValue = context.getValue();
const input = inputComponent(startValue, {
isReadonly: true
});
const row = rowComponent([input.view]);
const validation = validationErrorComponent();
const container = valueEditorContainerComponent([row.view, validation.view]);
validate();
return {
view: container.view,
reloadDependencies
};
}
function stringDictionaryItemComponent(item, i18n) {
function validate(error) {
validation.setError(error);
}
function onChanged() {
onItemChanged.forward({ key: keyInput.getValue(), value: valueInput.getValue() });
}
const onItemChanged = new sequentialWorkflowEditorModel.SimpleEvent();
const onDeleteClicked = new sequentialWorkflowEditorModel.SimpleEvent();
const keyInput = inputComponent(item.key, {
placeholder: i18n('stringDictionary.key', 'Key')
});
keyInput.onChanged.subscribe(onChanged);
const valueInput = inputComponent(item.value, {
placeholder: i18n('stringDictionary.value', 'Value')
});
valueInput.onChanged.subscribe(onChanged);
const deleteButton = buttonComponent(i18n('stringDictionary.delete', 'Delete'), {
size: 'small',
theme: 'secondary',
icon: Icons.close
});
deleteButton.onClick.subscribe(onDeleteClicked.forward);
const row = rowComponent([keyInput.view, valueInput.view, deleteButton.view], {
cols: [2, 3, null]
});
const validation = validationErrorComponent();
const view = Html.element('div', {
class: 'swe-dictionary-item'
});
view.appendChild(row.view);
view.appendChild(validation.view);
return {
view,
onItemChanged,
onDeleteClicked,
validate
};
}
function stringDictionaryValueEditor(context) {
function onChanged(items) {
context.setValue({
items
});
}
function onAddClicked() {
list.add({
key: '',
value: ''
});
}
const list = dynamicListComponent(context.getValue().items, stringDictionaryItemComponent, context, {
emptyMessage: context.i18n('stringDictionary.noItems', 'No items')
});
list.onChanged.subscribe(onChanged);
const container = valueEditorContainerComponent([list.view]);
const addButton = buttonComponent(context.i18n('stringDictionary.addItem', 'Add item'), {
size: 'small',
icon: Icons.add
});
addButton.onClick.subscribe(onAddClicked);
return {
view: container.view,
controlView: addButton.view
};
}
function hiddenValueEditor() {
const container = valueEditorContainerComponent([]);
return {
view: container.view,
isHidden: () => true
};
}
const defaultMap = {
[anyVariablesValueEditorId]: anyVariablesValueEditor,
[booleanValueEditorId]: booleanValueEditor,
[choiceValueEditorId]: choiceValueEditor,
[nullableAnyVariableValueEditorId]: nullableAnyVariableValueEditor,
[dynamicValueEditorId]: dynamicValueEditor,
[generatedStringValueEditorId]: generatedStringValueEditor,
[nullableVariableValueEditorId]: nullableVariableValueEditor,
[nullableVariableDefinitionValueEditorId]: nullableVariableDefinitionValueEditor,
[stringValueEditorId]: createStringValueEditor(),
[sequentialWorkflowEditorModel.stringDictionaryValueModelId]: stringDictionaryValueEditor,
[numberValueEditorId]: numberValueEditor,
[variableDefinitionsValueEditorId]: variableDefinitionsValueEditor,
[sequentialWorkflowEditorModel.sequenceValueModelId]: hiddenValueEditor,
[sequentialWorkflowEditorModel.branchesValueModelId]: hiddenValueEditor
};
class ValueEditorFactoryResolver {
static create(extensions) {
let map;
if (extensions) {
map = Object.assign({}, defaultMap);
extensions.forEach(extension => {
if (extension.valueEditors) {
extension.valueEditors.forEach(e => (map[e.editorId] = e.factory));
}
});
}
else {
map = defaultMap;
}
return new ValueEditorFactoryResolver(map);
}
constructor(map) {
this.map = map;
}
resolve(valueModelId, editorId) {
const id = editorId !== null && editorId !== void 0 ? editorId : valueModelId;
const editor = this.map[id];
if (!editor) {
throw new Error(`Editor id ${id} is not supported`);
}
return editor;
}
}
const defaultResolvers = [sequentialResolver, branchedResolver];
function branchedResolver(step) {
const branches = step.branches;
if (branches) {
return { type: StepChildrenType.branches, items: branches };
}
return null;
}
function sequentialResolver(step) {
const sequence = step.sequence;
if (sequence) {
return { type: StepChildrenType.sequence, items: sequence };
}
return null;
}
var StepChildrenType;
(function (StepChildrenType) {
StepChildrenType[StepChildrenType["sequence"] = 1] = "sequence";
StepChildrenType[StepChildrenType["branches"] = 2] = "branches";
})(StepChildrenType || (StepChildrenType = {}));
class DefinitionWalker {
constructor(resolvers) {
this.resolvers = resolvers ? resolvers.concat(defaultResolvers) : defaultResolvers;
}
/**
* Returns children of the step. If the step doesn't have children, returns null.
* @param step The step.
*/
getChildren(step) {
const count = this.resolvers.length;
for (let i = 0; i < count; i++) {
const result = this.resolvers[i](step);
if (result) {
return result;
}
}
return null;
}
/**
* Returns the parents of the step or the sequence.
* @param definition The definition.
* @param needle The step, stepId or sequence to find.
* @returns The parents of the step or the sequence.
*/
getParents(definition, needle) {
const result = [];
let searchSequence = null;
let searchStepId = null;
if (Array.isArray(needle)) {
searchSequence = needle;
}
else if (typeof needle === 'string') {
searchStepId = needle;
}
else {
searchStepId = needle.id;
}
if (this.find(definition.sequence, searchSequence, searchStepId, result)) {
result.reverse();
return result.map(item => {
return typeof item === 'string' ? item : item.step;
});
}
throw new Error(searchStepId ? `Cannot get parents of step: ${searchStepId}` : 'Cannot get parents of sequence');
}
findParentSequence(definition, stepId) {
const result = [];
if (this.find(definition.sequence, null, stepId, result)) {
return result[0];
}
return null;
}
getParentSequence(definition, stepId) {
const result = this.findParentSequence(definition, stepId);
if (!result) {
throw new Error(`Cannot find step by id: ${stepId}`);
}
return result;
}
findById(definition, stepId) {
const result = this.findParentSequence(definition, stepId);
return result ? result.step : null;
}
getById(definition, stepId) {
return this.getParentSequence(definition, stepId).step;
}
forEach(definition, callback) {
this.iterateSequence(definition.sequence, callback);
}
forEachSequence(sequence, callback) {
this.iterateSequence(sequence, callback);
}
forEachChildren(step, callback) {
this.iterateStep(step, callback);
}
find(sequence, needSequence, needStepId, result) {
if (needSequence && sequence === needSequence) {
return true;
}
const count = sequence.length;
for (let index = 0; index < count; index++) {
const step = sequence[index];
if (needStepId && step.id === needStepId) {
result.push({ step, index, parentSequence: sequence });
return true;
}
const children = this.getChildren(step);
if (children) {
switch (children.type) {
case StepChildrenType.sequence:
{
const parentSequence = children.items;
if (this.find(parentSequence, needSequence, needStepId, result)) {
result.push({ step, index, parentSequence });
return true;
}
}
break;
case StepChildrenType.branches:
{
const branches = children.items;
const branchNames = Object.keys(branches);
for (const branchName of branchNames) {
const parentSequence = branches[branchName];
if (this.find(parentSequence, needSequence, needStepId, result)) {
result.push(branchName);
result.push({ step, index, parentSequence });
return true;
}
}
}
break;
default:
throw new Error(`Not supported step children type: ${children.type}`);
}
}
}
return false;
}
iterateSequence(sequence, callback) {
const count = sequence.length;
for (let index = 0; index < count; index++) {
const step = sequence[index];
if (callback(step, index, sequence) === false) {
return false;
}
if (!this.iterateStep(step, callback)) {
return false;
}
}
return true;
}
iterateStep(step, callback) {
const children = this.getChildren(step);
if (children) {
switch (children.type) {
case StepChildrenType.sequence:
{
const sequence = children.items;
if (!this.iterateSequence(sequence, callback)) {
return false;
}
}
break;
case StepChildrenType.branches:
{
const sequences = Object.values(children.items);
for (const sequence of sequences) {
if (!this.iterateSequence(sequence, callback)) {
return false;
}
}
}
break;
default:
throw new Error(`Not supported step children type: ${children.type}`);
}
}
return true;
}
}
function propertyValidationErrorComponent(validator, context) {
const validation = validationErrorComponent();
function validate() {
const error = validator.validate(context);
validation.setError(error);
}
validate();
return {
view: validation.view,
validate,
isHidden: validation.isHidden
};
}
function propertyHint(text) {
let content = null;
const view = Html.element('div', {
class: 'swe-property-hint'
});
function toggle() {
if (content) {
view.removeChild(content);
content = null;
}
else {
content = Html.element('div', {
class: 'swe-property-hint-text'
});
appendMultilineText(content, text);
view.appendChild(content);
}
}
return {
view,
toggle
};
}
class PropertyEditor {
static create(propertyModel, stepType, definitionContext, editorServices) {
const valueContext = sequentialWorkflowEditorModel.ValueContext.createFromDefinitionContext(propertyModel.value, propertyModel, definitionContext, editorServices.i18n);
const valueEditorFactory = editorServices.valueEditorFactoryResolver.resolve(propertyModel.value.id, propertyModel.value.editorId);
const valueEditor = valueEditorFactory(valueContext, editorServices);
let hint = null;
const nameClassName = propertyModel.path.last();
const pathStr = propertyModel.path.toString();
const view = Html.element('div', {
class: `swe-property swe-name-${nameClassName}`
});
view.setAttribute('data-path', pathStr);
const header = Html.element('div', {
class: 'swe-property-header'
});
const label = Html.element('h4', {
class: 'swe-property-header-label'
});
const i18nPrefix = createStepI18nPrefix(stepType);
label.innerText = editorServices.i18n(i18nPrefix + pathStr, propertyModel.label);
header.appendChild(label);
view.appendChild(header);
if (propertyModel.hint) {
const toggle = Html.element('span', {
class: 'swe-property-header-hint-toggle'
});
const toggleIcon = Icons.createSvg(Icons.help, 'swe-property-header-hint-toggle-icon');
toggle.appendChild(toggleIcon);
toggle.addEventListener('click', () => hint === null || hint === void 0 ? void 0 : hint.toggle(), false);
header.appendChild(toggle);
hint = propertyHint(propertyModel.hint);
view.appendChild(hint.view);
}
view.appendChild(valueEditor.view);
let control = null;
if (valueEditor.controlView) {
control = Html.element('div', {
class: 'swe-property-header-control'
});
control.appendChild(valueEditor.controlView);
header.appendChild(control);
}
let propertyValidationError = null;
if (propertyModel.validator) {
const valueContext = sequentialWorkflowEditorModel.ValueContext.createFromDefinitionContext(propertyModel.value, propertyModel, definitionContext, editorServices.i18n);
const validatorContext = sequentialWorkflowEditorModel.PropertyValidatorContext.create(valueContext);
propertyValidationError = propertyValidationErrorComponent(propertyModel.validator, validatorContext);
view.appendChild(propertyValidationError.view);
}
const editor = new PropertyEditor(view, valueContext.onValueChanged, valueEditor, control, propertyValidationError);
if (propertyValidationError) {
valueContext.onValueChanged.subscribe(editor.onValueChangedHandler);
}
if (valueEditor.onIsHiddenChanged) {
valueEditor.onIsHiddenChanged.subscribe(editor.onEditorIsHiddenChanged);
}
editor.reloadVisibility();
return editor;
}
constructor(view, onValueChanged, valueEditor, control, propertyValidationError) {
this.view = view;
this.onValueChanged = onValueChanged;
this.valueEditor