suneditor 3.0.0-rc.5 → 3.0.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.
Files changed (118) hide show
  1. package/README.md +3 -2
  2. package/dist/suneditor-contents.min.css +1 -1
  3. package/dist/suneditor.min.css +1 -1
  4. package/dist/suneditor.min.js +1 -1
  5. package/package.json +2 -3
  6. package/src/assets/design/color.css +14 -2
  7. package/src/assets/design/typography.css +5 -0
  8. package/src/assets/icons/defaultIcons.js +22 -4
  9. package/src/assets/suneditor-contents.css +1 -1
  10. package/src/assets/suneditor.css +312 -18
  11. package/src/core/config/eventManager.js +6 -9
  12. package/src/core/editor.js +1 -1
  13. package/src/core/event/actions/index.js +5 -0
  14. package/src/core/event/effects/keydown.registry.js +25 -0
  15. package/src/core/event/eventOrchestrator.js +69 -2
  16. package/src/core/event/handlers/handler_ww_mouse.js +1 -0
  17. package/src/core/event/rules/keydown.rule.backspace.js +9 -1
  18. package/src/core/kernel/coreKernel.js +4 -0
  19. package/src/core/kernel/store.js +2 -0
  20. package/src/core/logic/dom/html.js +110 -11
  21. package/src/core/logic/dom/offset.js +89 -35
  22. package/src/core/logic/dom/selection.js +46 -19
  23. package/src/core/logic/panel/finder.js +982 -0
  24. package/src/core/logic/panel/menu.js +8 -6
  25. package/src/core/logic/panel/toolbar.js +112 -19
  26. package/src/core/logic/panel/viewer.js +214 -43
  27. package/src/core/logic/shell/_commandExecutor.js +7 -1
  28. package/src/core/logic/shell/commandDispatcher.js +1 -1
  29. package/src/core/logic/shell/component.js +5 -7
  30. package/src/core/logic/shell/history.js +24 -0
  31. package/src/core/logic/shell/shortcuts.js +3 -3
  32. package/src/core/logic/shell/ui.js +25 -26
  33. package/src/core/schema/frameContext.js +15 -1
  34. package/src/core/schema/options.js +75 -16
  35. package/src/core/section/constructor.js +61 -20
  36. package/src/core/section/documentType.js +1 -1
  37. package/src/events.js +12 -0
  38. package/src/helper/clipboard.js +1 -1
  39. package/src/helper/dom/domUtils.js +5 -14
  40. package/src/helper/index.js +3 -0
  41. package/src/helper/markdown.js +876 -0
  42. package/src/langs/ckb.js +9 -0
  43. package/src/langs/cs.js +9 -0
  44. package/src/langs/da.js +9 -0
  45. package/src/langs/de.js +9 -0
  46. package/src/langs/en.js +9 -0
  47. package/src/langs/es.js +9 -0
  48. package/src/langs/fa.js +9 -0
  49. package/src/langs/fr.js +9 -0
  50. package/src/langs/he.js +9 -0
  51. package/src/langs/hu.js +9 -0
  52. package/src/langs/it.js +9 -0
  53. package/src/langs/ja.js +9 -0
  54. package/src/langs/km.js +9 -0
  55. package/src/langs/ko.js +9 -0
  56. package/src/langs/lv.js +9 -0
  57. package/src/langs/nl.js +9 -0
  58. package/src/langs/pl.js +9 -0
  59. package/src/langs/pt_br.js +9 -0
  60. package/src/langs/ro.js +9 -0
  61. package/src/langs/ru.js +9 -0
  62. package/src/langs/se.js +9 -0
  63. package/src/langs/tr.js +9 -0
  64. package/src/langs/uk.js +9 -0
  65. package/src/langs/ur.js +9 -0
  66. package/src/langs/zh_cn.js +9 -0
  67. package/src/modules/contract/Controller.js +50 -39
  68. package/src/modules/manager/ApiManager.js +27 -4
  69. package/src/modules/manager/FileManager.js +1 -1
  70. package/src/modules/ui/SelectMenu.js +22 -11
  71. package/src/plugins/command/codeBlock.js +324 -0
  72. package/src/plugins/command/exportPDF.js +15 -3
  73. package/src/plugins/dropdown/blockStyle.js +1 -1
  74. package/src/plugins/dropdown/paragraphStyle.js +1 -2
  75. package/src/plugins/dropdown/table/render/table.html.js +1 -1
  76. package/src/plugins/dropdown/table/services/table.grid.js +16 -8
  77. package/src/plugins/dropdown/table/services/table.style.js +5 -9
  78. package/src/plugins/index.js +3 -0
  79. package/src/plugins/input/fontSize.js +4 -2
  80. package/src/plugins/modal/audio.js +2 -1
  81. package/src/plugins/modal/image/index.js +2 -1
  82. package/src/plugins/modal/math.js +2 -1
  83. package/src/plugins/modal/video/index.js +2 -1
  84. package/src/themes/cobalt.css +13 -4
  85. package/src/themes/cream.css +11 -2
  86. package/src/themes/dark.css +13 -4
  87. package/src/themes/midnight.css +13 -4
  88. package/src/typedef.js +4 -4
  89. package/types/assets/icons/defaultIcons.d.ts +12 -1
  90. package/types/core/config/eventManager.d.ts +6 -8
  91. package/types/core/event/actions/index.d.ts +1 -0
  92. package/types/core/event/effects/keydown.registry.d.ts +2 -0
  93. package/types/core/event/eventOrchestrator.d.ts +2 -1
  94. package/types/core/kernel/coreKernel.d.ts +5 -0
  95. package/types/core/kernel/store.d.ts +5 -0
  96. package/types/core/logic/dom/offset.d.ts +16 -3
  97. package/types/core/logic/dom/selection.d.ts +3 -3
  98. package/types/core/logic/panel/finder.d.ts +83 -0
  99. package/types/core/logic/panel/toolbar.d.ts +14 -1
  100. package/types/core/logic/panel/viewer.d.ts +22 -2
  101. package/types/core/logic/shell/shortcuts.d.ts +1 -1
  102. package/types/core/schema/frameContext.d.ts +22 -0
  103. package/types/core/schema/options.d.ts +153 -31
  104. package/types/events.d.ts +11 -0
  105. package/types/helper/dom/domUtils.d.ts +2 -2
  106. package/types/helper/index.d.ts +5 -0
  107. package/types/helper/markdown.d.ts +27 -0
  108. package/types/langs/_Lang.d.ts +9 -0
  109. package/types/modules/contract/Controller.d.ts +8 -1
  110. package/types/modules/ui/SelectMenu.d.ts +12 -0
  111. package/types/plugins/command/codeBlock.d.ts +53 -0
  112. package/types/plugins/index.d.ts +3 -0
  113. package/types/plugins/input/fontSize.d.ts +6 -2
  114. package/types/plugins/modal/audio.d.ts +4 -2
  115. package/types/plugins/modal/image/index.d.ts +3 -1
  116. package/types/plugins/modal/math.d.ts +3 -1
  117. package/types/plugins/modal/video/index.d.ts +3 -1
  118. package/types/typedef.d.ts +5 -2
