@sequent-org/moodboard 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (123) hide show
  1. package/package.json +44 -0
  2. package/src/assets/icons/README.md +105 -0
  3. package/src/assets/icons/attachments.svg +3 -0
  4. package/src/assets/icons/clear.svg +5 -0
  5. package/src/assets/icons/comments.svg +3 -0
  6. package/src/assets/icons/emoji.svg +6 -0
  7. package/src/assets/icons/frame.svg +3 -0
  8. package/src/assets/icons/image.svg +3 -0
  9. package/src/assets/icons/note.svg +3 -0
  10. package/src/assets/icons/pan.svg +3 -0
  11. package/src/assets/icons/pencil.svg +3 -0
  12. package/src/assets/icons/redo.svg +3 -0
  13. package/src/assets/icons/select.svg +9 -0
  14. package/src/assets/icons/shapes.svg +3 -0
  15. package/src/assets/icons/text-add.svg +3 -0
  16. package/src/assets/icons/topbar/README.md +39 -0
  17. package/src/assets/icons/topbar/grid-cross.svg +6 -0
  18. package/src/assets/icons/topbar/grid-dot.svg +3 -0
  19. package/src/assets/icons/topbar/grid-line.svg +3 -0
  20. package/src/assets/icons/topbar/grid-off.svg +3 -0
  21. package/src/assets/icons/topbar/paint.svg +3 -0
  22. package/src/assets/icons/undo.svg +3 -0
  23. package/src/core/ApiClient.js +309 -0
  24. package/src/core/EventBus.js +42 -0
  25. package/src/core/HistoryManager.js +261 -0
  26. package/src/core/KeyboardManager.js +710 -0
  27. package/src/core/PixiEngine.js +439 -0
  28. package/src/core/SaveManager.js +381 -0
  29. package/src/core/StateManager.js +64 -0
  30. package/src/core/commands/BaseCommand.js +68 -0
  31. package/src/core/commands/CopyObjectCommand.js +44 -0
  32. package/src/core/commands/CreateObjectCommand.js +46 -0
  33. package/src/core/commands/DeleteObjectCommand.js +146 -0
  34. package/src/core/commands/EditFileNameCommand.js +107 -0
  35. package/src/core/commands/GroupMoveCommand.js +47 -0
  36. package/src/core/commands/GroupReorderZCommand.js +74 -0
  37. package/src/core/commands/GroupResizeCommand.js +37 -0
  38. package/src/core/commands/GroupRotateCommand.js +41 -0
  39. package/src/core/commands/MoveObjectCommand.js +89 -0
  40. package/src/core/commands/PasteObjectCommand.js +103 -0
  41. package/src/core/commands/ReorderZCommand.js +45 -0
  42. package/src/core/commands/ResizeObjectCommand.js +135 -0
  43. package/src/core/commands/RotateObjectCommand.js +70 -0
  44. package/src/core/commands/index.js +14 -0
  45. package/src/core/events/Events.js +147 -0
  46. package/src/core/index.js +1632 -0
  47. package/src/core/rendering/GeometryUtils.js +89 -0
  48. package/src/core/rendering/HitTestManager.js +186 -0
  49. package/src/core/rendering/LayerManager.js +137 -0
  50. package/src/core/rendering/ObjectRenderer.js +363 -0
  51. package/src/core/rendering/PixiRenderer.js +140 -0
  52. package/src/core/rendering/index.js +9 -0
  53. package/src/grid/BaseGrid.js +164 -0
  54. package/src/grid/CrossGrid.js +75 -0
  55. package/src/grid/DotGrid.js +148 -0
  56. package/src/grid/GridFactory.js +173 -0
  57. package/src/grid/LineGrid.js +115 -0
  58. package/src/index.js +2 -0
  59. package/src/moodboard/ActionHandler.js +114 -0
  60. package/src/moodboard/DataManager.js +114 -0
  61. package/src/moodboard/MoodBoard.js +359 -0
  62. package/src/moodboard/WorkspaceManager.js +103 -0
  63. package/src/objects/BaseObject.js +1 -0
  64. package/src/objects/CommentObject.js +115 -0
  65. package/src/objects/DrawingObject.js +114 -0
  66. package/src/objects/EmojiObject.js +98 -0
  67. package/src/objects/FileObject.js +318 -0
  68. package/src/objects/FrameObject.js +127 -0
  69. package/src/objects/ImageObject.js +72 -0
  70. package/src/objects/NoteObject.js +227 -0
  71. package/src/objects/ObjectFactory.js +61 -0
  72. package/src/objects/ShapeObject.js +134 -0
  73. package/src/objects/StampObject.js +0 -0
  74. package/src/objects/StickerObject.js +0 -0
  75. package/src/objects/TextObject.js +123 -0
  76. package/src/services/BoardService.js +85 -0
  77. package/src/services/FileUploadService.js +398 -0
  78. package/src/services/FrameService.js +138 -0
  79. package/src/services/ImageUploadService.js +246 -0
  80. package/src/services/ZOrderManager.js +50 -0
  81. package/src/services/ZoomPanController.js +78 -0
  82. package/src/src.7z +0 -0
  83. package/src/src.zip +0 -0
  84. package/src/src2.zip +0 -0
  85. package/src/tools/AlignmentGuides.js +326 -0
  86. package/src/tools/BaseTool.js +257 -0
  87. package/src/tools/ResizeHandles.js +381 -0
  88. package/src/tools/ToolManager.js +580 -0
  89. package/src/tools/board-tools/PanTool.js +43 -0
  90. package/src/tools/board-tools/ZoomTool.js +393 -0
  91. package/src/tools/object-tools/DrawingTool.js +404 -0
  92. package/src/tools/object-tools/PlacementTool.js +1005 -0
  93. package/src/tools/object-tools/SelectTool.js +2183 -0
  94. package/src/tools/object-tools/TextTool.js +416 -0
  95. package/src/tools/object-tools/selection/BoxSelectController.js +105 -0
  96. package/src/tools/object-tools/selection/GeometryUtils.js +101 -0
  97. package/src/tools/object-tools/selection/GroupDragController.js +61 -0
  98. package/src/tools/object-tools/selection/GroupResizeController.js +90 -0
  99. package/src/tools/object-tools/selection/GroupRotateController.js +61 -0
  100. package/src/tools/object-tools/selection/HandlesSync.js +96 -0
  101. package/src/tools/object-tools/selection/ResizeController.js +68 -0
  102. package/src/tools/object-tools/selection/RotateController.js +58 -0
  103. package/src/tools/object-tools/selection/SelectionModel.js +42 -0
  104. package/src/tools/object-tools/selection/SimpleDragController.js +45 -0
  105. package/src/ui/CommentPopover.js +187 -0
  106. package/src/ui/ContextMenu.js +340 -0
  107. package/src/ui/FilePropertiesPanel.js +298 -0
  108. package/src/ui/FramePropertiesPanel.js +462 -0
  109. package/src/ui/HtmlHandlesLayer.js +778 -0
  110. package/src/ui/HtmlTextLayer.js +279 -0
  111. package/src/ui/MapPanel.js +290 -0
  112. package/src/ui/NotePropertiesPanel.js +502 -0
  113. package/src/ui/SaveStatus.js +250 -0
  114. package/src/ui/TextPropertiesPanel.js +911 -0
  115. package/src/ui/Toolbar.js +1118 -0
  116. package/src/ui/Topbar.js +220 -0
  117. package/src/ui/ZoomPanel.js +116 -0
  118. package/src/ui/styles/workspace.css +854 -0
  119. package/src/utils/colors.js +0 -0
  120. package/src/utils/geometry.js +0 -0
  121. package/src/utils/iconLoader.js +270 -0
  122. package/src/utils/objectIdGenerator.js +17 -0
  123. package/src/utils/topbarIconLoader.js +114 -0
