Skip to content

Commit

Permalink
0.7.2. (#17)
Browse files Browse the repository at this point in the history
  • Loading branch information
b4rtaz authored Jul 19, 2023
1 parent 70dec25 commit 770df05
Show file tree
Hide file tree
Showing 31 changed files with 409 additions and 68 deletions.
22 changes: 22 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,25 @@
## 0.7.2

We added a new type of a validator: step validator. It allows to restrict a placement of a step in a definition. For example, you can enforce that a step can be placed only inside a specific step.

```ts
createStepModel<WriteSocketStep>('writeSocket', 'task', step => {
step.validator({
validate(context: StepValidatorContext) {
const parentTypes = context.getParentStepTypes()
return parentTypes.includes('socket');
? null // No errors
: 'The write socket step must be inside a socket.';
}
});
});
```

Additionally we've renamed:

* the `CustomValidatorContext` class to `PropertyValidatorContext`,
* the `customValidator` method of the `PropertyModelBuilder` class to `validator`.

## 0.7.1

This version renames all `*ValueModel` functions to `create*ValueModel`, adding the `create` prefix.
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ Powerful workflow editor builder for sequential workflows. Written in TypeScript

* [🛠 Playground](https://nocode-js.github.io/sequential-workflow-editor/webpack-app/public/playground.html)
* [📖 Editors](https://nocode-js.github.io/sequential-workflow-editor/webpack-app/public/editors.html)
* [🎯 Placement Restrictions](https://nocode-js.github.io/sequential-workflow-editor/webpack-app/public/placement-restrictions.html)

## 🚀 Installation

Expand Down
4 changes: 2 additions & 2 deletions demos/webpack-app/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@
"sequential-workflow-model": "^0.1.4",
"sequential-workflow-designer": "^0.13.5",
"sequential-workflow-machine": "^0.3.0",
"sequential-workflow-editor-model": "^0.7.1",
"sequential-workflow-editor": "^0.7.1"
"sequential-workflow-editor-model": "^0.7.2",
"sequential-workflow-editor": "^0.7.2"
},
"devDependencies": {
"ts-loader": "^9.4.2",
Expand Down
26 changes: 26 additions & 0 deletions demos/webpack-app/public/assets/placement-restrictions.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
html,
body,
#designer {
margin: 0;
padding: 0;
width: 100vw;
height: 100vh;
overflow: hidden;
}
body,
input,
textarea {
font: 14px/1.3em Arial, Verdana, sans-serif;
}
.sqd-global-editor {
padding: 10px;
line-height: 1.3em;
box-sizing: border-box;
}
a {
color: #000;
text-decoration: underline;
}
a:hover {
text-decoration: none;
}
2 changes: 1 addition & 1 deletion demos/webpack-app/public/editors.html
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<!DOCTYPE html>
<html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>📖 Editors - Sequential Workflow Editor</title>
Expand Down
14 changes: 14 additions & 0 deletions demos/webpack-app/public/placement-restrictions.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>🎯 Placement Restrictions - Sequential Workflow Editor</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0" />
<link rel="icon" href="./assets/favicon.ico" />
<script src="./builds/placement-restrictions.js" defer></script>
<link rel="stylesheet" href="./assets/placement-restrictions.css" />
</head>
<body>
<div id="designer"></div>
</body>
</html>
2 changes: 1 addition & 1 deletion demos/webpack-app/public/playground.html
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<!DOCTYPE html>
<html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>🛠 Playground - Sequential Workflow Editor</title>
Expand Down
50 changes: 50 additions & 0 deletions demos/webpack-app/src/placement-restrictions/app.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import { EditorProvider } from 'sequential-workflow-editor';
import { SocketStep, definitionModel } from './definition-model';
import { Designer, Uid } from 'sequential-workflow-designer';

import 'sequential-workflow-designer/css/designer.css';
import 'sequential-workflow-designer/css/designer-light.css';
import 'sequential-workflow-editor/css/editor.css';

export class App {
public static create() {
const placeholder = document.getElementById('designer') as HTMLElement;

const editorProvider = EditorProvider.create(definitionModel, {
uidGenerator: Uid.next
});

const definition = editorProvider.activateDefinition();
const loop = editorProvider.activateStep('socket') as SocketStep;
loop.sequence.push(editorProvider.activateStep('writeSocket'));
const break_ = editorProvider.activateStep('writeSocket');
definition.sequence.push(loop);
definition.sequence.push(break_);

Designer.create(placeholder, definition, {
controlBar: true,
editors: {
globalEditorProvider: () => {
const editor = document.createElement('div');
editor.innerHTML =
'This example shows how to restrict the placement of steps. The write socket step can only be placed inside a socket step. <a href="https://github.com/nocode-js/sequential-workflow-editor">GitHub</a>';
return editor;
},
stepEditorProvider: editorProvider.createStepEditorProvider()
},
validator: {
step: editorProvider.createStepValidator(),
root: editorProvider.createRootValidator()
},
steps: {
iconUrlProvider: () => './assets/icon-task.svg'
},
toolbox: {
groups: editorProvider.getToolboxGroups(),
labelProvider: editorProvider.createStepLabelProvider()
}
});
}
}

document.addEventListener('DOMContentLoaded', App.create, false);
59 changes: 59 additions & 0 deletions demos/webpack-app/src/placement-restrictions/definition-model.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import {
createDefinitionModel,
createNumberValueModel,
createSequentialStepModel,
createStepModel,
createStringValueModel
} from 'sequential-workflow-editor-model';
import { SequentialStep } from 'sequential-workflow-model';

export interface SocketStep extends SequentialStep {
type: 'socket';
componentType: 'container';
properties: {
ip: string;
port: number;
};
}

export interface WriteSocketStep extends SequentialStep {
type: 'writeSocket';
componentType: 'task';
properties: {
data: string;
};
}

export const definitionModel = createDefinitionModel(model => {
model.root(() => {
//
});
model.steps([
createSequentialStepModel<SocketStep>('socket', 'container', step => {
step.property('ip').value(
createStringValueModel({
defaultValue: '127.0.0.1'
})
);
step.property('port').value(
createNumberValueModel({
defaultValue: 5000
})
);
}),
createStepModel<WriteSocketStep>('writeSocket', 'task', step => {
step.property('data').value(
createStringValueModel({
defaultValue: 'Hello World!'
})
);

step.validator({
validate(context) {
const parentTypes = context.getParentStepTypes();
return parentTypes.includes('socket') ? null : 'The write socket step must be inside a socket.';
}
});
})
]);
});
2 changes: 1 addition & 1 deletion demos/webpack-app/src/playground/model/root-model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ export const rootModel = createRootModel<MyDefinition>(root => {
.hint('Variables passed to the workflow from the outside.')
.value(createVariableDefinitionsValueModel({}))
.dependentProperty('outputs')
.customValidator({
.validator({
validate(context) {
const inputs = context.getPropertyValue('outputs');
return inputs.variables.length > 0 ? null : 'At least one input is required';
Expand Down
3 changes: 2 additions & 1 deletion demos/webpack-app/webpack.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,5 +29,6 @@ function bundle(name) {

module.exports = [
bundle('playground'),
bundle('editors')
bundle('editors'),
bundle('placement-restrictions')
];
3 changes: 3 additions & 0 deletions editor/css/editor.css
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,9 @@
margin: 0 0 10px;
color: #666;
}
.swe-editor > .swe-validation-error {
margin: 0 10px;
}

/* properties */

Expand Down
6 changes: 3 additions & 3 deletions editor/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "sequential-workflow-editor",
"version": "0.7.1",
"version": "0.7.2",
"type": "module",
"main": "./lib/esm/index.js",
"types": "./lib/index.d.ts",
Expand Down Expand Up @@ -46,11 +46,11 @@
"prettier:fix": "prettier --write ./src ./css"
},
"dependencies": {
"sequential-workflow-editor-model": "^0.7.1",
"sequential-workflow-editor-model": "^0.7.2",
"sequential-workflow-model": "^0.1.4"
},
"peerDependencies": {
"sequential-workflow-editor-model": "^0.7.1",
"sequential-workflow-editor-model": "^0.7.2",
"sequential-workflow-model": "^0.1.4"
},
"devDependencies": {
Expand Down
6 changes: 3 additions & 3 deletions editor/src/components/property-validation-error-component.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { CustomValidator, CustomValidatorContext } from 'sequential-workflow-editor-model';
import { PropertyValidator, PropertyValidatorContext } from 'sequential-workflow-editor-model';
import { Component } from './component';
import { validationErrorComponent } from './validation-error-component';

Expand All @@ -7,8 +7,8 @@ export interface PropertyValidationErrorComponent extends Component {
}

export function propertyValidationErrorComponent(
validator: CustomValidator,
context: CustomValidatorContext
validator: PropertyValidator,
context: PropertyValidatorContext
): PropertyValidationErrorComponent {
const validation = validationErrorComponent();

Expand Down
22 changes: 18 additions & 4 deletions editor/src/editor-provider.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,13 @@
import { Definition, DefinitionWalker, Step } from 'sequential-workflow-model';
import { Editor } from './editor';
import { DefinitionContext, DefinitionModel, ModelActivator, DefinitionValidator, Path } from 'sequential-workflow-editor-model';
import { Editor, EditorValidator } from './editor';
import {
DefinitionContext,
DefinitionModel,
ModelActivator,
DefinitionValidator,
Path,
StepValidatorContext
} from 'sequential-workflow-editor-model';
import { EditorServices, ValueEditorEditorFactoryResolver } from './value-editors';
import {
GlobalEditorContext,
Expand Down Expand Up @@ -43,7 +50,7 @@ export class EditorProvider<TDefinition extends Definition> {
return (definition: Definition, context: GlobalEditorContext): HTMLElement => {
const rootContext = DefinitionContext.createForRoot(definition, this.definitionModel, this.definitionWalker);
const typeClassName = 'root';
const editor = Editor.create(null, this.definitionModel.root.properties, rootContext, this.services, typeClassName);
const editor = Editor.create(null, null, this.definitionModel.root.properties, rootContext, this.services, typeClassName);
editor.onValuesChanged.subscribe(() => {
context.notifyPropertiesChanged();
});
Expand All @@ -65,7 +72,14 @@ export class EditorProvider<TDefinition extends Definition> {
description: stepModel.description
};

const editor = Editor.create(headerData, propertyModels, definitionContext, this.services, typeClassName);
let validator: EditorValidator | null = null;
if (stepModel.validator) {
const stepValidator = stepModel.validator;
const stepValidatorContext = StepValidatorContext.create(definitionContext);
validator = () => stepValidator.validate(stepValidatorContext);
}

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

editor.onValuesChanged.subscribe((paths: Path[]) => {
if (paths.some(path => path.equals(stepModel.name.value.path))) {
Expand Down
27 changes: 25 additions & 2 deletions editor/src/editor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,14 @@ import { PropertyEditor } from './property-editor/property-editor';
import { EditorServices, ValueEditorEditorFactoryResolver } from './value-editors';
import { EditorHeader, EditorHeaderData } from './editor-header';
import { StackedSimpleEvent } from './core/stacked-simple-event';
import { ValidationErrorComponent, validationErrorComponent } from './components/validation-error-component';

export type EditorValidator = () => string | null;

export class Editor {
public static create(
headerData: EditorHeaderData | null,
validator: EditorValidator | null,
propertyModels: PropertyModels,
definitionContext: DefinitionContext,
editorServices: EditorServices,
Expand All @@ -20,6 +24,12 @@ export class Editor {
root.appendChild(header.view);
}

let validationComponent: ValidationErrorComponent | null = null;
if (validator) {
validationComponent = validationErrorComponent();
root.appendChild(validationComponent.view);
}

const editors = new Map<PropertyModel, PropertyEditor>();
for (const propertyModel of propertyModels) {
if (ValueEditorEditorFactoryResolver.isHidden(propertyModel.value.id)) {
Expand All @@ -31,14 +41,27 @@ export class Editor {
editors.set(propertyModel, propertyEditor);
}

const editor = new Editor(root, editors);
const editor = new Editor(root, editors, validator, validationComponent);
editors.forEach(e => e.onValueChanged.subscribe(editor.onValueChangedHandler));
editor.validate();
return editor;
}

public readonly onValuesChanged = new StackedSimpleEvent<Path>();

private constructor(public readonly root: HTMLElement, private readonly editors: Map<PropertyModel, PropertyEditor>) {}
private constructor(
public readonly root: HTMLElement,
private readonly editors: Map<PropertyModel, PropertyEditor>,
private readonly validator: EditorValidator | null,
private readonly validationErrorComponent: ValidationErrorComponent | null
) {}

private validate() {
if (this.validator && this.validationErrorComponent) {
const error = this.validator();
this.validationErrorComponent.setError(error);
}
}

private readonly onValueChangedHandler = (path: Path) => {
this.onValuesChanged.push(path);
Expand Down
8 changes: 4 additions & 4 deletions editor/src/property-editor/property-editor.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import {
CustomValidatorContext,
PropertyValidatorContext,
DefinitionContext,
Path,
PropertyModel,
Expand Down Expand Up @@ -63,9 +63,9 @@ export class PropertyEditor implements Component {
}

let validationError: PropertyValidationErrorComponent | null = null;
if (propertyModel.customValidator) {
const customValidationContext = CustomValidatorContext.create(propertyModel, definitionContext);
validationError = propertyValidationErrorComponent(propertyModel.customValidator, customValidationContext);
if (propertyModel.validator) {
const validatorContext = PropertyValidatorContext.create(propertyModel, definitionContext);
validationError = propertyValidationErrorComponent(propertyModel.validator, validatorContext);
view.appendChild(validationError.view);
}

Expand Down
Loading

0 comments on commit 770df05

Please sign in to comment.