Skip to content

Commit

Permalink
Merge branch 'master' into u/juliaroldi/markdown-plugin-port
Browse files Browse the repository at this point in the history
  • Loading branch information
juliaroldi authored Mar 28, 2024
2 parents 274e8bf + c0198f4 commit cc8c4e1
Show file tree
Hide file tree
Showing 43 changed files with 2,599 additions and 255 deletions.
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@ To view the demo site, please click the link below:

[RoosterJs Demo Site](https://microsoft.github.io/roosterjs/index.html).

## Upgrade from RoosterJs 8.\*

Please see [here](https://github.com/microsoft/roosterjs/wiki/RoosterJs-9).

## Features

### Packages
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,13 @@ export const TextColorFormatRenderer: FormatRenderer<TextColorFormat> = createCo
TextColorFormat
>(
'Text color',
format => (format.textColor ? Color(format.textColor).hex() : ''),
format => {
try {
return format.textColor ? Color(format.textColor).hex() : '';
} catch (e) {
console.log(e);
}
},
(format, value) => {
format.textColor = value;
return undefined;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ function TextFormatItem<T>(props: {
(newValue: string) => {
setValue(newValue);
setter?.(format, newValue);
onUpdate();
onUpdate?.();
},
[setter, format]
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ interface FormatStateContext extends DomToModelContext {

/**
* @internal
* Export for test only
* In order to get format, we can still use the regular child processor. However, to improve performance, we don't need to create
* content model for the whole doc, instead we only need to traverse the tree path that can arrive current selected node.
* This "reduced" child processor will first create a node stack that stores DOM node from root to current common ancestor node of selection,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,9 @@ import type {
ContentModelDocument,
ContentModelEntity,
ContentModelParagraph,
DeleteSelectionResult,
FormatContentModelContext,
InsertEntityPosition,
InsertPoint,
} from 'roosterjs-content-model-types';

/**
Expand All @@ -27,11 +27,12 @@ export function insertEntityModel(
position: InsertEntityPosition,
isBlock: boolean,
focusAfterEntity?: boolean,
context?: FormatContentModelContext
context?: FormatContentModelContext,
insertPointOverride?: InsertPoint
) {
let blockParent: ContentModelBlockGroup | undefined;
let blockIndex = -1;
let deleteResult: DeleteSelectionResult;
let insertPoint: InsertPoint | null;

if (position == 'begin' || position == 'end') {
blockParent = model;
Expand All @@ -40,12 +41,8 @@ export function insertEntityModel(
if (!isBlock) {
Object.assign(entityModel.format, model.format);
}
} else if ((deleteResult = deleteSelection(model, [], context)).insertPoint) {
const { marker, paragraph, path } = deleteResult.insertPoint;

if (deleteResult.deleteResult == 'range') {
normalizeContentModel(model);
}
} else if ((insertPoint = getInsertPoint(model, insertPointOverride, context))) {
const { marker, paragraph, path } = insertPoint;

if (!isBlock) {
const index = paragraph.segments.indexOf(marker);
Expand Down Expand Up @@ -111,3 +108,22 @@ export function insertEntityModel(
}
}
}

function getInsertPoint(
model: ContentModelDocument,
insertPointOverride?: InsertPoint,
context?: FormatContentModelContext
): InsertPoint | null {
if (insertPointOverride) {
return insertPointOverride;
} else {
const deleteResult = deleteSelection(model, [], context);
const insertPoint = deleteResult.insertPoint;

if (deleteResult.deleteResult == 'range') {
normalizeContentModel(model);
}

return insertPoint;
}
}
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { formatInsertPointWithContentModel } from '../utils/formatInsertPointWithContentModel';
import { insertEntityModel } from '../../modelApi/entity/insertEntityModel';
import {
ChangeSource,
Expand All @@ -7,11 +8,15 @@ import {
} from 'roosterjs-content-model-dom';
import type {
ContentModelEntity,
DOMSelection,
InsertEntityPosition,
InsertEntityOptions,
IEditor,
EntityState,
DOMInsertPoint,
FormatContentModelOptions,
ContentModelDocument,
FormatContentModelContext,
InsertPoint,
} from 'roosterjs-content-model-types';

const BlockEntityTag = 'div';
Expand All @@ -32,7 +37,7 @@ export function insertEntity(
editor: IEditor,
type: string,
isBlock: boolean,
position: 'focus' | 'begin' | 'end' | DOMSelection,
position: 'focus' | 'begin' | 'end' | DOMInsertPoint,
options?: InsertEntityOptions
): ContentModelEntity | null;

Expand All @@ -51,15 +56,15 @@ export function insertEntity(
editor: IEditor,
type: string,
isBlock: true,
position: InsertEntityPosition | DOMSelection,
position: InsertEntityPosition | DOMInsertPoint,
options?: InsertEntityOptions
): ContentModelEntity | null;

export function insertEntity(
editor: IEditor,
type: string,
isBlock: boolean,
position?: InsertEntityPosition | DOMSelection,
position?: InsertEntityPosition | DOMInsertPoint,
options?: InsertEntityOptions
): ContentModelEntity | null {
const { contentNode, focusAfterEntity, wrapperDisplay, skipUndoSnapshot, initialEntityState } =
Expand All @@ -85,36 +90,45 @@ export function insertEntity(
editor.takeSnapshot();
}

editor.formatContentModel(
(model, context) => {
insertEntityModel(
model,
entityModel,
typeof position == 'string' ? position : 'focus',
isBlock,
focusAfterEntity,
context
);

normalizeContentModel(model);

context.skipUndoSnapshot = true;
context.newEntities.push(entityModel);

return true;
},
{
selectionOverride: typeof position === 'object' ? position : undefined,
changeSource: ChangeSource.InsertEntity,
getChangeData: () => ({
wrapper,
type,
id: '',
isReadonly: true,
}),
apiName: 'insertEntity',
}
);
const formatOptions: FormatContentModelOptions = {
changeSource: ChangeSource.InsertEntity,
getChangeData: () => ({
wrapper,
type,
id: '',
isReadonly: true,
}),
apiName: 'insertEntity',
};

const callback = (
model: ContentModelDocument,
context: FormatContentModelContext,
insertPoint?: InsertPoint
) => {
insertEntityModel(
model,
entityModel,
typeof position == 'string' ? position : 'focus',
isBlock,
focusAfterEntity,
context,
insertPoint
);

normalizeContentModel(model);

context.skipUndoSnapshot = true;
context.newEntities.push(entityModel);

return true;
};

if (typeof position == 'object') {
formatInsertPointWithContentModel(editor, position, callback, formatOptions);
} else {
editor.formatContentModel(callback, formatOptions);
}

if (!skipUndoSnapshot) {
let entityState: EntityState | undefined;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { reducedModelChildProcessor } from '../../modelApi/common/reducedModelChildProcessor';
import { retrieveModelFormatState } from 'roosterjs-content-model-dom';
import type { IEditor, ContentModelFormatState } from 'roosterjs-content-model-types';

Expand All @@ -7,15 +8,26 @@ import type { IEditor, ContentModelFormatState } from 'roosterjs-content-model-t
*/
export function getFormatState(editor: IEditor): ContentModelFormatState {
const pendingFormat = editor.getPendingFormat();
const model = editor.getContentModelCopy('reduced');
const manager = editor.getSnapshotsManager();
const result: ContentModelFormatState = {
canUndo: manager.hasNewContent || manager.canMove(-1),
canRedo: manager.canMove(1),
isDarkMode: editor.isDarkMode(),
};

retrieveModelFormatState(model, pendingFormat, result);
editor.formatContentModel(
model => {
retrieveModelFormatState(model, pendingFormat, result);

return false;
},
undefined /*options*/,
{
processorOverride: {
child: reducedModelChildProcessor,
},
}
);

return result;
}
Loading

0 comments on commit cc8c4e1

Please sign in to comment.