From 22758560ef3498505de156ce561e94a3f4b9c3cc Mon Sep 17 00:00:00 2001
From: rojer
Date: Wed, 9 Aug 2023 19:15:28 +0800
Subject: [PATCH] =?UTF-8?q?feat:=20:boom:=20=E9=A2=98=E7=9B=AE=E7=BC=96?=
=?UTF-8?q?=E6=8E=92&latex=E6=94=AF=E6=8C=81&=E4=BF=AE=E5=A4=8Dlink?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.dumirc.ts | 1 +
docs/demos/math.tsx | 114 ++++++++++
docs/getting-started/math.md | 17 ++
packages/antd/lib/components/index.tsx | 13 +-
packages/antd/lib/components/input.tsx | 19 ++
packages/antd/lib/components/select.tsx | 5 +-
packages/antd/lib/components/textarea.tsx | 19 ++
packages/antd/lib/index.tsx | 7 +-
.../component/lib/components/editable.tsx | 57 ++---
packages/component/lib/components/index.tsx | 21 +-
.../component/lib/components/textarea.tsx | 28 +++
packages/component/lib/element.tsx | 4 +-
packages/core/lib/components/core.tsx | 43 +++-
packages/core/lib/typing.ts | 1 +
packages/plugin/lib/index.tsx | 26 ++-
packages/plugin/lib/plugins/blockquote.tsx | 11 +
packages/plugin/lib/plugins/hr.tsx | 36 ++-
packages/plugin/lib/plugins/img/img.tsx | 87 +++++++-
packages/plugin/lib/plugins/img/index.tsx | 45 ++++
packages/plugin/lib/plugins/latex.tsx | 207 ++++++++++++++++++
packages/plugin/lib/plugins/link.tsx | 11 +
packages/plugin/lib/plugins/list.tsx | 58 +++++
packages/plugin/lib/plugins/paragraph.tsx | 33 +++
packages/plugin/lib/plugins/todo-list.tsx | 10 +
packages/plugin/package.json | 3 +
packages/semi/lib/index.tsx | 4 +-
pnpm-lock.yaml | 45 +++-
27 files changed, 833 insertions(+), 92 deletions(-)
create mode 100644 docs/demos/math.tsx
create mode 100644 docs/getting-started/math.md
create mode 100644 packages/antd/lib/components/input.tsx
create mode 100644 packages/antd/lib/components/textarea.tsx
create mode 100644 packages/component/lib/components/textarea.tsx
create mode 100755 packages/plugin/lib/plugins/latex.tsx
diff --git a/.dumirc.ts b/.dumirc.ts
index 81c140c..765c435 100644
--- a/.dumirc.ts
+++ b/.dumirc.ts
@@ -11,6 +11,7 @@ export default defineConfig({
github: 'https://github.com/rojer95/dslate',
},
},
+ mfsu: false,
monorepoRedirect: {
srcDir: ['packages'],
peerDeps: true,
diff --git a/docs/demos/math.tsx b/docs/demos/math.tsx
new file mode 100644
index 0000000..0988753
--- /dev/null
+++ b/docs/demos/math.tsx
@@ -0,0 +1,114 @@
+/**
+ * defaultShowCode: true
+ */
+import DSlate, { DefaultToolbar, DSlateRef } from '@dslate/antd';
+import { Button, ConfigProvider, Input, Space, theme } from 'antd';
+import React, { useEffect, useRef, useState } from 'react';
+import type { Descendant } from 'slate';
+
+export default () => {
+ const [resultWeapp, setResultWeapp] = useState();
+ const [resultHtml, setResultHtml] = useState();
+ const [value, setValue] = useState([
+ {
+ type: 'paragraph',
+ children: [
+ { text: '当' },
+ { type: 'latex', children: [{ text: '' }], formula: 'b=1' },
+ { text: '、' },
+ { type: 'latex', children: [{ text: '' }], formula: 'c=2' },
+ { text: '时,请计算' },
+ { type: 'latex', children: [{ text: '' }], formula: 'a=b+c' },
+ { text: '的结果?' },
+ ],
+ },
+ ]);
+
+ const ref = useRef(null);
+ const [mode, setMode] = useState(
+ document.documentElement.getAttribute('data-prefers-color') ?? 'light',
+ );
+
+ const switchMode = () => {
+ const doc = document.documentElement;
+ const attrName = 'data-prefers-color';
+ if (!doc.hasAttribute(attrName) || doc.getAttribute(attrName) === 'dark') {
+ doc.setAttribute(attrName, 'light');
+ setMode('light');
+ } else {
+ doc.setAttribute(attrName, 'dark');
+ setMode('dark');
+ }
+ };
+
+ const watch = (mutationsList: any[]) => {
+ const attrName = 'data-prefers-color';
+ for (const mutation of mutationsList) {
+ if (mutation.attributeName === attrName) {
+ setMode(document.documentElement.getAttribute(attrName) ?? 'light');
+ }
+ }
+ };
+
+ useEffect(() => {
+ let observer: any;
+ if (MutationObserver) {
+ observer = new MutationObserver(watch);
+ // 以上述配置开始观察目标节点
+ observer.observe(document.documentElement, { attributes: true });
+ // 之后,可停止观察
+ }
+ return () => observer?.disconnect?.();
+ }, []);
+
+ return (
+
+
+
+
+
+
+
+
+
+
小程序结果:
+
+
+
+
html结果:
+
+
+
+ );
+};
diff --git a/docs/getting-started/math.md b/docs/getting-started/math.md
new file mode 100644
index 0000000..746e329
--- /dev/null
+++ b/docs/getting-started/math.md
@@ -0,0 +1,17 @@
+---
+title: 题目编排
+order: 7
+nav: 文档
+---
+
+# 题目编排
+
+基于 DSlate 的 Antd 风格包,支持 html 渲染与小程序 RichNode 渲染模式
+
+> 小程序请手动引入 katex-mini 包的 css ,具体请看链接:https://github.com/rojer95/katex-mini
+
+> Antd 版本要求:antd >= 5.0
+
+## Demo
+
+
diff --git a/packages/antd/lib/components/index.tsx b/packages/antd/lib/components/index.tsx
index 435a6a2..ad8cc35 100644
--- a/packages/antd/lib/components/index.tsx
+++ b/packages/antd/lib/components/index.tsx
@@ -1,6 +1,7 @@
-export * from "./editor";
-export * from "./button";
-export * from "./select";
-export * from "./divider";
-export * from "./tooltip";
-export * from "./popover";
+export * from './button';
+export * from './divider';
+export * from './editor';
+export * from './input';
+export * from './popover';
+export * from './select';
+export * from './tooltip';
diff --git a/packages/antd/lib/components/input.tsx b/packages/antd/lib/components/input.tsx
new file mode 100644
index 0000000..c742fef
--- /dev/null
+++ b/packages/antd/lib/components/input.tsx
@@ -0,0 +1,19 @@
+import { Input as AntdInput } from 'antd';
+import { PropsWithChildren } from 'react';
+
+export const Input = ({
+ children,
+ onChange,
+ ...props
+}: PropsWithChildren) => {
+ return (
+ {
+ onChange?.(e.target.value);
+ }}
+ >
+ {children}
+
+ );
+};
diff --git a/packages/antd/lib/components/select.tsx b/packages/antd/lib/components/select.tsx
index 580ca1b..b6cf4d8 100644
--- a/packages/antd/lib/components/select.tsx
+++ b/packages/antd/lib/components/select.tsx
@@ -1,5 +1,6 @@
-import React, { PropsWithChildren } from "react";
-import { Select as AntdSelect } from "antd";
+/* eslint-disable @typescript-eslint/no-unused-vars */
+import { Select as AntdSelect } from 'antd';
+import { PropsWithChildren } from 'react';
export const Select = ({
children,
diff --git a/packages/antd/lib/components/textarea.tsx b/packages/antd/lib/components/textarea.tsx
new file mode 100644
index 0000000..924a19f
--- /dev/null
+++ b/packages/antd/lib/components/textarea.tsx
@@ -0,0 +1,19 @@
+import { Input as AntdInput } from 'antd';
+import { PropsWithChildren } from 'react';
+
+export const Textarea = ({
+ children,
+ onChange,
+ ...props
+}: PropsWithChildren) => {
+ return (
+ {
+ onChange?.(e.target.value);
+ }}
+ >
+ {children}
+
+ );
+};
diff --git a/packages/antd/lib/index.tsx b/packages/antd/lib/index.tsx
index 3c449fc..fc6e26b 100755
--- a/packages/antd/lib/index.tsx
+++ b/packages/antd/lib/index.tsx
@@ -10,11 +10,12 @@ import { registerElement } from '@dslate/component';
import DefaultPlugin from '@dslate/plugin';
-import { Input, InputNumber, Progress } from 'antd';
+import { InputNumber, Progress } from 'antd';
import {
AntdEditor,
Button,
Divider,
+ Input,
Popover,
Select,
Tooltip,
@@ -22,6 +23,7 @@ import {
import type { AntdStyleDSlateProps } from './typing';
+import { Textarea } from './components/textarea';
import EN_US from './locale/en_US';
import ZH_CN from './locale/zh_CN';
@@ -33,6 +35,7 @@ registerElement('input', Input);
registerElement('input-number', InputNumber);
registerElement('button', Button);
registerElement('select', Select);
+registerElement('textarea', Textarea);
export const DefaultLocales = [ZH_CN, EN_US];
@@ -81,7 +84,7 @@ export default forwardRef(
locales: mergeLocalteFromPlugins(locales, plugins),
plugins,
iconScriptUrl:
- '//at.alicdn.com/t/c/font_3062978_atuqwazgoap.js',
+ '//at.alicdn.com/t/c/font_3062978_igshjiflyft.js',
}}
>
diff --git a/packages/component/lib/components/editable.tsx b/packages/component/lib/components/editable.tsx
index 197ed94..a8a6f71 100755
--- a/packages/component/lib/components/editable.tsx
+++ b/packages/component/lib/components/editable.tsx
@@ -1,20 +1,20 @@
/* eslint-disable react-hooks/exhaustive-deps */
-import React, { useCallback } from "react";
+import {
+ mergeStyle,
+ PluginUuidContext,
+ useConfig,
+ useMessage,
+ usePluginHelper,
+} from '@dslate/core';
+import React, { useCallback } from 'react';
import {
DefaultElement,
Editable as SlateEditable,
RenderLeafProps,
useSlate,
-} from "slate-react";
-import {
- PluginUuidContext,
- usePluginHelper,
- mergeStyle,
- useConfig,
- useMessage,
-} from "@dslate/core";
+} from 'slate-react';
-import type { DSlatePlugin, RenderElementPropsWithStyle } from "@dslate/core";
+import type { DSlatePlugin, RenderElementPropsWithStyle } from '@dslate/core';
interface EditableProps {
disabled?: boolean;
@@ -33,10 +33,10 @@ const Editable = ({
const getMessage = useMessage();
const renderElement = useCallback((props: RenderElementPropsWithStyle) => {
- const style = mergeStyle(props.element, plugins, "element", editor);
+ const style = mergeStyle(props.element, plugins, 'element', editor);
const plugin = plugins.find(
(i: DSlatePlugin) =>
- i.nodeType === "element" && i.type === props.element.type
+ i.nodeType === 'element' && i.type === props.element.type,
) as DSlatePlugin | undefined;
let dom;
@@ -53,7 +53,7 @@ const Editable = ({
);
} else {
const defaultElementPlugin = plugins.find(
- (p: DSlatePlugin) => p.isDefaultElement
+ (p: DSlatePlugin) => p.isDefaultElement,
);
if (defaultElementPlugin && defaultElementPlugin.renderElement) {
@@ -77,10 +77,10 @@ const Editable = ({
const { attributes, children, leaf } = props;
const needRenderPlugin = plugins.find(
(i: DSlatePlugin) =>
- i.nodeType === "text" && i.type in leaf && !!i.renderLeaf
+ i.nodeType === 'text' && i.type in leaf && !!i.renderLeaf,
) as DSlatePlugin | undefined;
- const style = mergeStyle(leaf, plugins, "text", editor);
+ const style = mergeStyle(leaf, plugins, 'text', editor);
if (needRenderPlugin && needRenderPlugin.renderLeaf) {
return needRenderPlugin.renderLeaf({ ...props, style }, editor);
@@ -95,24 +95,27 @@ const Editable = ({
const onKeyDown = useCallback((e: React.KeyboardEvent) => {
for (const plugin of plugins) {
- if (typeof plugin.onKeyDown === "function") {
+ if (typeof plugin.onKeyDown === 'function') {
plugin.onKeyDown(e, editor);
}
}
}, []);
return (
- {
- setVisibleKey?.(undefined);
- }}
- onKeyDown={onKeyDown}
- readOnly={disabled}
- placeholder={placeholder ?? getMessage("placeholder", "")}
- />
+ <>
+ {
+ setVisibleKey?.(undefined);
+ }}
+ onKeyDown={onKeyDown}
+ readOnly={disabled}
+ placeholder={placeholder ?? getMessage('placeholder', '')}
+ />
+
+ >
);
};
diff --git a/packages/component/lib/components/index.tsx b/packages/component/lib/components/index.tsx
index 0e9ccb8..8a72360 100644
--- a/packages/component/lib/components/index.tsx
+++ b/packages/component/lib/components/index.tsx
@@ -1,10 +1,11 @@
-export * from "./counter";
-export * from "./divider";
-export * from "./input-number";
-export * from "./input";
-export * from "./popover";
-export * from "./progress";
-export * from "./tooltip";
-export * from "./icon";
-export * from "./toolbar";
-export * from "./editable";
+export * from './counter';
+export * from './divider';
+export * from './editable';
+export * from './icon';
+export * from './input';
+export * from './input-number';
+export * from './popover';
+export * from './progress';
+export * from './textarea';
+export * from './toolbar';
+export * from './tooltip';
diff --git a/packages/component/lib/components/textarea.tsx b/packages/component/lib/components/textarea.tsx
new file mode 100644
index 0000000..1f4ff19
--- /dev/null
+++ b/packages/component/lib/components/textarea.tsx
@@ -0,0 +1,28 @@
+import React, { PropsWithChildren } from 'react';
+import { getElement } from '../element';
+
+type InputProps = {
+ value?: string;
+ onChange: (value: string) => void;
+ autosize?: boolean;
+ [index: string]: any;
+};
+
+const Textarea = ({
+ children,
+ autosize,
+ ...props
+}: PropsWithChildren) => {
+ const InputElement = getElement('textarea');
+ if (!InputElement) return null;
+ return React.createElement(
+ InputElement,
+ {
+ ...(props || {}),
+ autoSize: autosize,
+ } as any,
+ children,
+ );
+};
+
+export { Textarea };
diff --git a/packages/component/lib/element.tsx b/packages/component/lib/element.tsx
index f935d0c..02707df 100644
--- a/packages/component/lib/element.tsx
+++ b/packages/component/lib/element.tsx
@@ -8,7 +8,8 @@ export type ElementType =
| 'input'
| 'input-number'
| 'button'
- | 'select';
+ | 'select'
+ | 'textarea';
export const elements: Record<
ElementType,
@@ -22,6 +23,7 @@ export const elements: Record<
'input-number': undefined,
button: undefined,
select: undefined,
+ textarea: undefined,
};
export const registerElement = (
diff --git a/packages/core/lib/components/core.tsx b/packages/core/lib/components/core.tsx
index cb6439d..300e608 100755
--- a/packages/core/lib/components/core.tsx
+++ b/packages/core/lib/components/core.tsx
@@ -23,6 +23,7 @@ export interface DSlateProps {
export type DSlateRef = {
serialize: (v: any) => string;
+ serializeWeapp: (v: any) => any;
getEditor: () => Editor;
};
@@ -88,11 +89,51 @@ const DSlateCore = forwardRef>(
[plugins, pluginProps],
);
+ const serializeWeapp = useCallback(
+ (node: any) => {
+ if (Text.isText(node)) {
+ const style = mergeStyle(node, plugins, 'text', editor);
+ return {
+ type: 'node',
+ name: 'span',
+ attrs: {
+ style: style2string(style),
+ },
+ children: [
+ {
+ type: 'text',
+ text: escapeHtml(node.text),
+ },
+ ],
+ };
+ }
+
+ const childrens = node.children.map((n: any) => serializeWeapp(n));
+ const style = mergeStyle(node, plugins, 'element', editor);
+ const match = Object.values(plugins).find(
+ (i) => i.type === node.type && i.nodeType === 'element',
+ );
+
+ if (match && match.serializeWeapp) {
+ const matchPluginProps = {
+ ...(match?.props ?? {}),
+ ...(pluginProps?.[match.type ?? ''] ?? {}),
+ style: style2string(style),
+ };
+ return match.serializeWeapp(node, matchPluginProps, childrens);
+ }
+
+ return childrens;
+ },
+ // eslint-disable-next-line react-hooks/exhaustive-deps
+ [plugins, pluginProps],
+ );
+
const getEditor = useCallback(() => {
return editor;
}, [editor]);
- useImperativeHandle(ref, () => ({ serialize, getEditor }), [
+ useImperativeHandle(ref, () => ({ serialize, getEditor, serializeWeapp }), [
serialize,
getEditor,
]);
diff --git a/packages/core/lib/typing.ts b/packages/core/lib/typing.ts
index 7f0fc41..d9dcd4d 100755
--- a/packages/core/lib/typing.ts
+++ b/packages/core/lib/typing.ts
@@ -67,6 +67,7 @@ export type DSlatePlugin = {
locale?: Locale[];
props?: Record;
serialize?: (element: any, pluginProps: any, children: any[]) => string;
+ serializeWeapp?: (element: any, pluginProps: any, children: any[]) => any;
};
export type Locale = {
diff --git a/packages/plugin/lib/index.tsx b/packages/plugin/lib/index.tsx
index f12b0ae..695de53 100755
--- a/packages/plugin/lib/index.tsx
+++ b/packages/plugin/lib/index.tsx
@@ -1,21 +1,22 @@
-import { ParagraphPlugin } from './plugins/paragraph';
+import { BackgroundColorPlugin } from './plugins/background-color';
+import { BlockquotePlugin } from './plugins/blockquote';
import { BoldPlugin } from './plugins/bold';
-import { DecorationPlugin } from './plugins/decoration';
-import { ItalicPlugin } from './plugins/italic';
-import { ColorPlugin } from './plugins/color';
import { ClearPlugin } from './plugins/clear';
-import { HistoryPlugin } from './plugins/history';
-import { BackgroundColorPlugin } from './plugins/background-color';
+import { ColorPlugin } from './plugins/color';
+import { DecorationPlugin } from './plugins/decoration';
import { FontSizePlugin } from './plugins/font-size';
-import { TextAlignPlugin } from './plugins/text-align';
+import { HistoryPlugin } from './plugins/history';
+import { HrPlugin } from './plugins/hr';
+import { ImgPlugin } from './plugins/img';
import { TextIndentPlugin } from './plugins/indent';
+import { ItalicPlugin } from './plugins/italic';
+import { LatexPlugin } from './plugins/latex';
+import { LineHeightPlugin } from './plugins/line-height';
+import { LinkPlugin } from './plugins/link';
import { ListPlugin } from './plugins/list';
+import { ParagraphPlugin } from './plugins/paragraph';
+import { TextAlignPlugin } from './plugins/text-align';
import { TodoListPlugin } from './plugins/todo-list';
-import { ImgPlugin } from './plugins/img';
-import { LinkPlugin } from './plugins/link';
-import { BlockquotePlugin } from './plugins/blockquote';
-import { HrPlugin } from './plugins/hr';
-import { LineHeightPlugin } from './plugins/line-height';
export default {
HistoryPlugin,
@@ -36,4 +37,5 @@ export default {
BlockquotePlugin,
HrPlugin,
LineHeightPlugin,
+ LatexPlugin,
};
diff --git a/packages/plugin/lib/plugins/blockquote.tsx b/packages/plugin/lib/plugins/blockquote.tsx
index db460d4..e445e41 100755
--- a/packages/plugin/lib/plugins/blockquote.tsx
+++ b/packages/plugin/lib/plugins/blockquote.tsx
@@ -160,6 +160,17 @@ const BlockquotePlugin: DSlatePlugin = {
'',
)}`;
},
+
+ serializeWeapp: (element, props, children) => {
+ return {
+ type: 'node',
+ name: 'blockquote',
+ attrs: {
+ style: props.style,
+ },
+ children,
+ };
+ },
};
export { BlockquotePlugin };
diff --git a/packages/plugin/lib/plugins/hr.tsx b/packages/plugin/lib/plugins/hr.tsx
index 154c4e7..41712c4 100755
--- a/packages/plugin/lib/plugins/hr.tsx
+++ b/packages/plugin/lib/plugins/hr.tsx
@@ -98,7 +98,7 @@ const HrWrap = (props: RenderElementPropsWithStyle) => {
const getMessage = useMessage();
return (
-
+
{
>
@@ -154,12 +154,15 @@ const renderElement = (props: RenderElementPropsWithStyle) => (
);
-const renderStyle = (node: Descendant) => {
+const renderStyle = (node: Descendant, _: Editor, props?: any) => {
if (node.type === TYPE) {
return {
- padding: '10px 0px',
- border: '1px solid transparent',
- borderRadius: 4,
+ border: 'none',
+ margin: '10px 0px',
+ borderBottomWidth: 1,
+ borderBottomStyle: 'solid',
+ borderBottomColor: props.color,
+ height: 1,
} as CSSProperties;
}
return {};
@@ -189,8 +192,19 @@ const HrPlugin: DSlatePlugin = {
remove: 'remove',
},
],
- serialize: (e, p) =>
- `
`,
+ serialize: (e, p) => {
+ return `
`;
+ },
+
+ serializeWeapp: (e, p) => {
+ return {
+ type: 'node',
+ name: 'hr',
+ attrs: {
+ style: p.style,
+ },
+ };
+ },
};
export { HrPlugin };
diff --git a/packages/plugin/lib/plugins/img/img.tsx b/packages/plugin/lib/plugins/img/img.tsx
index aaecf7d..ec788c7 100755
--- a/packages/plugin/lib/plugins/img/img.tsx
+++ b/packages/plugin/lib/plugins/img/img.tsx
@@ -1,5 +1,5 @@
/* eslint-disable react-hooks/exhaustive-deps */
-import { Icon, Input, Popover, Toolbar } from '@dslate/component';
+import { Icon, Input, InputNumber, Popover, Toolbar } from '@dslate/component';
import type { RenderElementPropsWithStyle } from '@dslate/core';
import {
promiseUploadFunc,
@@ -82,7 +82,9 @@ const Img = ({
});
useEffect(() => {
- setLoading(true);
+ if (element.url.indexOf(';base64,') === -1) {
+ setLoading(true);
+ }
}, [element.url]);
const selected = useSelected();
@@ -120,6 +122,21 @@ const Img = ({
);
};
+ const updateMargin = (position: string, margin: number) => {
+ Transforms.setNodes(
+ editor,
+ {
+ margin: {
+ ...(element.margin || {}),
+ [position]: margin,
+ },
+ },
+ {
+ at: path,
+ },
+ );
+ };
+
const updateEditableSizeEnd = () => {
updateSize(editable);
};
@@ -129,7 +146,10 @@ const Img = ({
const width = String(image.current?.naturalWidth ?? 1);
const height = String(image.current?.naturalHeight ?? 1);
const nSize = resize({ width, height }, key, value);
- setEditable(nSize);
+ setEditable((pre) => ({
+ ...pre,
+ ...nSize,
+ }));
};
useEffect(() => {
@@ -139,9 +159,12 @@ const Img = ({
*/
const width = element.imgWidth ?? image.current?.width ?? '';
const height = element.imgHeight ?? image.current?.height ?? '';
- setEditable({
- width: width,
- height: height,
+ setEditable((pre) => {
+ return {
+ ...pre,
+ width: width,
+ height: height,
+ };
});
}
@@ -172,9 +195,12 @@ const Img = ({
if (element.imgWidth) width = element.imgWidth;
if (element.imgHeight) height = element.imgHeight;
- setEditable({
- width: width,
- height: height,
+ setEditable((pre) => {
+ return {
+ ...pre,
+ width: width,
+ height: height,
+ };
});
setDraggable({
@@ -329,8 +355,10 @@ const Img = ({
display: 'flex',
alignItems: 'center',
gap: '8px',
+ marginBottom: 8,
}}
>
+ {getMessage('float', '浮动方式')}
{
@@ -353,6 +381,47 @@ const Img = ({
icon={}
/>
+
+ {getMessage('margin', '外边距')}
+ {
+ updateMargin('top', number as number);
+ }}
+ placeholder={getMessage('top', '上')}
+ />
+ {
+ updateMargin('right', number as number);
+ }}
+ placeholder={getMessage('right', '右')}
+ />
+ {
+ updateMargin('bottom', number as number);
+ }}
+ placeholder={getMessage('bottom', '下')}
+ />
+ {
+ updateMargin('left', number as number);
+ }}
+ placeholder={getMessage('left', '左')}
+ />
+
}
>
diff --git a/packages/plugin/lib/plugins/img/index.tsx b/packages/plugin/lib/plugins/img/index.tsx
index 3c723e8..2f66bec 100755
--- a/packages/plugin/lib/plugins/img/index.tsx
+++ b/packages/plugin/lib/plugins/img/index.tsx
@@ -35,6 +35,19 @@ const renderStyle = (node: Descendant) => {
if (node.align === 'left') style.float = 'left';
if (node.align === 'right') style.float = 'right';
if (node.align === '') style.float = undefined;
+ for (const position of ['Left', 'Top', 'Bottom', 'Right']) {
+ const cssKey = `margin${position}` as
+ | 'marginLeft'
+ | 'marginTop'
+ | 'marginBottom'
+ | 'marginRight';
+ if (node.margin?.[position.toLowerCase()]) {
+ style[cssKey] = `${node.margin[position.toLowerCase()]}px`;
+ } else {
+ style[cssKey] = undefined;
+ }
+ }
+
return style;
}
return {};
@@ -107,6 +120,15 @@ const ImgPlugin: DSlatePlugin = {
width: '宽',
loading: '图片加载中',
remove: '删除',
+ float: '浮动方式',
+ ['float-left']: '左浮动',
+ ['float-right']: '右浮动',
+ ['float-default']: '默认',
+ margin: '外边距',
+ top: '上',
+ left: '左',
+ right: '右',
+ bottom: '下',
},
{
locale: Locales.enUS,
@@ -117,6 +139,15 @@ const ImgPlugin: DSlatePlugin = {
width: 'width',
loading: 'loading',
remove: 'remove',
+ float: 'float',
+ ['float-left']: 'float left',
+ ['float-right']: 'float right',
+ ['float-default']: 'float default',
+ margin: 'margin',
+ top: 'top',
+ left: 'left',
+ right: 'right',
+ bottom: 'bottom',
},
],
serialize: (element, props) => {
@@ -126,6 +157,20 @@ const ImgPlugin: DSlatePlugin = {
style.push(`max-width: ${props.maxWidth}; height: auto;`);
return `
`;
},
+ serializeWeapp: (element, props) => {
+ const style = [];
+ if (props?.style) style.push(props.style);
+ if (props?.maxWidth)
+ style.push(`max-width: ${props.maxWidth}; height: auto;`);
+ return {
+ type: 'node',
+ name: 'img',
+ attrs: {
+ style: style.join(''),
+ src: element.url,
+ },
+ };
+ },
};
export { ImgPlugin };
diff --git a/packages/plugin/lib/plugins/latex.tsx b/packages/plugin/lib/plugins/latex.tsx
new file mode 100755
index 0000000..475ff22
--- /dev/null
+++ b/packages/plugin/lib/plugins/latex.tsx
@@ -0,0 +1,207 @@
+import { Icon, Popover, Textarea, Toolbar } from '@dslate/component';
+import {
+ DSlatePlugin,
+ isBlockActive,
+ Locales,
+ RenderElementPropsWithStyle,
+ useMessage,
+ usePlugin,
+} from '@dslate/core';
+import parseKatexWeapp from '@rojer/katex-mini';
+import katex from 'katex';
+import 'katex/dist/katex.min.css';
+import { useMemo } from 'react';
+import { Editor, Text, Transforms } from 'slate';
+import { ReactEditor, useSelected, useSlate } from 'slate-react';
+const TYPE = 'latex';
+
+const remove = (editor: Editor) => {
+ if (!editor.selection) return;
+ Transforms.unwrapNodes(editor, {
+ match: (n) => n.type === TYPE,
+ split: true,
+ });
+};
+
+const add = (editor: Editor) => {
+ if (!editor.selection) return;
+ Transforms.wrapNodes(
+ editor,
+ {
+ type: TYPE,
+ children: [],
+ },
+ {
+ at: editor.selection,
+ match: (n) => Text.isText(n),
+ split: true,
+ },
+ );
+};
+
+const ToolbarButton = () => {
+ const editor = useSlate();
+ const getMessage = useMessage();
+
+ const toggle = () => {
+ if (!editor.selection) return;
+ const isActive = isBlockActive(editor, TYPE);
+ if (isActive) {
+ remove(editor);
+ } else {
+ add(editor);
+ }
+ };
+
+ return (
+
}
+ />
+ );
+};
+
+const LatexWrap = ({
+ attributes,
+ element,
+ children,
+}: RenderElementPropsWithStyle) => {
+ const selected = useSelected();
+ const editor = useSlate();
+ const path = ReactEditor.findPath(editor, element);
+ const getMessage = useMessage();
+ const { props } = usePlugin();
+
+ const LatexDom = useMemo(() => {
+ if (typeof element.formula !== 'string' || !element.formula) {
+ return (
+
+ {getMessage('edit', '点击编辑Latex')}
+
+ );
+ }
+
+ const html = katex.renderToString(element.formula, {
+ displayMode: props?.displayMode,
+ throwOnError: false,
+ });
+
+ return (
+
+ );
+ }, [element.formula, props?.displayMode]);
+
+ return (
+
+
+
+ {getMessage('latex', 'Latex')}:
+
+ }
+ >
+ {LatexDom}
+
+
+ {children}
+
+ );
+};
+
+const renderElement = (props: RenderElementPropsWithStyle) => {
+ return ;
+};
+
+const LatexPlugin: DSlatePlugin = {
+ type: TYPE,
+ nodeType: 'element',
+ isVoid: true,
+ isInline: true,
+ renderElement,
+ toolbar: ToolbarButton,
+ props: {
+ displayMode: false,
+ },
+ locale: [
+ { locale: Locales.zhCN, remove: '删除', latex: 'Latex', edit: '点击编辑' },
+ {
+ locale: Locales.enUS,
+ remove: 'remove',
+ latex: 'Latex',
+ edit: 'click edit',
+ },
+ ],
+ serialize: (element, pluginProps) => {
+ if (typeof element.formula === 'string') {
+ return katex.renderToString(element.formula, {
+ displayMode: pluginProps.displayMode,
+ throwOnError: false,
+ });
+ }
+ return '';
+ },
+
+ serializeWeapp: (element, pluginProps) => {
+ if (typeof element.formula === 'string') {
+ return {
+ type: 'node',
+ name: 'span',
+ children: parseKatexWeapp(element.formula, {
+ throwError: false,
+ displayMode: pluginProps.displayMode,
+ }),
+ };
+ }
+ return {
+ type: 'text',
+ text: '',
+ };
+ },
+};
+
+export { LatexPlugin };
diff --git a/packages/plugin/lib/plugins/link.tsx b/packages/plugin/lib/plugins/link.tsx
index de8a1a4..b562ee7 100755
--- a/packages/plugin/lib/plugins/link.tsx
+++ b/packages/plugin/lib/plugins/link.tsx
@@ -197,6 +197,17 @@ const LinkPlugin: DSlatePlugin = {
`${children.join(
'',
)}`,
+
+ serializeWeapp: (element, props, children) => {
+ return {
+ type: 'node',
+ name: 'span',
+ attrs: {
+ style: props.style,
+ },
+ children,
+ };
+ },
};
export { LinkPlugin };
diff --git a/packages/plugin/lib/plugins/list.tsx b/packages/plugin/lib/plugins/list.tsx
index eb6de88..0befc12 100755
--- a/packages/plugin/lib/plugins/list.tsx
+++ b/packages/plugin/lib/plugins/list.tsx
@@ -252,6 +252,64 @@ const ListPlugin: DSlatePlugin = {
})
.join('');
},
+
+ serializeWeapp: (element, pluginProps, children) => {
+ const isOrdered = element?.[IS_ORDERED];
+ const { listStyles } = pluginProps ?? {};
+ let start = 0;
+ return {
+ type: 'node',
+ name: 'div',
+ children: (Array.isArray(children) ? children : [children]).map(
+ (li: any, index: number) => {
+ const node = element?.children[index];
+ const preNode = element?.children?.[index - 1];
+ const nodeIndent = node?.[TextIndentPlugin.type] ?? 0;
+ const preNodeIndent = preNode?.[TextIndentPlugin.type] ?? 0;
+
+ const style: string[] = [];
+
+ if (nodeIndent !== preNodeIndent) {
+ start = 0;
+ }
+
+ const listStyleType = !element?.[IS_ORDERED]
+ ? 'disc'
+ : listStyles?.[nodeIndent % listStyles?.length] ?? 'decimal';
+
+ style.push(`list-style-type: ${listStyleType};`);
+ let paddingLeft: string = '40px';
+
+ if (nodeIndent) {
+ paddingLeft = `calc(40px + ${nodeIndent * 2}em)`;
+ }
+
+ style.push(`padding-left: ${paddingLeft};`);
+
+ start++;
+
+ return {
+ type: 'node',
+ name: isOrdered ? 'ol' : 'ul',
+ attrs: {
+ start: isOrdered ? String(start) : undefined,
+ style: style.join(''),
+ },
+ children: [
+ {
+ type: 'node',
+ name: 'li',
+ attrs: {
+ style: `text-indent: ${-nodeIndent * 2}em`,
+ },
+ children: [li],
+ },
+ ],
+ };
+ },
+ ),
+ };
+ },
};
export { ListPlugin };
diff --git a/packages/plugin/lib/plugins/paragraph.tsx b/packages/plugin/lib/plugins/paragraph.tsx
index d7294b5..c171c14 100755
--- a/packages/plugin/lib/plugins/paragraph.tsx
+++ b/packages/plugin/lib/plugins/paragraph.tsx
@@ -218,6 +218,39 @@ const ParagraphPlugin: DSlatePlugin = {
isEmpty ? ' ' : ''
}${tag}>`;
},
+ serializeWeapp: (element, props, children) => {
+ const isEmpty = Node.string(element) === '';
+ let tag = 'div';
+
+ if (element?.[PROPS_KEY] === 'h1') {
+ tag = 'h1';
+ }
+
+ if (element?.[PROPS_KEY] === 'h2') {
+ tag = 'h2';
+ }
+
+ if (element?.[PROPS_KEY] === 'h3') {
+ tag = 'h3';
+ }
+
+ if (element?.[PROPS_KEY] === 'h4') {
+ tag = 'h4';
+ }
+
+ return {
+ type: 'node',
+ name: tag,
+ children: isEmpty
+ ? [
+ {
+ type: 'text',
+ text: ' ',
+ },
+ ]
+ : children,
+ };
+ },
};
export { ParagraphPlugin };
diff --git a/packages/plugin/lib/plugins/todo-list.tsx b/packages/plugin/lib/plugins/todo-list.tsx
index 4e4ffaf..8762619 100755
--- a/packages/plugin/lib/plugins/todo-list.tsx
+++ b/packages/plugin/lib/plugins/todo-list.tsx
@@ -105,6 +105,16 @@ const TodoListPlugin: DSlatePlugin = {
'',
)}
`;
},
+ serializeWeapp: (element, props, children) => {
+ return {
+ type: 'node',
+ name: 'p',
+ attrs: {
+ type: props.style,
+ },
+ children,
+ };
+ },
};
export { TodoListPlugin };
diff --git a/packages/plugin/package.json b/packages/plugin/package.json
index 19525c8..e7b9ac3 100644
--- a/packages/plugin/package.json
+++ b/packages/plugin/package.json
@@ -29,7 +29,9 @@
"dev": "tsc && vite build --watch"
},
"dependencies": {
+ "@rojer/katex-mini": "^1.1.3",
"is-hotkey": "^0.2.0",
+ "katex": "^0.16.8",
"rc-upload": "^4.3.4",
"react-color": "^2.19.3",
"react-rnd": "^10.4.1",
@@ -40,6 +42,7 @@
"@dslate/component": "workspace:2.0.6",
"@dslate/core": "workspace:2.0.6",
"@types/is-hotkey": "^0.1.7",
+ "@types/katex": "^0.16.2",
"@types/node": "^14.18.36",
"@types/react": "^18.0.27",
"@types/react-color": "^3.0.6",
diff --git a/packages/semi/lib/index.tsx b/packages/semi/lib/index.tsx
index 4ac9d72..2a7e590 100755
--- a/packages/semi/lib/index.tsx
+++ b/packages/semi/lib/index.tsx
@@ -16,6 +16,7 @@ import {
InputNumber,
Progress,
Select,
+ TextArea,
Tooltip,
} from '@douyinfe/semi-ui';
@@ -33,6 +34,7 @@ registerElement('input', Input);
registerElement('input-number', InputNumber);
registerElement('button', Button);
registerElement('select', Select);
+registerElement('textarea', TextArea);
export const DefaultLocales = [ZH_CN, EN_US];
@@ -81,7 +83,7 @@ export default forwardRef(
locales: mergeLocalteFromPlugins(locales, plugins),
plugins,
iconScriptUrl:
- '//at.alicdn.com/t/c/font_3062978_atuqwazgoap.js',
+ '//at.alicdn.com/t/c/font_3062978_igshjiflyft.js',
}}
>
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 538e6f6..03708e0 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -81,13 +81,13 @@ importers:
packages/antd:
dependencies:
'@dslate/component':
- specifier: workspace:2.0.5
+ specifier: workspace:2.0.6
version: link:../component
'@dslate/core':
- specifier: workspace:2.0.5
+ specifier: workspace:2.0.6
version: link:../core
'@dslate/plugin':
- specifier: workspace:2.0.5
+ specifier: workspace:2.0.6
version: link:../plugin
classnames:
specifier: ^2.3.2
@@ -152,7 +152,7 @@ importers:
version: 0.90.0(react-dom@18.2.0)(react@18.2.0)(slate@0.90.0)
devDependencies:
'@dslate/core':
- specifier: workspace:2.0.5
+ specifier: workspace:2.0.6
version: link:../core
'@types/node':
specifier: ^14.18.36
@@ -224,9 +224,15 @@ importers:
packages/plugin:
dependencies:
+ '@rojer/katex-mini':
+ specifier: ^1.1.3
+ version: 1.1.3
is-hotkey:
specifier: ^0.2.0
version: 0.2.0
+ katex:
+ specifier: ^0.16.8
+ version: 0.16.8
rc-upload:
specifier: ^4.3.4
version: 4.3.4(react-dom@18.2.0)(react@18.2.0)
@@ -256,14 +262,17 @@ importers:
version: 5.3.6(react-dom@18.2.0)(react-is@18.2.0)(react@18.2.0)
devDependencies:
'@dslate/component':
- specifier: workspace:2.0.5
+ specifier: workspace:2.0.6
version: link:../component
'@dslate/core':
- specifier: workspace:2.0.5
+ specifier: workspace:2.0.6
version: link:../core
'@types/is-hotkey':
specifier: ^0.1.7
version: 0.1.7
+ '@types/katex':
+ specifier: ^0.16.2
+ version: 0.16.2
'@types/node':
specifier: ^14.18.36
version: 14.18.36
@@ -295,13 +304,13 @@ importers:
packages/semi:
dependencies:
'@dslate/component':
- specifier: workspace:2.0.5
+ specifier: workspace:2.0.6
version: link:../component
'@dslate/core':
- specifier: workspace:2.0.5
+ specifier: workspace:2.0.6
version: link:../core
'@dslate/plugin':
- specifier: workspace:2.0.5
+ specifier: workspace:2.0.6
version: link:../plugin
classnames:
specifier: ^2.3.2
@@ -2644,6 +2653,12 @@ packages:
react-dom: 18.2.0(react@18.2.0)
dev: true
+ /@rojer/katex-mini@1.1.3:
+ resolution: {integrity: sha512-mG/DrlGKBhzlA9GKNhP7f4oeL1MDC/WnKZ2iRjtG0lUHx44/C48N478w6UVnlnA4RlPmBOug2xsOtAeQClXgag==}
+ dependencies:
+ katex: 0.16.8
+ dev: false
+
/@rollup/pluginutils@5.0.2:
resolution: {integrity: sha512-pTd9rIsP92h+B6wWwFbW8RkZv4hiR/xKsqre4SIuAOaOEQRxi0lqLke9k2/7WegC85GgUs9pjmOjCUi3In4vwA==}
engines: {node: '>=14.0.0'}
@@ -3135,6 +3150,10 @@ packages:
resolution: {integrity: sha512-Hr5Jfhc9eYOQNPYO5WLDq/n4jqijdHNlDXjuAQkkt+mWdQR+XJToOHrsD4cPaMXpn6KO7y2+wM8AZEs8VpBLVA==}
dev: true
+ /@types/katex@0.16.2:
+ resolution: {integrity: sha512-dHsSjSlU/EWEEbeNADr3FtZZOAXPkFPUO457QCnoNqcZQXNqNEu/svQd0Nritvd3wNff4vvC/f4e6xgX3Llt8A==}
+ dev: true
+
/@types/keyv@3.1.4:
resolution: {integrity: sha512-BQ5aZNSCpj7D6K2ksrRCTmKRLEpnPvWDiLPfoGyhZ++8YtiK9d/3DBKPJgry359X/P1PfruyYwvnvwFjuEiEIg==}
dependencies:
@@ -5236,7 +5255,6 @@ packages:
/commander@8.3.0:
resolution: {integrity: sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==}
engines: {node: '>= 12'}
- dev: true
/commander@9.5.0:
resolution: {integrity: sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ==}
@@ -8652,6 +8670,13 @@ packages:
object.assign: 4.1.4
dev: true
+ /katex@0.16.8:
+ resolution: {integrity: sha512-ftuDnJbcbOckGY11OO+zg3OofESlbR5DRl2cmN8HeWeeFIV7wTXvAOx8kEjZjobhA+9wh2fbKeO6cdcA9Mnovg==}
+ hasBin: true
+ dependencies:
+ commander: 8.3.0
+ dev: false
+
/kind-of@3.2.2:
resolution: {integrity: sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==}
engines: {node: '>=0.10.0'}