remons-markdown-editor 1.0.0-beta.6 → 1.0.0-beta.8

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.
@@ -9,15 +9,36 @@ import { ComponentSchema } from '../types';
9
9
  * 2. 标签包裹模式:name + wrapTags
10
10
  * 插入简单的标签包裹文本,如 <plugin-container>...</plugin-container>
11
11
  *
12
- * @example 容器模式
12
+ * @example 容器模式 - 使用 fields 自动生成表单(最简)
13
+ * ```tsx
14
+ * const schema: ComponentSchema = {
15
+ * name: 'alert',
16
+ * label: '警告框',
17
+ * icon: <AlertOutlined />,
18
+ * fields: [
19
+ * { key: 'type', name: '类型', componentType: 'select',
20
+ * componentProps: { options: [{ value: 'price', label: '价格' }, { value: 'info', label: '信息' }] } },
21
+ * { key: 'content', name: '内容', componentType: 'textarea',
22
+ * componentProps: { rows: 3 } },
23
+ * { key: 'enabled', name: '启用', componentType: 'switch' },
24
+ * ],
25
+ * }
26
+ * // 支持的 componentType: 'input' | 'textarea' | 'number' | 'select' | 'switch' | 'slider' | 'rate' | 'color' | 'date' | 'dateRange'
27
+ * ```
28
+ *
29
+ * @example 容器模式 - 使用 Form.Item 自动绑定(推荐)
13
30
  * ```tsx
14
31
  * const schema: ComponentSchema = {
15
32
  * name: 'alert',
16
33
  * label: '警告框',
17
34
  * icon: <AlertOutlined />,
18
35
  * fields: [{ key: 'level', defaultValue: 'info' }],
19
- * renderDialog: (props, onChange) => (
20
- * <Input value={props.level} onChange={e => onChange('level', e.target.value)} />
36
+ * renderDialog: ({ form }) => (
37
+ * <Form form={form} layout="vertical">
38
+ * <Form.Item name="level" label="级别">
39
+ * <Input />
40
+ * </Form.Item>
41
+ * </Form>
21
42
  * ),
22
43
  * }
23
44
  *
@@ -27,6 +48,35 @@ import { ComponentSchema } from '../types';
27
48
  * }
28
49
  * ```
29
50
  *
51
+ * @example 容器模式 - 手动控制
52
+ * ```tsx
53
+ * const schema: ComponentSchema = {
54
+ * name: 'alert',
55
+ * label: '警告框',
56
+ * icon: <AlertOutlined />,
57
+ * fields: [{ key: 'level', defaultValue: 'info' }],
58
+ * renderDialog: ({ props, onChange }) => (
59
+ * <Input value={props.level} onChange={e => onChange('level', e.target.value)} />
60
+ * ),
61
+ * }
62
+ * ```
63
+ *
64
+ * @example 容器模式 - 完全自定义弹窗
65
+ * ```tsx
66
+ * const schema: ComponentSchema = {
67
+ * name: 'alert',
68
+ * label: '警告框',
69
+ * icon: <AlertOutlined />,
70
+ * fields: [{ key: 'level', defaultValue: 'info' }],
71
+ * renderDialog: ({ props, onSave }) => (
72
+ * <MyCustomEditor
73
+ * value={props.level}
74
+ * onSave={({ level }) => onSave({ level })}
75
+ * />
76
+ * ),
77
+ * }
78
+ * ```
79
+ *
30
80
  * @example 标签包裹模式
