Skip to content

Commit 8f7f29a

Browse files
committed
add confirm dialog
1 parent a1702a1 commit 8f7f29a

File tree

9 files changed

+256
-1
lines changed

9 files changed

+256
-1
lines changed

playground/app.vue

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,24 @@
11
<script setup>
2+
const activeTab = ref('snackStack')
23
</script>
34

45
<template>
56
<v-app>
6-
<SnackStackPlayground />
7+
<v-tabs v-model="activeTab" color="primary">
8+
<v-tab value="snackStack">
9+
Snack Stack
10+
</v-tab>
11+
<v-tab value="confirmDialog">
12+
Confirm Dialog
13+
</v-tab>
14+
</v-tabs>
15+
<v-tabs-window v-model="activeTab">
16+
<v-tabs-window-item value="snackStack">
17+
<SnackStackPlayground />
18+
</v-tabs-window-item>
19+
<v-tabs-window-item value="confirmDialog">
20+
<ConfirmPlayground />
21+
</v-tabs-window-item>
22+
</v-tabs-window>
723
</v-app>
824
</template>
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
<script setup lang="ts">
2+
const confirm = useConfirmDialog()
3+
const confirmed = ref<boolean>()
4+
const options = reactive<ConfirmDialogOptions>({
5+
title: 'title',
6+
message: 'message',
7+
cancelText: 'cancelText',
8+
confirmText: 'confirmText',
9+
color: 'primary',
10+
icon: 'mdi-alien',
11+
persistent: true,
12+
})
13+
14+
async function showCustomDialog() {
15+
// pass undefined instead of empty string
16+
Object.keys(options).forEach((key) => {
17+
// @ts-expect-error works
18+
if (typeof options[key] === 'string' && options[key] === '') {
19+
// @ts-expect-error works
20+
options[key] = undefined
21+
}
22+
})
23+
24+
confirmed.value = await confirm.showDialog(options)
25+
}
26+
</script>
27+
28+
<template>
29+
<v-container>
30+
<div class="text-h4">
31+
Confirm Dialog
32+
</div>
33+
<v-card class="mb-2" :color="confirmed !== undefined ? confirmed ? 'success' : 'error' : undefined">
34+
<v-card-text>Is Confirmed? <span class="font-weight-bold">{{ confirmed }}</span></v-card-text>
35+
</v-card>
36+
37+
<v-card>
38+
<v-card-text>
39+
<v-row>
40+
<v-col cols="12">
41+
<v-text-field v-model="options.title" label="title" hide-details />
42+
</v-col>
43+
<v-col cols="12">
44+
<v-text-field v-model="options.message" label="message" hide-details />
45+
</v-col>
46+
<v-col cols="12" sm="6">
47+
<v-text-field v-model="options.cancelText" label="cancelText" hide-details />
48+
</v-col>
49+
<v-col cols="12" sm="6">
50+
<v-text-field v-model="options.confirmText" label="confirmText" hide-details />
51+
</v-col>
52+
<v-col cols="12" sm="6">
53+
<v-text-field v-model="options.color" label="color" hide-details />
54+
</v-col>
55+
<v-col cols="12" sm="6">
56+
<v-text-field v-model="options.icon" label="icon" hide-details />
57+
</v-col>
58+
<v-col cols="12">
59+
<v-checkbox v-model="options.persistent" label="persistent" hide-details />
60+
</v-col>
61+
</v-row>
62+
</v-card-text>
63+
<v-card-actions class="justify-end">
64+
<v-btn color="primary" @click="showCustomDialog">
65+
Show Confirm Dialog
66+
</v-btn>
67+
</v-card-actions>
68+
</v-card>
69+
</v-container>
70+
</template>

