mirror of
https://github.com/AmruthPillai/Reactive-Resume.git
synced 2026-07-27 02:14:50 +10:00
feat(editor): add multicolor highlight with auto-contrast text (#3110)
* feat(editor): add multicolor highlight with auto-contrast text Enable the Tiptap Highlight extension in multicolor mode, replacing the single-color yellow toggle with a full color picker (16 presets + custom). When the chosen highlight color is perceptually dark, text inside the mark automatically renders white for readability. Changes span the full pipeline: - Editor: ColorPicker UI, extended renderHTML for contrast detection - PDF: normalizeMarkElements preserves data-color as inline style - DOCX: mergeStyle reads actual background-color from <mark> - Utils: new isDarkColor() luminance helper Backward-compatible: legacy <mark> without data-color still renders yellow. Resolves #3109 * fix: handle multicolor highlight edge cases --------- Co-authored-by: Amruth Pillai <im.amruth@gmail.com>
This commit is contained in:
@@ -50,6 +50,29 @@ describe("normalizeRichTextHtml", () => {
|
||||
);
|
||||
});
|
||||
|
||||
it("preserves data-color as inline background-color on multicolor <mark>", () => {
|
||||
const result = normalizeRichTextHtml(
|
||||
'<mark data-color="rgba(204, 255, 204, 1)" style="background-color: rgba(204, 255, 204, 1)">green</mark>',
|
||||
);
|
||||
// data-color is kept, class is added, background-color is set from data-color
|
||||
expect(result).toContain("rr-pdf-mark");
|
||||
expect(result).toContain("background-color: rgba(204, 255, 204, 1)");
|
||||
expect(result).toContain(">green</span>");
|
||||
});
|
||||
|
||||
it("keeps generated dark-highlight contrast after existing Tiptap mark styles", () => {
|
||||
const result = normalizeRichTextHtml(
|
||||
'<mark data-color="rgba(0, 0, 0, 1)" style="background-color: rgba(0, 0, 0, 1); color: inherit">dark</mark>',
|
||||
);
|
||||
|
||||
expect(result.indexOf("color: inherit")).toBeLessThan(result.lastIndexOf("color: #ffffff"));
|
||||
});
|
||||
|
||||
it("does not add inline style to legacy <mark> without data-color", () => {
|
||||
const result = normalizeRichTextHtml("<mark>yellow</mark>");
|
||||
expect(result).toBe('<p><span class="rr-pdf-mark">yellow</span></p>');
|
||||
});
|
||||
|
||||
it("keeps highlighted text in the same react-pdf-html inline text bucket", () => {
|
||||
const root = renderHtml(normalizeRichTextHtml("before <mark>highlighted</mark> after"), {
|
||||
resetStyles: true,
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import type { HTMLElement, Node } from "node-html-parser";
|
||||
import { NodeType, parse } from "node-html-parser";
|
||||
import { isDarkColor } from "@reactive-resume/utils/color";
|
||||
|
||||
export const richTextMarkClassName = "rr-pdf-mark";
|
||||
|
||||
@@ -45,8 +46,19 @@ const mergeClassNames = (...classNames: (string | undefined)[]): string => {
|
||||
|
||||
const normalizeMarkElements = (root: ReturnType<typeof parse>) => {
|
||||
for (const mark of root.querySelectorAll("mark")) {
|
||||
const dataColor = mark.getAttribute("data-color");
|
||||
|
||||
mark.tagName = "span";
|
||||
mark.setAttribute("class", mergeClassNames(mark.getAttribute("class"), richTextMarkClassName));
|
||||
|
||||
// Preserve custom highlight color as inline background-color for react-pdf-html.
|
||||
// Legacy marks without data-color fall back to the .rr-pdf-mark stylesheet (yellow).
|
||||
if (dataColor) {
|
||||
const existingStyle = mark.getAttribute("style") ?? "";
|
||||
let inlineStyle = `background-color: ${dataColor}`;
|
||||
if (isDarkColor(dataColor)) inlineStyle += "; color: #ffffff";
|
||||
mark.setAttribute("style", existingStyle ? `${existingStyle}; ${inlineStyle}` : inlineStyle);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user