Repository navigation
feat(tooltip): show at a point - #2561
rkaraivanov wants to merge 8 commits into
Conversation
- Tooltip: `showAt(rect: TooltipRect)` places the tooltip in viewport coordinates. Repeated calls move it with no animation. A point has no triggers and no ARIA relation; the anchor applies again after it closes. - Popover: `anchorRect` anchors the content to a hidden top-layer proxy, correct inside `transform` and `zoom` ancestors, with no new floating-ui dependency. - Fixes: triggers bind once, `anchor` resolves before `show(target)`, a moved tooltip keeps Escape and the pointer, a removed `placement` no longer throws, the arrow stays inside small anchors, the fallback handles `zoom`, a new anchor starts from `placement`. - ChartPointer story with rich slotted content, specs, spec.md, changelog. Refs #2538
- Register only a connected tooltip for Escape; a same-value `open` write is a no-op. - `showAt()` with no rectangle shows at 0,0 instead of throwing. - Fallback: use `fixed` for an anchor inside a top-layer element, keep layout-shift tracking for anchors other than the rectangle, and round to device pixels before the zoom division. - Skip rebinding when the same anchor resolves again; spec cleanups. Refs #2538
There was a problem hiding this comment.
🟡 Changes recommended
The anchor accessor introduces a separate property-change mechanism that conflicts with coding guidelines requiring @coercedProperty and onChange for per-set side effects.
1 open issue
What changed in this PR
Adds showAt(rect: TooltipRect) so tooltips can follow pointer coordinates or computed rectangles without an anchor element, addressing #2538 for canvas, chart, and SVG use cases.
Changes:
- Adds rectangle anchoring to the shared popover implementation and exposes it through the tooltip API.
- Fixes zoom-aware positioning, arrow placement, trigger binding, and tooltip lifecycle behavior.
- Adds positioning tests, a chart example, specifications, and public API documentation.
| File | Description |
|---|---|
stories/tooltip.stories.ts |
Adds a chart demo with pointer-following and bar-anchored tooltips. |
src/internals/utils/dom.ts |
Adds a shared CSS zoom helper. |
src/components/types.ts |
Defines the public TooltipRect type. |
src/components/tooltip/tooltip.ts |
Adds showAt() and synchronizes anchor changes. |
src/components/tooltip/tooltip.spec.ts |
Tests rectangle positioning and tooltip regressions. |
src/components/tooltip/themes/tooltip.base.scss |
Removes a redundant popover background override. |
src/components/tooltip/spec.md |
Documents rectangle anchoring and its behavioral contract. |
src/components/tooltip/service.ts |
Makes the Escape callback collection readonly. |
src/components/tooltip/controller.ts |
Manages rectangle state, triggers, and listener restoration. |
src/components/tabs/tab-dom.ts |
Reuses the shared zoom helper. |
src/components/popover/themes/light/popover.base.scss |
Adds proxy-anchor styling and scopes container styles. |
src/components/popover/spec.md |
Documents rectangle anchors and positioning limits. |
src/components/popover/position/types.ts |
Defines rectangle inputs for positioning strategies. |
src/components/popover/position/strategy.ts |
Adds a dedicated repositioning operation. |
src/components/popover/position/native.ts |
Updates native repositioning and shared arrow handling. |
src/components/popover/position/floating.ts |
Accounts for zoom and top-layer anchors in fallback positioning. |
src/components/popover/position/arrow.ts |
Centralizes zoom-aware arrow centering and bounds. |
src/components/popover/popover.ts |
Implements rectangle proxy anchors and their lifecycle. |
src/components/popover/popover.spec.ts |
Tests rectangle anchors and top-layer positioning. |
public-api.json |
Records the new type, method, and popover property. |
CHANGELOG.md |
Describes the feature and related fixes. |
Review effort: Balanced
use coercedProperty for the anchor property
simeonoff
left a comment
There was a problem hiding this comment.
Ran the full suite in Chromium, and the tooltip/popover/select/combo/dropdown specs in WebKit and Firefox 157 against master. No new failures, and the new showAt() specs pass in all three. Existing behavior stays the same unless showAt() is called, so this looks good to me.
A few non-blocking things:
TooltipRectandPopoverRectdescribe the same thing.PopoverRectalso ends up inpublic-api.jsonthrough theanchorRectproperty ofigc-popover, but the type itself isn't exported. I'd either markanchorRectas@internalor export the type.PopoverRect = Required<TooltipRect>would make the relation explicit._resolveAnchor()intooltip.tsandresolveAnchor()in the controller share a name but do different things. The tooltip one only applies a pendinganchorchange, so inshow()it reads like a forced re-resolve. Something like_applyPendingAnchor()would be clearer.- The "only the rect moved" check in
update()ofpopover.tsrelies onproperties.size === 1. It works today, but if the tooltip changes any other property in the same update, it silently falls back to the slowerupdate()path. Checking the relevant properties by name would be more explicit. - The new
_placementgetter does the same asresolvePlacement()frompopover/position/types.ts. Reusing it keeps the default placement in one place.
- PopoverRect is now Required<TooltipRect>. - Tooltip: _resolveAnchor is renamed _applyPendingAnchor. The pending anchor is applied in update(), not in willUpdate(), because it binds listeners and writes aria-describedby. - Tooltip: the show and hide animations are module constants. The queued-transition check in _applyTooltipState is simpler. The Escape service handles the key synchronously. - Popover: an instanceof check replaces the position mode field. The fallback strategy checks a stale module load against _pendingLoad, not a separate attach counter. Two SCSS if() expressions are plain values, and the built CSS is the same. - Comments that only repeat the code are removed.

Description
Adds
showAt(rect: TooltipRect)toigc-tooltip. It shows the tooltip at a point or against a rectangle in viewport coordinates, with no anchor element. The main use is a tooltip that follows the pointer over a canvas, a chart or an SVG drawing.API
showAt({ x, y, width?, height? }).TooltipRectis a plain object type (jsonAPIPlainObject), so aDOMRectfits it and Blazor can pass a struct.show()keeps its signature, because Blazor cannot easily express a union overload.placement,offset,withArrowand the flip at the viewport edges apply.aria-describedby, and it ignores the pointer on itself. The app shows and hides it.Escapestill closes it.anchorapplies again, also ananchorthat changed while it showed at the point.Fixes found on the way
zoomis now at its anchor, with a centered arrow.-startor-endplacement on a small anchor, it stays inside the tooltip.show-triggersandhide-triggersis handled once.show(target)after ananchorchange in the same task shows at the target.placementno longer throws.Known limits
showAt()again or hide the tooltip.placementagain.Type of Change
Related Issues
Closes #2538
Checklist
npm run public-api:updateand committedpublic-api.jsonCHANGELOG.mdentry under[Unreleased]🤖 Generated with Claude Code