Dialog stack animations complete
This commit is contained in:
@@ -4,7 +4,8 @@
|
|||||||
Test Dialog
|
Test Dialog
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<v-btn @click="openDialog(_uid + 'btn')" :id="_uid + 'btn'">Open Dialog</v-btn>
|
<v-btn @click="openDialog(_uid + 'btn')" :id="_uid + 'btn'">Open Dialog</v-btn>
|
||||||
|
<v-btn fab @click="openDialog(_uid + 'fab')" :id="_uid + 'fab'" color="green">Open Dialog</v-btn>
|
||||||
</div>
|
</div>
|
||||||
</dialog-base>
|
</dialog-base>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -1,20 +1,25 @@
|
|||||||
<template>
|
<template>
|
||||||
<v-card>
|
<v-layout column style="height: 100%;">
|
||||||
<v-layout column style="height: 100%;">
|
<v-toolbar color="primary" dark class="base-dialog-toolbar" :flat="!offsetTop">
|
||||||
<v-toolbar color="primary" dark class="base-dialog-toolbar" :flat="!offsetTop">
|
<v-btn icon flat @click="close">
|
||||||
<slot name="toolbar"></slot>
|
<v-icon>
|
||||||
</v-toolbar>
|
arrow_back
|
||||||
<div id="base-dialog-body" v-scroll:#base-dialog-body="onScroll">
|
</v-icon>
|
||||||
<slot></slot>
|
</v-btn>
|
||||||
</div>
|
<slot name="toolbar"></slot>
|
||||||
<v-card-actions>
|
</v-toolbar>
|
||||||
<slot name="actions"></slot>
|
<v-card-text id="base-dialog-body" v-scroll:#base-dialog-body="onScroll">
|
||||||
</v-card-actions>
|
<slot></slot>
|
||||||
</v-layout>
|
</v-card-text>
|
||||||
</v-card>
|
<v-card-actions>
|
||||||
|
<slot name="actions"></slot>
|
||||||
|
</v-card-actions>
|
||||||
|
</v-layout>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
import store from "/imports/ui/vuexStore.js";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
data(){ return {
|
data(){ return {
|
||||||
offsetTop: 0,
|
offsetTop: 0,
|
||||||
@@ -23,6 +28,9 @@
|
|||||||
onScroll(e){
|
onScroll(e){
|
||||||
this.offsetTop = e.target.scrollTop
|
this.offsetTop = e.target.scrollTop
|
||||||
},
|
},
|
||||||
|
close(){
|
||||||
|
store.dispatch("popDialogStack");
|
||||||
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
<template lang="html">
|
<template lang="html">
|
||||||
<v-card-text>
|
<v-card-text>
|
||||||
<v-layout align-center justify-center>
|
<v-layout align-center justify-center>
|
||||||
<v-btn @click="openDialog(_uid + 'btn')" :id="_uid + 'btn'"/>
|
<v-btn @click="openDialog(_uid + 'btn')" :id="_uid + 'btn'">
|
||||||
|
Open Dialog
|
||||||
|
</v-btn>
|
||||||
</v-layout>
|
</v-layout>
|
||||||
</v-card-text>
|
</v-card-text>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -1,10 +1,12 @@
|
|||||||
<template>
|
<template>
|
||||||
<v-layout class="dialog-stack" align-center justify-center>
|
<v-layout class="dialog-stack" align-center justify-center>
|
||||||
<div
|
<transition name="backdrop-fade">
|
||||||
class="backdrop"
|
<div
|
||||||
@click="backdropClicked"
|
class="backdrop"
|
||||||
:class="dialogs.length ? '' : 'hidden' "
|
@click="backdropClicked"
|
||||||
></div>
|
v-if="dialogs.length"
|
||||||
|
></div>
|
||||||
|
</transition>
|
||||||
<transition-group
|
<transition-group
|
||||||
name="dialog-list"
|
name="dialog-list"
|
||||||
class="dialog-sizer"
|
class="dialog-sizer"
|
||||||
@@ -13,16 +15,17 @@
|
|||||||
@leave="leave"
|
@leave="leave"
|
||||||
>
|
>
|
||||||
<div class="sibling" key="sibling"/>
|
<div class="sibling" key="sibling"/>
|
||||||
<div
|
<v-card
|
||||||
v-for="(dialog, index) in dialogs"
|
v-for="(dialog, index) in dialogs"
|
||||||
:key="dialog._id"
|
:key="dialog._id"
|
||||||
class="dialog"
|
class="dialog"
|
||||||
:data-element-id="dialog.elementId"
|
:data-element-id="dialog.elementId"
|
||||||
:data-index="index"
|
:data-index="index"
|
||||||
:style="getDialogStyle(index)"
|
:style="getDialogStyle(index)"
|
||||||
|
:elevation="6"
|
||||||
>
|
>
|
||||||
<component :is="dialog.component" :data="dialog.data" @pop="popDialogStack($event)" class="dialog-component"></component>
|
<component :is="dialog.component" :data="dialog.data" @pop="popDialogStack($event)" class="dialog-component"></component>
|
||||||
</div>
|
</v-card>
|
||||||
</transition-group>
|
</transition-group>
|
||||||
</v-layout>
|
</v-layout>
|
||||||
</template>
|
</template>
|
||||||
@@ -35,7 +38,7 @@
|
|||||||
import Vue from "vue";
|
import Vue from "vue";
|
||||||
|
|
||||||
const OFFSET = 16;
|
const OFFSET = 16;
|
||||||
const MOCK_DURATION = 8000; // Keep in sync with css transition of .dialog
|
const MOCK_DURATION = 400; // Keep in sync with css transition of .dialog
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
computed: {
|
computed: {
|
||||||
@@ -64,7 +67,7 @@
|
|||||||
// Get the original styles so we can repair them later
|
// Get the original styles so we can repair them later
|
||||||
let originalStyle = {
|
let originalStyle = {
|
||||||
transform: target.style.transform,
|
transform: target.style.transform,
|
||||||
background: target.style.background,
|
backgroundColor: target.style.backgroundColor,
|
||||||
borderRadius: target.style.borderRadius,
|
borderRadius: target.style.borderRadius,
|
||||||
transition: target.style.transition,
|
transition: target.style.transition,
|
||||||
boxShadow: target.style.boxShadow,
|
boxShadow: target.style.boxShadow,
|
||||||
@@ -73,21 +76,21 @@
|
|||||||
|
|
||||||
// hide the source
|
// hide the source
|
||||||
source.style.transition = "none";
|
source.style.transition = "none";
|
||||||
source.style.visibility = "hidden";
|
source.style.opacity = "0";
|
||||||
|
|
||||||
// Instantly mock the source
|
// Instantly mock the source
|
||||||
target.style.transition = 'none';
|
target.style.transition = 'none';
|
||||||
mockElement({source, target});
|
mockElement({source, target});
|
||||||
|
|
||||||
// After a full tick, repair the original styles
|
// on the next animation frame, repair the styles
|
||||||
Vue.nextTick(() => {
|
requestAnimationFrame(() => {
|
||||||
target.style.transform = originalStyle.transform;
|
target.style.transform = originalStyle.transform;
|
||||||
target.style.background = originalStyle.background;
|
target.style.backgroundColor = originalStyle.backgroundColor;
|
||||||
target.style.borderRadius = originalStyle.borderRadius;
|
target.style.borderRadius = originalStyle.borderRadius;
|
||||||
target.style.transition = originalStyle.transition;
|
target.style.transition = originalStyle.transition;
|
||||||
target.style.boxShadow = originalStyle.boxShadow;
|
target.style.boxShadow = originalStyle.boxShadow;
|
||||||
source.style.transition = originalStyle.sourceTransition;
|
source.style.transition = originalStyle.sourceTransition;
|
||||||
setTimeout(done, MOCK_DURATION);
|
setTimeout(() => done, MOCK_DURATION);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
leave(target, done){
|
leave(target, done){
|
||||||
@@ -101,8 +104,17 @@
|
|||||||
mockElement({source, target});
|
mockElement({source, target});
|
||||||
}
|
}
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
source.style.visibility = null;
|
let originalTransition = source.style.transition;
|
||||||
done();
|
source.style.opacity = null;
|
||||||
|
source.style.transition = 'none';
|
||||||
|
target.style.transition = `opacity ${MOCK_DURATION / 4}ms, pointer-events 0s`
|
||||||
|
requestAnimationFrame(() => {
|
||||||
|
source.style.transition = originalTransition;
|
||||||
|
target.style.opacity = "0";
|
||||||
|
target.style.pointerEvents = "none";
|
||||||
|
target.style.setProperty('box-shadow', "none", 'important');
|
||||||
|
setTimeout(done, MOCK_DURATION / 4);
|
||||||
|
});
|
||||||
}, MOCK_DURATION);
|
}, MOCK_DURATION);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -110,6 +122,22 @@
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
|
.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-fade-enter-active, .backdrop-fade-leave-active {
|
||||||
|
transition: opacity 0.3s;
|
||||||
|
}
|
||||||
|
.backdrop-fade-enter, .backdrop-fade-leave-to {
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
.dialog-stack {
|
.dialog-stack {
|
||||||
position: fixed;
|
position: fixed;
|
||||||
top: 0;
|
top: 0;
|
||||||
@@ -121,35 +149,45 @@
|
|||||||
}
|
}
|
||||||
.dialog-sizer {
|
.dialog-sizer {
|
||||||
position: relative;
|
position: relative;
|
||||||
height: 600px;
|
width: 80%;
|
||||||
width: 600px;
|
width: calc(100% - 64px);
|
||||||
|
max-width: 1000px;
|
||||||
|
height: 80%;
|
||||||
|
height: calc(100% - 64px);
|
||||||
|
max-height: 800px;
|
||||||
z-index: 5;
|
z-index: 5;
|
||||||
flex: initial;
|
flex: initial;
|
||||||
}
|
}
|
||||||
.backdrop {
|
/* sm */
|
||||||
position: fixed;
|
@media only screen and (max-width: 960px) and (min-width: 601px){
|
||||||
top: 0;
|
.dialog-sizer {
|
||||||
left: 0;
|
width: calc(100% - 32px);
|
||||||
right: 0;
|
height: calc(100% - 32px);
|
||||||
bottom: 0;
|
}
|
||||||
background-color: rgba(0, 0, 0, 0.4);
|
}
|
||||||
z-index: 4;
|
/* xs */
|
||||||
pointer-events: initial;
|
@media only screen and (max-width: 600px) {
|
||||||
}
|
.dialog-sizer {
|
||||||
.backdrop.hidden {
|
width: 100%;
|
||||||
display: none
|
height: 100%;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
.dialog-list-enter .dialog-component, .dialog-list-leave-to .dialog-component {
|
.dialog-list-enter .dialog-component, .dialog-list-leave-to .dialog-component {
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
}
|
}
|
||||||
.dialog-list-enter-active .dialog-component {
|
.dialog-list-enter-active .dialog-component {
|
||||||
transition: opacity 4s;
|
transition: opacity 0.3s;
|
||||||
}
|
}
|
||||||
.dialog-list-leave-active .dialog-component {
|
.dialog-list-leave-active .dialog-component {
|
||||||
transition: opacity 4s 4s;
|
transition: opacity 0.3s 0.1s;
|
||||||
|
}
|
||||||
|
.dialog-list-enter-active {
|
||||||
|
transition: all 0.4s, box-shadow 0.1s;
|
||||||
|
}
|
||||||
|
.dialog-list-leave-active {
|
||||||
|
transition: all 0.4s, box-shadow 0.1s 0.3s, opacity 0.1s, pointer-events 0s;
|
||||||
}
|
}
|
||||||
.dialog {
|
.dialog {
|
||||||
transition: all 8s;
|
|
||||||
transform-origin: top left;
|
transform-origin: top left;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
@@ -157,7 +195,6 @@
|
|||||||
pointer-events: initial;
|
pointer-events: initial;
|
||||||
z-index: 1;
|
z-index: 1;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
background: white;
|
|
||||||
}
|
}
|
||||||
.dialog > * {
|
.dialog > * {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
|
|||||||
@@ -9,7 +9,6 @@ const dialogStackStore = {
|
|||||||
mutations: {
|
mutations: {
|
||||||
pushDialogStack(state, {component, data, elementId, returnElement, callback}){
|
pushDialogStack(state, {component, data, elementId, returnElement, callback}){
|
||||||
// Generate a new _id so that Vue knows how to shuffle the array
|
// Generate a new _id so that Vue knows how to shuffle the array
|
||||||
console.log({elementId});
|
|
||||||
const _id = Random.id();
|
const _id = Random.id();
|
||||||
state.dialogs.push({
|
state.dialogs.push({
|
||||||
_id,
|
_id,
|
||||||
@@ -22,7 +21,6 @@ const dialogStackStore = {
|
|||||||
updateHistory();
|
updateHistory();
|
||||||
},
|
},
|
||||||
popDialogStackMutation (state, result){
|
popDialogStackMutation (state, result){
|
||||||
console.log({popped: result});
|
|
||||||
const dialog = state.dialogs.pop();
|
const dialog = state.dialogs.pop();
|
||||||
state.currentResult = null;
|
state.currentResult = null;
|
||||||
updateHistory();
|
updateHistory();
|
||||||
|
|||||||
@@ -17,20 +17,20 @@ const transformedBoxShadow = (shadowString, deltaWidth, deltaHeight) => {
|
|||||||
if (shadowString[0] === 'r'){
|
if (shadowString[0] === 'r'){
|
||||||
let strings = shadowString.match(/rgba\([^)]+\)[^,]+/g);
|
let strings = shadowString.match(/rgba\([^)]+\)[^,]+/g);
|
||||||
strings = strings.map(string => {
|
strings = strings.map(string => {
|
||||||
// TODO move color to end
|
// Move color to end
|
||||||
strings.match(/(rgba\([^)]+\))([^,]+)/)
|
let m = string.match(/(rgba\([^)]+\))([^,]+)/);
|
||||||
|
return `${m[2].trim()} ${m[1]}`;
|
||||||
});
|
});
|
||||||
|
shadowString = strings.join(', ');
|
||||||
}
|
}
|
||||||
let scaleAverage = (deltaWidth + deltaHeight) / 2;
|
let scaleAverage = (deltaWidth + deltaHeight) / 2;
|
||||||
let shadows = parse(shadowString);
|
let shadows = parse(shadowString);
|
||||||
console.log({shadowString, shadows});
|
|
||||||
shadows.forEach(shadow => {
|
shadows.forEach(shadow => {
|
||||||
shadow.offsetX /= deltaWidth;
|
shadow.offsetX /= deltaWidth;
|
||||||
shadow.offsetY /= deltaHeight;
|
shadow.offsetY /= deltaHeight;
|
||||||
shadow.blurRadius /= scaleAverage;
|
shadow.blurRadius /= scaleAverage;
|
||||||
shadow.spreadRadius /= scaleAverage;
|
shadow.spreadRadius /= scaleAverage;
|
||||||
})
|
})
|
||||||
console.log({newShadows: shadows});
|
|
||||||
return stringify(shadows);
|
return stringify(shadows);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -48,11 +48,15 @@ export default function mockElement({source, target, offset = {x: 0, y: 0}}){
|
|||||||
target.style.transform = `translate(${deltaLeft}px, ${deltaTop}px) ` +
|
target.style.transform = `translate(${deltaLeft}px, ${deltaTop}px) ` +
|
||||||
`scale(${deltaWidth}, ${deltaHeight})`;
|
`scale(${deltaWidth}, ${deltaHeight})`;
|
||||||
|
|
||||||
target.style.background = getComputedStyle(source).background;
|
target.style.backgroundColor = getComputedStyle(source).backgroundColor;
|
||||||
target.style.borderRadius = transformedRadius(
|
// Edge might not combine all border radii into a single value,
|
||||||
getComputedStyle(source).borderRadius, deltaWidth, deltaHeight
|
// So we just sample the top left one if we need to
|
||||||
);
|
let oldRadius = getComputedStyle(source).borderRadius ||
|
||||||
//target.style.boxShadow = transformedBoxShadow(
|
getComputedStyle(source).borderTopLeftRadius;
|
||||||
// getComputedStyle(source).boxShadow, deltaWidth, deltaHeight
|
let borderRadius = transformedRadius(oldRadius, deltaWidth, deltaHeight);
|
||||||
//);
|
target.style.borderRadius = borderRadius;
|
||||||
|
let boxShadow = transformedBoxShadow(
|
||||||
|
getComputedStyle(source).boxShadow, deltaWidth, deltaHeight
|
||||||
|
);
|
||||||
|
target.style.setProperty('box-shadow', boxShadow, 'important');
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user