어나운스 바 설정 확장 v1.5.38

This commit is contained in:
2026-06-02 16:31:30 +09:00
parent ba17e3aa18
commit e3b8087b09
16 changed files with 240 additions and 43 deletions

View File

@@ -131,6 +131,10 @@ const markdownValue = computed({
/** textarea 포커스·블록 패널 상호작용 */
const isTextareaFocused = ref(false)
const isBlockPanelEngaged = ref(false)
/** 한글 등 IME 조합 입력 중 여부 */
const isTextComposing = ref(false)
/** 조합 입력 중 패널 닫힘을 방지하기 위한 마지막 블록 컨텍스트 */
const lastStableBlockContext = ref(null)
let blockPanelFocusTimer = null
/**
@@ -184,12 +188,24 @@ const activeBlockContext = computed(() => resolveActiveBlockContext(
/** @deprecated 내부 호환 alias */
const activeMediaBlock = activeBlockContext
/**
* IME 조합 중에는 일시적으로 블록 판별이 비어도 직전 패널을 유지한다.
* @returns {Object|null} 패널 표시용 블록 컨텍스트
*/
const visibleBlockContext = computed(() => {
if (activeBlockContext.value) {
return activeBlockContext.value
}
return isTextComposing.value ? lastStableBlockContext.value : null
})
/**
* 블록 설정 패널 표시 여부
* @returns {boolean}
*/
const isBlockPanelVisible = computed(() => (activeMode.value === 'write' || isBlockPanelEngaged.value)
&& Boolean(activeBlockContext.value)
&& Boolean(visibleBlockContext.value)
&& (isTextareaFocused.value || isBlockPanelEngaged.value))
/**
@@ -199,11 +215,17 @@ const isBlockPanelVisible = computed(() => (activeMode.value === 'write' || isBl
const syncBlockPanelState = () => {
emit('block-panel', {
open: isBlockPanelVisible.value,
panel: activeBlockContext.value
panel: visibleBlockContext.value
})
}
watch([isBlockPanelVisible, activeBlockContext], syncBlockPanelState, { deep: true })
watch(activeBlockContext, (context) => {
if (context) {
lastStableBlockContext.value = context
}
}, { deep: true, immediate: true })
watch([isBlockPanelVisible, visibleBlockContext], syncBlockPanelState, { deep: true })
/**
* 본문의 논리 줄(`\\n` 기준) 개수. 빈 본문은 1줄로 본다.
@@ -400,6 +422,28 @@ const handleBlockPanelFocusOut = () => {
}, 50)
}
/**
* IME 조합 입력 시작 시 패널 컨텍스트를 유지한다.
* @returns {void}
*/
const handleTextareaCompositionStart = () => {
isTextComposing.value = true
if (activeBlockContext.value) {
lastStableBlockContext.value = activeBlockContext.value
}
}
/**
* IME 조합 입력 종료 후 실제 커서 줄 기준으로 패널을 다시 동기화한다.
* @returns {void}
*/
const handleTextareaCompositionEnd = () => {
isTextComposing.value = false
refreshCaretLogicalLine()
nextTick(syncBlockPanelState)
}
/**
* 커서 위치 기준으로 활성 논리 줄 인덱스를 갱신하고 거터 스크롤을 맞춘다.
* @returns {void}
@@ -2756,6 +2800,8 @@ const handleKeydown = (event) => {
@drop="handleDrop"
@dragover.prevent
@input="refreshCaretLogicalLine"
@compositionstart="handleTextareaCompositionStart"
@compositionend="handleTextareaCompositionEnd"
@click="refreshCaretLogicalLine"
@keyup="refreshCaretLogicalLine"
@select="refreshCaretLogicalLine"

View File

@@ -17,6 +17,7 @@ const { data: siteSettings } = await useFetch('/api/site-settings', {
announcementText: '',
announcementUrl: '',
announcementBackgroundColor: '#15171a',
announcementAlignment: 'center',
updatedAt: null
})
})
@@ -48,6 +49,8 @@ const announcementLink = computed(() => normalizeAnnouncementUrl(siteSettings.va
const snoozeLabel = computed(() => `${ANNOUNCEMENT_SNOOZE_DAYS}일간 보지 않기`)
const announcementAlignment = computed(() => siteSettings.value?.announcementAlignment === 'left' ? 'left' : 'center')
const barStyle = computed(() => {
const backgroundColor = siteSettings.value?.announcementBackgroundColor || '#15171a'
return {
@@ -158,11 +161,17 @@ onBeforeUnmount(() => {
aria-label="사이트 공지"
:aria-hidden="(!expanded).toString()"
>
<div class="site-announcement-bar__inner relative mx-auto flex min-h-9 max-w-[1294px] items-center justify-center gap-3 px-4 py-2.5 sm:px-6 lg:px-8">
<div
class="site-announcement-bar__inner relative mx-auto flex min-h-9 max-w-[1294px] items-center gap-3 px-4 py-2.5 sm:px-6 lg:px-8"
:class="announcementAlignment === 'left' ? 'justify-start' : 'justify-center'"
>
<component
:is="announcementLink ? 'a' : 'span'"
class="site-announcement-bar__text min-w-0 flex-1 line-clamp-2 px-6 sm:px-8"
:class="announcementLink ? 'hover:underline' : ''"
class="site-announcement-bar__text min-w-0 line-clamp-2 px-6 sm:px-8"
:class="[
announcementLink ? 'hover:underline' : '',
announcementAlignment === 'left' ? 'flex-1 text-left' : 'flex-1 text-center'
]"
:href="announcementLink || undefined"
:target="announcementLink ? '_blank' : undefined"
:rel="announcementLink ? 'noreferrer' : undefined"