121 lines
3.1 KiB
Vue
121 lines
3.1 KiB
Vue
<script setup>
|
|
import AdminMarkdownUploadingBadge from './AdminMarkdownUploadingBadge.vue'
|
|
|
|
const props = defineProps({
|
|
modelValue: {
|
|
type: String,
|
|
default: ''
|
|
},
|
|
gutterLineCount: {
|
|
type: Number,
|
|
default: 1
|
|
},
|
|
getGutterLineHeight: {
|
|
type: Function,
|
|
required: true
|
|
},
|
|
isUploading: {
|
|
type: Boolean,
|
|
default: false
|
|
}
|
|
})
|
|
|
|
const emit = defineEmits([
|
|
'update:modelValue',
|
|
'keydown',
|
|
'paste',
|
|
'drop',
|
|
'input',
|
|
'compositionstart',
|
|
'compositionend',
|
|
'click',
|
|
'keyup',
|
|
'select',
|
|
'focus',
|
|
'blur'
|
|
])
|
|
|
|
const textareaRef = ref(null)
|
|
const gutterRef = ref(null)
|
|
|
|
/**
|
|
* textarea 입력 값을 부모 모델에 반영하고 입력 이벤트를 전달한다.
|
|
* @param {Event} event - textarea 입력 이벤트
|
|
* @returns {void}
|
|
*/
|
|
const onInput = (event) => {
|
|
emit('update:modelValue', event.target?.value || '')
|
|
emit('input', event)
|
|
}
|
|
|
|
/**
|
|
* 작성 textarea DOM을 반환한다.
|
|
* @returns {HTMLTextAreaElement|null} textarea DOM
|
|
*/
|
|
const getTextareaElement = () => textareaRef.value
|
|
|
|
/**
|
|
* 줄 번호 gutter DOM을 반환한다.
|
|
* @returns {HTMLElement|null} gutter DOM
|
|
*/
|
|
const getGutterElement = () => gutterRef.value
|
|
|
|
defineExpose({
|
|
getTextareaElement,
|
|
getGutterElement
|
|
})
|
|
</script>
|
|
|
|
<template>
|
|
<div class="admin-markdown-editor__write relative">
|
|
<div class="admin-markdown-editor__editor-surface min-h-[620px]">
|
|
<div class="admin-markdown-editor__write-columns flex items-start">
|
|
<div
|
|
ref="gutterRef"
|
|
class="admin-markdown-editor__gutter w-10 shrink-0 select-none py-5 pr-2 font-mono text-[13px] leading-7 text-[#a0a8b0]"
|
|
aria-hidden="true"
|
|
>
|
|
<div
|
|
v-for="ln in props.gutterLineCount"
|
|
:key="`gutter-line-${ln}`"
|
|
class="admin-markdown-editor__gutter-line text-right tabular-nums"
|
|
:style="{ height: `${props.getGutterLineHeight(ln - 1)}px` }"
|
|
>
|
|
{{ ln }}
|
|
</div>
|
|
</div>
|
|
<textarea
|
|
ref="textareaRef"
|
|
:value="modelValue"
|
|
class="admin-markdown-editor__textarea min-h-[620px] min-w-0 flex-1 resize-none overflow-hidden break-words border-0 bg-transparent py-5 pl-0 pr-5 text-[15px] leading-7 text-[#15171a] outline-none transition-colors placeholder:text-[#8e9cac] focus:ring-0"
|
|
placeholder="마크다운으로 글을 작성하세요."
|
|
spellcheck="false"
|
|
@keydown="emit('keydown', $event)"
|
|
@paste="emit('paste', $event)"
|
|
@drop="emit('drop', $event)"
|
|
@dragover.prevent
|
|
@input="onInput"
|
|
@compositionstart="emit('compositionstart', $event)"
|
|
@compositionend="emit('compositionend', $event)"
|
|
@click="emit('click', $event)"
|
|
@keyup="emit('keyup', $event)"
|
|
@select="emit('select', $event)"
|
|
@focus="emit('focus', $event)"
|
|
@blur="emit('blur', $event)"
|
|
/>
|
|
</div>
|
|
</div>
|
|
<AdminMarkdownUploadingBadge v-if="isUploading" />
|
|
</div>
|
|
</template>
|
|
|
|
<style scoped>
|
|
.admin-markdown-editor__gutter {
|
|
scrollbar-width: none;
|
|
}
|
|
|
|
.admin-markdown-editor__gutter::-webkit-scrollbar {
|
|
display: none;
|
|
}
|
|
</style>
|