Anonymous
Цвет перо и проблему курсора
Сообщение
Anonymous » 02 авг 2025, 15:01
Я внедряю пользовательский цветовой сборщик для Quill.js, чтобы пользователи выбирали цвета за пределами палитры по умолчанию. Мое решение включает в себя использование нативного элемента браузера, запускаемого пользовательским опцией «Picker» на панели инструментов. Выбранный текст в редакторе Quill должен обновить в режиме реального времени. При прослушивании входного события на элементе сборщика цвета, применение формата к редактору Quill заставляет курсор прыгать в начало редактора с каждым изменением цвета. Это делает живой предварительный просмотр непригодным.
Код: Выделить всё
import { Injectable } from '@angular/core';
import { Quill } from 'quill';
export interface QuillColorPickerConfig {
defaultColors?: string[];
colorPickerOption?: string;
onColorChange?: (content: string, quillInstance: Quill) => void;
}
@Injectable({
providedIn: 'root'
})
export class QuillCustomColorPickerService {
private readonly QUILL_DEFAULT_COLORS = [
"#000000", "#e60000", "#ff9900", "#ffff00", "#008a00", "#0066cc", "#9933ff",
"#ffffff", "#facccc", "#ffebcc", "#ffffcc", "#cce8cc", "#cce0f5", "#ebd6ff",
"#bbbbbb", "#f06666", "#ffc266", "#ffff66", "#66b966", "#66a3e0", "#c285ff",
"#888888", "#a10000", "#b26b00", "#b2b200", "#006100", "#0047b2", "#6b24b2",
"#444444", "#5c0000", "#663d00", "#666600", "#003700", "#002966", "#3d1466"
];
private currentQuillInstance: Quill | null = null;
private currentSelection: any = null;
/**
* Get default toolbar options with custom color picker
*/
getToolbarOptions(config?: QuillColorPickerConfig): any[] {
const colors = config?.defaultColors || this.QUILL_DEFAULT_COLORS;
const colorPickerOption = config?.colorPickerOption || 'color-picker';
const allCustomColors = [...colors, colorPickerOption];
return [
['bold', 'italic', 'underline', 'strike'],
['blockquote', 'code-block'],
[{ 'header': 1 }, { 'header': 2 }],
[{ 'list': 'ordered' }, { 'list': 'bullet' }],
[{ 'script': 'sub' }, { 'script': 'super' }],
[{ 'indent': '-1' }, { 'indent': '+1' }],
[{ 'direction': 'rtl' }],
[{ 'size': ['small', false, 'large', 'huge'] }],
[{ 'header': [1, 2, 3, 4, 5, 6, false] }],
[{ 'color': allCustomColors }, { 'background': allCustomColors }],
[{ 'font': [] }],
[{ 'align': [] }],
['clean'],
['link']
];
}
/**
* Setup custom color picker for a Quill editor instance
*/
setupCustomColorPicker(quillEditor: Quill, config?: QuillColorPickerConfig): void {
if (!quillEditor) {
console.warn('QuillCustomColorPickerService: Quill editor instance is required');
return;
}
this.currentQuillInstance = quillEditor;
const toolbar = quillEditor.getModule('toolbar');
const colorPickerOption = config?.colorPickerOption || 'color-picker';
// Setup color handler
toolbar.addHandler('color', (value: string) => {
this.handleColorChange(quillEditor, value, 'color', colorPickerOption, config);
});
// Setup background color handler
toolbar.addHandler('background', (value: string) => {
this.handleColorChange(quillEditor, value, 'background', colorPickerOption, config);
});
}
private handleColorChange(
quillEditor: Quill,
value: string,
type: 'color' | 'background',
colorPickerOption: string,
config?: QuillColorPickerConfig
): void {
// Store current selection
this.currentSelection = quillEditor.getSelection(true);
if (value === colorPickerOption) {
// Show custom color picker
this.showColorPicker(quillEditor, type, config);
} else {
// Apply predefined color
this.applyColor(quillEditor, value, type, config);
}
}
private showColorPicker(
quillEditor: Quill,
type: 'color' | 'background',
config?: QuillColorPickerConfig
): void {
// Remove existing color picker if any
this.removeExistingColorPicker();
// Create color input element
const colorInput = document.createElement('input');
colorInput.type = 'color';
colorInput.id = `quill-${type}-picker`;
colorInput.style.position = 'absolute';
colorInput.style.visibility = 'hidden';
colorInput.style.width = '0';
colorInput.style.height = '0';
// Get current color value if text is selected
const currentColor = this.getCurrentColor(quillEditor, type);
if (currentColor) {
colorInput.value = currentColor;
}
// Add event listeners
colorInput.addEventListener('input', (event) => {
const target = event.target as HTMLInputElement;
this.applyColorWithPreview(quillEditor, target.value, type, config);
});
colorInput.addEventListener('change', (event) => {
const target = event.target as HTMLInputElement;
this.applyColor(quillEditor, target.value, type, config);
this.removeExistingColorPicker();
});
colorInput.addEventListener('blur', () => {
setTimeout(() => this.removeExistingColorPicker(), 100);
});
// Add to DOM and trigger click
document.body.appendChild(colorInput);
colorInput.click();
}
private applyColorWithPreview(
quillEditor: Quill,
color: string,
type: 'color' | 'background',
config?: QuillColorPickerConfig
): void {
if (!this.currentSelection) return;
// Apply color to current selection
if (this.currentSelection.length > 0) {
quillEditor.setSelection(this.currentSelection.index, this.currentSelection.length);
quillEditor.format(type, color);
} else {
// Set formatting for next input
quillEditor.format(type, color);
}
// Notify color change
this.notifyColorChange(quillEditor, config);
}
private applyColor(
quillEditor: Quill,
color: string,
type: 'color' | 'background',
config?: QuillColorPickerConfig
): void {
if (!this.currentSelection) return;
// Restore selection
quillEditor.setSelection(this.currentSelection.index, this.currentSelection.length);
if (this.currentSelection.length > 0) {
// Apply to selected text
quillEditor.formatText(this.currentSelection.index, this.currentSelection.length, type, color);
} else {
// Set formatting for next input
quillEditor.format(type, color);
}
// Focus back to editor
quillEditor.focus();
// Notify color change
this.notifyColorChange(quillEditor, config);
}
private getCurrentColor(quillEditor: Quill, type: 'color' | 'background'): string | null {
if (!this.currentSelection || this.currentSelection.length === 0) {
return null;
}
const format = quillEditor.getFormat(this.currentSelection.index, this.currentSelection.length);
return format[type] || null;
}
private removeExistingColorPicker(): void {
const existingPickers = document.querySelectorAll('[id^="quill-"][id$="-picker"]');
existingPickers.forEach(picker => picker.remove());
}
private notifyColorChange(quillEditor: Quill, config?: QuillColorPickerConfig): void {
if (config?.onColorChange) {
const htmlContent = quillEditor.root.innerHTML;
config.onColorChange(htmlContent, quillEditor);
}
}
/**
* Clean up method to remove any remaining color pickers
*/
cleanup(): void {
this.removeExistingColorPicker();
this.currentQuillInstance = null;
this.currentSelection = null;
}
}
Подробнее здесь:
https://stackoverflow.com/questions/797 ... rsor-issue
1754136099
Anonymous
Я внедряю пользовательский цветовой сборщик для Quill.js, чтобы пользователи выбирали цвета за пределами палитры по умолчанию. Мое решение включает в себя использование нативного элемента браузера, запускаемого пользовательским опцией «Picker» на панели инструментов. Выбранный текст в редакторе Quill должен обновить в режиме реального времени. При прослушивании входного события на элементе сборщика цвета, применение формата к редактору Quill заставляет курсор прыгать в начало редактора с каждым изменением цвета. Это делает живой предварительный просмотр непригодным.[code]import { Injectable } from '@angular/core'; import { Quill } from 'quill'; export interface QuillColorPickerConfig { defaultColors?: string[]; colorPickerOption?: string; onColorChange?: (content: string, quillInstance: Quill) => void; } @Injectable({ providedIn: 'root' }) export class QuillCustomColorPickerService { private readonly QUILL_DEFAULT_COLORS = [ "#000000", "#e60000", "#ff9900", "#ffff00", "#008a00", "#0066cc", "#9933ff", "#ffffff", "#facccc", "#ffebcc", "#ffffcc", "#cce8cc", "#cce0f5", "#ebd6ff", "#bbbbbb", "#f06666", "#ffc266", "#ffff66", "#66b966", "#66a3e0", "#c285ff", "#888888", "#a10000", "#b26b00", "#b2b200", "#006100", "#0047b2", "#6b24b2", "#444444", "#5c0000", "#663d00", "#666600", "#003700", "#002966", "#3d1466" ]; private currentQuillInstance: Quill | null = null; private currentSelection: any = null; /** * Get default toolbar options with custom color picker */ getToolbarOptions(config?: QuillColorPickerConfig): any[] { const colors = config?.defaultColors || this.QUILL_DEFAULT_COLORS; const colorPickerOption = config?.colorPickerOption || 'color-picker'; const allCustomColors = [...colors, colorPickerOption]; return [ ['bold', 'italic', 'underline', 'strike'], ['blockquote', 'code-block'], [{ 'header': 1 }, { 'header': 2 }], [{ 'list': 'ordered' }, { 'list': 'bullet' }], [{ 'script': 'sub' }, { 'script': 'super' }], [{ 'indent': '-1' }, { 'indent': '+1' }], [{ 'direction': 'rtl' }], [{ 'size': ['small', false, 'large', 'huge'] }], [{ 'header': [1, 2, 3, 4, 5, 6, false] }], [{ 'color': allCustomColors }, { 'background': allCustomColors }], [{ 'font': [] }], [{ 'align': [] }], ['clean'], ['link'] ]; } /** * Setup custom color picker for a Quill editor instance */ setupCustomColorPicker(quillEditor: Quill, config?: QuillColorPickerConfig): void { if (!quillEditor) { console.warn('QuillCustomColorPickerService: Quill editor instance is required'); return; } this.currentQuillInstance = quillEditor; const toolbar = quillEditor.getModule('toolbar'); const colorPickerOption = config?.colorPickerOption || 'color-picker'; // Setup color handler toolbar.addHandler('color', (value: string) => { this.handleColorChange(quillEditor, value, 'color', colorPickerOption, config); }); // Setup background color handler toolbar.addHandler('background', (value: string) => { this.handleColorChange(quillEditor, value, 'background', colorPickerOption, config); }); } private handleColorChange( quillEditor: Quill, value: string, type: 'color' | 'background', colorPickerOption: string, config?: QuillColorPickerConfig ): void { // Store current selection this.currentSelection = quillEditor.getSelection(true); if (value === colorPickerOption) { // Show custom color picker this.showColorPicker(quillEditor, type, config); } else { // Apply predefined color this.applyColor(quillEditor, value, type, config); } } private showColorPicker( quillEditor: Quill, type: 'color' | 'background', config?: QuillColorPickerConfig ): void { // Remove existing color picker if any this.removeExistingColorPicker(); // Create color input element const colorInput = document.createElement('input'); colorInput.type = 'color'; colorInput.id = `quill-${type}-picker`; colorInput.style.position = 'absolute'; colorInput.style.visibility = 'hidden'; colorInput.style.width = '0'; colorInput.style.height = '0'; // Get current color value if text is selected const currentColor = this.getCurrentColor(quillEditor, type); if (currentColor) { colorInput.value = currentColor; } // Add event listeners colorInput.addEventListener('input', (event) => { const target = event.target as HTMLInputElement; this.applyColorWithPreview(quillEditor, target.value, type, config); }); colorInput.addEventListener('change', (event) => { const target = event.target as HTMLInputElement; this.applyColor(quillEditor, target.value, type, config); this.removeExistingColorPicker(); }); colorInput.addEventListener('blur', () => { setTimeout(() => this.removeExistingColorPicker(), 100); }); // Add to DOM and trigger click document.body.appendChild(colorInput); colorInput.click(); } private applyColorWithPreview( quillEditor: Quill, color: string, type: 'color' | 'background', config?: QuillColorPickerConfig ): void { if (!this.currentSelection) return; // Apply color to current selection if (this.currentSelection.length > 0) { quillEditor.setSelection(this.currentSelection.index, this.currentSelection.length); quillEditor.format(type, color); } else { // Set formatting for next input quillEditor.format(type, color); } // Notify color change this.notifyColorChange(quillEditor, config); } private applyColor( quillEditor: Quill, color: string, type: 'color' | 'background', config?: QuillColorPickerConfig ): void { if (!this.currentSelection) return; // Restore selection quillEditor.setSelection(this.currentSelection.index, this.currentSelection.length); if (this.currentSelection.length > 0) { // Apply to selected text quillEditor.formatText(this.currentSelection.index, this.currentSelection.length, type, color); } else { // Set formatting for next input quillEditor.format(type, color); } // Focus back to editor quillEditor.focus(); // Notify color change this.notifyColorChange(quillEditor, config); } private getCurrentColor(quillEditor: Quill, type: 'color' | 'background'): string | null { if (!this.currentSelection || this.currentSelection.length === 0) { return null; } const format = quillEditor.getFormat(this.currentSelection.index, this.currentSelection.length); return format[type] || null; } private removeExistingColorPicker(): void { const existingPickers = document.querySelectorAll('[id^="quill-"][id$="-picker"]'); existingPickers.forEach(picker => picker.remove()); } private notifyColorChange(quillEditor: Quill, config?: QuillColorPickerConfig): void { if (config?.onColorChange) { const htmlContent = quillEditor.root.innerHTML; config.onColorChange(htmlContent, quillEditor); } } /** * Clean up method to remove any remaining color pickers */ cleanup(): void { this.removeExistingColorPicker(); this.currentQuillInstance = null; this.currentSelection = null; } }[/code] Подробнее здесь: [url]https://stackoverflow.com/questions/79723341/quill-color-and-cursor-issue[/url]