@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,462 @@
1
+ import { Events } from '../core/events/Events.js';
2
+
3
+ /**
4
+ * Панель свойств фрейма
5
+ * Отображается над выделенным фреймом
6
+ */
7
+ export class FramePropertiesPanel {
8
+ constructor(eventBus, container, core = null) {
9
+ this.eventBus = eventBus;
10
+ this.container = container;
11
+ this.core = core;
12
+ this.panel = null;
13
+ this.currentId = null;
14
+
15
+ this._attachEvents();
16
+ this._createPanel();
17
+ }
18
+
19
+ _attachEvents() {
20
+ // Показываем панель при изменении выделения
21
+ this.eventBus.on(Events.Tool.SelectionAdd, () => this.updateFromSelection());
22
+ this.eventBus.on(Events.Tool.SelectionRemove, () => this.updateFromSelection());
23
+ this.eventBus.on(Events.Tool.SelectionClear, () => this.hide());
24
+
25
+ // Скрываем панель при удалении объекта
26
+ this.eventBus.on(Events.Object.Deleted, (objectId) => {
27
+ if (this.currentId && objectId === this.currentId) this.hide();
28
+ });
29
+
30
+ // Обновляем позицию при любых изменениях (как в TextPropertiesPanel)
31
+ this.eventBus.on(Events.Tool.DragUpdate, () => this.reposition());
32
+ this.eventBus.on(Events.Tool.GroupDragUpdate, () => this.reposition());
33
+ this.eventBus.on(Events.Tool.ResizeUpdate, () => this.reposition());
34
+ this.eventBus.on(Events.Tool.RotateUpdate, () => this.reposition());
35
+
36
+ // Обновляем позицию при зуме/пане
37
+ this.eventBus.on(Events.UI.ZoomPercent, () => {
38
+ if (this.currentId) this.reposition();
39
+ });
40
+
41
+ this.eventBus.on(Events.Tool.PanUpdate, () => {
42
+ if (this.currentId) this.reposition();
43
+ });
44
+
45
+ // Скрываем панель при активации других инструментов
46
+ this.eventBus.on(Events.Tool.Activated, ({ tool }) => {
47
+ if (tool !== 'select') {
48
+ this.hide();
49
+ }
50
+ });
51
+ }
52
+
53
+ updateFromSelection() {
54
+ // Показываем только для одиночного выделения фрейма
55
+ const ids = this.core?.selectTool ? Array.from(this.core.selectTool.selectedObjects || []) : [];
56
+
57
+ if (!ids || ids.length !== 1) {
58
+ this.hide();
59
+ return;
60
+ }
61
+
62
+ const id = ids[0];
63
+
64
+ // Избегаем дублирования - если уже показываем панель для этого объекта
65
+ if (this.currentId === id && this.panel && this.panel.style.display !== 'none') {
66
+ return;
67
+ }
68
+
69
+ const pixi = this.core?.pixi?.objects?.get ? this.core.pixi.objects.get(id) : null;
70
+ const isFrame = !!(pixi && pixi._mb && pixi._mb.type === 'frame');
71
+
72
+ console.log('🖼️ FramePropertiesPanel: updateFromSelection - id=', id, 'isFrame=', isFrame);
73
+
74
+ if (isFrame) {
75
+ this.showFor(id);
76
+ } else {
77
+ this.hide();
78
+ }
79
+ }
80
+
81
+ showFor(objectId) {
82
+ console.log('🖼️ FramePropertiesPanel: Showing panel for objectId:', objectId);
83
+ this.currentId = objectId;
84
+ if (this.panel) {
85
+ this.panel.style.display = 'flex';
86
+ this.reposition();
87
+ }
88
+
89
+ // Обновляем контролы в соответствии с текущими свойствами объекта
90
+ this._updateControlsFromObject();
91
+ }
92
+
93
+ hide() {
94
+ this.currentId = null;
95
+ if (this.panel) {
96
+ this.panel.style.display = 'none';
97
+ }
98
+ }
99
+
100
+ _createPanel() {
101
+ const panel = document.createElement('div');
102
+ panel.className = 'frame-properties-panel';
103
+ Object.assign(panel.style, {
104
+ position: 'absolute',
105
+ display: 'none',
106
+ alignItems: 'center',
107
+ gap: '8px',
108
+ padding: '8px 12px',
109
+ backgroundColor: 'white',
110
+ border: '1px solid #e0e0e0',
111
+ borderRadius: '8px',
112
+ boxShadow: '0 2px 8px rgba(0, 0, 0, 0.15)',
113
+ fontSize: '14px',
114
+ fontFamily: 'Arial, sans-serif',
115
+ minWidth: '280px',
116
+ height: '60px',
117
+ zIndex: '10000'
118
+ });
119
+
120
+ // Создаем контролы для фрейма
121
+ this._createFrameControls(panel);
122
+
123
+ // Добавляем ID для удобной настройки через DevTools
124
+ panel.id = 'frame-properties-panel';
125
+
126
+ this.panel = panel;
127
+ this.container.appendChild(panel);
128
+ }
129
+
130
+ _updateControlsFromObject() {
131
+ // Пока ничего не делаем, так как панель пустая
132
+ // Здесь будет логика синхронизации контролов с объектом
133
+ }
134
+
135
+ reposition() {
136
+ if (!this.panel || !this.currentId || this.panel.style.display === 'none') {
137
+ return;
138
+ }
139
+
140
+ // Проверяем, что наш объект все еще выделен
141
+ const ids = this.core?.selectTool ? Array.from(this.core.selectTool.selectedObjects || []) : [];
142
+ if (!ids.includes(this.currentId)) {
143
+ this.hide();
144
+ return;
145
+ }
146
+
147
+ // Получаем позицию и размеры объекта
148
+ const posData = { objectId: this.currentId, position: null };
149
+ this.eventBus.emit(Events.Tool.GetObjectPosition, posData);
150
+
151
+ const sizeData = { objectId: this.currentId, size: null };
152
+ this.eventBus.emit(Events.Tool.GetObjectSize, sizeData);
153
+
154
+ if (!posData.position || !sizeData.size) {
155
+ return;
156
+ }
157
+
158
+ const { x, y } = posData.position;
159
+ const { width, height } = sizeData.size;
160
+
161
+ // Позиционируем панель НАД фреймом, но ниже ручек ресайза
162
+ const panelWidth = this.panel.offsetWidth || 200;
163
+ const panelHeight = this.panel.offsetHeight || 44;
164
+
165
+ const panelX = x + panelWidth/2.5; // по центру фрейма
166
+
167
+ // Пытаемся разместить панель над фреймом
168
+ let panelY = y ;
169
+
170
+ // Если панель уходит за верхнюю границу экрана, размещаем её ниже фрейма
171
+ // 10px ниже фрейма
172
+
173
+ console.log('🖼️ FramePropertiesPanel: Positioning above frame:', {
174
+ frameX: x, frameY: y, frameWidth: width, frameHeight: height,
175
+ panelX, panelY
176
+ });
177
+
178
+ this.panel.style.left = `${panelX}px`;
179
+ this.panel.style.top = `${panelY}px`;
180
+
181
+ console.log('🖼️ FramePropertiesPanel: Panel CSS applied:', {
182
+ left: this.panel.style.left,
183
+ top: this.panel.style.top
184
+ });
185
+ }
186
+
187
+ _createFrameControls(panel) {
188
+ // Контейнер для названия
189
+ const titleContainer = document.createElement('div');
190
+ Object.assign(titleContainer.style, {
191
+ display: 'flex',
192
+ alignItems: 'center',
193
+ gap: '8px',
194
+ padding: '8px 12px'
195
+ });
196
+
197
+ // Лейбл
198
+ const titleLabel = document.createElement('span');
199
+ titleLabel.textContent = 'Название:';
200
+ titleLabel.style.fontSize = '12px';
201
+ titleLabel.style.color = '#666';
202
+ titleLabel.style.minWidth = '60px';
203
+
204
+ // Поле ввода для названия
205
+ const titleInput = document.createElement('input');
206
+ titleInput.type = 'text';
207
+ titleInput.placeholder = 'Название фрейма';
208
+ Object.assign(titleInput.style, {
209
+ flex: '1',
210
+ padding: '4px 8px',
211
+ border: '1px solid #ddd',
212
+ borderRadius: '4px',
213
+ fontSize: '12px',
214
+ outline: 'none'
215
+ });
216
+
217
+ // Обработчик изменения названия
218
+ titleInput.addEventListener('input', () => {
219
+ if (this.currentId) {
220
+ this._changeFrameTitle(titleInput.value);
221
+ }
222
+ });
223
+
224
+ // Обработчик Enter для подтверждения
225
+ titleInput.addEventListener('keypress', (e) => {
226
+ if (e.key === 'Enter') {
227
+ titleInput.blur();
228
+ }
229
+ });
230
+
231
+ // Сохраняем ссылку на поле ввода
232
+ this.titleInput = titleInput;
233
+
234
+ titleContainer.appendChild(titleLabel);
235
+ titleContainer.appendChild(titleInput);
236
+
237
+ // Контейнер для цвета фона
238
+ const colorContainer = document.createElement('div');
239
+ Object.assign(colorContainer.style, {
240
+ display: 'flex',
241
+ alignItems: 'center',
242
+ gap: '8px',
243
+ padding: '8px 12px'
244
+ });
245
+
246
+ // Лейбл для цвета
247
+ const colorLabel = document.createElement('span');
248
+ colorLabel.textContent = 'Фон:';
249
+ colorLabel.style.fontSize = '12px';
250
+ colorLabel.style.color = '#666';
251
+ colorLabel.style.minWidth = '60px';
252
+
253
+ // Кнопка выбора цвета
254
+ const colorButton = document.createElement('button');
255
+ Object.assign(colorButton.style, {
256
+ width: '32px',
257
+ height: '24px',
258
+ border: '1px solid #ccc',
259
+ borderRadius: '4px',
260
+ cursor: 'pointer',
261
+ backgroundColor: '#ffffff',
262
+ position: 'relative'
263
+ });
264
+
265
+ // Обработчик клика по кнопке цвета
266
+ colorButton.addEventListener('click', (e) => {
267
+ e.stopPropagation();
268
+ this._toggleColorPalette(colorButton);
269
+ });
270
+
271
+ // Сохраняем ссылки
272
+ this.colorButton = colorButton;
273
+
274
+ colorContainer.appendChild(colorLabel);
275
+ colorContainer.appendChild(colorButton);
276
+
277
+ panel.appendChild(titleContainer);
278
+ panel.appendChild(colorContainer);
279
+
280
+ // Создаем палитру цветов (скрытую)
281
+ this._createColorPalette(panel);
282
+ }
283
+
284
+ _changeFrameTitle(newTitle) {
285
+ if (!this.currentId) return;
286
+
287
+ console.log('🖼️ FramePropertiesPanel: Changing frame title to:', newTitle);
288
+
289
+ // Обновляем свойства объекта
290
+ this.eventBus.emit(Events.Object.StateChanged, {
291
+ objectId: this.currentId,
292
+ updates: { properties: { title: newTitle } }
293
+ });
294
+ }
295
+
296
+ _createColorPalette(panel) {
297
+ // Палитра из 6 популярных цветов
298
+ const colors = [
299
+ { name: 'Белый', hex: '#FFFFFF', pixi: 0xFFFFFF },
300
+ { name: 'Голубой', hex: '#E3F2FD', pixi: 0xE3F2FD },
301
+ { name: 'Зеленый', hex: '#E8F5E8', pixi: 0xE8F5E8 },
302
+ { name: 'Желтый', hex: '#FFF8E1', pixi: 0xFFF8E1 },
303
+ { name: 'Розовый', hex: '#FCE4EC', pixi: 0xFCE4EC },
304
+ { name: 'Серый', hex: '#F5F5F5', pixi: 0xF5F5F5 }
305
+ ];
306
+
307
+ const palette = document.createElement('div');
308
+ palette.className = 'color-palette';
309
+ Object.assign(palette.style, {
310
+ position: 'absolute',
311
+ top: '100%',
312
+ left: '0',
313
+ display: 'none',
314
+ flexWrap: 'wrap',
315
+ gap: '4px',
316
+ padding: '8px',
317
+ backgroundColor: 'white',
318
+ border: '1px solid #e0e0e0',
319
+ borderRadius: '8px',
320
+ boxShadow: '0 2px 8px rgba(0, 0, 0, 0.15)',
321
+ zIndex: '10001',
322
+ width: '120px'
323
+ });
324
+
325
+ colors.forEach(color => {
326
+ const colorSwatch = document.createElement('div');
327
+ Object.assign(colorSwatch.style, {
328
+ width: '24px',
329
+ height: '24px',
330
+ backgroundColor: color.hex,
331
+ border: '1px solid #ccc',
332
+ borderRadius: '4px',
333
+ cursor: 'pointer',
334
+ transition: 'transform 0.1s'
335
+ });
336
+
337
+ colorSwatch.title = color.name;
338
+
339
+ colorSwatch.addEventListener('click', () => {
340
+ this._selectColor(color);
341
+ this._hideColorPalette();
342
+ });
343
+
344
+ colorSwatch.addEventListener('mouseenter', () => {
345
+ colorSwatch.style.transform = 'scale(1.1)';
346
+ });
347
+
348
+ colorSwatch.addEventListener('mouseleave', () => {
349
+ colorSwatch.style.transform = 'scale(1)';
350
+ });
351
+
352
+ palette.appendChild(colorSwatch);
353
+ });
354
+
355
+ this.colorPalette = palette;
356
+ panel.appendChild(palette);
357
+ }
358
+
359
+ _toggleColorPalette(button) {
360
+ if (!this.colorPalette) return;
361
+
362
+ const isVisible = this.colorPalette.style.display !== 'none';
363
+
364
+ if (isVisible) {
365
+ this._hideColorPalette();
366
+ } else {
367
+ this._showColorPalette(button);
368
+ }
369
+ }
370
+
371
+ _showColorPalette(button) {
372
+ if (!this.colorPalette) return;
373
+
374
+ // Позиционируем палитру относительно кнопки
375
+ const buttonRect = button.getBoundingClientRect();
376
+ const panelRect = this.panel.getBoundingClientRect();
377
+
378
+ this.colorPalette.style.left = `${buttonRect.left - panelRect.left}px`;
379
+ this.colorPalette.style.top = `${buttonRect.bottom - panelRect.top + 4}px`;
380
+ this.colorPalette.style.display = 'flex';
381
+
382
+ // Добавляем обработчик клика по документу для закрытия палитры
383
+ setTimeout(() => {
384
+ document.addEventListener('click', this._documentClickHandler.bind(this));
385
+ }, 0);
386
+ }
387
+
388
+ _hideColorPalette() {
389
+ if (this.colorPalette) {
390
+ this.colorPalette.style.display = 'none';
391
+ }
392
+ document.removeEventListener('click', this._documentClickHandler.bind(this));
393
+ }
394
+
395
+ _documentClickHandler(e) {
396
+ if (this.colorPalette && !this.colorPalette.contains(e.target) &&
397
+ this.colorButton && !this.colorButton.contains(e.target)) {
398
+ this._hideColorPalette();
399
+ }
400
+ }
401
+
402
+ _selectColor(color) {
403
+ if (!this.currentId) return;
404
+
405
+ console.log('🖼️ FramePropertiesPanel: Selecting color:', color);
406
+
407
+ // Обновляем визуальное отображение кнопки
408
+ this.colorButton.style.backgroundColor = color.hex;
409
+ this.colorButton.title = `Цвет фона: ${color.name}`;
410
+
411
+ // Отправляем событие изменения цвета фона
412
+ this.eventBus.emit(Events.Object.StateChanged, {
413
+ objectId: this.currentId,
414
+ updates: { backgroundColor: color.pixi }
415
+ });
416
+ }
417
+
418
+ _updateControlsFromObject() {
419
+ if (!this.currentId) return;
420
+
421
+ const objectData = this.core.getObjectData(this.currentId);
422
+ if (objectData) {
423
+ // Обновляем поле названия
424
+ if (this.titleInput && objectData.properties && objectData.properties.title !== undefined) {
425
+ this.titleInput.value = objectData.properties.title || '';
426
+ }
427
+
428
+ // Обновляем кнопку цвета фона
429
+ // Проверяем backgroundColor на верхнем уровне или в properties
430
+ const backgroundColor = objectData.backgroundColor ||
431
+ (objectData.properties && objectData.properties.backgroundColor) ||
432
+ 0xFFFFFF; // белый по умолчанию
433
+
434
+ if (this.colorButton) {
435
+ this._updateColorButton(backgroundColor);
436
+ }
437
+ }
438
+ }
439
+
440
+ _updateColorButton(pixiColor) {
441
+ if (!this.colorButton) return;
442
+
443
+ // Конвертируем PIXI цвет в hex строку
444
+ const hexColor = `#${pixiColor.toString(16).padStart(6, '0').toUpperCase()}`;
445
+ this.colorButton.style.backgroundColor = hexColor;
446
+ this.colorButton.title = `Цвет фона: ${hexColor}`;
447
+ }
448
+
449
+ destroy() {
450
+ // Удаляем обработчик клика по документу
451
+ document.removeEventListener('click', this._documentClickHandler.bind(this));
452
+
453
+ if (this.panel && this.panel.parentNode) {
454
+ this.panel.parentNode.removeChild(this.panel);
455
+ }
456
+ this.panel = null;
457
+ this.colorPalette = null;
458
+ this.colorButton = null;
459
+ this.titleInput = null;
460
+ this.currentId = null;
461
+ }
462
+ }