All files / roosterjs-content-model-dom/lib/modelApi/editing setTableCellBackgroundColor.ts

100% Statements 56/56
91.53% Branches 54/59
100% Functions 10/10
100% Lines 56/56

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 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 1451x 1x 1x           1x 1x 1x 1x                 1x           401x 193x   193x 5x 5x 5x 5x       193x   193x 14x 179x 99x   80x     193x 184x     208x 208x 208x 206x           206x 135x 134x   134x         3x   134x 135x         3x               184x 104x 5x 5x   5x       5x         5x 5x       4x                       193x       193x 98x 98x 98x     98x   95x               15x 15x   15x 10x   5x    
import { mutateBlock } from '../common/mutate';
import { parseColor } from '../../formatHandlers/utils/color';
import { updateTableCellMetadata } from '../metadata/updateTableCellMetadata';
import type { ShallowMutableContentModelTableCell } from 'roosterjs-content-model-types';
 
// Using the HSL (hue, saturation and lightness) representation for RGB color values.
// If the value of the lightness is less than 20, the color is dark.
// If the value of the lightness is more than 80, the color is bright
const DARK_COLORS_LIGHTNESS = 20;
const BRIGHT_COLORS_LIGHTNESS = 80;
const White = '#ffffff';
const Black = '#000000';
 
/**
 * Set shade color of table cell
 * @param cell The cell to set shade color to
 * @param color The color to set
 * @param isColorOverride @optional When pass true, it means this shade color is not part of table format, so it can be preserved when apply table format
 * @param applyToSegments @optional When pass true, we will also apply text color from table cell to its child blocks and segments
 */
export function setTableCellBackgroundColor(
    cell: ShallowMutableContentModelTableCell,
    color: string | null | undefined,
    isColorOverride?: boolean,
    applyToSegments?: boolean
) {
    if (color) {
        cell.format.backgroundColor = color;
 
        if (isColorOverride) {
            updateTableCellMetadata(cell, metadata => {
                metadata = metadata || {};
                metadata.bgColorOverride = true;
                return metadata;
            });
        }
 
        const lightness = calculateLightness(color);
 
        if (lightness < DARK_COLORS_LIGHTNESS) {
            cell.format.textColor = White;
        } else if (lightness > BRIGHT_COLORS_LIGHTNESS) {
            cell.format.textColor = Black;
        } else {
            delete cell.format.textColor;
        }
 
        if (applyToSegments) {
            setAdaptiveCellColor(cell, color);
        }
    } else {
        delete cell.format.backgroundColor;
        delete cell.format.textColor;
        if (applyToSegments) {
            removeAdaptiveCellColor(cell);
        }
    }
}
 
function removeAdaptiveCellColor(cell: ShallowMutableContentModelTableCell) {
    cell.blocks.forEach(readonlyBlock => {
        if (readonlyBlock.blockType == 'Paragraph') {
            const block = mutateBlock(readonlyBlock);
 
            if (
                block.segmentFormat?.textColor &&
                (areSameColor(block.segmentFormat.textColor, White) ||
                    areSameColor(block.segmentFormat.textColor, Black))
            ) {
                delete block.segmentFormat.textColor;
            }
            block.segments.forEach(segment => {
                if (
                    segment.format.textColor &&
                    (areSameColor(segment.format.textColor, White) ||
                        areSameColor(segment.format.textColor, Black))
                ) {
                    delete segment.format.textColor;
                }
            });
        }
    });
}
 
function setAdaptiveCellColor(cell: ShallowMutableContentModelTableCell, backgroundColor: string) {
    if (cell.format.textColor) {
        cell.blocks.forEach(readonlyBlock => {
            Eif (readonlyBlock.blockType == 'Paragraph') {
                const block = mutateBlock(readonlyBlock);
 
                Eif (
                    !block.segmentFormat?.textColor ||
                    areSameColor(backgroundColor, block.segmentFormat.textColor)
                ) {
                    block.segmentFormat = {
                        ...block.segmentFormat,
                        textColor: cell.format.textColor,
                    };
                }
                block.segments.forEach(segment => {
                    if (
                        !segment.format?.textColor ||
                        areSameColor(backgroundColor, segment.format.textColor)
                    ) {
                        segment.format = {
                            ...segment.format,
                            textColor: cell.format.textColor,
                        };
                    }
                });
            }
        });
    }
}
 
function calculateLightness(color: string) {
    const colorValues = parseColor(color);
 
    // Use the values of r,g,b to calculate the lightness in the HSl representation
    //First calculate the fraction of the light in each color, since in css the value of r,g,b is in the interval of [0,255], we have
    if (colorValues) {
        const red = colorValues[0] / 255;
        const green = colorValues[1] / 255;
        const blue = colorValues[2] / 255;
 
        //Then the lightness in the HSL representation is the average between maximum fraction of r,g,b and the minimum fraction
        return (Math.max(red, green, blue) + Math.min(red, green, blue)) * 50;
    } else {
        return 255;
    }
}
 
/**
 * Check if two colors are the same by comparing their RGB values
 */
function areSameColor(color1: string, color2: string): boolean {
    const rgb1 = parseColor(color1);
    const rgb2 = parseColor(color2);
 
    if (rgb1 && rgb2) {
        return rgb1[0] === rgb2[0] && rgb1[1] === rgb2[1] && rgb1[2] === rgb2[2];
    }
    return false;
}