@sequent-org/moodboard 1.0.19 → 1.0.21
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/ui/styles/workspace.css +39 -33
- package/src/utils/topbarIconLoader.js +130 -84
- package/src/assets/icons/topbar/README.md +0 -39
- /package/src/assets/icons/{topbar/grid-cross.svg → grid-cross.svg} +0 -0
- /package/src/assets/icons/{topbar/grid-dot.svg → grid-dot.svg} +0 -0
- /package/src/assets/icons/{topbar/grid-line.svg → grid-line.svg} +0 -0
- /package/src/assets/icons/{topbar/grid-off.svg → grid-off.svg} +0 -0
- /package/src/assets/icons/{topbar/paint.svg → paint.svg} +0 -0
package/package.json
CHANGED
|
@@ -524,16 +524,16 @@
|
|
|
524
524
|
line-height: 1.4;
|
|
525
525
|
}
|
|
526
526
|
|
|
527
|
-
/* Topbar
|
|
527
|
+
/* Topbar */
|
|
528
528
|
.moodboard-topbar {
|
|
529
529
|
position: absolute;
|
|
530
|
-
top:
|
|
531
|
-
left:
|
|
532
|
-
|
|
533
|
-
display: flex;
|
|
530
|
+
top: 12px;
|
|
531
|
+
left: 16px;
|
|
532
|
+
height: 50px;
|
|
533
|
+
display: inline-flex;
|
|
534
534
|
align-items: center;
|
|
535
535
|
gap: 8px;
|
|
536
|
-
padding: 8px
|
|
536
|
+
padding: 8px 6px;
|
|
537
537
|
background: #fff;
|
|
538
538
|
border: 1px solid #e0e0e0;
|
|
539
539
|
border-radius: 10px;
|
|
@@ -542,35 +542,53 @@
|
|
|
542
542
|
pointer-events: auto;
|
|
543
543
|
}
|
|
544
544
|
|
|
545
|
-
.moodboard-topbar--dark {
|
|
546
|
-
background: #2a2a2a;
|
|
547
|
-
border-color: #444;
|
|
548
|
-
color: #fff;
|
|
549
|
-
}
|
|
550
|
-
|
|
551
545
|
.moodboard-topbar__button {
|
|
552
|
-
|
|
553
|
-
|
|
546
|
+
display: inline-flex;
|
|
547
|
+
align-items: center;
|
|
548
|
+
justify-content: center;
|
|
549
|
+
width: 38px;
|
|
550
|
+
height: 38px;
|
|
554
551
|
border: none;
|
|
555
552
|
border-radius: 8px;
|
|
556
|
-
background: #f0f0f0;
|
|
557
|
-
color: #333;
|
|
553
|
+
/* background: #f0f0f0; */
|
|
554
|
+
/* color: #333; */
|
|
555
|
+
font-size: 16px;
|
|
558
556
|
cursor: pointer;
|
|
559
|
-
display: flex;
|
|
560
|
-
align-items: center;
|
|
561
|
-
justify-content: center;
|
|
562
557
|
transition: all 0.2s ease;
|
|
563
558
|
}
|
|
564
559
|
|
|
560
|
+
/* Стили для SVG иконок в кнопках верхней панели */
|
|
561
|
+
.moodboard-topbar__button svg {
|
|
562
|
+
width: 16px;
|
|
563
|
+
height: 16px;
|
|
564
|
+
/* fill: currentColor;
|
|
565
|
+
stroke: currentColor; */
|
|
566
|
+
transition: all 0.2s ease;
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
.moodboard-topbar__button:hover svg {
|
|
570
|
+
/* transform: scale(1.1); */
|
|
571
|
+
}
|
|
572
|
+
|
|
565
573
|
.moodboard-topbar__button:hover {
|
|
566
574
|
background: #e6e6e6;
|
|
567
575
|
}
|
|
568
576
|
|
|
577
|
+
/* Divider for topbar */
|
|
578
|
+
.moodboard-topbar__divider {
|
|
579
|
+
width: 1px;
|
|
580
|
+
height: 28px;
|
|
581
|
+
background: #e5e7eb;
|
|
582
|
+
margin: 0 6px 0 2px;
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
/* Active state for topbar buttons */
|
|
569
586
|
.moodboard-topbar__button--active {
|
|
570
|
-
background: #
|
|
571
|
-
color: #
|
|
587
|
+
background: #dbeafe;
|
|
588
|
+
color: #1d4ed8;
|
|
572
589
|
}
|
|
573
590
|
|
|
591
|
+
/* Paint button specific styles */
|
|
574
592
|
.moodboard-topbar__button--paint {
|
|
575
593
|
background: #f0f0f0;
|
|
576
594
|
}
|
|
@@ -579,18 +597,6 @@
|
|
|
579
597
|
background: #e6e6e6;
|
|
580
598
|
}
|
|
581
599
|
|
|
582
|
-
.moodboard-topbar__divider {
|
|
583
|
-
width: 1px;
|
|
584
|
-
height: 24px;
|
|
585
|
-
background: #e0e0e0;
|
|
586
|
-
margin: 0 4px;
|
|
587
|
-
}
|
|
588
|
-
|
|
589
|
-
.moodboard-topbar svg {
|
|
590
|
-
width: 18px;
|
|
591
|
-
height: 18px;
|
|
592
|
-
}
|
|
593
|
-
|
|
594
600
|
/* Paint popover */
|
|
595
601
|
.moodboard-topbar__paint-popover {
|
|
596
602
|
position: absolute;
|
|
@@ -1,77 +1,53 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Загрузчик SVG иконок для верхней панели
|
|
3
|
+
* Работает точно так же как IconLoader для левой панели
|
|
3
4
|
*/
|
|
4
5
|
export class TopbarIconLoader {
|
|
5
6
|
constructor() {
|
|
6
|
-
this.
|
|
7
|
-
this.
|
|
7
|
+
this.cache = new Map();
|
|
8
|
+
this.icons = {};
|
|
8
9
|
}
|
|
9
10
|
|
|
11
|
+
/**
|
|
12
|
+
* Инициализирует иконки при создании экземпляра
|
|
13
|
+
*/
|
|
10
14
|
async init() {
|
|
15
|
+
// Импортируем все SVG файлы статически, как в IconLoader
|
|
11
16
|
try {
|
|
12
|
-
//
|
|
13
|
-
await
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
this.loadBuiltInIcons();
|
|
21
|
-
}
|
|
22
|
-
}
|
|
17
|
+
// Используем динамический импорт для всех иконок topbar
|
|
18
|
+
const iconModules = await Promise.all([
|
|
19
|
+
import('../assets/icons/grid-line.svg?raw'),
|
|
20
|
+
import('../assets/icons/grid-dot.svg?raw'),
|
|
21
|
+
import('../assets/icons/grid-cross.svg?raw'),
|
|
22
|
+
import('../assets/icons/grid-off.svg?raw'),
|
|
23
|
+
import('../assets/icons/paint.svg?raw')
|
|
24
|
+
]);
|
|
23
25
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
const iconNames = ['grid-line', 'grid-dot', 'grid-cross', 'grid-off', 'paint'];
|
|
27
|
-
|
|
28
|
-
for (const iconName of iconNames) {
|
|
29
|
-
try {
|
|
30
|
-
const svgContent = await this.loadIconFromFile(iconName);
|
|
31
|
-
this.icons.set(iconName, svgContent);
|
|
32
|
-
console.log(`✅ Загружена иконка: ${iconName}`);
|
|
33
|
-
} catch (error) {
|
|
34
|
-
console.warn(`⚠️ Не удалось загрузить иконку ${iconName}:`, error);
|
|
35
|
-
// Если не удалось загрузить из файла, используем встроенную версию
|
|
36
|
-
const builtInIcon = this.getBuiltInIcon(iconName);
|
|
37
|
-
if (builtInIcon) {
|
|
38
|
-
this.icons.set(iconName, builtInIcon);
|
|
39
|
-
console.log(`✅ Использована встроенная иконка для: ${iconName}`);
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
console.log(`📦 Загружено ${this.icons.size} иконок верхней панели`);
|
|
45
|
-
}
|
|
26
|
+
// Сохраняем иконки в кэш
|
|
27
|
+
const iconNames = ['grid-line', 'grid-dot', 'grid-cross', 'grid-off', 'paint'];
|
|
46
28
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
];
|
|
55
|
-
|
|
56
|
-
for (const path of paths) {
|
|
57
|
-
try {
|
|
58
|
-
const response = await fetch(path);
|
|
59
|
-
if (response.ok) {
|
|
60
|
-
const svgContent = await response.text();
|
|
61
|
-
console.log(`✅ Иконка ${iconName} загружена с пути: ${path}`);
|
|
62
|
-
return svgContent;
|
|
29
|
+
iconNames.forEach((name, index) => {
|
|
30
|
+
if (iconModules[index] && iconModules[index].default) {
|
|
31
|
+
this.icons[name] = iconModules[index].default;
|
|
32
|
+
this.cache.set(name, iconModules[index].default);
|
|
33
|
+
} else {
|
|
34
|
+
console.warn(`⚠️ Иконка ${name} не загружена, используем fallback`);
|
|
35
|
+
this.icons[name] = this.getFallbackIcon(name);
|
|
63
36
|
}
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
} catch (error) {
|
|
40
|
+
console.error('❌ Ошибка статической загрузки иконок topbar:', error);
|
|
41
|
+
console.log('🔄 Пробуем загрузить встроенные SVG иконки...');
|
|
42
|
+
// В случае ошибки загружаем встроенные SVG иконки
|
|
43
|
+
this.loadBuiltInIcons();
|
|
68
44
|
}
|
|
69
|
-
|
|
70
|
-
throw new Error(`Не удалось загрузить иконку ${iconName} ни с одного из путей`);
|
|
71
45
|
}
|
|
72
46
|
|
|
73
|
-
|
|
74
|
-
|
|
47
|
+
/**
|
|
48
|
+
* Загружает встроенные SVG иконки (резервный метод)
|
|
49
|
+
*/
|
|
50
|
+
loadBuiltInIcons() {
|
|
75
51
|
const builtInIcons = {
|
|
76
52
|
'grid-line': `<svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
77
53
|
<path d="M2 2H16V4H2V2Z" fill="currentColor"/>
|
|
@@ -119,44 +95,114 @@ export class TopbarIconLoader {
|
|
|
119
95
|
</svg>`
|
|
120
96
|
};
|
|
121
97
|
|
|
122
|
-
|
|
98
|
+
Object.keys(builtInIcons).forEach(name => {
|
|
99
|
+
this.icons[name] = builtInIcons[name];
|
|
100
|
+
this.cache.set(name, builtInIcons[name]);
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
console.log('📦 Загружены встроенные SVG иконки topbar');
|
|
123
104
|
}
|
|
124
105
|
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
106
|
+
/**
|
|
107
|
+
* Загружает SVG иконку по имени
|
|
108
|
+
* @param {string} iconName - имя иконки без расширения
|
|
109
|
+
* @returns {Promise<string>} SVG содержимое
|
|
110
|
+
*/
|
|
111
|
+
async loadIcon(iconName) {
|
|
112
|
+
if (this.cache.has(iconName)) {
|
|
113
|
+
console.log(`📦 Загружаем иконку ${iconName} из кэша`);
|
|
114
|
+
return this.cache.get(iconName);
|
|
134
115
|
}
|
|
135
|
-
|
|
136
|
-
console.log(`📦 Загружено ${this.icons.size} встроенных иконок верхней панели (fallback)`);
|
|
137
|
-
}
|
|
138
116
|
|
|
139
|
-
|
|
140
|
-
|
|
117
|
+
// Если иконка уже загружена статически
|
|
118
|
+
if (this.icons[iconName]) {
|
|
119
|
+
console.log(`📦 Загружаем иконку ${iconName} из статического кэша`);
|
|
120
|
+
return this.icons[iconName];
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// Возвращаем fallback
|
|
124
|
+
console.warn(`⚠️ Иконка ${iconName} не найдена, используем fallback`);
|
|
125
|
+
return this.getFallbackIcon(iconName);
|
|
141
126
|
}
|
|
142
127
|
|
|
143
128
|
/**
|
|
144
|
-
* Загружает все иконки
|
|
129
|
+
* Загружает все иконки для topbar
|
|
130
|
+
* @returns {Promise<Object>} объект с иконками
|
|
145
131
|
*/
|
|
146
132
|
async loadAllIcons() {
|
|
147
|
-
|
|
148
|
-
this.icons.
|
|
149
|
-
|
|
150
|
-
}
|
|
151
|
-
|
|
133
|
+
// Если иконки еще не инициализированы
|
|
134
|
+
if (Object.keys(this.icons).length === 0) {
|
|
135
|
+
await this.init();
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
return this.icons;
|
|
152
139
|
}
|
|
153
140
|
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
141
|
+
/**
|
|
142
|
+
* Возвращает fallback иконку если загрузка не удалась
|
|
143
|
+
* @param {string} iconName - имя иконки
|
|
144
|
+
* @returns {string} fallback SVG
|
|
145
|
+
*/
|
|
146
|
+
getFallbackIcon(iconName) {
|
|
147
|
+
// Простые fallback иконки в виде геометрических фигур
|
|
148
|
+
const fallbacks = {
|
|
149
|
+
'grid-line': '<svg width="18" height="18" viewBox="0 0 18 18"><path d="M2 2H16V4H2V2Z" fill="currentColor"/><path d="M2 7H16V9H2V7Z" fill="currentColor"/><path d="M2 12H16V14H2V12Z" fill="currentColor"/><path d="M2 2V16H4V2H2Z" fill="currentColor"/><path d="M7 2V16H9V2H7Z" fill="currentColor"/><path d="M12 2V16H14V2H12Z" fill="currentColor"/></svg>',
|
|
150
|
+
'grid-dot': '<svg width="18" height="18" viewBox="0 0 18 18"><circle cx="4" cy="4" r="1.5" fill="currentColor"/><circle cx="9" cy="4" r="1.5" fill="currentColor"/><circle cx="14" cy="4" r="1.5" fill="currentColor"/><circle cx="4" cy="9" r="1.5" fill="currentColor"/><circle cx="9" cy="9" r="1.5" fill="currentColor"/><circle cx="14" cy="9" r="1.5" fill="currentColor"/><circle cx="4" cy="14" r="1.5" fill="currentColor"/><circle cx="9" cy="14" r="1.5" fill="currentColor"/><circle cx="14" cy="14" r="1.5" fill="currentColor"/></svg>',
|
|
151
|
+
'grid-cross': '<svg width="18" height="18" viewBox="0 0 18 18"><path d="M3 3L6 6M6 3L3 6" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/><path d="M9 3L12 6M12 3L9 6" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/><path d="M3 9L6 12M6 9L3 12" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/><path d="M9 9L12 12M12 9L9 12" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/></svg>',
|
|
152
|
+
'grid-off': '<svg width="18" height="18" viewBox="0 0 18 18"><path d="M2 2H16V4H2V2Z" fill="currentColor" opacity="0.3"/><path d="M2 7H16V9H2V7Z" fill="currentColor" opacity="0.3"/><path d="M2 12H16V14H2V12Z" fill="currentColor" opacity="0.3"/><path d="M2 2V16H4V2H2Z" fill="currentColor" opacity="0.3"/><path d="M7 2V16H9V2H7Z" fill="currentColor" opacity="0.3"/><path d="M12 2V16H14V2H12Z" fill="currentColor" opacity="0.3"/><path d="M1 17L17 1" stroke="currentColor" stroke-width="2" stroke-linecap="round"/></svg>',
|
|
153
|
+
'paint': '<svg width="18" height="18" viewBox="0 0 18 18"><path d="M4 3H10L13 6V13A2 2 0 0 1 11 15H6A2 2 0 0 1 4 13V3Z" stroke="currentColor" stroke-width="1.5" fill="none"/><path d="M10 3V6H13" stroke="currentColor" stroke-width="1.5"/><path d="M14 10S15.5 11.5 15.5 13A1.5 1.5 0 0 1 13 13C13 11.5 14 10 14 10Z" fill="currentColor" stroke="currentColor" stroke-width="0.5"/></svg>'
|
|
154
|
+
};
|
|
155
|
+
|
|
156
|
+
return fallbacks[iconName] || fallbacks['grid-line'];
|
|
157
157
|
}
|
|
158
158
|
|
|
159
|
+
/**
|
|
160
|
+
* Очищает кэш иконок
|
|
161
|
+
*/
|
|
159
162
|
clearCache() {
|
|
160
|
-
this.
|
|
163
|
+
this.cache.clear();
|
|
161
164
|
}
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* Принудительно перезагружает иконку (игнорируя кэш)
|
|
168
|
+
* @param {string} iconName - имя иконки без расширения
|
|
169
|
+
* @returns {Promise<string>} SVG содержимое
|
|
170
|
+
*/
|
|
171
|
+
async reloadIcon(iconName) {
|
|
172
|
+
// Удаляем из кэша
|
|
173
|
+
console.log(`🗑️ Очищаем кэш для иконки ${iconName}`);
|
|
174
|
+
this.cache.delete(iconName);
|
|
175
|
+
|
|
176
|
+
try {
|
|
177
|
+
// Пробуем переимпортировать иконку
|
|
178
|
+
const iconModule = await import(`../assets/icons/${iconName}.svg?raw`);
|
|
179
|
+
if (iconModule && iconModule.default) {
|
|
180
|
+
const svgContent = iconModule.default;
|
|
181
|
+
console.log(`✅ Иконка ${iconName} перезагружена успешно`);
|
|
182
|
+
this.icons[iconName] = svgContent;
|
|
183
|
+
this.cache.set(iconName, svgContent);
|
|
184
|
+
return svgContent;
|
|
185
|
+
} else {
|
|
186
|
+
throw new Error(`Failed to reload icon: ${iconName}`);
|
|
187
|
+
}
|
|
188
|
+
} catch (error) {
|
|
189
|
+
console.error(`❌ Ошибка перезагрузки иконки ${iconName}:`, error);
|
|
190
|
+
return this.getFallbackIcon(iconName);
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
/**
|
|
195
|
+
* Получает иконку по имени (синхронный метод для совместимости)
|
|
196
|
+
*/
|
|
197
|
+
getIcon(name) {
|
|
198
|
+
return this.icons[name] || this.getFallbackIcon(name);
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
// Создаем глобальный экземпляр
|
|
203
|
+
export const topbarIconLoader = new TopbarIconLoader();
|
|
204
|
+
|
|
205
|
+
// Добавляем в глобальную область для отладки
|
|
206
|
+
if (typeof window !== 'undefined') {
|
|
207
|
+
window.topbarIconLoader = topbarIconLoader;
|
|
162
208
|
}
|
|
@@ -1,39 +0,0 @@
|
|
|
1
|
-
# SVG Иконки для Верхней Панели
|
|
2
|
-
|
|
3
|
-
Эта папка содержит SVG иконки для кнопок верхней панели MoodBoard.
|
|
4
|
-
|
|
5
|
-
## Иконки
|
|
6
|
-
|
|
7
|
-
### Сетка
|
|
8
|
-
- **`grid-line.svg`** - Сетка с линиями
|
|
9
|
-
- **`grid-dot.svg`** - Сетка с точками
|
|
10
|
-
- **`grid-cross.svg`** - Сетка с крестиками
|
|
11
|
-
- **`grid-off.svg`** - Сетка выключена (с перечеркиванием)
|
|
12
|
-
|
|
13
|
-
### Инструменты
|
|
14
|
-
- **`paint.svg`** - Палитра фона
|
|
15
|
-
|
|
16
|
-
## Технические детали
|
|
17
|
-
|
|
18
|
-
- **Размер**: 18x18 пикселей
|
|
19
|
-
- **ViewBox**: 0 0 18 18
|
|
20
|
-
- **Цвет**: Использует `currentColor` для наследования цвета от родительского элемента
|
|
21
|
-
- **Формат**: SVG с оптимизированными путями
|
|
22
|
-
|
|
23
|
-
## Использование
|
|
24
|
-
|
|
25
|
-
Иконки загружаются через `TopbarIconLoader` и добавляются в DOM как SVG символы с ID вида `icon-{имя}`.
|
|
26
|
-
|
|
27
|
-
```html
|
|
28
|
-
<svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
29
|
-
<use href="#icon-grid-line"/>
|
|
30
|
-
</svg>
|
|
31
|
-
```
|
|
32
|
-
|
|
33
|
-
## Добавление новых иконок
|
|
34
|
-
|
|
35
|
-
1. Создайте SVG файл в этой папке
|
|
36
|
-
2. Убедитесь, что используется `currentColor` для цвета
|
|
37
|
-
3. Добавьте иконку в `TopbarIconLoader.loadBuiltInIcons()` как fallback
|
|
38
|
-
4. Обновите соответствующий код в `Topbar.js`
|
|
39
|
-
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|