Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow customization of color key generation #2682

Merged
merged 5 commits into from
Jun 7, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { defaultGenerateColorKey } from 'roosterjs-content-model-dom';
import type {
DarkColorHandler,
ColorTransformFunction,
Expand All @@ -8,7 +9,8 @@ class DarkColorHandlerImpl implements DarkColorHandler {
constructor(
private readonly root: HTMLElement,
public getDarkColor: ColorTransformFunction,
public readonly knownColors: Record<string, Colors>
public readonly knownColors: Record<string, Colors>,
public generateColorKey: ColorTransformFunction
) {}

updateKnownColor(isDarkMode: boolean, key?: string, colorPair?: Colors): void {
Expand Down Expand Up @@ -48,7 +50,8 @@ class DarkColorHandlerImpl implements DarkColorHandler {
export function createDarkColorHandler(
root: HTMLElement,
getDarkColor: ColorTransformFunction,
knownColors: Record<string, Colors> = {}
knownColors: Record<string, Colors> = {},
generateColorKey: ColorTransformFunction = defaultGenerateColorKey
): DarkColorHandler {
return new DarkColorHandlerImpl(root, getDarkColor, knownColors);
return new DarkColorHandlerImpl(root, getDarkColor, knownColors, generateColorKey);
}
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,8 @@ export function createEditorCore(contentDiv: HTMLDivElement, options: EditorOpti
darkColorHandler: createDarkColorHandler(
contentDiv,
options.getDarkColor ?? getDarkColorFallback,
options.knownColors
options.knownColors,
options.generateColorKey
),
trustedHTMLHandler: options.trustedHTMLHandler || defaultTrustHtmlHandler,
domHelper: createDOMHelper(contentDiv),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,14 +28,21 @@ describe('DarkColorHandlerImpl.ctor', () => {
);
});

it('with knownColors', () => {
it('with knownColors and generateColorKey', () => {
const div = document.createElement('div');
const mockedGetDarkColor = jasmine.createSpy('getDarkColor');
const mockedKnownColors = 'KNOWN' as any;
const handler = createDarkColorHandler(div, mockedGetDarkColor, mockedKnownColors);
const mockedGenerateColorKey = 'KEY' as any;
const handler = createDarkColorHandler(
div,
mockedGetDarkColor,
mockedKnownColors,
mockedGenerateColorKey
);

expect(handler.knownColors).toEqual(mockedKnownColors);
expect(handler.getDarkColor).toBe(mockedGetDarkColor);
expect(handler.generateColorKey).toBe(mockedGenerateColorKey);
});
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,7 @@ describe('createEditorCore', () => {
expect(DarkColorHandlerImpl.createDarkColorHandler).toHaveBeenCalledWith(
mockedDiv,
getDarkColorFallback,
undefined,
undefined
);
});
Expand All @@ -146,6 +147,8 @@ describe('createEditorCore', () => {
const mockedGetDarkColor = 'DARK' as any;
const mockedTrustHtmlHandler = 'TRUST' as any;
const mockedDisposeErrorHandler = 'DISPOSE' as any;
const mockedGenerateColorKey = 'KEY' as any;
const mockedKnownColors = 'COLORS' as any;
const mockedOptions = {
coreApiOverride: {
a: 'b',
Expand All @@ -154,6 +157,8 @@ describe('createEditorCore', () => {
getDarkColor: mockedGetDarkColor,
trustedHTMLHandler: mockedTrustHtmlHandler,
disposeErrorHandler: mockedDisposeErrorHandler,
generateColorKey: mockedGenerateColorKey,
knownColors: mockedKnownColors,
} as any;

runTest(mockedDiv, mockedOptions, {
Expand Down Expand Up @@ -181,7 +186,8 @@ describe('createEditorCore', () => {
expect(DarkColorHandlerImpl.createDarkColorHandler).toHaveBeenCalledWith(
mockedDiv,
mockedGetDarkColor,
undefined
mockedKnownColors,
mockedGenerateColorKey
);
});

Expand Down Expand Up @@ -214,6 +220,7 @@ describe('createEditorCore', () => {
expect(DarkColorHandlerImpl.createDarkColorHandler).toHaveBeenCalledWith(
mockedDiv,
getDarkColorFallback,
undefined,
undefined
);
});
Expand Down Expand Up @@ -247,6 +254,7 @@ describe('createEditorCore', () => {
expect(DarkColorHandlerImpl.createDarkColorHandler).toHaveBeenCalledWith(
mockedDiv,
getDarkColorFallback,
undefined,
undefined
);
});
Expand Down Expand Up @@ -280,6 +288,7 @@ describe('createEditorCore', () => {
expect(DarkColorHandlerImpl.createDarkColorHandler).toHaveBeenCalledWith(
mockedDiv,
getDarkColorFallback,
undefined,
undefined
);
});
Expand Down Expand Up @@ -313,6 +322,7 @@ describe('createEditorCore', () => {
expect(DarkColorHandlerImpl.createDarkColorHandler).toHaveBeenCalledWith(
mockedDiv,
getDarkColorFallback,
undefined,
undefined
);
});
Expand Down Expand Up @@ -346,6 +356,7 @@ describe('createEditorCore', () => {
expect(DarkColorHandlerImpl.createDarkColorHandler).toHaveBeenCalledWith(
mockedDiv,
getDarkColorFallback,
undefined,
undefined
);
});
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
import { getObjectKeys } from '../../domUtils/getObjectKeys';
import type { DarkColorHandler, Colors } from 'roosterjs-content-model-types';
import type {
DarkColorHandler,
Colors,
ColorTransformFunction,
} from 'roosterjs-content-model-types';

/**
* List of deprecated colors
Expand Down Expand Up @@ -97,15 +101,13 @@ export function setColor(
color = fallbackColor ?? color;

if (darkColorHandler && color) {
const key = existingKey || `${COLOR_VAR_PREFIX}_${color.replace(/[^\d\w]/g, '_')}`;
const colorType = isBackground ? 'background' : 'text';
const key =
existingKey ||
darkColorHandler.generateColorKey(color, undefined /*baseLValue*/, colorType, element);
const darkModeColor =
darkColorHandler.knownColors?.[key]?.darkModeColor ||
darkColorHandler.getDarkColor(
color,
undefined /*baseLAValue*/,
isBackground ? 'background' : 'text',
element
);
darkColorHandler.getDarkColor(color, undefined /*baseLValue*/, colorType, element);

darkColorHandler.updateKnownColor(isDarkMode, key, {
lightModeColor: color,
Expand All @@ -119,6 +121,15 @@ export function setColor(
element.style.setProperty(isBackground ? 'background-color' : 'color', color || null);
}

/**
* Generate color key for dark color
* @param lightColor The input light color
* @returns Key of the color
*/
export const defaultGenerateColorKey: ColorTransformFunction = lightColor => {
return `${COLOR_VAR_PREFIX}_${lightColor.replace(/[^\d\w]/g, '_')}`;
};

/**
* Parse color string to r/g/b value.
* If the given color is not in a recognized format, return null
Expand Down
8 changes: 7 additions & 1 deletion packages/roosterjs-content-model-dom/lib/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,13 @@ export { getAutoListStyleType } from './modelApi/list/getAutoListStyleType';

export { parseValueWithUnit } from './formatHandlers/utils/parseValueWithUnit';
export { BorderKeys } from './formatHandlers/common/borderFormatHandler';
export { DeprecatedColors, getColor, setColor, parseColor } from './formatHandlers/utils/color';
export {
DeprecatedColors,
getColor,
setColor,
parseColor,
defaultGenerateColorKey,
} from './formatHandlers/utils/color';

export {
createDomToModelContext,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { backgroundColorFormatHandler } from '../../../lib/formatHandlers/common/backgroundColorFormatHandler';
import { createDomToModelContext } from '../../../lib/domToModel/context/createDomToModelContext';
import { createModelToDomContext } from '../../../lib/modelToDom/context/createModelToDomContext';
import { DeprecatedColors } from '../../../lib/formatHandlers/utils/color';
import { defaultGenerateColorKey, DeprecatedColors } from '../../../lib/formatHandlers/utils/color';
import { expectHtml } from '../../testUtils';
import {
BackgroundColorFormat,
Expand Down Expand Up @@ -113,6 +113,7 @@ describe('backgroundColorFormatHandler.apply', () => {
context.darkColorHandler = {
updateKnownColor: () => {},
getDarkColor: (lightColor: string) => `var(--darkColor_${lightColor}, ${lightColor})`,
generateColorKey: defaultGenerateColorKey,
} as any;

backgroundColorFormatHandler.apply(format, div, context);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { createDomToModelContext } from '../../../lib/domToModel/context/createDomToModelContext';
import { createModelToDomContext } from '../../../lib/modelToDom/context/createModelToDomContext';
import { defaultGenerateColorKey, DeprecatedColors } from '../../../lib';
import { defaultHTMLStyleMap } from '../../../lib/config/defaultHTMLStyleMap';
import { DeprecatedColors } from '../../../lib';
import { expectHtml } from '../../testUtils';
import { textColorFormatHandler } from '../../../lib/formatHandlers/segment/textColorFormatHandler';
import {
Expand Down Expand Up @@ -110,6 +110,7 @@ describe('textColorFormatHandler.apply', () => {
context.darkColorHandler = {
updateKnownColor: () => {},
getDarkColor: (lightColor: string) => `var(--darkColor_${lightColor}, ${lightColor})`,
generateColorKey: defaultGenerateColorKey,
} as any;

format = {};
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
import { Colors, DarkColorHandler } from 'roosterjs-content-model-types';
import { getColor, parseColor, setColor } from '../../../lib/formatHandlers/utils/color';
import {
defaultGenerateColorKey,
getColor,
parseColor,
setColor,
} from '../../../lib/formatHandlers/utils/color';

describe('getColor without darkColorHandler', () => {
it('no color', () => {
Expand Down Expand Up @@ -122,6 +127,7 @@ describe('getColor with darkColorHandler', () => {
getDarkColor: getDarkColorSpy,
updateKnownColor: updateKnownColorSpy,
reset: null!,
generateColorKey: defaultGenerateColorKey,
};
});

Expand Down Expand Up @@ -351,6 +357,7 @@ describe('setColor with darkColorHandler', () => {
getDarkColor: getDarkColorSpy,
updateKnownColor: updateKnownColorSpy,
reset: null!,
generateColorKey: defaultGenerateColorKey,
};
});

Expand Down Expand Up @@ -489,6 +496,49 @@ describe('setColor with darkColorHandler', () => {
darkModeColor: '--dark_green',
});
});

it('with customized generateColorKey', () => {
const generateColorKeySpy = jasmine
.createSpy('generateColorKey')
.and.callFake((color: string) => '--' + color + '_key');

const lightDiv = document.createElement('div');
const darkDiv = document.createElement('div');

darkColorHandler.generateColorKey = generateColorKeySpy;

setColor(lightDiv, 'red', true, false, darkColorHandler);
setColor(lightDiv, 'green', false, false, darkColorHandler);
setColor(darkDiv, 'red', true, true, darkColorHandler);
setColor(darkDiv, 'green', false, true, darkColorHandler);

expect(lightDiv.outerHTML).toBe('<div style="background-color: red; color: green;"></div>');
expect(darkDiv.outerHTML).toBe(
'<div style="background-color: var(--red_key, red); color: var(--green_key, green);"></div>'
);
expect(getDarkColorSpy).toHaveBeenCalledTimes(4);
expect(getDarkColorSpy).toHaveBeenCalledWith('green', undefined, 'text', lightDiv);
expect(getDarkColorSpy).toHaveBeenCalledWith('red', undefined, 'background', lightDiv);
expect(getDarkColorSpy).toHaveBeenCalledWith('green', undefined, 'text', darkDiv);
expect(getDarkColorSpy).toHaveBeenCalledWith('red', undefined, 'background', darkDiv);
expect(updateKnownColorSpy).toHaveBeenCalledTimes(4);
expect(updateKnownColorSpy).toHaveBeenCalledWith(false, '--red_key', {
lightModeColor: 'red',
darkModeColor: '--dark_red',
});
expect(updateKnownColorSpy).toHaveBeenCalledWith(false, '--green_key', {
lightModeColor: 'green',
darkModeColor: '--dark_green',
});
expect(updateKnownColorSpy).toHaveBeenCalledWith(true, '--red_key', {
lightModeColor: 'red',
darkModeColor: '--dark_red',
});
expect(updateKnownColorSpy).toHaveBeenCalledWith(true, '--green_key', {
lightModeColor: 'green',
darkModeColor: '--dark_green',
});
});
});

describe('parseColor', () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,4 +61,9 @@ export interface DarkColorHandler {
* A util function to transform light mode color to dark mode color
*/
getDarkColor: ColorTransformFunction;

/**
* Generate color key for dark mode color.
*/
generateColorKey: ColorTransformFunction;
}
Loading
Loading