import { ref, watch } from 'vue' /** * 검색어를 NFC로 정규화한다. * @param {string} value - 원본 입력값 * @returns {string} 정규화된 문자열 */ const normalizeSearchValue = (value) => String(value ?? '').normalize('NFC') /** * 한글 등 IME 조합 중에도 검색어가 즉시 반영되는 검색 입력 처리기를 만든다. * * Vue의 `v-model`은 IME 조합 중에는 값을 갱신하지 않아, 조합이 끝나야(스페이스·다음 글자) * 검색이 시작되는 문제가 있다. 이 컴포저블은 `compositionupdate`까지 반영해 조합 도중에도 * 검색이 되게 하고, 선택적으로 디바운스를 적용한다. * * @param {import('vue').Ref} targetRef - 검색/필터에 사용할 대상 ref(정규화된 값이 기록됨) * @param {Object} [options] - 옵션 * @param {number} [options.debounce=0] - 디바운스 지연(ms). 0이면 즉시 반영 * @returns {{ displayValue: import('vue').Ref, onInput: (event: Event) => void, onCompositionEnd: (event: Event) => void }} */ export const useImeSearchInput = (targetRef, options = {}) => { const debounce = Number(options.debounce) || 0 const displayValue = ref(String(targetRef.value ?? '')) /** @type {ReturnType | null} */ let debounceTimer = null /** * 대상 ref에 정규화된 값을 반영한다(디바운스 적용). * @param {string} value - 현재 입력값 * @returns {void} */ const commit = (value) => { if (debounce > 0) { clearTimeout(debounceTimer) debounceTimer = setTimeout(() => { targetRef.value = normalizeSearchValue(value) }, debounce) return } targetRef.value = normalizeSearchValue(value) } /** * 입력 이벤트에서 표시값과 검색어를 갱신한다(IME 조합 중 포함). * @param {Event} event - `input`/`compositionupdate` 이벤트 * @returns {void} */ const onInput = (event) => { const target = event.target if (!(target instanceof HTMLInputElement)) { return } displayValue.value = target.value commit(target.value) } /** * IME 조합 종료 시 최종 검색어를 즉시 반영한다. * @param {Event} event - `compositionend` 이벤트 * @returns {void} */ const onCompositionEnd = (event) => { const target = event.target if (target instanceof HTMLInputElement) { displayValue.value = target.value } clearTimeout(debounceTimer) targetRef.value = normalizeSearchValue(displayValue.value) } watch(targetRef, (value) => { if (normalizeSearchValue(displayValue.value) !== value) { displayValue.value = String(value ?? '') } }) return { displayValue, onInput, onCompositionEnd } }