Path: blob/master/src/packages/frontend/editors/slate/elements/register.ts
5929 views
/*1* This file is part of CoCalc: Copyright © 2020 Sagemath, Inc.2* License: MS-RSL – see LICENSE.md for details3*/45import type { RenderElementProps } from "../slate-react";6export type { RenderElementProps } from "../slate-react";7import React from "react";8import { Descendant, Element } from "slate";9import { State as MarkdownParserState, Token } from "../markdown-to-slate";10import { Info } from "../slate-to-markdown";11import { ChildInfo } from "../element-to-markdown";1213export interface SlateElement {14children: Descendant[];15}1617export interface markdownToSlateOptions {18type: string;19token: Token;20state: MarkdownParserState;21children: Node[];22isEmpty: boolean;23}2425export interface slateToMarkdownOptions {26node: Node;27children: string;28info: Info;29childInfo: ChildInfo;30cache?;31}3233type markdownToSlateFunction = (markdownToSlateOptions) => Element | undefined;3435type slateToMarkdownFunction = (slateToMarkdownOptions) => string;3637interface SizeEstimatorOptions {38node: Node;39fontSize: number; // unit of pixels40}4142type sizeEstimatorFunction = (SizeEstimatorOptions) => number | undefined;4344// This hook is called before the children of the node are serialized.45// Use this to mutate childInfo and add in extra information that the46// parent can deduce, but the children can't, since they have no way47// to get at the parent.48type childInfoHookFunction = (opts: {49node: Element;50childInfo: ChildInfo;51}) => void;5253// Rules of behavior for slate specific slate types. This is used for54// autoformat, e.g., type ```[space] and get a codemirror fenced code block editor.55interface Rules {56// autoFocus: if true, block element gets focused on creation in some cases.57autoFocus?: boolean;58// autoAdvance: in next render loop, move cursor forward59autoAdvance?: boolean;60}6162interface Handler {63// if array, register handlers for each entry64slateType: string | string[];6566// markdownType is the optional type of the markdown token67// if different than slateType; use an array if there are68// multiple distinct types of markdown tokens to handle69// with the same plugin.70markdownType?: string | string[];71toSlate?: markdownToSlateFunction;7273StaticElement?: React.FC<RenderElementProps>;7475Element?: React.FC<RenderElementProps>;7677sizeEstimator?: sizeEstimatorFunction;7879childInfoHook?: childInfoHookFunction;80fromSlate?: slateToMarkdownFunction;8182rules?: Rules;83}8485const renderer: { [slateType: string]: React.FC<RenderElementProps> } = {};86const staticRenderer: { [slateType: string]: React.FC<RenderElementProps> } =87{};88const markdownToSlate: {89[tokenType: string]: markdownToSlateFunction;90} = {};91const slateToMarkdown: {92[slateType: string]: slateToMarkdownFunction;93} = {};94const childInfoHooks: { [slateType: string]: childInfoHookFunction } = {};95const rules: { [slateType: string]: Rules } = {};96const sizeEstimators: {97[slateType: string]: sizeEstimatorFunction;98} = {};99100export function register(h: Handler): void {101const t = typeof h.slateType == "string" ? [h.slateType] : h.slateType;102for (const slateType of t) {103if (h.Element != null) {104renderer[slateType] = h.Element;105}106107if (h.StaticElement != null) {108staticRenderer[slateType] = h.StaticElement;109}110111if (h.rules != null) {112rules[slateType] = h.rules;113}114115const x = h.markdownType ?? slateType;116const types = typeof x == "string" ? [x] : x;117if (h.toSlate != null) {118for (const type of types) {119markdownToSlate[type] = h.toSlate;120}121}122123if (h.fromSlate != null) {124slateToMarkdown[slateType] = h.fromSlate;125}126127if (h.childInfoHook != null) {128childInfoHooks[slateType] = h.childInfoHook;129}130131if (h.sizeEstimator != null) {132sizeEstimators[slateType] = h.sizeEstimator;133}134}135}136137export function getRender(slateType: string): React.FC<RenderElementProps> {138if (renderer[slateType] == null) {139if (staticRenderer[slateType] != null) {140return staticRenderer[slateType];141}142console.log(143`WARNING -- getRender: using generic plugin for type '${slateType}'; this is NOT likely to work.`144);145return renderer["generic"];146}147return renderer[slateType];148}149150interface StaticRenderElementProps extends RenderElementProps {151setElement?: (obj: any) => void;152}153154export function getStaticRender(155slateType: string156): React.FC<StaticRenderElementProps> {157//console.log("getStaticRender", slateType);158if (staticRenderer[slateType] == null) {159console.log(160`WARNING -- getStaticRender: using generic plugin for type '${slateType}'; this is NOT likely to work.`161);162return renderer["generic"];163}164return staticRenderer[slateType];165}166167export function getMarkdownToSlate(168tokenType: string = ""169): markdownToSlateFunction {170if (markdownToSlate[tokenType] == null) {171console.log(172`getMarkdownToSlate: using generic plugin for type '${tokenType}'`173);174return markdownToSlate["generic"];175}176return markdownToSlate[tokenType];177}178179export function getSlateToMarkdown(180slateType: string = ""181): slateToMarkdownFunction {182if (slateToMarkdown[slateType] == null) {183console.log(184`getSlateToMarkdown: using generic plugin for type '${slateType}'`185);186return slateToMarkdown["generic"];187}188return slateToMarkdown[slateType];189}190191export function getChildInfoHook(192slateType: string193): childInfoHookFunction | undefined {194return childInfoHooks[slateType];195}196197export function getRules(slateType: string): Rules | undefined {198return rules[slateType];199}200201export function estimateSize(opts: SizeEstimatorOptions): number | undefined {202const estimate = sizeEstimators[opts.node["type"]]?.(opts);203// console.log(estimate, " -- estimated size of ", opts);204return estimate;205}206207208