@sequent-org/moodboard 1.2.21 → 1.2.23
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.
- package/package.json +1 -1
- package/src/index.js +14 -414
- package/src/moodboard/DataManager.js +10 -0
- package/src/moodboard/MoodBoard.js +273 -22
- package/src/tools/BaseTool.js +6 -8
- package/src/ui/styles/workspace.css +4 -2
package/package.json
CHANGED
package/src/index.js
CHANGED
|
@@ -2,428 +2,28 @@
|
|
|
2
2
|
export { MoodBoard } from './moodboard/MoodBoard.js';
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
|
-
*
|
|
6
|
-
*
|
|
5
|
+
* АВТОМАТИЧЕСКАЯ настройка эмоджи при импорте пакета
|
|
6
|
+
* Больше НЕ нужно вызывать fixEmojiPaths() вручную
|
|
7
7
|
*/
|
|
8
|
-
|
|
9
|
-
if (
|
|
10
|
-
// Если указано имя пакета - используем его
|
|
11
|
-
window.MOODBOARD_BASE_PATH = `${window.location.origin}/node_modules/${packageName}/`;
|
|
12
|
-
console.log('🔧 Установлен базовый путь для эмоджи:', window.MOODBOARD_BASE_PATH);
|
|
13
|
-
} else {
|
|
14
|
-
// Автоопределение - используем стандартное имя пакета
|
|
8
|
+
function autoSetupEmojiPaths() {
|
|
9
|
+
if (typeof window !== 'undefined' && !window.MOODBOARD_BASE_PATH) {
|
|
15
10
|
const detectedPackage = '@sequent-org/moodboard';
|
|
16
11
|
window.MOODBOARD_BASE_PATH = `${window.location.origin}/node_modules/${detectedPackage}/`;
|
|
17
|
-
console.log('🔧
|
|
12
|
+
console.log('🔧 Мудборд: автоматически установлен базовый путь для эмоджи:', window.MOODBOARD_BASE_PATH);
|
|
18
13
|
}
|
|
19
|
-
|
|
20
|
-
return window.MOODBOARD_BASE_PATH;
|
|
21
14
|
}
|
|
22
15
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
* Находит что именно переопределяет ширину панелей
|
|
26
|
-
*/
|
|
27
|
-
export function diagnosePanelConflicts() {
|
|
28
|
-
console.log('🔍 ДИАГНОСТИКА: поиск конфликтов стилей панелей...');
|
|
29
|
-
|
|
30
|
-
const panel = document.querySelector('.text-properties-panel, .frame-properties-panel');
|
|
31
|
-
if (!panel) {
|
|
32
|
-
console.log('❌ Панели не найдены. Создайте объект и выберите его.');
|
|
33
|
-
return;
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
console.log('📋 Найдена панель:', panel.className);
|
|
37
|
-
|
|
38
|
-
// Получаем все применяемые стили
|
|
39
|
-
const computedStyle = getComputedStyle(panel);
|
|
40
|
-
console.log('📏 Текущие размеры панели:');
|
|
41
|
-
console.log(' - width:', computedStyle.width);
|
|
42
|
-
console.log(' - min-width:', computedStyle.minWidth);
|
|
43
|
-
console.log(' - max-width:', computedStyle.maxWidth);
|
|
44
|
-
console.log(' - height:', computedStyle.height);
|
|
45
|
-
console.log(' - padding:', computedStyle.padding);
|
|
46
|
-
console.log(' - display:', computedStyle.display);
|
|
47
|
-
console.log(' - position:', computedStyle.position);
|
|
48
|
-
|
|
49
|
-
// Проверяем inline стили
|
|
50
|
-
if (panel.style.cssText) {
|
|
51
|
-
console.log('⚠️ НАЙДЕНЫ inline стили на панели:', panel.style.cssText);
|
|
52
|
-
} else {
|
|
53
|
-
console.log('✅ Inline стилей нет');
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
// Ищем все CSS правила, которые могут влиять на панель
|
|
57
|
-
console.log('🔍 Поиск CSS правил, влияющих на панель...');
|
|
58
|
-
|
|
59
|
-
// Проверяем основные подозрительные свойства
|
|
60
|
-
const suspiciousProperties = ['width', 'min-width', 'max-width', 'height', 'padding', 'display'];
|
|
61
|
-
|
|
62
|
-
suspiciousProperties.forEach(prop => {
|
|
63
|
-
const value = computedStyle.getPropertyValue(prop);
|
|
64
|
-
console.log(`📌 ${prop}: ${value}`);
|
|
65
|
-
|
|
66
|
-
// Проверяем приоритет
|
|
67
|
-
const priority = computedStyle.getPropertyPriority(prop);
|
|
68
|
-
if (priority) {
|
|
69
|
-
console.log(` ⚡ Приоритет: ${priority}`);
|
|
70
|
-
}
|
|
71
|
-
});
|
|
72
|
-
|
|
73
|
-
// Проверяем классы родительских элементов
|
|
74
|
-
let parent = panel.parentElement;
|
|
75
|
-
let level = 1;
|
|
76
|
-
console.log('🔗 Родительские элементы:');
|
|
77
|
-
while (parent && level <= 5) {
|
|
78
|
-
const parentStyles = getComputedStyle(parent);
|
|
79
|
-
console.log(` ${level}. ${parent.tagName}${parent.className ? '.' + parent.className : ''}`);
|
|
80
|
-
console.log(` width: ${parentStyles.width}, display: ${parentStyles.display}`);
|
|
81
|
-
parent = parent.parentElement;
|
|
82
|
-
level++;
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
// Ищем потенциальные конфликтующие CSS классы
|
|
86
|
-
console.log('⚠️ Поиск потенциальных конфликтов:');
|
|
87
|
-
|
|
88
|
-
const possibleConflicts = [
|
|
89
|
-
'bootstrap', 'tailwind', 'flex', 'grid', 'container', 'row', 'col',
|
|
90
|
-
'w-', 'width', 'min-w', 'max-w', 'panel', 'modal', 'popup'
|
|
91
|
-
];
|
|
92
|
-
|
|
93
|
-
const allClasses = panel.className.split(' ');
|
|
94
|
-
const parentClasses = panel.parentElement?.className?.split(' ') || [];
|
|
95
|
-
|
|
96
|
-
[...allClasses, ...parentClasses].forEach(cls => {
|
|
97
|
-
possibleConflicts.forEach(conflict => {
|
|
98
|
-
if (cls.includes(conflict)) {
|
|
99
|
-
console.log(`🚨 Подозрительный класс: "${cls}" (содержит "${conflict}")`);
|
|
100
|
-
}
|
|
101
|
-
});
|
|
102
|
-
});
|
|
103
|
-
|
|
104
|
-
return {
|
|
105
|
-
element: panel,
|
|
106
|
-
computedStyle: computedStyle,
|
|
107
|
-
currentWidth: computedStyle.width,
|
|
108
|
-
currentMinWidth: computedStyle.minWidth,
|
|
109
|
-
hasInlineStyles: !!panel.style.cssText
|
|
110
|
-
};
|
|
111
|
-
}
|
|
16
|
+
// Автоматически выполняем настройку при импорте пакета
|
|
17
|
+
autoSetupEmojiPaths();
|
|
112
18
|
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
* Исправляет только width и min-width, не трогая остальное
|
|
116
|
-
*/
|
|
117
|
-
export function surgicalPanelFix() {
|
|
118
|
-
console.log('🔧 ХИРУРГИЧЕСКОЕ исправление размеров панелей...');
|
|
119
|
-
|
|
120
|
-
const targetPanels = document.querySelectorAll(`
|
|
121
|
-
.text-properties-panel,
|
|
122
|
-
.frame-properties-panel,
|
|
123
|
-
.note-properties-panel,
|
|
124
|
-
.file-properties-panel,
|
|
125
|
-
.moodboard-file-properties-panel
|
|
126
|
-
`);
|
|
127
|
-
|
|
128
|
-
if (targetPanels.length === 0) {
|
|
129
|
-
console.log('❌ Панели не найдены');
|
|
130
|
-
return;
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
targetPanels.forEach((panel, index) => {
|
|
134
|
-
console.log(`🔧 Исправляем панель ${index + 1}: ${panel.className}`);
|
|
135
|
-
|
|
136
|
-
// Запоминаем текущие значения для диагностики
|
|
137
|
-
const beforeWidth = getComputedStyle(panel).width;
|
|
138
|
-
const beforeMinWidth = getComputedStyle(panel).minWidth;
|
|
139
|
-
|
|
140
|
-
// Применяем ТОЛЬКО минимально необходимые исправления
|
|
141
|
-
if (panel.classList.contains('text-properties-panel') ||
|
|
142
|
-
panel.classList.contains('frame-properties-panel')) {
|
|
143
|
-
panel.style.setProperty('min-width', '320px', 'important');
|
|
144
|
-
panel.style.setProperty('width', 'auto', 'important');
|
|
145
|
-
} else if (panel.classList.contains('note-properties-panel')) {
|
|
146
|
-
panel.style.setProperty('min-width', '280px', 'important');
|
|
147
|
-
panel.style.setProperty('width', 'auto', 'important');
|
|
148
|
-
} else if (panel.classList.contains('file-properties-panel') ||
|
|
149
|
-
panel.classList.contains('moodboard-file-properties-panel')) {
|
|
150
|
-
panel.style.setProperty('min-width', '250px', 'important');
|
|
151
|
-
panel.style.setProperty('width', 'auto', 'important');
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
// Проверяем результат
|
|
155
|
-
setTimeout(() => {
|
|
156
|
-
const afterWidth = getComputedStyle(panel).width;
|
|
157
|
-
const afterMinWidth = getComputedStyle(panel).minWidth;
|
|
158
|
-
|
|
159
|
-
console.log(`📏 Панель ${index + 1} результат:`);
|
|
160
|
-
console.log(` До: width: ${beforeWidth}, min-width: ${beforeMinWidth}`);
|
|
161
|
-
console.log(` После: width: ${afterWidth}, min-width: ${afterMinWidth}`);
|
|
162
|
-
|
|
163
|
-
if (parseInt(afterMinWidth) >= 250) {
|
|
164
|
-
console.log(`✅ Панель ${index + 1} исправлена успешно!`);
|
|
165
|
-
} else {
|
|
166
|
-
console.log(`❌ Панель ${index + 1} все еще имеет проблемы`);
|
|
167
|
-
}
|
|
168
|
-
}, 50);
|
|
169
|
-
});
|
|
170
|
-
}
|
|
19
|
+
// ПРИМЕЧАНИЕ: Стили должны загружаться через bundler (Vite/Webpack)
|
|
20
|
+
// import '@sequent-org/moodboard/style.css' в вашем приложении
|
|
171
21
|
|
|
172
|
-
// Дополнительные экспорты для
|
|
173
|
-
export { initMoodBoardNoBundler
|
|
174
|
-
export { StyleLoader } from './utils/styleLoader.js';
|
|
175
|
-
export { EmojiLoaderNoBundler } from './utils/emojiLoaderNoBundler.js';
|
|
22
|
+
// Дополнительные экспорты (только для специальных случаев)
|
|
23
|
+
export { initMoodBoardNoBundler } from './initNoBundler.js';
|
|
176
24
|
|
|
177
|
-
//
|
|
25
|
+
// Основные утилиты для эмоджи (если нужны)
|
|
178
26
|
export {
|
|
179
27
|
getInlinePngEmojiUrl,
|
|
180
|
-
getAvailableInlinePngEmojis
|
|
181
|
-
|
|
182
|
-
} from './utils/inlinePngEmojis.js';
|
|
183
|
-
|
|
184
|
-
// Экспорт встроенных SVG эмоджи (для пользователей, которые хотят добавить свои)
|
|
185
|
-
export {
|
|
186
|
-
addInlineSvgEmoji,
|
|
187
|
-
bulkAddInlineSvgEmojis,
|
|
188
|
-
getAvailableInlineEmojis,
|
|
189
|
-
isInlineSvgEmoji,
|
|
190
|
-
getInlineEmojiByCode
|
|
191
|
-
} from './utils/inlineSvgEmojis.js';
|
|
192
|
-
|
|
193
|
-
/**
|
|
194
|
-
* СУПЕР-АГРЕССИВНАЯ функция для исправления панелей в проектах с конфликтами CSS
|
|
195
|
-
*/
|
|
196
|
-
export function forceFixPanelStyles() {
|
|
197
|
-
console.log('💪 СУПЕР-АГРЕССИВНОЕ исправление панелей (для проектов с конфликтами)...');
|
|
198
|
-
|
|
199
|
-
const superForcedCSS = `
|
|
200
|
-
/* МАКСИМАЛЬНО АГРЕССИВНЫЕ стили панелей */
|
|
201
|
-
.text-properties-panel,
|
|
202
|
-
div.text-properties-panel,
|
|
203
|
-
[class*="text-properties-panel"] {
|
|
204
|
-
min-width: 320px !important;
|
|
205
|
-
max-width: none !important;
|
|
206
|
-
width: auto !important;
|
|
207
|
-
height: 36px !important;
|
|
208
|
-
padding: 12px 22px !important;
|
|
209
|
-
margin: 0 !important;
|
|
210
|
-
background: #ffffff !important;
|
|
211
|
-
background-color: #ffffff !important;
|
|
212
|
-
border: 1px solid #e0e0e0 !important;
|
|
213
|
-
border-radius: 9999px !important;
|
|
214
|
-
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.12) !important;
|
|
215
|
-
display: flex !important;
|
|
216
|
-
flex-direction: row !important;
|
|
217
|
-
align-items: center !important;
|
|
218
|
-
gap: 8px !important;
|
|
219
|
-
font-size: 13px !important;
|
|
220
|
-
font-family: 'Roboto', Arial, sans-serif !important;
|
|
221
|
-
position: absolute !important;
|
|
222
|
-
pointer-events: auto !important;
|
|
223
|
-
z-index: 10001 !important;
|
|
224
|
-
box-sizing: border-box !important;
|
|
225
|
-
transform: none !important;
|
|
226
|
-
opacity: 1 !important;
|
|
227
|
-
visibility: visible !important;
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
.frame-properties-panel,
|
|
231
|
-
div.frame-properties-panel,
|
|
232
|
-
[class*="frame-properties-panel"] {
|
|
233
|
-
min-width: 320px !important;
|
|
234
|
-
max-width: none !important;
|
|
235
|
-
width: auto !important;
|
|
236
|
-
height: 36px !important;
|
|
237
|
-
padding: 12px 32px !important;
|
|
238
|
-
margin: 0 !important;
|
|
239
|
-
background: #ffffff !important;
|
|
240
|
-
background-color: #ffffff !important;
|
|
241
|
-
border: 1px solid #e0e0e0 !important;
|
|
242
|
-
border-radius: 9999px !important;
|
|
243
|
-
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.12) !important;
|
|
244
|
-
display: inline-flex !important;
|
|
245
|
-
align-items: center !important;
|
|
246
|
-
gap: 8px !important;
|
|
247
|
-
font-size: 13px !important;
|
|
248
|
-
font-family: 'Roboto', Arial, sans-serif !important;
|
|
249
|
-
position: absolute !important;
|
|
250
|
-
pointer-events: auto !important;
|
|
251
|
-
z-index: 10001 !important;
|
|
252
|
-
box-sizing: border-box !important;
|
|
253
|
-
transform: none !important;
|
|
254
|
-
opacity: 1 !important;
|
|
255
|
-
visibility: visible !important;
|
|
256
|
-
}
|
|
257
|
-
|
|
258
|
-
.note-properties-panel,
|
|
259
|
-
div.note-properties-panel,
|
|
260
|
-
[class*="note-properties-panel"] {
|
|
261
|
-
min-width: 280px !important;
|
|
262
|
-
max-width: none !important;
|
|
263
|
-
width: auto !important;
|
|
264
|
-
height: 40px !important;
|
|
265
|
-
padding: 8px 40px !important;
|
|
266
|
-
margin: 0 !important;
|
|
267
|
-
background: white !important;
|
|
268
|
-
background-color: white !important;
|
|
269
|
-
border: 1px solid #e0e0e0 !important;
|
|
270
|
-
border-radius: 9999px !important;
|
|
271
|
-
box-shadow: 0 6px 24px rgba(0, 0, 0, 0.16) !important;
|
|
272
|
-
display: inline-flex !important;
|
|
273
|
-
flex-direction: row !important;
|
|
274
|
-
align-items: center !important;
|
|
275
|
-
gap: 8px !important;
|
|
276
|
-
font-size: 12px !important;
|
|
277
|
-
font-family: Arial, sans-serif !important;
|
|
278
|
-
position: absolute !important;
|
|
279
|
-
pointer-events: auto !important;
|
|
280
|
-
z-index: 10000 !important;
|
|
281
|
-
box-sizing: border-box !important;
|
|
282
|
-
backdrop-filter: blur(4px) !important;
|
|
283
|
-
transform: none !important;
|
|
284
|
-
opacity: 1 !important;
|
|
285
|
-
visibility: visible !important;
|
|
286
|
-
}
|
|
287
|
-
`;
|
|
288
|
-
|
|
289
|
-
// Удаляем предыдущие версии
|
|
290
|
-
const existingStyles = document.querySelectorAll('#moodboard-universal-panel-fix, #moodboard-super-force-panel-fix');
|
|
291
|
-
existingStyles.forEach(style => style.remove());
|
|
292
|
-
|
|
293
|
-
const style = document.createElement('style');
|
|
294
|
-
style.id = 'moodboard-super-force-panel-fix';
|
|
295
|
-
style.textContent = superForcedCSS;
|
|
296
|
-
|
|
297
|
-
// Вставляем в самый конец head для максимального приоритета
|
|
298
|
-
document.head.appendChild(style);
|
|
299
|
-
|
|
300
|
-
console.log('💪 Супер-агрессивные стили применены');
|
|
301
|
-
|
|
302
|
-
// Проверяем все панели
|
|
303
|
-
setTimeout(() => {
|
|
304
|
-
const panels = document.querySelectorAll('.text-properties-panel, .frame-properties-panel, .note-properties-panel');
|
|
305
|
-
panels.forEach((panel, index) => {
|
|
306
|
-
const computedStyle = getComputedStyle(panel);
|
|
307
|
-
const width = parseInt(computedStyle.minWidth);
|
|
308
|
-
console.log(`📏 Панель ${index + 1}: minWidth = ${width}px`);
|
|
309
|
-
});
|
|
310
|
-
}, 200);
|
|
311
|
-
}
|
|
312
|
-
|
|
313
|
-
/**
|
|
314
|
-
* Универсальная функция для исправления стилей панелей
|
|
315
|
-
* Работает с любой версией MoodBoard (bundled и no-bundler)
|
|
316
|
-
*/
|
|
317
|
-
export function fixPanelStyles() {
|
|
318
|
-
console.log('🔧 Исправляем стили панелей MoodBoard (универсальная версия)...');
|
|
319
|
-
|
|
320
|
-
const forcedPanelCSS = `
|
|
321
|
-
/* УНИВЕРСАЛЬНЫЕ принудительные стили панелей */
|
|
322
|
-
.text-properties-panel {
|
|
323
|
-
min-width: 320px !important;
|
|
324
|
-
width: auto !important;
|
|
325
|
-
height: 36px !important;
|
|
326
|
-
padding: 12px 22px !important;
|
|
327
|
-
background-color: #ffffff !important;
|
|
328
|
-
border: 1px solid #e0e0e0 !important;
|
|
329
|
-
border-radius: 9999px !important;
|
|
330
|
-
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.12) !important;
|
|
331
|
-
display: flex !important;
|
|
332
|
-
flex-direction: row !important;
|
|
333
|
-
align-items: center !important;
|
|
334
|
-
gap: 8px !important;
|
|
335
|
-
font-size: 13px !important;
|
|
336
|
-
font-family: 'Roboto', Arial, sans-serif !important;
|
|
337
|
-
position: absolute !important;
|
|
338
|
-
pointer-events: auto !important;
|
|
339
|
-
z-index: 1001 !important;
|
|
340
|
-
}
|
|
341
|
-
|
|
342
|
-
.frame-properties-panel {
|
|
343
|
-
min-width: 320px !important;
|
|
344
|
-
width: auto !important;
|
|
345
|
-
height: 36px !important;
|
|
346
|
-
padding: 12px 32px !important;
|
|
347
|
-
background-color: #ffffff !important;
|
|
348
|
-
border: 1px solid #e0e0e0 !important;
|
|
349
|
-
border-radius: 9999px !important;
|
|
350
|
-
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.12) !important;
|
|
351
|
-
display: inline-flex !important;
|
|
352
|
-
align-items: center !important;
|
|
353
|
-
gap: 8px !important;
|
|
354
|
-
font-size: 13px !important;
|
|
355
|
-
font-family: 'Roboto', Arial, sans-serif !important;
|
|
356
|
-
position: absolute !important;
|
|
357
|
-
pointer-events: auto !important;
|
|
358
|
-
z-index: 1001 !important;
|
|
359
|
-
}
|
|
360
|
-
|
|
361
|
-
.note-properties-panel {
|
|
362
|
-
min-width: 280px !important;
|
|
363
|
-
width: auto !important;
|
|
364
|
-
height: 36px !important;
|
|
365
|
-
padding: 12px 22px !important;
|
|
366
|
-
background-color: #ffffff !important;
|
|
367
|
-
border: 1px solid #e0e0e0 !important;
|
|
368
|
-
border-radius: 9999px !important;
|
|
369
|
-
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.12) !important;
|
|
370
|
-
display: inline-flex !important;
|
|
371
|
-
align-items: center !important;
|
|
372
|
-
gap: 8px !important;
|
|
373
|
-
font-size: 13px !important;
|
|
374
|
-
font-family: 'Roboto', Arial, sans-serif !important;
|
|
375
|
-
position: absolute !important;
|
|
376
|
-
pointer-events: auto !important;
|
|
377
|
-
z-index: 1001 !important;
|
|
378
|
-
}
|
|
379
|
-
|
|
380
|
-
.file-properties-panel, .moodboard-file-properties-panel {
|
|
381
|
-
min-width: 250px !important;
|
|
382
|
-
width: auto !important;
|
|
383
|
-
height: 36px !important;
|
|
384
|
-
padding: 8px 12px !important;
|
|
385
|
-
background-color: #ffffff !important;
|
|
386
|
-
border: 1px solid #e5e7eb !important;
|
|
387
|
-
border-radius: 8px !important;
|
|
388
|
-
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06) !important;
|
|
389
|
-
display: flex !important;
|
|
390
|
-
flex-direction: row !important;
|
|
391
|
-
align-items: center !important;
|
|
392
|
-
gap: 8px !important;
|
|
393
|
-
font-size: 14px !important;
|
|
394
|
-
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif !important;
|
|
395
|
-
position: absolute !important;
|
|
396
|
-
pointer-events: auto !important;
|
|
397
|
-
z-index: 1000 !important;
|
|
398
|
-
}
|
|
399
|
-
`;
|
|
400
|
-
|
|
401
|
-
// Проверяем, не добавлены ли уже стили
|
|
402
|
-
if (document.getElementById('moodboard-universal-panel-fix')) {
|
|
403
|
-
console.log('⚠️ Универсальные стили панелей уже добавлены');
|
|
404
|
-
return;
|
|
405
|
-
}
|
|
406
|
-
|
|
407
|
-
const style = document.createElement('style');
|
|
408
|
-
style.id = 'moodboard-universal-panel-fix';
|
|
409
|
-
style.textContent = forcedPanelCSS;
|
|
410
|
-
document.head.appendChild(style);
|
|
411
|
-
|
|
412
|
-
console.log('✅ Универсальные стили панелей добавлены');
|
|
413
|
-
|
|
414
|
-
// Проверяем результат через 100ms
|
|
415
|
-
setTimeout(() => {
|
|
416
|
-
const panel = document.querySelector('.text-properties-panel, .frame-properties-panel, .note-properties-panel');
|
|
417
|
-
if (panel) {
|
|
418
|
-
const computedStyle = getComputedStyle(panel);
|
|
419
|
-
const width = parseInt(computedStyle.minWidth);
|
|
420
|
-
console.log(`📏 Проверка панели: minWidth = ${width}px`);
|
|
421
|
-
|
|
422
|
-
if (width >= 250) {
|
|
423
|
-
console.log('✅ Стили панелей исправлены успешно!');
|
|
424
|
-
} else {
|
|
425
|
-
console.log('⚠️ Панель все еще узкая, возможны конфликты CSS');
|
|
426
|
-
}
|
|
427
|
-
}
|
|
428
|
-
}, 100);
|
|
429
|
-
}
|
|
28
|
+
getAvailableInlinePngEmojis
|
|
29
|
+
} from './utils/inlinePngEmojis.js';
|
|
@@ -37,6 +37,16 @@ export class DataManager {
|
|
|
37
37
|
this.loadViewport(data.viewport);
|
|
38
38
|
}
|
|
39
39
|
|
|
40
|
+
// ИСПРАВЛЕНИЕ: Принудительно пересоздаем HTML элементы после загрузки данных
|
|
41
|
+
// Это нужно потому что createObjectFromData() НЕ генерирует Events.Object.Created
|
|
42
|
+
// чтобы не запускать автосохранение, но HtmlTextLayer нуждается в этих событиях
|
|
43
|
+
setTimeout(() => {
|
|
44
|
+
// Ищем htmlTextLayer через глобальную переменную (установленную в MoodBoard.js)
|
|
45
|
+
if (window.moodboardHtmlTextLayer) {
|
|
46
|
+
window.moodboardHtmlTextLayer.rebuildFromState();
|
|
47
|
+
window.moodboardHtmlTextLayer.updateAll();
|
|
48
|
+
}
|
|
49
|
+
}, 100);
|
|
40
50
|
|
|
41
51
|
}
|
|
42
52
|
|
|
@@ -36,6 +36,12 @@ export class MoodBoard {
|
|
|
36
36
|
// Настройки по умолчанию
|
|
37
37
|
this.options = {
|
|
38
38
|
theme: 'light',
|
|
39
|
+
boardId: null,
|
|
40
|
+
apiUrl: '/api/moodboard',
|
|
41
|
+
autoLoad: true,
|
|
42
|
+
onSave: null,
|
|
43
|
+
onLoad: null,
|
|
44
|
+
onDestroy: null,
|
|
39
45
|
...options
|
|
40
46
|
};
|
|
41
47
|
|
|
@@ -63,6 +69,11 @@ export class MoodBoard {
|
|
|
63
69
|
*/
|
|
64
70
|
async init() {
|
|
65
71
|
try {
|
|
72
|
+
// Добавляем корневой класс к контейнеру для изоляции стилей
|
|
73
|
+
if (this.container) {
|
|
74
|
+
this.container.classList.add('moodboard-root');
|
|
75
|
+
}
|
|
76
|
+
|
|
66
77
|
// Создаем HTML структуру
|
|
67
78
|
const { workspace, toolbar, canvas, topbar } = this.workspaceManager.createWorkspaceStructure();
|
|
68
79
|
this.workspaceElement = workspace;
|
|
@@ -121,8 +132,13 @@ export class MoodBoard {
|
|
|
121
132
|
// Предоставляем доступ к сервису через core
|
|
122
133
|
this.coreMoodboard.imageUploadService = this.imageUploadService;
|
|
123
134
|
|
|
124
|
-
//
|
|
125
|
-
|
|
135
|
+
// Настраиваем коллбеки событий
|
|
136
|
+
this.setupEventCallbacks();
|
|
137
|
+
|
|
138
|
+
// Автоматически загружаем данные если включено
|
|
139
|
+
if (this.options.autoLoad) {
|
|
140
|
+
await this.loadExistingBoard();
|
|
141
|
+
}
|
|
126
142
|
|
|
127
143
|
} catch (error) {
|
|
128
144
|
console.error('MoodBoard init failed:', error);
|
|
@@ -326,34 +342,65 @@ export class MoodBoard {
|
|
|
326
342
|
try {
|
|
327
343
|
const boardId = this.options.boardId;
|
|
328
344
|
|
|
329
|
-
if (!boardId || !this.options.
|
|
330
|
-
|
|
345
|
+
if (!boardId || !this.options.apiUrl) {
|
|
346
|
+
console.log('📦 MoodBoard: нет boardId или apiUrl, загружаем пустую доску');
|
|
347
|
+
this.dataManager.loadData(this.data || { objects: [] });
|
|
348
|
+
|
|
349
|
+
// Вызываем коллбек onLoad
|
|
350
|
+
if (typeof this.options.onLoad === 'function') {
|
|
351
|
+
this.options.onLoad({ success: true, data: this.data || { objects: [] } });
|
|
352
|
+
}
|
|
331
353
|
return;
|
|
332
354
|
}
|
|
333
355
|
|
|
334
|
-
|
|
335
|
-
const boardData = await this.coreMoodboard.saveManager.loadBoardData(boardId);
|
|
356
|
+
console.log(`📦 MoodBoard: загружаем доску ${boardId} с ${this.options.apiUrl}`);
|
|
336
357
|
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
358
|
+
// Формируем URL для загрузки
|
|
359
|
+
const loadUrl = this.options.apiUrl.endsWith('/')
|
|
360
|
+
? `${this.options.apiUrl}load/${boardId}`
|
|
361
|
+
: `${this.options.apiUrl}/load/${boardId}`;
|
|
362
|
+
|
|
363
|
+
// Загружаем с сервера через fetch
|
|
364
|
+
const response = await fetch(loadUrl, {
|
|
365
|
+
method: 'GET',
|
|
366
|
+
headers: {
|
|
367
|
+
'Content-Type': 'application/json',
|
|
368
|
+
'X-CSRF-TOKEN': this.getCsrfToken()
|
|
369
|
+
}
|
|
370
|
+
});
|
|
371
|
+
|
|
372
|
+
if (!response.ok) {
|
|
373
|
+
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
const boardData = await response.json();
|
|
377
|
+
|
|
378
|
+
if (boardData && boardData.data) {
|
|
379
|
+
console.log('✅ MoodBoard: данные загружены с сервера', boardData.data);
|
|
380
|
+
this.dataManager.loadData(boardData.data);
|
|
381
|
+
|
|
382
|
+
// Вызываем коллбек onLoad
|
|
383
|
+
if (typeof this.options.onLoad === 'function') {
|
|
384
|
+
this.options.onLoad({ success: true, data: boardData.data });
|
|
347
385
|
}
|
|
348
|
-
this.dataManager.loadData(restoredData);
|
|
349
386
|
} else {
|
|
350
|
-
|
|
387
|
+
console.log('📦 MoodBoard: нет данных с сервера, загружаем пустую доску');
|
|
388
|
+
this.dataManager.loadData(this.data || { objects: [] });
|
|
389
|
+
|
|
390
|
+
// Вызываем коллбек onLoad
|
|
391
|
+
if (typeof this.options.onLoad === 'function') {
|
|
392
|
+
this.options.onLoad({ success: true, data: this.data || { objects: [] } });
|
|
393
|
+
}
|
|
351
394
|
}
|
|
352
395
|
|
|
353
396
|
} catch (error) {
|
|
354
|
-
console.warn('⚠️
|
|
355
|
-
|
|
356
|
-
|
|
397
|
+
console.warn('⚠️ MoodBoard: ошибка загрузки доски, создаем новую:', error.message);
|
|
398
|
+
this.dataManager.loadData(this.data || { objects: [] });
|
|
399
|
+
|
|
400
|
+
// Вызываем коллбек onLoad с ошибкой
|
|
401
|
+
if (typeof this.options.onLoad === 'function') {
|
|
402
|
+
this.options.onLoad({ success: false, error: error.message, data: this.data || { objects: [] } });
|
|
403
|
+
}
|
|
357
404
|
}
|
|
358
405
|
}
|
|
359
406
|
|
|
@@ -432,8 +479,212 @@ export class MoodBoard {
|
|
|
432
479
|
this.dataManager = null;
|
|
433
480
|
this.actionHandler = null;
|
|
434
481
|
|
|
435
|
-
//
|
|
482
|
+
// Удаляем корневой класс и очищаем ссылку на контейнер
|
|
483
|
+
if (this.container) {
|
|
484
|
+
this.container.classList.remove('moodboard-root');
|
|
485
|
+
}
|
|
436
486
|
this.container = null;
|
|
437
487
|
|
|
488
|
+
// Вызываем коллбек onDestroy
|
|
489
|
+
if (typeof this.options.onDestroy === 'function') {
|
|
490
|
+
try {
|
|
491
|
+
this.options.onDestroy();
|
|
492
|
+
} catch (error) {
|
|
493
|
+
console.warn('⚠️ Ошибка в коллбеке onDestroy:', error);
|
|
494
|
+
}
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
/**
|
|
499
|
+
* Настройка коллбеков событий
|
|
500
|
+
*/
|
|
501
|
+
setupEventCallbacks() {
|
|
502
|
+
if (!this.coreMoodboard || !this.coreMoodboard.eventBus) return;
|
|
503
|
+
|
|
504
|
+
// Коллбек для успешного сохранения
|
|
505
|
+
if (typeof this.options.onSave === 'function') {
|
|
506
|
+
this.coreMoodboard.eventBus.on('save:success', (data) => {
|
|
507
|
+
try {
|
|
508
|
+
// Создаем объединенный скриншот с HTML текстом
|
|
509
|
+
let screenshot = null;
|
|
510
|
+
if (this.coreMoodboard.pixi && this.coreMoodboard.pixi.app && this.coreMoodboard.pixi.app.view) {
|
|
511
|
+
screenshot = this.createCombinedScreenshot('image/jpeg', 0.6);
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
this.options.onSave({
|
|
515
|
+
success: true,
|
|
516
|
+
data: data,
|
|
517
|
+
screenshot: screenshot,
|
|
518
|
+
boardId: this.options.boardId
|
|
519
|
+
});
|
|
520
|
+
} catch (error) {
|
|
521
|
+
console.warn('⚠️ Ошибка в коллбеке onSave:', error);
|
|
522
|
+
}
|
|
523
|
+
});
|
|
524
|
+
|
|
525
|
+
// Коллбек для ошибки сохранения
|
|
526
|
+
this.coreMoodboard.eventBus.on('save:error', (data) => {
|
|
527
|
+
try {
|
|
528
|
+
this.options.onSave({
|
|
529
|
+
success: false,
|
|
530
|
+
error: data.error,
|
|
531
|
+
boardId: this.options.boardId
|
|
532
|
+
});
|
|
533
|
+
} catch (error) {
|
|
534
|
+
console.warn('⚠️ Ошибка в коллбеке onSave:', error);
|
|
535
|
+
}
|
|
536
|
+
});
|
|
537
|
+
}
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
/**
|
|
541
|
+
* Получение CSRF токена из всех возможных источников
|
|
542
|
+
*/
|
|
543
|
+
getCsrfToken() {
|
|
544
|
+
return document.querySelector('meta[name="csrf-token"]')?.getAttribute('content') ||
|
|
545
|
+
window.csrfToken ||
|
|
546
|
+
this.options.csrfToken ||
|
|
547
|
+
'';
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
/**
|
|
551
|
+
* Публичный метод для загрузки данных из API
|
|
552
|
+
*/
|
|
553
|
+
async loadFromApi(boardId = null) {
|
|
554
|
+
const targetBoardId = boardId || this.options.boardId;
|
|
555
|
+
if (!targetBoardId) {
|
|
556
|
+
throw new Error('boardId не указан');
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
// Временно меняем boardId для загрузки
|
|
560
|
+
const originalBoardId = this.options.boardId;
|
|
561
|
+
this.options.boardId = targetBoardId;
|
|
562
|
+
|
|
563
|
+
try {
|
|
564
|
+
await this.loadExistingBoard();
|
|
565
|
+
} finally {
|
|
566
|
+
// Восстанавливаем оригинальный boardId
|
|
567
|
+
this.options.boardId = originalBoardId;
|
|
568
|
+
}
|
|
569
|
+
}
|
|
570
|
+
|
|
571
|
+
/**
|
|
572
|
+
* Публичный метод для экспорта скриншота с HTML текстом
|
|
573
|
+
*/
|
|
574
|
+
exportScreenshot(format = 'image/jpeg', quality = 0.6) {
|
|
575
|
+
return this.createCombinedScreenshot(format, quality);
|
|
576
|
+
}
|
|
577
|
+
|
|
578
|
+
/**
|
|
579
|
+
* Разбивает текст на строки с учетом ширины элемента (имитирует HTML word-break: break-word)
|
|
580
|
+
*/
|
|
581
|
+
wrapText(ctx, text, maxWidth) {
|
|
582
|
+
const lines = [];
|
|
583
|
+
|
|
584
|
+
if (!text || maxWidth <= 0) {
|
|
585
|
+
return [text];
|
|
586
|
+
}
|
|
587
|
+
|
|
588
|
+
// Разбиваем по символам если не помещается (имитирует word-break: break-word)
|
|
589
|
+
let currentLine = '';
|
|
590
|
+
|
|
591
|
+
for (let i = 0; i < text.length; i++) {
|
|
592
|
+
const char = text[i];
|
|
593
|
+
const testLine = currentLine + char;
|
|
594
|
+
const metrics = ctx.measureText(testLine);
|
|
595
|
+
|
|
596
|
+
if (metrics.width > maxWidth && currentLine !== '') {
|
|
597
|
+
// Текущая строка не помещается, сохраняем предыдущую
|
|
598
|
+
lines.push(currentLine);
|
|
599
|
+
currentLine = char;
|
|
600
|
+
} else {
|
|
601
|
+
currentLine = testLine;
|
|
602
|
+
}
|
|
603
|
+
}
|
|
604
|
+
|
|
605
|
+
// Добавляем последнюю строку
|
|
606
|
+
if (currentLine) {
|
|
607
|
+
lines.push(currentLine);
|
|
608
|
+
}
|
|
609
|
+
|
|
610
|
+
return lines.length > 0 ? lines : [text];
|
|
611
|
+
}
|
|
612
|
+
|
|
613
|
+
/**
|
|
614
|
+
* Создает объединенный скриншот: PIXI canvas + HTML текстовые элементы
|
|
615
|
+
*/
|
|
616
|
+
createCombinedScreenshot(format = 'image/jpeg', quality = 0.6) {
|
|
617
|
+
if (!this.coreMoodboard || !this.coreMoodboard.pixi || !this.coreMoodboard.pixi.app || !this.coreMoodboard.pixi.app.view) {
|
|
618
|
+
throw new Error('Canvas не найден');
|
|
619
|
+
}
|
|
620
|
+
|
|
621
|
+
try {
|
|
622
|
+
// Получаем PIXI canvas
|
|
623
|
+
const pixiCanvas = this.coreMoodboard.pixi.app.view;
|
|
624
|
+
const pixiWidth = pixiCanvas.width;
|
|
625
|
+
const pixiHeight = pixiCanvas.height;
|
|
626
|
+
|
|
627
|
+
// Создаем временный canvas для объединения
|
|
628
|
+
const combinedCanvas = document.createElement('canvas');
|
|
629
|
+
combinedCanvas.width = pixiWidth;
|
|
630
|
+
combinedCanvas.height = pixiHeight;
|
|
631
|
+
const ctx = combinedCanvas.getContext('2d');
|
|
632
|
+
|
|
633
|
+
// 1. Рисуем PIXI canvas как основу
|
|
634
|
+
ctx.drawImage(pixiCanvas, 0, 0);
|
|
635
|
+
|
|
636
|
+
// 2. Рисуем HTML текстовые элементы поверх
|
|
637
|
+
const textElements = document.querySelectorAll('.mb-text');
|
|
638
|
+
|
|
639
|
+
textElements.forEach((textEl, index) => {
|
|
640
|
+
try {
|
|
641
|
+
// Получаем стили и позицию элемента
|
|
642
|
+
const computedStyle = window.getComputedStyle(textEl);
|
|
643
|
+
const text = textEl.textContent || '';
|
|
644
|
+
|
|
645
|
+
// Проверяем видимость
|
|
646
|
+
if (computedStyle.visibility === 'hidden' || computedStyle.opacity === '0' || !text.trim()) {
|
|
647
|
+
return;
|
|
648
|
+
}
|
|
649
|
+
|
|
650
|
+
// Используем CSS позицию (абсолютная позиция)
|
|
651
|
+
const left = parseInt(textEl.style.left) || 0;
|
|
652
|
+
const top = parseInt(textEl.style.top) || 0;
|
|
653
|
+
|
|
654
|
+
// Настраиваем стили текста
|
|
655
|
+
const fontSize = parseInt(computedStyle.fontSize) || 18;
|
|
656
|
+
const fontFamily = computedStyle.fontFamily || 'Arial, sans-serif';
|
|
657
|
+
const color = computedStyle.color || '#000000';
|
|
658
|
+
|
|
659
|
+
ctx.font = `${fontSize}px ${fontFamily}`;
|
|
660
|
+
ctx.fillStyle = color;
|
|
661
|
+
ctx.textAlign = 'left';
|
|
662
|
+
ctx.textBaseline = 'top';
|
|
663
|
+
|
|
664
|
+
// Получаем размеры элемента
|
|
665
|
+
const elementWidth = parseInt(textEl.style.width) || 182;
|
|
666
|
+
|
|
667
|
+
// Разбиваем текст на строки и рисуем каждую строку
|
|
668
|
+
const lines = this.wrapText(ctx, text, elementWidth);
|
|
669
|
+
const lineHeight = fontSize * 1.3; // Межстрочный интервал
|
|
670
|
+
|
|
671
|
+
lines.forEach((line, lineIndex) => {
|
|
672
|
+
const yPos = top + (lineIndex * lineHeight) + 2;
|
|
673
|
+
ctx.fillText(line, left, yPos);
|
|
674
|
+
});
|
|
675
|
+
} catch (error) {
|
|
676
|
+
console.warn(`⚠️ Ошибка при рисовании текста ${index + 1}:`, error);
|
|
677
|
+
}
|
|
678
|
+
});
|
|
679
|
+
|
|
680
|
+
// 3. Экспортируем объединенный результат
|
|
681
|
+
return combinedCanvas.toDataURL(format, quality);
|
|
682
|
+
|
|
683
|
+
} catch (error) {
|
|
684
|
+
console.warn('⚠️ Ошибка при создании объединенного скриншота, используем только PIXI canvas:', error);
|
|
685
|
+
// Fallback: только PIXI canvas
|
|
686
|
+
const canvas = this.coreMoodboard.pixi.app.view;
|
|
687
|
+
return canvas.toDataURL(format, quality);
|
|
688
|
+
}
|
|
438
689
|
}
|
|
439
690
|
}
|
package/src/tools/BaseTool.js
CHANGED
|
@@ -41,16 +41,14 @@ export class BaseTool {
|
|
|
41
41
|
|
|
42
42
|
/**
|
|
43
43
|
* Устанавливает курсор для инструмента
|
|
44
|
+
* Базовая реализация не устанавливает глобальный курсор
|
|
45
|
+
* Конкретные инструменты должны переопределить этот метод
|
|
46
|
+
* и устанавливать курсор на canvas или соответствующий элемент
|
|
44
47
|
*/
|
|
45
48
|
setCursor() {
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
document.body.style.cursor = '';
|
|
50
|
-
} else {
|
|
51
|
-
document.body.style.cursor = this.cursor;
|
|
52
|
-
}
|
|
53
|
-
}
|
|
49
|
+
// Базовая реализация ничего не делает
|
|
50
|
+
// Избегаем установки глобального курсора на document.body
|
|
51
|
+
// Конкретные инструменты устанавливают курсор на canvas
|
|
54
52
|
}
|
|
55
53
|
|
|
56
54
|
/**
|
|
@@ -14,8 +14,10 @@
|
|
|
14
14
|
font-display: swap;
|
|
15
15
|
}
|
|
16
16
|
|
|
17
|
-
/*
|
|
18
|
-
|
|
17
|
+
/* Default cursor inside moodboard root only */
|
|
18
|
+
.moodboard-root .moodboard-workspace,
|
|
19
|
+
.moodboard-root .moodboard-container,
|
|
20
|
+
.moodboard-root .moodboard-canvas {
|
|
19
21
|
cursor: url('../../assets/icons/cursor-default-custom.svg') 0 0, default;
|
|
20
22
|
}
|
|
21
23
|
|