1
0
mirror of https://github.com/ianstormtaylor/slate.git synced 2025-04-21 13:51:59 +02:00

adding selections to transforms

This commit is contained in:
Ian Storm Taylor 2016-06-23 15:39:44 -07:00
parent c639db25c4
commit 1c45670ff8
8 changed files with 363 additions and 293 deletions

View File

@ -11,6 +11,7 @@ watchify = $(bin)/watchify
# Flags.
DEBUG ?=
GREP ?=
# Config.
ifeq ($(DEBUG),true)
@ -93,6 +94,7 @@ test-browser: ./test/support/build.js
@ $(mocha-phantomjs) \
--reporter spec \
--timeout 5000 \
--fgrep "$(GREP)" \
./test/support/browser.html
# Run the server-side tests.
@ -102,6 +104,7 @@ test-server:
--require source-map-support/register \
--reporter spec \
--timeout 5000 \
--fgrep "$(GREP)" \
./test/server.js
# Watch the auto-markdown example.

View File

@ -23,6 +23,29 @@ class App extends React.Component {
state: Raw.deserialize(state)
};
/**
* Get the block type for a series of auto-markdown shortcut `chars`.
*
* @param {String} chars
* @return {String} block
*/
getType(chars) {
switch (chars) {
case '*':
case '-':
case '+': return 'list-item'
case '>': return 'block-quote'
case '#': return 'heading-one'
case '##': return 'heading-two'
case '###': return 'heading-three'
case '####': return 'heading-four'
case '#####': return 'heading-five'
case '######': return 'heading-six'
default: return null
}
}
/**
*
* Render the example.
@ -120,57 +143,27 @@ class App extends React.Component {
*/
onSpace(e, state) {
if (state.isCurrentlyExpanded) return
if (state.isExpanded) return
let { selection } = state
const { currentTextNodes, document } = state
const { startOffset } = selection
const node = currentTextNodes.first()
const { text } = node
const chars = text.slice(0, startOffset).replace(/\s*/g, '')
let transform = state.transform()
switch (chars) {
case '#':
transform = transform.setType('heading-one')
break
case '##':
transform = transform.setType('heading-two')
break
case '###':
transform = transform.setType('heading-three')
break
case '####':
transform = transform.setType('heading-four')
break
case '#####':
transform = transform.setType('heading-five')
break
case '######':
transform = transform.setType('heading-six')
break
case '>':
transform = transform.setType('block-quote')
break
case '*':
case '-':
case '+':
if (node.type == 'list-item') return
transform = transform
.setType('list-item')
.wrapBlock('bulleted-list')
break
default:
return
}
const { startText, startBlock, startOffset } = state
const chars = startBlock.text.slice(0, startOffset).replace(/\s*/g, '')
const type = this.getType(chars)
if (!type) return
if (type == 'list-item' && startBlock.type == 'list-item') return
e.preventDefault()
let transform = state
.transform()
.setBlock(type)
if (type == 'list-item') transform = transform.wrapBlock('bulleted-list')
state = transform
.deleteAtRange(selection.extendBackwardToStartOf(node))
.extendToStartOf(startBlock)
.delete()
.apply()
selection = selection.moveToStartOf(node)
state = state.merge({ selection })
return state
}
@ -184,18 +177,18 @@ class App extends React.Component {
*/
onBackspace(e, state) {
if (state.isCurrentlyExpanded) return
if (state.currentStartOffset != 0) return
const node = state.currentBlockNodes.first()
if (!node) debugger
if (node.type == 'paragraph') return
if (state.isExpanded) return
if (state.startOffset != 0) return
const { startBlock } = state
if (startBlock.type == 'paragraph') return
e.preventDefault()
let transform = state
.transform()
.setType('paragraph')
.setBlock('paragraph')
if (node.type == 'list-item') transform = transform.unwrapBlock('bulleted-list')
if (startBlock.type == 'list-item') transform = transform.unwrapBlock('bulleted-list')
state = transform.apply()
return state
@ -211,20 +204,19 @@ class App extends React.Component {
*/
onEnter(e, state) {
if (state.isCurrentlyExpanded) return
const node = state.currentBlockNodes.first()
if (!node) debugger
if (state.currentStartOffset == 0 && node.length == 0) return this.onBackspace(e, state)
if (state.currentEndOffset != node.length) return
if (state.isExpanded) return
const { startBlock, startOffset, endOffset } = state
if (startOffset == 0 && startBlock.length == 0) return this.onBackspace(e, state)
if (endOffset != startBlock.length) return
if (
node.type != 'heading-one' &&
node.type != 'heading-two' &&
node.type != 'heading-three' &&
node.type != 'heading-four' &&
node.type != 'heading-five' &&
node.type != 'heading-six' &&
node.type != 'block-quote'
startBlock.type != 'heading-one' &&
startBlock.type != 'heading-two' &&
startBlock.type != 'heading-three' &&
startBlock.type != 'heading-four' &&
startBlock.type != 'heading-five' &&
startBlock.type != 'heading-six' &&
startBlock.type != 'block-quote'
) {
return
}
@ -232,8 +224,8 @@ class App extends React.Component {
e.preventDefault()
return state
.transform()
.split()
.setType('paragraph')
.splitBlock()
.setBlock('paragraph')
.apply()
}

View File

@ -153,7 +153,8 @@ const Node = {
if (range.isAtStartOf(startNode)) {
const previous = node.getPreviousText(startNode)
range = range.extendBackwardToEndOf(previous)
range = range.extendToEndOf(previous)
range = range.normalize(node)
node = node.deleteAtRange(range)
return node
}
@ -190,7 +191,8 @@ const Node = {
if (range.isAtEndOf(startNode)) {
const next = node.getNextText(startNode)
range = range.extendForwardToStartOf(next)
range = range.extendToStartOf(next)
range = range.normalize(node)
node = node.deleteAtRange(range)
return node
}
@ -491,27 +493,19 @@ const Node = {
key = normalizeKey(key)
this.assertHasDescendant(key)
const match = this.getDescendant(key)
// Find the shallow matching child.
const child = this.nodes.find((node) => {
if (node == match) return true
return node.kind == 'text'
? false
: node.hasDescendant(match)
})
const isChild = this.hasChild(key)
const child = isChild
? this.getChild(key)
: this.nodes.find(node => node.hasDescendant && node.hasDescendant(key))
// Get all of the nodes that come before the matching child.
const befores = this.nodes.takeUntil(node => node.key == child.key)
// Calculate the offset of the nodes before the child.
const offset = this.nodes
.takeUntil(node => node == child)
.reduce((offset, child) => offset + child.length, 0)
// Calculate the offset of the nodes before the matching child.
const offset = befores.reduce((offset, child) => {
return offset + child.length
}, 0)
// If the child's parent is this node, return the offset of all of the nodes
// before it, otherwise recurse.
return this.nodes.find(node => node.key == match.key)
// Recurse if need be.
return isChild
? offset
: offset + child.getOffset(key)
},

View File

@ -10,7 +10,7 @@ const DEFAULTS = {
anchorOffset: 0,
focusKey: null,
focusOffset: 0,
isBackward: false,
isBackward: null,
isFocused: false
}
@ -52,7 +52,7 @@ class Selection extends Record(DEFAULTS) {
*/
get isExpanded() {
return ! this.isCollapsed
return !this.isCollapsed
}
/**
@ -72,7 +72,7 @@ class Selection extends Record(DEFAULTS) {
*/
get isForward() {
return ! this.isBackward
return this.isBackward == null ? null : !this.isBackward
}
/**
@ -141,10 +141,10 @@ class Selection extends Record(DEFAULTS) {
normalize(node) {
let selection = this
let { anchorKey, anchorOffset, focusKey, focusOffset } = selection
let { anchorKey, anchorOffset, focusKey, focusOffset, isBackward } = selection
// If the selection isn't formed yet, abort.
if (anchorKey == null || focusKey == null) return selection
if (this.isUnset) return this
// Asset that the anchor and focus nodes exist in the node tree.
node.assertHasDescendant(anchorKey)
@ -154,28 +154,37 @@ class Selection extends Record(DEFAULTS) {
// If the anchor node isn't a text node, match it to one.
if (anchorNode.kind != 'text') {
anchorNode = node.getTextAtOffset(anchorOffset)
let parent = node.getParent(anchorNode)
let offset = parent.getOffset(anchorNode)
let anchorText = anchorNode.getTextAtOffset(anchorOffset)
let offset = anchorNode.getOffset(anchorText)
anchorOffset = anchorOffset - offset
anchorKey = anchorNode.key
anchorNode = anchorText
}
// If the focus node isn't a text node, match it to one.
if (focusNode.kind != 'text') {
focusNode = node.getTextAtOffset(focusOffset)
let parent = node.getParent(focusNode)
let offset = parent.getOffset(focusNode)
let focusText = focusNode.getTextAtOffset(focusOffset)
let offset = focusNode.getOffset(focusText)
focusOffset = focusOffset - offset
focusKey = focusNode.key
focusNode = focusText
}
// If `isBackward` is not set, derive it.
if (isBackward == null) {
let texts = node.getTextNodes()
let anchorIndex = texts.indexOf(anchorNode)
let focusIndex = texts.indexOf(focusNode)
isBackward = anchorIndex == focusIndex
? anchorOffset > focusOffset
: anchorIndex > focusIndex
}
// Merge in any updated properties.
return selection.merge({
anchorKey,
anchorKey: anchorNode.key,
anchorOffset,
focusKey,
focusOffset
focusKey: focusNode.key,
focusOffset,
isBackward
})
}
@ -303,10 +312,6 @@ class Selection extends Record(DEFAULTS) {
*/
moveForward(n = 1) {
if (!this.isCollapsed) {
throw new Error('The selection must be collapsed to move forward.')
}
return this.merge({
anchorOffset: this.anchorOffset + n,
focusOffset: this.focusOffset + n
@ -321,10 +326,6 @@ class Selection extends Record(DEFAULTS) {
*/
moveBackward(n = 1) {
if (!this.isCollapsed) {
throw new Error('The selection must be collapsed to move backward.')
}
return this.merge({
anchorOffset: this.anchorOffset - n,
focusOffset: this.focusOffset - n
@ -339,13 +340,9 @@ class Selection extends Record(DEFAULTS) {
*/
extendForward(n = 1) {
if (!this.isCollapsed) {
throw new Error('The selection must be collapsed before extending.')
}
return this.merge({
focusOffset: this.focusOffset + n,
isBackward: false
isBackward: null
})
}
@ -357,89 +354,39 @@ class Selection extends Record(DEFAULTS) {
*/
extendBackward(n = 1) {
if (!this.isCollapsed) {
throw new Error('The selection must be collapsed before extending.')
}
return this.merge({
focusOffset: this.focusOffset - n,
isBackward: true
isBackward: null
})
}
/**
* Extend the focus forward to the start of a `node`.
* Extend the focus point to the start of a `node`.
*
* @param {Node} node
* @return {Selection} selection
*/
extendForwardToStartOf(node) {
if (!this.isCollapsed) {
throw new Error('The selection must be collapsed before extending.')
}
extendToStartOf(node) {
return this.merge({
focusKey: node.key,
focusOffset: 0,
isBackward: false
isBackward: null
})
}
/**
* Extend the focus backward to the start of a `node`.
* Extend the focus point to the end of a `node`.
*
* @param {Node} node
* @return {Selection} selection
*/
extendBackwardToStartOf(node) {
if (!this.isCollapsed) {
throw new Error('The selection must be collapsed before extending.')
}
return this.merge({
focusKey: node.key,
focusOffset: 0,
isBackward: true
})
}
/**
* Extend the focus forward to the end of a `node`.
*
* @param {Node} node
* @return {Selection} selection
*/
extendForwardToEndOf(node) {
if (!this.isCollapsed) {
throw new Error('The selection must be collapsed before extending.')
}
extendToEndOf(node) {
return this.merge({
focusKey: node.key,
focusOffset: node.length,
isBackward: false
})
}
/**
* Extend the focus backward to the end of a `node`.
*
* @param {Node} node
* @return {Selection} selection
*/
extendBackwardToEndOf(node) {
if (!this.isCollapsed) {
throw new Error('The selection must be collapsed before extending.')
}
return this.merge({
focusKey: node.key,
focusOffset: node.length,
isBackward: true
isBackward: null
})
}

View File

@ -24,27 +24,6 @@ const DEFAULTS = {
isNative: true
}
/**
* Node-like methods that should be mixed into the `State` prototype.
*/
const NODE_LIKE_METHODS = [
'deleteAtRange',
'deleteBackwardAtRange',
'deleteForwardAtRange',
'insertTextAtRange',
'markAtRange',
'setBlockAtRange',
'setInlineAtRange',
'splitBlockAtRange',
'splitInlineAtRange',
'unmarkAtRange',
'unwrapBlockAtRange',
'unwrapInlineAtRange',
'wrapBlockAtRange',
'wrapInlineAtRange'
]
/**
* State.
*/
@ -71,7 +50,7 @@ class State extends Record(DEFAULTS) {
* @return {Boolean} isCollapsed
*/
get isCurrentlyCollapsed() {
get isCollapsed() {
return this.selection.isCollapsed
}
@ -81,7 +60,7 @@ class State extends Record(DEFAULTS) {
* @return {Boolean} isExpanded
*/
get isCurrentlyExpanded() {
get isExpanded() {
return this.selection.isExpanded
}
@ -91,7 +70,7 @@ class State extends Record(DEFAULTS) {
* @return {Boolean} isBackward
*/
get isCurrentlyBackward() {
get isBackward() {
return this.selection.isBackward
}
@ -101,7 +80,7 @@ class State extends Record(DEFAULTS) {
* @return {Boolean} isForward
*/
get isCurrentlyForward() {
get isForward() {
return this.selection.isForward
}
@ -111,7 +90,7 @@ class State extends Record(DEFAULTS) {
* @return {String} startKey
*/
get currentStartKey() {
get startKey() {
return this.selection.startKey
}
@ -121,7 +100,7 @@ class State extends Record(DEFAULTS) {
* @return {String} endKey
*/
get currentEndKey() {
get endKey() {
return this.selection.endKey
}
@ -131,7 +110,7 @@ class State extends Record(DEFAULTS) {
* @return {String} startOffset
*/
get currentStartOffset() {
get startOffset() {
return this.selection.startOffset
}
@ -141,7 +120,7 @@ class State extends Record(DEFAULTS) {
* @return {String} endOffset
*/
get currentEndOffset() {
get endOffset() {
return this.selection.endOffset
}
@ -151,7 +130,7 @@ class State extends Record(DEFAULTS) {
* @return {String} anchorKey
*/
get currentAnchorKey() {
get anchorKey() {
return this.selection.anchorKey
}
@ -161,7 +140,7 @@ class State extends Record(DEFAULTS) {
* @return {String} focusKey
*/
get currentFocusKey() {
get focusKey() {
return this.selection.focusKey
}
@ -171,7 +150,7 @@ class State extends Record(DEFAULTS) {
* @return {String} anchorOffset
*/
get currentAnchorOffset() {
get anchorOffset() {
return this.selection.anchorOffset
}
@ -181,17 +160,97 @@ class State extends Record(DEFAULTS) {
* @return {String} focusOffset
*/
get currentFocusOffset() {
get focusOffset() {
return this.selection.focusOffset
}
/**
* Get the current start text node.
*
* @return {Text} text
*/
get startText() {
return this.document.getDescendant(this.selection.startKey)
}
/**
* Get the current end node.
*
* @return {Text} text
*/
get endText() {
return this.document.getDescendant(this.selection.endKey)
}
/**
* Get the current anchor node.
*
* @return {Text} text
*/
get anchorText() {
return this.document.getDescendant(this.selection.anchorKey)
}
/**
* Get the current focus node.
*
* @return {Text} text
*/
get focusText() {
return this.document.getDescendant(this.selection.focusKey)
}
/**
* Get the current start text node's closest block parent.
*
* @return {Block} block
*/
get startBlock() {
return this.document.getClosestBlock(this.selection.startKey)
}
/**
* Get the current end text node's closest block parent.
*
* @return {Block} block
*/
get endBlock() {
return this.document.getClosestBlock(this.selection.endKey)
}
/**
* Get the current anchor text node's closest block parent.
*
* @return {Block} block
*/
get anchorBlock() {
return this.document.getClosestBlock(this.selection.anchorKey)
}
/**
* Get the current focus text node's closest block parent.
*
* @return {Block} block
*/
get focusBlock() {
return this.document.getClosestBlock(this.selection.focusKey)
}
/**
* Get the characters in the current selection.
*
* @return {List} characters
*/
get currentCharacters() {
get characters() {
return this.document.getCharactersAtRange(this.selection)
}
@ -201,7 +260,7 @@ class State extends Record(DEFAULTS) {
* @return {Set} marks
*/
get currentMarks() {
get marks() {
return this.document.getMarksAtRange(this.selection)
}
@ -211,7 +270,7 @@ class State extends Record(DEFAULTS) {
* @return {OrderedMap} nodes
*/
get currentBlocks() {
get blocks() {
return this.document.getBlocksAtRange(this.selection)
}
@ -221,7 +280,7 @@ class State extends Record(DEFAULTS) {
* @return {OrderedMap} nodes
*/
get currentInlines() {
get inlines() {
return this.document.getInlinesAtRange(this.selection)
}
@ -231,7 +290,7 @@ class State extends Record(DEFAULTS) {
* @return {OrderedMap} nodes
*/
get currentTexts() {
get texts() {
return this.document.getTextsAtRange(this.selection)
}
@ -292,7 +351,7 @@ class State extends Record(DEFAULTS) {
else if (selection.isAtStartOf(startNode)) {
const parent = document.getParent(startNode)
const previous = document.getPrevious(parent).nodes.first()
const previous = document.getPreviousSibling(parent).nodes.first()
after = selection.moveToEndOf(previous)
}
@ -395,26 +454,50 @@ class State extends Record(DEFAULTS) {
}
/**
* Split the node at the current selection.
* Split the block node at the current selection.
*
* @return {State} state
*/
split() {
splitBlock() {
let state = this
let { document, selection } = state
let after
// Split the document.
document = document.splitAtRange(selection)
document = document.splitBlockAtRange(selection)
// Determine what the selection should be after splitting.
const { startKey } = selection
const startNode = document.getDescendant(startKey)
const parent = document.getParent(startNode)
const next = document.getNext(parent)
const text = next.nodes.first()
selection = selection.moveToStartOf(text)
const nextNode = document.getNextText(startNode)
selection = selection.moveToStartOf(nextNode)
state = state.merge({ document, selection })
return state
}
/**
* Split the inline nodes at the current selection.
*
* @return {State} state
*/
splitInline() {
let state = this
let { document, selection } = state
// Split the document.
document = document.splitInlineAtRange(selection)
// Determine what the selection should be after splitting.
const { startKey } = selection
const inlineParent = document.getClosestInline(startKey)
if (inlineParent) {
const startNode = document.getDescendant(startKey)
const nextNode = document.getNextText(startNode)
selection = selection.moveToStartOf(nextNode)
}
state = state.merge({ document, selection })
return state
@ -460,7 +543,6 @@ class State extends Record(DEFAULTS) {
unwrapBlock(type) {
let state = this
let { document, selection } = state
selection = selection.normalize(document)
document = document.unwrapBlockAtRange(selection, type)
state = state.merge({ document, selection })
return state
@ -492,7 +574,6 @@ class State extends Record(DEFAULTS) {
unwrapInline(type) {
let state = this
let { document, selection } = state
selection = selection.normalize(document)
document = document.unwrapInlineAtRange(selection, type)
state = state.merge({ document, selection })
return state
@ -500,18 +581,6 @@ class State extends Record(DEFAULTS) {
}
/**
* Mix in node-like methods.
*/
NODE_LIKE_METHODS.forEach((method) => {
State.prototype[method] = function (...args) {
let { document } = this
document = document[method](...args)
return this.merge({ document })
}
})
/**
* Export.
*/

View File

@ -22,6 +22,77 @@ const Step = Record({
args: null
})
/**
* Document transforms.
*/
const DOCUMENT_TRANSFORMS = [
'deleteAtRange',
'deleteBackwardAtRange',
'deleteForwardAtRange',
'insertTextAtRange',
'markAtRange',
'setBlockAtRange',
'setInlineAtRange',
'splitBlockAtRange',
'splitInlineAtRange',
'unmarkAtRange',
'unwrapBlockAtRange',
'unwrapInlineAtRange',
'wrapBlockAtRange',
'wrapInlineAtRange'
]
/**
* Selection transforms.
*/
const SELECTION_TRANSFORMS = [
'moveToAnchor',
'moveToFocus',
'moveToStart',
'moveToEnd',
'moveToStartOf',
'moveToEndOf',
'moveToRangeOf',
'moveForward',
'moveBackward',
'extendForward',
'extendBackward',
'extendToStartOf',
'extendToEndOf'
]
/**
* State transforms, that act on both the document and selection.
*/
const STATE_TRANSFORMS = [
'delete',
'deleteBackward',
'deleteForward',
'insertText',
'mark',
'setBlock',
'setInline',
'splitBlock',
'splitInline',
'unmark',
'unwrapBlock',
'unwrapInline',
'wrapBlock',
'wrapInline'
]
/**
* All transforms.
*/
const TRANSFORMS = []
.concat(DOCUMENT_TRANSFORMS)
.concat(SELECTION_TRANSFORMS)
.concat(STATE_TRANSFORMS)
/**
* Defaults.
*/
@ -31,41 +102,6 @@ const DEFAULT_PROPERTIES = {
steps: new List()
}
/**
* Transform types.
*/
const TRANSFORM_TYPES = [
'delete',
'deleteAtRange',
'deleteBackward',
'deleteBackwardAtRange',
'deleteForward',
'deleteForwardAtRange',
'insertText',
'insertTextAtRange',
'mark',
'markAtRange',
'setBlock',
'setBlockAtRange',
'setInline',
'setInlineAtRange',
'splitBlock',
'splitBlockAtRange',
'splitInline',
'splitInlineAtRange',
'unmark',
'unmarkAtRange',
'unwrapBlock',
'unwrapBlockAtRange',
'unwrapInline',
'unwrapInlineAtRange',
'wrapBlock',
'wrapBlockAtRange',
'wrapInline',
'wrapInlineAtRange'
]
/**
* Transform.
*/
@ -134,10 +170,7 @@ class Transform extends Record(DEFAULT_PROPERTIES) {
}
// Apply each of the steps in the transform, arriving at a new state.
state = steps.reduce((state, step) => {
const { type, args } = step
return state[type](...args)
}, state)
state = steps.reduce((state, step) => this.applyStep(state, step), state)
// Apply the "isNative" flag, which is used to allow for natively-handled
// content changes to skip rerendering the editor for performance.
@ -148,6 +181,41 @@ class Transform extends Record(DEFAULT_PROPERTIES) {
return state
}
/**
* Apply a single `step` to a `state`, differentiating between types.
*
* @param {State} state
* @param {Step} step
* @return {State} state
*/
applyStep(state, step) {
const { type, args } = step
if (DOCUMENT_TRANSFORMS.includes(type)) {
let { document, selection } = state
let [ range, ...rest ] = args
range = range.normalize(document)
document = document[type](range, ...rest)
selection = selection.normalize(document)
state = state.merge({ document, selection })
return state
}
else if (SELECTION_TRANSFORMS.includes(type)) {
let { document, selection } = state
selection = selection[type](...args)
selection = selection.normalize(document)
state = state.merge({ selection })
return state
}
else if (STATE_TRANSFORMS.includes(type)) {
state = state[type](...args)
return state
}
}
/**
* Undo to the previous state in the history.
*
@ -211,10 +279,10 @@ class Transform extends Record(DEFAULT_PROPERTIES) {
}
/**
* Add a step-creating method for each transform type.
* Add a step-creating method for each of the transforms.
*/
TRANSFORM_TYPES.forEach((type) => {
TRANSFORMS.forEach((type) => {
Transform.prototype[type] = function (...args) {
let transform = this
let { steps } = transform

View File

@ -1,7 +1,7 @@
import React from 'react'
import keycode from 'keycode'
import environment from '../utils/environment'
import { IS_WINDOWS, IS_MAC } from '../utils/environment'
/**
* Export.
@ -20,14 +20,13 @@ export default {
onKeyDown(e, state, editor) {
const key = keycode(e.which)
const { IS_WINDOWS, IS_MAC } = environment()
switch (key) {
case 'enter': {
e.preventDefault()
return state
.transform()
.split()
.splitBlock()
.apply()
}

View File

@ -4,27 +4,25 @@ import Parser from 'ua-parser-js'
/**
* Read the environment.
*
* @return {Object} environment
*/
function environment() {
return {
IS_ANDROID: browser.name === 'android',
IS_CHROME: browser.name === 'chrome',
IS_EDGE: browser.name === 'edge',
IS_FIREFOX: browser.name === 'firefox',
IS_IE: browser.name === 'ie',
IS_IOS: browser.name === 'ios',
IS_MAC: new Parser().getOS().name === 'Mac OS',
IS_UBUNTU: new Parser().getOS().name === 'Ubuntu',
IS_SAFARI: browser.name === 'safari',
IS_WINDOWS: new Parser().getOS().name.includes('Windows')
}
}
const ENVIRONMENT = process.browser
? {
IS_ANDROID: browser.name === 'android',
IS_CHROME: browser.name === 'chrome',
IS_EDGE: browser.name === 'edge',
IS_FIREFOX: browser.name === 'firefox',
IS_IE: browser.name === 'ie',
IS_IOS: browser.name === 'ios',
IS_MAC: new Parser().getOS().name === 'Mac OS',
IS_UBUNTU: new Parser().getOS().name === 'Ubuntu',
IS_SAFARI: browser.name === 'safari',
IS_WINDOWS: new Parser().getOS().name.includes('Windows')
}
: {}
/**
* Export.
*/
export default environment
export default ENVIRONMENT