From cc7f0352297461564937f2b0b5fba0a0baa2232b Mon Sep 17 00:00:00 2001 From: Sunny Hirai Date: Wed, 18 Dec 2019 18:18:35 -0800 Subject: [PATCH 1/4] Added a warning for Android in faq (#3356) --- docs/general/faq.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/general/faq.md b/docs/general/faq.md index fc9296b84..11b3cb802 100644 --- a/docs/general/faq.md +++ b/docs/general/faq.md @@ -18,6 +18,6 @@ Since Slate knows nothing about your domain, it can't know how to parse pasted H Slate's goal is to support all the modern browsers on both desktop and mobile devices. -However, right now Slate is in beta and is community-driven, so its support is not as robust as it could be. It's currently tested against the latest few versions of Chrome, Edge, Firefox and Safari on desktops. It is not regularly tested on mobile devices. And it does not work in Internet Explorer. If you want to add more browser or device support, we'd love for you to submit a pull request! Or in the case of incompatible browsers, build a plugin. +However, right now Slate is in beta and is community-driven, so its support is not as robust as it could be. It's currently tested against the latest few versions of Chrome, Edge, Firefox and Safari on desktops. And it does not work in Internet Explorer. On mobile, iOS devices are supported but not regularly tested. Chrome on Android is supported on Slate 0.47 but is not currently supported in Slate 0.50+. If you want to add more browser or device support, we'd love for you to submit a pull request! Or in the case of incompatible browsers, build a plugin. For older browsers, such as IE11, a lot of the now standard native APIs aren't available. Slate's position on this is that it is up to the user to bring polyfills (like https://polyfill.io) when needed for things like `el.closest`, etc. Otherwise we'd have to bundle and maintain lots of polyfills that others may not even need in the first place. From 3ffd73cd7d133e736d011693fbf7de92d07a6c58 Mon Sep 17 00:00:00 2001 From: Sunny Hirai Date: Wed, 18 Dec 2019 18:23:05 -0800 Subject: [PATCH 2/4] Fix broken link in introduction of docs (#3357) --- docs/Introduction.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/Introduction.md b/docs/Introduction.md index 5ca044216..0cbe2371f 100644 --- a/docs/Introduction.md +++ b/docs/Introduction.md @@ -76,7 +76,7 @@ If you have an idea for an example that shows a common use case, pull request it ## Documentation -If you're using Slate for the first time, check out the [Getting Started](http://docs.slatejs.org/walkthroughs/installing-slate) walkthroughs and the [Concepts](http://docs.slatejs.org/concepts) to familiarize yourself with Slate's architecture and mental models. +If you're using Slate for the first time, check out the [Getting Started](http://docs.slatejs.org/walkthroughs/01-installing-slate) walkthroughs and the [Concepts](http://docs.slatejs.org/concepts) to familiarize yourself with Slate's architecture and mental models. - [**Walkthroughs**](http://docs.slatejs.org/walkthroughs) - [**Concepts**](http://docs.slatejs.org/concepts) From e54b07eba8ab23a8c26b275f77e8ec207f8c9b55 Mon Sep 17 00:00:00 2001 From: "Corentin.Andre" Date: Thu, 19 Dec 2019 17:50:32 +0100 Subject: [PATCH 3/4] fix: undoing and redoing are switched (#3363) --- packages/slate-react/src/components/editable.tsx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/slate-react/src/components/editable.tsx b/packages/slate-react/src/components/editable.tsx index b1ed6c1d4..ad3738e1d 100644 --- a/packages/slate-react/src/components/editable.tsx +++ b/packages/slate-react/src/components/editable.tsx @@ -697,8 +697,8 @@ export const Editable = (props: EditableProps) => { if (Hotkeys.isRedo(nativeEvent)) { event.preventDefault() - if (editor.undo) { - editor.undo() + if (editor.redo) { + editor.redo() } return @@ -707,8 +707,8 @@ export const Editable = (props: EditableProps) => { if (Hotkeys.isUndo(nativeEvent)) { event.preventDefault() - if (editor.redo) { - editor.redo() + if (editor.undo) { + editor.undo() } return From 5418e260d46853df527c3db0ff1b1b82f0ca3060 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johan=20S=C3=B6rlin?= Date: Thu, 19 Dec 2019 17:51:44 +0100 Subject: [PATCH 4/4] moved getLeaves from react to slate core package and added some basic tests (#3358) --- packages/slate-react/src/components/text.tsx | 75 +------------------ packages/slate/src/interfaces/text.ts | 75 ++++++++++++++++++- .../test/interfaces/Text/decorations/end.js | 31 ++++++++ .../interfaces/Text/decorations/middle.js | 35 +++++++++ .../Text/decorations/overlapping.js | 49 ++++++++++++ .../test/interfaces/Text/decorations/start.js | 31 ++++++++ 6 files changed, 221 insertions(+), 75 deletions(-) create mode 100644 packages/slate/test/interfaces/Text/decorations/end.js create mode 100644 packages/slate/test/interfaces/Text/decorations/middle.js create mode 100644 packages/slate/test/interfaces/Text/decorations/overlapping.js create mode 100644 packages/slate/test/interfaces/Text/decorations/start.js diff --git a/packages/slate-react/src/components/text.tsx b/packages/slate-react/src/components/text.tsx index 6f7c5b139..4778ab775 100644 --- a/packages/slate-react/src/components/text.tsx +++ b/packages/slate-react/src/components/text.tsx @@ -25,7 +25,7 @@ const Text = (props: { const { decorations, isLast, parent, renderLeaf, text } = props const editor = useEditor() const ref = useRef(null) - const leaves = getLeaves(text, decorations) + const leaves = SlateText.decorations(text, decorations) const key = ReactEditor.findKey(editor, text) 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) => { return ( next.parent === prev.parent && diff --git a/packages/slate/src/interfaces/text.ts b/packages/slate/src/interfaces/text.ts index a1e178e90..80ca65f22 100755 --- a/packages/slate/src/interfaces/text.ts +++ b/packages/slate/src/interfaces/text.ts @@ -1,5 +1,5 @@ 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 @@ -83,4 +83,77 @@ export const Text = { 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 + }, } diff --git a/packages/slate/test/interfaces/Text/decorations/end.js b/packages/slate/test/interfaces/Text/decorations/end.js new file mode 100644 index 000000000..dadd76c7f --- /dev/null +++ b/packages/slate/test/interfaces/Text/decorations/end.js @@ -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', + }, +] diff --git a/packages/slate/test/interfaces/Text/decorations/middle.js b/packages/slate/test/interfaces/Text/decorations/middle.js new file mode 100644 index 000000000..983a61297 --- /dev/null +++ b/packages/slate/test/interfaces/Text/decorations/middle.js @@ -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', + }, +] diff --git a/packages/slate/test/interfaces/Text/decorations/overlapping.js b/packages/slate/test/interfaces/Text/decorations/overlapping.js new file mode 100644 index 000000000..2dd83d785 --- /dev/null +++ b/packages/slate/test/interfaces/Text/decorations/overlapping.js @@ -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', + }, +] diff --git a/packages/slate/test/interfaces/Text/decorations/start.js b/packages/slate/test/interfaces/Text/decorations/start.js new file mode 100644 index 000000000..53db2dfe4 --- /dev/null +++ b/packages/slate/test/interfaces/Text/decorations/start.js @@ -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', + }, +]