Skip to content

Commit f76f569

Browse files
authored
DEV-560 Text Editor Formatting Fixes (#68)
* Remove formatting and change font while typing * Default font size and family selection * Font size and family after deletion * Prettier
1 parent dafd429 commit f76f569

File tree

1 file changed

+106
-0
lines changed

1 file changed

+106
-0
lines changed

components/Editor.tsx

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -271,6 +271,27 @@ const Editor = forwardRef<any, RichTextEditorProps>(
271271
redo: function () {
272272
(this as any).quill.history.redo();
273273
},
274+
clean: function () {
275+
const quill = (this as any).quill;
276+
const range = quill.getSelection(true);
277+
if (range) {
278+
// Remove all formatting
279+
quill.removeFormat(range);
280+
// Set default font size
281+
quill.formatText(
282+
range.index,
283+
range.length,
284+
'size',
285+
'14px'
286+
);
287+
quill.formatText(
288+
range.index,
289+
range.length,
290+
'font',
291+
'arial'
292+
);
293+
}
294+
},
274295
fullscreen: function () {
275296
// Handled by manual event listener below (needs to toggle icon and fullscreen state)
276297
},
@@ -282,9 +303,53 @@ const Editor = forwardRef<any, RichTextEditorProps>(
282303

283304
quillRef.current = quill;
284305

306+
// Set default formats
285307
quill.format('size', '14px');
286308
quill.format('font', 'arial');
287309

310+
/** ----------------------------------------------
311+
* Update toolbar pickers to show active state on initial load
312+
* ---------------------------------------------- */
313+
const toolbarModule = quill.getModule('toolbar');
314+
const updatePicker = (pickerClass: string, value: string) => {
315+
const picker =
316+
toolbarModule.container.querySelector(pickerClass);
317+
const label = picker?.querySelector('.ql-picker-label');
318+
// Picker options may not be in DOM until first opened, so we need to trigger their creation
319+
const pickerButton = picker?.querySelector(
320+
'.ql-picker-label'
321+
) as HTMLElement;
322+
if (
323+
pickerButton &&
324+
!picker?.querySelector('.ql-picker-options')
325+
) {
326+
// Trigger picker to render options by simulating a click
327+
pickerButton.click();
328+
pickerButton.click(); // Click again to close it
329+
}
330+
331+
const options = picker?.querySelector('.ql-picker-options');
332+
const option = options?.querySelector(
333+
`[data-value="${value}"]`
334+
) as HTMLElement;
335+
336+
if (label && option) {
337+
const labelSpan = label.querySelector('span');
338+
if (labelSpan)
339+
labelSpan.textContent = option.textContent;
340+
label.setAttribute('data-value', value);
341+
options
342+
.querySelectorAll('.ql-picker-item')
343+
.forEach((item: any) => {
344+
item.classList.remove('ql-selected');
345+
});
346+
option.classList.add('ql-selected');
347+
}
348+
};
349+
350+
updatePicker('.ql-size', '14px');
351+
updatePicker('.ql-font', 'arial');
352+
288353
/** ----------------------------------------------
289354
* Enhanced color picker with hex input and remove button
290355
* ---------------------------------------------- */
@@ -687,6 +752,9 @@ const Editor = forwardRef<any, RichTextEditorProps>(
687752
const currentFormat = quill.getFormat(range);
688753
const hadHeader = currentFormat.header;
689754

755+
// Update lastFontSize ref when size is changed
756+
lastFontSize.current = value;
757+
690758
// Apply the size change
691759
const result = originalFormat(name, value, source);
692760

@@ -711,6 +779,12 @@ const Editor = forwardRef<any, RichTextEditorProps>(
711779
return result;
712780
}
713781
}
782+
783+
// When font is being changed, update lastFont ref
784+
if (name === 'font' && value !== null && value !== false) {
785+
lastFont.current = value;
786+
}
787+
714788
return originalFormat(name, value, source);
715789
};
716790

@@ -726,12 +800,29 @@ const Editor = forwardRef<any, RichTextEditorProps>(
726800
if (f.font) lastFont.current = f.font;
727801
});
728802

803+
// Update last font/size when format is changed via toolbar
804+
quill.on(
805+
Quill.events.TEXT_CHANGE,
806+
(delta, oldDelta, source) => {
807+
// When format is applied via toolbar (source === 'api'), update refs
808+
if (source === 'api') {
809+
const range = quill.getSelection(true);
810+
if (range) {
811+
const f = quill.getFormat(range);
812+
if (f.size) lastFontSize.current = f.size;
813+
if (f.font) lastFont.current = f.font;
814+
}
815+
}
816+
}
817+
);
818+
729819
quill.on(
730820
Quill.events.TEXT_CHANGE,
731821
(delta, oldDelta, source) => {
732822
if (source !== 'user') return;
733823

734824
let index = 0;
825+
let hasDeletion = false;
735826

736827
delta.ops?.forEach((op) => {
737828
if (typeof op.insert === 'string') {
@@ -788,8 +879,23 @@ const Editor = forwardRef<any, RichTextEditorProps>(
788879
index += text.length;
789880
} else if (typeof op.retain === 'number') {
790881
index += op.retain;
882+
} else if (typeof op.delete === 'number') {
883+
hasDeletion = true;
791884
}
792885
});
886+
887+
// After deletions, update lastFont and lastFontSize to match current cursor format
888+
if (hasDeletion) {
889+
const range = quill.getSelection(true);
890+
if (range) {
891+
const currentFormat = quill.getFormat(range);
892+
// Update font and size- use current format or default to last set values
893+
lastFont.current =
894+
currentFormat.font || lastFont.current;
895+
lastFontSize.current =
896+
currentFormat.size || lastFontSize.current;
897+
}
898+
}
793899
}
794900
);
795901
})();

0 commit comments

Comments
 (0)