v1.2.1: 블록 설정 패널·이미지 alt 토글 및 포커스 수정
게시물 설정 사이드바 오버레이로 이미지·갤러리·임베드를 편집하고, 파일명 alt 토글과 패널 입력 중 닫힘 문제를 해결했다. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -1,4 +1,10 @@
|
||||
<script setup>
|
||||
import {
|
||||
getImageAltAttribute,
|
||||
getImageCaption,
|
||||
parseImageMarkdownLine
|
||||
} from '../../lib/markdown-image.js'
|
||||
|
||||
const props = defineProps({
|
||||
content: {
|
||||
type: String,
|
||||
@@ -27,6 +33,8 @@ const createBlock = (type = 'paragraph', text = '', level = null, id = '', optio
|
||||
level,
|
||||
url: options.url || '',
|
||||
alt: options.alt || '',
|
||||
caption: options.caption || '',
|
||||
useAlt: options.useAlt === true,
|
||||
title: options.title || '',
|
||||
variant: options.variant || '',
|
||||
ordered: options.ordered || false,
|
||||
@@ -85,19 +93,7 @@ const parseCalloutOptions = (line) => {
|
||||
* @param {string} line - 마크다운 행
|
||||
* @returns {Object|null} 이미지 데이터
|
||||
*/
|
||||
const parseImageLine = (line) => {
|
||||
const match = line.trim().match(/^!\[([^\]]*)\]\(([^)]+)\)(?:\{width=(regular|wide|full)\})?$/)
|
||||
|
||||
if (!match) {
|
||||
return null
|
||||
}
|
||||
|
||||
return {
|
||||
alt: match[1],
|
||||
url: match[2],
|
||||
width: match[3] || 'regular'
|
||||
}
|
||||
}
|
||||
const parseImageLine = (line) => parseImageMarkdownLine(line)
|
||||
|
||||
/**
|
||||
* 독립 블록으로 해석할 수 있는 마크다운 행인지 확인한다.
|
||||
@@ -555,6 +551,56 @@ const showPreviousImage = () => {
|
||||
const showNextImage = () => {
|
||||
activeLightboxIndex.value = (activeLightboxIndex.value + 1) % activeLightboxImages.value.length
|
||||
}
|
||||
|
||||
/**
|
||||
* 라이트박스 키보드 조작(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>
|
||||
@@ -590,8 +636,15 @@ const showNextImage = () => {
|
||||
</template>
|
||||
</li>
|
||||
</ProseList>
|
||||
<ProseImage v-else-if="block.type === 'image'" :src="block.url" :alt="block.alt" :variant="block.width">
|
||||
{{ block.alt }}
|
||||
<ProseImage
|
||||
v-else-if="block.type === 'image'"
|
||||
:src="block.url"
|
||||
:alt="getImageAltAttribute(block)"
|
||||
:variant="block.width"
|
||||
>
|
||||
<template v-if="getImageCaption(block)">
|
||||
{{ getImageCaption(block) }}
|
||||
</template>
|
||||
</ProseImage>
|
||||
<ProseCallout
|
||||
v-else-if="block.type === 'callout'"
|
||||
@@ -639,7 +692,7 @@ const showNextImage = () => {
|
||||
type="button"
|
||||
@click="openLightbox(block.images, imageIndex)"
|
||||
>
|
||||
<img class="content-markdown-renderer__gallery-image aspect-[4/3] w-full object-cover transition-transform hover:scale-[1.02]" :src="image.url" :alt="image.alt">
|
||||
<img class="content-markdown-renderer__gallery-image aspect-[4/3] w-full object-cover transition-transform hover:scale-[1.02]" :src="image.url" :alt="getImageAltAttribute(image)">
|
||||
</button>
|
||||
</div>
|
||||
<pre
|
||||
@@ -666,6 +719,8 @@ const showNextImage = () => {
|
||||
class="content-markdown-renderer__lightbox fixed inset-0 z-50 grid place-items-center bg-black/90 px-5 py-8"
|
||||
role="dialog"
|
||||
aria-modal="true"
|
||||
aria-label="갤러리 이미지 보기"
|
||||
tabindex="-1"
|
||||
@click.self="closeLightbox"
|
||||
>
|
||||
<button class="content-markdown-renderer__lightbox-close absolute right-5 top-5 rounded bg-white px-3 py-2 text-sm font-semibold text-ink" type="button" @click="closeLightbox">
|
||||
@@ -679,7 +734,11 @@ const showNextImage = () => {
|
||||
>
|
||||
이전
|
||||
</button>
|
||||
<img class="content-markdown-renderer__lightbox-image max-h-[84vh] max-w-[92vw] object-contain" :src="activeLightboxImage.url" :alt="activeLightboxImage.alt">
|
||||
<img
|
||||
class="content-markdown-renderer__lightbox-image max-h-[84vh] max-w-[92vw] object-contain"
|
||||
:src="activeLightboxImage.url"
|
||||
:alt="getImageAltAttribute(activeLightboxImage)"
|
||||
>
|
||||
<button
|
||||
v-if="activeLightboxImages.length > 1"
|
||||
class="content-markdown-renderer__lightbox-next absolute right-5 top-1/2 rounded bg-white px-3 py-2 text-sm font-semibold text-ink"
|
||||
|
||||
@@ -17,7 +17,7 @@ defineProps({
|
||||
|
||||
<template>
|
||||
<figure
|
||||
class="prose-image my-8"
|
||||
class="prose-image mb-2.5"
|
||||
:class="{
|
||||
'prose-image--wide lg:-mx-10 lg:max-w-none': variant === 'wide',
|
||||
'prose-image--full lg:-mx-20 lg:max-w-none': variant === 'full'
|
||||
@@ -26,7 +26,7 @@ defineProps({
|
||||
<div class="overflow-hidden rounded-[10px] border border-[var(--site-line)] bg-[var(--site-panel)]">
|
||||
<img class="prose-image__media w-full object-cover" :src="src" :alt="alt">
|
||||
</div>
|
||||
<figcaption v-if="$slots.default" class="prose-image__caption mt-3 text-center text-sm text-[var(--site-muted)]">
|
||||
<figcaption v-if="$slots.default" class="prose-image__caption mt-1.5 text-center text-sm text-[var(--site-muted)]">
|
||||
<slot />
|
||||
</figcaption>
|
||||
</figure>
|
||||
|
||||
Reference in New Issue
Block a user