diff --git a/packages/core/src/components/axes/toolbar.ts b/packages/core/src/components/axes/toolbar.ts index 29c41d6700..90a352fedd 100644 --- a/packages/core/src/components/axes/toolbar.ts +++ b/packages/core/src/components/axes/toolbar.ts @@ -84,6 +84,7 @@ export class Toolbar extends Component { .merge(toolbarControls as any) .classed('disabled', (d: any) => d.shouldBeDisabled()) .attr('aria-disabled', (d: any) => d.shouldBeDisabled()) + .attr('role', 'button') .attr('aria-label', (d: any) => d.title) .html((d: any) => { return ` @@ -100,12 +101,33 @@ export class Toolbar extends Component { .each(function (d: any, index: number) { select(this) .select('svg') + .style('pointer-events', 'none') .style('will-change', 'transform') .style('width', d.iconSVG.width !== undefined ? d.iconSVG.width : '20px') .style('height', d.iconSVG.height !== undefined ? d.iconSVG.height : '20px') select(this) .select('button') + .on('mouseover focus', function (event: MouseEvent) { + const hoveredElement = select(this) + hoveredElement.classed('hovered', true) + self.services.events.dispatchEvent(Events.Tooltip.SHOW, { + event, + hoveredElement, + content: d.title, + placement: 'top' + }) + }) + .on('mousemove focusin', function (event: MouseEvent) { + self.services.events.dispatchEvent(Events.Tooltip.MOVE, { + event, + content: d.title, + placement: 'top' + }) + }) + .on('mouseout blur', function () { + self.services.events.dispatchEvent(Events.Tooltip.HIDE) + }) .on('click', (event: CustomEvent) => { if (!d.shouldBeDisabled()) { self.triggerFunctionAndEvent(d, event, this) diff --git a/packages/core/src/components/essentials/tooltip.ts b/packages/core/src/components/essentials/tooltip.ts index 6bb006b398..65cd22016e 100644 --- a/packages/core/src/components/essentials/tooltip.ts +++ b/packages/core/src/components/essentials/tooltip.ts @@ -1,5 +1,5 @@ import { select, pointer } from 'd3' -import Position, { PLACEMENTS } from '@carbon/utils-position' // position service +import Position, { PLACEMENTS, defaultPositions } from '@carbon/utils-position' // position service import { getProperty, truncateLabel } from '@/tools' import { zoomBar as zoomBarConfigs, tooltips as tooltipConfigs } from '@/configuration' import { carbonPrefix } from '@/configuration-non-customizable' // CSS prefix @@ -238,13 +238,47 @@ export class Tooltip extends Component { } } + getOffsetByPlacement(position: any, placement: string, offset: number) { + const newOffset = Object.assign({}, position) + if (placement == PLACEMENTS.LEFT) { + newOffset.left -= offset + } else if (placement == PLACEMENTS.RIGHT) { + newOffset.left += offset + } else if (placement == PLACEMENTS.TOP) { + newOffset.top -= offset + } else if (placement == PLACEMENTS.BOTTOM) { + newOffset.top += offset + } + return newOffset + } + positionTooltip(e: CustomEvent) { const holder = this.services.domUtils.getHolder() const target = this.tooltip.node() const options = this.getOptions() const isTopZoomBarEnabled = getProperty(options, 'zoomBar', 'top', 'enabled') + let { horizontalOffset, defaultOffsetSmall } = tooltipConfigs let mouseRelativePos = getProperty(e, 'detail', 'mousePosition') + const customPlacement: PLACEMENTS | undefined | null = getProperty(e, 'detail', 'placement') + + // set Tooltip based on custom placement relative to the triggered dom + if (customPlacement) { + const hovered = getProperty(e, 'detail', 'event', 'target') + const hoveredRect = hovered.getBoundingClientRect() + const position = defaultPositions[customPlacement]( + this.getOffsetByPlacement( + this.services.domUtils.getElementOffset(hovered), + customPlacement, + defaultOffsetSmall + ), + target, + hoveredRect + ) + this.positionService.setElement(target, position) + return ; + } + // set Tooltip based on mouse position if (!mouseRelativePos) { mouseRelativePos = pointer(getProperty(e, 'detail', 'event'), holder) } else { @@ -288,7 +322,6 @@ export class Tooltip extends Component { ) } - let { horizontalOffset } = tooltipConfigs if (bestPlacementOption === PLACEMENTS.LEFT) { horizontalOffset *= -1 } diff --git a/packages/core/src/configuration-non-customizable.ts b/packages/core/src/configuration-non-customizable.ts index 20b1d1cd52..d624d99ac4 100644 --- a/packages/core/src/configuration-non-customizable.ts +++ b/packages/core/src/configuration-non-customizable.ts @@ -228,7 +228,9 @@ export const spacers = { } export const tooltips = { - horizontalOffset: 10 + horizontalOffset: 10, + defaultOffsetSmall: 4, + defaultOffsetNormal: 8, } /** diff --git a/packages/core/src/services/essentials/dom-utils.ts b/packages/core/src/services/essentials/dom-utils.ts index a9b1c9a865..a46da5496f 100644 --- a/packages/core/src/services/essentials/dom-utils.ts +++ b/packages/core/src/services/essentials/dom-utils.ts @@ -230,6 +230,22 @@ export class DOMUtils extends Service { return this.chartID } + getElementOffset(element: HTMLElement) { + // get relative position { left, top } of the chart holder + const elementOffset = { left: 0, top: 0 } + const childRect = element.getBoundingClientRect() + const baseRect = this.getHolder().getBoundingClientRect() + + try { + elementOffset.left = childRect.left - baseRect.left + elementOffset.top = childRect.top - baseRect.top + } catch (e) { + console.error(e) + } + + return elementOffset + } + generateElementIDString(originalID: string | number) { return `chart-${this.chartID}-${originalID}` }