diff --git a/src/components/Dialog/DictionaryEditWordDialog.vue b/src/components/Dialog/DictionaryEditWordDialog.vue index 9cefe07a89..1a694a51cb 100644 --- a/src/components/Dialog/DictionaryEditWordDialog.vue +++ b/src/components/Dialog/DictionaryEditWordDialog.vue @@ -169,6 +169,7 @@ import ContextMenu from "@/components/Menu/ContextMenu/Container.vue"; import { useRightClickContextMenu } from "@/composables/useRightClickContextMenu"; import { useStore } from "@/store"; import type { FetchAudioResult } from "@/store/type"; +import { convertHankakuToZenkaku } from "@/domain/japanese"; const store = useStore(); @@ -262,16 +263,6 @@ const setYomiWhenEnter = (event?: KeyboardEvent) => { void setYomi(yomi.value); }; -const convertHankakuToZenkaku = (text: string) => { - // " "などの目に見えない文字をまとめて全角スペース(0x3000)に置き換える - text = text.replace(/\p{Z}/gu, () => String.fromCharCode(0x3000)); - - // "!"から"~"までの範囲の文字(数字やアルファベット)を全角に置き換える - return text.replace(/[\u0021-\u007e]/g, (s) => { - return String.fromCharCode(s.charCodeAt(0) + 0xfee0); - }); -}; - const setSurface = (text: string) => { // surfaceを全角化する // 入力は半角でも問題ないが、登録時に全角に変換され、isWordChangedの判断がおかしくなることがあるので、 diff --git a/src/components/Dialog/DictionaryManageDialog.vue b/src/components/Dialog/DictionaryManageDialog.vue index e10955dfae..c1491a9184 100644 --- a/src/components/Dialog/DictionaryManageDialog.vue +++ b/src/components/Dialog/DictionaryManageDialog.vue @@ -39,44 +39,95 @@ + +
-
-
- 単語一覧 +
+
+ 単語一覧 + 追加 + + + + + + 優先度をリストに表示する + + + + +
+
+ + +
+
{{ value.surface }} - {{ value.yomi }} + {{ value.yomi }} (undefined); const loadingDictState = ref("loading"); const userDict = ref>({}); +// 検索結果でフィルタリングされたユーザ辞書 +const filteredUserDict = computed(() => { + return Object.fromEntries( + Object.entries(userDict.value) + .sort((a, b) => { + let order; + switch (sortType.value.value) { + case "yomi": + order = a[1].yomi.localeCompare(b[1].yomi); + break; + case "priority": + order = b[1].priority - a[1].priority; + break; + default: + order = 0; + break; + } + return order * (isDesc.value ? -1 : 1); + }) + .filter(([, value]) => { + // 半角から全角に変換 + let searchWord = convertHankakuToZenkaku(wordFilter.value); + // ひらがなからカタカナに変換 + searchWord = convertHiraToKana(searchWord); + // 長音を適切な音に変換 + const searchWordLongVowel = convertLongVowel(searchWord); + return ( + value.surface.includes(searchWord) || + value.yomi.includes(searchWord) || + value.surface.includes(searchWordLongVowel) || + value.yomi.includes(searchWordLongVowel) + ); + }), + ); +}); + +// 表示順 +const sortTypes = [ + { + label: "読み順", + value: "yomi", + }, + { + label: "優先度順", + value: "priority", + }, +]; +const sortType = ref(sortTypes[0]); + +// 降順か? +const isDesc = ref(false); + const createUILockAction = function (action: Promise) { uiLocked.value = true; return action.finally(() => { @@ -233,6 +337,8 @@ watch(dictionaryManageDialogOpenedComputed, async (newValue) => { } }); +const wordFilter = ref(""); + const wordEditing = ref(false); const surfaceInput = ref(); const selectedId = ref(""); @@ -316,6 +422,19 @@ const computeDisplayAccent = () => { const wordPriority = ref(defaultDictPriority); +// 優先度表示 +const showPriorityOnDictionaryKey = "ShowPriorityOnDictionary"; +const showPriorityOnDictionary = ref( + localStorage.getItem(showPriorityOnDictionaryKey) === "true", +); +// 優先度表示の変更があった時に呼ばれるイベント +const OnClickShowPriorityOnDictionary = () => { + localStorage.setItem( + showPriorityOnDictionaryKey, + showPriorityOnDictionary.value.toString(), + ); +}; + // 操作(ステートの移動) const isWordChanged = computed(() => { if (selectedId.value === "") { @@ -384,6 +503,7 @@ const newWord = () => { wordPriority.value = defaultDictPriority; editWord(); }; + const editWord = () => { toWordEditingState(); }; @@ -458,23 +578,25 @@ provide(dictionaryManageDialogContextKey, { overflow-x: hidden; } +.word-list-header-text { + display: flex; + justify-content: center; + align-items: center; +} + .word-list-header { margin: 1rem; - - gap: 0.5rem; align-items: center; + vertical-align: middle; justify-content: space-between; - .word-list-title { - flex-grow: 1; - } } .word-list { // menubar-height + toolbar-height + window-border-width + - // 36(title & buttons) + 30(margin 15x2) + // 140(title & buttons) + 30(margin 15x2) height: calc( 100vh - #{vars.$menubar-height + vars.$toolbar-height + - vars.$window-border-width + 36px + 30px} + vars.$window-border-width + 140px + 30px} ); width: 100%; overflow-y: auto; diff --git a/src/domain/japanese/index.ts b/src/domain/japanese/index.ts index ae20a5275e..9b456db423 100644 --- a/src/domain/japanese/index.ts +++ b/src/domain/japanese/index.ts @@ -15,6 +15,17 @@ export const createKanaRegex = (includeSeparation?: boolean): RegExp => { return /^[\u3041-\u3094\u30A1-\u30F4\u30FC]+$/; }; +/** 半角文字を全角文字にする */ +export const convertHankakuToZenkaku = (text: string) => { + // " "などの目に見えない文字をまとめて全角スペース(0x3000)に置き換える + text = text.replace(/\p{Z}/gu, () => String.fromCharCode(0x3000)); + + // "!"から"~"までの範囲の文字(数字やアルファベット)を全角に置き換える + return text.replace(/[\u0021-\u007e]/g, (s) => { + return String.fromCharCode(s.charCodeAt(0) + 0xfee0); + }); +}; + /** ひらがなをカタカナにする */ export const convertHiraToKana = (text: string): string => { return text.replace(/[\u3041-\u3094]/g, (s) => {