suneditor 3.0.3 → 3.0.4

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "suneditor",
3
- "version": "3.0.3",
3
+ "version": "3.0.4",
4
4
  "description": "Vanilla JavaScript based WYSIWYG web editor",
5
5
  "author": "Yi JiHong",
6
6
  "license": "MIT",
@@ -1066,11 +1066,11 @@
1066
1066
  }
1067
1067
 
1068
1068
  .sun-editor .se-btn-select.se-btn-tool-font {
1069
- width: 100px;
1069
+ width: 128px;
1070
1070
  }
1071
1071
 
1072
1072
  .sun-editor .se-btn-select.se-btn-tool-format {
1073
- width: 86px;
1073
+ width: 96px;
1074
1074
  }
1075
1075
 
1076
1076
  .sun-editor .se-btn-select.se-btn-tool-font-size .se-txt {
@@ -1880,7 +1880,27 @@
1880
1880
  background: transparent;
1881
1881
  }
1882
1882
 
1883
+ /** status bar - wordCounter */
1884
+ .sun-editor .se-status-bar .se-word-counter-wrapper {
1885
+ position: relative;
1886
+ width: auto;
1887
+ height: auto;
1888
+ margin: 0;
1889
+ padding: 0;
1890
+ color: var(--se-statusbar-font-color);
1891
+ font-size: var(--se-statusbar-font-size);
1892
+ background: transparent;
1893
+ }
1894
+
1895
+ .sun-editor .se-status-bar .se-word-counter-wrapper .se-word-label {
1896
+ margin-right: 4px;
1897
+ }
1898
+
1883
1899
  /** status bar - charCounter */
1900
+ .sun-editor .se-status-bar .se-char-counter-wrapper.se-with-word-counter {
1901
+ margin-left: 0.6em;
1902
+ }
1903
+
1884
1904
  .sun-editor .se-status-bar .se-char-counter-wrapper {
1885
1905
  flex: none;
1886
1906
  position: relative;
@@ -1890,7 +1910,7 @@
1890
1910
  margin: 0;
1891
1911
  padding: 0;
1892
1912
  color: var(--se-statusbar-font-color);
1893
- font-size: var(--se-main-font-size);
1913
+ font-size: var(--se-statusbar-font-size);
1894
1914
  background: transparent;
1895
1915
  }
1896
1916
 
@@ -3884,10 +3904,20 @@
3884
3904
  }
3885
3905
 
3886
3906
  /* statusbar */
