1
0
mirror of https://github.com/ianstormtaylor/slate.git synced 2025-08-15 11:44:05 +02:00

expose render prop types, refactor default renderers

This commit is contained in:
Ian Storm Taylor
2019-12-01 17:56:56 -05:00
parent bd07635b1e
commit 00826407aa
7 changed files with 138 additions and 147 deletions

View File

@@ -7,10 +7,10 @@ import { ReactEditor } from '..'
import { useEditor } from '../hooks/use-editor' import { useEditor } from '../hooks/use-editor'
import { NODE_TO_INDEX, NODE_TO_PARENT } from '../utils/weak-maps' import { NODE_TO_INDEX, NODE_TO_PARENT } from '../utils/weak-maps'
import { import {
CustomDecorationProps, RenderDecorationProps,
CustomElementProps, RenderElementProps,
CustomMarkProps, RenderMarkProps,
} from './custom' } from './editable'
/** /**
* Children. * Children.
@@ -20,9 +20,9 @@ const Children = (props: {
decorate: (entry: NodeEntry) => Range[] decorate: (entry: NodeEntry) => Range[]
decorations: Range[] decorations: Range[]
node: Ancestor node: Ancestor
renderDecoration?: (props: CustomDecorationProps) => JSX.Element renderDecoration?: (props: RenderDecorationProps) => JSX.Element
renderElement?: (props: CustomElementProps) => JSX.Element renderElement?: (props: RenderElementProps) => JSX.Element
renderMark?: (props: CustomMarkProps) => JSX.Element renderMark?: (props: RenderMarkProps) => JSX.Element
selection: Range | null selection: Range | null
}) => { }) => {
const { const {

View File

@@ -1,106 +0,0 @@
import React from 'react'
import { Element, Range, Mark, Text } from 'slate'
import { useEditor } from '../hooks/use-editor'
import { Leaf } from '../utils/leaf'
export interface CustomDecorationProps {
children: any
decoration: Range
leaf: Leaf
text: Text
attributes: {
'data-slate-decoration': true
}
}
/**
* The default custom decoration renderer.
*/
export const CustomDecoration = (props: CustomDecorationProps) => {
const { attributes, children } = props
return <span {...attributes}>{children}</span>
}
/**
* `CustomElementProps` are passed to the `renderElement` handler.
*/
export interface CustomElementProps {
children: any
element: Element
attributes: {
'data-slate-inline'?: true
'data-slate-node': 'element'
'data-slate-void'?: true
dir?: 'rtl'
ref: any
}
}
/**
* The default element renderer.
*/
export const CustomElement = (props: CustomElementProps) => {
const { attributes, children, element } = props
const editor = useEditor()
const Tag = editor.isInline(element) ? 'span' : 'div'
return (
<Tag {...attributes} style={{ position: 'relative' }}>
{children}
</Tag>
)
}
/**
* `CustomMarkProps` are passed to the `renderMark` handler.
*/
export interface CustomMarkProps {
children: any
mark: Mark
leaf: Leaf
text: Text
attributes: {
'data-slate-mark': true
}
}
/**
* The default custom mark renderer.
*/
export const CustomMark = (props: CustomMarkProps) => {
const { attributes, children } = props
return <span {...attributes}>{children}</span>
}
/**
* A custom decoration for the default placeholder behavior.
*/
export const PlaceholderDecoration = (props: CustomDecorationProps) => {
const { decoration, attributes, children } = props
const { placeholder } = decoration
return (
<span {...attributes}>
<span
contentEditable={false}
style={{
pointerEvents: 'none',
display: 'inline-block',
verticalAlign: 'text-top',
width: '0',
maxWidth: '100%',
whiteSpace: 'nowrap',
opacity: '0.333',
}}
>
{placeholder}
</span>
{children}
</span>
)
}

View File

