From 7a6303a42d0c94e4871d9a3fe464ee24801ebbc2 Mon Sep 17 00:00:00 2001 From: b4rtaz Date: Fri, 20 Jun 2025 00:03:07 +0200 Subject: [PATCH 1/9] 0.30.0. --- designer/sass/designer-dark.scss | 6 + designer/sass/designer-light.scss | 1 + designer/sass/designer-soft.scss | 1 + designer/sass/designer-theme.scss | 30 +++ designer/src/api/designer-api.ts | 4 +- designer/src/api/toolbox-api.ts | 6 +- designer/src/behaviors/drag-step-behavior.ts | 2 +- designer/src/component-context.ts | 4 +- designer/src/core/icons.ts | 1 + designer/src/designer-configuration.ts | 10 + designer/src/designer-context.ts | 14 +- designer/src/designer-extension.ts | 28 +-- .../extensions/steps-designer-extension.ts | 6 + designer/src/modifier/state-modifier.ts | 46 ++--- designer/src/services.ts | 9 +- designer/src/smart-editor/editor.spec.ts | 3 +- .../src/toolbox/toolbox-data-provider.spec.ts | 10 +- designer/src/toolbox/toolbox-data-provider.ts | 2 +- .../src/workspace/common-views/join-view.ts | 2 +- designer/src/workspace/component.ts | 2 +- ...h-pad-step-component-view-configuration.ts | 11 ++ .../launch-pad-step-component-view.ts | 181 ++++++++++++++++++ ...launch-pad-step-extension-configuration.ts | 6 + .../launch-pad-step-extension.ts | 33 ++++ ...efault-placeholder-controller-extension.ts | 9 - designer/src/workspace/placeholder/index.ts | 1 + .../placeholder/placeholder-controller.ts | 15 ++ .../placeholder/rect-placeholder-extension.ts | 32 +++- .../workspace/placeholder/rect-placeholder.ts | 6 +- .../default-sequence-component-view.ts | 11 +- ...-root-component-extension-configuration.ts | 2 +- .../start-stop-root-component-extension.ts | 26 +-- ...-stop-root-component-view-configuration.ts | 4 +- .../start-stop-root-component-view.spec.ts | 4 +- .../start-stop-root-component-view.ts | 12 +- .../start-stop-root-component.spec.ts | 4 +- .../step-component-view-context-factory.ts | 17 ++ .../task-step/task-step-component-view.ts | 2 +- examples/assets/icon-trigger.svg | 1 + examples/assets/triggers.js | 135 +++++++++++++ examples/triggers.html | 55 ++++++ 41 files changed, 646 insertions(+), 108 deletions(-) create mode 100644 designer/src/workspace/launch-pad-step/launch-pad-step-component-view-configuration.ts create mode 100644 designer/src/workspace/launch-pad-step/launch-pad-step-component-view.ts create mode 100644 designer/src/workspace/launch-pad-step/launch-pad-step-extension-configuration.ts create mode 100644 designer/src/workspace/launch-pad-step/launch-pad-step-extension.ts delete mode 100644 designer/src/workspace/placeholder/default-placeholder-controller-extension.ts create mode 100644 designer/src/workspace/placeholder/placeholder-controller.ts create mode 100644 examples/assets/icon-trigger.svg create mode 100644 examples/assets/triggers.js create mode 100644 examples/triggers.html diff --git a/designer/sass/designer-dark.scss b/designer/sass/designer-dark.scss index 5fffb7c2..b0ae97e3 100644 --- a/designer/sass/designer-dark.scss +++ b/designer/sass/designer-dark.scss @@ -65,4 +65,10 @@ $outputFillColor: #707070 ); @include sqd-theme-switch-step-component('dark', $inputStrokeColor: #707070, $inputFillColor: #c6c6c6); +@include sqd-theme-launch-pad-step-component( + 'dark', + $emptyInputStrokeColor: #707070, + $emptyInputFillColor: #c6c6c6, + $emptyOutputFillColor: #707070 +); @include sqd-theme-container-step-component('dark', $inputStrokeColor: #707070, $inputFillColor: #c6c6c6); diff --git a/designer/sass/designer-light.scss b/designer/sass/designer-light.scss index 1a2afb5d..40dd8367 100644 --- a/designer/sass/designer-light.scss +++ b/designer/sass/designer-light.scss @@ -17,4 +17,5 @@ @include sqd-theme-task-step-component('light'); @include sqd-theme-switch-step-component('light'); +@include sqd-theme-launch-pad-step-component('light'); @include sqd-theme-container-step-component('light'); diff --git a/designer/sass/designer-soft.scss b/designer/sass/designer-soft.scss index 0dfd24d2..de28668a 100644 --- a/designer/sass/designer-soft.scss +++ b/designer/sass/designer-soft.scss @@ -44,4 +44,5 @@ $joinColor: #2a2a2a; $labelSecondaryFillColor: #e9e9e9, $inputStrokeColor: $joinColor ); +@include sqd-theme-launch-pad-step-component('soft', $emptyInputStrokeColor: $joinColor, $emptyOutputFillColor: $joinColor); @include sqd-theme-container-step-component('soft', $labelFillColor: #3747dd, $inputStrokeColor: $joinColor); diff --git a/designer/sass/designer-theme.scss b/designer/sass/designer-theme.scss index 0bf9eaa5..a2b93ba6 100644 --- a/designer/sass/designer-theme.scss +++ b/designer/sass/designer-theme.scss @@ -354,6 +354,36 @@ } } +@mixin sqd-theme-launch-pad-step-component( + $theme, + $stepType: '', + $emptyInputStrokeWidth: 2, + $emptyInputStrokeColor: #000, + $emptyInputFillColor: #fff, + $emptyOutputFillColor: #000, + $emptyOutputStrokeWidth: 0, + $emptyOutputStrokeColor: #000, + $emptyIconFillColor: #b3b3b3 +) { + .sqd-theme-#{$theme} .sqd-step-launch-pad#{if($stepType != '', '.sqd-type-' + $stepType, '')} { + & > g > { + @include _sqd-input( + $inputFillColor: $emptyInputFillColor, + $inputStrokeWidth: $emptyInputStrokeWidth, + $inputStrokeColor: $emptyInputStrokeColor + ); + @include _sqd-output( + $outputFillColor: $emptyOutputFillColor, + $outputStrokeWidth: $emptyOutputStrokeWidth, + $outputStrokeColor: $emptyOutputStrokeColor + ); + } + & > g > .sqd-launch-pad-empty-icon { + fill: $emptyIconFillColor; + } + } +} + @mixin sqd-theme-container-step-component( $theme, $stepType: '', diff --git a/designer/src/api/designer-api.ts b/designer/src/api/designer-api.ts index 85327ee7..a88bad93 100644 --- a/designer/src/api/designer-api.ts +++ b/designer/src/api/designer-api.ts @@ -14,15 +14,15 @@ export class DesignerApi { const workspace = new WorkspaceApi(context.state, context.definitionWalker, context.workspaceController); const viewportController = context.services.viewportController.create(workspace); const toolboxDataProvider = new ToolboxDataProvider( - context.componentContext.iconProvider, context.i18n, + context.componentContext.iconProvider, context.configuration.toolbox ); return new DesignerApi( context.configuration.shadowRoot, ControlBarApi.create(context.state, context.historyController, context.stateModifier), - new ToolboxApi(context.state, context, context.behaviorController, toolboxDataProvider, context.configuration.uidGenerator), + new ToolboxApi(context.state, context, context.behaviorController, toolboxDataProvider, context.uidGenerator), new EditorApi(context.state, context.definitionWalker, context.stateModifier), workspace, new ViewportApi(context.state, context.workspaceController, viewportController), diff --git a/designer/src/api/toolbox-api.ts b/designer/src/api/toolbox-api.ts index c2bda4cd..e3cb149e 100644 --- a/designer/src/api/toolbox-api.ts +++ b/designer/src/api/toolbox-api.ts @@ -1,6 +1,6 @@ import { Step } from '../definition'; import { BehaviorController } from '../behaviors/behavior-controller'; -import { ObjectCloner, SimpleEventListener, Uid, Vector } from '../core'; +import { ObjectCloner, SimpleEventListener, Vector } from '../core'; import { StepDefinition, UidGenerator } from '../designer-configuration'; import { DesignerState } from '../designer-state'; import { DragStepBehavior } from '../behaviors/drag-step-behavior'; @@ -13,7 +13,7 @@ export class ToolboxApi { private readonly designerContext: DesignerContext, private readonly behaviorController: BehaviorController, private readonly toolboxDataProvider: ToolboxDataProvider, - private readonly uidGenerator: UidGenerator | undefined + private readonly uidGenerator: UidGenerator ) {} public isCollapsed(): boolean { @@ -52,7 +52,7 @@ export class ToolboxApi { private activateStep(step: StepDefinition): Step { const newStep = ObjectCloner.deepClone(step) as Step; - newStep.id = this.uidGenerator ? this.uidGenerator() : Uid.next(); + newStep.id = this.uidGenerator(); return newStep; } } diff --git a/designer/src/behaviors/drag-step-behavior.ts b/designer/src/behaviors/drag-step-behavior.ts index 3fdf82fc..41a22d1e 100644 --- a/designer/src/behaviors/drag-step-behavior.ts +++ b/designer/src/behaviors/drag-step-behavior.ts @@ -9,7 +9,7 @@ import { DesignerState } from '../designer-state'; import { StateModifier } from '../modifier/state-modifier'; import { WorkspaceController } from '../workspace/workspace-controller'; import { StepComponent } from '../workspace/step-component'; -import { PlaceholderController } from '../designer-extension'; +import { PlaceholderController } from '../workspace/placeholder/placeholder-controller'; export class DragStepBehavior implements Behavior { public static create(designerContext: DesignerContext, step: Step, draggedStepComponent?: StepComponent): DragStepBehavior { diff --git a/designer/src/component-context.ts b/designer/src/component-context.ts index 67b82692..1b42ef83 100644 --- a/designer/src/component-context.ts +++ b/designer/src/component-context.ts @@ -2,20 +2,20 @@ import { DefinitionWalker } from 'sequential-workflow-model'; import { DefinitionValidator } from './core/definition-validator'; import { IconProvider } from './core/icon-provider'; import { DesignerConfiguration, I18n, PreferenceStorage } from './designer-configuration'; -import { PlaceholderController } from './designer-extension'; import { DesignerState } from './designer-state'; import { Services } from './services'; import { StepComponentFactory } from './workspace/step-component-factory'; import { StepExtensionResolver } from './workspace/step-extension-resolver'; +import { PlaceholderController } from './workspace/placeholder/placeholder-controller'; export class ComponentContext { public static create( configuration: DesignerConfiguration, state: DesignerState, stepExtensionResolver: StepExtensionResolver, + placeholderController: PlaceholderController, definitionWalker: DefinitionWalker, preferenceStorage: PreferenceStorage, - placeholderController: PlaceholderController, i18n: I18n, services: Services ): ComponentContext { diff --git a/designer/src/core/icons.ts b/designer/src/core/icons.ts index 9afc265b..99fe0ce4 100644 --- a/designer/src/core/icons.ts +++ b/designer/src/core/icons.ts @@ -31,6 +31,7 @@ export class Icons { public static stop = 'M10.75 37.25V10.7H37.3v26.55Z'; public static folder = 'M7.05 40q-1.2 0-2.1-.925-.9-.925-.9-2.075V11q0-1.15.9-2.075Q5.85 8 7.05 8h14l3 3h17q1.15 0 2.075.925.925.925.925 2.075v23q0 1.15-.925 2.075Q42.2 40 41.05 40Z'; + public static trigger = 'm12.93 46.3 8.93-16.75-17.87-2.23 26.8-25.68h4.47l-8.93 16.75 17.87 2.23L17.4 46.3h-4.47z'; public static appendPath(parent: SVGElement, pathClassName: string, d: string, size: number): SVGGElement { const g = Dom.svg('g'); diff --git a/designer/src/designer-configuration.ts b/designer/src/designer-configuration.ts index e56bb3dc..4b851e7c 100644 --- a/designer/src/designer-configuration.ts +++ b/designer/src/designer-configuration.ts @@ -28,6 +28,11 @@ export interface DesignerConfiguration string | null; +export interface PlaceholderConfiguration { + canCreate?: (sequence: Sequence, index: number) => boolean; + canShow?: (sequence: Sequence, index: number, draggingStepComponentType: ComponentType, draggingStepType: string) => boolean; +} + export interface ValidatorConfiguration { step?: StepValidator; root?: RootValidator; diff --git a/designer/src/designer-context.ts b/designer/src/designer-context.ts index b1aa97f4..c076fdfb 100644 --- a/designer/src/designer-context.ts +++ b/designer/src/designer-context.ts @@ -4,7 +4,7 @@ import { ObjectCloner } from './core/object-cloner'; import { CustomActionController } from './custom-action-controller'; import { Definition, DefinitionWalker } from './definition'; import { StateModifier } from './modifier/state-modifier'; -import { DesignerConfiguration, I18n } from './designer-configuration'; +import { DesignerConfiguration, I18n, UidGenerator } from './designer-configuration'; import { DesignerState } from './designer-state'; import { HistoryController } from './history-controller'; import { LayoutController } from './layout-controller'; @@ -12,7 +12,8 @@ import { Services } from './services'; import { StepExtensionResolver } from './workspace/step-extension-resolver'; import { WorkspaceController, WorkspaceControllerWrapper } from './workspace/workspace-controller'; import { MemoryPreferenceStorage } from './core/memory-preference-storage'; -import { PlaceholderController } from './designer-extension'; +import { PlaceholderController } from './workspace/placeholder/placeholder-controller'; +import { Uid } from './core'; export class DesignerContext { public static create( @@ -32,12 +33,13 @@ export class DesignerContext { const theme = configuration.theme || 'light'; const state = new DesignerState(definition, isReadonly, isToolboxCollapsed, isEditorCollapsed); const workspaceController = new WorkspaceControllerWrapper(); - const placeholderController = services.placeholderController.create(); const behaviorController = BehaviorController.create(configuration.shadowRoot); const stepExtensionResolver = StepExtensionResolver.create(services); + const placeholderController = PlaceholderController.create(configuration.placeholder); const definitionWalker = configuration.definitionWalker ?? new DefinitionWalker(); const i18n: I18n = configuration.i18n ?? ((_, defaultValue) => defaultValue); - const stateModifier = StateModifier.create(definitionWalker, state, configuration); + const uidGenerator = configuration.uidGenerator ?? Uid.next; + const stateModifier = StateModifier.create(definitionWalker, uidGenerator, state, configuration.steps); const customActionController = new CustomActionController(configuration, state, stateModifier); let historyController: HistoryController | undefined = undefined; @@ -50,9 +52,9 @@ export class DesignerContext { configuration, state, stepExtensionResolver, + placeholderController, definitionWalker, preferenceStorage, - placeholderController, i18n, services ); @@ -65,6 +67,7 @@ export class DesignerContext { componentContext, definitionWalker, i18n, + uidGenerator, stateModifier, layoutController, workspaceController, @@ -83,6 +86,7 @@ export class DesignerContext { public readonly componentContext: ComponentContext, public readonly definitionWalker: DefinitionWalker, public readonly i18n: I18n, + public readonly uidGenerator: UidGenerator, public readonly stateModifier: StateModifier, public readonly layoutController: LayoutController, public readonly workspaceController: WorkspaceControllerWrapper, diff --git a/designer/src/designer-extension.ts b/designer/src/designer-extension.ts index cd58b660..36fab73f 100644 --- a/designer/src/designer-extension.ts +++ b/designer/src/designer-extension.ts @@ -15,6 +15,7 @@ import { Placeholder, PlaceholderDirection, SequenceComponent, + StepComponent, StepComponentView } from './workspace'; @@ -28,7 +29,6 @@ export interface DesignerExtension { draggedComponent?: DraggedComponentExtension; wheelController?: WheelControllerExtension; viewportController?: ViewportControllerExtension; - placeholderController?: PlaceholderControllerExtension; placeholder?: PlaceholderExtension; regionComponentView?: RegionComponentViewExtension; grid?: GridExtension; @@ -51,7 +51,15 @@ export interface StepComponentViewContext { i18n: I18n; getStepName(): string; getStepIconUrl(): string | null; + createStepComponent(parentElement: SVGElement, parentSequence: Sequence, step: Step, position: number): StepComponent; createSequenceComponent(parentElement: SVGElement, sequence: Sequence): SequenceComponent; + getPlaceholderGapSize(orientation: PlaceholderGapOrientation): Vector; + createPlaceholderForGap( + parentElement: SVGElement, + sequence: Sequence, + index: number, + orientation: PlaceholderGapOrientation + ): Placeholder; createPlaceholderForArea( parentElement: SVGElement, size: Vector, @@ -204,22 +212,16 @@ export interface ContextMenuItem { readonly callback?: () => void; } -// PlaceholderControllerExtension - -export interface PlaceholderControllerExtension { - create(): PlaceholderController; -} +// PlaceholderExtension -export interface PlaceholderController { - canCreate(sequence: Sequence, index: number): boolean; - canShow?: (sequence: Sequence, index: number, draggingStepComponentType: ComponentType, draggingStepType: string) => boolean; +export enum PlaceholderGapOrientation { + along = 0, // Goes along with the flow + perpendicular = 1 // Goes perpendicular to the flow } -// PlaceholderExtension - export interface PlaceholderExtension { - gapSize: Vector; - createForGap(parentElement: SVGElement, sequence: Sequence, index: number): Placeholder; + getGapSize(orientation: PlaceholderGapOrientation): Vector; + createForGap(parentElement: SVGElement, sequence: Sequence, index: number, orientation: PlaceholderGapOrientation): Placeholder; createForArea(parentElement: SVGElement, size: Vector, direction: PlaceholderDirection, sequence: Sequence, index: number): Placeholder; } diff --git a/designer/src/extensions/steps-designer-extension.ts b/designer/src/extensions/steps-designer-extension.ts index bda4ef79..c43f5c31 100644 --- a/designer/src/extensions/steps-designer-extension.ts +++ b/designer/src/extensions/steps-designer-extension.ts @@ -6,11 +6,14 @@ import { SwitchStepExtensionConfiguration } from '../workspace/switch-step/switc import { SwitchStepExtension } from '../workspace/switch-step/switch-step-extension'; import { TaskStepExtensionConfiguration } from '../workspace/task-step/task-step-extension-configuration'; import { TaskStepExtension } from '../workspace/task-step/task-step-extension'; +import { LaunchPadStepExtensionConfiguration } from '../workspace/launch-pad-step/launch-pad-step-extension-configuration'; +import { LaunchPadStepExtension } from '../workspace/launch-pad-step/launch-pad-step-extension'; export interface StepsDesignerExtensionConfiguration { container?: ContainerStepExtensionConfiguration; switch?: SwitchStepExtensionConfiguration; task?: TaskStepExtensionConfiguration; + launchPad?: LaunchPadStepExtensionConfiguration; } export class StepsDesignerExtension implements DesignerExtension { @@ -25,6 +28,9 @@ export class StepsDesignerExtension implements DesignerExtension { if (configuration.task) { steps.push(TaskStepExtension.create(configuration.task)); } + if (configuration.launchPad) { + steps.push(LaunchPadStepExtension.create(configuration.launchPad)); + } return new StepsDesignerExtension(steps); } diff --git a/designer/src/modifier/state-modifier.ts b/designer/src/modifier/state-modifier.ts index 4db16abb..8d681de3 100644 --- a/designer/src/modifier/state-modifier.ts +++ b/designer/src/modifier/state-modifier.ts @@ -1,25 +1,30 @@ -import { Uid } from '../core'; import { SequenceModifier } from './sequence-modifier'; import { StepDuplicator } from '../core/step-duplicator'; import { Definition, DefinitionWalker, Sequence, Step } from '../definition'; -import { DefinitionChangeType, DesignerConfiguration } from '../designer-configuration'; +import { DefinitionChangeType, StepsConfiguration, UidGenerator } from '../designer-configuration'; import { DesignerState } from '../designer-state'; import { StateModifierDependency } from './state-modifier-dependency'; import { FolderPathDefinitionModifierDependency } from './folder-path-definition-modifier-dependency'; import { SelectedStepIdDefinitionModifierDependency } from './selected-step-id-definition-modifier-dependency'; export class StateModifier { - public static create(definitionWalker: DefinitionWalker, state: DesignerState, configuration: DesignerConfiguration): StateModifier { + public static create( + definitionWalker: DefinitionWalker, + uidGenerator: UidGenerator, + state: DesignerState, + configuration: StepsConfiguration + ): StateModifier { const dependencies: StateModifierDependency[] = []; dependencies.push(new SelectedStepIdDefinitionModifierDependency(state, definitionWalker)); dependencies.push(new FolderPathDefinitionModifierDependency(state, definitionWalker)); - return new StateModifier(definitionWalker, state, configuration, dependencies); + return new StateModifier(definitionWalker, uidGenerator, state, configuration, dependencies); } public constructor( private readonly definitionWalker: DefinitionWalker, + private readonly uidGenerator: UidGenerator, private readonly state: DesignerState, - private readonly configuration: DesignerConfiguration, + private readonly configuration: StepsConfiguration, private readonly dependencies: StateModifierDependency[] ) {} @@ -28,7 +33,7 @@ export class StateModifier { } public isSelectable(step: Step, parentSequence: Sequence): boolean { - return this.configuration.steps.isSelectable ? this.configuration.steps.isSelectable(step, parentSequence) : true; + return this.configuration.isSelectable ? this.configuration.isSelectable(step, parentSequence) : true; } public trySelectStep(step: Step, parentSequence: Sequence) { @@ -38,7 +43,7 @@ export class StateModifier { } public trySelectStepById(stepId: string) { - if (this.configuration.steps.isSelectable) { + if (this.configuration.isSelectable) { const result = this.definitionWalker.getParentSequence(this.state.definition, stepId); this.trySelectStep(result.step, result.parentSequence); } else { @@ -47,9 +52,9 @@ export class StateModifier { } public isDeletable(stepId: string): boolean { - if (this.configuration.steps.isDeletable) { + if (this.configuration.isDeletable) { const result = this.definitionWalker.getParentSequence(this.state.definition, stepId); - return this.configuration.steps.isDeletable(result.step, result.parentSequence); + return this.configuration.isDeletable(result.step, result.parentSequence); } return true; } @@ -57,8 +62,8 @@ export class StateModifier { public tryDelete(stepId: string): boolean { const result = this.definitionWalker.getParentSequence(this.state.definition, stepId); - const canDeleteStep = this.configuration.steps.canDeleteStep - ? this.configuration.steps.canDeleteStep(result.step, result.parentSequence) + const canDeleteStep = this.configuration.canDeleteStep + ? this.configuration.canDeleteStep(result.step, result.parentSequence) : true; if (!canDeleteStep) { return false; @@ -72,9 +77,7 @@ export class StateModifier { } public tryInsert(step: Step, targetSequence: Sequence, targetIndex: number): boolean { - const canInsertStep = this.configuration.steps.canInsertStep - ? this.configuration.steps.canInsertStep(step, targetSequence, targetIndex) - : true; + const canInsertStep = this.configuration.canInsertStep ? this.configuration.canInsertStep(step, targetSequence, targetIndex) : true; if (!canInsertStep) { return false; } @@ -82,14 +85,14 @@ export class StateModifier { SequenceModifier.insertStep(step, targetSequence, targetIndex); this.state.notifyDefinitionChanged(DefinitionChangeType.stepInserted, step.id); - if (!this.configuration.steps.isAutoSelectDisabled) { + if (!this.configuration.isAutoSelectDisabled) { this.trySelectStepById(step.id); } return true; } public isDraggable(step: Step, parentSequence: Sequence): boolean { - return this.configuration.steps.isDraggable ? this.configuration.steps.isDraggable(step, parentSequence) : true; + return this.configuration.isDraggable ? this.configuration.isDraggable(step, parentSequence) : true; } public tryMove(sourceSequence: Sequence, step: Step, targetSequence: Sequence, targetIndex: number): boolean { @@ -98,8 +101,8 @@ export class StateModifier { return false; } - const canMoveStep = this.configuration.steps.canMoveStep - ? this.configuration.steps.canMoveStep(sourceSequence, step, targetSequence, targetIndex) + const canMoveStep = this.configuration.canMoveStep + ? this.configuration.canMoveStep(sourceSequence, step, targetSequence, targetIndex) : true; if (!canMoveStep) { return false; @@ -108,19 +111,18 @@ export class StateModifier { apply(); this.state.notifyDefinitionChanged(DefinitionChangeType.stepMoved, step.id); - if (!this.configuration.steps.isAutoSelectDisabled) { + if (!this.configuration.isAutoSelectDisabled) { this.trySelectStep(step, targetSequence); } return true; } public isDuplicable(step: Step, parentSequence: Sequence): boolean { - return this.configuration.steps.isDuplicable ? this.configuration.steps.isDuplicable(step, parentSequence) : false; + return this.configuration.isDuplicable ? this.configuration.isDuplicable(step, parentSequence) : false; } public tryDuplicate(step: Step, parentSequence: Sequence): boolean { - const uidGenerator = this.configuration.uidGenerator ? this.configuration.uidGenerator : Uid.next; - const duplicator = new StepDuplicator(uidGenerator, this.definitionWalker); + const duplicator = new StepDuplicator(this.uidGenerator, this.definitionWalker); const index = parentSequence.indexOf(step); const newStep = duplicator.duplicate(step); diff --git a/designer/src/services.ts b/designer/src/services.ts index 7389a8ec..cfa2ef7a 100644 --- a/designer/src/services.ts +++ b/designer/src/services.ts @@ -7,7 +7,6 @@ import { SmartEditorExtension } from './smart-editor/smart-editor-extension'; import { ToolboxExtension } from './toolbox/toolbox-extension'; import { ValidationErrorBadgeExtension } from './workspace/badges/validation-error/validation-error-badge-extension'; import { ContainerStepExtension } from './workspace/container-step/container-step-extension'; -import { DefaultPlaceholderControllerExtension } from './workspace/placeholder/default-placeholder-controller-extension'; import { RectPlaceholderExtension } from './workspace/placeholder/rect-placeholder-extension'; import { StartStopRootComponentExtension } from './workspace/start-stop-root/start-stop-root-component-extension'; import { SwitchStepExtension } from './workspace/switch-step/switch-step-extension'; @@ -21,6 +20,7 @@ import { LineGridExtension } from './workspace/grid/line-grid-extension'; import { DefaultRegionComponentViewExtension } from './workspace/region/default-region-component-view-extension'; import { DefaultClickBehaviorWrapperExtension } from './behaviors/default-click-behavior-wrapper-extension'; import { DefaultStepBadgesDecoratorExtension } from './workspace/badges/default-step-badges-decorator-extension'; +import { LaunchPadStepExtension } from './workspace/launch-pad-step/launch-pad-step-extension'; export type Services = Required; @@ -59,9 +59,6 @@ function merge(services: Partial, extensions: DesignerExtension[]) { if (ext.wheelController) { services.wheelController = ext.wheelController; } - if (ext.placeholderController) { - services.placeholderController = ext.placeholderController; - } if (ext.placeholder) { services.placeholder = ext.placeholder; } @@ -96,6 +93,7 @@ function setDefaults(services: Partial, configuration: DesignerConfigu services.steps.push(ContainerStepExtension.create()); services.steps.push(SwitchStepExtension.create()); services.steps.push(TaskStepExtension.create()); + services.steps.push(LaunchPadStepExtension.create()); if (!services.stepComponentViewWrapper) { services.stepComponentViewWrapper = new DefaultStepComponentViewWrapperExtension(); @@ -133,9 +131,6 @@ function setDefaults(services: Partial, configuration: DesignerConfigu if (!services.wheelController) { services.wheelController = new ClassicWheelControllerExtension(); } - if (!services.placeholderController) { - services.placeholderController = new DefaultPlaceholderControllerExtension(); - } if (!services.placeholder) { services.placeholder = RectPlaceholderExtension.create(); } diff --git a/designer/src/smart-editor/editor.spec.ts b/designer/src/smart-editor/editor.spec.ts index caef9f8a..cff85193 100644 --- a/designer/src/smart-editor/editor.spec.ts +++ b/designer/src/smart-editor/editor.spec.ts @@ -4,6 +4,7 @@ import { DesignerState } from '../designer-state'; import { createDefinitionStub, createDesignerConfigurationStub, createStepStub } from '../test-tools/stubs'; import { StateModifier } from '../modifier/state-modifier'; import { Editor } from './editor'; +import { Uid } from '../core'; describe('Editor', () => { const step = createStepStub(); @@ -27,7 +28,7 @@ describe('Editor', () => { state = new DesignerState(definition, false, false, false); const walker = new DefinitionWalker(); - const modifier = StateModifier.create(walker, state, configuration); + const modifier = StateModifier.create(walker, Uid.next, state, configuration.steps); api = new EditorApi(state, walker, modifier); stepEditorProvider = jasmine.createSpy().and.returnValue(document.createElement('div')); diff --git a/designer/src/toolbox/toolbox-data-provider.spec.ts b/designer/src/toolbox/toolbox-data-provider.spec.ts index 69898f4b..5b5e9302 100644 --- a/designer/src/toolbox/toolbox-data-provider.spec.ts +++ b/designer/src/toolbox/toolbox-data-provider.spec.ts @@ -29,13 +29,13 @@ describe('ToolboxDataProvider', () => { describe('getAllGroups()', () => { it('return empty array if configuration is false', () => { - const dataProvider = new ToolboxDataProvider(iconProvider, i18n, false); + const dataProvider = new ToolboxDataProvider(i18n, iconProvider, false); expect(dataProvider.getAllGroups()).toEqual([]); }); it('returns groups with correct labels and descriptions', () => { - const dataProvider = new ToolboxDataProvider(iconProvider, i18n, { + const dataProvider = new ToolboxDataProvider(i18n, iconProvider, { groups }); @@ -53,7 +53,7 @@ describe('ToolboxDataProvider', () => { }); it('returns groups with correct labels and descriptions when custom providers are provided', () => { - const dataProvider = new ToolboxDataProvider(iconProvider, i18n, { + const dataProvider = new ToolboxDataProvider(i18n, iconProvider, { groups, labelProvider: reverseStepName, descriptionProvider: step => `Description of ${step.name}` @@ -75,7 +75,7 @@ describe('ToolboxDataProvider', () => { describe('applyFilter()', () => { it('filters by label', () => { - const dataProvider = new ToolboxDataProvider(iconProvider, i18n, { + const dataProvider = new ToolboxDataProvider(i18n, iconProvider, { groups }); @@ -98,7 +98,7 @@ describe('ToolboxDataProvider', () => { }); it('filters by custom label', () => { - const dataProvider = new ToolboxDataProvider(iconProvider, i18n, { + const dataProvider = new ToolboxDataProvider(i18n, iconProvider, { groups, labelProvider: reverseStepName }); diff --git a/designer/src/toolbox/toolbox-data-provider.ts b/designer/src/toolbox/toolbox-data-provider.ts index e1ea254f..f22e1ed2 100644 --- a/designer/src/toolbox/toolbox-data-provider.ts +++ b/designer/src/toolbox/toolbox-data-provider.ts @@ -4,8 +4,8 @@ import { I18n, StepDefinition, ToolboxConfiguration } from '../designer-configur export class ToolboxDataProvider { public constructor( - private readonly iconProvider: IconProvider, private readonly i18n: I18n, + private readonly iconProvider: IconProvider, private readonly configuration: ToolboxConfiguration | false ) {} diff --git a/designer/src/workspace/common-views/join-view.ts b/designer/src/workspace/common-views/join-view.ts index 0b02eb0d..828767f5 100644 --- a/designer/src/workspace/common-views/join-view.ts +++ b/designer/src/workspace/common-views/join-view.ts @@ -24,7 +24,7 @@ export class JoinView { switch (targets.length) { case 1: if (start.x === targets[0].x) { - JoinView.createStraightJoin(parent, start, firstTarget.y * dy); + JoinView.createStraightJoin(parent, start, h * 2 * dy); } else { appendCurvedJoins(parent, start, targets, h, dy); } diff --git a/designer/src/workspace/component.ts b/designer/src/workspace/component.ts index c311b26b..3eae8107 100644 --- a/designer/src/workspace/component.ts +++ b/designer/src/workspace/component.ts @@ -120,7 +120,7 @@ export interface Placeholder { } export enum PlaceholderDirection { - none = 0, + gap = 0, in = 1, out = 2 } diff --git a/designer/src/workspace/launch-pad-step/launch-pad-step-component-view-configuration.ts b/designer/src/workspace/launch-pad-step/launch-pad-step-component-view-configuration.ts new file mode 100644 index 00000000..a045eb83 --- /dev/null +++ b/designer/src/workspace/launch-pad-step/launch-pad-step-component-view-configuration.ts @@ -0,0 +1,11 @@ +export interface LaunchPadStepComponentViewConfiguration { + isRegionEnabled: boolean; + paddingY: number; + connectionHeight: number; + emptyPaddingX: number; + emptyPaddingY: number; + emptyInputSize: number; + emptyOutputSize: number; + emptyIconSize: number; + emptyIconD: string; +} diff --git a/designer/src/workspace/launch-pad-step/launch-pad-step-component-view.ts b/designer/src/workspace/launch-pad-step/launch-pad-step-component-view.ts new file mode 100644 index 00000000..ff14fae3 --- /dev/null +++ b/designer/src/workspace/launch-pad-step/launch-pad-step-component-view.ts @@ -0,0 +1,181 @@ +import { SequentialStep } from 'sequential-workflow-model'; +import { + PlaceholderGapOrientation, + RegionView, + RegionViewFactory, + StepComponentViewContext, + StepComponentViewFactory, + StepContext +} from '../../designer-extension'; +import { ComponentDom, InputView, JoinView, OutputView } from '../common-views'; +import { LaunchPadStepComponentViewConfiguration } from './launch-pad-step-component-view-configuration'; +import { ClickCommand, ClickDetails, Placeholder, StepComponentView } from '../component'; +import { Dom, getAbsolutePosition, Icons, Vector } from '../../core'; +import { StepComponent } from '../step-component'; + +const COMPONENT_CLASS_NAME = 'launch-pad'; + +function createView( + parentElement: SVGElement, + stepContext: StepContext, + viewContext: StepComponentViewContext, + regionViewFactory: RegionViewFactory | null, + isInterruptedIfEmpty: boolean, + cfg: LaunchPadStepComponentViewConfiguration +): StepComponentView { + const step = stepContext.step; + const sequence = stepContext.step.sequence; + const g = ComponentDom.stepG(COMPONENT_CLASS_NAME, step.type, step.id); + parentElement.appendChild(g); + + const components: StepComponent[] = []; + let width: number; + let height: number; + let joinX: number; + + const placeholdersX: number[] = []; + let placeholderOrientation: PlaceholderGapOrientation; + let placeholderSize: Vector; + let hasOutput: boolean; + + let inputView: InputView | null = null; + let outputView: OutputView | null = null; + + if (sequence.length > 0) { + let maxComponentHeight = 0; + for (let i = 0; i < sequence.length; i++) { + const component = viewContext.createStepComponent(g, sequence, sequence[i], i); + components.push(component); + maxComponentHeight = Math.max(maxComponentHeight, component.view.height); + } + + const joinsX: number[] = []; + const positionsX: number[] = []; + const spacesY: number[] = []; + + placeholderOrientation = PlaceholderGapOrientation.perpendicular; + placeholderSize = viewContext.getPlaceholderGapSize(placeholderOrientation); + placeholdersX.push(0); + let positionX = placeholderSize.x; + + for (let i = 0; i < components.length; i++) { + if (i > 0) { + placeholdersX.push(positionX); + positionX += placeholderSize.x; + } + const component = components[i]; + const componentY = (maxComponentHeight - component.view.height) / 2 + cfg.connectionHeight + cfg.paddingY; + Dom.translate(component.view.g, positionX, componentY); + + joinsX.push(positionX + component.view.joinX); + positionX += component.view.width; + positionsX.push(positionX); + spacesY.push(Math.max(0, (maxComponentHeight - component.view.height) / 2)); + } + + placeholdersX.push(positionX); + positionX += placeholderSize.x; + + width = positionX; + height = maxComponentHeight + 2 * cfg.connectionHeight + 2 * cfg.paddingY; + + const contentJoinX = + components.length % 2 === 0 + ? positionsX[Math.max(0, Math.floor(components.length / 2) - 1)] + placeholderSize.x / 2 + : joinsX[Math.floor(components.length / 2)]; + + if (stepContext.isInputConnected) { + const joinsTopY = joinsX.map(x => new Vector(x, cfg.connectionHeight)); + JoinView.createJoins(g, new Vector(contentJoinX, 0), joinsTopY); + for (let i = 0; i < joinsX.length; i++) { + JoinView.createStraightJoin(g, joinsTopY[i], cfg.paddingY + spacesY[i]); + } + } + + const joinsBottomY = joinsX.map(x => new Vector(x, cfg.connectionHeight + 2 * cfg.paddingY + maxComponentHeight)); + JoinView.createJoins(g, new Vector(contentJoinX, height), joinsBottomY); + for (let i = 0; i < joinsX.length; i++) { + JoinView.createStraightJoin(g, joinsBottomY[i], -(cfg.paddingY + spacesY[i])); + } + + hasOutput = true; + joinX = contentJoinX; + } else { + placeholderOrientation = PlaceholderGapOrientation.along; + placeholderSize = viewContext.getPlaceholderGapSize(placeholderOrientation); + + placeholdersX.push(cfg.emptyPaddingX); + + width = placeholderSize.x + cfg.emptyPaddingX * 2; + height = placeholderSize.y + cfg.emptyPaddingY * 2; + hasOutput = !isInterruptedIfEmpty; + + if (stepContext.isInputConnected) { + inputView = InputView.createRoundInput(g, width / 2, 0, cfg.emptyInputSize); + } + + if (stepContext.isOutputConnected && hasOutput) { + outputView = OutputView.create(g, width / 2, height, cfg.emptyOutputSize); + } + + const icon = Icons.appendPath(g, 'sqd-launch-pad-empty-icon', cfg.emptyIconD, cfg.emptyIconSize); + Dom.translate(icon, (width - cfg.emptyIconSize) / 2, (height - cfg.emptyIconSize) / 2); + + joinX = width / 2; + } + + let regionView: RegionView | null = null; + if (regionViewFactory) { + regionView = regionViewFactory(g, [width], height); + } + + const placeholders: Placeholder[] = []; + const placeholderY = (height - placeholderSize.y) / 2; + for (let i = 0; i < placeholdersX.length; i++) { + const placeholder = viewContext.createPlaceholderForGap(g, sequence, i, placeholderOrientation); + placeholders.push(placeholder); + Dom.translate(placeholder.view.g, placeholdersX[i], placeholderY); + } + + return { + g, + width, + height, + joinX, + sequenceComponents: components, + placeholders, + hasOutput, + + getClientPosition(): Vector { + return getAbsolutePosition(g); + }, + resolveClick(click: ClickDetails): true | ClickCommand | null { + if (regionView) { + const result = regionView.resolveClick(click); + return result === true || (result === null && g.contains(click.element)) ? true : result; + } + return null; + }, + setIsDragging(isDragging: boolean) { + inputView?.setIsHidden(isDragging); + outputView?.setIsHidden(isDragging); + }, + setIsDisabled(isDisabled: boolean) { + Dom.toggleClass(g, isDisabled, 'sqd-disabled'); + }, + setIsSelected(isSelected: boolean) { + regionView?.setIsSelected(isSelected); + } + }; +} + +export const createLaunchPadStepComponentViewFactory = + (isInterruptedIfEmpty: boolean, cfg: LaunchPadStepComponentViewConfiguration): StepComponentViewFactory => + (parentElement: SVGElement, stepContext: StepContext, viewContext: StepComponentViewContext): StepComponentView => { + if (cfg.isRegionEnabled) { + return viewContext.createRegionComponentView(parentElement, COMPONENT_CLASS_NAME, (g, regionViewBuilder) => { + return createView(g, stepContext, viewContext, regionViewBuilder, isInterruptedIfEmpty, cfg); + }); + } + return createView(parentElement, stepContext, viewContext, null, isInterruptedIfEmpty, cfg); + }; diff --git a/designer/src/workspace/launch-pad-step/launch-pad-step-extension-configuration.ts b/designer/src/workspace/launch-pad-step/launch-pad-step-extension-configuration.ts new file mode 100644 index 00000000..80e56c69 --- /dev/null +++ b/designer/src/workspace/launch-pad-step/launch-pad-step-extension-configuration.ts @@ -0,0 +1,6 @@ +import { LaunchPadStepComponentViewConfiguration } from './launch-pad-step-component-view-configuration'; + +export interface LaunchPadStepExtensionConfiguration { + view?: LaunchPadStepComponentViewConfiguration; + componentType?: string; +} diff --git a/designer/src/workspace/launch-pad-step/launch-pad-step-extension.ts b/designer/src/workspace/launch-pad-step/launch-pad-step-extension.ts new file mode 100644 index 00000000..8fab52e4 --- /dev/null +++ b/designer/src/workspace/launch-pad-step/launch-pad-step-extension.ts @@ -0,0 +1,33 @@ +import { SequentialStep } from 'sequential-workflow-model'; +import { StepExtension } from '../../designer-extension'; +import { LaunchPadStepExtensionConfiguration } from './launch-pad-step-extension-configuration'; +import { createLaunchPadStepComponentViewFactory } from './launch-pad-step-component-view'; +import { LaunchPadStepComponentViewConfiguration } from './launch-pad-step-component-view-configuration'; +import { Icons } from '../../core'; + +const defaultViewConfiguration: LaunchPadStepComponentViewConfiguration = { + isRegionEnabled: true, + paddingY: 10, + connectionHeight: 20, + emptyPaddingX: 20, + emptyPaddingY: 20, + emptyInputSize: 14, + emptyOutputSize: 10, + emptyIconSize: 24, + emptyIconD: Icons.trigger +}; + +export class LaunchPadStepExtension implements StepExtension { + public static create(configuration?: LaunchPadStepExtensionConfiguration): LaunchPadStepExtension { + return new LaunchPadStepExtension(configuration); + } + + public readonly componentType = this.configuration?.componentType || 'launchPad'; + + private constructor(private readonly configuration: LaunchPadStepExtensionConfiguration | undefined) {} + + public readonly createComponentView = createLaunchPadStepComponentViewFactory( + false, + this.configuration?.view ?? defaultViewConfiguration + ); +} diff --git a/designer/src/workspace/placeholder/default-placeholder-controller-extension.ts b/designer/src/workspace/placeholder/default-placeholder-controller-extension.ts deleted file mode 100644 index 60683253..00000000 --- a/designer/src/workspace/placeholder/default-placeholder-controller-extension.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { PlaceholderController, PlaceholderControllerExtension } from '../../designer-extension'; - -export class DefaultPlaceholderControllerExtension implements PlaceholderControllerExtension { - public create(): PlaceholderController { - return { - canCreate: () => true - }; - } -} diff --git a/designer/src/workspace/placeholder/index.ts b/designer/src/workspace/placeholder/index.ts index 7e772d83..071001ea 100644 --- a/designer/src/workspace/placeholder/index.ts +++ b/designer/src/workspace/placeholder/index.ts @@ -1,3 +1,4 @@ +export * from './placeholder-controller'; export * from './rect-placeholder'; export * from './rect-placeholder-view'; export * from './rect-placeholder-configuration'; diff --git a/designer/src/workspace/placeholder/placeholder-controller.ts b/designer/src/workspace/placeholder/placeholder-controller.ts new file mode 100644 index 00000000..8de0e8db --- /dev/null +++ b/designer/src/workspace/placeholder/placeholder-controller.ts @@ -0,0 +1,15 @@ +import { Sequence } from 'sequential-workflow-model'; +import { PlaceholderConfiguration } from '../../designer-configuration'; + +export class PlaceholderController { + public static create(configuration: PlaceholderConfiguration | undefined): PlaceholderController { + return new PlaceholderController(configuration); + } + + private constructor(private readonly configuration: PlaceholderConfiguration | undefined) {} + + public readonly canCreate: (sequence: Sequence, index: number) => boolean = this.configuration?.canCreate ?? (() => true); + + public readonly canShow: (sequence: Sequence, index: number, draggingStepComponentType: string, draggingStepType: string) => boolean = + this.configuration?.canShow ?? (() => true); +} diff --git a/designer/src/workspace/placeholder/rect-placeholder-extension.ts b/designer/src/workspace/placeholder/rect-placeholder-extension.ts index 8c92a035..b6775b8c 100644 --- a/designer/src/workspace/placeholder/rect-placeholder-extension.ts +++ b/designer/src/workspace/placeholder/rect-placeholder-extension.ts @@ -1,6 +1,6 @@ import { Sequence } from '../../definition'; import { Vector } from '../../core'; -import { PlaceholderExtension } from '../../designer-extension'; +import { PlaceholderExtension, PlaceholderGapOrientation } from '../../designer-extension'; import { PlaceholderDirection, Placeholder } from '../component'; import { RectPlaceholder } from './rect-placeholder'; import { RectPlaceholderConfiguration } from './rect-placeholder-configuration'; @@ -17,12 +17,26 @@ export class RectPlaceholderExtension implements PlaceholderExtension { return new RectPlaceholderExtension(configuration ?? defaultConfiguration); } - public readonly gapSize = new Vector(this.configuration.gapWidth, this.configuration.gapHeight); + private readonly alongGapSize = new Vector(defaultConfiguration.gapWidth, defaultConfiguration.gapHeight); + private readonly perpendicularGapSize = new Vector(defaultConfiguration.gapHeight, defaultConfiguration.gapWidth); private constructor(private readonly configuration: RectPlaceholderConfiguration) {} - public createForGap(parent: SVGElement, parentSequence: Sequence, index: number): Placeholder { - return RectPlaceholder.create(parent, this.gapSize, PlaceholderDirection.none, parentSequence, index, this.configuration); + public getGapSize(orientation: PlaceholderGapOrientation): Vector { + return orientation === PlaceholderGapOrientation.perpendicular ? this.perpendicularGapSize : this.alongGapSize; + } + + public createForGap(parent: SVGElement, parentSequence: Sequence, index: number, orientation: PlaceholderGapOrientation): Placeholder { + const gapSize = this.getGapSize(orientation); + return RectPlaceholder.create( + parent, + gapSize, + PlaceholderDirection.gap, + parentSequence, + index, + this.configuration.radius, + this.configuration.iconSize + ); } public createForArea( @@ -32,6 +46,14 @@ export class RectPlaceholderExtension implements PlaceholderExtension { parentSequence: Sequence, index: number ): Placeholder { - return RectPlaceholder.create(parent, size, direction, parentSequence, index, this.configuration); + return RectPlaceholder.create( + parent, + size, + direction, + parentSequence, + index, + this.configuration.radius, + this.configuration.iconSize + ); } } diff --git a/designer/src/workspace/placeholder/rect-placeholder.ts b/designer/src/workspace/placeholder/rect-placeholder.ts index e7325b8b..b34c1602 100644 --- a/designer/src/workspace/placeholder/rect-placeholder.ts +++ b/designer/src/workspace/placeholder/rect-placeholder.ts @@ -1,7 +1,6 @@ import { Vector } from '../../core'; import { Sequence } from '../../definition'; import { Placeholder, PlaceholderDirection } from '../component'; -import { RectPlaceholderConfiguration } from './rect-placeholder-configuration'; import { RectPlaceholderView } from './rect-placeholder-view'; export class RectPlaceholder implements Placeholder { @@ -11,9 +10,10 @@ export class RectPlaceholder implements Placeholder { direction: PlaceholderDirection, sequence: Sequence, index: number, - configuration: RectPlaceholderConfiguration + radius: number, + iconSize: number ): RectPlaceholder { - const view = RectPlaceholderView.create(parent, size.x, size.y, configuration.radius, configuration.iconSize, direction); + const view = RectPlaceholderView.create(parent, size.x, size.y, radius, iconSize, direction); return new RectPlaceholder(view, sequence, index); } diff --git a/designer/src/workspace/sequence/default-sequence-component-view.ts b/designer/src/workspace/sequence/default-sequence-component-view.ts index c7437ad8..87666c87 100644 --- a/designer/src/workspace/sequence/default-sequence-component-view.ts +++ b/designer/src/workspace/sequence/default-sequence-component-view.ts @@ -3,7 +3,7 @@ import { Vector } from '../../core/vector'; import { JoinView } from '../common-views/join-view'; import { ComponentView, Placeholder } from '../component'; import { ComponentContext } from '../../component-context'; -import { SequenceContext, StepContext } from '../../designer-extension'; +import { PlaceholderGapOrientation, SequenceContext, StepContext } from '../../designer-extension'; import { StepComponent } from '../step-component'; export class DefaultSequenceComponentView implements ComponentView { @@ -12,8 +12,9 @@ export class DefaultSequenceComponentView implements ComponentView { sequenceContext: SequenceContext, componentContext: ComponentContext ): DefaultSequenceComponentView { - const phWidth = componentContext.services.placeholder.gapSize.x; - const phHeight = componentContext.services.placeholder.gapSize.y; + const phSize = componentContext.services.placeholder.getGapSize(PlaceholderGapOrientation.along); + const phWidth = phSize.x; + const phHeight = phSize.y; const { sequence } = sequenceContext; const g = Dom.svg('g'); @@ -57,7 +58,7 @@ export class DefaultSequenceComponentView implements ComponentView { } if (!sequenceContext.isPreview && componentContext.placeholderController.canCreate(sequence, i)) { - const ph = componentContext.services.placeholder.createForGap(g, sequence, i); + const ph = componentContext.services.placeholder.createForGap(g, sequence, i, PlaceholderGapOrientation.along); Dom.translate(ph.view.g, joinX - phWidth / 2, offsetY - phHeight); placeholders.push(ph); } @@ -72,7 +73,7 @@ export class DefaultSequenceComponentView implements ComponentView { const newIndex = components.length; if (!sequenceContext.isPreview && componentContext.placeholderController.canCreate(sequence, newIndex)) { - const ph = componentContext.services.placeholder.createForGap(g, sequence, newIndex); + const ph = componentContext.services.placeholder.createForGap(g, sequence, newIndex, PlaceholderGapOrientation.along); Dom.translate(ph.view.g, joinX - phWidth / 2, offsetY - phHeight); placeholders.push(ph); } diff --git a/designer/src/workspace/start-stop-root/start-stop-root-component-extension-configuration.ts b/designer/src/workspace/start-stop-root/start-stop-root-component-extension-configuration.ts index 5aa2e66f..f0907deb 100644 --- a/designer/src/workspace/start-stop-root/start-stop-root-component-extension-configuration.ts +++ b/designer/src/workspace/start-stop-root/start-stop-root-component-extension-configuration.ts @@ -1,5 +1,5 @@ import { StartStopRootComponentViewConfiguration } from './start-stop-root-component-view-configuration'; export interface StartStopRootComponentExtensionConfiguration { - view: StartStopRootComponentViewConfiguration; + view?: Partial; } diff --git a/designer/src/workspace/start-stop-root/start-stop-root-component-extension.ts b/designer/src/workspace/start-stop-root/start-stop-root-component-extension.ts index 17dd2fd4..2c186f0c 100644 --- a/designer/src/workspace/start-stop-root/start-stop-root-component-extension.ts +++ b/designer/src/workspace/start-stop-root/start-stop-root-component-extension.ts @@ -5,24 +5,25 @@ import { RootComponentExtension, SequencePlaceIndicator } from '../../designer-e import { StartStopRootComponent } from './start-stop-root-component'; import { StartStopRootComponentExtensionConfiguration } from './start-stop-root-component-extension-configuration'; import { Component } from '../component'; +import { StartStopRootComponentViewConfiguration } from './start-stop-root-component-view-configuration'; -const defaultConfiguration: StartStopRootComponentExtensionConfiguration = { - view: { - size: 30, - defaultIconSize: 22, - folderIconSize: 18, - startIconD: Icons.play, - stopIconD: Icons.stop, - folderIconD: Icons.folder - } +const defaultViewConfiguration: StartStopRootComponentViewConfiguration = { + size: 30, + defaultIconSize: 22, + folderIconSize: 18, + start: { + iconD: Icons.play + }, + stopIconD: Icons.stop, + folderIconD: Icons.folder }; export class StartStopRootComponentExtension implements RootComponentExtension { public static create(configuration?: StartStopRootComponentExtensionConfiguration) { - return new StartStopRootComponentExtension(configuration ?? defaultConfiguration); + return new StartStopRootComponentExtension(configuration); } - private constructor(private readonly configuration: StartStopRootComponentExtensionConfiguration) {} + private constructor(private readonly configuration: StartStopRootComponentExtensionConfiguration | undefined) {} public create( parentElement: SVGElement, @@ -30,6 +31,7 @@ export class StartStopRootComponentExtension implements RootComponentExtension { parentPlaceIndicator: SequencePlaceIndicator | null, context: ComponentContext ): Component { - return StartStopRootComponent.create(parentElement, sequence, parentPlaceIndicator, context, this.configuration.view); + const view = this.configuration?.view ? { ...defaultViewConfiguration, ...this.configuration.view } : defaultViewConfiguration; + return StartStopRootComponent.create(parentElement, sequence, parentPlaceIndicator, context, view); } } diff --git a/designer/src/workspace/start-stop-root/start-stop-root-component-view-configuration.ts b/designer/src/workspace/start-stop-root/start-stop-root-component-view-configuration.ts index ee1c4b88..39889be5 100644 --- a/designer/src/workspace/start-stop-root/start-stop-root-component-view-configuration.ts +++ b/designer/src/workspace/start-stop-root/start-stop-root-component-view-configuration.ts @@ -3,6 +3,8 @@ export interface StartStopRootComponentViewConfiguration { defaultIconSize: number; folderIconSize: number; folderIconD: string; - startIconD: string; + start: { + iconD: string; + } | null; stopIconD: string; } diff --git a/designer/src/workspace/start-stop-root/start-stop-root-component-view.spec.ts b/designer/src/workspace/start-stop-root/start-stop-root-component-view.spec.ts index d89e3fd0..89506bce 100644 --- a/designer/src/workspace/start-stop-root/start-stop-root-component-view.spec.ts +++ b/designer/src/workspace/start-stop-root/start-stop-root-component-view.spec.ts @@ -12,7 +12,9 @@ describe('StartStopRootComponentView', () => { defaultIconSize: 22, folderIconSize: 22, folderIconD: Icons.folder, - startIconD: Icons.play, + start: { + iconD: Icons.play + }, stopIconD: Icons.stop }); expect(parent.children.length).not.toEqual(0); diff --git a/designer/src/workspace/start-stop-root/start-stop-root-component-view.ts b/designer/src/workspace/start-stop-root/start-stop-root-component-view.ts index 84742f4d..654e8646 100644 --- a/designer/src/workspace/start-stop-root/start-stop-root-component-view.ts +++ b/designer/src/workspace/start-stop-root/start-stop-root-component-view.ts @@ -26,7 +26,7 @@ export class StartStopRootComponentView implements ComponentView { { sequence, depth: 0, - isInputConnected: true, + isInputConnected: Boolean(cfg.start), isOutputConnected: true, isPreview: false }, @@ -36,11 +36,13 @@ export class StartStopRootComponentView implements ComponentView { const x = view.joinX - cfg.size / 2; const endY = cfg.size + view.height; - const iconSize = parentPlaceIndicator ? cfg.folderIconSize : cfg.defaultIconSize; - const startCircle = createCircle('start', parentPlaceIndicator ? cfg.folderIconD : cfg.startIconD, cfg.size, iconSize); - Dom.translate(startCircle, x, 0); - g.appendChild(startCircle); + + if (cfg.start) { + const startCircle = createCircle('start', parentPlaceIndicator ? cfg.folderIconD : cfg.start.iconD, cfg.size, iconSize); + Dom.translate(startCircle, x, 0); + g.appendChild(startCircle); + } Dom.translate(view.g, 0, cfg.size); diff --git a/designer/src/workspace/start-stop-root/start-stop-root-component.spec.ts b/designer/src/workspace/start-stop-root/start-stop-root-component.spec.ts index d2b861a2..61b0e5c6 100644 --- a/designer/src/workspace/start-stop-root/start-stop-root-component.spec.ts +++ b/designer/src/workspace/start-stop-root/start-stop-root-component.spec.ts @@ -12,7 +12,9 @@ describe('StartStopRootComponent', () => { defaultIconSize: 22, folderIconSize: 22, folderIconD: Icons.folder, - startIconD: Icons.play, + start: { + iconD: Icons.play + }, stopIconD: Icons.stop }); diff --git a/designer/src/workspace/step-component-view-context-factory.ts b/designer/src/workspace/step-component-view-context-factory.ts index 24b92a87..c6b858e8 100644 --- a/designer/src/workspace/step-component-view-context-factory.ts +++ b/designer/src/workspace/step-component-view-context-factory.ts @@ -10,6 +10,21 @@ export class StepComponentViewContextFactory { i18n: componentContext.i18n, getStepIconUrl: () => componentContext.iconProvider.getIconUrl(stepContext.step), getStepName: () => componentContext.i18n(`step.${stepContext.step.type}.name`, stepContext.step.name), + createStepComponent: (parentElement: SVGElement, parentSequence: Sequence, step: Step, position: number) => { + return componentContext.stepComponentFactory.create( + parentElement, + { + parentSequence, + step, + depth: stepContext.depth + 1, + position, + isInputConnected: stepContext.isInputConnected, + isOutputConnected: stepContext.isOutputConnected, + isPreview: stepContext.isPreview + }, + componentContext + ); + }, createSequenceComponent: (parentElement: SVGElement, sequence: Sequence) => { const sequenceContext: SequenceContext = { sequence, @@ -33,6 +48,8 @@ export class StepComponentViewContextFactory { contentFactory ); }, + getPlaceholderGapSize: orientation => componentContext.services.placeholder.getGapSize(orientation), + createPlaceholderForGap: componentContext.services.placeholder.createForGap.bind(componentContext.services.placeholder), createPlaceholderForArea: componentContext.services.placeholder.createForArea.bind(componentContext.services.placeholder), getPreference: (key: string) => componentContext.preferenceStorage.getItem(preferenceKeyPrefix + key), setPreference: (key: string, value: string) => componentContext.preferenceStorage.setItem(preferenceKeyPrefix + key, value) diff --git a/designer/src/workspace/task-step/task-step-component-view.ts b/designer/src/workspace/task-step/task-step-component-view.ts index 44fc0760..40620aab 100644 --- a/designer/src/workspace/task-step/task-step-component-view.ts +++ b/designer/src/workspace/task-step/task-step-component-view.ts @@ -60,7 +60,7 @@ export const createTaskStepComponentViewFactory = }); g.appendChild(icon); - const isInputViewHidden = stepContext.depth === 0 && stepContext.position === 0 && !stepContext.isInputConnected; + const isInputViewHidden = !stepContext.isInputConnected; // TODO: handle inside the folder const isOutputViewHidden = isInterrupted; const inputView = isInputViewHidden ? null : InputView.createRoundInput(g, boxWidth / 2, 0, cfg.inputSize); diff --git a/examples/assets/icon-trigger.svg b/examples/assets/icon-trigger.svg new file mode 100644 index 00000000..11922200 --- /dev/null +++ b/examples/assets/icon-trigger.svg @@ -0,0 +1 @@ + diff --git a/examples/assets/triggers.js b/examples/assets/triggers.js new file mode 100644 index 00000000..b1c44be7 --- /dev/null +++ b/examples/assets/triggers.js @@ -0,0 +1,135 @@ +/* global document, sequentialWorkflowDesigner */ + +function createTriggerStep(id, name, properties) { + return { + id, + componentType: 'task', + type: 'trigger', + name, + properties: properties || {} + }; +} + +function createTaskStep(id, type, name, properties) { + return { + id, + componentType: 'task', + type, + name, + properties: properties || {} + }; +} + +function createLoopStep(id, steps) { + return { + id, + componentType: 'container', + type: 'loop', + name: 'Loop', + properties: {}, + sequence: steps + }; +} + +const configuration = { + theme: 'soft', + undoStackSize: 20, + + toolbox: { + groups: [ + { + name: 'Signals', + steps: [ + createTriggerStep(null, 'On email received'), + createTriggerStep(null, 'On file created'), + createTriggerStep(null, 'On file modified') + ] + }, + { + name: 'Tasks', + steps: [createTaskStep(null, 'save', 'Save file'), createTaskStep(null, 'text', 'Send email'), createLoopStep(null, [])] + } + ] + }, + + controlBar: true, + + steps: { + iconUrlProvider: (_, type) => { + return `./assets/icon-${type}.svg`; + }, + isDuplicable(step) { + return step.componentType !== 'launchPad'; + }, + isDeletable(step) { + return step.componentType !== 'launchPad'; + }, + isDraggable(step) { + return step.componentType !== 'launchPad'; + }, + isSelectable(step) { + return step.componentType !== 'launchPad'; + } + }, + + placeholder: { + canCreate(sequence, index) { + const definition = designer.getDefinition(); + const isRoot = sequence === definition.sequence; + if (isRoot) { + return index !== 0; + } + return true; + }, + canShow(sequence, _0, _1, draggingStepType) { + const definition = designer.getDefinition(); + const isTriggerStep = draggingStepType === 'trigger'; + const isLaunchPadSequence = sequence === definition.sequence[0].sequence; + return (isLaunchPadSequence && isTriggerStep) || (!isLaunchPadSequence && !isTriggerStep); + } + }, + + validator: { + step: step => { + return !step.properties['isInvalid']; + } + }, + + editors: { + rootEditorProvider: () => { + const root = document.createElement('div'); + return root; + }, + stepEditorProvider: step => { + const root = document.createElement('div'); + root.innerText = step.type; + return root; + } + }, + + extensions: [ + sequentialWorkflowDesigner.StartStopRootComponentDesignerExtension.create({ + view: { + start: null + } + }) + ] +}; + +const definition = { + properties: {}, + sequence: [ + { + id: 'launchPad', + name: 'Launch Pad', + componentType: 'launchPad', + type: 'launchPad', + properties: {}, + sequence: [createTriggerStep('0x1', 'On email received'), createTriggerStep('0x2', 'On file created')] + }, + createTaskStep('0x3', 'save', 'Save file') + ] +}; + +const placeholder = document.getElementById('designer'); +const designer = sequentialWorkflowDesigner.Designer.create(placeholder, definition, configuration); diff --git a/examples/triggers.html b/examples/triggers.html new file mode 100644 index 00000000..98fe9381 --- /dev/null +++ b/examples/triggers.html @@ -0,0 +1,55 @@ + + + + + + + 💥 Triggers Example - Sequential Workflow Designer + + + + + + + + + + + + +
+
+

