Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemathinc
GitHub Repository: sagemathinc/cocalc
Path: blob/master/src/packages/frontend/editors/slate/slate-react/components/string.tsx
1698 views
1
import { Editor, Text, Path, Element, Node } from "slate";
2
import { ReactEditor, useSlateStatic } from "..";
3
4
/**
5
* Leaf content strings.
6
*/
7
8
const String = (props: {
9
isLast: boolean;
10
leaf: Text;
11
parent: Element;
12
text: Text;
13
}) => {
14
const { isLast, leaf, parent, text } = props;
15
const editor = useSlateStatic();
16
let path;
17
try {
18
path = ReactEditor.findPath(editor, text);
19
} catch (err) {
20
console.warn("WARNING: String -- unable to find path to node", text, err);
21
return <ZeroWidthString />;
22
}
23
const parentPath = Path.parent(path);
24
25
// COMPAT: Render text inside void nodes with a zero-width space.
26
// So the node can contain selection but the text is not visible.
27
if (editor.isVoid(parent)) {
28
return <ZeroWidthString length={Node.string(parent).length} />;
29
}
30
31
// COMPAT: If this is the last text node in an empty block, render a zero-
32
// width space that will convert into a line break when copying and pasting
33
// to support expected plain text.
34
if (
35
leaf.text === "" &&
36
parent.children[parent.children.length - 1] === text &&
37
!editor.isInline(parent) &&
38
Editor.string(editor, parentPath) === ""
39
) {
40
return <ZeroWidthString isLineBreak />;
41
}
42
43
// COMPAT: If the text is empty, it's because it's on the edge of an inline
44
// node, so we render a zero-width space so that the selection can be
45
// inserted next to it still.
46
if (leaf.text === "") {
47
return <ZeroWidthString />;
48
}
49
50
// COMPAT: Browsers will collapse trailing new lines at the end of blocks,
51
// so we need to add an extra trailing new lines to prevent that.
52
if (isLast && leaf.text.slice(-1) === "\n") {
53
return <TextString isTrailing text={leaf.text} />;
54
}
55
56
return <TextString text={leaf.text} />;
57
};
58
59
/**
60
* Leaf strings with text in them.
61
*/
62
63
const TextString = (props: { text: string; isTrailing?: boolean }) => {
64
const { text, isTrailing = false } = props;
65
return (
66
<span data-slate-string>
67
{text}
68
{isTrailing ? "\n" : null}
69
</span>
70
);
71
};
72
73
/**
74
75
Leaf strings without text, render as zero-width strings... or do they? See below:
76
77
The style below is a hack to workaround a bug when using Chrome, which doesn't happen on Firefox or Safari.
78
The solution below is inspired by https://stackoverflow.com/questions/25897883/edit-cursor-not-displayed-on-chrome-in-contenteditable
79
Here's how to reproduce the bug in cocalc and the style below removed.
80
81
1. Open a new blank doc with markdown source on the left and slate on the right.
82
2. You can click either side and it focuses and shows a cursor.
83
3. Click in the right slate side, then click the x to close the *left hand* markdown source.
84
4. Broken -- no matter where you click, you can't get the slate editor to show a cursor (except on firefox and safari it works).
85
86
The workaround of rendering a ZeroWidthString as actually 1px in width and display inline block,
87
evidently gives the cursor somewhere to be in the case of an empty document. It seems harmless to
88
leave this 1px width even for nonempty documents.
89
*/
90
91
const ZeroWidthString = (props: { length?: number; isLineBreak?: boolean }) => {
92
const { length = 0, isLineBreak = false } = props;
93
return (
94
<span
95
data-slate-zero-width={isLineBreak ? "n" : "z"}
96
data-slate-length={length}
97
style={
98
/* see note above! */
99
{
100
display: "inline-block",
101
width: "1px",
102
textIndent: 0 /* This is needed to offset textIndex that is used in task list items!
103
This example breaks otherwise, if you put the cursor in the third bullet point and move up to previous line in the link.
104
105
- x
106
- [ ] y wstein.org
107
-
108
*/,
109
}
110
}
111
>
112
{"\uFEFF"}
113
{isLineBreak ? <br /> : null}
114
</span>
115
);
116
};
117
118
export default String;
119
120