Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemathinc
GitHub Repository: sagemathinc/cocalc
Path: blob/master/src/packages/frontend/editors/slate/elements/paragraph/editable.tsx
1698 views
1
/*
2
* This file is part of CoCalc: Copyright © 2020 Sagemath, Inc.
3
* License: MS-RSL – see LICENSE.md for details
4
*/
5
6
import { register } from "../register";
7
import { useFocused, useSelected, useCollapsed } from "../hooks";
8
9
register({
10
slateType: "paragraph",
11
12
Element: ({ attributes, children, element }) => {
13
if (element.type != "paragraph") throw Error("bug");
14
15
// All this complexity is because we only show empty paragraphs
16
// when the cursor is in them, since we create them dynamically in
17
// order to work around a fundamental shortcoming in the design
18
// of slatejs wrt cursor navigation (e.g., you can't move the cursor
19
// between block voids or before various elements at the beginning
20
// of a document such as bulleted lists).
21
const focused = useFocused();
22
const selected = useSelected();
23
const collapsed = useCollapsed();
24
const isEmpty =
25
element.children.length == 1 && element.children[0]["text"] == "";
26
if (isEmpty && !(collapsed && focused && selected)) {
27
// Only show empty paragraph if selection is collapsed, editor is
28
// focused, and para is selected.
29
return (
30
<span {...attributes} style={{ position: "absolute" }}>
31
{children}
32
</span>
33
);
34
}
35
36
// Below the textIndent: 0 is needed due to task lists -- see slate/elements/list/list-item.tsx
37
38
if (hasImageAsChild(element)) {
39
// We use a div in this case, since our image renderer resize functionality
40
// (via the re-resizer packages) uses divs, and divs are not allowed inside
41
// of paragraphs.
42
return (
43
<div {...attributes}>
44
<span style={{ textIndent: 0 }}>{children}</span>
45
</div>
46
);
47
}
48
49
// Normal paragraph rendering.
50
return (
51
<p {...attributes}>
52
<span style={{ textIndent: 0 }}>{children}</span>
53
</p>
54
);
55
56
/*
57
// I wish I could just use a div instead of a p because
58
// you can't have
59
// any div's inside of a p, and things like image resize use
60
// div's under the hood in the implementation.
61
// However, there are rules (e.g., from bootstrap's type.less)
62
// like this
63
// blockquote {... p { &:last-child { margin-bottom: 0; } }
64
// so, e.g., a paragraph in a quote doesn't have that extra
65
// bottom margin. That's a lot more work to re-implement
66
// using a div...
67
return (
68
<div {...attributes} style={{ marginBottom: "1em" }}>
69
{children}
70
</div>
71
);
72
*/
73
},
74
75
fromSlate: ({ children, info }) => {
76
if (children.trim() == "") {
77
// We discard empty paragraphs entirely, since that's
78
// what markdown does. Also, to make void blocks easier to
79
// work with, we sometimes automatically add blank paragraphs
80
// above or below them, and it is silly if those result in
81
// lots of meaningless blank lines in the md file.
82
return "";
83
}
84
85
// trimLeft is because prettier (say) strips whitespace from beginning of paragraphs.
86
const s = children.trimLeft() + "\n";
87
if (info.lastChild || info.parent?.type == "list_item") return s;
88
return s + "\n";
89
},
90
});
91
92
function hasImageAsChild(element): boolean {
93
for (const x of element.children) {
94
if (x["type"] == "image") return true;
95
}
96
return false;
97
}
98
99