Skip to content

Commit 60a0c7f

Browse files
authored
0.4.0. (#9)
1 parent 0d9c597 commit 60a0c7f

File tree

70 files changed

+733
-310
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

70 files changed

+733
-310
lines changed

CHANGELOG.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,14 @@
1+
## 0.4.0
2+
3+
* Added new value model: `generatedString` (`generatedStringValueEditor({ ... })`). The new value model allows you to generate a string value for some property, depending on the values of other properties. Mainly this feature is designed to generate a step name automatically.
4+
* The `StepModel` interface has one new property: `label`. The label is used to display a step name in the editor and the toolbox.
5+
6+
**Breaking changes:**
7+
8+
* The `ValueModelFactory` type is changed to the interface.
9+
* The `ValueModelContext` class is renamed to `ValueContext`.
10+
* The `VariablesProvider` class skips variables from own step.
11+
112
## 0.3.2
213

314
* The `StepModel` interface has two new properties: `category` and `description`. The category is used to group steps in the toolbox. The description is used to display an additional information about a step in the editor.

demos/webpack-app/package.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,10 @@
1616
"dependencies": {
1717
"xstate": "^4.37.2",
1818
"sequential-workflow-model": "^0.1.3",
19-
"sequential-workflow-designer": "^0.13.2",
19+
"sequential-workflow-designer": "^0.13.3",
2020
"sequential-workflow-machine": "^0.2.0",
21-
"sequential-workflow-editor-model": "^0.3.2",
22-
"sequential-workflow-editor": "^0.3.2"
21+
"sequential-workflow-editor-model": "^0.4.0",
22+
"sequential-workflow-editor": "^0.4.0"
2323
},
2424
"devDependencies": {
2525
"ts-loader": "^9.4.2",

demos/webpack-app/src/playground/app.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,8 @@ export class App {
3333
}
3434
},
3535
toolbox: {
36-
groups: editorProvider.getToolboxGroups()
36+
groups: editorProvider.getToolboxGroups(),
37+
labelProvider: editorProvider.createStepLabelProvider()
3738
},
3839
undoStackSize: 10,
3940
definitionWalker

demos/webpack-app/src/playground/default-state.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,8 @@ const definition: MyDefinition = {
5454
componentType: 'task',
5555
properties: {
5656
message: { modelId: 'string', value: '🌺 TRUE 🌺' },
57-
variables: { variables: [{ name: 'index', type: 'number' }] }
57+
variables: { variables: [{ name: 'index', type: 'number' }] },
58+
note: { modelId: 'string', value: '0!' }
5859
}
5960
}
6061
],
@@ -66,7 +67,8 @@ const definition: MyDefinition = {
6667
componentType: 'task',
6768
properties: {
6869
message: { modelId: 'string', value: '🌼 FALSE 🌼' },
69-
variables: { variables: [{ name: 'index', type: 'number' }] }
70+
variables: { variables: [{ name: 'index', type: 'number' }] },
71+
note: { modelId: 'generatedString', value: 'Dumped 1 variables' }
7072
}
7173
}
7274
]