@@ -1,5 +1,5 @@
import React, { useLayoutEffect, useRef, useMemo, useCallback } from 'react' import React, { useLayoutEffect, useRef, useMemo, useCallback } from 'react'
import { Editor, Element, NodeEntry, Node, Range } from 'slate' import { Editor, Element, NodeEntry, Node, Range, Text, Mark } from 'slate'
import debounce from 'debounce' import debounce from 'debounce'
import scrollIntoView from 'scroll-into-view-if-needed' import scrollIntoView from 'scroll-into-view-if-needed'
@@ -9,6 +9,7 @@ import { IS_FIREFOX, IS_SAFARI } from '../utils/environment'
import { ReactEditor } from '..' import { ReactEditor } from '..'
import { ReadOnlyContext } from '../hooks/use-read-only' import { ReadOnlyContext } from '../hooks/use-read-only'
import { useSlate } from '../hooks/use-slate' import { useSlate } from '../hooks/use-slate'
import { Leaf } from '../utils/leaf'
import { import {
DOMElement, DOMElement,
DOMNode, DOMNode,
@@ -26,11 +27,50 @@ import {
IS_FOCUSED, IS_FOCUSED,
PLACEHOLDER_SYMBOL, PLACEHOLDER_SYMBOL,
} from '../utils/weak-maps' } from '../utils/weak-maps'
import {
CustomDecorationProps, /**
CustomElementProps, * `RenderDecorationProps` are passed to the `renderDecoration` handler.
CustomMarkProps, */
} from './custom'
export interface RenderDecorationProps {
children: any
decoration: Range
leaf: Leaf
text: Text
attributes: {
'data-slate-decoration': true
}
}
/**
* `RenderElementProps` are passed to the `renderElement` handler.
*/
export interface RenderElementProps {
children: any
element: Element
attributes: {
'data-slate-inline'?: true
'data-slate-node': 'element'
'data-slate-void'?: true
dir?: 'rtl'
ref: any
}
}
/**
* `RenderMarkProps` are passed to the `renderMark` handler.
*/
export interface RenderMarkProps {
children: any
mark: Mark
leaf: Leaf
text: Text
attributes: {
'data-slate-mark': true
}
}
/** /**
* Editable. * Editable.
@@ -44,9 +84,9 @@ export const Editable = (
readOnly?: boolean readOnly?: boolean
role?: string role?: string
style?: React.CSSProperties style?: React.CSSProperties
renderDecoration?: (props: CustomDecorationProps) => JSX.Element renderDecoration?: (props: RenderDecorationProps) => JSX.Element
renderElement?: (props: CustomElementProps) => JSX.Element renderElement?: (props: RenderElementProps) => JSX.Element
renderMark?: (props: CustomMarkProps) => JSX.Element renderMark?: (props: RenderMarkProps) => JSX.Element
} & React.TextareaHTMLAttributes<HTMLDivElement> } & React.TextareaHTMLAttributes<HTMLDivElement>
) => { ) => {
const { const {

View File

@@ -14,11 +14,10 @@ import {
KEY_TO_ELEMENT, KEY_TO_ELEMENT,
} from '../utils/weak-maps' } from '../utils/weak-maps'
import { import {
CustomDecorationProps, RenderDecorationProps,
CustomElement, RenderElementProps,
CustomElementProps, RenderMarkProps,
CustomMarkProps, } from './editable'
} from './custom'
import { isRangeListEqual } from '../utils/leaf' import { isRangeListEqual } from '../utils/leaf'
/** /**
@@ -29,9 +28,9 @@ const Element = (props: {
decorate: (entry: NodeEntry) => Range[] decorate: (entry: NodeEntry) => Range[]
decorations: Range[] decorations: Range[]
element: SlateElement element: SlateElement
renderDecoration?: (props: CustomDecorationProps) => JSX.Element renderDecoration?: (props: RenderDecorationProps) => JSX.Element
renderElement?: (props: CustomElementProps) => JSX.Element renderElement?: (props: RenderElementProps) => JSX.Element
renderMark?: (props: CustomMarkProps) => JSX.Element renderMark?: (props: RenderMarkProps) => JSX.Element
selection: Range | null selection: Range | null
}) => { }) => {
const { const {
@@ -39,7 +38,7 @@ const Element = (props: {
decorations, decorations,
element, element,
renderDecoration, renderDecoration,
renderElement = (p: CustomElementProps) => <CustomElement {...p} />, renderElement = (p: RenderElementProps) => <DefaultElement {...p} />,
renderMark, renderMark,
selection, selection,
} = props } = props
@@ -153,4 +152,19 @@ const MemoizedElement = React.memo(Element, (prev, next) => {
) )
}) })
/**
* The default element renderer.
*/
export const DefaultElement = (props: RenderElementProps) => {
const { attributes, children, element } = props
const editor = useEditor()
const Tag = editor.isInline(element) ? 'span' : 'div'
return (
<Tag {...attributes} style={{ position: 'relative' }}>
{children}
</Tag>
)
}
export default MemoizedElement export default MemoizedElement

View File

@@ -1,15 +1,10 @@
import React from 'react' import React from 'react'
import { Text, Element } from 'slate' import { Text, Element } from 'slate'
import String from './string' import String from './string'
import {
CustomDecoration,
CustomDecorationProps,
CustomMark,
CustomMarkProps,
PlaceholderDecoration,
} from './custom'
import { PLACEHOLDER_SYMBOL } from '../utils/weak-maps'
import { Leaf as SlateLeaf } from '../utils/leaf' import { Leaf as SlateLeaf } from '../utils/leaf'
import { PLACEHOLDER_SYMBOL } from '../utils/weak-maps'
import { RenderDecorationProps, RenderMarkProps } from './editable'
/** /**
* Individual leaves in a text node with unique formatting. * Individual leaves in a text node with unique formatting.
@@ -19,8 +14,8 @@ const Leaf = (props: {
isLast: boolean isLast: boolean
leaf: SlateLeaf leaf: SlateLeaf
parent: Element parent: Element
renderDecoration?: (props: CustomDecorationProps) => JSX.Element renderDecoration?: (props: RenderDecorationProps) => JSX.Element
renderMark?: (props: CustomMarkProps) => JSX.Element renderMark?: (props: RenderMarkProps) => JSX.Element
text: Text text: Text
}) => { }) => {
const { const {
@@ -28,10 +23,10 @@ const Leaf = (props: {
isLast, isLast,
text, text,
parent, parent,
renderDecoration = (props: CustomDecorationProps) => ( renderDecoration = (props: RenderDecorationProps) => (
<CustomDecoration {...props} /> <DefaultDecoration {...props} />
), ),
renderMark = (props: CustomMarkProps) => <CustomMark {...props} />, renderMark = (props: RenderMarkProps) => <DefaultMark {...props} />,
} = props } = props
let children = ( let children = (
@@ -95,4 +90,50 @@ const MemoizedLeaf = React.memo(Leaf, (prev, next) => {
) )
}) })
/**
* The default custom decoration renderer.
*/
export const DefaultDecoration = (props: RenderDecorationProps) => {
const { attributes, children } = props
return <span {...attributes}>{children}</span>
}
/**
* The default custom mark renderer.
*/
export const DefaultMark = (props: RenderMarkProps) => {
const { attributes, children } = props
return <span {...attributes}>{children}</span>
}
/**
* A custom decoration for the default placeholder behavior.
*/
const PlaceholderDecoration = (props: RenderDecorationProps) => {
const { decoration, attributes, children } = props
const { placeholder } = decoration
return (
<span {...attributes}>
<span
contentEditable={false}
style={{
pointerEvents: 'none',
display: 'inline-block',
verticalAlign: 'text-top',
width: '0',
maxWidth: '100%',
whiteSpace: 'nowrap',
opacity: '0.333',
}}
>
{placeholder}
</span>
{children}
</span>
)
}
export default MemoizedLeaf export default MemoizedLeaf

View File

@@ -4,12 +4,12 @@ import { Range, Element, Text as SlateText } from 'slate'
import Leaf from './leaf' import Leaf from './leaf'
import { Leaf as SlateLeaf } from '../utils/leaf' import { Leaf as SlateLeaf } from '../utils/leaf'
import { ReactEditor, useEditor } from '..' import { ReactEditor, useEditor } from '..'
import { RenderDecorationProps, RenderMarkProps } from './editable'
import { import {
KEY_TO_ELEMENT, KEY_TO_ELEMENT,
NODE_TO_ELEMENT, NODE_TO_ELEMENT,
ELEMENT_TO_NODE, ELEMENT_TO_NODE,
} from '../utils/weak-maps' } from '../utils/weak-maps'
import { CustomDecorationProps, CustomMarkProps } from './custom'
/** /**
* Text. * Text.
@@ -19,8 +19,8 @@ const Text = (props: {
decorations: Range[] decorations: Range[]
isLast: boolean isLast: boolean
parent: Element parent: Element
renderDecoration?: (props: CustomDecorationProps) => JSX.Element renderDecoration?: (props: RenderDecorationProps) => JSX.Element
renderMark?: (props: CustomMarkProps) => JSX.Element renderMark?: (props: RenderMarkProps) => JSX.Element
text: SlateText text: SlateText
}) => { }) => {
const { const {

View File

@@ -1,4 +1,6 @@
export * from './components/editable' export * from './components/editable'
export { DefaultElement } from './components/element'
export { DefaultMark, DefaultDecoration } from './components/leaf'
export * from './hooks/use-editor' export * from './hooks/use-editor'
export * from './hooks/use-focused' export * from './hooks/use-focused'
export * from './hooks/use-read-only' export * from './hooks/use-read-only'