Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemathinc
GitHub Repository: sagemathinc/cocalc
Path: blob/master/src/packages/frontend/editors/slate/elements/math/math-widget.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
// Katex support -- NOTE: this import of katex is pretty LARGE.
7
import "katex/dist/katex.min.css";
8
9
// Everything else.
10
import {
11
React,
12
useEffect,
13
useFrameContext,
14
useRef,
15
useState,
16
} from "@cocalc/frontend/app-framework";
17
import { SlateCodeMirror } from "../codemirror";
18
import { useFocused, useSelected } from "../../slate-react";
19
import { useCollapsed } from "../hooks";
20
import { StaticElement } from "./index";
21
import { Popover } from "antd";
22
import { Icon } from "@cocalc/frontend/components/icon";
23
24
interface Props {
25
value: string;
26
isInline: boolean;
27
onChange?: (string) => void;
28
}
29
30
export const SlateMath: React.FC<Props> = React.memo(
31
({ value, onChange, isInline }) => {
32
const [editMode, setEditMode] = useState<boolean>(false);
33
const frameContext = useFrameContext();
34
const justBlurred = useRef<boolean>(false);
35
36
const focused = useFocused();
37
const selected = useSelected();
38
const collapsed = useCollapsed();
39
40
useEffect(() => {
41
if (focused && selected && collapsed && !justBlurred.current) {
42
setEditMode(true);
43
}
44
}, [selected, focused, collapsed]);
45
46
function renderLaTeX() {
47
const Element = isInline ? "span" : "div";
48
return (
49
<Element
50
style={
51
editMode
52
? {
53
color: "#337ab7",
54
border: "1px solid #337ab7",
55
borderRadius: "8px",
56
...(isInline
57
? { margin: "-7px -3px", padding: "6px 2px" }
58
: undefined),
59
}
60
: !isInline
61
? { border: "1px solid transparent" }
62
: undefined
63
}
64
onClick={async (e) => {
65
e.preventDefault();
66
e.stopPropagation();
67
// switch to edit mode when you click on it.
68
setEditMode?.(true);
69
// also make the frame containing this active... if we're in a frame editor (hence the ?. !)
70
frameContext.actions?.set_active_id?.(frameContext.id);
71
}}
72
>
73
{/* below since we are abusing the StaticElement component a bit */}
74
<StaticElement
75
element={
76
{ value, type: isInline ? "math_inline" : "math_block" } as any
77
}
78
children={undefined}
79
attributes={{} as any}
80
/>
81
</Element>
82
);
83
}
84
// putting renderEditMode before is critical since as we type, length of formula changes,
85
// and default would move the popover as we type, which is horrible
86
87
// !frameContext.project_id is so that this also works when using editor outside of any
88
// particular project.
89
const open =
90
editMode &&
91
((frameContext.isFocused && frameContext.isVisible) ||
92
!frameContext.project_id);
93
94
return (
95
<span contentEditable={false} style={{ cursor: "pointer" }}>
96
<Popover
97
open={open}
98
destroyOnHidden
99
title={
100
<>
101
<Icon name="pencil" style={{ marginRight: "5px" }} />{" "}
102
{isInline ? "Inline" : "Display"} LaTeX Mathematics
103
</>
104
}
105
content={() => (
106
<SlateCodeMirror
107
style={{ maxWidth: "90vw", width: "700px" }}
108
value={value}
109
onChange={(value) => {
110
onChange?.(value.trim().replace(/^\s*[\r\n]/gm, ""));
111
}}
112
onBlur={() => {
113
justBlurred.current = true;
114
setTimeout(() => {
115
justBlurred.current = false;
116
}, 1);
117
setEditMode(false);
118
}}
119
info="tex"
120
options={{
121
lineWrapping: true,
122
autofocus: true,
123
}}
124
isInline={true}
125
/>
126
)}
127
>
128
{renderLaTeX()}
129
</Popover>
130
</span>
131
);
132
},
133
);
134
135