Skip to content

Commit

Permalink
Merge branch 'master' into u/jisong/formatonli
Browse files Browse the repository at this point in the history
  • Loading branch information
JiuqingSong authored Jun 7, 2024
2 parents f25a6aa + 3cab230 commit d455d2a
Show file tree
Hide file tree
Showing 31 changed files with 2,657 additions and 524 deletions.
9 changes: 9 additions & 0 deletions packages/roosterjs-content-model-core/lib/editor/Editor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ import type {
CachedElementHandler,
DomToModelOptionForCreateModel,
AnnounceData,
ExperimentalFeature,
} from 'roosterjs-content-model-types';

/**
Expand Down Expand Up @@ -406,6 +407,14 @@ export class Editor implements IEditor {
core.api.announce(core, announceData);
}

/**
* Check if a given feature is enabled
* @param featureName The name of feature to check
*/
isExperimentalFeatureEnabled(featureName: ExperimentalFeature | string): boolean {
return this.getCore().experimentalFeatures.indexOf(featureName) >= 0;
}

/**
* @returns the current EditorCore object
* @throws a standard Error if there's no core object
Expand Down
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
32 changes: 32 additions & 0 deletions packages/roosterjs-content-model-core/test/editor/EditorTest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1116,4 +1116,36 @@ describe('Editor', () => {
expect(resetSpy).toHaveBeenCalledWith();
expect(() => editor.announce(mockedData)).toThrow();
});

it('isExperimentalFeatureEnabled', () => {
const div = document.createElement('div');
const resetSpy = jasmine.createSpy('reset');
const mockedCore = {
plugins: [],
darkColorHandler: {
updateKnownColor: updateKnownColorSpy,
reset: resetSpy,
},
api: {
setContentModel: setContentModelSpy,
},
experimentalFeatures: ['Feature1', 'Feature2'],
} as any;

createEditorCoreSpy.and.returnValue(mockedCore);

const editor = new Editor(div);

const result1 = editor.isExperimentalFeatureEnabled('Feature1');
const result2 = editor.isExperimentalFeatureEnabled('Feature2');
const result3 = editor.isExperimentalFeatureEnabled('Feature3');

expect(result1).toBeTrue();
expect(result2).toBeTrue();
expect(result3).toBeFalse();

editor.dispose();
expect(resetSpy).toHaveBeenCalledWith();
expect(() => editor.isExperimentalFeatureEnabled('Feature4')).toThrow();
});
});
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
12 changes: 9 additions & 3 deletions 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 All @@ -104,8 +110,6 @@ export { cacheGetEventData } from './domUtils/event/cacheGetEventData';

export { isBlockGroupOfType } from './modelApi/typeCheck/isBlockGroupOfType';

export { getClosestAncestorBlockGroupIndex } from './modelApi/editing/getClosestAncestorBlockGroupIndex';

export { iterateSelections } from './modelApi/selection/iterateSelections';
export {
getFirstSelectedListItem,
Expand Down Expand Up @@ -136,6 +140,8 @@ export { setTableCellBackgroundColor } from './modelApi/editing/setTableCellBack
export { retrieveModelFormatState } from './modelApi/editing/retrieveModelFormatState';
export { getListStyleTypeFromString } from './modelApi/editing/getListStyleTypeFromString';
export { getSegmentTextFormat } from './modelApi/editing/getSegmentTextFormat';
export { getClosestAncestorBlockGroupIndex } from './modelApi/editing/getClosestAncestorBlockGroupIndex';
export { runEditSteps } from './modelApi/editing/runEditSteps';

export { updateImageMetadata, getImageMetadata } from './modelApi/metadata/updateImageMetadata';
export {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import { deleteExpandedSelection } from './deleteExpandedSelection';
import { mutateBlock } from '../common/mutate';
import { runEditSteps } from './runEditSteps';
import type {
DeleteSelectionContext,
DeleteSelectionResult,
DeleteSelectionStep,
FormatContentModelContext,
ReadonlyContentModelDocument,
ValidDeleteSelectionContext,
} from 'roosterjs-content-model-types';

/**
Expand All @@ -22,23 +22,16 @@ export function deleteSelection(
formatContext?: FormatContentModelContext
): DeleteSelectionResult {
const context = deleteExpandedSelection(model, formatContext);
const steps = additionalSteps.filter(
(x: DeleteSelectionStep | null): x is DeleteSelectionStep => !!x
);

additionalSteps.forEach(step => {
if (step && isValidDeleteSelectionContext(context)) {
step(context);
}
});
runEditSteps(steps, context);

mergeParagraphAfterDelete(context);
return context;
}

function isValidDeleteSelectionContext(
context: DeleteSelectionContext
): context is ValidDeleteSelectionContext {
return !!context.insertPoint;
}

// If we end up with multiple paragraphs impacted, we need to merge them
function mergeParagraphAfterDelete(context: DeleteSelectionContext) {
const { insertPoint, deleteResult, lastParagraph, lastTableContext } = context;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import type {
DeleteSelectionContext,
DeleteSelectionResult,
DeleteSelectionStep,
ValidDeleteSelectionContext,
} from 'roosterjs-content-model-types';

/**
* Run editing steps on top of a given context object which includes current insert point and previous editing result
* @param steps The editing steps to run
* @param context Context for the editing steps.
*/
export function runEditSteps(steps: DeleteSelectionStep[], context: DeleteSelectionResult) {
steps.forEach(step => {
if (step && isValidDeleteSelectionContext(context)) {
step(context);
}
});
}

function isValidDeleteSelectionContext(
context: DeleteSelectionContext
): context is ValidDeleteSelectionContext {
return !!context.insertPoint;
}
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
Loading

0 comments on commit d455d2a

Please sign in to comment.