From ec65dac307bff3a49a71d38a7e1debedca0a5549 Mon Sep 17 00:00:00 2001 From: Bart Tadych Date: Sat, 9 Nov 2024 00:28:59 +0100 Subject: [PATCH] 0.26.0. (#171) --- CHANGELOG.md | 4 ++ README.md | 9 ++-- 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 +- designer/src/api/control-bar-api.ts | 21 ++------ designer/src/api/designer-api.ts | 5 +- designer/src/api/viewport-api.ts | 19 ++++--- designer/src/api/workspace-api.ts | 16 +++--- .../src/behaviors/behavior-controller.spec.ts | 51 +++++++++++++++++++ designer/src/behaviors/behavior-controller.ts | 14 ++--- designer/src/behaviors/behavior.ts | 6 ++- ...efault-click-behavior-wrapper-extension.ts | 8 +++ .../default-click-behavior-wrapper.ts | 6 +++ designer/src/behaviors/index.ts | 2 + .../select-step-behavior-end-token.spec.ts | 16 ++++++ .../select-step-behavior-end-token.ts | 16 ++++++ .../src/behaviors/select-step-behavior.ts | 10 ++-- designer/src/control-bar/control-bar.ts | 10 ++-- designer/src/designer-extension.ts | 14 +++++ ...-viewport-controller-designer-extension.ts | 10 ++++ designer/src/extensions/index.ts | 4 ++ .../line-grid-designer-extension.ts | 6 +-- ...-stop-root-component-designer-extension.ts | 10 ++++ .../steps-designer-extension.ts | 16 +++--- designer/src/index.ts | 4 +- designer/src/services.ts | 7 +++ .../workspace/viewport/viewport-animator.ts | 11 ++-- designer/src/workspace/workspace.ts | 9 +++- examples/assets/lib.js | 2 +- react/package.json | 6 +-- svelte/package.json | 6 +-- 35 files changed, 250 insertions(+), 102 deletions(-) create mode 100644 designer/src/behaviors/behavior-controller.spec.ts create mode 100644 designer/src/behaviors/default-click-behavior-wrapper-extension.ts create mode 100644 designer/src/behaviors/default-click-behavior-wrapper.ts create mode 100644 designer/src/behaviors/index.ts create mode 100644 designer/src/behaviors/select-step-behavior-end-token.spec.ts create mode 100644 designer/src/behaviors/select-step-behavior-end-token.ts create mode 100644 designer/src/extensions/default-viewport-controller-designer-extension.ts create mode 100644 designer/src/extensions/index.ts rename designer/src/{ => extensions}/line-grid-designer-extension.ts (60%) create mode 100644 designer/src/extensions/start-stop-root-component-designer-extension.ts rename designer/src/{ => extensions}/steps-designer-extension.ts (53%) diff --git a/CHANGELOG.md b/CHANGELOG.md index 862160b..eaca795 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +# 0.26.0 + +This version introduces a few internal changes to support the double-click feature in the pro version. + # 0.25.0 This version introduces the pinch-to-zoom feature. Now you can zoom in and out using the pinch gesture on touch devices. diff --git a/README.md b/README.md index d7d5aa1..ba37fd0 100644 --- a/README.md +++ b/README.md @@ -55,6 +55,7 @@ Pro: * [👋 Custom Dragged Component](https://nocode-js.com/examples/sequential-workflow-designer-pro/webpack-pro-app/public/custom-dragged-component.html) * [🔰 Badges](https://nocode-js.com/examples/sequential-workflow-designer-pro/webpack-pro-app/public/badges.html) * [🎩 Custom Viewport](https://nocode-js.com/examples/sequential-workflow-designer-pro/webpack-pro-app/public/custom-viewport.html) +* [👊 Double Click](https://nocode-js.com/examples/sequential-workflow-designer-pro/webpack-pro-app/public/double-click.html) * [🛎 Clickable Placeholder](https://nocode-js.com/examples/sequential-workflow-designer-pro/webpack-pro-app/public/clickable-placeholder.html) * [📮 Conditional Placeholders](https://nocode-js.com/examples/sequential-workflow-designer-pro/webpack-pro-app/public/conditional-placeholders.html) * [React Pro Demo](https://nocode-js.com/examples/sequential-workflow-designer-pro/react-pro-app/build/index.html) @@ -103,10 +104,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 8f175a2..06c4589 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.25.0", + "version": "0.26.0", "author": { "name": "NoCode JS", "url": "https://nocode-js.com/" @@ -15,7 +15,7 @@ "peerDependencies": { "@angular/common": "12 - 18", "@angular/core": "12 - 18", - "sequential-workflow-designer": "^0.25.0" + "sequential-workflow-designer": "^0.26.0" }, "dependencies": { "tslib": "^2.3.0" diff --git a/demos/angular-app/package.json b/demos/angular-app/package.json index 19aeb99..17147bf 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.25.0", - "sequential-workflow-designer-angular": "^0.25.0", + "sequential-workflow-designer": "^0.26.0", + "sequential-workflow-designer-angular": "^0.26.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 e7dc148..82fef3a 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.25.0: - version "0.25.0" - resolved "https://registry.yarnpkg.com/sequential-workflow-designer-angular/-/sequential-workflow-designer-angular-0.25.0.tgz#eb65370283a408c10eeb7b71b67dae2af9d6196b" - integrity sha512-E100P2es8Gn5th0f0ErfjRr9TWWcgocz16kZocWvCHeOn5/iLo+oCuvSSZjewpnYTVA6oDcHH//KJnq5sPN69g== +sequential-workflow-designer-angular@^0.26.0: + version "0.26.0" + resolved "https://registry.yarnpkg.com/sequential-workflow-designer-angular/-/sequential-workflow-designer-angular-0.26.0.tgz#f27885071efaf6e20e6884129f0666aefcb66ba6" + integrity sha512-UkWmksYIXggsDGWTHcjDW6RVpEpi3gKkBKBtMsyYTtZsE8PtMm0W5PF6gTHL3t1JBDw40jHCVLeEFNfjOp6jKg== dependencies: tslib "^2.3.0" -sequential-workflow-designer@^0.25.0: - version "0.25.0" - resolved "https://registry.yarnpkg.com/sequential-workflow-designer/-/sequential-workflow-designer-0.25.0.tgz#db8d35441a68f0b05f169e458f12f5457f9beca9" - integrity sha512-KNOTA4zx/TkpL0LHQFvNe2S07vejA4YjRcl6UNpLjG4fODQKfiZ8zj5RypcG54O6TArBU8eDEUwtXgnuopGEYQ== +sequential-workflow-designer@^0.26.0: + version "0.26.0" + resolved "https://registry.yarnpkg.com/sequential-workflow-designer/-/sequential-workflow-designer-0.26.0.tgz#6ea3148d663e8fda9939b50af575568a19d2c157" + integrity sha512-PIHiMBti6TWhou5gz7DXMe6HF7AEX9ZkRvXOBLhn07hZn+rk9MPFkV83h7sZDdiTrH6VoMw7rZ7f/th/knm/0A== dependencies: sequential-workflow-model "^0.2.0" diff --git a/demos/react-app/package.json b/demos/react-app/package.json index 5009226..83feab1 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.25.0", - "sequential-workflow-designer-react": "^0.25.0" + "sequential-workflow-designer": "^0.26.0", + "sequential-workflow-designer-react": "^0.26.0" }, "devDependencies": { "@types/jest": "^29.2.5", diff --git a/demos/svelte-app/package.json b/demos/svelte-app/package.json index 87815f4..3be861c 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.25.0", - "sequential-workflow-designer-svelte": "^0.25.0" + "sequential-workflow-designer": "^0.26.0", + "sequential-workflow-designer-svelte": "^0.26.0" }, "devDependencies": { "@sveltejs/adapter-static": "^2.0.3", diff --git a/designer/package.json b/designer/package.json index fb9b488..60353a2 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.25.0", + "version": "0.26.0", "type": "module", "main": "./lib/esm/index.js", "types": "./lib/index.d.ts", diff --git a/designer/src/api/control-bar-api.ts b/designer/src/api/control-bar-api.ts index ed6a8bb..54750b2 100644 --- a/designer/src/api/control-bar-api.ts +++ b/designer/src/api/control-bar-api.ts @@ -2,16 +2,14 @@ import { race, SimpleEvent } from '../core'; import { StateModifier } from '../modifier/state-modifier'; import { DesignerState } from '../designer-state'; import { HistoryController } from '../history-controller'; -import { ViewportApi } from './viewport-api'; export class ControlBarApi { public static create( state: DesignerState, historyController: HistoryController | undefined, - stateModifier: StateModifier, - viewportApi: ViewportApi + stateModifier: StateModifier ): ControlBarApi { - const api = new ControlBarApi(state, historyController, stateModifier, viewportApi); + const api = new ControlBarApi(state, historyController, stateModifier); race( 0, @@ -26,24 +24,11 @@ export class ControlBarApi { private constructor( private readonly state: DesignerState, private readonly historyController: HistoryController | undefined, - private readonly stateModifier: StateModifier, - private readonly viewportApi: ViewportApi + private readonly stateModifier: StateModifier ) {} public readonly onStateChanged = new SimpleEvent(); - public resetViewport() { - this.viewportApi.resetViewport(); - } - - public zoomIn() { - this.viewportApi.zoom(true); - } - - public zoomOut() { - this.viewportApi.zoom(false); - } - public isDragDisabled(): boolean { return this.state.isDragDisabled; } diff --git a/designer/src/api/designer-api.ts b/designer/src/api/designer-api.ts index 1971032..30d20de 100644 --- a/designer/src/api/designer-api.ts +++ b/designer/src/api/designer-api.ts @@ -13,7 +13,6 @@ export class DesignerApi { public static create(context: DesignerContext): DesignerApi { const workspace = new WorkspaceApi(context.state, context.workspaceController); const viewportController = context.services.viewportController.create(workspace); - const viewport = new ViewportApi(context.workspaceController, viewportController, workspace); const toolboxDataProvider = new ToolboxDataProvider( context.componentContext.iconProvider, context.i18n, @@ -22,11 +21,11 @@ export class DesignerApi { return new DesignerApi( context.configuration.shadowRoot, - ControlBarApi.create(context.state, context.historyController, context.stateModifier, viewport), + ControlBarApi.create(context.state, context.historyController, context.stateModifier), new ToolboxApi(context.state, context, context.behaviorController, toolboxDataProvider, context.configuration.uidGenerator), new EditorApi(context.state, context.definitionWalker, context.stateModifier), workspace, - viewport, + new ViewportApi(context.state, context.workspaceController, viewportController), new PathBarApi(context.state, context.definitionWalker), context.definitionWalker, context.i18n diff --git a/designer/src/api/viewport-api.ts b/designer/src/api/viewport-api.ts index 7341e3b..feaa403 100644 --- a/designer/src/api/viewport-api.ts +++ b/designer/src/api/viewport-api.ts @@ -1,17 +1,17 @@ import { Vector } from '../core'; import { ViewportController } from '../designer-extension'; +import { DesignerState } from '../designer-state'; import { ViewportAnimator } from '../workspace/viewport/viewport-animator'; import { ZoomByWheelCalculator } from '../workspace/viewport/zoom-by-wheel-calculator'; import { WorkspaceControllerWrapper } from '../workspace/workspace-controller'; -import { WorkspaceApi } from './workspace-api'; export class ViewportApi { - private readonly animator = new ViewportAnimator(this.api); + private readonly animator = new ViewportAnimator(this.state); public constructor( + private readonly state: DesignerState, private readonly workspaceController: WorkspaceControllerWrapper, - private readonly viewportController: ViewportController, - private readonly api: WorkspaceApi + private readonly viewportController: ViewportController ) {} public limitScale(scale: number): number { @@ -20,13 +20,13 @@ export class ViewportApi { public resetViewport() { const defaultViewport = this.viewportController.getDefault(); - this.api.setViewport(defaultViewport); + this.state.setViewport(defaultViewport); } public zoom(direction: boolean) { const viewport = this.viewportController.getZoomed(direction); if (viewport) { - this.api.setViewport(viewport); + this.state.setViewport(viewport); } } @@ -43,12 +43,11 @@ export class ViewportApi { } public handleWheelEvent(e: WheelEvent) { - const viewport = this.api.getViewport(); - const canvasPosition = this.api.getCanvasPosition(); + const canvasPosition = this.workspaceController.getCanvasPosition(); - const newViewport = ZoomByWheelCalculator.calculate(this.viewportController, viewport, canvasPosition, e); + const newViewport = ZoomByWheelCalculator.calculate(this.viewportController, this.state.viewport, canvasPosition, e); if (newViewport) { - this.api.setViewport(newViewport); + this.state.setViewport(newViewport); } } } diff --git a/designer/src/api/workspace-api.ts b/designer/src/api/workspace-api.ts index 3957460..7786e63 100644 --- a/designer/src/api/workspace-api.ts +++ b/designer/src/api/workspace-api.ts @@ -9,6 +9,14 @@ export class WorkspaceApi { private readonly workspaceController: WorkspaceControllerWrapper ) {} + public getViewport(): Viewport { + return this.state.viewport; + } + + public setViewport(viewport: Viewport) { + this.state.setViewport(viewport); + } + public getCanvasPosition(): Vector { return this.workspaceController.getCanvasPosition(); } @@ -21,14 +29,6 @@ export class WorkspaceApi { return this.workspaceController.getRootComponentSize(); } - public getViewport(): Viewport { - return this.state.viewport; - } - - public setViewport(viewport: Viewport) { - this.state.setViewport(viewport); - } - public updateRootComponent() { this.workspaceController.updateRootComponent(); } diff --git a/designer/src/behaviors/behavior-controller.spec.ts b/designer/src/behaviors/behavior-controller.spec.ts new file mode 100644 index 0000000..4369cdc --- /dev/null +++ b/designer/src/behaviors/behavior-controller.spec.ts @@ -0,0 +1,51 @@ +import { Vector } from '../core'; +import { Behavior } from './behavior'; +import { BehaviorController } from './behavior-controller'; +import { SelectStepBehaviorEndToken } from './select-step-behavior-end-token'; + +describe('BehaviorController', () => { + it('passes the last end token to a next behavior', done => { + const controller = BehaviorController.create(undefined); + const baseBehavior = { + onStart(position: Vector) { + expect(position.x).toBe(1); + expect(position.y).toBe(2); + }, + onMove() { + /* ... */ + } + }; + + function stage0() { + const behavior0: Behavior = { + ...baseBehavior, + onEnd() { + setTimeout(stage1); + return new SelectStepBehaviorEndToken('step-id', 12345); + } + }; + + controller.start(new Vector(1, 2), behavior0); + window.dispatchEvent(new MouseEvent('mouseup')); + } + + function stage1() { + const behavior1: Behavior = { + ...baseBehavior, + onEnd(_0, _1, previousEndToken) { + if (SelectStepBehaviorEndToken.is(previousEndToken)) { + expect(previousEndToken.stepId).toBe('step-id'); + expect(previousEndToken.time).toBe(12345); + done(); + return; + } + fail('Invalid end token'); + } + }; + controller.start(new Vector(1, 2), behavior1); + window.dispatchEvent(new MouseEvent('mouseup')); + } + + stage0(); + }); +}); diff --git a/designer/src/behaviors/behavior-controller.ts b/designer/src/behaviors/behavior-controller.ts index 97af39f..6e60eda 100644 --- a/designer/src/behaviors/behavior-controller.ts +++ b/designer/src/behaviors/behavior-controller.ts @@ -1,5 +1,5 @@ import { Vector } from '../core/vector'; -import { Behavior } from './behavior'; +import { Behavior, BehaviorEndToken } from './behavior'; import { readMousePosition, readTouchPosition } from '../core/event-readers'; const notInitializedError = 'State is not initialized'; @@ -13,11 +13,12 @@ export class BehaviorController { return new BehaviorController(shadowRoot ?? document, shadowRoot); } - private state?: { + private previousEndToken: BehaviorEndToken | null = null; + private state: { startPosition: Vector; behavior: Behavior; lastPosition?: Vector; - }; + } | null = null; private constructor( private readonly dom: Document | ShadowRoot, @@ -106,7 +107,7 @@ export class BehaviorController { const delta = this.state.startPosition.subtract(position); const newBehavior = this.state.behavior.onMove(delta); if (newBehavior) { - this.state.behavior.onEnd(true, null); + this.state.behavior.onEnd(true, null, null); this.state.behavior = newBehavior; this.state.startPosition = position; @@ -124,7 +125,8 @@ export class BehaviorController { } this.unbind(window); - this.state.behavior.onEnd(interrupt, element); - this.state = undefined; + const endToken = this.state.behavior.onEnd(interrupt, element, this.previousEndToken); + this.state = null; + this.previousEndToken = endToken || null; } } diff --git a/designer/src/behaviors/behavior.ts b/designer/src/behaviors/behavior.ts index 74ac13f..4f2f752 100644 --- a/designer/src/behaviors/behavior.ts +++ b/designer/src/behaviors/behavior.ts @@ -3,5 +3,9 @@ import { Vector } from '../core/vector'; export interface Behavior { onStart(position: Vector): void; onMove(delta: Vector): Behavior | void; - onEnd(interrupt: boolean, element: Element | null): void; + onEnd(interrupt: boolean, element: Element | null, previousEndToken: BehaviorEndToken | null): BehaviorEndToken | void; +} + +export interface BehaviorEndToken { + type: string; } diff --git a/designer/src/behaviors/default-click-behavior-wrapper-extension.ts b/designer/src/behaviors/default-click-behavior-wrapper-extension.ts new file mode 100644 index 0000000..961eb42 --- /dev/null +++ b/designer/src/behaviors/default-click-behavior-wrapper-extension.ts @@ -0,0 +1,8 @@ +import { ClickBehaviorWrapper, ClickBehaviorWrapperExtension } from '../designer-extension'; +import { DefaultClickBehaviorWrapper } from './default-click-behavior-wrapper'; + +export class DefaultClickBehaviorWrapperExtension implements ClickBehaviorWrapperExtension { + public create(): ClickBehaviorWrapper { + return new DefaultClickBehaviorWrapper(); + } +} diff --git a/designer/src/behaviors/default-click-behavior-wrapper.ts b/designer/src/behaviors/default-click-behavior-wrapper.ts new file mode 100644 index 0000000..ae5e73c --- /dev/null +++ b/designer/src/behaviors/default-click-behavior-wrapper.ts @@ -0,0 +1,6 @@ +import { ClickBehaviorWrapper } from '../designer-extension'; +import { Behavior } from './behavior'; + +export class DefaultClickBehaviorWrapper implements ClickBehaviorWrapper { + public readonly wrap = (behavior: Behavior) => behavior; +} diff --git a/designer/src/behaviors/index.ts b/designer/src/behaviors/index.ts new file mode 100644 index 0000000..57fa2cd --- /dev/null +++ b/designer/src/behaviors/index.ts @@ -0,0 +1,2 @@ +export * from './behavior'; +export * from './select-step-behavior-end-token'; diff --git a/designer/src/behaviors/select-step-behavior-end-token.spec.ts b/designer/src/behaviors/select-step-behavior-end-token.spec.ts new file mode 100644 index 0000000..290330f --- /dev/null +++ b/designer/src/behaviors/select-step-behavior-end-token.spec.ts @@ -0,0 +1,16 @@ +import { BehaviorEndToken } from './behavior'; +import { SelectStepBehaviorEndToken } from './select-step-behavior-end-token'; + +describe('SelectStepBehaviorEndToken', () => { + it('can detect own type', () => { + const endToken = new SelectStepBehaviorEndToken('0', 0); + + expect(SelectStepBehaviorEndToken.is(endToken)).toBe(true); + + expect(SelectStepBehaviorEndToken.is({ type: 'test' })).toBe(false); + + expect(SelectStepBehaviorEndToken.is(null)).toBe(false); + + expect(SelectStepBehaviorEndToken.is(undefined as unknown as BehaviorEndToken)).toBe(false); + }); +}); diff --git a/designer/src/behaviors/select-step-behavior-end-token.ts b/designer/src/behaviors/select-step-behavior-end-token.ts new file mode 100644 index 0000000..4a79da5 --- /dev/null +++ b/designer/src/behaviors/select-step-behavior-end-token.ts @@ -0,0 +1,16 @@ +import { BehaviorEndToken } from './behavior'; + +export const TYPE = 'selectStep'; + +export class SelectStepBehaviorEndToken implements BehaviorEndToken { + public static is(token: BehaviorEndToken | null): token is SelectStepBehaviorEndToken { + return Boolean(token) && (token as SelectStepBehaviorEndToken).type === TYPE; + } + + public readonly type = TYPE; + + public constructor( + public readonly stepId: string, + public readonly time: number + ) {} +} diff --git a/designer/src/behaviors/select-step-behavior.ts b/designer/src/behaviors/select-step-behavior.ts index 5f802b0..a022915 100644 --- a/designer/src/behaviors/select-step-behavior.ts +++ b/designer/src/behaviors/select-step-behavior.ts @@ -6,6 +6,7 @@ import { StepComponent } from '../workspace/step-component'; import { Behavior } from './behavior'; import { DragStepBehavior } from './drag-step-behavior'; import { MoveViewportBehavior } from './move-viewport-behavior'; +import { SelectStepBehaviorEndToken } from './select-step-behavior-end-token'; export class SelectStepBehavior implements Behavior { public static create(pressedStepComponent: StepComponent, forceMove: boolean, context: DesignerContext): SelectStepBehavior { @@ -40,9 +41,12 @@ export class SelectStepBehavior implements Behavior { } } - public onEnd(interrupt: boolean) { - if (!interrupt) { - this.stateModifier.trySelectStep(this.pressedStepComponent.step, this.pressedStepComponent.parentSequence); + public onEnd(interrupt: boolean): SelectStepBehaviorEndToken | void { + if (interrupt) { + return; } + + this.stateModifier.trySelectStep(this.pressedStepComponent.step, this.pressedStepComponent.parentSequence); + return new SelectStepBehaviorEndToken(this.pressedStepComponent.step.id, Date.now()); } } diff --git a/designer/src/control-bar/control-bar.ts b/designer/src/control-bar/control-bar.ts index 13912da..253570e 100644 --- a/designer/src/control-bar/control-bar.ts +++ b/designer/src/control-bar/control-bar.ts @@ -2,12 +2,13 @@ import { ControlBarView } from './control-bar-view'; import { UiComponent } from '../designer-extension'; import { ControlBarApi } from '../api/control-bar-api'; import { DesignerApi } from '../api/designer-api'; +import { ViewportApi } from '../api'; export class ControlBar implements UiComponent { public static create(parent: HTMLElement, api: DesignerApi): UiComponent { const isUndoRedoSupported = api.controlBar.isUndoRedoSupported(); const view = ControlBarView.create(parent, isUndoRedoSupported, api.i18n); - const bar = new ControlBar(view, api.controlBar, isUndoRedoSupported); + const bar = new ControlBar(view, api.controlBar, api.viewport, isUndoRedoSupported); view.bindResetButtonClick(() => bar.onResetButtonClicked()); view.bindZoomInButtonClick(() => bar.onZoomInButtonClicked()); @@ -28,6 +29,7 @@ export class ControlBar implements UiComponent { private constructor( private readonly view: ControlBarView, private readonly controlBarApi: ControlBarApi, + private readonly viewportApi: ViewportApi, private readonly isUndoRedoSupported: boolean ) {} @@ -40,15 +42,15 @@ export class ControlBar implements UiComponent { } private onResetButtonClicked() { - this.controlBarApi.resetViewport(); + this.viewportApi.resetViewport(); } private onZoomInButtonClicked() { - this.controlBarApi.zoomIn(); + this.viewportApi.zoom(true); } private onZoomOutButtonClicked() { - this.controlBarApi.zoomOut(); + this.viewportApi.zoom(false); } private onMoveButtonClicked() { diff --git a/designer/src/designer-extension.ts b/designer/src/designer-extension.ts index 8799b0f..d271225 100644 --- a/designer/src/designer-extension.ts +++ b/designer/src/designer-extension.ts @@ -1,6 +1,7 @@ import { WorkspaceApi } from './api'; import { DesignerApi } from './api/designer-api'; import { ViewportApi } from './api/viewport-api'; +import { Behavior } from './behaviors'; import { ComponentContext } from './component-context'; import { Vector } from './core'; import { CustomActionController } from './custom-action-controller'; @@ -20,6 +21,7 @@ import { export interface DesignerExtension { steps?: StepExtension[]; stepComponentViewWrapper?: StepComponentViewWrapperExtension; + clickBehaviorWrapperExtension?: ClickBehaviorWrapperExtension; badges?: BadgeExtension[]; uiComponents?: UiComponentExtension[]; draggedComponent?: DraggedComponentExtension; @@ -83,10 +85,22 @@ export interface SequenceContext { isPreview: boolean; } +// StepComponentViewWrapperExtension + export interface StepComponentViewWrapperExtension { wrap(view: StepComponentView, stepContext: StepContext): StepComponentView; } +// ClickBehaviorWrapperExtension + +export interface ClickBehaviorWrapperExtension { + create(customActionController: CustomActionController): ClickBehaviorWrapper; +} + +export interface ClickBehaviorWrapper { + wrap(behavior: Behavior, commandOrNull: ClickCommand | null): Behavior; +} + // BadgeExtension export interface BadgeExtension { diff --git a/designer/src/extensions/default-viewport-controller-designer-extension.ts b/designer/src/extensions/default-viewport-controller-designer-extension.ts new file mode 100644 index 0000000..1e78776 --- /dev/null +++ b/designer/src/extensions/default-viewport-controller-designer-extension.ts @@ -0,0 +1,10 @@ +import { DesignerExtension, ViewportControllerExtension } from '../designer-extension'; +import { DefaultViewportControllerExtension, DefaultViewportControllerExtensionConfiguration } from '../workspace'; + +export class DefaultViewportControllerDesignerExtension implements DesignerExtension { + public static create(configuration: DefaultViewportControllerExtensionConfiguration): DesignerExtension { + return new DefaultViewportControllerDesignerExtension(DefaultViewportControllerExtension.create(configuration)); + } + + private constructor(public readonly viewportController: ViewportControllerExtension) {} +} diff --git a/designer/src/extensions/index.ts b/designer/src/extensions/index.ts new file mode 100644 index 0000000..7354702 --- /dev/null +++ b/designer/src/extensions/index.ts @@ -0,0 +1,4 @@ +export * from './default-viewport-controller-designer-extension'; +export * from './line-grid-designer-extension'; +export * from './start-stop-root-component-designer-extension'; +export * from './steps-designer-extension'; diff --git a/designer/src/line-grid-designer-extension.ts b/designer/src/extensions/line-grid-designer-extension.ts similarity index 60% rename from designer/src/line-grid-designer-extension.ts rename to designer/src/extensions/line-grid-designer-extension.ts index d72eeb6..5372cce 100644 --- a/designer/src/line-grid-designer-extension.ts +++ b/designer/src/extensions/line-grid-designer-extension.ts @@ -1,6 +1,6 @@ -import { DesignerExtension } from './designer-extension'; -import { LineGridConfiguration } from './workspace/grid/line-grid-configuration'; -import { LineGridExtension } from './workspace/grid/line-grid-extension'; +import { DesignerExtension } from '../designer-extension'; +import { LineGridConfiguration } from '../workspace/grid/line-grid-configuration'; +import { LineGridExtension } from '../workspace/grid/line-grid-extension'; export class LineGridDesignerExtension implements DesignerExtension { public static create(configuration?: LineGridConfiguration): DesignerExtension { diff --git a/designer/src/extensions/start-stop-root-component-designer-extension.ts b/designer/src/extensions/start-stop-root-component-designer-extension.ts new file mode 100644 index 0000000..a154c59 --- /dev/null +++ b/designer/src/extensions/start-stop-root-component-designer-extension.ts @@ -0,0 +1,10 @@ +import { DesignerExtension, RootComponentExtension } from '../designer-extension'; +import { StartStopRootComponentExtension, StartStopRootComponentExtensionConfiguration } from '../workspace'; + +export class StartStopRootComponentDesignerExtension implements DesignerExtension { + public static create(configuration: StartStopRootComponentExtensionConfiguration): DesignerExtension { + return new StartStopRootComponentDesignerExtension(StartStopRootComponentExtension.create(configuration)); + } + + private constructor(public readonly rootComponent: RootComponentExtension) {} +} diff --git a/designer/src/steps-designer-extension.ts b/designer/src/extensions/steps-designer-extension.ts similarity index 53% rename from designer/src/steps-designer-extension.ts rename to designer/src/extensions/steps-designer-extension.ts index 2a700c9..bda4ef7 100644 --- a/designer/src/steps-designer-extension.ts +++ b/designer/src/extensions/steps-designer-extension.ts @@ -1,11 +1,11 @@ -import { Step } from './definition'; -import { DesignerExtension, StepExtension } from './designer-extension'; -import { ContainerStepExtension } from './workspace/container-step/container-step-extension'; -import { ContainerStepExtensionConfiguration } from './workspace/container-step/container-step-extension-configuration'; -import { SwitchStepExtensionConfiguration } from './workspace/switch-step/switch-step-extension-configuration'; -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 { Step } from '../definition'; +import { DesignerExtension, StepExtension } from '../designer-extension'; +import { ContainerStepExtension } from '../workspace/container-step/container-step-extension'; +import { ContainerStepExtensionConfiguration } from '../workspace/container-step/container-step-extension-configuration'; +import { SwitchStepExtensionConfiguration } from '../workspace/switch-step/switch-step-extension-configuration'; +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'; export interface StepsDesignerExtensionConfiguration { container?: ContainerStepExtensionConfiguration; diff --git a/designer/src/index.ts b/designer/src/index.ts index 898e12f..2b7a2b0 100644 --- a/designer/src/index.ts +++ b/designer/src/index.ts @@ -1,5 +1,7 @@ export * from './api'; +export * from './behaviors'; export * from './core'; +export * from './extensions'; export * from './modifier'; export * from './component-context'; export * from './custom-action-controller'; @@ -11,6 +13,4 @@ export * from './designer-context'; export * from './designer-extension'; export * from './designer-state'; export * from './designer'; -export * from './line-grid-designer-extension'; export * from './services'; -export * from './steps-designer-extension'; diff --git a/designer/src/services.ts b/designer/src/services.ts index d57969a..dc29e1a 100644 --- a/designer/src/services.ts +++ b/designer/src/services.ts @@ -19,6 +19,7 @@ import { DefaultSequenceComponentExtension } from './workspace/sequence/default- import { DefaultStepComponentViewWrapperExtension } from './workspace/default-step-component-view-wrapper-extension'; 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'; export type Services = Required; @@ -39,6 +40,9 @@ function merge(services: Partial, extensions: DesignerExtension[]) { if (ext.stepComponentViewWrapper) { services.stepComponentViewWrapper = ext.stepComponentViewWrapper; } + if (ext.clickBehaviorWrapperExtension) { + services.clickBehaviorWrapperExtension = ext.clickBehaviorWrapperExtension; + } if (ext.badges) { services.badges = (services.badges || []).concat(ext.badges); } @@ -92,6 +96,9 @@ function setDefaults(services: Partial, configuration: DesignerConfigu if (!services.stepComponentViewWrapper) { services.stepComponentViewWrapper = new DefaultStepComponentViewWrapperExtension(); } + if (!services.clickBehaviorWrapperExtension) { + services.clickBehaviorWrapperExtension = new DefaultClickBehaviorWrapperExtension(); + } if (!services.badges) { services.badges = []; diff --git a/designer/src/workspace/viewport/viewport-animator.ts b/designer/src/workspace/viewport/viewport-animator.ts index 5112a3e..8134818 100644 --- a/designer/src/workspace/viewport/viewport-animator.ts +++ b/designer/src/workspace/viewport/viewport-animator.ts @@ -1,26 +1,25 @@ -import { WorkspaceApi } from '../../api'; import { animate, Animation } from '../../core/animation'; import { Viewport } from '../../designer-extension'; +import { DesignerState } from '../../designer-state'; export class ViewportAnimator { private animation?: Animation; - public constructor(private readonly api: WorkspaceApi) {} + public constructor(private readonly state: DesignerState) {} public execute(target: Viewport) { if (this.animation && this.animation.isAlive) { this.animation.stop(); } - const viewport = this.api.getViewport(); - const startPosition = viewport.position; - const startScale = viewport.scale; + const startPosition = this.state.viewport.position; + const startScale = this.state.viewport.scale; const deltaPosition = startPosition.subtract(target.position); const deltaScale = startScale - target.scale; this.animation = animate(150, progress => { const newScale = startScale - deltaScale * progress; - this.api.setViewport({ + this.state.setViewport({ position: startPosition.subtract(deltaPosition.multiplyByScalar(progress)), scale: newScale }); diff --git a/designer/src/workspace/workspace.ts b/designer/src/workspace/workspace.ts index 3657e37..ea79ab7 100644 --- a/designer/src/workspace/workspace.ts +++ b/designer/src/workspace/workspace.ts @@ -9,7 +9,7 @@ import { WorkspaceController } from './workspace-controller'; import { ClickBehaviorResolver } from '../behaviors/click-behavior-resolver'; import { BehaviorController } from '../behaviors/behavior-controller'; import { SimpleEvent } from '../core/simple-event'; -import { SequencePlaceIndicator, Viewport, WheelController } from '../designer-extension'; +import { ClickBehaviorWrapper, SequencePlaceIndicator, Viewport, WheelController } from '../designer-extension'; import { DesignerApi } from '../api/designer-api'; import { StepComponent } from './step-component'; import { BadgesResultFactory } from './badges/badges-result-factory'; @@ -26,6 +26,8 @@ export class Workspace implements WorkspaceController { const view = WorkspaceView.create(parent, designerContext.componentContext); const clickBehaviorResolver = new ClickBehaviorResolver(designerContext); + const clickBehaviorWrapper = designerContext.services.clickBehaviorWrapperExtension.create(designerContext.customActionController); + const wheelController = designerContext.services.wheelController.create(api.viewport, api.workspace); const pinchToZoomController = PinchToZoomController.create(api.workspace, api.viewport, api.shadowRoot); @@ -53,6 +55,7 @@ export class Workspace implements WorkspaceController { pinchToZoomController, contextMenuController, clickBehaviorResolver, + clickBehaviorWrapper, api.viewport, designerContext.services ); @@ -95,6 +98,7 @@ export class Workspace implements WorkspaceController { private readonly pinchToZoomController: PinchToZoomController, private readonly contextMenuController: ContextMenuController, private readonly clickBehaviorResolver: ClickBehaviorResolver, + private readonly clickBehaviorWrapper: ClickBehaviorWrapper, private readonly viewportApi: ViewportApi, private readonly services: Services ) {} @@ -188,7 +192,8 @@ export class Workspace implements WorkspaceController { const commandOrNull = this.resolveClick(target, position); const behavior = this.clickBehaviorResolver.resolve(commandOrNull, target, forceMove); - this.behaviorController.start(position, behavior); + const wrappedBehavior = this.clickBehaviorWrapper.wrap(behavior, commandOrNull); + this.behaviorController.start(position, wrappedBehavior); } }; diff --git a/examples/assets/lib.js b/examples/assets/lib.js index d88c1a7..72d8808 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.25.0'; +const baseUrl = isTestEnv() ? '../designer' : '//cdn.jsdelivr.net/npm/sequential-workflow-designer@0.26.0'; embedScript(`${baseUrl}/dist/index.umd.js`); embedStylesheet(`${baseUrl}/css/designer.css`); diff --git a/react/package.json b/react/package.json index c270847..047c0ba 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.25.0", + "version": "0.26.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.25.0" + "sequential-workflow-designer": "^0.26.0" }, "devDependencies": { "@rollup/plugin-node-resolve": "^15.0.1", @@ -63,7 +63,7 @@ "prettier": "^3.2.5", "react": "^18.2.0", "react-dom": "^18.2.0", - "sequential-workflow-designer": "^0.25.0", + "sequential-workflow-designer": "^0.26.0", "rollup": "^3.18.0", "rollup-plugin-dts": "^5.2.0", "rollup-plugin-typescript2": "^0.34.1", diff --git a/svelte/package.json b/svelte/package.json index c77c8ed..ff22178 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.25.0", + "version": "0.26.0", "license": "MIT", "scripts": { "prepare": "cp ../LICENSE LICENSE", @@ -28,10 +28,10 @@ ], "peerDependencies": { "svelte": "^4.0.0", - "sequential-workflow-designer": "^0.25.0" + "sequential-workflow-designer": "^0.26.0" }, "devDependencies": { - "sequential-workflow-designer": "^0.25.0", + "sequential-workflow-designer": "^0.26.0", "@sveltejs/adapter-static": "^2.0.3", "@sveltejs/kit": "^1.20.4", "@sveltejs/package": "^2.0.0",