diff --git a/components/admin/AdminMarkdownEditor.vue b/components/admin/AdminMarkdownEditor.vue index 8301429..854ca40 100644 --- a/components/admin/AdminMarkdownEditor.vue +++ b/components/admin/AdminMarkdownEditor.vue @@ -42,10 +42,12 @@ const mediaSearchQuery = ref('') const selectedMediaUrls = ref([]) const lastSelectionState = ref({ start: 0, - end: 0, - scrollTop: 0 + end: 0 }) +/** 작성 textarea 최소 높이(px) */ +const MIN_TEXTAREA_HEIGHT_PX = 620 + const markdownValue = computed({ get: () => normalizeMarkdownContent(props.modelValue), set: (value) => emit('update:modelValue', value) @@ -120,16 +122,20 @@ const gutterLineCount = computed(() => { }) /** - * textarea와 줄 번호 거터의 세로 스크롤을 맞춘다. + * textarea 높이를 본문 길이에 맞춘다. 내부 스크롤 없이 부모(`editor-scroll`)만 스크롤한다. * @returns {void} */ -const syncGutterScroll = () => { - const gutter = gutterRef.value - const textarea = textareaRef.value +const syncTextareaHeight = () => { + nextTick(() => { + const textarea = textareaRef.value - if (gutter && textarea) { - gutter.scrollTop = textarea.scrollTop - } + if (!textarea) { + return + } + + textarea.style.height = '0px' + textarea.style.height = `${Math.max(MIN_TEXTAREA_HEIGHT_PX, textarea.scrollHeight)}px` + }) } /** @@ -203,24 +209,14 @@ const refreshCaretLogicalLine = () => { lastSelectionState.value = { start: Math.min(textarea.selectionStart, value.length), - end: Math.min(textarea.selectionEnd, value.length), - scrollTop: textarea.scrollTop + end: Math.min(textarea.selectionEnd, value.length) } activeLogicalLineIndex.value = Math.max(0, lineIndex) - syncGutterScroll() + syncTextareaHeight() syncBlockPanelState() }) } -/** - * textarea 스크롤 시 선택 위치를 기억하고 거터 스크롤을 맞춘다. - * @returns {void} - */ -const onTextareaScroll = () => { - rememberTextareaSelection() - syncGutterScroll() -} - /** * textarea의 선택 영역과 스크롤 위치를 기억한다. * @returns {void} @@ -232,16 +228,14 @@ const rememberTextareaSelection = () => { if (!textarea) { lastSelectionState.value = { start: value.length, - end: value.length, - scrollTop: 0 + end: value.length } return } lastSelectionState.value = { start: Math.min(textarea.selectionStart, value.length), - end: Math.min(textarea.selectionEnd, value.length), - scrollTop: textarea.scrollTop + end: Math.min(textarea.selectionEnd, value.length) } } @@ -263,7 +257,8 @@ const restoreTextareaFocus = () => { textarea.focus() textarea.setSelectionRange(start, end) - textarea.scrollTop = lastSelectionState.value.scrollTop + syncTextareaHeight() + textarea.scrollIntoView({ block: 'nearest', inline: 'nearest' }) refreshCaretLogicalLine() }) } @@ -1278,37 +1273,38 @@ const handleKeydown = (event) => {