From f35eb0f6d9330924513a5b6eded04c9b71196f0d Mon Sep 17 00:00:00 2001
From: かっこかり <67428053+kakkokari-gtyih@users.noreply.github.com>
Date: Sun, 9 Mar 2025 14:21:23 +0900
Subject: enhnace(frontend): 文字列比較のためのローマナイズを強化（設定の検索）
 (#15632)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* enhnace(frontend): 文字列比較のためのローマナイズを強化

* docs

* fix

* fix

* fix

* comment

* wanakanaの初回ロードをコンポーネント内に移動

* comment

* fix

* add tests

---------

Co-authored-by: syuilo <4439005+syuilo@users.noreply.github.com>
---
 packages/frontend/src/components/MkSuperMenu.vue | 39 ++++++++++++++++++------
 1 file changed, 29 insertions(+), 10 deletions(-)

(limited to 'packages/frontend/src/components/MkSuperMenu.vue')

diff --git a/packages/frontend/src/components/MkSuperMenu.vue b/packages/frontend/src/components/MkSuperMenu.vue
index d8dec3aa2f..ce0fa86219 100644
--- a/packages/frontend/src/components/MkSuperMenu.vue
+++ b/packages/frontend/src/components/MkSuperMenu.vue
@@ -6,16 +6,18 @@ SPDX-License-Identifier: AGPL-3.0-only
 <template>
 <div ref="rootEl" class="rrevdjwu" :class="{ grid }">
 	<MkInput
-		v-model="search"
+		v-if="searchIndex && searchIndex.length > 0"
+		v-model="searchQuery"
 		:placeholder="i18n.ts.search"
 		type="search"
 		style="margin-bottom: 16px;"
+		@input.passive="searchOnInput"
 		@keydown="searchOnKeyDown"
 	>
 		<template #prefix><i class="ti ti-search"></i></template>
 	</MkInput>
 
-	<template v-if="search == ''">
+	<template v-if="rawSearchQuery == ''">
 		<div v-for="group in def" class="group">
 			<div v-if="group.title" class="title">{{ group.title }}</div>
 
@@ -97,17 +99,22 @@ import MkInput from '@/components/MkInput.vue';
 import { i18n } from '@/i18n.js';
 import { getScrollContainer } from '@@/js/scroll.js';
 import { useRouter } from '@/router/supplier.js';
+import { initIntlString, compareStringIncludes } from '@/scripts/intl-string.js';
 
 const props = defineProps<{
 	def: SuperMenuDef[];
 	grid?: boolean;
-	searchIndex: SearchIndexItem[];
+	searchIndex?: SearchIndexItem[];
 }>();
 
+initIntlString();
+
 const router = useRouter();
 const rootEl = useTemplateRef('rootEl');
 
-const search = ref('');
+const searchQuery = ref('');
+const rawSearchQuery = ref('');
+
 const searchSelectedIndex = ref<null | number>(null);
 const searchResult = ref<{
 	id: string;
@@ -118,7 +125,11 @@ const searchResult = ref<{
 	parentLabels: string[];
 }[]>([]);
 
-watch(search, (value) => {
+watch(searchQuery, (value) => {
+	rawSearchQuery.value = value;
+});
+
+watch(rawSearchQuery, (value) => {
 	searchResult.value = [];
 	searchSelectedIndex.value = null;
 
@@ -128,14 +139,15 @@ watch(search, (value) => {
 
 	const dive = (items: SearchIndexItem[], parents: SearchIndexItem[] = []) => {
 		for (const item of items) {
-			const matched =
-				item.label.includes(value.toLowerCase()) ||
-				item.keywords.some((x) => x.toLowerCase().includes(value.toLowerCase()));
+			const matched = (
+				compareStringIncludes(item.label, value) ||
+				item.keywords.some((x) => compareStringIncludes(x, value))
+			);
 
 			if (matched) {
 				searchResult.value.push({
 					id: item.id,
-					path: item.path ?? parents.find((x) => x.path != null)?.path,
+					path: item.path ?? parents.find((x) => x.path != null)?.path ?? '/', // never gets `/`
 					label: item.label,
 					parentLabels: parents.map((x) => x.label).toReversed(),
 					icon: item.icon ?? parents.find((x) => x.icon != null)?.icon,
@@ -149,9 +161,16 @@ watch(search, (value) => {
 		}
 	};
 
-	dive(props.searchIndex);
+	if (props.searchIndex) {
+		dive(props.searchIndex);
+	}
 });
 
+function searchOnInput(ev: InputEvent) {
+	searchSelectedIndex.value = null;
+	rawSearchQuery.value = (ev.target as HTMLInputElement).value;
+}
+
 function searchOnKeyDown(ev: KeyboardEvent) {
 	if (ev.isComposing) return;
 
-- 
cgit v1.2.3-freya

