@mirus/tiptap-editor 2.0.0 → 2.1.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.
- package/README.md +39 -33
- package/dist/tiptap-editor.mjs +22380 -0
- package/package.json +3 -2
- package/src/App.vue +6 -3
- package/src/tiptap-editor.vue +37 -9
- package/src/warnings.js +27 -4
- package/vite.config.js +14 -6
- package/dist/assets/index-40717dd0.js +0 -115
- package/dist/assets/index-7678a4fb.css +0 -1
- package/dist/index.html +0 -14
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mirus/tiptap-editor",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.1.0",
|
|
4
4
|
"description": "custom setup for the tiptap editor",
|
|
5
5
|
"repository": "https://github.com/mirusresearch/tiptap-editor",
|
|
6
6
|
"bugs": {
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
"homepage": "https://github.com/mirusresearch/tiptap-editor",
|
|
10
10
|
"author": "alec@mirusresearch.com",
|
|
11
11
|
"license": "MIT",
|
|
12
|
-
"main": "dist/tiptap-editor.
|
|
12
|
+
"main": "dist/tiptap-editor.mjs",
|
|
13
13
|
"browser": {
|
|
14
14
|
"./sfc": "src/tiptap-editor.vue"
|
|
15
15
|
},
|
|
@@ -75,6 +75,7 @@
|
|
|
75
75
|
"ie >= 9"
|
|
76
76
|
],
|
|
77
77
|
"dependencies": {
|
|
78
|
+
"vite-plugin-css-injected-by-js": "^3.3.0",
|
|
78
79
|
"vue-template-compiler": "^2.7.14"
|
|
79
80
|
}
|
|
80
81
|
}
|
package/src/App.vue
CHANGED
|
@@ -19,7 +19,7 @@ export default {
|
|
|
19
19
|
components: { tiptapEditor },
|
|
20
20
|
data() {
|
|
21
21
|
return {
|
|
22
|
-
maxCharacterCount:
|
|
22
|
+
maxCharacterCount: 250, //default null for infinity
|
|
23
23
|
height: '200px', // default it 300px
|
|
24
24
|
showMenu: true, // false to hide
|
|
25
25
|
warnings: [
|
|
@@ -27,7 +27,8 @@ export default {
|
|
|
27
27
|
value: 'red',
|
|
28
28
|
message: 'did you mean...',
|
|
29
29
|
options: ['read', 'reed'], // optional
|
|
30
|
-
//
|
|
30
|
+
offset: 55, // identifies which instance of the word is problematic
|
|
31
|
+
length: 3, // length of the word
|
|
31
32
|
},
|
|
32
33
|
{
|
|
33
34
|
value: 'prob|emati(', // avoid regexs that go bump in the night
|
|
@@ -39,6 +40,8 @@ export default {
|
|
|
39
40
|
isWord: false,
|
|
40
41
|
message: 'you sure you wanted a tag?',
|
|
41
42
|
overrideClass: 'underline-blue', // optional
|
|
43
|
+
offset: 10,
|
|
44
|
+
length: 8,
|
|
42
45
|
},
|
|
43
46
|
{
|
|
44
47
|
value: 'green',
|
|
@@ -52,7 +55,7 @@ export default {
|
|
|
52
55
|
},
|
|
53
56
|
],
|
|
54
57
|
localtext:
|
|
55
|
-
'Hi! 👋🏻 <script> welcome to tiptap editor, here is a red mark blue with suggestions. Even prob|emati( strings. You can highlight with other colors, e.g blue, orange, and green!!',
|
|
58
|
+
'Hi! 👋🏻 <script> welcome to tiptap editor, here is a red mark blue with suggestions. Even prob|emati( strings. You can highlight with other colors, e.g blue, orange, and green!! This <script> should not have any highlight.',
|
|
56
59
|
localcount: null,
|
|
57
60
|
};
|
|
58
61
|
},
|
package/src/tiptap-editor.vue
CHANGED
|
@@ -80,8 +80,7 @@
|
|
|
80
80
|
<circle r="6" cx="10" cy="10" fill="white" />
|
|
81
81
|
</svg>
|
|
82
82
|
<div class="character-count__text" aria-live="polite">
|
|
83
|
-
{{
|
|
84
|
-
{{ maxCharacterCount }} characters
|
|
83
|
+
{{ currentCharacterCount }} / {{ maxCharacterCount }} characters
|
|
85
84
|
</div>
|
|
86
85
|
</div>
|
|
87
86
|
</div>
|
|
@@ -162,7 +161,8 @@ export default {
|
|
|
162
161
|
navigatedOptionIndex: 0,
|
|
163
162
|
insertOption: () => {},
|
|
164
163
|
optionsRange: null,
|
|
165
|
-
|
|
164
|
+
initialCharacterCount: 0,
|
|
165
|
+
previousCharacterCount: 0,
|
|
166
166
|
};
|
|
167
167
|
},
|
|
168
168
|
computed: {
|
|
@@ -177,20 +177,23 @@ export default {
|
|
|
177
177
|
isWord: isWord,
|
|
178
178
|
value: isWord ? mistake.value : unescape(mistake.value),
|
|
179
179
|
message: mistake.message,
|
|
180
|
+
offset: mistake.offset,
|
|
181
|
+
length: mistake.length,
|
|
180
182
|
options: (mistake.options || []).map((value, id) => ({ value, id })),
|
|
181
183
|
};
|
|
182
184
|
});
|
|
183
185
|
},
|
|
186
|
+
currentCharacterCount() {
|
|
187
|
+
return this.editor.storage.characterCount.characters();
|
|
188
|
+
},
|
|
184
189
|
maxCharacterCountExceeded() {
|
|
185
190
|
if (this.editor) {
|
|
186
|
-
return this.
|
|
191
|
+
return this.currentCharacterCount >= this.maxCharacterCount;
|
|
187
192
|
}
|
|
188
193
|
},
|
|
189
194
|
characterCountPercentage() {
|
|
190
195
|
if (this.editor) {
|
|
191
|
-
return Math.round(
|
|
192
|
-
(100 / this.maxCharacterCount) * this.editor.storage.characterCount.characters()
|
|
193
|
-
);
|
|
196
|
+
return Math.round((100 / this.maxCharacterCount) * this.currentCharacterCount);
|
|
194
197
|
}
|
|
195
198
|
},
|
|
196
199
|
},
|
|
@@ -218,6 +221,7 @@ export default {
|
|
|
218
221
|
Text,
|
|
219
222
|
Warning.configure({
|
|
220
223
|
getErrorWords: this.getErrorWords,
|
|
224
|
+
getInitialCharacterCount: this.getInitialCharacterCount,
|
|
221
225
|
onEnter: ({ range, command, virtualNode, text }) => {
|
|
222
226
|
this.currentWarning = this.errors.find((err) => err.value === text);
|
|
223
227
|
this.currentOptions = this.currentWarning.options || [];
|
|
@@ -281,6 +285,21 @@ export default {
|
|
|
281
285
|
arrowType: 'round',
|
|
282
286
|
hideOnClick: false,
|
|
283
287
|
});
|
|
288
|
+
this.initialCharacterCount = this.currentCharacterCount;
|
|
289
|
+
this.previousCharacterCount = this.currentCharacterCount;
|
|
290
|
+
this.editor.on('update', ({ editor }) => {
|
|
291
|
+
this.warnings.forEach((warning) => {
|
|
292
|
+
if (warning.length && warning.offset) {
|
|
293
|
+
if (editor.state.selection.head - 1 <= warning.offset) {
|
|
294
|
+
const charCountDif =
|
|
295
|
+
this.currentCharacterCount - this.previousCharacterCount;
|
|
296
|
+
warning.offset += charCountDif;
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
});
|
|
300
|
+
this.previousCharacterCount = this.currentCharacterCount;
|
|
301
|
+
this.editor.commands.focus();
|
|
302
|
+
});
|
|
284
303
|
},
|
|
285
304
|
destroyed() {
|
|
286
305
|
this.editor.destroy();
|
|
@@ -297,8 +316,13 @@ export default {
|
|
|
297
316
|
value: err.value,
|
|
298
317
|
overrideClass: err.overrideClass,
|
|
299
318
|
isWord: err.isWord,
|
|
319
|
+
offset: err.offset,
|
|
320
|
+
length: err.length,
|
|
300
321
|
}));
|
|
301
322
|
},
|
|
323
|
+
getInitialCharacterCount() {
|
|
324
|
+
return this.initialCharacterCount;
|
|
325
|
+
},
|
|
302
326
|
upHandler() {
|
|
303
327
|
this.navigatedOptionIndex =
|
|
304
328
|
(this.navigatedOptionIndex + this.currentOptions.length - 1) %
|
|
@@ -362,8 +386,11 @@ export default {
|
|
|
362
386
|
if (this.editor) {
|
|
363
387
|
// preserve selection after updating warnings
|
|
364
388
|
const oldSelection = this.editor.selection;
|
|
365
|
-
this.editor.setContent(this.currentValue);
|
|
366
|
-
this.editor.setSelection(oldSelection.from, oldSelection.to);
|
|
389
|
+
this.editor.commands.setContent(this.currentValue);
|
|
390
|
+
this.editor.commands.setSelection(oldSelection.from, oldSelection.to);
|
|
391
|
+
|
|
392
|
+
// record length of text that was used to generate the list of warnings
|
|
393
|
+
this.initialCharacterCount = this.currentCharacterCount;
|
|
367
394
|
}
|
|
368
395
|
},
|
|
369
396
|
},
|
|
@@ -382,6 +409,7 @@ export default {
|
|
|
382
409
|
|
|
383
410
|
.tiptap-editor {
|
|
384
411
|
border: 1px solid #e5e7eb;
|
|
412
|
+
background: white;
|
|
385
413
|
border-radius: 8px;
|
|
386
414
|
padding: 4px;
|
|
387
415
|
|
package/src/warnings.js
CHANGED
|
@@ -20,7 +20,7 @@ function escapeRegExp(string) {
|
|
|
20
20
|
return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); // $& means the whole matched string
|
|
21
21
|
}
|
|
22
22
|
|
|
23
|
-
function lint(doc, position, prev, getErrorWords) {
|
|
23
|
+
function lint(doc, position, prev, getErrorWords, getInitialCharacterCount) {
|
|
24
24
|
const words = getErrorWords();
|
|
25
25
|
const regexString = words
|
|
26
26
|
.map((w) => (isWord(w) ? `\\b(${escapeRegExp(w.value)})\\b` : `(${escapeRegExp(w.value)})`))
|
|
@@ -64,7 +64,17 @@ function lint(doc, position, prev, getErrorWords) {
|
|
|
64
64
|
// Scan text nodes for bad words
|
|
65
65
|
let m;
|
|
66
66
|
while ((m = badWordsRegex.exec(node.text))) {
|
|
67
|
-
|
|
67
|
+
const originalErrorWord = words.find((word) => word.value === m[0]);
|
|
68
|
+
|
|
69
|
+
// highlight specific instance if the error has offset data
|
|
70
|
+
const indexOfMatchedWord = pos + m.index;
|
|
71
|
+
if (originalErrorWord.offset && originalErrorWord.length) {
|
|
72
|
+
if (indexOfMatchedWord == originalErrorWord.offset) {
|
|
73
|
+
record(indexOfMatchedWord, indexOfMatchedWord + m[0].length, m[0]);
|
|
74
|
+
}
|
|
75
|
+
} else {
|
|
76
|
+
record(indexOfMatchedWord, indexOfMatchedWord + m[0].length, m[0]);
|
|
77
|
+
}
|
|
68
78
|
}
|
|
69
79
|
}
|
|
70
80
|
});
|
|
@@ -82,6 +92,7 @@ const Warning = Node.create({
|
|
|
82
92
|
onEnter: () => {},
|
|
83
93
|
onExit: () => {},
|
|
84
94
|
onKeyDown: () => {},
|
|
95
|
+
getInitialCharacterCount: () => 0,
|
|
85
96
|
defaultClass: 'underline-red',
|
|
86
97
|
};
|
|
87
98
|
},
|
|
@@ -202,13 +213,25 @@ const Warning = Node.create({
|
|
|
202
213
|
},
|
|
203
214
|
state: {
|
|
204
215
|
init(_, { doc }) {
|
|
205
|
-
return lint(
|
|
216
|
+
return lint(
|
|
217
|
+
doc,
|
|
218
|
+
null,
|
|
219
|
+
{},
|
|
220
|
+
self.options.getErrorWords,
|
|
221
|
+
self.options.getInitialCharacterCount
|
|
222
|
+
);
|
|
206
223
|
},
|
|
207
224
|
apply(tr, prev) {
|
|
208
225
|
const { selection } = tr;
|
|
209
226
|
const next = Object.assign({}, prev);
|
|
210
227
|
const position = selection.$from;
|
|
211
|
-
return lint(
|
|
228
|
+
return lint(
|
|
229
|
+
tr.doc,
|
|
230
|
+
position,
|
|
231
|
+
prev,
|
|
232
|
+
self.options.getErrorWords,
|
|
233
|
+
self.options.getInitialCharacterCount
|
|
234
|
+
);
|
|
212
235
|
},
|
|
213
236
|
},
|
|
214
237
|
props: {
|
package/vite.config.js
CHANGED
|
@@ -1,8 +1,16 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import
|
|
3
|
-
|
|
4
|
-
|
|
1
|
+
import { resolve } from 'path';
|
|
2
|
+
import { defineConfig } from 'vite';
|
|
3
|
+
import vue from '@vitejs/plugin-vue2';
|
|
4
|
+
import cssInjectedByJsPlugin from 'vite-plugin-css-injected-by-js';
|
|
5
5
|
|
|
6
6
|
export default defineConfig({
|
|
7
|
-
plugins: [vue()],
|
|
8
|
-
|
|
7
|
+
plugins: [vue(), cssInjectedByJsPlugin()],
|
|
8
|
+
build: {
|
|
9
|
+
lib: {
|
|
10
|
+
entry: resolve(__dirname, 'src/index.js'),
|
|
11
|
+
name: 'TiptapEditor',
|
|
12
|
+
fileName: 'tiptap-editor',
|
|
13
|
+
formats: ['es'],
|
|
14
|
+
},
|
|
15
|
+
},
|
|
16
|
+
});
|