stable designer on dagre-d3-version

This commit is contained in:
Ivan Hrynevytskyi 2021-04-23 12:07:31 +03:00
parent aabf9ff2c2
commit 511bf7ce7d
31 changed files with 1587 additions and 10849 deletions

File diff suppressed because it is too large Load diff

View file

@ -18,7 +18,7 @@
"push:preview": "npm publish --access-public --tag preview",
"build:prod": "stencil build --dev",
"build:dev": "stencil build --dev --tailwind:dev",
"start:dev": "stencil build --dev --tailwind:dev --watch --serve",
"start:dev": "node --max_old_space_size=8192 --optimize_for_size --stack_size=8192 ./node_modules/.bin/stencil build --dev --tailwind:dev --watch --serve",
"start": "stencil build --watch --serve",
"test": "stencil test --spec --e2e",
"test.watch": "stencil test --spec --e2e --watchAll",
@ -39,6 +39,8 @@
"autoprefixer": "10.2.5",
"axios": "^0.21.1",
"cssnano": "^4.1.10",
"d3": "^6.6.2",
"dagre-d3": "^0.6.4",
"el-transition": "^0.0.7",
"eslint": "7.23.0",
"js-event-bus": "^1.0.3",
@ -55,6 +57,8 @@
"typescript-tslint-plugin": "1.0.1"
},
"devDependencies": {
"@types/d3": "^6.3.0",
"@types/dagre-d3": "^0.6.2",
"copyfiles": "^2.4.1",
"cross-env": "^7.0.3"
},

View file

