Skip to content

Instantly share code, notes, and snippets.

@Neophen
Created June 11, 2021 05:07
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save Neophen/877f8662cfe46f39cfb6f6393a6fff4a to your computer and use it in GitHub Desktop.
Save Neophen/877f8662cfe46f39cfb6f6393a6fff4a to your computer and use it in GitHub Desktop.
Vue.js Snippets - useDialog
<template>
<div class="flex items-center justify-center bg-gray-100 h-screen">
<DialogsDemo />
</div>
<Dialogs :state="dialogState" /> <!-- This is important -->
</template>
<script>
import { provideDialogs } from "./composables/useDialog.js"; // This is important
import DialogsDemo from "./components/Dialogs/DialogsDemo.vue";
import Dialogs from "./components/Dialogs/Dialogs.vue";
export default {
components: {
DialogsDemo,
Dialogs,
},
setup() {
const dialogState = provideDialogs(); // This is important
return {
dialogState, // This is important
};
},
};
</script>
<template>
<div
v-if="dialogIsShowing"
class="
fixed
inset-0
h-screen
w-screen
z-10
flex
justify-center
items-center
bg-black bg-opacity-50
"
@click="onResolve(false)"
>
<div @click.stop>
<div class="p-8 bg-white rounded shadow-lg w-96">
<h1 class="text-4xl">{{ dialog.titleText }}</h1>
<p class="text-gray-500 mt-4">{{ dialog.messageText }}</p>
<div class="flex items-center justify-between mt-8">
<button
@click="onResolve(false)"
:disabled="dialog.isLoading"
type="button"
class="px-4 py-2 bg-white"
>
{{ dialog.cancelText }}
</button>
<button
@click="onResolve(true)"
:disabled="dialog.isLoading"
type="button"
class="px-4 py-2 bg-indigo-500 text-white"
>
{{ dialog.isLoading ? "Loading..." : dialog.confirmText }}
</button>
</div>
</div>
</div>
</div>
</template>
<script>
export default {
name: "Dialogs",
props: {
state: { // This is important
type: Object,
required: true,
},
},
setup(props) {
// This component can be whatever you want!
return props.state; // This is important
},
};
</script>
<template>
<div
class="
rounded-xl
shadow-md
bg-white
flex flex-col
items-center
w-80
p-8
space-y-4
"
>
<h1 class="text-4xl font-bold">useDialog</h1>
<p>Result: {{ result }}</p>
<button
@click="onShow"
type="button"
class="
rounded
px-4
py-2
text-white
font-bold
bg-blue-500
hover:bg-blue-600
shadow
"
>
await
</button>
</div>
</template>
<script>
import { ref } from "vue";
import { Dialog, useDialogs } from "../../composables/useDialog";
export default {
name: "DialogsDemo",
setup() {
const sleep = (milliseconds) => {
return new Promise((resolve) => setTimeout(resolve, milliseconds));
};
const result = ref(null);
const showDialog = useDialogs();
const sayHelloDialog = Dialog.info()
.title("Hello")
.message("Is this not awesome")
.confirm("Well hello to you to!")
.cancel("Speak to the hand")
.onConfirm(async () => {
await sleep(2000);
result.value = "Confirmed";
})
.onCancel(() => {
result.value = "Cancelled";
});
const onShow = () => {
showDialog(sayHelloDialog);
};
return {
result,
onShow,
};
},
};
</script>
import { inject, provide, reactive, toRefs } from "vue";
/**
* @typedef DialogsState
* @type {object}
* @property {Dialog} dialog - the current dialog
* @property {bool} dialogIsShowing - is the dialog showing
*/
const DIALOG_SYMBOL = Symbol("DIALOG_SYMBOL");
export const provideDialogs = () => {
/**
* @type {DialogsState}
*/
const state = reactive({
dialog: null,
dialogIsShowing: false,
});
/**
* @param {Dialog} dialog
*/
const showDialog = (dialog) => {
state.dialog = dialog.__onClose(() => {
state.dialogIsShowing = false;
});
state.dialogIsShowing = true;
};
const onResolve = (result) => {
state.dialog.onResolve(result);
};
provide(DIALOG_SYMBOL, showDialog);
return { ...toRefs(state), onResolve };
};
export const useDialogs = () => inject(DIALOG_SYMBOL);
// Make your own types, to use with your components
const DIALOG_TYPE = {
DANGER: "DANGER",
INFO: "INFO",
};
export class Dialog {
__titleText = "";
__messageText = null;
__cancelText = "Cancel";
__confirmText = "Confirm";
__isLoading = false;
__onConfirmCallback = () => {};
__onCancelCallback = () => {};
__onCloseCallback = () => {};
static danger(title = "", message = "") {
return new Dialog(DIALOG_TYPE.DANGER).title(title).message(message);
}
static info(title = "", message = "") {
return new Dialog(DIALOG_TYPE.INFO).title(title).message(message);
}
/**
* @param {DIALOG_TYPE} type
*/
constructor(type) {
this.__type = type;
}
/**
* @return {string}
*/
get type() {
return this.__type;
}
/**
* @return {boolean}
*/
get isLoading() {
return this.__isLoading;
}
/**
* @param {string} text
* @returns Dialog
*/
title(text) {
this.__titleText = text;
return this;
}
/**
* @return {string}
*/
get titleText() {
return this.__titleText;
}
/**
* @param {string} text
*/
message(text) {
this.__messageText = text;
return this;
}
/**
* @return {string}
*/
get messageText() {
return this.__messageText;
}
/**
* @param {string} text
*/
cancel(text) {
this.__cancelText = text;
return this;
}
/**
* @return {string}
*/
get cancelText() {
return this.__cancelText;
}
/**
* @param {string} text
*/
confirm(text) {
this.__confirmText = text;
return this;
}
/**
* @return {string}
*/
get confirmText() {
return this.__confirmText;
}
/**
* @param {Function} callback
*/
onConfirm(callback) {
this.__onConfirmCallback = callback;
return this;
}
/**
* @param {Function} callback
*/
onCancel(callback) {
this.__onCancelCallback = callback;
return this;
}
/**
* @param {Function} callback
*/
__onClose(callback) {
this.__onCloseCallback = callback;
return this;
}
async onResolve(result) {
if (result === false) {
try {
this.__isLoading = true;
await this.__onCancelCallback(result);
} catch (error) {
throw error;
} finally {
this.__isLoading = false;
this.__onCloseCallback();
}
return;
}
try {
this.__isLoading = true;
await this.__onConfirmCallback(result);
} catch (error) {
throw error;
} finally {
this.__isLoading = false;
this.__onCloseCallback();
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment