2022-04-28 00:08:57 +00:00
|
|
|
<script>
|
|
|
|
import {
|
|
|
|
GlLink,
|
|
|
|
GlForm,
|
|
|
|
GlFormGroup,
|
|
|
|
GlFormInput,
|
|
|
|
GlButton,
|
|
|
|
GlButtonGroup,
|
|
|
|
GlTooltipDirective as GlTooltip,
|
|
|
|
} from '@gitlab/ui';
|
|
|
|
import Link from '../../extensions/link';
|
|
|
|
import EditorStateObserver from '../editor_state_observer.vue';
|
2022-08-24 18:12:18 +00:00
|
|
|
import BubbleMenu from './bubble_menu.vue';
|
2022-04-28 00:08:57 +00:00
|
|
|
|
|
|
|
export default {
|
|
|
|
components: {
|
|
|
|
BubbleMenu,
|
|
|
|
GlForm,
|
|
|
|
GlFormGroup,
|
|
|
|
GlFormInput,
|
|
|
|
GlLink,
|
|
|
|
GlButton,
|
|
|
|
GlButtonGroup,
|
|
|
|
EditorStateObserver,
|
|
|
|
},
|
|
|
|
directives: {
|
|
|
|
GlTooltip,
|
|
|
|
},
|
2022-05-05 15:08:47 +00:00
|
|
|
inject: ['tiptapEditor', 'contentEditor'],
|
2022-04-28 00:08:57 +00:00
|
|
|
data() {
|
|
|
|
return {
|
|
|
|
linkHref: undefined,
|
|
|
|
linkCanonicalSrc: undefined,
|
|
|
|
linkTitle: undefined,
|
|
|
|
|
|
|
|
isEditing: false,
|
|
|
|
};
|
|
|
|
},
|
|
|
|
methods: {
|
|
|
|
shouldShow() {
|
2022-09-02 09:10:27 +00:00
|
|
|
return this.tiptapEditor.isActive(Link.name);
|
2022-04-28 00:08:57 +00:00
|
|
|
},
|
|
|
|
|
|
|
|
startEditingLink() {
|
|
|
|
// select the entire link
|
|
|
|
this.tiptapEditor.chain().focus().extendMarkRange(Link.name).run();
|
|
|
|
|
|
|
|
this.isEditing = true;
|
|
|
|
},
|
|
|
|
|
2022-05-05 15:08:47 +00:00
|
|
|
async endEditingLink() {
|
2022-04-28 00:08:57 +00:00
|
|
|
this.isEditing = false;
|
|
|
|
|
2022-05-06 00:07:56 +00:00
|
|
|
this.linkHref = await this.contentEditor.resolveUrl(this.linkCanonicalSrc);
|
2022-05-05 15:08:47 +00:00
|
|
|
|
2022-04-28 00:08:57 +00:00
|
|
|
if (!this.linkCanonicalSrc && !this.linkHref) {
|
|
|
|
this.removeLink();
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
|
|
|
cancelEditingLink() {
|
|
|
|
this.endEditingLink();
|
|
|
|
this.updateLinkToState();
|
|
|
|
},
|
|
|
|
|
2022-05-05 15:08:47 +00:00
|
|
|
async saveEditedLink() {
|
2022-04-28 00:08:57 +00:00
|
|
|
if (!this.linkCanonicalSrc) {
|
|
|
|
this.removeLink();
|
|
|
|
} else {
|
|
|
|
this.tiptapEditor
|
|
|
|
.chain()
|
|
|
|
.focus()
|
|
|
|
.extendMarkRange(Link.name)
|
|
|
|
.updateAttributes(Link.name, {
|
|
|
|
href: this.linkCanonicalSrc,
|
|
|
|
canonicalSrc: this.linkCanonicalSrc,
|
|
|
|
title: this.linkTitle,
|
|
|
|
})
|
|
|
|
.run();
|
|
|
|
}
|
|
|
|
|
|
|
|
this.endEditingLink();
|
|
|
|
},
|
|
|
|
|
|
|
|
updateLinkToState() {
|
2022-09-02 09:10:27 +00:00
|
|
|
const editor = this.tiptapEditor;
|
2022-04-28 00:08:57 +00:00
|
|
|
|
2022-09-02 09:10:27 +00:00
|
|
|
const { href, title, canonicalSrc } = editor.getAttributes(Link.name);
|
|
|
|
|
|
|
|
if (
|
|
|
|
canonicalSrc === this.linkCanonicalSrc &&
|
|
|
|
href === this.linkHref &&
|
|
|
|
title === this.linkTitle
|
|
|
|
) {
|
|
|
|
return;
|
|
|
|
}
|
2022-04-28 00:08:57 +00:00
|
|
|
|
|
|
|
this.linkTitle = title;
|
|
|
|
this.linkHref = href;
|
|
|
|
this.linkCanonicalSrc = canonicalSrc || href;
|
2022-09-02 09:10:27 +00:00
|
|
|
|
|
|
|
this.isEditing = !this.linkCanonicalSrc;
|
2022-04-28 00:08:57 +00:00
|
|
|
},
|
|
|
|
|
|
|
|
copyLinkHref() {
|
|
|
|
navigator.clipboard.writeText(this.linkCanonicalSrc);
|
|
|
|
},
|
|
|
|
|
|
|
|
removeLink() {
|
|
|
|
this.tiptapEditor.chain().focus().extendMarkRange(Link.name).unsetLink().run();
|
|
|
|
},
|
2022-09-02 09:10:27 +00:00
|
|
|
|
|
|
|
resetBubbleMenuState() {
|
|
|
|
this.linkTitle = undefined;
|
|
|
|
this.linkHref = undefined;
|
|
|
|
this.linkCanonicalSrc = undefined;
|
|
|
|
},
|
2022-04-28 00:08:57 +00:00
|
|
|
},
|
2022-08-24 18:12:18 +00:00
|
|
|
tippyOptions: {
|
|
|
|
placement: 'bottom',
|
|
|
|
},
|
2022-04-28 00:08:57 +00:00
|
|
|
};
|
|
|
|
</script>
|
|
|
|
<template>
|
|
|
|
<bubble-menu
|
|
|
|
data-testid="link-bubble-menu"
|
|
|
|
class="gl-shadow gl-rounded-base gl-bg-white"
|
|
|
|
plugin-key="bubbleMenuLink"
|
2022-08-24 18:12:18 +00:00
|
|
|
:should-show="shouldShow"
|
|
|
|
:tippy-options="$options.tippyOptions"
|
2022-09-02 09:10:27 +00:00
|
|
|
@show="updateLinkToState"
|
|
|
|
@hidden="resetBubbleMenuState"
|
2022-04-28 00:08:57 +00:00
|
|
|
>
|
2022-09-02 09:10:27 +00:00
|
|
|
<editor-state-observer @selectionUpdate="updateLinkToState">
|
2022-04-28 00:08:57 +00:00
|
|
|
<gl-button-group v-if="!isEditing" class="gl-display-flex gl-align-items-center">
|
|
|
|
<gl-link
|
|
|
|
v-gl-tooltip
|
|
|
|
:href="linkHref"
|
|
|
|
:aria-label="linkCanonicalSrc"
|
|
|
|
:title="linkCanonicalSrc"
|
|
|
|
target="_blank"
|
|
|
|
class="gl-px-3 gl-overflow-hidden gl-white-space-nowrap gl-text-overflow-ellipsis"
|
|
|
|
>
|
|
|
|
{{ linkCanonicalSrc }}
|
|
|
|
</gl-link>
|
|
|
|
<gl-button
|
|
|
|
v-gl-tooltip
|
|
|
|
variant="default"
|
|
|
|
category="tertiary"
|
|
|
|
size="medium"
|
|
|
|
data-testid="copy-link-url"
|
|
|
|
:aria-label="__('Copy link URL')"
|
|
|
|
:title="__('Copy link URL')"
|
|
|
|
icon="copy-to-clipboard"
|
|
|
|
@click="copyLinkHref"
|
|
|
|
/>
|
|
|
|
<gl-button
|
|
|
|
v-gl-tooltip
|
|
|
|
variant="default"
|
|
|
|
category="tertiary"
|
|
|
|
size="medium"
|
|
|
|
data-testid="edit-link"
|
|
|
|
:aria-label="__('Edit link')"
|
|
|
|
:title="__('Edit link')"
|
|
|
|
icon="pencil"
|
|
|
|
@click="startEditingLink"
|
|
|
|
/>
|
|
|
|
<gl-button
|
|
|
|
v-gl-tooltip
|
|
|
|
variant="default"
|
|
|
|
category="tertiary"
|
|
|
|
size="medium"
|
|
|
|
data-testid="remove-link"
|
|
|
|
:aria-label="__('Remove link')"
|
|
|
|
:title="__('Remove link')"
|
|
|
|
icon="unlink"
|
|
|
|
@click="removeLink"
|
|
|
|
/>
|
|
|
|
</gl-button-group>
|
2022-05-05 15:08:47 +00:00
|
|
|
<gl-form v-else class="bubble-menu-form gl-p-4 gl-w-100" @submit.prevent="saveEditedLink">
|
|
|
|
<gl-form-group :label="__('URL')" label-for="link-href">
|
|
|
|
<gl-form-input id="link-href" v-model="linkCanonicalSrc" data-testid="link-href" />
|
2022-04-28 00:08:57 +00:00
|
|
|
</gl-form-group>
|
2022-05-05 15:08:47 +00:00
|
|
|
<gl-form-group :label="__('Title')" label-for="link-title">
|
|
|
|
<gl-form-input id="link-title" v-model="linkTitle" data-testid="link-title" />
|
2022-04-28 00:08:57 +00:00
|
|
|
</gl-form-group>
|
|
|
|
<div class="gl-display-flex gl-justify-content-end">
|
|
|
|
<gl-button class="gl-mr-3" data-testid="cancel-link" @click="cancelEditingLink">
|
|
|
|
{{ __('Cancel') }}
|
|
|
|
</gl-button>
|
|
|
|
<gl-button variant="confirm" type="submit">
|
|
|
|
{{ __('Apply') }}
|
|
|
|
</gl-button>
|
|
|
|
</div>
|
|
|
|
</gl-form>
|
|
|
|
</editor-state-observer>
|
|
|
|
</bubble-menu>
|
|
|
|
</template>
|