Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add: ダイアログのデザインを調整し、文言も調整していく #2410

13 changes: 10 additions & 3 deletions docs/UX・UIデザインの方針.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,9 +59,16 @@
- それ以外はトーストで通知(`SHOW_NOTIFY_`系)
- 例: 音声を書き出しました。
- 例: エンジンが異常終了しました。エンジンの再起動が必要です。
- ダイアログの warning と confirm の使い分け
- 続行することが望まれそうな場合は confirm
- キャンセルすることが望まれそうな場合は warning
- ダイアログに関して
- warning と confirm の使い分け
- 続行することが望まれそうな場合は confirm
- キャンセルすることが望まれそうな場合は warning
- ダイアログの文面
- タイトルは疑問文「~しますか?」
- 本文は簡潔にし、「よろしいですか?」などを避ける
- ボタンの色
- 肯定的で、そちらを選ぶ可能性が高いボタンはPrimary色にする
- 破壊的で、状態を戻すのに手間がかかる操作を行うボタンはWarningn色にする

## 文章など

Expand Down
41 changes: 34 additions & 7 deletions src/components/Dialog/Dialog.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import { withProgress } from "@/store/ui";
type MediaType = "audio" | "text";

export type TextDialogResult = "OK" | "CANCEL";
export type AlertDialogOptions = {
export type MessageDialogOptions = {
type?: DialogType;
title: string;
message: string;
Expand All @@ -26,21 +26,23 @@ export type ConfirmDialogOptions = {
type?: DialogType;
title: string;
message: string;
actionName: string;
actionName: string; // ボタンテキスト
idPrimaryColorButton?: boolean; // ボタンをPrimary色にするか
Hiroshiba marked this conversation as resolved.
Show resolved Hide resolved
cancel?: string;
};
export type WarningDialogOptions = {
type?: DialogType;
title: string;
message: string;
actionName: string;
actionName: string; // ボタンテキスト
isWarningColorButton?: boolean; // ボタンをWarning色にするか
cancel?: string;
};
export type QuestionDialogOptions = {
type?: DialogType;
title: string;
message: string;
buttons: string[];
buttons: (string | { text: string; color: string })[];
cancel: number;
default?: number;
};
Expand All @@ -55,7 +57,9 @@ export type NotifyAndNotShowAgainButtonOption = {
export type LoadingScreenOption = { message: string };

// 汎用ダイアログを表示
export const showAlertDialog = async (options: AlertDialogOptions) => {

/** メッセージを知らせるダイアログ */
export const showMessageDialog = async (options: MessageDialogOptions) => {
options.ok ??= "閉じる";

const { promise, resolve } = Promise.withResolvers<void>();
Expand All @@ -74,6 +78,17 @@ export const showAlertDialog = async (options: AlertDialogOptions) => {
return "OK" as const;
};

/** エラーが起こったことを知らせるダイアログ */
export const showAlertDialog = async (
options: Omit<MessageDialogOptions, "type">,
) => {
return await showMessageDialog({
...options,
type: "error",
});
};

/** 続行することが望まれそうな場合の質問ダイアログ */
export const showConfirmDialog = async (options: ConfirmDialogOptions) => {
options.cancel ??= "キャンセル";

Expand All @@ -84,7 +99,12 @@ export const showConfirmDialog = async (options: ConfirmDialogOptions) => {
type: options.type ?? "question",
title: options.title,
message: options.message,
buttons: [options.cancel, options.actionName],
buttons: [
options.cancel,
options.idPrimaryColorButton
? { text: options.actionName, color: "primary" }
: options.actionName,
],
default: 1,
},
}).onOk(({ index }: { index: number }) => resolve(index));
Expand All @@ -94,6 +114,7 @@ export const showConfirmDialog = async (options: ConfirmDialogOptions) => {
return index === 1 ? "OK" : "CANCEL";
};

/** キャンセルすることが望まれそうな場合の質問ダイアログ */
export const showWarningDialog = async (options: WarningDialogOptions) => {
options.cancel ??= "キャンセル";

Expand All @@ -104,7 +125,12 @@ export const showWarningDialog = async (options: WarningDialogOptions) => {
type: options.type ?? "warning",
title: options.title,
message: options.message,
buttons: [options.cancel, options.actionName],
buttons: [
options.cancel,
options.isWarningColorButton
? { text: options.actionName, color: "warning" }
: options.actionName,
],
default: 0,
},
}).onOk(({ index }: { index: number }) => resolve(index));
Expand All @@ -114,6 +140,7 @@ export const showWarningDialog = async (options: WarningDialogOptions) => {
return index === 1 ? "OK" : "CANCEL";
};

/** キャンセル以外に複数の選択肢がある質問ダイアログ */
export const showQuestionDialog = async (options: QuestionDialogOptions) => {
const { promise, resolve } = Promise.withResolvers<number>();
Dialog.create({
Expand Down
10 changes: 6 additions & 4 deletions src/components/Dialog/DictionaryManageDialog.vue
Original file line number Diff line number Diff line change
Expand Up @@ -599,9 +599,10 @@ const saveWord = async () => {
};
const deleteWord = async () => {
const result = await store.actions.SHOW_WARNING_DIALOG({
title: "登録された単語を削除しますか?",
title: "単語を削除しますか?",
message: "削除された単語は元に戻せません。",
actionName: "削除",
actionName: "削除する",
isWarningColorButton: true,
});
if (result === "OK") {
try {
Expand All @@ -625,7 +626,7 @@ const resetWord = async (id: string) => {
const result = await store.actions.SHOW_WARNING_DIALOG({
title: "単語の変更をリセットしますか?",
message: "単語の変更は破棄されてリセットされます。",
actionName: "リセット",
actionName: "リセットする",
});
if (result === "OK") {
selectedId.value = id;
Expand All @@ -640,7 +641,8 @@ const discardOrNotDialog = async (okCallback: () => void) => {
const result = await store.actions.SHOW_WARNING_DIALOG({
title: "単語の追加・変更を破棄しますか?",
message: "破棄すると、単語の追加・変更はリセットされます。",
actionName: "破棄",
actionName: "破棄する",
isWarningColorButton: true,
});
if (result === "OK") {
okCallback();
Expand Down
30 changes: 15 additions & 15 deletions src/components/Dialog/EngineManageDialog.vue
Original file line number Diff line number Diff line change
Expand Up @@ -409,10 +409,10 @@ const getEngineDirValidationMessage = (result: EngineDirValidationResult) => {

const addEngine = async () => {
const result = await store.actions.SHOW_WARNING_DIALOG({
title: "エンジン追加の確認",
title: "エンジンを追加しますか?",
message:
"この操作はコンピュータに損害を与える可能性があります。エンジンの配布元が信頼できない場合は追加しないでください。",
actionName: "追加",
actionName: "追加する",
});
if (result === "OK") {
if (engineLoaderType.value === "dir") {
Expand All @@ -424,7 +424,7 @@ const addEngine = async () => {
);

void requireReload(
"エンジンを追加しました。反映には再読み込みが必要です。今すぐ再読み込みしますか?",
"エンジンを追加しました。反映には再読み込みが必要です。",
);
} else {
const success = await lockUi(
Expand All @@ -433,7 +433,7 @@ const addEngine = async () => {
);
if (success) {
void requireReload(
"エンジンを追加しました。反映には再読み込みが必要です。今すぐ再読み込みしますか?",
"エンジンを追加しました。反映には再読み込みが必要です。",
);
}
}
Expand All @@ -450,10 +450,11 @@ const deleteEngine = async () => {
throw new Error("default engine cannot be deleted");
}

const result = await store.actions.SHOW_CONFIRM_DIALOG({
title: "エンジン削除の確認",
message: "選択中のエンジンを削除します。よろしいですか?",
actionName: "削除",
const result = await store.actions.SHOW_WARNING_DIALOG({
title: "エンジンを削除しますか?",
message: "選択中のエンジンを削除します。",
actionName: "削除する",
isWarningColorButton: true,
});
if (result === "OK") {
switch (engineInfo.type) {
Expand All @@ -468,7 +469,7 @@ const deleteEngine = async () => {
}),
);
void requireReload(
"エンジンを削除しました。反映には再読み込みが必要です。今すぐ再読み込みしますか?",
"エンジンを削除しました。反映には再読み込みが必要です。",
);
break;
}
Expand All @@ -478,9 +479,7 @@ const deleteEngine = async () => {
store.actions.UNINSTALL_VVPP_ENGINE(engineId),
);
if (success) {
void requireReload(
"エンジンの削除には再読み込みが必要です。今すぐ再読み込みしますか?",
);
void requireReload("エンジンの削除には再読み込みが必要です。");
}
break;
}
Expand Down Expand Up @@ -509,11 +508,12 @@ const restartSelectedEngine = () => {
};

const requireReload = async (message: string) => {
const result = await store.actions.SHOW_WARNING_DIALOG({
title: "再読み込みが必要です",
const result = await store.actions.SHOW_CONFIRM_DIALOG({
title: "再読み込みしますか?",
message: message,
actionName: "再読み込み",
actionName: "再読み込みする",
cancel: "後で",
idPrimaryColorButton: true,
});
toInitialState();
if (result === "OK") {
Expand Down
5 changes: 2 additions & 3 deletions src/components/Dialog/HotkeySettingDialog.vue
Original file line number Diff line number Diff line change
Expand Up @@ -242,10 +242,9 @@ const isDefaultCombination = (action: string) => {

const resetHotkey = async (action: string) => {
const result = await store.actions.SHOW_CONFIRM_DIALOG({
title: "ショートカットキーをデフォルトに戻します",
message: `${action}のショートカットキーをデフォルトに戻します。\n本当に戻しますか?`,
title: "デフォルトに戻しますか?",
message: `${action}のショートカットキーをデフォルトに戻します。`,
actionName: "デフォルトに戻す",
cancel: "デフォルトに戻さない",
});

if (result !== "OK") return;
Expand Down
9 changes: 5 additions & 4 deletions src/components/Dialog/PresetManageDialog.vue
Original file line number Diff line number Diff line change
Expand Up @@ -103,10 +103,11 @@ const reorderPreset = (featurePresetList: (Preset & { key: PresetKey })[]) => {
};

const deletePreset = async (key: PresetKey) => {
const result = await store.actions.SHOW_CONFIRM_DIALOG({
title: "プリセット削除の確認",
message: `プリセット "${presetItems.value[key].name}" を削除してもよろしいですか?`,
actionName: "削除",
const result = await store.actions.SHOW_WARNING_DIALOG({
title: "プリセットを削除しますか?",
message: `プリセット "${presetItems.value[key].name}" を削除します。`,
actionName: "削除する",
isWarningColorButton: true,
});
if (result === "OK") {
await store.actions.DELETE_PRESET({
Expand Down
5 changes: 2 additions & 3 deletions src/components/Dialog/SettingDialog/SettingDialog.vue
Original file line number Diff line number Diff line change
Expand Up @@ -767,11 +767,10 @@ const outputSamplingRate = computed({
set: async (outputSamplingRate: SamplingRateOption) => {
if (outputSamplingRate !== "engineDefault") {
const result = await store.actions.SHOW_CONFIRM_DIALOG({
title: "出力サンプリングレートを変更します",
title: "出力サンプリングレートを変更しますか?",
message:
"出力サンプリングレートを変更しても、音質は変化しません。また、音声の生成処理に若干時間がかかる場合があります。\n変更しますか?",
"出力サンプリングレートを変更しても、音質は変化しません。また、音声の生成処理に若干時間がかかる場合があります。",
actionName: "変更する",
cancel: "変更しない",
});
if (result !== "OK") {
return;
Expand Down
18 changes: 12 additions & 6 deletions src/components/Dialog/TextDialog/MessageDialog.vue
Original file line number Diff line number Diff line change
Expand Up @@ -13,20 +13,26 @@
<QCard class="q-py-sm q-px-md dialog-card">
<QCardSection class="title">
<QIcon
v-if="props.type !== 'none'"
:name="iconName"
class="text-h5 q-mr-sm"
v-if="props.type !== 'info'"
:name="`sym_o_${iconName}`"
size="2rem"
class="q-mr-sm"
:color
/>
<div class="text-h5" :class="[`text-${color}`]">{{ props.title }}</div>
<div class="text-h5">{{ props.title }}</div>
</QCardSection>

<QCardSection class="q-py-none message">
<QSeparator />

<QCardSection class="message">
{{ props.message }}
</QCardSection>

<QSeparator />

<QCardActions align="right">
<QBtn
unelevated
outline
:label="props.ok"
color="toolbar-button"
textColor="toolbar-button-display"
Expand Down
14 changes: 13 additions & 1 deletion src/components/Dialog/TextDialog/QuestionDialog.stories.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,11 +33,23 @@ export const Opened: Story = {
export const OpenedMultiline: Story = {
name: "開いている:複数行",
args: {
modelValue: true,
...Opened.args,
message: "メッセージ\n複数行",
},
};

export const OpenedButtonColor: Story = {
name: "開いている:ボタン色を変える",
args: {
...Opened.args,
buttons: [
{ text: "primary", color: "primary" },
{ text: "warning", color: "warning" },
"default",
],
},
};

export const Close: Story = {
name: "Aを押す",
args: { ...Opened.args },
Expand Down
Loading
Loading