1
0
mirror of https://github.com/ianstormtaylor/slate.git synced 2025-02-19 06:35:03 +01:00
slate/examples/links/index.js
Ian Storm Taylor 509d3d50fc remove rendering from schema & make it expressive (#1262)
* split rendering out of schema

* remove default components

* first stab at new schema

* make default normalizations smarter

* revert to forcing defaults to be verbose?

* refactor reason constants

* split nodes into blocks/inlines

* get tests passing

* restructure schema tests

* add parent test

* cleanup

* remove defaults from schema

* refactor schema rule.nodes validation, update example

* embed schema in state objects

* fixes

* update examples, and fixes

* update walkthroughs

* update docs

* remove old schemas doc page

* add more tests

* update benchmarks
2017-10-25 17:32:29 -07:00

208 lines
3.7 KiB
JavaScript

import { Editor, getEventTransfer } from 'slate-react'
import { State } from 'slate'
import React from 'react'
import initialState from './state.json'
import isUrl from 'is-url'
/**
* A change helper to standardize wrapping links.
*
* @param {Change} change
* @param {String} href
*/
function wrapLink(change, href) {
change.wrapInline({
type: 'link',
data: { href }
})
change.collapseToEnd()
}
/**
* A change helper to standardize unwrapping links.
*
* @param {Change} change
*/
function unwrapLink(change) {
change.unwrapInline('link')
}
/**
* The links example.
*
* @type {Component}
*/
class Links extends React.Component {
/**
* Deserialize the raw initial state.
*
* @type {Object}
*/
state = {
state: State.fromJSON(initialState)
}
/**
* Check whether the current selection has a link in it.
*
* @return {Boolean} hasLinks
*/
hasLinks = () => {
const { state } = this.state
return state.inlines.some(inline => inline.type == 'link')
}
/**
* On change.
*
* @param {Change} change
*/
onChange = ({ state }) => {
this.setState({ state })
}
/**
* When clicking a link, if the selection has a link in it, remove the link.
* Otherwise, add a new link with an href and text.
*
* @param {Event} event
*/
onClickLink = (event) => {
event.preventDefault()
const { state } = this.state
const hasLinks = this.hasLinks()
const change = state.change()
if (hasLinks) {
change.call(unwrapLink)
}
else if (state.isExpanded) {
const href = window.prompt('Enter the URL of the link:')
change.call(wrapLink, href)
}
else {
const href = window.prompt('Enter the URL of the link:')
const text = window.prompt('Enter the text for the link:')
change
.insertText(text)
.extend(0 - text.length)
.call(wrapLink, href)
}
this.onChange(change)
}
/**
* On paste, if the text is a link, wrap the selection in a link.
*
* @param {Event} event
* @param {Change} change
*/
onPaste = (event, change) => {
if (change.state.isCollapsed) return
const transfer = getEventTransfer(event)
const { type, text } = transfer
if (type != 'text' && type != 'html') return
if (!isUrl(text)) return
if (this.hasLinks()) {
change.call(unwrapLink)
}
change.call(wrapLink, text)
return true
}
/**
* Render the app.
*
* @return {Element} element
*/
render() {
return (
<div>
{this.renderToolbar()}
{this.renderEditor()}
</div>
)
}
/**
* Render the toolbar.
*
* @return {Element} element
*/
renderToolbar = () => {
const hasLinks = this.hasLinks()
return (
<div className="menu toolbar-menu">
<span className="button" onMouseDown={this.onClickLink} data-active={hasLinks}>
<span className="material-icons">link</span>
</span>
</div>
)
}
/**
* Render the editor.
*
* @return {Element} element
*/
renderEditor = () => {
return (
<div className="editor">
<Editor
placeholder="Enter some text..."
state={this.state.state}
onChange={this.onChange}
onPaste={this.onPaste}
renderNode={this.renderNode}
/>
</div>
)
}
/**
* Render a Slate node.
*
* @param {Object} props
* @return {Element}
*/
renderNode = (props) => {
const { attributes, children, node } = props
switch (node.type) {
case 'link': {
const { data } = node
const href = data.get('href')
return <a {...attributes} href={href}>{children}</a>
}
}
}
}
/**
* Export.
*/
export default Links