aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTerry Truong <terry06890@gmail.com>2022-03-20 20:38:03 +1100
committerTerry Truong <terry06890@gmail.com>2022-03-20 20:38:03 +1100
commit9f3492dad0a37361ca796e8c4de2bfc32328b554 (patch)
tree8a29153e5dc90d534e8cace63c52677d7f71ecb2
parent3063a9a83cc9c8a5aa026e0bfc0393170188833a (diff)
Add animation on expand/collapse failure
-rw-r--r--src/components/Tile.vue69
-rw-r--r--src/components/TileTree.vue56
2 files changed, 83 insertions, 42 deletions
diff --git a/src/components/Tile.vue b/src/components/Tile.vue
index 0c596ed..444ee9a 100644
--- a/src/components/Tile.vue
+++ b/src/components/Tile.vue
@@ -2,27 +2,26 @@
import {defineComponent, PropType} from 'vue';
import {LayoutNode} from '../lib';
-// Configurable settings (integer values specify pixels)
+// Configurable settings
let options = {
- borderRadius: 5,
+ borderRadius: 5, //px
shadowNormal: '0 0 2px black',
shadowWithHover: '0 0 1px 2px greenyellow',
// For leaf tiles
- leafHeaderX: 4,
- leafHeaderY: 4,
- leafHeaderFontSz: 15,
+ leafHeaderX: 4, //px
+ leafHeaderY: 4, //px
+ leafHeaderFontSz: 15, //px
leafHeaderColor: '#fafaf9',
expandableLeafHeaderColor: 'greenyellow', //yellow, greenyellow, turquoise,
// For non-leaf tile-groups
nonLeafBgColors: ['#44403c', '#57534e'], //tiles at depth N use the Nth color, repeating from the start as needed
- nonLeafHeaderFontSz: 15,
+ nonLeafHeaderFontSz: 15, //px
nonLeafHeaderColor: '#fafaf9',
nonLeafHeaderBgColor: '#1c1917',
};
// Component holds a tree-node structure representing a tile or tile-group to be rendered
export default defineComponent({
- name: 'tile', // Need this to use self in template
props: {
layoutNode: {type: Object as PropType<LayoutNode>, required: true},
isRoot: {type: Boolean, default: false},
@@ -44,7 +43,7 @@ export default defineComponent({
return this.layoutNode.children.length == 0;
},
isExpandable(){
- return this.layoutNode.tolNode.children.length > this.layoutNode.children;
+ return this.layoutNode.tolNode.children.length > this.layoutNode.children.length;
},
showHeader(){
return (this.layoutNode.showHeader && !this.layoutNode.sepSweptArea) ||
@@ -167,53 +166,65 @@ export default defineComponent({
methods: {
// For tile expansion and collapse
onLeafClick(){
- this.$emit('leaf-clicked', this.layoutNode);
- // Increase z-index and hide overflow during transition
+ this.$emit('leaf-clicked', {layoutNode: this.layoutNode, domNode: this.$el});
+ (this.$refs.leaf as Element).classList.replace('shadow-highlight', 'shadow-normal');
+ // Temporary changes during transition
this.zIdx = 1;
this.overflow = 'hidden';
- setTimeout(() => {this.zIdx = 0; this.overflow = 'visible'}, this.transitionDuration);
+ setTimeout(() => {
+ this.zIdx = 0;
+ this.overflow = 'visible';
+ }, this.transitionDuration);
},
- onInnerLeafClicked(node: LayoutNode){
- this.$emit('leaf-clicked', node);
+ onInnerLeafClicked(data: {layoutNode: LayoutNode, domNode: HTMLElement}){
+ this.$emit('leaf-clicked', data);
},
onHeaderClick(){
- this.$emit('header-clicked', this.layoutNode);
- // Increase z-index and hide overflow during transition
+ this.$emit('header-clicked', {layoutNode: this.layoutNode, domNode: this.$el});
+ (this.$refs.nonLeaf as Element).classList.replace('shadow-highlight', 'shadow-normal');
+ // Temporary changes during transition
this.zIdx = 1;
this.overflow = 'hidden';
- setTimeout(() => {this.zIdx = 0; this.overflow = 'visible'}, this.transitionDuration);
+ setTimeout(() => {
+ this.zIdx = 0;
+ this.overflow = 'visible';
+ }, this.transitionDuration);
},
- onInnerHeaderClicked(node: LayoutNode){
- this.$emit('header-clicked', node);
+ onInnerHeaderClicked(data: {layoutNode: LayoutNode, domNode: HTMLElement}){
+ this.$emit('header-clicked', data);
},
// For coloured-outlines on hovered-over leaf-tiles or non-leaf-headers
- onMouseEnter(evt){
+ onMouseEnter(evt: Event){
if (!this.isLeaf){
- this.$refs.nonLeaf.classList.replace('shadow-normal', 'shadow-highlight');
- if (this.$refs.sepSweptArea != null){
- this.$refs.sepSweptArea.classList.replace('shadow-normal', 'shadow-highlight');
+ (this.$refs.nonLeaf as Element).classList.replace('shadow-normal', 'shadow-highlight');
+ let sepSweptArea = (this.$refs.sepSweptArea as Element | null);
+ if (sepSweptArea != null){
+ sepSweptArea.classList.replace('shadow-normal', 'shadow-highlight');
}
} else if (this.isExpandable){
- evt.target.classList.replace('shadow-normal', 'shadow-highlight');
+ (evt.target as Element).classList.replace('shadow-normal', 'shadow-highlight');
}
},
- onMouseLeave(evt){
+ onMouseLeave(evt: Event){
if (!this.isLeaf){
- this.$refs.nonLeaf.classList.replace('shadow-highlight', 'shadow-normal');
- if (this.$refs.sepSweptArea != null){
- this.$refs.sepSweptArea.classList.replace('shadow-highlight', 'shadow-normal');
+ (this.$refs.nonLeaf as Element).classList.replace('shadow-highlight', 'shadow-normal');
+ let sepSweptArea = this.$refs.sepSweptArea as Element | null;
+ if (sepSweptArea != null){
+ sepSweptArea.classList.replace('shadow-highlight', 'shadow-normal');
}
} else if (this.isExpandable){
- evt.target.classList.replace('shadow-highlight', 'shadow-normal');
+ (evt.target as Element).classList.replace('shadow-highlight', 'shadow-normal');
}
},
},
+ name: 'tile', // Need this to use self in template
+ emits: ['leaf-clicked', 'header-clicked'],
});
</script>
<template>
<div :style="tileStyles">
- <div v-if="isLeaf" :style="leafStyles"
+ <div v-if="isLeaf" :style="leafStyles" ref="leaf"
:class="['shadow-normal'].concat(isExpandable ? ['hover:cursor-pointer'] : [])"
@click="onLeafClick" @mouseenter="onMouseEnter" @mouseleave="onMouseLeave">
<div :style="{borderRadius: options.borderRadius + 'px'}" class="scrim-upper-half"/>
diff --git a/src/components/TileTree.vue b/src/components/TileTree.vue
index f0d8f07..85516c9 100644
--- a/src/components/TileTree.vue
+++ b/src/components/TileTree.vue
@@ -18,12 +18,12 @@ function preprocessTol(node: any): any {
}
const tol: TolNode = preprocessTol(tolRaw);
-// Configurable settings (integer values specify pixels)
+// Configurable settings
let layoutOptions: LayoutOptions = {
- tileSpacing: 8,
- headerSz: 20,
- minTileSz: 50,
- maxTileSz: 200,
+ tileSpacing: 8, //px
+ headerSz: 20, //px
+ minTileSz: 50, //px
+ maxTileSz: 200, //px
layoutType: 'sweep', //'sqr' | 'rect' | 'sweep'
rectMode: 'auto', //'horz' | 'vert' | 'linear' | 'auto'
sweepMode: 'left', //'left' | 'top' | 'shorter' | 'auto'
@@ -63,18 +63,28 @@ export default defineComponent({
setTimeout(() => {this.resizeThrottled = false;}, otherOptions.resizeDelay);
}
},
- onInnerLeafClicked(clickedNode: LayoutNode){
- if (clickedNode.tolNode.children.length == 0){
- console.log('Tile-to-expand has no children');
+ onInnerLeafClicked({layoutNode, domNode}: {layoutNode: LayoutNode, domNode: HTMLElement}){
+ if (layoutNode.tolNode.children.length == 0){
+ //console.log('Tile to expand has no children');
return;
}
- if (!this.layoutTree.tryLayoutOnExpand([0,0], [this.width,this.height], clickedNode)){
- console.log('Unable to layout tree');
+ let success = this.layoutTree.tryLayoutOnExpand([0,0], [this.width,this.height], layoutNode);
+ if (!success){
+ // Trigger failure animation
+ domNode.classList.remove('animate-expand-shrink');
+ domNode.offsetWidth; // Triggers reflow
+ domNode.classList.add('animate-expand-shrink');
+ //console.log('Unable to layout tree');
}
},
- onInnerHeaderClicked(clickedNode: LayoutNode){
- if (!this.layoutTree.tryLayoutOnCollapse([0,0], [this.width,this.height], clickedNode)){
- console.log('Unable to layout tree');
+ onInnerHeaderClicked({layoutNode, domNode}: {layoutNode: LayoutNode, domNode: HTMLElement}){
+ let success = this.layoutTree.tryLayoutOnCollapse([0,0], [this.width,this.height], layoutNode);
+ if (!success){
+ // Trigger failure animation
+ domNode.classList.remove('animate-expand-shrink');
+ domNode.offsetWidth; // Triggers reflow
+ domNode.classList.add('animate-expand-shrink');
+ //console.log('Unable to layout tree');
}
},
},
@@ -101,3 +111,23 @@ export default defineComponent({
@leaf-clicked="onInnerLeafClicked" @header-clicked="onInnerHeaderClicked"/>
</div>
</template>
+
+<style>
+.animate-expand-shrink {
+ animation-name: expand-shrink;
+ animation-duration: 300ms;
+ animation-iteration-count: 1;
+ animation-timing-function: ease-in-out;
+}
+@keyframes expand-shrink {
+ from {
+ transform: scale(1, 1);
+ }
+ 50% {
+ transform: scale(1.1, 1.1);
+ }
+ to {
+ transform: scale(1, 1);
+ }
+}
+</style>