diff --git a/AddObjectForm.svelte b/AddObjectForm.svelte new file mode 100644 index 0000000..9a8300b --- /dev/null +++ b/AddObjectForm.svelte @@ -0,0 +1,271 @@ + + +
+ +
+ + {#if типОбъекта === 'Прибор'} + +
+ + +
+ + +
+ + +
+ + +
+ +Функции + {#each функции as функция, i} +
+ + + + +
+ {/each} + + + {:else} + +
+ + +
+ {/if} +
+ +
+
+ \ No newline at end of file diff --git a/App.svelte b/App.svelte new file mode 100644 index 0000000..5e39ba3 --- /dev/null +++ b/App.svelte @@ -0,0 +1,17 @@ + + +
+

FSA Editor

+ + +
+ + diff --git a/CustomGlobalConnectRules.ts b/CustomGlobalConnectRules.ts new file mode 100644 index 0000000..210f8bd --- /dev/null +++ b/CustomGlobalConnectRules.ts @@ -0,0 +1,43 @@ +import RuleProvider from 'diagram-js/lib/features/rules/RuleProvider'; +export default class CustomGlobalConnectRules extends RuleProvider { + static $inject = ['eventBus']; + + constructor(eventBus: any) { + super(eventBus); + } + + init(): void { + // Правило для начала соединения + this.addRule('connection.start', (context) => { + return true; + const { source } = context; + + if (source?.businessObject?.type === 'custom:connectable') { + return true; // Разрешить соединение + } + + return false; // Запретить соединение + }); + + // Правило для создания соединения + this.addRule('connection.create', (context) => { + //return true; + const { source, target } = context; + // console.log(typeof source); + // console.log(source.constructor.name) + // instanceof Shape); + + //if (source?.type === Shape) + + //if ( + // source?.businessObject?.type === 'custom:connectable' && + // target?.businessObject?.type === 'custom:connectable' + //) { + return { type: 'Connection' }; + // { type: 'Connection' }; // Тип соединения +// } + + return false; // Запретить соединение + }); + } +} diff --git a/CustomGlobalConnectRulesModule.ts b/CustomGlobalConnectRulesModule.ts new file mode 100644 index 0000000..424e449 --- /dev/null +++ b/CustomGlobalConnectRulesModule.ts @@ -0,0 +1,8 @@ +import CustomGlobalConnectRules from './CustomGlobalConnectRules'; + +const CustomGlobalConnectRulesModule = { + __init__: ['customGlobalConnectRules'], + customGlobalConnectRules: ['type', CustomGlobalConnectRules] +}; + +export default CustomGlobalConnectRulesModule; \ No newline at end of file diff --git a/CustomOutlineModule.ts b/CustomOutlineModule.ts new file mode 100644 index 0000000..7133159 --- /dev/null +++ b/CustomOutlineModule.ts @@ -0,0 +1,62 @@ +import { Element } from 'diagram-js/lib/model/Types'; +import { Outline } from 'diagram-js/lib/features/outline/OutlineProvider'; +import OutlineProvider from 'diagram-js/lib/features/outline/OutlineProvider'; +import { + attr as svgAttr, + create as svgCreate} from 'tiny-svg'; + +import Styles from 'diagram-js/lib/draw/Styles'; + +function CustomOutlineProvider(outline:OutlineProvider, styles:Styles) { + this._styles = styles; + outline.registerProvider(this); +} + +CustomOutlineProvider.$inject = [ + 'outline', + 'styles' +]; + +CustomOutlineProvider.prototype.getOutline = function(element:Element) { + if (element.type === 'custom:circle') { + const outline = svgCreate('circle'); + + svgAttr(outline , { + x: `${element.x}`, + y: `${element.y}`, + cx: element.width / 2, + cy: element.height / 2, + r: 60, + fill: "none", + }); + + console.log(outline); + return outline; + } +} + +CustomOutlineProvider.prototype.updateOutline = function(element: Element, outline: Outline) { + if (element.type === 'custom:circle') { + outline = svgCreate('rect'); + + svgAttr(outline , { + x: `${element.x}`, + y: `${element.y}`, + cx: element.width / 2, + cy: element.height / 2, + r: 60, + fill: "none", + }); + } + console.log(outline); + + return false; +} + +const CustomOutlineModule = { + __init__: ['outlineProvider'], + __depends__: [ 'Outline' ], + outlineProvider: ['type', CustomOutlineProvider] +}; + +export default CustomOutlineModule; diff --git a/CustomPaletteModule.ts b/CustomPaletteModule.ts new file mode 100644 index 0000000..9c9eb51 --- /dev/null +++ b/CustomPaletteModule.ts @@ -0,0 +1,89 @@ +import ElementFactory from "diagram-js/lib/core/ElementFactory"; +import Palette from "diagram-js/lib/features/palette/Palette"; +import LassoTool from "diagram-js/lib/features/lasso-tool/LassoTool"; +import Create from "diagram-js/lib/features/create/Create"; +import GlobalConnect from "diagram-js/lib/features/global-connect/GlobalConnect"; + +function PalettePlugin (create: Create, + elementFactory:ElementFactory, + globalConnect: GlobalConnect, + lassoTool: LassoTool, + palette: Palette) { + palette.registerProvider({ + getPaletteEntries: () => ({ + 'hand-tool': { + group: 'tools', + className: 'icon-hand-tool', + title: 'Hand Tool', + action: { + click: function() { + //console.log("Hello"); + } + } + }, + 'lasso-tool': { + group: 'tools', + className: 'icon-lasso-tool', + title: 'Lasso Tool', + action: { + click: function(event) { + lassoTool.activateSelection(event as MouseEvent); + } + } + }, + 'tool-separator': { + group: 'tools', + separator: true, + action: {} + }, + 'create-shape': { + group: 'create', + className: 'icon-create-shape', + title: 'Create Shape', + action: { + click: function() { + var shape = elementFactory.createShape({ + width: 100, + height: 80, + canStartConnection:true + }); + console.log(shape.canStartConnection); + create.start(event, shape); + } + } + }, + 'create-device': { + group: 'create', + className: 'icon-create-shape', + title: 'Create Device', + action: { + click: function() { + var shape = elementFactory.createShape({ + width: 100, + height: 80, + canStartConnection:true, + type: 'custom:circle' + }); + console.log(shape.canStartConnection); + create.start(event, shape); + } + } + }, + 'create-connection': { + group: 'create', + className: 'icon-connect', + title: 'Create Connection', + action: { + click: (event) => { + globalConnect.start(event, false); + } + } + } + }) + }); +} + +export default { + __init__: [ 'palettePlugin' ], + palettePlugin: [ 'type', PalettePlugin ] + }; \ No newline at end of file diff --git a/CustomShapeRendererModule.ts b/CustomShapeRendererModule.ts new file mode 100644 index 0000000..8ffac93 --- /dev/null +++ b/CustomShapeRendererModule.ts @@ -0,0 +1,67 @@ +import BaseRenderer from 'diagram-js/lib/draw/BaseRenderer'; +import { assign } from 'min-dash'; +import { + append as svgAppend, + attr as svgAttr, + create as svgCreate +} from 'tiny-svg'; +import { data } from './AddObjectForm.svelte'; + + +const HIGH_PRIORITY = 1500; + +class CustomShapeRenderer extends BaseRenderer { + + static $inject = ['eventBus', 'styles']; + + constructor(eventBus: any, styles: any) { + super(eventBus, HIGH_PRIORITY); + this.styles = styles; + this.SHAPE_STYLE = styles.style({ fill: 'Canvas', stroke: 'CanvasText', strokeWidth: 2 }); + } + + canRender(element: any): boolean { + return element.type === 'custom:circle'; + } + + + drawShape(visuals, element, attrs): SVGElement { + console.log(data); + var circle = svgCreate('circle'); + + svgAttr(circle, { + cx: `${element.width / 2}`, + cy: `${element.height / 2 - 40}`, + r: '2.5mm', + fill: "none", + stroke: "CanvasText", + }); + + var line = svgCreate('line'); + svgAttr(line, { + x1: element.width / 2, + x2: element.width / 2, + y1: element.height/2, + y2: element.height/2 - 30 - 2, + stroke: "CanvasText", + + }) + + var g = svgCreate('g'); + svgAppend(g, circle); + svgAppend(g, line); + + svgAttr(g, assign({}, this.SHAPE_STYLE, attrs || {})); + svgAppend(visuals, g); + return g; + } +} + + +const CustomShapeRendererModule = { + __init__: ['customShapeRenderer'], + customShapeRenderer: ['type', CustomShapeRenderer] +}; + + +export default CustomShapeRendererModule; \ No newline at end of file diff --git a/Diagram.svelte b/Diagram.svelte new file mode 100644 index 0000000..7b7896b --- /dev/null +++ b/Diagram.svelte @@ -0,0 +1,28 @@ + + + + +
\ No newline at end of file diff --git a/ManhattanConnectionModule.ts b/ManhattanConnectionModule.ts new file mode 100644 index 0000000..05d16e1 --- /dev/null +++ b/ManhattanConnectionModule.ts @@ -0,0 +1,54 @@ + +import { connectPoints } from 'diagram-js/lib/layout/ManhattanLayout'; +import Modeling from 'diagram-js/lib/features/modeling/Modeling'; +import EventBus from 'diagram-js/lib/core/EventBus'; +import { Connection } from 'diagram-js/lib/model/Types'; + +export function updateWaypointsByManhattan (connection: Connection, modeling: Modeling ): void { + if (connection.source && connection.target) { + const x0 = connection.source.x + connection.source.width / 2; + const x1 = connection.target.x + connection.target.width / 2; + const y0 = connection.source.y + connection.source.height / 2; + const y1 = connection.target.y + connection.target.height / 2; + const x2 = (Math.abs(x0-x1) < 5) ? x0 : x1; + const y2 = (Math.abs(y0-y1) < 5) ? y0 : y1; + const waypoints = connectPoints( + { x: x0, y: y0 }, + { x: x2, y: y2 }); + + const hasChanged = JSON.stringify(connection.waypoints) != JSON.stringify(waypoints); + if (hasChanged) { + modeling.updateWaypoints(connection, waypoints) + } + } +} + +function ManhattanLayoutPlugin(eventBus: EventBus, modeling: Modeling) { + eventBus.on('commandStack.connection.create.executed', (event) => { + var connection = (event as any).element; + if (connection) { + updateWaypointsByManhattan(connection, modeling); + } + }); + + + eventBus.on("shape.move.end", (event) => { + var shape = (event as any).shape; + if (shape.incoming) { + shape.incoming.forEach((element: Connection) => { + updateWaypointsByManhattan(element, modeling); + }); + } + + if (shape.outgoing) { + shape.outgoing.forEach((element: Connection) => { + updateWaypointsByManhattan(element, modeling); + }); + } + }); +} + +export default { + __init__: [ 'manhattanLayoutPlugin' ], + manhattanLayoutPlugin: [ 'type', ManhattanLayoutPlugin ] +}; diff --git a/StyleModule.ts b/StyleModule.ts new file mode 100644 index 0000000..8bff962 --- /dev/null +++ b/StyleModule.ts @@ -0,0 +1,11 @@ +export default { + __init__: ['customRenderer'], + customRenderer: [ + 'type', + function (defaultRenderer: any) { + defaultRenderer.CONNECTION_STYLE = { fill: 'none', strokeWidth: 5, stroke: 'CanvasText' }; + defaultRenderer.SHAPE_STYLE = { fill: 'Canvas', stroke: 'CanvasText', strokeWidth: 2 }; + defaultRenderer.FRAME_STYLE = { fill: 'none', stroke: 'CanvasText', strokeDasharray: 4, strokeWidth: 2 }; + } + ] +}; \ No newline at end of file diff --git a/editor.ts b/editor.ts new file mode 100644 index 0000000..4c823b3 --- /dev/null +++ b/editor.ts @@ -0,0 +1,64 @@ +import Diagram from 'diagram-js'; +import ConnectModule from 'diagram-js/lib/features/connect'; +import ContextPadModule from 'diagram-js/lib/features/context-pad'; +import CreateModule from 'diagram-js/lib/features/create'; +import LassoToolModule from 'diagram-js/lib/features/lasso-tool'; +import ModelingModule from 'diagram-js/lib/features/modeling'; +import MoveCanvasModule from 'diagram-js/lib/navigation/movecanvas'; +import MoveModule from 'diagram-js/lib/features/move'; +import OutlineModule from 'diagram-js/lib/features/outline'; +import PaletteModule from 'diagram-js/lib/features/palette'; +import ResizeModule from 'diagram-js/lib/features/resize'; +import RulesModule from 'diagram-js/lib/features/rules'; +import SelectionModule from 'diagram-js/lib/features/selection'; +import GlobalConnectModule from 'diagram-js/lib/features/global-connect'; +import ZoomScrollModule from 'diagram-js/lib/navigation/zoomscroll'; +import BendpointMoveModule from 'diagram-js/lib/features/bendpoints'; +import StyleModule from './StyleModule.ts'; +import ManhattanConnectionModule from './ManhattanConnectionModule.ts'; +import CustomPaletteModule from './CustomPaletteModule.ts'; +import CustomGlobalConnectRulesModule from './CustomGlobalConnectRulesModule.ts'; +import CustomShapeRendererModule from './CustomShapeRendererModule.ts'; +import CustomOutlineModule from './CustomOutlineModule.ts'; + +interface EditorOptions { + container: HTMLElement; + additionalModules?: Array; +} + +export default function Editor(options: EditorOptions): Diagram { + const { container } = options; + + const modules = [ + BendpointMoveModule + , ConnectModule + , ContextPadModule + , CreateModule + , GlobalConnectModule + , LassoToolModule + , ModelingModule + , MoveCanvasModule + , MoveModule + //, OutlineModule + , PaletteModule + //, ResizeModule + , RulesModule + , SelectionModule + , ZoomScrollModule + , StyleModule + , ManhattanConnectionModule + , CustomPaletteModule + , CustomGlobalConnectRulesModule + , CustomShapeRendererModule + , CustomOutlineModule + ]; + + + console.log(container); + return new Diagram({ + canvas: { + container + }, + modules: modules + }); +} diff --git a/main.ts b/main.ts new file mode 100644 index 0000000..56f8d21 --- /dev/null +++ b/main.ts @@ -0,0 +1,13 @@ +import { mount } from 'svelte' +import App from './App.svelte'; + +const target = document.getElementById('app'); + +if (!target) { + throw new Error("Target element with ID 'app' not found in DOM."); +} + +const app = mount(App, { + target: document.getElementById('app')!, +}) +export default app; \ No newline at end of file diff --git a/style.css b/style.css new file mode 100644 index 0000000..09016c3 --- /dev/null +++ b/style.css @@ -0,0 +1,143 @@ +:root { + color: CanvasText; + background-color: Canvas; +} + +button { + background-color: ButtonFace; + color: ButtonText; + border-color: ButtonBorder; +} +button:hover { + border-color: AccentColor; +} +button:focus { + outline: 4px auto Highlight; +} + +.anchor { + background-color: AccentColor; + color: AccentColorText; + visibility: hidden; + transition: visibility 0.2s ease-in-out; +} + +@media (prefers-color-scheme: dark) { + :root { + color: CanvasText; + background-color: Canvas; + } +} + + +:root { + font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif; + line-height: 1.5; + font-weight: 400; + + --primary-color: CanvasText; + --hover-color: #747bff; + --background-color: #242424; + --background-light: #ffffff; + + color-scheme: light dark; + color: rgba(255, 255, 255, 0.87); + background-color: var(--background-color); + + font-synthesis: none; + text-rendering: optimizeLegibility; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} + +body { + margin: 0; + place-items: center; + min-width: 320px; + min-height: 100vh; +} + +button { + border-radius: 8px; + border: 1px solid transparent; + padding: 0.6em 1.2em; + font-size: 1em; + font-weight: 500; + font-family: inherit; + background-color: var(--background-color); + color: inherit; + cursor: pointer; + transition: border-color 0.25s; +} +button:hover { + border-color: var(--primary-color); +} +button:focus { + outline: 4px auto -webkit-focus-ring-color; +} + +.svgContainer { + overflow: hidden; + display: flex; + align-items: center; + justify-content: center; + width: 100%; + height: 100%; +} + +@media (prefers-color-scheme: light) { + :root { + color: var(--background-color); + background-color: var(--background-light); + } + button { + background-color: #f9f9f9; + } + button:hover { + border-color: var(--hover-color); + } +} + +.svgContainer { + border: solid CanvasText 1px; +} + +.icon-hand-tool { + background: url('../resources/icon-hand-tool.svg'); +} + +.icon-lasso-tool { + background: url('../resources/icon-lasso-tool.svg'); +} + +.icon-hand-tool:hover { + background: url('../resources/hovered/icon-hand-tool.svg'); +} + +.icon-lasso-tool:hover { + background: url('../resources/hovered/icon-lasso-tool.svg'); +} + +.pad-icon-remove { + background: url('data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20fill%3D%22none%22%20stroke%3D%22%23000%22%20stroke-width%3D%221.5%22%20width%3D%2246%22%20height%3D%2246%22%3E%3Cline%20x1%3D%225%22%20y1%3D%225%22%20x2%3D%2215%22%20y2%3D%2215%22%2F%3E%3Cline%20x1%3D%2215%22%20y1%3D%225%22%20x2%3D%225%22%20y2%3D%2215%22%2F%3E%3C%2Fsvg%3E') !important; +} + +.pad-icon-connect { + background: url('data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20fill%3D%22none%22%20stroke%3D%22%23000%22%20stroke-width%3D%221.5%22%20width%3D%2246%22%20height%3D%2246%22%3E%3Cline%20x1%3D%2215%22%20y1%3D%225%22%20x2%3D%225%22%20y2%3D%2215%22%2F%3E%3C%2Fsvg%3E') !important; +} + +.icon-create-shape { + background: url('../resources/icon-create-rect.svg'); +} + +.icon-create-shape:hover { + background: url('../resources/hovered/icon-create-rect.svg'); +} + +.icon-connect { + background: url('../resources/icon-connect.svg'); +} + +.icon-connect:hover { + background: url('../resources/hovered/icon-connect.svg'); +} \ No newline at end of file diff --git a/vite-env.d.ts b/vite-env.d.ts new file mode 100644 index 0000000..11f02fe --- /dev/null +++ b/vite-env.d.ts @@ -0,0 +1 @@ +///