diff --git a/src/App.vue b/src/App.vue
index 1a7459fd..d1d3642e 100644
--- a/src/App.vue
+++ b/src/App.vue
@@ -15,6 +15,7 @@
+
@@ -33,6 +34,7 @@
import AlertDialogModal from './components/ui/alert-dialog/AlertDialogModal.vue';
import MacOSTitleBar from './components/MacOSTitleBar.vue';
+ import PromptDialogModal from './components/ui/dialog/PromptDialogModal.vue';
import VRCXUpdateDialog from './components/dialogs/VRCXUpdateDialog.vue';
import '@/styles/globals.css';
diff --git a/src/components/ui/dialog/PromptDialogModal.vue b/src/components/ui/dialog/PromptDialogModal.vue
new file mode 100644
index 00000000..427851a2
--- /dev/null
+++ b/src/components/ui/dialog/PromptDialogModal.vue
@@ -0,0 +1,138 @@
+
+
+
+
+
diff --git a/src/localization/en.json b/src/localization/en.json
index 1de84874..dc1da735 100644
--- a/src/localization/en.json
+++ b/src/localization/en.json
@@ -916,6 +916,9 @@
"cancel": "Cancel",
"confirm": "Confirm"
},
+ "prompt": {
+ "input_invalid": "Invalid input"
+ },
"user": {
"status": {
"active": "Active",
diff --git a/src/stores/modal.js b/src/stores/modal.js
index 40bf698b..85f4c8ef 100644
--- a/src/stores/modal.js
+++ b/src/stores/modal.js
@@ -33,6 +33,25 @@ function translate(key, fallback) {
* @property {boolean=} dismissible
*/
+/**
+ * @typedef {Object} PromptResult
+ * @property {boolean} ok
+ * @property {'ok' | 'cancel' | 'dismiss' | 'replaced'} reason
+ * @property {string} value
+ */
+
+/**
+ * @typedef {Object} PromptOptions
+ * @property {string} title
+ * @property {string} description
+ * @property {string=} confirmText
+ * @property {string=} cancelText
+ * @property {string=} inputValue
+ * @property {RegExp | string=} pattern
+ * @property {string=} errorMessage
+ * @property {boolean=} dismissible
+ */
+
// TODO: Method chains for confirm
export const useModalStore = defineStore('Modal', () => {
@@ -44,13 +63,29 @@ export const useModalStore = defineStore('Modal', () => {
const alertCancelText = ref('');
const alertDismissible = ref(true);
+ const promptOpen = ref(false);
+ const promptTitle = ref('');
+ const promptDescription = ref('');
+ const promptOkText = ref('');
+ const promptCancelText = ref('');
+ const promptDismissible = ref(true);
+ const promptInputValue = ref('');
+ const promptPattern = ref(null);
+ const promptErrorMessage = ref('');
+
/** @type {{ resolve: ((result: ConfirmResult) => void) | null } | null} */
let pending = null;
+ /** @type {{ resolve: ((result: PromptResult) => void) | null } | null} */
+ let pendingPrompt = null;
function closeDialog() {
alertOpen.value = false;
}
+ function closePromptDialog() {
+ promptOpen.value = false;
+ }
+
/**
* @param {'ok' | 'cancel' | 'dismiss' | 'replaced'} reason
*/
@@ -70,6 +105,27 @@ export const useModalStore = defineStore('Modal', () => {
if (resolve) resolve({ ok: reason === 'ok', reason });
}
+ /**
+ * @param {'ok' | 'cancel' | 'dismiss' | 'replaced'} reason
+ * @param {string} value
+ */
+ function finishPrompt(reason, value) {
+ const resolve = pendingPrompt?.resolve;
+ pendingPrompt = null;
+ closePromptDialog();
+ if (resolve) resolve({ ok: reason === 'ok', reason, value });
+ }
+
+ /**
+ * @param {'ok' | 'cancel' | 'dismiss' | 'replaced'} reason
+ * @param {string} value
+ */
+ function finishPromptWithoutClosing(reason, value) {
+ const resolve = pendingPrompt?.resolve;
+ pendingPrompt = null;
+ if (resolve) resolve({ ok: reason === 'ok', reason, value });
+ }
+
/**
* @param {'confirm' | 'alert'} mode
* @param {any} options
@@ -106,6 +162,44 @@ export const useModalStore = defineStore('Modal', () => {
});
}
+ /**
+ * @param {PromptOptions} options
+ * @returns {Promise}
+ */
+ function openPrompt(options) {
+ if (pendingPrompt) {
+ finishPromptWithoutClosing('replaced', promptInputValue.value);
+ }
+
+ const inputValue = options.inputValue ?? '';
+ const inputValueCopy =
+ typeof inputValue === 'string'
+ ? inputValue.slice()
+ : String(inputValue);
+
+ promptTitle.value = options.title;
+ promptDescription.value = options.description;
+ promptDismissible.value = options.dismissible !== false;
+ promptInputValue.value = inputValueCopy;
+ promptPattern.value = options.pattern ?? null;
+ promptErrorMessage.value =
+ options.errorMessage ||
+ translate('dialog.prompt.input_invalid', '输入错误');
+
+ promptOkText.value =
+ options.confirmText ||
+ translate('dialog.alertdialog.confirm', 'Confirm');
+ promptCancelText.value =
+ options.cancelText ||
+ translate('dialog.alertdialog.cancel', 'Cancel');
+
+ promptOpen.value = true;
+
+ return new Promise((resolve) => {
+ pendingPrompt = { resolve };
+ });
+ }
+
/**
* confirm: always resolve({ok, reason})
* @param {ConfirmOptions} options
@@ -124,11 +218,25 @@ export const useModalStore = defineStore('Modal', () => {
return openBase('alert', options);
}
+ /**
+ * prompt: always resolve({ok, reason, value})
+ * @param {PromptOptions} options
+ * @returns {Promise}
+ */
+ function prompt(options) {
+ return openPrompt(options);
+ }
+
function handleOk() {
if (!pending) return;
finish('ok');
}
+ function handlePromptOk(value) {
+ if (!pendingPrompt) return;
+ finishPrompt('ok', value ?? '');
+ }
+
function handleCancel() {
if (!pending) return;
@@ -141,6 +249,11 @@ export const useModalStore = defineStore('Modal', () => {
finish('cancel');
}
+ function handlePromptCancel(value) {
+ if (!pendingPrompt) return;
+ finishPrompt('cancel', value ?? '');
+ }
+
function handleDismiss() {
if (!pending) return;
if (!alertDismissible.value) return;
@@ -154,10 +267,20 @@ export const useModalStore = defineStore('Modal', () => {
finish('dismiss');
}
+ function handlePromptDismiss(value) {
+ if (!pendingPrompt) return;
+ if (!promptDismissible.value) return;
+ finishPrompt('dismiss', value ?? '');
+ }
+
function setAlertOpen(open) {
alertOpen.value = !!open;
}
+ function setPromptOpen(open) {
+ promptOpen.value = !!open;
+ }
+
return {
alertOpen,
alertMode,
@@ -166,13 +289,27 @@ export const useModalStore = defineStore('Modal', () => {
alertOkText,
alertCancelText,
alertDismissible,
+ promptOpen,
+ promptTitle,
+ promptDescription,
+ promptOkText,
+ promptCancelText,
+ promptDismissible,
+ promptInputValue,
+ promptPattern,
+ promptErrorMessage,
confirm,
alert,
+ prompt,
handleOk,
handleCancel,
handleDismiss,
- setAlertOpen
+ handlePromptOk,
+ handlePromptCancel,
+ handlePromptDismiss,
+ setAlertOpen,
+ setPromptOpen
};
-});
+};);