@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.
Files changed (262) hide show
  1. package/README.md +13 -13
  2. package/dist/.build-stamp +1 -1
  3. package/dist/components/ds-app-shell.js +1 -1
  4. package/dist/components/ds-banner.js +1 -1
  5. package/dist/components/ds-banner.js.map +1 -1
  6. package/dist/components/ds-bar-nav.js +1 -1
  7. package/dist/components/ds-bar-nav.js.map +1 -1
  8. package/dist/components/ds-button-filled.js +1 -1
  9. package/dist/components/{ds-accordion.d.ts → ds-button-unfilled.d.ts} +4 -4
  10. package/dist/components/ds-button-unfilled.js +2 -0
  11. package/dist/components/{ds-card-data-viz.d.ts → ds-card-data-viz-donut.d.ts} +4 -4
  12. package/dist/components/ds-card-data-viz-donut.js +2 -0
  13. package/dist/components/ds-card-data-viz-donut.js.map +1 -0
  14. package/dist/components/ds-card-setting.js +1 -1
  15. package/dist/components/ds-card-setting.js.map +1 -1
  16. package/dist/components/{ds-fade.d.ts → ds-card.d.ts} +4 -4
  17. package/dist/components/ds-card.js +2 -0
  18. package/dist/components/ds-card.js.map +1 -0
  19. package/dist/components/ds-chart-bar.js +1 -1
  20. package/dist/components/ds-chart-bar.js.map +1 -1
  21. package/dist/components/ds-chart-donut.js +1 -1
  22. package/dist/components/ds-chart-donut.js.map +1 -1
  23. package/dist/components/ds-chart-legend.js +1 -1
  24. package/dist/components/ds-chart-legend.js.map +1 -1
  25. package/dist/components/ds-chart-line.js +1 -1
  26. package/dist/components/ds-checkbox.js +1 -1
  27. package/dist/components/ds-checkbox.js.map +1 -1
  28. package/dist/components/ds-chip.js +1 -1
  29. package/dist/components/ds-chip.js.map +1 -1
  30. package/dist/components/ds-empty-state.js +1 -1
  31. package/dist/components/ds-empty-state.js.map +1 -1
  32. package/dist/components/ds-field.js +1 -1
  33. package/dist/components/ds-icon.js +1 -1
  34. package/dist/components/ds-input.js +1 -1
  35. package/dist/components/ds-input.js.map +1 -1
  36. package/dist/components/ds-loader.js +1 -1
  37. package/dist/components/ds-loader.js.map +1 -1
  38. package/dist/components/ds-menu.js +1 -1
  39. package/dist/components/ds-modal.js +1 -1
  40. package/dist/components/ds-pagination.js +1 -1
  41. package/dist/components/ds-pagination.js.map +1 -1
  42. package/dist/components/ds-panel-nav.js +1 -1
  43. package/dist/components/ds-panel-nav.js.map +1 -1
  44. package/dist/components/ds-panel-tools.js +1 -1
  45. package/dist/components/ds-panel-tools.js.map +1 -1
  46. package/dist/components/ds-radio-group.js +1 -1
  47. package/dist/components/ds-radio-group.js.map +1 -1
  48. package/dist/components/ds-select.js +1 -1
  49. package/dist/components/ds-select.js.map +1 -1
  50. package/dist/components/ds-shell-gradient-picker.js +1 -1
  51. package/dist/components/ds-shell-gradient-swatch.js +1 -1
  52. package/dist/components/ds-skeleton.js +1 -1
  53. package/dist/components/ds-skeleton.js.map +1 -1
  54. package/dist/components/ds-slider.js +1 -1
  55. package/dist/components/ds-slider.js.map +1 -1
  56. package/dist/components/ds-tab-group-nav.js +1 -1
  57. package/dist/components/ds-tab-group.js +1 -1
  58. package/dist/components/ds-tab-group.js.map +1 -1
  59. package/dist/components/ds-table.js +1 -1
  60. package/dist/components/ds-table.js.map +1 -1
  61. package/dist/components/ds-tag.js +1 -1
  62. package/dist/components/ds-tag.js.map +1 -1
  63. package/dist/components/ds-text.js +1 -1
  64. package/dist/components/ds-toggle.js +1 -1
  65. package/dist/components/ds-toggle.js.map +1 -1
  66. package/dist/components/ds-tooltip-data-viz.js +1 -1
  67. package/dist/components/ds-tooltip-data-viz.js.map +1 -1
  68. package/dist/components/ds-tooltip.js +1 -1
  69. package/dist/components/ds-tooltip.js.map +1 -1
  70. package/dist/components/p-1m8r0wFg.js +2 -0
  71. package/dist/components/p-1m8r0wFg.js.map +1 -0
  72. package/dist/components/p-3FLHei19.js +2 -0
  73. package/dist/components/p-3FLHei19.js.map +1 -0
  74. package/dist/components/p-B2_D4IDN.js +2 -0
  75. package/dist/components/p-B2_D4IDN.js.map +1 -0
  76. package/dist/components/p-B5NcWtpp.js +2 -0
  77. package/dist/components/p-B5NcWtpp.js.map +1 -0
  78. package/dist/components/{p-8Nt92j1-.js → p-B82KZX9D.js} +2 -2
  79. package/dist/components/p-BQq26pt9.js.map +1 -1
  80. package/dist/components/p-B_g8X0eh.js +2 -0
  81. package/dist/components/p-B_g8X0eh.js.map +1 -0
  82. package/dist/components/{p-DdFUTZ3y.js → p-Be3PHv34.js} +2 -2
  83. package/dist/components/p-CQmf2CeW.js +2 -0
  84. package/dist/components/p-CQmf2CeW.js.map +1 -0
  85. package/dist/components/{p-DOU4cbpc.js → p-CtwNUCnp.js} +2 -2
  86. package/dist/components/p-CuRjYmBk.js +2 -0
  87. package/dist/components/p-CuRjYmBk.js.map +1 -0
  88. package/dist/components/p-CwARVsZQ.js +2 -0
  89. package/dist/components/p-CwARVsZQ.js.map +1 -0
  90. package/dist/components/p-DohXlNCJ.js +2 -0
  91. package/dist/components/p-DohXlNCJ.js.map +1 -0
  92. package/dist/components/p-DuFm-ieS.js +2 -0
  93. package/dist/components/p-DuFm-ieS.js.map +1 -0
  94. package/dist/components/p-EK1hOP19.js.map +1 -1
  95. package/dist/lib/nav/index.js +3 -0
  96. package/dist/lib/nav/index.js.map +2 -2
  97. package/dist/lib/utils/control-width.d.ts +4 -0
  98. package/dist/lib/utils/index.d.ts +2 -0
  99. package/dist/lib/utils/index.js +20 -1
  100. package/dist/lib/utils/index.js.map +3 -3
  101. package/dist/lib/utils/token-defaults.d.ts +7 -0
  102. package/dist/types/components/ButtonFilled/ButtonFilled.d.ts +21 -3
  103. package/dist/types/components/ButtonFilled/index.d.ts +1 -1
  104. package/dist/types/components/ButtonUnfilled/ButtonUnfilled.d.ts +70 -0
  105. package/dist/types/components/ButtonUnfilled/index.d.ts +2 -0
  106. package/dist/types/components/Card/Card.d.ts +20 -0
  107. package/dist/types/components/Card/index.d.ts +2 -0
  108. package/dist/types/components/CardDataVizDonut/CardDataVizDonut.d.ts +26 -0
  109. package/dist/types/components/CardDataVizDonut/index.d.ts +2 -0
  110. package/dist/types/components/CardSetting/CardSetting.d.ts +7 -2
  111. package/dist/types/components/ChartDonut/ChartDonut.d.ts +31 -1
  112. package/dist/types/components/Checkbox/Checkbox.d.ts +1 -1
  113. package/dist/types/components/Chip/Chip.d.ts +18 -14
  114. package/dist/types/components/Chip/index.d.ts +1 -1
  115. package/dist/types/components/Input/Input.d.ts +1 -1
  116. package/dist/types/components/Pagination/Pagination.d.ts +1 -1
  117. package/dist/types/components/PanelNav/PanelNav.d.ts +2 -0
  118. package/dist/types/components/PanelTools/panel-tools-types.d.ts +1 -1
  119. package/dist/types/components/RadioGroup/RadioGroup.d.ts +2 -2
  120. package/dist/types/components/Select/Select.d.ts +34 -2
  121. package/dist/types/components/Select/index.d.ts +2 -0
  122. package/dist/types/components/ShellGradientSwatch/ShellGradientSwatch.d.ts +1 -1
  123. package/dist/types/components/Slider/Slider.d.ts +1 -1
  124. package/dist/types/components/TabGroup/TabGroup.d.ts +1 -1
  125. package/dist/types/components/TabGroup/tab-item-utils.d.ts +2 -1
  126. package/dist/types/components/TabGroupNav/TabGroupNav.d.ts +1 -1
  127. package/dist/types/components/Tag/Tag.d.ts +0 -2
  128. package/dist/types/components/Tag/index.d.ts +1 -1
  129. package/dist/types/components/Toggle/Toggle.d.ts +1 -1
  130. package/dist/types/components/Tooltip/Tooltip.d.ts +25 -6
  131. package/dist/types/components/TooltipDataViz/TooltipDataViz.d.ts +18 -0
  132. package/dist/types/components.d.ts +474 -576
  133. package/dist/types/utils/control-width.d.ts +5 -0
  134. package/dist/types/utils/index.d.ts +2 -0
  135. package/dist/types/utils/token-defaults.d.ts +7 -0
  136. package/package.json +7 -4
  137. package/src/angular/index.ts +3 -9
  138. package/src/angular/proxies.ts +62 -215
  139. package/src/react/components.ts +3 -9
  140. package/src/react/ds-button-unfilled.ts +33 -0
  141. package/src/react/ds-card-data-viz-donut.ts +27 -0
  142. package/src/react/{ds-fade.ts → ds-card.ts} +7 -7
  143. package/src/react/ds-chip.ts +2 -10
  144. package/src/wc/components/BarNav/BarNav.tsx +3 -2
  145. package/src/wc/components/BarNav/bar-nav-tabs-menu-utils.ts +1 -1
  146. package/src/wc/components/ButtonFilled/ButtonFilled.tsx +97 -11
  147. package/src/wc/components/ButtonFilled/index.ts +7 -1
  148. package/src/wc/components/ButtonUnfilled/ButtonUnfilled.tsx +230 -0
  149. package/src/wc/components/ButtonUnfilled/index.ts +7 -0
  150. package/src/wc/components/Card/Card.tsx +79 -0
  151. package/src/wc/components/Card/index.ts +1 -0
  152. package/src/wc/components/{CardDataViz/CardDataViz.tsx → CardDataVizDonut/CardDataVizDonut.tsx} +47 -41
  153. package/src/wc/components/CardDataVizDonut/index.ts +1 -0
  154. package/src/wc/components/CardSetting/CardSetting.tsx +21 -36
  155. package/src/wc/components/ChartDonut/ChartDonut.tsx +159 -11
  156. package/src/wc/components/ChartLegend/ChartLegend.tsx +3 -0
  157. package/src/wc/components/Checkbox/Checkbox.tsx +5 -5
  158. package/src/wc/components/Chip/Chip.tsx +50 -67
  159. package/src/wc/components/Chip/index.ts +1 -1
  160. package/src/wc/components/Input/Input.tsx +4 -4
  161. package/src/wc/components/Menu/Menu.tsx +7 -4
  162. package/src/wc/components/Pagination/Pagination.tsx +6 -6
  163. package/src/wc/components/PanelNav/PanelNav.tsx +77 -48
  164. package/src/wc/components/PanelTools/PanelTools.tsx +19 -16
  165. package/src/wc/components/PanelTools/panel-tools-types.ts +1 -1
  166. package/src/wc/components/PanelTools/panel-tools-utils.ts +1 -1
  167. package/src/wc/components/RadioGroup/RadioGroup.tsx +7 -7
  168. package/src/wc/components/Select/Select.tsx +145 -64
  169. package/src/wc/components/Select/index.ts +1 -0
  170. package/src/wc/components/ShellGradientSwatch/ShellGradientSwatch.tsx +4 -3
  171. package/src/wc/components/Slider/Slider.tsx +7 -3
  172. package/src/wc/components/TabGroup/TabGroup.tsx +13 -7
  173. package/src/wc/components/TabGroup/tab-item-utils.ts +2 -1
  174. package/src/wc/components/TabGroupNav/TabGroupNav.tsx +14 -13
  175. package/src/wc/components/Tag/Tag.tsx +25 -24
  176. package/src/wc/components/Tag/index.ts +1 -1
  177. package/src/wc/components/Toggle/Toggle.tsx +6 -6
  178. package/src/wc/components/Tooltip/Tooltip.tsx +166 -64
  179. package/src/wc/components/TooltipDataViz/TooltipDataViz.tsx +57 -2
  180. package/src/wc/components.d.ts +474 -579
  181. package/src/wc/utils/control-density.css +42 -0
  182. package/src/wc/utils/control-inactive.css +20 -0
  183. package/src/wc/utils/control-width.css +28 -0
  184. package/src/wc/utils/control-width.ts +10 -0
  185. package/src/wc/utils/index.ts +2 -0
  186. package/src/wc/utils/interaction-fill.css +162 -0
  187. package/src/wc/utils/scroll-edge-fade.css +5 -0
  188. package/src/wc/utils/token-defaults.ts +10 -0
  189. package/dist/components/ds-accordion.js +0 -2
  190. package/dist/components/ds-accordion.js.map +0 -1
  191. package/dist/components/ds-breadcrumb.d.ts +0 -11
  192. package/dist/components/ds-breadcrumb.js +0 -2
  193. package/dist/components/ds-breadcrumb.js.map +0 -1
  194. package/dist/components/ds-button-unfilled-icon.d.ts +0 -11
  195. package/dist/components/ds-button-unfilled-icon.js +0 -2
  196. package/dist/components/ds-card-data-viz.js +0 -2
  197. package/dist/components/ds-card-data-viz.js.map +0 -1
  198. package/dist/components/ds-fade.js +0 -2
  199. package/dist/components/ds-fade.js.map +0 -1
  200. package/dist/components/ds-header.d.ts +0 -11
  201. package/dist/components/ds-header.js +0 -2
  202. package/dist/components/ds-header.js.map +0 -1
  203. package/dist/components/ds-scrollbar.d.ts +0 -11
  204. package/dist/components/ds-scrollbar.js +0 -2
  205. package/dist/components/ds-scrollbar.js.map +0 -1
  206. package/dist/components/ds-surface.d.ts +0 -11
  207. package/dist/components/ds-surface.js +0 -2
  208. package/dist/components/ds-surface.js.map +0 -1
  209. package/dist/components/ds-toast-provider.d.ts +0 -11
  210. package/dist/components/ds-toast-provider.js +0 -2
  211. package/dist/components/ds-toast-provider.js.map +0 -1
  212. package/dist/components/p-BMQbEIrQ.js +0 -2
  213. package/dist/components/p-BMQbEIrQ.js.map +0 -1
  214. package/dist/components/p-BSFgy5r6.js +0 -2
  215. package/dist/components/p-BSFgy5r6.js.map +0 -1
  216. package/dist/components/p-CBUIn8dk.js +0 -2
  217. package/dist/components/p-CBUIn8dk.js.map +0 -1
  218. package/dist/components/p-ChVwCVHy.js +0 -2
  219. package/dist/components/p-ChVwCVHy.js.map +0 -1
  220. package/dist/components/p-CpNIQXz6.js +0 -2
  221. package/dist/components/p-CpNIQXz6.js.map +0 -1
  222. package/dist/components/p-CvgHQfvT.js +0 -2
  223. package/dist/components/p-CvgHQfvT.js.map +0 -1
  224. package/dist/components/p-bDf6yBQd.js +0 -2
  225. package/dist/components/p-bDf6yBQd.js.map +0 -1
  226. package/dist/types/components/Accordion/Accordion.d.ts +0 -28
  227. package/dist/types/components/Breadcrumb/Breadcrumb.d.ts +0 -16
  228. package/dist/types/components/ButtonUnfilledIcon/ButtonUnfilledIcon.d.ts +0 -45
  229. package/dist/types/components/ButtonUnfilledIcon/index.d.ts +0 -2
  230. package/dist/types/components/CardDataViz/CardDataViz.d.ts +0 -22
  231. package/dist/types/components/CardDataViz/index.d.ts +0 -2
  232. package/dist/types/components/Fade/Fade.d.ts +0 -22
  233. package/dist/types/components/Fade/index.d.ts +0 -2
  234. package/dist/types/components/Header/Header.d.ts +0 -7
  235. package/dist/types/components/Scrollbar/Scrollbar.d.ts +0 -27
  236. package/dist/types/components/Surface/Surface.d.ts +0 -22
  237. package/dist/types/components/Toast/Toast.d.ts +0 -16
  238. package/dist/types/components/Toast/toast-service.d.ts +0 -24
  239. package/src/react/ds-accordion.ts +0 -27
  240. package/src/react/ds-breadcrumb.ts +0 -27
  241. package/src/react/ds-button-unfilled-icon.ts +0 -33
  242. package/src/react/ds-card-data-viz.ts +0 -26
  243. package/src/react/ds-header.ts +0 -26
  244. package/src/react/ds-scrollbar.ts +0 -26
  245. package/src/react/ds-surface.ts +0 -26
  246. package/src/react/ds-toast-provider.ts +0 -26
  247. package/src/wc/components/Accordion/Accordion.tsx +0 -144
  248. package/src/wc/components/Breadcrumb/Breadcrumb.tsx +0 -61
  249. package/src/wc/components/ButtonUnfilledIcon/ButtonUnfilledIcon.tsx +0 -129
  250. package/src/wc/components/ButtonUnfilledIcon/index.ts +0 -1
  251. package/src/wc/components/CardDataViz/index.ts +0 -1
  252. package/src/wc/components/Fade/Fade.tsx +0 -58
  253. package/src/wc/components/Fade/index.ts +0 -1
  254. package/src/wc/components/Header/Header.tsx +0 -33
  255. package/src/wc/components/Scrollbar/Scrollbar.tsx +0 -189
  256. package/src/wc/components/Surface/Surface.tsx +0 -81
  257. package/src/wc/components/Toast/Toast.tsx +0 -179
  258. package/src/wc/components/Toast/toast-service.ts +0 -67
  259. /package/dist/components/{ds-button-unfilled-icon.js.map → ds-button-unfilled.js.map} +0 -0
  260. /package/dist/components/{p-8Nt92j1-.js.map → p-B82KZX9D.js.map} +0 -0
  261. /package/dist/components/{p-DdFUTZ3y.js.map → p-Be3PHv34.js.map} +0 -0
  262. /package/dist/components/{p-DOU4cbpc.js.map → p-CtwNUCnp.js.map} +0 -0
