@sequent-org/moodboard 1.4.56 → 1.4.58
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/core/commands/PasteObjectCommand.js +7 -1
- package/src/core/commands/UpdateContentCommand.js +95 -0
- package/src/core/flows/ObjectLifecycleFlow.js +10 -1
- package/src/initNoBundler.js +28 -4
- package/src/services/text/TextBoxMetrics.js +133 -0
- package/src/tools/object-tools/selection/MindmapInlineEditorController.js +98 -7
- package/src/tools/object-tools/selection/SelectInputRouter.js +56 -3
- package/src/tools/object-tools/selection/TextEditorCaretService.js +113 -0
- package/src/tools/object-tools/selection/TextEditorDomFactory.js +37 -21
- package/src/tools/object-tools/selection/TextEditorInteractionController.js +111 -12
- package/src/tools/object-tools/selection/TextEditorPositioningService.js +19 -1
- package/src/tools/object-tools/selection/TextEditorSyncService.js +2 -1
- package/src/tools/object-tools/selection/TextInlineEditorController.js +70 -45
- package/src/tools/object-tools/selection/TransformInteractionController.js +7 -1
- package/src/ui/FramePropertiesPanel.js +7 -0
- package/src/ui/HtmlTextLayer.js +174 -64
- package/src/ui/ImagePropertiesPanel.js +7 -0
- package/src/ui/TextPropertiesPanel.js +305 -2
- package/src/ui/handles/HandlesDomRenderer.js +2 -0
- package/src/ui/handles/HandlesInteractionController.js +40 -0
- package/src/ui/styles/panels.css +240 -19
- package/src/ui/styles/workspace.css +94 -7
- package/src/ui/text-properties/TextFormatControls.js +132 -21
- package/src/ui/text-properties/TextLinkControl.js +255 -0
- package/src/ui/text-properties/TextLockMoreControls.js +173 -0
- package/src/ui/text-properties/TextPropertiesPanelBindings.js +45 -0
- package/src/ui/text-properties/TextPropertiesPanelMapper.js +52 -0
- package/src/ui/text-properties/TextPropertiesPanelRenderer.js +263 -26
- package/src/ui/text-properties/TextPropertiesPanelState.js +6 -0
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { updateLinkButtonState } from './TextLinkControl.js';
|
|
2
|
+
|
|
1
3
|
function hidePresetTicks(buttons) {
|
|
2
4
|
buttons.forEach((button) => {
|
|
3
5
|
const tick = button.querySelector('i');
|
|
@@ -108,6 +110,43 @@ export function bindTextPropertiesPanelControls(panel) {
|
|
|
108
110
|
};
|
|
109
111
|
document.addEventListener('click', panel._onColorDocumentClick);
|
|
110
112
|
|
|
113
|
+
if (panel.currentHighlightButton) {
|
|
114
|
+
// Снимок выделения textarea до ухода фокуса (capture-фаза, до click/blur).
|
|
115
|
+
// Покрывает и кнопку, и пресеты, и нативный color-input внутри контейнера.
|
|
116
|
+
if (panel._highlightSelectorContainer) {
|
|
117
|
+
panel._highlightSelectorContainer.addEventListener('mousedown', () => {
|
|
118
|
+
panel._snapshotTextSelection();
|
|
119
|
+
}, true);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
panel.currentHighlightButton.addEventListener('click', (event) => {
|
|
123
|
+
event.stopPropagation();
|
|
124
|
+
panel._toggleHighlightDropdown();
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
panel._highlightPresetButtons.forEach((button) => {
|
|
128
|
+
button.addEventListener('click', () => {
|
|
129
|
+
hidePresetTicks(panel._highlightPresetButtons);
|
|
130
|
+
const tick = button.querySelector('i');
|
|
131
|
+
if (tick) {
|
|
132
|
+
tick.style.display = 'block';
|
|
133
|
+
}
|
|
134
|
+
panel._selectHighlightColor(button.dataset.colorValue);
|
|
135
|
+
});
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
panel.highlightInput.addEventListener('change', (event) => {
|
|
139
|
+
panel._selectHighlightColor(event.target.value);
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
panel._onHighlightDocumentClick = (event) => {
|
|
143
|
+
if (!panel._highlightSelectorContainer || !event.target || !panel._highlightSelectorContainer.contains(event.target)) {
|
|
144
|
+
panel._hideHighlightDropdown();
|
|
145
|
+
}
|
|
146
|
+
};
|
|
147
|
+
document.addEventListener('click', panel._onHighlightDocumentClick);
|
|
148
|
+
}
|
|
149
|
+
|
|
111
150
|
panel.currentBgColorButton.addEventListener('click', (event) => {
|
|
112
151
|
event.stopPropagation();
|
|
113
152
|
panel._toggleBgColorDropdown();
|
|
@@ -138,6 +177,7 @@ export function bindTextPropertiesPanelControls(panel) {
|
|
|
138
177
|
if (panel.markdownToggle) {
|
|
139
178
|
panel.markdownToggle.addEventListener('change', (event) => {
|
|
140
179
|
panel._changeMarkdown(event.target.checked);
|
|
180
|
+
updateLinkButtonState(panel, event.target.checked);
|
|
141
181
|
});
|
|
142
182
|
}
|
|
143
183
|
|
|
@@ -171,6 +211,11 @@ export function unbindTextPropertiesPanelControls(panel) {
|
|
|
171
211
|
panel._onColorDocumentClick = null;
|
|
172
212
|
}
|
|
173
213
|
|
|
214
|
+
if (panel._onHighlightDocumentClick) {
|
|
215
|
+
document.removeEventListener('click', panel._onHighlightDocumentClick);
|
|
216
|
+
panel._onHighlightDocumentClick = null;
|
|
217
|
+
}
|
|
218
|
+
|
|
174
219
|
if (panel._onBgDocumentClick) {
|
|
175
220
|
document.removeEventListener('click', panel._onBgDocumentClick);
|
|
176
221
|
panel._onBgDocumentClick = null;
|
|
@@ -97,6 +97,7 @@ export function getControlValuesFromProperties(properties) {
|
|
|
97
97
|
fontFamily: properties.fontFamily || 'Roboto, Arial, sans-serif',
|
|
98
98
|
fontSize: String(properties.fontSize || 18),
|
|
99
99
|
color: properties.color || '#000000',
|
|
100
|
+
highlightColor: properties.highlightColor !== undefined ? properties.highlightColor : 'transparent',
|
|
100
101
|
backgroundColor: properties.backgroundColor !== undefined ? properties.backgroundColor : 'transparent',
|
|
101
102
|
markdown: properties.markdown === true,
|
|
102
103
|
bold: properties.bold === true,
|
|
@@ -114,6 +115,7 @@ export function getFallbackControlValues() {
|
|
|
114
115
|
fontFamily: 'Arial, sans-serif',
|
|
115
116
|
fontSize: '18',
|
|
116
117
|
color: '#000000',
|
|
118
|
+
highlightColor: 'transparent',
|
|
117
119
|
backgroundColor: 'transparent',
|
|
118
120
|
markdown: false,
|
|
119
121
|
bold: false,
|
|
@@ -150,6 +152,12 @@ export function buildBackgroundColorUpdate(backgroundColor) {
|
|
|
150
152
|
};
|
|
151
153
|
}
|
|
152
154
|
|
|
155
|
+
export function buildHighlightColorUpdate(highlightColor) {
|
|
156
|
+
return {
|
|
157
|
+
properties: { highlightColor },
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
|
|
153
161
|
export function buildMarkdownUpdate(markdown) {
|
|
154
162
|
return {
|
|
155
163
|
properties: { markdown },
|
|
@@ -162,6 +170,35 @@ export function buildPropertyUpdate(key, value) {
|
|
|
162
170
|
|
|
163
171
|
export function applyTextAppearanceToDom(objectId, properties) {
|
|
164
172
|
const htmlElement = document.querySelector(`[data-id="${objectId}"]`);
|
|
173
|
+
|
|
174
|
+
// Также обновляем активный редактор, если он открыт
|
|
175
|
+
const activeEditor = document.querySelector('.moodboard-text-editor');
|
|
176
|
+
if (activeEditor) {
|
|
177
|
+
const textarea = activeEditor.querySelector('.moodboard-text-input');
|
|
178
|
+
const backdrop = activeEditor.querySelector('.moodboard-text-backdrop');
|
|
179
|
+
|
|
180
|
+
if (textarea) {
|
|
181
|
+
if (properties.fontFamily) {
|
|
182
|
+
textarea.style.fontFamily = properties.fontFamily;
|
|
183
|
+
if (backdrop) backdrop.style.fontFamily = properties.fontFamily;
|
|
184
|
+
}
|
|
185
|
+
if (properties.fontSize) {
|
|
186
|
+
textarea.style.fontSize = `${properties.fontSize}px`;
|
|
187
|
+
if (backdrop) backdrop.style.fontSize = `${properties.fontSize}px`;
|
|
188
|
+
}
|
|
189
|
+
if (properties.textAlign !== undefined) {
|
|
190
|
+
textarea.style.textAlign = properties.textAlign;
|
|
191
|
+
if (backdrop) backdrop.style.textAlign = properties.textAlign;
|
|
192
|
+
}
|
|
193
|
+
if (properties.color && backdrop) {
|
|
194
|
+
backdrop.style.color = properties.color;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
// Эмулируем событие input для перерисовки backdrop
|
|
198
|
+
textarea.dispatchEvent(new Event('input', { bubbles: true }));
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
|
|
165
202
|
if (!htmlElement) {
|
|
166
203
|
return;
|
|
167
204
|
}
|
|
@@ -182,6 +219,21 @@ export function applyTextAppearanceToDom(objectId, properties) {
|
|
|
182
219
|
htmlElement.style.backgroundColor = properties.backgroundColor;
|
|
183
220
|
}
|
|
184
221
|
}
|
|
222
|
+
if (properties.highlightColor !== undefined) {
|
|
223
|
+
// Устанавливаем CSS-переменную --highlight-color на элементе текста,
|
|
224
|
+
// чтобы она могла использоваться для подкраски выделенного фрагмента
|
|
225
|
+
if (properties.highlightColor === 'transparent') {
|
|
226
|
+
htmlElement.style.removeProperty('--highlight-color');
|
|
227
|
+
// Не сбрасываем backgroundColor, так как он может быть установлен отдельно
|
|
228
|
+
} else {
|
|
229
|
+
htmlElement.style.setProperty('--highlight-color', properties.highlightColor);
|
|
230
|
+
// Для обычного текста без Quill мы можем просто установить backgroundColor
|
|
231
|
+
// если нет выделения
|
|
232
|
+
if (!htmlElement.querySelector('span[style*="background-color"]')) {
|
|
233
|
+
htmlElement.style.backgroundColor = properties.highlightColor;
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
}
|
|
185
237
|
if (properties.bold !== undefined) {
|
|
186
238
|
htmlElement.style.fontWeight = properties.bold ? 'bold' : '';
|
|
187
239
|
}
|
|
@@ -5,6 +5,7 @@ import {
|
|
|
5
5
|
TEXT_COLOR_PRESETS,
|
|
6
6
|
} from './TextPropertiesPanelMapper.js';
|
|
7
7
|
import { createTextFormatControls } from './TextFormatControls.js';
|
|
8
|
+
import { createTextLockMoreControls } from './TextLockMoreControls.js';
|
|
8
9
|
|
|
9
10
|
export function createTextPropertiesPanelRenderer(panelInstance) {
|
|
10
11
|
const panel = document.createElement('div');
|
|
@@ -36,7 +37,9 @@ export function hideColorDropdown(panelInstance) {
|
|
|
36
37
|
|
|
37
38
|
export function updateCurrentColorButton(panelInstance, color) {
|
|
38
39
|
if (panelInstance.currentColorButton) {
|
|
39
|
-
panelInstance.
|
|
40
|
+
if (panelInstance.colorIndicator) {
|
|
41
|
+
panelInstance.colorIndicator.style.backgroundColor = color;
|
|
42
|
+
}
|
|
40
43
|
panelInstance.currentColorButton.title = `Текущий цвет: ${color}`;
|
|
41
44
|
}
|
|
42
45
|
if (panelInstance.colorInput) {
|
|
@@ -44,6 +47,36 @@ export function updateCurrentColorButton(panelInstance, color) {
|
|
|
44
47
|
}
|
|
45
48
|
}
|
|
46
49
|
|
|
50
|
+
export function toggleHighlightDropdown(panelInstance) {
|
|
51
|
+
if (!panelInstance.highlightDropdown) {
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
if (panelInstance.highlightDropdown.style.display === 'none') {
|
|
56
|
+
panelInstance.highlightDropdown.style.display = 'block';
|
|
57
|
+
} else {
|
|
58
|
+
panelInstance.highlightDropdown.style.display = 'none';
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export function hideHighlightDropdown(panelInstance) {
|
|
63
|
+
if (panelInstance.highlightDropdown) {
|
|
64
|
+
panelInstance.highlightDropdown.style.display = 'none';
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export function updateCurrentHighlightButton(panelInstance, color) {
|
|
69
|
+
if (panelInstance.currentHighlightButton) {
|
|
70
|
+
if (panelInstance.highlightIndicator) {
|
|
71
|
+
panelInstance.highlightIndicator.style.backgroundColor = color === 'transparent' ? 'transparent' : color;
|
|
72
|
+
}
|
|
73
|
+
panelInstance.currentHighlightButton.title = color === 'transparent' ? 'Без фона текста' : `Цвет фона текста: ${color}`;
|
|
74
|
+
}
|
|
75
|
+
if (panelInstance.highlightInput) {
|
|
76
|
+
panelInstance.highlightInput.value = color === 'transparent' ? '#ffff99' : color;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
47
80
|
export function toggleBgColorDropdown(panelInstance) {
|
|
48
81
|
if (!panelInstance.bgColorDropdown) {
|
|
49
82
|
return;
|
|
@@ -68,24 +101,27 @@ export function updateCurrentBgColorButton(panelInstance, color) {
|
|
|
68
101
|
panelInstance.currentBgColorButton.style.backgroundColor = 'white';
|
|
69
102
|
panelInstance.currentBgColorButton.title = 'Без выделения';
|
|
70
103
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
line.
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
transform: translate(-50%, -50%) rotate(45deg);
|
|
83
|
-
`;
|
|
84
|
-
panelInstance.currentBgColorButton.appendChild(line);
|
|
104
|
+
const line = panelInstance.currentBgColorButton.querySelector('div');
|
|
105
|
+
if (line) {
|
|
106
|
+
line.remove();
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
if (!panelInstance.currentBgColorButton.querySelector('img.no-color-icon')) {
|
|
110
|
+
const img = document.createElement('img');
|
|
111
|
+
img.src = '/icons/no-color.svg';
|
|
112
|
+
img.className = 'no-color-icon';
|
|
113
|
+
img.style.cssText = 'width: 20px; height: 20px; pointer-events: none; position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%);';
|
|
114
|
+
panelInstance.currentBgColorButton.appendChild(img);
|
|
85
115
|
}
|
|
86
116
|
} else {
|
|
87
117
|
panelInstance.currentBgColorButton.style.backgroundColor = color;
|
|
88
118
|
panelInstance.currentBgColorButton.title = `Цвет выделения: ${color}`;
|
|
119
|
+
|
|
120
|
+
const img = panelInstance.currentBgColorButton.querySelector('img.no-color-icon');
|
|
121
|
+
if (img) {
|
|
122
|
+
img.remove();
|
|
123
|
+
}
|
|
124
|
+
|
|
89
125
|
const line = panelInstance.currentBgColorButton.querySelector('div');
|
|
90
126
|
if (line) {
|
|
91
127
|
line.remove();
|
|
@@ -114,6 +150,7 @@ function createFontControls(panelInstance, panel) {
|
|
|
114
150
|
trigger.appendChild(triggerLabel);
|
|
115
151
|
|
|
116
152
|
const dropdown = document.createElement('div');
|
|
153
|
+
dropdown.id = `tpp-font-dropdown-${Date.now()}-${Math.floor(Math.random() * 10000)}`;
|
|
117
154
|
dropdown.className = 'font-dropdown';
|
|
118
155
|
dropdown.setAttribute('role', 'listbox');
|
|
119
156
|
|
|
@@ -167,6 +204,10 @@ function createFontControls(panelInstance, panel) {
|
|
|
167
204
|
panelInstance.fontDropdown = dropdown;
|
|
168
205
|
panelInstance._fontSelectWrapper = fontWrapper;
|
|
169
206
|
|
|
207
|
+
const fontSeparator = document.createElement('div');
|
|
208
|
+
fontSeparator.style.cssText = 'width:1px;height:18px;background:#e0e0e0;margin:0 6px;flex-shrink:0;';
|
|
209
|
+
panel.appendChild(fontSeparator);
|
|
210
|
+
|
|
170
211
|
const fontSizeWrapper = document.createElement('div');
|
|
171
212
|
fontSizeWrapper.className = 'font-size-wrapper';
|
|
172
213
|
|
|
@@ -201,8 +242,14 @@ function createFontControls(panelInstance, panel) {
|
|
|
201
242
|
|
|
202
243
|
panel.appendChild(fontSizeWrapper);
|
|
203
244
|
|
|
245
|
+
const colorSeparator = document.createElement('div');
|
|
246
|
+
colorSeparator.style.cssText = 'width:1px;height:18px;background:#e0e0e0;margin:0 6px;flex-shrink:0;';
|
|
247
|
+
panel.appendChild(colorSeparator);
|
|
248
|
+
|
|
204
249
|
createCompactColorSelector(panelInstance, panel);
|
|
205
250
|
|
|
251
|
+
createCompactHighlightSelector(panelInstance, panel);
|
|
252
|
+
|
|
206
253
|
createCompactBackgroundSelector(panelInstance, panel);
|
|
207
254
|
|
|
208
255
|
const mdSeparator = document.createElement('div');
|
|
@@ -227,6 +274,8 @@ function createFontControls(panelInstance, panel) {
|
|
|
227
274
|
panel.appendChild(mdLabel);
|
|
228
275
|
|
|
229
276
|
createTextFormatControls(panelInstance, panel);
|
|
277
|
+
|
|
278
|
+
createTextLockMoreControls(panelInstance, panel);
|
|
230
279
|
}
|
|
231
280
|
|
|
232
281
|
function createCompactColorSelector(panelInstance, panel) {
|
|
@@ -234,7 +283,6 @@ function createCompactColorSelector(panelInstance, panel) {
|
|
|
234
283
|
colorSelectorContainer.style.cssText = `
|
|
235
284
|
position: relative;
|
|
236
285
|
display: inline-block;
|
|
237
|
-
margin-left: 4px;
|
|
238
286
|
`;
|
|
239
287
|
panelInstance._colorSelectorContainer = colorSelectorContainer;
|
|
240
288
|
|
|
@@ -243,7 +291,26 @@ function createCompactColorSelector(panelInstance, panel) {
|
|
|
243
291
|
panelInstance.currentColorButton.title = 'Выбрать цвет';
|
|
244
292
|
panelInstance.currentColorButton.className = 'current-color-button';
|
|
245
293
|
|
|
294
|
+
const colorIcon = document.createElement('span');
|
|
295
|
+
colorIcon.innerHTML = '<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" xmlns="http://www.w3.org/2000/svg" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><path d="M8.5636 13.9875L12 5L15.4364 13.9875"></path><path d="M9.88525 10.8155H14.1147"></path></svg>';
|
|
296
|
+
colorIcon.style.cssText = 'width: 24px; height: 24px; pointer-events: none; display: flex; align-items: center; justify-content: center;';
|
|
297
|
+
|
|
298
|
+
panelInstance.colorIndicator = document.createElement('div');
|
|
299
|
+
panelInstance.colorIndicator.style.cssText = `
|
|
300
|
+
width: 18px;
|
|
301
|
+
height: 5px;
|
|
302
|
+
position: absolute;
|
|
303
|
+
bottom: 2px;
|
|
304
|
+
background: rgb(255, 235, 164);
|
|
305
|
+
box-shadow: rgba(0, 0, 0, 0.1) 0px 0px 0px 1px inset;
|
|
306
|
+
border-radius: 2px;
|
|
307
|
+
`;
|
|
308
|
+
|
|
309
|
+
panelInstance.currentColorButton.appendChild(colorIcon);
|
|
310
|
+
panelInstance.currentColorButton.appendChild(panelInstance.colorIndicator);
|
|
311
|
+
|
|
246
312
|
panelInstance.colorDropdown = document.createElement('div');
|
|
313
|
+
panelInstance.colorDropdown.id = `tpp-color-dropdown-${Date.now()}-${Math.floor(Math.random() * 10000)}`;
|
|
247
314
|
panelInstance.colorDropdown.style.cssText = `
|
|
248
315
|
position: absolute;
|
|
249
316
|
top: 100%;
|
|
@@ -255,7 +322,7 @@ function createCompactColorSelector(panelInstance, panel) {
|
|
|
255
322
|
padding: 8px;
|
|
256
323
|
display: none;
|
|
257
324
|
z-index: 10000;
|
|
258
|
-
|
|
325
|
+
width: max-content;
|
|
259
326
|
`;
|
|
260
327
|
|
|
261
328
|
createColorGrid(panelInstance, panelInstance.colorDropdown);
|
|
@@ -356,6 +423,178 @@ function createColorGrid(panelInstance, container) {
|
|
|
356
423
|
container.appendChild(customContainer);
|
|
357
424
|
}
|
|
358
425
|
|
|
426
|
+
function createCompactHighlightSelector(panelInstance, panel) {
|
|
427
|
+
const highlightSelectorContainer = document.createElement('div');
|
|
428
|
+
highlightSelectorContainer.style.cssText = `
|
|
429
|
+
position: relative;
|
|
430
|
+
display: inline-block;
|
|
431
|
+
margin-left: 4px;
|
|
432
|
+
`;
|
|
433
|
+
panelInstance._highlightSelectorContainer = highlightSelectorContainer;
|
|
434
|
+
|
|
435
|
+
panelInstance.currentHighlightButton = document.createElement('button');
|
|
436
|
+
panelInstance.currentHighlightButton.type = 'button';
|
|
437
|
+
panelInstance.currentHighlightButton.title = 'Выбрать цвет фона текста';
|
|
438
|
+
panelInstance.currentHighlightButton.className = 'current-highlight-button';
|
|
439
|
+
|
|
440
|
+
const highlightIcon = document.createElement('span');
|
|
441
|
+
highlightIcon.innerHTML = '<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M11.431 13.4828L17.5 6.27586L15.2241 4L8.01724 10.069M11.431 13.4828L6.5 15L8.01724 10.069M11.431 13.4828L8.01724 10.069" stroke="currentColor" stroke-width="1.4" stroke-linecap="round" stroke-linejoin="round"></path></svg>';
|
|
442
|
+
highlightIcon.style.cssText = 'width: 24px; height: 24px; pointer-events: none; display: flex; align-items: center; justify-content: center;';
|
|
443
|
+
|
|
444
|
+
panelInstance.highlightIndicator = document.createElement('div');
|
|
445
|
+
panelInstance.highlightIndicator.style.cssText = `
|
|
446
|
+
width: 18px;
|
|
447
|
+
height: 5px;
|
|
448
|
+
position: absolute;
|
|
449
|
+
bottom: 2px;
|
|
450
|
+
background: transparent;
|
|
451
|
+
box-shadow: rgba(0, 0, 0, 0.1) 0px 0px 0px 1px inset;
|
|
452
|
+
border-radius: 2px;
|
|
453
|
+
`;
|
|
454
|
+
|
|
455
|
+
panelInstance.currentHighlightButton.appendChild(highlightIcon);
|
|
456
|
+
panelInstance.currentHighlightButton.appendChild(panelInstance.highlightIndicator);
|
|
457
|
+
|
|
458
|
+
panelInstance.highlightDropdown = document.createElement('div');
|
|
459
|
+
panelInstance.highlightDropdown.id = `tpp-highlight-dropdown-${Date.now()}-${Math.floor(Math.random() * 10000)}`;
|
|
460
|
+
panelInstance.highlightDropdown.style.cssText = `
|
|
461
|
+
position: absolute;
|
|
462
|
+
top: 100%;
|
|
463
|
+
left: 0;
|
|
464
|
+
background: white;
|
|
465
|
+
border: 1px solid #ddd;
|
|
466
|
+
border-radius: 6px;
|
|
467
|
+
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
|
|
468
|
+
padding: 8px;
|
|
469
|
+
display: none;
|
|
470
|
+
z-index: 10000;
|
|
471
|
+
width: max-content;
|
|
472
|
+
`;
|
|
473
|
+
|
|
474
|
+
createHighlightColorGrid(panelInstance, panelInstance.highlightDropdown);
|
|
475
|
+
|
|
476
|
+
highlightSelectorContainer.appendChild(panelInstance.currentHighlightButton);
|
|
477
|
+
highlightSelectorContainer.appendChild(panelInstance.highlightDropdown);
|
|
478
|
+
panel.appendChild(highlightSelectorContainer);
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
function createHighlightColorGrid(panelInstance, container) {
|
|
482
|
+
const presetsGrid = document.createElement('div');
|
|
483
|
+
presetsGrid.style.cssText = `
|
|
484
|
+
display: grid;
|
|
485
|
+
grid-template-columns: repeat(6, 28px);
|
|
486
|
+
gap: 6px;
|
|
487
|
+
margin-bottom: 8px;
|
|
488
|
+
align-items: center;
|
|
489
|
+
justify-items: center;
|
|
490
|
+
`;
|
|
491
|
+
|
|
492
|
+
panelInstance._highlightPresetButtons = [];
|
|
493
|
+
|
|
494
|
+
BACKGROUND_COLOR_PRESETS.forEach((preset) => {
|
|
495
|
+
const colorButton = document.createElement('button');
|
|
496
|
+
colorButton.type = 'button';
|
|
497
|
+
colorButton.title = preset.name;
|
|
498
|
+
colorButton.dataset.colorValue = preset.color;
|
|
499
|
+
|
|
500
|
+
if (preset.color === 'transparent') {
|
|
501
|
+
colorButton.style.cssText = `
|
|
502
|
+
width: 28px;
|
|
503
|
+
height: 28px;
|
|
504
|
+
border: 1px solid #ddd;
|
|
505
|
+
border-radius: 50%;
|
|
506
|
+
background: white;
|
|
507
|
+
cursor: pointer;
|
|
508
|
+
margin: 0;
|
|
509
|
+
padding: 0;
|
|
510
|
+
display: flex;
|
|
511
|
+
align-items: center;
|
|
512
|
+
justify-content: center;
|
|
513
|
+
box-sizing: border-box;
|
|
514
|
+
position: relative;
|
|
515
|
+
`;
|
|
516
|
+
|
|
517
|
+
const img = document.createElement('img');
|
|
518
|
+
img.src = '/icons/no-color.svg';
|
|
519
|
+
img.className = 'no-color-icon';
|
|
520
|
+
img.style.cssText = 'width: 20px; height: 20px; pointer-events: none;';
|
|
521
|
+
colorButton.appendChild(img);
|
|
522
|
+
} else {
|
|
523
|
+
colorButton.style.cssText = `
|
|
524
|
+
width: 28px;
|
|
525
|
+
height: 28px;
|
|
526
|
+
border: 1px solid #ddd;
|
|
527
|
+
border-radius: 50%;
|
|
528
|
+
background-color: ${preset.color};
|
|
529
|
+
cursor: pointer;
|
|
530
|
+
margin: 0;
|
|
531
|
+
padding: 0;
|
|
532
|
+
display: block;
|
|
533
|
+
box-sizing: border-box;
|
|
534
|
+
${preset.color === '#ffffff' ? 'border-color: #ccc;' : ''}
|
|
535
|
+
position: relative;
|
|
536
|
+
`;
|
|
537
|
+
|
|
538
|
+
const tick = document.createElement('i');
|
|
539
|
+
tick.style.cssText = `
|
|
540
|
+
position: absolute;
|
|
541
|
+
left: 50%;
|
|
542
|
+
top: 50%;
|
|
543
|
+
width: 8px;
|
|
544
|
+
height: 5px;
|
|
545
|
+
transform: translate(-50%, -50%) rotate(315deg) scaleX(-1);
|
|
546
|
+
border-right: 2px solid #111;
|
|
547
|
+
border-bottom: 2px solid #111;
|
|
548
|
+
display: none;
|
|
549
|
+
pointer-events: none;
|
|
550
|
+
`;
|
|
551
|
+
colorButton.appendChild(tick);
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
presetsGrid.appendChild(colorButton);
|
|
555
|
+
panelInstance._highlightPresetButtons.push(colorButton);
|
|
556
|
+
});
|
|
557
|
+
|
|
558
|
+
container.appendChild(presetsGrid);
|
|
559
|
+
|
|
560
|
+
const separator = document.createElement('div');
|
|
561
|
+
separator.style.cssText = `
|
|
562
|
+
height: 1px;
|
|
563
|
+
background: #eee;
|
|
564
|
+
margin: 8px 0;
|
|
565
|
+
`;
|
|
566
|
+
container.appendChild(separator);
|
|
567
|
+
|
|
568
|
+
const customContainer = document.createElement('div');
|
|
569
|
+
customContainer.style.cssText = `
|
|
570
|
+
display: flex;
|
|
571
|
+
align-items: center;
|
|
572
|
+
gap: 8px;
|
|
573
|
+
`;
|
|
574
|
+
|
|
575
|
+
const customLabel = document.createElement('span');
|
|
576
|
+
customLabel.textContent = 'Свой цвет:';
|
|
577
|
+
customLabel.style.cssText = `
|
|
578
|
+
font-size: 12px;
|
|
579
|
+
color: #666;
|
|
580
|
+
`;
|
|
581
|
+
|
|
582
|
+
panelInstance.highlightInput = document.createElement('input');
|
|
583
|
+
panelInstance.highlightInput.type = 'color';
|
|
584
|
+
panelInstance.highlightInput.style.cssText = `
|
|
585
|
+
width: 32px;
|
|
586
|
+
height: 24px;
|
|
587
|
+
border: 1px solid #ddd;
|
|
588
|
+
border-radius: 3px;
|
|
589
|
+
cursor: pointer;
|
|
590
|
+
padding: 0;
|
|
591
|
+
`;
|
|
592
|
+
|
|
593
|
+
customContainer.appendChild(customLabel);
|
|
594
|
+
customContainer.appendChild(panelInstance.highlightInput);
|
|
595
|
+
container.appendChild(customContainer);
|
|
596
|
+
}
|
|
597
|
+
|
|
359
598
|
function createCompactBackgroundSelector(panelInstance, panel) {
|
|
360
599
|
const bgSelectorContainer = document.createElement('div');
|
|
361
600
|
bgSelectorContainer.style.cssText = `
|
|
@@ -371,6 +610,7 @@ function createCompactBackgroundSelector(panelInstance, panel) {
|
|
|
371
610
|
panelInstance.currentBgColorButton.className = 'current-bgcolor-button';
|
|
372
611
|
|
|
373
612
|
panelInstance.bgColorDropdown = document.createElement('div');
|
|
613
|
+
panelInstance.bgColorDropdown.id = `tpp-bgcolor-dropdown-${Date.now()}-${Math.floor(Math.random() * 10000)}`;
|
|
374
614
|
panelInstance.bgColorDropdown.style.cssText = `
|
|
375
615
|
position: absolute;
|
|
376
616
|
top: 100%;
|
|
@@ -382,7 +622,7 @@ function createCompactBackgroundSelector(panelInstance, panel) {
|
|
|
382
622
|
padding: 8px;
|
|
383
623
|
display: none;
|
|
384
624
|
z-index: 10000;
|
|
385
|
-
|
|
625
|
+
width: max-content;
|
|
386
626
|
`;
|
|
387
627
|
|
|
388
628
|
createBackgroundColorGrid(panelInstance, panelInstance.bgColorDropdown);
|
|
@@ -428,14 +668,11 @@ function createBackgroundColorGrid(panelInstance, container) {
|
|
|
428
668
|
position: relative;
|
|
429
669
|
`;
|
|
430
670
|
|
|
431
|
-
const
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
transform: rotate(45deg);
|
|
437
|
-
`;
|
|
438
|
-
colorButton.appendChild(line);
|
|
671
|
+
const img = document.createElement('img');
|
|
672
|
+
img.src = '/icons/no-color.svg';
|
|
673
|
+
img.className = 'no-color-icon';
|
|
674
|
+
img.style.cssText = 'width: 20px; height: 20px; pointer-events: none;';
|
|
675
|
+
colorButton.appendChild(img);
|
|
439
676
|
} else {
|
|
440
677
|
colorButton.style.cssText = `
|
|
441
678
|
width: 28px;
|
|
@@ -9,6 +9,9 @@ export function createTextPropertiesPanelState() {
|
|
|
9
9
|
currentColorButton: null,
|
|
10
10
|
colorDropdown: null,
|
|
11
11
|
colorInput: null,
|
|
12
|
+
currentHighlightButton: null,
|
|
13
|
+
highlightDropdown: null,
|
|
14
|
+
highlightInput: null,
|
|
12
15
|
currentBgColorButton: null,
|
|
13
16
|
bgColorDropdown: null,
|
|
14
17
|
bgColorInput: null,
|
|
@@ -24,10 +27,13 @@ export function createTextPropertiesPanelState() {
|
|
|
24
27
|
_eventBridgeAttached: false,
|
|
25
28
|
_eventBridgeHandlers: null,
|
|
26
29
|
_colorSelectorContainer: null,
|
|
30
|
+
_highlightSelectorContainer: null,
|
|
27
31
|
_bgSelectorContainer: null,
|
|
28
32
|
_colorPresetButtons: [],
|
|
33
|
+
_highlightPresetButtons: [],
|
|
29
34
|
_bgPresetButtons: [],
|
|
30
35
|
_onColorDocumentClick: null,
|
|
36
|
+
_onHighlightDocumentClick: null,
|
|
31
37
|
_onBgDocumentClick: null,
|
|
32
38
|
_docMouseDownAttached: false,
|
|
33
39
|
};
|