This commit is contained in:
Doruk 2025-06-15 18:13:50 +02:00
parent b1bf26289e
commit 89fc5f1665
2 changed files with 67 additions and 60 deletions

View file

@ -30,11 +30,10 @@
<div v-if="$root.styleElapsedTime === 'with-line'" class="connecting-line"></div> <div v-if="$root.styleElapsedTime === 'with-line'" class="connecting-line"></div>
<div>{{ timeSinceLastBeat }}</div> <div>{{ timeSinceLastBeat }}</div>
</div> </div>
<!-- Custom Tooltip --> <!-- Custom Tooltip -->
<Tooltip <Tooltip
:visible="tooltipVisible" :visible="tooltipVisible"
:content="tooltipContent" :content="tooltipContent"
:x="tooltipX" :x="tooltipX"
:y="tooltipY" :y="tooltipY"
@ -86,7 +85,7 @@ export default {
tooltipContent: null, tooltipContent: null,
tooltipX: 0, tooltipX: 0,
tooltipY: 0, tooltipY: 0,
tooltipPosition: 'below', tooltipPosition: "below",
tooltipTimeoutId: null, tooltipTimeoutId: null,
}; };
}, },
@ -369,7 +368,7 @@ export default {
const statusText = { const statusText = {
0: "Down", 0: "Down",
1: "Up", 1: "Up",
2: "Pending", 2: "Pending",
3: "Maintenance" 3: "Maintenance"
}[beat.status] || "Unknown"; }[beat.status] || "Unknown";
@ -396,40 +395,40 @@ export default {
// Small delay for better UX // Small delay for better UX
this.tooltipTimeoutId = setTimeout(() => { this.tooltipTimeoutId = setTimeout(() => {
this.tooltipContent = beat; this.tooltipContent = beat;
// Calculate position relative to viewport // Calculate position relative to viewport
const rect = event.target.getBoundingClientRect(); const rect = event.target.getBoundingClientRect();
// Position relative to viewport // Position relative to viewport
const x = rect.left + (rect.width / 2); const x = rect.left + (rect.width / 2);
const y = rect.top; const y = rect.top;
// Check if tooltip would go off-screen and adjust position // Check if tooltip would go off-screen and adjust position
const tooltipHeight = 80; // Approximate tooltip height const tooltipHeight = 80; // Approximate tooltip height
const viewportHeight = window.innerHeight; const viewportHeight = window.innerHeight;
const spaceAbove = y; const spaceAbove = y;
const spaceBelow = viewportHeight - y - rect.height; const spaceBelow = viewportHeight - y - rect.height;
if (spaceAbove > tooltipHeight && spaceBelow < tooltipHeight) { if (spaceAbove > tooltipHeight && spaceBelow < tooltipHeight) {
// Show above - arrow points down // Show above - arrow points down
this.tooltipPosition = 'above'; this.tooltipPosition = "above";
this.tooltipY = y - 10; this.tooltipY = y - 10;
} else { } else {
// Show below - arrow points up // Show below - arrow points up
this.tooltipPosition = 'below'; this.tooltipPosition = "below";
this.tooltipY = y + rect.height + 10; this.tooltipY = y + rect.height + 10;
} }
// Ensure tooltip doesn't go off the left or right edge // Ensure tooltip doesn't go off the left or right edge
const tooltipWidth = 120; // Approximate tooltip width const tooltipWidth = 120; // Approximate tooltip width
let adjustedX = x; let adjustedX = x;
if (x - tooltipWidth/2 < 10) { if (x - tooltipWidth / 2 < 10) {
adjustedX = tooltipWidth/2 + 10; adjustedX = tooltipWidth / 2 + 10;
} else if (x + tooltipWidth/2 > window.innerWidth - 10) { } else if (x + tooltipWidth / 2 > window.innerWidth - 10) {
adjustedX = window.innerWidth - tooltipWidth/2 - 10; adjustedX = window.innerWidth - tooltipWidth / 2 - 10;
} }
this.tooltipX = adjustedX; this.tooltipX = adjustedX;
this.tooltipVisible = true; this.tooltipVisible = true;
}, 150); }, 150);
@ -443,7 +442,7 @@ export default {
clearTimeout(this.tooltipTimeoutId); clearTimeout(this.tooltipTimeoutId);
this.tooltipTimeoutId = null; this.tooltipTimeoutId = null;
} }
this.tooltipVisible = false; this.tooltipVisible = false;
this.tooltipContent = null; this.tooltipContent = null;
}, },

View file

