remons-markdown-editor 1.0.0-beta.7 → 1.0.0-beta.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/hooks/useRegisterToolbar.d.ts +53 -3
- package/dist/index.cjs +146 -18
- package/dist/index.cjs.map +1 -1
- package/dist/index.css +40 -45
- package/dist/index.js +147 -19
- package/dist/index.js.map +1 -1
- package/dist/types/index.d.ts +78 -6
- package/package.json +79 -81
- package/readme.md +309 -248
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import React, { forwardRef, useCallback, useEffect, useImperativeHandle, useMemo, useRef, useState } from "react";
|
|
2
|
-
import { Button, Dropdown, Modal, Splitter, Tooltip, message } from "antd";
|
|
2
|
+
import { Button, ColorPicker, DatePicker, Dropdown, Form, Input, InputNumber, Modal, Rate, Select, Slider, Splitter, Switch, Tooltip, message } from "antd";
|
|
3
3
|
import { EditorView, basicSetup } from "codemirror";
|
|
4
4
|
import { EditorState, Facet } from "@codemirror/state";
|
|
5
5
|
import { Decoration, EditorView as EditorView$1, ViewPlugin, WidgetType, keymap } from "@codemirror/view";
|
|
@@ -552,15 +552,16 @@ var Preview = forwardRef(({ content, previewOptions }, ref) => {
|
|
|
552
552
|
Preview.displayName = "Preview";
|
|
553
553
|
//#endregion
|
|
554
554
|
//#region src/components/Panel/SplitPanels.tsx
|
|
555
|
+
var { Panel } = Splitter;
|
|
555
556
|
function SplitPanels({ leftPanel, rightPanel }) {
|
|
556
557
|
return /* @__PURE__ */ jsxs(Splitter, {
|
|
557
558
|
className: "split-panels",
|
|
558
|
-
children: [/* @__PURE__ */ jsx(
|
|
559
|
+
children: [/* @__PURE__ */ jsx(Panel, {
|
|
559
560
|
defaultSize: "50%",
|
|
560
561
|
min: "20%",
|
|
561
562
|
max: "80%",
|
|
562
563
|
children: leftPanel
|
|
563
|
-
}), /* @__PURE__ */ jsx(
|
|
564
|
+
}), /* @__PURE__ */ jsx(Panel, { children: rightPanel })]
|
|
564
565
|
});
|
|
565
566
|
}
|
|
566
567
|
//#endregion
|
|
@@ -1041,12 +1042,59 @@ function Toolbar({ editorView, fullscreen = true, onToggleFullscreen, extraItems
|
|
|
1041
1042
|
//#endregion
|
|
1042
1043
|
//#region src/components/Dialog/EditDialog.tsx
|
|
1043
1044
|
/**
|
|
1045
|
+
* 组件类型到 antd 表单组件的映射
|
|
1046
|
+
*/
|
|
1047
|
+
var FormComponents = {
|
|
1048
|
+
input: Input,
|
|
1049
|
+
textarea: Input.TextArea,
|
|
1050
|
+
number: InputNumber,
|
|
1051
|
+
select: Select,
|
|
1052
|
+
switch: Switch,
|
|
1053
|
+
slider: Slider,
|
|
1054
|
+
rate: Rate,
|
|
1055
|
+
color: ColorPicker,
|
|
1056
|
+
date: DatePicker,
|
|
1057
|
+
dateRange: DatePicker.RangePicker
|
|
1058
|
+
};
|
|
1059
|
+
/**
|
|
1060
|
+
* 渲染对应类型的 antd 表单组件
|
|
1061
|
+
*/
|
|
1062
|
+
function renderFormItemComponent(field) {
|
|
1063
|
+
const props = field.componentProps || {};
|
|
1064
|
+
const Component = field.componentType ? FormComponents[field.componentType] : Input;
|
|
1065
|
+
return /* @__PURE__ */ jsx(Component, { ...props });
|
|
1066
|
+
}
|
|
1067
|
+
/**
|
|
1068
|
+
* 根据 fields 自动生成 Form.Item 列表
|
|
1069
|
+
*/
|
|
1070
|
+
function renderAutoForm(form, fields) {
|
|
1071
|
+
if (!fields || fields.length === 0) return null;
|
|
1072
|
+
return /* @__PURE__ */ jsx(Form, {
|
|
1073
|
+
form,
|
|
1074
|
+
layout: "vertical",
|
|
1075
|
+
children: fields.map((field) => {
|
|
1076
|
+
if (!field.componentType) return null;
|
|
1077
|
+
const formItemProps = {
|
|
1078
|
+
name: field.key,
|
|
1079
|
+
label: field.name || field.key,
|
|
1080
|
+
valuePropName: field.componentType === "switch" ? "checked" : void 0,
|
|
1081
|
+
...field.componentProps
|
|
1082
|
+
};
|
|
1083
|
+
return /* @__PURE__ */ jsx(Form.Item, {
|
|
1084
|
+
...formItemProps,
|
|
1085
|
+
children: renderFormItemComponent(field)
|
|
1086
|
+
}, field.key);
|
|
1087
|
+
})
|
|
1088
|
+
});
|
|
1089
|
+
}
|
|
1090
|
+
/**
|
|
1044
1091
|
* 属性编辑弹窗组件
|
|
1045
1092
|
* 根据组件 schema 的 renderDialog 函数渲染弹窗内容
|
|
1046
1093
|
*/
|
|
1047
1094
|
function EditDialog({ visible, typeName, currentProps, onSave, onClose }) {
|
|
1048
1095
|
const schema = componentRegistry.get(typeName);
|
|
1049
1096
|
const [formProps, setFormProps] = useState({});
|
|
1097
|
+
const [form] = Form.useForm();
|
|
1050
1098
|
const mergedProps = useMemo(() => {
|
|
1051
1099
|
if (!schema) return currentProps;
|
|
1052
1100
|
const defaults = {};
|
|
@@ -1056,24 +1104,52 @@ function EditDialog({ visible, typeName, currentProps, onSave, onClose }) {
|
|
|
1056
1104
|
...currentProps
|
|
1057
1105
|
};
|
|
1058
1106
|
}, [schema, currentProps]);
|
|
1107
|
+
useEffect(() => {
|
|
1108
|
+
if (visible) {
|
|
1109
|
+
form.setFieldsValue(mergedProps);
|
|
1110
|
+
setFormProps({});
|
|
1111
|
+
}
|
|
1112
|
+
}, [
|
|
1113
|
+
visible,
|
|
1114
|
+
mergedProps,
|
|
1115
|
+
form
|
|
1116
|
+
]);
|
|
1059
1117
|
const handleDialogChange = (key, value) => {
|
|
1060
1118
|
setFormProps((prev) => ({
|
|
1061
1119
|
...prev,
|
|
1062
1120
|
[key]: value
|
|
1063
1121
|
}));
|
|
1064
1122
|
};
|
|
1065
|
-
const handleOk = () => {
|
|
1066
|
-
|
|
1123
|
+
const handleOk = async () => {
|
|
1124
|
+
let finalProps = visible ? {
|
|
1067
1125
|
...mergedProps,
|
|
1068
1126
|
...formProps
|
|
1069
|
-
} : mergedProps
|
|
1070
|
-
|
|
1127
|
+
} : mergedProps;
|
|
1128
|
+
try {
|
|
1129
|
+
const formValues = await form.validateFields();
|
|
1130
|
+
finalProps = {
|
|
1131
|
+
...finalProps,
|
|
1132
|
+
...formValues
|
|
1133
|
+
};
|
|
1134
|
+
} catch {}
|
|
1135
|
+
onSave(finalProps);
|
|
1071
1136
|
};
|
|
1072
1137
|
const handleClose = () => {
|
|
1073
1138
|
setFormProps({});
|
|
1074
1139
|
onClose();
|
|
1075
1140
|
};
|
|
1076
|
-
if (!schema
|
|
1141
|
+
if (!schema) return null;
|
|
1142
|
+
const context = {
|
|
1143
|
+
props: mergedProps,
|
|
1144
|
+
form,
|
|
1145
|
+
onChange: handleDialogChange,
|
|
1146
|
+
onSave
|
|
1147
|
+
};
|
|
1148
|
+
const renderDialogContent = () => {
|
|
1149
|
+
if (schema.renderDialog) return schema.renderDialog(context);
|
|
1150
|
+
if (schema.fields?.some((f) => f.componentType) && schema.fields) return renderAutoForm(form, schema.fields);
|
|
1151
|
+
return null;
|
|
1152
|
+
};
|
|
1077
1153
|
return /* @__PURE__ */ jsx(Modal, {
|
|
1078
1154
|
title: `${schema.label} 属性编辑`,
|
|
1079
1155
|
open: visible,
|
|
@@ -1081,10 +1157,7 @@ function EditDialog({ visible, typeName, currentProps, onSave, onClose }) {
|
|
|
1081
1157
|
onCancel: handleClose,
|
|
1082
1158
|
okText: "保存",
|
|
1083
1159
|
cancelText: "取消",
|
|
1084
|
-
children:
|
|
1085
|
-
...mergedProps,
|
|
1086
|
-
...formProps
|
|
1087
|
-
} : mergedProps, handleDialogChange)
|
|
1160
|
+
children: renderDialogContent()
|
|
1088
1161
|
});
|
|
1089
1162
|
}
|
|
1090
1163
|
//#endregion
|
|
@@ -1194,7 +1267,7 @@ function useScrollSync(sourceRef, targetRef) {
|
|
|
1194
1267
|
}
|
|
1195
1268
|
//#endregion
|
|
1196
1269
|
//#region src/MarkdownEditor.tsx
|
|
1197
|
-
var MarkdownEditor_default = forwardRef(function MarkdownEditor({ defaultValue = "", value, onChange, showPreview = true, fullscreen: fullscreenProp =
|
|
1270
|
+
var MarkdownEditor_default = forwardRef(function MarkdownEditor({ defaultValue = "", value, onChange, showPreview = true, fullscreen: fullscreenProp = true, previewOptions, uploadConfig }, ref) {
|
|
1198
1271
|
const [content, setContent] = useState(value ?? defaultValue);
|
|
1199
1272
|
const [editorView, setEditorView] = useState(null);
|
|
1200
1273
|
const scrollDOMRef = useRef(null);
|
|
@@ -1213,14 +1286,19 @@ var MarkdownEditor_default = forwardRef(function MarkdownEditor({ defaultValue =
|
|
|
1213
1286
|
onChange?.(newContent);
|
|
1214
1287
|
}, [onChange]);
|
|
1215
1288
|
useImperativeHandle(ref, () => ({ getContent: () => content }), [content]);
|
|
1216
|
-
const extraToolbarItems = componentRegistry.getAll().
|
|
1289
|
+
const extraToolbarItems = componentRegistry.getAll().flatMap((schema) => {
|
|
1290
|
+
if (schema.wrapTags) return {
|
|
1291
|
+
id: `wrap-${schema.name}`,
|
|
1292
|
+
label: schema.label,
|
|
1293
|
+
icon: () => schema.icon ?? /* @__PURE__ */ jsx(ReadOutlined, {}),
|
|
1294
|
+
wrapTags: schema.wrapTags
|
|
1295
|
+
};
|
|
1217
1296
|
const defaultProps = {};
|
|
1218
1297
|
for (const field of schema?.fields ?? []) defaultProps[field.key] = field.defaultValue ?? "";
|
|
1219
|
-
const iconNode = schema.icon ?? /* @__PURE__ */ jsx(ReadOutlined, {});
|
|
1220
1298
|
return {
|
|
1221
1299
|
id: `container-${schema.name}`,
|
|
1222
1300
|
label: schema.label,
|
|
1223
|
-
icon: () =>
|
|
1301
|
+
icon: () => schema.icon ?? /* @__PURE__ */ jsx(ReadOutlined, {}),
|
|
1224
1302
|
handler: (view, _state) => insertContainer(view, schema.name, defaultProps)
|
|
1225
1303
|
};
|
|
1226
1304
|
});
|
|
@@ -1302,15 +1380,36 @@ var MarkdownEditor_default = forwardRef(function MarkdownEditor({ defaultValue =
|
|
|
1302
1380
|
* 2. 标签包裹模式:name + wrapTags
|
|
1303
1381
|
* 插入简单的标签包裹文本,如 <plugin-container>...</plugin-container>
|
|
1304
1382
|
*
|
|
1305
|
-
* @example 容器模式
|
|
1383
|
+
* @example 容器模式 - 使用 fields 自动生成表单(最简)
|
|
1384
|
+
* ```tsx
|
|
1385
|
+
* const schema: ComponentSchema = {
|
|
1386
|
+
* name: 'alert',
|
|
1387
|
+
* label: '警告框',
|
|
1388
|
+
* icon: <AlertOutlined />,
|
|
1389
|
+
* fields: [
|
|
1390
|
+
* { key: 'type', name: '类型', componentType: 'select',
|
|
1391
|
+
* componentProps: { options: [{ value: 'price', label: '价格' }, { value: 'info', label: '信息' }] } },
|
|
1392
|
+
* { key: 'content', name: '内容', componentType: 'textarea',
|
|
1393
|
+
* componentProps: { rows: 3 } },
|
|
1394
|
+
* { key: 'enabled', name: '启用', componentType: 'switch' },
|
|
1395
|
+
* ],
|
|
1396
|
+
* }
|
|
1397
|
+
* // 支持的 componentType: 'input' | 'textarea' | 'number' | 'select' | 'switch' | 'slider' | 'rate' | 'color' | 'date' | 'dateRange'
|
|
1398
|
+
* ```
|
|
1399
|
+
*
|
|
1400
|
+
* @example 容器模式 - 使用 Form.Item 自动绑定(推荐)
|
|
1306
1401
|
* ```tsx
|
|
1307
1402
|
* const schema: ComponentSchema = {
|
|
1308
1403
|
* name: 'alert',
|
|
1309
1404
|
* label: '警告框',
|
|
1310
1405
|
* icon: <AlertOutlined />,
|
|
1311
1406
|
* fields: [{ key: 'level', defaultValue: 'info' }],
|
|
1312
|
-
* renderDialog: (
|
|
1313
|
-
* <
|
|
1407
|
+
* renderDialog: ({ form }) => (
|
|
1408
|
+
* <Form form={form} layout="vertical">
|
|
1409
|
+
* <Form.Item name="level" label="级别">
|
|
1410
|
+
* <Input />
|
|
1411
|
+
* </Form.Item>
|
|
1412
|
+
* </Form>
|
|
1314
1413
|
* ),
|
|
1315
1414
|
* }
|
|
1316
1415
|
*
|
|
@@ -1320,6 +1419,35 @@ var MarkdownEditor_default = forwardRef(function MarkdownEditor({ defaultValue =
|
|
|
1320
1419
|
* }
|
|
1321
1420
|
* ```
|
|
1322
1421
|
*
|
|
1422
|
+
* @example 容器模式 - 手动控制
|
|
1423
|
+
* ```tsx
|
|
1424
|
+
* const schema: ComponentSchema = {
|
|
1425
|
+
* name: 'alert',
|
|
1426
|
+
* label: '警告框',
|
|
1427
|
+
* icon: <AlertOutlined />,
|
|
1428
|
+
* fields: [{ key: 'level', defaultValue: 'info' }],
|
|
1429
|
+
* renderDialog: ({ props, onChange }) => (
|
|
1430
|
+
* <Input value={props.level} onChange={e => onChange('level', e.target.value)} />
|
|
1431
|
+
* ),
|
|
1432
|
+
* }
|
|
1433
|
+
* ```
|
|
1434
|
+
*
|
|
1435
|
+
* @example 容器模式 - 完全自定义弹窗
|
|
1436
|
+
* ```tsx
|
|
1437
|
+
* const schema: ComponentSchema = {
|
|
1438
|
+
* name: 'alert',
|
|
1439
|
+
* label: '警告框',
|
|
1440
|
+
* icon: <AlertOutlined />,
|
|
1441
|
+
* fields: [{ key: 'level', defaultValue: 'info' }],
|
|
1442
|
+
* renderDialog: ({ props, onSave }) => (
|
|
1443
|
+
* <MyCustomEditor
|
|
1444
|
+
* value={props.level}
|
|
1445
|
+
* onSave={({ level }) => onSave({ level })}
|
|
1446
|
+
* />
|
|
1447
|
+
* ),
|
|
1448
|
+
* }
|
|
1449
|
+
* ```
|
|
1450
|
+
*
|
|
1323
1451
|
* @example 标签包裹模式
|
|
1324
1452
|
* ```tsx
|
|
1325
1453
|
* function MyPage() {
|