mirror of
https://github.com/isledecomp/isle.pizza.git
synced 2026-02-28 13:57:38 +00:00
Add dynamic tooltip positioning using Floating UI (#18)
- Use @floating-ui/dom for intelligent tooltip placement - Tooltips now shift/flip to stay within viewport bounds - Collapse hidden tooltips to prevent horizontal scroll overflow - Move tooltip setup to App.svelte for global coverage
This commit is contained in:
parent
eed65faeac
commit
a680bc67ef
23
package-lock.json
generated
23
package-lock.json
generated
@ -8,6 +8,7 @@
|
||||
"name": "isle.pizza",
|
||||
"version": "1.0.0",
|
||||
"dependencies": {
|
||||
"@floating-ui/dom": "^1.7.5",
|
||||
"three": "^0.182.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
@ -1892,6 +1893,28 @@
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/@floating-ui/core": {
|
||||
"version": "1.7.4",
|
||||
"resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.7.4.tgz",
|
||||
"integrity": "sha512-C3HlIdsBxszvm5McXlB8PeOEWfBhcGBTZGkGlWc2U0KFY5IwG5OQEuQ8rq52DZmcHDlPLd+YFBK+cZcytwIFWg==",
|
||||
"dependencies": {
|
||||
"@floating-ui/utils": "^0.2.10"
|
||||
}
|
||||
},
|
||||
"node_modules/@floating-ui/dom": {
|
||||
"version": "1.7.5",
|
||||
"resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.7.5.tgz",
|
||||
"integrity": "sha512-N0bD2kIPInNHUHehXhMke1rBGs1dwqvC9O9KYMyyjK7iXt7GAhnro7UlcuYcGdS/yYOlq0MAVgrow8IbWJwyqg==",
|
||||
"dependencies": {
|
||||
"@floating-ui/core": "^1.7.4",
|
||||
"@floating-ui/utils": "^0.2.10"
|
||||
}
|
||||
},
|
||||
"node_modules/@floating-ui/utils": {
|
||||
"version": "0.2.10",
|
||||
"resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.10.tgz",
|
||||
"integrity": "sha512-aGTxbpbg8/b5JfU1HXSrbH3wXZuLPJcNEcZQFMxLs3oSzgtVu6nFPkbbGGUvBcUjKV2YyB9Wxxabo+HEH9tcRQ=="
|
||||
},
|
||||
"node_modules/@isaacs/balanced-match": {
|
||||
"version": "4.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@isaacs/balanced-match/-/balanced-match-4.0.1.tgz",
|
||||
|
||||
@ -11,6 +11,7 @@
|
||||
"prepare:assets": "node scripts/prepare.js"
|
||||
},
|
||||
"dependencies": {
|
||||
"@floating-ui/dom": "^1.7.5",
|
||||
"three": "^0.182.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
<script>
|
||||
import { onMount } from 'svelte';
|
||||
import { computePosition, flip, shift, offset } from '@floating-ui/dom';
|
||||
import { currentPage, debugEnabled } from './stores.js';
|
||||
import { registerServiceWorker, checkCacheStatus } from './core/service-worker.js';
|
||||
import { setupCanvasEvents } from './core/emscripten.js';
|
||||
@ -15,6 +16,54 @@
|
||||
import DebugPanel from './lib/DebugPanel.svelte';
|
||||
import CanvasWrapper from './lib/CanvasWrapper.svelte';
|
||||
|
||||
async function positionTooltip(trigger) {
|
||||
const tooltip = trigger.querySelector('.tooltip-content');
|
||||
if (!tooltip) return;
|
||||
|
||||
const { x, y } = await computePosition(trigger, tooltip, {
|
||||
placement: 'top',
|
||||
middleware: [
|
||||
offset(8),
|
||||
flip(),
|
||||
shift({ padding: 8 })
|
||||
]
|
||||
});
|
||||
|
||||
Object.assign(tooltip.style, {
|
||||
left: `${x}px`,
|
||||
top: `${y}px`
|
||||
});
|
||||
}
|
||||
|
||||
function setupTooltips() {
|
||||
const isTouchDevice = window.matchMedia('(any-pointer: coarse)').matches;
|
||||
|
||||
// Touch devices: position and show on click
|
||||
document.addEventListener('click', (e) => {
|
||||
const trigger = e.target.closest('.tooltip-trigger');
|
||||
if (trigger) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
const wasActive = trigger.classList.contains('active');
|
||||
document.querySelectorAll('.tooltip-trigger.active').forEach(t => t.classList.remove('active'));
|
||||
if (!wasActive) {
|
||||
positionTooltip(trigger);
|
||||
trigger.classList.add('active');
|
||||
}
|
||||
} else {
|
||||
document.querySelectorAll('.tooltip-trigger.active').forEach(t => t.classList.remove('active'));
|
||||
}
|
||||
});
|
||||
|
||||
// Desktop: position on hover
|
||||
if (!isTouchDevice) {
|
||||
document.addEventListener('mouseenter', (e) => {
|
||||
const trigger = e.target.closest('.tooltip-trigger');
|
||||
if (trigger) positionTooltip(trigger);
|
||||
}, true);
|
||||
}
|
||||
}
|
||||
|
||||
onMount(async () => {
|
||||
// Disable browser's automatic scroll restoration
|
||||
if ('scrollRestoration' in history) {
|
||||
@ -30,6 +79,9 @@
|
||||
// Setup canvas events
|
||||
setupCanvasEvents();
|
||||
|
||||
// Setup global tooltip positioning
|
||||
setupTooltips();
|
||||
|
||||
// Initialize history state based on current page
|
||||
const initialHash = window.location.hash;
|
||||
if (initialHash) {
|
||||
|
||||
16
src/app.css
16
src/app.css
@ -786,13 +786,13 @@ body {
|
||||
|
||||
.tooltip-content {
|
||||
position: absolute;
|
||||
bottom: 140%;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
width: 220px;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: max-content;
|
||||
max-width: 220px;
|
||||
background-color: var(--color-bg-panel);
|
||||
color: var(--color-text-light);
|
||||
padding: 10px;
|
||||
padding: 0;
|
||||
border-radius: 5px;
|
||||
font-size: 0.85em;
|
||||
font-weight: normal;
|
||||
@ -802,14 +802,18 @@ body {
|
||||
z-index: 10000;
|
||||
opacity: 0;
|
||||
visibility: hidden;
|
||||
transition: opacity 0.2s, visibility 0.2s;
|
||||
pointer-events: none;
|
||||
overflow: hidden;
|
||||
max-height: 0;
|
||||
}
|
||||
|
||||
.tooltip-trigger:hover>.tooltip-content,
|
||||
.tooltip-trigger.active>.tooltip-content {
|
||||
opacity: 1;
|
||||
visibility: visible;
|
||||
padding: 10px;
|
||||
max-height: none;
|
||||
overflow: visible;
|
||||
}
|
||||
|
||||
.option-list {
|
||||
|
||||
@ -101,28 +101,8 @@
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Setup tooltip handling for touch devices
|
||||
if (isTouchDevice) {
|
||||
setupTouchTooltips();
|
||||
}
|
||||
});
|
||||
|
||||
function setupTouchTooltips() {
|
||||
document.addEventListener('click', (e) => {
|
||||
const trigger = e.target.closest('.tooltip-trigger');
|
||||
if (trigger) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
const wasActive = trigger.classList.contains('active');
|
||||
document.querySelectorAll('.tooltip-trigger.active').forEach(t => t.classList.remove('active'));
|
||||
if (!wasActive) trigger.classList.add('active');
|
||||
} else {
|
||||
document.querySelectorAll('.tooltip-trigger.active').forEach(t => t.classList.remove('active'));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function getSiFiles() {
|
||||
const hdMusic = document.getElementById('check-hd-music');
|
||||
const widescreenBgs = document.getElementById('check-widescreen-bgs');
|
||||
|
||||
Loading…
Reference in New Issue
Block a user