mirror of
https://github.com/ianstormtaylor/slate.git
synced 2025-08-17 20:51:20 +02:00
moved getLeaves from react to slate core package and added some basic tests (#3358)
This commit is contained in:
committed by
Ian Storm Taylor
parent
e54b07eba8
commit
5418e260d4
@@ -25,7 +25,7 @@ const Text = (props: {
|
|||||||
const { decorations, isLast, parent, renderLeaf, text } = props
|
const { decorations, isLast, parent, renderLeaf, text } = props
|
||||||
const editor = useEditor()
|
const editor = useEditor()
|
||||||
const ref = useRef<HTMLSpanElement>(null)
|
const ref = useRef<HTMLSpanElement>(null)
|
||||||
const leaves = getLeaves(text, decorations)
|
const leaves = SlateText.decorations(text, decorations)
|
||||||
const key = ReactEditor.findKey(editor, text)
|
const key = ReactEditor.findKey(editor, text)
|
||||||
const children = []
|
const children = []
|
||||||
|
|
||||||
@@ -63,79 +63,6 @@ const Text = (props: {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the leaves for a text node given decorations.
|
|
||||||
*/
|
|
||||||
|
|
||||||
const getLeaves = (node: SlateText, decorations: Range[]): SlateText[] => {
|
|
||||||
let leaves: SlateText[] = [{ ...node }]
|
|
||||||
|
|
||||||
for (const dec of decorations) {
|
|
||||||
const { anchor, focus, ...rest } = dec
|
|
||||||
const [start, end] = Range.edges(dec)
|
|
||||||
const next = []
|
|
||||||
let o = 0
|
|
||||||
|
|
||||||
for (const leaf of leaves) {
|
|
||||||
const { length } = leaf.text
|
|
||||||
const offset = o
|
|
||||||
o += length
|
|
||||||
|
|
||||||
// If the range encompases the entire leaf, add the range.
|
|
||||||
if (start.offset <= offset && end.offset >= offset + length) {
|
|
||||||
Object.assign(leaf, rest)
|
|
||||||
next.push(leaf)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
// If the range starts after the leaf, or ends before it, continue.
|
|
||||||
if (
|
|
||||||
start.offset > offset + length ||
|
|
||||||
end.offset < offset ||
|
|
||||||
(end.offset === offset && offset !== 0)
|
|
||||||
) {
|
|
||||||
next.push(leaf)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
// Otherwise we need to split the leaf, at the start, end, or both,
|
|
||||||
// and add the range to the middle intersecting section. Do the end
|
|
||||||
// split first since we don't need to update the offset that way.
|
|
||||||
let middle = leaf
|
|
||||||
let before
|
|
||||||
let after
|
|
||||||
|
|
||||||
if (end.offset < offset + length) {
|
|
||||||
const off = end.offset - offset
|
|
||||||
after = { ...middle, text: middle.text.slice(off) }
|
|
||||||
middle = { ...middle, text: middle.text.slice(0, off) }
|
|
||||||
}
|
|
||||||
|
|
||||||
if (start.offset > offset) {
|
|
||||||
const off = start.offset - offset
|
|
||||||
before = { ...middle, text: middle.text.slice(0, off) }
|
|
||||||
middle = { ...middle, text: middle.text.slice(off) }
|
|
||||||
}
|
|
||||||
|
|
||||||
Object.assign(middle, rest)
|
|
||||||
|
|
||||||
if (before) {
|
|
||||||
next.push(before)
|
|
||||||
}
|
|
||||||
|
|
||||||
next.push(middle)
|
|
||||||
|
|
||||||
if (after) {
|
|
||||||
next.push(after)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
leaves = next
|
|
||||||
}
|
|
||||||
|
|
||||||
return leaves
|
|
||||||
}
|
|
||||||
|
|
||||||
const MemoizedText = React.memo(Text, (prev, next) => {
|
const MemoizedText = React.memo(Text, (prev, next) => {
|
||||||
return (
|
return (
|
||||||
next.parent === prev.parent &&
|
next.parent === prev.parent &&
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
import isPlainObject from 'is-plain-object'
|
import isPlainObject from 'is-plain-object'
|
||||||
import { Path } from '..'
|
import { Range } from '..'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* `Text` objects represent the nodes that contain the actual text content of a
|
* `Text` objects represent the nodes that contain the actual text content of a
|
||||||
@@ -83,4 +83,77 @@ export const Text = {
|
|||||||
|
|
||||||
return true
|
return true
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the leaves for a text node given decorations.
|
||||||
|
*/
|
||||||
|
|
||||||
|
decorations(node: Text, decorations: Range[]): Text[] {
|
||||||
|
let leaves: Text[] = [{ ...node }]
|
||||||
|
|
||||||
|
for (const dec of decorations) {
|
||||||
|
const { anchor, focus, ...rest } = dec
|
||||||
|
const [start, end] = Range.edges(dec)
|
||||||
|
const next = []
|
||||||
|
let o = 0
|
||||||
|
|
||||||
|
for (const leaf of leaves) {
|
||||||
|
const { length } = leaf.text
|
||||||
|
const offset = o
|
||||||
|
o += length
|
||||||
|
|
||||||
|
// If the range encompases the entire leaf, add the range.
|
||||||
|
if (start.offset <= offset && end.offset >= offset + length) {
|
||||||
|
Object.assign(leaf, rest)
|
||||||
|
next.push(leaf)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the range starts after the leaf, or ends before it, continue.
|
||||||
|
if (
|
||||||
|
start.offset > offset + length ||
|
||||||
|
end.offset < offset ||
|
||||||
|
(end.offset === offset && offset !== 0)
|
||||||
|
) {
|
||||||
|
next.push(leaf)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// Otherwise we need to split the leaf, at the start, end, or both,
|
||||||
|
// and add the range to the middle intersecting section. Do the end
|
||||||
|
// split first since we don't need to update the offset that way.
|
||||||
|
let middle = leaf
|
||||||
|
let before
|
||||||
|
let after
|
||||||
|
|
||||||
|
if (end.offset < offset + length) {
|
||||||
|
const off = end.offset - offset
|
||||||
|
after = { ...middle, text: middle.text.slice(off) }
|
||||||
|
middle = { ...middle, text: middle.text.slice(0, off) }
|
||||||
|
}
|
||||||
|
|
||||||
|
if (start.offset > offset) {
|
||||||
|
const off = start.offset - offset
|
||||||
|
before = { ...middle, text: middle.text.slice(0, off) }
|
||||||
|
middle = { ...middle, text: middle.text.slice(off) }
|
||||||
|
}
|
||||||
|
|
||||||
|
Object.assign(middle, rest)
|
||||||
|
|
||||||
|
if (before) {
|
||||||
|
next.push(before)
|
||||||
|
}
|
||||||
|
|
||||||
|
next.push(middle)
|
||||||
|
|
||||||
|
if (after) {
|
||||||
|
next.push(after)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
leaves = next
|
||||||
|
}
|
||||||
|
|
||||||
|
return leaves
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
31
packages/slate/test/interfaces/Text/decorations/end.js
Normal file
31
packages/slate/test/interfaces/Text/decorations/end.js
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
import { Text } from 'slate'
|
||||||
|
|
||||||
|
export const input = [
|
||||||
|
{
|
||||||
|
anchor: {
|
||||||
|
path: [0],
|
||||||
|
offset: 2,
|
||||||
|
},
|
||||||
|
focus: {
|
||||||
|
path: [0],
|
||||||
|
offset: 3,
|
||||||
|
},
|
||||||
|
decoration: 'decoration',
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
export const test = decorations => {
|
||||||
|
return Text.decorations({ text: 'abc', mark: 'mark' }, decorations)
|
||||||
|
}
|
||||||
|
|
||||||
|
export const output = [
|
||||||
|
{
|
||||||
|
text: 'ab',
|
||||||
|
mark: 'mark',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: 'c',
|
||||||
|
mark: 'mark',
|
||||||
|
decoration: 'decoration',
|
||||||
|
},
|
||||||
|
]
|
35
packages/slate/test/interfaces/Text/decorations/middle.js
Normal file
35
packages/slate/test/interfaces/Text/decorations/middle.js
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
import { Text } from 'slate'
|
||||||
|
|
||||||
|
export const input = [
|
||||||
|
{
|
||||||
|
anchor: {
|
||||||
|
path: [0],
|
||||||
|
offset: 1,
|
||||||
|
},
|
||||||
|
focus: {
|
||||||
|
path: [0],
|
||||||
|
offset: 2,
|
||||||
|
},
|
||||||
|
decoration: 'decoration',
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
export const test = decorations => {
|
||||||
|
return Text.decorations({ text: 'abc', mark: 'mark' }, decorations)
|
||||||
|
}
|
||||||
|
|
||||||
|
export const output = [
|
||||||
|
{
|
||||||
|
text: 'a',
|
||||||
|
mark: 'mark',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: 'b',
|
||||||
|
mark: 'mark',
|
||||||
|
decoration: 'decoration',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: 'c',
|
||||||
|
mark: 'mark',
|
||||||
|
},
|
||||||
|
]
|
@@ -0,0 +1,49 @@
|
|||||||
|
import { Text } from 'slate'
|
||||||
|
|
||||||
|
export const input = [
|
||||||
|
{
|
||||||
|
anchor: {
|
||||||
|
path: [0],
|
||||||
|
offset: 1,
|
||||||
|
},
|
||||||
|
focus: {
|
||||||
|
path: [0],
|
||||||
|
offset: 2,
|
||||||
|
},
|
||||||
|
decoration1: 'decoration1',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
anchor: {
|
||||||
|
path: [0],
|
||||||
|
offset: 0,
|
||||||
|
},
|
||||||
|
focus: {
|
||||||
|
path: [0],
|
||||||
|
offset: 3,
|
||||||
|
},
|
||||||
|
decoration2: 'decoration2',
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
export const test = decorations => {
|
||||||
|
return Text.decorations({ text: 'abc', mark: 'mark' }, decorations)
|
||||||
|
}
|
||||||
|
|
||||||
|
export const output = [
|
||||||
|
{
|
||||||
|
text: 'a',
|
||||||
|
mark: 'mark',
|
||||||
|
decoration2: 'decoration2',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: 'b',
|
||||||
|
mark: 'mark',
|
||||||
|
decoration1: 'decoration1',
|
||||||
|
decoration2: 'decoration2',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: 'c',
|
||||||
|
mark: 'mark',
|
||||||
|
decoration2: 'decoration2',
|
||||||
|
},
|
||||||
|
]
|
31
packages/slate/test/interfaces/Text/decorations/start.js
Normal file
31
packages/slate/test/interfaces/Text/decorations/start.js
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
import { Text } from 'slate'
|
||||||
|
|
||||||
|
export const input = [
|
||||||
|
{
|
||||||
|
anchor: {
|
||||||
|
path: [0],
|
||||||
|
offset: 0,
|
||||||
|
},
|
||||||
|
focus: {
|
||||||
|
path: [0],
|
||||||
|
offset: 1,
|
||||||
|
},
|
||||||
|
decoration: 'decoration',
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
export const test = decorations => {
|
||||||
|
return Text.decorations({ text: 'abc', mark: 'mark' }, decorations)
|
||||||
|
}
|
||||||
|
|
||||||
|
export const output = [
|
||||||
|
{
|
||||||
|
text: 'a',
|
||||||
|
mark: 'mark',
|
||||||
|
decoration: 'decoration',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: 'bc',
|
||||||
|
mark: 'mark',
|
||||||
|
},
|
||||||
|
]
|
Reference in New Issue
Block a user