3887
- .sun-editor.se-rtl .se-status-bar .se-navigation {
3907
+ .sun-editor.se-rtl .se-status-bar {
3888
3908
  direction: rtl;
3889
3909
  }
3890
3910
 
3911
+ .sun-editor.se-rtl .se-status-bar .se-word-counter-wrapper .se-word-label {
3912
+ margin-right: 0;
3913
+ margin-left: 4px;
3914
+ }
3915
+
3916
+ .sun-editor.se-rtl .se-status-bar .se-char-counter-wrapper.se-with-word-counter {
3917
+ margin-left: 0;
3918
+ margin-right: 0.6em;
3919
+ }
3920
+
3891
3921
  /* button--- */
3892
3922
  /* button - select text */
3893
3923
  .sun-editor.se-rtl .se-btn-select .se-txt {
@@ -331,7 +331,7 @@ export default class OptionProvider {
331
331
  }
332
332
 
333
333
  #GetResetDiffKey(key) {
334
- if (/^statusbar|^charCounter/.test(key)) return 'statusbar-changed';
334
+ if (/^statusbar|^charCounter|^wordCounter/.test(key)) return 'statusbar-changed';
335
335
  return key;
336
336
  }
337
337
 
@@ -204,6 +204,8 @@ class Editor {
204
204
 
205
205
  // char counter
206
206
  if (e.has('charCounter')) e.get('charCounter').textContent = String(this.$.char.getLength());
207
+ // word counter
208
+ if (e.has('wordCounter')) e.get('wordCounter').textContent = String(this.$.char.getWordCount());
207
209
 
208
210
  // document type init
209
211
  if (this.$.options.get('type') === 'document') {
@@ -669,6 +669,9 @@ class EventOrchestrator extends KernelInjector {
669
669
  if (this.#store.mode.isInline || this.#store.mode.isBalloonAlways) this.#toolbar.show();
670
670
  if (this.#store.mode.isSubBalloonAlways) this.$.subToolbar.show();
671
671
 
672
+ // sticky
673
+ this.#toolbar._resetSticky();
674
+
672
675
  // user event
673
676
  this.#eventManager.triggerEvent('onFocus', { frameContext, event });
674
677
  // plugin event
@@ -92,15 +92,38 @@ class Char {
92
92
  }
93
93
 
94
94
  /**
95
- * @description Set the char count to charCounter element textContent.
95
+ * @description Get the number of words in the content.
96
+ * - If [content] is `undefined`, get the current editor's word count.
97
+ * @param {string} [content] Content to count. (default: wysiwyg textContent)
98
+ * @returns {number}
99
+ * const currentWords = editor.$.char.getWordCount();
100
+ * const textWords = editor.$.char.getWordCount('Hello World');
101
+ */
102
+ getWordCount(content) {
103
+ if (typeof content !== 'string') {
104
+ content = this.#frameContext.get('wysiwyg').innerText;
105
+ }
106
+
107
+ const trimmed = content.trim();
108
+ if (!trimmed) return 0;
109
+
110
+ return trimmed.split(/\s+/).length;
111
+ }
112
+
113
+ /**
114
+ * @description Set the char count and word count to counter element textContent.
96
115
  * @param {?SunEditor.FrameContext} [fc] Frame context
97
116
  */
98
117
  display(fc) {
99
- const charCounter = (fc || this.#frameContext).get('charCounter');
100
- if (charCounter) {
118
+ const ctx = fc || this.#frameContext;
119
+ const charCounter = ctx.get('charCounter');
120
+ const wordCounter = ctx.get('wordCounter');
121
+
122
+ if (charCounter || wordCounter) {
101
123
  // Defer count update — DOM content may still be mutating from the current input/paste action
102
124
  _w.setTimeout(() => {
103
- charCounter.textContent = String(this.getLength());
125
+ if (charCounter) charCounter.textContent = String(this.getLength());
126
+ if (wordCounter) wordCounter.textContent = String(this.getWordCount());
104
127
  }, 0);
105
128
  }
106
129
  }
@@ -38,6 +38,8 @@ import { get as getNumber } from '../../helper/numbers';
38
38
  * @property {HTMLElement} navigation - Navigation element (e.g., for outline or bookmarks).
39
39
  * @property {HTMLElement} charWrapper - Wrapper for the character counter element.
40
40
  * @property {HTMLElement} charCounter - Element showing the character counter.
41
+ * @property {HTMLElement} wordWrapper - Wrapper for the word counter element.
42
+ * @property {HTMLElement} wordCounter - Element showing the word counter.
41
43
  * @property {Window} [_ww] - The window object of the WYSIWYG frame (iframe window).
42
44
  * @property {Document} [_wd] - The document object of the WYSIWYG frame (iframe document).
43
45
  *
@@ -171,4 +173,8 @@ export function UpdateStatusbarContext(statusbar, mapper) {
171
173
  navigation ? mapper.set('navigation', navigation) : mapper.delete('navigation');
172
174
  charWrapper ? mapper.set('charWrapper', charWrapper) : mapper.delete('charWrapper');
173
175
  charCounter ? mapper.set('charCounter', charCounter) : mapper.delete('charCounter');
176
+ const wordWrapper = statusbar ? statusbar.querySelector('.se-word-counter-wrapper') : null;
177
+ const wordCounter = statusbar ? statusbar.querySelector('.se-word-counter-wrapper .se-word-counter') : null;
178
+ wordWrapper ? mapper.set('wordWrapper', wordWrapper) : mapper.delete('wordWrapper');
179
+ wordCounter ? mapper.set('wordCounter', wordCounter) : mapper.delete('wordCounter');
174
180
  }
@@ -150,6 +150,13 @@ export const DEFAULTS = {
150
150
  * - `char`: Characters length.
151
151
  * - `byte`: Binary data size of characters.
152
152
  * - `byte-html`: Binary data size of the full HTML string.
153
+ *
154
+ * === Word Counter ===
155
+ * @property {boolean} [wordCounter=false] - Shows the number of words in the editor.
156
+ * @property {?string} [wordCounter_label=null] - Text to be displayed in the `wordCounter` area of the bottom bar.
157
+ * ```js
158
+ * { wordCounter_label: 'Words :' }
159
+ * ```
153
160
  */
154
161
 
155
162
  /** ================================================================================================================================ */
@@ -681,6 +688,8 @@ export const OPTION_FRAME_FIXED_FLAG = {
681
688
  charCounter_max: true,
682
689
  charCounter_label: true,
683
690
  charCounter_type: true,
691
+ wordCounter: true,
692
+ wordCounter_label: true,
684
693
  };
685
694
 
686
695
  /**
@@ -772,12 +772,14 @@ export function InitOptions(options, editorTargets, plugins) {
772
772
  * @description Create a context object for the editor frame.
773
773
  * @param {SunEditor.FrameOptions} targetOptions - `editor.frameOptions`
774
774
  * @param {HTMLElement} statusbar - statusbar element
775
- * @returns {{statusbar: HTMLElement, navigation: HTMLElement, charWrapper: HTMLElement, charCounter: HTMLElement}}
775
+ * @returns {{statusbar: HTMLElement, navigation: HTMLElement, charWrapper: HTMLElement, charCounter: HTMLElement, wordWrapper: HTMLElement, wordCounter: HTMLElement}}
776
776
  */
777
777
  export function CreateStatusbar(targetOptions, statusbar) {
778
778
  let navigation = null;
779
779
  let charWrapper = null;
780
780
  let charCounter = null;
781
+ let wordWrapper = null;
782
+ let wordCounter = null;
781
783
 
782
784
  if (targetOptions.get('statusbar')) {
783
785
  statusbar ||= dom.utils.createElement('DIV', { class: 'se-status-bar sun-editor-common' });
@@ -786,10 +788,31 @@ export function CreateStatusbar(targetOptions, statusbar) {
786
788
  navigation = statusbar.querySelector('.se-navigation') || dom.utils.createElement('DIV', { class: 'se-navigation sun-editor-common' });
787
789
  statusbar.appendChild(navigation);
788
790
 
789
- /** char counter */
791
+ /** word counter (left) */
792
+ if (targetOptions.get('wordCounter')) {
793
+ wordWrapper = statusbar.querySelector('.se-word-counter-wrapper') || dom.utils.createElement('DIV', { class: 'se-word-counter-wrapper' });
794
+
795
+ if (targetOptions.get('wordCounter_label')) {
796
+ const wordLabel = wordWrapper.querySelector('.se-word-label') || dom.utils.createElement('SPAN', { class: 'se-word-label' });
797
+ wordLabel.textContent = targetOptions.get('wordCounter_label');
798
+ wordWrapper.appendChild(wordLabel);
799
+ }
800
+
801
+ wordCounter = wordWrapper.querySelector('.se-word-counter') || dom.utils.createElement('SPAN', { class: 'se-word-counter' });
802
+ wordCounter.textContent = '0';
803
+ wordWrapper.appendChild(wordCounter);
804
+
805
+ statusbar.appendChild(wordWrapper);
806
+ }
807
+
808
+ /** char counter (right) */
790
809
  if (targetOptions.get('charCounter')) {
791
810
  charWrapper = statusbar.querySelector('.se-char-counter-wrapper') || dom.utils.createElement('DIV', { class: 'se-char-counter-wrapper' });
792
811
 
812
+ if (targetOptions.get('wordCounter') && charWrapper.className.indexOf('se-with-word-counter') === -1) {
813
+ charWrapper.className += ' se-with-word-counter';
814
+ }
815
+
793
816
  if (targetOptions.get('charCounter_label')) {
794
817
  const charLabel = charWrapper.querySelector('.se-char-label') || dom.utils.createElement('SPAN', { class: 'se-char-label' });
795
818
  charLabel.textContent = targetOptions.get('charCounter_label');
@@ -815,6 +838,8 @@ export function CreateStatusbar(targetOptions, statusbar) {
815
838
  navigation: /** @type {HTMLElement} */ (navigation),
816
839
  charWrapper: /** @type {HTMLElement} */ (charWrapper),
817
840
  charCounter: /** @type {HTMLElement} */ (charCounter),
841
+ wordWrapper: /** @type {HTMLElement} */ (wordWrapper),
842
+ wordCounter: /** @type {HTMLElement} */ (wordCounter),
818
843
  };
819
844
  }
820
845
 
@@ -852,6 +877,8 @@ function InitFrameOptions(o, origin) {
852
877
  const charCounter_max = barContainer || o.charCounter_max === undefined ? origin.charCounter_max : o.charCounter_max;
853
878
  const charCounter_label = barContainer || o.charCounter_label === undefined ? origin.charCounter_label : o.charCounter_label;
854
879
  const charCounter_type = barContainer || o.charCounter_type === undefined ? origin.charCounter_type : o.charCounter_type;
880
+ const wordCounter = barContainer || o.wordCounter === undefined ? origin.wordCounter : o.wordCounter;
881
+ const wordCounter_label = barContainer || o.wordCounter_label === undefined ? origin.wordCounter_label : o.wordCounter_label;
855
882
 
856
883
  // value
857
884
  fo.set('value', value);
@@ -881,6 +908,9 @@ function InitFrameOptions(o, origin) {
881
908
  fo.set('charCounter_max', numbers.is(charCounter_max) && charCounter_max > -1 ? charCounter_max * 1 : null);
882
909
  fo.set('charCounter_label', typeof charCounter_label === 'string' ? charCounter_label.trim() : null);
883
910
  fo.set('charCounter_type', typeof charCounter_type === 'string' ? charCounter_type : 'char');
911
+ // status bar - word count
912
+ fo.set('wordCounter', typeof wordCounter === 'boolean' ? wordCounter : false);
913
+ fo.set('wordCounter_label', typeof wordCounter_label === 'string' ? wordCounter_label.trim() : null);
884
914
 
885
915
  return fo;
886
916
  }
@@ -36,7 +36,16 @@ declare class Char {
36
36
  */
37
37
  getByteLength(text: string): number;
38
38
  /**
39
- * @description Set the char count to charCounter element textContent.
39
+ * @description Get the number of words in the content.
40
+ * - If [content] is `undefined`, get the current editor's word count.
41
+ * @param {string} [content] Content to count. (default: wysiwyg textContent)
42
+ * @returns {number}
43
+ * const currentWords = editor.$.char.getWordCount();
44
+ * const textWords = editor.$.char.getWordCount('Hello World');
45
+ */
46
+ getWordCount(content?: string): number;
47
+ /**
48
+ * @description Set the char count and word count to counter element textContent.
40
49
  * @param {?SunEditor.FrameContext} [fc] Frame context
41
50
  */
42
51
  display(fc?: SunEditor.FrameContext | null): void;
@@ -37,6 +37,8 @@ import type {} from '../../typedef';
37
37
  * @property {HTMLElement} navigation - Navigation element (e.g., for outline or bookmarks).
38
38
  * @property {HTMLElement} charWrapper - Wrapper for the character counter element.
39
39
  * @property {HTMLElement} charCounter - Element showing the character counter.
40
+ * @property {HTMLElement} wordWrapper - Wrapper for the word counter element.
41
+ * @property {HTMLElement} wordCounter - Element showing the word counter.
40
42
  * @property {Window} [_ww] - The window object of the WYSIWYG frame (iframe window).
41
43
  * @property {Document} [_wd] - The document object of the WYSIWYG frame (iframe document).
42
44
  *
@@ -215,6 +217,14 @@ export type FrameContextStore = {
215
217
  * - Element showing the character counter.
216
218
  */
217
219
  charCounter: HTMLElement;
220
+ /**
221
+ * - Wrapper for the word counter element.
222
+ */
223
+ wordWrapper: HTMLElement;
224
+ /**
225
+ * - Element showing the word counter.
226
+ */
227
+ wordCounter: HTMLElement;
218
228
  /**
219
229
  * - The window object of the WYSIWYG frame (iframe window).
220
230
  */
@@ -105,6 +105,13 @@ export namespace DEFAULTS {
105
105
  * - `char`: Characters length.
106
106
  * - `byte`: Binary data size of characters.
107
107
  * - `byte-html`: Binary data size of the full HTML string.
108
+ *
109
+ * === Word Counter ===
110
+ * @property {boolean} [wordCounter=false] - Shows the number of words in the editor.
111
+ * @property {?string} [wordCounter_label=null] - Text to be displayed in the `wordCounter` area of the bottom bar.
112
+ * ```js
113
+ * { wordCounter_label: 'Words :' }
114
+ * ```
108
115
  */
109
116
  /** ================================================================================================================================ */
110
117
  /**
@@ -729,8 +736,21 @@ export type EditorFrameOptions = {
729
736
  * - `char`: Characters length.
730
737
  * - `byte`: Binary data size of characters.
731
738
  * - `byte-html`: Binary data size of the full HTML string.
739
+ *
740
+ * === Word Counter ===
732
741
  */
733
742
  charCounter_type?: 'char' | 'byte' | 'byte-html';
743
+ /**
744
+ * - Shows the number of words in the editor.
745
+ */
746
+ wordCounter?: boolean;
747
+ /**
748
+ * - Text to be displayed in the `wordCounter` area of the bottom bar.
749
+ * ```js
750
+ * { wordCounter_label: 'Words :' }
751
+ * ```
752
+ */
753
+ wordCounter_label?: string | null;
734
754
  };
735
755
  export type OptionStyleResult = {
736
756
  /**
@@ -42,7 +42,7 @@ export function InitOptions(
42
42
  * @description Create a context object for the editor frame.
43
43
  * @param {SunEditor.FrameOptions} targetOptions - `editor.frameOptions`
44
44
  * @param {HTMLElement} statusbar - statusbar element
45
- * @returns {{statusbar: HTMLElement, navigation: HTMLElement, charWrapper: HTMLElement, charCounter: HTMLElement}}
45
+ * @returns {{statusbar: HTMLElement, navigation: HTMLElement, charWrapper: HTMLElement, charCounter: HTMLElement, wordWrapper: HTMLElement, wordCounter: HTMLElement}}
46
46
  */
47
47
  export function CreateStatusbar(
48
48
  targetOptions: SunEditor.FrameOptions,
@@ -52,6 +52,8 @@ export function CreateStatusbar(
52
52
  navigation: HTMLElement;
53
53
  charWrapper: HTMLElement;
54
54
  charCounter: HTMLElement;
55
+ wordWrapper: HTMLElement;
56
+ wordCounter: HTMLElement;
55
57
  };
56
58
  /**
57
59
  * @description Update a button state, attributes, and icons