@ -22,7 +22,6 @@ export namespace Components {
"propertyModel": ActivityDefinitionProperty;
}
interface ElsaDesignerTree {
"destroyJsPlumb": () => Promise<void>;
"model": WorkflowModel;
}
interface ElsaDesignerTreeActivity {

View file

@ -6,7 +6,7 @@
background-image: url(build/assets/tile.png);
background-repeat: repeat;
background-size: 30px 30px;
background-color: #FBFBFB;
background-color: #fbfbfb;
}
div.canvas {
@ -77,7 +77,7 @@ div.tree {
width: 0;
height: 20px;
}
/*
.activity {
min-width: 316px;
box-sizing: border-box;
@ -85,3 +85,24 @@ div.tree {
white-space: normal;
display: inline-block;
}
*/
.node.activity {
fill: transparent;
}
.node.activity .label-container {
stroke: none;
}
.node.add .label-container {
fill: transparent;
}
.edgePath {
stroke: black;
}
.outcome {
background-color: #fbfbfb;
}

View file

@ -1,148 +1,125 @@
import {Component, Host, h, Prop, State, Event, EventEmitter, Listen, Watch, Method} from '@stencil/core';
import {Map, addConnection, findActivity, getChildActivities, getInboundConnections, getOutboundConnections, removeActivity} from '../../../../utils/utils';
import {cleanup, destroy, updateConnections} from '../../../../utils/jsplumb-helper';
import {ActivityDescriptor, ActivityDesignDisplayContext, ActivityModel, ConnectionModel, EventTypes, WorkflowModel, WorkflowPersistenceBehavior} from "../../../../models";
import {eventBus} from '../../../../services/event-bus';
import jsPlumb from "jsplumb";
import { Component, Host, h, Prop, State, Event, EventEmitter, Listen, Watch, Method } from '@stencil/core';
import { Map, addConnection, findActivity, getChildActivities, getInboundConnections, getOutboundConnections, removeActivity } from '../../../../utils/utils';
import { cleanup, destroy, updateConnections } from '../../../../utils/jsplumb-helper';
import { ActivityDescriptor, ActivityDesignDisplayContext, ActivityModel, ActivityTraits, ConnectionModel, EventTypes, WorkflowModel, WorkflowPersistenceBehavior } from '../../../../models';
import { eventBus } from '../../../../services/event-bus';
import jsPlumb from 'jsplumb';
import * as d3 from 'd3';
import dagreD3 from 'dagre-d3';
import { toggle } from 'el-transition';
import { registerClickOutside } from 'stencil-click-outside';
import state from "../../../../utils/store";
import uuid = jsPlumb.jsPlumbUtil.uuid;
import {ActivityIcon} from "../../../icons/activity-icon";
import { ActivityIcon } from '../../../icons/activity-icon';
@Component({
tag: 'elsa-designer-tree',
styleUrls: ['elsa-designer-tree.css'],
assetsDirs: ['assets'],
shadow: false
shadow: false,
})
export class ElsaWorkflowDesigner {
@Prop() model: WorkflowModel = { activities: [], connections: [], persistenceBehavior: WorkflowPersistenceBehavior.WorkflowBurst };
@Event({eventName: 'workflow-changed', bubbles: true, composed: true, cancelable: true}) workflowChanged: EventEmitter<WorkflowModel>
@State() workflowModel: WorkflowModel
el: HTMLElement
canvasElement: HTMLElement;
@Event({ eventName: 'workflow-changed', bubbles: true, composed: true, cancelable: true }) workflowChanged: EventEmitter<WorkflowModel>;
@State() workflowModel: WorkflowModel;
el: HTMLElement;
svg: SVGElement;
inner: SVGElement;
svgD3Selected: d3.Selection<SVGElement, unknown, null, undefined>;
innerD3Selected: d3.Selection<SVGElement, unknown, null, undefined>;
zoomParams: { x: number; y: number; scale: number } = { x: 0, y: 0, scale: 1 };
dagreD3Renderer: dagreD3.Render = new dagreD3.render();
graph: dagreD3.graphlib.Graph = new dagreD3.graphlib.Graph().setGraph({});
zoom: d3.ZoomBehavior<Element, unknown>;
parentActivityId?: string;
parentActivityOutcome?: string;
activityDisplayContexts: Map<ActivityDesignDisplayContext> = {};
@State() contextMenu: {
shown: boolean;
x: number;
y: number;
activity?: ActivityModel | null;
} = {
shown: false,
x: 0,
y: 0,
activity: null,
};
handleContextMenuChange(x: number, y: number, shown: boolean, activity: ActivityModel) {
this.contextMenu = {
shown,
x,
y,
activity,
};
}
@Watch('model')
handleModelChanged(newValue: WorkflowModel) {
this.workflowModel = newValue;
}
@Method()
async destroyJsPlumb(){
destroy();
// @Listen('edit-activity')
handleEditActivity(activity: ActivityModel) {
this.showActivityEditor(activity, true);
}
@Listen('edit-activity')
handleEditActivity(e: CustomEvent<ActivityModel>) {
this.showActivityEditor(e.detail, true);
updateWorkflowModel(model: WorkflowModel) {
this.workflowModel = model;
this.workflowChanged.emit(model);
console.log('rerendering')
this.rerenderTree();
}
@Listen('remove-activity')
handleRemoveActivity(e: CustomEvent<ActivityModel>) {
this.removeActivity(e.detail.activityId);
}
componentWillLoad() {
this.workflowModel = this.model;
eventBus.on(EventTypes.ActivityPicked, async args => {
const activityDescriptor = (args as ActivityDescriptor);
const connectFromRoot = !this.parentActivityOutcome || this.parentActivityOutcome == "";
const sourceId = connectFromRoot ? null : this.parentActivityId;
const targetId = connectFromRoot ? this.parentActivityId : null;
const activityModel = this.addActivity(activityDescriptor, sourceId, targetId, this.parentActivityOutcome);
this.showActivityEditor(activityModel, false);
});
eventBus.on(EventTypes.UpdateActivity, async args => {
const activityModel = (args as ActivityModel);
this.updateActivity(activityModel);
});
}
componentWillRender() {
destroy();
const activityModels = this.workflowModel.activities;
const displayContexts: Map<ActivityDesignDisplayContext> = {};
for (const model of activityModels) {
const description = model.description;
const bodyText = description && description.length > 0 ? description : undefined;
const bodyDisplay = bodyText ? <p>{bodyText}</p> : undefined;
const displayContext: ActivityDesignDisplayContext = {
activityModel: model,
activityIcon: <ActivityIcon/>,
bodyDisplay: bodyDisplay,
outcomes: [...model.outcomes]
};
eventBus.emit(EventTypes.ActivityDesignDisplaying, this, displayContext);
displayContexts[model.activityId] = displayContext;
}
this.activityDisplayContexts = displayContexts;
}
componentDidRender() {
const canvasElement = this.canvasElement;
const connections = this.getJsPlumbConnections();
const sourceEndpoints = this.getJsPlumbSourceEndpoints();
const targets = this.getJsPlumbTargets();
const invalidConnections = updateConnections(
canvasElement,
connections,
sourceEndpoints,
targets,
connection => this.onConnectionCreated(connection),
connection => this.onConnectionDetached(connection));
this.removeInvalidConnections(invalidConnections);
connectedCallback() {
eventBus.on(EventTypes.ActivityPicked, this.onActivityPicked);
eventBus.on(EventTypes.UpdateActivity, this.onUpdateActivity);
}
disconnectedCallback() {
eventBus.off(EventTypes.ActivityPicked, this.onActivityPicked);
eventBus.off(EventTypes.UpdateActivity, this.onUpdateActivity);
cleanup();
}
showActivityPicker() {
eventBus.emit(EventTypes.ShowActivityPicker);
}
onActivityPicked = async args => {
const activityDescriptor = args as ActivityDescriptor;
const connectFromRoot = !this.parentActivityOutcome || this.parentActivityOutcome == '';
const sourceId = connectFromRoot ? null : this.parentActivityId;
const targetId = connectFromRoot ? this.parentActivityId : null;
const activityModel = this.addActivity(activityDescriptor, sourceId, targetId, this.parentActivityOutcome);
this.showActivityEditor(activityModel, false);
};
showActivityEditor(activity: ActivityModel, animate: boolean) {
eventBus.emit(EventTypes.ShowActivityEditor, this, activity, animate);
}
getRootActivities(): Array<ActivityModel> {
return getChildActivities(this.workflowModel, null);
}
onUpdateActivity = args => {
const activityModel = args as ActivityModel;
this.updateActivity(activityModel);
};
addActivity(activityDescriptor: ActivityDescriptor, sourceActivityId?: string, targetActivityId?: string, outcome?: string): ActivityModel {
outcome = outcome || 'Done';
const activity: ActivityModel =
{
activityId: uuid(),
type: activityDescriptor.type,
outcomes: activityDescriptor.outcomes,
displayName: activityDescriptor.displayName,
properties: []
};
const activity: ActivityModel = {
activityId: uuid(),
type: activityDescriptor.type,
outcomes: activityDescriptor.outcomes,
displayName: activityDescriptor.displayName,
properties: [],
};
for (const property of activityDescriptor.properties) {
activity.properties[property.name] = {
syntax: '',
expression: ''
}
expression: '',
};
}
const workflowModel = {...this.workflowModel, activities: [...this.workflowModel.activities, activity]};
const workflowModel = { ...this.workflowModel, activities: [...this.workflowModel.activities, activity] };
if (targetActivityId) {
const existingConnection = workflowModel.connections.find(x => x.targetId == targetActivityId && x.outcome == outcome);
@ -152,12 +129,12 @@ export class ElsaWorkflowDesigner {
const replacementConnection = {
...existingConnection,
sourceId: activity.activityId
sourceId: activity.activityId,
};
workflowModel.connections.push(replacementConnection);
} else {
workflowModel.connections.push({sourceId: activity.activityId, targetId: targetActivityId, outcome: outcome});
workflowModel.connections.push({ sourceId: activity.activityId, targetId: targetActivityId, outcome: outcome });
}
}
@ -169,14 +146,14 @@ export class ElsaWorkflowDesigner {
const replacementConnection = {
...existingConnection,
targetId: activity.activityId
}
targetId: activity.activityId,
};
workflowModel.connections.push(replacementConnection);
const connection: ConnectionModel = {sourceId: activity.activityId, targetId: existingConnection.targetId, outcome};
const connection: ConnectionModel = { sourceId: activity.activityId, targetId: existingConnection.targetId, outcome };
workflowModel.connections.push(connection);
} else {
const connection: ConnectionModel = {sourceId: sourceActivityId, targetId: activity.activityId, outcome: outcome};
const connection: ConnectionModel = { sourceId: sourceActivityId, targetId: activity.activityId, outcome: outcome };
workflowModel.connections.push(connection);
}
}
@ -185,301 +162,315 @@ export class ElsaWorkflowDesigner {
return activity;
}
removeActivity(activityId: string) {
let workflowModel = {...this.workflowModel};
const incomingConnections = getInboundConnections(workflowModel, activityId);
const outgoingConnections = getOutboundConnections(workflowModel, activityId);
addConnection(sourceActivityId: string, targetActivityId: string, outcome: string) {
const workflowModel = { ...this.workflowModel };
const connection: ConnectionModel = { sourceId: sourceActivityId, targetId: targetActivityId, outcome: outcome };
workflowModel.connections.push(connection);
this.updateWorkflowModel(workflowModel);
}
updateActivity(activity: ActivityModel) {
let workflowModel = { ...this.workflowModel };
const activities = [...workflowModel.activities];
const index = activities.findIndex(x => x.activityId === activity.activityId);
activities[index] = activity;
this.updateWorkflowModel({ ...workflowModel, activities: activities });
}
componentWillLoad() {
this.workflowModel = this.model;
}
componentDidLoad() {
this.svgD3Selected = d3.select(this.svg);
this.innerD3Selected = d3.select(this.inner);
this.applyZoom();
this.setEnities();
this.renderNodes();
}
componentWillRender() {
const activityModels = this.workflowModel.activities;
const displayContexts: Map<ActivityDesignDisplayContext> = {};
const activityDescriptors: Array<ActivityDescriptor> = state.activityDescriptors;
for (const model of activityModels) {
const descriptor = activityDescriptors.find(x => x.type == model.type);
const description = model.description;
const bodyText = description && description.length > 0 ? description : undefined;
const bodyDisplay = bodyText ? <p>{bodyText}</p> : undefined;
const color = (descriptor.traits &= ActivityTraits.Trigger) == ActivityTraits.Trigger ? 'rose' : 'light-blue';
const displayContext: ActivityDesignDisplayContext = {
activityModel: model,
activityIcon: <ActivityIcon color={color}/>,
bodyDisplay: bodyDisplay,
outcomes: [...model.outcomes]
};
eventBus.emit(EventTypes.ActivityDesignDisplaying, this, displayContext);
displayContexts[model.activityId] = displayContext;
}
this.activityDisplayContexts = displayContexts;
}
showActivityPicker() {
eventBus.emit(EventTypes.ShowActivityPicker);
}
showActivityEditor(activity: ActivityModel, animate: boolean) {
eventBus.emit(EventTypes.ShowActivityEditor, this, activity, animate);
}
removeActivity(activity: ActivityModel) {
let workflowModel = { ...this.workflowModel };
const incomingConnections = getInboundConnections(workflowModel, activity.activityId);
const outgoingConnections = getOutboundConnections(workflowModel, activity.activityId);
// Remove activity (will also remove its connections).
workflowModel = removeActivity(workflowModel, activityId);
workflowModel = removeActivity(workflowModel, activity.activityId);
// For each incoming activity, try to connect it to a outgoing activity based on outcome.
for (const incomingConnection of incomingConnections) {
const incomingActivity = findActivity(workflowModel, incomingConnection.sourceId);
const outgoingConnection = outgoingConnections.find(x => x.outcome === incomingConnection.outcome);
if (outgoingConnection)
workflowModel = addConnection(workflowModel, incomingActivity.activityId, outgoingConnection.targetId, incomingConnection.outcome);
if (outgoingConnection) workflowModel = addConnection(workflowModel, incomingActivity.activityId, outgoingConnection.targetId, incomingConnection.outcome);
}
this.updateWorkflowModel(workflowModel);
}
updateActivity(activity: ActivityModel) {
let workflowModel = {...this.workflowModel};
const activities = [...workflowModel.activities];
const index = activities.findIndex(x => x.activityId === activity.activityId);
activities[index] = activity;
this.updateWorkflowModel({...workflowModel, activities: activities});
// onOutcomeButtonClick(e: Event) {
// console.log(e)
// e.preventDefault();
// console.log(e)
// // this.parentActivityId = activityId;
// // this.parentActivityOutcome = outcome;
// this.showActivityPicker();
// }
applyZoom() {
this.zoom = d3.zoom().on('zoom', event => {
const { transform } = event;
this.innerD3Selected.attr('transform', transform);
this.zoomParams = {
x: transform.x,
y: transform.y,
scale: transform.k,
};
});
this.svgD3Selected.call(this.zoom);
}
updateWorkflowModel(model: WorkflowModel) {
this.workflowModel = model;
this.workflowChanged.emit(model);
}
setEnities() {
this.graph = new dagreD3.graphlib.Graph().setGraph({})
console.log(this.workflowModel.activities)
removeInvalidConnections(invalidConnections: Array<ConnectionModel>){
if (invalidConnections.length > 0) {
this.workflowModel.activities.forEach(activity => {
this.graph.setNode(activity.activityId, this.createActivityOptions(activity));
const isValid = (connection: ConnectionModel): boolean => invalidConnections.findIndex(x => x.targetId == connection.targetId && x.sourceId == connection.sourceId && x.outcome == connection.outcome) < 0;
const workflowModel = {...this.workflowModel};
workflowModel.connections = workflowModel.connections.filter(isValid);
this.updateWorkflowModel(workflowModel);
}
}
getJsPlumbConnections(): Array<any> {
const rootActivities = getChildActivities(this.workflowModel, null);
const rootConnections = rootActivities.flatMap(x => {
return [
{
sourceId: `start-button`,
sourceActivityId: undefined,
targetId: `start-button-plus-${x.activityId}`,
targetActivityId: x.activityId,
outcome: undefined
},
{
sourceId: `start-button-plus-${x.activityId}`,
sourceActivityId: undefined,
targetId: `activity-${x.activityId}`,
targetActivityId: x.activityId,
outcome: undefined
}]
activity.outcomes.forEach(outcome => {
this.graph.setNode(`${activity.activityId}/${outcome}`, { shape: 'rect', outcome, activity, label: this.renderOutcomeButton(), labelType: 'html', class: 'add' });
this.graph.setEdge(activity.activityId, `${activity.activityId}/${outcome}`, {
label: `<p class="outcome">${outcome}</p>`,
labelpos: 'c',
labelType: 'html',
arrowhead: 'undirected',
});
});
});
const sourceConnections = this.workflowModel.activities.flatMap(activity => {
const displayContext = this.activityDisplayContexts[activity.activityId];
return displayContext.outcomes.map(x =>
({
sourceId: `activity-${activity.activityId}`,
sourceActivityId: activity.activityId,
targetId: `${activity.activityId}-${x}`,
targetActivityId: undefined,
outcome: x
}));
this.workflowModel.connections.forEach(({ sourceId, targetId, outcome }) => {
this.graph.setEdge(`${sourceId}/${outcome}`, targetId, { arrowhead: 'undirected' });
});
const connections = this.workflowModel.connections.flatMap(x => [
{
sourceId: `${x.sourceId}-${x.outcome}`,
sourceActivityId: x.sourceId,
targetId: `activity-${x.targetId}`,
targetActivityId: x.targetId,
outcome: x.outcome
}]
);
return [...rootConnections, ...connections, ...sourceConnections];
console.log(this.workflowModel.activities)
console.log(this.graph.nodes().map(node => this.graph.node(node)))
}
getJsPlumbSourceEndpoints(): Array<any> {
const rootActivities = getChildActivities(this.workflowModel, null);
const rootSourceEndpoints = rootActivities.map(x =>
({
sourceId: `start-button-plus-${x.activityId}`,
sourceActivityId: x.activityId,
outcome: undefined
}));
const otherSourceEndpoints = this.workflowModel.activities.flatMap(activity => {
const displayContext = this.activityDisplayContexts[activity.activityId];
// closeContextMenu() {
// leave(this.contextMenu);
// }
return displayContext.outcomes.map(x =>
({
sourceId: `${activity.activityId}-${x}`,
sourceActivityId: activity.activityId,
outcome: x
}));
});
return [...rootSourceEndpoints, ...otherSourceEndpoints];
onDeleteActivityClick(e: Event) {
e.preventDefault();
this.removeActivity(this.contextMenu.activity);
this.handleContextMenuChange(0, 0, false, null);
}
getJsPlumbTargets() {
return this.workflowModel.activities.map(x =>
({
targetId: `activity-${x.activityId}`,
targetActivityId: x.activityId
onEditActivityClick(e: Event) {
e.preventDefault();
this.handleEditActivity(this.contextMenu.activity);
this.handleContextMenuChange(0, 0, false, null);
}
renderNodes() {
const prevTransform = this.innerD3Selected.attr('transform');
const scaleAfter = this.zoomParams.scale;
this.svgD3Selected.call(this.zoom.scaleTo, 1);
this.dagreD3Renderer(this.innerD3Selected as any, this.graph as any);
this.svgD3Selected.call(this.zoom.scaleTo, scaleAfter);
this.innerD3Selected.attr('transform', prevTransform);
d3.selectAll('.node.add').each((n: any) => {
const node = this.graph.node(n) as any;
d3.select(node.elem).on('click', e => {
e.preventDefault();
this.parentActivityId = node.activity.activityId;
this.parentActivityOutcome = node.outcome;
if(e.shiftKey){
d3.select(node.elem).select('svg').classed("text-green-400", true).classed("text-gray-400", false).classed('hover:text-blue-500', false)
return;
}
));
this.showActivityPicker();
});
});
d3.selectAll('.node.activity').each((n: any) => {
const node = this.graph.node(n) as any;
// console.log(d3.select(node.elem).select('.context-menu'));
d3.select(node.elem).on('click', () => {
this.addConnection(this.parentActivityId, node.activity.activityId, this.parentActivityOutcome)
this.parentActivityId = null;
this.parentActivityOutcome = null;
})
d3.select(node.elem)
.select('button')
.on('click', evt => {
evt.stopPropagation();
this.handleContextMenuChange(evt.clientX, evt.clientY, true, node.activity);
});
// .on('click', () => toggleMenu()))
});
// d3.selectAll('.node.add > rect').each((node) => {
// console.log(node)
// })
}
onConnectionCreated(connection: ConnectionModel) {
const workflowModel = {...this.workflowModel};
workflowModel.connections = [...workflowModel.connections, connection];
this.updateWorkflowModel(workflowModel);
rerenderTree() {
this.applyZoom();
this.setEnities();
this.renderNodes();
}
onConnectionDetached(connection: ConnectionModel) {
const workflowModel = {...this.workflowModel};
workflowModel.connections = workflowModel.connections.filter(x => !(x.sourceId == connection.sourceId && x.targetId == connection.targetId && x.outcome == connection.outcome));
// createActivity() {
// this.graph.setNode('node-1', this.createActivityOptions({ displayName: 'node-1', activityId: '1' }));
// }
this.updateWorkflowModel(workflowModel);
createActivityOptions(activity: ActivityModel) {
return {
shape: 'rect',
label: this.renderActivity(activity),
rx: 5,
ry: 5,
labelType: 'html',
class: 'activity',
activity,
};
}
onAddButtonClick() {
this.showActivityPicker();
createOutcomeActivityOptions() {
return { shape: 'circle', label: this.renderOutcomeButton(), labelType: 'html', class: 'add', width: 32, height: 32 };
}
onOutcomeButtonClick(e: Event, outcome?: string, activityId?: string) {
e.preventDefault();
this.parentActivityId = activityId;
this.parentActivityOutcome = outcome;
this.showActivityPicker();
renderOutcomeButton() {
return `<svg class="h-8 w-8 text-gray-400 hover:text-blue-500 cursor-pointer" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 9v3m0 0v3m0-3h3m-3 0H9m12 0a9 9 0 11-18 0 9 9 0 0118 0z" />
</svg>`;
}
async onDragStartActivity(e: DragEvent, activityDisplayContext: ActivityDesignDisplayContext) {
const activityModel = activityDisplayContext.activityModel;
e.dataTransfer.setData("text", activityModel.activityId);
e.dataTransfer.effectAllowed = 'move';
renderActivity(activity: ActivityModel) {
const displayContext = this.activityDisplayContexts[activity.activityId] || undefined
return `<div id=${`activity-${activity.activityId}`}
class="activity border-2 border-solid border-white rounded bg-white text-left text-black text-lg hover:border-blue-600 select-none max-w-md shadow-sm relative">
<div class="p-5">
<div class="flex justify-between space-x-8">
<div class="flex-shrink-0">
${displayContext?.activityIcon || ""}
</div>
<div class="flex-1 font-medium leading-8">
<p>${activity.displayName}</p>
</div>
<div class="context-menu-wrapper flex-shrink-0">
<button aria-haspopup="true"
class="w-8 h-8 inline-flex items-center justify-center text-gray-400 rounded-full bg-transparent hover:text-gray-500 focus:outline-none focus:text-gray-500 focus:bg-gray-100 transition ease-in-out duration-150">
<svg class="h-6 w-6 text-gray-400" width="24" height="24" viewBox="0 0 24 24" stroke-width="2"
stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round">
<path stroke="none" d="M0 0h24v24H0z"/>
<circle cx="5" cy="12" r="1"/>
<circle cx="12" cy="12" r="1"/>
<circle cx="19" cy="12" r="1"/>
</svg>
</button>
</div>
</div>
</div>
${this.renderActivityBody(activity.description)}
</div>`;
}
onDragOverOutcome(e: DragEvent, outcome: string, sourceActivityId: string) {
e.preventDefault();
}
onDropOnOutcome(e: DragEvent, outcome: string, sourceActivityId) {
const targetActivityId = e.dataTransfer.getData('text');
if (sourceActivityId === targetActivityId)
return;
e.preventDefault();
const workflowModel = {...this.workflowModel};
const targetActivityDisplayContext = this.activityDisplayContexts[targetActivityId];
let connections = [...workflowModel.connections];
// Remove current connection where dropped activity is target.
connections = connections.filter(x => x.targetId !== targetActivityId)
// Check if there is already a connection.
const existingConnection = connections.find(x => x.sourceId == sourceActivityId && x.outcome == outcome);
if (existingConnection) {
// Try to find a matching outcome on the dropped activity to attach the existing activity to.
const newOutcome = targetActivityDisplayContext.outcomes.find(x => x == existingConnection.outcome) || targetActivityDisplayContext.outcomes[0];
existingConnection.sourceId = targetActivityId;
existingConnection.outcome = newOutcome;
// Create new connection.
const newConnection: ConnectionModel = {
sourceId: sourceActivityId,
targetId: targetActivityId,
outcome: outcome
};
workflowModel.connections = [...connections, newConnection];
} else {
// Create new connection.
const newConnection: ConnectionModel = {
sourceId: sourceActivityId,
targetId: targetActivityId,
outcome: outcome
};
workflowModel.connections = [...connections, newConnection];
}
this.updateWorkflowModel(workflowModel);
renderActivityBody(description: string | null) {
if (!description) return '';
return `<div class="p-6 text-gray-400 text-sm border-t border-t-solid">
${description}
</div>`;
}
render() {
const renderedActivities = new Set<string>();
return (
<Host class="workflow-canvas flex-1 flex" ref={el => this.el = el}>
<div class="flex-1 text-gray-200">
<div class="p-10">
<div class="canvas select-none" ref={el => this.canvasElement = el}>
<div class="tree">
<ul>
<li>
<div class="inline-flex flex flex-col items-center">
<button id="start-button"
onClick={() => this.onAddButtonClick()}
type="button"
class="px-6 py-3 border border-transparent text-base leading-6 font-medium rounded-md text-white bg-green-600 hover:bg-green-500 focus:outline-none focus:border-green-700 focus:shadow-outline-green active:bg-green-700 transition ease-in-out duration-150">
Start
</button>
</div>
{this.renderTree(this.getRootActivities(), true, renderedActivities)}
</li>
</ul>
</div>
<Host class="workflow-canvas flex-1 flex" ref={el => (this.el = el)}>
<svg ref={el => (this.svg = el)} id="svg" style={{ height: '100vh', width: '100vw', pointerEvents: this.contextMenu.shown ? 'none' : '' }}>
<g ref={el => (this.inner = el)} />
</svg>
<div
data-transition-enter="transition ease-out duration-100"
data-transition-enter-start="transform opacity-0 scale-95"
data-transition-enter-end="transform opacity-100 scale-100"
data-transition-leave="transition ease-in duration-75"
data-transition-leave-start="transform opacity-100 scale-100"
data-transition-leave-end="transform opacity-0 scale-95"
class={`${this.contextMenu.shown ? '' : 'hidden'} context-menu z-10 mx-3 w-48 mt-1 rounded-md shadow-lg`}
style={{ position: 'absolute', left: `${this.contextMenu.x}px`, top: `${this.contextMenu.y}px` }}
ref={el =>
registerClickOutside(this, el, () => {
this.handleContextMenuChange(0, 0, false, null);
})
}
>
<div class="rounded-md bg-white shadow-xs" role="menu" aria-orientation="vertical" aria-labelledby="pinned-project-options-menu-0">
<div class="py-1">
<a
onClick={e => this.onEditActivityClick(e)}
href="#"
class="block px-4 py-2 text-sm leading-5 text-gray-700 hover:bg-gray-100 hover:text-gray-900 focus:outline-none focus:bg-gray-100 focus:text-gray-900"
role="menuitem"
>
Edit
</a>
</div>
<div class="border-t border-gray-100" />
<div class="py-1">
<a
onClick={e => this.onDeleteActivityClick(e)}
href="#"
class="block px-4 py-2 text-sm leading-5 text-gray-700 hover:bg-gray-100 hover:text-gray-900 focus:outline-none focus:bg-gray-100 focus:text-gray-900"
role="menuitem"
>
Delete
</a>
</div>
</div>
</div>
<div innerHTML={<ActivityIcon/>}/>
</Host>
);
}
renderTree(activities: Array<ActivityModel>, isRoot: boolean, renderedActivities: Set<string>): any {
const list = activities.filter(x => !renderedActivities.has(x.activityId));
const cssClass = isRoot ? "root" : undefined;
for (const activity of list)
renderedActivities.add(activity.activityId);
if (list.length == 0)
return null;
return (
<ul class={cssClass}>
{activities.map(x => {
const activityId = x.activityId;
const children = getChildActivities(this.workflowModel, activityId);
const displayContext = this.activityDisplayContexts[activityId];
return <li key={x.activityId}>
<div class="inline-flex flex flex-col items-center">
{isRoot ? this.renderOutcomeButton(`start-button-plus-${x.activityId}`, '', activityId, e => this.onOutcomeButtonClick(e, null, activityId)) : undefined}
{this.renderActivity(displayContext)}
{this.renderOutcomeButtons(displayContext)}
</div>
{children.length > 0 ? this.renderTree(children, false, renderedActivities) : undefined}
</li>;
})}
</ul>
);
}
renderOutcomeButtons(displayContext: ActivityDesignDisplayContext) {
const activityId = displayContext.activityModel.activityId;
return (
<div class="flex flex-row space-x-6">
{displayContext.outcomes.map(x => this.renderOutcomeButton(`${activityId}-${x}`, x, activityId, e => this.onOutcomeButtonClick(e, x, activityId)))}
</div>
);
}
renderOutcomeButton(id: string, outcome: string, activityId: string, clickHandler: (e: MouseEvent) => void) {
return (
<div class="my-6 flex flex-col items-center">
{outcome && outcome.length > 0 ? <div
class="mb-4 relative z-10 px-2.5 py-0.5 rounded-full text-xs font-medium leading-4 bg-cool-gray-100 text-cool-gray-800 capitalize">{outcome}</div> : undefined}
<a key={id} id={id} href="#" onClick={e => clickHandler(e)}
onDragOver={e => this.onDragOverOutcome(e, outcome, activityId)}
onDrop={e => this.onDropOnOutcome(e, outcome, activityId)}
>
<svg class="h-8 w-8 text-gray-400 hover:text-blue-500 cursor-pointer" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 9v3m0 0v3m0-3h3m-3 0H9m12 0a9 9 0 11-18 0 9 9 0 0118 0z"/>
</svg>
</a>
</div>
);
}
renderActivity(displayContext: ActivityDesignDisplayContext) {
return <elsa-designer-tree-activity displayContext={displayContext} draggable={true}
onDragStart={e => this.onDragStartActivity(e, displayContext)}
/>;
}
}

View file

@ -62,7 +62,7 @@ export class ElsaWorkflowDefinitionEditor {
this.networkError = null;
// A small hack to make sure JS Plumb is cleaned up before HTML elements get removed.
await this.designer.destroyJsPlumb();
// await this.designer.destroyJsPlumb();
try {
const workflowDefinition = await client.workflowDefinitionsApi.import(this.workflowDefinition.definitionId, file);

View file

@ -3,10 +3,10 @@
export const ActivityIcon = props => {
const color = props.color || 'light-blue';
return (
<span class={`rounded-lg inline-flex p-3 bg-${color}-50 text-${color}-700 ring-4 ring-white`}>
`<span class="${`rounded-lg inline-flex p-3 bg-${color}-50 text-${color}-700 ring-4 ring-white`}">
<svg class="h-6 w-6" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor" aria-hidden="true">
<path stroke="none" d="M0 0h24v24H0z"/> <polyline points="21 12 17 12 14 20 10 4 7 12 3 12"/>
</svg>
</span>
</span>`
);
};

View file

@ -2,11 +2,11 @@
export const BreakIcon = props =>
(
<span class="rounded-lg inline-flex p-3 bg-green-50 text-green-700 ring-4 ring-white">
`<span class="rounded-lg inline-flex p-3 bg-green-50 text-green-700 ring-4 ring-white">
<svg class="h-6 w-6" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round">
<path stroke="none" d="M0 0h24v24H0z"/>
<path d="M14 8v-2a2 2 0 0 0 -2 -2h-7a2 2 0 0 0 -2 2v12a2 2 0 0 0 2 2h7a2 2 0 0 0 2 -2v-2"/>
<path d="M7 12h14l-3 -3m0 6l3 -3"/>
</svg>
</span>
</span>`
);

View file

@ -2,10 +2,10 @@
export const CogIcon = props =>
(
<span class="rounded-lg inline-flex p-3 bg-light-blue-50 text-light-blue-700 ring-4 ring-white">
`<span class="rounded-lg inline-flex p-3 bg-light-blue-50 text-light-blue-700 ring-4 ring-white">
<svg class="h-6 w-6" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10.325 4.317c.426-1.756 2.924-1.756 3.35 0a1.724 1.724 0 002.573 1.066c1.543-.94 3.31.826 2.37 2.37a1.724 1.724 0 001.065 2.572c1.756.426 1.756 2.924 0 3.35a1.724 1.724 0 00-1.066 2.573c.94 1.543-.826 3.31-2.37 2.37a1.724 1.724 0 00-2.572 1.065c-.426 1.756-2.924 1.756-3.35 0a1.724 1.724 0 00-2.573-1.066c-1.543.94-3.31-.826-2.37-2.37a1.724 1.724 0 00-1.065-2.572c-1.756-.426-1.756-2.924 0-3.35a1.724 1.724 0 001.066-2.573c-.94-1.543.826-3.31 2.37-2.37.996.608 2.296.07 2.572-1.065z"/>
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z"/>
</svg>
</span>
</span>`
);

View file

@ -2,11 +2,11 @@
export const CorrelateIcon = props =>
(
<span class={`rounded-lg inline-flex p-3 bg-light-blue-50 text-light-blue-700 ring-4 ring-white`}>
`<span class="rounded-lg inline-flex p-3 bg-light-blue-50 text-light-blue-700 ring-4 ring-white">
<svg class="h-6 w-6" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round">
<path stroke="none" d="M0 0h24v24H0z"/>
<path d="M10 14a3.5 3.5 0 0 0 5 0l4 -4a3.5 3.5 0 0 0 -5 -5l-.5 .5"/>
<path d="M14 10a3.5 3.5 0 0 0 -5 0l-4 4a3.5 3.5 0 0 0 5 5l.5 -.5"/>
</svg>
</span>
</span>`
);

View file

@ -2,11 +2,11 @@
export const EraseIcon = props =>
(
<span class={`rounded-lg inline-flex p-3 bg-light-blue-50 text-light-blue-700 ring-4 ring-white`}>
`<span class="${`rounded-lg inline-flex p-3 bg-light-blue-50 text-light-blue-700 ring-4 ring-white`}">
<svg class="h-6 w-6" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round">
<path stroke="none" d="M0 0h24v24H0z"/>
<path d="M19 19h-11l-4 -4a1 1 0 0 1 0 -1.41l10 -10a1 1 0 0 1 1.41 0l5 5a1 1 0 0 1 0 1.41l-9 9"/>
<path d="M18 12.3l-6.3 -6.3"/>
</svg>
</span>
</span>`
);

View file

@ -2,10 +2,10 @@
export const FinishIcon = props =>
(
<span class={`rounded-lg inline-flex p-3 bg-light-blue-50 text-light-blue-700 ring-4 ring-white`}>
`<span class="${`rounded-lg inline-flex p-3 bg-light-blue-50 text-light-blue-700 ring-4 ring-white`}">
<svg class="h-6 w-6" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<path d="M4 15s1-1 4-1 5 2 8 2 4-1 4-1V3s-1 1-4 1-5-2-8-2-4 1-4 1z"/>
<line x1="4" y1="22" x2="4" y2="15"/>
</svg>
</span>
</span>`
);

View file

@ -2,7 +2,7 @@
export const ForkIcon = props =>
(
<span class="rounded-lg inline-flex p-3 bg-green-50 text-green-700 ring-4 ring-white">
`<span class="rounded-lg inline-flex p-3 bg-green-50 text-green-700 ring-4 ring-white">
<svg class="h-6 w-6 transform rotate-180" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor" aria-hidden="true">
<path stroke="none" d="M0 0h24v24H0z"/>
<circle cx="12" cy="18" r="2"/>
@ -11,5 +11,5 @@ export const ForkIcon = props =>
<path d="M7 8v2a2 2 0 0 0 2 2h6a2 2 0 0 0 2 -2v-2"/>
<line x1="12" y1="12" x2="12" y2="16"/>
</svg>
</span>
</span>`
);

View file

@ -2,9 +2,9 @@
export const HttpEndpointIcon = props =>
(
<span class={`rounded-lg inline-flex p-3 bg-rose-50 text-rose-700 ring-4 ring-white`}>
`<span class="${`rounded-lg inline-flex p-3 bg-rose-50 text-rose-700 ring-4 ring-white`}">
<svg class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width={2} d="M3 15a4 4 0 004 4h9a5 5 0 10-.1-9.999 5.002 5.002 0 10-9.78 2.096A4.001 4.001 0 003 15z"/>
</svg>
</span>
</span>`
);

View file

@ -2,9 +2,9 @@
export const IfIcon = props =>
(
<span class="rounded-lg inline-flex p-3 bg-green-50 text-green-700 ring-4 ring-white">
`<span class="rounded-lg inline-flex p-3 bg-green-50 text-green-700 ring-4 ring-white">
<svg class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width={2} d="M8.228 9c.549-1.165 2.03-2 3.772-2 2.21 0 4 1.343 4 3 0 1.4-1.278 2.575-3.006 2.907-.542.104-.994.54-.994 1.093m0 3h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"/>
</svg>
</span>
</span>`
);

View file

@ -2,12 +2,12 @@
export const InterruptTriggerIcon = props =>
(
<span class={`rounded-lg inline-flex p-3 bg-light-blue-50 text-light-blue-700 ring-4 ring-white`}>
`<span class="${`rounded-lg inline-flex p-3 bg-light-blue-50 text-light-blue-700 ring-4 ring-white`}">
<svg class="h-6 w-6" xmlns="http://www.w3.org/2000/svg" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round">
<path stroke="none" d="M0 0h24v24H0z"/>
<path d="M8 16v-4a4 4 0 0 1 8 0v4"/>
<path d="M3 12h1M12 3v1M20 12h1M5.6 5.6l.7 .7M18.4 5.6l-.7 .7"/>
<rect x="6" y="16" width="12" height="4" rx="1"/>
</svg>
</span>
</span>`
);

View file

@ -2,7 +2,7 @@
export const JoinIcon = props =>
(
<span class="rounded-lg inline-flex p-3 bg-green-50 text-green-700 ring-4 ring-white">
`<span class="rounded-lg inline-flex p-3 bg-green-50 text-green-700 ring-4 ring-white">
<svg class="h-6 w-6" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor" aria-hidden="true">
<path stroke="none" d="M0 0h24v24H0z"/>
<circle cx="12" cy="18" r="2"/>
@ -11,5 +11,5 @@ export const JoinIcon = props =>
<path d="M7 8v2a2 2 0 0 0 2 2h6a2 2 0 0 0 2 -2v-2"/>
<line x1="12" y1="12" x2="12" y2="16"/>
</svg>
</span>
</span>`
);

View file

@ -2,12 +2,12 @@
export const LoopIcon = props =>
(
<span class="rounded-lg inline-flex p-3 bg-green-50 text-green-700 ring-4 ring-white">
`<span class="rounded-lg inline-flex p-3 bg-green-50 text-green-700 ring-4 ring-white">
<svg class="h-6 w-6" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round">
<path stroke="none" d="M0 0h24v24H0z"/>
<path d="M4 12v-3a3 3 0 0 1 3 -3h13m-3 -3l3 3l-3 3"/>
<path d="M20 12v3a3 3 0 0 1 -3 3h-13m3 3l-3-3l3-3"/>
<path d="M11 11l1 -1v4"/>
</svg>
</span>
</span>`
);

View file

@ -2,11 +2,11 @@
export const ReadLineIcon = props =>
(
<span class={`rounded-lg inline-flex p-3 bg-light-blue-50 text-light-blue-700 ring-4 ring-white`}>
`<span class="rounded-lg inline-flex p-3 bg-light-blue-50 text-light-blue-700 ring-4 ring-white">
<svg class="h-6 w-6" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round">
<path stroke="none" d="M0 0h24v24H0z"/>
<polyline points="5 7 10 12 5 17"/>
<line x1="13" y1="17" x2="19" y2="17"/>
</svg>
</span>
</span>`
);

View file

@ -2,12 +2,12 @@
export const RedirectIcon = props =>
(
<span class={`rounded-lg inline-flex p-3 bg-light-blue-50 text-light-blue-700 ring-4 ring-white`}>
`<span class="${`rounded-lg inline-flex p-3 bg-light-blue-50 text-light-blue-700 ring-4 ring-white`}">
<svg class="h-6 w-6" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round">
<path stroke="none" d="M0 0h24v24H0z"/>
<circle cx="6" cy="19" r="2"/>
<circle cx="18" cy="5" r="2"/>
<path d="M12 19h4.5a3.5 3.5 0 0 0 0 -7h-8a3.5 3.5 0 0 1 0 -7h3.5"/>
</svg>
</span>
</span>`
);

View file

@ -2,7 +2,7 @@
export const RunWorkflowIcon = props =>
(
<span class={`rounded-lg inline-flex p-3 bg-light-blue-50 text-light-blue-700 ring-4 ring-white`}>
`<span class="${`rounded-lg inline-flex p-3 bg-light-blue-50 text-light-blue-700 ring-4 ring-white`}">
<svg class="h-6 w-6" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round">
<path stroke="none" d="M0 0h24v24H0z"/>
<rect x="3" y="3" width="6" height="6" rx="1"/>
@ -10,5 +10,5 @@ export const RunWorkflowIcon = props =>
<path d="M21 11v-3a2 2 0 0 0 -2 -2h-6l3 3m0 -6l-3 3"/>
<path d="M3 13v3a2 2 0 0 0 2 2h6l-3 -3m0 6l3 -3"/>
</svg>
</span>
</span>`
);

View file

@ -2,12 +2,12 @@
export const ScriptIcon = props =>
(
<span class={`rounded-lg inline-flex p-3 bg-light-blue-50 text-light-blue-700 ring-4 ring-white`}>
`<span class="${`rounded-lg inline-flex p-3 bg-light-blue-50 text-light-blue-700 ring-4 ring-white`}">
<svg class="h-6 w-6" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round">
<path stroke="none" d="M0 0h24v24H0z"/>
<path d="M15 21h-9a3 3 0 0 1 -3 -3v-1h10v2a2 2 0 0 0 4 0v-14a2 2 0 1 1 2 2h-2m2 -4h-11a3 3 0 0 0 -3 3v11"/>
<line x1="9" y1="7" x2="13" y2="7"/>
<line x1="9" y1="11" x2="13" y2="11"/>
</svg>
</span>
</span>`
);

View file

@ -2,9 +2,9 @@
export const SendEmailIcon = props =>
(
<span class={`rounded-lg inline-flex p-3 bg-light-blue-50 text-light-blue-700 ring-4 ring-white`}>
`<span class="rounded-lg inline-flex p-3 bg-light-blue-50 text-light-blue-700 ring-4 ring-white">
<svg class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width={2} d="M16 12a4 4 0 10-8 0 4 4 0 008 0zm0 0v1.5a2.5 2.5 0 005 0V12a9 9 0 10-9 9m4.5-1.206a8.959 8.959 0 01-4.5 1.207" />
</svg>
</span>
</span>`
);

View file

@ -2,9 +2,9 @@
export const SendHttpRequestIcon = props =>
(
<span class={`rounded-lg inline-flex p-3 bg-light-blue-50 text-light-blue-700 ring-4 ring-white`}>
`<span class="${`rounded-lg inline-flex p-3 bg-light-blue-50 text-light-blue-700 ring-4 ring-white`}">
<svg class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width={2} d="M3 15a4 4 0 004 4h9a5 5 0 10-.1-9.999 5.002 5.002 0 10-9.78 2.096A4.001 4.001 0 003 15z"/>
</svg>
</span>
</span>`
);

View file

@ -2,10 +2,10 @@
export const SendSignalIcon = props =>
(
<span class={`rounded-lg inline-flex p-3 bg-light-blue-50 text-light-blue-700 ring-4 ring-white`}>
`<span class="${`rounded-lg inline-flex p-3 bg-light-blue-50 text-light-blue-700 ring-4 ring-white`}">
<svg class="h-6 w-6" xmlns="http://www.w3.org/2000/svg" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<circle cx="12" cy="12" r="2"/>
<path d="M16.24 7.76a6 6 0 0 1 0 8.49m-8.48-.01a6 6 0 0 1 0-8.49m11.31-2.82a10 10 0 0 1 0 14.14m-14.14 0a10 10 0 0 1 0-14.14"/>
</svg>
</span>
</span>`
);

View file

@ -2,9 +2,9 @@
export const SignalReceivedIcon = props =>
(
<span class={`rounded-lg inline-flex p-3 bg-rose-50 text-rose-700 ring-4 ring-white`}>
`<span class="${`rounded-lg inline-flex p-3 bg-rose-50 text-rose-700 ring-4 ring-white`}">
<svg class="h-6 w-6" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor" aria-hidden="true">
<path stroke="none" d="M0 0h24v24H0z"/> <polyline points="21 12 17 12 14 20 10 4 7 12 3 12"/>
</svg>
</span>
</span>`
);

View file

@ -2,7 +2,7 @@
export const SwitchIcon = props =>
(
<span class={`rounded-lg inline-flex p-3 bg-green-50 text-green-700 ring-4 ring-white`}>
`<span class="${`rounded-lg inline-flex p-3 bg-green-50 text-green-700 ring-4 ring-white`}">
<svg class="h-6 w-6" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round">
<path stroke="none" d="M0 0h24v24H0z"/>
<polyline points="15 4 19 4 19 8"/>
@ -11,5 +11,5 @@ export const SwitchIcon = props =>
<polyline points="15 19 19 19 19 15"/>
<line x1="5" y1="5" x2="19" y2="19"/>
</svg>
</span>
</span>`
);

View file

@ -2,9 +2,9 @@
export const TimerIcon = props =>
(
<span class={`rounded-lg inline-flex p-3 bg-rose-50 text-rose-700 ring-4 ring-white`}>
`<span class="${`rounded-lg inline-flex p-3 bg-rose-50 text-rose-700 ring-4 ring-white`}">
<svg class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width={2} d="M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z"/>
</svg>
</span>
</span>`
);

View file

@ -2,9 +2,9 @@
export const WriteHttpResponseIcon = props =>
(
<span class={`rounded-lg inline-flex p-3 bg-light-blue-50 text-light-blue-700 ring-4 ring-white`}>
`<span class="${`rounded-lg inline-flex p-3 bg-light-blue-50 text-light-blue-700 ring-4 ring-white`}">
<svg class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M11 5H6a2 2 0 00-2 2v11a2 2 0 002 2h11a2 2 0 002-2v-5m-1.414-9.414a2 2 0 112.828 2.828L11.828 15H9v-2.828l8.586-8.586z"/>
</svg>
</span>
</span>`
);

View file

@ -2,9 +2,9 @@
export const WriteLineIcon = props =>
(
<span class={`rounded-lg inline-flex p-3 bg-light-blue-50 text-light-blue-700 ring-4 ring-white`}>
`<span class="rounded-lg inline-flex p-3 bg-light-blue-50 text-light-blue-700 ring-4 ring-white">
<svg class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width={2} d="M15.232 5.232l3.536 3.536m-2.036-5.036a2.5 2.5 0 113.536 3.536L6.5 21.036H3v-3.572L16.732 3.732z"/>
</svg>
</span>
</span>`
);

View file

@ -145,9 +145,8 @@ export class ElsaActivityPickerModal {
{displayContexts.map(displayContext => (
<a href="#" onClick={e => this.onActivityClick(e, displayContext.activityDescriptor)} class="relative rounded group p-6 focus-within:ring-2 focus-within:ring-inset focus-within:ring-blue-500">
<div class="flex space-x-10">
<div class="flex-0">
<div>
{displayContext.activityIcon}
<div class="flex flex-0 items-center">
<div innerHTML={displayContext.activityIcon}>
</div>
</div>
<div class="flex-1 mt-2">