Skip to content

Commit

Permalink
Merge pull request #188 from Microsoft/mahuangh-fix-editor-typing
Browse files Browse the repository at this point in the history
fix ctrl+a -> typing issue
  • Loading branch information
Adjective-Object authored Dec 31, 2018
2 parents 61172be + c422f17 commit f838900
Showing 1 changed file with 34 additions and 7 deletions.
41 changes: 34 additions & 7 deletions packages/roosterjs-editor-core/lib/editor/CorePlugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,13 @@ import Editor from './Editor';
import EditorPlugin from './EditorPlugin';
import {
ChangeSource,
NodePosition,
PluginCompositionEvent,
PluginEvent,
PluginEventType,
PluginKeyboardEvent,
PluginMouseUpEvent,
PositionType,
NodePosition,
} from 'roosterjs-editor-types';
import {
Browser,
Expand Down Expand Up @@ -47,7 +48,7 @@ export default class CorePlugin implements EditorPlugin {
constructor(
private contentDiv: HTMLDivElement,
private disableRestoreSelectionOnFocus: boolean
) {}
) { }

/**
* Get a friendly name of this plugin
Expand Down Expand Up @@ -123,9 +124,10 @@ export default class CorePlugin implements EditorPlugin {
/**
* Ensure we are typing in an HTML Element inside editor, and apply default format if current block is empty
* @param node Current node
* @param event (optional) The keyboard event that we are ensuring is typing in an element.
* @returns A new position to select
*/
public ensureTypeInElement(position: NodePosition): NodePosition {
public ensureTypeInElement(position: NodePosition, event?: PluginKeyboardEvent): NodePosition {
position = position.normalize();
let block = getBlockElementAtNode(this.contentDiv, position.node);
let formatNode: HTMLElement;
Expand All @@ -148,7 +150,9 @@ export default class CorePlugin implements EditorPlugin {

// if the block is empty, apply default format
// Otherwise, leave it as it is as we don't want to change the style for existing data
formatNode = formatNode && isNodeEmpty(formatNode) ? formatNode : null;
// unless the block was just created by the keyboard event (e.g. ctrl+a & start typing)
const shouldSetNodeStyles = isNodeEmpty(formatNode) || (event && this.wasNodeJustCreatedByKeyboardEvent(event, formatNode));
formatNode = formatNode && shouldSetNodeStyles ? formatNode : null;
}

if (formatNode) {
Expand All @@ -158,6 +162,10 @@ export default class CorePlugin implements EditorPlugin {
return position;
}

private wasNodeJustCreatedByKeyboardEvent(event: PluginKeyboardEvent, formatNode: HTMLElement): boolean {
return event.rawEvent.target instanceof Node && event.rawEvent.target.contains(formatNode) && event.rawEvent.key === formatNode.innerText;
}

/**
* Handle events triggered from editor
* @param event PluginEvent object
Expand All @@ -176,7 +184,7 @@ export default class CorePlugin implements EditorPlugin {
this.onContentChanged(event.rawEvent);
break;
case PluginEventType.KeyPress:
this.onKeyPress();
this.onKeyPress(event);
break;
}
}
Expand Down Expand Up @@ -210,22 +218,41 @@ export default class CorePlugin implements EditorPlugin {
}
}

private onKeyPress(event: PluginKeyboardEvent) {
// If normalization was not possible before the keypress,
// check again after the keyboard event has been processed by browser native behaviour.
//
// This handles the case where the keyboard event that first inserts content happens when
// there is already content under the selection (e.g. Ctrl+a -> type new content).
//
// Only scheudle when the range is not collapsed to catch this edge case.
let range = this.editor.getSelectionRange();
if (!this.tryNormalizeTyping(event) && !range.collapsed) {
requestAnimationFrame(() => {
this.tryNormalizeTyping(event);
});
}
}

/**
* Check if user is typing right under the content div
* When typing goes directly under content div, many things can go wrong
* We fix it by wrapping it with a div and reposition cursor within the div
*/
private onKeyPress() {
private tryNormalizeTyping(event: PluginKeyboardEvent): boolean {
let range = this.editor.getSelectionRange();

if (
range &&
range.collapsed &&
findClosestElementAncestor(range.startContainer) == this.contentDiv
) {
let position = this.ensureTypeInElement(Position.getStart(range));
let position = this.ensureTypeInElement(Position.getStart(range), event);
this.editor.select(position);
return true;
}

return false;
}

private onMouseDown = () => {
Expand Down

0 comments on commit f838900

Please sign in to comment.