diff options
Diffstat (limited to 'src/components')
| -rw-r--r-- | src/components/Tile.vue | 121 | ||||
| -rw-r--r-- | src/components/TileImg.vue | 8 | ||||
| -rw-r--r-- | src/components/TileTree.vue | 16 |
3 files changed, 81 insertions, 64 deletions
diff --git a/src/components/Tile.vue b/src/components/Tile.vue index 7722101..f0b506e 100644 --- a/src/components/Tile.vue +++ b/src/components/Tile.vue @@ -15,7 +15,7 @@ export default defineComponent({ data(){ return { nonLeafHighlight: false, - dblClickTimer: 0, // Used to delay a click action until a double-click timeout ends + clickHoldTimer: 0, // Used to recognise a click-and-hold event zIdx: 0, // Used during transitions overflow: 'visible', // Used during transitions } @@ -121,58 +121,69 @@ export default defineComponent({ }, }, methods: { - // For click handling - onLeafClick(evt: UIEvent){ + // Leaf click handling + onLeafMouseDown(){ + clearTimeout(this.clickHoldTimer); + this.clickHoldTimer = setTimeout(() => { + this.clickHoldTimer = 0; + this.onLeafClickHold(); + }, this.options.clickHoldDuration); + }, + onLeafMouseUp(){ + if (this.clickHoldTimer > 0){ + clearTimeout(this.clickHoldTimer); + this.clickHoldTimer = 0; + this.onLeafClick(); + } + }, + onLeafClick(){ if (!this.isExpandable){ - console.log('Ignored click on non-expandable leaf node'); + console.log('Ignored click on non-expandable node'); return; } - let prepForTransition = () => { - // Temporary changes to prevent content overlap and overflow - this.zIdx = 1; - this.overflow = 'hidden'; - setTimeout(() => {this.zIdx = 0; this.overflow = 'visible';}, this.options.transitionDuration); - }; - if (evt.detail == 1){ - this.dblClickTimer = setTimeout(() => { - this.$emit('leaf-clicked', {layoutNode: this.layoutNode, domNode: this.$el}); - prepForTransition(); - }, this.options.dblClickWait); - } else if (evt.detail == 2){ - clearTimeout(this.dblClickTimer); - this.$emit('leaf-dbl-clicked', this.layoutNode); - prepForTransition(); + this.$emit('leaf-clicked', {layoutNode: this.layoutNode, domNode: this.$el}); + this.leafPrepTransition(); + }, + onLeafClickHold(){ + if (!this.isExpandable){ + console.log('Ignored click-hold on non-expandable node'); + return; } + this.$emit('leaf-click-held', this.layoutNode); + this.leafPrepTransition(); }, - onInnerLeafClicked(data: {layoutNode: LayoutNode, domNode: HTMLElement}){ - this.$emit('leaf-clicked', data); + leafPrepTransition(){ // Temporary style changes to prevent content overlap and overflow + this.zIdx = 1; + this.overflow = 'hidden'; + setTimeout(() => {this.zIdx = 0; this.overflow = 'visible';}, this.options.transitionDuration); }, - onHeaderClick(evt: UIEvent){ + // Non-leaf click handling + onHeaderMouseDown(){ this.nonLeafHighlight = false; - let prepForTransition = () => { - // Temporary changes to prevent content overlap and overflow - this.zIdx = 1; - setTimeout(() => {this.zIdx = 0}, this.options.transitionDuration); - }; - if (evt.detail == 1){ - this.dblClickTimer = setTimeout(() => { - this.$emit('header-clicked', {layoutNode: this.layoutNode, domNode: this.$el}); - prepForTransition(); - }, this.options.dblClickWait); - } else if (evt.detail == 2){ - clearTimeout(this.dblClickTimer); - this.$emit('header-dbl-clicked', this.layoutNode); - prepForTransition(); + clearTimeout(this.clickHoldTimer); + this.clickHoldTimer = setTimeout(() => { + this.clickHoldTimer = 0; + this.onHeaderClickHold(); + }, this.options.clickHoldDuration); + }, + onHeaderMouseUp(){ + if (this.clickHoldTimer > 0){ + clearTimeout(this.clickHoldTimer); + this.clickHoldTimer = 0; + this.onHeaderClick(); } }, - onInnerHeaderClicked(data: {layoutNode: LayoutNode, domNode: HTMLElement}){ - this.$emit('header-clicked', data); + onHeaderClick(){ + this.$emit('header-clicked', {layoutNode: this.layoutNode, domNode: this.$el}); + this.nonLeafPrepForTransition(); }, - onInnerLeafDblClicked(data: LayoutNode){ - this.$emit('leaf-dbl-clicked', data); + onHeaderClickHold(){ + this.$emit('header-click-held', this.layoutNode); + this.nonLeafPrepForTransition(); }, - onInnerHeaderDblClicked(data: LayoutNode){ - this.$emit('header-dbl-clicked', data); + nonLeafPrepForTransition(){ // Temporary style changes to prevent content overlap and overflow + this.zIdx = 1; + setTimeout(() => {this.zIdx = 0}, this.options.transitionDuration); }, // For coloured-outlines on hovered-over leaf-tiles or non-leaf-headers onNonLeafMouseEnter(evt: Event){ @@ -181,7 +192,19 @@ export default defineComponent({ onNonLeafMouseLeave(evt: Event){ this.nonLeafHighlight = false; }, - // For child event propagation + // Child event propagation + onInnerLeafClicked(data: {layoutNode: LayoutNode, domNode: HTMLElement}){ + this.$emit('leaf-clicked', data); + }, + onInnerHeaderClicked(data: {layoutNode: LayoutNode, domNode: HTMLElement}){ + 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); } @@ -190,31 +213,33 @@ export default defineComponent({ components: { TileImg, }, - emits: ['leaf-clicked', 'header-clicked', 'leaf-dbl-clicked', 'header-dbl-clicked', 'info-icon-clicked'], + emits: ['leaf-clicked', 'header-clicked', 'leaf-click-held', 'header-click-held', 'info-icon-clicked'], }); </script> <template> <div :style="tileStyles"> <tile-img v-if="isLeaf" :layoutNode="layoutNode" :tileSz="layoutNode.dims[0]" :options="options" - @click="onLeafClick" @info-icon-clicked="onInnerInfoIconClicked"/> + @mousedown="onLeafMouseDown" @mouseup="onLeafMouseUp" @info-icon-clicked="onInnerInfoIconClicked"/> <div v-else :style="nonLeafStyles" ref="nonLeaf"> <h1 v-if="showHeader" :style="nonLeafHeaderStyles" class="hover:cursor-pointer" - @click="onHeaderClick($event)" @mouseenter="onNonLeafMouseEnter" @mouseleave="onNonLeafMouseLeave"> + @mouseenter="onNonLeafMouseEnter" @mouseleave="onNonLeafMouseLeave" + @mousedown="onHeaderMouseDown" @mouseup="onHeaderMouseUp"> {{layoutNode.tolNode.name}} </h1> <div :style="sepSweptAreaStyles" ref="sepSweptArea" :class="layoutNode?.sepSweptArea?.sweptLeft ? 'hide-right-edge' : 'hide-top-edge'"> <h1 v-if="layoutNode?.sepSweptArea?.sweptLeft === false" :style="nonLeafHeaderStyles" class="hover:cursor-pointer" - @click="onHeaderClick($event)" @mouseenter="onNonLeafMouseEnter" @mouseleave="onNonLeafMouseLeave"> + @mouseenter="onNonLeafMouseEnter" @mouseleave="onNonLeafMouseLeave" + @mousedown="onHeaderMouseDown" @mouseup="onHeaderMouseUp"> {{layoutNode.tolNode.name}} </h1> </div> <tile v-for="child in layoutNode.children" :key="child.tolNode.name" :layoutNode="child" :headerSz="headerSz" :tileSpacing="tileSpacing" :options="options" @leaf-clicked="onInnerLeafClicked" @header-clicked="onInnerHeaderClicked" - @leaf-dbl-clicked="onInnerLeafDblClicked" @header-dbl-clicked="onInnerHeaderDblClicked" + @leaf-click-held="onInnerLeafClickHeld" @header-click-held="onInnerHeaderClickHeld" @info-icon-clicked="onInnerInfoIconClicked"/> </div> </div> diff --git a/src/components/TileImg.vue b/src/components/TileImg.vue index 2445f98..7e44f2a 100644 --- a/src/components/TileImg.vue +++ b/src/components/TileImg.vue @@ -74,10 +74,8 @@ export default defineComponent({ this.highlight = false; } }, - onClick(evt: Event){ - if (this.isExpandable){ - this.highlight = false; - } + onMouseDown(evt: Event){ + this.highlight = false; }, onInfoMouseEnter(evt: Event){ this.infoMouseOver = true; @@ -94,7 +92,7 @@ export default defineComponent({ </script> <template> -<div :style="styles" @mouseenter="onMouseEnter" @mouseleave="onMouseLeave" @click="onClick" +<div :style="styles" @mouseenter="onMouseEnter" @mouseleave="onMouseLeave" @mousedown="onMouseDown" :class="isExpandable ? ['hover:cursor-pointer'] : []"> <h1 :style="headerStyles">{{layoutNode.tolNode.name}}</h1> <svg :style="infoIconStyles" class="hover:cursor-pointer" diff --git a/src/components/TileTree.vue b/src/components/TileTree.vue index 99ea9e3..7bc1ec2 100644 --- a/src/components/TileTree.vue +++ b/src/components/TileTree.vue @@ -55,7 +55,7 @@ const defaultComponentOptions = { infoModalImgSz: 200, // Timing related transitionDuration: 300, //ms - dblClickWait: 200, //ms + clickHoldDuration: 400, //ms (duration after mousedown when a click-and-hold is recognised) }; const defaultOwnOptions = { tileAreaOffset: 5, //px (space between root tile and display boundary) @@ -174,7 +174,7 @@ export default defineComponent({ } }, // For expand-to-view events - onInnerLeafDblClicked(layoutNode: LayoutNode){ + onInnerLeafClickHeld(layoutNode: LayoutNode){ if (layoutNode == this.activeRoot){ console.log('Ignored expand-to-view on root node'); return; @@ -184,7 +184,7 @@ export default defineComponent({ tryLayout(layoutNode, this.tileAreaPos, this.tileAreaDims, this.layoutOptions, true, {type: 'expand', node: layoutNode}); }, - onInnerHeaderDblClicked(layoutNode: LayoutNode){ + onInnerHeaderClickHeld(layoutNode: LayoutNode){ if (layoutNode.parent == null){ console.log('Ignored expand-to-view on root node'); return; @@ -205,12 +205,6 @@ export default defineComponent({ onInfoModalClose(){ this.infoModalNode = null; }, - // For preventing double-clicks from highlighting text - onMouseDown(evt: UIEvent){ - if (evt.detail == 2){ - evt.preventDefault(); - } - }, }, created(){ window.addEventListener('resize', this.onResize); @@ -228,11 +222,11 @@ export default defineComponent({ </script> <template> -<div :style="styles" @mousedown="onMouseDown"> +<div :style="styles"> <tile :layoutNode="layoutTree" :headerSz="layoutOptions.headerSz" :tileSpacing="layoutOptions.tileSpacing" :options="componentOptions" @leaf-clicked="onInnerLeafClicked" @header-clicked="onInnerHeaderClicked" - @leaf-dbl-clicked="onInnerLeafDblClicked" @header-dbl-clicked="onInnerHeaderDblClicked" + @leaf-click-held="onInnerLeafClickHeld" @header-click-held="onInnerHeaderClickHeld" @info-icon-clicked="onInnerInfoIconClicked"/> <parent-bar v-if="sepdParents != null" :pos="[0,0]" :dims="parentBarDims" :nodes="sepdParents" :options="componentOptions" |
