Improved tooltip behavior and styling.
This commit is contained in:
parent
229d74d266
commit
a7e6f470f7
|
@ -38,6 +38,24 @@ async function setConsent(consent) {
|
|||
}
|
||||
}
|
||||
|
||||
function blur(event) {
|
||||
this.events.emit('blur', event);
|
||||
}
|
||||
|
||||
function resize(event) {
|
||||
this.events.emit('resize', event);
|
||||
}
|
||||
|
||||
function mounted() {
|
||||
document.addEventListener('click', this.blur);
|
||||
window.addEventListener('resize', this.resize);
|
||||
}
|
||||
|
||||
function beforeUnmount() {
|
||||
document.removeEventListener('click', this.blur);
|
||||
window.removeEventListener('resize', this.resize);
|
||||
}
|
||||
|
||||
export default {
|
||||
components: {
|
||||
Header,
|
||||
|
@ -50,9 +68,13 @@ export default {
|
|||
showWarning: localStorage.getItem('consent') !== window.env.sessionId,
|
||||
};
|
||||
},
|
||||
mounted,
|
||||
beforeUnmount,
|
||||
methods: {
|
||||
toggleSidebar,
|
||||
setConsent,
|
||||
blur,
|
||||
resize,
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
</div>
|
||||
|
||||
<template v-slot:tooltip>
|
||||
<div>
|
||||
<div class="filter-options">
|
||||
<select
|
||||
v-model="mode"
|
||||
class="filter-mode"
|
||||
|
@ -105,3 +105,9 @@ export default {
|
|||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.filter-options {
|
||||
width: 15rem;
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -95,14 +95,17 @@
|
|||
<template v-slot:tooltip>
|
||||
<div class="menu">
|
||||
<ul class="menu-items noselect">
|
||||
<li class="menu-item disabled">
|
||||
<li
|
||||
class="menu-item disabled"
|
||||
@click.stop
|
||||
>
|
||||
<Icon icon="enter2" />Sign in
|
||||
</li>
|
||||
|
||||
<li
|
||||
v-show="!sfw"
|
||||
class="menu-item"
|
||||
@click="setSfw(true)"
|
||||
@click.stop="setSfw(true)"
|
||||
>
|
||||
<Icon
|
||||
icon="flower"
|
||||
|
@ -113,7 +116,7 @@
|
|||
<li
|
||||
v-show="sfw"
|
||||
class="menu-item"
|
||||
@click="setSfw(false)"
|
||||
@click.stop="setSfw(false)"
|
||||
>
|
||||
<Icon
|
||||
icon="fire"
|
||||
|
@ -124,7 +127,7 @@
|
|||
<li
|
||||
v-show="theme === 'light'"
|
||||
class="menu-item"
|
||||
@click="setTheme('dark')"
|
||||
@click.stop="setTheme('dark')"
|
||||
>
|
||||
<Icon
|
||||
icon="moon"
|
||||
|
@ -135,7 +138,7 @@
|
|||
<li
|
||||
v-show="theme === 'dark'"
|
||||
class="menu-item"
|
||||
@click="setTheme('light')"
|
||||
@click.stop="setTheme('light')"
|
||||
>
|
||||
<Icon
|
||||
icon="sun"
|
||||
|
@ -143,7 +146,10 @@
|
|||
/>Light theme
|
||||
</li>
|
||||
|
||||
<li class="menu-item">
|
||||
<li
|
||||
class="menu-item disabled"
|
||||
@click.stop
|
||||
>
|
||||
<Icon icon="filter" />Filters
|
||||
</li>
|
||||
</ul>
|
||||
|
|
|
@ -1,13 +1,24 @@
|
|||
<template>
|
||||
<div class="tooltip-container">
|
||||
<div class="trigger">
|
||||
<div
|
||||
ref="trigger"
|
||||
class="trigger noselect"
|
||||
@click.stop="toggle"
|
||||
>
|
||||
<slot />
|
||||
</div>
|
||||
|
||||
<teleport to="body">
|
||||
<div class="tooltip">
|
||||
<div class="tooltip-wrapper">
|
||||
<slot name="tooltip" />
|
||||
<div
|
||||
v-if="opened"
|
||||
ref="tooltip"
|
||||
class="tooltip-wrapper"
|
||||
:style="{ transform: `translate3d(${tooltipX}px, ${tooltipY}px, 0)` }"
|
||||
>
|
||||
<div class="tooltip-inner">
|
||||
<div class="tooltip">
|
||||
<slot name="tooltip" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</teleport>
|
||||
|
@ -15,23 +26,126 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
import { nextTick } from 'vue';
|
||||
|
||||
function getX(triggerBoundary, tooltipBoundary) {
|
||||
const idealPosition = triggerBoundary.left + (triggerBoundary.width / 2) - (tooltipBoundary.width / 2);
|
||||
|
||||
// don't overflow left edge
|
||||
if (idealPosition < 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// don't overflow right edge
|
||||
if (idealPosition + tooltipBoundary.width > window.innerWidth) {
|
||||
return window.innerWidth - tooltipBoundary.width;
|
||||
}
|
||||
|
||||
// position at the center of trigger
|
||||
return idealPosition;
|
||||
}
|
||||
|
||||
async function calculate() {
|
||||
if (!this.opened) {
|
||||
return;
|
||||
}
|
||||
|
||||
const triggerBoundary = this.$refs.trigger.getBoundingClientRect();
|
||||
const tooltipBoundary = this.$refs.tooltip.getBoundingClientRect();
|
||||
|
||||
this.tooltipY = triggerBoundary.top + triggerBoundary.height;
|
||||
this.tooltipX = this.getX(triggerBoundary, tooltipBoundary);
|
||||
}
|
||||
|
||||
async function open() {
|
||||
this.events.emit('blur');
|
||||
|
||||
await nextTick();
|
||||
|
||||
this.opened = true;
|
||||
await nextTick();
|
||||
|
||||
this.calculate();
|
||||
}
|
||||
|
||||
function close() {
|
||||
this.opened = false;
|
||||
|
||||
this.tooltipY = 0;
|
||||
this.tooltipX = 0;
|
||||
}
|
||||
|
||||
function toggle() {
|
||||
if (this.opened) {
|
||||
this.close();
|
||||
return;
|
||||
}
|
||||
|
||||
this.open();
|
||||
}
|
||||
|
||||
function mounted() {
|
||||
this.events.on('blur', () => {
|
||||
this.close();
|
||||
});
|
||||
|
||||
this.events.on('resize', () => {
|
||||
this.calculate();
|
||||
});
|
||||
}
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
opened: false,
|
||||
tooltipX: 0,
|
||||
tooltipY: 0,
|
||||
};
|
||||
},
|
||||
mounted,
|
||||
methods: {
|
||||
calculate,
|
||||
getX,
|
||||
open,
|
||||
close,
|
||||
toggle,
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.tooltip-container {
|
||||
position: relative;
|
||||
font-size: 1rem;
|
||||
.tooltip-wrapper {
|
||||
display: flex;
|
||||
top: 0;
|
||||
left: 0;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
position: absolute;
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
.tooltip-frame {
|
||||
position: fixed;
|
||||
.tooltip-inner {
|
||||
position: relative;
|
||||
box-shadow: 0 0 3px var(--darken-weak);
|
||||
|
||||
&:after {
|
||||
content: '';
|
||||
width: 0;
|
||||
height: 0;
|
||||
position: absolute;
|
||||
z-index : 11;
|
||||
top: -.5rem;
|
||||
left: calc(50% - .5rem);
|
||||
border-left: .5rem solid transparent;
|
||||
border-right: .5rem solid transparent;
|
||||
border-bottom: .5rem solid var(--background-light);
|
||||
margin: 0 auto;
|
||||
filter: drop-shadow(0 0 3px var(--darken-weak));
|
||||
}
|
||||
}
|
||||
|
||||
.tooltip {
|
||||
position: absolute;
|
||||
z-index: 10;
|
||||
top: 2rem;
|
||||
position: relative;
|
||||
background: var(--background-light);
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
import { createApp, reactive } from 'vue';
|
||||
import dayjs from 'dayjs';
|
||||
import mitt from 'mitt';
|
||||
|
||||
|
||||
import router from './router';
|
||||
import initStore from './store';
|
||||
|
@ -17,6 +19,7 @@ import Tooltip from '../components/tooltip/tooltip.vue';
|
|||
async function init() {
|
||||
const store = initStore(reactive(router));
|
||||
const app = createApp(Container);
|
||||
const events = mitt();
|
||||
|
||||
initUiObservers(store, router);
|
||||
|
||||
|
@ -36,6 +39,11 @@ async function init() {
|
|||
Tooltip,
|
||||
'v-popover': Tooltip,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
events,
|
||||
};
|
||||
},
|
||||
watch: {
|
||||
pageTitle(title) {
|
||||
if (title) {
|
||||
|
|
|
@ -7569,6 +7569,11 @@
|
|||
"minipass": "^2.9.0"
|
||||
}
|
||||
},
|
||||
"mitt": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/mitt/-/mitt-2.1.0.tgz",
|
||||
"integrity": "sha512-ILj2TpLiysu2wkBbWjAmww7TkZb65aiQO+DkVdUTBpBXq+MHYiETENkKFMtsJZX1Lf4pe4QOrTSjIfUwN5lRdg=="
|
||||
},
|
||||
"mixin-deep": {
|
||||
"version": "1.3.2",
|
||||
"resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.2.tgz",
|
||||
|
|
|
@ -107,6 +107,7 @@
|
|||
"knex-migrate": "^1.7.4",
|
||||
"longjohn": "^0.2.12",
|
||||
"mime": "^2.4.4",
|
||||
"mitt": "^2.1.0",
|
||||
"moment": "^2.24.0",
|
||||
"nanoid": "^2.1.11",
|
||||
"opn": "^5.5.0",
|
||||
|
|
Loading…
Reference in New Issue