diff --git a/demo/scripts/controls/ContentModelEditorMainPane.tsx b/demo/scripts/controls/ContentModelEditorMainPane.tsx index 1a277e010f0..84751b237c0 100644 --- a/demo/scripts/controls/ContentModelEditorMainPane.tsx +++ b/demo/scripts/controls/ContentModelEditorMainPane.tsx @@ -20,6 +20,7 @@ import { ContentModelRibbonPlugin } from './ribbonButtons/contentModel/ContentMo import { ContentModelSegmentFormat } from 'roosterjs-content-model-types'; import { createEmojiPlugin, createPasteOptionPlugin, RibbonPlugin } from 'roosterjs-react'; import { EditorPlugin } from 'roosterjs-editor-types'; +import { getDarkColor } from 'roosterjs-color-utils'; import { PartialTheme } from '@fluentui/react/lib/Theme'; import { trustedHTMLHandler } from '../utils/trustedHTMLHandler'; import { @@ -236,6 +237,7 @@ class ContentModelEditorMainPane extends MainPaneBase plugins={allPlugins} defaultSegmentFormat={defaultFormat} inDarkMode={this.state.isDarkMode} + getDarkColor={getDarkColor} experimentalFeatures={this.state.initState.experimentalFeatures} undoMetadataSnapshotService={this.snapshotPlugin.getSnapshotService()} trustedHTMLHandler={trustedHTMLHandler} diff --git a/packages-content-model/roosterjs-content-model-core/lib/editor/DarkColorHandlerImpl.ts b/packages-content-model/roosterjs-content-model-core/lib/editor/DarkColorHandlerImpl.ts index cda3a6bb61d..0f9687fcc6e 100644 --- a/packages-content-model/roosterjs-content-model-core/lib/editor/DarkColorHandlerImpl.ts +++ b/packages-content-model/roosterjs-content-model-core/lib/editor/DarkColorHandlerImpl.ts @@ -1,4 +1,3 @@ -import * as Color from 'color'; import { getObjectKeys, parseColor, setColor } from 'roosterjs-editor-dom'; import type { ColorKeyAndValue, @@ -6,7 +5,6 @@ import type { ModeIndependentColor, } from 'roosterjs-editor-types'; -const DefaultLightness = 21.25; // Lightness for #333333 const VARIABLE_REGEX = /^\s*var\(\s*(\-\-[a-zA-Z0-9\-_]+)\s*(?:,\s*(.*))?\)\s*$/; const VARIABLE_PREFIX = 'var('; const COLOR_VAR_PREFIX = 'darkColor'; @@ -30,11 +28,8 @@ const ColorAttributeName: { [key in ColorAttributeEnum]: string }[] = [ */ export class DarkColorHandlerImpl implements DarkColorHandler { private knownColors: Record> = {}; - readonly baseLightness: number; - constructor(private contentDiv: HTMLElement, baseDarkColor?: string) { - this.baseLightness = getLightness(baseDarkColor); - } + constructor(private contentDiv: HTMLElement, private getDarkColor: (color: string) => string) {} /** * Get a copy of known colors @@ -66,7 +61,7 @@ export class DarkColorHandlerImpl implements DarkColorHandler { colorKey || `--${COLOR_VAR_PREFIX}_${lightModeColor.replace(/[^\d\w]/g, '_')}`; if (!this.knownColors[colorKey]) { - darkModeColor = darkModeColor || getDarkColor(lightModeColor, this.baseLightness); + darkModeColor = darkModeColor || this.getDarkColor(lightModeColor); this.knownColors[colorKey] = { lightModeColor, darkModeColor }; this.contentDiv.style.setProperty(colorKey, darkModeColor); @@ -176,30 +171,3 @@ export class DarkColorHandlerImpl implements DarkColorHandler { }); } } - -function getDarkColor(color: string, baseLightness: number): string { - try { - const computedColor = Color(color || undefined); - const colorLab = computedColor.lab().array(); - const newLValue = (100 - colorLab[0]) * ((100 - baseLightness) / 100) + baseLightness; - color = Color.lab(newLValue, colorLab[1], colorLab[2]) - .rgb() - .alpha(computedColor.alpha()) - .toString(); - } catch {} - - return color; -} - -function getLightness(color?: string): number { - let result = DefaultLightness; - - if (color) { - try { - const computedColor = Color(color || undefined); - result = computedColor.lab().array()[0]; - } catch {} - } - - return result; -} diff --git a/packages-content-model/roosterjs-content-model-core/lib/editor/createStandaloneEditorCore.ts b/packages-content-model/roosterjs-content-model-core/lib/editor/createStandaloneEditorCore.ts index aa92da5fad5..c561fa1b6a1 100644 --- a/packages-content-model/roosterjs-content-model-core/lib/editor/createStandaloneEditorCore.ts +++ b/packages-content-model/roosterjs-content-model-core/lib/editor/createStandaloneEditorCore.ts @@ -42,7 +42,10 @@ export function createStandaloneEditorCore( corePlugins.lifecycle, ], environment: createEditorEnvironment(), - darkColorHandler: new DarkColorHandlerImpl(contentDiv, options.baseDarkColor), + darkColorHandler: new DarkColorHandlerImpl( + contentDiv, + options.getDarkColor ?? getDarkColorFallback + ), trustedHTMLHandler: options.trustedHTMLHandler || defaultTrustHtmlHandler, ...createStandaloneEditorDefaultSettings(options), ...getPluginState(corePlugins), @@ -82,3 +85,8 @@ function getPluginState(corePlugins: StandaloneEditorCorePlugins): StandaloneEdi selection: corePlugins.selection.getState(), }; } + +// A fallback function, always return original color +function getDarkColorFallback(color: string) { + return color; +} diff --git a/packages-content-model/roosterjs-content-model-core/package.json b/packages-content-model/roosterjs-content-model-core/package.json index 40d2aab4f68..c037037dea2 100644 --- a/packages-content-model/roosterjs-content-model-core/package.json +++ b/packages-content-model/roosterjs-content-model-core/package.json @@ -3,7 +3,6 @@ "description": "Content Model for roosterjs (Under development)", "dependencies": { "tslib": "^2.3.1", - "color": "^3.0.0", "roosterjs-editor-types": "", "roosterjs-editor-dom": "", "roosterjs-content-model-dom": "", diff --git a/packages-content-model/roosterjs-content-model-core/test/editor/DarkColorHandlerImplTest.ts b/packages-content-model/roosterjs-content-model-core/test/editor/DarkColorHandlerImplTest.ts index 0372c39c164..61ca7c9cc99 100644 --- a/packages-content-model/roosterjs-content-model-core/test/editor/DarkColorHandlerImplTest.ts +++ b/packages-content-model/roosterjs-content-model-core/test/editor/DarkColorHandlerImplTest.ts @@ -1,26 +1,21 @@ import { ColorKeyAndValue } from 'roosterjs-editor-types'; import { DarkColorHandlerImpl } from '../../lib/editor/DarkColorHandlerImpl'; +function getDarkColor(color: string) { + return 'Dark_' + color; +} + describe('DarkColorHandlerImpl.ctor', () => { it('No additional param', () => { const div = document.createElement('div'); - const handler = new DarkColorHandlerImpl(div); - - expect(handler).toBeDefined(); - expect(handler.baseLightness).toBe(21.25); - }); - - it('With customized base color', () => { - const div = document.createElement('div'); - const handler = new DarkColorHandlerImpl(div, '#555555'); + const handler = new DarkColorHandlerImpl(div, getDarkColor); expect(handler).toBeDefined(); - expect(Math.round(handler.baseLightness)).toBe(36); }); it('Calculate color using customized base color', () => { const div = document.createElement('div'); - const handler = new DarkColorHandlerImpl(div, '#555555'); + const handler = new DarkColorHandlerImpl(div, getDarkColor); const darkColor = handler.registerColor('red', true); const parsedColor = handler.parseColorValue(darkColor); @@ -29,7 +24,7 @@ describe('DarkColorHandlerImpl.ctor', () => { expect(parsedColor).toEqual({ key: '--darkColor_red', lightModeColor: 'red', - darkModeColor: 'rgb(255, 72, 40)', + darkModeColor: 'Dark_red', }); }); }); @@ -40,7 +35,7 @@ describe('DarkColorHandlerImpl.parseColorValue', () => { beforeEach(() => { div = document.createElement('div'); - handler = new DarkColorHandlerImpl(div); + handler = new DarkColorHandlerImpl(div, getDarkColor); }); function runTest(input: string, expectedOutput: ColorKeyAndValue) { @@ -143,7 +138,7 @@ describe('DarkColorHandlerImpl.registerColor', () => { setProperty, }, } as any) as HTMLElement; - handler = new DarkColorHandlerImpl(div); + handler = new DarkColorHandlerImpl(div, getDarkColor); }); function runTest( @@ -186,10 +181,10 @@ describe('DarkColorHandlerImpl.registerColor', () => { { '--darkColor_red': { lightModeColor: 'red', - darkModeColor: 'rgb(255, 39, 17)', + darkModeColor: 'Dark_red', }, }, - [['--darkColor_red', 'rgb(255, 39, 17)']] + [['--darkColor_red', 'Dark_red']] ); }); @@ -222,10 +217,10 @@ describe('DarkColorHandlerImpl.registerColor', () => { { '--aa': { lightModeColor: 'red', - darkModeColor: 'rgb(255, 39, 17)', + darkModeColor: 'Dark_red', }, }, - [['--aa', 'rgb(255, 39, 17)']] + [['--aa', 'Dark_red']] ); }); @@ -395,7 +390,7 @@ describe('DarkColorHandlerImpl.transformElementColor', () => { beforeEach(() => { contentDiv = document.createElement('div'); - handler = new DarkColorHandlerImpl(contentDiv); + handler = new DarkColorHandlerImpl(contentDiv, getDarkColor); parseColorSpy = spyOn(handler, 'parseColorValue').and.callThrough(); registerColorSpy = spyOn(handler, 'registerColor').and.callThrough(); diff --git a/packages-content-model/roosterjs-content-model-types/lib/editor/StandaloneEditorOptions.ts b/packages-content-model/roosterjs-content-model-types/lib/editor/StandaloneEditorOptions.ts index 6aeb08701ef..43b52d4b30e 100644 --- a/packages-content-model/roosterjs-content-model-types/lib/editor/StandaloneEditorOptions.ts +++ b/packages-content-model/roosterjs-content-model-types/lib/editor/StandaloneEditorOptions.ts @@ -52,10 +52,10 @@ export interface StandaloneEditorOptions { scrollContainer?: HTMLElement; /** - * Base dark mode color. We will use this color to calculate the dark mode color from a given light mode color - * @default #333333 + * A util function to transform light mode color to dark mode color + * Default value is to return the original light color */ - baseDarkColor?: string; + getDarkColor?: (lightColor: string) => string; /** * Customized trusted type handler used for sanitizing HTML string before assign to DOM tree