@@ -1,6 +1,7 @@
1
- import { Component, Element, State, h, Host, Prop } from '@stencil/core';
1
+ import { Component, Element, Event, EventEmitter, State, h, Host, Prop } from '@stencil/core';
2
+ import type { CardWidth } from '../Card/Card';
2
3
 
3
- export type CardDataVizWidth = 'xs' | 'sm' | 'md' | 'lg';
4
+ export type CardDataVizDonutWidth = CardWidth;
4
5
 
5
6
  /** Matches the `dsSliceHover`/`dsItemHover` detail shape emitted by `ds-chart-*` and `ds-chart-legend`. */
6
7
  type HoveredDatum = { label: string } | null;
@@ -8,31 +9,26 @@ type HoveredDatum = { label: string } | null;
8
9
  /** A slotted chart/legend that opts into hover sync by exposing an `activeLabel` prop. */
9
10
  type SyncableSlot = HTMLElement & { activeLabel?: string | null };
10
11
 
11
- const CARD_WIDTH_VARS: Record<CardDataVizWidth, string> = {
12
- xs: 'var(--dimension-card-width-xs)',
13
- sm: 'var(--dimension-card-width-sm)',
14
- md: 'var(--dimension-card-width-md)',
15
- lg: 'var(--dimension-card-width-lg)',
16
- };
17
-
18
12
  /**
19
- * Copied from CardSetting as a starting scaffold header edit affordance removed for now
20
- * (data-viz widgets don't have an obvious "edit" action the way settings fields do); revisit
21
- * once the data-viz card's real header actions (menu, caption, footer stat) are designed.
13
+ * Donut data-viz card shared `ds-card` chrome with a fill chart region and
14
+ * content-sized legend. Hover sync between chart and legend stays here.
22
15
  */
