diff options
| author | Terry Truong <terry06890@gmail.com> | 2022-03-28 20:59:19 +1100 |
|---|---|---|
| committer | Terry Truong <terry06890@gmail.com> | 2022-03-28 20:59:19 +1100 |
| commit | 712d9c14911ce1e470100c6cc41b14026c033574 (patch) | |
| tree | 7ec7d7531ea5d4d3e438226822d3e18c152495e6 /src/components/NonLeafTile.vue | |
| parent | 50e1e7ae48daf04323093a438aff2d55ff20193d (diff) | |
Separate parts of Tile into LeafTile and NonLeafTiletest-leaftile-nonleaftile
Diffstat (limited to 'src/components/NonLeafTile.vue')
| -rw-r--r-- | src/components/NonLeafTile.vue | 205 |
1 files changed, 205 insertions, 0 deletions
diff --git a/src/components/NonLeafTile.vue b/src/components/NonLeafTile.vue new file mode 100644 index 0000000..f9f2983 --- /dev/null +++ b/src/components/NonLeafTile.vue @@ -0,0 +1,205 @@ +<script lang="ts"> +import {defineComponent, PropType} from 'vue'; +import Tile from './Tile.vue'; +import {LayoutNode} from '../layout'; +import type {LayoutOptions} from '../layout'; + +export default defineComponent({ + props: { + layoutNode: {type: Object as PropType<LayoutNode>, required: true}, + lytOpts: {type: Object as PropType<LayoutOptions>, required: true}, + uiOpts: {type: Object, required: true}, + nonAbsPos: {type: Boolean, default: false}, + highlight: {type: Boolean, default: false}, + inTransition: {type: Boolean, default: false}, + }, + computed: { + showHeader(){ + return (this.layoutNode.showHeader && !this.layoutNode.sepSweptArea) || + (this.layoutNode.sepSweptArea && this.layoutNode.sepSweptArea.sweptLeft); + }, + nonLeafBgColor(){ + let colorArray = this.uiOpts.nonLeafBgColors; + return colorArray[this.layoutNode.depth % colorArray.length]; + }, + styles(): Record<string,string> { + let placementStyles; + if (!this.layoutNode.hidden){ + placementStyles = { + position: 'absolute', + left: this.layoutNode.pos[0] + 'px', + top: this.layoutNode.pos[1] + 'px', + width: this.layoutNode.dims[0] + 'px', + height: this.layoutNode.dims[1] + 'px', + visibility: 'visible', + }; + } else { + placementStyles = { + position: 'absolute', + left: '0', + top: '0', + width: '0', + height: '0', + visibility: 'hidden', + }; + } + let borderR = this.uiOpts.borderRadius + 'px'; + if (this.layoutNode.sepSweptArea != null){ + borderR = this.layoutNode.sepSweptArea.sweptLeft ? + `${borderR} ${borderR} ${borderR} 0` : + `${borderR} 0 ${borderR} ${borderR}`; + } + return { + ...placementStyles, + // Transition related + transitionDuration: this.uiOpts.transitionDuration + 'ms', + transitionProperty: 'left, top, width, height, visibility', + transitionTimingFunction: 'ease-out', + zIndex: this.inTransition ? '1' : '0', + overflow: this.inTransition ? 'hidden' : 'visible', + // CSS variables + '--nonLeafBgColor': this.nonLeafBgColor, + '--tileSpacing': this.lytOpts.tileSpacing + 'px', + // Other + borderRadius: borderR, + backgroundColor: this.nonLeafBgColor, + boxShadow: this.inTransition ? 'none' : + (this.highlight ? this.uiOpts.shadowHighlight : + (this.layoutNode.hasFocus ? this.uiOpts.shadowFocused : this.uiOpts.shadowNormal)), + }; + }, + headerStyles(): Record<string,string> { + let borderR = this.uiOpts.borderRadius + 'px'; + return { + height: this.lytOpts.headerSz + 'px', + lineHeight: this.lytOpts.headerSz + 'px', + fontSize: this.uiOpts.nonLeafHeaderFontSz + 'px', + textAlign: 'center', + color: this.uiOpts.nonLeafHeaderColor, + backgroundColor: this.uiOpts.nonLeafHeaderBgColor, + borderRadius: `${borderR} ${borderR} 0 0`, + // For ellipsis + overflow: 'hidden', + textOverflow: 'ellipsis', + whiteSpace: 'nowrap', + }; + }, + sepSweptAreaStyles(): Record<string,string> { + let commonStyles = { + position: 'absolute', + backgroundColor: this.nonLeafBgColor, + boxShadow: this.inTransition ? 'none' : + (this.highlight ? this.uiOpts.shadowHighlight : + (this.layoutNode.hasFocus ? this.uiOpts.shadowFocused : this.uiOpts.shadowNormal)), + transitionDuration: this.uiOpts.transitionDuration + 'ms', + transitionProperty: 'left, top, width, height', + transitionTimingFunction: 'ease-out', + }; + let area = this.layoutNode.sepSweptArea; + if (this.layoutNode.hidden || area == null){ + return { + ...commonStyles, + visibility: 'hidden', + left: '0', + top: this.lytOpts.headerSz + 'px', + width: '0', + height: '0', + }; + } else { + let borderR = this.uiOpts.borderRadius + 'px'; + return { + ...commonStyles, + left: area.pos[0] + 'px', + top: area.pos[1] + 'px', + width: area.dims[0] + 'px', + height: area.dims[1] + 'px', + borderRadius: area.sweptLeft ? + `${borderR} 0 0 ${borderR}` : + `${borderR} ${borderR} 0 0`, + }; + } + }, + }, + methods: { + onMouseEnter(){ + this.$emit('tile-mouse-enter'); + }, + onMouseLeave(){ + this.$emit('tile-mouse-leave'); + }, + onMouseDown(){ + this.$emit('tile-mouse-down'); + }, + onMouseUp(){ + this.$emit('tile-mouse-up'); + }, + // Child event propagation + onInnerLeafClicked(data: LayoutNode){ + this.$emit('leaf-clicked', data); + }, + onInnerHeaderClicked(data: LayoutNode){ + this.$emit('header-clicked', data); + }, + onInnerLeafClickHeld(data: LayoutNode){ + this.$emit('leaf-click-held', data); + }, + onInnerHeaderClickHeld(data: LayoutNode){ + this.$emit('header-click-held', data); + }, + onInnerInfoIconClicked(data: LayoutNode){ + this.$emit('info-icon-clicked', data); + }, + }, + name: 'non-leaf-tile', // Need this to use self in template + emits: [ + 'leaf-clicked', 'header-clicked', 'leaf-click-held', 'header-click-held', 'info-icon-clicked', + 'tile-mouse-enter', 'tile-mouse-leave', 'tile-mouse-down', 'tile-mouse-up', + ], + components: {Tile, }, +}); +</script> + +<template> +<div :style="styles"> + <h1 v-if="showHeader" :style="headerStyles" class="hover:cursor-pointer" + @mouseenter="onMouseEnter" @mouseleave="onMouseLeave" + @mousedown="onMouseDown" @mouseup="onMouseUp"> + {{layoutNode.tolNode.name}} + </h1> + <div :style="sepSweptAreaStyles" + :class="layoutNode?.sepSweptArea?.sweptLeft ? 'hide-right-edge' : 'hide-top-edge'"> + <h1 v-if="layoutNode?.sepSweptArea?.sweptLeft === false" + :style="headerStyles" class="hover:cursor-pointer" + @mouseenter="onMouseEnter" @mouseleave="onMouseLeave" + @mousedown="onMouseDown" @mouseup="onMouseUp"> + {{layoutNode.tolNode.name}} + </h1> + </div> + <tile v-for="child in layoutNode.children" :key="child.tolNode.name" + :layoutNode="child" :lytOpts="lytOpts" :uiOpts="uiOpts" + @leaf-clicked="onInnerLeafClicked" @header-clicked="onInnerHeaderClicked" + @leaf-click-held="onInnerLeafClickHeld" @header-click-held="onInnerHeaderClickHeld" + @info-icon-clicked="onInnerInfoIconClicked"/> +</div> +</template> + +<style> +.hide-right-edge::before { + content: ''; + position: absolute; + background-color: var(--nonLeafBgColor); + right: calc(0px - var(--tileSpacing)); + bottom: 0; + width: var(--tileSpacing); + height: calc(100% + var(--tileSpacing)); +} +.hide-top-edge::before { + content: ''; + position: absolute; + background-color: var(--nonLeafBgColor); + bottom: calc(0px - var(--tileSpacing)); + right: 0; + width: calc(100% + var(--tileSpacing)); + height: var(--tileSpacing); +} +</style> |
