mirror of
https://github.com/ianstormtaylor/slate.git
synced 2025-08-18 21:21:21 +02:00
refactor rtl support, add rtl example
This commit is contained in:
@@ -29,11 +29,19 @@ img {
|
||||
blockquote {
|
||||
border-left: 2px solid #ddd;
|
||||
margin-left: 0;
|
||||
margin-right: 0;
|
||||
padding-left: 10px;
|
||||
color: #aaa;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
blockquote[dir="rtl"] {
|
||||
border-left: none;
|
||||
padding-left: 0;
|
||||
padding-right: 10px;
|
||||
border-right: 2px solid #ddd;
|
||||
}
|
||||
|
||||
table {
|
||||
border-collapse: collapse;
|
||||
}
|
||||
|
@@ -18,6 +18,7 @@ import PlainText from './plain-text'
|
||||
import Plugins from './plugins'
|
||||
import ReadOnly from './read-only'
|
||||
import RichText from './rich-text'
|
||||
import RTL from './rtl'
|
||||
import Tables from './tables'
|
||||
import DevPerformancePlain from './development/performance-plain'
|
||||
import DevPerformanceRich from './development/performance-rich'
|
||||
@@ -73,6 +74,7 @@ class App extends React.Component {
|
||||
{this.renderTab('Code Highlighting', 'code-highlighting')}
|
||||
{this.renderTab('Paste HTML', 'paste-html')}
|
||||
{this.renderTab('Read-only', 'read-only')}
|
||||
{this.renderTab('RTL', 'rtl')}
|
||||
{this.renderTab('Plugins', 'plugins')}
|
||||
</div>
|
||||
)
|
||||
@@ -128,6 +130,7 @@ const router = (
|
||||
<Route path="plugins" component={Plugins} />
|
||||
<Route path="read-only" component={ReadOnly} />
|
||||
<Route path="rich-text" component={RichText} />
|
||||
<Route path="rtl" component={RTL} />
|
||||
<Route path="tables" component={Tables} />
|
||||
<Route path="dev-performance-plain" component={DevPerformancePlain} />
|
||||
<Route path="dev-performance-rich" component={DevPerformanceRich} />
|
||||
|
@@ -81,26 +81,6 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"kind": "block",
|
||||
"type": "paragraph",
|
||||
"nodes": [
|
||||
{
|
||||
"kind": "text",
|
||||
"text": "It support both LTR (English) and RTL (Arabic, Hebrew) edition:"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"kind": "block",
|
||||
"type": "paragraph",
|
||||
"nodes": [
|
||||
{
|
||||
"kind": "text",
|
||||
"text": "مرحبا بالعالم"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"kind": "block",
|
||||
"type": "paragraph",
|
||||
|
8
examples/rtl/Readme.md
Normal file
8
examples/rtl/Readme.md
Normal file
@@ -0,0 +1,8 @@
|
||||
|
||||
# Plain Text Example
|
||||
|
||||

|
||||
|
||||
This is the most basic Slate example. It's basically a glorified `<textarea>`. But it gives you a sense for the absolute basics of Slate.
|
||||
|
||||
Check out the [Examples readme](..) to see how to run it!
|
79
examples/rtl/index.js
Normal file
79
examples/rtl/index.js
Normal file
@@ -0,0 +1,79 @@
|
||||
|
||||
import { Editor, Raw } from '../..'
|
||||
import React from 'react'
|
||||
import SoftBreak from 'slate-soft-break'
|
||||
import initialState from './state.json'
|
||||
|
||||
/**
|
||||
* Define a set of node renderers.
|
||||
*
|
||||
* @type {Object}
|
||||
*/
|
||||
|
||||
const NODES = {
|
||||
'block-quote': (props) => <blockquote {...props.attributes}>{props.children}</blockquote>,
|
||||
}
|
||||
|
||||
/**
|
||||
* The plain text example.
|
||||
*
|
||||
* @type {Component}
|
||||
*/
|
||||
|
||||
class PlainText extends React.Component {
|
||||
|
||||
/**
|
||||
* Deserialize the initial editor state.
|
||||
*
|
||||
* @type {Object}
|
||||
*/
|
||||
|
||||
state = {
|
||||
state: Raw.deserialize(initialState, { terse: true })
|
||||
};
|
||||
|
||||
/**
|
||||
* On change.
|
||||
*
|
||||
* @param {State} state
|
||||
*/
|
||||
|
||||
onChange = (state) => {
|
||||
this.setState({ state })
|
||||
}
|
||||
|
||||
/**
|
||||
* Render the editor.
|
||||
*
|
||||
* @return {Component} component
|
||||
*/
|
||||
|
||||
render = () => {
|
||||
return (
|
||||
<Editor
|
||||
placeholder={'Enter some plain text...'}
|
||||
onChange={this.onChange}
|
||||
renderNode={this.renderNode}
|
||||
state={this.state.state}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a node renderer for a Slate `node`.
|
||||
*
|
||||
* @param {Node} node
|
||||
* @return {Component or Void}
|
||||
*/
|
||||
|
||||
renderNode = (node) => {
|
||||
return NODES[node.type]
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Export.
|
||||
*/
|
||||
|
||||
export default PlainText
|
54
examples/rtl/state.json
Normal file
54
examples/rtl/state.json
Normal file
@@ -0,0 +1,54 @@
|
||||
{
|
||||
"nodes": [
|
||||
{
|
||||
"kind": "block",
|
||||
"type": "paragraph",
|
||||
"nodes": [
|
||||
{
|
||||
"kind": "text",
|
||||
"text": "Slate supports both left-to-right text editing (English, French, etc.) and right-to-left text editing (Arabic, Hebrew, etc.) which it automatically detects. Here's an example featuring excerpts from Khalil Gibran:"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"kind": "block",
|
||||
"type": "block-quote",
|
||||
"nodes": [
|
||||
{
|
||||
"kind": "text",
|
||||
"text": "Et un jeune dit : parle-nous de l'amitié.\nEt il répondit, disant :\nVotre ami est votre besoin qui a trouvé une réponse.\nIl est le champ que vous semez avec amour et moissonnez avec reconnaissance.\nIl est votre table et votre foyer."
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"kind": "block",
|
||||
"type": "block-quote",
|
||||
"nodes": [
|
||||
{
|
||||
"kind": "text",
|
||||
"text": "ثم قال له شاب: هات حدثناعن الصداقة.\nفأجاب و قال:\nإن صديقك هو كفاية حاجاتك.\nهو حقك الذي تزرعه بالمحبة و تحصده بالشكر.\nهو مائدتك و موقدك."
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"kind": "block",
|
||||
"type": "block-quote",
|
||||
"nodes": [
|
||||
{
|
||||
"kind": "text",
|
||||
"text": "And a youth said, \"Speak to us of Friendship.\"\nYour friend is your needs answered.\nHe is your field which you sow with love and reap with thanksgiving.\nAnd he is your board and your fireside."
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"kind": "block",
|
||||
"type": "paragraph",
|
||||
"nodes": [
|
||||
{
|
||||
"kind": "text",
|
||||
"text": "Try it out for yourself!"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
@@ -93,17 +93,20 @@ class Node extends React.Component {
|
||||
.map(child => this.renderNode(child))
|
||||
.toArray()
|
||||
|
||||
// Calcul direction of text
|
||||
const dir = node.textDir
|
||||
|
||||
// Attributes that the developer has to mix into the element in their custom
|
||||
// renderer component.
|
||||
// Attributes that the developer must to mix into the element in their
|
||||
// custom node renderer component.
|
||||
const attributes = {
|
||||
'dir': dir !== 'ltr' ? dir : undefined,
|
||||
'data-key': node.key,
|
||||
'onDragStart': this.onDragStart
|
||||
}
|
||||
|
||||
// If it's a block node with inline children, add the proper `dir` attribute
|
||||
// for text direction.
|
||||
if (node.kind == 'block' && node.nodes.first().kind != 'block') {
|
||||
const direction = node.getTextDirection()
|
||||
if (direction == 'rtl') attributes.dir = 'rtl'
|
||||
}
|
||||
|
||||
return (
|
||||
<Component
|
||||
attributes={attributes}
|
||||
|
@@ -15,7 +15,6 @@ import Inline from './inline'
|
||||
import Node from './node'
|
||||
import Text from './text'
|
||||
import uid from '../utils/uid'
|
||||
import bidi from '../utils/bidi'
|
||||
import Immutable, { Map, List, Record } from 'immutable'
|
||||
|
||||
/**
|
||||
@@ -111,16 +110,6 @@ class Block extends new Record(DEFAULTS) {
|
||||
.join('')
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the text direction.
|
||||
*
|
||||
* @return {String} dir
|
||||
*/
|
||||
|
||||
get textDir() {
|
||||
return bidi.getDirection(this.text)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -8,6 +8,7 @@ import Mark from './mark'
|
||||
import Selection from './selection'
|
||||
import Transforms from './transforms'
|
||||
import Text from './text'
|
||||
import direction from 'direction'
|
||||
import includes from 'lodash/includes'
|
||||
import memoize from '../utils/memoize'
|
||||
import uid from '../utils/uid'
|
||||
@@ -731,6 +732,20 @@ const Node = {
|
||||
})
|
||||
},
|
||||
|
||||
/**
|
||||
* Get the direction of the node's text.
|
||||
*
|
||||
* @return {String} direction
|
||||
*/
|
||||
|
||||
getTextDirection() {
|
||||
const text = this.text
|
||||
const dir = direction(text)
|
||||
return dir == 'neutral'
|
||||
? undefined
|
||||
: dir
|
||||
},
|
||||
|
||||
/**
|
||||
* Recursively get all of the child text nodes in order of appearance.
|
||||
*
|
||||
@@ -1131,6 +1146,7 @@ memoize(Node, [
|
||||
'getPreviousText',
|
||||
'getPreviousBlock',
|
||||
'getTextAtOffset',
|
||||
'getTextDirection',
|
||||
'getTexts',
|
||||
'getTextsAtRange',
|
||||
'hasChild',
|
||||
|
@@ -1,30 +0,0 @@
|
||||
import direction from 'direction'
|
||||
import { Map } from 'immutable'
|
||||
|
||||
let cache = {}
|
||||
|
||||
/**
|
||||
* Get direction of a text
|
||||
* @param {String} text
|
||||
* @return {String} dir?
|
||||
*/
|
||||
|
||||
function getDirection(text) {
|
||||
if (cache[text]) {
|
||||
return cache[text]
|
||||
}
|
||||
|
||||
let dir = direction(text)
|
||||
if (dir === 'neutral') {
|
||||
return
|
||||
}
|
||||
|
||||
return dir
|
||||
}
|
||||
|
||||
|
||||
const bidi = {
|
||||
getDirection
|
||||
}
|
||||
|
||||
export default bidi
|
4
test/rendering/fixtures/nested-text-direction/index.js
Normal file
4
test/rendering/fixtures/nested-text-direction/index.js
Normal file
@@ -0,0 +1,4 @@
|
||||
|
||||
/**
|
||||
* Nothing, pure defaults.
|
||||
*/
|
17
test/rendering/fixtures/nested-text-direction/input.yaml
Normal file
17
test/rendering/fixtures/nested-text-direction/input.yaml
Normal file
@@ -0,0 +1,17 @@
|
||||
|
||||
nodes:
|
||||
- kind: block
|
||||
type: default
|
||||
nodes:
|
||||
- kind: block
|
||||
type: default
|
||||
nodes:
|
||||
- kind: text
|
||||
ranges:
|
||||
- text: "مرحبا بالعالم"
|
||||
- kind: block
|
||||
type: default
|
||||
nodes:
|
||||
- kind: text
|
||||
ranges:
|
||||
- text: "שלום עולם"
|
14
test/rendering/fixtures/nested-text-direction/output.html
Normal file
14
test/rendering/fixtures/nested-text-direction/output.html
Normal file
@@ -0,0 +1,14 @@
|
||||
|
||||
<div contenteditable="true">
|
||||
<div style="position:relative;">
|
||||
<div dir="rtl" style="position:relative;">
|
||||
<span>
|
||||
<span>مرحبا بالعالم</span>
|
||||
</span>
|
||||
</div>
|
||||
<div dir="rtl" style="position:relative;">
|
||||
<span>
|
||||
<span>שלום עולם</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
Reference in New Issue
Block a user