goodteditor-ui 1.0.55 → 1.0.57

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": "goodteditor-ui",
3
- "version": "1.0.55",
3
+ "version": "1.0.57",
4
4
  "main": "index.js",
5
5
  "homepage": "https://goodt-ui.netlify.app/",
6
6
  "scripts": {
@@ -13,19 +13,18 @@
13
13
  "dependencies": {
14
14
  "@popperjs/core": "2.11.2",
15
15
  "@tiptap/extension-bubble-menu": "^2.2.4",
16
- "@tiptap/extension-color": "2.0.x",
17
- "@tiptap/extension-font-family": "2.0.x",
18
- "@tiptap/extension-image": "2.0.x",
19
- "@tiptap/extension-link": "2.0.x",
20
- "@tiptap/extension-table": "2.0.x",
21
- "@tiptap/extension-table-cell": "2.0.x",
22
- "@tiptap/extension-table-header": "2.0.x",
23
- "@tiptap/extension-table-row": "2.0.x",
24
- "@tiptap/extension-text-align": "2.0.x",
25
- "@tiptap/extension-text-style": "2.0.x",
26
- "@tiptap/extension-underline": "2.0.x",
27
- "@tiptap/starter-kit": "2.0.x",
28
- "@tiptap/vue-2": "2.0.x"
16
+ "@tiptap/extension-color": "^2.0.0",
17
+ "@tiptap/extension-font-family": "^2.0.0",
18
+ "@tiptap/extension-image": "^2.0.0",
19
+ "@tiptap/extension-link": "^2.0.0",
20
+ "@tiptap/extension-table": "^2.0.0",
21
+ "@tiptap/extension-table-cell": "^2.0.0",
22
+ "@tiptap/extension-table-header": "^2.0.0",
23
+ "@tiptap/extension-table-row": "^2.0.0",
24
+ "@tiptap/extension-text-style": "^2.0.0",
25
+ "@tiptap/extension-underline": "^2.0.0",
26
+ "@tiptap/starter-kit": "^2.0.0",
27
+ "@tiptap/vue-2": "^2.0.0"
29
28
  },
30
29
  "devDependencies": {
31
30
  "@babel/plugin-syntax-dynamic-import": "^7.8.3",
@@ -234,23 +234,12 @@ export default {
234
234
  }
235
235
  this.tagSelected = tagSelected;
236
236
  },
237
- /**
238
- *
239
- * @param {KeyboardEvent} e
240
- * @return {boolean}
241
- */
242
- canSelectTag(e) {
243
- return e.target.value === '' && ['Backspace', 'ArrowLeft', 'ArrowRight', 'ArrowUp', 'ArrowDown'].indexOf(e.code)
244
- },
245
237
  /**
246
238
  *
247
239
  * @param {KeyboardEvent} e
248
240
  */
249
241
  onInputKeydown(e) {
250
242
  switch (e.key) {
251
- case ',':
252
- case ';':
253
- case 'Tab':
254
243
  case 'Enter': {
255
244
  this.onInputEnter(e);
256
245
  return;
@@ -261,7 +250,8 @@ export default {
261
250
  }
262
251
  }
263
252
 
264
- if (this.canSelectTag(e) === false) {
253
+ // if some text in input exist
254
+ if (e.target.value !== '') {
265
255
  return;
266
256
  }
267
257
 
@@ -29,7 +29,7 @@ Advanced example. Using custom tool groups, bubble menu and 'change' event inste
29
29
  tools,
30
30
  focusVisible: false,
31
31
  autofocus: false,
32
- bubbleMenu: { isEnabled: true}
32
+ bubbleMenu: true
33
33
  }"
34
34
  @change="onChange"></ui-wysiwyg-editor>
35
35
  </div>
@@ -1,81 +1,66 @@
1
1
  <template>
2
- <grid class="editor" v-bind="$options.static.GridProps">
3
- <template #editor-toolbar="{ style }">
4
- <div :style="style" ref="menu" :class="{ 'bubble': bubbleMenu.isEnabled }" class="editor-toolbar">
5
- <!--
6
- @slot Toolbar slot
7
- @binding {title: string, group: Tool[]} toolGroups array of using tools
8
- @binding {Function} buildToolBinds build tool binds function(tool:Tool)
9
- -->
10
- <slot
11
- v-if="editor != null"
12
- name="toolbar"
13
- v-bind="{ toolGroups, buildToolBinds }">
14
- <div class="row row-gap-l1">
15
- <div
16
- v-for="({ title, group }, groupIndex) of toolGroups"
17
- :key="groupIndex"
18
- class="col col-vbot col-auto">
19
- <div class="d-flex flex-col flex-v-center">
20
- <div v-if="resolveGroupTitle(title, groupIndex) !== ''" class="group-header">
2
+ <div class="d-flex flex-col">
3
+ <div ref="menu" :class="{ bubble: bubbleMenu, 'mar-bot-l1': !bubbleMenu }" class="editor-toolbar">
4
+ <!--
5
+ @slot Toolbar slot
6
+ @binding {title: string, group: Tool[]} toolGroups array of using tools
7
+ @binding {Function} buildToolBinds build tool binds function(tool:Tool)
8
+ -->
9
+ <slot v-if="editor != null" name="toolbar" v-bind="{ toolGroups, buildToolBinds }">
10
+ <div class="row row-gap-l1">
11
+ <div
12
+ v-for="({ title, group }, groupIndex) of toolGroups"
13
+ :key="groupIndex"
14
+ class="col col-vbot col-auto">
15
+ <div class="d-flex flex-col flex-v-center">
16
+ <div v-if="resolveGroupTitle(title, groupIndex) !== ''" class="group-header">
17
+ <!--
18
+ @slot Group header slot
19
+ @binding {number} groupIndex index of the tools group
20
+ @binding {string} title tools group title
21
+ -->
22
+ <slot name="group-header" v-bind="{ groupIndex, title }">
23
+ <div class="text-small text-center">{{ resolveGroupTitle(title, groupIndex) }}</div>
24
+ </slot>
25
+ </div>
26
+ <div>
27
+ <div v-for="tool of group" :key="tool.name" class="tool d-inline-block">
21
28
  <!--
22
- @slot Group header slot
23
- @binding {number} groupIndex index of the tools group
24
- @binding {string} title tools group title
29
+ @slot Tool slot
30
+ @binding {Tool} tool tool's already bound to editor context
25
31
  -->
26
- <slot name="group-header" v-bind="{ groupIndex, title }">
27
- <div class="text-small text-center">{{ resolveGroupTitle(title, groupIndex) }}</div>
32
+ <slot name="tool" v-bind="{ tool: buildToolBinds(tool) }">
33
+ <component :is="tool.render" :tool="buildToolBinds(tool)" />
28
34
  </slot>
29
35
  </div>
30
- <div>
31
- <div
32
- v-for="tool of group"
33
- :key="tool.name"
34
- class="tool d-inline-block">
35
- <!--
36
- @slot Tool slot
37
- @binding {Tool} tool tool's already bound to editor context
38
- -->
39
- <slot name="tool" v-bind="{ tool: buildToolBinds(tool) }">
40
- <component :is="tool.render" :tool="buildToolBinds(tool)" />
41
- </slot>
42
- </div>
43
- </div>
44
36
  </div>
45
37
  </div>
46
38
  </div>
47
- </slot>
48
- </div>
49
- </template>
50
- <template #editor-content="{ style }">
51
- <div :style="style" class="pad-v-l1" >
52
- <div class="editor-content h-100">
53
- <editor-content :editor="editor" class="h-100"/>
54
39
  </div>
55
- </div>
56
- </template>
57
- </grid>
40
+ </slot>
41
+ </div>
42
+ <div class="editor-content flex-grow d-flex">
43
+ <editor-content :editor="editor" class="w-100"/>
44
+ </div>
45
+ </div>
58
46
  </template>
59
47
 
60
48
  <script>
61
49
  import { Editor, EditorContent } from '@tiptap/vue-2';
62
50
 
63
51
  import { debounce } from '../utils/Helpers';
64
- import Grid from '../Grid.vue';
65
52
  import { resolveExtensions } from './extensions';
66
53
  import { buildToolGroups, bindContext } from './utils';
67
54
  import { DefaultTools, WysiwygAutofocus } from './constants';
68
55
 
69
56
  /**
70
57
  * @typedef {Omit<import('@tiptap/extension-bubble-menu').BubbleMenuOptions, 'element'>} BubbleMenuOptions
71
- * @property {boolean} isEnabled
72
- * @typedef {import('vue').PropOptions.<BubbleMenuOptions>} BubbleMenuOptionsProp
58
+ * @typedef {import('vue').PropOptions.<Boolean|BubbleMenuOptions>} BubbleMenuOptionsProp
73
59
  * @typedef {import('./constants').ITool} ITool
74
60
  */
75
61
 
76
62
  export default {
77
63
  components: {
78
- Grid,
79
64
  EditorContent
80
65
  },
81
66
  props: {
@@ -108,10 +93,8 @@ export default {
108
93
  * @see See [Bubble menu settings](https://tiptap.dev/docs/editor/api/extensions/bubble-menu#settings)
109
94
  */
110
95
  bubbleMenu: {
111
- type: Object,
112
- default: () => ({
113
- isEnabled: false
114
- })
96
+ type: [Boolean, Object],
97
+ default: false
115
98
  },
116
99
  /**
117
100
  * whether the outline of the focused content editor should be visible
@@ -147,7 +130,7 @@ export default {
147
130
  return buildToolGroups(this.tools);
148
131
  },
149
132
  editorClass() {
150
- const classes = ['pad-3', 'scroll-y'];
133
+ const classes = ['h-100', 'pad-3', 'scroll-y'];
151
134
 
152
135
  if (this.focusVisible === false) {
153
136
  classes.push('focus-outline-none');
@@ -156,13 +139,6 @@ export default {
156
139
  return classes.join(' ');
157
140
  }
158
141
  },
159
- static: {
160
- GridProps: {
161
- areas: [['editor-toolbar'], ['editor-content']],
162
- rows: ['auto', '1fr'],
163
- gap: ['0.5rem', 0]
164
- }
165
- },
166
142
  watch: {
167
143
  /**
168
144
  * @param {string} value
@@ -182,21 +158,21 @@ export default {
182
158
  },
183
159
  mounted() {
184
160
  const { $refs, value, bubbleMenu, autofocus, editorClass, onSelectionUpdateDebounced } = this;
185
- const { isEnabled: isBubbleMenuEnabled, ...bubbleMenuOptions } = bubbleMenu;
186
161
 
187
162
  this.editor = new Editor({
188
163
  content: value,
189
164
  extensions: resolveExtensions({
190
- bubbleMenu: {
191
- element: isBubbleMenuEnabled ? $refs.menu : null,
192
- ...bubbleMenuOptions
193
- }
165
+ ...(bubbleMenu && {
166
+ bubbleMenu: {
167
+ element: $refs.menu,
168
+ ...bubbleMenu
169
+ }
170
+ })
194
171
  }),
195
172
  autofocus,
196
173
  editorProps: {
197
174
  attributes: {
198
- class: editorClass,
199
- style: 'min-height: 100%; height: 0;'
175
+ class: editorClass
200
176
  }
201
177
  },
202
178
  onUpdate: () => {
@@ -10,9 +10,9 @@ import BubbleMenuToExtend from '@tiptap/extension-bubble-menu';
10
10
  * @return {Extension<BubbleMenuOptions, any>}
11
11
  */
12
12
  export const BubbleMenu = ({
13
- element,
14
- tippyOptions = { maxWidth: 'none', zIndex: 1000 },
15
- }) => BubbleMenuToExtend.configure({
13
+ element = null,
14
+ tippyOptions = { maxWidth: 'none', zIndex: 1010 },
15
+ } = {}) => BubbleMenuToExtend.configure({
16
16
  element,
17
17
  tippyOptions,
18
18
  });
@@ -1,6 +1,72 @@
1
- import { TextAlign as TextAlignToExtend } from '@tiptap/extension-text-align';
1
+ import { Extension } from '@tiptap/core';
2
2
  import { NodeType, MarkType } from '../constants';
3
3
 
4
- export const TextAlign = TextAlignToExtend.configure({
5
- types: [NodeType.HEADING, NodeType.PARAGRAPH, MarkType.LINK, NodeType.CODE_BLOCK, NodeType.BLOCKQUOTE]
4
+ /**
5
+ * we cannot use existing textAlign extension due to need to change textAlign global attribute renderHTML function
6
+ * that not allowed by simple default extension configuration
7
+ */
8
+ export const TextAlign = Extension.create({
9
+ name: 'textAlign',
10
+ addOptions() {
11
+ return {
12
+ types: [NodeType.HEADING, NodeType.PARAGRAPH, MarkType.LINK, NodeType.CODE_BLOCK, NodeType.BLOCKQUOTE],
13
+ alignments: ['left', 'center', 'right', 'justify'],
14
+ defaultAlignment: 'left',
15
+ };
16
+ },
17
+ addGlobalAttributes() {
18
+ return [
19
+ {
20
+ types: this.options.types,
21
+ attributes: {
22
+ textAlign: {
23
+ default: this.options.defaultAlignment,
24
+ parseHTML: element => {
25
+ return element.style.textAlign || this.options.defaultAlignment;
26
+ },
27
+ renderHTML: attributes => {
28
+ if (attributes.textAlign === this.options.defaultAlignment) {
29
+ return {};
30
+ }
31
+ /**
32
+ * because we gave users access to style attribute
33
+ * (and now editor doesn't reset it when renders HTML)
34
+ * we have to sanitize style attribute by ourselves to avoid duplication of textAlign written
35
+ * as independent attribute and part of style attribute string in the same time
36
+ */
37
+ if (/text-align/.test(attributes?.style)) {
38
+ attributes.style = attributes.style.replace(/text-align:\s?\w+;?\s?/, '');
39
+ }
40
+ return { style: `text-align: ${attributes.textAlign}` };
41
+ }
42
+ }
43
+ }
44
+ },
45
+ ];
46
+ },
47
+ addCommands() {
48
+ return {
49
+ setTextAlign: (alignment) => ({ commands }) => {
50
+ if (!this.options.alignments.includes(alignment)) {
51
+ return false;
52
+ }
53
+ return this.options.types
54
+ .map(type => commands.updateAttributes(type, { textAlign: alignment }))
55
+ .every(response => response);
56
+ },
57
+ unsetTextAlign: () => ({ commands }) => {
58
+ return this.options.types
59
+ .map(type => commands.resetAttributes(type, 'textAlign'))
60
+ .every(response => response);
61
+ },
62
+ };
63
+ },
64
+ addKeyboardShortcuts() {
65
+ return {
66
+ 'Mod-Shift-l': () => this.editor.commands.setTextAlign('left'),
67
+ 'Mod-Shift-e': () => this.editor.commands.setTextAlign('center'),
68
+ 'Mod-Shift-r': () => this.editor.commands.setTextAlign('right'),
69
+ 'Mod-Shift-j': () => this.editor.commands.setTextAlign('justify'),
70
+ };
71
+ }
6
72
  });
@@ -25,7 +25,7 @@
25
25
  </div>
26
26
  <div class="row row-hgap-3 mar-bot-l1">
27
27
  <div class="col col-vmid text-truncate">
28
- <div class="form-label form-label-xsmall text-truncate">Target</div>
28
+ <div class="form-label form-label-xsmall text-truncate">Тип перехода</div>
29
29
  </div>
30
30
  <div class="col col-vmid col-12-12">
31
31
  <ui-select
@@ -68,8 +68,8 @@ export default {
68
68
  }),
69
69
  static: {
70
70
  TargetTypeOptions: [
71
- { label: 'blank', value: TargetType.BLANK },
72
- { label: 'self', value: TargetType.SELF }
71
+ { label: 'В новой вкладке', value: TargetType.BLANK },
72
+ { label: 'В текущей вкладке', value: TargetType.SELF }
73
73
  ]
74
74
  },
75
75
  methods: {