@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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mirus/tiptap-editor",
3
- "version": "2.0.0",
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.umd.min.js",
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: 200, //default null for infinity
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
- // default class is red
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&gt should not have any highlight.',
56
59
  localcount: null,
57
60
  };
58
61
  },
@@ -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
- {{ editor.storage.characterCount.characters() }} /
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
- currentCharacterCount: 0,
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.editor.storage.characterCount.characters() >= this.maxCharacterCount;
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
- record(pos + m.index, pos + m.index + m[0].length, m[0]);
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(doc, null, {}, self.options.getErrorWords);
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(tr.doc, position, prev, self.options.getErrorWords);
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 { defineConfig } from 'vite'
2
- import vue from '@vitejs/plugin-vue2'
3
-
4
- const path = require("path");
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
+ });