diff --git a/components/content/ContentMarkdownRenderer.vue b/components/content/ContentMarkdownRenderer.vue index b4f869a..c24e159 100644 --- a/components/content/ContentMarkdownRenderer.vue +++ b/components/content/ContentMarkdownRenderer.vue @@ -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>} 갤러리 이미지 자연 비율 */ 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} 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) - } -})