goodteditor-ui 1.0.44 → 1.0.45

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/index.js CHANGED
@@ -22,14 +22,16 @@ import Select from './src/components/ui/Select.vue';
22
22
  import TimePicker from './src/components/ui/TimePicker.vue';
23
23
  import Tooltip from './src/components/ui/Tooltip.vue';
24
24
  import Grid from './src/components/ui/Grid.vue';
25
- import WysiwygEditor from './src/components/ui/WysiwygEditor.vue';
25
+ import WysiwygEditor, { ToolType as WysiwygTool } from './src/components/ui/WysiwygEditor.vue';
26
26
  // utils stuff
27
27
  import FormComponent from './src/components/ui/utils/FormComponent';
28
28
 
29
29
  const Utils = { FormComponent };
30
+ const Constants = { WysiwygTool };
30
31
 
31
32
  export {
32
33
  Utils,
34
+ Constants,
33
35
  Avatar,
34
36
  Badge,
35
37
  Collapse,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "goodteditor-ui",
3
- "version": "1.0.44",
3
+ "version": "1.0.45",
4
4
  "main": "index.js",
5
5
  "homepage": "https://goodt-ui.netlify.app/",
6
6
  "scripts": {
@@ -12,6 +12,7 @@
12
12
  },
13
13
  "dependencies": {
14
14
  "@popperjs/core": "2.11.2",
15
+ "@tiptap/extension-bubble-menu": "^2.2.4",
15
16
  "@tiptap/extension-color": "2.0.x",
16
17
  "@tiptap/extension-font-family": "2.0.x",
17
18
  "@tiptap/extension-image": "2.0.x",
@@ -255,19 +255,28 @@ export const createLinkTool = ({ getValue = () => null, ...toolOptions }) => ({
255
255
  });
256
256
 
257
257
  export const DefaultTools = [
258
- [ToolType.PARAGRAPH_STYLE, ToolType.TEXT_COLOR, ToolType.FONT_SIZE, ToolType.FONT_FAMILY, ToolType.BLOCKQUOTE],
259
- [ToolType.BOLD, ToolType.ITALIC, ToolType.UNDERLINE, ToolType.STRIKE, ToolType.CODE],
260
- [ToolType.TEXT_ALIGN_LEFT, ToolType.TEXT_ALIGN_CENTER, ToolType.TEXT_ALIGN_RIGHT],
261
- [ToolType.ORDERED_LIST, ToolType.BULLET_LIST],
262
- [ToolType.IMAGE, ToolType.LINK, ToolType.TABLE],
263
- [ToolType.CLEAR_FORMATTING, ToolType.HARD_BREAK]
264
- ];
265
-
266
- export const DefaultToolGroupsTitles = [
267
- 'Оформление',
268
- 'Форматирование',
269
- 'Выравнивание',
270
- 'Нумерация',
271
- 'Вставка',
272
- ''
258
+ {
259
+ title: 'Оформление',
260
+ group: [ToolType.PARAGRAPH_STYLE, ToolType.TEXT_COLOR, ToolType.FONT_SIZE, ToolType.FONT_FAMILY, ToolType.BLOCKQUOTE]
261
+ },
262
+ {
263
+ title: 'Форматирование',
264
+ group: [ToolType.BOLD, ToolType.ITALIC, ToolType.UNDERLINE, ToolType.STRIKE, ToolType.CODE]
265
+ },
266
+ {
267
+ title: 'Выравнивание',
268
+ group: [ToolType.TEXT_ALIGN_LEFT, ToolType.TEXT_ALIGN_CENTER, ToolType.TEXT_ALIGN_RIGHT]
269
+ },
270
+ {
271
+ title: 'Нумерация',
272
+ group: [ToolType.ORDERED_LIST, ToolType.BULLET_LIST]
273
+ },
274
+ {
275
+ title: 'Вставка',
276
+ group: [ToolType.IMAGE, ToolType.LINK, ToolType.TABLE]
277
+ },
278
+ {
279
+ title: '',
280
+ group: [ToolType.CLEAR_FORMATTING, ToolType.HARD_BREAK]
281
+ }
273
282
  ];
@@ -0,0 +1,18 @@
1
+ import BubbleMenuToExtend from '@tiptap/extension-bubble-menu';
2
+
3
+ /**
4
+ * @typedef {import('@tiptap/extension-bubble-menu').BubbleMenuOptions} BubbleMenuOptions
5
+ * @typedef {import('@tiptap/core').Extension} Extension
6
+ */
7
+
8
+ /**
9
+ * @param {BubbleMenuOptions} options
10
+ * @return {Extension<BubbleMenuOptions, any>}
11
+ */
12
+ export const BubbleMenu = ({
13
+ element,
14
+ tippyOptions = { maxWidth: 'none', zIndex: 1000 },
15
+ }) => BubbleMenuToExtend.configure({
16
+ element,
17
+ tippyOptions,
18
+ });
@@ -1,32 +1,4 @@
1
- export { Document } from '@tiptap/extension-document';
2
- export { Text } from '@tiptap/extension-text';
3
- export { History } from '@tiptap/extension-history';
4
- export { Dropcursor } from '@tiptap/extension-dropcursor';
5
- export { Gapcursor } from '@tiptap/extension-gapcursor';
6
- export { Color } from '@tiptap/extension-color';
7
- export { FontFamily } from '@tiptap/extension-font-family';
8
- export { HardBreak } from '@tiptap/extension-hard-break';
1
+ import { Extension } from '@tiptap/core';
2
+ import { BubbleMenuOptions } from '@tiptap/extension-bubble-menu';
9
3
 
10
- export { Table } from './table';
11
- export { TableRow } from './table-row';
12
- export { TableCell } from './table-cell';
13
- export { TableHeader } from './table-header';
14
- export { Heading } from './heading';
15
- export { Code } from './code';
16
- export { CodeBlock } from './code-block';
17
- export { Link } from './link';
18
- export { TextAlign } from './text-align';
19
- export { FontSize } from './font-size';
20
- export { Paragraph } from './paragraph';
21
- export { Formatting } from './formatting';
22
- export { TextStyle } from './text-style';
23
- export { Blockquote } from './blockquote';
24
- export { Bold } from './bold';
25
- export { Italic } from './italic';
26
- export { Underline } from './underline';
27
- export { Strike } from './strike';
28
- export { OrderedList } from './ordered-list';
29
- export { BulletList } from './bullet-list';
30
- export { ListItem } from './list-item';
31
- export { Image } from './image';
32
- export { HorizontalRule } from './horizontal-rule';
4
+ export function resolveExtensions(options: { bubbleMenu: BubbleMenuOptions }): (Node<any, any>|Extension<any, any>)[]
@@ -1,32 +1,80 @@
1
- export { Document } from '@tiptap/extension-document';
2
- export { Text } from '@tiptap/extension-text';
3
- export { History } from '@tiptap/extension-history';
4
- export { Dropcursor } from '@tiptap/extension-dropcursor';
5
- export { Gapcursor } from '@tiptap/extension-gapcursor';
6
- export { Color } from '@tiptap/extension-color';
7
- export { FontFamily } from '@tiptap/extension-font-family';
8
- export { HardBreak } from '@tiptap/extension-hard-break';
1
+ import { Document } from '@tiptap/extension-document';
2
+ import { Text } from '@tiptap/extension-text';
3
+ import { History } from '@tiptap/extension-history';
4
+ import { Dropcursor } from '@tiptap/extension-dropcursor';
5
+ import { Gapcursor } from '@tiptap/extension-gapcursor';
6
+ import { Color } from '@tiptap/extension-color';
7
+ import { FontFamily } from '@tiptap/extension-font-family';
8
+ import { HardBreak } from '@tiptap/extension-hard-break';
9
9
 
10
- export { Table } from './table';
11
- export { TableRow } from './table-row';
12
- export { TableCell } from './table-cell';
13
- export { TableHeader } from './table-header';
14
- export { Heading } from './heading';
15
- export { Code } from './code';
16
- export { CodeBlock } from './code-block';
17
- export { Link } from './link';
18
- export { TextAlign } from './text-align';
19
- export { FontSize } from './font-size';
20
- export { Paragraph } from './paragraph';
21
- export { Formatting } from './formatting';
22
- export { TextStyle } from './text-style';
23
- export { Blockquote } from './blockquote';
24
- export { Bold } from './bold';
25
- export { Italic } from './italic';
26
- export { Underline } from './underline';
27
- export { Strike } from './strike';
28
- export { OrderedList } from './ordered-list';
29
- export { BulletList } from './bullet-list';
30
- export { ListItem } from './list-item';
31
- export { Image } from './image';
32
- export { HorizontalRule } from './horizontal-rule';
10
+ import { Table } from './table';
11
+ import { TableRow } from './table-row';
12
+ import { TableCell } from './table-cell';
13
+ import { TableHeader } from './table-header';
14
+ import { Heading } from './heading';
15
+ import { Code } from './code';
16
+ import { CodeBlock } from './code-block';
17
+ import { Link } from './link';
18
+ import { TextAlign } from './text-align';
19
+ import { FontSize } from './font-size';
20
+ import { Paragraph } from './paragraph';
21
+ import { Formatting } from './formatting';
22
+ import { TextStyle } from './text-style';
23
+ import { Blockquote } from './blockquote';
24
+ import { Bold } from './bold';
25
+ import { Italic } from './italic';
26
+ import { Underline } from './underline';
27
+ import { Strike } from './strike';
28
+ import { OrderedList } from './ordered-list';
29
+ import { BulletList } from './bullet-list';
30
+ import { ListItem } from './list-item';
31
+ import { Image } from './image';
32
+ import { HorizontalRule } from './horizontal-rule';
33
+ import { BubbleMenu } from './bubble-menu';
34
+
35
+ /**
36
+ * @typedef {import('@tiptap/core').Extension} Extension
37
+ * @typedef {import('@tiptap/extension-bubble-menu').BubbleMenuOptions} BubbleMenuOptions
38
+ */
39
+
40
+ /**
41
+ *
42
+ * @param {{bubbleMenu: BubbleMenuOptions}} options
43
+ * @return {Array<Node<any, any>|Extension<any,any>>}
44
+ */
45
+ export function resolveExtensions(options) {
46
+ return [
47
+ Document,
48
+ Text,
49
+ History,
50
+ Dropcursor,
51
+ Gapcursor,
52
+ Color,
53
+ FontFamily,
54
+ HardBreak,
55
+ Table,
56
+ TableRow,
57
+ TableCell,
58
+ TableHeader,
59
+ Heading,
60
+ Code,
61
+ CodeBlock,
62
+ Link,
63
+ TextAlign,
64
+ FontSize,
65
+ Paragraph,
66
+ Formatting,
67
+ TextStyle,
68
+ Blockquote,
69
+ Bold,
70
+ Italic,
71
+ Underline,
72
+ Strike,
73
+ OrderedList,
74
+ BulletList,
75
+ ListItem,
76
+ Image,
77
+ HorizontalRule,
78
+ BubbleMenu(options.bubbleMenu)
79
+ ]
80
+ }
@@ -1,9 +1,9 @@
1
1
  <template>
2
2
  <div :title="title">
3
3
  <button
4
- :class="{ 'btn-outline': !isActive }"
4
+ :class="{ 'btn-outline': !isActive, 'btn-primary': isActive }"
5
5
  :disabled="!isEnabled"
6
- class="btn btn-small btn-icon btn-primary"
6
+ class="btn btn-small btn-icon"
7
7
  @click.stop="onClick">
8
8
  <div class="icon">
9
9
  <i :class="icon" class="mdi"></i>
@@ -1,7 +1,8 @@
1
1
  <template>
2
2
  <div :data-popover="popoverTargetId" :title="title">
3
3
  <button
4
- class="btn btn-small btn-outline btn-primary btn-icon"
4
+ :class="{ 'btn-outline': !popoverShow, 'btn-primary': popoverShow }"
5
+ class="btn btn-small btn-icon"
5
6
  @click="togglePopover">
6
7
  <div class="icon">
7
8
  <i class="mdi" :class="icon"></i>
@@ -3,7 +3,8 @@
3
3
  <template #button="{ togglePopover }">
4
4
  <button
5
5
  :title="title"
6
- class="btn btn-small btn-primary btn-outline btn-icon"
6
+ :class="{ 'btn-outline': !isPopoverShown, 'btn-primary': isPopoverShown }"
7
+ class="btn btn-small btn-icon"
7
8
  @click="togglePopover">
8
9
  <div class="icon">
9
10
  <i class="mdi" :class="icon"></i>
@@ -54,7 +55,7 @@
54
55
  <div class="col col-vmid col-12-12">
55
56
  <ui-select
56
57
  v-model="image.align"
57
- :options="AlignOptions"
58
+ :options="$options.static.AlignOptions"
58
59
  size="small"
59
60
  class="w-100"
60
61
  @change="onChange" />
@@ -70,7 +71,7 @@
70
71
  <div class="col col-vmid col-12-12">
71
72
  <input-units
72
73
  v-model="image.width"
73
- :units="SizeUnits"
74
+ :units="$options.static.SizeUnits"
74
75
  :disabled="image.isResponsive"
75
76
  size="small"
76
77
  @change="onChange">
@@ -86,7 +87,7 @@
86
87
  <div class="col col-vmid col-12-12">
87
88
  <input-units
88
89
  v-model="image.height"
89
- :units="SizeUnits"
90
+ :units="$options.static.SizeUnits"
90
91
  :disabled="image.isResponsive"
91
92
  size="small"
92
93
  @change="onChange">
@@ -3,7 +3,8 @@
3
3
  <template #button="{ togglePopover }">
4
4
  <button
5
5
  :title="title"
6
- class="btn btn-small btn-primary btn-outline btn-icon"
6
+ :class="{ 'btn-outline': !isPopoverShown, 'btn-primary': isPopoverShown }"
7
+ class="btn btn-small btn-icon"
7
8
  @click="togglePopover">
8
9
  <div class="icon">
9
10
  <i class="mdi" :class="icon"></i>
@@ -25,7 +26,7 @@
25
26
  <div class="col col-vmid col-12-12">
26
27
  <ui-select
27
28
  v-model="target"
28
- :options="TargetTypeOptions"
29
+ :options="$options.static.TargetTypeOptions"
29
30
  class="w-100"
30
31
  size="small" />
31
32
  </div>
@@ -1,7 +1,8 @@
1
1
  <template>
2
2
  <div :data-popover="popoverTargetId" :title="title">
3
3
  <button
4
- class="btn btn-small btn-outline btn-primary btn-icon"
4
+ :class="{ 'btn-outline': !popoverShow, 'btn-primary': popoverShow }"
5
+ class="btn btn-small btn-icon"
5
6
  @click="togglePopover">
6
7
  <div class="icon">
7
8
  <i class="mdi" :class="icon"></i>
@@ -3,49 +3,21 @@ import { ToolsMap } from './tools-and-commands';
3
3
  /**
4
4
  * @typedef {import('./WysiwygEditor').ToolDef} Tool
5
5
  * @typedef {import('./WysiwygEditor').CommandDef} Command
6
+ * @typedef {Object} ToolGroup
7
+ * @property {string} title
8
+ * @property {string[]} group
6
9
  */
7
10
 
8
11
  /**
9
- * @param {Tool | string} tool
10
- * @param {string} toolName
11
- * @return boolean
12
+ * @param {ToolGroup[]} toolGroups
13
+ * @return {{title:string, group: Tool[]}[]}
12
14
  */
13
- const findTool = (tool, toolName) => {
14
- if (typeof tool === 'string') {
15
- return toolName === tool;
16
- }
17
-
18
- return tool?.name === toolName;
19
- };
20
-
21
- /**
22
- * @param {Tool[][]} tools
23
- * @return Tool[][]
24
- */
25
- export const buildToolGroups = (tools) => {
26
- const toolsFlatted = tools.flat();
27
-
28
- if (toolsFlatted.length === 0) {
29
- return [Object.values(ToolsMap)];
30
- }
31
-
32
- const availableTools = Object
33
- .values(ToolsMap)
34
- .filter(({ name }) => toolsFlatted.some((tool) => findTool(tool, name)));
35
-
36
- /**
37
- * @param {Tool[]} toolsGroup
38
- * @return Tool[]
39
- */
40
- const populateToolsGroup = (toolsGroup) =>
41
- toolsGroup.reduce((acc, tool) => {
42
- const { name: toolName = tool, ...rest } = typeof tool === 'string' ? {} : tool;
43
- const foundTool = availableTools.find(({ name }) => findTool(tool, name));
44
-
45
- return [...acc, { ...foundTool, ...rest }];
46
- }, []);
15
+ export const buildToolGroups = (toolGroups) => {
16
+ const toolsFlattened = new Set(toolGroups.flatMap(({ group }) => group));
17
+ const availableTools = Object.values(ToolsMap).filter(({ name }) => toolsFlattened.has(name));
18
+ const populateToolsGroup = (tools)=> availableTools.filter(({ name }) => tools.has(name));
47
19
 
48
- return tools.reduce((acc, toolsGroup) => [...acc, populateToolsGroup(toolsGroup)], []);
20
+ return toolGroups.reduce((acc, { title, group }) => [...acc, { title, group: populateToolsGroup(new Set(group)) }], []);
49
21
  };
50
22
 
51
23
  /**
@@ -1,20 +1,19 @@
1
1
  <template>
2
- <grid class="editor" v-bind="GridProps">
2
+ <grid class="editor" v-bind="$options.static.GridProps">
3
3
  <template #editor-toolbar="{ style }">
4
- <div :style="style" class="editor-toolbar">
4
+ <div :style="style" ref="menu" :class="{ 'bubble': bubbleMenu }" class="editor-toolbar">
5
5
  <!--
6
6
  @slot Toolbar slot
7
- @binding {Tool[][]} toolGroups 2d matrix of using tools split into groups
8
- @binding {Function} resolveGroupTitle resolve group title function(index:number)
9
- @binding {Function} buildToolBinds build tool binds function(tool:Tool)
7
+ @binding {title: string, group: Tool[]} toolGroups array of using tools
8
+ @binding {Function} buildToolBinds build tool binds function(tool:Tool)
10
9
  -->
11
10
  <slot
12
11
  v-if="editor != null"
13
12
  name="toolbar"
14
- v-bind="{ toolGroups, resolveGroupTitle, buildToolBinds }">
13
+ v-bind="{ toolGroups, buildToolBinds }">
15
14
  <div class="row row-gap-l1">
16
15
  <div
17
- v-for="(toolsGroup, groupIndex) of toolGroups"
16
+ v-for="({ title, group }, groupIndex) of toolGroups"
18
17
  :key="groupIndex"
19
18
  class="col col-vmid col-auto">
20
19
  <div class="d-flex flex-col flex-v-center">
@@ -22,22 +21,22 @@
22
21
  <!--
23
22
  @slot Group header slot
24
23
  @binding {number} groupIndex index of the tools group
25
- @binding {Function} resolveGroupTitle resolve group title function(index:number)
24
+ @binding {string} title tools group title
26
25
  -->
27
- <slot name="group-header" v-bind="{ groupIndex, resolveGroupTitle }">
28
- <div class="text-small text-center">{{ resolveGroupTitle(groupIndex) }}</div>
26
+ <slot name="group-header" v-bind="{ groupIndex, title }">
27
+ <div class="text-small text-center">{{ resolveGroupTitle(title, groupIndex) }}</div>
29
28
  </slot>
30
29
  </div>
31
30
  <div>
32
31
  <div
33
- v-for="tool of toolsGroup"
32
+ v-for="tool of group"
34
33
  :key="tool.name"
35
34
  class="tool d-inline-block">
36
35
  <!--
37
36
  @slot Tool slot
38
37
  @binding {Tool} tool tool's already bound to editor context
39
38
  -->
40
- <slot name="tool" v-bind="buildToolBinds(tool)">
39
+ <slot name="tool" v-bind="{ tool: buildToolBinds(tool) }">
41
40
  <component :is="tool.render" :tool="buildToolBinds(tool)" />
42
41
  </slot>
43
42
  </div>
@@ -63,16 +62,23 @@ import { Editor, EditorContent } from '@tiptap/vue-2';
63
62
 
64
63
  import { debounce } from './utils/Helpers';
65
64
  import Grid from './Grid.vue';
66
- import * as Extensions from './WysiwygEditor/extensions';
65
+ import { resolveExtensions } from './WysiwygEditor/extensions';
67
66
  import { buildToolGroups, bindContext } from './WysiwygEditor/utils';
68
- import { DefaultTools, DefaultToolGroupsTitles } from './WysiwygEditor/constants';
67
+ import { DefaultTools } from './WysiwygEditor/constants';
68
+ import Popup from '@/components/ui/Popup.vue';
69
+
70
+ export { ToolType } from './WysiwygEditor/constants';
69
71
 
70
72
  /**
73
+ * @typedef {Omit<import('@tiptap/extension-bubble-menu').BubbleMenuOptions, 'element'>} BubbleMenuOptions
74
+ * @property {boolean} isEnabled
75
+ * @typedef {import('vue').PropOptions.<BubbleMenuOptions>} BubbleMenuOptionsProp
71
76
  * @typedef {import('./WysiwygEditor/WysiwygEditor').ToolDef} Tool
72
77
  */
73
78
 
74
79
  export default {
75
80
  components: {
81
+ Popup,
76
82
  Grid,
77
83
  EditorContent
78
84
  },
@@ -85,22 +91,12 @@ export default {
85
91
  default: '',
86
92
  },
87
93
  /**
88
- * 2d matrix of using tools
94
+ * array of using tools: { title: string, group: string[] }
89
95
  */
90
96
  tools: {
91
97
  type: Array,
92
98
  default: () => [],
93
- validator: (tools) => tools.flat().every((tool) =>
94
- typeof tool === 'string' || (tool != null && typeof tool === 'object' && Array.isArray(tool) === false)
95
- )
96
- },
97
- /**
98
- * editor tool groups titles
99
- */
100
- groupsTitles: {
101
- type: Array,
102
- default: () => [],
103
- validator: (titles) => titles.every((title) => typeof title === 'string')
99
+ validator: (tools) => tools.every((tool) => typeof tool === 'object' && Array.isArray(tool) === false)
104
100
  },
105
101
  /**
106
102
  * @public
@@ -109,16 +105,29 @@ export default {
109
105
  getImageUrl: {
110
106
  type: Function,
111
107
  default: async () => {}
108
+ },
109
+ /**
110
+ * bubble menu options
111
+ * @type {BubbleMenuOptionsProp}
112
+ * @see See [Bubble menu settings](https://tiptap.dev/docs/editor/api/extensions/bubble-menu#settings)
113
+ */
114
+ bubbleMenu: {
115
+ type: Object,
116
+ default: () => ({
117
+ isEnabled: false
118
+ })
119
+ },
120
+ /**
121
+ * whether the outline of the focused content editor should be visible
122
+ */
123
+ focusVisible: {
124
+ type: Boolean,
125
+ default: true
112
126
  }
113
127
  },
114
128
  data: () => ({
115
129
  /** @type {Editor} */
116
130
  editor: null,
117
- /** @type {Tool[][]} */
118
- appliedTools: null,
119
- /** @type {string[]} */
120
- appliedGroupsTitles: null,
121
- /** @type {number} */
122
131
  caretPosition: 0
123
132
  }),
124
133
  computed: {
@@ -129,10 +138,20 @@ export default {
129
138
  return this.editor?.getHTML() ?? '';
130
139
  },
131
140
  /**
132
- * @return {Tool[][]}
141
+ * @return {{title: string, group: Tool[]}[]}
133
142
  */
134
143
  toolGroups() {
135
- return buildToolGroups(this.appliedTools);
144
+ const tools = this.tools.length === 0 ? DefaultTools : this.tools;
145
+ return buildToolGroups(tools);
146
+ },
147
+ editorClass() {
148
+ const classes = ['pad-3', 'scroll-y'];
149
+
150
+ if (this.focusVisible === false) {
151
+ classes.push('focus-outline-none');
152
+ }
153
+
154
+ return classes.join(' ');
136
155
  }
137
156
  },
138
157
  static: {
@@ -158,16 +177,22 @@ export default {
158
177
  },
159
178
  created() {
160
179
  this.onSelectionUpdateDebounced = debounce(this.onSelectionUpdate, 300);
161
- this.appliedTools = this.tools.length === 0 ? DefaultTools : this.tools;
162
- this.appliedGroupsTitles = this.groupsTitles.length === 0 ? DefaultToolGroupsTitles : this.groupsTitles;
180
+ },
181
+ mounted() {
182
+ const { isEnabled: isBubbleMenuEnabled, ...bubbleMenuOptions } = this.bubbleMenu;
163
183
 
164
184
  this.editor = new Editor({
165
185
  content: this.value,
166
- extensions: Object.values(Extensions),
186
+ extensions: resolveExtensions({
187
+ bubbleMenu: {
188
+ element: isBubbleMenuEnabled ? this.$refs.menu : null,
189
+ ...bubbleMenuOptions
190
+ }
191
+ }),
167
192
  autofocus: 'end',
168
193
  editorProps: {
169
194
  attributes: {
170
- class: 'pad-3 scroll-y',
195
+ class: this.editorClass,
171
196
  style: 'min-height: 100%; height: 0;'
172
197
  }
173
198
  },
@@ -220,20 +245,20 @@ export default {
220
245
  return tool;
221
246
  },
222
247
  get tools() {
223
- return toolGroups.flat();
248
+ return toolGroups.flatMap(({ group }) => group);
224
249
  },
225
250
  change: this.change,
226
- getImageUrl: this.getImageUrl
251
+ getImageUrl: this.getImageUrl,
227
252
  });
228
253
 
229
254
  return bindContext(tool, context);
230
255
  },
231
256
  /**
257
+ * @param {string} title
232
258
  * @param {number} groupIndex
233
259
  * @return string
234
260
  */
235
- resolveGroupTitle(groupIndex) {
236
- const title = this.appliedGroupsTitles[groupIndex];
261
+ resolveGroupTitle(title, groupIndex) {
237
262
  return typeof title === 'string' ? title : `Group ${groupIndex + 1}`;
238
263
  }
239
264
  }
@@ -241,7 +266,14 @@ export default {
241
266
  </script>
242
267
  <style lang="less" scoped>
243
268
  .editor {
244
- &-toolbar {}
269
+ &-toolbar {
270
+ &.bubble {
271
+ padding: 1rem;
272
+ background-color: var(--color-white);
273
+ border-radius: var(--border-radius);
274
+ box-shadow: 0 1px 4px -2px rgba(0, 0, 0, 0.25);
275
+ }
276
+ }
245
277
 
246
278
  &-content {
247
279
  min-height: 7.5rem;
@@ -259,6 +291,10 @@ export default {
259
291
  }
260
292
 
261
293
  ::v-deep .ProseMirror {
294
+ &.focus-outline-none:focus-visible {
295
+ outline: none;
296
+ }
297
+
262
298
  & .ProseMirror-selectednode {
263
299
  outline: 2px solid var(--color-focus);
264
300
  }