Skip to content

Commit

Permalink
Standalone editor: Remove dependencies to enums Part 2 (#2153)
Browse files Browse the repository at this point in the history
* Standalone editor: Remove dependencies to enums Part 2

* fix build
  • Loading branch information
JiuqingSong authored Oct 18, 2023
1 parent e55b4d6 commit 88bdecf
Show file tree
Hide file tree
Showing 41 changed files with 903 additions and 134 deletions.
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { areSameRangeEx } from '../../modelApi/selection/areSameRangeEx';
import { isCharacterValue } from '../../domUtils/eventUtils';
import { Keys, PluginEventType } from 'roosterjs-editor-types';
import { PluginEventType } from 'roosterjs-editor-types';
import type ContentModelContentChangedEvent from '../../publicTypes/event/ContentModelContentChangedEvent';
import type { ContentModelCachePluginState } from '../../publicTypes/pluginState/ContentModelCachePluginState';
import type { IContentModelEditor } from '../../publicTypes/IContentModelEditor';
Expand Down Expand Up @@ -167,7 +167,7 @@ export default class ContentModelCachePlugin
// 3. ENTER key is pressed. ENTER key will create new paragraph, so need to update cache to reflect this change
// TODO: Handle ENTER key to better reuse content model

if (rawEvent.which == Keys.ENTER) {
if (rawEvent.key == 'Enter') {
return true;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import paste from '../../publicApi/utils/paste';
import { addRangeToSelection, extractClipboardItems } from 'roosterjs-editor-dom';
import { ChangeSource, ColorTransformDirection, PluginEventType } from 'roosterjs-editor-types';
import { ChangeSource } from '../../publicTypes/event/ContentModelContentChangedEvent';
import { cloneModel } from '../../modelApi/common/cloneModel';
import { ColorTransformDirection, PluginEventType } from 'roosterjs-editor-types';
import { DeleteResult } from '../../modelApi/edit/utils/DeleteSelectionStep';
import { deleteSelection } from '../../modelApi/edit/deleteSelection';
import { formatWithContentModel } from '../../publicApi/utils/formatWithContentModel';
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import keyboardDelete from '../../publicApi/editing/keyboardDelete';
import { Keys, PluginEventType } from 'roosterjs-editor-types';
import { PluginEventType } from 'roosterjs-editor-types';
import type { IContentModelEditor } from '../../publicTypes/IContentModelEditor';
import type {
EditorPlugin,
Expand Down Expand Up @@ -62,13 +62,12 @@ export default class ContentModelEditPlugin implements EditorPlugin {

private handleKeyDownEvent(editor: IContentModelEditor, event: PluginKeyDownEvent) {
const rawEvent = event.rawEvent;
const which = rawEvent.which;

if (!rawEvent.defaultPrevented && !event.handledByEditFeature) {
// TODO: Consider use ContentEditFeature and need to hide other conflict features that are not based on Content Model
switch (which) {
case Keys.BACKSPACE:
case Keys.DELETE:
switch (rawEvent.key) {
case 'Backspace':
case 'Delete':
// Use our API to handle BACKSPACE/DELETE key.
// No need to clear cache here since if we rely on browser's behavior, there will be Input event and its handler will reconcile cache
keyboardDelete(editor, rawEvent);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,23 @@ import applyPendingFormat from '../../publicApi/format/applyPendingFormat';
import { canApplyPendingFormat, clearPendingFormat } from '../../modelApi/format/pendingFormat';
import { getObjectKeys } from 'roosterjs-content-model-dom';
import { isCharacterValue } from '../../domUtils/eventUtils';
import { Keys, PluginEventType } from 'roosterjs-editor-types';
import { PluginEventType } from 'roosterjs-editor-types';
import type { IContentModelEditor } from '../../publicTypes/IContentModelEditor';
import type { IEditor, PluginEvent, PluginWithState } from 'roosterjs-editor-types';
import type { ContentModelFormatPluginState } from '../../publicTypes/pluginState/ContentModelFormatPluginState';

// During IME input, KeyDown event will have "Process" as key
const ProcessKey = 'Process';
const CursorMovingKeys = new Set<string>([
'ArrowUp',
'ArrowDown',
'ArrowLeft',
'ArrowRight',
'Home',
'End',
'PageUp',
'PageDown',
]);

/**
* ContentModelFormat plugins helps editor to do formatting on top of content model.
Expand Down Expand Up @@ -92,7 +102,7 @@ export default class ContentModelFormatPlugin
break;

case PluginEventType.KeyDown:
if (event.rawEvent.which >= Keys.PAGEUP && event.rawEvent.which <= Keys.DOWN) {
if (CursorMovingKeys.has(event.rawEvent.key)) {
clearPendingFormat(this.editor);
} else if (
this.hasDefaultFormat &&
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,16 @@
import addParser from './utils/addParser';
import { BorderKeys } from 'roosterjs-content-model-dom';
import { chainSanitizerCallback, getPasteSource } from 'roosterjs-editor-dom';
import { chainSanitizerCallback } from 'roosterjs-editor-dom';
import { deprecatedBorderColorParser } from './utils/deprecatedColorParser';
import { KnownPasteSourceType, PasteType, PluginEventType } from 'roosterjs-editor-types';
import { getPasteSource } from './pasteSourceValidations/getPasteSource';
import { parseLink } from './utils/linkParser';
import { PastePropertyNames } from './pasteSourceValidations/constants';
import { PasteType as OldPasteType, PluginEventType } from 'roosterjs-editor-types';
import { processPastedContentFromExcel } from './Excel/processPastedContentFromExcel';
import { processPastedContentFromPowerPoint } from './PowerPoint/processPastedContentFromPowerPoint';
import { processPastedContentFromWordDesktop } from './WordDesktop/processPastedContentFromWordDesktop';
import { processPastedContentWacComponents } from './WacComponents/processPastedContentWacComponents';
import type { PasteType } from '../../../publicTypes/parameter/PasteType';
import type ContentModelBeforePasteEvent from '../../../publicTypes/event/ContentModelBeforePasteEvent';
import type {
BorderFormat,
Expand All @@ -23,7 +26,14 @@ import type {
PluginEvent,
} from 'roosterjs-editor-types';

const GOOGLE_SHEET_NODE_NAME = 'google-sheets-html-origin';
// Map old PasteType to new PasteType
// TODO: We can remove this once we have standalone editor
const PasteTypeMap: Record<OldPasteType, PasteType> = {
[OldPasteType.AsImage]: 'asImage',
[OldPasteType.AsPlainText]: 'asPlainText',
[OldPasteType.MergeFormat]: 'mergeFormat',
[OldPasteType.Normal]: 'normal',
};

/**
* Paste plugin, handles BeforePaste event and reformat some special content, including:
Expand Down Expand Up @@ -81,28 +91,34 @@ export default class ContentModelPastePlugin implements EditorPlugin {
}

const ev = event as ContentModelBeforePasteEvent;

if (!ev.domToModelOption) {
return;
}

const pasteSource = getPasteSource(ev, false);
const pasteType = PasteTypeMap[ev.pasteType];

switch (pasteSource) {
case KnownPasteSourceType.WordDesktop:
case 'wordDesktop':
processPastedContentFromWordDesktop(ev);
break;
case KnownPasteSourceType.WacComponents:
case 'wacComponents':
processPastedContentWacComponents(ev);
break;
case KnownPasteSourceType.ExcelOnline:
case KnownPasteSourceType.ExcelDesktop:
if (ev.pasteType === PasteType.Normal || ev.pasteType === PasteType.MergeFormat) {
case 'excelOnline':
case 'excelDesktop':
if (pasteType === 'normal' || pasteType === 'mergeFormat') {
// Handle HTML copied from Excel
processPastedContentFromExcel(ev, this.editor.getTrustedHTMLHandler());
}
break;
case KnownPasteSourceType.GoogleSheets:
ev.sanitizingOption.additionalTagReplacements[GOOGLE_SHEET_NODE_NAME] = '*';
case 'googleSheets':
ev.sanitizingOption.additionalTagReplacements[
PastePropertyNames.GOOGLE_SHEET_NODE_NAME
] = '*';
break;
case KnownPasteSourceType.PowerPointDesktop:
case 'powerPointDesktop':
processPastedContentFromPowerPoint(ev, this.editor.getTrustedHTMLHandler());
break;
}
Expand All @@ -113,7 +129,7 @@ export default class ContentModelPastePlugin implements EditorPlugin {
addParser(ev.domToModelOption, 'table', deprecatedBorderColorParser);
sanitizeBlockStyles(ev.sanitizingOption);

if (ev.pasteType === PasteType.MergeFormat) {
if (pasteType === 'mergeFormat') {
addParser(ev.domToModelOption, 'block', blockElementParser);
addParser(ev.domToModelOption, 'listLevel', blockElementParser);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
/**
* @internal
*/
export const enum PastePropertyNames {
/**
* Node attribute used to identify if the content is from Google Sheets.
*/
GOOGLE_SHEET_NODE_NAME = 'google-sheets-html-origin',

/**
* Name of the HTMLMeta Property that provides the Office App Source of the pasted content
*/
PROG_ID_NAME = 'ProgId',

/**
* Name of the HTMLMeta Property that identifies pated content as from Excel Desktop
*/
EXCEL_DESKTOP_ATTRIBUTE_NAME = 'xmlns:x',
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import type { GetSourceFunction } from './getPasteSource';

const WORD_ONLINE_TABLE_TEMP_ELEMENT_CLASSES = [
'TableInsertRowGapBlank',
'TableColumnResizeHandle',
'TableCellTopBorderHandle',
'TableCellLeftBorderHandle',
'TableHoverColumnHandle',
'TableHoverRowHandle',
];

const WAC_IDENTIFY_SELECTOR =
'ul[class^="BulletListStyle"]>.OutlineElement,ol[class^="NumberListStyle"]>.OutlineElement,span.WACImageContainer,' +
WORD_ONLINE_TABLE_TEMP_ELEMENT_CLASSES.map(c => `table div[class^="${c}"]`).join(',');

/**
* @internal
* Check whether the fragment provided contain Wac Elements
* @param props Properties related to the PasteEvent
* @returns
*/
export const documentContainWacElements: GetSourceFunction = props => {
const { fragment } = props;
return !!fragment.querySelector(WAC_IDENTIFY_SELECTOR);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
import { documentContainWacElements } from './documentContainWacElements';
import { isExcelDesktopDocument } from './isExcelDesktopDocument';
import { isExcelOnlineDocument } from './isExcelOnlineDocument';
import { isGoogleSheetDocument } from './isGoogleSheetDocument';
import { isPowerPointDesktopDocument } from './isPowerPointDesktopDocument';
import { isWordDesktopDocument } from './isWordDesktopDocument';
import { shouldConvertToSingleImage } from './shouldConvertToSingleImage';
import type { BeforePasteEvent, ClipboardData } from 'roosterjs-editor-types';

/**
* @internal
*/
export type GetSourceInputParams = {
htmlAttributes: Record<string, string>;
fragment: DocumentFragment;
shouldConvertSingleImage: boolean;
clipboardData: ClipboardData;
};

/**
* @internal
* Represent the types of sources to handle in the Paste Plugin
*/
export type KnownPasteSourceType =
| 'wordDesktop'
| 'excelDesktop'
| 'excelOnline'
| 'powerPointDesktop'
| 'googleSheets'
| 'wacComponents'
| 'default'
| 'singleImage';

/**
* @internal
*/
export type GetSourceFunction = (props: GetSourceInputParams) => boolean;

const getSourceFunctions = new Map<KnownPasteSourceType, GetSourceFunction>([
['wordDesktop', isWordDesktopDocument],
['excelDesktop', isExcelDesktopDocument],
['excelOnline', isExcelOnlineDocument],
['powerPointDesktop', isPowerPointDesktopDocument],
['wacComponents', documentContainWacElements],
['googleSheets', isGoogleSheetDocument],
['singleImage', shouldConvertToSingleImage],
]);

/**
* @internal
* This function tries to get the source of the Pasted content
* @param event the before paste event
* @param shouldConvertSingleImage Whether convert single image is enabled.
* @returns The Type of pasted content, if no type found will return {KnownSourceType.Default}
*/
export function getPasteSource(
event: BeforePasteEvent,
shouldConvertSingleImage: boolean
): KnownPasteSourceType {
const { htmlAttributes, clipboardData, fragment } = event;

let result: KnownPasteSourceType | null = null;
const param: GetSourceInputParams = {
htmlAttributes,
fragment,
shouldConvertSingleImage,
clipboardData,
};

getSourceFunctions.forEach((func, key) => {
if (!result && func(param)) {
result = key;
}
});

return result ?? 'default';
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { PastePropertyNames } from './constants';
import type { GetSourceFunction } from './getPasteSource';

const EXCEL_ATTRIBUTE_VALUE = 'urn:schemas-microsoft-com:office:excel';

/**
* @internal
* Checks whether the Array provided contains strings that identify Excel Desktop documents
* @param props Properties related to the PasteEvent
* @returns
*/
export const isExcelDesktopDocument: GetSourceFunction = props => {
const { htmlAttributes } = props;
// The presence of this attribute confirms its origin from Excel Desktop
return htmlAttributes[PastePropertyNames.EXCEL_DESKTOP_ATTRIBUTE_NAME] == EXCEL_ATTRIBUTE_VALUE;
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { PastePropertyNames } from './constants';
import type { GetSourceFunction } from './getPasteSource';

// Excel Desktop also has this attribute
const EXCEL_ONLINE_ATTRIBUTE_VALUE = 'Excel.Sheet';

/**
* @internal
* Checks whether the Array provided contains strings that identify Excel Online documents
* @param props Properties related to the PasteEvent
* @returns
*/
export const isExcelOnlineDocument: GetSourceFunction = props => {
const { htmlAttributes } = props;
// The presence of Excel.Sheet confirms its origin from Excel, the absence of EXCEL_DESKTOP_ATTRIBUTE_NAME confirms it is from the Online version
return (
htmlAttributes[PastePropertyNames.PROG_ID_NAME] == EXCEL_ONLINE_ATTRIBUTE_VALUE &&
htmlAttributes[PastePropertyNames.EXCEL_DESKTOP_ATTRIBUTE_NAME] == undefined
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { PastePropertyNames } from './constants';
import type { GetSourceFunction } from './getPasteSource';

/**
* @internal
* Checks whether the fragment provided contain elements from Google sheets
* @param props Properties related to the PasteEvent
* @returns
*/
export const isGoogleSheetDocument: GetSourceFunction = props => {
const { fragment } = props;
return !!fragment.querySelector(PastePropertyNames.GOOGLE_SHEET_NODE_NAME);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { PastePropertyNames } from './constants';
import type { GetSourceFunction } from './getPasteSource';

const POWERPOINT_ATTRIBUTE_VALUE = 'PowerPoint.Slide';

/**
* @internal
* Checks whether the Array provided contains strings that identify Power Point Desktop documents
* @param props Properties related to the PasteEvent
* @returns
*/
export const isPowerPointDesktopDocument: GetSourceFunction = props => {
return props.htmlAttributes[PastePropertyNames.PROG_ID_NAME] == POWERPOINT_ATTRIBUTE_VALUE;
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { PastePropertyNames } from './constants';
import type { GetSourceFunction } from './getPasteSource';

const WORD_ATTRIBUTE_NAME = 'xmlns:w';
const WORD_ATTRIBUTE_VALUE = 'urn:schemas-microsoft-com:office:word';
const WORD_PROG_ID = 'Word.Document';

/**
* @internal
* Checks whether the Array provided contains strings that identify Word Desktop documents
* @param props Properties related to the PasteEvent
* @returns
*/
export const isWordDesktopDocument: GetSourceFunction = props => {
const { htmlAttributes } = props;
return (
htmlAttributes[WORD_ATTRIBUTE_NAME] == WORD_ATTRIBUTE_VALUE ||
htmlAttributes[PastePropertyNames.PROG_ID_NAME] == WORD_PROG_ID
);
};
Loading

0 comments on commit 88bdecf

Please sign in to comment.