text: import { Editor, getEventRange, getEventTransfer } from 'slate-react'
  import { Block, Value } from 'slate'
  
  import React from 'react'
  import initialValue from './value.json'
  import imageExtensions from 'image-extensions'
  import isUrl from 'is-url'
  import styled from 'react-emotion'
  import { Button, Icon, Toolbar } from '../components'
  
  /**
   * A styled image block component.
   *
   * @type {Component}
   */
  
  const Image = styled('img')`
    display: block;
    max-width: 100%;
    max-height: 20em;
    box-shadow: ${props => (props.selected ? '0 0 0 2px blue;' : 'none')};
  `
  
  /*
   * A function to determine whether a URL has an image extension.
   *
   * @param {String} url
   * @return {Boolean}
   */
  
  function isImage(url) {
    return !!imageExtensions.find(url.endsWith)
  }
  
  /**
   * A change function to standardize inserting images.
   *
   * @param {Editor} editor
   * @param {String} src
   * @param {Range} target
   */
  
  function insertImage(editor, src, target) {
    if (target) {
      editor.select(target)
    }
  
    editor.insertBlock({
      type: 'image',
      data: { src },
    })
  }
  
  /**
   * The editor's schema.
   *
   * @type {Object}
   */
  
  const schema = {
    document: {
      last: { type: 'paragraph' },
      normalize: (editor, { code, node, child }) => {
        switch (code) {
          case 'last_child_type_invalid': {
            const paragraph = Block.create('paragraph')
            return editor.insertNodeByKey(node.key, node.nodes.size, paragraph)
          }
        }
      },
    },
    blocks: {
      image: {
        isVoid: true,
      },
    },
  }
  
  /**
   * The images example.
   *
   * @type {Component}
   */
  
  class Images extends React.Component {
    /**
     * Deserialize the raw initial value.
     *
     * @type {Object}
     */
  
    state = {
      value: Value.fromJSON(initialValue),
    }
  
    /**
     * Store a reference to the `editor`.
     *
     * @param {Editor} editor
     */
  
    ref = editor => {
      this.editor = editor
    }
  
    /**
     * Render the app.
     *
     * @return {Element} element
     */
  
    render() {
      return (
        <div>
          <Toolbar>
            <Button onMouseDown={this.onClickImage}>
              <Icon>image</Icon>
            </Button>
          </Toolbar>
          <Editor
            placeholder="Enter some text..."
            ref={this.ref}
            value={this.state.value}
            schema={schema}
            onChange={this.onChange}
            onDrop={this.onDropOrPaste}
            onPaste={this.onDropOrPaste}
            renderNode={this.renderNode}
          />
        </div>
      )
    }
  
    /**
     * Render a Slate node.
     *
     * @param {Object} props
     * @return {Element}
     */
  
    renderNode = (props, editor, next) => {
      const { attributes, node, isFocused } = props
  
      switch (node.type) {
        case 'image': {
          const src = node.data.get('src')
          return <Image src={src} selected={isFocused} {...attributes} />
        }
  
        default: {
          return next()
        }
      }
    }
  
    /**
     * On change.
     *
     * @param {Editor} editor
     */
  
    onChange = ({ value }) => {
      this.setState({ value })
    }
  
    /**
     * On clicking the image button, prompt for an image and insert it.
     *
     * @param {Event} event
     */
  
    onClickImage = event => {
      event.preventDefault()
      const src = window.prompt('Enter the URL of the image:')
      if (!src) return
      this.editor.command(insertImage, src)
    }
  
    /**
     * On drop, insert the image wherever it is dropped.
     *
     * @param {Event} event
     * @param {Editor} editor
     * @param {Function} next
     */
  
    onDropOrPaste = (event, editor, next) => {
      const target = getEventRange(event, editor)
      if (!target && event.type === 'drop') return next()
  
      const transfer = getEventTransfer(event)
      const { type, text, files } = transfer
  
      if (type === 'files') {
        for (const file of files) {
          const reader = new FileReader()
          const [mime] = file.type.split('/')
          if (mime !== 'image') continue
  
          reader.addEventListener('load', () => {
            editor.command(insertImage, reader.result, target)
          })
  
          reader.readAsDataURL(file)
        }
        return
      }
  
      if (type === 'text') {
        if (!isUrl(text)) return next()
        if (!isImage(text)) return next()
        editor.command(insertImage, text, target)
        return
      }
  
      next()
    }
  }
  
  /**
   * Export.
   */
  
  export default Images
  