All files / roosterjs-content-model-dom/lib/domUtils/style transformColor.ts

90.91% Statements 30/33
76.92% Branches 20/26
100% Functions 6/6
90.63% Lines 29/32

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 1051x 1x 1x                                                 1x             20x 20x 20x 20x 20x   20x 20x   20x 4x       20x               4x 4x 16x 16x 16x 12x 12x 12x             12x 12x                         20x 20x     20x                         20x 20x    
import { BorderColorKeyMap, BorderKeys } from '../../formatHandlers/utils/borderKeys';
import { isElementOfType } from '../isElementOfType';
import {
    adaptColor,
    getColor,
    getLightModeColor,
    setColor,
} from '../../formatHandlers/utils/color';
import type { DarkColorHandler } from 'roosterjs-content-model-types';
 
/**
 * Configuration options for controlling which elements and styles undergo color transformation.
 * By default, text and background colors are transformed for all elements.
 */
export interface TransformColorOptions {
    tableBorders: boolean;
}
 
/**
 * Edit and transform color of elements between light mode and dark mode
 * By default, text and background colors are transformed for all elements.
 * @param rootNode The root DOM node to transform
 * @param includeSelf True to transform the root node as well, otherwise false
 * @param direction To specify the transform direction, light to dark, or dark to light
 * @param darkColorHandler The dark color handler object to help do color transformation
 * @param transformColorOptions Configuration options for controlling which elements and styles undergo color transformation.
 */
export function transformColor(
    rootNode: Node,
    includeSelf: boolean,
    direction: 'lightToDark' | 'darkToLight',
    darkColorHandler?: DarkColorHandler,
    transformColorOptions?: TransformColorOptions
) {
    const toDarkMode = direction == 'lightToDark';
    const tableBorders = transformColorOptions?.tableBorders || false;
    const transformer = (element: HTMLElement) => {
        const textColor = getColor(element, false /*isBackground*/, !toDarkMode, darkColorHandler);
        const backColor = getColor(element, true /*isBackground*/, !toDarkMode, darkColorHandler);
 
        setColor(element, textColor, false /*isBackground*/, toDarkMode, darkColorHandler);
        setColor(element, backColor, true /*isBackground*/, toDarkMode, darkColorHandler);
 
        if (tableBorders) {
            transformBorderColor(element, toDarkMode, darkColorHandler);
        }
    };
 
    iterateElements(rootNode, transformer, includeSelf);
}
 
function transformBorderColor(
    element: HTMLElement,
    toDarkMode: boolean,
    darkColorHandler?: DarkColorHandler
) {
    Eif (isElementOfType(element, 'td') || isElementOfType(element, 'th')) {
        BorderKeys.forEach(key => {
            const borderColorProperty = BorderColorKeyMap[key];
            const style = element.style.getPropertyValue(borderColorProperty);
            if (style) {
                const lightColor = getLightModeColor(style, !toDarkMode, darkColorHandler);
                Eif (lightColor) {
                    const transformedColor = adaptColor(
                        element,
                        lightColor,
                        'border',
                        toDarkMode,
                        darkColorHandler
                    );
                    Eif (transformedColor) {
                        element.style.setProperty(borderColorProperty, transformedColor);
                    }
                }
            }
        });
    }
}
 
function iterateElements(
    root: Node,
    transformer: (element: HTMLElement) => void,
    includeSelf?: boolean
) {
    Eif (includeSelf && isHTMLElement(root)) {
        transformer(root);
    }
 
    for (let child = root.firstChild; child; child = child.nextSibling) {
        if (isHTMLElement(child)) {
            transformer(child);
        }
 
        iterateElements(child, transformer);
    }
}
 
// This is not a strict check, we just need to make sure this element has style so that we can set style to it
// We don't use safeInstanceOf() here since this function will be called very frequently when extract html content
// in dark mode, so we need to make sure this check is fast enough
function isHTMLElement(node: Node): node is HTMLElement {
    const htmlElement = <HTMLElement>node;
    return node.nodeType == Node.ELEMENT_NODE && !!htmlElement.style;
}