@@ -0,0 +1,89 @@
1
+ /**
2
+ * Утилиты для геометрических вычислений
3
+ * Вынесены из PixiEngine для упрощения основного класса
4
+ */
5
+ export class GeometryUtils {
6
+ /**
7
+ * Вычисляет расстояние от точки до отрезка.
8
+ * Точка - то место куда кликнул пользователь.
9
+ * Отрезок - нарисованная линия.
10
+ * Возвращает - 0 или больше 0, если 0 то клик по линии.
11
+ *
12
+ * @param {number} px - X координата точки
13
+ * @param {number} py - Y координата точки
14
+ * @param {number} ax - X координата начала отрезка
15
+ * @param {number} ay - Y координата начала отрезка
16
+ * @param {number} bx - X координата конца отрезка
17
+ * @param {number} by - Y координата конца отрезка
18
+ * @returns {number} Расстояние от точки до отрезка
19
+ */
20
+ static distancePointToSegment(px, py, ax, ay, bx, by) {
21
+ const vectorABx = bx - ax;
22
+ const vectorABy = by - ay;
23
+ const vectorAPx = px - ax;
24
+ const vectorAPy = py - ay;
25
+ const squaredLengthAB = vectorABx * vectorABx + vectorABy * vectorABy;
26
+
27
+ if (squaredLengthAB === 0) {
28
+ return Math.hypot(px - ax, py - ay);
29
+ }
30
+
31
+ let t = (vectorAPx * vectorABx + vectorAPy * vectorABy) / squaredLengthAB;
32
+ t = Math.max(0, Math.min(1, t));
33
+
34
+ const closestX = ax + t * vectorABx;
35
+ const closestY = ay + t * vectorABy;
36
+
37
+ return Math.hypot(px - closestX, py - closestY);
38
+ }
39
+
40
+ /**
41
+ * Проверяет, находится ли точка внутри прямоугольника
42
+ * Точка - то место куда кликнул пользователь.
43
+ *
44
+ * @param {number} x - X координата точки
45
+ * @param {number} y - Y координата точки
46
+ * @param {number} rectX - X координата левого верхнего угла прямоугольника
47
+ * @param {number} rectY - Y координата левого верхнего угла прямоугольника
48
+ * @param {number} rectWidth - Ширина прямоугольника
49
+ * @param {number} rectHeight - Высота прямоугольника
50
+ * @returns {boolean} true если точка внутри прямоугольника
51
+ */
52
+ static pointInRect(x, y, rectX, rectY, rectWidth, rectHeight) {
53
+ return x >= rectX && x <= rectX + rectWidth &&
54
+ y >= rectY && y <= rectY + rectHeight;
55
+ }
56
+
57
+ /**
58
+ * Вычисляет центр прямоугольника
59
+ * @param {number} x - X координата левого верхнего угла
60
+ * @param {number} y - Y координата левого верхнего угла
61
+ * @param {number} width - Ширина
62
+ * @param {number} height - Высота
63
+ * @returns {{x: number, y: number}} Координаты центра
64
+ */
65
+ static getRectCenter(x, y, width, height) {
66
+ return {
67
+ x: x + width / 2,
68
+ y: y + height / 2
69
+ };
70
+ }
71
+
72
+ /**
73
+ * Конвертирует градусы в радианы
74
+ * @param {number} degrees - Угол в градусах
75
+ * @returns {number} Угол в радианах
76
+ */
77
+ static degreesToRadians(degrees) {
78
+ return degrees * Math.PI / 180;
79
+ }
80
+
81
+ /**
82
+ * Конвертирует радианы в градусы
83
+ * @param {number} radians - Угол в радианах
84
+ * @returns {number} Угол в градусах
85
+ */
86
+ static radiansToDegrees(radians) {
87
+ return radians * 180 / Math.PI;
88
+ }
89
+ }
@@ -0,0 +1,186 @@
1
+ import * as PIXI from 'pixi.js';
2
+ import { GeometryUtils } from './GeometryUtils.js';
3
+
4
+ /**
5
+ * Менеджер hit-testing объектов
6
+ * Отвечает за определение объектов под курсором
7
+ */
8
+ export class HitTestManager {
9
+ constructor(layerManager, objectsMap) {
10
+ this.layerManager = layerManager;
11
+ this.objectsMap = objectsMap; // Map<id, pixiObject>
12
+ }
13
+
14
+ /**
15
+ * Поиск объекта в указанной позиции
16
+ * @param {number} x - X координата в экранных координатах
17
+ * @param {number} y - Y координата в экранных координатах
18
+ * @returns {{type: string, object?: string, pixiObject?: PIXI.DisplayObject}} Результат hit-test
19
+ */
20
+ hitTest(x, y) {
21
+ const point = new PIXI.Point(x, y);
22
+ const worldLayer = this.layerManager.getWorldLayer();
23
+
24
+ if (!worldLayer) {
25
+ return { type: 'empty' };
26
+ }
27
+
28
+ // Проходим по объектам в worldLayer от верхних к нижним
29
+ for (let i = worldLayer.children.length - 1; i >= 0; i--) {
30
+ const child = worldLayer.children[i];
31
+
32
+ if (this._testObjectHit(child, point)) {
33
+ return this._createHitResult(child);
34
+ }
35
+ }
36
+
37
+ return { type: 'empty' };
38
+ }
39
+
40
+ /**
41
+ * Тестирует попадание в конкретный объект
42
+ * @param {PIXI.DisplayObject} pixiObject - PIXI объект для тестирования
43
+ * @param {PIXI.Point} point - Точка для тестирования
44
+ * @returns {boolean} true если точка попадает в объект
45
+ * @private
46
+ */
47
+ _testObjectHit(pixiObject, point) {
48
+ // Основной hit-test через PIXI
49
+ if (pixiObject.containsPoint && pixiObject.containsPoint(point)) {
50
+ return true;
51
+ }
52
+
53
+ // Дополнительный hit-test для нарисованных линий
54
+ return this._testDrawingHit(pixiObject, point);
55
+ }
56
+
57
+ /**
58
+ * Специальный hit-test для нарисованных линий
59
+ * @param {PIXI.DisplayObject} pixiObject - PIXI объект
60
+ * @param {PIXI.Point} point - Точка для тестирования
61
+ * @returns {boolean} true если точка попадает в линию
62
+ * @private
63
+ */
64
+ _testDrawingHit(pixiObject, point) {
65
+ const meta = pixiObject._mb;
66
+
67
+ // Проверяем только drawing объекты
68
+ if (!meta || meta.type !== 'drawing' || !pixiObject.toLocal) {
69
+ return false;
70
+ }
71
+
72
+ // Переводим точку в локальные координаты объекта
73
+ const local = pixiObject.toLocal(point);
74
+ const props = meta.properties || {};
75
+ const points = Array.isArray(props.points) ? props.points : [];
76
+
77
+ if (points.length < 2) {
78
+ return false;
79
+ }
80
+
81
+ // Оценка текущего масштаба относительно базовых размеров
82
+ const baseW = props.baseWidth || 1;
83
+ const baseH = props.baseHeight || 1;
84
+ const bounds = pixiObject.getBounds();
85
+ const scaleX = baseW ? (bounds.width / baseW) : 1;
86
+ const scaleY = baseH ? (bounds.height / baseH) : 1;
87
+
88
+ // Толщина линии с учётом режима маркера
89
+ const baseWidth = props.strokeWidth || 2;
90
+ const lineWidth = (props.mode === 'marker' ? baseWidth * 2 : baseWidth);
91
+ const threshold = Math.max(4, lineWidth / 2 + 3);
92
+
93
+ // Проверяем расстояние до каждого сегмента
94
+ for (let j = 0; j < points.length - 1; j++) {
95
+ const ax = points[j].x * scaleX;
96
+ const ay = points[j].y * scaleY;
97
+ const bx = points[j + 1].x * scaleX;
98
+ const by = points[j + 1].y * scaleY;
99
+
100
+ const dist = GeometryUtils.distancePointToSegment(
101
+ local.x, local.y, ax, ay, bx, by
102
+ );
103
+
104
+ if (dist <= threshold) {
105
+ return true;
106
+ }
107
+ }
108
+
109
+ return false;
110
+ }
111
+
112
+ /**
113
+ * Создает результат hit-test
114
+ * @param {PIXI.DisplayObject} pixiObject - PIXI объект
115
+ * @returns {{type: string, object: string, pixiObject: PIXI.DisplayObject}} Результат
116
+ * @private
117
+ */
118
+ _createHitResult(pixiObject) {
119
+ // Находим ID объекта
120
+ for (const [objectId, obj] of this.objectsMap.entries()) {
121
+ if (obj === pixiObject) {
122
+ return {
123
+ type: 'object',
124
+ object: objectId,
125
+ pixiObject: pixiObject
126
+ };
127
+ }
128
+ }
129
+
130
+ // Если ID не найден, возвращаем объект без ID
131
+ return {
132
+ type: 'object',
133
+ object: null,
134
+ pixiObject: pixiObject
135
+ };
136
+ }
137
+
138
+ /**
139
+ * Получить все объекты в указанной области
140
+ * @param {number} x - X координата левого верхнего угла
141
+ * @param {number} y - Y координата левого верхнего угла
142
+ * @param {number} width - Ширина области
143
+ * @param {number} height - Высота области
144
+ * @returns {Array} Массив объектов в области
145
+ */
146
+ hitTestArea(x, y, width, height) {
147
+ const worldLayer = this.layerManager.getWorldLayer();
148
+ if (!worldLayer) return [];
149
+
150
+ const results = [];
151
+ const worldObjects = this.layerManager.getWorldObjects();
152
+
153
+ for (const pixiObject of worldObjects) {
154
+ if (this._testObjectInArea(pixiObject, x, y, width, height)) {
155
+ const result = this._createHitResult(pixiObject);
156
+ if (result.object) {
157
+ results.push(result);
158
+ }
159
+ }
160
+ }
161
+
162
+ return results;
163
+ }
164
+
165
+ /**
166
+ * Проверяет, находится ли объект в указанной области
167
+ * @param {PIXI.DisplayObject} pixiObject - PIXI объект
168
+ * @param {number} areaX - X координата области
169
+ * @param {number} areaY - Y координата области
170
+ * @param {number} areaWidth - Ширина области
171
+ * @param {number} areaHeight - Высота области
172
+ * @returns {boolean} true если объект в области
173
+ * @private
174
+ */
175
+ _testObjectInArea(pixiObject, areaX, areaY, areaWidth, areaHeight) {
176
+ const bounds = pixiObject.getBounds();
177
+
178
+ return GeometryUtils.pointInRect(
179
+ bounds.x, bounds.y,
180
+ areaX, areaY, areaWidth, areaHeight
181
+ ) || GeometryUtils.pointInRect(
182
+ bounds.x + bounds.width, bounds.y + bounds.height,
183
+ areaX, areaY, areaWidth, areaHeight
184
+ );
185
+ }
186
+ }
@@ -0,0 +1,137 @@
1
+ import * as PIXI from 'pixi.js';
2
+
3
+ /**
4
+ * Менеджер слоев PIXI приложения
5
+ * Управляет созданием и организацией слоев
6
+ */
7
+ export class LayerManager {
8
+ constructor(pixiApp) {
9
+ this.app = pixiApp;
10
+ this.layers = new Map();
11
+ this._createLayers();
12
+ }
13
+
14
+ /**
15
+ * Создает основные слои приложения
16
+ * @private
17
+ */
18
+ _createLayers() {
19
+ // Слой сетки (не двигается)
20
+ this.gridLayer = new PIXI.Container();
21
+ this.gridLayer.name = 'gridLayer';
22
+ this.gridLayer.zIndex = 0;
23
+ this.app.stage.addChild(this.gridLayer);
24
+
25
+ // Слой мира с объектами (двигается)
26
+ this.worldLayer = new PIXI.Container();
27
+ this.worldLayer.name = 'worldLayer';
28
+ this.worldLayer.zIndex = 1;
29
+ this.worldLayer.sortableChildren = true;
30
+ this.app.stage.addChild(this.worldLayer);
31
+
32
+ // Сохраняем ссылки на слои
33
+ this.layers.set('grid', this.gridLayer);
34
+ this.layers.set('world', this.worldLayer);
35
+ }
36
+
37
+ /**
38
+ * Получить слой по имени
39
+ * @param {string} layerName - Имя слоя
40
+ * @returns {PIXI.Container|null} Слой или null если не найден
41
+ */
42
+ getLayer(layerName) {
43
+ return this.layers.get(layerName) || null;
44
+ }
45
+
46
+ /**
47
+ * Получить слой сетки
48
+ * @returns {PIXI.Container} Слой сетки
49
+ */
50
+ getGridLayer() {
51
+ return this.gridLayer;
52
+ }
53
+
54
+ /**
55
+ * Получить слой мира
56
+ * @returns {PIXI.Container} Слой мира
57
+ */
58
+ getWorldLayer() {
59
+ return this.worldLayer;
60
+ }
61
+
62
+ /**
63
+ * Установить сетку в слой сетки
64
+ * @param {Object} gridInstance - Экземпляр сетки
65
+ */
66
+ setGrid(gridInstance) {
67
+ if (!this.gridLayer) return;
68
+
69
+ // Очищаем слой сетки
70
+ this.gridLayer.removeChildren();
71
+
72
+ if (gridInstance && gridInstance.getPixiObject) {
73
+ const gridObject = gridInstance.getPixiObject();
74
+ gridObject.zIndex = 0;
75
+ gridObject.x = 0;
76
+ gridObject.y = 0;
77
+ this.gridLayer.addChild(gridObject);
78
+ }
79
+ }
80
+
81
+ /**
82
+ * Добавить объект в слой мира
83
+ * @param {PIXI.DisplayObject} pixiObject - PIXI объект
84
+ */
85
+ addToWorldLayer(pixiObject) {
86
+ if (this.worldLayer) {
87
+ this.worldLayer.addChild(pixiObject);
88
+ }
89
+ }
90
+
91
+ /**
92
+ * Удалить объект из слоя мира
93
+ * @param {PIXI.DisplayObject} pixiObject - PIXI объект
94
+ */
95
+ removeFromWorldLayer(pixiObject) {
96
+ if (this.worldLayer && this.worldLayer.children.includes(pixiObject)) {
97
+ this.worldLayer.removeChild(pixiObject);
98
+ }
99
+ }
100
+
101
+ /**
102
+ * Получить все объекты в слое мира
103
+ * @returns {PIXI.DisplayObject[]} Массив объектов
104
+ */
105
+ getWorldObjects() {
106
+ return this.worldLayer ? this.worldLayer.children : [];
107
+ }
108
+
109
+ /**
110
+ * Очистить слой мира
111
+ */
112
+ clearWorldLayer() {
113
+ if (this.worldLayer) {
114
+ this.worldLayer.removeChildren();
115
+ }
116
+ }
117
+
118
+ /**
119
+ * Установить Z-индекс для объекта
120
+ * @param {PIXI.DisplayObject} pixiObject - PIXI объект
121
+ * @param {number} zIndex - Z-индекс
122
+ */
123
+ setObjectZIndex(pixiObject, zIndex) {
124
+ if (pixiObject) {
125
+ pixiObject.zIndex = zIndex;
126
+ }
127
+ }
128
+
129
+ /**
130
+ * Сортировать объекты в слое мира по Z-индексу
131
+ */
132
+ sortWorldLayer() {
133
+ if (this.worldLayer) {
134
+ this.worldLayer.sortChildren();
135
+ }
136
+ }
137
+ }