💥 Triggers Example

+
+
+ GitHub | + NoCode JS +
+
+ +
+ + + + + From 55f469869ba8ff83a14275e65f0d819f83e08857 Mon Sep 17 00:00:00 2001 From: b4rtaz Date: Fri, 20 Jun 2025 00:06:33 +0200 Subject: [PATCH 2/9] fix: html. --- examples/triggers.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/triggers.html b/examples/triggers.html index 98fe9381..ae9f1b05 100644 --- a/examples/triggers.html +++ b/examples/triggers.html @@ -7,7 +7,7 @@ 💥 Triggers Example - Sequential Workflow Designer - + From 1d0d3ee59531a5f6ae52f0ab5348b9f1bd283983 Mon Sep 17 00:00:00 2001 From: b4rtaz Date: Fri, 20 Jun 2025 23:03:14 +0200 Subject: [PATCH 3/9] update framework packages. --- angular/designer/src/designer.component.ts | 4 ++++ react/src/SequentialWorkflowDesigner.tsx | 17 +++++++++++------ .../src/lib/SequentialWorkflowDesigner.svelte | 9 ++++++--- 3 files changed, 21 insertions(+), 9 deletions(-) diff --git a/angular/designer/src/designer.component.ts b/angular/designer/src/designer.component.ts index 43cbd3f9..64dc9a6e 100644 --- a/angular/designer/src/designer.component.ts +++ b/angular/designer/src/designer.component.ts @@ -29,6 +29,7 @@ import { ToolboxConfiguration, UidGenerator, ValidatorConfiguration, + PlaceholderConfiguration, I18n, PreferenceStorage } from 'sequential-workflow-designer'; @@ -69,6 +70,8 @@ export class DesignerComponent implements AfterViewInit, OnChanges, OnDestroy { public stepsConfiguration?: StepsConfiguration; @Input('validatorConfiguration') public validatorConfiguration?: ValidatorConfiguration; + @Input('placeholderConfiguration') + public placeholderConfiguration?: PlaceholderConfiguration; @Input('toolboxConfiguration') public toolboxConfiguration?: AngularToolboxConfiguration | false; @Input('controlBar') @@ -214,6 +217,7 @@ export class DesignerComponent implements AfterViewInit, OnChanges, OnDestroy { }, steps: this.stepsConfiguration, validator: this.validatorConfiguration, + placeholder: this.placeholderConfiguration, toolbox: this.toolboxConfiguration ? { isCollapsed: this.isToolboxCollapsed, diff --git a/react/src/SequentialWorkflowDesigner.tsx b/react/src/SequentialWorkflowDesigner.tsx index 7656ddb3..d33aff23 100644 --- a/react/src/SequentialWorkflowDesigner.tsx +++ b/react/src/SequentialWorkflowDesigner.tsx @@ -18,7 +18,8 @@ import { StepEditorProvider, KeyboardConfiguration, I18n, - PreferenceStorage + PreferenceStorage, + PlaceholderConfiguration } from 'sequential-workflow-designer'; import { RootEditorWrapperContext } from './RootEditorWrapper'; import { StepEditorWrapperContext } from './StepEditorWrapper'; @@ -46,6 +47,7 @@ export interface SequentialWorkflowDesignerProps undoStackSize?: number; stepsConfiguration: StepsConfiguration; validatorConfiguration?: ValidatorConfiguration; + placeholderConfiguration?: PlaceholderConfiguration; toolboxConfiguration: false | ReactToolboxConfiguration; isToolboxCollapsed?: boolean; onIsToolboxCollapsedChanged?: (isCollapsed: boolean) => void; @@ -63,7 +65,7 @@ export interface SequentialWorkflowDesignerProps } export function SequentialWorkflowDesigner(props: SequentialWorkflowDesignerProps) { - const [placeholder, setPlaceholder] = useState(null); + const [root, setRoot] = useState(null); const onDefinitionChangeRef = useRef(props.onDefinitionChange); const onSelectedStepIdChangedRef = useRef(props.onSelectedStepIdChanged); @@ -84,6 +86,7 @@ export function SequentialWorkflowDesigner(props const undoStackSize = props.undoStackSize; const steps = props.stepsConfiguration; const validator = props.validatorConfiguration; + const placeholder = props.placeholderConfiguration; const toolbox = props.toolboxConfiguration; const isEditorCollapsed = props.isEditorCollapsed; const isToolboxCollapsed = props.isToolboxCollapsed; @@ -181,7 +184,7 @@ export function SequentialWorkflowDesigner(props }, [props.customActionHandler]); useEffect(() => { - if (!placeholder) { + if (!root) { return; } @@ -215,7 +218,7 @@ export function SequentialWorkflowDesigner(props tryDestroy(); } - const designer = Designer.create(placeholder, definition.value, { + const designer = Designer.create(root, definition.value, { theme, undoStackSize, toolbox: toolbox @@ -226,6 +229,7 @@ export function SequentialWorkflowDesigner(props : false, steps, validator, + placeholder, controlBar, contextMenu, keyboard, @@ -272,7 +276,7 @@ export function SequentialWorkflowDesigner(props designerRef.current = designer; }, [ - placeholder, + root, definition, selectedStepId, isReadonly, @@ -287,6 +291,7 @@ export function SequentialWorkflowDesigner(props controlBar, steps, validator, + placeholder, extensions, i18n ]); @@ -295,5 +300,5 @@ export function SequentialWorkflowDesigner(props return tryDestroy; }, []); - return
; + return
; } diff --git a/svelte/src/lib/SequentialWorkflowDesigner.svelte b/svelte/src/lib/SequentialWorkflowDesigner.svelte index 09cf5ae1..55dacfd4 100644 --- a/svelte/src/lib/SequentialWorkflowDesigner.svelte +++ b/svelte/src/lib/SequentialWorkflowDesigner.svelte @@ -11,6 +11,7 @@ type RootEditorContext, type UndoStack, type ValidatorConfiguration, + type PlaceholderConfiguration, type UidGenerator, type DesignerExtension, type EditorsConfiguration, @@ -50,6 +51,7 @@ export let undoStackSize: number | undefined = undefined; export let undoStack: UndoStack | undefined = undefined; export let validator: ValidatorConfiguration | undefined = undefined; + export let placeholder: PlaceholderConfiguration | undefined = undefined; export let definitionWalker: DefinitionWalker | undefined = undefined; export let extensions: DesignerExtension[] | undefined = undefined; export let i18n: I18n | undefined = undefined; @@ -79,7 +81,7 @@ let isFirstChange = true; let designer: Designer | null = null; - let placeholder: HTMLElement; + let root: HTMLElement; function init() { const editors: EditorsConfiguration | false = @@ -123,7 +125,7 @@ } : false; - const d = Designer.create(placeholder, definition, { + const d = Designer.create(root, definition, { steps, controlBar, toolbox: _toolbox, @@ -135,6 +137,7 @@ undoStackSize, undoStack, validator, + placeholder, definitionWalker, extensions, isReadonly, @@ -205,4 +208,4 @@ }); -
+
From 1235649ed568224dcfaab2ec2daa941f3f3733b2 Mon Sep 17 00:00:00 2001 From: b4rtaz Date: Fri, 20 Jun 2025 23:07:09 +0200 Subject: [PATCH 4/9] expose interfaces. --- designer/src/workspace/index.ts | 1 + designer/src/workspace/launch-pad-step/index.ts | 3 +++ 2 files changed, 4 insertions(+) create mode 100644 designer/src/workspace/launch-pad-step/index.ts diff --git a/designer/src/workspace/index.ts b/designer/src/workspace/index.ts index 04b82d6a..1c9cde94 100644 --- a/designer/src/workspace/index.ts +++ b/designer/src/workspace/index.ts @@ -4,6 +4,7 @@ export * from './sequence'; export * from './start-stop-root'; export * from './container-step'; export * from './grid'; +export * from './launch-pad-step'; export * from './switch-step'; export * from './task-step'; export * from './viewport'; diff --git a/designer/src/workspace/launch-pad-step/index.ts b/designer/src/workspace/launch-pad-step/index.ts new file mode 100644 index 00000000..88e77054 --- /dev/null +++ b/designer/src/workspace/launch-pad-step/index.ts @@ -0,0 +1,3 @@ +export * from './launch-pad-step-component-view'; +export * from './launch-pad-step-component-view-configuration'; +export * from './launch-pad-step-extension-configuration'; From 1a64969c64826c4b96930be4c7257bddeca330e4 Mon Sep 17 00:00:00 2001 From: b4rtaz Date: Sat, 21 Jun 2025 10:29:42 +0200 Subject: [PATCH 5/9] fix: configuration. --- .../launch-pad-step/launch-pad-step-extension-configuration.ts | 1 - .../src/workspace/launch-pad-step/launch-pad-step-extension.ts | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/designer/src/workspace/launch-pad-step/launch-pad-step-extension-configuration.ts b/designer/src/workspace/launch-pad-step/launch-pad-step-extension-configuration.ts index 80e56c69..614c634c 100644 --- a/designer/src/workspace/launch-pad-step/launch-pad-step-extension-configuration.ts +++ b/designer/src/workspace/launch-pad-step/launch-pad-step-extension-configuration.ts @@ -2,5 +2,4 @@ import { LaunchPadStepComponentViewConfiguration } from './launch-pad-step-compo export interface LaunchPadStepExtensionConfiguration { view?: LaunchPadStepComponentViewConfiguration; - componentType?: string; } diff --git a/designer/src/workspace/launch-pad-step/launch-pad-step-extension.ts b/designer/src/workspace/launch-pad-step/launch-pad-step-extension.ts index 8fab52e4..652170ff 100644 --- a/designer/src/workspace/launch-pad-step/launch-pad-step-extension.ts +++ b/designer/src/workspace/launch-pad-step/launch-pad-step-extension.ts @@ -22,7 +22,7 @@ export class LaunchPadStepExtension implements StepExtension { return new LaunchPadStepExtension(configuration); } - public readonly componentType = this.configuration?.componentType || 'launchPad'; + public readonly componentType = 'launchPad'; private constructor(private readonly configuration: LaunchPadStepExtensionConfiguration | undefined) {} From 4d952ec56f989c99c3556ff5b106f38a7fa77b8c Mon Sep 17 00:00:00 2001 From: b4rtaz Date: Sat, 21 Jun 2025 12:51:01 +0200 Subject: [PATCH 6/9] changed structure. --- designer/src/behaviors/placeholder-finder.ts | 11 +++++---- designer/src/workspace/component.ts | 2 +- .../container-step-component-view.ts | 2 +- .../launch-pad-step-component-view.ts | 2 +- .../sequence/default-sequence-component.ts | 12 ++++------ designer/src/workspace/step-component.ts | 24 ++++++++++++------- .../switch-step/switch-step-component-view.ts | 2 +- .../task-step/task-step-component-view.ts | 2 +- 8 files changed, 33 insertions(+), 24 deletions(-) diff --git a/designer/src/behaviors/placeholder-finder.ts b/designer/src/behaviors/placeholder-finder.ts index ac93e43d..a3be3d58 100644 --- a/designer/src/behaviors/placeholder-finder.ts +++ b/designer/src/behaviors/placeholder-finder.ts @@ -14,6 +14,7 @@ export class PlaceholderFinder { placeholder: Placeholder; lt: Vector; // left top br: Vector; // bottom right + diagSq: number; // left top diagonal squared }[]; private constructor( @@ -27,15 +28,17 @@ export class PlaceholderFinder { this.cache = this.placeholders.map(placeholder => { const rect = placeholder.getClientRect(); + const lt = new Vector(rect.x, rect.y).add(scroll); + const br = new Vector(rect.x + rect.width, rect.y + rect.height).add(scroll); return { placeholder, - lt: new Vector(rect.x, rect.y).add(scroll), - br: new Vector(rect.x + rect.width, rect.y + rect.height).add(scroll) + lt, + br, + diagSq: lt.x * lt.x + lt.y * lt.y }; }); - this.cache.sort((a, b) => a.lt.y - b.lt.y); + this.cache.sort((a, b) => a.diagSq - b.diagSq); } - const vR = vLt.x + vWidth; const vB = vLt.y + vHeight; return this.cache.find(p => { diff --git a/designer/src/workspace/component.ts b/designer/src/workspace/component.ts index 3eae8107..2a1c0199 100644 --- a/designer/src/workspace/component.ts +++ b/designer/src/workspace/component.ts @@ -25,7 +25,7 @@ export interface ComponentView { } export interface StepComponentView extends ComponentView { - sequenceComponents: SequenceComponent[] | null; + components: Component[] | null; placeholders: Placeholder[] | null; hasOutput: boolean; diff --git a/designer/src/workspace/container-step/container-step-component-view.ts b/designer/src/workspace/container-step/container-step-component-view.ts index 62d43726..553be8b1 100644 --- a/designer/src/workspace/container-step/container-step-component-view.ts +++ b/designer/src/workspace/container-step/container-step-component-view.ts @@ -47,7 +47,7 @@ export const createContainerStepComponentViewFactory = height, joinX, placeholders: null, - sequenceComponents: [sequenceComponent], + components: [sequenceComponent], hasOutput: sequenceComponent.hasOutput, getClientPosition(): Vector { diff --git a/designer/src/workspace/launch-pad-step/launch-pad-step-component-view.ts b/designer/src/workspace/launch-pad-step/launch-pad-step-component-view.ts index ff14fae3..f4e83261 100644 --- a/designer/src/workspace/launch-pad-step/launch-pad-step-component-view.ts +++ b/designer/src/workspace/launch-pad-step/launch-pad-step-component-view.ts @@ -142,7 +142,7 @@ function createView( width, height, joinX, - sequenceComponents: components, + components, placeholders, hasOutput, diff --git a/designer/src/workspace/sequence/default-sequence-component.ts b/designer/src/workspace/sequence/default-sequence-component.ts index 48f762b3..3c3476dc 100644 --- a/designer/src/workspace/sequence/default-sequence-component.ts +++ b/designer/src/workspace/sequence/default-sequence-component.ts @@ -33,22 +33,20 @@ export class DefaultSequenceComponent implements SequenceComponent { public findById(stepId: string): StepComponent | null { for (const component of this.view.components) { - const sc = component.findById(stepId); - if (sc) { - return sc; + const result = component.findById(stepId); + if (result) { + return result; } } return null; } public resolvePlaceholders(skipComponent: StepComponent | undefined, result: FoundPlaceholders) { + this.view.components.forEach(component => component.resolvePlaceholders(skipComponent, result)); this.view.placeholders.forEach(placeholder => result.placeholders.push(placeholder)); - this.view.components.forEach(c => c.resolvePlaceholders(skipComponent, result)); } public updateBadges(result: BadgesResult) { - for (const component of this.view.components) { - component.updateBadges(result); - } + this.view.components.forEach(component => component.updateBadges(result)); } } diff --git a/designer/src/workspace/step-component.ts b/designer/src/workspace/step-component.ts index 0d223a91..306f4596 100644 --- a/designer/src/workspace/step-component.ts +++ b/designer/src/workspace/step-component.ts @@ -22,8 +22,8 @@ export class StepComponent implements Component { if (this.step.id === stepId) { return this; } - if (this.view.sequenceComponents) { - for (const component of this.view.sequenceComponents) { + if (this.view.components) { + for (const component of this.view.components) { const result = component.findById(stepId); if (result) { return result; @@ -34,14 +34,22 @@ export class StepComponent implements Component { } public resolveClick(click: ClickDetails): ClickCommand | null { - if (this.view.sequenceComponents) { - for (const component of this.view.sequenceComponents) { + if (this.view.components) { + for (const component of this.view.components) { const result = component.resolveClick(click); if (result) { return result; } } } + if (this.view.placeholders) { + for (const placeholder of this.view.placeholders) { + const result = placeholder.resolveClick(click); + if (result) { + return result; + } + } + } const badgeResult = this.badges.resolveClick(click); if (badgeResult) { return badgeResult; @@ -60,8 +68,8 @@ export class StepComponent implements Component { public resolvePlaceholders(skipComponent: StepComponent | undefined, result: FoundPlaceholders) { if (skipComponent !== this) { - if (this.view.sequenceComponents) { - this.view.sequenceComponents.forEach(component => component.resolvePlaceholders(skipComponent, result)); + if (this.view.components) { + this.view.components.forEach(component => component.resolvePlaceholders(skipComponent, result)); } if (this.view.placeholders) { this.view.placeholders.forEach(ph => result.placeholders.push(ph)); @@ -83,8 +91,8 @@ export class StepComponent implements Component { } public updateBadges(result: BadgesResult) { - if (this.view.sequenceComponents) { - this.view.sequenceComponents.forEach(component => component.updateBadges(result)); + if (this.view.components) { + this.view.components.forEach(component => component.updateBadges(result)); } this.badges.update(result); } diff --git a/designer/src/workspace/switch-step/switch-step-component-view.ts b/designer/src/workspace/switch-step/switch-step-component-view.ts index 83a200e9..45bbe24c 100644 --- a/designer/src/workspace/switch-step/switch-step-component-view.ts +++ b/designer/src/workspace/switch-step/switch-step-component-view.ts @@ -32,7 +32,7 @@ function createView( height, joinX, placeholders: null, - sequenceComponents, + components: sequenceComponents, hasOutput: sequenceComponents ? sequenceComponents.some(c => c.hasOutput) : true, getClientPosition(): Vector { diff --git a/designer/src/workspace/task-step/task-step-component-view.ts b/designer/src/workspace/task-step/task-step-component-view.ts index 40620aab..614ed209 100644 --- a/designer/src/workspace/task-step/task-step-component-view.ts +++ b/designer/src/workspace/task-step/task-step-component-view.ts @@ -71,7 +71,7 @@ export const createTaskStepComponentViewFactory = width: boxWidth, height: boxHeight, joinX: boxWidth / 2, - sequenceComponents: null, + components: null, placeholders: null, hasOutput: !!outputView, From c83d3a2ca620c20dce916dd67a5c1f2cf23b83a9 Mon Sep 17 00:00:00 2001 From: b4rtaz Date: Sat, 21 Jun 2025 13:45:36 +0200 Subject: [PATCH 7/9] icon strategy. --- designer/sass/designer-theme.scss | 6 +----- designer/src/core/icons.ts | 1 - ...nch-pad-step-component-view-configuration.ts | 1 - .../launch-pad-step-component-view.ts | 17 ++++++++++++++--- .../launch-pad-step-extension.ts | 4 +--- examples/assets/triggers.js | 3 +++ 6 files changed, 19 insertions(+), 13 deletions(-) diff --git a/designer/sass/designer-theme.scss b/designer/sass/designer-theme.scss index a2b93ba6..a18ff93e 100644 --- a/designer/sass/designer-theme.scss +++ b/designer/sass/designer-theme.scss @@ -362,8 +362,7 @@ $emptyInputFillColor: #fff, $emptyOutputFillColor: #000, $emptyOutputStrokeWidth: 0, - $emptyOutputStrokeColor: #000, - $emptyIconFillColor: #b3b3b3 + $emptyOutputStrokeColor: #000 ) { .sqd-theme-#{$theme} .sqd-step-launch-pad#{if($stepType != '', '.sqd-type-' + $stepType, '')} { & > g > { @@ -378,9 +377,6 @@ $outputStrokeColor: $emptyOutputStrokeColor ); } - & > g > .sqd-launch-pad-empty-icon { - fill: $emptyIconFillColor; - } } } diff --git a/designer/src/core/icons.ts b/designer/src/core/icons.ts index 99fe0ce4..9afc265b 100644 --- a/designer/src/core/icons.ts +++ b/designer/src/core/icons.ts @@ -31,7 +31,6 @@ export class Icons { public static stop = 'M10.75 37.25V10.7H37.3v26.55Z'; public static folder = 'M7.05 40q-1.2 0-2.1-.925-.9-.925-.9-2.075V11q0-1.15.9-2.075Q5.85 8 7.05 8h14l3 3h17q1.15 0 2.075.925.925.925.925 2.075v23q0 1.15-.925 2.075Q42.2 40 41.05 40Z'; - public static trigger = 'm12.93 46.3 8.93-16.75-17.87-2.23 26.8-25.68h4.47l-8.93 16.75 17.87 2.23L17.4 46.3h-4.47z'; public static appendPath(parent: SVGElement, pathClassName: string, d: string, size: number): SVGGElement { const g = Dom.svg('g'); diff --git a/designer/src/workspace/launch-pad-step/launch-pad-step-component-view-configuration.ts b/designer/src/workspace/launch-pad-step/launch-pad-step-component-view-configuration.ts index a045eb83..0275f7e6 100644 --- a/designer/src/workspace/launch-pad-step/launch-pad-step-component-view-configuration.ts +++ b/designer/src/workspace/launch-pad-step/launch-pad-step-component-view-configuration.ts @@ -7,5 +7,4 @@ export interface LaunchPadStepComponentViewConfiguration { emptyInputSize: number; emptyOutputSize: number; emptyIconSize: number; - emptyIconD: string; } diff --git a/designer/src/workspace/launch-pad-step/launch-pad-step-component-view.ts b/designer/src/workspace/launch-pad-step/launch-pad-step-component-view.ts index f4e83261..b76103f1 100644 --- a/designer/src/workspace/launch-pad-step/launch-pad-step-component-view.ts +++ b/designer/src/workspace/launch-pad-step/launch-pad-step-component-view.ts @@ -10,7 +10,7 @@ import { import { ComponentDom, InputView, JoinView, OutputView } from '../common-views'; import { LaunchPadStepComponentViewConfiguration } from './launch-pad-step-component-view-configuration'; import { ClickCommand, ClickDetails, Placeholder, StepComponentView } from '../component'; -import { Dom, getAbsolutePosition, Icons, Vector } from '../../core'; +import { Dom, getAbsolutePosition, Vector } from '../../core'; import { StepComponent } from '../step-component'; const COMPONENT_CLASS_NAME = 'launch-pad'; @@ -118,8 +118,19 @@ function createView( outputView = OutputView.create(g, width / 2, height, cfg.emptyOutputSize); } - const icon = Icons.appendPath(g, 'sqd-launch-pad-empty-icon', cfg.emptyIconD, cfg.emptyIconSize); - Dom.translate(icon, (width - cfg.emptyIconSize) / 2, (height - cfg.emptyIconSize) / 2); + if (cfg.emptyIconSize > 0) { + const iconUrl = viewContext.getStepIconUrl(); + if (iconUrl) { + const icon = Dom.svg('image', { + href: iconUrl, + x: (width - cfg.emptyIconSize) / 2, + y: (height - cfg.emptyIconSize) / 2, + width: cfg.emptyIconSize, + height: cfg.emptyIconSize + }); + g.appendChild(icon); + } + } joinX = width / 2; } diff --git a/designer/src/workspace/launch-pad-step/launch-pad-step-extension.ts b/designer/src/workspace/launch-pad-step/launch-pad-step-extension.ts index 652170ff..9c3938c4 100644 --- a/designer/src/workspace/launch-pad-step/launch-pad-step-extension.ts +++ b/designer/src/workspace/launch-pad-step/launch-pad-step-extension.ts @@ -3,7 +3,6 @@ import { StepExtension } from '../../designer-extension'; import { LaunchPadStepExtensionConfiguration } from './launch-pad-step-extension-configuration'; import { createLaunchPadStepComponentViewFactory } from './launch-pad-step-component-view'; import { LaunchPadStepComponentViewConfiguration } from './launch-pad-step-component-view-configuration'; -import { Icons } from '../../core'; const defaultViewConfiguration: LaunchPadStepComponentViewConfiguration = { isRegionEnabled: true, @@ -13,8 +12,7 @@ const defaultViewConfiguration: LaunchPadStepComponentViewConfiguration = { emptyPaddingY: 20, emptyInputSize: 14, emptyOutputSize: 10, - emptyIconSize: 24, - emptyIconD: Icons.trigger + emptyIconSize: 24 }; export class LaunchPadStepExtension implements StepExtension { diff --git a/examples/assets/triggers.js b/examples/assets/triggers.js index b1c44be7..bd3dcfa3 100644 --- a/examples/assets/triggers.js +++ b/examples/assets/triggers.js @@ -56,6 +56,9 @@ const configuration = { steps: { iconUrlProvider: (_, type) => { + if (type === 'launchPad') { + return './assets/icon-trigger.svg'; + } return `./assets/icon-${type}.svg`; }, isDuplicable(step) { From 86c48f62d0b151d2b9f84a907628dee4da5d1ce2 Mon Sep 17 00:00:00 2001 From: b4rtaz Date: Sun, 22 Jun 2025 11:46:39 +0200 Subject: [PATCH 8/9] small fixes. --- README.md | 1 + .../src/behaviors/select-step-behavior.ts | 5 ++- designer/src/modifier/state-modifier.ts | 4 +- .../launch-pad-step-component-view.spec.ts | 45 +++++++++++++++++++ examples/assets/triggers.js | 19 ++++++-- examples/triggers.html | 4 ++ 6 files changed, 73 insertions(+), 5 deletions(-) create mode 100644 designer/src/workspace/launch-pad-step/launch-pad-step-component-view.spec.ts diff --git a/README.md b/README.md index 4abb9e96..1c77f0ff 100644 --- a/README.md +++ b/README.md @@ -22,6 +22,7 @@ Features: ## 👀 Examples * [⏩ Live Testing](https://nocode-js.github.io/sequential-workflow-designer/examples/live-testing.html) +* [💥 Triggers](https://nocode-js.github.io/sequential-workflow-designer/examples/triggers.html) * [❎ Fullscreen](https://nocode-js.github.io/sequential-workflow-designer/examples/fullscreen.html) * [🌅 Image Filter](https://nocode-js.github.io/sequential-workflow-designer/examples/image-filter.html) * [🔴 Particles](https://nocode-js.github.io/sequential-workflow-designer/examples/particles.html) diff --git a/designer/src/behaviors/select-step-behavior.ts b/designer/src/behaviors/select-step-behavior.ts index a022915e..9a3bb675 100644 --- a/designer/src/behaviors/select-step-behavior.ts +++ b/designer/src/behaviors/select-step-behavior.ts @@ -46,7 +46,10 @@ export class SelectStepBehavior implements Behavior { return; } - this.stateModifier.trySelectStep(this.pressedStepComponent.step, this.pressedStepComponent.parentSequence); + if (!this.stateModifier.trySelectStep(this.pressedStepComponent.step, this.pressedStepComponent.parentSequence)) { + // If we cannot select the step, we clear the selection. + this.state.setSelectedStepId(null); + } return new SelectStepBehaviorEndToken(this.pressedStepComponent.step.id, Date.now()); } } diff --git a/designer/src/modifier/state-modifier.ts b/designer/src/modifier/state-modifier.ts index 8d681de3..97b82cad 100644 --- a/designer/src/modifier/state-modifier.ts +++ b/designer/src/modifier/state-modifier.ts @@ -36,10 +36,12 @@ export class StateModifier { return this.configuration.isSelectable ? this.configuration.isSelectable(step, parentSequence) : true; } - public trySelectStep(step: Step, parentSequence: Sequence) { + public trySelectStep(step: Step, parentSequence: Sequence): boolean { if (this.isSelectable(step, parentSequence)) { this.state.setSelectedStepId(step.id); + return true; } + return false; } public trySelectStepById(stepId: string) { diff --git a/designer/src/workspace/launch-pad-step/launch-pad-step-component-view.spec.ts b/designer/src/workspace/launch-pad-step/launch-pad-step-component-view.spec.ts new file mode 100644 index 00000000..842a6f03 --- /dev/null +++ b/designer/src/workspace/launch-pad-step/launch-pad-step-component-view.spec.ts @@ -0,0 +1,45 @@ +import { Dom } from '../../core/dom'; +import { SequentialStep } from '../../definition'; +import { StepContext } from '../../designer-extension'; +import { createComponentContextStub } from '../../test-tools/stubs'; +import { StepComponentViewContextFactory } from '../step-component-view-context-factory'; +import { createLaunchPadStepComponentViewFactory } from './launch-pad-step-component-view'; + +describe('LaunchPadStepComponentView', () => { + it('create() creates view', () => { + const parent = Dom.svg('svg'); + const step: SequentialStep = { + id: '0x', + componentType: 'launchPad', + name: 'x', + properties: {}, + type: 'launchPad', + sequence: [] + }; + const stepContext: StepContext = { + depth: 0, + position: 0, + isInputConnected: true, + isOutputConnected: false, + step, + parentSequence: [step], + isPreview: false + }; + const componentContext = createComponentContextStub(); + const viewContext = StepComponentViewContextFactory.create(stepContext, componentContext); + + const factory = createLaunchPadStepComponentViewFactory(false, { + isRegionEnabled: true, + paddingY: 10, + connectionHeight: 20, + emptyPaddingX: 20, + emptyPaddingY: 20, + emptyInputSize: 14, + emptyOutputSize: 10, + emptyIconSize: 24 + }); + factory(parent, stepContext, viewContext); + + expect(parent.children.length).not.toEqual(0); + }); +}); diff --git a/examples/assets/triggers.js b/examples/assets/triggers.js index bd3dcfa3..d1297067 100644 --- a/examples/assets/triggers.js +++ b/examples/assets/triggers.js @@ -38,7 +38,7 @@ const configuration = { toolbox: { groups: [ { - name: 'Signals', + name: 'Triggers', steps: [ createTriggerStep(null, 'On email received'), createTriggerStep(null, 'On file created'), @@ -101,11 +101,23 @@ const configuration = { editors: { rootEditorProvider: () => { const root = document.createElement('div'); + const h3 = document.createElement('h3'); + h3.innerText = 'Workflows Activated by Triggers'; + const p0 = document.createElement('p'); + p0.innerText = + 'This example demonstrates how to build a sequential workflow designer with support for triggers and tasks. A workflow can be initiated by any one of multiple triggers, after which a defined sequence of tasks is executed.'; + const p1 = document.createElement('p'); + p1.innerText = + 'Please note that only trigger steps can be added in the launch section, while any task steps can be added in the section below.'; + + root.appendChild(h3); + root.appendChild(p0); + root.appendChild(p1); return root; }, stepEditorProvider: step => { const root = document.createElement('div'); - root.innerText = step.type; + root.innerText = `Selected step type: ${step.type}`; return root; } }, @@ -130,7 +142,8 @@ const definition = { properties: {}, sequence: [createTriggerStep('0x1', 'On email received'), createTriggerStep('0x2', 'On file created')] }, - createTaskStep('0x3', 'save', 'Save file') + createTaskStep('0x3', 'save', 'Save file'), + createLoopStep('0x4', [createTaskStep('0x6', 'text', 'Send SMS')]) ] }; diff --git a/examples/triggers.html b/examples/triggers.html index ae9f1b05..a10b351c 100644 --- a/examples/triggers.html +++ b/examples/triggers.html @@ -28,6 +28,10 @@ #designer { flex: 1; } + .sqd-editor { + padding: 10px; + line-height: 1.3em; + } .sqd-type-trigger > .sqd-step-task-rect { fill: #ebfcff !important; } From 5fa5d2e3f2778d9ad9b449787ecdd6c95f4587e4 Mon Sep 17 00:00:00 2001 From: b4rtaz Date: Sun, 22 Jun 2025 19:43:48 +0200 Subject: [PATCH 9/9] 0.30.0. --- CHANGELOG.md | 10 ++++++++++ README.md | 8 ++++---- angular/designer/package.json | 4 ++-- demos/angular-app/package.json | 4 ++-- demos/angular-app/yarn.lock | 16 ++++++++-------- demos/react-app/package.json | 4 ++-- demos/svelte-app/package.json | 4 ++-- designer/package.json | 2 +- examples/assets/lib.js | 2 +- react/package.json | 6 +++--- svelte/package.json | 6 +++--- 11 files changed, 38 insertions(+), 28 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 991aa334..a1088888 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,13 @@ +# 0.30.0 + +This version introduces a new step component: `launchPad`. + +The `launchPad` step component allows you to place multiple steps along a horizontal axis. Its design suggests that any of the contained steps can be executed independently or simultaneously. You can use it as a container for parallel execution or as a trigger hub-waiting for one or more embedded trigger steps to activate the workflow. + +The main goal of this addition is to enable the creation of workflows with multiple triggers in the standard version of the designer. + +To see how it looks, please check out [this example](https://nocode-js.github.io/sequential-workflow-designer/examples/triggers.html). + # 0.29.2 Added a new theme: `soft`. diff --git a/README.md b/README.md index 1c77f0ff..58d358f9 100644 --- a/README.md +++ b/README.md @@ -105,10 +105,10 @@ Add the below code to your head section in HTML document. ```html ... - - - - + + + + ``` Call the designer by: diff --git a/angular/designer/package.json b/angular/designer/package.json index cea6b807..1ba1a371 100644 --- a/angular/designer/package.json +++ b/angular/designer/package.json @@ -1,7 +1,7 @@ { "name": "sequential-workflow-designer-angular", "description": "Angular wrapper for Sequential Workflow Designer component.", - "version": "0.29.2", + "version": "0.30.0", "author": { "name": "NoCode JS", "url": "https://nocode-js.com/" @@ -15,7 +15,7 @@ "peerDependencies": { "@angular/common": "12 - 19", "@angular/core": "12 - 19", - "sequential-workflow-designer": "^0.29.2" + "sequential-workflow-designer": "^0.30.0" }, "dependencies": { "tslib": "^2.3.0" diff --git a/demos/angular-app/package.json b/demos/angular-app/package.json index cfa8da8b..f967d4ac 100644 --- a/demos/angular-app/package.json +++ b/demos/angular-app/package.json @@ -26,8 +26,8 @@ "@angular/platform-browser-dynamic": "^17.3.9", "@angular/router": "^17.3.9", "rxjs": "~7.8.0", - "sequential-workflow-designer": "^0.29.2", - "sequential-workflow-designer-angular": "^0.29.2", + "sequential-workflow-designer": "^0.30.0", + "sequential-workflow-designer-angular": "^0.30.0", "tslib": "^2.3.0", "zone.js": "~0.14.6" }, diff --git a/demos/angular-app/yarn.lock b/demos/angular-app/yarn.lock index 6dbf3908..da36d43f 100644 --- a/demos/angular-app/yarn.lock +++ b/demos/angular-app/yarn.lock @@ -6744,17 +6744,17 @@ send@0.18.0: range-parser "~1.2.1" statuses "2.0.1" -sequential-workflow-designer-angular@^0.29.2: - version "0.29.2" - resolved "https://registry.yarnpkg.com/sequential-workflow-designer-angular/-/sequential-workflow-designer-angular-0.29.2.tgz#04a12899ea6daf50e12bae286bd2d792519f55c2" - integrity sha512-IiOi46UrE31EUe4DL9ZT1MwswDIsmpq/qIgC5GBEeI5kKENXL09e4KbePhJQXurnpSJCzcX7cUCnRuoFsMsCdg== +sequential-workflow-designer-angular@^0.30.0: + version "0.30.0" + resolved "https://registry.yarnpkg.com/sequential-workflow-designer-angular/-/sequential-workflow-designer-angular-0.30.0.tgz#94cf034281c6e3409a0fef921d132b67e3562fcd" + integrity sha512-7eRPzyZkEF65nhBMmNS4O3u9S0tuWBo07QxJsNjAijQMpkDE3BgMmNDssvjeUwFdKPR1oQdH5H1LzsBAIyXN3Q== dependencies: tslib "^2.3.0" -sequential-workflow-designer@^0.29.2: - version "0.29.2" - resolved "https://registry.yarnpkg.com/sequential-workflow-designer/-/sequential-workflow-designer-0.29.2.tgz#bdb610325396baab5f5e91f95bb846159cb0cb0b" - integrity sha512-yPxMLLZUV529HQIAVSEWHEjOLDWn+abEZ+E7VIq3vIAs9ew3fG7U/StUz+mA8+zr7iTSp9wlGUXdZi8ZRtQ1Qw== +sequential-workflow-designer@^0.30.0: + version "0.30.0" + resolved "https://registry.yarnpkg.com/sequential-workflow-designer/-/sequential-workflow-designer-0.30.0.tgz#98a11796ab3323030048965f8849d1b93a79e8c4" + integrity sha512-SwjlRMhO6auFpB9DiYm+1Lb1KHbLL7h66GgOEs60Ej4n0Y5yDTN9ifDLGOiMvUSWqTe3xkfEEsVQ/WLuW9Lz8Q== dependencies: sequential-workflow-model "^0.2.0" diff --git a/demos/react-app/package.json b/demos/react-app/package.json index 85d1954a..a4735d00 100644 --- a/demos/react-app/package.json +++ b/demos/react-app/package.json @@ -6,8 +6,8 @@ "dependencies": { "react": "^18.2.0", "react-dom": "^18.2.0", - "sequential-workflow-designer": "^0.29.2", - "sequential-workflow-designer-react": "^0.29.2" + "sequential-workflow-designer": "^0.30.0", + "sequential-workflow-designer-react": "^0.30.0" }, "devDependencies": { "@types/jest": "^29.2.5", diff --git a/demos/svelte-app/package.json b/demos/svelte-app/package.json index 8880f86a..b16d4208 100644 --- a/demos/svelte-app/package.json +++ b/demos/svelte-app/package.json @@ -16,8 +16,8 @@ "eslint": "eslint ./src --ext .ts" }, "dependencies": { - "sequential-workflow-designer": "^0.29.2", - "sequential-workflow-designer-svelte": "^0.29.2" + "sequential-workflow-designer": "^0.30.0", + "sequential-workflow-designer-svelte": "^0.30.0" }, "devDependencies": { "@sveltejs/adapter-static": "^2.0.3", diff --git a/designer/package.json b/designer/package.json index eeac9c14..fba08d55 100644 --- a/designer/package.json +++ b/designer/package.json @@ -1,7 +1,7 @@ { "name": "sequential-workflow-designer", "description": "Customizable no-code component for building flow-based programming applications.", - "version": "0.29.2", + "version": "0.30.0", "type": "module", "main": "./lib/esm/index.js", "types": "./lib/index.d.ts", diff --git a/examples/assets/lib.js b/examples/assets/lib.js index 94c33462..5c46b1ab 100644 --- a/examples/assets/lib.js +++ b/examples/assets/lib.js @@ -13,7 +13,7 @@ function embedStylesheet(url) { document.write(``); } -const baseUrl = isTestEnv() ? '../designer' : '//cdn.jsdelivr.net/npm/sequential-workflow-designer@0.29.2'; +const baseUrl = isTestEnv() ? '../designer' : '//cdn.jsdelivr.net/npm/sequential-workflow-designer@0.30.0'; embedScript(`${baseUrl}/dist/index.umd.js`); embedStylesheet(`${baseUrl}/css/designer.css`); diff --git a/react/package.json b/react/package.json index 70f44b43..e9106b05 100644 --- a/react/package.json +++ b/react/package.json @@ -1,7 +1,7 @@ { "name": "sequential-workflow-designer-react", "description": "React wrapper for Sequential Workflow Designer component.", - "version": "0.29.2", + "version": "0.30.0", "type": "module", "main": "./lib/esm/index.js", "types": "./lib/index.d.ts", @@ -47,7 +47,7 @@ "peerDependencies": { "react": ">=18.2.0", "react-dom": ">=18.2.0", - "sequential-workflow-designer": "^0.29.2" + "sequential-workflow-designer": "^0.30.0" }, "devDependencies": { "@rollup/plugin-node-resolve": "^16.0.1", @@ -63,7 +63,7 @@ "prettier": "^3.2.5", "react": "^18.2.0", "react-dom": "^18.2.0", - "sequential-workflow-designer": "^0.29.2", + "sequential-workflow-designer": "^0.30.0", "rollup": "^4.40.0", "rollup-plugin-dts": "^6.2.1", "rollup-plugin-typescript2": "^0.36.0", diff --git a/svelte/package.json b/svelte/package.json index f406acc6..d233fb13 100644 --- a/svelte/package.json +++ b/svelte/package.json @@ -1,7 +1,7 @@ { "name": "sequential-workflow-designer-svelte", "description": "Svelte wrapper for Sequential Workflow Designer component.", - "version": "0.29.2", + "version": "0.30.0", "license": "MIT", "scripts": { "prepare": "cp ../LICENSE LICENSE", @@ -28,10 +28,10 @@ ], "peerDependencies": { "svelte": "^4.0.0", - "sequential-workflow-designer": "^0.29.2" + "sequential-workflow-designer": "^0.30.0" }, "devDependencies": { - "sequential-workflow-designer": "^0.29.2", + "sequential-workflow-designer": "^0.30.0", "@sveltejs/adapter-static": "^2.0.3", "@sveltejs/kit": "^1.20.4", "@sveltejs/package": "^2.0.0",