mirror of
https://github.com/ianstormtaylor/slate.git
synced 2025-03-08 23:09:47 +01:00
* Fix spell check bug by add data-text:true * Fix spell check bug by spell check add length to a leaf * Fix tests to use data-text:true for marks * Rename data-text to data-slate-leaf; Remove setRef; unlift attributes in leaf * Update examples with data-* * Add attributes to document * Fix renderMark in all documents * Prettier markdown
209 lines
4.0 KiB
JavaScript
209 lines
4.0 KiB
JavaScript
import { Editor } from 'slate-react'
|
|
import { Value } from 'slate'
|
|
|
|
import React from 'react'
|
|
import ReactDOM from 'react-dom'
|
|
import initialValue from './value.json'
|
|
|
|
/**
|
|
* The menu.
|
|
*
|
|
* @type {Component}
|
|
*/
|
|
|
|
class Menu extends React.Component {
|
|
/**
|
|
* Check if the current selection has a mark with `type` in it.
|
|
*
|
|
* @param {String} type
|
|
* @return {Boolean}
|
|
*/
|
|
|
|
hasMark(type) {
|
|
const { value } = this.props
|
|
return value.activeMarks.some(mark => mark.type == type)
|
|
}
|
|
|
|
/**
|
|
* When a mark button is clicked, toggle the current mark.
|
|
*
|
|
* @param {Event} event
|
|
* @param {String} type
|
|
*/
|
|
|
|
onClickMark(event, type) {
|
|
const { value, onChange } = this.props
|
|
event.preventDefault()
|
|
const change = value.change().toggleMark(type)
|
|
onChange(change)
|
|
}
|
|
|
|
/**
|
|
* Render a mark-toggling toolbar button.
|
|
*
|
|
* @param {String} type
|
|
* @param {String} icon
|
|
* @return {Element}
|
|
*/
|
|
|
|
renderMarkButton(type, icon) {
|
|
const isActive = this.hasMark(type)
|
|
const onMouseDown = event => this.onClickMark(event, type)
|
|
|
|
return (
|
|
// eslint-disable-next-line react/jsx-no-bind
|
|
<span className="button" onMouseDown={onMouseDown} data-active={isActive}>
|
|
<span className="material-icons">{icon}</span>
|
|
</span>
|
|
)
|
|
}
|
|
|
|
/**
|
|
* Render.
|
|
*
|
|
* @return {Element}
|
|
*/
|
|
|
|
render() {
|
|
const root = window.document.getElementById('root')
|
|
|
|
return ReactDOM.createPortal(
|
|
<div className="menu hover-menu" ref={this.props.menuRef}>
|
|
{this.renderMarkButton('bold', 'format_bold')}
|
|
{this.renderMarkButton('italic', 'format_italic')}
|
|
{this.renderMarkButton('underlined', 'format_underlined')}
|
|
{this.renderMarkButton('code', 'code')}
|
|
</div>,
|
|
root
|
|
)
|
|
}
|
|
}
|
|
|
|
/**
|
|
* The hovering menu example.
|
|
*
|
|
* @type {Component}
|
|
*/
|
|
|
|
class HoveringMenu extends React.Component {
|
|
/**
|
|
* Deserialize the raw initial value.
|
|
*
|
|
* @type {Object}
|
|
*/
|
|
|
|
state = {
|
|
value: Value.fromJSON(initialValue),
|
|
}
|
|
|
|
/**
|
|
* On update, update the menu.
|
|
*/
|
|
|
|
componentDidMount = () => {
|
|
this.updateMenu()
|
|
}
|
|
|
|
componentDidUpdate = () => {
|
|
this.updateMenu()
|
|
}
|
|
|
|
/**
|
|
* Update the menu's absolute position.
|
|
*/
|
|
|
|
updateMenu = () => {
|
|
const { value } = this.state
|
|
const menu = this.menu
|
|
if (!menu) return
|
|
|
|
if (value.isBlurred || value.isEmpty) {
|
|
menu.removeAttribute('style')
|
|
return
|
|
}
|
|
|
|
const selection = window.getSelection()
|
|
const range = selection.getRangeAt(0)
|
|
const rect = range.getBoundingClientRect()
|
|
menu.style.opacity = 1
|
|
menu.style.top = `${rect.top + window.pageYOffset - menu.offsetHeight}px`
|
|
menu.style.left = `${rect.left +
|
|
window.pageXOffset -
|
|
menu.offsetWidth / 2 +
|
|
rect.width / 2}px`
|
|
}
|
|
|
|
/**
|
|
* On change.
|
|
*
|
|
* @param {Change} change
|
|
*/
|
|
|
|
onChange = ({ value }) => {
|
|
this.setState({ value })
|
|
}
|
|
|
|
/**
|
|
* Save the `menu` ref.
|
|
*
|
|
* @param {Menu} menu
|
|
*/
|
|
|
|
menuRef = menu => {
|
|
this.menu = menu
|
|
}
|
|
|
|
/**
|
|
* Render.
|
|
*
|
|
* @return {Element}
|
|
*/
|
|
|
|
render() {
|
|
return (
|
|
<div>
|
|
<Menu
|
|
menuRef={this.menuRef}
|
|
value={this.state.value}
|
|
onChange={this.onChange}
|
|
/>
|
|
<div className="editor">
|
|
<Editor
|
|
placeholder="Enter some text..."
|
|
value={this.state.value}
|
|
onChange={this.onChange}
|
|
renderMark={this.renderMark}
|
|
/>
|
|
</div>
|
|
</div>
|
|
)
|
|
}
|
|
|
|
/**
|
|
* Render a Slate mark.
|
|
*
|
|
* @param {Object} props
|
|
* @return {Element}
|
|
*/
|
|
|
|
renderMark = props => {
|
|
const { children, mark, attributes } = props
|
|
switch (mark.type) {
|
|
case 'bold':
|
|
return <strong {...attributes}>{children}</strong>
|
|
case 'code':
|
|
return <code {...attributes}>{children}</code>
|
|
case 'italic':
|
|
return <em {...attributes}>{children}</em>
|
|
case 'underlined':
|
|
return <u {...attributes}>{children}</u>
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Export.
|
|
*/
|
|
|
|
export default HoveringMenu
|