공개 본문 라이트박스 상태 분리

This commit is contained in:
2026-07-27 12:22:18 +09:00
parent bf48af7bd3
commit b15282f41f
10 changed files with 348 additions and 105 deletions

View File

@@ -44,6 +44,7 @@ import { getInArticleAdAfterBlockIds } from '../../lib/content-in-article-ad.js'
import { getContentLightboxImages, getGalleryRows } from '../../lib/content-gallery-images.js'
import { parseMarkdownBlocks } from '../../lib/markdown-block-parser.js'
import { createHeadingIdFactory } from '../../lib/markdown-toc.js'
import { useContentMarkdownLightbox } from '../../composables/useContentMarkdownLightbox.js'
import ContentMarkdownCodeBlockEditor from './ContentMarkdownCodeBlockEditor.vue'
import ProseCodeBlock from './ProseCodeBlock.vue'
import ProseHtml from './ProseHtml.vue'
@@ -100,8 +101,6 @@ const emit = defineEmits([
const LIVE_IMAGE_DRAG_MIME = 'application/x-sori-live-image'
/** @type {string} 본문 리스트 마커 색상 */
const CONTENT_LIST_MARKER_COLOR = '#2eb6ea'
const activeLightboxImages = ref([])
const activeLightboxIndex = ref(0)
/** @type {import('vue').Ref<Record<string, number>>} 갤러리 이미지 자연 비율 */
const galleryImageAspectRatios = ref({})
/** @type {import('vue').Ref<{ kind: 'image'|'gallery', sourceLine?: number, startLine?: number, endLine?: number, blockId: string, imageIndex?: number, image?: Object }|null>} */
@@ -124,6 +123,15 @@ const rawEditingSourceLines = ref(new Set())
/** @type {number} 문단 분리 연속 호출 방지 */
let lastParagraphSplitAt = 0
const {
activeLightboxImages,
activeLightboxImage,
openContentLightbox: openContentLightboxFromImages,
closeLightbox,
showPreviousImage,
showNextImage
} = useContentMarkdownLightbox()
/**
* 빈 줄 공백 블록 높이를 반환한다.
* @param {Object} block - 렌더링 블록
@@ -202,8 +210,6 @@ const tailInsertBeforeLine = computed(() => {
return (props.content || '').split('\n').length
})
const activeLightboxImage = computed(() => activeLightboxImages.value[activeLightboxIndex.value])
const contentLightboxImages = computed(() => getContentLightboxImages(blocks.value))
/**
@@ -1629,58 +1635,13 @@ const onMergeWithPreviousLine = (lineIndex, payload) => {
emit('merge-with-previous-line', { lineIndex, mergedLine })
}
/**
* 라이트박스를 연다
* @param {Array<Object>} images - 이미지 목록
* @param {number} index - 시작 인덱스
* @returns {void}
*/
const openLightbox = (images, index) => {
activeLightboxImages.value = images
activeLightboxIndex.value = index
}
/**
* 본문 전체 이미지 목록에서 지정 이미지를 라이트박스로 연다.
* @param {string} lightboxKey - 이미지 식별 키
* @returns {void}
*/
const openContentLightbox = (lightboxKey) => {
const images = contentLightboxImages.value
const index = images.findIndex((image) => image.lightboxKey === lightboxKey)
if (index < 0) {
return
}
openLightbox(images, index)
}
/**
* 라이트박스를 닫는다
* @returns {void}
*/
const closeLightbox = () => {
activeLightboxImages.value = []
activeLightboxIndex.value = 0
}
/**
* 라이트박스 이전 이미지로 이동
* @returns {void}
*/
const showPreviousImage = () => {
activeLightboxIndex.value = activeLightboxIndex.value === 0
? activeLightboxImages.value.length - 1
: activeLightboxIndex.value - 1
}
/**
* 라이트박스 다음 이미지로 이동
* @returns {void}
*/
const showNextImage = () => {
activeLightboxIndex.value = (activeLightboxIndex.value + 1) % activeLightboxImages.value.length
openContentLightboxFromImages(contentLightboxImages.value, lightboxKey)
}
/**
@@ -2110,55 +2071,6 @@ const onGalleryDrop = (event, block, targetIndex) => {
clearLiveImageDragUi()
}
/**
* 라이트박스 키보드 조작(Esc 닫기, 좌우 이전·다음)
* @param {KeyboardEvent} event - 키보드 이벤트
* @returns {void}
*/
const handleLightboxKeydown = (event) => {
if (!activeLightboxImage.value) {
return
}
if (event.key === 'Escape') {
event.preventDefault()
closeLightbox()
return
}
if (activeLightboxImages.value.length <= 1) {
return
}
if (event.key === 'ArrowLeft') {
event.preventDefault()
showPreviousImage()
return
}
if (event.key === 'ArrowRight') {
event.preventDefault()
showNextImage()
}
}
watch(activeLightboxImage, (image) => {
if (!import.meta.client) {
return
}
if (image) {
window.addEventListener('keydown', handleLightboxKeydown)
} else {
window.removeEventListener('keydown', handleLightboxKeydown)
}
})
onBeforeUnmount(() => {
if (import.meta.client) {
window.removeEventListener('keydown', handleLightboxKeydown)
}
})
</script>
<template>