initial commit

This commit is contained in:
Gregory Bednov 2025-01-13 15:00:11 +03:00
commit 3751b8ddb0
23 changed files with 2048 additions and 0 deletions

15
src/App.svelte Normal file
View file

@ -0,0 +1,15 @@
<script>
import Diagram from './Diagram.svelte'; // Подключаем компонент с diagram-js
</script>
<main>
<h1>FSA Editor</h1>
<Diagram />
</main>
<style>
main {
font-family: Arial, sans-serif;
padding: 1rem;
}
</style>

View file

@ -0,0 +1,61 @@
function PalettePlugin(palette, lassoTool, create, elementFactory, globalConnect) {
palette.registerProvider({
getPaletteEntries: () => ({
'hand-tool': {
group: 'tools',
className: 'icon-hand-tool',
title: 'Hand Tool',
action: {
click: function(event) {
console.log("Hello");
}
}
},
'lasso-tool': {
group: 'tools',
className: 'icon-lasso-tool',
title: 'Lasso Tool',
action: {
click: function(event) {
lassoTool.activateSelection(event);
}
}
},
'tool-separator': {
group: 'tools',
separator: true
},
'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-connection': {
group: 'create',
className: 'connection-create-shape',
title: 'Create Connection',
action: {
click: (event) => {
globalConnect.start(event);
}
}
}
})
});
}
export default {
__init__: [ 'palettePlugin' ],
palettePlugin: [ 'type', PalettePlugin ]
};

102
src/Diagram.svelte Normal file
View file

@ -0,0 +1,102 @@
<script lang="ts">
import { connectPoints } from 'diagram-js/lib/layout/ManhattanLayout';
import { onMount } from "svelte";
import Diagram from "diagram-js";
import Canvas from "diagram-js/lib/core/Canvas";
import LassoTool from "diagram-js/lib/features/lasso-tool"
import Editor from './editor.ts'
import 'diagram-js/assets/diagram-js.css';
let container: HTMLDivElement | null = null;
function createAction(type, group, className, title, options) {
function createListener(event) {
var shape = elementFactory.createShape(assign({ type: type }, options));
create.start(event, shape);
}
return {
group: group,
className: className,
title: title,
action: {
dragstart: createListener,
click: createListener
}
};
}
onMount(() => {
if (container === null) return;
const diagram = new Editor({ container });
const canvas = diagram.get<Canvas>("canvas");
const elementFactory = diagram.get('elementFactory');
var root = elementFactory.createRoot();
canvas.setRootElement(root);
var shape1 = elementFactory.createShape({
x: 150,
y: 100,
width: 100,
height: 80
});
canvas.addShape(shape1, root);
var shape2 = elementFactory.createShape({
x: 290,
y: 220,
width: 100,
height: 80
});
canvas.addShape(shape2, root);
var shape3 = elementFactory.createShape({
x: 450,
y: 80,
width: 100,
height: 80
});
canvas.addShape(shape3, root);
var shape4 = elementFactory.createShape({
x: 425,
y: 50,
width: 300,
height: 200,
isFrame: true
});
canvas.addShape(shape4, root);
const manhattanLayout = connectPoints(
{ x: shape1.x + shape1.width / 2, y: shape1.y + shape1.height / 2 }, // Начальная точка
{ x: shape2.x + shape2.width / 2, y: shape2.y + shape2.height / 2 } // Конечная точка
);
var connection1 = elementFactory.createConnection({
waypoints: manhattanLayout,
source: shape1,
target: shape2
});
canvas.addConnection(connection1, root);
});
</script>
<style>
.container {
width: 100%;
height: 500px;
border: 1px solid #aaa;
}
</style>
<div bind:this={container} class="container"></div>

View file

@ -0,0 +1,32 @@
import { connectPoints } from 'diagram-js/lib/layout/ManhattanLayout';
function ManhattanLayoutPlugin(eventBus, modeling) {
eventBus.on('connection.changed', (event) => {
var connection = event.element;
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)
}
}
});
}
// export as module
export default {
__init__: [ 'manhattanLayoutPlugin' ],
manhattanLayoutPlugin: [ 'type', ManhattanLayoutPlugin ]
};

11
src/StyleModule.ts Normal file
View file

@ -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 };
}
]
};

View file

@ -0,0 +1,55 @@
const CustomConnectionModule = {
__init__: [
[
'eventBus',
'modeling',
function (eventBus: any, modeling: any) {
console.log('CustomConnectionModule initialized');
// Регистрируем обработчик событий
eventBus.on('connection.changed', (event: any) => {
const connection = event.element;
if (connection.source && connection.target) {
const waypoints = calculateWaypoints(connection);
// Проверяем изменения
if (JSON.stringify(connection.waypoints) != JSON.stringify(waypoints)) {
modeling.updateWaypoints(connection, waypoints);
console.log('Waypoints updated', waypoints);
}
}
});
}
]
]
};
/**
* Функция для вычисления новых точек маршрута соединений.
*/
function calculateWaypoints(connection: any): { x: number; y: number }[] {
const x0 = connection.source.x + connection.source.width / 2;
const y0 = connection.source.y + connection.source.height / 2;
const x1 = connection.target.x + connection.target.width / 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;
return connectPoints({ x: x0, y: y0 }, { x: x2, y: y2 });
}
/**
* Функция для соединения двух точек.
*/
function connectPoints(
start: { x: number; y: number },
end: { x: number; y: number }
): { x: number; y: number }[] {
return [start, end];
}
export default CustomConnectionModule;

58
src/editor.ts Normal file
View file

@ -0,0 +1,58 @@
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 AlignElementsModule from 'diagram-js/lib/features/align-elements';
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 CustomConnectionModule from './CustomConnectionModule';
import StyleModule from './StyleModule.ts';
import ManhattanConnectionModule from './ManhattanConnectionModule.ts';
import CustomPaletteModule from './CustomPaletteModule.ts';
interface EditorOptions {
container: HTMLElement;
additionalModules?: Array<any>;
}
export default function Editor(options: EditorOptions): Diagram {
const { container, additionalModules = [] } = options;
const modules = [
BendpointMoveModule
, ConnectModule
, ContextPadModule
, CreateModule
, GlobalConnectModule
, LassoToolModule
, ModelingModule
, MoveCanvasModule
, MoveModule
, OutlineModule
, PaletteModule
, ResizeModule
, RulesModule
, SelectionModule
, ZoomScrollModule
, StyleModule
, ManhattanConnectionModule
, CustomPaletteModule
];
return new Diagram({
canvas: {
container
},
modules: modules
});
}

14
src/main.ts Normal file
View file

@ -0,0 +1,14 @@
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;

135
src/style.css Normal file
View file

@ -0,0 +1,135 @@
: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');
}

1
src/vite-env.d.ts vendored Normal file
View file

@ -0,0 +1 @@
/// <reference types="vite/client" />