replace ElMessageBox(alert, confirm) with alert dialog

This commit is contained in:
pa
2026-01-13 22:40:13 +09:00
committed by Natsumi
parent 870c7a4938
commit fc5afe9e69
53 changed files with 1250 additions and 862 deletions

View File

@@ -67,11 +67,10 @@
import { reactive, watch } from 'vue';
import { Button } from '@/components/ui/button';
import { CaretBottom } from '@element-plus/icons-vue';
import { ElMessageBox } from 'element-plus';
import { toast } from 'vue-sonner';
import { useI18n } from 'vue-i18n';
import { useGroupStore, useInstanceStore, useLocationStore, useUserStore } from '../stores';
import { useGroupStore, useInstanceStore, useLocationStore, useModalStore, useUserStore } from '../stores';
import { formatDateFilter, hasGroupPermission } from '../shared/utils';
import { miscRequest } from '../api';
@@ -81,6 +80,7 @@
const userStore = useUserStore();
const groupStore = useGroupStore();
const instanceStore = useInstanceStore();
const modalStore = useModalStore();
const props = defineProps({
location: String,
@@ -126,13 +126,13 @@
}
function closeInstance(location) {
ElMessageBox.confirm('Continue? Close Instance, nobody will be able to join', 'Confirm', {
confirmButtonText: 'Confirm',
cancelButtonText: 'Cancel',
type: 'warning'
})
.then(async (action) => {
if (action !== 'confirm') return;
modalStore
.confirm({
description: 'Continue? Close Instance, nobody will be able to join',
title: 'Confirm'
})
.then(async ({ ok }) => {
if (!ok) return;
const args = await miscRequest.closeInstance({ location, hardClose: false });
if (args.json) {
toast.success(t('message.instance.closed'));

View File

@@ -246,8 +246,8 @@
<script setup>
import { computed, defineAsyncComponent, onMounted, ref, watch } from 'vue';
import { ElMessageBox, dayjs } from 'element-plus';
import { Button } from '@/components/ui/button';
import { dayjs } from 'element-plus';
import { storeToRefs } from 'pinia';
import { useI18n } from 'vue-i18n';
import { useRouter } from 'vue-router';
@@ -255,6 +255,7 @@
import {
useAppearanceSettingsStore,
useAuthStore,
useModalStore,
useSearchStore,
useUiStore,
useVRCXUpdaterStore
@@ -269,6 +270,7 @@
const { t, locale } = useI18n();
const router = useRouter();
const modalStore = useModalStore();
const createDefaultNavLayout = () => [
{ type: 'item', key: 'feed' },
@@ -559,12 +561,15 @@
};
const handleCustomNavReset = () => {
ElMessageBox.confirm(t('nav_menu.custom_nav.restore_default_confirm'), {
type: 'warning',
confirmButtonText: t('nav_menu.custom_nav.restore_default'),
cancelButtonText: t('nav_menu.custom_nav.cancel')
})
.then(async () => {
modalStore
.confirm({
description: t('nav_menu.custom_nav.restore_default_confirm'),
title: t('confirm.title'),
confirmText: t('nav_menu.custom_nav.restore_default'),
cancelText: t('nav_menu.custom_nav.cancel')
})
.then(async ({ ok }) => {
if (!ok) return;
const defaults = sanitizeLayout(createDefaultNavLayout());
navLayout.value = defaults;
await saveNavLayout(defaults);

View File

@@ -559,6 +559,7 @@
useFavoriteStore,
useGalleryStore,
useGameStore,
useModalStore,
useUserStore
} from '../../../stores';
import {
@@ -591,6 +592,7 @@
const { deleteVRChatCache } = useGameStore();
const { showFullscreenImageDialog } = useGalleryStore();
const { isDarkMode } = storeToRefs(useAppearanceSettingsStore());
const modalStore = useModalStore();
const { t } = useI18n();
@@ -752,15 +754,13 @@
showFavoriteDialog('avatar', D.id);
break;
default:
ElMessageBox.confirm(`Continue? ${command}`, 'Confirm', {
confirmButtonText: 'Confirm',
cancelButtonText: 'Cancel',
type: 'info'
})
.then((action) => {
if (action !== 'confirm') {
return;
}
modalStore
.confirm({
title: 'Confirm',
description: `Continue? ${command}`
})
.then(({ ok }) => {
if (!ok) return;
switch (command) {
case 'Delete Favorite':
favoriteRequest.deleteFavorite({
@@ -897,8 +897,7 @@
});
break;
}
})
.catch(() => {});
});
break;
}
}

View File

@@ -1146,11 +1146,10 @@
View,
Warning
} from '@element-plus/icons-vue';
import { Card } from '@/components/ui/card';
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select';
import { computed, nextTick, reactive, ref, watch } from 'vue';
import { Button } from '@/components/ui/button';
import { ElMessageBox } from 'element-plus';
import { Card } from '@/components/ui/card';
import { InputGroupField } from '@/components/ui/input-group';
import { RefreshCcw } from 'lucide-vue-next';
import { Spinner } from '@/components/ui/spinner';
@@ -1176,6 +1175,14 @@
userImage,
userStatusClass
} from '../../../shared/utils';
import {
useAppearanceSettingsStore,
useGalleryStore,
useGroupStore,
useLocationStore,
useModalStore,
useUserStore
} from '../../../stores';
import {
DropdownMenu,
DropdownMenuContent,
@@ -1183,13 +1190,6 @@
DropdownMenuSeparator,
DropdownMenuTrigger
} from '../../ui/dropdown-menu';
import {
useAppearanceSettingsStore,
useGalleryStore,
useGroupStore,
useLocationStore,
useUserStore
} from '../../../stores';
import { formatJsonVars, getNextDialogIndex } from '../../../shared/utils/base/ui';
import { groupDialogFilterOptions, groupDialogSortingOptions } from '../../../shared/constants';
import { Badge } from '../../ui/badge';
@@ -1203,6 +1203,8 @@
const { t } = useI18n();
const modalStore = useModalStore();
const { showUserDialog } = useUserStore();
const { currentUser } = storeToRefs(useUserStore());
const { groupDialog, inviteGroupDialog } = storeToRefs(useGroupStore());
@@ -1469,43 +1471,42 @@
});
}
function confirmDeleteGroupPost(post) {
ElMessageBox.confirm('Are you sure you want to delete this post?', 'Confirm', {
confirmButtonText: 'Confirm',
cancelButtonText: 'Cancel',
type: 'info'
})
.then((action) => {
if (action === 'confirm') {
groupRequest
.deleteGroupPost({
groupId: post.groupId,
postId: post.id
})
.then((args) => {
const D = groupDialog.value;
if (D.id !== args.params.groupId) {
return;
}
modalStore
.confirm({
description: 'Are you sure you want to delete this post?',
title: 'Confirm'
})
.then(({ ok }) => {
if (!ok) return;
groupRequest
.deleteGroupPost({
groupId: post.groupId,
postId: post.id
})
.then((args) => {
const D = groupDialog.value;
if (D.id !== args.params.groupId) {
return;
}
const postId = args.params.postId;
// remove existing post
for (const item of D.posts) {
if (item.id === postId) {
removeFromArray(D.posts, item);
break;
}
const postId = args.params.postId;
// remove existing post
for (const item of D.posts) {
if (item.id === postId) {
removeFromArray(D.posts, item);
break;
}
// remove/update announcement
if (postId === D.announcement.id) {
if (D.posts.length > 0) {
D.announcement = D.posts[0];
} else {
D.announcement = {};
}
}
// remove/update announcement
if (postId === D.announcement.id) {
if (D.posts.length > 0) {
D.announcement = D.posts[0];
} else {
D.announcement = {};
}
updateGroupPostSearch();
});
}
}
updateGroupPostSearch();
});
})
.catch(() => {});
}
@@ -1571,46 +1572,44 @@
}
function blockGroup(groupId) {
ElMessageBox.confirm('Are you sure you want to block this group?', 'Confirm', {
confirmButtonText: 'Confirm',
cancelButtonText: 'Cancel',
type: 'info'
})
.then((action) => {
if (action === 'confirm') {
groupRequest
.blockGroup({
groupId
})
.then((args) => {
if (groupDialog.value.visible && groupDialog.value.id === args.params.groupId) {
showGroupDialog(args.params.groupId);
}
});
}
modalStore
.confirm({
description: 'Are you sure you want to block this group?',
title: 'Confirm'
})
.then(({ ok }) => {
if (!ok) return;
groupRequest
.blockGroup({
groupId
})
.then((args) => {
if (groupDialog.value.visible && groupDialog.value.id === args.params.groupId) {
showGroupDialog(args.params.groupId);
}
});
})
.catch(() => {});
}
function unblockGroup(groupId) {
ElMessageBox.confirm('Are you sure you want to unblock this group?', 'Confirm', {
confirmButtonText: 'Confirm',
cancelButtonText: 'Cancel',
type: 'info'
})
.then((action) => {
if (action === 'confirm') {
groupRequest
.unblockGroup({
groupId,
userId: currentUser.value.id
})
.then((args) => {
if (groupDialog.value.visible && groupDialog.value.id === args.params.groupId) {
showGroupDialog(args.params.groupId);
}
});
}
modalStore
.confirm({
description: 'Are you sure you want to unblock this group?',
title: 'Confirm'
})
.then(({ ok }) => {
if (!ok) return;
groupRequest
.unblockGroup({
groupId,
userId: currentUser.value.id
})
.then((args) => {
if (groupDialog.value.visible && groupDialog.value.id === args.params.groupId) {
showGroupDialog(args.params.groupId);
}
});
})
.catch(() => {});
}

View File

@@ -87,12 +87,11 @@
import { computed, ref } from 'vue';
import { Button } from '@/components/ui/button';
import { Check as CheckIcon } from 'lucide-vue-next';
import { ElMessageBox } from 'element-plus';
import { storeToRefs } from 'pinia';
import { toast } from 'vue-sonner';
import { useI18n } from 'vue-i18n';
import { useFriendStore, useGalleryStore, useInviteStore, useUserStore } from '../../../stores';
import { useFriendStore, useGalleryStore, useInviteStore, useModalStore, useUserStore } from '../../../stores';
import { parseLocation, userImage, userStatusClass } from '../../../shared/utils';
import { instanceRequest, notificationRequest } from '../../../api';
import { VirtualCombobox } from '../../ui/virtual-combobox';
@@ -104,6 +103,8 @@
const { currentUser } = storeToRefs(useUserStore());
const { clearInviteImageUpload } = useGalleryStore();
const modalStore = useModalStore();
const { t } = useI18n();
const props = defineProps({
inviteDialog: {
@@ -233,14 +234,15 @@
}
function sendInvite() {
ElMessageBox.confirm('Continue? Invite', 'Confirm', {
confirmButtonText: 'Confirm',
cancelButtonText: 'Cancel',
type: 'info'
})
.then((action) => {
modalStore
.confirm({
description: 'Continue? Invite',
title: 'Confirm'
})
.then(({ ok }) => {
if (!ok) return;
const D = props.inviteDialog;
if (action !== 'confirm' || D.loading === true) {
if (D.loading === true) {
return;
}
D.loading = true;
@@ -275,7 +277,8 @@
}
};
inviteLoop();
})
.catch(() => {});
});
}
</script>
}) .catch(() => {});

View File

@@ -79,14 +79,13 @@
import { computed, nextTick, ref, watch } from 'vue';
import { Button } from '@/components/ui/button';
import { Check as CheckIcon } from 'lucide-vue-next';
import { ElMessageBox } from 'element-plus';
import { storeToRefs } from 'pinia';
import { toast } from 'vue-sonner';
import { useI18n } from 'vue-i18n';
import { hasGroupPermission, userImage, userStatusClass } from '../../shared/utils';
import { useFriendStore, useGroupStore, useModalStore } from '../../stores';
import { groupRequest, userRequest } from '../../api';
import { useFriendStore, useGroupStore } from '../../stores';
import { VirtualCombobox } from '../ui/virtual-combobox';
import { getNextDialogIndex } from '../../shared/utils/base/ui';
@@ -96,6 +95,7 @@
const { currentUserGroups, inviteGroupDialog } = storeToRefs(useGroupStore());
const { applyGroup } = useGroupStore();
const { t } = useI18n();
const modalStore = useModalStore();
watch(
() => inviteGroupDialog.value.visible,
@@ -272,14 +272,15 @@
});
}
function sendGroupInvite() {
ElMessageBox.confirm('Continue? Invite User(s) To Group', 'Confirm', {
confirmButtonText: 'Confirm',
cancelButtonText: 'Cancel',
type: 'info'
})
.then((action) => {
modalStore
.confirm({
description: 'Continue? Invite User(s) To Group',
title: 'Confirm'
})
.then(({ ok }) => {
if (!ok) return;
const D = inviteGroupDialog.value;
if (action !== 'confirm' || D.loading === true) {
if (D.loading === true) {
return;
}
D.loading = true;

View File

@@ -135,7 +135,6 @@
import { Button } from '@/components/ui/button';
import { ButtonGroup } from '@/components/ui/button-group';
import { Copy } from 'lucide-vue-next';
import { ElMessageBox } from 'element-plus';
import { InputGroupField } from '@/components/ui/input-group';
import { MoreHorizontal } from 'lucide-vue-next';
import { Warning } from '@element-plus/icons-vue';
@@ -143,7 +142,14 @@
import { toast } from 'vue-sonner';
import { useI18n } from 'vue-i18n';
import { useFriendStore, useGameStore, useInviteStore, useLaunchStore, useLocationStore } from '../../stores';
import {
useFriendStore,
useGameStore,
useInviteStore,
useLaunchStore,
useLocationStore,
useModalStore
} from '../../stores';
import { checkCanInvite, getLaunchURL, isRealInstance, parseLocation } from '../../shared/utils';
import { instanceRequest, worldRequest } from '../../api';
import { getNextDialogIndex } from '../../shared/utils/base/ui';
@@ -153,6 +159,8 @@
const { t } = useI18n();
const modalStore = useModalStore();
const { friends } = storeToRefs(useFriendStore());
const { lastLocation } = storeToRefs(useLocationStore());
const { launchGame, tryOpenInstanceInVrc } = useLaunchStore();
@@ -245,16 +253,17 @@
}
function handleLaunchGame(location, shortName, desktop) {
if (isGameRunning.value) {
ElMessageBox.confirm(t('dialog.launch.game_running_warning'), t('dialog.launch.header'), {
confirmButtonText: t('dialog.launch.confirm_yes'),
cancelButtonText: t('dialog.launch.confirm_no'),
type: 'warning'
})
.then((action) => {
if (action === 'confirm') {
launchGame(location, shortName, desktop);
isVisible.value = false;
}
modalStore
.confirm({
description: t('dialog.launch.game_running_warning'),
title: t('dialog.launch.header'),
confirmText: t('dialog.launch.confirm_yes'),
cancelText: t('dialog.launch.confirm_no')
})
.then(({ ok }) => {
if (!ok) return;
launchGame(location, shortName, desktop);
isVisible.value = false;
})
.catch(() => {});
return;

View File

@@ -29,7 +29,6 @@
<script setup>
import { computed, nextTick, ref, watch } from 'vue';
import { ElMessageBox } from 'element-plus';
import { InputGroupField } from '@/components/ui/input-group';
import { storeToRefs } from 'pinia';
import { useI18n } from 'vue-i18n';
@@ -41,7 +40,7 @@
removeFromArray,
timeToText
} from '../../../shared/utils';
import { useInstanceStore, useSearchStore, useUiStore, useVrcxStore } from '../../../stores';
import { useInstanceStore, useModalStore, useSearchStore, useUiStore, useVrcxStore } from '../../../stores';
import { DataTableLayout } from '../../ui/data-table';
import { createColumns } from './previousInstancesGroupColumns.jsx';
import { database } from '../../../service/database';
@@ -56,6 +55,8 @@
const previousInstancesGroupDialogIndex = ref(2000);
const loading = ref(false);
const modalStore = useModalStore();
const vrcxStore = useVrcxStore();
const rawRows = ref([]);
const search = ref('');
@@ -169,15 +170,14 @@
}
function deleteGameLogGroupInstancePrompt(row) {
ElMessageBox.confirm('Continue? Delete GameLog Instance', 'Confirm', {
confirmButtonText: 'Confirm',
cancelButtonText: 'Cancel',
type: 'info'
})
.then((action) => {
if (action === 'confirm') {
deleteGameLogGroupInstance(row);
}
modalStore
.confirm({
description: 'Continue? Delete GameLog Instance',
title: 'Confirm'
})
.then(({ ok }) => {
if (!ok) return;
deleteGameLogGroupInstance(row);
})
.catch(() => {});
}

View File

@@ -30,11 +30,18 @@
<script setup>
import { computed, nextTick, ref, watch } from 'vue';
import { ElMessageBox } from 'element-plus';
import { InputGroupField } from '@/components/ui/input-group';
import { storeToRefs } from 'pinia';
import { useI18n } from 'vue-i18n';
import {
useInstanceStore,
useModalStore,
useSearchStore,
useUiStore,
useUserStore,
useVrcxStore
} from '../../../stores';
import {
compareByCreatedAt,
localeIncludes,
@@ -42,7 +49,6 @@
removeFromArray,
timeToText
} from '../../../shared/utils';
import { useInstanceStore, useSearchStore, useUiStore, useUserStore, useVrcxStore } from '../../../stores';
import { DataTableLayout } from '../../ui/data-table';
import { createColumns } from './previousInstancesWorldColumns.jsx';
import { database } from '../../../service/database';
@@ -51,6 +57,8 @@
const { t } = useI18n();
const modalStore = useModalStore();
const props = defineProps({
previousInstancesWorldDialog: {
type: Object,
@@ -164,15 +172,14 @@
}
function deleteGameLogWorldInstancePrompt(row) {
ElMessageBox.confirm('Continue? Delete GameLog Instance', 'Confirm', {
confirmButtonText: 'Confirm',
cancelButtonText: 'Cancel',
type: 'info'
})
.then((action) => {
if (action === 'confirm') {
deleteGameLogWorldInstance(row);
}
modalStore
.confirm({
description: 'Continue? Delete GameLog Instance',
title: 'Confirm'
})
.then(({ ok }) => {
if (!ok) return;
deleteGameLogWorldInstance(row);
})
.catch(() => {});
}

View File

@@ -30,11 +30,18 @@
<script setup>
import { computed, nextTick, ref, watch } from 'vue';
import { ElMessageBox } from 'element-plus';
import { InputGroupField } from '@/components/ui/input-group';
import { storeToRefs } from 'pinia';
import { useI18n } from 'vue-i18n';
import {
useInstanceStore,
useLaunchStore,
useModalStore,
useSearchStore,
useUiStore,
useVrcxStore
} from '../../../stores';
import {
compareByCreatedAt,
localeIncludes,
@@ -42,7 +49,6 @@
removeFromArray,
timeToText
} from '../../../shared/utils';
import { useInstanceStore, useLaunchStore, useSearchStore, useUiStore, useVrcxStore } from '../../../stores';
import { DataTableLayout } from '../../ui/data-table';
import { createColumns } from './previousInstancesUserColumns.jsx';
import { database } from '../../../service/database';
@@ -68,6 +74,8 @@
});
const emit = defineEmits(['update:previous-instances-user-dialog']);
const modalStore = useModalStore();
const loading = ref(false);
const rawRows = ref([]);
const search = ref('');
@@ -189,13 +197,14 @@
}
function deleteGameLogUserInstancePrompt(row) {
ElMessageBox.confirm('Continue? Delete User From GameLog Instance', 'Confirm', {
confirmButtonText: 'Confirm',
cancelButtonText: 'Cancel',
type: 'info'
})
.then((action) => {
if (action === 'confirm') deleteGameLogUserInstance(row);
modalStore
.confirm({
description: 'Continue? Delete User From GameLog Instance',
title: 'Confirm'
})
.then(({ ok }) => {
if (!ok) return;
deleteGameLogUserInstance(row);
})
.catch(() => {});
}

View File

@@ -1318,7 +1318,6 @@
import { Download, LogOut, RefreshCcw } from 'lucide-vue-next';
import { Button } from '@/components/ui/button';
import { Checkbox } from '@/components/ui/checkbox';
import { ElMessageBox } from 'element-plus';
import { Spinner } from '@/components/ui/spinner';
import { storeToRefs } from 'pinia';
import { toast } from 'vue-sonner';
@@ -1358,6 +1357,7 @@
useGroupStore,
useInviteStore,
useLocationStore,
useModalStore,
useModerationStore,
useUiStore,
useUserStore,
@@ -1393,6 +1393,8 @@
const { t } = useI18n();
const modalStore = useModalStore();
const { hideUserNotes, hideUserMemos, isDarkMode } = storeToRefs(useAppearanceSettingsStore());
const { bioLanguage, avatarRemoteDatabase, translationApi, translationApiType } =
storeToRefs(useAdvancedSettingsStore());
@@ -1894,19 +1896,17 @@
? command
: t(`${i18nPreFix}${formattedCommand}`);
ElMessageBox.confirm(
t('confirm.message', {
command: displayCommandText
}),
t('confirm.title'),
{
confirmButtonText: t('confirm.confirm_button'),
cancelButtonText: t('confirm.cancel_button'),
type: 'info'
}
)
.then((action) => {
if (action === 'confirm') {
modalStore
.confirm({
description: t('confirm.message', {
command: displayCommandText
}),
title: t('confirm.title'),
confirmText: t('confirm.confirm_button'),
cancelText: t('confirm.cancel_button')
})
.then(({ ok }) => {
if (ok) {
performUserDialogCommand(command, D.id);
}
})
@@ -2422,22 +2422,21 @@
}
function resetHome() {
ElMessageBox.confirm('Continue? Reset Home', 'Confirm', {
confirmButtonText: 'Confirm',
cancelButtonText: 'Cancel',
type: 'info'
})
.then((action) => {
if (action === 'confirm') {
userRequest
.saveCurrentUser({
homeLocation: ''
})
.then((args) => {
toast.success('Home world has been reset');
return args;
});
}
modalStore
.confirm({
description: 'Continue? Reset Home',
title: 'Confirm'
})
.then(({ ok }) => {
if (!ok) return;
userRequest
.saveCurrentUser({
homeLocation: ''
})
.then((args) => {
toast.success('Home world has been reset');
return args;
});
})
.catch(() => {});
}

View File

@@ -795,6 +795,7 @@
useInstanceStore,
useInviteStore,
useLocationStore,
useModalStore,
useUserStore,
useWorldStore
} from '../../../stores';
@@ -810,6 +811,8 @@
import { Badge } from '../../ui/badge';
import { database } from '../../../service/database.js';
const modalStore = useModalStore();
const NewInstanceDialog = defineAsyncComponent(() => import('../NewInstanceDialog.vue'));
const PreviousInstancesWorldDialog = defineAsyncComponent(
() => import('../PreviousInstancesDialog/PreviousInstancesWorldDialog.vue')
@@ -988,15 +991,13 @@
case 'Unpublish':
case 'Delete Persistent Data':
case 'Delete':
ElMessageBox.confirm(`Continue? ${command}`, 'Confirm', {
confirmButtonText: 'Confirm',
cancelButtonText: 'Cancel',
type: 'info'
})
.then((action) => {
if (action !== 'confirm') {
return;
}
modalStore
.confirm({
description: `Continue? ${command}`,
title: 'Confirm'
})
.then(({ ok }) => {
if (!ok) return;
switch (command) {
case 'Delete Favorite':
favoriteRequest.deleteFavorite({

View File

@@ -0,0 +1,17 @@
<script setup>
import { AlertDialogRoot, useForwardPropsEmits } from 'reka-ui';
const props = defineProps({
open: { type: Boolean, required: false },
defaultOpen: { type: Boolean, required: false }
});
const emits = defineEmits(['update:open']);
const forwarded = useForwardPropsEmits(props, emits);
</script>
<template>
<AlertDialogRoot v-slot="slotProps" data-slot="alert-dialog" v-bind="forwarded">
<slot v-bind="slotProps" />
</AlertDialogRoot>
</template>

View File

@@ -0,0 +1,20 @@
<script setup>
import { AlertDialogAction } from 'reka-ui';
import { buttonVariants } from '@/components/ui/button';
import { cn } from '@/lib/utils';
import { reactiveOmit } from '@vueuse/core';
const props = defineProps({
asChild: { type: Boolean, required: false },
as: { type: null, required: false },
class: { type: null, required: false }
});
const delegatedProps = reactiveOmit(props, 'class');
</script>
<template>
<AlertDialogAction v-bind="delegatedProps" :class="cn(buttonVariants(), props.class)">
<slot />
</AlertDialogAction>
</template>

View File

@@ -0,0 +1,22 @@
<script setup>
import { AlertDialogCancel } from 'reka-ui';
import { buttonVariants } from '@/components/ui/button';
import { cn } from '@/lib/utils';
import { reactiveOmit } from '@vueuse/core';
const props = defineProps({
asChild: { type: Boolean, required: false },
as: { type: null, required: false },
class: { type: null, required: false }
});
const delegatedProps = reactiveOmit(props, 'class');
</script>
<template>
<AlertDialogCancel
v-bind="delegatedProps"
:class="cn(buttonVariants({ variant: 'outline' }), 'mt-2 sm:mt-0', props.class)">
<slot />
</AlertDialogCancel>
</template>

View File

@@ -0,0 +1,48 @@
<script setup>
import { AlertDialogContent, AlertDialogOverlay, AlertDialogPortal, useForwardPropsEmits } from 'reka-ui';
import { cn } from '@/lib/utils';
import { reactiveOmit } from '@vueuse/core';
defineOptions({
inheritAttrs: false
});
const props = defineProps({
forceMount: { type: Boolean, required: false },
disableOutsidePointerEvents: { type: Boolean, required: false },
asChild: { type: Boolean, required: false },
as: { type: null, required: false },
class: { type: null, required: false }
});
const emits = defineEmits([
'escapeKeyDown',
'pointerDownOutside',
'focusOutside',
'interactOutside',
'openAutoFocus',
'closeAutoFocus'
]);
const delegatedProps = reactiveOmit(props, 'class');
const forwarded = useForwardPropsEmits(delegatedProps, emits);
</script>
<template>
<AlertDialogPortal>
<AlertDialogOverlay
data-slot="alert-dialog-overlay"
class="data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 fixed inset-0 z-10000 bg-black/80" />
<AlertDialogContent
data-slot="alert-dialog-content"
v-bind="{ ...$attrs, ...forwarded }"
:class="
cn(
'bg-background data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 fixed top-[50%] left-[50%] z-10000 grid w-full max-w-[calc(100%-2rem)] translate-x-[-50%] translate-y-[-50%] gap-4 rounded-lg border p-6 shadow-lg duration-200 sm:max-w-lg',
props.class
)
">
<slot />
</AlertDialogContent>
</AlertDialogPortal>
</template>

View File

@@ -0,0 +1,22 @@
<script setup>
import { AlertDialogDescription } from 'reka-ui';
import { cn } from '@/lib/utils';
import { reactiveOmit } from '@vueuse/core';
const props = defineProps({
asChild: { type: Boolean, required: false },
as: { type: null, required: false },
class: { type: null, required: false }
});
const delegatedProps = reactiveOmit(props, 'class');
</script>
<template>
<AlertDialogDescription
data-slot="alert-dialog-description"
v-bind="delegatedProps"
:class="cn('text-muted-foreground text-sm', props.class)">
<slot />
</AlertDialogDescription>
</template>

View File

@@ -0,0 +1,15 @@
<script setup>
import { cn } from '@/lib/utils';
const props = defineProps({
class: { type: null, required: false }
});
</script>
<template>
<div
data-slot="alert-dialog-footer"
:class="cn('flex flex-col-reverse gap-2 sm:flex-row sm:justify-end', props.class)">
<slot />
</div>
</template>

View File

@@ -0,0 +1,13 @@
<script setup>
import { cn } from '@/lib/utils';
const props = defineProps({
class: { type: null, required: false }
});
</script>
<template>
<div data-slot="alert-dialog-header" :class="cn('flex flex-col gap-2 text-center sm:text-left', props.class)">
<slot />
</div>
</template>

View File

@@ -0,0 +1,67 @@
<script setup>
import {
AlertDialog,
AlertDialogAction,
AlertDialogCancel,
AlertDialogContent,
AlertDialogDescription,
AlertDialogFooter,
AlertDialogHeader,
AlertDialogTitle
} from '@/components/ui/alert-dialog';
import { storeToRefs } from 'pinia';
import { useModalStore } from '@/stores';
const modalStore = useModalStore();
const { alertOpen, alertMode, alertTitle, alertDescription, alertOkText, alertCancelText, alertDismissible } =
storeToRefs(modalStore);
function onEscapeKeyDown(event) {
if (!alertDismissible.value) {
event.preventDefault();
return;
}
modalStore.handleDismiss();
}
function onPointerDownOutside(event) {
if (!alertDismissible.value) {
event.preventDefault();
return;
}
modalStore.handleDismiss();
}
function onInteractOutside(event) {
if (!alertDismissible.value) {
event.preventDefault();
return;
}
modalStore.handleDismiss();
}
</script>
<template>
<AlertDialog :open="alertOpen" @update:open="modalStore.setAlertOpen">
<AlertDialogContent
@escapeKeyDown="onEscapeKeyDown"
@pointerDownOutside="onPointerDownOutside"
@interactOutside="onInteractOutside">
<AlertDialogHeader>
<AlertDialogTitle>{{ alertTitle }}</AlertDialogTitle>
<AlertDialogDescription>{{ alertDescription }}</AlertDialogDescription>
</AlertDialogHeader>
<AlertDialogFooter>
<AlertDialogCancel v-if="alertMode === 'confirm'" @click="modalStore.handleCancel">
{{ alertCancelText }}
</AlertDialogCancel>
<AlertDialogAction @click="modalStore.handleOk">
{{ alertOkText }}
</AlertDialogAction>
</AlertDialogFooter>
</AlertDialogContent>
</AlertDialog>
</template>

View File

@@ -0,0 +1,22 @@
<script setup>
import { AlertDialogTitle } from 'reka-ui';
import { cn } from '@/lib/utils';
import { reactiveOmit } from '@vueuse/core';
const props = defineProps({
asChild: { type: Boolean, required: false },
as: { type: null, required: false },
class: { type: null, required: false }
});
const delegatedProps = reactiveOmit(props, 'class');
</script>
<template>
<AlertDialogTitle
data-slot="alert-dialog-title"
v-bind="delegatedProps"
:class="cn('text-lg font-semibold', props.class)">
<slot />
</AlertDialogTitle>
</template>

View File

@@ -0,0 +1,14 @@
<script setup>
import { AlertDialogTrigger } from 'reka-ui';
const props = defineProps({
asChild: { type: Boolean, required: false },
as: { type: null, required: false }
});
</script>
<template>
<AlertDialogTrigger data-slot="alert-dialog-trigger" v-bind="props">
<slot />
</AlertDialogTrigger>
</template>

View File

@@ -0,0 +1,9 @@
export { default as AlertDialog } from './AlertDialog.vue';
export { default as AlertDialogAction } from './AlertDialogAction.vue';
export { default as AlertDialogCancel } from './AlertDialogCancel.vue';
export { default as AlertDialogContent } from './AlertDialogContent.vue';
export { default as AlertDialogDescription } from './AlertDialogDescription.vue';
export { default as AlertDialogFooter } from './AlertDialogFooter.vue';
export { default as AlertDialogHeader } from './AlertDialogHeader.vue';
export { default as AlertDialogTitle } from './AlertDialogTitle.vue';
export { default as AlertDialogTrigger } from './AlertDialogTrigger.vue';