@madgex/design-system-ce 5.1.2 → 5.3.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/.eslintrc.js +9 -2
- package/CHANGELOG.md +49 -0
- package/components/internals/MdsIcon.vue +29 -0
- package/components/text-editor/TextEditor.ce.vue +71 -0
- package/components/text-editor/TextEditorButton.vue +60 -0
- package/components/text-editor/TextEditorContent.vue +76 -0
- package/components/text-editor/TextEditorPopover.vue +113 -0
- package/components/text-editor/TextEditorPopoverLink.vue +100 -0
- package/components/text-editor/TextEditorToolbar.vue +178 -0
- package/components/text-editor/apply-missing-protocol.js +7 -0
- package/custom-elements/mds-combobox.js +13 -0
- package/custom-elements/mds-text-editor.js +12 -0
- package/dist/custom-elements/mds-combobox.js +1 -0
- package/dist/custom-elements/mds-text-editor.js +94 -0
- package/dist/index.js +1 -2
- package/dist/manifest.json +25 -11
- package/dist/plugin-vue_export-helper.js +2 -0
- package/index-dev.js +2 -0
- package/index.js +8 -11
- package/package.json +6 -2
- package/vite.config.js +7 -15
- package/dist/assets/polyfills-legacy.bfe7dd4c.js +0 -1
- package/dist/index-legacy.js +0 -1
package/.eslintrc.js
CHANGED
|
@@ -1,7 +1,14 @@
|
|
|
1
1
|
module.exports = {
|
|
2
|
-
extends: ['plugin:vue/essential', '@vue/prettier'],
|
|
3
|
-
rules: {},
|
|
4
2
|
env: {
|
|
5
3
|
browser: true,
|
|
6
4
|
},
|
|
5
|
+
extends: ['eslint:recommended', 'plugin:vue/vue3-recommended', 'prettier'],
|
|
6
|
+
rules: {
|
|
7
|
+
'vue/no-v-html': 0,
|
|
8
|
+
'no-debugger': 0,
|
|
9
|
+
'vue/singleline-html-element-content-newline': 'off',
|
|
10
|
+
'vue/multiline-html-element-content-newline': 'off',
|
|
11
|
+
'vue/html-indent': 'off',
|
|
12
|
+
'vue/html-self-closing': ['error', { html: { void: 'any' } }],
|
|
13
|
+
},
|
|
7
14
|
};
|
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,55 @@
|
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
|
4
4
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
|
5
5
|
|
|
6
|
+
## [5.3.0](https://github.com/projects/MDS/repos/mds-branding/compare/diff?targetBranch=refs/tags/@madgex/design-system-ce@5.2.0&sourceBranch=refs/tags/@madgex/design-system-ce@5.3.0) (2022-11-25)
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
### Features
|
|
10
|
+
|
|
11
|
+
* add link for text editor ([288ad29](https://github.com/projects/MDS/repos/mds-branding/commits/288ad2988076cebddeebab420e3e5438a5d2f818))
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
### Bug Fixes
|
|
15
|
+
|
|
16
|
+
* automatically apply protocol to textEditor link ([fee5fc7](https://github.com/projects/MDS/repos/mds-branding/commits/fee5fc760d7fc3e01f3bff56022b5fd942cb6d44))
|
|
17
|
+
* remove link editor popover/button ([db6c5f8](https://github.com/projects/MDS/repos/mds-branding/commits/db6c5f8bb64d2a2d44a5ce13b7f4d943664a7aad))
|
|
18
|
+
* remove link editor popover/button ([0b263f8](https://github.com/projects/MDS/repos/mds-branding/commits/0b263f824e7deebd74655aa990e5c1c9e771b831))
|
|
19
|
+
* remove unused code ([b29e960](https://github.com/projects/MDS/repos/mds-branding/commits/b29e96030d7f53242d65335022a2b0af49fff733))
|
|
20
|
+
* texteditor anchor validation ([5e40b46](https://github.com/projects/MDS/repos/mds-branding/commits/5e40b462974a26473d36c2b033de4d1eb606c368))
|
|
21
|
+
* texteditor applyMissingProtocol mailto ([af94428](https://github.com/projects/MDS/repos/mds-branding/commits/af944282bc8f2aa239031a09310112cf0d153924))
|
|
22
|
+
* textEditor link enabled if valid selection ([654dc8d](https://github.com/projects/MDS/repos/mds-branding/commits/654dc8d61d1846e684d3c3cd3b88b84d011cbc87))
|
|
23
|
+
* textEditor link popover spacing ([2236c64](https://github.com/projects/MDS/repos/mds-branding/commits/2236c6442261f05dee13ab82319bc503d9262832))
|
|
24
|
+
* textEditor links always underline ([d7851e3](https://github.com/projects/MDS/repos/mds-branding/commits/d7851e355b1e8dcbbf4c75c6ad8eac8b6c60ed44))
|
|
25
|
+
* textEditor linkUrl i18n, for/id couple ([2cdad01](https://github.com/projects/MDS/repos/mds-branding/commits/2cdad0129cbc5354441ebf1830a7324559386533))
|
|
26
|
+
* textEditor remove auto-focus on open link editor ([1c42419](https://github.com/projects/MDS/repos/mds-branding/commits/1c42419ced20efe5bb0cb1e80221eab509b29645))
|
|
27
|
+
* textEditori18n ([6bf5d03](https://github.com/projects/MDS/repos/mds-branding/commits/6bf5d0352ce1ec77908c03a9e597d8ddbc7f3ba9))
|
|
28
|
+
* textEdtorPopover ([13c39be](https://github.com/projects/MDS/repos/mds-branding/commits/13c39bee9c0d676fa87bf815c64873b92ba9ee7e))
|
|
29
|
+
* tweak text editor link ([32e3992](https://github.com/projects/MDS/repos/mds-branding/commits/32e399236782169c3075a4a2ebc853c6a690f966))
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
## [5.2.0](https://github.com/projects/MDS/repos/mds-branding/compare/diff?targetBranch=refs/tags/@madgex/design-system-ce@5.1.2&sourceBranch=refs/tags/@madgex/design-system-ce@5.2.0) (2022-08-18)
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
### Features
|
|
37
|
+
|
|
38
|
+
* add text editor (wip) ([1d424b3](https://github.com/projects/MDS/repos/mds-branding/commits/1d424b399f1ce71a1db99b644dc72aa650eee1f0))
|
|
39
|
+
* add text editor (wip) ([804cabe](https://github.com/projects/MDS/repos/mds-branding/commits/804cabee541b65e8943a3e29b9c6108313a3400a))
|
|
40
|
+
* manage tabindex in toolbar, add examples and documentation ([74e1442](https://github.com/projects/MDS/repos/mds-branding/commits/74e1442437e9f27580b9f3f56d2c37a3874e98f6))
|
|
41
|
+
* refactoring toolbar, adding keyboard interaction and fixing icons ([b4c5121](https://github.com/projects/MDS/repos/mds-branding/commits/b4c5121ddfd1d1323ac3fb3d87ca52b85470768a))
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
### Bug Fixes
|
|
45
|
+
|
|
46
|
+
* adding reusable icon vue component + svgs for editor menu ([f8bfaae](https://github.com/projects/MDS/repos/mds-branding/commits/f8bfaae311d607ce1ef15cf65f857b442d1fcbc5))
|
|
47
|
+
* export each custom element separately ([da9f94d](https://github.com/projects/MDS/repos/mds-branding/commits/da9f94de94c6612c8a645855f9e32a6abeee9194))
|
|
48
|
+
* move aria-pressed to toolbar instead of button ([5114f9d](https://github.com/projects/MDS/repos/mds-branding/commits/5114f9dd829ca10a5dbfd88a5784163575c2ac12))
|
|
49
|
+
* removed the disabled attribute to only aria-disabled to avoid focus issue ([2076227](https://github.com/projects/MDS/repos/mds-branding/commits/2076227c8a98c5eb359320271ccf5dc6e6a5bded))
|
|
50
|
+
* trying to separate the text editor from the main js ([db089b8](https://github.com/projects/MDS/repos/mds-branding/commits/db089b8779bbbf0d4a7c1bfbb2de6c9fc20d947d))
|
|
51
|
+
* update filenames ([7ad354f](https://github.com/projects/MDS/repos/mds-branding/commits/7ad354fb77629e738ca55a00f71dc87f365382ae))
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
|
|
6
55
|
## [5.1.2](https://github.com/projects/MDS/repos/mds-branding/compare/diff?targetBranch=refs/tags/@madgex/design-system-ce@5.1.1&sourceBranch=refs/tags/@madgex/design-system-ce@5.1.2) (2022-07-12)
|
|
7
56
|
|
|
8
57
|
**Note:** Version bump only for package @madgex/design-system-ce
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<span>
|
|
3
|
+
<svg aria-hidden="true" focusable="false" class="mds-icon" :class="`mds-icon--${iconName} ${classes}`">
|
|
4
|
+
<use :href="`${iconPath}#icon-${iconName}`" />
|
|
5
|
+
</svg>
|
|
6
|
+
<span v-if="visuallyHiddenLabel" class="mds-visually-hidden">{{ visuallyHiddenLabel }}</span>
|
|
7
|
+
</span>
|
|
8
|
+
</template>
|
|
9
|
+
|
|
10
|
+
<script>
|
|
11
|
+
export default {
|
|
12
|
+
name: 'MdsIcon',
|
|
13
|
+
props: {
|
|
14
|
+
iconName: {
|
|
15
|
+
type: String,
|
|
16
|
+
default: '',
|
|
17
|
+
},
|
|
18
|
+
classes: {
|
|
19
|
+
type: String,
|
|
20
|
+
default: '',
|
|
21
|
+
},
|
|
22
|
+
visuallyHiddenLabel: {
|
|
23
|
+
type: String,
|
|
24
|
+
default: '',
|
|
25
|
+
},
|
|
26
|
+
},
|
|
27
|
+
inject: ['iconPath'],
|
|
28
|
+
};
|
|
29
|
+
</script>
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div>
|
|
3
|
+
<TextEditorContent v-model="content" />
|
|
4
|
+
<input type="hidden" :name="editorid" :value="content" />
|
|
5
|
+
</div>
|
|
6
|
+
</template>
|
|
7
|
+
|
|
8
|
+
<script>
|
|
9
|
+
import TextEditorContent from './TextEditorContent.vue';
|
|
10
|
+
|
|
11
|
+
export default {
|
|
12
|
+
name: 'TextEditor',
|
|
13
|
+
components: {
|
|
14
|
+
TextEditorContent,
|
|
15
|
+
},
|
|
16
|
+
provide() {
|
|
17
|
+
return {
|
|
18
|
+
iconPath: this.iconpath,
|
|
19
|
+
id: this.editorid,
|
|
20
|
+
customMenuButtons: this.customMenuButtons,
|
|
21
|
+
i18nText: this.i18nText,
|
|
22
|
+
};
|
|
23
|
+
},
|
|
24
|
+
props: {
|
|
25
|
+
editorid: {
|
|
26
|
+
type: String,
|
|
27
|
+
required: true,
|
|
28
|
+
},
|
|
29
|
+
iconpath: {
|
|
30
|
+
type: String,
|
|
31
|
+
default: '/assets/icons.svg',
|
|
32
|
+
},
|
|
33
|
+
menuButtons: {
|
|
34
|
+
type: String,
|
|
35
|
+
default: '',
|
|
36
|
+
},
|
|
37
|
+
i18n: {
|
|
38
|
+
type: String,
|
|
39
|
+
default: '',
|
|
40
|
+
},
|
|
41
|
+
value: {
|
|
42
|
+
type: String,
|
|
43
|
+
default: '',
|
|
44
|
+
},
|
|
45
|
+
},
|
|
46
|
+
data() {
|
|
47
|
+
return {
|
|
48
|
+
content: this.value,
|
|
49
|
+
};
|
|
50
|
+
},
|
|
51
|
+
computed: {
|
|
52
|
+
customMenuButtons() {
|
|
53
|
+
return this.menuButtons ? JSON.parse(this.menuButtons) : null;
|
|
54
|
+
},
|
|
55
|
+
i18nText() {
|
|
56
|
+
const parsedI18n = this.i18n ? JSON.parse(this.i18n) : {};
|
|
57
|
+
return {
|
|
58
|
+
toolbarLabel: 'Text formatting',
|
|
59
|
+
addLink: 'Add link',
|
|
60
|
+
removeLink: 'Remove link',
|
|
61
|
+
visuallyHiddenLinkUrlLabel: 'Link url',
|
|
62
|
+
...parsedI18n,
|
|
63
|
+
};
|
|
64
|
+
},
|
|
65
|
+
},
|
|
66
|
+
mounted() {
|
|
67
|
+
const fallback = document.querySelector(`#text-editor-fallback-${this.editorid}`);
|
|
68
|
+
if (fallback) fallback.remove();
|
|
69
|
+
},
|
|
70
|
+
};
|
|
71
|
+
</script>
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<button
|
|
3
|
+
type="button"
|
|
4
|
+
:title="label"
|
|
5
|
+
:aria-label="label"
|
|
6
|
+
class="mds-text-editor__button"
|
|
7
|
+
:class="{ 'mds-text-editor__button--active': isActive }"
|
|
8
|
+
:tabindex="tabindex"
|
|
9
|
+
:aria-disabled="disabled"
|
|
10
|
+
:aria-pressed="ariaPressed"
|
|
11
|
+
@focus="$emit('updateTabIndex', id)"
|
|
12
|
+
>
|
|
13
|
+
<MdsIcon :icon-name="iconName" />
|
|
14
|
+
</button>
|
|
15
|
+
</template>
|
|
16
|
+
|
|
17
|
+
<script>
|
|
18
|
+
import MdsIcon from '../internals/MdsIcon.vue';
|
|
19
|
+
export default {
|
|
20
|
+
name: 'TextEditorButton',
|
|
21
|
+
components: {
|
|
22
|
+
MdsIcon,
|
|
23
|
+
},
|
|
24
|
+
emits: ['updateTabIndex'],
|
|
25
|
+
props: {
|
|
26
|
+
id: {
|
|
27
|
+
type: String,
|
|
28
|
+
required: true,
|
|
29
|
+
},
|
|
30
|
+
label: {
|
|
31
|
+
type: String,
|
|
32
|
+
default: '',
|
|
33
|
+
},
|
|
34
|
+
iconName: {
|
|
35
|
+
type: String,
|
|
36
|
+
default: '',
|
|
37
|
+
},
|
|
38
|
+
isActive: {
|
|
39
|
+
type: Boolean,
|
|
40
|
+
default: false,
|
|
41
|
+
},
|
|
42
|
+
initialTabindex: {
|
|
43
|
+
type: Number,
|
|
44
|
+
default: -1,
|
|
45
|
+
},
|
|
46
|
+
ariaPressed: {
|
|
47
|
+
type: Boolean,
|
|
48
|
+
default: false,
|
|
49
|
+
},
|
|
50
|
+
disabled: {
|
|
51
|
+
type: Boolean,
|
|
52
|
+
},
|
|
53
|
+
},
|
|
54
|
+
data() {
|
|
55
|
+
return {
|
|
56
|
+
tabindex: this.initialTabindex,
|
|
57
|
+
};
|
|
58
|
+
},
|
|
59
|
+
};
|
|
60
|
+
</script>
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div class="mds-text-editor" v-if="editor">
|
|
3
|
+
<TextEditorToolbar :editor="editor" />
|
|
4
|
+
<EditorContent :editor="editor" />
|
|
5
|
+
</div>
|
|
6
|
+
</template>
|
|
7
|
+
|
|
8
|
+
<script>
|
|
9
|
+
import StarterKit from '@tiptap/starter-kit';
|
|
10
|
+
import { Editor, EditorContent } from '@tiptap/vue-3';
|
|
11
|
+
import Link from '@tiptap/extension-link';
|
|
12
|
+
import TextEditorToolbar from './TextEditorToolbar.vue';
|
|
13
|
+
|
|
14
|
+
export default {
|
|
15
|
+
name: 'TextEditorContent',
|
|
16
|
+
components: {
|
|
17
|
+
EditorContent,
|
|
18
|
+
TextEditorToolbar,
|
|
19
|
+
},
|
|
20
|
+
props: {
|
|
21
|
+
modelValue: {
|
|
22
|
+
type: String,
|
|
23
|
+
default: '',
|
|
24
|
+
},
|
|
25
|
+
},
|
|
26
|
+
inject: ['id'],
|
|
27
|
+
|
|
28
|
+
emits: ['update:modelValue'],
|
|
29
|
+
|
|
30
|
+
data() {
|
|
31
|
+
return {
|
|
32
|
+
editor: null,
|
|
33
|
+
};
|
|
34
|
+
},
|
|
35
|
+
|
|
36
|
+
watch: {
|
|
37
|
+
modelValue(value) {
|
|
38
|
+
const isSame = this.editor.getHTML() === value;
|
|
39
|
+
|
|
40
|
+
if (isSame) {
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
this.editor.commands.setContent(value, false);
|
|
45
|
+
},
|
|
46
|
+
},
|
|
47
|
+
|
|
48
|
+
mounted() {
|
|
49
|
+
this.editor = new Editor({
|
|
50
|
+
extensions: [
|
|
51
|
+
StarterKit,
|
|
52
|
+
Link.configure({
|
|
53
|
+
protocols: ['http', 'https', 'mailto'], // automatically create links of text with these protocols
|
|
54
|
+
openOnClick: false,
|
|
55
|
+
}),
|
|
56
|
+
],
|
|
57
|
+
editorProps: {
|
|
58
|
+
attributes: {
|
|
59
|
+
class: 'mds-text-editor__content mds-edited-text',
|
|
60
|
+
id: this.id,
|
|
61
|
+
role: 'textbox',
|
|
62
|
+
[`aria-labelledby`]: `text-editor-label-${this.id}`,
|
|
63
|
+
},
|
|
64
|
+
},
|
|
65
|
+
content: this.modelValue,
|
|
66
|
+
onUpdate: () => {
|
|
67
|
+
this.$emit('update:modelValue', this.editor.getHTML());
|
|
68
|
+
},
|
|
69
|
+
});
|
|
70
|
+
},
|
|
71
|
+
|
|
72
|
+
beforeUnmount() {
|
|
73
|
+
this.editor.destroy();
|
|
74
|
+
},
|
|
75
|
+
};
|
|
76
|
+
</script>
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div ref="popperRef" class="mds-popover" :class="{ 'mds-popover--active': visible }" :aria-hidden="!visible">
|
|
3
|
+
<div class="mds-popover__arrow" data-popper-arrow />
|
|
4
|
+
<div class="mds-tooltip__inner">
|
|
5
|
+
<slot />
|
|
6
|
+
</div>
|
|
7
|
+
</div>
|
|
8
|
+
</template>
|
|
9
|
+
|
|
10
|
+
<script setup>
|
|
11
|
+
import { onMounted, onUnmounted, ref, watch } from 'vue';
|
|
12
|
+
import { createPopper } from '@popperjs/core';
|
|
13
|
+
import { isNodeSelection, posToDOMRect } from '@tiptap/core';
|
|
14
|
+
|
|
15
|
+
// eslint-disable-next-line no-undef
|
|
16
|
+
const props = defineProps({
|
|
17
|
+
editor: {
|
|
18
|
+
type: Object,
|
|
19
|
+
required: true,
|
|
20
|
+
},
|
|
21
|
+
id: {
|
|
22
|
+
type: String,
|
|
23
|
+
required: true,
|
|
24
|
+
},
|
|
25
|
+
visible: {
|
|
26
|
+
type: Boolean,
|
|
27
|
+
default: false,
|
|
28
|
+
},
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
// eslint-disable-next-line no-undef
|
|
32
|
+
const emit = defineEmits(['update:visible']);
|
|
33
|
+
|
|
34
|
+
const popperRef = ref(null);
|
|
35
|
+
const instance = ref(undefined);
|
|
36
|
+
|
|
37
|
+
const baseSize = 4;
|
|
38
|
+
|
|
39
|
+
const destroy = () => {
|
|
40
|
+
instance.value?.destroy();
|
|
41
|
+
instance.value = undefined;
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
const virtualElement = {
|
|
45
|
+
getBoundingClientRect: () => {
|
|
46
|
+
const { view, state } = props.editor;
|
|
47
|
+
const { ranges } = state.selection;
|
|
48
|
+
const from = Math.min(...ranges.map((range) => range.$from.pos));
|
|
49
|
+
const to = Math.max(...ranges.map((range) => range.$to.pos));
|
|
50
|
+
if (isNodeSelection(state.selection)) {
|
|
51
|
+
const node = view.nodeDOM(from);
|
|
52
|
+
if (node) return node.getBoundingClientRect();
|
|
53
|
+
}
|
|
54
|
+
return posToDOMRect(view, from, to);
|
|
55
|
+
},
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
const initPopper = () => {
|
|
59
|
+
if (instance.value) destroy();
|
|
60
|
+
if (!popperRef.value) return;
|
|
61
|
+
instance.value = createPopper(virtualElement, popperRef.value, {
|
|
62
|
+
placement: 'bottom',
|
|
63
|
+
strategy: 'fixed',
|
|
64
|
+
modifiers: [
|
|
65
|
+
{ name: 'offset', options: { offset: [0, baseSize * 3] } },
|
|
66
|
+
// arrow will always be at least 5px away from the edge
|
|
67
|
+
{ name: 'arrow', options: { padding: 5 } },
|
|
68
|
+
],
|
|
69
|
+
});
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
const hideMouseDown = (event) => {
|
|
73
|
+
if (!popperRef.value?.contains(event.target) && props.visible) {
|
|
74
|
+
console.log('visiblevisible');
|
|
75
|
+
|
|
76
|
+
emit('update:visible', false);
|
|
77
|
+
}
|
|
78
|
+
};
|
|
79
|
+
const hideKeyDown = (event) => {
|
|
80
|
+
if (event.keyCode === 27) {
|
|
81
|
+
emit('update:visible', false);
|
|
82
|
+
}
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
const updateInstance = () => instance.value?.update();
|
|
86
|
+
const onEditorFocus = () => {
|
|
87
|
+
emit('update:visible', false);
|
|
88
|
+
};
|
|
89
|
+
watch(
|
|
90
|
+
() => props.visible,
|
|
91
|
+
(val) => {
|
|
92
|
+
if (val) {
|
|
93
|
+
updateInstance();
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
);
|
|
97
|
+
|
|
98
|
+
onMounted(() => {
|
|
99
|
+
initPopper();
|
|
100
|
+
props.editor.on('focus', onEditorFocus);
|
|
101
|
+
props.editor.on('blur', updateInstance);
|
|
102
|
+
document.addEventListener('mousedown', hideMouseDown);
|
|
103
|
+
document.addEventListener('keydown', hideKeyDown);
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
onUnmounted(() => {
|
|
107
|
+
props.editor.off('focus', onEditorFocus);
|
|
108
|
+
props.editor.off('blur', updateInstance);
|
|
109
|
+
document.removeEventListener('mousedown', hideMouseDown);
|
|
110
|
+
document.removeEventListener('keydown', hideKeyDown);
|
|
111
|
+
destroy();
|
|
112
|
+
});
|
|
113
|
+
</script>
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<TextEditorPopover id="link-editor" v-model:visible="visibleModel" :editor="editor">
|
|
3
|
+
<div class="mds-grid">
|
|
4
|
+
<div class="mds-grid-row">
|
|
5
|
+
<span class="mds-grid-col mds-grid-fit-content">
|
|
6
|
+
<label class="mds-visually-hidden" for="link-url">{{ i18nText.visuallyHiddenLinkUrlLabel }}</label>
|
|
7
|
+
<input
|
|
8
|
+
id="link-url"
|
|
9
|
+
ref="linkInput"
|
|
10
|
+
v-model="linkUrlModel"
|
|
11
|
+
class="mds-form-control"
|
|
12
|
+
placeholder="example.com"
|
|
13
|
+
/>
|
|
14
|
+
</span>
|
|
15
|
+
<span class="mds-grid-col">
|
|
16
|
+
<button class="mds-button mds-width-full" @click="setLink">{{ i18nText.addLink }}</button>
|
|
17
|
+
<button class="mds-button mds-button--plain mds-width-full" @click="removeLink">
|
|
18
|
+
{{ i18nText.removeLink }}
|
|
19
|
+
</button>
|
|
20
|
+
</span>
|
|
21
|
+
</div>
|
|
22
|
+
</div>
|
|
23
|
+
</TextEditorPopover>
|
|
24
|
+
</template>
|
|
25
|
+
|
|
26
|
+
<script setup>
|
|
27
|
+
import { computed, inject } from 'vue';
|
|
28
|
+
import TextEditorPopover from './TextEditorPopover.vue';
|
|
29
|
+
import { applyMissingProtocol } from './apply-missing-protocol';
|
|
30
|
+
import { nextTick } from 'process';
|
|
31
|
+
|
|
32
|
+
// eslint-disable-next-line no-undef
|
|
33
|
+
const props = defineProps({
|
|
34
|
+
visible: {
|
|
35
|
+
type: Boolean,
|
|
36
|
+
default: false,
|
|
37
|
+
},
|
|
38
|
+
linkUrl: {
|
|
39
|
+
type: String,
|
|
40
|
+
default: '',
|
|
41
|
+
},
|
|
42
|
+
editor: {
|
|
43
|
+
type: Object,
|
|
44
|
+
required: true,
|
|
45
|
+
},
|
|
46
|
+
});
|
|
47
|
+
// eslint-disable-next-line no-undef
|
|
48
|
+
const emit = defineEmits(['update:visible', 'update:linkUrl']);
|
|
49
|
+
|
|
50
|
+
const i18nText = inject('i18nText');
|
|
51
|
+
|
|
52
|
+
const visibleModel = computed({
|
|
53
|
+
get() {
|
|
54
|
+
return props.visible;
|
|
55
|
+
},
|
|
56
|
+
set(val) {
|
|
57
|
+
emit('update:visible', val);
|
|
58
|
+
},
|
|
59
|
+
});
|
|
60
|
+
const linkUrlModel = computed({
|
|
61
|
+
get() {
|
|
62
|
+
return props.linkUrl;
|
|
63
|
+
},
|
|
64
|
+
set(val) {
|
|
65
|
+
emit('update:linkUrl', val);
|
|
66
|
+
},
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
const setLink = () => {
|
|
70
|
+
// cancelled
|
|
71
|
+
if (linkUrlModel.value === null) {
|
|
72
|
+
visibleModel.value = false;
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// unset link if empty
|
|
77
|
+
if (linkUrlModel.value?.trim() === '') {
|
|
78
|
+
props.editor.chain().focus().extendMarkRange('link').unsetLink().run();
|
|
79
|
+
visibleModel.value = false;
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
linkUrlModel.value = applyMissingProtocol(linkUrlModel.value);
|
|
83
|
+
|
|
84
|
+
// update link
|
|
85
|
+
nextTick(() => {
|
|
86
|
+
props.editor
|
|
87
|
+
.chain()
|
|
88
|
+
.focus()
|
|
89
|
+
.extendMarkRange('link')
|
|
90
|
+
.setLink({ href: linkUrlModel.value })
|
|
91
|
+
.setTextSelection(0)
|
|
92
|
+
.run();
|
|
93
|
+
visibleModel.value = false;
|
|
94
|
+
});
|
|
95
|
+
};
|
|
96
|
+
const removeLink = () => {
|
|
97
|
+
props.editor.chain().focus().extendMarkRange('link').unsetLink().run();
|
|
98
|
+
linkUrlModel.value = false;
|
|
99
|
+
};
|
|
100
|
+
</script>
|