Skip to content

Commit

Permalink
Add cursor points at head and tail of cards
Browse files Browse the repository at this point in the history
* Use zero-width non-joiner characters to ensure
  addressable text nodes at the start and end of a card
* Handle arrow keys when on card cursor nodes
* Keep selections working using arrows through a card
* Delete selections including cards and before/after cards
* Disallow typing or pasting on cards
* Newline insertion before and after card sections

Fixes #182
  • Loading branch information
mixonic committed Nov 9, 2015
1 parent 061ca48 commit ac4fac8
Show file tree
Hide file tree
Showing 36 changed files with 1,603 additions and 281 deletions.
4 changes: 3 additions & 1 deletion demo/app/mobiledocs/simple-card.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@ export var simpleCard = {
sections: [
[],
[
[10, 'simple-card']
[1, 'p', [[[], 0, 'before']]],
[10, 'simple-card'],
[1, 'p', [[[], 0, 'after']]]
]
]
};
1 change: 1 addition & 0 deletions src/css/application.less
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
@import 'editor';
@import 'cards';
@import 'tooltip';
3 changes: 3 additions & 0 deletions src/css/cards.less
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
.ck-card {
display: inline-block;
}
87 changes: 63 additions & 24 deletions src/js/editor/editor.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import MobiledocRenderer from '../renderers/mobiledoc';

import { mergeWithOptions } from 'content-kit-utils';
import { clearChildNodes, addClassName } from '../utils/dom-utils';
import { forEach, filter } from '../utils/array-utils';
import { forEach, filter, contains } from '../utils/array-utils';
import { setData } from '../utils/element-utils';
import mixin from '../utils/mixin';
import EventListenerMixin from '../utils/event-listener';
Expand All @@ -35,9 +35,13 @@ import {
parsePostFromPaste,
setClipboardCopyData
} from '../utils/paste-utils';
import { DIRECTION } from 'content-kit-editor/utils/key';

export const EDITOR_ELEMENT_CLASS_NAME = 'ck-editor';

const ELEMENT_EVENTS = ['keydown', 'keyup', 'input', 'cut', 'copy', 'paste'];
const DOCUMENT_EVENTS= ['mouseup'];

const defaults = {
placeholder: 'Write here...',
spellcheck: true,
Expand Down Expand Up @@ -220,8 +224,8 @@ class Editor {
const range = this.cursor.offsets;

if (this.cursor.hasSelection()) {
this.run(postEditor => postEditor.deleteRange(range));
this.cursor.moveToPosition(range.head);
let nextPosition = this.run(postEditor => postEditor.deleteRange(range));
this.cursor.moveToPosition(nextPosition);
} else if (event) {
const key = Key.fromEvent(event);
const nextPosition = this.run(postEditor => {
Expand All @@ -239,9 +243,9 @@ class Editor {
const range = this.cursor.offsets;
const cursorSection = this.run(postEditor => {
if (!range.isCollapsed) {
postEditor.deleteRange(range);
if (range.head.section.isBlank) {
return range.head.section;
let nextPosition = postEditor.deleteRange(range);
if (nextPosition.section.isBlank) {
return nextPosition.section;
}
}
return postEditor.splitSection(range.head)[1];
Expand Down Expand Up @@ -505,24 +509,27 @@ class Editor {
}

_setupListeners() {
const elementEvents = ['keydown', 'keyup', 'input', 'cut', 'copy', 'paste'];
const documentEvents = ['mouseup'];

elementEvents.forEach(eventName => {
ELEMENT_EVENTS.forEach(eventName => {
this.addEventListener(this.element, eventName,
(...args) => this.handleEvent(eventName, ...args)
);
});

documentEvents.forEach(eventName => {
DOCUMENT_EVENTS.forEach(eventName => {
this.addEventListener(document, eventName,
(...args) => this.handleEvent(eventName, ...args)
);
});
}

handleEvent(eventName, ...args) {
if (this.cursor.isInCard()) { return; }
if (contains(ELEMENT_EVENTS, eventName)) {
let [{target: element}] = args;
if (!this.cursor.isAddressable(element)) {
// abort handling this event
return true;
}
}

const methodName = `handle${capitalize(eventName)}`;
if (!this[methodName]) { throw new Error(`No handler for ${eventName}`); }
Expand All @@ -531,7 +538,7 @@ class Editor {

handleMouseup() {
// mouseup does not correctly report a selection until the next tick
setTimeout(() => this._reportSelectionState());
setTimeout(() => this._reportSelectionState(), 0);
}

handleKeyup() {
Expand Down Expand Up @@ -571,17 +578,44 @@ class Editor {

const key = Key.fromEvent(event);

if (key.isDelete()) {
this.handleDeletion(event);
event.preventDefault();
} else if (key.isEnter()) {
this.handleNewline(event);
} else if (key.isPrintable()) {
if (this.cursor.hasSelection()) {
const range = this.cursor.offsets;
this.run(postEditor => postEditor.deleteRange(range));
this.cursor.moveToPosition(range.head);
}
let range, nextPosition;

switch(true) {
case key.isHorizontalArrow():
range = this.cursor.offsets;
let position = range.tail;
if (range.direction === DIRECTION.BACKWARD) {
position = range.head;
}
if (position.section.isCardSection) {
nextPosition = position.move(key.direction);
if (nextPosition) {
if (key.isShift()) {
let newRange = range.moveFocusedPosition(key.direction);
this.cursor.selectRange(newRange);
} else {
this.cursor.moveToPosition(nextPosition);
}
event.preventDefault();
}
}
break;
case key.isDelete():
this.handleDeletion(event);
event.preventDefault();
break;
case key.isEnter():
this.handleNewline(event);
break;
case key.isPrintable():
let range = this.cursor.offsets;
if (this.cursor.hasSelection()) {
let nextPosition = this.run(postEditor => postEditor.deleteRange(range));
this.cursor.moveToPosition(nextPosition);
} else if (range.head.section.isCardSection) {
event.preventDefault();
}
break;
}

this.handleExpansion(event);
Expand Down Expand Up @@ -630,6 +664,11 @@ class Editor {
event.preventDefault();

const { head: position } = this.cursor.offsets;

if (position.section.isCardSection) {
return;
}

if (this.cursor.hasSelection()) {
this.handleDeletion();
}
Expand Down
4 changes: 2 additions & 2 deletions src/js/editor/key-commands.js
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,8 @@ export const DEFAULT_KEY_COMMANDS = [{
if (!editor.cursor.hasSelection()) {
range.tail = new Position(range.head.section, range.head.section.length);
}
editor.run(postEditor => postEditor.deleteRange(range));
editor.cursor.moveToPosition(range.head);
let nextPosition = editor.run(postEditor => postEditor.deleteRange(range));
editor.cursor.moveToPosition(nextPosition);
}
}, {
modifier: MODIFIERS.META,
Expand Down
Loading

0 comments on commit ac4fac8

Please sign in to comment.