Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions RELEASE_NOTES.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@

## [Unreleased]

### Changed
* Tooltip elements (`div.fsdocs-tip`) now use the [Popover API](https://developer.mozilla.org/en-US/docs/Web/API/Popover_API) when available (Baseline 2024). This places tooltips in the browser's top layer, ensuring they always render above overlapping content without relying on `z-index`. Also fixes a positioning bug where tooltips would appear offset when the page was scrolled. Falls back to the previous `display` toggle approach in browsers without Popover API support. [#422](https://github.com/fsprojects/FSharp.Formatting/issues/422)

### Refactored
* Split `MarkdownParser.fs` (1500 lines) into `MarkdownInlineParser.fs` (inline formatting) and `MarkdownParser.fs` (block-level parsing) for better maintainability. [#1022](https://github.com/fsprojects/FSharp.Formatting/issues/1022)

Expand Down
6 changes: 6 additions & 0 deletions docs/content/fsdocs-default.css
Original file line number Diff line number Diff line change
Expand Up @@ -1026,12 +1026,18 @@ div.fsdocs-tip {
font-variant-ligatures: none;
color: var(--code-color);
box-shadow: 0 1px 1px var(--shadow-color);
margin: 0;

& code {
color: var(--code-color);
}
}

/* When using the Popover API, the browser adds :popover-open when shown */
div.fsdocs-tip:popover-open {
display: block;
}

span[onmouseout] {
cursor: pointer;
}
Expand Down
52 changes: 38 additions & 14 deletions docs/content/fsdocs-tips.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,35 @@ let currentTipElement = null;

function hideTip(evt, name, unique) {
const el = document.getElementById(name);
el.style.display = "none";
if (el) {
if (el.hidePopover) {
try { el.hidePopover(); } catch (_) { }
} else {
el.style.display = "none";
}
}
currentTip = null;
currentTipElement = null;
}

function hideUsingEsc(e) {
hideTip(e, currentTipElement, currentTip);
if (currentTipElement) {
hideTip(e, currentTipElement, currentTip);
}
}

function showTip(evt, name, unique, owner) {
document.onkeydown = hideUsingEsc;
if (currentTip === unique) return;

// Hide the previously shown tooltip (for non-auto-popover fallback path)
if (currentTipElement !== null) {
const prev = document.getElementById(currentTipElement);
if (prev && !prev.showPopover) {
prev.style.display = "none";
}
}

currentTip = unique;
currentTipElement = name;

Expand All @@ -22,25 +40,31 @@ function showTip(evt, name, unique, owner) {
let y = evt.clientY + offset;

const el = document.getElementById(name);
el.style.position = "absolute";
el.style.display = "block";
el.style.left = `${x}px`;
el.style.top = `${y}px`;
const maxWidth = document.documentElement.clientWidth - x - 16;
el.style.maxWidth = `${maxWidth}px`;
el.style.left = `${x}px`;
el.style.top = `${y}px`;

if (el.showPopover) {
// Popover API path: element is placed in the top layer with fixed positioning
el.style.position = "fixed";
el.showPopover();
} else {
// Fallback for browsers without Popover API support
el.style.position = "absolute";
el.style.display = "block";
}

const rect = el.getBoundingClientRect();
// Move tooltip if it is out of sight
if(rect.bottom > window.innerHeight) {
const rect = el.getBoundingClientRect();
// Move tooltip if it would appear outside the viewport
if (rect.bottom > window.innerHeight) {
y = y - el.clientHeight - offset;
el.style.top = `${y}px`;
}

if (rect.right > window.innerWidth) {
x = y - el.clientWidth - offset;
x = x - el.clientWidth - offset;
el.style.left = `${x}px`;
const maxWidth = document.documentElement.clientWidth - x - 16;
el.style.maxWidth = `${maxWidth}px`;
el.style.maxWidth = `${document.documentElement.clientWidth - x - 16}px`;
}
}

Expand All @@ -60,4 +84,4 @@ function Clipboard_CopyTo(value) {
window.showTip = showTip;
window.hideTip = hideTip;
// Used by API documentation
window.Clipboard_CopyTo = Clipboard_CopyTo;
window.Clipboard_CopyTo = Clipboard_CopyTo;
2 changes: 1 addition & 1 deletion src/FSharp.Formatting.CodeFormat/HtmlFormatting.fs
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ type ToolTipFormatter(prefix) =
/// Returns all generated tool tip elements
member x.WriteTipElements(writer: TextWriter) =
for (KeyValue(_, (index, html))) in tips do
writer.WriteLine(sprintf "<div class=\"fsdocs-tip\" id=\"%s%d\">%s</div>" prefix index html)
writer.WriteLine(sprintf "<div popover class=\"fsdocs-tip\" id=\"%s%d\">%s</div>" prefix index html)


/// Represents context used by the formatter
Expand Down