@@ -115,6 +115,9 @@ function Constructor(editorTargets, options) {
115
115
  toolbar.style.width = o.get('toolbar_width');
116
116
  toolbar.appendChild(dom.utils.createElement('DIV', { class: 'se-arrow' }));
117
117
  }
118
+ if (o.get('_toolbar_bottom')) {
119
+ toolbar.className += ' se-toolbar-bottom';
120
+ }
118
121
 
119
122
  /** --- subToolbar --------------------------------------------------------------- */
120
123
  if (optionMap.subButtons) {
@@ -180,20 +183,36 @@ function Constructor(editorTargets, options) {
180
183
  // root key
181
184
  const key = editTarget.key || null;
182
185
 
183
- // code view - wrapper
184
- const codeWrapper = dom.utils.createElement('DIV', { class: 'se-code-wrapper' }, textarea);
185
- codeWrapper.style.setProperty('display', 'none', 'important');
186
- editor_div.appendChild(codeWrapper);
187
-
188
- // check code mirror
189
- const codeMirrorEl = _checkCodeMirror(o, to, textarea);
190
- // not used code mirror
191
- if (textarea === codeMirrorEl) {
192
- // add line nubers
193
- const codeNumbers = dom.utils.createElement('TEXTAREA', { class: 'se-code-view-line', readonly: 'true' }, null);
194
- codeWrapper.insertBefore(codeNumbers, textarea);
195
- } else {
196
- textarea = codeMirrorEl;
186
+ // code view - wrapper (only created when codeView button exists)
187
+ let codeWrapper = null;
188
+ if (o.get('buttons').has('codeView')) {
189
+ codeWrapper = dom.utils.createElement('DIV', { class: 'se-code-wrapper' }, textarea);
190
+ codeWrapper.style.setProperty('display', 'none', 'important');
191
+ editor_div.appendChild(codeWrapper);
192
+
193
+ // check code mirror
194
+ const codeMirrorEl = _checkCodeMirror(o, to, textarea);
195
+ // not used code mirror
196
+ if (textarea === codeMirrorEl) {
197
+ // add line nubers
198
+ const codeNumbers = dom.utils.createElement('TEXTAREA', { class: 'se-code-view-line', readonly: 'true' }, null);
199
+ codeWrapper.insertBefore(codeNumbers, textarea);
200
+ } else {
201
+ textarea = codeMirrorEl;
202
+ }
203
+ }
204
+
205
+ // markdown view - wrapper (only created when markdownView button exists)
206
+ let markdownWrapper = null;
207
+ let markdownTextarea = null;
208
+ if (o.get('buttons').has('markdownView')) {
209
+ markdownTextarea = initElements.markdownView;
210
+ const markdownNumbers = dom.utils.createElement('TEXTAREA', { class: 'se-markdown-view-line', readonly: 'true' }, null);
211
+ markdownWrapper = dom.utils.createElement('DIV', { class: 'se-markdown-wrapper' });
212
+ markdownWrapper.appendChild(markdownNumbers);
213
+ markdownWrapper.appendChild(markdownTextarea);
214
+ markdownWrapper.style.setProperty('display', 'none', 'important');
215
+ editor_div.appendChild(markdownWrapper);
197
216
  }
198
217
 
199
218
  // document type
@@ -216,7 +235,10 @@ function Constructor(editorTargets, options) {
216
235
  // set container
217
236
  top_div.appendChild(container);
218
237
  rootKeys.push(key);
219
- frameRoots.set(key, CreateFrameContext({ target: editTarget.target, key: editTarget.key, options: to }, top_div, wysiwyg_div, codeWrapper, textarea, default_status_bar || statusbar, documentTypeInner, key));
238
+ frameRoots.set(
239
+ key,
240
+ CreateFrameContext({ target: editTarget.target, key: editTarget.key, options: to }, top_div, wysiwyg_div, codeWrapper, textarea, markdownWrapper, markdownTextarea, default_status_bar || statusbar, documentTypeInner, key),
241
+ );
220
242
  }
221
243
  /** frame - root set - end -------------------------------------------------------------- */
222
244
 
@@ -232,8 +254,14 @@ function Constructor(editorTargets, options) {
232
254
  toolbar_container.appendChild(dom.utils.createElement('DIV', { class: 'se-toolbar-sticky-dummy' }));
233
255
  } else {
234
256
  const rootContainer = frameRoots.get(rootId).get('container');
235
- rootContainer.insertBefore(toolbar, rootContainer.firstElementChild);
236
- if (subbar) rootContainer.insertBefore(subbar, rootContainer.firstElementChild);
257
+ if (o.get('_toolbar_bottom')) {
258
+ const statusbar = rootContainer.querySelector('.se-statusbar');
259
+ rootContainer.insertBefore(toolbar, statusbar);
260
+ if (subbar) rootContainer.insertBefore(subbar, toolbar);
261
+ } else {
262
+ rootContainer.insertBefore(toolbar, rootContainer.firstElementChild);
263
+ if (subbar) rootContainer.insertBefore(subbar, rootContainer.firstElementChild);
264
+ }
237
265
  }
238
266
 
239
267
  return {
@@ -395,9 +423,13 @@ export function InitOptions(options, editorTargets, plugins) {
395
423
  ...(typeof options.strictMode === 'boolean' ? {} : options.strictMode),
396
424
  });
397
425
  o.set('freeCodeViewMode', !!options.freeCodeViewMode);
426
+ o.set('finder_panel', options.finder_panel !== false);
427
+ o.set('finder_liveSearch', options.finder_liveSearch !== false);
398
428
  o.set('__lineFormatFilter', options.__lineFormatFilter ?? true);
399
429
  o.set('__pluginRetainFilter', options.__pluginRetainFilter ?? true);
400
- o.set('mode', options.mode || 'classic'); // classic, inline, balloon, balloon-always
430
+ const [modeBase, modePart] = (options.mode || 'classic').split(':');
431
+ o.set('mode', modeBase); // classic, inline, balloon, balloon-always
432
+ o.set('_toolbar_bottom', modePart === 'bottom' && /^(classic|inline)$/i.test(modeBase));
401
433
  o.set('type', options.type?.split(':')[0] || ''); // document:header,page
402
434
  o.set('theme', options.theme || '');
403
435
  o.set('_themeClass', options.theme ? ` se-theme-${options.theme}` : '');
@@ -506,6 +538,7 @@ export function InitOptions(options, editorTargets, plugins) {
506
538
 
507
539
  // etc
508
540
  o.set('historyStackDelayTime', typeof options.historyStackDelayTime === 'number' ? options.historyStackDelayTime : 400);
541
+ o.set('historyStackSize', typeof options.historyStackSize === 'number' && options.historyStackSize > 0 ? options.historyStackSize : 100);
509
542
  o.set('_editableClass', 'sun-editor-editable' + o.get('_themeClass') + (o.get('_rtl') ? ' se-rtl' : '') + (o.get('type') === 'document' ? ' se-type-document-editable-a4' : ''));
510
543
  o.set('lineAttrReset', ['id'].concat(options.lineAttrReset && typeof options.lineAttrReset === 'string' ? options.lineAttrReset.toLowerCase().split('|') : []));
511
544
  o.set('printClass', typeof options.printClass === 'string' ? options.printClass + ' ' + o.get('_editableClass') : null);
@@ -624,6 +657,7 @@ export function InitOptions(options, editorTargets, plugins) {
624
657
  indent: ['c+BracketRight', ']'],
625
658
  outdent: ['c+BracketLeft', '['],
626
659
  save: ['c+KeyS', 'S'],
660
+ ...(o.get('finder_panel') ? { finder: ['c+KeyF', 'F'] } : {}),
627
661
  // plugins
628
662
  link: ['c+KeyK', 'K'],
629
663
  hr: ['!+---+=+~shortcut', ''],
@@ -844,7 +878,7 @@ function InitFrameOptions(o, origin) {
844
878
  * @param {Map<string, *>} options - Options
845
879
  * @param {HTMLElement} topDiv - Top div
846
880
  * @param {SunEditor.FrameOptions} targetOptions - `editor.frameOptions`
847
- * @returns {{bottomBar: ReturnType<CreateStatusbar>, wysiwygFrame: HTMLElement, codeView: HTMLElement, placeholder: HTMLElement}}
881
+ * @returns {{bottomBar: ReturnType<CreateStatusbar>, wysiwygFrame: HTMLElement, codeView: HTMLElement, markdownView: HTMLElement, placeholder: HTMLElement}}
848
882
  */
849
883
  function _initTargetElements(key, options, topDiv, targetOptions) {
850
884
  const editorStyles = targetOptions.get('_defaultStyles');
@@ -900,6 +934,10 @@ function _initTargetElements(key, options, topDiv, targetOptions) {
900
934
 
901
935
  // textarea for code view
902
936
  const textarea = dom.utils.createElement('TEXTAREA', { class: 'se-wrapper-inner se-code-viewer', style: editorStyles.frame });
937
+
938
+ // textarea for markdown view
939
+ const markdownTextarea = dom.utils.createElement('TEXTAREA', { class: 'se-wrapper-inner se-markdown-viewer', style: editorStyles.frame });
940
+
903
941
  const placeholder = dom.utils.createElement('SPAN', { class: 'se-placeholder' });
904
942
  if (targetOptions.get('placeholder')) {
905
943
  placeholder.textContent = targetOptions.get('placeholder');
@@ -909,6 +947,7 @@ function _initTargetElements(key, options, topDiv, targetOptions) {
909
947
  bottomBar: CreateStatusbar(targetOptions, null),
910
948
  wysiwygFrame: wysiwygDiv,
911
949
  codeView: textarea,
950
+ markdownView: markdownTextarea,
912
951
  placeholder: placeholder,
913
952
  };
914
953
  }
@@ -929,7 +968,7 @@ function _checkCodeMirror(options, targetOptions, textarea) {
929
968
  mode: 'htmlmixed',
930
969
  htmlMode: true,
931
970
  lineNumbers: true,
932
- lineWrapping: true,
971
+ lineWrapping: false,
933
972
  },
934
973
  codeMirror.options || {},
935
974
  ].reduce((init, option) => {
@@ -1038,6 +1077,7 @@ function _defaultButtons(isRTL, icons, lang) {
1038
1077
  fullScreen: ['se-code-view-enabled se-component-enabled', lang.fullScreen, 'fullScreen', '', icons.expansion],
1039
1078
  showBlocks: ['', lang.showBlocks, 'showBlocks', '', icons.show_blocks],
1040
1079
  codeView: ['se-code-view-enabled se-component-enabled', lang.codeView, 'codeView', '', icons.code_view],
1080
+ markdownView: ['se-code-view-enabled se-component-enabled', lang.markdownView, 'markdownView', '', icons.markdown_view],
1041
1081
  undo: ['se-component-enabled', lang.undo, 'undo', '', icons.undo],
1042
1082
  redo: ['se-component-enabled', lang.redo, 'redo', '', icons.redo],
1043
1083
  preview: ['se-component-enabled', lang.preview, 'preview', '', icons.preview],
@@ -1046,6 +1086,7 @@ function _defaultButtons(isRTL, icons, lang) {
1046
1086
  dir: ['', lang[isRTL ? 'dir_ltr' : 'dir_rtl'], 'dir', '', icons[isRTL ? 'dir_ltr' : 'dir_rtl']],
1047
1087
  dir_ltr: ['', lang.dir_ltr, 'dir_ltr', '', icons.dir_ltr],
1048
1088
  dir_rtl: ['', lang.dir_rtl, 'dir_rtl', '', icons.dir_rtl],
1089
+ finder: ['se-component-enabled', lang.find, 'finder', '', icons.finder],
1049
1090
  save: ['se-component-enabled', lang.save, 'save', '', icons.save],
1050
1091
  newDocument: ['se-component-enabled', lang.newDocument, 'newDocument', '', icons.new_document],
1051
1092
  selectAll: ['se-component-enabled', lang.selectAll, 'selectAll', '', icons.select_all],
@@ -489,7 +489,7 @@ class DocumentType {
489
489
  if (!force) this.#selection.setRange(c, 0, c, 0);
490
490
  const scrollTop = i === 0 && isScrollable ? 0 : c.offsetTop - this.#page.offsetTop - c.offsetHeight + globalTop;
491
491
  this._applyPageScroll(scrollTop, () => {
492
- if (this.#toolbar.isSticky) {
492
+ if (this.#toolbar.isSticky && !this.#store.mode.isBottom) {
493
493
  this._getDisplayPage().scrollTo({ top: scrollTop - this.#context.get('toolbar_main').offsetHeight, behavior: 'smooth' });
494
494
  }
495
495
  });
package/src/events.js CHANGED
@@ -312,6 +312,17 @@ function onBeforeShowController(params) {}
312
312
  */
313
313
  function onToggleCodeView(params) {}
314
314
 
315
+ /**
316
+ * @callback
317
+ * @description Fired when the editor switches between WYSIWYG view and markdown view.
318
+ * The `is` parameter indicates whether markdown view is now active (`true`) or WYSIWYG view is active (`false`).
319
+ * @param {Object} params
320
+ * @param {SunEditor.Deps} params.$ - Kernel dependencies
321
+ * @param {SunEditor.FrameContext} params.frameContext - frame context
322
+ * @param {boolean} params.is - markdown view status
323
+ */
324
+ function onToggleMarkdownView(params) {}
325
+
315
326
  /**
316
327
  * @callback
317
328
  * @description Fired when the editor enters or exits fullscreen mode.
@@ -859,6 +870,7 @@ function onEmbedDeleteBefore(params) {
859
870
  * @property {?onShowController} [onShowController]
860
871
  * @property {?onBeforeShowController} [onBeforeShowController]
861
872
  * @property {?onToggleCodeView} [onToggleCodeView]
873
+ * @property {?onToggleMarkdownView} [onToggleMarkdownView]
862
874
  * @property {?onToggleFullScreen} [onToggleFullScreen]
863
875
  * @property {?onResizeEditor} [onResizeEditor]
864
876
  * @property {?onSetToolbarButtons} [onSetToolbarButtons]
@@ -43,7 +43,7 @@ export async function write(content) {
43
43
 
44
44
  try {
45
45
  await navigator.clipboard.write([
46
- /* eslint-disable-next-line compat/compat */
46
+ // eslint-disable-next-line compat/compat
47
47
  new ClipboardItem({
48
48
  'text/html': new Blob([htmlString], { type: 'text/html' }),
49
49
  'text/plain': new Blob([plainText], { type: 'text/plain' }),
@@ -368,7 +368,8 @@ export function setDisabled(buttonList, disabled, important) {
368
368
  */
369
369
  export function hasClass(element, className) {
370
370
  if (!element || element.nodeType !== 1) return;
371
- return /** @type {HTMLElement} */ (element).classList.contains(className);
371
+
372
+ return className.split('|').some((cls) => /** @type {HTMLElement} */ (element).classList.contains(cls));
372
373
  }
373
374
 
374
375
  /**
@@ -420,25 +421,15 @@ export function removeClass(element, className) {
420
421
  * @description Argument value If there is no class name, insert it and delete the class name if it exists
421
422
  * @param {Node} element Element to replace class name
422
423
  * @param {string} className Class name to be change
423
- * @returns {boolean|undefined}
424
+ * @param {boolean} [force] If true, adds the class; if false, removes it.
424
425
  */
425
- export function toggleClass(element, className) {
426
+ export function toggleClass(element, className, force) {
426
427
  if (!element || element.nodeType !== 1) return;
427
428
 
428
429
  const el = /** @type {HTMLElement} */ (element);
429
-
430
- let result = false;
431
- const valid = new RegExp(`(\\s|^)${className}(\\s|$)`);
432
- if (valid.test(el.className)) {
433
- el.className = el.className.replace(valid, ' ').trim();
434
- } else {
435
- el.className += ' ' + className;
436
- result = true;
437
- }
430
+ el.classList.toggle(className, force);
438
431
 
439
432
  if (!el.className.trim()) el.removeAttribute('class');
440
-
441
- return result;
442
433
  }
443
434
 
444
435
  /**
@@ -5,6 +5,7 @@ import Dom from './dom';
5
5
  import Numbers from './numbers';
6
6
  import KeyCodeMap from './keyCodeMap';
7
7
  import Clipboard from './clipboard';
8
+ import Markdown from './markdown';
8
9
 
9
10
  export const env = Env;
10
11
  export const unicode = Unicode;
@@ -13,6 +14,7 @@ export const dom = Dom;
13
14
  export const numbers = Numbers;
14
15
  export const keyCodeMap = KeyCodeMap;
15
16
  export const clipboard = Clipboard;
17
+ export const markdown = Markdown;
16
18
 
17
19
  export default {
18
20
  env,
@@ -22,4 +24,5 @@ export default {
22
24
  numbers,
23
25
  keyCodeMap,
24
26
  clipboard,
27
+ markdown,
25
28
  };