Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemathinc
GitHub Repository: sagemathinc/cocalc
Path: blob/master/src/packages/frontend/editors/slate/elements/register.ts
5929 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 type { RenderElementProps } from "../slate-react";
7
export type { RenderElementProps } from "../slate-react";
8
import React from "react";
9
import { Descendant, Element } from "slate";
10
import { State as MarkdownParserState, Token } from "../markdown-to-slate";
11
import { Info } from "../slate-to-markdown";
12
import { ChildInfo } from "../element-to-markdown";
13
14
export interface SlateElement {
15
children: Descendant[];
16
}
17
18
export interface markdownToSlateOptions {
19
type: string;
20
token: Token;
21
state: MarkdownParserState;
22
children: Node[];
23
isEmpty: boolean;
24
}
25
26
export interface slateToMarkdownOptions {
27
node: Node;
28
children: string;
29
info: Info;
30
childInfo: ChildInfo;
31
cache?;
32
}
33
34
type markdownToSlateFunction = (markdownToSlateOptions) => Element | undefined;
35
36
type slateToMarkdownFunction = (slateToMarkdownOptions) => string;
37
38
interface SizeEstimatorOptions {
39
node: Node;
40
fontSize: number; // unit of pixels
41
}
42
43
type sizeEstimatorFunction = (SizeEstimatorOptions) => number | undefined;
44
45
// This hook is called before the children of the node are serialized.
46
// Use this to mutate childInfo and add in extra information that the
47
// parent can deduce, but the children can't, since they have no way
48
// to get at the parent.
49
type childInfoHookFunction = (opts: {
50
node: Element;
51
childInfo: ChildInfo;
52
}) => void;
53
54
// Rules of behavior for slate specific slate types. This is used for
55
// autoformat, e.g., type ```[space] and get a codemirror fenced code block editor.
56
interface Rules {
57
// autoFocus: if true, block element gets focused on creation in some cases.
58
autoFocus?: boolean;
59
// autoAdvance: in next render loop, move cursor forward
60
autoAdvance?: boolean;
61
}
62
63
interface Handler {
64
// if array, register handlers for each entry
65
slateType: string | string[];
66
67
// markdownType is the optional type of the markdown token
68
// if different than slateType; use an array if there are
69
// multiple distinct types of markdown tokens to handle
70
// with the same plugin.
71
markdownType?: string | string[];
72
toSlate?: markdownToSlateFunction;
73
74
StaticElement?: React.FC<RenderElementProps>;
75
76
Element?: React.FC<RenderElementProps>;
77
78
sizeEstimator?: sizeEstimatorFunction;
79
80
childInfoHook?: childInfoHookFunction;
81
fromSlate?: slateToMarkdownFunction;
82
83
rules?: Rules;
84
}
85
86
const renderer: { [slateType: string]: React.FC<RenderElementProps> } = {};
87
const staticRenderer: { [slateType: string]: React.FC<RenderElementProps> } =
88
{};
89
const markdownToSlate: {
90
[tokenType: string]: markdownToSlateFunction;
91
} = {};
92
const slateToMarkdown: {
93
[slateType: string]: slateToMarkdownFunction;
94
} = {};
95
const childInfoHooks: { [slateType: string]: childInfoHookFunction } = {};
96
const rules: { [slateType: string]: Rules } = {};
97
const sizeEstimators: {
98
[slateType: string]: sizeEstimatorFunction;
99
} = {};
100
101
export function register(h: Handler): void {
102
const t = typeof h.slateType == "string" ? [h.slateType] : h.slateType;
103
for (const slateType of t) {
104
if (h.Element != null) {
105
renderer[slateType] = h.Element;
106
}
107
108
if (h.StaticElement != null) {
109
staticRenderer[slateType] = h.StaticElement;
110
}
111
112
if (h.rules != null) {
113
rules[slateType] = h.rules;
114
}
115
116
const x = h.markdownType ?? slateType;
117
const types = typeof x == "string" ? [x] : x;
118
if (h.toSlate != null) {
119
for (const type of types) {
120
markdownToSlate[type] = h.toSlate;
121
}
122
}
123
124
if (h.fromSlate != null) {
125
slateToMarkdown[slateType] = h.fromSlate;
126
}
127
128
if (h.childInfoHook != null) {
129
childInfoHooks[slateType] = h.childInfoHook;
130
}
131
132
if (h.sizeEstimator != null) {
133
sizeEstimators[slateType] = h.sizeEstimator;
134
}
135
}
136
}
137
138
export function getRender(slateType: string): React.FC<RenderElementProps> {
139
if (renderer[slateType] == null) {
140
if (staticRenderer[slateType] != null) {
141
return staticRenderer[slateType];
142
}
143
console.log(
144
`WARNING -- getRender: using generic plugin for type '${slateType}'; this is NOT likely to work.`
145
);
146
return renderer["generic"];
147
}
148
return renderer[slateType];
149
}
150
151
interface StaticRenderElementProps extends RenderElementProps {
152
setElement?: (obj: any) => void;
153
}
154
155
export function getStaticRender(
156
slateType: string
157
): React.FC<StaticRenderElementProps> {
158
//console.log("getStaticRender", slateType);
159
if (staticRenderer[slateType] == null) {
160
console.log(
161
`WARNING -- getStaticRender: using generic plugin for type '${slateType}'; this is NOT likely to work.`
162
);
163
return renderer["generic"];
164
}
165
return staticRenderer[slateType];
166
}
167
168
export function getMarkdownToSlate(
169
tokenType: string = ""
170
): markdownToSlateFunction {
171
if (markdownToSlate[tokenType] == null) {
172
console.log(
173
`getMarkdownToSlate: using generic plugin for type '${tokenType}'`
174
);
175
return markdownToSlate["generic"];
176
}
177
return markdownToSlate[tokenType];
178
}
179
180
export function getSlateToMarkdown(
181
slateType: string = ""
182
): slateToMarkdownFunction {
183
if (slateToMarkdown[slateType] == null) {
184
console.log(
185
`getSlateToMarkdown: using generic plugin for type '${slateType}'`
186
);
187
return slateToMarkdown["generic"];
188
}
189
return slateToMarkdown[slateType];
190
}
191
192
export function getChildInfoHook(
193
slateType: string
194
): childInfoHookFunction | undefined {
195
return childInfoHooks[slateType];
196
}
197
198
export function getRules(slateType: string): Rules | undefined {
199
return rules[slateType];
200
}
201
202
export function estimateSize(opts: SizeEstimatorOptions): number | undefined {
203
const estimate = sizeEstimators[opts.node["type"]]?.(opts);
204
// console.log(estimate, " -- estimated size of ", opts);
205
return estimate;
206
}
207
208