23
16
  @Component({
24
- tag: 'ds-card-data-viz',
25
- styleUrl: 'CardDataViz.css',
17
+ tag: 'ds-card-data-viz-donut',
18
+ styleUrl: 'CardDataVizDonut.css',
26
19
  scoped: true,
27
20
  })
28
- export class CardDataViz {
21
+ export class CardDataVizDonut {
29
22
  @Element() el!: HTMLElement;
30
23
 
31
24
  /** Widget heading shown in the card header. */
32
25
  @Prop() heading!: string;
33
26
 
34
- /** Card width token. */
35
- @Prop() cardWidth: CardDataVizWidth = 'md';
27
+ /** Card width token — also sets matching min-height. */
28
+ @Prop() cardWidth: CardDataVizDonutWidth = 'md';
29
+
30
+ /** Emits when the header filter control is activated. */
31
+ @Event() dsFilterClick!: EventEmitter<void>;
36
32
 
37
33
  // Scoped (light-DOM) components have no native slot projection to detect emptiness from CSS —
38
34
  // check the actual children so an unused region (e.g. no legend) doesn't render a dangling
@@ -41,13 +37,16 @@ export class CardDataViz {
41
37
  @State() private hasLegendSlot = false;
42
38
 
43
39
  componentWillRender() {
44
- this.hasChartSlot = !!this.el.querySelector(':scope > [slot="chart"]');
45
- this.hasLegendSlot = !!this.el.querySelector(':scope > [slot="legend"]');
40
+ // Not `:scope >` — Stencil's scoped slot polyfill relocates slotted children into
41
+ // region wrappers (and here into nested `ds-card`), so they may not stay direct
42
+ // host children across renders.
43
+ this.hasChartSlot = !!this.el.querySelector('[slot="chart"]');
44
+ this.hasLegendSlot = !!this.el.querySelector('[slot="legend"]');
46
45
  }
47
46
 
48
47
  componentDidLoad() {
49
48
  // Sync the chart's and legend's `activeLabel` from each other's hover events, so any
50
- // ds-chart-* + ds-chart-legend pair cross-highlights without every consumer having to
49
+ // ds-chart-donut + ds-chart-legend pair cross-highlights without every consumer having to
51
50
  // wire it up by hand. Delegated on the host (rather than bound to the specific slotted
52
51
  // elements) since Stencil events bubble and the slot content can be swapped at runtime.
53
52
  this.el.addEventListener('dsSliceHover', this.handleChartHover);
@@ -61,8 +60,8 @@ export class CardDataViz {
61
60
 
62
61
  private handleChartHover = (e: Event) => {
63
62
  // Not `:scope >` — Stencil's slot polyfill for scoped (non-shadow) components relocates
64
- // slotted children into an inner `.chart-region`/`.legend-region` wrapper on render, so
65
- // they're no longer direct children of the host by the time this fires.
63
+ // slotted children into an inner region wrapper on render, so they're no longer direct
64
+ // children of the host by the time this fires.
66
65
  const legend = this.el.querySelector('[slot="legend"]') as SyncableSlot | null;
67
66
  if (!legend || !('activeLabel' in legend)) return;
68
67
  legend.activeLabel = (e as CustomEvent<HoveredDatum>).detail?.label ?? null;
@@ -74,28 +73,35 @@ export class CardDataViz {
74
73
  chart.activeLabel = (e as CustomEvent<HoveredDatum>).detail?.label ?? null;
75
74
  };
76
75
 
76
+ private handleFilterClick = () => {
77
+ this.dsFilterClick.emit();
78
+ };
79
+
77
80
  render() {
78
81
  return (
79
- <Host class="card-data-viz" style={{ '--_card-data-viz-width': CARD_WIDTH_VARS[this.cardWidth] }}>
80
- <header class="card-data-viz__header">
81
- <div class="card-data-viz__title-wrap">
82
- <ds-text class="card-data-viz__title" variant="text-title-small" color="primary" as="h2">
83
- {this.heading}
84
- </ds-text>
82
+ <Host class="card-data-viz-donut">
83
+ <ds-card heading={this.heading} cardWidth={this.cardWidth}>
84
+ <ds-button-unfilled
85
+ slot="actions"
86
+ variant="icon"
87
+ type="button"
88
+ icon="Filters"
89
+ aria-label="Filter"
90
+ onDsClick={this.handleFilterClick}
91
+ />
92
+ <div class="card-data-viz-donut__layout">
93
+ {this.hasChartSlot && (
94
+ <div class="card-data-viz-donut__chart-region">
95
+ <slot name="chart" />
96
+ </div>
97
+ )}
98
+ {this.hasLegendSlot && (
99
+ <div class="card-data-viz-donut__legend-region">
100
+ <slot name="legend" />
101
+ </div>
102
+ )}
85
103
  </div>
86
- </header>
87
- <div class="card-data-viz__body">
88
- {this.hasChartSlot && (
89
- <div class="card-data-viz__chart-region">
90
- <slot name="chart" />
91
- </div>
92
- )}
93
- {this.hasLegendSlot && (
94
- <div class="card-data-viz__legend-region">
95
- <slot name="legend" />
96
- </div>
97
- )}
98
- </div>
104
+ </ds-card>
99
105
  </Host>
100
106
  );
101
107
  }
@@ -0,0 +1 @@
1
+ export type { CardDataVizDonutWidth } from './CardDataVizDonut';
@@ -1,15 +1,7 @@
1
1
  import { Component, Event, EventEmitter, h, Host, Prop } from '@stencil/core';
2
+ import type { CardWidth } from '../Card/Card';
2
3
 
3
- export type CardSettingWidth = 'xs' | 'sm' | 'md' | 'lg';
4
-
5
- const CARD_WIDTH_VARS: Record<CardSettingWidth, string> = {
6
- xs: 'var(--dimension-card-width-xs)',
7
- sm: 'var(--dimension-card-width-sm)',
8
- md: 'var(--dimension-card-width-md)',
9
- lg: 'var(--dimension-card-width-lg)',
10
- };
11
-
12
- const FAINT_BRAND_TITLE_COLOR = 'var(--color-foreground-faint-brand)';
4
+ export type CardSettingWidth = CardWidth;
13
5
 
14
6
  @Component({
15
7
  tag: 'ds-card-setting',
@@ -20,7 +12,11 @@ export class CardSetting {
20
12
  /** Section heading shown in the card header. */
21
13
  @Prop() heading!: string;
22
14
 
23
- /** Card width token. */
15
+ /**
16
+ * Card width token (`sm` / `md` / `lg`). Also sets host `min-height` to the
17
+ * matching `--dimension-card-height-*` so the body fills available space
18
+ * even when the slot is empty.
19
+ */
24
20
  @Prop() cardWidth: CardSettingWidth = 'md';
25
21
 
26
22
  /** Controlled edit state — parent owns single-edit orchestration. */
@@ -41,42 +37,33 @@ export class CardSetting {
41
37
  const editing = this.editing;
42
38
 
43
39
  return (
44
- <Host
45
- class={{
46
- 'card-setting': true,
47
- 'card-setting--editing': editing,
48
- }}
49
- style={{ '--_card-setting-width': CARD_WIDTH_VARS[this.cardWidth] }}
50
- >
51
- <header class="card-setting__header">
52
- <div class="card-setting__title-wrap">
53
- <ds-text
54
- class="card-setting__title"
55
- variant="text-title-small"
56
- color={editing ? FAINT_BRAND_TITLE_COLOR : 'primary'}
57
- as="h2"
58
- >
59
- {this.heading}
60
- </ds-text>
61
- </div>
40
+ <Host class="card-setting">
41
+ <ds-card
42
+ heading={this.heading}
43
+ cardWidth={this.cardWidth}
44
+ appearance={editing ? 'editing' : 'default'}
45
+ >
62
46
  {!editing ? (
63
- <ds-button-unfilled-icon
47
+ <ds-button-unfilled
48
+ slot="actions"
49
+ variant="icon"
64
50
  type="button"
65
51
  icon="Pencil"
66
52
  aria-label="Edit"
67
53
  onDsClick={this.enterEdit}
68
54
  />
69
55
  ) : (
70
- <div class="card-setting__actions">
71
- <ds-button-unfilled-icon
56
+ <div slot="actions" class="card-setting__actions">
57
+ <ds-button-unfilled
58
+ variant="icon"
72
59
  type="button"
73
60
  icon="Cross"
74
61
  backgroundContrast="bold"
75
- has-border
76
62
  aria-label="Cancel"
77
63
  onDsClick={this.exitEdit}
78
64
  />
79
65
  <ds-button-filled
66
+ variant="icon"
80
67
  type="button"
81
68
  icon="Check"
82
69
  intent="brand"
@@ -86,10 +73,8 @@ export class CardSetting {
86
73
  />
87
74
  </div>
88
75
  )}
89
- </header>
90
- <div class="card-setting__body">
91
76
  <slot />
92
- </div>
77
+ </ds-card>
93
78
  </Host>
94
79
  );
95
80
  }
@@ -1,4 +1,4 @@
1
- import { Component, Prop, State, Event, EventEmitter, h, Host } from '@stencil/core';
1
+ import { Component, Prop, State, Event, EventEmitter, Element, Watch, h, Host } from '@stencil/core';
2
2
  import { arc, pie } from 'd3-shape';
3
3
  import { categoryColor } from '../../utils/chart-colors';
4
4
  import { formatCompactNumber, resolveCssLengthPx, truncateSvgTextToWidth, TOKEN_DEFAULTS } from '../../utils';
@@ -9,6 +9,14 @@ const DIMMED_OPACITY = 0.25;
9
9
  const CENTER_GAP_PX = 0;
10
10
  /** Fraction of the inner-circle diameter center text is allowed to use before truncating. */
11
11
  const CENTER_TEXT_SAFE_WIDTH_RATIO = 0.82;
12
+ /** Fallback when filling before the first ResizeObserver measurement (matches fill min). */
13
+ const FILL_FALLBACK_PX = 128;
14
+
15
+ type DonutTooltipState = {
16
+ datum: ChartDatum;
17
+ x: number;
18
+ y: number;
19
+ };
12
20
 
13
21
  @Component({
14
22
  tag: 'ds-chart-donut',
@@ -16,9 +24,17 @@ const CENTER_TEXT_SAFE_WIDTH_RATIO = 0.82;
16
24
  scoped: true,
17
25
  })
18
26
  export class ChartDonut {
27
+ @Element() el!: HTMLElement;
28
+
19
29
  /** Slices to render. Set as a JS property (not an HTML attribute). */
20
30
  @Prop() data: ChartDatum[] = [];
21
- @Prop() size: number = 175;
31
+ /**
32
+ * Explicit diameter in px. When unset, the donut sizes to its container
33
+ * (ResizeObserver) clamped between `--dimension-size-base * 16` (128px) and
34
+ * `* 24` (192px), and stays centered in the leftover space. Prefer unset
35
+ * inside card layouts.
36
+ */
37
+ @Prop() size: number | undefined;
22
38
  /** Ring thickness — number (px) or TokoMo length. Defaults to `--dimension-size-200` (16px). */
23
39
  @Prop() thickness: number | string = TOKEN_DEFAULTS.size200;
24
40
  /** Corner radius on each slice — number (px) or TokoMo length. Defaults to `--dimension-radius-025` (2px). */
@@ -33,27 +49,133 @@ export class ChartDonut {
33
49
  * Externally controlled highlight, matched by `label` — e.g. drive this from a sibling
34
50
  * `ds-chart-legend`'s `dsItemHover` event to keep chart and legend hover in sync.
35
51
  * Falls back to this component's own pointer/focus hover when unset.
52
+ * External sync dims slices only — the data-viz tooltip is reserved for this
53
+ * component's own pointer/keyboard interaction.
36
54
  */
37
55
  @Prop() activeLabel: string | null = null;
38
56
 
39
57
  @State() private hoveredLabel: string | null = null;
58
+ /** Own-hover tooltip (cursor-following). Cleared on leave; not driven by `activeLabel`. */
59
+ @State() private tooltip: DonutTooltipState | null = null;
40
60
  /** Center text, truncated with an ellipsis if it doesn't fit inside the inner circle. */
41
61
  @State() private displayedValueText: string = '';
42
62
  @State() private displayedCaptionText: string = '';
63
+ /** Measured fill diameter when `size` is unset. */
64
+ @State() private measuredSize: number = FILL_FALLBACK_PX;
43
65
 
44
66
  /** Fires with the hovered/focused slice's datum, or `null` on leave/blur. */
45
67
  @Event() dsSliceHover!: EventEmitter<ChartDatum | null>;
46
68
 
47
69
  private valueTextEl?: SVGTextElement;
48
70
  private captionTextEl?: SVGTextElement;
71
+ private wrapperEl?: HTMLDivElement;
72
+ private resizeObserver: ResizeObserver | null = null;
73
+
74
+ private get isFillMode(): boolean {
75
+ return this.size == null || !Number.isFinite(this.size) || this.size <= 0;
76
+ }
77
+
78
+ private get fillMinPx(): number {
79
+ return resolveCssLengthPx(TOKEN_DEFAULTS.donutFillMin, FILL_FALLBACK_PX);
80
+ }
81
+
82
+ private get fillMaxPx(): number {
83
+ return resolveCssLengthPx(TOKEN_DEFAULTS.donutFillMax, 192);
84
+ }
49
85
 
50
- private handleHover(datum: ChartDatum | null) {
86
+ /** Clamp available square side into the fill min/max band. */
87
+ private clampFillSize(availablePx: number): number {
88
+ const minPx = this.fillMinPx;
89
+ const maxPx = this.fillMaxPx;
90
+ if (!(availablePx > 0)) return minPx;
91
+ return Math.round(Math.min(maxPx, Math.max(minPx, availablePx)));
92
+ }
93
+
94
+ private get resolvedSize(): number {
95
+ if (!this.isFillMode) return this.size as number;
96
+ return this.clampFillSize(this.measuredSize);
97
+ }
98
+
99
+ componentDidLoad() {
100
+ this.syncResizeObserver();
101
+ }
102
+
103
+ @Watch('size')
104
+ onSizePropChange() {
105
+ this.syncResizeObserver();
106
+ }
107
+
108
+ disconnectedCallback() {
109
+ this.teardownResizeObserver();
110
+ }
111
+
112
+ private syncResizeObserver() {
113
+ this.teardownResizeObserver();
114
+ if (!this.isFillMode) return;
115
+ // Observe the host (which fills its parent), not the wrapper — the wrapper
116
+ // only wraps the SVG and would collapse to the clamped size, locking fill at min.
117
+ const target = this.el;
118
+ if (!target || typeof ResizeObserver === 'undefined') return;
119
+
120
+ this.resizeObserver = new ResizeObserver(entries => {
121
+ const entry = entries[0];
122
+ if (!entry) return;
123
+ this.applyAvailableSize(entry.contentRect.width, entry.contentRect.height);
124
+ });
125
+ this.resizeObserver.observe(target);
126
+ this.measureHost();
127
+ }
128
+
129
+ private measureHost() {
130
+ const rect = this.el.getBoundingClientRect();
131
+ this.applyAvailableSize(rect.width, rect.height);
132
+ }
133
+
134
+ private applyAvailableSize(width: number, height: number) {
135
+ const available = Math.floor(Math.min(width, height));
136
+ if (available > 0 && available !== this.measuredSize) {
137
+ this.measuredSize = available;
138
+ }
139
+ }
140
+
141
+ private teardownResizeObserver() {
142
+ this.resizeObserver?.disconnect();
143
+ this.resizeObserver = null;
144
+ }
145
+
146
+ private handleHover(datum: ChartDatum | null, event?: MouseEvent) {
51
147
  this.hoveredLabel = datum?.label ?? null;
52
148
  this.dsSliceHover.emit(datum);
149
+ if (!datum) {
150
+ this.tooltip = null;
151
+ return;
152
+ }
153
+ this.updateTooltip(datum, event);
154
+ }
155
+
156
+ private handleSliceMove(datum: ChartDatum, event: MouseEvent) {
157
+ if (this.hoveredLabel !== datum.label) return;
158
+ this.updateTooltip(datum, event);
159
+ }
160
+
161
+ private updateTooltip(datum: ChartDatum, event?: MouseEvent) {
162
+ const wrapper = this.wrapperEl;
163
+ if (!wrapper) return;
164
+ const size = this.resolvedSize;
165
+ const rect = wrapper.getBoundingClientRect();
166
+ const x = event ? event.clientX - rect.left : size / 2;
167
+ const y = event ? event.clientY - rect.top : size / 2;
168
+ this.tooltip = { datum, x, y };
53
169
  }
54
170
 
55
171
  componentDidRender() {
56
- const radius = this.size / 2;
172
+ // Re-bind after HMR when DidLoad's observer was dropped.
173
+ if (this.isFillMode && !this.resizeObserver) {
174
+ this.syncResizeObserver();
175
+ }
176
+
177
+ const size = this.resolvedSize;
178
+ const radius = size / 2;
57
179
  const thicknessPx = resolveCssLengthPx(this.thickness, TOKEN_DEFAULTS.size200);
58
180
  const innerRadius = radius - thicknessPx;
59
181
  const maxWidth = innerRadius * 2 * CENTER_TEXT_SAFE_WIDTH_RATIO;
@@ -75,7 +197,8 @@ export class ChartDonut {
75
197
  }
76
198
 
77
199
  render() {
78
- const radius = this.size / 2;
200
+ const size = this.resolvedSize;
201
+ const radius = size / 2;
79
202
  const thicknessPx = resolveCssLengthPx(this.thickness, TOKEN_DEFAULTS.size200);
80
203
  const cornerRadiusPx = resolveCssLengthPx(this.cornerRadius, TOKEN_DEFAULTS.radius025);
81
204
  const innerRadius = radius - thicknessPx;
@@ -115,15 +238,30 @@ export class ChartDonut {
115
238
  // Show the full text before the post-render truncation pass has measured it (avoids a blank first paint).
116
239
  const fullValueText = this.centerValue ?? formatCompactNumber(total);
117
240
  const fullCaptionText = this.centerCaption ?? '';
241
+ const tip = this.tooltip;
242
+ const fill = this.isFillMode;
118
243
 
119
244
  return (
120
- <Host class="chart-donut">
121
- <div class="chart-donut__wrapper" style={{ width: `${this.size}px`, height: `${this.size}px` }}>
245
+ <Host
246
+ class={{
247
+ 'chart-donut': true,
248
+ 'chart-donut--fill': fill,
249
+ 'chart-donut--fixed': !fill,
250
+ }}
251
+ style={!fill ? { width: `${size}px`, height: `${size}px` } : undefined}
252
+ >
253
+ <div
254
+ class="chart-donut__wrapper"
255
+ style={!fill ? { width: `${size}px`, height: `${size}px` } : undefined}
256
+ ref={el => {
257
+ this.wrapperEl = (el as HTMLDivElement) ?? undefined;
258
+ }}
259
+ >
122
260
  <svg
123
261
  class="chart-donut__svg"
124
- viewBox={`0 0 ${this.size} ${this.size}`}
125
- width={this.size}
126
- height={this.size}
262
+ viewBox={`0 0 ${size} ${size}`}
263
+ width={size}
264
+ height={size}
127
265
  >
128
266
  <g transform={`translate(${radius}, ${radius})`}>
129
267
  {isEmpty ? (
@@ -141,7 +279,8 @@ export class ChartDonut {
141
279
  tabindex={0}
142
280
  role="img"
143
281
  aria-label={`${datum.label}: ${datum.value}`}
144
- onMouseEnter={() => this.handleHover(datum)}
282
+ onMouseEnter={(e: MouseEvent) => this.handleHover(datum, e)}
283
+ onMouseMove={(e: MouseEvent) => this.handleSliceMove(datum, e)}
145
284
  onMouseLeave={() => this.handleHover(null)}
146
285
  onFocus={() => this.handleHover(datum)}
147
286
  onBlur={() => this.handleHover(null)}
@@ -177,6 +316,15 @@ export class ChartDonut {
177
316
  </text>
178
317
  )}
179
318
  </svg>
319
+ {tip && (
320
+ <ds-tooltip-data-viz
321
+ key={tip.datum.label}
322
+ label={tip.datum.label}
323
+ value={formatCompactNumber(tip.datum.value)}
324
+ x={tip.x}
325
+ y={tip.y}
326
+ />
327
+ )}
180
328
  </div>
181
329
  </Host>
182
330
  );
@@ -73,6 +73,9 @@ export class ChartLegend {
73
73
  class={{
74
74
  'chart-legend__item': true,
75
75
  'chart-legend__item--interactive': !!item.href,
76
+ 'ds-control--md': true,
77
+ 'ds-interaction-fill': true,
78
+ 'ds-focus-ring-inset': !!item.href,
76
79
  }}
77
80
  style={{ opacity: isDimmed ? String(DIMMED_OPACITY) : '1' }}
78
81
  href={item.href}
@@ -13,12 +13,12 @@ export class Checkbox {
13
13
  @Prop() label!: string;
14
14
  @Prop({ mutable: true }) checked: boolean = false;
15
15
  @Prop() indeterminate: boolean = false;
16
- @Prop() inactive: boolean = false;
16
+ @Prop() isInactive: boolean = false;
17
17
 
18
18
  @Event() dsChange!: EventEmitter<boolean>;
19
19
 
20
20
  private handleActivate = () => {
21
- if (this.inactive) return;
21
+ if (this.isInactive) return;
22
22
  this.checked = !this.checked;
23
23
  this.dsChange.emit(this.checked);
24
24
  };
@@ -37,10 +37,10 @@ export class Checkbox {
37
37
  <Host
38
38
  role="checkbox"
39
39
  aria-checked={this.indeterminate ? 'mixed' : String(this.checked)}
40
- aria-disabled={this.inactive || undefined}
40
+ aria-disabled={this.isInactive || undefined}
41
41
  aria-labelledby={this.labelId}
42
- tabIndex={this.inactive ? -1 : 0}
43
- class={{ checkbox: true, inactive: this.inactive }}
42
+ tabIndex={this.isInactive ? -1 : 0}
43
+ class={{ checkbox: true, 'ds-control-inactive': this.isInactive }}
44
44
  onClick={this.handleActivate}
45
45
  onKeyDown={this.handleKeyDown}
46
46
  >