diff --git a/packages/editor/cypress/snapshots/relationship.cy.ts/move ctl pont 0.snap.png b/packages/editor/cypress/snapshots/relationship.cy.ts/move ctl pont 0.snap.png
index bec552bf..cbae598a 100644
Binary files a/packages/editor/cypress/snapshots/relationship.cy.ts/move ctl pont 0.snap.png and b/packages/editor/cypress/snapshots/relationship.cy.ts/move ctl pont 0.snap.png differ
diff --git a/packages/editor/cypress/snapshots/topicDragAndDrop.cy.ts/moveleftNode.snap.png b/packages/editor/cypress/snapshots/topicDragAndDrop.cy.ts/moveleftNode.snap.png
index 4c63b69a..3653755f 100644
Binary files a/packages/editor/cypress/snapshots/topicDragAndDrop.cy.ts/moveleftNode.snap.png and b/packages/editor/cypress/snapshots/topicDragAndDrop.cy.ts/moveleftNode.snap.png differ
diff --git a/packages/editor/cypress/snapshots/topicIcon.cy.ts/add-new-icon.snap.png b/packages/editor/cypress/snapshots/topicIcon.cy.ts/add-new-icon.snap.png
index 117ce247..78388ecf 100644
Binary files a/packages/editor/cypress/snapshots/topicIcon.cy.ts/add-new-icon.snap.png and b/packages/editor/cypress/snapshots/topicIcon.cy.ts/add-new-icon.snap.png differ
diff --git a/packages/editor/cypress/snapshots/topicManager.cy.ts/addChildNodeSortcut.snap.png b/packages/editor/cypress/snapshots/topicManager.cy.ts/addChildNodeSortcut.snap.png
index 8d34e100..d869e227 100644
Binary files a/packages/editor/cypress/snapshots/topicManager.cy.ts/addChildNodeSortcut.snap.png and b/packages/editor/cypress/snapshots/topicManager.cy.ts/addChildNodeSortcut.snap.png differ
diff --git a/packages/editor/cypress/snapshots/topicManager.cy.ts/editor-shortcut-edit.snap.png b/packages/editor/cypress/snapshots/topicManager.cy.ts/editor-shortcut-edit.snap.png
index b8ded9b7..87a73f33 100644
Binary files a/packages/editor/cypress/snapshots/topicManager.cy.ts/editor-shortcut-edit.snap.png and b/packages/editor/cypress/snapshots/topicManager.cy.ts/editor-shortcut-edit.snap.png differ
diff --git a/packages/editor/cypress/snapshots/topicShape.cy.ts/topicShapePanel.snap.png b/packages/editor/cypress/snapshots/topicShape.cy.ts/topicShapePanel.snap.png
index f7df302d..ca124f68 100644
Binary files a/packages/editor/cypress/snapshots/topicShape.cy.ts/topicShapePanel.snap.png and b/packages/editor/cypress/snapshots/topicShape.cy.ts/topicShapePanel.snap.png differ
diff --git a/packages/editor/src/components/editor-toolbar/configBuilder.tsx b/packages/editor/src/components/editor-toolbar/configBuilder.tsx
index 7795d0ca..63e26d37 100644
--- a/packages/editor/src/components/editor-toolbar/configBuilder.tsx
+++ b/packages/editor/src/components/editor-toolbar/configBuilder.tsx
@@ -16,7 +16,7 @@
* limitations under the License.
*/
import React from 'react';
-import BrushOutlinedIcon from '@mui/icons-material/BrushOutlined';
+import FormatPaintIconOutlineIcon from '@mui/icons-material/FormatPaintOutlined';
import FontDownloadOutlinedIcon from '@mui/icons-material/FontDownloadOutlined';
import TextIncreaseOutlinedIcon from '@mui/icons-material/TextIncreaseOutlined';
import TextDecreaseOutlinedIcon from '@mui/icons-material/TextDecreaseOutlined';
@@ -38,6 +38,7 @@ import TimelineOutined from '@mui/icons-material/TimelineOutlined';
import ShareOutlined from '@mui/icons-material/ShareOutlined';
import SwapCallsOutlined from '@mui/icons-material/SwapCallsOutlined';
import NotInterestedOutlined from '@mui/icons-material/NotInterestedOutlined';
+import ShortcutIconOutlined from '@mui/icons-material/ShortcutOutlined';
import Palette from '@mui/icons-material/Square';
import SquareOutlined from '@mui/icons-material/SquareOutlined';
@@ -62,7 +63,7 @@ export function buildEditorPanelConfig(model: Editor, intl: IntlShape): ActionCo
const modelBuilder = new NodePropertyValueModelBuilder(model.getDesigner());
// eslint-disable-next-line react-hooks/rules-of-hooks
const colorAndShapeToolbarConfiguration: ActionConfig = {
- icon: ,
+ icon: ,
tooltip: intl.formatMessage({
id: 'editor-panel.tooltip-topic-style',
defaultMessage: 'Topic Style',
@@ -182,6 +183,15 @@ export function buildEditorPanelConfig(model: Editor, intl: IntlShape): ActionCo
onClick: () => modelBuilder.getConnectionStyleModel().setValue(LineType.THICK_CURVED),
selected: () => modelBuilder.getConnectionStyleModel().getValue() === LineType.THICK_CURVED,
},
+ {
+ icon: ,
+ tooltip: intl.formatMessage({
+ id: 'editor-panel.tooltip-connection-style-arc',
+ defaultMessage: 'Arc',
+ }),
+ onClick: () => modelBuilder.getConnectionStyleModel().setValue(LineType.ARC),
+ selected: () => modelBuilder.getConnectionStyleModel().getValue() === LineType.ARC,
+ },
{
icon: ,
tooltip: intl.formatMessage({
@@ -444,9 +454,9 @@ export function buildEditorPanelConfig(model: Editor, intl: IntlShape): ActionCo
return [
addNodeToolbarConfiguration,
deleteNodeToolbarConfiguration,
- connectionStyleConfiguration,
- fontFormatToolbarConfiguration,
colorAndShapeToolbarConfiguration,
+ fontFormatToolbarConfiguration,
+ connectionStyleConfiguration,
editIconConfiguration,
editNoteConfiguration,
editLinkUrlConfiguration,
diff --git a/packages/mindplot/src/components/ConnectionLine.ts b/packages/mindplot/src/components/ConnectionLine.ts
index f192f3be..59df6201 100644
--- a/packages/mindplot/src/components/ConnectionLine.ts
+++ b/packages/mindplot/src/components/ConnectionLine.ts
@@ -22,6 +22,7 @@ import PositionType from './PositionType';
import Topic from './Topic';
import TopicConfig from './TopicConfig';
import Canvas from './Canvas';
+import ArcLine from './widget/ArcLine';
// eslint-disable-next-line no-shadow
export enum LineType {
@@ -29,6 +30,7 @@ export enum LineType {
POLYLINE_MIDDLE,
POLYLINE_CURVED,
THICK_CURVED,
+ ARC,
}
class ConnectionLine {
@@ -81,8 +83,13 @@ class ConnectionLine {
line = new CurvedLine();
(line as CurvedLine).setWidth(this._targetTopic.isCentralTopic() ? 15 : 3);
break;
- default:
- throw new Error(`Unexpected line type. ${lineType}`);
+ case LineType.ARC:
+ line = new ArcLine(this._sourceTopic, this._targetTopic);
+ break;
+ default: {
+ const exhaustiveCheck: never = lineType;
+ throw new Error(exhaustiveCheck);
+ }
}
return line;
}
@@ -110,8 +117,13 @@ class ConnectionLine {
this._line.setStroke(1, 'solid', color, 1);
this._line.setFill(color, 1);
break;
- default:
- throw new Error(`Unexpected line type. ${this._type}`);
+ case LineType.ARC:
+ this._line.setStroke(1, 'solid', color, 1);
+ break;
+ default: {
+ const exhaustiveCheck: never = this._type;
+ throw new Error(exhaustiveCheck);
+ }
}
return color;
}
diff --git a/packages/mindplot/src/components/Designer.ts b/packages/mindplot/src/components/Designer.ts
index 365bc388..0d0d380b 100644
--- a/packages/mindplot/src/components/Designer.ts
+++ b/packages/mindplot/src/components/Designer.ts
@@ -890,10 +890,9 @@ class Designer extends Events {
}
changeConnectionStyle(type: LineType): void {
- const validateFunc = (topic: Topic) => !topic.isCentralTopic();
-
- const validateError = $msg('CENTRAL_TOPIC_CONNECTION_STYLE_CAN_NOT_BE_CHANGED');
- const topicsIds = this.getModel().filterTopicsIds(validateFunc, validateError);
+ const topicsIds = this.getModel()
+ .filterSelectedTopics()
+ .map((t) => t.getId());
if (topicsIds.length > 0) {
this._actionDispatcher.changeConnectionStyleToTopic(topicsIds, type);
}
diff --git a/packages/mindplot/src/components/Relationship.ts b/packages/mindplot/src/components/Relationship.ts
index e4859ac6..14dfe3e4 100644
--- a/packages/mindplot/src/components/Relationship.ts
+++ b/packages/mindplot/src/components/Relationship.ts
@@ -277,8 +277,6 @@ class Relationship extends ConnectionLine {
this._focusShape.setSrcControlPoint(ctrlPoints[0]);
this._focusShape.setDestControlPoint(ctrlPoints[1]);
-
- this._focusShape.updateLine();
}
addEvent(eventType: string, listener: () => void) {
diff --git a/packages/mindplot/src/components/widget/ArcLine.ts b/packages/mindplot/src/components/widget/ArcLine.ts
new file mode 100644
index 00000000..e866e1e9
--- /dev/null
+++ b/packages/mindplot/src/components/widget/ArcLine.ts
@@ -0,0 +1,46 @@
+/*
+ * Copyright [2021] [wisemapping]
+ *
+ * Licensed under WiseMapping Public License, Version 1.0 (the "License").
+ * It is basically the Apache License, Version 2.0 (the "License") plus the
+ * "powered by wisemapping" text requirement on every single page;
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the license at
+ *
+ * http://www.wisemapping.org/license
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import { ArcLine as ArcLine2d } from '@wisemapping/web2d';
+import Topic from '../Topic';
+
+class ArcLine extends ArcLine2d {
+ private _targetTopic: Topic;
+
+ private _sourceTopic: Topic;
+
+ constructor(sourceTopic: Topic, targetTopic: Topic) {
+ super();
+ this._targetTopic = targetTopic;
+ this._sourceTopic = sourceTopic;
+ }
+
+ // Adjust the x position so there is not overlap with the connector.
+ setFrom(x: number, y: number): void {
+ let xOffset = x;
+ if (this._targetTopic.isCentralTopic()) {
+ const sourceX = this._sourceTopic.getPosition().x;
+ xOffset = Math.sign(sourceX) * (this._targetTopic.getSize().width / 3);
+ } else {
+ xOffset = x + 3 * Math.sign(x);
+ }
+
+ super.setFrom(xOffset, y);
+ }
+}
+export default ArcLine;
diff --git a/packages/web2d/package.json b/packages/web2d/package.json
index fff99b65..6d101614 100644
--- a/packages/web2d/package.json
+++ b/packages/web2d/package.json
@@ -20,7 +20,6 @@
"dev": "webpack --config webpack.dev.js",
"build": "webpack --config webpack.prod.js",
"lint": "eslint src",
- "playground": "webpack serve --config webpack.playground.js",
"cy:run": "cypress run",
"cy:open": "cypress open",
"test:integration": "start-server-and-test storybook http-get://localhost:6006 cy:run",
diff --git a/packages/web2d/src/components/ArcLine.ts b/packages/web2d/src/components/ArcLine.ts
new file mode 100644
index 00000000..580c6a5e
--- /dev/null
+++ b/packages/web2d/src/components/ArcLine.ts
@@ -0,0 +1,103 @@
+/*
+ * Copyright [2021] [wisemapping]
+ *
+ * Licensed under WiseMapping Public License, Version 1.0 (the "License").
+ * It is basically the Apache License, Version 2.0 (the "License") plus the
+ * "powered by wisemapping" text requirement on every single page;
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the license at
+ *
+ * http://www.wisemapping.org/license
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import { $assert } from '@wisemapping/core-js';
+import WorkspaceElement from './WorkspaceElement';
+import Line from './Line';
+import StyleAttributes from './StyleAttributes';
+import Toolkit from './Toolkit';
+import ArcLinePeer from './peer/svg/ArcLinePeer';
+import PositionType from './PositionType';
+
+class ArcLine extends WorkspaceElement implements Line {
+ constructor(attributes?: StyleAttributes) {
+ const peer = Toolkit.createArcLine();
+ const defaultAttributes = {
+ strokeColor: 'blue',
+ strokeWidth: 1,
+ strokeStyle: 'solid',
+ strokeOpacity: 1,
+ fill: 'none 0',
+ };
+
+ const mergedAttr = { ...defaultAttributes, ...attributes };
+ super(peer, mergedAttr);
+ }
+
+ getType(): string {
+ return 'ArcLine';
+ }
+
+ setFrom(x: number, y: number): void {
+ $assert(!Number.isNaN(x), 'x must be defined');
+ $assert(!Number.isNaN(y), 'y must be defined');
+
+ this.peer.setFrom(x, y);
+ }
+
+ setTo(x: number, y: number): void {
+ $assert(!Number.isNaN(x), 'x must be defined');
+ $assert(!Number.isNaN(y), 'y must be defined');
+
+ this.peer.setTo(x, y);
+ }
+
+ getFrom() {
+ return this.peer.getFrom();
+ }
+
+ getTo() {
+ return this.peer.getTo();
+ }
+
+ getElementClass(): ArcLine {
+ return this;
+ }
+
+ setIsSrcControlPointCustom(value: boolean): void {
+ throw new Error('Method not implemented.');
+ }
+
+ setIsDestControlPointCustom(value: boolean): void {
+ throw new Error('Method not implemented.');
+ }
+
+ setDashed(v: number, v2: number): void {
+ throw new Error('Method not implemented.');
+ }
+ setSrcControlPoint(value: PositionType): void {
+ throw new Error('Method not implemented.');
+ }
+
+ setDestControlPoint(value: PositionType): void {
+ throw new Error('Method not implemented.');
+ }
+
+ isDestControlPointCustom(): boolean {
+ throw new Error('Method not implemented.');
+ }
+
+ isSrcControlPointCustom(): boolean {
+ throw new Error('Method not implemented.');
+ }
+
+ getControlPoints(): [PositionType, PositionType] {
+ throw new Error('Method not implemented.');
+ }
+}
+
+export default ArcLine;
diff --git a/packages/web2d/src/components/Line.ts b/packages/web2d/src/components/Line.ts
index ce099c68..73023210 100644
--- a/packages/web2d/src/components/Line.ts
+++ b/packages/web2d/src/components/Line.ts
@@ -70,8 +70,6 @@ interface Line {
removeEvent(value, listener): void;
- updateLine(): void;
-
getElementClass(): WorkspaceElement;
}
export default Line;
diff --git a/packages/web2d/src/components/PolyLine.ts b/packages/web2d/src/components/PolyLine.ts
index 309946a4..f1aa68a5 100644
--- a/packages/web2d/src/components/PolyLine.ts
+++ b/packages/web2d/src/components/PolyLine.ts
@@ -42,9 +42,6 @@ class PolyLine extends WorkspaceElement implements Line {
return this;
}
- updateLine() {
- throw new Error('Method not implemented.');
- }
getTo(): PositionType {
throw new Error('Method not implemented.');
}
diff --git a/packages/web2d/src/components/StraightLine.ts b/packages/web2d/src/components/StraightLine.ts
index 808e25fe..0e6017b0 100644
--- a/packages/web2d/src/components/StraightLine.ts
+++ b/packages/web2d/src/components/StraightLine.ts
@@ -36,9 +36,6 @@ class StraightLine extends WorkspaceElement implements Line {
return this;
}
- updateLine() {
- throw new Error('Method not implemented.');
- }
setIsSrcControlPointCustom(value: boolean): void {
throw new Error('Method not implemented.');
}
diff --git a/packages/web2d/src/components/Toolkit.ts b/packages/web2d/src/components/Toolkit.ts
index efb47a2b..e5f33427 100644
--- a/packages/web2d/src/components/Toolkit.ts
+++ b/packages/web2d/src/components/Toolkit.ts
@@ -26,6 +26,7 @@ import ArrowPeer from './peer/svg/ArrowPeer';
import TextPeer from './peer/svg/TextPeer';
import ImagePeer from './peer/svg/ImagePeer';
import RectPeer from './peer/svg/RectPeer';
+import ArcLinePeer from './peer/svg/ArcLinePeer';
class Toolkit {
static createWorkspace(element?: HTMLElement) {
@@ -52,6 +53,10 @@ class Toolkit {
return new CurvedLinePeer();
}
+ static createArcLine(): ArcLinePeer {
+ return new ArcLinePeer();
+ }
+
static createArrow() {
return new ArrowPeer();
}
diff --git a/packages/web2d/src/components/peer/svg/ArcLinePeer.ts b/packages/web2d/src/components/peer/svg/ArcLinePeer.ts
new file mode 100644
index 00000000..ed14548c
--- /dev/null
+++ b/packages/web2d/src/components/peer/svg/ArcLinePeer.ts
@@ -0,0 +1,85 @@
+/*
+ * Copyright [2021] [wisemapping]
+ *
+ * Licensed under WiseMapping Public License, Version 1.0 (the "License").
+ * It is basically the Apache License, Version 2.0 (the "License") plus the
+ * "powered by wisemapping" text requirement on every single page;
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the license at
+ *
+ * http://www.wisemapping.org/license
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import { $defined } from '@wisemapping/core-js';
+import PositionType from '../../PositionType';
+import ElementPeer from './ElementPeer';
+
+class ArcLinePeer extends ElementPeer {
+ private _x1: number;
+ private _y1: number;
+ private _x2: number;
+ private _y2: number;
+
+ constructor() {
+ const svgElement = window.document.createElementNS('http://www.w3.org/2000/svg', 'path');
+ super(svgElement);
+ this._x1 = 0;
+ this._x2 = 0;
+ this._y1 = 0;
+ this._y2 = 0;
+ this._updatePath();
+ }
+
+ setFrom(x1: number, y1: number): void {
+ const change = this._x1 !== x1 || this._y1 !== y1;
+ this._x1 = x1;
+ this._y1 = y1;
+ if (change) {
+ this._updatePath();
+ }
+ }
+
+ setTo(x2: number, y2: number) {
+ const change = this._x2 !== x2 || this._y2 !== y2;
+ this._x2 = x2;
+ this._y2 = y2;
+ if (change) this._updatePath();
+ }
+
+ getFrom(): PositionType {
+ return { x: this._x1, y: this._y1 };
+ }
+
+ getTo(): PositionType {
+ return { x: this._x2, y: this._y2 };
+ }
+
+ setStrokeWidth(width: number): void {
+ this._native.setAttribute('stroke-width', String(width));
+ }
+
+ private static pointToStr(x: number, y: number) {
+ return `${x.toFixed(1)},${y.toFixed(1)} `;
+ }
+
+ private _updatePath() {
+ // Update style based on width ....
+ if ($defined(this._x1) && $defined(this._y1) && $defined(this._x2) && $defined(this._y2)) {
+ const fromPoint = ArcLinePeer.pointToStr(this._x1, this._y1);
+ const toPoint = ArcLinePeer.pointToStr(this._x2, this._y2);
+
+ const curveP1 = ArcLinePeer.pointToStr(this._x1, this._y1 + (this._y2 - this._y1) / 8);
+ const curveP2 = ArcLinePeer.pointToStr(this._x2 - (this._x2 - this._x1), this._y2);
+
+ const path = `M${fromPoint} C${curveP1},${curveP2} ${toPoint}`;
+ this._native.setAttribute('d', path);
+ }
+ }
+}
+
+export default ArcLinePeer;
diff --git a/packages/web2d/src/index.ts b/packages/web2d/src/index.ts
index cbcc8f8d..73e57106 100644
--- a/packages/web2d/src/index.ts
+++ b/packages/web2d/src/index.ts
@@ -27,6 +27,7 @@ import Rect from './components/Rect';
import Text from './components/Text';
import Point from './components/Point';
import Image from './components/Image';
+import ArcLine from './components/ArcLine';
import WorkspaceElement from './components/WorkspaceElement';
import Line from './components/Line';
import ElementPeer from './components/peer/svg/ElementPeer';
@@ -42,6 +43,7 @@ export {
StraightLine,
Point,
PolyLine,
+ ArcLine,
Rect,
Text,
Workspace,
diff --git a/packages/web2d/storybook/src/stories/ArcLine.js b/packages/web2d/storybook/src/stories/ArcLine.js
new file mode 100644
index 00000000..bf9e4392
--- /dev/null
+++ b/packages/web2d/storybook/src/stories/ArcLine.js
@@ -0,0 +1,86 @@
+/* eslint-disable import/prefer-default-export */
+// eslint-disable-next-line import/prefer-default-export
+import Ellipse from '../../../src/components/Ellipse';
+import Workspace from '../../../src/components/Workspace';
+import ArcLine from '../../../src/components/ArcLine';
+
+export const createArcLine = ({
+ strokeColor,
+ strokeWidth,
+ strokeStyle,
+}) => {
+ const divElem = document.createElement('div');
+
+ const workspace = new Workspace();
+ workspace.setSize('400px', '400px');
+ workspace.setCoordSize(400, 400);
+ workspace.setCoordOrigin(-200, -200);
+
+ // Line 1 ...
+ const line1 = new ArcLine();
+ line1.setFrom(0, 0);
+ line1.setTo(100, 100);
+ line1.setStroke(strokeWidth, strokeStyle, strokeColor, 1);
+ workspace.append(line1);
+
+ const line2 = new ArcLine();
+ line2.setFrom(0, 0);
+ line2.setTo(-100, -100);
+ line2.setStroke(strokeWidth, strokeStyle, strokeColor, 1);
+ workspace.append(line2);
+
+ const line3 = new ArcLine();
+ line3.setFrom(0, 0);
+ line3.setTo(100, -100);
+ line3.setStroke(strokeWidth, strokeStyle, strokeColor, 1);
+ workspace.append(line3);
+
+ const line4 = new ArcLine();
+ line4.setFrom(0, 0);
+ line4.setTo(-100, 100);
+ line4.setStroke(strokeWidth, strokeStyle, strokeColor, 1);
+ workspace.append(line4);
+
+ const line5 = new ArcLine();
+ line5.setFrom(0, 0);
+ line5.setTo(-100, 0);
+ line5.setStroke(strokeWidth, strokeStyle, strokeColor, 1);
+ workspace.append(line5);
+
+ const line6 = new ArcLine();
+ line6.setFrom(0, 0);
+ line6.setTo(100, 0);
+ line6.setStroke(strokeWidth, strokeStyle, strokeColor, 1);
+ workspace.append(line6);
+
+ // Add referene point ...
+ const e1 = new Ellipse();
+ e1.setSize(5, 5);
+ e1.setPosition(0, 0);
+ e1.setFill('red');
+ workspace.append(e1);
+
+ const e2 = new Ellipse();
+ e2.setPosition(-100, -100);
+ e2.setSize(10, 10);
+ workspace.append(e2);
+
+ const e3 = new Ellipse();
+ e3.setPosition(100, 100);
+ e3.setSize(10, 10);
+ workspace.append(e3);
+
+ const e4 = new Ellipse();
+ e4.setPosition(-100, 100);
+ e4.setSize(10, 10);
+ workspace.append(e4);
+
+ const e5 = new Ellipse();
+ e5.setPosition(100, -100);
+ e5.setSize(10, 10);
+ workspace.append(e5);
+
+ workspace.addItAsChildTo(divElem);
+
+ return divElem;
+};
diff --git a/packages/web2d/storybook/src/stories/ArcLine.stories.js b/packages/web2d/storybook/src/stories/ArcLine.stories.js
new file mode 100644
index 00000000..e780f6cc
--- /dev/null
+++ b/packages/web2d/storybook/src/stories/ArcLine.stories.js
@@ -0,0 +1,32 @@
+import { createArcLine } from './ArcLine';
+
+// More on default export: https://storybook.js.org/docs/html/writing-stories/introduction#default-export
+export default {
+ title: 'Shapes/ArcLine',
+ // More on argTypes: https://storybook.js.org/docs/html/api/argtypes
+ argTypes: {
+ strokeColor: { control: 'color' },
+ strokeStyle: {
+ control: { type: 'select' },
+ options: ['dash', 'dot', 'solid', 'longdash', 'dashdot'],
+ },
+ strokeWidth: { control: { type: 'number', min: 0, max: 30, step: 1 } },
+ },
+};
+
+// More on component templates: https://storybook.js.org/docs/html/writing-stories/introduction#using-args
+const Template = ({ label, ...args }) => createArcLine({ label, ...args });
+
+export const Width = Template.bind({});
+Width.args = {
+ strokeWidth: 3,
+ strokeStyle: 'solid',
+ strokeColor: 'blue',
+};
+
+export const Stroke = Template.bind({});
+Stroke.args = {
+ strokeWidth: 10,
+ strokeStyle: 'longdash',
+ strokeColor: 'red',
+};