From 376510058068748aa74d393878e2bb750ff5ae84 Mon Sep 17 00:00:00 2001 From: Jiuqing Song Date: Tue, 4 Jun 2024 11:53:58 -0700 Subject: [PATCH 1/3] Allow customization of color key generation --- .../lib/editor/core/DarkColorHandlerImpl.ts | 8 +- .../lib/editor/core/createEditorCore.ts | 4 +- .../lib/formatHandlers/utils/color.ts | 27 ++-- .../roosterjs-content-model-dom/lib/index.ts | 8 +- .../lib/context/DarkColorHandler.ts | 5 + .../lib/editor/EditorOptions.ts | 135 +++++++++++------- .../lib/index.ts | 9 +- 7 files changed, 134 insertions(+), 62 deletions(-) diff --git a/packages/roosterjs-content-model-core/lib/editor/core/DarkColorHandlerImpl.ts b/packages/roosterjs-content-model-core/lib/editor/core/DarkColorHandlerImpl.ts index fdbfc103055..da0425e1f77 100644 --- a/packages/roosterjs-content-model-core/lib/editor/core/DarkColorHandlerImpl.ts +++ b/packages/roosterjs-content-model-core/lib/editor/core/DarkColorHandlerImpl.ts @@ -8,7 +8,8 @@ class DarkColorHandlerImpl implements DarkColorHandler { constructor( private readonly root: HTMLElement, public getDarkColor: ColorTransformFunction, - public readonly knownColors: Record + public readonly knownColors: Record, + public generateColorKey: ColorTransformFunction ) {} updateKnownColor(isDarkMode: boolean, key?: string, colorPair?: Colors): void { @@ -48,7 +49,8 @@ class DarkColorHandlerImpl implements DarkColorHandler { export function createDarkColorHandler( root: HTMLElement, getDarkColor: ColorTransformFunction, - knownColors: Record = {} + knownColors: Record = {}, + generateColorKey: ColorTransformFunction ): DarkColorHandler { - return new DarkColorHandlerImpl(root, getDarkColor, knownColors); + return new DarkColorHandlerImpl(root, getDarkColor, knownColors, generateColorKey); } diff --git a/packages/roosterjs-content-model-core/lib/editor/core/createEditorCore.ts b/packages/roosterjs-content-model-core/lib/editor/core/createEditorCore.ts index b1e05569c4c..58e8aa60ccb 100644 --- a/packages/roosterjs-content-model-core/lib/editor/core/createEditorCore.ts +++ b/packages/roosterjs-content-model-core/lib/editor/core/createEditorCore.ts @@ -3,6 +3,7 @@ import { createDarkColorHandler } from './DarkColorHandlerImpl'; import { createDOMHelper } from './DOMHelperImpl'; import { createDomToModelSettings, createModelToDomSettings } from './createEditorDefaultSettings'; import { createEditorCorePlugins } from '../../corePlugin/createEditorCorePlugins'; +import { generateColorKey } from 'roosterjs-content-model-dom'; import type { EditorEnvironment, PluginState, @@ -40,7 +41,8 @@ export function createEditorCore(contentDiv: HTMLDivElement, options: EditorOpti darkColorHandler: createDarkColorHandler( contentDiv, options.getDarkColor ?? getDarkColorFallback, - options.knownColors + options.knownColors, + options.generateColorKey ?? generateColorKey ), trustedHTMLHandler: options.trustedHTMLHandler || defaultTrustHtmlHandler, domHelper: createDOMHelper(contentDiv), diff --git a/packages/roosterjs-content-model-dom/lib/formatHandlers/utils/color.ts b/packages/roosterjs-content-model-dom/lib/formatHandlers/utils/color.ts index 3841dca57b0..3731f6e0858 100644 --- a/packages/roosterjs-content-model-dom/lib/formatHandlers/utils/color.ts +++ b/packages/roosterjs-content-model-dom/lib/formatHandlers/utils/color.ts @@ -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 @@ -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, @@ -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 generateColorKey: 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 diff --git a/packages/roosterjs-content-model-dom/lib/index.ts b/packages/roosterjs-content-model-dom/lib/index.ts index 00eaa77bd95..368c3d2c7ee 100644 --- a/packages/roosterjs-content-model-dom/lib/index.ts +++ b/packages/roosterjs-content-model-dom/lib/index.ts @@ -76,7 +76,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, + generateColorKey, +} from './formatHandlers/utils/color'; export { createDomToModelContext, diff --git a/packages/roosterjs-content-model-types/lib/context/DarkColorHandler.ts b/packages/roosterjs-content-model-types/lib/context/DarkColorHandler.ts index e9aac2fc723..f9f459cd992 100644 --- a/packages/roosterjs-content-model-types/lib/context/DarkColorHandler.ts +++ b/packages/roosterjs-content-model-types/lib/context/DarkColorHandler.ts @@ -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; } diff --git a/packages/roosterjs-content-model-types/lib/editor/EditorOptions.ts b/packages/roosterjs-content-model-types/lib/editor/EditorOptions.ts index 6e421f6f8ee..ae0d2212630 100644 --- a/packages/roosterjs-content-model-types/lib/editor/EditorOptions.ts +++ b/packages/roosterjs-content-model-types/lib/editor/EditorOptions.ts @@ -12,36 +12,53 @@ import type { Snapshots } from '../parameter/Snapshot'; import type { TrustedHTMLHandler } from '../parameter/TrustedHTMLHandler'; /** - * Options for editor + * Options for colors and dark mode */ -export interface EditorOptions { +export interface ColorOptions { /** - * Default options used for DOM to Content Model conversion + * A util function to transform light mode color to dark mode color + * Default value is to return the original light color */ - defaultDomToModelOptions?: DomToModelOption; + getDarkColor?: ColorTransformFunction; /** - * Default options used for Content Model to DOM conversion + * A util function to generate color key for dark mode color. + * By default, the color key is generated from the light mode color. For example, + * color "#123456" will have the key "_123456", and + * color "rgb(0,0,0)" will have key "rgb_0_0_0_". + * Pass in this function to customize this behavior. */ - defaultModelToDomOptions?: ModelToDomOption; + generateColorKey?: ColorTransformFunction; /** - * @deprecated + * Existing known color pairs */ - disableCache?: boolean; + knownColors?: Record; /** - * Enabled experimental features + * Whether to skip the adjust editor process when for light/dark mode */ - experimentalFeatures?: (ExperimentalFeature | string)[]; + doNotAdjustEditorColor?: boolean; /** - * List of plugins. - * The order of plugins here determines in what order each event will be dispatched. - * Plugins not appear in this list will not be added to editor, including built-in plugins. - * Default value is empty array. + * If the editor is currently in dark mode */ - plugins?: EditorPlugin[]; + inDarkMode?: boolean; +} + +/** + * Options for Content Model + */ +export interface ContentModelOptions { + /** + * Default options used for DOM to Content Model conversion + */ + defaultDomToModelOptions?: DomToModelOption; + + /** + * Default options used for Content Model to DOM conversion + */ + defaultModelToDomOptions?: ModelToDomOption; /** * Default format of editor content. This will be applied to empty content. @@ -51,65 +68,82 @@ export interface EditorOptions { defaultSegmentFormat?: ContentModelSegmentFormat; /** - * Allowed custom content type when paste besides text/plain, text/html and images - * Only text types are supported, and do not add "text/" prefix to the type values + * @deprecated */ - allowedCustomPasteType?: string[]; + disableCache?: boolean; +} +/** + * Options for selection + */ +export interface SelectionOptions { /** - * The scroll container to get scroll event from. - * By default, the scroll container will be the same with editor content DIV + * Color of the border of a selectedImage. Default color: '#DB626C' */ - scrollContainer?: HTMLElement; + imageSelectionBorderColor?: string; /** - * A util function to transform light mode color to dark mode color - * Default value is to return the original light color + * Background color of a selected table cell. Default color: '#C6C6C6' */ - getDarkColor?: ColorTransformFunction; + tableCellSelectionBackgroundColor?: string; +} +/** + * Options for paste + */ +export interface PasteOptions { /** - * Existing known color pairs + * Allowed custom content type when paste besides text/plain, text/html and images + * Only text types are supported, and do not add "text/" prefix to the type values */ - knownColors?: Record; + allowedCustomPasteType?: string[]; /** - * Customized trusted type handler used for sanitizing HTML string before assign to DOM tree - * This is required when trusted-type Content-Security-Policy (CSP) is enabled. - * See https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/trusted-types + * Default paste type. By default will use the normal (as-is) paste type. */ - trustedHTMLHandler?: TrustedHTMLHandler; + defaultPasteType?: PasteType; +} +/** + * Options for editor fundamental data structure + */ +export interface EditorBaseOptions { /** - * A function map to override default core API implementation - * Default value is null + * Enabled experimental features */ - coreApiOverride?: Partial; + experimentalFeatures?: (ExperimentalFeature | string)[]; /** - * Color of the border of a selectedImage. Default color: '#DB626C' + * List of plugins. + * The order of plugins here determines in what order each event will be dispatched. + * Plugins not appear in this list will not be added to editor, including built-in plugins. + * Default value is empty array. */ - imageSelectionBorderColor?: string; + plugins?: EditorPlugin[]; /** - * Background color of a selected table cell. Default color: '#C6C6C6' + * The scroll container to get scroll event from. + * By default, the scroll container will be the same with editor content DIV */ - tableCellSelectionBackgroundColor?: string; + scrollContainer?: HTMLElement; /** - * Initial Content Model + * Customized trusted type handler used for sanitizing HTML string before assign to DOM tree + * This is required when trusted-type Content-Security-Policy (CSP) is enabled. + * See https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/trusted-types */ - initialModel?: ContentModelDocument; + trustedHTMLHandler?: TrustedHTMLHandler; /** - * Whether to skip the adjust editor process when for light/dark mode + * A function map to override default core API implementation + * Default value is null */ - doNotAdjustEditorColor?: boolean; + coreApiOverride?: Partial; /** - * If the editor is currently in dark mode + * Initial Content Model */ - inDarkMode?: boolean; + initialModel?: ContentModelDocument; /** * Undo snapshot. Use this parameter to provide an external storage of undo snapshots @@ -123,11 +157,6 @@ export interface EditorOptions { */ disposeErrorHandler?: (plugin: EditorPlugin, error: Error) => void; - /** - * Default paste type. By default will use the normal (as-is) paste type. - */ - defaultPasteType?: PasteType; - /** * A callback to help get string template to announce, used for accessibility * @param key The key of known announce data @@ -135,3 +164,13 @@ export interface EditorOptions { */ announcerStringGetter?: (key: KnownAnnounceStrings) => string; } + +/** + * Options for editor + */ +export interface EditorOptions + extends EditorBaseOptions, + ColorOptions, + ContentModelOptions, + SelectionOptions, + PasteOptions {} diff --git a/packages/roosterjs-content-model-types/lib/index.ts b/packages/roosterjs-content-model-types/lib/index.ts index f236306d258..5d816d6e349 100644 --- a/packages/roosterjs-content-model-types/lib/index.ts +++ b/packages/roosterjs-content-model-types/lib/index.ts @@ -327,7 +327,14 @@ export { DarkColorHandler, Colors, ColorTransformFunction } from './context/Dark export { IEditor } from './editor/IEditor'; export { ExperimentalFeature } from './editor/ExperimentalFeature'; -export { EditorOptions } from './editor/EditorOptions'; +export { + EditorOptions, + ColorOptions, + ContentModelOptions, + SelectionOptions, + PasteOptions, + EditorBaseOptions, +} from './editor/EditorOptions'; export { CreateContentModel, CreateEditorContext, From 31c48ea553ed7806939e61de00a2da98f56a55fb Mon Sep 17 00:00:00 2001 From: Jiuqing Song Date: Thu, 6 Jun 2024 13:24:28 -0700 Subject: [PATCH 2/3] fix build --- .../lib/editor/core/DarkColorHandlerImpl.ts | 3 ++- .../lib/editor/core/createEditorCore.ts | 3 +-- .../test/editor/core/createEditorCoreTest.ts | 7 +++++++ .../lib/formatHandlers/utils/color.ts | 2 +- packages/roosterjs-content-model-dom/lib/index.ts | 2 +- .../common/backgroundColorFormatHandlerTest.ts | 3 ++- .../formatHandlers/segment/textColorFormatHandlerTest.ts | 3 ++- .../test/formatHandlers/utils/colorTest.ts | 9 ++++++++- 8 files changed, 24 insertions(+), 8 deletions(-) diff --git a/packages/roosterjs-content-model-core/lib/editor/core/DarkColorHandlerImpl.ts b/packages/roosterjs-content-model-core/lib/editor/core/DarkColorHandlerImpl.ts index da0425e1f77..79973ab8c39 100644 --- a/packages/roosterjs-content-model-core/lib/editor/core/DarkColorHandlerImpl.ts +++ b/packages/roosterjs-content-model-core/lib/editor/core/DarkColorHandlerImpl.ts @@ -1,3 +1,4 @@ +import { defaultGenerateColorKey } from 'roosterjs-content-model-dom'; import type { DarkColorHandler, ColorTransformFunction, @@ -50,7 +51,7 @@ export function createDarkColorHandler( root: HTMLElement, getDarkColor: ColorTransformFunction, knownColors: Record = {}, - generateColorKey: ColorTransformFunction + generateColorKey: ColorTransformFunction = defaultGenerateColorKey ): DarkColorHandler { return new DarkColorHandlerImpl(root, getDarkColor, knownColors, generateColorKey); } diff --git a/packages/roosterjs-content-model-core/lib/editor/core/createEditorCore.ts b/packages/roosterjs-content-model-core/lib/editor/core/createEditorCore.ts index 58e8aa60ccb..7dbea590e45 100644 --- a/packages/roosterjs-content-model-core/lib/editor/core/createEditorCore.ts +++ b/packages/roosterjs-content-model-core/lib/editor/core/createEditorCore.ts @@ -3,7 +3,6 @@ import { createDarkColorHandler } from './DarkColorHandlerImpl'; import { createDOMHelper } from './DOMHelperImpl'; import { createDomToModelSettings, createModelToDomSettings } from './createEditorDefaultSettings'; import { createEditorCorePlugins } from '../../corePlugin/createEditorCorePlugins'; -import { generateColorKey } from 'roosterjs-content-model-dom'; import type { EditorEnvironment, PluginState, @@ -42,7 +41,7 @@ export function createEditorCore(contentDiv: HTMLDivElement, options: EditorOpti contentDiv, options.getDarkColor ?? getDarkColorFallback, options.knownColors, - options.generateColorKey ?? generateColorKey + options.generateColorKey ), trustedHTMLHandler: options.trustedHTMLHandler || defaultTrustHtmlHandler, domHelper: createDOMHelper(contentDiv), diff --git a/packages/roosterjs-content-model-core/test/editor/core/createEditorCoreTest.ts b/packages/roosterjs-content-model-core/test/editor/core/createEditorCoreTest.ts index 24a82bd0f19..b63dfb846a9 100644 --- a/packages/roosterjs-content-model-core/test/editor/core/createEditorCoreTest.ts +++ b/packages/roosterjs-content-model-core/test/editor/core/createEditorCoreTest.ts @@ -130,6 +130,7 @@ describe('createEditorCore', () => { expect(DarkColorHandlerImpl.createDarkColorHandler).toHaveBeenCalledWith( mockedDiv, getDarkColorFallback, + undefined, undefined ); }); @@ -181,6 +182,7 @@ describe('createEditorCore', () => { expect(DarkColorHandlerImpl.createDarkColorHandler).toHaveBeenCalledWith( mockedDiv, mockedGetDarkColor, + undefined, undefined ); }); @@ -214,6 +216,7 @@ describe('createEditorCore', () => { expect(DarkColorHandlerImpl.createDarkColorHandler).toHaveBeenCalledWith( mockedDiv, getDarkColorFallback, + undefined, undefined ); }); @@ -247,6 +250,7 @@ describe('createEditorCore', () => { expect(DarkColorHandlerImpl.createDarkColorHandler).toHaveBeenCalledWith( mockedDiv, getDarkColorFallback, + undefined, undefined ); }); @@ -280,6 +284,7 @@ describe('createEditorCore', () => { expect(DarkColorHandlerImpl.createDarkColorHandler).toHaveBeenCalledWith( mockedDiv, getDarkColorFallback, + undefined, undefined ); }); @@ -313,6 +318,7 @@ describe('createEditorCore', () => { expect(DarkColorHandlerImpl.createDarkColorHandler).toHaveBeenCalledWith( mockedDiv, getDarkColorFallback, + undefined, undefined ); }); @@ -346,6 +352,7 @@ describe('createEditorCore', () => { expect(DarkColorHandlerImpl.createDarkColorHandler).toHaveBeenCalledWith( mockedDiv, getDarkColorFallback, + undefined, undefined ); }); diff --git a/packages/roosterjs-content-model-dom/lib/formatHandlers/utils/color.ts b/packages/roosterjs-content-model-dom/lib/formatHandlers/utils/color.ts index 3731f6e0858..bd3e117634d 100644 --- a/packages/roosterjs-content-model-dom/lib/formatHandlers/utils/color.ts +++ b/packages/roosterjs-content-model-dom/lib/formatHandlers/utils/color.ts @@ -126,7 +126,7 @@ export function setColor( * @param lightColor The input light color * @returns Key of the color */ -export const generateColorKey: ColorTransformFunction = lightColor => { +export const defaultGenerateColorKey: ColorTransformFunction = lightColor => { return `${COLOR_VAR_PREFIX}_${lightColor.replace(/[^\d\w]/g, '_')}`; }; diff --git a/packages/roosterjs-content-model-dom/lib/index.ts b/packages/roosterjs-content-model-dom/lib/index.ts index 55c0f0658c0..c8bdc50dbd0 100644 --- a/packages/roosterjs-content-model-dom/lib/index.ts +++ b/packages/roosterjs-content-model-dom/lib/index.ts @@ -82,7 +82,7 @@ export { getColor, setColor, parseColor, - generateColorKey, + defaultGenerateColorKey, } from './formatHandlers/utils/color'; export { diff --git a/packages/roosterjs-content-model-dom/test/formatHandlers/common/backgroundColorFormatHandlerTest.ts b/packages/roosterjs-content-model-dom/test/formatHandlers/common/backgroundColorFormatHandlerTest.ts index 8b2fff7bd78..3e5f24ccc61 100644 --- a/packages/roosterjs-content-model-dom/test/formatHandlers/common/backgroundColorFormatHandlerTest.ts +++ b/packages/roosterjs-content-model-dom/test/formatHandlers/common/backgroundColorFormatHandlerTest.ts @@ -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, @@ -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); diff --git a/packages/roosterjs-content-model-dom/test/formatHandlers/segment/textColorFormatHandlerTest.ts b/packages/roosterjs-content-model-dom/test/formatHandlers/segment/textColorFormatHandlerTest.ts index 379dab76eab..96978de9563 100644 --- a/packages/roosterjs-content-model-dom/test/formatHandlers/segment/textColorFormatHandlerTest.ts +++ b/packages/roosterjs-content-model-dom/test/formatHandlers/segment/textColorFormatHandlerTest.ts @@ -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 { @@ -110,6 +110,7 @@ describe('textColorFormatHandler.apply', () => { context.darkColorHandler = { updateKnownColor: () => {}, getDarkColor: (lightColor: string) => `var(--darkColor_${lightColor}, ${lightColor})`, + generateColorKey: defaultGenerateColorKey, } as any; format = {}; diff --git a/packages/roosterjs-content-model-dom/test/formatHandlers/utils/colorTest.ts b/packages/roosterjs-content-model-dom/test/formatHandlers/utils/colorTest.ts index baeaf39c4de..82451f0838c 100644 --- a/packages/roosterjs-content-model-dom/test/formatHandlers/utils/colorTest.ts +++ b/packages/roosterjs-content-model-dom/test/formatHandlers/utils/colorTest.ts @@ -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', () => { @@ -122,6 +127,7 @@ describe('getColor with darkColorHandler', () => { getDarkColor: getDarkColorSpy, updateKnownColor: updateKnownColorSpy, reset: null!, + generateColorKey: defaultGenerateColorKey, }; }); @@ -351,6 +357,7 @@ describe('setColor with darkColorHandler', () => { getDarkColor: getDarkColorSpy, updateKnownColor: updateKnownColorSpy, reset: null!, + generateColorKey: defaultGenerateColorKey, }; }); From 1b67dd6996a04d06b96b24cc2bd1f59a3a0dd103 Mon Sep 17 00:00:00 2001 From: Jiuqing Song Date: Thu, 6 Jun 2024 13:41:37 -0700 Subject: [PATCH 3/3] add test --- .../editor/core/DarkColorHandlerImplTest.ts | 11 ++++- .../test/editor/core/createEditorCoreTest.ts | 8 +++- .../test/formatHandlers/utils/colorTest.ts | 43 +++++++++++++++++++ .../lib/editor/EditorOptions.ts | 5 ++- 4 files changed, 61 insertions(+), 6 deletions(-) diff --git a/packages/roosterjs-content-model-core/test/editor/core/DarkColorHandlerImplTest.ts b/packages/roosterjs-content-model-core/test/editor/core/DarkColorHandlerImplTest.ts index fc495d755fc..ee878574e32 100644 --- a/packages/roosterjs-content-model-core/test/editor/core/DarkColorHandlerImplTest.ts +++ b/packages/roosterjs-content-model-core/test/editor/core/DarkColorHandlerImplTest.ts @@ -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); }); }); diff --git a/packages/roosterjs-content-model-core/test/editor/core/createEditorCoreTest.ts b/packages/roosterjs-content-model-core/test/editor/core/createEditorCoreTest.ts index b63dfb846a9..b4af3645965 100644 --- a/packages/roosterjs-content-model-core/test/editor/core/createEditorCoreTest.ts +++ b/packages/roosterjs-content-model-core/test/editor/core/createEditorCoreTest.ts @@ -147,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', @@ -155,6 +157,8 @@ describe('createEditorCore', () => { getDarkColor: mockedGetDarkColor, trustedHTMLHandler: mockedTrustHtmlHandler, disposeErrorHandler: mockedDisposeErrorHandler, + generateColorKey: mockedGenerateColorKey, + knownColors: mockedKnownColors, } as any; runTest(mockedDiv, mockedOptions, { @@ -182,8 +186,8 @@ describe('createEditorCore', () => { expect(DarkColorHandlerImpl.createDarkColorHandler).toHaveBeenCalledWith( mockedDiv, mockedGetDarkColor, - undefined, - undefined + mockedKnownColors, + mockedGenerateColorKey ); }); diff --git a/packages/roosterjs-content-model-dom/test/formatHandlers/utils/colorTest.ts b/packages/roosterjs-content-model-dom/test/formatHandlers/utils/colorTest.ts index 82451f0838c..bdbd4560018 100644 --- a/packages/roosterjs-content-model-dom/test/formatHandlers/utils/colorTest.ts +++ b/packages/roosterjs-content-model-dom/test/formatHandlers/utils/colorTest.ts @@ -496,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('
'); + expect(darkDiv.outerHTML).toBe( + '
' + ); + 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', () => { diff --git a/packages/roosterjs-content-model-types/lib/editor/EditorOptions.ts b/packages/roosterjs-content-model-types/lib/editor/EditorOptions.ts index ae0d2212630..a1aa222999f 100644 --- a/packages/roosterjs-content-model-types/lib/editor/EditorOptions.ts +++ b/packages/roosterjs-content-model-types/lib/editor/EditorOptions.ts @@ -24,9 +24,10 @@ export interface ColorOptions { /** * A util function to generate color key for dark mode color. * By default, the color key is generated from the light mode color. For example, - * color "#123456" will have the key "_123456", and - * color "rgb(0,0,0)" will have key "rgb_0_0_0_". + * color "#123456" will have the key "--darkColor__123456", and + * color "rgb(0,0,0)" will have key "--darkColor_rgb_0_0_0_". * Pass in this function to customize this behavior. + * The return value must be a valid CSS variable, starts with "--" */ generateColorKey?: ColorTransformFunction;