ux(tooltip): fix(tooltip): move tooltip into top-bar, that fix issue with RTE languages
This commit is contained in:
@@ -1,29 +1,19 @@
|
||||
/* ── Item tooltip — "technical sheet" ─────────────────────────────────────── */
|
||||
/* */
|
||||
/* Overlays the search bar area in the top-bar while hovering a file item. */
|
||||
/* Spans from the sidebar right-edge to just before .user-controls. */
|
||||
/* Absolutely overlays `.search-slot` in the top-bar. The search bar stays in */
|
||||
/* place underneath; only the tooltip's opacity is animated, so there is no */
|
||||
/* second element to keep in sync and no display:none flicker between rapid */
|
||||
/* hover transitions. RTL just works (inset:0). */
|
||||
/* */
|
||||
/* 3-column CSS grid: [icon] [label] [value] */
|
||||
/* All values start at the same x position regardless of label width. */
|
||||
/* 3-column CSS grid: [icon] [label] [value] */
|
||||
/* All values start at the same x position regardless of label width. */
|
||||
|
||||
.path-tooltip {
|
||||
position: fixed;
|
||||
|
||||
/* Fill most of the top-bar (topbar height 70px, 8px margin top/bottom) */
|
||||
top: 8px;
|
||||
|
||||
/* Desktop: starts at sidebar right edge + top-bar padding */
|
||||
left: calc(var(--sidebar-width) + 30px);
|
||||
|
||||
/*
|
||||
* Right edge stops just before .user-controls:
|
||||
* 30px (top-bar right padding) + ~90px (notif bell + gap + avatar btn) + 4px gap
|
||||
* Using left + right instead of width so the browser computes it dynamically.
|
||||
*/
|
||||
right: 124px;
|
||||
|
||||
/* High enough to sit above the top-bar content */
|
||||
z-index: 200;
|
||||
/* Cover the search slot, sit above it (later in DOM order). */
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
/* Opaque background so the search bar underneath is fully masked. */
|
||||
background-color: var(--color-bg-surface);
|
||||
|
||||
display: grid;
|
||||
grid-template-columns: 1em max-content 1fr;
|
||||
@@ -31,20 +21,24 @@
|
||||
row-gap: 6px;
|
||||
align-items: center;
|
||||
|
||||
/* Fill the top-bar height minus an 8px margin on each side */
|
||||
height: 54px;
|
||||
padding: 0 16px;
|
||||
border-radius: 10px;
|
||||
border: 2px solid var(--color-border-medium);
|
||||
|
||||
background-color: var(--color-bg-input);
|
||||
color: var(--color-text-muted);
|
||||
font-size: 0.75rem;
|
||||
|
||||
pointer-events: none;
|
||||
|
||||
/* avoid blinking when pointer moves between items */
|
||||
transition: display 0.2s allow-discrete;
|
||||
/* Show instantly. The hide direction (.hidden) overrides this with a
|
||||
1s delay so the tooltip lingers after the mouse leaves an item;
|
||||
rapid item-to-item moves cancel the pending fade before it fires. */
|
||||
opacity: 1;
|
||||
transition: opacity 0.2s ease 0s;
|
||||
}
|
||||
|
||||
/* Hidden state — override the global `.hidden { display:none !important }`
|
||||
so we can fade with opacity instead of snapping to display:none. */
|
||||
.path-tooltip.hidden {
|
||||
display: grid !important;
|
||||
opacity: 0;
|
||||
transition-delay: 1s;
|
||||
}
|
||||
|
||||
/* Icon column */
|
||||
@@ -85,23 +79,6 @@
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
/* ── Narrow screens (sidebar hidden) ────────────────────────────────────── */
|
||||
/* On mobile the sidebar slides off-screen; align after the #sidebar-toggle */
|
||||
/* button (≈44px) that takes its place in the top-bar. */
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.path-tooltip {
|
||||
/* 16px topbar padding + ~44px sidebar-toggle + 8px gap */
|
||||
left: 68px;
|
||||
|
||||
/*
|
||||
* Right edge stops before search-toggle-btn + user-controls:
|
||||
* ~40px (search-toggle) + 12px (gap) + ~90px (user-controls) + 16px (padding)
|
||||
*/
|
||||
right: 158px;
|
||||
}
|
||||
}
|
||||
|
||||
/* ── Vignette inside tooltip ─────────────────────────────────────────────── */
|
||||
/* The vignette name hard-codes 14px; scale it down to match the tooltip. */
|
||||
|
||||
|
||||
@@ -86,11 +86,14 @@
|
||||
color: var(--color-text-heading);
|
||||
}
|
||||
|
||||
.search-container {
|
||||
/* Slot wrapping .search-container + #path-tooltip — it owns the flex slot
|
||||
so the tooltip can absolutely overlay the search bar via inset:0 instead
|
||||
of viewport-fixed positioning. */
|
||||
.search-slot {
|
||||
flex-grow: 1;
|
||||
max-width: 600px;
|
||||
position: relative;
|
||||
margin-right: 20px;
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
@@ -100,6 +103,13 @@
|
||||
}
|
||||
}
|
||||
|
||||
.search-container {
|
||||
flex-grow: 1;
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.search-container input {
|
||||
width: 100%;
|
||||
padding: 12px 50px 12px 44px;
|
||||
@@ -204,8 +214,8 @@
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
/* Hide full search bar on mobile by default */
|
||||
.search-container {
|
||||
/* Hide full search slot on mobile by default */
|
||||
.search-slot {
|
||||
display: none;
|
||||
}
|
||||
|
||||
@@ -224,7 +234,7 @@
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.top-bar--search-active .search-container {
|
||||
.top-bar--search-active .search-slot {
|
||||
display: flex;
|
||||
flex-grow: 1;
|
||||
max-width: none;
|
||||
|
||||
+11
-6
@@ -136,12 +136,17 @@
|
||||
<button class="search-back-btn" id="search-back-btn" aria-label="Close search">
|
||||
<i class="fas fa-arrow-left"></i>
|
||||
</button>
|
||||
<div class="search-container">
|
||||
<i class="fas fa-search search-icon"></i>
|
||||
<input type="text" id="search-input" data-i18n-placeholder="actions.search" placeholder="Search files, folders...">
|
||||
<button id="search-button" class="search-button" data-i18n-title="actions.search_btn" title="Search">
|
||||
<i class="fas fa-search"></i>
|
||||
</button>
|
||||
<!-- Search slot — positioning context for the item tooltip, which
|
||||
absolutely overlays the search bar when an item is hovered. -->
|
||||
<div class="search-slot">
|
||||
<div class="search-container">
|
||||
<i class="fas fa-search search-icon"></i>
|
||||
<input type="text" id="search-input" data-i18n-placeholder="actions.search" placeholder="Search files, folders...">
|
||||
<button id="search-button" class="search-button" data-i18n-title="actions.search_btn" title="Search">
|
||||
<i class="fas fa-search"></i>
|
||||
</button>
|
||||
</div>
|
||||
<div id="path-tooltip" class="path-tooltip hidden" aria-hidden="true"></div>
|
||||
</div>
|
||||
|
||||
<div class="user-controls">
|
||||
|
||||
@@ -27,21 +27,19 @@ import { i18n } from '../core/i18n.js';
|
||||
import { systemUsers } from '../model/systemUsers.js';
|
||||
|
||||
// ── Tooltip DOM ───────────────────────────────────────────────────────────────
|
||||
//
|
||||
// The tooltip element is a static node inside `.search-slot` (see index.html)
|
||||
// and is absolutely positioned over `.search-container`. Show/hide is a
|
||||
// simple `.hidden` toggle on the tooltip itself — the search bar underneath
|
||||
// is never touched, so there's no two-element swap to keep in sync.
|
||||
|
||||
/** @returns {HTMLElement} */
|
||||
function _getOrCreateTooltip() {
|
||||
let el = document.getElementById('path-tooltip');
|
||||
if (!el) {
|
||||
el = document.createElement('div');
|
||||
el.id = 'path-tooltip';
|
||||
el.className = 'path-tooltip hidden';
|
||||
document.body.appendChild(el);
|
||||
}
|
||||
return el;
|
||||
/** @returns {HTMLElement | null} */
|
||||
function _getTooltip() {
|
||||
return document.getElementById('path-tooltip');
|
||||
}
|
||||
|
||||
function _hide() {
|
||||
const el = document.getElementById('path-tooltip');
|
||||
const el = _getTooltip();
|
||||
if (el) el.classList.add('hidden');
|
||||
}
|
||||
|
||||
@@ -98,7 +96,8 @@ function _onEnter(e) {
|
||||
// Nothing to show — don't display an all-? tooltip.
|
||||
if (!ownerId && !path) return;
|
||||
|
||||
const tooltip = _getOrCreateTooltip();
|
||||
const tooltip = _getTooltip();
|
||||
if (!tooltip) return;
|
||||
|
||||
// Clear previous content.
|
||||
while (tooltip.firstChild) tooltip.removeChild(tooltip.firstChild);
|
||||
@@ -140,14 +139,19 @@ const _registry = new WeakMap();
|
||||
|
||||
// ── Public API ────────────────────────────────────────────────────────────────
|
||||
|
||||
/** Containers known to expose `data-path`/`data-owner-id` for tooltip use.
|
||||
* Add new opt-in row classes here when other views want the tooltip — each
|
||||
* one must stamp the dataset attributes itself. */
|
||||
const _TOOLTIP_SELECTOR = '.file-item, .ms-resource-row';
|
||||
|
||||
/**
|
||||
* Attach tooltip listeners to every `.file-item` inside `container`.
|
||||
* Items with neither `data-owner-id` nor `data-path` will not trigger the
|
||||
* Attach tooltip listeners to every tooltip-capable row inside `container`.
|
||||
* Rows with neither `data-owner-id` nor `data-path` will not trigger the
|
||||
* tooltip. Safe to call repeatedly — already-wired elements are skipped.
|
||||
* @param {HTMLElement} container
|
||||
*/
|
||||
function init(container) {
|
||||
for (const item of container.querySelectorAll('.file-item')) {
|
||||
for (const item of container.querySelectorAll(_TOOLTIP_SELECTOR)) {
|
||||
const el = /** @type {HTMLElement} */ (item);
|
||||
if (_registry.has(el)) continue; // already wired
|
||||
|
||||
@@ -161,12 +165,12 @@ function init(container) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove tooltip listeners from all `.file-item` elements inside `container`
|
||||
* Remove tooltip listeners from all tooltip-capable rows inside `container`
|
||||
* and hide any visible tooltip.
|
||||
* @param {HTMLElement} container
|
||||
*/
|
||||
function destroy(container) {
|
||||
for (const item of container.querySelectorAll('.file-item')) {
|
||||
for (const item of container.querySelectorAll(_TOOLTIP_SELECTOR)) {
|
||||
const el = /** @type {HTMLElement} */ (item);
|
||||
const h = _registry.get(el);
|
||||
if (h) {
|
||||
|
||||
Reference in New Issue
Block a user