| 1 |
<template> |
| 2 |
<IvyPopover |
| 3 |
ref="popoverRef" |
| 4 |
trigger="click" |
| 5 |
:width="showUrlInput ? 350 : 200" |
| 6 |
popper-background="level-2-foreground" |
| 7 |
:placement="showUrlInput ? 'bottom-end' : 'bottom'" |
| 8 |
@hide="onHide" |
| 9 |
> |
| 10 |
<template v-if="!showUrlInput"> |
| 11 |
<IvyButtonAction |
| 12 |
type="ghost" |
| 13 |
class="insert-image-button" |
| 14 |
priority="secondary" |
| 15 |
@click.stop.prevent="showUrlInput = true" |
| 16 |
> |
| 17 |
{{ getLabel('insert_image_by_url') }} |
| 18 |
</IvyButtonAction> |
| 19 |
|
| 20 |
<el-upload :auto-upload="false" :show-file-list="false" :on-change="uploadImage"> |
| 21 |
<template #trigger> |
| 22 |
<IvyButtonAction type="ghost" priority="secondary"> |
| 23 |
{{ getLabel('upload_image') }} |
| 24 |
</IvyButtonAction> |
| 25 |
</template> |
| 26 |
</el-upload> |
| 27 |
</template> |
| 28 |
|
| 29 |
<template v-else> |
| 30 |
<div class="ivyforms-flex ivyforms-gap-8"> |
| 31 |
<IvyTextInput v-model="imageUrl" :placeholder="getLabel('image_url')" /> |
| 32 |
<IvyButtonAction @click="applyImageUrl" @click.stop.prevent> |
| 33 |
{{ getLabel('save') }}</IvyButtonAction |
| 34 |
> |
| 35 |
</div> |
| 36 |
<IvyCheckbox v-model="openInNewTab" class="ivyforms-ml-8"> |
| 37 |
{{ getLabel('open_in_new_tab') }} |
| 38 |
</IvyCheckbox> |
| 39 |
</template> |
| 40 |
|
| 41 |
<template #reference> |
| 42 |
<IvyButtonOption |
| 43 |
size="s" |
| 44 |
type="ghost" |
| 45 |
priority="white" |
| 46 |
icon-start-category="text-formatting" |
| 47 |
icon-start="image" |
| 48 |
@click.stop.prevent |
| 49 |
/> |
| 50 |
</template> |
| 51 |
</IvyPopover> |
| 52 |
</template> |
| 53 |
|
| 54 |
<script setup lang="ts"> |
| 55 |
import { ref, unref } from 'vue' |
| 56 |
import { Editor } from '@tiptap/vue-3' |
| 57 |
import type { UploadFile } from 'element-plus' |
| 58 |
import IvyTextInput from '@/views/_components/input/IvyTextInput.vue' |
| 59 |
import IvyButtonOption from '@/views/_components/button/IvyButtonOption.vue' |
| 60 |
import IvyButtonAction from '@/views/_components/button/IvyButtonAction.vue' |
| 61 |
import { useLabels } from '@/composables/useLabels' |
| 62 |
const { getLabel } = useLabels() |
| 63 |
|
| 64 |
const props = defineProps<{ |
| 65 |
editor: Editor |
| 66 |
}>() |
| 67 |
|
| 68 |
const popoverRef = ref(null) |
| 69 |
const showUrlInput = ref<boolean>(false) |
| 70 |
const imageUrl = ref<string | null>(null) |
| 71 |
const openInNewTab = ref<boolean>(false) |
| 72 |
|
| 73 |
const uploadImage = async (file: UploadFile) => { |
| 74 |
const url = await readFileDataUrl(file.raw) |
| 75 |
props.editor.commands.setImage({ src: url }) |
| 76 |
unref(popoverRef).hide() |
| 77 |
} |
| 78 |
|
| 79 |
const applyImageUrl = () => { |
| 80 |
props.editor.commands.setImage({ src: imageUrl.value }) |
| 81 |
unref(popoverRef).hide() |
| 82 |
} |
| 83 |
|
| 84 |
const readFileDataUrl = (file: File): Promise<string> => { |
| 85 |
const reader = new FileReader() |
| 86 |
|
| 87 |
return new Promise((resolve, reject) => { |
| 88 |
reader.onload = (readerEvent: ProgressEvent<FileReader>) => { |
| 89 |
if (readerEvent.target?.result) { |
| 90 |
resolve(readerEvent.target.result as string) |
| 91 |
} else { |
| 92 |
reject(new Error('Failed to read file')) |
| 93 |
} |
| 94 |
} |
| 95 |
reader.onerror = reject |
| 96 |
|
| 97 |
reader.readAsDataURL(file) |
| 98 |
}) |
| 99 |
} |
| 100 |
|
| 101 |
const onHide = () => { |
| 102 |
imageUrl.value = null |
| 103 |
showUrlInput.value = false |
| 104 |
} |
| 105 |
</script> |
| 106 |
<style lang="scss"> |
| 107 |
.ivyforms-popover-popper { |
| 108 |
.insert-image-button { |
| 109 |
.ivyforms-button-action { |
| 110 |
span { |
| 111 |
display: flex; |
| 112 |
text-overflow: unset; |
| 113 |
overflow: unset; |
| 114 |
} |
| 115 |
} |
| 116 |
} |
| 117 |
} |
| 118 |
</style> |
| 119 |
|