Skip to content

Commit 70cea02

Browse files
committed
basic keyboard support.
1 parent d737853 commit 70cea02

File tree

14 files changed

+94
-77
lines changed

14 files changed

+94
-77
lines changed

README.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -83,10 +83,11 @@ const placeholder = document.getElementById('placeholder');
8383

8484
const definition = {
8585
properties: {
86-
// global properties
86+
'myProperty': 'my-value',
87+
// global properties...
8788
},
8889
sequence: [
89-
// root steps
90+
// root steps...
9091
]
9192
};
9293

css/designer.css

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
box-sizing: border-box;
3131
border-radius: 10px;
3232
width: 130px;
33+
user-select: none;
3334
}
3435
.sqd-toolbox-header {
3536
position: relative;
@@ -149,6 +150,7 @@
149150
flex: 1;
150151
position: relative;
151152
display: block;
153+
user-select: none;
152154
}
153155
.sqd-workspace-canvas {
154156
position: absolute;

examples/assets/fullscreen.js

Lines changed: 18 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,18 @@
11
/* global document, sequentialWorkflowDesigner, console */
22

3-
function uid() {
4-
return Math.ceil(Math.random() * 10**16).toString(16);
5-
}
6-
7-
function createTaskStep(type, name) {
3+
function createTaskStep(id, type, name) {
84
return {
9-
id: uid(),
5+
id,
106
componentType: 'task',
117
type,
128
name,
139
properties: {}
1410
};
1511
}
1612

17-
function createIfStep(_true, _false) {
13+
function createIfStep(id, _true, _false) {
1814
return {
19-
id: uid(),
15+
id,
2016
componentType: 'switch',
2117
type: 'if',
2218
name: 'If',
@@ -28,9 +24,9 @@ function createIfStep(_true, _false) {
2824
};
2925
}
3026

31-
function createContainerStep(steps) {
27+
function createContainerStep(id, steps) {
3228
return {
33-
id: uid(),
29+
id,
3430
componentType: 'container',
3531
type: 'loop',
3632
name: 'Loop',
@@ -43,11 +39,11 @@ function toolboxGroup(name) {
4339
return {
4440
name,
4541
steps: [
46-
createTaskStep('save', 'Save file'),
47-
createTaskStep('text', 'Send email'),
48-
createTaskStep('task', 'Create task'),
49-
createIfStep([], []),
50-
createContainerStep([])
42+
createTaskStep(null, 'save', 'Save file'),
43+
createTaskStep(null, 'text', 'Send email'),
44+
createTaskStep(null, 'task', 'Create task'),
45+
createIfStep(null, [], []),
46+
createContainerStep(null, [])
5147
]
5248
};
5349
}
@@ -102,18 +98,18 @@ const configuration = {
10298
const startDefinition = {
10399
properties: {},
104100
sequence: [
105-
createIfStep(
106-
[ createTaskStep('save', 'Save file') ],
107-
[ createTaskStep('text', 'Send email') ]
101+
createIfStep('00000000000000000000000000000001',
102+
[ createTaskStep('00000000000000000000000000000002', 'save', 'Save file') ],
103+
[ createTaskStep('00000000000000000000000000000003', 'text', 'Send email') ]
108104
),
109-
createContainerStep([
110-
createTaskStep('task', 'Create task')
105+
createContainerStep('00000000000000000000000000000004', [
106+
createTaskStep('00000000000000000000000000000005', 'task', 'Create task')
111107
])
112108
]
113109
};
114110

115111
const placeholder = document.getElementById('designer');
116112
designer = sequentialWorkflowDesigner.create(placeholder, startDefinition, configuration);
117-
designer.onDefinitionChanged.subscribe(() => {
118-
console.log('the definition has changed');
113+
designer.onDefinitionChanged.subscribe((newDefinition) => {
114+
console.log('the definition has changed', newDefinition);
119115
});

examples/assets/image-filter.js

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -79,9 +79,9 @@ async function renderImage(definition) {
7979
context.putImageData(imageData, 0, 0);
8080
}
8181

82-
function createTaskStep(type, name) {
82+
function createTaskStep(id, type, name) {
8383
return {
84-
id: '0x0',
84+
id,
8585
componentType: 'task',
8686
type,
8787
name,
@@ -98,10 +98,10 @@ const configuration = {
9898
{
9999
name: 'Filters',
100100
steps: [
101-
createTaskStep('grayscale', 'Grayscale'),
102-
createTaskStep('reverse-color', 'Reverse color'),
103-
createTaskStep('contrast', 'Contrast'),
104-
createTaskStep('shift-color', 'Shift color')
101+
createTaskStep(null, 'grayscale', 'Grayscale'),
102+
createTaskStep(null, 'reverse-color', 'Reverse color'),
103+
createTaskStep(null, 'contrast', 'Contrast'),
104+
createTaskStep(null, 'shift-color', 'Shift color')
105105
]
106106
}
107107
]
@@ -129,7 +129,7 @@ const configuration = {
129129
const startDefinition = {
130130
properties: {},
131131
sequence: [
132-
createTaskStep('contrast', 'Contrast')
132+
createTaskStep('00000000000000000000000000000001', 'contrast', 'Contrast')
133133
]
134134
};
135135

examples/assets/light-dark.js

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
/* global window, document, sequentialWorkflowDesigner */
22

3-
function createStep() {
3+
function createStep(id) {
44
return {
5-
id: '0x0',
5+
id,
66
componentType: 'task',
77
type: 'task',
88
name: 'Open TCP port',
@@ -13,7 +13,7 @@ function createStep() {
1313
function install(placeholder, theme) {
1414
const definition = {
1515
sequence: [
16-
createStep()
16+
createStep('00000000000000000000000000000000')
1717
],
1818
properties: {}
1919
};
@@ -25,7 +25,7 @@ function install(placeholder, theme) {
2525
{
2626
name: 'Tasks',
2727
steps: [
28-
createStep()
28+
createStep(null)
2929
]
3030
}
3131
]

src/control-bar/control-bar.ts

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import { SequenceModifier } from '../core/sequence-modifier';
21
import { DesignerContext } from '../designer-context';
32
import { ControlBarView } from './control-bar-view';
43

@@ -40,11 +39,7 @@ export class ControlBar {
4039

4140
private onDeleteButtonClicked() {
4241
if (this.context.selectedStep) {
43-
const parentSequence = this.context.getSelectedStepParentSequence();
44-
SequenceModifier.deleteStep(this.context.selectedStep, parentSequence);
45-
46-
this.context.setSelectedStep(null);
47-
this.context.notifiyDefinitionChanged();
42+
this.context.deleteStepById(this.context.selectedStep.id);
4843
}
4944
}
5045

src/designer-configuration.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,11 @@ export interface ToolboxConfiguration {
1414
groups: ToolboxGroupConfiguration[];
1515
}
1616

17+
export type StepDefinition = Omit<Step, 'id'>;
18+
1719
export interface ToolboxGroupConfiguration {
1820
name: string;
19-
steps: Step[];
21+
steps: StepDefinition[];
2022
}
2123

2224
export interface StepsConfiguration {

src/designer-context.ts

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
import { BehaviorController } from './behaviors/behavior-controller';
22
import { animate, Animation } from './core/animation';
3+
import { SequenceModifier } from './core/sequence-modifier';
34
import { SimpleEvent } from './core/simple-event';
45
import { Vector } from './core/vector';
5-
import { Definition, Sequence, Step } from './definition';
6+
import { Definition, Step } from './definition';
67
import { DesignerConfiguration } from './designer-configuration';
78
import { LayoutController } from './layout-controller';
89
import { Placeholder, StepComponent } from './workspace/component';
@@ -98,15 +99,18 @@ export class DesignerContext {
9899
}
99100
}
100101

101-
public getSelectedStepParentSequence(): Sequence {
102-
if (!this.selectedStep) {
103-
throw new Error('No selected step');
104-
}
105-
const component = this.getProvider().findStepComponentById(this.selectedStep.id);
102+
public deleteStepById(stepId: string) {
103+
const component = this.getProvider().findStepComponentById(stepId);
106104
if (!component) {
107105
throw new Error('Cannot find component');
108106
}
109-
return component.parentSequence;
107+
if (this.selectedStep?.id === stepId) {
108+
this.setSelectedStep(null);
109+
}
110+
111+
SequenceModifier.deleteStep(component.step, component.parentSequence);
112+
113+
this.notifiyDefinitionChanged();
110114
}
111115

112116
public setIsReadonly(isReadonly: boolean) {

src/designer-view.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ export class DesignerView {
3333
}
3434

3535
private readonly onResizeHandler = () => this.onResize();
36+
private readonly onKeyUpHandlers: KeyUpHandler[] = [];
3637

3738
public constructor(
3839
private readonly root: HTMLElement,
@@ -41,7 +42,15 @@ export class DesignerView {
4142
private readonly toolbox?: Toolbox
4243
) {}
4344

45+
public bindKeyUp(handler: KeyUpHandler) {
46+
document.addEventListener('keyup', handler, false);
47+
this.onKeyUpHandlers.push(handler);
48+
}
49+
4450
public destroy() {
51+
window.removeEventListener('resize', this.onResizeHandler, false);
52+
this.onKeyUpHandlers.forEach(h => document.removeEventListener('keyup', h, false));
53+
4554
this.workspace.destroy();
4655
this.toolbox?.destroy();
4756

@@ -58,3 +67,5 @@ export class DesignerView {
5867
Dom.toggleClass(this.root, isMobile, 'sqd-layout-mobile');
5968
}
6069
}
70+
71+
export type KeyUpHandler = (e: KeyboardEvent) => void;

src/designer.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ export default class Designer {
1818

1919
const view = DesignerView.create(parent, context, configuration);
2020
const designer = new Designer(view, context);
21+
view.bindKeyUp(e => designer.onKeyUp(e));
2122
context.onDefinitionChanged.subscribe(d => designer.onDefinitionChanged.forward(d));
2223
return designer;
2324
}
@@ -69,4 +70,22 @@ export default class Designer {
6970
public destroy() {
7071
this.view.destroy();
7172
}
73+
74+
private onKeyUp(e: KeyboardEvent) {
75+
const supportedKeys = ['Backspace', 'Delete'];
76+
if (!supportedKeys.includes(e.key)) {
77+
return;
78+
}
79+
const ignoreTagNames = ['input', 'textarea'];
80+
if (document.activeElement && ignoreTagNames.includes(document.activeElement.tagName.toLowerCase())) {
81+
return;
82+
}
83+
if (!this.context.selectedStep || this.context.isReadonly || this.context.isDragging) {
84+
return;
85+
}
86+
87+
e.preventDefault();
88+
e.stopPropagation();
89+
this.context.deleteStepById(this.context.selectedStep.id);
90+
}
7291
}

0 commit comments

Comments
 (0)