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/dist/suneditor.min.css +1 -1
- package/dist/suneditor.min.js +1 -1
- package/package.json +1 -1
- package/src/assets/suneditor.css +34 -4
- package/src/core/config/optionProvider.js +1 -1
- package/src/core/editor.js +2 -0
- package/src/core/event/eventOrchestrator.js +3 -0
- package/src/core/logic/dom/char.js +27 -4
- package/src/core/schema/frameContext.js +6 -0
- package/src/core/schema/options.js +9 -0
- package/src/core/section/constructor.js +32 -2
- package/types/core/logic/dom/char.d.ts +10 -1
- package/types/core/schema/frameContext.d.ts +10 -0
- package/types/core/schema/options.d.ts +20 -0
- package/types/core/section/constructor.d.ts +3 -1
package/package.json
CHANGED
package/src/assets/suneditor.css
CHANGED
|
@@ -1066,11 +1066,11 @@
|
|
|
1066
1066
|
}
|
|
1067
1067
|
|
|
1068
1068
|
.sun-editor .se-btn-select.se-btn-tool-font {
|
|
1069
|
-
width:
|
|
1069
|
+
width: 128px;
|
|
1070
1070
|
}
|
|
1071
1071
|
|
|
1072
1072
|
.sun-editor .se-btn-select.se-btn-tool-format {
|
|
1073
|
-
width:
|
|
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-
|
|
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
|
|
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
|
|
package/src/core/editor.js
CHANGED
|
@@ -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
|
|
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
|
|
100
|
-
|
|
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
|
-
/**
|
|
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
|
|
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
|