1
0
mirror of https://github.com/ianstormtaylor/slate.git synced 2025-08-30 18:39:51 +02:00

fix firefox support

This commit is contained in:
Ian Storm Taylor
2016-07-07 19:37:34 -07:00
parent db1151bd15
commit 9b3bcd837d
13 changed files with 428 additions and 238 deletions

View File

@@ -1,7 +1,8 @@
import { Editor, Mark, Raw } from '../..' import { Editor, Mark, Raw, Utils } from '../..'
import React from 'react' import React from 'react'
import initialState from './state.json' import initialState from './state.json'
import keycode from 'keycode'
/** /**
* Node renderers. * Node renderers.
@@ -55,42 +56,6 @@ class RichText extends React.Component {
state: Raw.deserialize(initialState) state: Raw.deserialize(initialState)
}; };
hasMark = (type) => {
const { state } = this.state
return state.marks.some(mark => mark.type == type)
}
hasBlock = (type) => {
const { state } = this.state
return state.blocks.some(node => node.type == type)
}
onClickMark = (e, type) => {
e.preventDefault()
const isActive = this.hasMark(type)
let { state } = this.state
state = state
.transform()
[isActive ? 'unmark' : 'mark'](type)
.apply()
this.setState({ state })
}
onClickBlock = (e, type) => {
e.preventDefault()
const isActive = this.hasBlock(type)
let { state } = this.state
state = state
.transform()
.setBlock(isActive ? 'paragraph' : type)
.apply()
this.setState({ state })
}
render = () => { render = () => {
return ( return (
<div> <div>
@@ -146,6 +111,7 @@ class RichText extends React.Component {
renderNode={this.renderNode} renderNode={this.renderNode}
renderMark={this.renderMark} renderMark={this.renderMark}
onChange={this.onChange} onChange={this.onChange}
onKeyDown={this.onKeyDown}
/> />
</div> </div>
) )
@@ -159,6 +125,16 @@ class RichText extends React.Component {
return MARKS[mark.type] return MARKS[mark.type]
} }
hasMark = (type) => {
const { state } = this.state
return state.marks.some(mark => mark.type == type)
}
hasBlock = (type) => {
const { state } = this.state
return state.blocks.some(node => node.type == type)
}
onChange = (state) => { onChange = (state) => {
console.groupCollapsed('Change!') console.groupCollapsed('Change!')
console.log('Document:', state.document.toJS()) console.log('Document:', state.document.toJS())
@@ -168,6 +144,63 @@ class RichText extends React.Component {
this.setState({ state }) this.setState({ state })
} }
onKeyDown = (e, state) => {
if (!Utils.Key.isCommand(e)) return
const key = keycode(e.which)
let mark
switch (key) {
case 'b':
mark = 'bold'
break
case 'i':
mark = 'italic'
break
case 'u':
mark = 'underlined'
break
case '`':
mark = 'code'
break
default:
return
}
state = state
.transform()
[this.hasMark(mark) ? 'unmark' : 'mark'](mark)
.apply()
e.preventDefault()
return state
}
onClickMark = (e, type) => {
e.preventDefault()
const isActive = this.hasMark(type)
let { state } = this.state
state = state
.transform()
[isActive ? 'unmark' : 'mark'](type)
.apply()
this.setState({ state })
}
onClickBlock = (e, type) => {
e.preventDefault()
const isActive = this.hasBlock(type)
let { state } = this.state
state = state
.transform()
.setBlock(isActive ? 'paragraph' : type)
.apply()
this.setState({ state })
}
} }
/** /**

View File

@@ -65,7 +65,7 @@
} }
] ]
},{ },{
"text": ", or add a semanticlly rendered block quote in the middle of the page, like this:" "text": ", or add a semantically rendered block quote in the middle of the page, like this:"
} }
] ]
} }

View File

@@ -1,11 +1,18 @@
import Key from '../utils/key'
import OffsetKey from '../utils/offset-key' import OffsetKey from '../utils/offset-key'
import Raw from '../serializers/raw'
import React from 'react' import React from 'react'
import Text from './text' import Text from './text'
import Void from './void' import Void from './void'
import keycode from 'keycode' import keycode from 'keycode'
import { Raw } from '..' import { IS_FIREFOX } from '../utils/environment'
import { isCommand, isWindowsCommand } from '../utils/event'
/**
* Noop.
*/
function noop() {}
/** /**
* Content. * Content.
@@ -63,20 +70,10 @@ class Content extends React.Component {
* @param {Object} props * @param {Object} props
*/ */
componentWillMount = () => {
this.tmp.isRendering = true
}
componentWillUpdate = (props, state) => { componentWillUpdate = (props, state) => {
this.tmp.isRendering = true this.tmp.isRendering = true
} }
componentDidMount = () => {
setTimeout(() => {
this.tmp.isRendering = false
})
}
componentDidUpdate = (props, state) => { componentDidUpdate = (props, state) => {
setTimeout(() => { setTimeout(() => {
this.tmp.isRendering = false this.tmp.isRendering = false
@@ -214,10 +211,10 @@ class Content extends React.Component {
(key == 'enter') || (key == 'enter') ||
(key == 'backspace') || (key == 'backspace') ||
(key == 'delete') || (key == 'delete') ||
(key == 'b' && isCommand(e)) || (key == 'b' && Key.isCommand(e)) ||
(key == 'i' && isCommand(e)) || (key == 'i' && Key.isCommand(e)) ||
(key == 'y' && isWindowsCommand(e)) || (key == 'y' && Key.isWindowsCommand(e)) ||
(key == 'z' && isCommand(e)) (key == 'z' && Key.isCommand(e))
) { ) {
e.preventDefault() e.preventDefault()
} }
@@ -234,7 +231,7 @@ class Content extends React.Component {
onPaste = (e) => { onPaste = (e) => {
e.preventDefault() e.preventDefault()
const data = e.clipboardData const data = e.clipboardData
const { types } = data const types = Array.from(data.types)
const paste = {} const paste = {}
// Handle files. // Handle files.
@@ -308,13 +305,13 @@ class Content extends React.Component {
state = state state = state
.transform() .transform()
.focus()
.moveTo({ .moveTo({
anchorKey: anchor.key, anchorKey: anchor.key,
anchorOffset: anchor.offset, anchorOffset: anchor.offset,
focusKey: focus.key, focusKey: focus.key,
focusOffset: focus.offset focusOffset: focus.offset
}) })
.focus()
.apply({ isNative: true }) .apply({ isNative: true })
this.onChange(state) this.onChange(state)
@@ -350,6 +347,7 @@ class Content extends React.Component {
onKeyDown={this.onKeyDown} onKeyDown={this.onKeyDown}
onPaste={this.onPaste} onPaste={this.onPaste}
onSelect={this.onSelect} onSelect={this.onSelect}
onKeyUp={noop}
> >
{children} {children}
</div> </div>

View File

@@ -27,17 +27,22 @@ class Leaf extends React.Component {
* Should component update? * Should component update?
* *
* @param {Object} props * @param {Object} props
* @param {Object} state
* @return {Boolean} shouldUpdate * @return {Boolean} shouldUpdate
*/ */
shouldComponentUpdate(props, state) { shouldComponentUpdate(props) {
return ( const { start, end, node, state } = props
const { selection } = state
const should = (
selection.hasEdgeBetween(node, start, end) ||
props.start != this.props.start || props.start != this.props.start ||
props.end != this.props.end || props.end != this.props.end ||
props.text != this.props.text || props.text != this.props.text ||
props.marks != this.props.marks props.marks != this.props.marks
) )
return should
} }
componentDidMount() { componentDidMount() {
@@ -55,19 +60,19 @@ class Leaf extends React.Component {
// If the selection is not focused we have nothing to do. // If the selection is not focused we have nothing to do.
if (!selection.isFocused) return if (!selection.isFocused) return
const { anchorKey, anchorOffset, focusKey, focusOffset } = selection const { anchorOffset, focusOffset } = selection
const { node, start, end } = this.props const { node, start, end } = this.props
const { key } = node
// If neither matches, the selection doesn't start or end here, so exit. // If neither matches, the selection doesn't start or end here, so exit.
const hasStart = key == anchorKey && start <= anchorOffset && anchorOffset <= end const hasStart = selection.hasStartBetween(node, start, end)
const hasEnd = key == focusKey && start <= focusOffset && focusOffset <= end const hasEnd = selection.hasEndBetween(node, start, end)
if (!hasStart && !hasEnd) return if (!hasStart && !hasEnd) return
// We have a selection to render, so prepare a few things... // We have a selection to render, so prepare a few things...
const native = window.getSelection() const native = window.getSelection()
const el = ReactDOM.findDOMNode(this).firstChild const el = ReactDOM.findDOMNode(this).firstChild
// If both the start and end are here, set the selection all at once. // If both the start and end are here, set the selection all at once.
if (hasStart && hasEnd) { if (hasStart && hasEnd) {
native.removeAllRanges() native.removeAllRanges()

View File

@@ -1,6 +1,5 @@
import Leaf from './leaf' import Leaf from './leaf'
import OffsetKey from '../utils/offset-key'
import React from 'react' import React from 'react'
import groupByMarks from '../utils/group-by-marks' import groupByMarks from '../utils/group-by-marks'
import { List } from 'immutable' import { List } from 'immutable'
@@ -32,6 +31,7 @@ class Text extends React.Component {
shouldComponentUpdate(props, state) { shouldComponentUpdate(props, state) {
return ( return (
props.state.selection.hasEdgeIn(props.node) ||
props.node.decorations != this.props.node.decorations || props.node.decorations != this.props.node.decorations ||
props.node.characters != this.props.node.characters props.node.characters != this.props.node.characters
) )
@@ -67,7 +67,7 @@ class Text extends React.Component {
const offset = previous.size const offset = previous.size
? previous.map(r => r.text).join('').length ? previous.map(r => r.text).join('').length
: 0 : 0
return this.renderLeaf(range, offset) return this.renderLeaf(range, i, offset)
}) })
} }
@@ -75,25 +75,21 @@ class Text extends React.Component {
* Render a single leaf node given a `range` and `offset`. * Render a single leaf node given a `range` and `offset`.
* *
* @param {Object} range * @param {Object} range
* @param {Number} index
* @param {Number} offset * @param {Number} offset
* @return {Element} leaf * @return {Element} leaf
*/ */
renderLeaf(range, offset) { renderLeaf(range, index, offset) {
const { node, renderMark, state } = this.props const { node, renderMark, state } = this.props
const text = range.text const text = range.text
const marks = range.marks const marks = range.marks
const start = offset const start = offset
const end = offset + text.length const end = offset + text.length
const offsetKey = OffsetKey.stringify({
key: node.key,
start,
end
})
return ( return (
<Leaf <Leaf
key={offsetKey} key={`${node.key}-${index}`}
state={state} state={state}
node={node} node={node}
start={start} start={start}

View File

@@ -3,25 +3,69 @@
* Components. * Components.
*/ */
export { default as Editor } from './components/editor' import Editor from './components/editor'
/** /**
* Models. * Models.
*/ */
export { default as Block } from './models/block' import Block from './models/block'
export { default as Character } from './models/character' import Character from './models/character'
export { default as Data } from './models/data' import Data from './models/data'
export { default as Document } from './models/document' import Document from './models/document'
export { default as Inline } from './models/inline' import Inline from './models/inline'
export { default as Mark } from './models/mark' import Mark from './models/mark'
export { default as Selection } from './models/selection' import Selection from './models/selection'
export { default as State } from './models/state' import State from './models/state'
export { default as Text } from './models/text' import Text from './models/text'
/** /**
* Serializers. * Serializers.
*/ */
export { default as Html } from './serializers/html' import Html from './serializers/html'
export { default as Raw } from './serializers/raw' import Raw from './serializers/raw'
/**
* Utils.
*/
import Key from './utils/key'
const Utils = { Key }
/**
* Export.
*/
export {
Block,
Character,
Data,
Document,
Editor,
Html,
Inline,
Mark,
Raw,
Selection,
State,
Text,
Utils
}
export default {
Block,
Character,
Data,
Document,
Editor,
Html,
Inline,
Mark,
Raw,
Selection,
State,
Text,
Utils
}

View File

@@ -2,7 +2,26 @@
import { Record } from 'immutable' import { Record } from 'immutable'
/** /**
* Record. * Start-and-end convenience methods to auto-generate.
*/
const START_END_METHODS = [
'moveTo%'
]
/**
* Start-end-and-edge convenience methods to auto-generate.
*/
const EDGE_METHODS = [
'has%AtStartOf',
'has%AtEndOf',
'has%Between',
'has%In'
]
/**
* Default properties.
*/ */
const DEFAULTS = { const DEFAULTS = {
@@ -115,6 +134,114 @@ class Selection extends new Record(DEFAULTS) {
: this.focusOffset : this.focusOffset
} }
/**
* Check whether anchor point of the selection is at the start of a `node`.
*
* @param {Node} node
* @return {Boolean}
*/
hasAnchorAtStartOf(node) {
const first = node.kind == 'text' ? node : node.getTextNodes().first()
return this.anchorKey == first.key && this.anchorOffset == 0
}
/**
* Check whether anchor point of the selection is at the end of a `node`.
*
* @param {Node} node
* @return {Boolean}
*/
hasAnchorAtEndOf(node) {
const last = node.kind == 'text' ? node : node.getTextNodes().last()
return this.anchorKey == last.key && this.anchorOffset == last.length
}
/**
* Check whether the anchor edge of a selection is in a `node` and at an
* offset between `start` and `end`.
*
* @param {Node} node
* @param {Number} start
* @param {Number} end
* @return {Boolean}
*/
hasAnchorBetween(node, start, end) {
return (
this.hasAnchorIn(node) &&
start <= this.anchorOffset &&
this.anchorOffset <= end
)
}
/**
* Check whether the anchor edge of a selection is in a `node`.
*
* @param {Node} node
* @return {Boolean}
*/
hasAnchorIn(node) {
const nodes = node.kind == 'text' ? [node] : node.getTextNodes()
return nodes.some(n => n.key == this.anchorKey)
}
/**
* Check whether focus point of the selection is at the end of a `node`.
*
* @param {Node} node
* @return {Boolean}
*/
hasFocusAtEndOf(node) {
const last = node.kind == 'text' ? node : node.getTextNodes().last()
return this.focusKey == last.key && this.focusOffset == last.length
}
/**
* Check whether focus point of the selection is at the start of a `node`.
*
* @param {Node} node
* @return {Boolean}
*/
hasFocusAtStartOf(node) {
const first = node.kind == 'text' ? node : node.getTextNodes().first()
return this.focusKey == first.key && this.focusOffset == 0
}
/**
* Check whether the focus edge of a selection is in a `node` and at an
* offset between `start` and `end`.
*
* @param {Node} node
* @param {Number} start
* @param {Number} end
* @return {Boolean}
*/
hasFocusBetween(node, start, end) {
return (
this.hasFocusIn(node) &&
start <= this.focusOffset &&
this.focusOffset <= end
)
}
/**
* Check whether the focus edge of a selection is in a `node`.
*
* @param {Node} node
* @return {Boolean}
*/
hasFocusIn(node) {
const nodes = node.kind == 'text' ? [node] : node.getTextNodes()
return nodes.some(n => n.key == this.focusKey)
}
/** /**
* Check whether the selection is at the start of a `node`. * Check whether the selection is at the start of a `node`.
* *
@@ -141,38 +268,6 @@ class Selection extends new Record(DEFAULTS) {
return this.isCollapsed && endKey == last.key && endOffset == last.length return this.isCollapsed && endKey == last.key && endOffset == last.length
} }
/**
* Check whether the selection has an edge at the start of a `node`.
*
* @param {Node} node
* @return {Boolean} hasEdgeAtStart
*/
hasEdgeAtStartOf(node) {
const { startKey, startOffset, endKey, endOffset } = this
const first = node.kind == 'text' ? node : node.getTextNodes().first()
return (
(startKey == first.key && startOffset == 0) ||
(endKey == first.key && endOffset == 0)
)
}
/**
* Check whether the selection has an edge at the end of a `node`.
*
* @param {Node} node
* @return {Boolean} hasEdgeAtEnd
*/
hasEdgeAtEndOf(node) {
const { startKey, startOffset, endKey, endOffset } = this
const last = node.kind == 'text' ? node : node.getTextNodes().last()
return (
(startKey == last.key && startOffset == last.length) ||
(endKey == last.key && endOffset == last.length)
)
}
/** /**
* Normalize the selection, relative to a `node`, ensuring that the anchor * Normalize the selection, relative to a `node`, ensuring that the anchor
* and focus nodes of the selection always refer to leaf text nodes. * and focus nodes of the selection always refer to leaf text nodes.
@@ -221,36 +316,6 @@ class Selection extends new Record(DEFAULTS) {
: anchorIndex > focusIndex : anchorIndex > focusIndex
} }
// If the selection is expanded and has an edge on a void block, move it.
// if (isExpanded) {
// let anchorBlock = node.getClosestBlock(anchorNode)
// let focusBlock = node.getClosestBlock(focusNode)
// if (anchorBlock.isVoid) {
// while (anchorBanchorBlock.isVoid) {
// anchorBlock = isBackward
// ? node.getPreviousBlock(anchorBlock)
// : node.getNextBlock(anchorBlock)
// }
// anchorNode = isBackward
// ? anchorBlock.getTextNodes().last()
// : anchorBlock.getTextNodes().first()
// anchorOffset = isBackward
// ? anchorNode.length
// : 0
// }
// else if (focusBlock.isVoid) {
// focusNode = isBackward
// ? node.getNextBlock(focusBlock).getTextNodes().first()
// : node.getPreviousBlock(focusBlock).getTextNodes().last()
// focusOffset = isBackward
// ? 0
// : focusNode.length
// }
// }
// Merge in any updated properties. // Merge in any updated properties.
return selection.merge({ return selection.merge({
anchorKey: anchorNode.key, anchorKey: anchorNode.key,
@@ -285,17 +350,6 @@ class Selection extends new Record(DEFAULTS) {
}) })
} }
/**
* Move the selection to a specific anchor and focus point.
*
* @param {Object} properties
* @return {Selection} selection
*/
moveTo(properties) {
return this.merge(properties)
}
/** /**
* Move the focus point to the anchor point. * Move the focus point to the anchor point.
* *
@@ -324,46 +378,6 @@ class Selection extends new Record(DEFAULTS) {
}) })
} }
/**
* Move the end point to the start point.
*
* @return {Selection} selection
*/
moveToStart() {
return this.isBackward
? this.merge({
anchorKey: this.focusKey,
anchorOffset: this.focusOffset,
isBackward: false
})
: this.merge({
focusKey: this.anchorKey,
focusOffset: this.anchorOffset,
isBackward: false
})
}
/**
* Move the start point to the end point.
*
* @return {Selection} selection
*/
moveToEnd() {
return this.isBackward
? this.merge({
focusKey: this.anchorKey,
focusOffset: this.anchorOffset,
isBackward: false
})
: this.merge({
anchorKey: this.focusKey,
anchorOffset: this.focusOffset,
isBackward: false
})
}
/** /**
* Move to the start of a `node`. * Move to the start of a `node`.
* *
@@ -502,6 +516,41 @@ class Selection extends new Record(DEFAULTS) {
} }
/**
* Add start, end and edge convenience methods.
*/
START_END_METHODS.concat(EDGE_METHODS).forEach((pattern) => {
const [ p, s ] = pattern.split('%')
const anchor = `${p}Anchor${s}`
const edge = `${p}Edge${s}`
const end = `${p}End${s}`
const focus = `${p}Focus${s}`
const start = `${p}Start${s}`
Selection.prototype[start] = function (...args) {
return this.isBackward
? this[focus](...args)
: this[anchor](...args)
}
Selection.prototype[end] = function (...args) {
return this.isBackward
? this[anchor](...args)
: this[focus](...args)
}
if (!EDGE_METHODS.includes(pattern)) return
Selection.prototype[edge] = function (...args) {
return this[anchor](...args) || this[focus](...args)
}
})
/**
* Add edge methods.
*/
/** /**
* Export. * Export.
*/ */

View File

@@ -519,6 +519,31 @@ class State extends new Record(DEFAULTS) {
return state return state
} }
/**
* Move the selection to a specific anchor and focus point.
*
* @param {Object} properties
* @return {State} state
*/
moveTo(properties) {
let state = this
let { document, selection } = state
// Pass in properties, and force `isBackward` to be re-resolved.
selection = selection.merge({
anchorKey: properties.anchorKey,
anchorOffset: properties.anchorOffset,
focusKey: properties.focusKey,
focusOffset: properties.focusOffset,
isBackward: null
})
selection = selection.normalize(document)
state = state.merge({ selection })
return state
}
/** /**
* Move the selection to the start of the previous block. * Move the selection to the start of the previous block.
* *

View File

@@ -57,7 +57,6 @@ const SELECTION_TRANSFORMS = [
'focus', 'focus',
'moveBackward', 'moveBackward',
'moveForward', 'moveForward',
'moveTo',
'moveToAnchor', 'moveToAnchor',
'moveToEnd', 'moveToEnd',
'moveToEndOf', 'moveToEndOf',
@@ -78,6 +77,7 @@ const STATE_TRANSFORMS = [
'insertFragment', 'insertFragment',
'insertText', 'insertText',
'mark', 'mark',
'moveTo',
'moveToStartOfPreviousBlock', 'moveToStartOfPreviousBlock',
'moveToEndOfPreviousBlock', 'moveToEndOfPreviousBlock',
'moveToStartOfNextBlock', 'moveToStartOfNextBlock',

View File

@@ -1,7 +1,7 @@
import Key from '../utils/key'
import React from 'react' import React from 'react'
import keycode from 'keycode' import keycode from 'keycode'
import { isCommand, isCtrl, isWindowsCommand, isWord } from '../utils/event'
import { IS_WINDOWS, IS_MAC } from '../utils/environment' import { IS_WINDOWS, IS_MAC } from '../utils/environment'
/** /**
@@ -93,13 +93,13 @@ export default {
} }
case 'backspace': { case 'backspace': {
return isWord(e) return Key.isWord(e)
? transform.backspaceWord().apply() ? transform.backspaceWord().apply()
: transform.deleteBackward().apply() : transform.deleteBackward().apply()
} }
case 'delete': { case 'delete': {
return isWord(e) return Key.isWord(e)
? transform.deleteWord().apply() ? transform.deleteWord().apply()
: transform.deleteForward().apply() : transform.deleteForward().apply()
} }
@@ -137,13 +137,13 @@ export default {
} }
case 'y': { case 'y': {
if (!isWindowsCommand(e)) return if (!Key.isWindowsCommand(e)) return
return transform.redo() return transform.redo()
} }
case 'z': { case 'z': {
if (!isCommand(e)) return if (!Key.isCommand(e)) return
return IS_MAC && e.shiftKey return IS_MAC && Key.isShift(e)
? transform.redo() ? transform.redo()
: transform.undo() : transform.undo()
} }

View File

@@ -16,3 +16,16 @@ export const IS_MAC = process.browser && new Parser().getOS().name == 'Mac OS'
export const IS_SAFARI = process.browser && browser.name == 'safari' export const IS_SAFARI = process.browser && browser.name == 'safari'
export const IS_UBUNTU = process.browser && new Parser().getOS().name == 'Ubuntu' export const IS_UBUNTU = process.browser && new Parser().getOS().name == 'Ubuntu'
export const IS_WINDOWS = process.browser && new Parser().getOS().name.includes('Windows') export const IS_WINDOWS = process.browser && new Parser().getOS().name.includes('Windows')
export default {
IS_ANDROID,
IS_CHROME,
IS_EDGE,
IS_FIREFOX,
IS_IE,
IS_IOS,
IS_MAC,
IS_SAFARI,
IS_UBUNTU,
IS_WINDOWS
}

View File

@@ -2,16 +2,27 @@
import { IS_MAC, IS_WINDOWS } from './environment' import { IS_MAC, IS_WINDOWS } from './environment'
/** /**
* Does an `e` have the word-level modifier? * Does an `e` have the alt modifier?
* *
* @param {Event} e * @param {Event} e
* @return {Boolean} * @return {Boolean}
*/ */
export function isWord(e) { function isAlt(e) {
return e.altKey
}
/**
* Does an `e` have the command modifier?
*
* @param {Event} e
* @return {Boolean}
*/
function isCommand(e) {
return IS_MAC return IS_MAC
? e.altKey ? e.metaKey && !e.altKey
: e.ctrlKey : e.ctrlKey && !e.altKey
} }
/** /**
@@ -21,10 +32,21 @@ export function isWord(e) {
* @return {Boolean} * @return {Boolean}
*/ */
export function isCtrl(e) { function isCtrl(e) {
return e.ctrlKey && !e.altKey return e.ctrlKey && !e.altKey
} }
/**
* Does an `e` have the Mac command modifier?
*
* @param {Event} e
* @return {Boolean}
*/
function isMacCommand(e) {
return IS_MAC && isCommand(e)
}
/** /**
* Does an `e` have the option modifier? * Does an `e` have the option modifier?
* *
@@ -32,7 +54,7 @@ export function isCtrl(e) {
* @return {Boolean} * @return {Boolean}
*/ */
export function isOption(e) { function isOption(e) {
return IS_MAC && e.altKey return IS_MAC && e.altKey
} }
@@ -43,34 +65,10 @@ export function isOption(e) {
* @return {Boolean} * @return {Boolean}
*/ */
export function isShift(e) { function isShift(e) {
return e.shiftKey return e.shiftKey
} }
/**
* Does an `e` have the command modifier?
*
* @param {Event} e
* @return {Boolean}
*/
export function isCommand(e) {
return IS_MAC
? e.metaKey && !e.altKey
: e.ctrlKey && !e.altKey
}
/**
* Does an `e` have the Mac command modifier?
*
* @param {Event} e
* @return {Boolean}
*/
export function isMacCommand(e) {
return IS_MAC && isCommand(e)
}
/** /**
* Does an `e` have the Windows command modifier? * Does an `e` have the Windows command modifier?
* *
@@ -78,6 +76,34 @@ export function isMacCommand(e) {
* @return {Boolean} * @return {Boolean}
*/ */
export function isWindowsCommand(e) { function isWindowsCommand(e) {
return IS_WINDOWS && isCommand(e) return IS_WINDOWS && isCommand(e)
} }
/**
* Does an `e` have the word-level modifier?
*
* @param {Event} e
* @return {Boolean}
*/
function isWord(e) {
return IS_MAC
? e.altKey
: e.ctrlKey
}
/**
* Export.
*/
export default {
isAlt,
isCommand,
isCtrl,
isMacCommand,
isOption,
isShift,
isWindowsCommand,
isWord
}

View File

@@ -3,7 +3,7 @@
"version": "0.1.0", "version": "0.1.0",
"license": "MIT", "license": "MIT",
"repository": "git://github.com/ianstormtaylor/slate.git", "repository": "git://github.com/ianstormtaylor/slate.git",
"main": "./dist/index.js", "main": "./lib/index.js",
"scripts": { "scripts": {
"prepublish": "make dist", "prepublish": "make dist",
"test": "make check" "test": "make check"
@@ -32,6 +32,7 @@
"babelify": "^7.3.0", "babelify": "^7.3.0",
"browserify": "^13.0.1", "browserify": "^13.0.1",
"component-type": "^1.2.1", "component-type": "^1.2.1",
"draft-js": "^0.7.0",
"eslint": "^3.0.1", "eslint": "^3.0.1",
"eslint-plugin-import": "^1.10.2", "eslint-plugin-import": "^1.10.2",
"eslint-plugin-react": "^5.2.2", "eslint-plugin-react": "^5.2.2",