에디터 슬래시 상태와 단축키 판별 분리
This commit is contained in:
157
composables/useAdminMarkdownSlashMenu.js
Normal file
157
composables/useAdminMarkdownSlashMenu.js
Normal file
@@ -0,0 +1,157 @@
|
||||
import { computed, ref } from 'vue'
|
||||
import {
|
||||
addSlashSuppressedLine,
|
||||
clampSlashHighlightedIndex,
|
||||
filterSlashCommands,
|
||||
removeSlashSuppressedLine
|
||||
} from '../lib/markdown-slash-commands.js'
|
||||
|
||||
/**
|
||||
* 관리자 Markdown 에디터의 슬래시 메뉴 상태를 관리한다.
|
||||
* @returns {{
|
||||
* liveSlashSourceLine: import('vue').Ref<number|null>,
|
||||
* liveSlashQuery: import('vue').Ref<string>,
|
||||
* liveSlashHighlightedIndex: import('vue').Ref<number>,
|
||||
* liveSlashMenuPosition: import('vue').Ref<{ top: number, left: number }>,
|
||||
* liveSlashMenuRef: import('vue').Ref<HTMLElement|null>,
|
||||
* liveSlashInsertLine: import('vue').Ref<number|null>,
|
||||
* liveSlashSuppressedLines: import('vue').Ref<Set<number>>,
|
||||
* liveSlashKeyboardNav: import('vue').Ref<boolean>,
|
||||
* liveSlashVisible: import('vue').ComputedRef<boolean>,
|
||||
* liveSlashSuppressedLineList: import('vue').ComputedRef<number[]>,
|
||||
* visibleLiveSlashCommands: import('vue').ComputedRef<import('../lib/markdown-slash-commands.js').MarkdownSlashCommand[]>,
|
||||
* clearLiveSlashState: () => void,
|
||||
* openLiveSlashMenu: (sourceLine: number, query: string) => void,
|
||||
* isLiveSlashLineSuppressed: (line: number) => boolean,
|
||||
* suppressLiveSlashLine: (line: number) => void,
|
||||
* releaseLiveSlashLine: (line: number) => void,
|
||||
* resetLiveSlashHighlightedIndex: () => void,
|
||||
* clampLiveSlashHighlightedIndex: (commandCount: number) => void,
|
||||
* startLiveSlashKeyboardNavigation: (durationMs?: number) => void,
|
||||
* stopLiveSlashKeyboardNavigation: () => void
|
||||
* }} 슬래시 메뉴 상태와 액션
|
||||
*/
|
||||
export const useAdminMarkdownSlashMenu = () => {
|
||||
const liveSlashSourceLine = ref(null)
|
||||
const liveSlashQuery = ref('')
|
||||
const liveSlashHighlightedIndex = ref(0)
|
||||
const liveSlashMenuPosition = ref({ top: 0, left: 0 })
|
||||
const liveSlashMenuRef = ref(null)
|
||||
const liveSlashInsertLine = ref(null)
|
||||
const liveSlashSuppressedLines = ref(new Set())
|
||||
const liveSlashKeyboardNav = ref(false)
|
||||
let liveSlashKeyboardNavTimer = null
|
||||
|
||||
const liveSlashVisible = computed(() => liveSlashSourceLine.value !== null)
|
||||
const liveSlashSuppressedLineList = computed(() => [...liveSlashSuppressedLines.value])
|
||||
const visibleLiveSlashCommands = computed(() => filterSlashCommands(liveSlashQuery.value))
|
||||
|
||||
/**
|
||||
* 슬래시 메뉴 입력 상태를 초기화한다.
|
||||
* @returns {void}
|
||||
*/
|
||||
const clearLiveSlashState = () => {
|
||||
liveSlashSourceLine.value = null
|
||||
liveSlashQuery.value = ''
|
||||
liveSlashHighlightedIndex.value = 0
|
||||
}
|
||||
|
||||
/**
|
||||
* 슬래시 메뉴를 지정 줄과 검색어로 연다.
|
||||
* @param {number} sourceLine - 슬래시 명령이 입력된 줄
|
||||
* @param {string} query - 검색어
|
||||
* @returns {void}
|
||||
*/
|
||||
const openLiveSlashMenu = (sourceLine, query) => {
|
||||
liveSlashSourceLine.value = sourceLine
|
||||
liveSlashQuery.value = String(query ?? '')
|
||||
}
|
||||
|
||||
/**
|
||||
* 슬래시 메뉴가 억제된 줄인지 확인한다.
|
||||
* @param {number} line - 줄 번호
|
||||
* @returns {boolean} 억제 여부
|
||||
*/
|
||||
const isLiveSlashLineSuppressed = (line) => liveSlashSuppressedLines.value.has(line)
|
||||
|
||||
/**
|
||||
* 지정 줄에서 슬래시 메뉴가 다시 열리지 않도록 억제한다.
|
||||
* @param {number} line - 줄 번호
|
||||
* @returns {void}
|
||||
*/
|
||||
const suppressLiveSlashLine = (line) => {
|
||||
liveSlashSuppressedLines.value = addSlashSuppressedLine(liveSlashSuppressedLines.value, line)
|
||||
}
|
||||
|
||||
/**
|
||||
* 지정 줄의 슬래시 메뉴 억제를 해제한다.
|
||||
* @param {number} line - 줄 번호
|
||||
* @returns {void}
|
||||
*/
|
||||
const releaseLiveSlashLine = (line) => {
|
||||
liveSlashSuppressedLines.value = removeSlashSuppressedLine(liveSlashSuppressedLines.value, line)
|
||||
}
|
||||
|
||||
/**
|
||||
* 슬래시 메뉴 하이라이트를 첫 항목으로 되돌린다.
|
||||
* @returns {void}
|
||||
*/
|
||||
const resetLiveSlashHighlightedIndex = () => {
|
||||
liveSlashHighlightedIndex.value = 0
|
||||
}
|
||||
|
||||
/**
|
||||
* 슬래시 메뉴 하이라이트를 명령 개수 안으로 보정한다.
|
||||
* @param {number} commandCount - 표시 가능한 명령 개수
|
||||
* @returns {void}
|
||||
*/
|
||||
const clampLiveSlashHighlightedIndex = (commandCount) => {
|
||||
liveSlashHighlightedIndex.value = clampSlashHighlightedIndex(liveSlashHighlightedIndex.value, commandCount)
|
||||
}
|
||||
|
||||
/**
|
||||
* 키보드 탐색 중 mouseenter 하이라이트를 잠시 무시한다.
|
||||
* @param {number} [durationMs=200] - 유지 시간(ms)
|
||||
* @returns {void}
|
||||
*/
|
||||
const startLiveSlashKeyboardNavigation = (durationMs = 200) => {
|
||||
liveSlashKeyboardNav.value = true
|
||||
globalThis.clearTimeout(liveSlashKeyboardNavTimer)
|
||||
liveSlashKeyboardNavTimer = globalThis.setTimeout(() => {
|
||||
liveSlashKeyboardNav.value = false
|
||||
}, durationMs)
|
||||
}
|
||||
|
||||
/**
|
||||
* 슬래시 메뉴 키보드 탐색 타이머를 정리한다.
|
||||
* @returns {void}
|
||||
*/
|
||||
const stopLiveSlashKeyboardNavigation = () => {
|
||||
globalThis.clearTimeout(liveSlashKeyboardNavTimer)
|
||||
liveSlashKeyboardNavTimer = null
|
||||
liveSlashKeyboardNav.value = false
|
||||
}
|
||||
|
||||
return {
|
||||
liveSlashSourceLine,
|
||||
liveSlashQuery,
|
||||
liveSlashHighlightedIndex,
|
||||
liveSlashMenuPosition,
|
||||
liveSlashMenuRef,
|
||||
liveSlashInsertLine,
|
||||
liveSlashSuppressedLines,
|
||||
liveSlashKeyboardNav,
|
||||
liveSlashVisible,
|
||||
liveSlashSuppressedLineList,
|
||||
visibleLiveSlashCommands,
|
||||
clearLiveSlashState,
|
||||
openLiveSlashMenu,
|
||||
isLiveSlashLineSuppressed,
|
||||
suppressLiveSlashLine,
|
||||
releaseLiveSlashLine,
|
||||
resetLiveSlashHighlightedIndex,
|
||||
clampLiveSlashHighlightedIndex,
|
||||
startLiveSlashKeyboardNavigation,
|
||||
stopLiveSlashKeyboardNavigation
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user