관리자 에디터 작성 표면 컴포넌트 분리
This commit is contained in:
120
components/admin/AdminMarkdownWriteSurface.vue
Normal file
120
components/admin/AdminMarkdownWriteSurface.vue
Normal file
@@ -0,0 +1,120 @@
|
||||
<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>
|
||||
Reference in New Issue
Block a user