mirror of
https://github.com/ianstormtaylor/slate.git
synced 2025-01-18 22:08:18 +01:00
more work on richtext
This commit is contained in:
parent
7435c4019c
commit
e293c9794c
@ -15,6 +15,34 @@ p {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
p + p {
|
||||
.editor p + p {
|
||||
margin-top: 1em;
|
||||
}
|
||||
|
||||
.menu {
|
||||
margin: 0 -10px;
|
||||
padding: 1px 0 9px 7px;
|
||||
border-bottom: 2px solid #eee;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.menu > * {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.menu > * + * {
|
||||
margin-left: 10px;
|
||||
}
|
||||
|
||||
.button {
|
||||
color: #ccc;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.button[data-active="true"] {
|
||||
color: black;
|
||||
}
|
||||
|
||||
.material-icons {
|
||||
font-size: 18px;
|
||||
}
|
||||
|
@ -2,6 +2,7 @@
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<title>Editor | Richtext Example</title>
|
||||
<link rel="stylesheet" href="https://fonts.googleapis.com/icon?family=Material+Icons">
|
||||
<link rel="stylesheet" href="index.css">
|
||||
</head>
|
||||
<body>
|
||||
|
@ -17,18 +17,7 @@ const state = {
|
||||
type: 'text',
|
||||
ranges: [
|
||||
{
|
||||
text: 'This is '
|
||||
},
|
||||
{
|
||||
text: 'editable',
|
||||
marks: [
|
||||
{
|
||||
type: 'italic'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
text: ' '
|
||||
text: 'This is editable '
|
||||
},
|
||||
{
|
||||
text: 'rich',
|
||||
@ -39,7 +28,18 @@ const state = {
|
||||
]
|
||||
},
|
||||
{
|
||||
text: ' text, much better than a '
|
||||
text: ' text, '
|
||||
},
|
||||
{
|
||||
text: 'much',
|
||||
marks: [
|
||||
{
|
||||
type: 'italic'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
text: ' better than a '
|
||||
},
|
||||
{
|
||||
text: '<textarea>',
|
||||
@ -69,18 +69,77 @@ class App extends React.Component {
|
||||
state: Raw.deserialize(state)
|
||||
};
|
||||
|
||||
isMarkActive(type) {
|
||||
const { state } = this.state
|
||||
const { document, selection } = state
|
||||
const { startKey, startOffset } = selection
|
||||
const startNode = document.getNode(startKey)
|
||||
if (!startNode) return false
|
||||
|
||||
const { characters } = startNode
|
||||
const character = characters.get(startOffset)
|
||||
const { marks } = character
|
||||
return marks.some(mark => mark.type == type)
|
||||
}
|
||||
|
||||
onClickMark(e, type) {
|
||||
e.preventDefault()
|
||||
|
||||
let { state } = this.state
|
||||
const { marks } = state
|
||||
const isActive = this.isMarkActive(type)
|
||||
|
||||
state = state
|
||||
.transform()
|
||||
[isActive ? 'unmark' : 'mark']()
|
||||
.apply()
|
||||
|
||||
this.onChange(state)
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<Editor
|
||||
state={this.state.state}
|
||||
renderNode={node => this.renderNode(node)}
|
||||
renderMark={mark => this.renderMark(mark)}
|
||||
onChange={(state) => {
|
||||
console.log('Document:', state.document.toJS())
|
||||
console.log('Content:', Raw.serialize(state))
|
||||
this.setState({ state })
|
||||
}}
|
||||
/>
|
||||
<div>
|
||||
{this.renderToolbar()}
|
||||
{this.renderEditor()}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
renderToolbar() {
|
||||
const isBold = this.isMarkActive('bold')
|
||||
const isItalic = this.isMarkActive('italic')
|
||||
const isCode = this.isMarkActive('code')
|
||||
|
||||
return (
|
||||
<div className="menu">
|
||||
<span className="button" onClick={e => this.onClickMark(e, 'bold')} data-active={isBold}>
|
||||
<span className="material-icons">format_bold</span>
|
||||
</span>
|
||||
<span className="button" onClick={e => this.onClickMark(e, 'italic')} data-active={isItalic}>
|
||||
<span className="material-icons">format_italic</span>
|
||||
</span>
|
||||
<span className="button" onClick={e => this.onClickMark(e, 'code')} data-active={isCode}>
|
||||
<span className="material-icons">code</span>
|
||||
</span>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
renderEditor() {
|
||||
return (
|
||||
<div className="editor">
|
||||
<Editor
|
||||
state={this.state.state}
|
||||
renderNode={node => this.renderNode(node)}
|
||||
renderMark={mark => this.renderMark(mark)}
|
||||
onChange={(state) => {
|
||||
console.log('Document:', state.document.toJS())
|
||||
console.log('Content:', Raw.serialize(state))
|
||||
this.setState({ state })
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -39,7 +39,7 @@ class Text extends React.Component {
|
||||
: ranges.map((range, i, ranges) => {
|
||||
const previous = ranges.slice(0, i)
|
||||
const offset = previous.size
|
||||
? previous.map(range => range.get('text')).join('').length
|
||||
? previous.map(range => range.text).join('').length
|
||||
: 0
|
||||
return this.renderLeaf(range, offset)
|
||||
})
|
||||
@ -47,8 +47,8 @@ class Text extends React.Component {
|
||||
|
||||
renderLeaf(range, offset) {
|
||||
const { node, renderMark, state } = this.props
|
||||
const text = range.get('text')
|
||||
const marks = range.get('marks')
|
||||
const text = range.text
|
||||
const marks = range.marks
|
||||
const start = offset
|
||||
const end = offset + text.length
|
||||
const offsetKey = OffsetKey.stringify({
|
||||
|
@ -64,7 +64,7 @@ function serializeCharacters(characters) {
|
||||
.map((range) => {
|
||||
return {
|
||||
text: range.text,
|
||||
mark: serializeMark(range.mark)
|
||||
marks: range.marks.map(serializeMark)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
@ -1,5 +1,14 @@
|
||||
|
||||
import { List, Map } from 'immutable'
|
||||
import { List, Map, Record } from 'immutable'
|
||||
|
||||
/**
|
||||
* Range.
|
||||
*/
|
||||
|
||||
const Range = new Record({
|
||||
text: '',
|
||||
marks: new List()
|
||||
})
|
||||
|
||||
/**
|
||||
* Group a list of `characters` into ranges by the marks they have.
|
||||
@ -16,7 +25,7 @@ function groupByMarks(characters) {
|
||||
|
||||
// The first one can always just be created.
|
||||
if (i == 0) {
|
||||
return ranges.push(new Map({ text, marks }))
|
||||
return ranges.push(new Range({ text, marks }))
|
||||
}
|
||||
|
||||
// Otherwise, compare to the previous and see if a new range should be
|
||||
@ -37,7 +46,7 @@ function groupByMarks(characters) {
|
||||
}
|
||||
|
||||
// Otherwise, create a new range.
|
||||
return ranges.push(new Map({ text, marks }))
|
||||
return ranges.push(new Range({ text, marks }))
|
||||
}, new List())
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user