dn-react-text-editor 0.3.0 → 0.3.2

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/index.mjs CHANGED
@@ -1,4 +1,4 @@
1
- // src/create_text_editor.tsx
1
+ // src/text_editor.tsx
2
2
  import React2, {
3
3
  useMemo
4
4
  } from "react";
@@ -9,17 +9,12 @@ import React, {
9
9
  useEffect
10
10
  } from "react";
11
11
  import { debounceTime, filter } from "rxjs";
12
- function TextEditorInput({
13
- controller,
14
- onChange,
15
- updateDelay = 0,
16
- ...props
17
- }) {
12
+ function TextEditorInput({ controller, onChange, ...props }) {
18
13
  const inputRef = React.useRef(null);
19
14
  useEffect(() => {
20
15
  const sub = controller.subject.pipe(
21
16
  filter((tr) => tr.docChanged),
22
- debounceTime(updateDelay)
17
+ debounceTime(controller.props.updateDelay || 0)
23
18
  ).subscribe(() => {
24
19
  if (inputRef.current) {
25
20
  inputRef.current.value = controller.value;
@@ -31,7 +26,16 @@ function TextEditorInput({
31
26
  sub.unsubscribe();
32
27
  };
33
28
  }, []);
34
- return /* @__PURE__ */ React.createElement("input", { ...props, ref: inputRef, type: "hidden", onInput: onChange });
29
+ return /* @__PURE__ */ React.createElement(
30
+ "input",
31
+ {
32
+ ...props,
33
+ ref: inputRef,
34
+ type: "hidden",
35
+ onInput: onChange,
36
+ defaultValue: controller.props.defaultValue
37
+ }
38
+ );
35
39
  }
36
40
 
37
41
  // src/text_editor_controller.tsx
@@ -184,23 +188,6 @@ function buildKeymap(schema) {
184
188
  bind("Mod-z", undo);
185
189
  bind("Shift-Mod-z", redo);
186
190
  bind("Mod-y", redo);
187
- const li = schema.nodes.list_item;
188
- bind(
189
- "Enter",
190
- chainCommands(splitListItem(li), (state, dispatch) => {
191
- const { $head } = state.selection;
192
- if ($head.parent.type === state.schema.nodes.paragraph) {
193
- splitBlockAs((n) => {
194
- return {
195
- type: n.type,
196
- attrs: n.attrs
197
- };
198
- })(state, dispatch);
199
- return true;
200
- }
201
- return false;
202
- })
203
- );
204
191
  bind("ArrowDown", (state, dispatch) => {
205
192
  const doc = state.doc;
206
193
  const lastNode = doc.lastChild;
@@ -217,6 +204,51 @@ function buildKeymap(schema) {
217
204
  }
218
205
  return false;
219
206
  });
207
+ const li = schema.nodes.list_item;
208
+ bind(
209
+ "Enter",
210
+ chainCommands(
211
+ splitListItem(li),
212
+ (state, dispatch) => {
213
+ const { $head } = state.selection;
214
+ if ($head.parent.type === state.schema.nodes.paragraph) {
215
+ splitBlockAs((n) => {
216
+ return {
217
+ type: n.type,
218
+ attrs: n.attrs
219
+ };
220
+ })(state, dispatch);
221
+ return true;
222
+ }
223
+ return false;
224
+ },
225
+ (state, dispatch) => {
226
+ const { selection } = state;
227
+ const { $from, $to } = selection;
228
+ const lines = state.doc.textBetween($from.before(), $to.pos).split("\n");
229
+ const currentLine = lines[lines.length - 1];
230
+ const match = currentLine.match(/^(\s+).*$/);
231
+ if (match) {
232
+ if (dispatch) {
233
+ dispatch(state.tr.insertText("\n" + match[1], $from.pos));
234
+ }
235
+ return true;
236
+ }
237
+ return false;
238
+ }
239
+ )
240
+ );
241
+ bind("Tab", (state, dispatch) => {
242
+ const { selection } = state;
243
+ const { $from, $to } = selection;
244
+ if ($from.parent.type === schema.nodes.code_block) {
245
+ if (dispatch) {
246
+ dispatch(state.tr.insertText(" ", $from.pos, $to.pos));
247
+ }
248
+ return true;
249
+ }
250
+ return false;
251
+ });
220
252
  return keys;
221
253
  }
222
254
 
@@ -788,7 +820,12 @@ function createAttachFile({
788
820
  if (!node) {
789
821
  return;
790
822
  }
791
- view.dispatch(tr2.replaceWith($pos, $pos, node));
823
+ const current = view.state.doc.resolve($pos);
824
+ if (current.parentOffset === 0) {
825
+ view.dispatch(tr2.replaceWith($pos - 1, $pos, node));
826
+ } else {
827
+ view.dispatch(tr2.replaceWith($pos, $pos, node));
828
+ }
792
829
  } catch (e) {
793
830
  view.dispatch(tr.setMeta(uploadPlaceholderPlugin, { remove: { id } }));
794
831
  }
@@ -877,9 +914,9 @@ var createCommands = (schema, view, options = {}) => {
877
914
  // src/text_editor_controller.tsx
878
915
  import { DOMParser, DOMSerializer } from "prosemirror-model";
879
916
  import { Subject } from "rxjs";
917
+ import { highlightPlugin } from "prosemirror-highlightjs";
880
918
  var TextEditorController = class {
881
919
  schema;
882
- options;
883
920
  props;
884
921
  subject;
885
922
  view;
@@ -902,9 +939,8 @@ var TextEditorController = class {
902
939
  );
903
940
  this.view.dispatch(tr);
904
941
  }
905
- constructor(options = {}, props = {}) {
942
+ constructor(props = {}) {
906
943
  this.schema = createSchema();
907
- this.options = options;
908
944
  this.props = props;
909
945
  this.subject = new Subject();
910
946
  this.prosemirrorParser = DOMParser.fromSchema(this.schema);
@@ -919,8 +955,8 @@ var TextEditorController = class {
919
955
  attachFile(files) {
920
956
  return createAttachFile({
921
957
  schema: this.schema,
922
- generateMetadata: this.options.attachFile?.generateMetadata,
923
- uploadFile: this.options.attachFile?.uploadFile
958
+ generateMetadata: this.props.attachFile?.generateMetadata,
959
+ uploadFile: this.props.attachFile?.uploadFile
924
960
  })(this.view, files);
925
961
  }
926
962
  bind(container) {
@@ -939,9 +975,9 @@ var TextEditorController = class {
939
975
  })();
940
976
  return {
941
977
  ...propsAttributes,
942
- class: cn(this.options?.className, propsAttributes?.class),
978
+ class: cn(this.props.className, propsAttributes?.class),
943
979
  spellcheck: propsAttributes?.spellcheck || "false",
944
- style: this.options.style || "width: 100%; height: inherit; outline: none;"
980
+ style: this.props.style || "width: 100%; height: inherit; outline: none;"
945
981
  };
946
982
  },
947
983
  state: EditorState2.create({
@@ -959,7 +995,11 @@ var TextEditorController = class {
959
995
  dragAndDropPlugin({
960
996
  attachFile: (view, files) => this.attachFile(files)
961
997
  }),
962
- this.props.placeholder && placeholderPlugin(this.props.placeholder)
998
+ this.props.placeholder && placeholderPlugin(this.props.placeholder),
999
+ highlightPlugin(highlighter, ["code_block"], (node) => {
1000
+ const auto = highlighter.highlightAuto(node.textContent);
1001
+ return auto.language || "";
1002
+ })
963
1003
  ].filter((e) => !!e)
964
1004
  }),
965
1005
  dispatchTransaction: (tr) => {
@@ -998,57 +1038,66 @@ var TextEditorController = class {
998
1038
  this.view?.destroy();
999
1039
  }
1000
1040
  };
1041
+ var configTextEditorController = (options = {}) => {
1042
+ return (props) => new TextEditorController({
1043
+ ...props,
1044
+ className: props.className || options.className,
1045
+ style: props.style || options.style,
1046
+ attachFile: props.attachFile || options.attachFile
1047
+ });
1048
+ };
1001
1049
 
1002
- // src/create_text_editor.tsx
1003
- function createTextEditor(options = {}) {
1004
- function Component({
1005
- controller: externalController,
1006
- className,
1007
- autoFocus,
1008
- onChange,
1009
- mode,
1010
- state,
1011
- editor,
1012
- defaultValue,
1013
- updateDelay,
1014
- placeholder,
1015
- ...props
1016
- } = {}) {
1017
- const containerRef = useRef(null);
1018
- const innerController = useMemo(
1019
- () => new TextEditorController(options, {
1020
- mode,
1021
- state,
1022
- editor,
1023
- autoFocus,
1024
- placeholder,
1025
- updateDelay,
1026
- defaultValue
1027
- }),
1028
- []
1029
- );
1030
- const controller = externalController || innerController;
1031
- useEffect2(() => {
1032
- const container = containerRef.current;
1033
- if (!container) {
1034
- return;
1035
- }
1036
- controller.bind(container);
1037
- return () => {
1038
- controller.dispose();
1039
- };
1040
- }, [controller]);
1041
- return /* @__PURE__ */ React2.createElement(React2.Fragment, null, /* @__PURE__ */ React2.createElement("div", { ...props, ref: containerRef, className }), /* @__PURE__ */ React2.createElement(
1042
- TextEditorInput,
1043
- {
1044
- controller,
1045
- updateDelay,
1046
- defaultValue,
1047
- onChange
1048
- }
1049
- ));
1050
- }
1051
- return Component;
1050
+ // src/text_editor.tsx
1051
+ function TextEditor({
1052
+ controller: externalController,
1053
+ name,
1054
+ className,
1055
+ autoFocus,
1056
+ onChange,
1057
+ mode,
1058
+ state,
1059
+ editor,
1060
+ defaultValue,
1061
+ updateDelay,
1062
+ placeholder,
1063
+ attachFile,
1064
+ style,
1065
+ ...props
1066
+ } = {}) {
1067
+ const containerRef = useRef(null);
1068
+ const innerController = useMemo(
1069
+ () => new TextEditorController({
1070
+ mode,
1071
+ state,
1072
+ editor,
1073
+ autoFocus,
1074
+ placeholder,
1075
+ updateDelay,
1076
+ defaultValue,
1077
+ attachFile,
1078
+ style
1079
+ }),
1080
+ []
1081
+ );
1082
+ const controller = externalController || innerController;
1083
+ useEffect2(() => {
1084
+ const container = containerRef.current;
1085
+ if (!container) {
1086
+ return;
1087
+ }
1088
+ controller.bind(container);
1089
+ return () => {
1090
+ controller.dispose();
1091
+ };
1092
+ }, [controller]);
1093
+ return /* @__PURE__ */ React2.createElement(React2.Fragment, null, /* @__PURE__ */ React2.createElement("div", { ...props, ref: containerRef, className }), /* @__PURE__ */ React2.createElement(
1094
+ TextEditorInput,
1095
+ {
1096
+ name,
1097
+ controller,
1098
+ onChange
1099
+ }
1100
+ ));
1052
1101
  }
1053
1102
 
1054
1103
  // src/html.tsx
@@ -1058,11 +1107,12 @@ function createInnerHTML(raw) {
1058
1107
  return raw.replace(/<\/p>/g, "<br></p>").replace(/(<p><br><\/p>)+$/g, "").replace(
1059
1108
  /<code class="language-(\w+)">([\s\S]*?)<\/code>/g,
1060
1109
  (_, lang, code) => {
1110
+ if (lang === "undefined") {
1111
+ return `<code>${decode(code)}</code>`;
1112
+ }
1061
1113
  try {
1062
- const highlighted = highlighter.highlight(code, {
1063
- language: lang
1064
- }).value;
1065
- return `<code class="language-${lang}">${decode(highlighted)}</code>`;
1114
+ const { language, value } = highlighter.highlightAuto(code);
1115
+ return `<code class="language-${language}">${decode(value)}</code>`;
1066
1116
  } catch (e) {
1067
1117
  return `<code class="language-${lang}">${decode(code)}</code>`;
1068
1118
  }
@@ -1095,10 +1145,11 @@ function createTextEditorView(options = {}) {
1095
1145
  };
1096
1146
  }
1097
1147
  export {
1148
+ TextEditor,
1098
1149
  TextEditorController,
1150
+ configTextEditorController,
1099
1151
  createAttachFile,
1100
1152
  createInnerHTML,
1101
1153
  createSchema,
1102
- createTextEditor,
1103
1154
  createTextEditorView
1104
1155
  };
package/dist/input.d.mts CHANGED
@@ -10,10 +10,9 @@ import 'rxjs';
10
10
 
11
11
  type Props = Omit<DetailedHTMLProps<HTMLAttributes<HTMLDivElement>, HTMLDivElement>, "ref"> & {
12
12
  controller: TextEditorController;
13
- updateDelay?: number;
14
13
  name?: string;
15
14
  onChange?: (e: React.ChangeEvent<HTMLInputElement>) => void;
16
15
  };
17
- declare function TextEditorInput({ controller, onChange, updateDelay, ...props }: Props): React.JSX.Element;
16
+ declare function TextEditorInput({ controller, onChange, ...props }: Props): React.JSX.Element;
18
17
 
19
18
  export { TextEditorInput };
package/dist/input.d.ts CHANGED
@@ -10,10 +10,9 @@ import 'rxjs';
10
10
 
11
11
  type Props = Omit<DetailedHTMLProps<HTMLAttributes<HTMLDivElement>, HTMLDivElement>, "ref"> & {
12
12
  controller: TextEditorController;
13
- updateDelay?: number;
14
13
  name?: string;
15
14
  onChange?: (e: React.ChangeEvent<HTMLInputElement>) => void;
16
15
  };
17
- declare function TextEditorInput({ controller, onChange, updateDelay, ...props }: Props): React.JSX.Element;
16
+ declare function TextEditorInput({ controller, onChange, ...props }: Props): React.JSX.Element;
18
17
 
19
18
  export { TextEditorInput };
package/dist/input.js CHANGED
@@ -35,17 +35,12 @@ __export(input_exports, {
35
35
  module.exports = __toCommonJS(input_exports);
36
36
  var import_react = __toESM(require("react"));
37
37
  var import_rxjs = require("rxjs");
38
- function TextEditorInput({
39
- controller,
40
- onChange,
41
- updateDelay = 0,
42
- ...props
43
- }) {
38
+ function TextEditorInput({ controller, onChange, ...props }) {
44
39
  const inputRef = import_react.default.useRef(null);
45
40
  (0, import_react.useEffect)(() => {
46
41
  const sub = controller.subject.pipe(
47
42
  (0, import_rxjs.filter)((tr) => tr.docChanged),
48
- (0, import_rxjs.debounceTime)(updateDelay)
43
+ (0, import_rxjs.debounceTime)(controller.props.updateDelay || 0)
49
44
  ).subscribe(() => {
50
45
  if (inputRef.current) {
51
46
  inputRef.current.value = controller.value;
@@ -57,7 +52,16 @@ function TextEditorInput({
57
52
  sub.unsubscribe();
58
53
  };
59
54
  }, []);
60
- return /* @__PURE__ */ import_react.default.createElement("input", { ...props, ref: inputRef, type: "hidden", onInput: onChange });
55
+ return /* @__PURE__ */ import_react.default.createElement(
56
+ "input",
57
+ {
58
+ ...props,
59
+ ref: inputRef,
60
+ type: "hidden",
61
+ onInput: onChange,
62
+ defaultValue: controller.props.defaultValue
63
+ }
64
+ );
61
65
  }
62
66
  // Annotate the CommonJS export names for ESM import in node:
63
67
  0 && (module.exports = {
package/dist/input.mjs CHANGED
@@ -3,17 +3,12 @@ import React, {
3
3
  useEffect
4
4
  } from "react";
5
5
  import { debounceTime, filter } from "rxjs";
6
- function TextEditorInput({
7
- controller,
8
- onChange,
9
- updateDelay = 0,
10
- ...props
11
- }) {
6
+ function TextEditorInput({ controller, onChange, ...props }) {
12
7
  const inputRef = React.useRef(null);
13
8
  useEffect(() => {
14
9
  const sub = controller.subject.pipe(
15
10
  filter((tr) => tr.docChanged),
16
- debounceTime(updateDelay)
11
+ debounceTime(controller.props.updateDelay || 0)
17
12
  ).subscribe(() => {
18
13
  if (inputRef.current) {
19
14
  inputRef.current.value = controller.value;
@@ -25,7 +20,16 @@ function TextEditorInput({
25
20
  sub.unsubscribe();
26
21
  };
27
22
  }, []);
28
- return /* @__PURE__ */ React.createElement("input", { ...props, ref: inputRef, type: "hidden", onInput: onChange });
23
+ return /* @__PURE__ */ React.createElement(
24
+ "input",
25
+ {
26
+ ...props,
27
+ ref: inputRef,
28
+ type: "hidden",
29
+ onInput: onChange,
30
+ defaultValue: controller.props.defaultValue
31
+ }
32
+ );
29
33
  }
30
34
  export {
31
35
  TextEditorInput
@@ -35,23 +35,6 @@ function buildKeymap(schema) {
35
35
  bind("Mod-z", import_prosemirror_history.undo);
36
36
  bind("Shift-Mod-z", import_prosemirror_history.redo);
37
37
  bind("Mod-y", import_prosemirror_history.redo);
38
- const li = schema.nodes.list_item;
39
- bind(
40
- "Enter",
41
- (0, import_prosemirror_commands.chainCommands)((0, import_prosemirror_schema_list.splitListItem)(li), (state, dispatch) => {
42
- const { $head } = state.selection;
43
- if ($head.parent.type === state.schema.nodes.paragraph) {
44
- (0, import_prosemirror_commands.splitBlockAs)((n) => {
45
- return {
46
- type: n.type,
47
- attrs: n.attrs
48
- };
49
- })(state, dispatch);
50
- return true;
51
- }
52
- return false;
53
- })
54
- );
55
38
  bind("ArrowDown", (state, dispatch) => {
56
39
  const doc = state.doc;
57
40
  const lastNode = doc.lastChild;
@@ -68,6 +51,51 @@ function buildKeymap(schema) {
68
51
  }
69
52
  return false;
70
53
  });
54
+ const li = schema.nodes.list_item;
55
+ bind(
56
+ "Enter",
57
+ (0, import_prosemirror_commands.chainCommands)(
58
+ (0, import_prosemirror_schema_list.splitListItem)(li),
59
+ (state, dispatch) => {
60
+ const { $head } = state.selection;
61
+ if ($head.parent.type === state.schema.nodes.paragraph) {
62
+ (0, import_prosemirror_commands.splitBlockAs)((n) => {
63
+ return {
64
+ type: n.type,
65
+ attrs: n.attrs
66
+ };
67
+ })(state, dispatch);
68
+ return true;
69
+ }
70
+ return false;
71
+ },
72
+ (state, dispatch) => {
73
+ const { selection } = state;
74
+ const { $from, $to } = selection;
75
+ const lines = state.doc.textBetween($from.before(), $to.pos).split("\n");
76
+ const currentLine = lines[lines.length - 1];
77
+ const match = currentLine.match(/^(\s+).*$/);
78
+ if (match) {
79
+ if (dispatch) {
80
+ dispatch(state.tr.insertText("\n" + match[1], $from.pos));
81
+ }
82
+ return true;
83
+ }
84
+ return false;
85
+ }
86
+ )
87
+ );
88
+ bind("Tab", (state, dispatch) => {
89
+ const { selection } = state;
90
+ const { $from, $to } = selection;
91
+ if ($from.parent.type === schema.nodes.code_block) {
92
+ if (dispatch) {
93
+ dispatch(state.tr.insertText(" ", $from.pos, $to.pos));
94
+ }
95
+ return true;
96
+ }
97
+ return false;
98
+ });
71
99
  return keys;
72
100
  }
73
101
  // Annotate the CommonJS export names for ESM import in node:
@@ -11,23 +11,6 @@ function buildKeymap(schema) {
11
11
  bind("Mod-z", undo);
12
12
  bind("Shift-Mod-z", redo);
13
13
  bind("Mod-y", redo);
14
- const li = schema.nodes.list_item;
15
- bind(
16
- "Enter",
17
- chainCommands(splitListItem(li), (state, dispatch) => {
18
- const { $head } = state.selection;
19
- if ($head.parent.type === state.schema.nodes.paragraph) {
20
- splitBlockAs((n) => {
21
- return {
22
- type: n.type,
23
- attrs: n.attrs
24
- };
25
- })(state, dispatch);
26
- return true;
27
- }
28
- return false;
29
- })
30
- );
31
14
  bind("ArrowDown", (state, dispatch) => {
32
15
  const doc = state.doc;
33
16
  const lastNode = doc.lastChild;
@@ -44,6 +27,51 @@ function buildKeymap(schema) {
44
27
  }
45
28
  return false;
46
29
  });
30
+ const li = schema.nodes.list_item;
31
+ bind(
32
+ "Enter",
33
+ chainCommands(
34
+ splitListItem(li),
35
+ (state, dispatch) => {
36
+ const { $head } = state.selection;
37
+ if ($head.parent.type === state.schema.nodes.paragraph) {
38
+ splitBlockAs((n) => {
39
+ return {
40
+ type: n.type,
41
+ attrs: n.attrs
42
+ };
43
+ })(state, dispatch);
44
+ return true;
45
+ }
46
+ return false;
47
+ },
48
+ (state, dispatch) => {
49
+ const { selection } = state;
50
+ const { $from, $to } = selection;
51
+ const lines = state.doc.textBetween($from.before(), $to.pos).split("\n");
52
+ const currentLine = lines[lines.length - 1];
53
+ const match = currentLine.match(/^(\s+).*$/);
54
+ if (match) {
55
+ if (dispatch) {
56
+ dispatch(state.tr.insertText("\n" + match[1], $from.pos));
57
+ }
58
+ return true;
59
+ }
60
+ return false;
61
+ }
62
+ )
63
+ );
64
+ bind("Tab", (state, dispatch) => {
65
+ const { selection } = state;
66
+ const { $from, $to } = selection;
67
+ if ($from.parent.type === schema.nodes.code_block) {
68
+ if (dispatch) {
69
+ dispatch(state.tr.insertText(" ", $from.pos, $to.pos));
70
+ }
71
+ return true;
72
+ }
73
+ return false;
74
+ });
47
75
  return keys;
48
76
  }
49
77
  export {
@@ -0,0 +1,18 @@
1
+ import React, { DetailedHTMLProps, InputHTMLAttributes } from 'react';
2
+ import { TextEditorController, TextEditorControllerProps } from './text_editor_controller.mjs';
3
+ import 'prosemirror-model';
4
+ import 'prosemirror-state';
5
+ import 'prosemirror-view';
6
+ import './attach_file.mjs';
7
+ import './schema.mjs';
8
+ import 'orderedmap';
9
+ import 'rxjs';
10
+
11
+ type HTMLElementProps = DetailedHTMLProps<InputHTMLAttributes<HTMLInputElement>, HTMLInputElement>;
12
+ type TextEditorProps = Omit<HTMLElementProps, "ref"> & {
13
+ controller?: TextEditorController;
14
+ name?: string;
15
+ } & TextEditorControllerProps;
16
+ declare function TextEditor({ controller: externalController, name, className, autoFocus, onChange, mode, state, editor, defaultValue, updateDelay, placeholder, attachFile, style, ...props }?: TextEditorProps): React.JSX.Element;
17
+
18
+ export { TextEditor, type TextEditorProps };
@@ -0,0 +1,18 @@
1
+ import React, { DetailedHTMLProps, InputHTMLAttributes } from 'react';
2
+ import { TextEditorController, TextEditorControllerProps } from './text_editor_controller.js';
3
+ import 'prosemirror-model';
4
+ import 'prosemirror-state';
5
+ import 'prosemirror-view';
6
+ import './attach_file.js';
7
+ import './schema.js';
8
+ import 'orderedmap';
9
+ import 'rxjs';
10
+
11
+ type HTMLElementProps = DetailedHTMLProps<InputHTMLAttributes<HTMLInputElement>, HTMLInputElement>;
12
+ type TextEditorProps = Omit<HTMLElementProps, "ref"> & {
13
+ controller?: TextEditorController;
14
+ name?: string;
15
+ } & TextEditorControllerProps;
16
+ declare function TextEditor({ controller: externalController, name, className, autoFocus, onChange, mode, state, editor, defaultValue, updateDelay, placeholder, attachFile, style, ...props }?: TextEditorProps): React.JSX.Element;
17
+
18
+ export { TextEditor, type TextEditorProps };