전역 단축키 확장
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
<script setup>
|
||||
import { computed, onBeforeUnmount, onMounted, provide, ref, watch } from 'vue'
|
||||
import { computed, nextTick, onBeforeUnmount, onMounted, provide, ref, watch } from 'vue'
|
||||
import { useRoute, useRouter } from 'vue-router'
|
||||
import { useAuthStore } from './stores/auth'
|
||||
import { commentsPath, editorNewPath, favoritesPath, followingFeedPath, homePath, loginPath, mePath, templatesPath } from './lib/paths'
|
||||
@@ -32,6 +32,8 @@ const leftRailCollapsed = ref(false)
|
||||
const mobileLeftNavOpen = ref(false)
|
||||
const rightRailOpen = ref(true)
|
||||
const searchQuery = ref('')
|
||||
const searchInputEl = ref(null)
|
||||
const collapsedSearchInputEl = ref(null)
|
||||
const leftRailSearchPlaceholder = computed(() => {
|
||||
if (route.name === 'templates') return '주제 템플릿 검색'
|
||||
if (route.name === 'topicHub') return '이 템플릿의 공개 티어표 검색'
|
||||
@@ -155,7 +157,7 @@ const guideSteps = [
|
||||
title: '단축키로 빠른 조작',
|
||||
summary: '사이드 패널과 전체 화면을 키보드로 빠르게 전환합니다.',
|
||||
description:
|
||||
'[ 키는 왼쪽 사이드를 열고 닫고, ] 키는 오른쪽 사이드를 열고 닫습니다. F 키는 전체 화면 보기 토글, S 키는 티어표 편집 화면의 아이템 검색창으로 바로 이동할 때 사용할 수 있어요. 한글 입력 상태에서는 F 자리의 ㄹ, S 자리의 ㄴ 키도 같은 단축키로 처리됩니다. 각종 모달은 Esc 키로 닫을 수 있습니다. 단, 검색창이나 입력칸에 글을 쓰는 중에는 단축키가 동작하지 않도록 처리되어 있어요.',
|
||||
'[ 키는 왼쪽 사이드를 열고 닫고, ] 키는 오른쪽 사이드를 열고 닫습니다. F/ㄹ은 전체 화면, S/ㄴ은 검색 포커스(편집 화면에서는 아이템 검색), G/ㅎ은 그리드 보기, L/ㅣ는 리스트 보기, A/ㅁ은 관리자 계정일 때 관리자 화면으로 이동합니다. 각종 모달은 Esc 키로 닫을 수 있고, 입력칸에 글을 쓰는 중에는 단축키가 동작하지 않도록 처리되어 있어요.',
|
||||
},
|
||||
]
|
||||
const currentGuideStep = computed(() => guideSteps[guideStepIndex.value] || guideSteps[0])
|
||||
@@ -402,6 +404,7 @@ onMounted(async () => {
|
||||
})
|
||||
|
||||
function handleGlobalKeydown(event) {
|
||||
const normalizedKey = String(event.key || '').toLowerCase()
|
||||
if (event.key === 'Escape' && isGuideModalOpen.value) {
|
||||
closeGuideModal()
|
||||
return
|
||||
@@ -423,14 +426,33 @@ function handleGlobalKeydown(event) {
|
||||
toggleRightRail()
|
||||
return
|
||||
}
|
||||
if (['f', 'ㄹ'].includes(String(event.key || '').toLowerCase())) {
|
||||
if (['f', 'ㄹ'].includes(normalizedKey)) {
|
||||
event.preventDefault()
|
||||
toggleFullscreen()
|
||||
return
|
||||
}
|
||||
if (['s', 'ㄴ'].includes(String(event.key || '').toLowerCase()) && ['editEditor', 'newEditor'].includes(String(route.name || ''))) {
|
||||
if (['s', 'ㄴ'].includes(normalizedKey)) {
|
||||
event.preventDefault()
|
||||
window.dispatchEvent(new CustomEvent('tier-maker:focus-editor-item-search'))
|
||||
if (['editEditor', 'newEditor'].includes(String(route.name || ''))) {
|
||||
window.dispatchEvent(new CustomEvent('tier-maker:focus-editor-item-search'))
|
||||
return
|
||||
}
|
||||
focusGlobalSearch()
|
||||
return
|
||||
}
|
||||
if (['g', 'ㅎ'].includes(normalizedKey) && showTopicViewToggle.value) {
|
||||
event.preventDefault()
|
||||
setTopicViewMode('grid')
|
||||
return
|
||||
}
|
||||
if (['l', 'ㅣ'].includes(normalizedKey) && showTopicViewToggle.value) {
|
||||
event.preventDefault()
|
||||
setTopicViewMode('list')
|
||||
return
|
||||
}
|
||||
if (['a', 'ㅁ'].includes(normalizedKey) && isAdmin.value) {
|
||||
event.preventDefault()
|
||||
router.push('/admin/featured')
|
||||
}
|
||||
}
|
||||
|
||||
@@ -570,6 +592,23 @@ function closeCollapsedSearch() {
|
||||
isCollapsedSearchOpen.value = false
|
||||
}
|
||||
|
||||
async function focusGlobalSearch() {
|
||||
if (leftRailCollapsed.value && !isMobileLayout.value) {
|
||||
openCollapsedSearch()
|
||||
await nextTick()
|
||||
if (collapsedSearchInputEl.value?.focus) {
|
||||
collapsedSearchInputEl.value.focus()
|
||||
collapsedSearchInputEl.value.select?.()
|
||||
}
|
||||
return
|
||||
}
|
||||
await nextTick()
|
||||
if (searchInputEl.value?.focus) {
|
||||
searchInputEl.value.focus()
|
||||
searchInputEl.value.select?.()
|
||||
}
|
||||
}
|
||||
|
||||
function openGuideModal(stepIndex = 0) {
|
||||
guideStepIndex.value = Math.min(Math.max(Number(stepIndex) || 0, 0), guideSteps.length - 1)
|
||||
isGuideModalOpen.value = true
|
||||
@@ -690,7 +729,7 @@ function reloadApp() {
|
||||
<SvgIcon :src="iconSearch" :size="24" />
|
||||
</span>
|
||||
</button>
|
||||
<input v-model="searchQuery" class="searchStub__input" type="search" :placeholder="leftRailCollapsed ? '' : leftRailSearchPlaceholder" />
|
||||
<input ref="searchInputEl" v-model="searchQuery" class="searchStub__input" type="search" :placeholder="leftRailCollapsed ? '' : leftRailSearchPlaceholder" />
|
||||
</form>
|
||||
|
||||
<nav
|
||||
@@ -771,7 +810,7 @@ function reloadApp() {
|
||||
<span class="collapsedSearchBar__icon">
|
||||
<SvgIcon :src="iconSearch" :size="24" />
|
||||
</span>
|
||||
<input v-model="searchQuery" class="collapsedSearchBar__input" type="search" :placeholder="leftRailSearchPlaceholder" autofocus />
|
||||
<input ref="collapsedSearchInputEl" v-model="searchQuery" class="collapsedSearchBar__input" type="search" :placeholder="leftRailSearchPlaceholder" autofocus />
|
||||
</form>
|
||||
</div>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user