31
81
  * ```tsx
32
82
  * function MyPage() {
package/dist/index.cjs CHANGED
@@ -577,15 +577,16 @@ var Preview = (0, react.forwardRef)(({ content, previewOptions }, ref) => {
577
577
  Preview.displayName = "Preview";
578
578
  //#endregion
579
579
  //#region src/components/Panel/SplitPanels.tsx
580
+ var { Panel } = antd.Splitter;
580
581
  function SplitPanels({ leftPanel, rightPanel }) {
581
582
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(antd.Splitter, {
582
583
  className: "split-panels",
583
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(antd.Splitter.Panel, {
584
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Panel, {
584
585
  defaultSize: "50%",
585
586
  min: "20%",
586
587
  max: "80%",
587
588
  children: leftPanel
588
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(antd.Splitter.Panel, { children: rightPanel })]
589
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Panel, { children: rightPanel })]
589
590
  });
590
591
  }
591
592
  //#endregion
@@ -1066,12 +1067,59 @@ function Toolbar({ editorView, fullscreen = true, onToggleFullscreen, extraItems
1066
1067
  //#endregion
1067
1068
  //#region src/components/Dialog/EditDialog.tsx
1068
1069
  /**
1070
+ * 组件类型到 antd 表单组件的映射
1071
+ */
1072
+ var FormComponents = {
1073
+ input: antd.Input,
1074
+ textarea: antd.Input.TextArea,
1075
+ number: antd.InputNumber,
1076
+ select: antd.Select,
1077
+ switch: antd.Switch,
1078
+ slider: antd.Slider,
1079
+ rate: antd.Rate,
1080
+ color: antd.ColorPicker,
1081
+ date: antd.DatePicker,
1082
+ dateRange: antd.DatePicker.RangePicker
1083
+ };
1084
+ /**
1085
+ * 渲染对应类型的 antd 表单组件
1086
+ */
1087
+ function renderFormItemComponent(field) {
1088
+ const props = field.componentProps || {};
1089
+ const Component = field.componentType ? FormComponents[field.componentType] : antd.Input;
1090
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Component, { ...props });
1091
+ }
1092
+ /**
1093
+ * 根据 fields 自动生成 Form.Item 列表
1094
+ */
1095
+ function renderAutoForm(form, fields) {
1096
+ if (!fields || fields.length === 0) return null;
1097
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(antd.Form, {
1098
+ form,
1099
+ layout: "vertical",
1100
+ children: fields.map((field) => {
1101
+ if (!field.componentType) return null;
1102
+ const formItemProps = {
1103
+ name: field.key,
1104
+ label: field.name || field.key,
1105
+ valuePropName: field.componentType === "switch" ? "checked" : void 0,
1106
+ ...field.componentProps
1107
+ };
1108
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(antd.Form.Item, {
1109
+ ...formItemProps,
1110
+ children: renderFormItemComponent(field)
1111
+ }, field.key);
1112
+ })
1113
+ });
1114
+ }
1115
+ /**
1069
1116
  * 属性编辑弹窗组件
1070
1117
  * 根据组件 schema 的 renderDialog 函数渲染弹窗内容
1071
1118
  */
1072
1119
  function EditDialog({ visible, typeName, currentProps, onSave, onClose }) {
1073
1120
  const schema = componentRegistry.get(typeName);
1074
1121
  const [formProps, setFormProps] = (0, react.useState)({});
1122
+ const [form] = antd.Form.useForm();
1075
1123
  const mergedProps = (0, react.useMemo)(() => {
1076
1124
  if (!schema) return currentProps;
1077
1125
  const defaults = {};
@@ -1081,24 +1129,52 @@ function EditDialog({ visible, typeName, currentProps, onSave, onClose }) {
1081
1129
  ...currentProps
1082
1130
  };
1083
1131
  }, [schema, currentProps]);
1132
+ (0, react.useEffect)(() => {
1133
+ if (visible) {
1134
+ form.setFieldsValue(mergedProps);
1135
+ setFormProps({});
1136
+ }
1137
+ }, [
1138
+ visible,
1139
+ mergedProps,
1140
+ form
1141
+ ]);
1084
1142
  const handleDialogChange = (key, value) => {
1085
1143
  setFormProps((prev) => ({
1086
1144
  ...prev,
1087
1145
  [key]: value
1088
1146
  }));
1089
1147
  };
1090
- const handleOk = () => {
1091
- onSave(visible ? {
1148
+ const handleOk = async () => {
1149
+ let finalProps = visible ? {
1092
1150
  ...mergedProps,
1093
1151
  ...formProps
1094
- } : mergedProps);
1095
- setFormProps({});
1152
+ } : mergedProps;
1153
+ try {
1154
+ const formValues = await form.validateFields();
1155
+ finalProps = {
1156
+ ...finalProps,
1157
+ ...formValues
1158
+ };
1159
+ } catch {}
1160
+ onSave(finalProps);
1096
1161
  };
1097
1162
  const handleClose = () => {
1098
1163
  setFormProps({});
1099
1164
  onClose();
1100
1165
  };
1101
- if (!schema || !schema.renderDialog) return null;
1166
+ if (!schema) return null;
1167
+ const context = {
1168
+ props: mergedProps,
1169
+ form,
1170
+ onChange: handleDialogChange,
1171
+ onSave
1172
+ };
1173
+ const renderDialogContent = () => {
1174
+ if (schema.renderDialog) return schema.renderDialog(context);
1175
+ if (schema.fields?.some((f) => f.componentType) && schema.fields) return renderAutoForm(form, schema.fields);
1176
+ return null;
1177
+ };
1102
1178
  return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(antd.Modal, {
1103
1179
  title: `${schema.label} 属性编辑`,
1104
1180
  open: visible,
@@ -1106,10 +1182,7 @@ function EditDialog({ visible, typeName, currentProps, onSave, onClose }) {
1106
1182
  onCancel: handleClose,
1107
1183
  okText: "保存",
1108
1184
  cancelText: "取消",
1109
- children: schema.renderDialog(visible ? {
1110
- ...mergedProps,
1111
- ...formProps
1112
- } : mergedProps, handleDialogChange)
1185
+ children: renderDialogContent()
1113
1186
  });
1114
1187
  }
1115
1188
  //#endregion
@@ -1219,7 +1292,7 @@ function useScrollSync(sourceRef, targetRef) {
1219
1292
  }
1220
1293
  //#endregion
1221
1294
  //#region src/MarkdownEditor.tsx
1222
- var MarkdownEditor_default = (0, react.forwardRef)(function MarkdownEditor({ defaultValue = "", value, onChange, showPreview = true, fullscreen: fullscreenProp = false, previewOptions, uploadConfig }, ref) {
1295
+ var MarkdownEditor_default = (0, react.forwardRef)(function MarkdownEditor({ defaultValue = "", value, onChange, showPreview = true, fullscreen: fullscreenProp = true, previewOptions, uploadConfig }, ref) {
1223
1296
  const [content, setContent] = (0, react.useState)(value ?? defaultValue);
1224
1297
  const [editorView, setEditorView] = (0, react.useState)(null);
1225
1298
  const scrollDOMRef = (0, react.useRef)(null);
@@ -1238,14 +1311,19 @@ var MarkdownEditor_default = (0, react.forwardRef)(function MarkdownEditor({ def
1238
1311
  onChange?.(newContent);
1239
1312
  }, [onChange]);
1240
1313
  (0, react.useImperativeHandle)(ref, () => ({ getContent: () => content }), [content]);
1241
- const extraToolbarItems = componentRegistry.getAll().map((schema) => {
1314
+ const extraToolbarItems = componentRegistry.getAll().flatMap((schema) => {
1315
+ if (schema.wrapTags) return {
1316
+ id: `wrap-${schema.name}`,
1317
+ label: schema.label,
1318
+ icon: () => schema.icon ?? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_ant_design_icons.ReadOutlined, {}),
1319
+ wrapTags: schema.wrapTags
1320
+ };
1242
1321
  const defaultProps = {};
1243
1322
  for (const field of schema?.fields ?? []) defaultProps[field.key] = field.defaultValue ?? "";
1244
- const iconNode = schema.icon ?? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_ant_design_icons.ReadOutlined, {});
1245
1323
  return {
1246
1324
  id: `container-${schema.name}`,
1247
1325
  label: schema.label,
1248
- icon: () => iconNode,
1326
+ icon: () => schema.icon ?? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_ant_design_icons.ReadOutlined, {}),
1249
1327
  handler: (view, _state) => insertContainer(view, schema.name, defaultProps)
1250
1328
  };
1251
1329
  });
@@ -1272,7 +1350,7 @@ var MarkdownEditor_default = (0, react.forwardRef)(function MarkdownEditor({ def
1272
1350
  setDialogVisible(false);
1273
1351
  }, []);
1274
1352
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1275
- className: fullscreen ? "app fullscreen" : "app",
1353
+ className: fullscreen ? "markdown-editor-app fullscreen" : "markdown-editor-app",
1276
1354
  children: [
1277
1355
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Toolbar, {
1278
1356
  editorView,
@@ -1327,15 +1405,36 @@ var MarkdownEditor_default = (0, react.forwardRef)(function MarkdownEditor({ def
1327
1405
  * 2. 标签包裹模式:name + wrapTags
1328
1406
  * 插入简单的标签包裹文本,如 <plugin-container>...</plugin-container>
1329
1407
  *
1330
- * @example 容器模式
1408
+ * @example 容器模式 - 使用 fields 自动生成表单(最简)
1409
+ * ```tsx
1410
+ * const schema: ComponentSchema = {
1411
+ * name: 'alert',
1412
+ * label: '警告框',
1413
+ * icon: <AlertOutlined />,
1414
+ * fields: [
1415
+ * { key: 'type', name: '类型', componentType: 'select',
1416
+ * componentProps: { options: [{ value: 'price', label: '价格' }, { value: 'info', label: '信息' }] } },
1417
+ * { key: 'content', name: '内容', componentType: 'textarea',
1418
+ * componentProps: { rows: 3 } },
1419
+ * { key: 'enabled', name: '启用', componentType: 'switch' },
1420
+ * ],
1421
+ * }
1422
+ * // 支持的 componentType: 'input' | 'textarea' | 'number' | 'select' | 'switch' | 'slider' | 'rate' | 'color' | 'date' | 'dateRange'
1423
+ * ```
1424
+ *
1425
+ * @example 容器模式 - 使用 Form.Item 自动绑定(推荐)
1331
1426
  * ```tsx
