mirror of
https://github.com/ianstormtaylor/slate.git
synced 2025-08-18 21:21:21 +02:00
refactor placeholder to use schema (#1253)
* refactor placeholder to use schema * update placeholder, remove old export * add maxWidth to prevent overflow * update docs
This commit is contained in:
@@ -48,7 +48,6 @@
|
|||||||
## Slate React
|
## Slate React
|
||||||
|
|
||||||
- [Editor](./reference/slate-react/editor.md)
|
- [Editor](./reference/slate-react/editor.md)
|
||||||
- [Placeholder](./reference/slate-react/placeholder.md)
|
|
||||||
- [Plugins](./reference/slate-react/plugins.md)
|
- [Plugins](./reference/slate-react/plugins.md)
|
||||||
- [Custom Nodes](./reference/slate-react/custom-nodes.md)
|
- [Custom Nodes](./reference/slate-react/custom-nodes.md)
|
||||||
- [Core Plugins](./reference/slate-react/core-plugins.md)
|
- [Core Plugins](./reference/slate-react/core-plugins.md)
|
||||||
|
@@ -1,58 +0,0 @@
|
|||||||
|
|
||||||
# `<Placeholder>`
|
|
||||||
|
|
||||||
```js
|
|
||||||
import { Placeholder } from 'slate-react'
|
|
||||||
```
|
|
||||||
|
|
||||||
A simple component that adds a placeholder to a node. It encapsulates all of the Slate-related logic that determines when to render the placeholder, so you don't have to think about it.
|
|
||||||
|
|
||||||
|
|
||||||
## Properties
|
|
||||||
|
|
||||||
```js
|
|
||||||
<Placeholder
|
|
||||||
className={String}
|
|
||||||
node={Node}
|
|
||||||
parent={Node}
|
|
||||||
state={State}
|
|
||||||
style={Object}
|
|
||||||
>
|
|
||||||
{children}
|
|
||||||
</Placeholder>
|
|
||||||
```
|
|
||||||
|
|
||||||
### `children`
|
|
||||||
`Any`
|
|
||||||
|
|
||||||
React child elements to render inside the placeholder `<span>` element.
|
|
||||||
|
|
||||||
### `className`
|
|
||||||
`String`
|
|
||||||
|
|
||||||
An optional class name string to add to the placeholder `<span>` element.
|
|
||||||
|
|
||||||
### `firstOnly`
|
|
||||||
`Boolean`
|
|
||||||
|
|
||||||
An optional toggle that allows the Placeholder to render even if it is not the first node of the parent. This is useful for cases where the Placeholder should show up at every empty instance of the node. Defaults to `true`.
|
|
||||||
|
|
||||||
### `node`
|
|
||||||
`Node`
|
|
||||||
|
|
||||||
The node to render the placeholder element on top of. The placeholder is positioned absolutely, covering the entire node.
|
|
||||||
|
|
||||||
### `parent`
|
|
||||||
`Node`
|
|
||||||
|
|
||||||
The node to check for non-empty content, to determine whether the placeholder should be shown or not, if `firstOnly` is set to `false`.
|
|
||||||
|
|
||||||
### `state`
|
|
||||||
`State`
|
|
||||||
|
|
||||||
The current state of the editor.
|
|
||||||
|
|
||||||
### `style`
|
|
||||||
`Object`
|
|
||||||
|
|
||||||
An optional dictionary of styles to pass to the placeholder `<span>` element.
|
|
@@ -84,6 +84,7 @@ Internally, the `marks` and `nodes` properties of a schema are simply converted
|
|||||||
match: Function,
|
match: Function,
|
||||||
decorate: Function,
|
decorate: Function,
|
||||||
normalize: Function,
|
normalize: Function,
|
||||||
|
placeholder: Component || Function,
|
||||||
render: Component || Function || Object || String,
|
render: Component || Function || Object || String,
|
||||||
validate: Function
|
validate: Function
|
||||||
}
|
}
|
||||||
@@ -140,6 +141,18 @@ The `decorate` property allows you define a function that will apply extra marks
|
|||||||
|
|
||||||
The `normalize` property is a function to run that recovers the editor's state after the `validate` property of a rule has determined that an object is invalid. It is passed a [`Change`](./change.md) that it can use to make modifications. It is also passed the return value of the `validate` function, which makes it easy to quickly determine the failure reason from the validation.
|
The `normalize` property is a function to run that recovers the editor's state after the `validate` property of a rule has determined that an object is invalid. It is passed a [`Change`](./change.md) that it can use to make modifications. It is also passed the return value of the `validate` function, which makes it easy to quickly determine the failure reason from the validation.
|
||||||
|
|
||||||
|
### `placeholder`
|
||||||
|
`Component` <br/>
|
||||||
|
`Function`
|
||||||
|
|
||||||
|
```js
|
||||||
|
{
|
||||||
|
placeholder: (props) => <span>{props.editor.props.placeholder}</span>
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
The `placeholder` property determines which React component Slate will use to render a placeholder for the editor.
|
||||||
|
|
||||||
### `render`
|
### `render`
|
||||||
`Component` <br/>
|
`Component` <br/>
|
||||||
`Function` <br/>
|
`Function` <br/>
|
||||||
|
@@ -145,7 +145,7 @@ class CheckLists extends React.Component {
|
|||||||
<div className="editor">
|
<div className="editor">
|
||||||
<Editor
|
<Editor
|
||||||
spellCheck
|
spellCheck
|
||||||
placeholder={'Enter some text...'}
|
placeholder="Get to work..."
|
||||||
schema={schema}
|
schema={schema}
|
||||||
state={this.state.state}
|
state={this.state.state}
|
||||||
onChange={this.onChange}
|
onChange={this.onChange}
|
||||||
|
@@ -190,6 +190,7 @@ class CodeHighlighting extends React.Component {
|
|||||||
return (
|
return (
|
||||||
<div className="editor">
|
<div className="editor">
|
||||||
<Editor
|
<Editor
|
||||||
|
placeholder="Write some code..."
|
||||||
schema={schema}
|
schema={schema}
|
||||||
state={this.state.state}
|
state={this.state.state}
|
||||||
onKeyDown={this.onKeyDown}
|
onKeyDown={this.onKeyDown}
|
||||||
|
@@ -56,6 +56,7 @@ class Embeds extends React.Component {
|
|||||||
return (
|
return (
|
||||||
<div className="editor">
|
<div className="editor">
|
||||||
<Editor
|
<Editor
|
||||||
|
placeholder="Enter some text..."
|
||||||
schema={schema}
|
schema={schema}
|
||||||
state={this.state.state}
|
state={this.state.state}
|
||||||
onChange={this.onChange}
|
onChange={this.onChange}
|
||||||
|
@@ -129,6 +129,7 @@ class Emojis extends React.Component {
|
|||||||
return (
|
return (
|
||||||
<div className="editor">
|
<div className="editor">
|
||||||
<Editor
|
<Editor
|
||||||
|
placeholder="Write some 😍👋🎉..."
|
||||||
schema={schema}
|
schema={schema}
|
||||||
state={this.state.state}
|
state={this.state.state}
|
||||||
onChange={this.onChange}
|
onChange={this.onChange}
|
||||||
|
@@ -101,6 +101,7 @@ class ForcedLayout extends React.Component {
|
|||||||
render() {
|
render() {
|
||||||
return (
|
return (
|
||||||
<Editor
|
<Editor
|
||||||
|
placeholder="Enter a title..."
|
||||||
state={this.state.state}
|
state={this.state.state}
|
||||||
schema={this.state.schema}
|
schema={this.state.schema}
|
||||||
onChange={this.onChange}
|
onChange={this.onChange}
|
||||||
|
@@ -165,6 +165,7 @@ class HoveringMenu extends React.Component {
|
|||||||
/>
|
/>
|
||||||
<div className="editor">
|
<div className="editor">
|
||||||
<Editor
|
<Editor
|
||||||
|
placeholder="Enter some text..."
|
||||||
schema={schema}
|
schema={schema}
|
||||||
state={this.state.state}
|
state={this.state.state}
|
||||||
onChange={this.onChange}
|
onChange={this.onChange}
|
||||||
|
@@ -105,7 +105,7 @@ class HugeDocument extends React.Component {
|
|||||||
render() {
|
render() {
|
||||||
return (
|
return (
|
||||||
<Editor
|
<Editor
|
||||||
placeholder={'Enter some text...'}
|
placeholder="Enter some text..."
|
||||||
schema={schema}
|
schema={schema}
|
||||||
spellCheck={false}
|
spellCheck={false}
|
||||||
state={this.state.state}
|
state={this.state.state}
|
||||||
|
@@ -152,6 +152,7 @@ class Images extends React.Component {
|
|||||||
return (
|
return (
|
||||||
<div className="editor">
|
<div className="editor">
|
||||||
<Editor
|
<Editor
|
||||||
|
placeholder="Enter some text..."
|
||||||
schema={schema}
|
schema={schema}
|
||||||
state={this.state.state}
|
state={this.state.state}
|
||||||
onChange={this.onChange}
|
onChange={this.onChange}
|
||||||
|
@@ -187,6 +187,7 @@ class Links extends React.Component {
|
|||||||
return (
|
return (
|
||||||
<div className="editor">
|
<div className="editor">
|
||||||
<Editor
|
<Editor
|
||||||
|
placeholder="Enter some text..."
|
||||||
schema={schema}
|
schema={schema}
|
||||||
state={this.state.state}
|
state={this.state.state}
|
||||||
onChange={this.onChange}
|
onChange={this.onChange}
|
||||||
|
@@ -155,6 +155,7 @@ class MarkdownPreview extends React.Component {
|
|||||||
return (
|
return (
|
||||||
<div className="editor">
|
<div className="editor">
|
||||||
<Editor
|
<Editor
|
||||||
|
placeholder="Write some markdown..."
|
||||||
schema={schema}
|
schema={schema}
|
||||||
state={this.state.state}
|
state={this.state.state}
|
||||||
onChange={this.onChange}
|
onChange={this.onChange}
|
||||||
|
@@ -77,6 +77,7 @@ class MarkdownShortcuts extends React.Component {
|
|||||||
return (
|
return (
|
||||||
<div className="editor">
|
<div className="editor">
|
||||||
<Editor
|
<Editor
|
||||||
|
placeholder="Write some markdown..."
|
||||||
schema={schema}
|
schema={schema}
|
||||||
state={this.state.state}
|
state={this.state.state}
|
||||||
onChange={this.onChange}
|
onChange={this.onChange}
|
||||||
|
@@ -196,6 +196,7 @@ class PasteHtml extends React.Component {
|
|||||||
return (
|
return (
|
||||||
<div className="editor">
|
<div className="editor">
|
||||||
<Editor
|
<Editor
|
||||||
|
placeholder="Paste in some HTML..."
|
||||||
schema={schema}
|
schema={schema}
|
||||||
state={this.state.state}
|
state={this.state.state}
|
||||||
onPaste={this.onPaste}
|
onPaste={this.onPaste}
|
||||||
|
@@ -41,7 +41,7 @@ class PlainText extends React.Component {
|
|||||||
render() {
|
render() {
|
||||||
return (
|
return (
|
||||||
<Editor
|
<Editor
|
||||||
placeholder={'Enter some plain text...'}
|
placeholder="Enter some plain text..."
|
||||||
state={this.state.state}
|
state={this.state.state}
|
||||||
onChange={this.onChange}
|
onChange={this.onChange}
|
||||||
/>
|
/>
|
||||||
|
@@ -83,7 +83,7 @@ The third is an example of using the plugin.render property to create a higher-o
|
|||||||
render() {
|
render() {
|
||||||
return (
|
return (
|
||||||
<Editor
|
<Editor
|
||||||
placeholder={'Enter some text...'}
|
placeholder="Enter some text..."
|
||||||
plugins={plugins}
|
plugins={plugins}
|
||||||
state={this.state.state}
|
state={this.state.state}
|
||||||
onChange={this.onChange}
|
onChange={this.onChange}
|
||||||
|
@@ -42,7 +42,7 @@ class ReadOnly extends React.Component {
|
|||||||
return (
|
return (
|
||||||
<Editor
|
<Editor
|
||||||
readOnly
|
readOnly
|
||||||
placeholder={'Enter some text...'}
|
placeholder="Enter some text..."
|
||||||
state={this.state.state}
|
state={this.state.state}
|
||||||
onChange={this.onChange}
|
onChange={this.onChange}
|
||||||
/>
|
/>
|
||||||
|
@@ -295,12 +295,12 @@ class RichTextExample extends React.Component {
|
|||||||
return (
|
return (
|
||||||
<div className="editor">
|
<div className="editor">
|
||||||
<Editor
|
<Editor
|
||||||
|
placeholder="Enter some rich text..."
|
||||||
|
schema={schema}
|
||||||
|
spellCheck
|
||||||
state={this.state.state}
|
state={this.state.state}
|
||||||
onChange={this.onChange}
|
onChange={this.onChange}
|
||||||
onKeyDown={this.onKeyDown}
|
onKeyDown={this.onKeyDown}
|
||||||
schema={schema}
|
|
||||||
placeholder={'Enter some rich text...'}
|
|
||||||
spellCheck
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
@@ -69,7 +69,7 @@ class PlainText extends React.Component {
|
|||||||
render() {
|
render() {
|
||||||
return (
|
return (
|
||||||
<Editor
|
<Editor
|
||||||
placeholder={'Enter some plain text...'}
|
placeholder="Enter some plain text..."
|
||||||
schema={schema}
|
schema={schema}
|
||||||
state={this.state.state}
|
state={this.state.state}
|
||||||
onChange={this.onChange}
|
onChange={this.onChange}
|
||||||
|
@@ -133,7 +133,7 @@ class SearchHighlighting extends React.Component {
|
|||||||
state={this.state.state}
|
state={this.state.state}
|
||||||
onChange={this.onChange}
|
onChange={this.onChange}
|
||||||
schema={schema}
|
schema={schema}
|
||||||
placeholder={'Enter some rich text...'}
|
placeholder="Enter some rich text..."
|
||||||
spellCheck
|
spellCheck
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
@@ -209,7 +209,7 @@ class SyncingEditor extends React.Component {
|
|||||||
onChange={this.onChange}
|
onChange={this.onChange}
|
||||||
onKeyDown={this.onKeyDown}
|
onKeyDown={this.onKeyDown}
|
||||||
schema={schema}
|
schema={schema}
|
||||||
placeholder={'Enter some rich text...'}
|
placeholder="Enter some text..."
|
||||||
spellCheck
|
spellCheck
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
@@ -134,6 +134,7 @@ class Tables extends React.Component {
|
|||||||
return (
|
return (
|
||||||
<div className="editor">
|
<div className="editor">
|
||||||
<Editor
|
<Editor
|
||||||
|
placeholder="Enter some text..."
|
||||||
schema={schema}
|
schema={schema}
|
||||||
state={this.state.state}
|
state={this.state.state}
|
||||||
onKeyDown={this.onKeyDown}
|
onKeyDown={this.onKeyDown}
|
||||||
|
51
packages/slate-react/src/components/default-node.js
Normal file
51
packages/slate-react/src/components/default-node.js
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
|
||||||
|
import React from 'react'
|
||||||
|
import SlateTypes from 'slate-prop-types'
|
||||||
|
import Types from 'prop-types'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default node.
|
||||||
|
*
|
||||||
|
* @type {Component}
|
||||||
|
*/
|
||||||
|
|
||||||
|
class DefaultNode extends React.Component {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prop types.
|
||||||
|
*
|
||||||
|
* @type {Object}
|
||||||
|
*/
|
||||||
|
|
||||||
|
static propTypes = {
|
||||||
|
attributes: Types.object.isRequired,
|
||||||
|
editor: Types.object.isRequired,
|
||||||
|
isSelected: Types.bool.isRequired,
|
||||||
|
node: SlateTypes.node.isRequired,
|
||||||
|
parent: SlateTypes.node.isRequired,
|
||||||
|
readOnly: Types.bool.isRequired,
|
||||||
|
state: SlateTypes.state.isRequired,
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Render.
|
||||||
|
*
|
||||||
|
* @return {Element}
|
||||||
|
*/
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const { attributes, children, node } = this.props
|
||||||
|
const Tag = node.kind == 'block' ? 'div' : 'span'
|
||||||
|
const style = { position: 'relative' }
|
||||||
|
return <Tag {...attributes} style={style}>{children}</Tag>
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Export.
|
||||||
|
*
|
||||||
|
* @type {Component}
|
||||||
|
*/
|
||||||
|
|
||||||
|
export default DefaultNode
|
64
packages/slate-react/src/components/default-placeholder.js
Normal file
64
packages/slate-react/src/components/default-placeholder.js
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
|
||||||
|
import React from 'react'
|
||||||
|
import SlateTypes from 'slate-prop-types'
|
||||||
|
import Types from 'prop-types'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default placeholder.
|
||||||
|
*
|
||||||
|
* @type {Component}
|
||||||
|
*/
|
||||||
|
|
||||||
|
class DefaultPlaceholder extends React.Component {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Property types.
|
||||||
|
*
|
||||||
|
* @type {Object}
|
||||||
|
*/
|
||||||
|
|
||||||
|
static propTypes = {
|
||||||
|
editor: Types.object.isRequired,
|
||||||
|
isSelected: Types.bool.isRequired,
|
||||||
|
node: SlateTypes.node.isRequired,
|
||||||
|
parent: SlateTypes.node.isRequired,
|
||||||
|
readOnly: Types.bool.isRequired,
|
||||||
|
state: SlateTypes.state.isRequired,
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Render.
|
||||||
|
*
|
||||||
|
* @return {Element}
|
||||||
|
*/
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const { editor, state } = this.props
|
||||||
|
if (state.document.nodes.size > 1) return null
|
||||||
|
if (!editor.props.placeholder) return null
|
||||||
|
|
||||||
|
const style = {
|
||||||
|
pointerEvents: 'none',
|
||||||
|
display: 'inline-block',
|
||||||
|
width: '0',
|
||||||
|
maxWidth: '100%',
|
||||||
|
whiteSpace: 'nowrap',
|
||||||
|
opacity: '0.333',
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<span contentEditable={false} style={style}>
|
||||||
|
{editor.props.placeholder}
|
||||||
|
</span>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Export.
|
||||||
|
*
|
||||||
|
* @type {Component}
|
||||||
|
*/
|
||||||
|
|
||||||
|
export default DefaultPlaceholder
|
@@ -54,6 +54,7 @@ class Node extends React.Component {
|
|||||||
const { node, schema } = props
|
const { node, schema } = props
|
||||||
this.state = {}
|
this.state = {}
|
||||||
this.state.Component = node.getComponent(schema)
|
this.state.Component = node.getComponent(schema)
|
||||||
|
this.state.Placeholder = node.getPlaceholder(schema)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -78,7 +79,8 @@ class Node extends React.Component {
|
|||||||
componentWillReceiveProps = (props) => {
|
componentWillReceiveProps = (props) => {
|
||||||
if (props.node == this.props.node) return
|
if (props.node == this.props.node) return
|
||||||
const Component = props.node.getComponent(props.schema)
|
const Component = props.node.getComponent(props.schema)
|
||||||
this.setState({ Component })
|
const Placeholder = props.node.getPlaceholder(props.schema)
|
||||||
|
this.setState({ Component, Placeholder })
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -154,7 +156,7 @@ class Node extends React.Component {
|
|||||||
this.debug('render', { props })
|
this.debug('render', { props })
|
||||||
|
|
||||||
const { editor, isSelected, node, parent, readOnly, state } = props
|
const { editor, isSelected, node, parent, readOnly, state } = props
|
||||||
const { Component } = this.state
|
const { Component, Placeholder } = this.state
|
||||||
const { selection } = state
|
const { selection } = state
|
||||||
const indexes = node.getSelectionIndexes(selection, isSelected)
|
const indexes = node.getSelectionIndexes(selection, isSelected)
|
||||||
const children = node.nodes.toArray().map((child, i) => {
|
const children = node.nodes.toArray().map((child, i) => {
|
||||||
@@ -173,17 +175,19 @@ class Node extends React.Component {
|
|||||||
if (direction == 'rtl') attributes.dir = 'rtl'
|
if (direction == 'rtl') attributes.dir = 'rtl'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const p = {
|
||||||
|
editor,
|
||||||
|
isSelected,
|
||||||
|
key: node.key,
|
||||||
|
node,
|
||||||
|
parent,
|
||||||
|
readOnly,
|
||||||
|
state
|
||||||
|
}
|
||||||
|
|
||||||
const element = (
|
const element = (
|
||||||
<Component
|
<Component {...p} attributes={attributes}>
|
||||||
attributes={attributes}
|
{Placeholder && <Placeholder {...p} />}
|
||||||
editor={editor}
|
|
||||||
isSelected={isSelected}
|
|
||||||
key={node.key}
|
|
||||||
node={node}
|
|
||||||
parent={parent}
|
|
||||||
readOnly={readOnly}
|
|
||||||
state={state}
|
|
||||||
>
|
|
||||||
{children}
|
{children}
|
||||||
</Component>
|
</Component>
|
||||||
)
|
)
|
||||||
|
@@ -1,125 +0,0 @@
|
|||||||
|
|
||||||
import React from 'react'
|
|
||||||
import SlateTypes from 'slate-prop-types'
|
|
||||||
import Types from 'prop-types'
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Placeholder.
|
|
||||||
*
|
|
||||||
* @type {Component}
|
|
||||||
*/
|
|
||||||
|
|
||||||
class Placeholder extends React.Component {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Property types.
|
|
||||||
*
|
|
||||||
* @type {Object}
|
|
||||||
*/
|
|
||||||
|
|
||||||
static propTypes = {
|
|
||||||
children: Types.any.isRequired,
|
|
||||||
className: Types.string,
|
|
||||||
firstOnly: Types.bool,
|
|
||||||
node: SlateTypes.node.isRequired,
|
|
||||||
parent: SlateTypes.node,
|
|
||||||
state: SlateTypes.state.isRequired,
|
|
||||||
style: Types.object,
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Default properties.
|
|
||||||
*
|
|
||||||
* @type {Object}
|
|
||||||
*/
|
|
||||||
|
|
||||||
static defaultProps = {
|
|
||||||
firstOnly: true,
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Should the placeholder update?
|
|
||||||
*
|
|
||||||
* @param {Object} props
|
|
||||||
* @param {Object} state
|
|
||||||
* @return {Boolean}
|
|
||||||
*/
|
|
||||||
|
|
||||||
shouldComponentUpdate = (props, state) => {
|
|
||||||
return (
|
|
||||||
props.children != this.props.children ||
|
|
||||||
props.className != this.props.className ||
|
|
||||||
props.firstOnly != this.props.firstOnly ||
|
|
||||||
props.parent != this.props.parent ||
|
|
||||||
props.node != this.props.node ||
|
|
||||||
props.style != this.props.style
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Is the placeholder visible?
|
|
||||||
*
|
|
||||||
* @return {Boolean}
|
|
||||||
*/
|
|
||||||
|
|
||||||
isVisible = () => {
|
|
||||||
const { firstOnly, node, parent } = this.props
|
|
||||||
if (node.text) return false
|
|
||||||
|
|
||||||
if (firstOnly) {
|
|
||||||
if (parent.nodes.size > 1) return false
|
|
||||||
if (parent.nodes.first() === node) return true
|
|
||||||
return false
|
|
||||||
} else {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Render.
|
|
||||||
*
|
|
||||||
* If the placeholder is a string, and no `className` or `style` has been
|
|
||||||
* passed, give it a default style of lowered opacity.
|
|
||||||
*
|
|
||||||
* @return {Element}
|
|
||||||
*/
|
|
||||||
|
|
||||||
render() {
|
|
||||||
const isVisible = this.isVisible()
|
|
||||||
if (!isVisible) return null
|
|
||||||
|
|
||||||
const { children, className } = this.props
|
|
||||||
let { style } = this.props
|
|
||||||
|
|
||||||
if (typeof children === 'string' && style == null && className == null) {
|
|
||||||
style = { opacity: '0.333' }
|
|
||||||
} else if (style == null) {
|
|
||||||
style = {}
|
|
||||||
}
|
|
||||||
|
|
||||||
const styles = {
|
|
||||||
position: 'absolute',
|
|
||||||
top: '0px',
|
|
||||||
right: '0px',
|
|
||||||
bottom: '0px',
|
|
||||||
left: '0px',
|
|
||||||
pointerEvents: 'none',
|
|
||||||
...style
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<span contentEditable={false} className={className} style={styles}>
|
|
||||||
{children}
|
|
||||||
</span>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Export.
|
|
||||||
*
|
|
||||||
* @type {Component}
|
|
||||||
*/
|
|
||||||
|
|
||||||
export default Placeholder
|
|
@@ -1,6 +1,5 @@
|
|||||||
|
|
||||||
import Editor from './components/editor'
|
import Editor from './components/editor'
|
||||||
import Placeholder from './components/placeholder'
|
|
||||||
import findDOMNode from './utils/find-dom-node'
|
import findDOMNode from './utils/find-dom-node'
|
||||||
import findDOMRange from './utils/find-dom-range'
|
import findDOMRange from './utils/find-dom-range'
|
||||||
import findNode from './utils/find-node'
|
import findNode from './utils/find-node'
|
||||||
@@ -17,7 +16,6 @@ import setEventTransfer from './utils/set-event-transfer'
|
|||||||
|
|
||||||
export {
|
export {
|
||||||
Editor,
|
Editor,
|
||||||
Placeholder,
|
|
||||||
findDOMNode,
|
findDOMNode,
|
||||||
findDOMRange,
|
findDOMRange,
|
||||||
findNode,
|
findNode,
|
||||||
@@ -29,7 +27,6 @@ export {
|
|||||||
|
|
||||||
export default {
|
export default {
|
||||||
Editor,
|
Editor,
|
||||||
Placeholder,
|
|
||||||
findDOMNode,
|
findDOMNode,
|
||||||
findDOMRange,
|
findDOMRange,
|
||||||
findNode,
|
findNode,
|
||||||
|
@@ -4,12 +4,13 @@ import Debug from 'debug'
|
|||||||
import Plain from 'slate-plain-serializer'
|
import Plain from 'slate-plain-serializer'
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
import getWindow from 'get-window'
|
import getWindow from 'get-window'
|
||||||
import { Block, Inline, coreSchema } from 'slate'
|
import { Block, Inline, Text, coreSchema } from 'slate'
|
||||||
|
|
||||||
import EVENT_HANDLERS from '../constants/event-handlers'
|
import EVENT_HANDLERS from '../constants/event-handlers'
|
||||||
import HOTKEYS from '../constants/hotkeys'
|
import HOTKEYS from '../constants/hotkeys'
|
||||||
import Content from '../components/content'
|
import Content from '../components/content'
|
||||||
import Placeholder from '../components/placeholder'
|
import DefaultNode from '../components/default-node'
|
||||||
|
import DefaultPlaceholder from '../components/default-placeholder'
|
||||||
import findDOMNode from '../utils/find-dom-node'
|
import findDOMNode from '../utils/find-dom-node'
|
||||||
import findNode from '../utils/find-node'
|
import findNode from '../utils/find-node'
|
||||||
import findPoint from '../utils/find-point'
|
import findPoint from '../utils/find-point'
|
||||||
@@ -31,19 +32,10 @@ const debug = Debug('slate:core:after')
|
|||||||
* The after plugin.
|
* The after plugin.
|
||||||
*
|
*
|
||||||
* @param {Object} options
|
* @param {Object} options
|
||||||
* @property {Element} placeholder
|
|
||||||
* @property {String} placeholderClassName
|
|
||||||
* @property {Object} placeholderStyle
|
|
||||||
* @return {Object}
|
* @return {Object}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
function AfterPlugin(options = {}) {
|
function AfterPlugin(options = {}) {
|
||||||
const {
|
|
||||||
placeholder,
|
|
||||||
placeholderClassName,
|
|
||||||
placeholderStyle,
|
|
||||||
} = options
|
|
||||||
|
|
||||||
let isDraggingInternally = null
|
let isDraggingInternally = null
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -726,55 +718,6 @@ function AfterPlugin(options = {}) {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* A default schema rule to render block nodes.
|
|
||||||
*
|
|
||||||
* @type {Object}
|
|
||||||
*/
|
|
||||||
|
|
||||||
const BLOCK_RENDER_RULE = {
|
|
||||||
match: (node) => {
|
|
||||||
return node.kind == 'block'
|
|
||||||
},
|
|
||||||
render: (props) => {
|
|
||||||
return (
|
|
||||||
<div {...props.attributes} style={{ position: 'relative' }}>
|
|
||||||
{props.children}
|
|
||||||
{placeholder
|
|
||||||
? <Placeholder
|
|
||||||
className={placeholderClassName}
|
|
||||||
node={props.node}
|
|
||||||
parent={props.state.document}
|
|
||||||
state={props.state}
|
|
||||||
style={placeholderStyle}
|
|
||||||
>
|
|
||||||
{placeholder}
|
|
||||||
</Placeholder>
|
|
||||||
: null}
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A default schema rule to render inline nodes.
|
|
||||||
*
|
|
||||||
* @type {Object}
|
|
||||||
*/
|
|
||||||
|
|
||||||
const INLINE_RENDER_RULE = {
|
|
||||||
match: (node) => {
|
|
||||||
return node.kind == 'inline'
|
|
||||||
},
|
|
||||||
render: (props) => {
|
|
||||||
return (
|
|
||||||
<span {...props.attributes} style={{ position: 'relative' }}>
|
|
||||||
{props.children}
|
|
||||||
</span>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add default rendering rules to the schema.
|
* Add default rendering rules to the schema.
|
||||||
*
|
*
|
||||||
@@ -783,8 +726,14 @@ function AfterPlugin(options = {}) {
|
|||||||
|
|
||||||
const schema = {
|
const schema = {
|
||||||
rules: [
|
rules: [
|
||||||
BLOCK_RENDER_RULE,
|
{
|
||||||
INLINE_RENDER_RULE
|
match: obj => obj.kind == 'block' || obj.kind == 'inline',
|
||||||
|
render: DefaultNode,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
match: obj => obj.kind == 'block' && Text.isTextList(obj.nodes) && obj.text == '',
|
||||||
|
placeholder: DefaultPlaceholder,
|
||||||
|
},
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -1328,6 +1328,17 @@ class Node {
|
|||||||
return path
|
return path
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the placeholder for the node from a `schema`.
|
||||||
|
*
|
||||||
|
* @param {Schema} schema
|
||||||
|
* @return {Component|Void}
|
||||||
|
*/
|
||||||
|
|
||||||
|
getPlaceholder(schema) {
|
||||||
|
return schema.__getPlaceholder(this)
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the block node before a descendant text node by `key`.
|
* Get the block node before a descendant text node by `key`.
|
||||||
*
|
*
|
||||||
@@ -2142,6 +2153,7 @@ memoize(Node.prototype, [
|
|||||||
'getOffsetAtRange',
|
'getOffsetAtRange',
|
||||||
'getParent',
|
'getParent',
|
||||||
'getPath',
|
'getPath',
|
||||||
|
'getPlaceholder',
|
||||||
'getPreviousBlock',
|
'getPreviousBlock',
|
||||||
'getPreviousSibling',
|
'getPreviousSibling',
|
||||||
'getPreviousText',
|
'getPreviousText',
|
||||||
|
@@ -110,22 +110,39 @@ class Schema extends Record(DEFAULTS) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return the renderer for an `object`.
|
* Return the component for an `object`.
|
||||||
*
|
*
|
||||||
* This method is private, because it should always be called on one of the
|
* This method is private, because it should always be called on one of the
|
||||||
* often-changing immutable objects instead, since it will be memoized for
|
* often-changing immutable objects instead, since it will be memoized for
|
||||||
* much better performance.
|
* much better performance.
|
||||||
*
|
*
|
||||||
* @param {Mixed} object
|
* @param {Mixed} object
|
||||||
* @return {Component|Void}
|
* @return {Component|Null}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
__getComponent(object) {
|
__getComponent(object) {
|
||||||
const match = find(this.rules, rule => rule.render && rule.match(object))
|
const match = find(this.rules, rule => rule.render && rule.match(object))
|
||||||
if (!match) return
|
if (!match) return null
|
||||||
return match.render
|
return match.render
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the placeholder for an `object`.
|
||||||
|
*
|
||||||
|
* This method is private, because it should always be called on one of the
|
||||||
|
* often-changing immutable objects instead, since it will be memoized for
|
||||||
|
* much better performance.
|
||||||
|
*
|
||||||
|
* @param {Mixed} object
|
||||||
|
* @return {Component|Null}
|
||||||
|
*/
|
||||||
|
|
||||||
|
__getPlaceholder(object) {
|
||||||
|
const match = find(this.rules, rule => rule.placeholder && rule.match(object))
|
||||||
|
if (!match) return null
|
||||||
|
return match.placeholder
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return the decorations for an `object`.
|
* Return the decorations for an `object`.
|
||||||
*
|
*
|
||||||
|
Reference in New Issue
Block a user