demos/webpack-app/src/playground/model/calculate-step-model.ts

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
1+
import { formatVariableName } from 'sequential-workflow-editor';
12
import {
23
Dynamic,
34
NullableVariable,
45
ValueKnownType,
56
choiceValueModel,
67
createStepModel,
78
dynamicValueModel,
9+
generatedStringValueModel,
810
nullableVariableValueModel,
911
numberValueModel
1012
} from 'sequential-workflow-editor-model';
@@ -25,6 +27,27 @@ export const calculateStepModel = createStepModel<CalculateStep>('calculate', 't
2527
step.category('Values');
2628
step.description('Calculate value from two numbers. Result is stored in variable.');
2729

30+
step.name()
31+
.value(
32+
generatedStringValueModel({
33+
generator: context => {
34+
const result = context.formatPropertyValue('result', value => formatVariableName(value.name));
35+
const a = context.formatPropertyValue('a', ({ value }) => {
36+
return value && typeof value === 'object' ? formatVariableName(value.name) : String(value ?? '?');
37+
});
38+
const operator = context.getPropertyValue('operator');
39+
const b = context.formatPropertyValue('b', ({ value }) => {
40+
return value && typeof value === 'object' ? formatVariableName(value.name) : String(value ?? '?');
41+
});
42+
return `${result} = ${a} ${operator} ${b}`;
43+
}
44+
})
45+
)
46+
.dependentProperty('result')
47+
.dependentProperty('a')
48+
.dependentProperty('b')
49+
.dependentProperty('operator');
50+
2851
const val = dynamicValueModel({
2952
models: [
3053
numberValueModel({}),

demos/webpack-app/src/playground/model/log-step-model.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
11
import {
22
AnyVariables,
33
Dynamic,
4+
GeneratedStringContext,
45
NullableVariable,
56
ValueKnownType,
67
anyVariablesValueModel,
78
createStepModel,
89
dynamicValueModel,
10+
generatedStringValueModel,
911
nullableVariableValueModel,
1012
stringValueModel
1113
} from 'sequential-workflow-editor-model';
@@ -17,6 +19,7 @@ export interface LogStep extends Step {
1719
properties: {
1820
message: Dynamic<string | NullableVariable>;
1921
variables: AnyVariables;
22+
note: Dynamic<string>;
2023
};
2124
}
2225

@@ -38,4 +41,21 @@ export const logStepModel = createStepModel<LogStep>('log', 'task', step => {
3841
.label('Text');
3942

4043
step.property('variables').value(anyVariablesValueModel({})).label('Log variables');
44+
45+
step.property('note')
46+
.dependentProperty('variables')
47+
.value(
48+
dynamicValueModel({
49+
models: [
50+
generatedStringValueModel({
51+
generator: (context: GeneratedStringContext<LogStep['properties']>) => {
52+
// TODO: if the type would be deleted from arguments, then the auto type is wrong.
53+
const variables = context.getPropertyValue('variables');
54+
return `Dumped ${variables.variables.length} variables`;
55+
}
56+
}),
57+
stringValueModel({})
58+
]
59+
})
60+
);
4161
});

demos/webpack-app/src/playground/storage.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { MyDefinition } from './model/definition-model';
22
import { RawInputData } from './playground';
33

4-
const version = 2;
4+
const version = 3;
55
const definitionKey = `definition_${version}`;
66
const inputDataKey = `inputData_${version}`;
77

editor/css/editor.css

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,12 @@
100100
.swe-input:focus {
101101
border-color: #b7b7b7;
102102
}
103+
.swe-input[readonly] {
104+
color: #666;
105+
background: #f7f7f7;
106+
cursor: not-allowed;
107+
border-style: dashed;
108+
}
103109
.swe-select-small {
104110
padding: 2px 5px;
105111
}

editor/package.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "sequential-workflow-editor",
3-
"version": "0.3.2",
3+
"version": "0.4.0",
44
"type": "module",
55
"main": "./lib/esm/index.js",
66
"types": "./lib/index.d.ts",
@@ -46,11 +46,11 @@
4646
"prettier:fix": "prettier --write ./src ./css"
4747
},
4848
"dependencies": {
49-
"sequential-workflow-editor-model": "^0.3.2",
49+
"sequential-workflow-editor-model": "^0.4.0",
5050
"sequential-workflow-model": "^0.1.3"
5151
},
5252
"peerDependencies": {
53-
"sequential-workflow-editor-model": "^0.3.2",
53+
"sequential-workflow-editor-model": "^0.4.0",
5454
"sequential-workflow-model": "^0.1.3"
5555
},
5656
"devDependencies": {

editor/src/components/input-component.ts

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,30 +4,46 @@ import { Html } from '../core';
44

55
export interface InputComponent extends Component {
66
onChanged: SimpleEvent<string>;
7+
setValue(value: string): void;
8+
getValue(): string;
79
}
810

911
export interface InputConfiguration {
1012
type?: 'text' | 'number';
13+
isReadonly?: boolean;
1114
placeholder?: string;
1215
}
1316

14-
export function inputComponent(value: string, configuration?: InputConfiguration): InputComponent {
17+
export function inputComponent(startValue: string, configuration?: InputConfiguration): InputComponent {
1518
const onChanged = new SimpleEvent<string>();
1619

20+
function setValue(value: string) {
21+
view.value = value;
22+
}
23+
24+
function getValue(): string {
25+
return view.value;
26+
}
27+
1728
const view = Html.element('input', {
1829
class: 'swe-input swe-stretched',
1930
type: configuration?.type ?? 'text'
2031
});
2132
if (configuration?.placeholder) {
2233
view.setAttribute('placeholder', configuration.placeholder);
2334
}
24-
view.value = value;
35+
if (configuration?.isReadonly) {
36+
view.setAttribute('readonly', 'readonly');
37+
}
38+
view.value = startValue;
2539
view.addEventListener('input', () => {
2640
onChanged.forward(view.value);
2741
});
2842

2943
return {
3044
view,
31-
onChanged
45+
onChanged,
46+
setValue,
47+
getValue
3248
};
3349
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import { StackedSimpleEvent } from './stacked-simple-event';
2+
3+
describe('StackedSimpleEvent', () => {
4+
let event: StackedSimpleEvent<number>;
5+
6+
beforeEach(() => {
7+
event = new StackedSimpleEvent<number>();
8+
});
9+
10+
it('should emit an event with a single value when a single value is pushed', done => {
11+
event.subscribe(values => {
12+
expect(values).toEqual([1]);
13+
done();
14+
});
15+
16+
event.push(1);
17+
});
18+
19+
it('should emit an event with multiple values when multiple values are pushed', done => {
20+
event.subscribe(values => {
21+
expect(values).toEqual([1, 2, 3]);
22+
done();
23+
});
24+
25+
event.push(1);
26+
event.push(2);
27+
event.push(3);
28+
});
29+
});
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import { SimpleEvent, SimpleEventListener } from 'sequential-workflow-editor-model';
2+
3+
export class StackedSimpleEvent<T> {
4+
private readonly event = new SimpleEvent<T[]>();
5+
6+
private readonly stack: T[] = [];
7+
private to: ReturnType<typeof setTimeout> | null = null;
8+
9+
public push(value: T) {
10+
this.stack.push(value);
11+
12+
if (this.to) {
13+
return;
14+
}
15+
16+
this.to = setTimeout(() => {
17+
this.to = null;
18+
this.event.forward(this.stack);
19+
this.stack.length = 0;
20+
});
21+
}
22+
23+
public subscribe(listener: SimpleEventListener<T[]>) {
24+
this.event.subscribe(listener);
25+
}
26+
}

editor/src/editor-header.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { Html } from './core';
33
import { appendMultilineText } from './core/append-multiline-text';
44

55
export interface EditorHeaderData {
6-
title: string;
6+
label: string;
77
description?: string;
88
}
99

@@ -12,7 +12,7 @@ export class EditorHeader implements Component {
1212
const view = Html.element('div', { class: 'swe-editor-header' });
1313

1414
const title = Html.element('h3', { class: 'swe-editor-header-title' });
15-
title.textContent = data.title;
15+
title.textContent = data.label;
1616
view.appendChild(title);
1717

1818
if (data.description) {

editor/src/editor-provider.ts

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import {
88
RootValidator,
99
StepEditorContext,
1010
StepEditorProvider,
11+
StepLabelProvider,
1112
StepValidator,
1213
ToolboxGroup
1314
} from './external-types';
@@ -43,7 +44,7 @@ export class EditorProvider<TDefinition extends Definition> {
4344
const rootContext = DefinitionContext.createForRoot(definition, this.definitionModel, this.definitionWalker);
4445
const typeClassName = 'root';
4546
const editor = Editor.create(null, this.definitionModel.root.properties, rootContext, this.services, typeClassName);
46-
editor.onValueChanged.subscribe(() => {
47+
editor.onValuesChanged.subscribe(() => {
4748
context.notifyPropertiesChanged();
4849
});
4950
return editor.root;
@@ -65,14 +66,14 @@ export class EditorProvider<TDefinition extends Definition> {
6566
const headerData: EditorHeaderData | null = this.configuration.isHeaderHidden
6667
? null
6768
: {
68-
title: stepModel.name.value.getDefaultValue(this.activator),
69+
label: stepModel.label,
6970
description: stepModel.description
7071
};
7172

7273
const editor = Editor.create(headerData, propertyModels, definitionContext, this.services, typeClassName);
7374

74-
editor.onValueChanged.subscribe((path: Path) => {
75-
if (path.equals(stepModel.name.value.path)) {
75+
editor.onValuesChanged.subscribe((paths: Path[]) => {
76+
if (paths.some(path => path.equals(stepModel.name.value.path))) {
7677
context.notifyNameChanged();
7778
} else {
7879
context.notifyPropertiesChanged();
@@ -119,4 +120,11 @@ export class EditorProvider<TDefinition extends Definition> {
119120
groups.sort((a, b) => a.name.localeCompare(b.name));
120121
return groups;
121122
}
123+
124+
public createStepLabelProvider(): StepLabelProvider {
125+
return (step: { type: string }): string => {
126+
const stepModel = this.definitionModel.steps[step.type];
127+
return stepModel.label;
128+
};
129+
}
122130
}

editor/src/editor.ts

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
1-
import { DefinitionContext, Path, PropertyModel, PropertyModels, SimpleEvent } from 'sequential-workflow-editor-model';
1+
import { DefinitionContext, Path, PropertyModel, PropertyModels } from 'sequential-workflow-editor-model';
22
import { PropertyEditor } from './property-editor/property-editor';
33
import { EditorServices, ValueEditorEditorFactoryResolver } from './value-editors';
44
import { EditorHeader, EditorHeaderData } from './editor-header';
5+
import { StackedSimpleEvent } from './core/stacked-simple-event';
56

67
export class Editor {
78
public static create(
@@ -35,21 +36,21 @@ export class Editor {
3536
return editor;
3637
}
3738

38-
public readonly onValueChanged = new SimpleEvent<Path>();
39+
public readonly onValuesChanged = new StackedSimpleEvent<Path>();
3940

4041
private constructor(public readonly root: HTMLElement, private readonly editors: Map<PropertyModel, PropertyEditor>) {}
4142

4243
private readonly onValueChangedHandler = (path: Path) => {
43-
this.onValueChanged.forward(path);
44+
this.onValuesChanged.push(path);
45+
// console.log('onValueChangedHandler', path.toString());
4446

4547
this.editors.forEach((editor, propertyModel) => {
46-
if (propertyModel.value.path.equals(path)) {
48+
if (path.startsWith(propertyModel.path)) {
4749
// Skip self
4850
return;
4951
}
50-
51-
if (propertyModel.dependencies.some(dep => dep.startsWith(path))) {
52-
editor.validate();
52+
if (propertyModel.dependencies.some(dependency => path.startsWith(dependency))) {
53+
editor.reloadDependencies();
5354
}
5455
});
5556
};

0 commit comments

Comments
 (0)