1332
1427
  * const schema: ComponentSchema = {
1333
1428
  * name: 'alert',
1334
1429
  * label: '警告框',
1335
1430
  * icon: <AlertOutlined />,
1336
1431
  * fields: [{ key: 'level', defaultValue: 'info' }],
1337
- * renderDialog: (props, onChange) => (
1338
- * <Input value={props.level} onChange={e => onChange('level', e.target.value)} />
1432
+ * renderDialog: ({ form }) => (
1433
+ * <Form form={form} layout="vertical">
1434
+ * <Form.Item name="level" label="级别">
1435
+ * <Input />
1436
+ * </Form.Item>
1437
+ * </Form>
1339
1438
  * ),
1340
1439
  * }
1341
1440
  *
@@ -1345,6 +1444,35 @@ var MarkdownEditor_default = (0, react.forwardRef)(function MarkdownEditor({ def
1345
1444
  * }
1346
1445
  * ```
1347
1446
  *
1447
+ * @example 容器模式 - 手动控制
1448
+ * ```tsx
1449
+ * const schema: ComponentSchema = {
1450
+ * name: 'alert',
1451
+ * label: '警告框',
1452
+ * icon: <AlertOutlined />,
1453
+ * fields: [{ key: 'level', defaultValue: 'info' }],
1454
+ * renderDialog: ({ props, onChange }) => (
1455
+ * <Input value={props.level} onChange={e => onChange('level', e.target.value)} />
1456
+ * ),
1457
+ * }
1458
+ * ```
1459
+ *
1460
+ * @example 容器模式 - 完全自定义弹窗
1461
+ * ```tsx
1462
+ * const schema: ComponentSchema = {
1463
+ * name: 'alert',
1464
+ * label: '警告框',
1465
+ * icon: <AlertOutlined />,
1466
+ * fields: [{ key: 'level', defaultValue: 'info' }],
1467
+ * renderDialog: ({ props, onSave }) => (
1468
+ * <MyCustomEditor
1469
+ * value={props.level}
1470
+ * onSave={({ level }) => onSave({ level })}
1471
+ * />
1472
+ * ),
1473
+ * }
1474
+ * ```
1475
+ *
1348
1476
  * @example 标签包裹模式
1349
1477
  * ```tsx
1350
1478
  * function MyPage() {