192 lines
4.8 KiB
JavaScript
192 lines
4.8 KiB
JavaScript
import { computed, getCurrentInstance, onBeforeUnmount, ref } from 'vue'
|
|
|
|
/**
|
|
* 공개 Markdown 렌더러의 이미지 라이트박스 상태를 관리한다.
|
|
* @param {Object} [options] - 설정
|
|
* @param {() => (Window|null)} [options.getKeyboardTarget] - 키보드 이벤트 대상 반환 함수
|
|
* @returns {{
|
|
* activeLightboxImages: import('vue').Ref<Array<Object>>,
|
|
* activeLightboxIndex: import('vue').Ref<number>,
|
|
* activeLightboxImage: import('vue').ComputedRef<Object|undefined>,
|
|
* openLightbox: (images: Array<Object>, index: number) => void,
|
|
* openContentLightbox: (images: Array<Object>, lightboxKey: string) => void,
|
|
* closeLightbox: () => void,
|
|
* showPreviousImage: () => void,
|
|
* showNextImage: () => void,
|
|
* handleLightboxKeydown: (event: KeyboardEvent) => void
|
|
* }} 라이트박스 상태와 액션
|
|
*/
|
|
export const useContentMarkdownLightbox = (options = {}) => {
|
|
const getKeyboardTarget = options.getKeyboardTarget || (() => {
|
|
if (!import.meta.client) {
|
|
return null
|
|
}
|
|
|
|
return window
|
|
})
|
|
|
|
const activeLightboxImages = ref([])
|
|
const activeLightboxIndex = ref(0)
|
|
const activeLightboxImage = computed(() => activeLightboxImages.value[activeLightboxIndex.value])
|
|
let keyboardListenerAttached = false
|
|
|
|
/**
|
|
* 전역 키보드 이벤트를 연결한다.
|
|
* @returns {void}
|
|
*/
|
|
const attachKeyboardListener = () => {
|
|
if (keyboardListenerAttached) {
|
|
return
|
|
}
|
|
|
|
const target = getKeyboardTarget()
|
|
|
|
if (!target) {
|
|
return
|
|
}
|
|
|
|
target.addEventListener('keydown', handleLightboxKeydown)
|
|
keyboardListenerAttached = true
|
|
}
|
|
|
|
/**
|
|
* 전역 키보드 이벤트를 해제한다.
|
|
* @returns {void}
|
|
*/
|
|
const detachKeyboardListener = () => {
|
|
if (!keyboardListenerAttached) {
|
|
return
|
|
}
|
|
|
|
const target = getKeyboardTarget()
|
|
|
|
if (target) {
|
|
target.removeEventListener('keydown', handleLightboxKeydown)
|
|
}
|
|
|
|
keyboardListenerAttached = false
|
|
}
|
|
|
|
/**
|
|
* 라이트박스를 연다.
|
|
* @param {Array<Object>} images - 이미지 목록
|
|
* @param {number} index - 시작 인덱스
|
|
* @returns {void}
|
|
*/
|
|
const openLightbox = (images, index) => {
|
|
const nextImages = Array.isArray(images) ? images : []
|
|
|
|
if (!nextImages.length) {
|
|
activeLightboxImages.value = []
|
|
activeLightboxIndex.value = 0
|
|
detachKeyboardListener()
|
|
return
|
|
}
|
|
|
|
activeLightboxImages.value = nextImages
|
|
activeLightboxIndex.value = Math.max(0, Math.min(Number(index) || 0, nextImages.length - 1))
|
|
attachKeyboardListener()
|
|
}
|
|
|
|
/**
|
|
* 본문 전체 이미지 목록에서 지정 이미지를 라이트박스로 연다.
|
|
* @param {Array<Object>} images - 본문 라이트박스 이미지 목록
|
|
* @param {string} lightboxKey - 이미지 식별 키
|
|
* @returns {void}
|
|
*/
|
|
const openContentLightbox = (images, lightboxKey) => {
|
|
const targetImages = Array.isArray(images) ? images : []
|
|
const index = targetImages.findIndex((image) => image.lightboxKey === lightboxKey)
|
|
|
|
if (index < 0) {
|
|
return
|
|
}
|
|
|
|
openLightbox(targetImages, index)
|
|
}
|
|
|
|
/**
|
|
* 라이트박스를 닫는다.
|
|
* @returns {void}
|
|
*/
|
|
const closeLightbox = () => {
|
|
activeLightboxImages.value = []
|
|
activeLightboxIndex.value = 0
|
|
detachKeyboardListener()
|
|
}
|
|
|
|
/**
|
|
* 라이트박스 이전 이미지로 이동한다.
|
|
* @returns {void}
|
|
*/
|
|
const showPreviousImage = () => {
|
|
if (activeLightboxImages.value.length <= 1) {
|
|
return
|
|
}
|
|
|
|
activeLightboxIndex.value = activeLightboxIndex.value === 0
|
|
? activeLightboxImages.value.length - 1
|
|
: activeLightboxIndex.value - 1
|
|
}
|
|
|
|
/**
|
|
* 라이트박스 다음 이미지로 이동한다.
|
|
* @returns {void}
|
|
*/
|
|
const showNextImage = () => {
|
|
if (activeLightboxImages.value.length <= 1) {
|
|
return
|
|
}
|
|
|
|
activeLightboxIndex.value = (activeLightboxIndex.value + 1) % activeLightboxImages.value.length
|
|
}
|
|
|
|
/**
|
|
* 라이트박스 키보드 조작을 처리한다.
|
|
* @param {KeyboardEvent} event - 키보드 이벤트
|
|
* @returns {void}
|
|
*/
|
|
function 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()
|
|
}
|
|
}
|
|
|
|
if (getCurrentInstance()) {
|
|
onBeforeUnmount(detachKeyboardListener)
|
|
}
|
|
|
|
return {
|
|
activeLightboxImages,
|
|
activeLightboxIndex,
|
|
activeLightboxImage,
|
|
openLightbox,
|
|
openContentLightbox,
|
|
closeLightbox,
|
|
showPreviousImage,
|
|
showNextImage,
|
|
handleLightboxKeydown
|
|
}
|
|
}
|