mirror of
https://github.com/go-gitea/gitea.git
synced 2026-07-29 20:07:52 +00:00
enhance(ui): tweak tooltip style and misc fixes (#38524)
Tooltip changes: - center tooltips over their target (default placement `top` instead of `top-start`) - fix the arrow rendering off-center on references narrower than 22px like 16px icons, caused by tippy's hardcoded 3px arrow padding - fix arrowed tooltips overlapping their reference by 1px because the offset distance was derived from the raw arrow option - tweak tooltip colors and reduce content padding - fix a Firefox bug where lazily created tooltips would not show on the first hover, because Firefox skips enter/leave event dispatch when no such listener existed in the window at the time of the pointer crossing Other UI tweaks: - tweak repo sidebar release style - enhance actions back link - center step chevron - add border on workflow graph <img width="92" height="76" alt="Screenshot 2026-07-21 at 18 07 42" src="https://github.com/user-attachments/assets/968a51de-e470-48c0-b592-4a207dfcbb63" /> <img width="94" height="76" alt="Screenshot 2026-07-21 at 18 07 53" src="https://github.com/user-attachments/assets/5e8e0ce3-7536-4689-a6ae-9cfb6e867ddb" /> <img width="203" height="77" alt="Screenshot 2026-07-19 at 00 14 32" src="https://github.com/user-attachments/assets/71c16a54-ad14-4636-8af4-e68b3599f1d0" /> <img width="309" height="290" alt="Screenshot 2026-07-18 at 23 37 21" src="https://github.com/user-attachments/assets/29630504-d69a-4e23-978b-41ae6d0bff92" /> <img width="151" height="110" alt="Screenshot 2026-07-18 at 23 35 47" src="https://github.com/user-attachments/assets/b8107602-12f1-4f9b-a3f6-b930af25c9e7" /> --------- Signed-off-by: wxiaoguang <wxiaoguang@gmail.com> Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
This commit is contained in:
@@ -12,10 +12,21 @@ type TippyOpts = {
|
||||
const visibleInstances = new Set<Instance>();
|
||||
const arrowSvg = html`<svg width="16" height="7"><path d="m0 7 8-7 8 7Z" class="tippy-svg-arrow-outer"/><path d="m0 8 8-7 8 7Z" class="tippy-svg-arrow-inner"/></svg>`;
|
||||
|
||||
// shrink tippy's default 3px arrow padding so the arrow can point at the center of
|
||||
// narrow references like 16px icons with "start"/"end" placements
|
||||
function arrowPadding({placement, reference}: {placement: Placement, reference: {width: number, height: number}}): number {
|
||||
const isVertical = placement.startsWith('left') || placement.startsWith('right');
|
||||
const referenceLength = isVertical ? reference.height : reference.width;
|
||||
return Math.max(0, Math.min(3, referenceLength / 2 - 8)); // 8 = half of arrow width
|
||||
}
|
||||
|
||||
export function createTippy(target: Element, opts: TippyOpts = {}): Instance {
|
||||
// the callback functions should be destructured from opts,
|
||||
// because we should use our own wrapper functions to handle them, do not let the user override them
|
||||
const {onHide, onShow, onDestroy, role, theme, arrow, ...other} = opts;
|
||||
// CSS theme, either "default", "tooltip", "menu", "box-with-header" or "bare"
|
||||
const resolvedTheme = theme || role || 'default';
|
||||
const resolvedArrow = arrow ?? (resolvedTheme === 'bare' ? false : arrowSvg);
|
||||
|
||||
const instance: Instance = tippy(target, {
|
||||
appendTo: document.body,
|
||||
@@ -44,12 +55,12 @@ export function createTippy(target: Element, opts: TippyOpts = {}): Instance {
|
||||
target.setAttribute('aria-controls', instance.popper.id);
|
||||
return onShow?.(instance);
|
||||
},
|
||||
arrow: arrow ?? (theme === 'bare' ? false : arrowSvg),
|
||||
arrow: resolvedArrow,
|
||||
popperOptions: {modifiers: [{name: 'arrow', options: {padding: arrowPadding}}]},
|
||||
// HTML role attribute, ideally the default role would be "popover" but it does not exist
|
||||
role: role || 'menu',
|
||||
// CSS theme, either "default", "tooltip", "menu", "box-with-header" or "bare"
|
||||
theme: theme || role || 'default',
|
||||
offset: [0, arrow ? 10 : 6],
|
||||
theme: resolvedTheme,
|
||||
offset: [0, resolvedArrow ? 10 : 6],
|
||||
plugins: [followCursor],
|
||||
...other,
|
||||
} satisfies Partial<Props>);
|
||||
@@ -87,7 +98,7 @@ function attachTooltip(target: Element, content: Content | null = null): Instanc
|
||||
theme: 'tooltip',
|
||||
hideOnClick,
|
||||
allowHTML: target.getAttribute('data-tooltip-render') === 'html',
|
||||
placement: target.getAttribute('data-tooltip-placement') as Placement || 'top-start',
|
||||
placement: target.getAttribute('data-tooltip-placement') as Placement || 'top',
|
||||
followCursor: target.getAttribute('data-tooltip-follow-cursor') as Props['followCursor'] || false,
|
||||
...((target.getAttribute('data-tooltip-interactive') === 'true') && {interactive: true, aria: {content: 'describedby', expanded: false}}),
|
||||
};
|
||||
@@ -115,9 +126,14 @@ function switchTitleToTooltip(target: Element): void {
|
||||
* Some browsers like PaleMoon don't support "addEventListener('mouseenter', capture)"
|
||||
* The tippy by default uses "mouseenter" event to show, so we use "mouseover" event to switch to tippy
|
||||
*/
|
||||
function lazyTooltipOnMouseHover(this: HTMLElement, e: Event): void {
|
||||
(e.target as HTMLElement).removeEventListener('mouseover', lazyTooltipOnMouseHover, true);
|
||||
attachTooltip(this);
|
||||
function lazyTooltipOnMouseHover(e: Event): void {
|
||||
const el = e.currentTarget as HTMLElement;
|
||||
el.removeEventListener('mouseover', lazyTooltipOnMouseHover, true);
|
||||
// Firefox skips enter/leave dispatch when the window had no such listeners at the time of the
|
||||
// pointer crossing, so the new tippy misses its first "mouseenter". Show via a synthetic event,
|
||||
// carrying over the cursor position for "followCursor" tooltips.
|
||||
const {clientX, clientY} = e as MouseEvent;
|
||||
attachTooltip(el)?.reference.dispatchEvent(new MouseEvent('mouseenter', {clientX, clientY}));
|
||||
}
|
||||
|
||||
// Activate the tooltip for current element.
|
||||
|
||||
Reference in New Issue
Block a user