Got basic dialog working, no morph animation yet
This commit is contained in:
13
app/imports/ui/dialogStack/DialogBase.vue
Normal file
13
app/imports/ui/dialogStack/DialogBase.vue
Normal file
@@ -0,0 +1,13 @@
|
||||
<template>
|
||||
<v-card>
|
||||
<v-toolbar color="primary" dark>
|
||||
<slot name="toolbar"></slot>
|
||||
</v-toolbar>
|
||||
<v-layout>
|
||||
<slot></slot>
|
||||
</v-layout>
|
||||
<v-card-actions>
|
||||
<slot name="actions"></slot>
|
||||
</v-card-actions>
|
||||
</v-card>
|
||||
</template>
|
||||
98
app/imports/ui/dialogStack/DialogStack.vue
Normal file
98
app/imports/ui/dialogStack/DialogStack.vue
Normal file
@@ -0,0 +1,98 @@
|
||||
<template>
|
||||
<v-layout class="dialog-stack" align-center justify-center>
|
||||
<div
|
||||
class="backdrop"
|
||||
@click="backdropClicked"
|
||||
:class="dialogs.length ? '' : 'hidden' "
|
||||
></div>
|
||||
<transition-group name="dialog-list" class="dialog-sizer" tag="div">
|
||||
<div
|
||||
v-for="(dialog, index) in dialogs"
|
||||
:key="dialog._id"
|
||||
class="dialog"
|
||||
:style="getDialogStyle(index)"
|
||||
>
|
||||
<component :is="dialog.component" :data="dialog.data"></component>
|
||||
</div>
|
||||
</transition-group>
|
||||
</v-layout>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import "/imports/ui/dialogStack/dialogStackWindowEvents.js";
|
||||
import store from "/imports/ui/vuexStore.js";
|
||||
import anime from "animejs";
|
||||
|
||||
const offset = 16;
|
||||
|
||||
export default {
|
||||
computed: {
|
||||
dialogs(){
|
||||
return store.state.dialogStack.dialogs;
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
popDialogStack(){
|
||||
store.dispatch("popDialogStack");
|
||||
},
|
||||
backdropClicked(event){
|
||||
if (event.target === event.currentTarget) this.popDialogStack();
|
||||
},
|
||||
getDialogStyle(index){
|
||||
const length = store.state.dialogStack.dialogs.length;
|
||||
if (index >= length) return;
|
||||
const num = length - 1;
|
||||
const left = (num - index) * -offset;
|
||||
const top = (num - index) * -offset;
|
||||
return `left:${left}px; top:${top}px;`;
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.dialog-stack {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
pointer-events: none;
|
||||
}
|
||||
.dialog-sizer {
|
||||
position: relative;
|
||||
height: 500px;
|
||||
width: 500px;
|
||||
z-index: 5;
|
||||
flex: initial;
|
||||
}
|
||||
.backdrop {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background-color: rgba(0, 0, 0, 0.4);
|
||||
z-index: 4;
|
||||
pointer-events: initial;
|
||||
}
|
||||
.backdrop.hidden {
|
||||
display: none
|
||||
}
|
||||
.dialog-list-move {
|
||||
transition: transform 400ms;
|
||||
}
|
||||
.dialog-list-leave-active {
|
||||
|
||||
}
|
||||
.dialog {
|
||||
position: absolute;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
pointer-events: initial;
|
||||
}
|
||||
.dialog > * {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
}
|
||||
</style>
|
||||
28
app/imports/ui/dialogStack/TestDialog.vue
Normal file
28
app/imports/ui/dialogStack/TestDialog.vue
Normal file
@@ -0,0 +1,28 @@
|
||||
<template>
|
||||
<dialog-base>
|
||||
<div slot="toolbar">
|
||||
Test Dialog
|
||||
</div>
|
||||
<div>
|
||||
<v-btn @click="openDialog">Open Dialog</v-btn>
|
||||
</div>
|
||||
</dialog-base>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import store from "/imports/ui/vuexStore.js";
|
||||
import DialogBase from "/imports/ui/dialogStack/DialogBase.vue";
|
||||
const component = {
|
||||
methods: {
|
||||
openDialog(event){
|
||||
store.commit("pushDialogStack", {
|
||||
component,
|
||||
});
|
||||
}
|
||||
},
|
||||
components: {
|
||||
DialogBase,
|
||||
},
|
||||
};
|
||||
export default component;
|
||||
</script>
|
||||
87
app/imports/ui/dialogStack/dialogStackStore.js
Normal file
87
app/imports/ui/dialogStack/dialogStackStore.js
Normal file
@@ -0,0 +1,87 @@
|
||||
import store from "/imports/ui/vuexStore.js";
|
||||
|
||||
const offset = 16;
|
||||
const duration = 400;
|
||||
let dialogStack = {};
|
||||
dialogStack.dialogs = [];
|
||||
|
||||
const dialogStackStore = {
|
||||
state: {
|
||||
dialogs: [],
|
||||
},
|
||||
mutations: {
|
||||
pushDialogStack(state, {component, data, element, returnElement, callback}){
|
||||
// Generate a new _id so that Vue knows how to shuffle the array
|
||||
const _id = Random.id();
|
||||
state.dialogs.push({
|
||||
_id,
|
||||
component,
|
||||
data,
|
||||
element,
|
||||
returnElement,
|
||||
callback,
|
||||
});
|
||||
updateHistory();
|
||||
},
|
||||
popDialogStackMutation (state, result){
|
||||
const dialog = state.dialogs.pop();
|
||||
updateHistory();
|
||||
if (!dialog) return;
|
||||
dialog.callback && dialog.callback(result);
|
||||
},
|
||||
},
|
||||
actions: {
|
||||
popDialogStack(context, result){
|
||||
if (history && history.state && history.state.openDialogs){
|
||||
history.back();
|
||||
} else {
|
||||
context.commit("popDialogStackMutation", result)
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export default dialogStackStore;
|
||||
|
||||
const updateHistory = function(){
|
||||
// history should looks like: [{openDialogs: 0}, {openDialogs: n}] where
|
||||
// n is the number of open dialogs
|
||||
|
||||
// If we can't access the history object, give up
|
||||
if (!history) return;
|
||||
// Make sure that there is a state tracking open dialogs
|
||||
// replace the state without bashing it in the process
|
||||
if (!history.state || !_.isFinite(history.state.openDialogs)){
|
||||
let newState = _.clone(history.state) || {};
|
||||
newState.openDialogs = 0;
|
||||
history.replaceState(newState, "");
|
||||
}
|
||||
|
||||
const numDialogs = dialogStackStore.state.dialogs.length;
|
||||
const stateDialogs = history.state.openDialogs;
|
||||
|
||||
// If the number of dialogs and state dialogs are equal, we don't need to do
|
||||
// anything
|
||||
if (numDialogs === stateDialogs) return;
|
||||
|
||||
if (stateDialogs > 0){
|
||||
// On a dialog count
|
||||
if (numDialogs === 0){
|
||||
// but shouldn't be
|
||||
history.back();
|
||||
} else {
|
||||
// but should replace with correct count
|
||||
let newState = _.clone(history.state) || {};
|
||||
newState.openDialogs = dialogStackStore.state.dialogs.length;
|
||||
history.replaceState(newState, "");
|
||||
}
|
||||
} else if (numDialogs > 0 && stateDialogs === 0){
|
||||
// On the zero state, push a dialog count
|
||||
history.pushState({openDialogs: numDialogs}, "");
|
||||
} else {
|
||||
console.warn(
|
||||
"History could not be updated correctly, unexpected case",
|
||||
{stateDialogs, numDialogs},
|
||||
)
|
||||
}
|
||||
};
|
||||
11
app/imports/ui/dialogStack/dialogStackWindowEvents.js
Normal file
11
app/imports/ui/dialogStack/dialogStackWindowEvents.js
Normal file
@@ -0,0 +1,11 @@
|
||||
import store from "/imports/ui/vuexStore.js";
|
||||
|
||||
if (window){
|
||||
window.onpopstate = function(event){
|
||||
let state = event.state;
|
||||
let numDialogs = store.state.dialogStack.dialogs.length;
|
||||
if (_.isFinite(state.openDialogs) && numDialogs > state.openDialogs){
|
||||
store.commit("popDialogStackMutation");
|
||||
}
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user