@ -1,6 +1,6 @@
<template> <template>
<teleport to="body"> <teleport to="body">
<div <div
v-if="visible && content" v-if="visible && content"
ref="tooltip" ref="tooltip"
class="tooltip-wrapper" class="tooltip-wrapper"
@ -41,7 +41,7 @@ export default {
type: Number, type: Number,
default: 0 default: 0
}, },
/** Y position (viewport coordinates) */ /** Y position (viewport coordinates) */
y: { y: {
type: Number, type: Number,
default: 0 default: 0
@ -49,50 +49,58 @@ export default {
/** Position relative to target element */ /** Position relative to target element */
position: { position: {
type: String, type: String,
default: 'below', default: "below",
validator: (value) => ['above', 'below'].includes(value) validator: (value) => [ "above", "below" ].includes(value)
} }
}, },
computed: { computed: {
tooltipStyle() { tooltipStyle() {
return { return {
left: this.x + 'px', left: this.x + "px",
top: this.y + 'px', top: this.y + "px",
}; };
}, },
statusText() { statusText() {
if (!this.content || this.content === 0) return this.$t('Unknown'); if (!this.content || this.content === 0) {
return this.$t("Unknown");
}
const statusMap = { const statusMap = {
0: this.$t('Down'), 0: this.$t("Down"),
1: this.$t('Up'), 1: this.$t("Up"),
2: this.$t('Pending'), 2: this.$t("Pending"),
3: this.$t('Maintenance') 3: this.$t("Maintenance")
}; };
return statusMap[this.content.status] || this.$t('Unknown'); return statusMap[this.content.status] || this.$t("Unknown");
}, },
statusClass() { statusClass() {
if (!this.content || this.content === 0) return 'status-empty'; if (!this.content || this.content === 0) {
return "status-empty";
}
const classMap = { const classMap = {
0: 'status-down', 0: "status-down",
1: 'status-up', 1: "status-up",
2: 'status-pending', 2: "status-pending",
3: 'status-maintenance' 3: "status-maintenance"
}; };
return classMap[this.content.status] || 'status-unknown'; return classMap[this.content.status] || "status-unknown";
}, },
timeText() { timeText() {
if (!this.content || this.content === 0) return ''; if (!this.content || this.content === 0) {
return "";
}
return this.$root.datetime(this.content.time); return this.$root.datetime(this.content.time);
}, },
message() { message() {
if (!this.content || this.content === 0) return ''; if (!this.content || this.content === 0) {
return this.content.msg || ''; return "";
}
return this.content.msg || "";
} }
} }
}; };
@ -106,7 +114,7 @@ export default {
z-index: 9999; z-index: 9999;
pointer-events: none; pointer-events: none;
transform: translateX(-50%); transform: translateX(-50%);
.tooltip-content { .tooltip-content {
background: rgba(17, 24, 39, 0.95); background: rgba(17, 24, 39, 0.95);
backdrop-filter: blur(8px); backdrop-filter: blur(8px);
@ -116,41 +124,41 @@ export default {
box-shadow: 0 10px 25px rgba(0, 0, 0, 0.25); box-shadow: 0 10px 25px rgba(0, 0, 0, 0.25);
min-width: 120px; min-width: 120px;
text-align: center; text-align: center;
.tooltip-status { .tooltip-status {
font-size: 12px; font-size: 12px;
font-weight: 600; font-weight: 600;
margin-bottom: 4px; margin-bottom: 4px;
text-transform: uppercase; text-transform: uppercase;
letter-spacing: 0.5px; letter-spacing: 0.5px;
&.status-up { &.status-up {
color: $primary; color: $primary;
} }
&.status-down { &.status-down {
color: $danger; color: $danger;
} }
&.status-pending { &.status-pending {
color: $warning; color: $warning;
} }
&.status-maintenance { &.status-maintenance {
color: $maintenance; color: $maintenance;
} }
&.status-empty { &.status-empty {
color: $secondary-text; color: $secondary-text;
} }
} }
.tooltip-time { .tooltip-time {
color: #d1d5db; color: #d1d5db;
font-size: 11px; font-size: 11px;
margin-bottom: 2px; margin-bottom: 2px;
} }
.tooltip-message { .tooltip-message {
color: #f3f4f6; color: #f3f4f6;
font-size: 10px; font-size: 10px;
@ -159,7 +167,7 @@ export default {
border-top: 1px solid rgba(75, 85, 99, 0.3); border-top: 1px solid rgba(75, 85, 99, 0.3);
} }
} }
.tooltip-arrow { .tooltip-arrow {
position: absolute; position: absolute;
left: 50%; left: 50%;
@ -168,11 +176,11 @@ export default {
height: 0; height: 0;
border-left: 6px solid transparent; border-left: 6px solid transparent;
border-right: 6px solid transparent; border-right: 6px solid transparent;
// Default: tooltip below element, arrow points up // Default: tooltip below element, arrow points up
border-bottom: 6px solid rgba(17, 24, 39, 0.95); border-bottom: 6px solid rgba(17, 24, 39, 0.95);
top: -6px; top: -6px;
&.arrow-above { &.arrow-above {
// Tooltip above element, arrow points down // Tooltip above element, arrow points down
top: auto; top: auto;
@ -181,10 +189,10 @@ export default {
border-top: 6px solid rgba(17, 24, 39, 0.95); border-top: 6px solid rgba(17, 24, 39, 0.95);
} }
} }
// Smooth entrance animation // Smooth entrance animation
animation: tooltip-fade-in 0.2s $easing-out; animation: tooltip-fade-in 0.2s $easing-out;
&.tooltip-above { &.tooltip-above {
transform: translateX(-50%) translateY(-8px); transform: translateX(-50%) translateY(-8px);
} }
@ -196,10 +204,10 @@ export default {
background: rgba(31, 41, 55, 0.95); background: rgba(31, 41, 55, 0.95);
border-color: rgba(107, 114, 128, 0.3); border-color: rgba(107, 114, 128, 0.3);
} }
.tooltip-arrow { .tooltip-arrow {
border-bottom-color: rgba(31, 41, 55, 0.95); border-bottom-color: rgba(31, 41, 55, 0.95);
&.arrow-above { &.arrow-above {
border-top-color: rgba(31, 41, 55, 0.95); border-top-color: rgba(31, 41, 55, 0.95);
} }
@ -223,4 +231,4 @@ export default {
animation: none !important; animation: none !important;
} }
} }
</style> </style>