Skip to content

Commit

Permalink
Merge branch 'master' into u/acampostams/port-table-edit-plugin
Browse files Browse the repository at this point in the history
  • Loading branch information
Andres-CT98 authored Feb 29, 2024
2 parents 4078d26 + 78b498a commit 3860805
Show file tree
Hide file tree
Showing 21 changed files with 467 additions and 111 deletions.
Original file line number Diff line number Diff line change
@@ -1,12 +1,17 @@
import { ChangeSource } from 'roosterjs-content-model-core';
import { createEntity, normalizeContentModel } from 'roosterjs-content-model-dom';
import { insertEntityModel } from '../../modelApi/entity/insertEntityModel';
import {
createEntity,
normalizeContentModel,
parseEntityFormat,
} from 'roosterjs-content-model-dom';
import type {
ContentModelEntity,
DOMSelection,
InsertEntityPosition,
InsertEntityOptions,
IEditor,
EntityState,
} from 'roosterjs-content-model-types';

const BlockEntityTag = 'div';
Expand Down Expand Up @@ -57,7 +62,8 @@ export default function insertEntity(
position?: InsertEntityPosition | DOMSelection,
options?: InsertEntityOptions
): ContentModelEntity | null {
const { contentNode, focusAfterEntity, wrapperDisplay, skipUndoSnapshot } = options || {};
const { contentNode, focusAfterEntity, wrapperDisplay, skipUndoSnapshot, initialEntityState } =
options || {};
const document = editor.getDocument();
const wrapper = document.createElement(isBlock ? BlockEntityTag : InlineEntityTag);
const display = wrapperDisplay ?? (isBlock ? undefined : 'inline-block');
Expand All @@ -75,6 +81,10 @@ export default function insertEntity(

const entityModel = createEntity(wrapper, true /* isReadonly */, undefined /*format*/, type);

if (!skipUndoSnapshot) {
editor.takeSnapshot();
}

editor.formatContentModel(
(model, context) => {
insertEntityModel(
Expand All @@ -88,7 +98,7 @@ export default function insertEntity(

normalizeContentModel(model);

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

return true;
Expand All @@ -106,5 +116,25 @@ export default function insertEntity(
}
);

if (!skipUndoSnapshot) {
let entityState: EntityState | undefined;

if (initialEntityState) {
const format = parseEntityFormat(wrapper);
const { id, entityType } = format;

entityState =
id && entityType
? {
id: id,
type: entityType,
state: initialEntityState,
}
: undefined;
}

editor.takeSnapshot(entityState);
}

return entityModel;
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ describe('insertEntity', () => {
let insertEntityModelSpy: jasmine.Spy;
let isDarkModeSpy: jasmine.Spy;
let normalizeContentModelSpy: jasmine.Spy;
let takeSnapshotSpy: jasmine.Spy;

const type = 'Entity';
const apiName = 'insertEntity';
Expand All @@ -39,6 +40,7 @@ describe('insertEntity', () => {
appendChildSpy = jasmine.createSpy('appendChildSpy');
insertEntityModelSpy = spyOn(insertEntityModel, 'insertEntityModel');
isDarkModeSpy = jasmine.createSpy('isDarkMode');
takeSnapshotSpy = jasmine.createSpy('takeSnapshot');

wrapper = {
style: {
Expand Down Expand Up @@ -68,6 +70,7 @@ describe('insertEntity', () => {
getDocument: getDocumentSpy,
isDarkMode: isDarkModeSpy,
formatContentModel: formatWithContentModelSpy,
takeSnapshot: takeSnapshotSpy,
} as any;

spyOn(entityUtils, 'addDelimiters').and.returnValue([]);
Expand All @@ -76,6 +79,9 @@ describe('insertEntity', () => {
it('insert inline entity to top', () => {
const entity = insertEntity(editor, type, false, 'begin');

expect(takeSnapshotSpy).toHaveBeenCalledTimes(2);
expect(takeSnapshotSpy).toHaveBeenCalledWith();
expect(takeSnapshotSpy).toHaveBeenCalledWith(undefined);
expect(createElementSpy).toHaveBeenCalledWith('span');
expect(setPropertySpy).toHaveBeenCalledWith('display', 'inline-block');
expect(appendChildSpy).not.toHaveBeenCalled();
Expand Down Expand Up @@ -120,6 +126,9 @@ describe('insertEntity', () => {
it('block inline entity to root', () => {
const entity = insertEntity(editor, type, true, 'root');

expect(takeSnapshotSpy).toHaveBeenCalledTimes(2);
expect(takeSnapshotSpy).toHaveBeenCalledWith();
expect(takeSnapshotSpy).toHaveBeenCalledWith(undefined);
expect(createElementSpy).toHaveBeenCalledWith('div');
expect(setPropertySpy).toHaveBeenCalledWith('display', 'inline-block');
expect(setPropertySpy).toHaveBeenCalledWith('width', '100%');
Expand Down Expand Up @@ -172,6 +181,7 @@ describe('insertEntity', () => {
wrapperDisplay: 'none',
});

expect(takeSnapshotSpy).toHaveBeenCalledTimes(0);
expect(createElementSpy).toHaveBeenCalledWith('div');
expect(setPropertySpy).toHaveBeenCalledWith('display', 'none');
expect(setPropertySpy).not.toHaveBeenCalledWith('display', 'inline-block');
Expand Down Expand Up @@ -221,6 +231,81 @@ describe('insertEntity', () => {

const entity = insertEntity(editor, type, false, 'begin');

expect(takeSnapshotSpy).toHaveBeenCalledTimes(2);
expect(takeSnapshotSpy).toHaveBeenCalledWith();
expect(takeSnapshotSpy).toHaveBeenCalledWith(undefined);
expect(createElementSpy).toHaveBeenCalledWith('span');
expect(setPropertySpy).toHaveBeenCalledWith('display', 'inline-block');
expect(appendChildSpy).not.toHaveBeenCalled();
expect(formatWithContentModelSpy.calls.argsFor(0)[1].apiName).toBe(apiName);
expect(formatWithContentModelSpy.calls.argsFor(0)[1].changeSource).toBe(
ChangeSource.InsertEntity
);
expect(insertEntityModelSpy).toHaveBeenCalledWith(
model,
{
segmentType: 'Entity',
blockType: 'Entity',
format: {},
entityFormat: {
id: undefined,
entityType: type,
isReadonly: true,
},
wrapper: wrapper,
},
'begin',
false,
undefined,
context
);
expect(triggerContentChangedEventSpy).not.toHaveBeenCalled();
expect(normalizeContentModelSpy).toHaveBeenCalled();

expect(context.newEntities).toEqual([
{
segmentType: 'Entity',
blockType: 'Entity',
format: {},
entityFormat: {
id: undefined,
entityType: 'Entity',
isReadonly: true,
},
wrapper,
},
]);

expect(entity).toEqual({
segmentType: 'Entity',
blockType: 'Entity',
format: {},
entityFormat: {
id: undefined,
entityType: type,
isReadonly: true,
},
wrapper: wrapper,
});
});

it('Insert with initial state', () => {
spyOn(entityUtils, 'parseEntityFormat').and.returnValue({
id: 'A',
entityType: 'B',
});

const entity = insertEntity(editor, type, false, 'begin', {
initialEntityState: 'test',
});

expect(takeSnapshotSpy).toHaveBeenCalledTimes(2);
expect(takeSnapshotSpy).toHaveBeenCalledWith();
expect(takeSnapshotSpy).toHaveBeenCalledWith({
id: 'A',
type: 'B',
state: 'test',
});
expect(createElementSpy).toHaveBeenCalledWith('span');
expect(setPropertySpy).toHaveBeenCalledWith('display', 'inline-block');
expect(appendChildSpy).not.toHaveBeenCalled();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,9 +42,7 @@ export const formatContentModel: FormatContentModel = (core, formatter, options)
if (shouldAddSnapshot) {
core.undo.isNested = true;

if (core.undo.snapshotsManager.hasNewContent || entityStates) {
core.api.addUndoSnapshot(core, !!canUndoByBackspace);
}
core.api.addUndoSnapshot(core, !!canUndoByBackspace, entityStates);
}

try {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,11 @@ import {
getAllEntityWrappers,
getObjectKeys,
isEntityElement,
parseEntityClassName,
parseEntityFormat,
} from 'roosterjs-content-model-dom';
import type {
ChangedEntity,
ContentChangedEvent,
ContentModelEntityFormat,
EntityOperation,
EntityPluginState,
IEditor,
Expand Down Expand Up @@ -209,16 +208,19 @@ class EntityPlugin implements PluginWithState<EntityPluginState> {
result.splice(index, 1);
} else {
// Entity is not in editor, which means it is deleted, use a temporary entity here to represent this entity
const tempEntity = createEntity(entry.element);
let isEntity = false;

entry.element.classList.forEach(name => {
isEntity = parseEntityClassName(name, tempEntity.entityFormat) || isEntity;
});
const format = parseEntityFormat(entry.element);

if (!format.isFakeEntity) {
const entity = createEntity(
entry.element,
format.isReadonly,
{},
format.entityType,
format.id
);

if (isEntity) {
result.push({
entity: tempEntity,
entity: entity,
operation: 'overwrite',
});
}
Expand All @@ -244,10 +246,7 @@ class EntityPlugin implements PluginWithState<EntityPluginState> {
rawEvent?: Event,
state?: string
) {
const format: ContentModelEntityFormat = {};
wrapper.classList.forEach(name => {
parseEntityClassName(name, format);
});
const format = parseEntityFormat(wrapper);

return format.id && format.entityType && !format.isFakeEntity
? editor.triggerEvent('entityOperation', {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import type {
EditorOptions,
TrustedHTMLHandler,
Rect,
EntityState,
} from 'roosterjs-content-model-types';

/**
Expand Down Expand Up @@ -174,11 +175,16 @@ export class Editor implements IEditor {

/**
* Add a single undo snapshot to undo stack
* @param entityState @optional State for entity if we want to add entity state for this snapshot
*/
takeSnapshot(): Snapshot | null {
takeSnapshot(entityState?: EntityState): Snapshot | null {
const core = this.getCore();

return core.api.addUndoSnapshot(core, false /*canUndoByBackspace*/);
return core.api.addUndoSnapshot(
core,
false /*canUndoByBackspace*/,
entityState ? [entityState] : undefined
);
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,8 +52,9 @@ class SnapshotsManagerImpl implements SnapshotsManager {
currentSnapshot.html == snapshot.html &&
!currentSnapshot.entityStates &&
!snapshot.entityStates;
const addSnapshot = !currentSnapshot || shouldAddSnapshot(currentSnapshot, snapshot);

if (this.snapshots.currentIndex < 0 || !currentSnapshot || !isSameSnapshot) {
if (this.snapshots.currentIndex < 0 || addSnapshot) {
this.clearRedo();
this.snapshots.snapshots.push(snapshot);
this.snapshots.currentIndex++;
Expand Down Expand Up @@ -129,3 +130,13 @@ class SnapshotsManagerImpl implements SnapshotsManager {
export function createSnapshotsManager(snapshots?: Snapshots): SnapshotsManager {
return new SnapshotsManagerImpl(snapshots);
}

function shouldAddSnapshot(currentSnapshot: Snapshot, snapshot: Snapshot) {
return (
currentSnapshot.html !== snapshot.html ||
(currentSnapshot.entityStates &&
snapshot.entityStates &&
currentSnapshot.entityStates !== snapshot.entityStates) ||
(!currentSnapshot.entityStates && snapshot.entityStates)
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -38,9 +38,6 @@ export function deleteBlock(
: undefined;

if (operation !== undefined) {
const wrapper = blockToDelete.wrapper;

wrapper.parentNode?.removeChild(wrapper);
replacement ? blocks.splice(index, 1, replacement) : blocks.splice(index, 1);
context?.deletedEntities.push({
entity: blockToDelete,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,9 +48,6 @@ export function deleteSegment(
? 'removeFromEnd'
: undefined;
if (operation !== undefined) {
const wrapper = segmentToDelete.wrapper;

wrapper.parentNode?.removeChild(wrapper);
segments.splice(index, 1);
context?.deletedEntities.push({
entity: segmentToDelete,
Expand Down
Loading

0 comments on commit 3860805

Please sign in to comment.