@ds-mo/ui 2.11.2 → 2.13.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +13 -13
- package/dist/.build-stamp +1 -1
- package/dist/components/ds-app-shell.js +1 -1
- package/dist/components/ds-banner.js +1 -1
- package/dist/components/ds-banner.js.map +1 -1
- package/dist/components/ds-bar-nav.js +1 -1
- package/dist/components/ds-bar-nav.js.map +1 -1
- package/dist/components/ds-button-filled.js +1 -1
- package/dist/components/{ds-accordion.d.ts → ds-button-unfilled.d.ts} +4 -4
- package/dist/components/ds-button-unfilled.js +2 -0
- package/dist/components/{ds-card-data-viz.d.ts → ds-card-data-viz-donut.d.ts} +4 -4
- package/dist/components/ds-card-data-viz-donut.js +2 -0
- package/dist/components/ds-card-data-viz-donut.js.map +1 -0
- package/dist/components/ds-card-setting.js +1 -1
- package/dist/components/ds-card-setting.js.map +1 -1
- package/dist/components/{ds-fade.d.ts → ds-card.d.ts} +4 -4
- package/dist/components/ds-card.js +2 -0
- package/dist/components/ds-card.js.map +1 -0
- package/dist/components/ds-chart-bar.js +1 -1
- package/dist/components/ds-chart-bar.js.map +1 -1
- package/dist/components/ds-chart-donut.js +1 -1
- package/dist/components/ds-chart-donut.js.map +1 -1
- package/dist/components/ds-chart-legend.js +1 -1
- package/dist/components/ds-chart-legend.js.map +1 -1
- package/dist/components/ds-chart-line.js +1 -1
- package/dist/components/ds-checkbox.js +1 -1
- package/dist/components/ds-checkbox.js.map +1 -1
- package/dist/components/ds-chip.js +1 -1
- package/dist/components/ds-chip.js.map +1 -1
- package/dist/components/ds-empty-state.js +1 -1
- package/dist/components/ds-empty-state.js.map +1 -1
- package/dist/components/ds-field.js +1 -1
- package/dist/components/ds-icon.js +1 -1
- package/dist/components/ds-input.js +1 -1
- package/dist/components/ds-input.js.map +1 -1
- package/dist/components/ds-loader.js +1 -1
- package/dist/components/ds-loader.js.map +1 -1
- package/dist/components/ds-menu.js +1 -1
- package/dist/components/ds-modal.js +1 -1
- package/dist/components/ds-pagination.js +1 -1
- package/dist/components/ds-pagination.js.map +1 -1
- package/dist/components/ds-panel-nav.js +1 -1
- package/dist/components/ds-panel-nav.js.map +1 -1
- package/dist/components/ds-panel-tools.js +1 -1
- package/dist/components/ds-panel-tools.js.map +1 -1
- package/dist/components/ds-radio-group.js +1 -1
- package/dist/components/ds-radio-group.js.map +1 -1
- package/dist/components/ds-select.js +1 -1
- package/dist/components/ds-select.js.map +1 -1
- package/dist/components/ds-shell-gradient-picker.js +1 -1
- package/dist/components/ds-shell-gradient-swatch.js +1 -1
- package/dist/components/ds-skeleton.js +1 -1
- package/dist/components/ds-skeleton.js.map +1 -1
- package/dist/components/ds-slider.js +1 -1
- package/dist/components/ds-slider.js.map +1 -1
- package/dist/components/ds-tab-group-nav.js +1 -1
- package/dist/components/ds-tab-group.js +1 -1
- package/dist/components/ds-tab-group.js.map +1 -1
- package/dist/components/ds-table.js +1 -1
- package/dist/components/ds-table.js.map +1 -1
- package/dist/components/ds-tag.js +1 -1
- package/dist/components/ds-tag.js.map +1 -1
- package/dist/components/ds-text.js +1 -1
- package/dist/components/ds-toggle.js +1 -1
- package/dist/components/ds-toggle.js.map +1 -1
- package/dist/components/ds-tooltip-data-viz.js +1 -1
- package/dist/components/ds-tooltip-data-viz.js.map +1 -1
- package/dist/components/ds-tooltip.js +1 -1
- package/dist/components/ds-tooltip.js.map +1 -1
- package/dist/components/p-1m8r0wFg.js +2 -0
- package/dist/components/p-1m8r0wFg.js.map +1 -0
- package/dist/components/p-3FLHei19.js +2 -0
- package/dist/components/p-3FLHei19.js.map +1 -0
- package/dist/components/p-B2_D4IDN.js +2 -0
- package/dist/components/p-B2_D4IDN.js.map +1 -0
- package/dist/components/p-B5NcWtpp.js +2 -0
- package/dist/components/p-B5NcWtpp.js.map +1 -0
- package/dist/components/{p-8Nt92j1-.js → p-B82KZX9D.js} +2 -2
- package/dist/components/p-BQq26pt9.js.map +1 -1
- package/dist/components/p-B_g8X0eh.js +2 -0
- package/dist/components/p-B_g8X0eh.js.map +1 -0
- package/dist/components/{p-DdFUTZ3y.js → p-Be3PHv34.js} +2 -2
- package/dist/components/p-CQmf2CeW.js +2 -0
- package/dist/components/p-CQmf2CeW.js.map +1 -0
- package/dist/components/{p-DOU4cbpc.js → p-CtwNUCnp.js} +2 -2
- package/dist/components/p-CuRjYmBk.js +2 -0
- package/dist/components/p-CuRjYmBk.js.map +1 -0
- package/dist/components/p-CwARVsZQ.js +2 -0
- package/dist/components/p-CwARVsZQ.js.map +1 -0
- package/dist/components/p-DohXlNCJ.js +2 -0
- package/dist/components/p-DohXlNCJ.js.map +1 -0
- package/dist/components/p-DuFm-ieS.js +2 -0
- package/dist/components/p-DuFm-ieS.js.map +1 -0
- package/dist/components/p-EK1hOP19.js.map +1 -1
- package/dist/lib/nav/index.js +3 -0
- package/dist/lib/nav/index.js.map +2 -2
- package/dist/lib/utils/control-width.d.ts +4 -0
- package/dist/lib/utils/index.d.ts +2 -0
- package/dist/lib/utils/index.js +20 -1
- package/dist/lib/utils/index.js.map +3 -3
- package/dist/lib/utils/token-defaults.d.ts +7 -0
- package/dist/types/components/ButtonFilled/ButtonFilled.d.ts +21 -3
- package/dist/types/components/ButtonFilled/index.d.ts +1 -1
- package/dist/types/components/ButtonUnfilled/ButtonUnfilled.d.ts +70 -0
- package/dist/types/components/ButtonUnfilled/index.d.ts +2 -0
- package/dist/types/components/Card/Card.d.ts +20 -0
- package/dist/types/components/Card/index.d.ts +2 -0
- package/dist/types/components/CardDataVizDonut/CardDataVizDonut.d.ts +26 -0
- package/dist/types/components/CardDataVizDonut/index.d.ts +2 -0
- package/dist/types/components/CardSetting/CardSetting.d.ts +7 -2
- package/dist/types/components/ChartDonut/ChartDonut.d.ts +31 -1
- package/dist/types/components/Checkbox/Checkbox.d.ts +1 -1
- package/dist/types/components/Chip/Chip.d.ts +18 -14
- package/dist/types/components/Chip/index.d.ts +1 -1
- package/dist/types/components/Input/Input.d.ts +1 -1
- package/dist/types/components/Pagination/Pagination.d.ts +1 -1
- package/dist/types/components/PanelNav/PanelNav.d.ts +2 -0
- package/dist/types/components/PanelTools/panel-tools-types.d.ts +1 -1
- package/dist/types/components/RadioGroup/RadioGroup.d.ts +2 -2
- package/dist/types/components/Select/Select.d.ts +34 -2
- package/dist/types/components/Select/index.d.ts +2 -0
- package/dist/types/components/ShellGradientSwatch/ShellGradientSwatch.d.ts +1 -1
- package/dist/types/components/Slider/Slider.d.ts +1 -1
- package/dist/types/components/TabGroup/TabGroup.d.ts +1 -1
- package/dist/types/components/TabGroup/tab-item-utils.d.ts +2 -1
- package/dist/types/components/TabGroupNav/TabGroupNav.d.ts +1 -1
- package/dist/types/components/Tag/Tag.d.ts +0 -2
- package/dist/types/components/Tag/index.d.ts +1 -1
- package/dist/types/components/Toggle/Toggle.d.ts +1 -1
- package/dist/types/components/Tooltip/Tooltip.d.ts +25 -6
- package/dist/types/components/TooltipDataViz/TooltipDataViz.d.ts +18 -0
- package/dist/types/components.d.ts +474 -576
- package/dist/types/utils/control-width.d.ts +5 -0
- package/dist/types/utils/index.d.ts +2 -0
- package/dist/types/utils/token-defaults.d.ts +7 -0
- package/package.json +7 -4
- package/src/angular/index.ts +3 -9
- package/src/angular/proxies.ts +62 -215
- package/src/react/components.ts +3 -9
- package/src/react/ds-button-unfilled.ts +33 -0
- package/src/react/ds-card-data-viz-donut.ts +27 -0
- package/src/react/{ds-fade.ts → ds-card.ts} +7 -7
- package/src/react/ds-chip.ts +2 -10
- package/src/wc/components/BarNav/BarNav.tsx +3 -2
- package/src/wc/components/BarNav/bar-nav-tabs-menu-utils.ts +1 -1
- package/src/wc/components/ButtonFilled/ButtonFilled.tsx +97 -11
- package/src/wc/components/ButtonFilled/index.ts +7 -1
- package/src/wc/components/ButtonUnfilled/ButtonUnfilled.tsx +230 -0
- package/src/wc/components/ButtonUnfilled/index.ts +7 -0
- package/src/wc/components/Card/Card.tsx +79 -0
- package/src/wc/components/Card/index.ts +1 -0
- package/src/wc/components/{CardDataViz/CardDataViz.tsx → CardDataVizDonut/CardDataVizDonut.tsx} +47 -41
- package/src/wc/components/CardDataVizDonut/index.ts +1 -0
- package/src/wc/components/CardSetting/CardSetting.tsx +21 -36
- package/src/wc/components/ChartDonut/ChartDonut.tsx +159 -11
- package/src/wc/components/ChartLegend/ChartLegend.tsx +3 -0
- package/src/wc/components/Checkbox/Checkbox.tsx +5 -5
- package/src/wc/components/Chip/Chip.tsx +50 -67
- package/src/wc/components/Chip/index.ts +1 -1
- package/src/wc/components/Input/Input.tsx +4 -4
- package/src/wc/components/Menu/Menu.tsx +7 -4
- package/src/wc/components/Pagination/Pagination.tsx +6 -6
- package/src/wc/components/PanelNav/PanelNav.tsx +77 -48
- package/src/wc/components/PanelTools/PanelTools.tsx +19 -16
- package/src/wc/components/PanelTools/panel-tools-types.ts +1 -1
- package/src/wc/components/PanelTools/panel-tools-utils.ts +1 -1
- package/src/wc/components/RadioGroup/RadioGroup.tsx +7 -7
- package/src/wc/components/Select/Select.tsx +145 -64
- package/src/wc/components/Select/index.ts +1 -0
- package/src/wc/components/ShellGradientSwatch/ShellGradientSwatch.tsx +4 -3
- package/src/wc/components/Slider/Slider.tsx +7 -3
- package/src/wc/components/TabGroup/TabGroup.tsx +13 -7
- package/src/wc/components/TabGroup/tab-item-utils.ts +2 -1
- package/src/wc/components/TabGroupNav/TabGroupNav.tsx +14 -13
- package/src/wc/components/Tag/Tag.tsx +25 -24
- package/src/wc/components/Tag/index.ts +1 -1
- package/src/wc/components/Toggle/Toggle.tsx +6 -6
- package/src/wc/components/Tooltip/Tooltip.tsx +166 -64
- package/src/wc/components/TooltipDataViz/TooltipDataViz.tsx +57 -2
- package/src/wc/components.d.ts +474 -579
- package/src/wc/utils/control-density.css +42 -0
- package/src/wc/utils/control-inactive.css +20 -0
- package/src/wc/utils/control-width.css +28 -0
- package/src/wc/utils/control-width.ts +10 -0
- package/src/wc/utils/index.ts +2 -0
- package/src/wc/utils/interaction-fill.css +162 -0
- package/src/wc/utils/scroll-edge-fade.css +5 -0
- package/src/wc/utils/token-defaults.ts +10 -0
- package/dist/components/ds-accordion.js +0 -2
- package/dist/components/ds-accordion.js.map +0 -1
- package/dist/components/ds-breadcrumb.d.ts +0 -11
- package/dist/components/ds-breadcrumb.js +0 -2
- package/dist/components/ds-breadcrumb.js.map +0 -1
- package/dist/components/ds-button-unfilled-icon.d.ts +0 -11
- package/dist/components/ds-button-unfilled-icon.js +0 -2
- package/dist/components/ds-card-data-viz.js +0 -2
- package/dist/components/ds-card-data-viz.js.map +0 -1
- package/dist/components/ds-fade.js +0 -2
- package/dist/components/ds-fade.js.map +0 -1
- package/dist/components/ds-header.d.ts +0 -11
- package/dist/components/ds-header.js +0 -2
- package/dist/components/ds-header.js.map +0 -1
- package/dist/components/ds-scrollbar.d.ts +0 -11
- package/dist/components/ds-scrollbar.js +0 -2
- package/dist/components/ds-scrollbar.js.map +0 -1
- package/dist/components/ds-surface.d.ts +0 -11
- package/dist/components/ds-surface.js +0 -2
- package/dist/components/ds-surface.js.map +0 -1
- package/dist/components/ds-toast-provider.d.ts +0 -11
- package/dist/components/ds-toast-provider.js +0 -2
- package/dist/components/ds-toast-provider.js.map +0 -1
- package/dist/components/p-BMQbEIrQ.js +0 -2
- package/dist/components/p-BMQbEIrQ.js.map +0 -1
- package/dist/components/p-BSFgy5r6.js +0 -2
- package/dist/components/p-BSFgy5r6.js.map +0 -1
- package/dist/components/p-CBUIn8dk.js +0 -2
- package/dist/components/p-CBUIn8dk.js.map +0 -1
- package/dist/components/p-ChVwCVHy.js +0 -2
- package/dist/components/p-ChVwCVHy.js.map +0 -1
- package/dist/components/p-CpNIQXz6.js +0 -2
- package/dist/components/p-CpNIQXz6.js.map +0 -1
- package/dist/components/p-CvgHQfvT.js +0 -2
- package/dist/components/p-CvgHQfvT.js.map +0 -1
- package/dist/components/p-bDf6yBQd.js +0 -2
- package/dist/components/p-bDf6yBQd.js.map +0 -1
- package/dist/types/components/Accordion/Accordion.d.ts +0 -28
- package/dist/types/components/Breadcrumb/Breadcrumb.d.ts +0 -16
- package/dist/types/components/ButtonUnfilledIcon/ButtonUnfilledIcon.d.ts +0 -45
- package/dist/types/components/ButtonUnfilledIcon/index.d.ts +0 -2
- package/dist/types/components/CardDataViz/CardDataViz.d.ts +0 -22
- package/dist/types/components/CardDataViz/index.d.ts +0 -2
- package/dist/types/components/Fade/Fade.d.ts +0 -22
- package/dist/types/components/Fade/index.d.ts +0 -2
- package/dist/types/components/Header/Header.d.ts +0 -7
- package/dist/types/components/Scrollbar/Scrollbar.d.ts +0 -27
- package/dist/types/components/Surface/Surface.d.ts +0 -22
- package/dist/types/components/Toast/Toast.d.ts +0 -16
- package/dist/types/components/Toast/toast-service.d.ts +0 -24
- package/src/react/ds-accordion.ts +0 -27
- package/src/react/ds-breadcrumb.ts +0 -27
- package/src/react/ds-button-unfilled-icon.ts +0 -33
- package/src/react/ds-card-data-viz.ts +0 -26
- package/src/react/ds-header.ts +0 -26
- package/src/react/ds-scrollbar.ts +0 -26
- package/src/react/ds-surface.ts +0 -26
- package/src/react/ds-toast-provider.ts +0 -26
- package/src/wc/components/Accordion/Accordion.tsx +0 -144
- package/src/wc/components/Breadcrumb/Breadcrumb.tsx +0 -61
- package/src/wc/components/ButtonUnfilledIcon/ButtonUnfilledIcon.tsx +0 -129
- package/src/wc/components/ButtonUnfilledIcon/index.ts +0 -1
- package/src/wc/components/CardDataViz/index.ts +0 -1
- package/src/wc/components/Fade/Fade.tsx +0 -58
- package/src/wc/components/Fade/index.ts +0 -1
- package/src/wc/components/Header/Header.tsx +0 -33
- package/src/wc/components/Scrollbar/Scrollbar.tsx +0 -189
- package/src/wc/components/Surface/Surface.tsx +0 -81
- package/src/wc/components/Toast/Toast.tsx +0 -179
- package/src/wc/components/Toast/toast-service.ts +0 -67
- /package/dist/components/{ds-button-unfilled-icon.js.map → ds-button-unfilled.js.map} +0 -0
- /package/dist/components/{p-8Nt92j1-.js.map → p-B82KZX9D.js.map} +0 -0
- /package/dist/components/{p-DdFUTZ3y.js.map → p-Be3PHv34.js.map} +0 -0
- /package/dist/components/{p-DOU4cbpc.js.map → p-CtwNUCnp.js.map} +0 -0
|
@@ -1,12 +1,39 @@
|
|
|
1
|
-
import { Component, Prop,
|
|
1
|
+
import { Component, Prop, Element, Watch, h, Host } from '@stencil/core';
|
|
2
2
|
import { resolveCssLengthPx, resolveCssTimeMs, TOKEN_CSS_LENGTHS, TOKEN_DEFAULTS } from '../../utils';
|
|
3
3
|
|
|
4
4
|
export type TooltipSide = 'top' | 'right' | 'bottom' | 'left';
|
|
5
5
|
export type TooltipAlign = 'start' | 'center' | 'end';
|
|
6
|
+
export type TooltipSize = 'md' | 'sm' | 'xs';
|
|
7
|
+
|
|
8
|
+
/** Body text per control-density size (regular weight — not emphasis). */
|
|
9
|
+
const TEXT_VARIANT: Record<TooltipSize, string> = {
|
|
10
|
+
md: 'text-body-medium',
|
|
11
|
+
sm: 'text-body-small',
|
|
12
|
+
xs: 'text-caption',
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
const LINE_HEIGHT: Record<TooltipSize, string> = {
|
|
16
|
+
md: 'var(--typography-lineheight-md)',
|
|
17
|
+
sm: 'var(--typography-lineheight-sm)',
|
|
18
|
+
xs: 'var(--typography-lineheight-xs)',
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
const FALLBACK_HEIGHT: Record<TooltipSize, string> = {
|
|
22
|
+
md: TOKEN_DEFAULTS.size400,
|
|
23
|
+
sm: TOKEN_DEFAULTS.size300,
|
|
24
|
+
xs: TOKEN_DEFAULTS.size200,
|
|
25
|
+
};
|
|
6
26
|
|
|
7
27
|
let lastDismissedAt = 0;
|
|
8
28
|
let tooltipIdCounter = 0;
|
|
29
|
+
/** Currently open tooltip — force-cleared on warm handoff so tips don't cross-fade. */
|
|
30
|
+
let activeTooltip: Tooltip | null = null;
|
|
9
31
|
|
|
32
|
+
/**
|
|
33
|
+
* Imperative body portal for the popup.
|
|
34
|
+
* Stencil must not own the portaled node — moving a VDOM child to `document.body`
|
|
35
|
+
* causes recreate → re-portal → @State loops that freeze the page.
|
|
36
|
+
*/
|
|
10
37
|
@Component({
|
|
11
38
|
tag: 'ds-tooltip',
|
|
12
39
|
styleUrl: 'Tooltip.css',
|
|
@@ -16,47 +43,67 @@ export class Tooltip {
|
|
|
16
43
|
@Element() el!: HTMLElement;
|
|
17
44
|
|
|
18
45
|
@Prop() label!: string;
|
|
46
|
+
|
|
47
|
+
/** Control density (height, padding, type). */
|
|
48
|
+
@Prop() size: TooltipSize = 'md';
|
|
49
|
+
|
|
19
50
|
@Prop() side: TooltipSide = 'top';
|
|
20
51
|
@Prop() align: TooltipAlign = 'center';
|
|
21
52
|
/** Gap between anchor and tooltip — number (px) or TokoMo length. */
|
|
22
53
|
@Prop() sideOffset: number | string = TOKEN_CSS_LENGTHS.space050;
|
|
23
54
|
/** Cross-axis offset — number (px) or TokoMo length. */
|
|
24
55
|
@Prop() alignOffset: number | string = 0;
|
|
25
|
-
/**
|
|
26
|
-
|
|
56
|
+
/**
|
|
57
|
+
* Hover show delay before the tooltip appears.
|
|
58
|
+
* Default: `--effect-animation-delay-medium-3` (1000ms). Accepts a number (ms)
|
|
59
|
+
* or a TokoMo time token / `var(--effect-animation-delay-*)`. Applies to the
|
|
60
|
+
* initial hover/focus show only — after a recent dismiss, reopen is instant.
|
|
61
|
+
* Prefer the default; override only for denser chrome or rare/destructive actions.
|
|
62
|
+
*/
|
|
63
|
+
@Prop() delay: number | string = TOKEN_DEFAULTS.animationDelayMedium3;
|
|
27
64
|
@Prop() shortcutKey: string | undefined;
|
|
28
65
|
@Prop() shortcutKeyPosition: 'start' | 'end' = 'end';
|
|
29
66
|
|
|
30
|
-
@State() private visible: boolean = false;
|
|
31
|
-
@State() private closing: boolean = false;
|
|
32
|
-
@State() private pos: { x: number; y: number } = { x: 0, y: 0 };
|
|
33
|
-
|
|
34
67
|
private tooltipId = `ds-tooltip-${++tooltipIdCounter}`;
|
|
35
68
|
private delayTimer: ReturnType<typeof setTimeout> | null = null;
|
|
36
69
|
private closeTimer: ReturnType<typeof setTimeout> | null = null;
|
|
37
70
|
private anchor: HTMLElement | null = null;
|
|
38
|
-
private
|
|
71
|
+
private popupEl: HTMLElement | null = null;
|
|
72
|
+
private skipEnterAnimation = false;
|
|
73
|
+
private isOpen = false;
|
|
39
74
|
private mouseEnterHandler: () => void = () => this.show();
|
|
40
75
|
private mouseLeaveHandler: () => void = () => this.hide();
|
|
76
|
+
/** focusin/focusout so nested focus targets (e.g. ds-button-* inner button) still open the tip. */
|
|
41
77
|
private focusHandler: () => void = () => this.show();
|
|
42
|
-
private blurHandler: () => void = () =>
|
|
78
|
+
private blurHandler: (e: FocusEvent) => void = (e) => {
|
|
79
|
+
const next = e.relatedTarget as Node | null;
|
|
80
|
+
if (next && this.anchor?.contains(next)) return;
|
|
81
|
+
this.hide();
|
|
82
|
+
};
|
|
43
83
|
|
|
44
84
|
componentDidLoad() {
|
|
45
85
|
this.bindAnchor();
|
|
46
86
|
}
|
|
47
87
|
|
|
48
88
|
disconnectedCallback() {
|
|
89
|
+
if (activeTooltip === this) activeTooltip = null;
|
|
49
90
|
this.unbindAnchor();
|
|
50
91
|
this.clearTimers();
|
|
92
|
+
this.destroyPopup();
|
|
51
93
|
}
|
|
52
94
|
|
|
53
95
|
@Watch('label')
|
|
96
|
+
@Watch('size')
|
|
54
97
|
@Watch('side')
|
|
55
98
|
@Watch('align')
|
|
56
99
|
@Watch('sideOffset')
|
|
57
100
|
@Watch('alignOffset')
|
|
58
|
-
|
|
59
|
-
|
|
101
|
+
@Watch('shortcutKey')
|
|
102
|
+
@Watch('shortcutKeyPosition')
|
|
103
|
+
onContentOrPositionChange() {
|
|
104
|
+
if (!this.popupEl || !this.isOpen) return;
|
|
105
|
+
this.renderPopupContent();
|
|
106
|
+
this.calculatePosition();
|
|
60
107
|
}
|
|
61
108
|
|
|
62
109
|
private get viewportPadPx(): number {
|
|
@@ -72,7 +119,7 @@ export class Tooltip {
|
|
|
72
119
|
}
|
|
73
120
|
|
|
74
121
|
private get hoverDelayMs(): number {
|
|
75
|
-
return resolveCssTimeMs(this.delay, TOKEN_DEFAULTS.
|
|
122
|
+
return resolveCssTimeMs(this.delay, TOKEN_DEFAULTS.animationDelayMedium3);
|
|
76
123
|
}
|
|
77
124
|
|
|
78
125
|
private get instantReopenMs(): number {
|
|
@@ -91,7 +138,8 @@ export class Tooltip {
|
|
|
91
138
|
}
|
|
92
139
|
|
|
93
140
|
private get tooltipFallbackHeightPx(): number {
|
|
94
|
-
|
|
141
|
+
const token = FALLBACK_HEIGHT[this.size];
|
|
142
|
+
return resolveCssLengthPx(token, token);
|
|
95
143
|
}
|
|
96
144
|
|
|
97
145
|
private bindAnchor() {
|
|
@@ -103,8 +151,8 @@ export class Tooltip {
|
|
|
103
151
|
child.setAttribute('aria-describedby', this.tooltipId);
|
|
104
152
|
child.addEventListener('mouseenter', this.mouseEnterHandler);
|
|
105
153
|
child.addEventListener('mouseleave', this.mouseLeaveHandler);
|
|
106
|
-
child.addEventListener('
|
|
107
|
-
child.addEventListener('
|
|
154
|
+
child.addEventListener('focusin', this.focusHandler);
|
|
155
|
+
child.addEventListener('focusout', this.blurHandler);
|
|
108
156
|
}
|
|
109
157
|
|
|
110
158
|
private unbindAnchor() {
|
|
@@ -112,8 +160,8 @@ export class Tooltip {
|
|
|
112
160
|
this.anchor.removeAttribute('aria-describedby');
|
|
113
161
|
this.anchor.removeEventListener('mouseenter', this.mouseEnterHandler);
|
|
114
162
|
this.anchor.removeEventListener('mouseleave', this.mouseLeaveHandler);
|
|
115
|
-
this.anchor.removeEventListener('
|
|
116
|
-
this.anchor.removeEventListener('
|
|
163
|
+
this.anchor.removeEventListener('focusin', this.focusHandler);
|
|
164
|
+
this.anchor.removeEventListener('focusout', this.blurHandler);
|
|
117
165
|
this.anchor = null;
|
|
118
166
|
}
|
|
119
167
|
|
|
@@ -122,39 +170,114 @@ export class Tooltip {
|
|
|
122
170
|
if (this.closeTimer) { clearTimeout(this.closeTimer); this.closeTimer = null; }
|
|
123
171
|
}
|
|
124
172
|
|
|
173
|
+
private destroyPopup() {
|
|
174
|
+
if (!this.popupEl) return;
|
|
175
|
+
this.popupEl.remove();
|
|
176
|
+
this.popupEl = null;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/** Drop the open tip immediately (warm handoff to another tooltip). */
|
|
180
|
+
private hideInstant() {
|
|
181
|
+
this.clearTimers();
|
|
182
|
+
lastDismissedAt = Date.now();
|
|
183
|
+
if (activeTooltip === this) activeTooltip = null;
|
|
184
|
+
this.destroyPopup();
|
|
185
|
+
this.isOpen = false;
|
|
186
|
+
this.skipEnterAnimation = false;
|
|
187
|
+
}
|
|
188
|
+
|
|
125
189
|
private show() {
|
|
126
190
|
this.clearTimers();
|
|
127
|
-
|
|
191
|
+
let instant = Date.now() - lastDismissedAt < this.instantReopenMs;
|
|
192
|
+
if (activeTooltip && activeTooltip !== this) {
|
|
193
|
+
activeTooltip.hideInstant();
|
|
194
|
+
instant = true;
|
|
195
|
+
}
|
|
196
|
+
activeTooltip = this;
|
|
197
|
+
this.skipEnterAnimation = instant;
|
|
128
198
|
if (instant) {
|
|
129
|
-
this.
|
|
130
|
-
this.visible = true;
|
|
131
|
-
requestAnimationFrame(() => this.calculatePosition());
|
|
199
|
+
this.mountPopup();
|
|
132
200
|
return;
|
|
133
201
|
}
|
|
134
202
|
this.delayTimer = setTimeout(() => {
|
|
135
203
|
this.delayTimer = null;
|
|
136
|
-
this.
|
|
137
|
-
this.visible = true;
|
|
138
|
-
requestAnimationFrame(() => this.calculatePosition());
|
|
204
|
+
this.mountPopup();
|
|
139
205
|
}, this.hoverDelayMs);
|
|
140
206
|
}
|
|
141
207
|
|
|
142
208
|
private hide() {
|
|
143
209
|
this.clearTimers();
|
|
144
|
-
if (!this.
|
|
210
|
+
if (!this.popupEl && !this.isOpen) return;
|
|
145
211
|
lastDismissedAt = Date.now();
|
|
146
|
-
this
|
|
212
|
+
if (activeTooltip === this) activeTooltip = null;
|
|
213
|
+
if (!this.popupEl) {
|
|
214
|
+
this.isOpen = false;
|
|
215
|
+
return;
|
|
216
|
+
}
|
|
217
|
+
this.popupEl.classList.remove('tooltip-popup--instant');
|
|
218
|
+
this.popupEl.classList.add('tooltip-popup--closing');
|
|
147
219
|
this.closeTimer = setTimeout(() => {
|
|
148
220
|
this.closeTimer = null;
|
|
149
|
-
this.
|
|
150
|
-
this.
|
|
221
|
+
this.destroyPopup();
|
|
222
|
+
this.isOpen = false;
|
|
223
|
+
this.skipEnterAnimation = false;
|
|
151
224
|
}, this.fadeOutMs);
|
|
152
225
|
}
|
|
153
226
|
|
|
227
|
+
private mountPopup() {
|
|
228
|
+
if (!this.popupEl) {
|
|
229
|
+
const tip = document.createElement('div');
|
|
230
|
+
tip.id = this.tooltipId;
|
|
231
|
+
tip.setAttribute('role', 'tooltip');
|
|
232
|
+
// `sc-ds-tooltip` is required for Stencil scoped CSS to match outside the host.
|
|
233
|
+
tip.className = 'tooltip-popup sc-ds-tooltip';
|
|
234
|
+
Object.assign(tip.style, {
|
|
235
|
+
position: 'fixed',
|
|
236
|
+
left: '0',
|
|
237
|
+
top: '0',
|
|
238
|
+
zIndex: 'var(--dimension-z-index-tooltip)',
|
|
239
|
+
pointerEvents: 'none',
|
|
240
|
+
});
|
|
241
|
+
document.body.appendChild(tip);
|
|
242
|
+
this.popupEl = tip;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
this.popupEl.classList.toggle('tooltip-popup--instant', this.skipEnterAnimation);
|
|
246
|
+
this.popupEl.classList.remove('tooltip-popup--closing');
|
|
247
|
+
this.renderPopupContent();
|
|
248
|
+
this.calculatePosition();
|
|
249
|
+
this.isOpen = true;
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
private renderPopupContent() {
|
|
253
|
+
if (!this.popupEl) return;
|
|
254
|
+
const textVariant = TEXT_VARIANT[this.size];
|
|
255
|
+
const lineHeight = LINE_HEIGHT[this.size];
|
|
256
|
+
const density =
|
|
257
|
+
this.size === 'md' ? 'ds-control--md' : this.size === 'sm' ? 'ds-control--sm' : 'ds-control--xs';
|
|
258
|
+
// Every node needs `sc-ds-tooltip` so scoped selectors (`.foo.sc-ds-tooltip`) match.
|
|
259
|
+
const sc = 'sc-ds-tooltip';
|
|
260
|
+
|
|
261
|
+
const startKey =
|
|
262
|
+
this.shortcutKey && this.shortcutKeyPosition === 'start'
|
|
263
|
+
? `<div class="key-hint ${sc}" aria-hidden="true"><span class="text-caption-emphasis ${sc}">${escapeHtml(this.shortcutKey)}</span></div>`
|
|
264
|
+
: '';
|
|
265
|
+
const endKey =
|
|
266
|
+
this.shortcutKey && this.shortcutKeyPosition === 'end'
|
|
267
|
+
? `<div class="key-hint ${sc}" aria-hidden="true"><span class="text-caption-emphasis ${sc}">${escapeHtml(this.shortcutKey)}</span></div>`
|
|
268
|
+
: '';
|
|
269
|
+
|
|
270
|
+
this.popupEl.innerHTML =
|
|
271
|
+
`<div class="tooltip-inner ${density} ${sc}">` +
|
|
272
|
+
startKey +
|
|
273
|
+
`<span class="tooltip-label ${textVariant} ${sc}" style="line-height:${lineHeight}">${escapeHtml(this.label)}</span>` +
|
|
274
|
+
endKey +
|
|
275
|
+
`</div>`;
|
|
276
|
+
}
|
|
277
|
+
|
|
154
278
|
private calculatePosition() {
|
|
155
|
-
if (!this.anchor) return;
|
|
156
|
-
const tip = this.
|
|
157
|
-
if (!tip) return;
|
|
279
|
+
if (!this.anchor || !this.popupEl) return;
|
|
280
|
+
const tip = this.popupEl;
|
|
158
281
|
const a = this.anchor.getBoundingClientRect();
|
|
159
282
|
const tw = tip.offsetWidth || this.tooltipFallbackWidthPx;
|
|
160
283
|
const th = tip.offsetHeight || this.tooltipFallbackHeightPx;
|
|
@@ -190,46 +313,25 @@ export class Tooltip {
|
|
|
190
313
|
break;
|
|
191
314
|
}
|
|
192
315
|
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
};
|
|
316
|
+
const nextX = Math.min(Math.max(x, vpPad), window.innerWidth - tw - vpPad);
|
|
317
|
+
const nextY = Math.min(Math.max(y, vpPad), window.innerHeight - th - vpPad);
|
|
318
|
+
tip.style.transform = `translate(${Math.round(nextX)}px, ${Math.round(nextY)}px)`;
|
|
197
319
|
}
|
|
198
320
|
|
|
199
321
|
render() {
|
|
200
322
|
return (
|
|
201
323
|
<Host style={{ display: 'contents' }}>
|
|
202
324
|
<slot />
|
|
203
|
-
{(this.visible || this.closing) && (
|
|
204
|
-
<div
|
|
205
|
-
id={this.tooltipId}
|
|
206
|
-
class={{ 'tooltip-popup': true, 'tooltip-popup--closing': this.closing }}
|
|
207
|
-
role="tooltip"
|
|
208
|
-
style={{
|
|
209
|
-
position: 'fixed',
|
|
210
|
-
left: '0',
|
|
211
|
-
top: '0',
|
|
212
|
-
transform: `translate(${Math.round(this.pos.x)}px, ${Math.round(this.pos.y)}px)`,
|
|
213
|
-
zIndex: '10000',
|
|
214
|
-
pointerEvents: 'none',
|
|
215
|
-
}}
|
|
216
|
-
>
|
|
217
|
-
<div class="tooltip-inner">
|
|
218
|
-
{this.shortcutKey && this.shortcutKeyPosition === 'start' && (
|
|
219
|
-
<div class="key-hint" aria-hidden="true">
|
|
220
|
-
<span class="text-caption-emphasis">{this.shortcutKey}</span>
|
|
221
|
-
</div>
|
|
222
|
-
)}
|
|
223
|
-
<span class="text-body-small">{this.label}</span>
|
|
224
|
-
{this.shortcutKey && this.shortcutKeyPosition === 'end' && (
|
|
225
|
-
<div class="key-hint" aria-hidden="true">
|
|
226
|
-
<span class="text-caption-emphasis">{this.shortcutKey}</span>
|
|
227
|
-
</div>
|
|
228
|
-
)}
|
|
229
|
-
</div>
|
|
230
|
-
</div>
|
|
231
|
-
)}
|
|
232
325
|
</Host>
|
|
233
326
|
);
|
|
234
327
|
}
|
|
235
328
|
}
|
|
329
|
+
|
|
330
|
+
function escapeHtml(value: string): string {
|
|
331
|
+
return value
|
|
332
|
+
.replace(/&/g, '&')
|
|
333
|
+
.replace(/</g, '<')
|
|
334
|
+
.replace(/>/g, '>')
|
|
335
|
+
.replace(/"/g, '"')
|
|
336
|
+
.replace(/'/g, ''');
|
|
337
|
+
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { Component, Prop, State, Element, Watch, h, Host } from '@stencil/core';
|
|
2
|
+
import { resolveCssTimeMs, TOKEN_DEFAULTS } from '../../utils';
|
|
2
3
|
|
|
3
4
|
const CURSOR_OFFSET_PX = 12;
|
|
4
5
|
const VIEWPORT_PAD_PX = 8;
|
|
@@ -16,6 +17,9 @@ export type TooltipDataVizAlign = 'top' | 'bottom';
|
|
|
16
17
|
* with `x`/`y` as pixel coordinates within that wrapper. Defaults to sitting below-right
|
|
17
18
|
* of the anchor (matching cursor-following tooltips), flipping to whichever side/edge
|
|
18
19
|
* keeps it on-screen.
|
|
20
|
+
*
|
|
21
|
+
* Mount (or remount) when a hover session starts so `delay` applies once per hover;
|
|
22
|
+
* keep the instance mounted while the cursor moves so tracking stays instant.
|
|
19
23
|
*/
|
|
20
24
|
@Component({
|
|
21
25
|
tag: 'ds-tooltip-data-viz',
|
|
@@ -31,19 +35,67 @@ export class TooltipDataViz {
|
|
|
31
35
|
@Prop() x: number = 0;
|
|
32
36
|
@Prop() y: number = 0;
|
|
33
37
|
|
|
38
|
+
/**
|
|
39
|
+
* Show delay after mount before the callout appears.
|
|
40
|
+
* Default: `--effect-animation-delay-instant` (0ms). Accepts a number (ms)
|
|
41
|
+
* or a TokoMo time token / `var(--effect-animation-delay-*)`. Charts need
|
|
42
|
+
* immediate feedback while scrubbing; prefer the default. Mount once per
|
|
43
|
+
* hover session so any non-zero delay runs once, then track `x`/`y` instantly.
|
|
44
|
+
*/
|
|
45
|
+
@Prop() delay: number | string = TOKEN_DEFAULTS.animationDelayInstant;
|
|
46
|
+
|
|
34
47
|
@State() private side: TooltipDataVizSide = 'right';
|
|
35
48
|
@State() private align: TooltipDataVizAlign = 'bottom';
|
|
49
|
+
@State() private visible: boolean = false;
|
|
50
|
+
|
|
51
|
+
private delayTimer: ReturnType<typeof setTimeout> | null = null;
|
|
36
52
|
|
|
37
53
|
componentDidLoad() {
|
|
54
|
+
this.scheduleShow();
|
|
38
55
|
this.calculatePlacement();
|
|
39
56
|
}
|
|
40
57
|
|
|
58
|
+
disconnectedCallback() {
|
|
59
|
+
this.clearDelayTimer();
|
|
60
|
+
}
|
|
61
|
+
|
|
41
62
|
@Watch('x')
|
|
42
63
|
@Watch('y')
|
|
43
64
|
onAnchorChange() {
|
|
44
65
|
requestAnimationFrame(() => this.calculatePlacement());
|
|
45
66
|
}
|
|
46
67
|
|
|
68
|
+
@Watch('delay')
|
|
69
|
+
onDelayChange() {
|
|
70
|
+
if (!this.visible) this.scheduleShow();
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
private get showDelayMs(): number {
|
|
74
|
+
return resolveCssTimeMs(this.delay, TOKEN_DEFAULTS.animationDelayInstant);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
private clearDelayTimer() {
|
|
78
|
+
if (this.delayTimer) {
|
|
79
|
+
clearTimeout(this.delayTimer);
|
|
80
|
+
this.delayTimer = null;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
private scheduleShow() {
|
|
85
|
+
this.clearDelayTimer();
|
|
86
|
+
this.visible = false;
|
|
87
|
+
const ms = this.showDelayMs;
|
|
88
|
+
if (ms <= 0) {
|
|
89
|
+
this.visible = true;
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
this.delayTimer = setTimeout(() => {
|
|
93
|
+
this.delayTimer = null;
|
|
94
|
+
this.visible = true;
|
|
95
|
+
requestAnimationFrame(() => this.calculatePlacement());
|
|
96
|
+
}, ms);
|
|
97
|
+
}
|
|
98
|
+
|
|
47
99
|
private calculatePlacement() {
|
|
48
100
|
const rect = this.el.getBoundingClientRect();
|
|
49
101
|
if (!rect.width || !rect.height) return;
|
|
@@ -61,10 +113,13 @@ export class TooltipDataViz {
|
|
|
61
113
|
|
|
62
114
|
return (
|
|
63
115
|
<Host
|
|
64
|
-
class=
|
|
116
|
+
class={{
|
|
117
|
+
'tooltip-data-viz': true,
|
|
118
|
+
'tooltip-data-viz--visible': this.visible,
|
|
119
|
+
}}
|
|
65
120
|
style={{ left: `${this.x}px`, top: `${this.y}px`, transform: `translate(${translateX}, ${translateY})` }}
|
|
66
121
|
>
|
|
67
|
-
<div class="tooltip-data-viz__item">
|
|
122
|
+
<div class="tooltip-data-viz__item ds-control--md">
|
|
68
123
|
<span class="tooltip-data-viz__label">
|
|
69
124
|
<ds-text as="span" variant="text-body-medium" color="var(--color-foreground-on-translucent-background-secondary)">
|
|
70
125
|
{this.label}
|