@ds-mo/ui 12.7.0 → 12.9.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/dist/.build-stamp +1 -1
- package/dist/agent-patterns.json +22 -9
- package/dist/agent.json +246 -19
- package/dist/angular/ds-button-filled.d.ts +1 -1
- package/dist/angular/ds-button-filled.js +3 -3
- package/dist/angular/ds-button-unfilled.d.ts +1 -1
- package/dist/angular/ds-button-unfilled.js +3 -3
- package/dist/angular/ds-card-overview.d.ts +19 -0
- package/dist/angular/ds-card-overview.js +42 -0
- package/dist/angular/index.d.ts +1 -1
- package/dist/angular/index.js +1 -0
- package/dist/angular/proxies.d.ts +1 -0
- package/dist/angular/proxies.js +1 -0
- package/dist/components/ds-agent-activity.js +1 -1
- package/dist/components/ds-agent-response.js +1 -1
- package/dist/components/ds-agent-source-list.js +1 -1
- package/dist/components/ds-agent-tool-call.js +1 -1
- package/dist/components/ds-attachment-list.js +1 -1
- package/dist/components/ds-badge.js +1 -1
- package/dist/components/ds-bar-nav.js +1 -1
- package/dist/components/ds-bar-title.js +1 -1
- package/dist/components/ds-bar-title.js.map +1 -1
- package/dist/components/ds-bar-workflow.js +1 -1
- package/dist/components/ds-breadcrumb.js +1 -1
- package/dist/components/ds-button-filled.js +1 -1
- package/dist/components/ds-button-unfilled.js +1 -1
- package/dist/components/ds-card-data-viz-bar.js +1 -1
- package/dist/components/ds-card-data-viz-bar.js.map +1 -1
- package/dist/components/ds-card-data-viz-donut.js +1 -1
- package/dist/components/ds-card-data-viz-donut.js.map +1 -1
- package/dist/components/ds-card-data-viz-line.js +1 -1
- package/dist/components/ds-card-data-viz-line.js.map +1 -1
- package/dist/components/ds-card-overview.d.ts +11 -0
- package/dist/components/ds-card-overview.js +2 -0
- package/dist/components/ds-card-overview.js.map +1 -0
- package/dist/components/ds-card-setting.js +1 -1
- package/dist/components/ds-card-shell-data-viz.js +1 -1
- package/dist/components/ds-chart-bar-stacked.js +1 -1
- package/dist/components/ds-chart-bar-stacked.js.map +1 -1
- 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-line.js +1 -1
- package/dist/components/ds-chart-line.js.map +1 -1
- package/dist/components/ds-checkbox.js +1 -1
- package/dist/components/ds-chip.js +1 -1
- package/dist/components/ds-code-block.js +1 -1
- package/dist/components/ds-conversation-list-item.js +1 -1
- package/dist/components/ds-conversation-list-item.js.map +1 -1
- package/dist/components/ds-conversation-list-section.js +1 -1
- package/dist/components/ds-conversation-list.js +1 -1
- package/dist/components/ds-conversation-list.js.map +1 -1
- package/dist/components/ds-divider.js +1 -1
- package/dist/components/ds-empty-state.js +1 -1
- package/dist/components/ds-field.js +1 -1
- package/dist/components/ds-input.js +1 -1
- package/dist/components/ds-markdown.js +1 -1
- package/dist/components/ds-menu.js +1 -1
- package/dist/components/ds-message-bubble.js +1 -1
- package/dist/components/ds-message-composer.js +1 -1
- package/dist/components/ds-message-scroller.js +1 -1
- package/dist/components/ds-message-scroller.js.map +1 -1
- package/dist/components/ds-message.js +1 -1
- package/dist/components/ds-mobile-bar-nav.js +1 -1
- package/dist/components/ds-mobile-header.js +1 -1
- package/dist/components/ds-mobile-section-switcher.js +1 -1
- package/dist/components/ds-mobile-sheet-nav.js +1 -1
- package/dist/components/ds-modal.js +1 -1
- package/dist/components/ds-modal.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-sub-nav.js +1 -1
- package/dist/components/ds-panel-tool-header.js +1 -1
- package/dist/components/ds-panel-tool-search.js +1 -1
- package/dist/components/ds-panel-tools.js +1 -1
- package/dist/components/ds-radio.js +1 -1
- package/dist/components/ds-scroll-overlay.js +1 -1
- package/dist/components/ds-scroll-overlay.js.map +1 -1
- package/dist/components/ds-select-multi.js +1 -1
- package/dist/components/ds-select-multi.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-app.js +1 -1
- package/dist/components/ds-shell-gradient-picker.js +1 -1
- package/dist/components/ds-shell-gradient-picker.js.map +1 -1
- package/dist/components/ds-shell-gradient-swatch.js +1 -1
- package/dist/components/ds-shell-page.js +1 -1
- package/dist/components/ds-shell-page.js.map +1 -1
- package/dist/components/ds-shell-tools.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-swatch-picker.js +1 -1
- package/dist/components/ds-switch.js +1 -1
- package/dist/components/ds-tab-group.js +1 -1
- package/dist/components/ds-tag.js +1 -1
- package/dist/components/ds-text.js +1 -1
- package/dist/components/ds-toast.js +1 -1
- package/dist/components/ds-toast.js.map +1 -1
- package/dist/components/ds-tooltip-data-viz.js +1 -1
- package/dist/components/ds-tooltip.js +1 -1
- package/dist/components/ds-typing-indicator.js +1 -1
- package/dist/components/p-2foAUsDN.js +2 -0
- package/dist/components/p-2foAUsDN.js.map +1 -0
- package/dist/components/{p-BuiZcSga.js → p-8GK7B7K4.js} +2 -2
- package/dist/components/p-8GK7B7K4.js.map +1 -0
- package/dist/components/p-B5tMlca-.js +2 -0
- package/dist/components/p-B5tMlca-.js.map +1 -0
- package/dist/components/p-B6Vuayt6.js +2 -0
- package/dist/components/p-B6Vuayt6.js.map +1 -0
- package/dist/components/{p-B-9A0e_0.js → p-B88n0pO3.js} +2 -2
- package/dist/components/p-BBePKRHC.js +2 -0
- package/dist/components/p-BBePKRHC.js.map +1 -0
- package/dist/components/p-BBxcMVqk.js +2 -0
- package/dist/components/p-BBxcMVqk.js.map +1 -0
- package/dist/components/p-BDKtu6Wb.js +2 -0
- package/dist/components/{p-Bl001G0Y.js.map → p-BDKtu6Wb.js.map} +1 -1
- package/dist/components/p-BKOlvCGS.js +2 -0
- package/dist/components/p-BKOlvCGS.js.map +1 -0
- package/dist/components/{p-C7bZ-jiU.js → p-BO5aqZcf.js} +2 -2
- package/dist/components/{p-guQ7hv6W.js → p-BOCfICln.js} +2 -2
- package/dist/components/p-BX0BMhgz.js +2 -0
- package/dist/components/p-BX0BMhgz.js.map +1 -0
- package/dist/components/p-BhV5e6vF.js +2 -0
- package/dist/components/{p-1darPzys.js.map → p-BhV5e6vF.js.map} +1 -1
- package/dist/components/p-C8HU4W8q.js +2 -0
- package/dist/components/{p-pgxlkvUy.js.map → p-C8HU4W8q.js.map} +1 -1
- package/dist/components/{p-CvyTZ4Mg.js → p-CChcuL04.js} +2 -2
- package/dist/components/{p-Crsn82rz.js → p-CCzG5xDD.js} +2 -2
- package/dist/components/p-CGVXbXv9.js +2 -0
- package/dist/components/p-CGVXbXv9.js.map +1 -0
- package/dist/components/{p-BT5hgg5i.js → p-CIiBpjCn.js} +2 -2
- package/dist/components/{p-B_5nxOeB.js → p-CgEYdMee.js} +2 -2
- package/dist/components/{p-k0Hmj1KV.js → p-Cr3tEx7o.js} +2 -2
- package/dist/components/p-Cr3tEx7o.js.map +1 -0
- package/dist/components/{p-5-QAFB4j.js → p-Cuih680v.js} +2 -2
- package/dist/components/{p-C_9ZH0A3.js → p-Cuqxh-Tq.js} +2 -2
- package/dist/components/{p--eKXTylf.js → p-DCvA3Dc9.js} +2 -2
- package/dist/components/{p-DNFd4Ao-.js → p-DLZm_-sU.js} +2 -2
- package/dist/components/{p-C9h13YnX.js → p-DRnIuCiT.js} +2 -2
- package/dist/components/p-DhKvqh5G.js +2 -0
- package/dist/components/p-DhKvqh5G.js.map +1 -0
- package/dist/components/p-DjKkSqrG.js +2 -0
- package/dist/components/p-DjKkSqrG.js.map +1 -0
- package/dist/components/{p-CUAYsK44.js → p-DlscFn2m.js} +2 -2
- package/dist/components/{p-BcfC6phQ.js → p-DxkEC0hR.js} +2 -2
- package/dist/components/p-DxkEC0hR.js.map +1 -0
- package/dist/components/{p-C6xC48hG.js → p-Re2kPrXm.js} +2 -2
- package/dist/components/{p-DIa73GJP.js → p-XMZCaXs7.js} +2 -2
- package/dist/components/p-YR1XeuZf.js +2 -0
- package/dist/components/p-YR1XeuZf.js.map +1 -0
- package/dist/components/{p-CqWV4Hl_.js → p-dyUp3Adm.js} +2 -2
- package/dist/components/{p-CnAkobDW.js → p-nkYN0Bub.js} +2 -2
- package/dist/components/p-yhj1lIPx.js +2 -0
- package/dist/docs/components.json +847 -167
- package/dist/lib/components/Menu/menu-position.d.ts +6 -6
- package/dist/lib/toast/toast-types.d.ts +3 -3
- package/dist/lib/utils/anchored-position.d.ts +34 -0
- package/dist/lib/utils/choice-popup-alignment.d.ts +2 -2
- package/dist/lib/utils/index.d.ts +2 -0
- package/dist/lib/utils/index.js +31 -0
- package/dist/lib/utils/index.js.map +3 -3
- package/dist/lib/utils/metric-change.d.ts +52 -0
- package/dist/lib/utils/token-defaults.d.ts +3 -0
- package/dist/mcp-data/registry/agent-activity.json +1 -1
- package/dist/mcp-data/registry/agent-response.json +1 -1
- package/dist/mcp-data/registry/agent-source-list.json +1 -1
- package/dist/mcp-data/registry/agent-tool-call.json +1 -1
- package/dist/mcp-data/registry/attachment-list.json +1 -1
- package/dist/mcp-data/registry/avatar.json +1 -1
- package/dist/mcp-data/registry/badge.json +1 -1
- package/dist/mcp-data/registry/bar-nav.json +1 -1
- package/dist/mcp-data/registry/bar-title.json +2 -2
- package/dist/mcp-data/registry/bar-workflow.json +1 -1
- package/dist/mcp-data/registry/breadcrumb.json +1 -1
- package/dist/mcp-data/registry/button-filled.json +122 -6
- package/dist/mcp-data/registry/button-unfilled.json +49 -5
- package/dist/mcp-data/registry/card-data-viz-bar.json +2 -2
- package/dist/mcp-data/registry/card-data-viz-donut.json +2 -1
- package/dist/mcp-data/registry/card-data-viz-line.json +2 -2
- package/dist/mcp-data/registry/card-overview.json +360 -0
- package/dist/mcp-data/registry/card-setting.json +1 -1
- package/dist/mcp-data/registry/card-shell-data-viz.json +1 -1
- package/dist/mcp-data/registry/chart-bar-stacked.json +14 -9
- package/dist/mcp-data/registry/chart-bar.json +15 -10
- package/dist/mcp-data/registry/chart-donut.json +3 -2
- package/dist/mcp-data/registry/chart-legend.json +1 -1
- package/dist/mcp-data/registry/chart-line.json +15 -10
- package/dist/mcp-data/registry/checkbox.json +1 -1
- package/dist/mcp-data/registry/chip.json +1 -1
- package/dist/mcp-data/registry/code-block.json +1 -1
- package/dist/mcp-data/registry/conversation-list-item.json +1 -1
- package/dist/mcp-data/registry/conversation-list-section.json +1 -1
- package/dist/mcp-data/registry/conversation-list.json +1 -1
- package/dist/mcp-data/registry/divider.json +1 -1
- package/dist/mcp-data/registry/empty-state.json +1 -1
- package/dist/mcp-data/registry/field.json +1 -1
- package/dist/mcp-data/registry/icon.json +1 -1
- package/dist/mcp-data/registry/input.json +1 -1
- package/dist/mcp-data/registry/loader.json +1 -1
- package/dist/mcp-data/registry/markdown.json +1 -1
- package/dist/mcp-data/registry/menu.json +3 -3
- package/dist/mcp-data/registry/message-bubble.json +1 -1
- package/dist/mcp-data/registry/message-composer.json +1 -1
- package/dist/mcp-data/registry/message-scroller.json +1 -1
- package/dist/mcp-data/registry/message.json +1 -1
- package/dist/mcp-data/registry/mobile-bar-nav.json +1 -1
- package/dist/mcp-data/registry/mobile-header.json +1 -1
- package/dist/mcp-data/registry/mobile-section-switcher.json +1 -1
- package/dist/mcp-data/registry/mobile-sheet-nav.json +1 -1
- package/dist/mcp-data/registry/modal.json +1 -1
- package/dist/mcp-data/registry/panel-nav.json +1 -1
- package/dist/mcp-data/registry/panel-sub-nav.json +1 -1
- package/dist/mcp-data/registry/panel-tool-header.json +1 -1
- package/dist/mcp-data/registry/panel-tool-search.json +1 -1
- package/dist/mcp-data/registry/panel-tools.json +3 -2
- package/dist/mcp-data/registry/radio.json +1 -1
- package/dist/mcp-data/registry/registry.json +249 -19
- package/dist/mcp-data/registry/scroll-overlay.json +1 -1
- package/dist/mcp-data/registry/select-multi.json +2 -2
- package/dist/mcp-data/registry/select.json +2 -2
- package/dist/mcp-data/registry/shell-app.json +1 -1
- package/dist/mcp-data/registry/shell-gradient-picker.json +1 -1
- package/dist/mcp-data/registry/shell-gradient-swatch.json +1 -1
- package/dist/mcp-data/registry/shell-page.json +5 -4
- package/dist/mcp-data/registry/shell-tools.json +1 -1
- package/dist/mcp-data/registry/skeleton.json +1 -1
- package/dist/mcp-data/registry/slider.json +1 -1
- package/dist/mcp-data/registry/swatch-picker.json +1 -1
- package/dist/mcp-data/registry/switch.json +1 -1
- package/dist/mcp-data/registry/tab-group.json +1 -1
- package/dist/mcp-data/registry/tag.json +1 -1
- package/dist/mcp-data/registry/text.json +3 -3
- package/dist/mcp-data/registry/toast.json +2 -2
- package/dist/mcp-data/registry/tooltip-data-viz.json +3 -1
- package/dist/mcp-data/registry/tooltip.json +3 -8
- package/dist/mcp-data/registry/typing-indicator.json +1 -1
- package/dist/react/components.d.ts +1 -0
- package/dist/react/components.js +1 -0
- package/dist/react/ds-card-overview.d.ts +12 -0
- package/dist/react/ds-card-overview.js +12 -0
- package/dist/styles/control-elevation.css +11 -0
- package/dist/types/components/ButtonFilled/ButtonFilled.d.ts +32 -0
- package/dist/types/components/ButtonUnfilled/ButtonUnfilled.d.ts +20 -0
- package/dist/types/components/CardOverview/CardOverview.d.ts +64 -0
- package/dist/types/components/CardOverview/card-overview-types.d.ts +34 -0
- package/dist/types/components/CardOverview/index.d.ts +2 -0
- package/dist/types/components/ChartBar/ChartBar.d.ts +11 -0
- package/dist/types/components/ChartBarStacked/ChartBarStacked.d.ts +11 -0
- package/dist/types/components/ChartLine/ChartLine.d.ts +11 -0
- package/dist/types/components/Menu/Menu.d.ts +7 -5
- package/dist/types/components/Menu/menu-position.d.ts +6 -6
- package/dist/types/components/ShellPage/ShellPage.d.ts +1 -0
- package/dist/types/components/Tooltip/Tooltip.d.ts +1 -4
- package/dist/types/components.d.ts +234 -2
- package/dist/types/toast/toast-types.d.ts +3 -3
- package/dist/types/utils/anchored-position-controller.d.ts +105 -0
- package/dist/types/utils/anchored-position.d.ts +35 -0
- package/dist/types/utils/cartesian-chart-layout.d.ts +13 -0
- package/dist/types/utils/chart-viewport-controller.d.ts +21 -0
- package/dist/types/utils/choice-popup-alignment.d.ts +2 -2
- package/dist/types/utils/index.d.ts +2 -0
- package/dist/types/utils/metric-change.d.ts +53 -0
- package/dist/types/utils/select-controller.d.ts +3 -3
- package/dist/types/utils/token-defaults.d.ts +3 -0
- package/package.json +3 -3
- package/dist/components/p-1darPzys.js +0 -2
- package/dist/components/p-B8C_yfLQ.js +0 -2
- package/dist/components/p-B8C_yfLQ.js.map +0 -1
- package/dist/components/p-BcfC6phQ.js.map +0 -1
- package/dist/components/p-Bl001G0Y.js +0 -2
- package/dist/components/p-BuiZcSga.js.map +0 -1
- package/dist/components/p-Bv9frkvW.js +0 -2
- package/dist/components/p-Bv9frkvW.js.map +0 -1
- package/dist/components/p-C0-K8vnW.js +0 -2
- package/dist/components/p-C0-K8vnW.js.map +0 -1
- package/dist/components/p-C2ym_Ich.js +0 -2
- package/dist/components/p-C_ewSXTT.js +0 -2
- package/dist/components/p-C_ewSXTT.js.map +0 -1
- package/dist/components/p-Ca9-fnFl.js +0 -2
- package/dist/components/p-Ca9-fnFl.js.map +0 -1
- package/dist/components/p-CydY-v7X.js +0 -2
- package/dist/components/p-CydY-v7X.js.map +0 -1
- package/dist/components/p-De7NquTI.js +0 -2
- package/dist/components/p-De7NquTI.js.map +0 -1
- package/dist/components/p-Dg3xLna0.js +0 -2
- package/dist/components/p-Dg3xLna0.js.map +0 -1
- package/dist/components/p-k0Hmj1KV.js.map +0 -1
- package/dist/components/p-pgxlkvUy.js +0 -2
- package/dist/lib/utils/anchored-popup.d.ts +0 -23
- package/dist/types/components/Tooltip/tooltip-position.d.ts +0 -21
- package/dist/types/utils/anchored-popup.d.ts +0 -24
- /package/dist/components/{p-B-9A0e_0.js.map → p-B88n0pO3.js.map} +0 -0
- /package/dist/components/{p-C7bZ-jiU.js.map → p-BO5aqZcf.js.map} +0 -0
- /package/dist/components/{p-guQ7hv6W.js.map → p-BOCfICln.js.map} +0 -0
- /package/dist/components/{p-CvyTZ4Mg.js.map → p-CChcuL04.js.map} +0 -0
- /package/dist/components/{p-Crsn82rz.js.map → p-CCzG5xDD.js.map} +0 -0
- /package/dist/components/{p-BT5hgg5i.js.map → p-CIiBpjCn.js.map} +0 -0
- /package/dist/components/{p-B_5nxOeB.js.map → p-CgEYdMee.js.map} +0 -0
- /package/dist/components/{p-5-QAFB4j.js.map → p-Cuih680v.js.map} +0 -0
- /package/dist/components/{p-C_9ZH0A3.js.map → p-Cuqxh-Tq.js.map} +0 -0
- /package/dist/components/{p--eKXTylf.js.map → p-DCvA3Dc9.js.map} +0 -0
- /package/dist/components/{p-DNFd4Ao-.js.map → p-DLZm_-sU.js.map} +0 -0
- /package/dist/components/{p-C9h13YnX.js.map → p-DRnIuCiT.js.map} +0 -0
- /package/dist/components/{p-CUAYsK44.js.map → p-DlscFn2m.js.map} +0 -0
- /package/dist/components/{p-C6xC48hG.js.map → p-Re2kPrXm.js.map} +0 -0
- /package/dist/components/{p-DIa73GJP.js.map → p-XMZCaXs7.js.map} +0 -0
- /package/dist/components/{p-CqWV4Hl_.js.map → p-dyUp3Adm.js.map} +0 -0
- /package/dist/components/{p-CnAkobDW.js.map → p-nkYN0Bub.js.map} +0 -0
- /package/dist/components/{p-C2ym_Ich.js.map → p-yhj1lIPx.js.map} +0 -0
|
@@ -51,6 +51,8 @@
|
|
|
51
51
|
"The owning chart mounts the callout when a local interaction starts and unmounts it when that interaction ends.",
|
|
52
52
|
"Coordinate updates track within one mounted interaction session without restarting the show delay.",
|
|
53
53
|
"Placement flips horizontally or vertically when the rendered callout approaches a viewport edge.",
|
|
54
|
+
"Placement is chosen as a quadrant rather than computed as coordinates: the component measures its own rendered rect and flips its side and align props, which CSS consumes. It therefore does not use the shared anchored-position utilities, which resolve an anchor element into x and y.",
|
|
55
|
+
"This separation is deliberate. The callout follows the pointer instead of an element, so it has no anchor rect to resolve; adopting the shared geometry would mean rewriting its CSS to consume coordinates for no behavioral gain.",
|
|
54
56
|
"Reduced motion preserves the final visible state without an opacity transition."
|
|
55
57
|
],
|
|
56
58
|
"responsiveBehavior": [
|
|
@@ -193,7 +195,7 @@
|
|
|
193
195
|
},
|
|
194
196
|
"peerDependencies": {
|
|
195
197
|
"required": [
|
|
196
|
-
"@ds-mo/tokens ^
|
|
198
|
+
"@ds-mo/tokens ^5.0.0",
|
|
197
199
|
"@ds-mo/icons ^6.4.0"
|
|
198
200
|
],
|
|
199
201
|
"frameworks": "Custom Elements; React 18/19 wrappers; Angular 19-22 standalone adapters."
|
|
@@ -89,7 +89,7 @@
|
|
|
89
89
|
},
|
|
90
90
|
{
|
|
91
91
|
"label": "Positioning unit coverage",
|
|
92
|
-
"path": "tests/
|
|
92
|
+
"path": "tests/anchored-position.test.ts"
|
|
93
93
|
}
|
|
94
94
|
]
|
|
95
95
|
},
|
|
@@ -277,7 +277,7 @@
|
|
|
277
277
|
},
|
|
278
278
|
"peerDependencies": {
|
|
279
279
|
"required": [
|
|
280
|
-
"@ds-mo/tokens ^
|
|
280
|
+
"@ds-mo/tokens ^5.0.0",
|
|
281
281
|
"@ds-mo/icons ^6.4.0"
|
|
282
282
|
],
|
|
283
283
|
"frameworks": "Custom Elements; React 18/19 wrappers; Angular 19-22 standalone adapters."
|
|
@@ -299,12 +299,7 @@
|
|
|
299
299
|
},
|
|
300
300
|
{
|
|
301
301
|
"path": "src/wc/components/Tooltip/Tooltip.tsx",
|
|
302
|
-
"content": "import { Component, Prop, Element, Watch, h, Host } from '@stencil/core';\nimport {\n CONTROL_TEXT_VARIANT,\n resolveCssLengthPx,\n resolveCssTimeMs,\n resolveMotionTimeMs,\n TOKEN_CSS_LENGTHS,\n TOKEN_DEFAULTS,\n} from '../../utils';\nimport type { TextVariant } from '../Text/text-types';\nimport { shortcutKeyLabels } from '../../utils/shortcut-key';\nimport { computeTooltipPosition } from './tooltip-position';\n// Side-effect: register `ds-text` — the portal builds it via createElement, not JSX.\nimport '../Text/Text';\n\nexport type TooltipSide = 'top' | 'right' | 'bottom' | 'left';\nexport type TooltipAlign = 'start' | 'center' | 'end';\nexport type TooltipSize = 'md' | 'sm' | 'xs';\n\nconst FALLBACK_HEIGHT: Record<TooltipSize, string> = {\n md: TOKEN_DEFAULTS.size400,\n sm: TOKEN_DEFAULTS.size300,\n xs: TOKEN_DEFAULTS.size200,\n};\n\nlet lastDismissedAt = 0;\nlet tooltipIdCounter = 0;\n/** Currently open tooltip — force-cleared on warm handoff so tips don't cross-fade. */\nlet activeTooltip: Tooltip | null = null;\n\ntype DsTextEl = HTMLElement & {\n as: string;\n variant: TextVariant;\n emphasis: boolean;\n color: string;\n wrap: string;\n};\n\nfunction createDsText(opts: {\n variant: TextVariant;\n emphasis?: boolean;\n color?: string;\n wrap?: 'wrap' | 'nowrap' | 'balance' | 'pretty';\n text: string;\n}): DsTextEl {\n const el = document.createElement('ds-text') as DsTextEl;\n el.as = 'span';\n el.variant = opts.variant;\n el.emphasis = opts.emphasis ?? false;\n el.color = opts.color ?? 'inherit';\n if (opts.wrap) el.wrap = opts.wrap;\n el.textContent = opts.text;\n return el;\n}\n\n/**\n * Control-density label styles for portaled ds-text.\n * Scoped CSS requires `sc-ds-tooltip` on the host, but Text's `<Host class>`\n * overwrites imperative className — so these must be inline to stick.\n */\nfunction styleTooltipLabel(el: HTMLElement) {\n el.style.flexShrink = '0';\n el.style.minWidth = 'max-content';\n el.style.maxWidth = 'none';\n el.style.padding = '0 var(--ds-control-label-inset, var(--dimension-space-025))';\n el.style.color = 'var(--color-foreground-primary)';\n}\n\n/**\n * Imperative body portal for the popup.\n * Stencil must not own the portaled node — moving a VDOM child to `document.body`\n * causes recreate → re-portal → @State loops that freeze the page.\n */\n@Component({\n tag: 'ds-tooltip',\n styleUrl: 'Tooltip.css',\n scoped: true,\n})\nexport class Tooltip {\n @Element() el!: HTMLElement;\n\n /**\n * Tooltip text. Empty/whitespace skips show — useful when the wrapper must stay\n * mounted for layout/animation stability (e.g. PanelNav expand/collapse).\n */\n @Prop() label!: string;\n\n /** Control density (height, padding, type). */\n @Prop() size: TooltipSize = 'md';\n\n @Prop() side: TooltipSide = 'top';\n @Prop() align: TooltipAlign = 'center';\n /** Gap between anchor and tooltip — number (px) or TokoMo length. */\n @Prop() sideOffset: number | string = TOKEN_CSS_LENGTHS.space050;\n /** Cross-axis offset — number (px) or TokoMo length. */\n @Prop() alignOffset: number | string = 0;\n /**\n * Hover show delay before the tooltip appears.\n * Default: `--effect-animation-delay-medium-3` (1000ms). Accepts a number (ms)\n * or a TokoMo time token / `var(--effect-animation-delay-*)`. Applies to the\n * initial hover/focus show only — after a recent dismiss, reopen is instant.\n * Prefer the default; override only for denser chrome or rare/destructive actions.\n */\n @Prop() delay: number | string = TOKEN_DEFAULTS.animationDelayMedium3;\n @Prop() shortcutKey: string | undefined;\n @Prop() shortcutKeyPosition: 'start' | 'end' = 'end';\n\n private tooltipId = `ds-tooltip-${++tooltipIdCounter}`;\n private delayTimer: ReturnType<typeof setTimeout> | null = null;\n private closeTimer: ReturnType<typeof setTimeout> | null = null;\n private anchor: HTMLElement | null = null;\n private popupEl: HTMLElement | null = null;\n private positionObserver: ResizeObserver | null = null;\n private anchorObserver: MutationObserver | null = null;\n private positionFrame: number | null = null;\n private skipEnterAnimation = false;\n private isOpen = false;\n private lastInteractionWasKeyboard = false;\n private pointerEnterHandler = (event: PointerEvent) => {\n if (event.pointerType === 'touch') return;\n this.show();\n };\n private pointerLeaveHandler = (event: PointerEvent) => {\n if (event.pointerType === 'touch') return;\n this.hide();\n };\n private pointerDownHandler = (event: PointerEvent) => {\n this.lastInteractionWasKeyboard = false;\n if (event.pointerType === 'touch' && (this.isOpen || this.popupEl)) this.hideInstant();\n };\n private keyDownHandler = () => {\n this.lastInteractionWasKeyboard = true;\n };\n /** focusin/focusout so nested focus targets (e.g. ds-button-* inner button) still open the tip. */\n private focusHandler = () => {\n if (this.lastInteractionWasKeyboard || this.anchor?.matches(':focus-visible')) this.show();\n };\n private blurHandler: (e: FocusEvent) => void = (e) => {\n const next = e.relatedTarget as Node | null;\n if (next && this.anchor?.contains(next)) return;\n this.hide();\n };\n private scrollResizeHandler = () => {\n if (this.positionFrame !== null) return;\n this.positionFrame = requestAnimationFrame(() => {\n this.positionFrame = null;\n this.calculatePosition();\n });\n };\n private escapeHandler = (event: KeyboardEvent) => {\n if (event.key !== 'Escape' || !this.isOpen) return;\n event.preventDefault();\n this.hide();\n };\n\n componentDidLoad() {\n this.bindAnchor();\n this.anchorObserver = new MutationObserver(() => this.handleSlotChange());\n this.anchorObserver.observe(this.el, { childList: true });\n }\n\n disconnectedCallback() {\n if (activeTooltip === this) activeTooltip = null;\n this.anchorObserver?.disconnect();\n this.anchorObserver = null;\n this.unbindAnchor();\n this.clearTimers();\n this.destroyPopup();\n }\n\n @Watch('label')\n @Watch('size')\n @Watch('side')\n @Watch('align')\n @Watch('sideOffset')\n @Watch('alignOffset')\n @Watch('shortcutKey')\n @Watch('shortcutKeyPosition')\n onContentOrPositionChange() {\n if (!this.label?.trim()) {\n if (this.isOpen || this.popupEl) this.hideInstant();\n return;\n }\n if (!this.popupEl || !this.isOpen) return;\n this.renderPopupContent();\n this.schedulePosition();\n }\n\n private get viewportPadPx(): number {\n return resolveCssLengthPx(TOKEN_DEFAULTS.space050, TOKEN_DEFAULTS.space050);\n }\n\n private get sideOffsetPx(): number {\n return resolveCssLengthPx(this.sideOffset, TOKEN_DEFAULTS.space050);\n }\n\n private get alignOffsetPx(): number {\n return resolveCssLengthPx(this.alignOffset, 0);\n }\n\n private get hoverDelayMs(): number {\n return resolveCssTimeMs(this.delay, TOKEN_DEFAULTS.animationDelayMedium3);\n }\n\n private get instantReopenMs(): number {\n return resolveCssTimeMs(\n TOKEN_DEFAULTS.animationDurationMedium1,\n TOKEN_DEFAULTS.animationDurationMedium1,\n );\n }\n\n private get fadeOutMs(): number {\n return resolveMotionTimeMs(TOKEN_DEFAULTS.motionShort3, TOKEN_DEFAULTS.animationDurationShort3);\n }\n\n private get tooltipFallbackWidthPx(): number {\n return resolveCssLengthPx(TOKEN_DEFAULTS.tooltipFallbackWidth, TOKEN_DEFAULTS.tooltipFallbackWidth);\n }\n\n private get tooltipFallbackHeightPx(): number {\n const token = FALLBACK_HEIGHT[this.size];\n return resolveCssLengthPx(token, token);\n }\n\n private bindAnchor() {\n const slot = this.el.querySelector('slot') as HTMLSlotElement | null;\n const slotted = slot?.assignedElements?.() ?? [];\n const child = (slotted[0] ?? this.el.firstElementChild) as HTMLElement | null;\n if (!child || child === this.anchor) return;\n this.anchor = child;\n child.addEventListener('pointerenter', this.pointerEnterHandler);\n child.addEventListener('pointerleave', this.pointerLeaveHandler);\n child.addEventListener('pointerdown', this.pointerDownHandler);\n child.addEventListener('keydown', this.keyDownHandler);\n child.addEventListener('focusin', this.focusHandler);\n child.addEventListener('focusout', this.blurHandler);\n }\n\n private unbindAnchor() {\n if (!this.anchor) return;\n this.unlinkDescribedBy();\n this.anchor.removeEventListener('pointerenter', this.pointerEnterHandler);\n this.anchor.removeEventListener('pointerleave', this.pointerLeaveHandler);\n this.anchor.removeEventListener('pointerdown', this.pointerDownHandler);\n this.anchor.removeEventListener('keydown', this.keyDownHandler);\n this.anchor.removeEventListener('focusin', this.focusHandler);\n this.anchor.removeEventListener('focusout', this.blurHandler);\n this.anchor = null;\n }\n\n private linkDescribedBy() {\n if (!this.anchor || !this.popupEl) return;\n const ids = new Set((this.anchor.getAttribute('aria-describedby') ?? '').split(/\\s+/).filter(Boolean));\n ids.add(this.tooltipId);\n this.anchor.setAttribute('aria-describedby', Array.from(ids).join(' '));\n }\n\n private unlinkDescribedBy() {\n if (!this.anchor) return;\n const ids = (this.anchor.getAttribute('aria-describedby') ?? '')\n .split(/\\s+/)\n .filter(id => id && id !== this.tooltipId);\n if (ids.length) this.anchor.setAttribute('aria-describedby', ids.join(' '));\n else this.anchor.removeAttribute('aria-describedby');\n }\n\n private handleSlotChange = () => {\n const slot = this.el.querySelector('slot') as HTMLSlotElement | null;\n const next = (slot?.assignedElements?.()[0] ?? this.el.firstElementChild) as HTMLElement | null;\n if (next === this.anchor) return;\n if (this.isOpen || this.popupEl) this.hideInstant();\n this.unbindAnchor();\n this.bindAnchor();\n };\n\n private clearTimers() {\n if (this.delayTimer) { clearTimeout(this.delayTimer); this.delayTimer = null; }\n if (this.closeTimer) { clearTimeout(this.closeTimer); this.closeTimer = null; }\n }\n\n private setupOpenListeners() {\n window.addEventListener('scroll', this.scrollResizeHandler, true);\n window.addEventListener('resize', this.scrollResizeHandler);\n document.addEventListener('keydown', this.escapeHandler);\n }\n\n private teardownOpenListeners() {\n window.removeEventListener('scroll', this.scrollResizeHandler, true);\n window.removeEventListener('resize', this.scrollResizeHandler);\n document.removeEventListener('keydown', this.escapeHandler);\n if (this.positionFrame !== null) {\n cancelAnimationFrame(this.positionFrame);\n this.positionFrame = null;\n }\n }\n\n private destroyPopup() {\n this.teardownOpenListeners();\n this.positionObserver?.disconnect();\n this.positionObserver = null;\n this.unlinkDescribedBy();\n if (!this.popupEl) return;\n this.popupEl.remove();\n this.popupEl = null;\n }\n\n /**\n * Position after `ds-text` upgrades and lays out. Measuring synchronously in\n * `mountPopup` reads a collapsed/fallback width and places left/right tips wrong.\n */\n private schedulePosition() {\n if (!this.popupEl) return;\n\n const reapplyLabelStyles = () => {\n const label = this.popupEl?.querySelector(':scope > .tooltip-inner > ds-text');\n if (label instanceof HTMLElement) styleTooltipLabel(label);\n };\n\n requestAnimationFrame(() => {\n reapplyLabelStyles();\n this.calculatePosition();\n requestAnimationFrame(() => {\n reapplyLabelStyles();\n this.calculatePosition();\n });\n });\n\n this.positionObserver?.disconnect();\n this.positionObserver = new ResizeObserver(() => this.calculatePosition());\n this.positionObserver.observe(this.popupEl);\n if (this.anchor) this.positionObserver.observe(this.anchor);\n }\n\n /** Drop the open tip immediately (warm handoff to another tooltip). */\n private hideInstant() {\n this.clearTimers();\n lastDismissedAt = Date.now();\n if (activeTooltip === this) activeTooltip = null;\n this.destroyPopup();\n this.isOpen = false;\n this.skipEnterAnimation = false;\n }\n\n private show() {\n // Empty label = wrapper present for DOM stability (e.g. PanelNav expand/collapse)\n // but no tip should appear.\n if (!this.label?.trim()) return;\n this.clearTimers();\n let instant = Date.now() - lastDismissedAt < this.instantReopenMs;\n if (activeTooltip && activeTooltip !== this) {\n activeTooltip.hideInstant();\n instant = true;\n }\n // eslint-disable-next-line @typescript-eslint/no-this-alias -- module singleton tracks the active instance\n activeTooltip = this;\n this.skipEnterAnimation = instant;\n if (instant) {\n this.mountPopup();\n return;\n }\n this.delayTimer = setTimeout(() => {\n this.delayTimer = null;\n if (!this.label?.trim()) return;\n this.mountPopup();\n }, this.hoverDelayMs);\n }\n\n private hide() {\n this.clearTimers();\n if (!this.popupEl && !this.isOpen) return;\n lastDismissedAt = Date.now();\n if (!this.popupEl) {\n this.isOpen = false;\n return;\n }\n this.popupEl.classList.remove('tooltip-popup--instant');\n this.popupEl.classList.add('tooltip-popup--closing');\n if (this.fadeOutMs === 0) {\n if (activeTooltip === this) activeTooltip = null;\n this.destroyPopup();\n this.isOpen = false;\n this.skipEnterAnimation = false;\n return;\n }\n this.closeTimer = setTimeout(() => {\n this.closeTimer = null;\n if (activeTooltip === this) activeTooltip = null;\n this.destroyPopup();\n this.isOpen = false;\n this.skipEnterAnimation = false;\n }, this.fadeOutMs);\n }\n\n private mountPopup() {\n if (!this.popupEl) {\n const tip = document.createElement('div');\n tip.id = this.tooltipId;\n tip.setAttribute('role', 'tooltip');\n // `sc-ds-tooltip` is required for Stencil scoped CSS to match outside the host.\n tip.className = 'tooltip-popup sc-ds-tooltip';\n Object.assign(tip.style, {\n position: 'fixed',\n left: '0',\n top: '0',\n zIndex: 'var(--dimension-z-index-tooltip)',\n pointerEvents: 'none',\n });\n document.body.appendChild(tip);\n this.popupEl = tip;\n }\n\n this.setupOpenListeners();\n this.linkDescribedBy();\n this.popupEl.classList.toggle('tooltip-popup--instant', this.skipEnterAnimation);\n this.popupEl.classList.remove('tooltip-popup--closing');\n this.renderPopupContent();\n this.schedulePosition();\n this.isOpen = true;\n }\n\n private renderPopupContent() {\n if (!this.popupEl) return;\n const textVariant = CONTROL_TEXT_VARIANT[this.size];\n const density =\n this.size === 'md' ? 'ds-control--md' : this.size === 'sm' ? 'ds-control--sm' : 'ds-control--xs';\n // Every layout node needs `sc-ds-tooltip` so scoped selectors (`.foo.sc-ds-tooltip`) match.\n const sc = 'sc-ds-tooltip';\n\n const inner = document.createElement('div');\n inner.className = `tooltip-inner ${density} ${sc}`;\n\n const appendShortcut = (position: 'start' | 'end') => {\n if (!this.shortcutKey || this.shortcutKeyPosition !== position) return;\n const group = document.createElement('span');\n group.className = `key-hint-group ${sc}`;\n group.setAttribute('aria-hidden', 'true');\n\n for (const label of shortcutKeyLabels(this.shortcutKey)) {\n const hint = document.createElement('span');\n const wide = Array.from(label).length > 1 ? ' key-hint--wide' : '';\n hint.className = `key-hint${wide} ${sc}`;\n const text = document.createElement('span');\n text.className = `key-hint__label ${sc}`;\n text.textContent = label;\n hint.appendChild(text);\n group.appendChild(hint);\n }\n inner.appendChild(group);\n };\n\n appendShortcut('start');\n\n // ds-text is the layout box (metric-box + 2px control label-inset).\n // Inline styles required — see styleTooltipLabel.\n const label = createDsText({ variant: textVariant, wrap: 'nowrap', text: this.label });\n styleTooltipLabel(label);\n inner.appendChild(label);\n\n appendShortcut('end');\n\n this.popupEl.replaceChildren(inner);\n }\n\n private calculatePosition() {\n if (!this.anchor || !this.popupEl) return;\n const tip = this.popupEl;\n const a = this.anchor.getBoundingClientRect();\n const tw = tip.offsetWidth || this.tooltipFallbackWidthPx;\n const th = tip.offsetHeight || this.tooltipFallbackHeightPx;\n const { x, y, resolvedSide } = computeTooltipPosition({\n anchorRect: a,\n popupWidth: tw,\n popupHeight: th,\n side: this.side,\n align: this.align,\n sideOffsetPx: this.sideOffsetPx,\n alignOffsetPx: this.alignOffsetPx,\n viewportPadPx: this.viewportPadPx,\n viewportWidth: window.innerWidth,\n viewportHeight: window.innerHeight,\n });\n tip.dataset.side = resolvedSide;\n tip.style.transform = `translate(${Math.round(x)}px, ${Math.round(y)}px)`;\n }\n\n render() {\n return (\n <Host style={{ display: 'contents' }}>\n <slot onSlotchange={this.handleSlotChange} />\n </Host>\n );\n }\n}\n",
|
|
303
|
-
"type": "registry:ui"
|
|
304
|
-
},
|
|
305
|
-
{
|
|
306
|
-
"path": "src/wc/components/Tooltip/tooltip-position.ts",
|
|
307
|
-
"content": "import type { TooltipAlign, TooltipSide } from './Tooltip';\n\nexport interface TooltipPositionInput {\n anchorRect: Pick<DOMRectReadOnly, 'top' | 'left' | 'right' | 'bottom' | 'width' | 'height'>;\n popupWidth: number;\n popupHeight: number;\n side: TooltipSide;\n align: TooltipAlign;\n sideOffsetPx: number;\n alignOffsetPx: number;\n viewportPadPx: number;\n viewportWidth: number;\n viewportHeight: number;\n}\n\nexport interface TooltipPosition {\n x: number;\n y: number;\n resolvedSide: TooltipSide;\n}\n\nconst OPPOSITE_SIDE: Record<TooltipSide, TooltipSide> = {\n top: 'bottom',\n right: 'left',\n bottom: 'top',\n left: 'right',\n};\n\nfunction rawPosition(\n input: TooltipPositionInput,\n side: TooltipSide,\n): Pick<TooltipPosition, 'x' | 'y'> {\n const {\n anchorRect: anchor,\n popupWidth,\n popupHeight,\n align,\n sideOffsetPx,\n alignOffsetPx,\n } = input;\n\n if (side === 'top' || side === 'bottom') {\n return {\n x:\n align === 'start'\n ? anchor.left + alignOffsetPx\n : align === 'end'\n ? anchor.right - popupWidth + alignOffsetPx\n : anchor.left + anchor.width / 2 - popupWidth / 2 + alignOffsetPx,\n y: side === 'top'\n ? anchor.top - popupHeight - sideOffsetPx\n : anchor.bottom + sideOffsetPx,\n };\n }\n\n return {\n x: side === 'left'\n ? anchor.left - popupWidth - sideOffsetPx\n : anchor.right + sideOffsetPx,\n y:\n align === 'start'\n ? anchor.top + alignOffsetPx\n : align === 'end'\n ? anchor.bottom - popupHeight + alignOffsetPx\n : anchor.top + anchor.height / 2 - popupHeight / 2 + alignOffsetPx,\n };\n}\n\nfunction mainAxisOverflow(\n input: TooltipPositionInput,\n side: TooltipSide,\n position: Pick<TooltipPosition, 'x' | 'y'>,\n): number {\n const {\n popupWidth,\n popupHeight,\n viewportPadPx,\n viewportWidth,\n viewportHeight,\n } = input;\n\n if (side === 'top' || side === 'bottom') {\n return Math.max(viewportPadPx - position.y, 0) +\n Math.max(position.y + popupHeight - (viewportHeight - viewportPadPx), 0);\n }\n\n return Math.max(viewportPadPx - position.x, 0) +\n Math.max(position.x + popupWidth - (viewportWidth - viewportPadPx), 0);\n}\n\n/** Flip on the preferred axis when needed, then clamp within the viewport. */\nexport function computeTooltipPosition(input: TooltipPositionInput): TooltipPosition {\n const preferred = rawPosition(input, input.side);\n const oppositeSide = OPPOSITE_SIDE[input.side];\n const opposite = rawPosition(input, oppositeSide);\n const preferredOverflow = mainAxisOverflow(input, input.side, preferred);\n const oppositeOverflow = mainAxisOverflow(input, oppositeSide, opposite);\n const useOpposite = preferredOverflow > 0 && oppositeOverflow < preferredOverflow;\n const resolvedSide = useOpposite ? oppositeSide : input.side;\n const position = useOpposite ? opposite : preferred;\n const maxX = Math.max(input.viewportPadPx, input.viewportWidth - input.popupWidth - input.viewportPadPx);\n const maxY = Math.max(input.viewportPadPx, input.viewportHeight - input.popupHeight - input.viewportPadPx);\n\n return {\n x: Math.min(Math.max(position.x, input.viewportPadPx), maxX),\n y: Math.min(Math.max(position.y, input.viewportPadPx), maxY),\n resolvedSide,\n };\n}\n",
|
|
302
|
+
"content": "import { Component, Prop, Element, Watch, h, Host } from '@stencil/core';\nimport {\n CONTROL_TEXT_VARIANT,\n resolveCssLengthPx,\n resolveCssTimeMs,\n resolveMotionTimeMs,\n TOKEN_CSS_LENGTHS,\n TOKEN_DEFAULTS,\n} from '../../utils';\nimport type { TextVariant } from '../Text/text-types';\nimport { shortcutKeyLabels } from '../../utils/shortcut-key';\nimport { AnchoredPositionController } from '../../utils/anchored-position-controller';\n// Side-effect: register `ds-text` — the portal builds it via createElement, not JSX.\nimport '../Text/Text';\n\nexport type TooltipSide = 'top' | 'right' | 'bottom' | 'left';\nexport type TooltipAlign = 'start' | 'center' | 'end';\nexport type TooltipSize = 'md' | 'sm' | 'xs';\n\nconst FALLBACK_HEIGHT: Record<TooltipSize, string> = {\n md: TOKEN_DEFAULTS.size400,\n sm: TOKEN_DEFAULTS.size300,\n xs: TOKEN_DEFAULTS.size200,\n};\n\nlet lastDismissedAt = 0;\nlet tooltipIdCounter = 0;\n/** Currently open tooltip — force-cleared on warm handoff so tips don't cross-fade. */\nlet activeTooltip: Tooltip | null = null;\n\ntype DsTextEl = HTMLElement & {\n as: string;\n variant: TextVariant;\n emphasis: boolean;\n color: string;\n wrap: string;\n};\n\nfunction createDsText(opts: {\n variant: TextVariant;\n emphasis?: boolean;\n color?: string;\n wrap?: 'wrap' | 'nowrap' | 'balance' | 'pretty';\n text: string;\n}): DsTextEl {\n const el = document.createElement('ds-text') as DsTextEl;\n el.as = 'span';\n el.variant = opts.variant;\n el.emphasis = opts.emphasis ?? false;\n el.color = opts.color ?? 'inherit';\n if (opts.wrap) el.wrap = opts.wrap;\n el.textContent = opts.text;\n return el;\n}\n\n/**\n * Control-density label styles for portaled ds-text.\n * Scoped CSS requires `sc-ds-tooltip` on the host, but Text's `<Host class>`\n * overwrites imperative className — so these must be inline to stick.\n */\nfunction styleTooltipLabel(el: HTMLElement) {\n el.style.flexShrink = '0';\n el.style.minWidth = 'max-content';\n el.style.maxWidth = 'none';\n el.style.padding = '0 var(--ds-control-label-inset, var(--dimension-space-025))';\n el.style.color = 'var(--color-foreground-primary)';\n}\n\n/**\n * Imperative body portal for the popup.\n * Stencil must not own the portaled node — moving a VDOM child to `document.body`\n * causes recreate → re-portal → @State loops that freeze the page.\n */\n@Component({\n tag: 'ds-tooltip',\n styleUrl: 'Tooltip.css',\n scoped: true,\n})\nexport class Tooltip {\n @Element() el!: HTMLElement;\n\n /**\n * Tooltip text. Empty/whitespace skips show — useful when the wrapper must stay\n * mounted for layout/animation stability (e.g. PanelNav expand/collapse).\n */\n @Prop() label!: string;\n\n /** Control density (height, padding, type). */\n @Prop() size: TooltipSize = 'md';\n\n @Prop() side: TooltipSide = 'top';\n @Prop() align: TooltipAlign = 'center';\n /** Gap between anchor and tooltip — number (px) or TokoMo length. */\n @Prop() sideOffset: number | string = TOKEN_CSS_LENGTHS.space050;\n /** Cross-axis offset — number (px) or TokoMo length. */\n @Prop() alignOffset: number | string = 0;\n /**\n * Hover show delay before the tooltip appears.\n * Default: `--effect-animation-delay-medium-3` (1000ms). Accepts a number (ms)\n * or a TokoMo time token / `var(--effect-animation-delay-*)`. Applies to the\n * initial hover/focus show only — after a recent dismiss, reopen is instant.\n * Prefer the default; override only for denser chrome or rare/destructive actions.\n */\n @Prop() delay: number | string = TOKEN_DEFAULTS.animationDelayMedium3;\n @Prop() shortcutKey: string | undefined;\n @Prop() shortcutKeyPosition: 'start' | 'end' = 'end';\n\n private tooltipId = `ds-tooltip-${++tooltipIdCounter}`;\n private delayTimer: ReturnType<typeof setTimeout> | null = null;\n private closeTimer: ReturnType<typeof setTimeout> | null = null;\n private anchor: HTMLElement | null = null;\n private popupEl: HTMLElement | null = null;\n private anchorObserver: MutationObserver | null = null;\n private readonly position = new AnchoredPositionController({\n getAnchor: () => this.anchor,\n getPopup: () => this.popupEl,\n measure: (anchor, tip) => ({\n anchorRect: anchor.getBoundingClientRect(),\n // Fall back to token metrics until `ds-text` upgrades and reports a width.\n popupWidth: tip.offsetWidth || this.tooltipFallbackWidthPx,\n popupHeight: tip.offsetHeight || this.tooltipFallbackHeightPx,\n side: this.side,\n align: this.align,\n sideOffsetPx: this.sideOffsetPx,\n alignOffsetPx: this.alignOffsetPx,\n viewportPadPx: this.viewportPadPx,\n viewportWidth: window.innerWidth,\n viewportHeight: window.innerHeight,\n }),\n apply: ({ x, y, resolvedSide }) => {\n if (!this.popupEl) return;\n this.popupEl.dataset.side = resolvedSide;\n this.popupEl.style.transform = `translate(${Math.round(x)}px, ${Math.round(y)}px)`;\n },\n liveUpdate: 'frame',\n observeResize: true,\n });\n private skipEnterAnimation = false;\n private isOpen = false;\n private lastInteractionWasKeyboard = false;\n private pointerEnterHandler = (event: PointerEvent) => {\n if (event.pointerType === 'touch') return;\n this.show();\n };\n private pointerLeaveHandler = (event: PointerEvent) => {\n if (event.pointerType === 'touch') return;\n this.hide();\n };\n private pointerDownHandler = (event: PointerEvent) => {\n this.lastInteractionWasKeyboard = false;\n if (event.pointerType === 'touch' && (this.isOpen || this.popupEl)) this.hideInstant();\n };\n private keyDownHandler = () => {\n this.lastInteractionWasKeyboard = true;\n };\n /** focusin/focusout so nested focus targets (e.g. ds-button-* inner button) still open the tip. */\n private focusHandler = () => {\n if (this.lastInteractionWasKeyboard || this.anchor?.matches(':focus-visible')) this.show();\n };\n private blurHandler: (e: FocusEvent) => void = (e) => {\n const next = e.relatedTarget as Node | null;\n if (next && this.anchor?.contains(next)) return;\n this.hide();\n };\n private escapeHandler = (event: KeyboardEvent) => {\n if (event.key !== 'Escape' || !this.isOpen) return;\n event.preventDefault();\n this.hide();\n };\n\n componentDidLoad() {\n this.bindAnchor();\n this.anchorObserver = new MutationObserver(() => this.handleSlotChange());\n this.anchorObserver.observe(this.el, { childList: true });\n }\n\n disconnectedCallback() {\n if (activeTooltip === this) activeTooltip = null;\n this.anchorObserver?.disconnect();\n this.anchorObserver = null;\n this.unbindAnchor();\n this.clearTimers();\n this.destroyPopup();\n }\n\n @Watch('label')\n @Watch('size')\n @Watch('side')\n @Watch('align')\n @Watch('sideOffset')\n @Watch('alignOffset')\n @Watch('shortcutKey')\n @Watch('shortcutKeyPosition')\n onContentOrPositionChange() {\n if (!this.label?.trim()) {\n if (this.isOpen || this.popupEl) this.hideInstant();\n return;\n }\n if (!this.popupEl || !this.isOpen) return;\n this.renderPopupContent();\n this.schedulePosition();\n }\n\n private get viewportPadPx(): number {\n return resolveCssLengthPx(TOKEN_DEFAULTS.space050, TOKEN_DEFAULTS.space050);\n }\n\n private get sideOffsetPx(): number {\n return resolveCssLengthPx(this.sideOffset, TOKEN_DEFAULTS.space050);\n }\n\n private get alignOffsetPx(): number {\n return resolveCssLengthPx(this.alignOffset, 0);\n }\n\n private get hoverDelayMs(): number {\n return resolveCssTimeMs(this.delay, TOKEN_DEFAULTS.animationDelayMedium3);\n }\n\n private get instantReopenMs(): number {\n return resolveCssTimeMs(\n TOKEN_DEFAULTS.animationDurationMedium1,\n TOKEN_DEFAULTS.animationDurationMedium1,\n );\n }\n\n private get fadeOutMs(): number {\n return resolveMotionTimeMs(TOKEN_DEFAULTS.motionShort3, TOKEN_DEFAULTS.animationDurationShort3);\n }\n\n private get tooltipFallbackWidthPx(): number {\n return resolveCssLengthPx(TOKEN_DEFAULTS.tooltipFallbackWidth, TOKEN_DEFAULTS.tooltipFallbackWidth);\n }\n\n private get tooltipFallbackHeightPx(): number {\n const token = FALLBACK_HEIGHT[this.size];\n return resolveCssLengthPx(token, token);\n }\n\n private bindAnchor() {\n const slot = this.el.querySelector('slot') as HTMLSlotElement | null;\n const slotted = slot?.assignedElements?.() ?? [];\n const child = (slotted[0] ?? this.el.firstElementChild) as HTMLElement | null;\n if (!child || child === this.anchor) return;\n this.anchor = child;\n child.addEventListener('pointerenter', this.pointerEnterHandler);\n child.addEventListener('pointerleave', this.pointerLeaveHandler);\n child.addEventListener('pointerdown', this.pointerDownHandler);\n child.addEventListener('keydown', this.keyDownHandler);\n child.addEventListener('focusin', this.focusHandler);\n child.addEventListener('focusout', this.blurHandler);\n }\n\n private unbindAnchor() {\n if (!this.anchor) return;\n this.unlinkDescribedBy();\n this.anchor.removeEventListener('pointerenter', this.pointerEnterHandler);\n this.anchor.removeEventListener('pointerleave', this.pointerLeaveHandler);\n this.anchor.removeEventListener('pointerdown', this.pointerDownHandler);\n this.anchor.removeEventListener('keydown', this.keyDownHandler);\n this.anchor.removeEventListener('focusin', this.focusHandler);\n this.anchor.removeEventListener('focusout', this.blurHandler);\n this.anchor = null;\n }\n\n private linkDescribedBy() {\n if (!this.anchor || !this.popupEl) return;\n const ids = new Set((this.anchor.getAttribute('aria-describedby') ?? '').split(/\\s+/).filter(Boolean));\n ids.add(this.tooltipId);\n this.anchor.setAttribute('aria-describedby', Array.from(ids).join(' '));\n }\n\n private unlinkDescribedBy() {\n if (!this.anchor) return;\n const ids = (this.anchor.getAttribute('aria-describedby') ?? '')\n .split(/\\s+/)\n .filter(id => id && id !== this.tooltipId);\n if (ids.length) this.anchor.setAttribute('aria-describedby', ids.join(' '));\n else this.anchor.removeAttribute('aria-describedby');\n }\n\n private handleSlotChange = () => {\n const slot = this.el.querySelector('slot') as HTMLSlotElement | null;\n const next = (slot?.assignedElements?.()[0] ?? this.el.firstElementChild) as HTMLElement | null;\n if (next === this.anchor) return;\n if (this.isOpen || this.popupEl) this.hideInstant();\n this.unbindAnchor();\n this.bindAnchor();\n };\n\n private clearTimers() {\n if (this.delayTimer) { clearTimeout(this.delayTimer); this.delayTimer = null; }\n if (this.closeTimer) { clearTimeout(this.closeTimer); this.closeTimer = null; }\n }\n\n private setupOpenListeners() {\n // Binds scroll/resize + the resize observer; placement itself is scheduled\n // by schedulePosition() so the label restyle stays interleaved.\n this.position.observe();\n document.addEventListener('keydown', this.escapeHandler);\n }\n\n private teardownOpenListeners() {\n this.position.unobserve();\n document.removeEventListener('keydown', this.escapeHandler);\n }\n\n private destroyPopup() {\n this.teardownOpenListeners();\n this.unlinkDescribedBy();\n if (!this.popupEl) return;\n this.popupEl.remove();\n this.popupEl = null;\n }\n\n /**\n * Position after `ds-text` upgrades and lays out. Measuring synchronously in\n * `mountPopup` reads a collapsed/fallback width and places left/right tips wrong.\n */\n private schedulePosition() {\n if (!this.popupEl) return;\n\n const reapplyLabelStyles = () => {\n const label = this.popupEl?.querySelector(':scope > .tooltip-inner > ds-text');\n if (label instanceof HTMLElement) styleTooltipLabel(label);\n };\n\n requestAnimationFrame(() => {\n reapplyLabelStyles();\n this.position.update();\n requestAnimationFrame(() => {\n reapplyLabelStyles();\n this.position.update();\n });\n });\n\n // The popup remounts on every show, so re-target the observer bound in start().\n this.position.observeResizeTargets();\n }\n\n /** Drop the open tip immediately (warm handoff to another tooltip). */\n private hideInstant() {\n this.clearTimers();\n lastDismissedAt = Date.now();\n if (activeTooltip === this) activeTooltip = null;\n this.destroyPopup();\n this.isOpen = false;\n this.skipEnterAnimation = false;\n }\n\n private show() {\n // Empty label = wrapper present for DOM stability (e.g. PanelNav expand/collapse)\n // but no tip should appear.\n if (!this.label?.trim()) return;\n this.clearTimers();\n let instant = Date.now() - lastDismissedAt < this.instantReopenMs;\n if (activeTooltip && activeTooltip !== this) {\n activeTooltip.hideInstant();\n instant = true;\n }\n // eslint-disable-next-line @typescript-eslint/no-this-alias -- module singleton tracks the active instance\n activeTooltip = this;\n this.skipEnterAnimation = instant;\n if (instant) {\n this.mountPopup();\n return;\n }\n this.delayTimer = setTimeout(() => {\n this.delayTimer = null;\n if (!this.label?.trim()) return;\n this.mountPopup();\n }, this.hoverDelayMs);\n }\n\n private hide() {\n this.clearTimers();\n if (!this.popupEl && !this.isOpen) return;\n lastDismissedAt = Date.now();\n if (!this.popupEl) {\n this.isOpen = false;\n return;\n }\n this.popupEl.classList.remove('tooltip-popup--instant');\n this.popupEl.classList.add('tooltip-popup--closing');\n if (this.fadeOutMs === 0) {\n if (activeTooltip === this) activeTooltip = null;\n this.destroyPopup();\n this.isOpen = false;\n this.skipEnterAnimation = false;\n return;\n }\n this.closeTimer = setTimeout(() => {\n this.closeTimer = null;\n if (activeTooltip === this) activeTooltip = null;\n this.destroyPopup();\n this.isOpen = false;\n this.skipEnterAnimation = false;\n }, this.fadeOutMs);\n }\n\n private mountPopup() {\n if (!this.popupEl) {\n const tip = document.createElement('div');\n tip.id = this.tooltipId;\n tip.setAttribute('role', 'tooltip');\n // `sc-ds-tooltip` is required for Stencil scoped CSS to match outside the host.\n tip.className = 'tooltip-popup sc-ds-tooltip';\n Object.assign(tip.style, {\n position: 'fixed',\n left: '0',\n top: '0',\n zIndex: 'var(--dimension-z-index-tooltip)',\n pointerEvents: 'none',\n });\n document.body.appendChild(tip);\n this.popupEl = tip;\n }\n\n this.setupOpenListeners();\n this.linkDescribedBy();\n this.popupEl.classList.toggle('tooltip-popup--instant', this.skipEnterAnimation);\n this.popupEl.classList.remove('tooltip-popup--closing');\n this.renderPopupContent();\n this.schedulePosition();\n this.isOpen = true;\n }\n\n private renderPopupContent() {\n if (!this.popupEl) return;\n const textVariant = CONTROL_TEXT_VARIANT[this.size];\n const density =\n this.size === 'md' ? 'ds-control--md' : this.size === 'sm' ? 'ds-control--sm' : 'ds-control--xs';\n // Every layout node needs `sc-ds-tooltip` so scoped selectors (`.foo.sc-ds-tooltip`) match.\n const sc = 'sc-ds-tooltip';\n\n const inner = document.createElement('div');\n inner.className = `tooltip-inner ${density} ${sc}`;\n\n const appendShortcut = (position: 'start' | 'end') => {\n if (!this.shortcutKey || this.shortcutKeyPosition !== position) return;\n const group = document.createElement('span');\n group.className = `key-hint-group ${sc}`;\n group.setAttribute('aria-hidden', 'true');\n\n for (const label of shortcutKeyLabels(this.shortcutKey)) {\n const hint = document.createElement('span');\n const wide = Array.from(label).length > 1 ? ' key-hint--wide' : '';\n hint.className = `key-hint${wide} ${sc}`;\n const text = document.createElement('span');\n text.className = `key-hint__label ${sc}`;\n text.textContent = label;\n hint.appendChild(text);\n group.appendChild(hint);\n }\n inner.appendChild(group);\n };\n\n appendShortcut('start');\n\n // ds-text is the layout box (metric-box + 2px control label-inset).\n // Inline styles required — see styleTooltipLabel.\n const label = createDsText({ variant: textVariant, wrap: 'nowrap', text: this.label });\n styleTooltipLabel(label);\n inner.appendChild(label);\n\n appendShortcut('end');\n\n this.popupEl.replaceChildren(inner);\n }\n\n render() {\n return (\n <Host style={{ display: 'contents' }}>\n <slot onSlotchange={this.handleSlotChange} />\n </Host>\n );\n }\n}\n",
|
|
308
303
|
"type": "registry:ui"
|
|
309
304
|
}
|
|
310
305
|
]
|
|
@@ -18,6 +18,7 @@ export { DsButtonUnfilled } from "./ds-button-unfilled.js";
|
|
|
18
18
|
export { DsCardDataVizBar } from "./ds-card-data-viz-bar.js";
|
|
19
19
|
export { DsCardDataVizDonut } from "./ds-card-data-viz-donut.js";
|
|
20
20
|
export { DsCardDataVizLine } from "./ds-card-data-viz-line.js";
|
|
21
|
+
export { DsCardOverview } from "./ds-card-overview.js";
|
|
21
22
|
export { DsCardSetting } from "./ds-card-setting.js";
|
|
22
23
|
export { DsCardShellDataViz } from "./ds-card-shell-data-viz.js";
|
|
23
24
|
export { DsChartBarStacked } from "./ds-chart-bar-stacked.js";
|
package/dist/react/components.js
CHANGED
|
@@ -19,6 +19,7 @@ export { DsButtonUnfilled } from "./ds-button-unfilled.js";
|
|
|
19
19
|
export { DsCardDataVizBar } from "./ds-card-data-viz-bar.js";
|
|
20
20
|
export { DsCardDataVizDonut } from "./ds-card-data-viz-donut.js";
|
|
21
21
|
export { DsCardDataVizLine } from "./ds-card-data-viz-line.js";
|
|
22
|
+
export { DsCardOverview } from "./ds-card-overview.js";
|
|
22
23
|
export { DsCardSetting } from "./ds-card-setting.js";
|
|
23
24
|
export { DsCardShellDataViz } from "./ds-card-shell-data-viz.js";
|
|
24
25
|
export { DsChartBarStacked } from "./ds-chart-bar-stacked.js";
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* This file was automatically generated by the Stencil React Output Target.
|
|
3
|
+
* Changes to this file may cause incorrect behavior and will be lost if the code is regenerated.
|
|
4
|
+
*/
|
|
5
|
+
import type { EventName, StencilReactComponent } from '@stencil/react-output-target/runtime';
|
|
6
|
+
import { type DsCardOverviewCustomEvent, type OverviewMetric } from "@ds-mo/ui";
|
|
7
|
+
import type { Components } from "@ds-mo/ui/dist/components";
|
|
8
|
+
import { DsCardOverview as DsCardOverviewElement } from "@ds-mo/ui/dist/components/ds-card-overview.js";
|
|
9
|
+
export type DsCardOverviewEvents = {
|
|
10
|
+
onDsMetricSelect: EventName<DsCardOverviewCustomEvent<OverviewMetric>>;
|
|
11
|
+
};
|
|
12
|
+
export declare const DsCardOverview: StencilReactComponent<DsCardOverviewElement, DsCardOverviewEvents, Components.DsCardOverview>;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { createComponent } from '@stencil/react-output-target/runtime';
|
|
3
|
+
import React from 'react';
|
|
4
|
+
import { DsCardOverview as DsCardOverviewElement, defineCustomElement as defineDsCardOverview } from "@ds-mo/ui/dist/components/ds-card-overview.js";
|
|
5
|
+
export const DsCardOverview = /*@__PURE__*/ createComponent({
|
|
6
|
+
tagName: 'ds-card-overview',
|
|
7
|
+
elementClass: DsCardOverviewElement,
|
|
8
|
+
// @ts-ignore - ignore potential React type mismatches between the Stencil Output Target and your project.
|
|
9
|
+
react: React,
|
|
10
|
+
events: { onDsMetricSelect: 'dsMetricSelect' },
|
|
11
|
+
defineCustomElement: defineDsCardOverview
|
|
12
|
+
});
|
|
@@ -7,14 +7,22 @@
|
|
|
7
7
|
* approved press-scaling button so its surface and elevation move as one unit.
|
|
8
8
|
* The owner still supplies layout, radius, surface, and any backdrop blur.
|
|
9
9
|
* Keep wrapped controls' optional resting borders off.
|
|
10
|
+
*
|
|
11
|
+
* Both `:host(.ds-control-elevation)` and `.ds-control-elevation` are listed so
|
|
12
|
+
* Stencil scoped components work whether the class sits on the host (a component
|
|
13
|
+
* that is itself the elevated surface) or on an inner wrapper. Scoped builds
|
|
14
|
+
* suffix host and child classes differently, so a plain class alone silently
|
|
15
|
+
* misses the host.
|
|
10
16
|
*/
|
|
11
17
|
|
|
18
|
+
:host(.ds-control-elevation),
|
|
12
19
|
.ds-control-elevation {
|
|
13
20
|
position: relative;
|
|
14
21
|
overflow: visible;
|
|
15
22
|
box-shadow: var(--ds-control-elevation-shadow, none);
|
|
16
23
|
}
|
|
17
24
|
|
|
25
|
+
:host(.ds-control-elevation)::after,
|
|
18
26
|
.ds-control-elevation::after {
|
|
19
27
|
content: '';
|
|
20
28
|
position: absolute;
|
|
@@ -26,16 +34,19 @@
|
|
|
26
34
|
pointer-events: none;
|
|
27
35
|
}
|
|
28
36
|
|
|
37
|
+
:host(.ds-control-elevation--sm),
|
|
29
38
|
.ds-control-elevation--sm {
|
|
30
39
|
--ds-control-elevation-shadow: var(--effect-shadow-elevated-sm);
|
|
31
40
|
--ds-control-elevation-highlight: var(--effect-highlight-elevated-sm);
|
|
32
41
|
}
|
|
33
42
|
|
|
43
|
+
:host(.ds-control-elevation--md),
|
|
34
44
|
.ds-control-elevation--md {
|
|
35
45
|
--ds-control-elevation-shadow: var(--effect-shadow-elevated-md);
|
|
36
46
|
--ds-control-elevation-highlight: var(--effect-highlight-elevated-md);
|
|
37
47
|
}
|
|
38
48
|
|
|
49
|
+
:host(.ds-control-elevation--floating),
|
|
39
50
|
.ds-control-elevation--floating {
|
|
40
51
|
--ds-control-elevation-shadow: var(--effect-shadow-elevated-floating);
|
|
41
52
|
--ds-control-elevation-highlight: var(--effect-highlight-elevated-floating);
|
|
@@ -7,6 +7,7 @@ export type ButtonFilledBackground = ChoiceBackground;
|
|
|
7
7
|
export type ButtonFilledVariant = 'icon' | 'label' | 'icon-label';
|
|
8
8
|
export type ButtonFilledSize = 'lg' | 'md' | 'sm' | 'xs';
|
|
9
9
|
export type ButtonFilledWidth = ControlWidth;
|
|
10
|
+
export type ButtonFilledPopup = 'true' | 'menu' | 'listbox' | 'tree' | 'grid' | 'dialog';
|
|
10
11
|
export declare class ButtonFilled {
|
|
11
12
|
el: HTMLElement;
|
|
12
13
|
/**
|
|
@@ -38,6 +39,11 @@ export declare class ButtonFilled {
|
|
|
38
39
|
background: ButtonFilledBackground | undefined;
|
|
39
40
|
/** Use the half-radius treatment instead of the default control radius. */
|
|
40
41
|
rounded: boolean;
|
|
42
|
+
/**
|
|
43
|
+
* Scale down during a physical pointer press.
|
|
44
|
+
* Disable when an owning composite requires fixed child or background geometry.
|
|
45
|
+
*/
|
|
46
|
+
pressScale: boolean;
|
|
41
47
|
/** Disables interaction. */
|
|
42
48
|
isInactive: boolean;
|
|
43
49
|
/** Shows an inline loader and prevents interaction without applying inactive opacity. */
|
|
@@ -46,12 +52,38 @@ export declare class ButtonFilled {
|
|
|
46
52
|
type: 'button' | 'submit' | 'reset';
|
|
47
53
|
/** Accessible name override. Required for icon-only buttons. */
|
|
48
54
|
ariaLabel: string | null;
|
|
55
|
+
/** ID of the popup this button controls. */
|
|
56
|
+
controls: string | undefined;
|
|
57
|
+
/**
|
|
58
|
+
* Controlled open state of the popup this button triggers. Holds the pressed
|
|
59
|
+
* wash for the popup's rendered lifecycle. ButtonFilled has no selected state,
|
|
60
|
+
* so an open popup never promotes to an active treatment.
|
|
61
|
+
*/
|
|
62
|
+
expanded: boolean | undefined;
|
|
63
|
+
/** Popup type exposed to assistive technology. */
|
|
64
|
+
haspopup: ButtonFilledPopup | undefined;
|
|
65
|
+
/** Pressed semantics for a toggle command that reports state elsewhere. */
|
|
66
|
+
pressed: boolean | undefined;
|
|
67
|
+
/**
|
|
68
|
+
* This action *has* a menu: implies `aria-haspopup="menu"` and adds the trailing
|
|
69
|
+
* chevron that carries the affordance.
|
|
70
|
+
*
|
|
71
|
+
* Only `label` and `icon-label` are supported. A filled button is never the
|
|
72
|
+
* overflow / more-options control — that role belongs to ButtonUnfilled with an
|
|
73
|
+
* `Ellipses` glyph, which conveys the menu without a chevron.
|
|
74
|
+
*
|
|
75
|
+
* Use `haspopup` directly for non-menu popups.
|
|
76
|
+
*/
|
|
77
|
+
hasMenu: boolean;
|
|
49
78
|
dsClick: EventEmitter<MouseEvent>;
|
|
50
79
|
private buttonEl;
|
|
51
80
|
setFocus(): Promise<void>;
|
|
52
81
|
private handleClick;
|
|
53
82
|
private get showIcon();
|
|
54
83
|
private get showLabel();
|
|
84
|
+
/** Icon-only triggers rely on their own glyph, so the chevron is label-bound. */
|
|
85
|
+
private get showChevron();
|
|
86
|
+
private get resolvedHaspopup();
|
|
55
87
|
private get accessibleName();
|
|
56
88
|
render(): any;
|
|
57
89
|
}
|
|
@@ -32,6 +32,11 @@ export declare class ButtonUnfilled {
|
|
|
32
32
|
hasBorder: boolean;
|
|
33
33
|
/** Use the half-radius treatment instead of the default control radius. */
|
|
34
34
|
rounded: boolean;
|
|
35
|
+
/**
|
|
36
|
+
* Scale down during a physical pointer press.
|
|
37
|
+
* Disable when an owning composite requires fixed child or background geometry.
|
|
38
|
+
*/
|
|
39
|
+
pressScale: boolean;
|
|
35
40
|
/** Show a notification dot at the top-right of the icon zone (icon variant only). */
|
|
36
41
|
dot: boolean;
|
|
37
42
|
/** Disables interaction. */
|
|
@@ -48,6 +53,18 @@ export declare class ButtonUnfilled {
|
|
|
48
53
|
expanded: boolean | undefined;
|
|
49
54
|
haspopup: ButtonUnfilledPopup | undefined;
|
|
50
55
|
pressed: boolean | undefined;
|
|
56
|
+
/**
|
|
57
|
+
* Mark the button as a Menu trigger. Implies `aria-haspopup="menu"` and covers
|
|
58
|
+
* both menu-button shapes:
|
|
59
|
+
*
|
|
60
|
+
* - `label` / `icon-label` — the action *has* a menu; a trailing chevron carries
|
|
61
|
+
* the affordance.
|
|
62
|
+
* - `icon` — the button *is* a menu, i.e. the overflow / more-options control.
|
|
63
|
+
* No chevron is added, so the glyph must convey it on its own; use `Ellipses`.
|
|
64
|
+
*
|
|
65
|
+
* Use `haspopup` directly for non-menu popups.
|
|
66
|
+
*/
|
|
67
|
+
hasMenu: boolean;
|
|
51
68
|
/**
|
|
52
69
|
* Native `tabindex` for roving keyboard groups in shell chrome.
|
|
53
70
|
* Omit for the default button tab stop (`0`).
|
|
@@ -61,6 +78,9 @@ export declare class ButtonUnfilled {
|
|
|
61
78
|
private get showIcon();
|
|
62
79
|
private get showLabel();
|
|
63
80
|
private get showDot();
|
|
81
|
+
/** Icon-only triggers rely on their own glyph, so the chevron is label-bound. */
|
|
82
|
+
private get showChevron();
|
|
83
|
+
private get resolvedHaspopup();
|
|
64
84
|
/** An expanded popup trigger is visually active even when selection is controlled separately. */
|
|
65
85
|
private get visuallyActive();
|
|
66
86
|
/** Knock-out ring: selected fill → active wash; otherwise surface token. */
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { EventEmitter } from '../../stencil-public-runtime';
|
|
2
|
+
import type { CardOverviewLayout, CardOverviewVariant, OverviewMetric, OverviewScore } from './card-overview-types';
|
|
3
|
+
export declare class CardOverview {
|
|
4
|
+
el: HTMLElement;
|
|
5
|
+
/** Full summary card, or the condensed 48px summary bar. */
|
|
6
|
+
variant: CardOverviewVariant;
|
|
7
|
+
/** Intrinsic responsive layout, or a page-owned single-column stack. */
|
|
8
|
+
layout: CardOverviewLayout;
|
|
9
|
+
/** Leading summary block. Omit to render the bar without a headline figure. */
|
|
10
|
+
score: OverviewScore | undefined;
|
|
11
|
+
/** Current reporting period, for example `Jun 29, 2026 – Jul 26, 2026`. */
|
|
12
|
+
periodLabel: string;
|
|
13
|
+
/** Comparison caption, for example `vs Previous period`. */
|
|
14
|
+
comparisonLabel: string;
|
|
15
|
+
/** Measures rendered in the responsive grid. Only the first six are shown. */
|
|
16
|
+
metrics: OverviewMetric[];
|
|
17
|
+
/**
|
|
18
|
+
* Width a metric cell may shrink to before the grid drops a column. The grid
|
|
19
|
+
* reflows and then stacks from this alone, so no measurement is required.
|
|
20
|
+
*/
|
|
21
|
+
metricMinWidth: string;
|
|
22
|
+
/** Replace the score and metrics with skeletons while data resolves. */
|
|
23
|
+
isLoading: boolean;
|
|
24
|
+
/** Message shown in place of the score when its figure cannot be resolved. */
|
|
25
|
+
scoreErrorMessage: string | undefined;
|
|
26
|
+
/** Accessible name for the region. */
|
|
27
|
+
overviewLabel: string;
|
|
28
|
+
/** Emitted when a metric that is not inactive is activated. */
|
|
29
|
+
dsMetricSelect: EventEmitter<OverviewMetric>;
|
|
30
|
+
/** Roving tab stop across selectable metrics. */
|
|
31
|
+
private focusedMetricIndex;
|
|
32
|
+
private get hasScore();
|
|
33
|
+
private get visibleMetrics();
|
|
34
|
+
/** Index of the roving tab stop, skipping inactive metrics. */
|
|
35
|
+
private get rovingIndex();
|
|
36
|
+
private selectMetric;
|
|
37
|
+
/** Move the roving tab stop, wrapping and skipping inactive metrics. */
|
|
38
|
+
private moveFocus;
|
|
39
|
+
private handleMetricKeyDown;
|
|
40
|
+
/**
|
|
41
|
+
* A skeleton bar matching the type metrics of the text it stands in for, so the
|
|
42
|
+
* loading state holds the same shape and rhythm as the resolved content.
|
|
43
|
+
*
|
|
44
|
+
* `background` is always passed: the default base is the theme's quaternary
|
|
45
|
+
* foreground, which is black and therefore invisible on this permanently dark
|
|
46
|
+
* surface.
|
|
47
|
+
*/
|
|
48
|
+
private bar;
|
|
49
|
+
/**
|
|
50
|
+
* Arrow and change in one text node.
|
|
51
|
+
*
|
|
52
|
+
* The arrow is a typeface glyph rather than an icon so it inherits the exact
|
|
53
|
+
* size, weight, and baseline of the value beside it; an icon would need its
|
|
54
|
+
* own size scale and would drift whenever the type scale changed.
|
|
55
|
+
*
|
|
56
|
+
* Tone is supplied by the caller and never inferred here.
|
|
57
|
+
*/
|
|
58
|
+
private renderTrend;
|
|
59
|
+
private renderScore;
|
|
60
|
+
private renderMetric;
|
|
61
|
+
private renderMetrics;
|
|
62
|
+
render(): any;
|
|
63
|
+
}
|
|
64
|
+
//# sourceMappingURL=CardOverview.d.ts.map
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import type { MetricTrend } from '../../utils/metric-change';
|
|
2
|
+
export type CardOverviewVariant = 'default' | 'compact';
|
|
3
|
+
export type CardOverviewLayout = 'auto' | 'stacked';
|
|
4
|
+
/**
|
|
5
|
+
* Leading summary block — the headline figure the rest of the bar contextualises.
|
|
6
|
+
*
|
|
7
|
+
* Derive `trend` with `resolveMetricTrend` so whether a rise reads well stays a
|
|
8
|
+
* product decision rather than a design-system one.
|
|
9
|
+
*/
|
|
10
|
+
export interface OverviewScore {
|
|
11
|
+
/** Category above the figure, for example `Safety score`. */
|
|
12
|
+
label: string;
|
|
13
|
+
/** Display-ready headline figure. */
|
|
14
|
+
value: string | number;
|
|
15
|
+
/** Change against the comparison period. Omit when there is nothing to report. */
|
|
16
|
+
trend?: MetricTrend;
|
|
17
|
+
/** Qualitative band beneath the figure, for example `Good (67–83)`. */
|
|
18
|
+
band?: string;
|
|
19
|
+
}
|
|
20
|
+
/** One measure in the overview grid. */
|
|
21
|
+
export interface OverviewMetric {
|
|
22
|
+
/** Stable identity for keyed rendering and selection events. */
|
|
23
|
+
id: string;
|
|
24
|
+
label: string;
|
|
25
|
+
/** Display-ready value; format compactly before passing it in. */
|
|
26
|
+
value: string | number;
|
|
27
|
+
/** Change against the comparison period. Omit when there is nothing to report. */
|
|
28
|
+
trend?: MetricTrend;
|
|
29
|
+
/** Present the measure without making it selectable. */
|
|
30
|
+
isInactive?: boolean;
|
|
31
|
+
/** Supplementary explanation for the label. */
|
|
32
|
+
labelTooltip?: string;
|
|
33
|
+
}
|
|
34
|
+
//# sourceMappingURL=card-overview-types.d.ts.map
|
|
@@ -2,8 +2,19 @@ import type { ChartDatum } from '../../utils/chart-types';
|
|
|
2
2
|
export declare class ChartBar {
|
|
3
3
|
/** Bars to render. Set as a JS property (not an HTML attribute). */
|
|
4
4
|
data: ChartDatum[];
|
|
5
|
+
/** Standalone intrinsic width. Container constraints reflow the plot at rendered pixel size. */
|
|
5
6
|
width: number;
|
|
7
|
+
/** Standalone intrinsic height. Container constraints reflow the plot at rendered pixel size. */
|
|
6
8
|
height: number;
|
|
9
|
+
private viewport;
|
|
10
|
+
private viewportController;
|
|
11
|
+
private svgEl;
|
|
12
|
+
componentDidLoad(): void;
|
|
13
|
+
connectedCallback(): void;
|
|
14
|
+
componentDidRender(): void;
|
|
15
|
+
private connectViewportController;
|
|
16
|
+
disconnectedCallback(): void;
|
|
17
|
+
handleIntrinsicSizeChange(): void;
|
|
7
18
|
render(): any;
|
|
8
19
|
}
|
|
9
20
|
//# sourceMappingURL=ChartBar.d.ts.map
|
|
@@ -6,8 +6,19 @@ export declare class ChartBarStacked {
|
|
|
6
6
|
/** X-axis labels — must match each series' `data` length. Set as a JS property. */
|
|
7
7
|
categories: string[];
|
|
8
8
|
variant: ChartBarStackedVariant;
|
|
9
|
+
/** Standalone intrinsic width. Container constraints reflow the plot at rendered pixel size. */
|
|
9
10
|
width: number;
|
|
11
|
+
/** Standalone intrinsic height. Container constraints reflow the plot at rendered pixel size. */
|
|
10
12
|
height: number;
|
|
13
|
+
private viewport;
|
|
14
|
+
private viewportController;
|
|
15
|
+
private svgEl;
|
|
16
|
+
componentDidLoad(): void;
|
|
17
|
+
connectedCallback(): void;
|
|
18
|
+
componentDidRender(): void;
|
|
19
|
+
private connectViewportController;
|
|
20
|
+
disconnectedCallback(): void;
|
|
21
|
+
handleIntrinsicSizeChange(): void;
|
|
11
22
|
private valuesForCategory;
|
|
12
23
|
private accessibleLabel;
|
|
13
24
|
render(): any;
|
|
@@ -4,9 +4,20 @@ export declare class ChartLine {
|
|
|
4
4
|
series: ChartSeries[];
|
|
5
5
|
/** X-axis labels — must match each series' `data` length. Set as a JS property. */
|
|
6
6
|
categories: string[];
|
|
7
|
+
/** Standalone intrinsic width. Container constraints reflow the plot at rendered pixel size. */
|
|
7
8
|
width: number;
|
|
9
|
+
/** Standalone intrinsic height. Container constraints reflow the plot at rendered pixel size. */
|
|
8
10
|
height: number;
|
|
9
11
|
showPoints: boolean;
|
|
12
|
+
private viewport;
|
|
13
|
+
private viewportController;
|
|
14
|
+
private svgEl;
|
|
15
|
+
componentDidLoad(): void;
|
|
16
|
+
connectedCallback(): void;
|
|
17
|
+
componentDidRender(): void;
|
|
18
|
+
private connectViewportController;
|
|
19
|
+
disconnectedCallback(): void;
|
|
20
|
+
handleIntrinsicSizeChange(): void;
|
|
10
21
|
render(): any;
|
|
11
22
|
}
|
|
12
23
|
//# sourceMappingURL=ChartLine.d.ts.map
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { EventEmitter } from '../../stencil-public-runtime';
|
|
2
2
|
import { type ChoicePopupAnchorAlignment, type ControlSize } from '../../utils';
|
|
3
|
-
import {
|
|
3
|
+
import type { MenuAlign, MenuSide } from './menu-position';
|
|
4
4
|
import type { MenuItemData, MenuSection } from './menu-types';
|
|
5
5
|
export type MenuSelectionMode = 'none' | 'single';
|
|
6
6
|
export type MenuSize = ControlSize;
|
|
@@ -48,15 +48,17 @@ export declare class Menu {
|
|
|
48
48
|
/** Emitted when a generic `swatch-picker` section option is chosen. */
|
|
49
49
|
dsSwatchSelect: EventEmitter<string>;
|
|
50
50
|
private clickOutsideHandler;
|
|
51
|
-
private scrollResizeHandler;
|
|
52
51
|
private closeTimer;
|
|
53
|
-
|
|
54
|
-
|
|
52
|
+
/**
|
|
53
|
+
* Anchored placement. The choice-cell measurement stays here on purpose: the
|
|
54
|
+
* inner-cell align offset and the `--dimension-menu-width-xs` floor are Menu
|
|
55
|
+
* semantics, not shared geometry.
|
|
56
|
+
*/
|
|
57
|
+
private readonly position;
|
|
55
58
|
/** Last content actually painted while open; retained unchanged through exit motion. */
|
|
56
59
|
private lastRenderedSections;
|
|
57
60
|
private closingSections;
|
|
58
61
|
componentDidLoad(): void;
|
|
59
|
-
componentDidRender(): void;
|
|
60
62
|
disconnectedCallback(): void;
|
|
61
63
|
onOpenChange(isOpen: boolean): void;
|
|
62
64
|
onAnchorChange(): void;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import {
|
|
2
|
-
export type MenuSide =
|
|
3
|
-
export type MenuAlign =
|
|
4
|
-
export type MenuPositionInput =
|
|
5
|
-
/** Backward-compatible menu alias for the shared anchored-
|
|
6
|
-
export declare const computeMenuPosition: typeof
|
|
1
|
+
import { computeAnchoredPosition, type AnchoredAlign, type AnchoredPositionInput, type AnchoredSide } from '../../utils/anchored-position';
|
|
2
|
+
export type MenuSide = AnchoredSide;
|
|
3
|
+
export type MenuAlign = AnchoredAlign;
|
|
4
|
+
export type MenuPositionInput = AnchoredPositionInput;
|
|
5
|
+
/** Backward-compatible menu alias for the shared anchored-position layout math. */
|
|
6
|
+
export declare const computeMenuPosition: typeof computeAnchoredPosition;
|
|
7
7
|
//# sourceMappingURL=menu-position.d.ts.map
|
|
@@ -37,9 +37,8 @@ export declare class Tooltip {
|
|
|
37
37
|
private closeTimer;
|
|
38
38
|
private anchor;
|
|
39
39
|
private popupEl;
|
|
40
|
-
private positionObserver;
|
|
41
40
|
private anchorObserver;
|
|
42
|
-
private
|
|
41
|
+
private readonly position;
|
|
43
42
|
private skipEnterAnimation;
|
|
44
43
|
private isOpen;
|
|
45
44
|
private lastInteractionWasKeyboard;
|
|
@@ -50,7 +49,6 @@ export declare class Tooltip {
|
|
|
50
49
|
/** focusin/focusout so nested focus targets (e.g. ds-button-* inner button) still open the tip. */
|
|
51
50
|
private focusHandler;
|
|
52
51
|
private blurHandler;
|
|
53
|
-
private scrollResizeHandler;
|
|
54
52
|
private escapeHandler;
|
|
55
53
|
componentDidLoad(): void;
|
|
56
54
|
disconnectedCallback(): void;
|
|
@@ -83,7 +81,6 @@ export declare class Tooltip {
|
|
|
83
81
|
private hide;
|
|
84
82
|
private mountPopup;
|
|
85
83
|
private renderPopupContent;
|
|
86
|
-
private calculatePosition;
|
|
87
84
|
render(): any;
|
|
88
85
|
}
|
|
89
86
|
//# sourceMappingURL=Tooltip.d.ts.map
|