@sequent-org/moodboard 1.2.21 → 1.2.22
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 +264 -21
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
|
|
|
@@ -121,8 +127,13 @@ export class MoodBoard {
|
|
|
121
127
|
// Предоставляем доступ к сервису через core
|
|
122
128
|
this.coreMoodboard.imageUploadService = this.imageUploadService;
|
|
123
129
|
|
|
124
|
-
//
|
|
125
|
-
|
|
130
|
+
// Настраиваем коллбеки событий
|
|
131
|
+
this.setupEventCallbacks();
|
|
132
|
+
|
|
133
|
+
// Автоматически загружаем данные если включено
|
|
134
|
+
if (this.options.autoLoad) {
|
|
135
|
+
await this.loadExistingBoard();
|
|
136
|
+
}
|
|
126
137
|
|
|
127
138
|
} catch (error) {
|
|
128
139
|
console.error('MoodBoard init failed:', error);
|
|
@@ -326,34 +337,65 @@ export class MoodBoard {
|
|
|
326
337
|
try {
|
|
327
338
|
const boardId = this.options.boardId;
|
|
328
339
|
|
|
329
|
-
if (!boardId || !this.options.
|
|
330
|
-
|
|
340
|
+
if (!boardId || !this.options.apiUrl) {
|
|
341
|
+
console.log('📦 MoodBoard: нет boardId или apiUrl, загружаем пустую доску');
|
|
342
|
+
this.dataManager.loadData(this.data || { objects: [] });
|
|
343
|
+
|
|
344
|
+
// Вызываем коллбек onLoad
|
|
345
|
+
if (typeof this.options.onLoad === 'function') {
|
|
346
|
+
this.options.onLoad({ success: true, data: this.data || { objects: [] } });
|
|
347
|
+
}
|
|
331
348
|
return;
|
|
332
349
|
}
|
|
333
350
|
|
|
334
|
-
|
|
335
|
-
const boardData = await this.coreMoodboard.saveManager.loadBoardData(boardId);
|
|
351
|
+
console.log(`📦 MoodBoard: загружаем доску ${boardId} с ${this.options.apiUrl}`);
|
|
336
352
|
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
353
|
+
// Формируем URL для загрузки
|
|
354
|
+
const loadUrl = this.options.apiUrl.endsWith('/')
|
|
355
|
+
? `${this.options.apiUrl}load/${boardId}`
|
|
356
|
+
: `${this.options.apiUrl}/load/${boardId}`;
|
|
357
|
+
|
|
358
|
+
// Загружаем с сервера через fetch
|
|
359
|
+
const response = await fetch(loadUrl, {
|
|
360
|
+
method: 'GET',
|
|
361
|
+
headers: {
|
|
362
|
+
'Content-Type': 'application/json',
|
|
363
|
+
'X-CSRF-TOKEN': this.getCsrfToken()
|
|
364
|
+
}
|
|
365
|
+
});
|
|
366
|
+
|
|
367
|
+
if (!response.ok) {
|
|
368
|
+
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
const boardData = await response.json();
|
|
372
|
+
|
|
373
|
+
if (boardData && boardData.data) {
|
|
374
|
+
console.log('✅ MoodBoard: данные загружены с сервера', boardData.data);
|
|
375
|
+
this.dataManager.loadData(boardData.data);
|
|
376
|
+
|
|
377
|
+
// Вызываем коллбек onLoad
|
|
378
|
+
if (typeof this.options.onLoad === 'function') {
|
|
379
|
+
this.options.onLoad({ success: true, data: boardData.data });
|
|
347
380
|
}
|
|
348
|
-
this.dataManager.loadData(restoredData);
|
|
349
381
|
} else {
|
|
350
|
-
|
|
382
|
+
console.log('📦 MoodBoard: нет данных с сервера, загружаем пустую доску');
|
|
383
|
+
this.dataManager.loadData(this.data || { objects: [] });
|
|
384
|
+
|
|
385
|
+
// Вызываем коллбек onLoad
|
|
386
|
+
if (typeof this.options.onLoad === 'function') {
|
|
387
|
+
this.options.onLoad({ success: true, data: this.data || { objects: [] } });
|
|
388
|
+
}
|
|
351
389
|
}
|
|
352
390
|
|
|
353
391
|
} catch (error) {
|
|
354
|
-
console.warn('⚠️
|
|
355
|
-
|
|
356
|
-
|
|
392
|
+
console.warn('⚠️ MoodBoard: ошибка загрузки доски, создаем новую:', error.message);
|
|
393
|
+
this.dataManager.loadData(this.data || { objects: [] });
|
|
394
|
+
|
|
395
|
+
// Вызываем коллбек onLoad с ошибкой
|
|
396
|
+
if (typeof this.options.onLoad === 'function') {
|
|
397
|
+
this.options.onLoad({ success: false, error: error.message, data: this.data || { objects: [] } });
|
|
398
|
+
}
|
|
357
399
|
}
|
|
358
400
|
}
|
|
359
401
|
|
|
@@ -435,5 +477,206 @@ export class MoodBoard {
|
|
|
435
477
|
// Очищаем ссылку на контейнер
|
|
436
478
|
this.container = null;
|
|
437
479
|
|
|
480
|
+
// Вызываем коллбек onDestroy
|
|
481
|
+
if (typeof this.options.onDestroy === 'function') {
|
|
482
|
+
try {
|
|
483
|
+
this.options.onDestroy();
|
|
484
|
+
} catch (error) {
|
|
485
|
+
console.warn('⚠️ Ошибка в коллбеке onDestroy:', error);
|
|
486
|
+
}
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
/**
|
|
491
|
+
* Настройка коллбеков событий
|
|
492
|
+
*/
|
|
493
|
+
setupEventCallbacks() {
|
|
494
|
+
if (!this.coreMoodboard || !this.coreMoodboard.eventBus) return;
|
|
495
|
+
|
|
496
|
+
// Коллбек для успешного сохранения
|
|
497
|
+
if (typeof this.options.onSave === 'function') {
|
|
498
|
+
this.coreMoodboard.eventBus.on('save:success', (data) => {
|
|
499
|
+
try {
|
|
500
|
+
// Создаем объединенный скриншот с HTML текстом
|
|
501
|
+
let screenshot = null;
|
|
502
|
+
if (this.coreMoodboard.pixi && this.coreMoodboard.pixi.app && this.coreMoodboard.pixi.app.view) {
|
|
503
|
+
screenshot = this.createCombinedScreenshot('image/jpeg', 0.6);
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
this.options.onSave({
|
|
507
|
+
success: true,
|
|
508
|
+
data: data,
|
|
509
|
+
screenshot: screenshot,
|
|
510
|
+
boardId: this.options.boardId
|
|
511
|
+
});
|
|
512
|
+
} catch (error) {
|
|
513
|
+
console.warn('⚠️ Ошибка в коллбеке onSave:', error);
|
|
514
|
+
}
|
|
515
|
+
});
|
|
516
|
+
|
|
517
|
+
// Коллбек для ошибки сохранения
|
|
518
|
+
this.coreMoodboard.eventBus.on('save:error', (data) => {
|
|
519
|
+
try {
|
|
520
|
+
this.options.onSave({
|
|
521
|
+
success: false,
|
|
522
|
+
error: data.error,
|
|
523
|
+
boardId: this.options.boardId
|
|
524
|
+
});
|
|
525
|
+
} catch (error) {
|
|
526
|
+
console.warn('⚠️ Ошибка в коллбеке onSave:', error);
|
|
527
|
+
}
|
|
528
|
+
});
|
|
529
|
+
}
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
/**
|
|
533
|
+
* Получение CSRF токена из всех возможных источников
|
|
534
|
+
*/
|
|
535
|
+
getCsrfToken() {
|
|
536
|
+
return document.querySelector('meta[name="csrf-token"]')?.getAttribute('content') ||
|
|
537
|
+
window.csrfToken ||
|
|
538
|
+
this.options.csrfToken ||
|
|
539
|
+
'';
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
/**
|
|
543
|
+
* Публичный метод для загрузки данных из API
|
|
544
|
+
*/
|
|
545
|
+
async loadFromApi(boardId = null) {
|
|
546
|
+
const targetBoardId = boardId || this.options.boardId;
|
|
547
|
+
if (!targetBoardId) {
|
|
548
|
+
throw new Error('boardId не указан');
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
// Временно меняем boardId для загрузки
|
|
552
|
+
const originalBoardId = this.options.boardId;
|
|
553
|
+
this.options.boardId = targetBoardId;
|
|
554
|
+
|
|
555
|
+
try {
|
|
556
|
+
await this.loadExistingBoard();
|
|
557
|
+
} finally {
|
|
558
|
+
// Восстанавливаем оригинальный boardId
|
|
559
|
+
this.options.boardId = originalBoardId;
|
|
560
|
+
}
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
/**
|
|
564
|
+
* Публичный метод для экспорта скриншота с HTML текстом
|
|
565
|
+
*/
|
|
566
|
+
exportScreenshot(format = 'image/jpeg', quality = 0.6) {
|
|
567
|
+
return this.createCombinedScreenshot(format, quality);
|
|
568
|
+
}
|
|
569
|
+
|
|
570
|
+
/**
|
|
571
|
+
* Разбивает текст на строки с учетом ширины элемента (имитирует HTML word-break: break-word)
|
|
572
|
+
*/
|
|
573
|
+
wrapText(ctx, text, maxWidth) {
|
|
574
|
+
const lines = [];
|
|
575
|
+
|
|
576
|
+
if (!text || maxWidth <= 0) {
|
|
577
|
+
return [text];
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
// Разбиваем по символам если не помещается (имитирует word-break: break-word)
|
|
581
|
+
let currentLine = '';
|
|
582
|
+
|
|
583
|
+
for (let i = 0; i < text.length; i++) {
|
|
584
|
+
const char = text[i];
|
|
585
|
+
const testLine = currentLine + char;
|
|
586
|
+
const metrics = ctx.measureText(testLine);
|
|
587
|
+
|
|
588
|
+
if (metrics.width > maxWidth && currentLine !== '') {
|
|
589
|
+
// Текущая строка не помещается, сохраняем предыдущую
|
|
590
|
+
lines.push(currentLine);
|
|
591
|
+
currentLine = char;
|
|
592
|
+
} else {
|
|
593
|
+
currentLine = testLine;
|
|
594
|
+
}
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
// Добавляем последнюю строку
|
|
598
|
+
if (currentLine) {
|
|
599
|
+
lines.push(currentLine);
|
|
600
|
+
}
|
|
601
|
+
|
|
602
|
+
return lines.length > 0 ? lines : [text];
|
|
603
|
+
}
|
|
604
|
+
|
|
605
|
+
/**
|
|
606
|
+
* Создает объединенный скриншот: PIXI canvas + HTML текстовые элементы
|
|
607
|
+
*/
|
|
608
|
+
createCombinedScreenshot(format = 'image/jpeg', quality = 0.6) {
|
|
609
|
+
if (!this.coreMoodboard || !this.coreMoodboard.pixi || !this.coreMoodboard.pixi.app || !this.coreMoodboard.pixi.app.view) {
|
|
610
|
+
throw new Error('Canvas не найден');
|
|
611
|
+
}
|
|
612
|
+
|
|
613
|
+
try {
|
|
614
|
+
// Получаем PIXI canvas
|
|
615
|
+
const pixiCanvas = this.coreMoodboard.pixi.app.view;
|
|
616
|
+
const pixiWidth = pixiCanvas.width;
|
|
617
|
+
const pixiHeight = pixiCanvas.height;
|
|
618
|
+
|
|
619
|
+
// Создаем временный canvas для объединения
|
|
620
|
+
const combinedCanvas = document.createElement('canvas');
|
|
621
|
+
combinedCanvas.width = pixiWidth;
|
|
622
|
+
combinedCanvas.height = pixiHeight;
|
|
623
|
+
const ctx = combinedCanvas.getContext('2d');
|
|
624
|
+
|
|
625
|
+
// 1. Рисуем PIXI canvas как основу
|
|
626
|
+
ctx.drawImage(pixiCanvas, 0, 0);
|
|
627
|
+
|
|
628
|
+
// 2. Рисуем HTML текстовые элементы поверх
|
|
629
|
+
const textElements = document.querySelectorAll('.mb-text');
|
|
630
|
+
|
|
631
|
+
textElements.forEach((textEl, index) => {
|
|
632
|
+
try {
|
|
633
|
+
// Получаем стили и позицию элемента
|
|
634
|
+
const computedStyle = window.getComputedStyle(textEl);
|
|
635
|
+
const text = textEl.textContent || '';
|
|
636
|
+
|
|
637
|
+
// Проверяем видимость
|
|
638
|
+
if (computedStyle.visibility === 'hidden' || computedStyle.opacity === '0' || !text.trim()) {
|
|
639
|
+
return;
|
|
640
|
+
}
|
|
641
|
+
|
|
642
|
+
// Используем CSS позицию (абсолютная позиция)
|
|
643
|
+
const left = parseInt(textEl.style.left) || 0;
|
|
644
|
+
const top = parseInt(textEl.style.top) || 0;
|
|
645
|
+
|
|
646
|
+
// Настраиваем стили текста
|
|
647
|
+
const fontSize = parseInt(computedStyle.fontSize) || 18;
|
|
648
|
+
const fontFamily = computedStyle.fontFamily || 'Arial, sans-serif';
|
|
649
|
+
const color = computedStyle.color || '#000000';
|
|
650
|
+
|
|
651
|
+
ctx.font = `${fontSize}px ${fontFamily}`;
|
|
652
|
+
ctx.fillStyle = color;
|
|
653
|
+
ctx.textAlign = 'left';
|
|
654
|
+
ctx.textBaseline = 'top';
|
|
655
|
+
|
|
656
|
+
// Получаем размеры элемента
|
|
657
|
+
const elementWidth = parseInt(textEl.style.width) || 182;
|
|
658
|
+
|
|
659
|
+
// Разбиваем текст на строки и рисуем каждую строку
|
|
660
|
+
const lines = this.wrapText(ctx, text, elementWidth);
|
|
661
|
+
const lineHeight = fontSize * 1.3; // Межстрочный интервал
|
|
662
|
+
|
|
663
|
+
lines.forEach((line, lineIndex) => {
|
|
664
|
+
const yPos = top + (lineIndex * lineHeight) + 2;
|
|
665
|
+
ctx.fillText(line, left, yPos);
|
|
666
|
+
});
|
|
667
|
+
} catch (error) {
|
|
668
|
+
console.warn(`⚠️ Ошибка при рисовании текста ${index + 1}:`, error);
|
|
669
|
+
}
|
|
670
|
+
});
|
|
671
|
+
|
|
672
|
+
// 3. Экспортируем объединенный результат
|
|
673
|
+
return combinedCanvas.toDataURL(format, quality);
|
|
674
|
+
|
|
675
|
+
} catch (error) {
|
|
676
|
+
console.warn('⚠️ Ошибка при создании объединенного скриншота, используем только PIXI canvas:', error);
|
|
677
|
+
// Fallback: только PIXI canvas
|
|
678
|
+
const canvas = this.coreMoodboard.pixi.app.view;
|
|
679
|
+
return canvas.toDataURL(format, quality);
|
|
680
|
+
}
|
|
438
681
|
}
|
|
439
682
|
}
|