src/module.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { defineNuxtModule } from '@nuxt/kit'
2+
import setupConfirmDialog from './runtime/confirm/setup'
23
import setupSnackStack from './runtime/snack/setup'
34
// Module options TypeScript interface definition
45
export interface SimpleMessagesOptions { }
@@ -12,5 +13,6 @@ export default defineNuxtModule<SimpleMessagesOptions>({
1213
defaults: {},
1314
setup(_options, _nuxt) {
1415
setupSnackStack()
16+
setupConfirmDialog()
1517
},
1618
})
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
<script lang="ts" setup>
2+
import { ref, watch } from 'vue'
3+
4+
const dialog = ref(false)
5+
const options = ref<ConfirmDialogOptions>()
6+
let resolve: any = null
7+
8+
function open(_options: ConfirmDialogOptions) {
9+
dialog.value = true
10+
options.value = _options
11+
return new Promise((_resolve) => {
12+
resolve = _resolve
13+
})
14+
}
15+
defineExpose({ open })
16+
17+
function resetPromise() {
18+
resolve = null
19+
setTimeout(() => {
20+
options.value = undefined
21+
}, 400)
22+
}
23+
24+
watch(dialog, () => {
25+
if (!dialog.value && resolve) {
26+
resolve(false)
27+
resetPromise()
28+
}
29+
})
30+
31+
function confirm() {
32+
resolve(true)
33+
resetPromise()
34+
dialog.value = false
35+
}
36+
37+
function cancel() {
38+
dialog.value = false
39+
}
40+
</script>
41+
42+
<template>
43+
<v-dialog v-if="options" v-model="dialog" width="600" :persistent="options.persistent" style="z-index: 99999" @confirm="confirm">
44+
<v-card>
45+
<v-card-text class="d-flex w-100">
46+
<v-avatar v-if="options.icon" class="align-self-center mr-6" :color="options.color" size="96">
47+
<v-icon :icon="options.icon" size="80" />
48+
</v-avatar>
49+
<div class="w-100 d-flex justify-space-between">
50+
<div class="flex-grow-1">
51+
<div class="text-h5">
52+
{{ options.title }}
53+
</div>
54+
<div>{{ options.message }}</div>
55+
</div>
56+
<v-btn class="mr-n5 mt-n3" icon="$close" variant="text" @click="cancel" />
57+
</div>
58+
</v-card-text>
59+
<v-card-actions>
60+
<v-btn @click="cancel">
61+
{{ options.cancelText ?? 'Cancel' }}
62+
</v-btn>
63+
<v-btn :color="options.color" variant="tonal" min-width="100" @click="confirm">
64+
{{ options.confirmText ?? 'OK' }}
65+
</v-btn>
66+
</v-card-actions>
67+
</v-card>
68+
</v-dialog>
69+
</template>
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
<script setup lang="ts">
2+
import { useState } from '#app'
3+
import { onMounted, ref } from 'vue'
4+
import ConfirmDialogAsync from './ConfirmDialogAsync.vue'
5+
6+
const confirm = ref()
7+
8+
onMounted(() => {
9+
useState('confirm-dialog-ref', () => confirm)
10+
})
11+
</script>
12+
13+
<template>
14+
<ConfirmDialogAsync ref="confirm" />
15+
</template>

src/runtime/confirm/composable.ts

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
import { useState } from '#app'
2+
3+
export function useConfirmDialog() {
4+
function showDialog(options: ConfirmDialogOptions | string): Promise<boolean> {
5+
return useState<any>('confirm-dialog-ref').value?.open(parseOptions(options))
6+
}
7+
8+
const showSuccessDialog: typeof showDialog = options =>
9+
showDialog(setOptions(parseOptions(options), '$success', 'success'))
10+
11+
const showErrorDialog: typeof showDialog = options =>
12+
showDialog(setOptions(parseOptions(options), '$error', 'error'))
13+
14+
const showInfoDialog: typeof showDialog = options =>
15+
showDialog(setOptions(parseOptions(options), '$info', 'info'))
16+
17+
const showWarningDialog: typeof showDialog = options =>
18+
showDialog(setOptions(parseOptions(options), '$warning', 'warning'))
19+
20+
return {
21+
showDialog,
22+
showSuccessDialog,
23+
showErrorDialog,
24+
showWarningDialog,
25+
showInfoDialog,
26+
}
27+
}
28+
29+
function parseOptions(options: ConfirmDialogOptions | string): ConfirmDialogOptions {
30+
if (typeof options === 'string') {
31+
return { title: options }
32+
}
33+
return options
34+
}
35+
36+
function setOptions(options: ConfirmDialogOptions, icon: string, color: string) {
37+
return {
38+
icon,
39+
...options,
40+
color,
41+
}
42+
}

src/runtime/confirm/plugin.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import { defineNuxtPlugin, useNuxtApp } from '#app'
2+
import { createVNode, render } from 'vue'
3+
import ConfirmDialog from './components/ConfirmDialogWrapper.vue'
4+
5+
export default defineNuxtPlugin({
6+
hooks: {
7+
'app:mounted': () => {
8+
// create container div and add it to the body
9+
const el = document.createElement('div')
10+
el.setAttribute('class', 'simpleMessages--confirmDialog')
11+
document.body.appendChild(el)
12+
13+
// mount SnackStack component to the created div
14+
const app = useNuxtApp()
15+
const vNode = createVNode(ConfirmDialog)
16+
vNode.appContext = app.vueApp._context
17+
render(vNode, el)
18+
},
19+
},
20+
})

src/runtime/confirm/setup.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import { addImports, addPlugin, createResolver } from '@nuxt/kit'
2+
3+
export default function () {
4+
const resolver = createResolver(import.meta.url)
5+
6+
addImports({
7+
name: 'useConfirmDialog',
8+
from: resolver.resolve('composable'),
9+
})
10+
// Do not add the extension since the `.ts` will be transpiled to `.mjs` after `npm run prepack`
11+
addPlugin(resolver.resolve('plugin'))
12+
}

src/runtime/confirm/types.d.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
interface ConfirmDialogOptions {
2+
title: string
3+
message?: string
4+
icon?: string
5+
color?: string
6+
cancelText?: string
7+
confirmText?: string
8+
persistent?: boolean
9+
}

0 commit comments

Comments
 (0)