diff options
Diffstat (limited to 'src/components/LeafTile.vue')
| -rw-r--r-- | src/components/LeafTile.vue | 115 |
1 files changed, 115 insertions, 0 deletions
diff --git a/src/components/LeafTile.vue b/src/components/LeafTile.vue new file mode 100644 index 0000000..7448746 --- /dev/null +++ b/src/components/LeafTile.vue @@ -0,0 +1,115 @@ +<script lang="ts"> +import {defineComponent, PropType} from 'vue'; +import InfoIcon from './icon/InfoIcon.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: { + isExpandable(){ + return this.layoutNode.tolNode.children.length > 0; + }, + 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', + }; + } + if (this.nonAbsPos){ + placementStyles.position = 'static'; + } + return { + ...placementStyles, + // Image + backgroundImage: + 'linear-gradient(to bottom, rgba(0,0,0,0.4), rgba(0,0,0,0) 40%, rgba(0,0,0,0) 60%, rgba(0,0,0,0.4) 100%),' + + 'url(\'/img/' + this.layoutNode.tolNode.name.replaceAll('\'', '\\\'') + '.png\')', + backgroundSize: 'cover', + // Child layout + display: 'flex', + flexDirection: 'column', + // Transition related + transitionDuration: this.uiOpts.transitionDuration + 'ms', + transitionProperty: 'left, top, width, height, visibility', + transitionTimingFunction: 'ease-out', + zIndex: this.inTransition ? '1' : '0', + overflow: 'visible', + // Other + borderRadius: this.uiOpts.borderRadius + 'px', + boxShadow: this.highlight ? this.uiOpts.shadowHighlight : + (this.layoutNode.hasFocus ? this.uiOpts.shadowFocused : this.uiOpts.shadowNormal), + }; + }, + headerStyles(): Record<string,string> { + return { + height: (this.uiOpts.imgTileFontSz + this.uiOpts.imgTilePadding * 2) + 'px', + lineHeight: this.uiOpts.imgTileFontSz + 'px', + fontSize: this.uiOpts.imgTileFontSz + 'px', + padding: this.uiOpts.imgTilePadding + 'px', + color: this.isExpandable ? this.uiOpts.expandableImgTileColor : this.uiOpts.imgTileColor, + // For ellipsis + overflow: 'hidden', + textOverflow: 'ellipsis', + whiteSpace: 'nowrap', + }; + }, + }, + methods: { + onInfoClick(evt: Event){ + this.$emit('info-icon-clicked', this.layoutNode); + }, + onMouseEnter(){ + this.$emit('tile-mouse-enter'); + }, + onMouseLeave(){ + this.$emit('tile-mouse-leave'); + }, + onMouseDown(){ + this.$emit('tile-mouse-down'); + }, + onMouseUp(){ + this.$emit('tile-mouse-up'); + }, + }, + components: {InfoIcon, }, + emits: [ + 'info-icon-clicked', + 'tile-mouse-enter', 'tile-mouse-leave', 'tile-mouse-down', 'tile-mouse-up', + ], +}); +</script> + +<template> +<div :style="styles" :class="isExpandable ? ['hover:cursor-pointer'] : []" + @mouseenter="onMouseEnter" @mouseleave="onMouseLeave" + @mousedown="onMouseDown" @mouseup="onMouseUp"> + <h1 :style="headerStyles">{{layoutNode.tolNode.name}}</h1> + <info-icon + class="w-[18px] h-[18px] mt-auto mb-[2px] mr-[2px] self-end + text-white/30 hover:text-white hover:cursor-pointer" + @click.stop="onInfoClick" @mousedown.stop @mouseup.stop/> +</div> +</template> |
