dn-react-text-editor 0.2.3 → 0.3.0

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.
@@ -192,6 +192,496 @@ function cn(...classes) {
192
192
  // src/attach_file.tsx
193
193
  import "prosemirror-view";
194
194
 
195
+ // src/schema.tsx
196
+ import { Schema } from "prosemirror-model";
197
+ import { addListNodes } from "prosemirror-schema-list";
198
+
199
+ // src/plugins/highlighter.ts
200
+ import hljs from "highlight.js";
201
+ import bash from "highlight.js/lib/languages/bash";
202
+ import c from "highlight.js/lib/languages/c";
203
+ import cpp from "highlight.js/lib/languages/cpp";
204
+ import css from "highlight.js/lib/languages/css";
205
+ import java from "highlight.js/lib/languages/java";
206
+ import ts from "highlight.js/lib/languages/typescript";
207
+ import js from "highlight.js/lib/languages/javascript";
208
+ import json from "highlight.js/lib/languages/json";
209
+ import xml from "highlight.js/lib/languages/xml";
210
+ import python from "highlight.js/lib/languages/python";
211
+ import dart from "highlight.js/lib/languages/dart";
212
+ import csharp from "highlight.js/lib/languages/csharp";
213
+ import markdown from "highlight.js/lib/languages/markdown";
214
+ import nginx from "highlight.js/lib/languages/nginx";
215
+ import php from "highlight.js/lib/languages/php";
216
+ import ruby from "highlight.js/lib/languages/ruby";
217
+ import sql from "highlight.js/lib/languages/sql";
218
+ import swift from "highlight.js/lib/languages/swift";
219
+ import yaml from "highlight.js/lib/languages/yaml";
220
+ import rust from "highlight.js/lib/languages/rust";
221
+ hljs.registerLanguage("bash", bash);
222
+ hljs.registerLanguage("c", c);
223
+ hljs.registerLanguage("cpp", cpp);
224
+ hljs.registerLanguage("css", css);
225
+ hljs.registerLanguage("java", java);
226
+ hljs.registerLanguage("markdown", markdown);
227
+ hljs.registerLanguage("nginx", nginx);
228
+ hljs.registerLanguage("php", php);
229
+ hljs.registerLanguage("ruby", ruby);
230
+ hljs.registerLanguage("sql", sql);
231
+ hljs.registerLanguage("swift", swift);
232
+ hljs.registerLanguage("yaml", yaml);
233
+ hljs.registerLanguage("rust", rust);
234
+ hljs.registerLanguage("json", json);
235
+ hljs.registerLanguage("javascript", js);
236
+ hljs.registerLanguage("typescript", ts);
237
+ hljs.registerLanguage("xml", xml);
238
+ hljs.registerLanguage("python", python);
239
+ hljs.registerLanguage("dart", dart);
240
+ hljs.registerLanguage("csharp", csharp);
241
+ var supportedLanguages = [
242
+ "bash",
243
+ "c",
244
+ "cpp",
245
+ "css",
246
+ "java",
247
+ "markdown",
248
+ "nginx",
249
+ "php",
250
+ "ruby",
251
+ "sql",
252
+ "swift",
253
+ "yaml",
254
+ "rust",
255
+ "json",
256
+ "javascript",
257
+ "typescript",
258
+ "xml",
259
+ "python",
260
+ "dart",
261
+ "csharp"
262
+ ];
263
+ var highlighter = hljs;
264
+
265
+ // src/schema.tsx
266
+ function createSchema(spec = { nodes: {}, marks: {} }) {
267
+ const customSchema = new Schema({
268
+ nodes: {
269
+ doc: { content: "block+" },
270
+ paragraph: {
271
+ attrs: { align: { default: null } },
272
+ content: "inline*",
273
+ group: "block",
274
+ parseDOM: [
275
+ {
276
+ tag: "p",
277
+ getAttrs(dom) {
278
+ return {
279
+ align: dom.style.textAlign || null
280
+ };
281
+ }
282
+ }
283
+ ],
284
+ toDOM(node) {
285
+ return [
286
+ "p",
287
+ {
288
+ style: node.attrs.align ? `text-align: ${node.attrs.align}` : null
289
+ },
290
+ 0
291
+ ];
292
+ }
293
+ },
294
+ text: {
295
+ group: "inline"
296
+ },
297
+ hard_break: {
298
+ inline: true,
299
+ group: "inline",
300
+ selectable: false,
301
+ parseDOM: [{ tag: "br" }],
302
+ toDOM() {
303
+ return ["br"];
304
+ }
305
+ },
306
+ heading: {
307
+ attrs: { level: { default: 1 }, align: { default: null } },
308
+ content: "inline*",
309
+ group: "block",
310
+ defining: true,
311
+ parseDOM: [
312
+ {
313
+ tag: "h1",
314
+ getAttrs(node) {
315
+ return {
316
+ level: 1,
317
+ algin: node.style.textAlign || null
318
+ };
319
+ }
320
+ },
321
+ {
322
+ tag: "h2",
323
+ getAttrs(node) {
324
+ return {
325
+ level: 2,
326
+ algin: node.style.textAlign || null
327
+ };
328
+ }
329
+ },
330
+ {
331
+ tag: "h3",
332
+ getAttrs(node) {
333
+ return {
334
+ level: 3,
335
+ algin: node.style.textAlign || null
336
+ };
337
+ }
338
+ },
339
+ {
340
+ tag: "h4",
341
+ getAttrs(node) {
342
+ return {
343
+ level: 4,
344
+ algin: node.style.textAlign || null
345
+ };
346
+ }
347
+ },
348
+ {
349
+ tag: "h5",
350
+ getAttrs(node) {
351
+ return {
352
+ level: 5,
353
+ algin: node.style.textAlign || null
354
+ };
355
+ }
356
+ },
357
+ {
358
+ tag: "h6",
359
+ getAttrs(node) {
360
+ return {
361
+ level: 6,
362
+ algin: node.style.textAlign || null
363
+ };
364
+ }
365
+ }
366
+ ],
367
+ toDOM(node) {
368
+ return [
369
+ "h" + node.attrs.level,
370
+ {
371
+ id: node.textContent.toLowerCase().replace(/\s+/g, "-"),
372
+ style: node.attrs.align ? `text-align: ${node.attrs.align};` : null
373
+ },
374
+ 0
375
+ ];
376
+ }
377
+ },
378
+ horizontal_rule: {
379
+ group: "block",
380
+ parseDOM: [{ tag: "hr" }],
381
+ toDOM() {
382
+ return ["hr"];
383
+ }
384
+ },
385
+ image: {
386
+ attrs: {
387
+ src: { validate: "string" },
388
+ alt: { default: null, validate: "string|null" },
389
+ title: {
390
+ default: null,
391
+ validate: "string|null"
392
+ },
393
+ width: {
394
+ default: null,
395
+ validate: "number|null"
396
+ },
397
+ height: {
398
+ default: null,
399
+ validate: "number|null"
400
+ }
401
+ },
402
+ inline: false,
403
+ group: "block",
404
+ draggable: true,
405
+ parseDOM: [
406
+ {
407
+ tag: "img",
408
+ getAttrs(dom) {
409
+ return {
410
+ src: dom.getAttribute("src"),
411
+ alt: dom.getAttribute("alt"),
412
+ width: dom.getAttribute("width"),
413
+ height: dom.getAttribute("height")
414
+ };
415
+ }
416
+ }
417
+ ],
418
+ toDOM(node) {
419
+ const { src, alt, srcSet, sizes, width, height } = node.attrs;
420
+ return [
421
+ "img",
422
+ {
423
+ src,
424
+ alt,
425
+ srcSet,
426
+ sizes,
427
+ width,
428
+ height
429
+ }
430
+ ];
431
+ }
432
+ },
433
+ video: {
434
+ inline: false,
435
+ group: "block",
436
+ draggable: true,
437
+ attrs: {
438
+ src: { validate: "string" },
439
+ title: {
440
+ default: null,
441
+ validate: "string|null"
442
+ },
443
+ width: {
444
+ default: null,
445
+ validate: "number|null"
446
+ },
447
+ height: {
448
+ default: null,
449
+ validate: "number|null"
450
+ },
451
+ poster: {
452
+ default: null,
453
+ validate: "string|null"
454
+ }
455
+ },
456
+ parseDOM: [
457
+ {
458
+ tag: "video",
459
+ getAttrs(dom) {
460
+ return {
461
+ src: dom.getAttribute("src"),
462
+ title: dom.getAttribute("title"),
463
+ width: dom.getAttribute("width"),
464
+ height: dom.getAttribute("height"),
465
+ poster: dom.getAttribute("poster")
466
+ };
467
+ }
468
+ }
469
+ ],
470
+ toDOM(node) {
471
+ const { src, title, width, height, poster } = node.attrs;
472
+ return [
473
+ "video",
474
+ {
475
+ src,
476
+ title,
477
+ poster,
478
+ width,
479
+ height,
480
+ playsinline: "true",
481
+ controls: "controls",
482
+ style: `aspect-ratio: ${width} / ${height}`
483
+ }
484
+ ];
485
+ }
486
+ },
487
+ iframe: {
488
+ group: "block",
489
+ draggable: true,
490
+ attrs: {
491
+ src: { validate: "string" },
492
+ title: {
493
+ default: null,
494
+ validate: "string|null"
495
+ },
496
+ width: {
497
+ default: null,
498
+ validate: "number|null"
499
+ },
500
+ height: {
501
+ default: null,
502
+ validate: "number|null"
503
+ },
504
+ allow: {
505
+ default: null,
506
+ validate: "string|null"
507
+ },
508
+ allowfullscreen: {
509
+ default: null,
510
+ validate: "string|null"
511
+ },
512
+ referrerPolicy: {
513
+ default: null,
514
+ validate: "string|null"
515
+ },
516
+ style: {
517
+ default: null,
518
+ validate: "string|null"
519
+ }
520
+ },
521
+ parseDOM: [
522
+ {
523
+ tag: "iframe[src]",
524
+ getAttrs(dom) {
525
+ return {
526
+ src: dom.getAttribute("src"),
527
+ title: dom.getAttribute("title"),
528
+ width: dom.getAttribute("width"),
529
+ height: dom.getAttribute("height"),
530
+ style: dom.getAttribute("style"),
531
+ allow: dom.getAttribute("allow"),
532
+ allowfullscreen: dom.getAttribute("allowfullscreen"),
533
+ referrerpolicy: dom.getAttribute("referrerpolicy")
534
+ };
535
+ }
536
+ }
537
+ ],
538
+ toDOM(node) {
539
+ const {
540
+ src,
541
+ title,
542
+ width,
543
+ height,
544
+ allow,
545
+ allowfullscreen,
546
+ referrerpolicy,
547
+ style
548
+ } = node.attrs;
549
+ return [
550
+ "iframe",
551
+ {
552
+ src,
553
+ title,
554
+ width,
555
+ height,
556
+ style,
557
+ allow,
558
+ allowfullscreen,
559
+ referrerpolicy,
560
+ frameborder: "0"
561
+ }
562
+ ];
563
+ }
564
+ },
565
+ blockquote: {
566
+ content: "block+",
567
+ group: "block",
568
+ defining: true,
569
+ parseDOM: [{ tag: "blockquote" }],
570
+ toDOM() {
571
+ return ["blockquote", 0];
572
+ }
573
+ },
574
+ code_block: {
575
+ content: "text*",
576
+ marks: "",
577
+ group: "block",
578
+ code: true,
579
+ defining: true,
580
+ parseDOM: [{ tag: "pre", preserveWhitespace: "full" }],
581
+ toDOM(node) {
582
+ const auto = highlighter.highlightAuto(
583
+ node.textContent,
584
+ supportedLanguages
585
+ );
586
+ return [
587
+ "pre",
588
+ {
589
+ class: "hljs"
590
+ },
591
+ [
592
+ "code",
593
+ {
594
+ class: `language-${auto.language}`
595
+ },
596
+ 0
597
+ ]
598
+ ];
599
+ }
600
+ },
601
+ ...spec.nodes
602
+ },
603
+ marks: {
604
+ link: {
605
+ attrs: {
606
+ href: { default: "" },
607
+ title: { default: null }
608
+ },
609
+ inclusive: false,
610
+ parseDOM: [
611
+ {
612
+ tag: "a[href]",
613
+ getAttrs(dom) {
614
+ return {
615
+ href: dom.getAttribute("href"),
616
+ title: dom.getAttribute("title")
617
+ };
618
+ }
619
+ }
620
+ ],
621
+ toDOM(node) {
622
+ const { href, title } = node.attrs;
623
+ const target = "_blank";
624
+ const rel = "noopener noreferrer";
625
+ return ["a", { href, title: title || href, target, rel }, 0];
626
+ }
627
+ },
628
+ bold: {
629
+ parseDOM: [
630
+ { tag: "strong" },
631
+ {
632
+ tag: "b",
633
+ getAttrs: (node) => node.style.fontWeight != "normal" && null
634
+ },
635
+ {
636
+ style: "font-weight=400",
637
+ clearMark: (m) => m.type.name == "strong"
638
+ },
639
+ {
640
+ style: "font-weight",
641
+ getAttrs: (value) => /^(bold(er)?|[5-9]\d{2,})$/.test(value) && null
642
+ }
643
+ ],
644
+ toDOM() {
645
+ return ["strong", 0];
646
+ }
647
+ },
648
+ italic: {
649
+ parseDOM: [
650
+ { tag: "em" },
651
+ { tag: "i" },
652
+ { style: "font-style=italic" },
653
+ {
654
+ style: "font-style=normal",
655
+ clearMark: (m) => m.type.name == "em"
656
+ }
657
+ ],
658
+ toDOM() {
659
+ return ["em", 0];
660
+ }
661
+ },
662
+ underline: {
663
+ parseDOM: [
664
+ { tag: "u" },
665
+ {
666
+ style: "text-decoration",
667
+ getAttrs: (value) => value === "underline" && null
668
+ }
669
+ ],
670
+ toDOM() {
671
+ return ["u", 0];
672
+ }
673
+ },
674
+ ...spec.marks
675
+ },
676
+ topNode: spec.topNode
677
+ });
678
+ const prosemirrorSchema = new Schema({
679
+ nodes: addListNodes(customSchema.spec.nodes, "paragraph block*", "block"),
680
+ marks: customSchema.spec.marks
681
+ });
682
+ return prosemirrorSchema;
683
+ }
684
+
195
685
  // src/base64_file_uploader.ts
196
686
  var base64FileUploader = async (file) => {
197
687
  const base64 = await new Promise((resolve, reject) => {
@@ -351,120 +841,127 @@ var createCommands = (schema, view, options = {}) => {
351
841
  // src/text_editor_controller.tsx
352
842
  import { DOMParser, DOMSerializer } from "prosemirror-model";
353
843
  import { Subject } from "rxjs";
354
- function createTextEditorController(container, schema, options, {
355
- mode = "html",
356
- state,
357
- editor,
358
- defaultValue,
359
- updateDelay = 500,
360
- placeholder
361
- }) {
362
- const subject = new Subject();
363
- const prosemirrorParser = DOMParser.fromSchema(schema);
364
- const prosemirrorSerializer = DOMSerializer.fromSchema(schema);
365
- const wrapper = document.createElement("div");
366
- const toInnerHTML = (value) => {
367
- if (mode === "text") {
844
+ var TextEditorController = class {
845
+ schema;
846
+ options;
847
+ props;
848
+ subject;
849
+ view;
850
+ prosemirrorParser;
851
+ prosemirrorSerializer;
852
+ get value() {
853
+ if (this.props.mode === "text") {
854
+ return this.toTextContent();
855
+ }
856
+ return this.toHTML();
857
+ }
858
+ set value(value) {
859
+ const wrap = document.createElement("div");
860
+ wrap.innerHTML = this.toInnerHTML(value);
861
+ const doc = this.prosemirrorParser.parse(wrap);
862
+ const tr = this.view.state.tr.replaceWith(
863
+ 0,
864
+ this.view.state.doc.content.size,
865
+ doc.content
866
+ );
867
+ this.view.dispatch(tr);
868
+ }
869
+ constructor(options = {}, props = {}) {
870
+ this.schema = createSchema();
871
+ this.options = options;
872
+ this.props = props;
873
+ this.subject = new Subject();
874
+ this.prosemirrorParser = DOMParser.fromSchema(this.schema);
875
+ this.prosemirrorSerializer = DOMSerializer.fromSchema(this.schema);
876
+ }
877
+ toInnerHTML(value) {
878
+ if (this.props.mode === "text") {
368
879
  return value.split("\n").map((line) => `<p>${line}</p>`).join("");
369
880
  }
370
881
  return value;
371
- };
372
- wrapper.innerHTML = toInnerHTML(defaultValue ? String(defaultValue) : "");
373
- const attachFile = createAttachFile({
374
- schema,
375
- generateMetadata: options.attachFile?.generateMetadata,
376
- uploadFile: options.attachFile?.uploadFile
377
- });
378
- const view = new EditorView3(container, {
379
- ...editor,
380
- attributes: (state2) => {
381
- const propsAttributes = (() => {
382
- if (typeof editor?.attributes === "function") {
383
- return editor.attributes(state2);
882
+ }
883
+ attachFile(files) {
884
+ return createAttachFile({
885
+ schema: this.schema,
886
+ generateMetadata: this.options.attachFile?.generateMetadata,
887
+ uploadFile: this.options.attachFile?.uploadFile
888
+ })(this.view, files);
889
+ }
890
+ bind(container) {
891
+ const wrapper = document.createElement("div");
892
+ wrapper.innerHTML = this.toInnerHTML(
893
+ this.props.defaultValue ? String(this.props.defaultValue) : ""
894
+ );
895
+ this.view = new EditorView3(container, {
896
+ ...this.props.editor,
897
+ attributes: (state) => {
898
+ const propsAttributes = (() => {
899
+ if (typeof this.props.editor?.attributes === "function") {
900
+ return this.props.editor.attributes(state);
901
+ }
902
+ return this.props.editor?.attributes;
903
+ })();
904
+ return {
905
+ ...propsAttributes,
906
+ class: cn(this.options?.className, propsAttributes?.class),
907
+ spellcheck: propsAttributes?.spellcheck || "false",
908
+ style: this.options.style || "width: 100%; height: inherit; outline: none;"
909
+ };
910
+ },
911
+ state: EditorState2.create({
912
+ ...this.props.state,
913
+ schema: this.props.state?.schema || this.schema,
914
+ doc: this.props.state?.doc || this.prosemirrorParser.parse(wrapper),
915
+ plugins: [
916
+ ...this.props.state?.plugins || [],
917
+ history({
918
+ newGroupDelay: this.props.updateDelay
919
+ }),
920
+ keymap(buildKeymap(this.schema)),
921
+ keymap(commands2.baseKeymap),
922
+ uploadPlaceholderPlugin,
923
+ dragAndDropPlugin({
924
+ attachFile: (view, files) => this.attachFile(files)
925
+ }),
926
+ this.props.placeholder && placeholderPlugin(this.props.placeholder)
927
+ ].filter((e) => !!e)
928
+ }),
929
+ dispatchTransaction: (tr) => {
930
+ let result;
931
+ if (this.props.editor?.dispatchTransaction) {
932
+ result = this.props.editor.dispatchTransaction(tr);
933
+ } else {
934
+ this.view?.updateState(this.view.state.apply(tr));
384
935
  }
385
- return editor?.attributes;
386
- })();
387
- return {
388
- ...propsAttributes,
389
- class: cn(options?.className, propsAttributes?.class),
390
- spellcheck: propsAttributes?.spellcheck || "false",
391
- style: options.style || "width: 100%; height: inherit; outline: none;"
392
- };
393
- },
394
- state: EditorState2.create({
395
- ...state,
396
- schema: state?.schema || schema,
397
- doc: state?.doc || prosemirrorParser.parse(wrapper),
398
- plugins: [
399
- ...state?.plugins || [],
400
- history({
401
- newGroupDelay: updateDelay
402
- }),
403
- keymap(buildKeymap(schema)),
404
- keymap(commands2.baseKeymap),
405
- uploadPlaceholderPlugin,
406
- dragAndDropPlugin({
407
- attachFile
408
- }),
409
- placeholder && placeholderPlugin(placeholder)
410
- ].filter((e) => !!e)
411
- }),
412
- dispatchTransaction(tr) {
413
- let result;
414
- if (editor?.dispatchTransaction) {
415
- result = editor.dispatchTransaction(tr);
416
- } else {
417
- view.updateState(view.state.apply(tr));
936
+ this.subject.next(tr);
937
+ return result;
418
938
  }
419
- subject.next(tr);
420
- return result;
939
+ });
940
+ if (this.props.autoFocus) {
941
+ this.view?.focus();
421
942
  }
422
- });
423
- function setValue(value) {
424
- const wrap = document.createElement("div");
425
- wrap.innerHTML = toInnerHTML(value);
426
- const doc = prosemirrorParser.parse(wrap);
427
- const tr = view.state.tr.replaceWith(
428
- 0,
429
- view.state.doc.content.size,
430
- doc.content
431
- );
432
- view.dispatch(tr);
433
943
  }
434
- function toHTML() {
435
- const fragment = prosemirrorSerializer.serializeFragment(
436
- view.state.doc.content
944
+ toHTML() {
945
+ const fragment = this.prosemirrorSerializer.serializeFragment(
946
+ this.view.state.doc.content
437
947
  );
438
- const container2 = document.createElement("div");
439
- container2.appendChild(fragment);
440
- return container2.innerHTML;
948
+ const container = document.createElement("div");
949
+ container.appendChild(fragment);
950
+ return container.innerHTML;
441
951
  }
442
- function toTextContent() {
443
- const state2 = view.state;
444
- return state2.doc.textBetween(0, state2.doc.content.size, "\n");
952
+ toTextContent() {
953
+ const state = this.view.state;
954
+ return state.doc.textBetween(0, state.doc.content.size, "\n");
445
955
  }
446
- const textEditorCommands = createCommands(schema, view, {
447
- attachFile
448
- });
449
- const textEditorController = {
450
- schema,
451
- view,
452
- subject,
453
- set value(value) {
454
- setValue(value);
455
- },
456
- get value() {
457
- switch (mode) {
458
- case "text":
459
- return toTextContent();
460
- default:
461
- return toHTML();
462
- }
463
- },
464
- commands: textEditorCommands
465
- };
466
- return textEditorController;
467
- }
956
+ get commands() {
957
+ return createCommands(this.schema, this.view, {
958
+ attachFile: (view, files) => this.attachFile(files)
959
+ });
960
+ }
961
+ dispose() {
962
+ this.view?.destroy();
963
+ }
964
+ };
468
965
  export {
469
- createTextEditorController
966
+ TextEditorController
470
967
  };