-
Notifications
You must be signed in to change notification settings - Fork 80
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* Add a statically mounted popup library to allow launching stackable popups and get user interaction result
- Loading branch information
1 parent
b8df9e8
commit dcb5d84
Showing
6 changed files
with
542 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,189 @@ | ||
<template> | ||
<vue-final-modal | ||
v-model="open" | ||
:click-to-close="options.allowClickToClose ?? true" | ||
:esc-to-close="options.allowEscToClose ?? true" | ||
:prevent-click="options.preventClick ?? false" | ||
:z-index-base="zIndexBase" | ||
classes="modal-container" | ||
content-class="modal-content" | ||
@before-close="onBeforeClose" | ||
> | ||
<v-card> | ||
<v-card-title class="justify-space-between text-h5"> | ||
<span>{{ options.title }}</span> | ||
<v-icon | ||
v-if="options.showCloseButton ?? true" | ||
icon | ||
@click="dismiss()" | ||
> | ||
mdi-close | ||
</v-icon> | ||
</v-card-title> | ||
<v-card-title | ||
v-if="options.icon ?? true" | ||
class="justify-center" | ||
> | ||
<v-icon | ||
color="white" | ||
size="45" | ||
> | ||
{{ icon }} | ||
</v-icon> | ||
</v-card-title> | ||
<v-card-subtitle | ||
class="mt-1" | ||
> | ||
{{ options.text }} | ||
</v-card-subtitle> | ||
<v-card-actions class="justify-center"> | ||
<v-btn | ||
v-if="options.showCancelButton ?? true" | ||
class="ma-2 elevation-2" | ||
color="grey" | ||
text | ||
@click="cancel()" | ||
> | ||
{{ options.cancelButtonText ?? 'Cancel' }} | ||
</v-btn> | ||
<v-btn | ||
v-if="options.showConfirmButton ?? true" | ||
class="ma-2 elevation-2" | ||
color="primary" | ||
text | ||
@click="confirm()" | ||
> | ||
{{ options.confirmButtonText ?? 'Confirm' }} | ||
</v-btn> | ||
</v-card-actions> | ||
</v-card> | ||
</vue-final-modal> | ||
</template> | ||
<script lang="ts"> | ||
import Vue, { PropType } from 'vue' | ||
import { PopupOptions, PopupResult } from '@/types/popups' | ||
export default Vue.extend({ | ||
name: 'Popup', | ||
props: { | ||
identifier: { | ||
type: String, | ||
required: true, | ||
}, | ||
options: { | ||
type: Object as PropType<PopupOptions>, | ||
required: true, | ||
}, | ||
dismissed: { | ||
type: Boolean, | ||
required: true, | ||
}, | ||
}, | ||
data() { | ||
return { | ||
open: true, | ||
resolved: false, | ||
} | ||
}, | ||
computed: { | ||
icon(): string { | ||
switch (this.options.icon) { | ||
case 'info': | ||
return 'mdi-information' | ||
case 'success': | ||
return 'mdi-check-circle' | ||
case 'warning': | ||
return 'mdi-alert-octagon' | ||
case 'error': | ||
return 'mdi-close-circle' | ||
case 'question': | ||
return 'mdi-help-circle' | ||
default: | ||
return '' | ||
} | ||
}, | ||
zIndexBase(): number { | ||
switch (this.options.priority) { | ||
case 'low': | ||
return 10000 | ||
case 'medium': | ||
return 11000 | ||
case 'high': | ||
return 12000 | ||
default: | ||
return 9999 | ||
} | ||
}, | ||
}, | ||
watch: { | ||
dismissed(dismissed: boolean): void { | ||
if (dismissed) { | ||
this.dismiss() | ||
} | ||
}, | ||
}, | ||
methods: { | ||
resolve(result: PopupResult): void { | ||
this.open = false | ||
this.resolved = true | ||
setTimeout(() => { | ||
this.$emit('resolve', result) | ||
}, 300) | ||
}, | ||
confirm(): void { | ||
this.resolve({ | ||
id: this.identifier, | ||
confirmed: true, | ||
canceled: false, | ||
dismissed: false, | ||
}) | ||
}, | ||
cancel(): void { | ||
this.resolve({ | ||
id: this.identifier, | ||
confirmed: false, | ||
canceled: true, | ||
dismissed: false, | ||
}) | ||
}, | ||
dismiss(): void { | ||
this.resolve({ | ||
id: this.identifier, | ||
confirmed: false, | ||
canceled: false, | ||
dismissed: true, | ||
}) | ||
}, | ||
onBeforeClose(): void { | ||
if (!this.resolved) { | ||
this.resolve({ | ||
id: this.identifier, | ||
confirmed: false, | ||
canceled: false, | ||
dismissed: true, | ||
}) | ||
} | ||
}, | ||
}, | ||
}) | ||
</script> | ||
<style scoped> | ||
::v-deep .modal-container { | ||
display: flex; | ||
justify-content: center; | ||
align-items: center; | ||
} | ||
::v-deep .modal-content { | ||
position: relative; | ||
display: flex; | ||
flex-direction: column; | ||
max-height: 90%; | ||
margin: 0 1rem; | ||
padding: 1rem; | ||
border-radius: 0.25rem; | ||
} | ||
</style> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
<template> | ||
<div | ||
style="z-index: 9999;" | ||
> | ||
<Popup | ||
v-for="popup in popups" | ||
:key="popup.id" | ||
:identifier="popup.id" | ||
:options="popup.options" | ||
:dismissed="popup.dismissed" | ||
@resolve="popup.resolve" | ||
/> | ||
</div> | ||
</template> | ||
|
||
<script lang="ts"> | ||
import Vue from 'vue' | ||
import popups from '@/store/popups' | ||
import Popup from './Popup.vue' | ||
export default Vue.extend({ | ||
name: 'PopupsManager', | ||
components: { | ||
Popup, | ||
}, | ||
computed: { | ||
popups() { | ||
return popups.popup_values | ||
}, | ||
}, | ||
}) | ||
</script> | ||
|
||
<style> | ||
</style> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,77 @@ | ||
import { v4 as uuid } from 'uuid' | ||
|
||
import popups from '@/store/popups' | ||
import { PopupOptions, PopupResult } from '@/types/popups' | ||
|
||
class Popup { | ||
/** | ||
* The id of the popup | ||
* @type {string} | ||
*/ | ||
public id: string | ||
|
||
/** | ||
* The options to use when firing this popup | ||
* @type {PopupOptions} | ||
*/ | ||
public options: PopupOptions | ||
|
||
/** | ||
* Popup constructor | ||
* @param {PopupOptions} options Options to use when firing the popup | ||
*/ | ||
constructor(options: PopupOptions, id?: string) { | ||
this.id = id ?? uuid() | ||
this.options = { | ||
...options, | ||
id: this.id, | ||
} | ||
} | ||
|
||
/** | ||
* Fire a popup with the given options | ||
* @param {PopupOptions} options Options to use when firing the popup | ||
* @param {string} id Optional id to use when firing the popup so it can be | ||
* programmatically closed after | ||
* @returns {Promise<PopupResult>} A promise that resolves to the popup result | ||
*/ | ||
public static fire(options: PopupOptions, id?: string): Promise<PopupResult> { | ||
options.id = id ?? options.id ?? uuid() | ||
return popups.fire(options) | ||
} | ||
|
||
/** | ||
* Close a popup with the given id resolving it to a dismissed result | ||
* @param {string} id The id of the popup to close | ||
* @returns {void} | ||
*/ | ||
public static close(id: string): void { | ||
popups.close(id) | ||
} | ||
|
||
/** | ||
* Close all popups resolving them to dismissed results | ||
* @returns {void} | ||
*/ | ||
public static closeAll(): void { | ||
popups.closeAll() | ||
} | ||
|
||
/** | ||
* Close current popup resolving it to a dismissed result | ||
* @returns {void} | ||
*/ | ||
public close(): void { | ||
popups.close(this.id) | ||
} | ||
|
||
/** | ||
* Fire current popup | ||
* @returns {Promise<PopupResult>} A promise that resolves to the popup result | ||
*/ | ||
public fire(): Promise<PopupResult> { | ||
return popups.fire(this.options) | ||
} | ||
} | ||
|
||
export default Popup |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,67 @@ | ||
import Vue from 'vue' | ||
import { | ||
Action, getModule, Module, Mutation, VuexModule, | ||
} from 'vuex-module-decorators' | ||
|
||
import store from '@/store' | ||
import { ActivePopup, PopupOptions, PopupResult } from '@/types/popups' | ||
|
||
@Module({ | ||
dynamic: true, | ||
store, | ||
name: 'popups', | ||
}) | ||
class PopupsStore extends VuexModule { | ||
popups: Record<string, ActivePopup> = {} | ||
|
||
@Mutation | ||
addPopup(popup: ActivePopup): void { | ||
Vue.set(this.popups, popup.id, popup) | ||
} | ||
|
||
@Mutation | ||
removePopup(id: string): void { | ||
Vue.delete(this.popups, id) | ||
} | ||
|
||
@Mutation | ||
close(id: string): void { | ||
if (this.popups[id]) { | ||
this.popups[id].dismissed = true | ||
this.popups = { ...this.popups } | ||
} | ||
} | ||
|
||
@Mutation | ||
closeAll(): void { | ||
for (const id in this.popups) { | ||
this.popups[id].dismissed = true | ||
} | ||
|
||
this.popups = { ...this.popups } | ||
} | ||
|
||
@Action | ||
fire(options: PopupOptions): Promise<PopupResult> { | ||
return new Promise<PopupResult>((resolve) => { | ||
this.addPopup({ | ||
id: options.id as string, | ||
dismissed: false, | ||
options, | ||
resolve: (result: PopupResult) => { | ||
this.removePopup(result.id) | ||
resolve(result) | ||
}, | ||
}) | ||
}) | ||
} | ||
|
||
get popup_values(): ActivePopup[] { | ||
return Object.values(this.popups) | ||
} | ||
} | ||
|
||
export { PopupsStore } | ||
|
||
const popups: PopupsStore = getModule(PopupsStore) | ||
export default popups |
Oops, something went wrong.