@ds-mo/ui 2.14.0 → 3.0.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 (175) hide show
  1. package/dist/.build-stamp +1 -1
  2. package/dist/components/ds-app-shell.js +1 -1
  3. package/dist/components/ds-badge.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-button-unfilled.js +1 -1
  10. package/dist/components/ds-card-data-viz-donut.js +1 -1
  11. package/dist/components/ds-card-data-viz-donut.js.map +1 -1
  12. package/dist/components/ds-card-setting.js +1 -1
  13. package/dist/components/ds-card-setting.js.map +1 -1
  14. package/dist/components/ds-card.js +1 -1
  15. package/dist/components/ds-chart-bar.js +1 -1
  16. package/dist/components/ds-chart-bar.js.map +1 -1
  17. package/dist/components/ds-chart-donut.js +1 -1
  18. package/dist/components/ds-chart-donut.js.map +1 -1
  19. package/dist/components/ds-chart-legend.js +1 -1
  20. package/dist/components/ds-chart-legend.js.map +1 -1
  21. package/dist/components/ds-chart-line.js +1 -1
  22. package/dist/components/ds-chart-line.js.map +1 -1
  23. package/dist/components/ds-checkbox.js +1 -1
  24. package/dist/components/ds-checkbox.js.map +1 -1
  25. package/dist/components/ds-chip.js +1 -1
  26. package/dist/components/ds-chip.js.map +1 -1
  27. package/dist/components/ds-divider.js +1 -1
  28. package/dist/components/ds-empty-state.js +1 -1
  29. package/dist/components/ds-empty-state.js.map +1 -1
  30. package/dist/components/ds-field.js +1 -1
  31. package/dist/components/ds-field.js.map +1 -1
  32. package/dist/components/ds-icon.js +1 -1
  33. package/dist/components/ds-input.js +1 -1
  34. package/dist/components/ds-input.js.map +1 -1
  35. package/dist/components/ds-loader.js +1 -1
  36. package/dist/components/ds-loader.js.map +1 -1
  37. package/dist/components/ds-menu.js +1 -1
  38. package/dist/components/ds-modal.js +1 -1
  39. package/dist/components/ds-modal.js.map +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-slider.js +1 -1
  53. package/dist/components/ds-slider.js.map +1 -1
  54. package/dist/components/ds-tab-group-nav.js +1 -1
  55. package/dist/components/ds-tab-group.js +1 -1
  56. package/dist/components/ds-tab-group.js.map +1 -1
  57. package/dist/components/ds-table.js +1 -1
  58. package/dist/components/ds-table.js.map +1 -1
  59. package/dist/components/ds-tag.js +1 -1
  60. package/dist/components/ds-tag.js.map +1 -1
  61. package/dist/components/ds-text.js +1 -1
  62. package/dist/components/ds-toggle.js +1 -1
  63. package/dist/components/ds-tooltip-data-viz.js +1 -1
  64. package/dist/components/ds-tooltip-data-viz.js.map +1 -1
  65. package/dist/components/ds-tooltip.js +1 -1
  66. package/dist/components/p-8WTsyLQ9.js +2 -0
  67. package/dist/components/p-8WTsyLQ9.js.map +1 -0
  68. package/dist/components/{p-B82KZX9D.js → p-9Qu1ls2y.js} +2 -2
  69. package/dist/components/p-BSNlp8Di.js +2 -0
  70. package/dist/components/p-BSNlp8Di.js.map +1 -0
  71. package/dist/components/p-BVNJnFI9.js +2 -0
  72. package/dist/components/p-BVNJnFI9.js.map +1 -0
  73. package/dist/components/p-B_ENgncs.js +2 -0
  74. package/dist/components/p-B_ENgncs.js.map +1 -0
  75. package/dist/components/p-CNgNgezy.js +2 -0
  76. package/dist/components/p-CNgNgezy.js.map +1 -0
  77. package/dist/components/p-CQmul5NK.js +2 -0
  78. package/dist/components/p-CQmul5NK.js.map +1 -0
  79. package/dist/components/p-CsaW5gDM.js +2 -0
  80. package/dist/components/{p-CYmVDfd_.js.map → p-CsaW5gDM.js.map} +1 -1
  81. package/dist/components/p-Cx-ywr1e.js +2 -0
  82. package/dist/components/p-Cx-ywr1e.js.map +1 -0
  83. package/dist/components/p-DQ3l2TXw.js +2 -0
  84. package/dist/components/p-DQ3l2TXw.js.map +1 -0
  85. package/dist/components/p-DRqJ3RUb.js +2 -0
  86. package/dist/components/p-DRqJ3RUb.js.map +1 -0
  87. package/dist/components/p-V2qW8NL0.js +2 -0
  88. package/dist/components/p-V2qW8NL0.js.map +1 -0
  89. package/dist/components/p-sBx2MuF2.js +2 -0
  90. package/dist/components/p-sBx2MuF2.js.map +1 -0
  91. package/dist/lib/components/Text/text-types.d.ts +9 -0
  92. package/dist/lib/utils/control-text.d.ts +5 -0
  93. package/dist/lib/utils/index.d.ts +2 -0
  94. package/dist/lib/utils/index.js +8 -0
  95. package/dist/lib/utils/index.js.map +3 -3
  96. package/dist/types/components/ButtonFilled/ButtonFilled.d.ts +1 -1
  97. package/dist/types/components/ButtonUnfilled/ButtonUnfilled.d.ts +1 -1
  98. package/dist/types/components/ChartDonut/ChartDonut.d.ts +2 -7
  99. package/dist/types/components/Loader/Loader.d.ts +7 -3
  100. package/dist/types/components/Loader/index.d.ts +1 -1
  101. package/dist/types/components/Select/Select.d.ts +1 -1
  102. package/dist/types/components/Text/Text.d.ts +9 -9
  103. package/dist/types/components/Text/index.d.ts +2 -1
  104. package/dist/types/components/Text/text-types.d.ts +10 -0
  105. package/dist/types/components/Text/text-utils.d.ts +6 -0
  106. package/dist/types/components/Tooltip/Tooltip.d.ts +1 -0
  107. package/dist/types/components/TooltipDataViz/TooltipDataViz.d.ts +3 -2
  108. package/dist/types/components.d.ts +54 -19
  109. package/dist/types/utils/control-text.d.ts +6 -0
  110. package/dist/types/utils/index.d.ts +2 -0
  111. package/package.json +7 -7
  112. package/src/angular/proxies.ts +4 -4
  113. package/src/wc/components/Badge/Badge.tsx +6 -8
  114. package/src/wc/components/Banner/Banner.tsx +3 -1
  115. package/src/wc/components/BarNav/BarNav.tsx +10 -1
  116. package/src/wc/components/ButtonFilled/ButtonFilled.tsx +10 -11
  117. package/src/wc/components/ButtonUnfilled/ButtonUnfilled.tsx +10 -11
  118. package/src/wc/components/Card/Card.tsx +9 -10
  119. package/src/wc/components/ChartDonut/ChartDonut.tsx +4 -47
  120. package/src/wc/components/ChartLegend/ChartLegend.tsx +23 -15
  121. package/src/wc/components/Checkbox/Checkbox.tsx +2 -2
  122. package/src/wc/components/Chip/Chip.tsx +4 -10
  123. package/src/wc/components/EmptyState/EmptyState.tsx +3 -5
  124. package/src/wc/components/Field/Field.tsx +3 -1
  125. package/src/wc/components/Input/Input.tsx +10 -3
  126. package/src/wc/components/Loader/Loader.tsx +77 -30
  127. package/src/wc/components/Loader/index.ts +1 -1
  128. package/src/wc/components/Menu/Menu.tsx +21 -6
  129. package/src/wc/components/Modal/Modal.tsx +16 -3
  130. package/src/wc/components/Pagination/Pagination.tsx +5 -5
  131. package/src/wc/components/PanelNav/PanelNav.tsx +30 -30
  132. package/src/wc/components/PanelTools/PanelTools.tsx +10 -7
  133. package/src/wc/components/RadioGroup/RadioGroup.tsx +3 -1
  134. package/src/wc/components/Select/Select.tsx +15 -17
  135. package/src/wc/components/Slider/Slider.tsx +6 -2
  136. package/src/wc/components/TabGroup/TabGroup.tsx +3 -2
  137. package/src/wc/components/TabGroupNav/TabGroupNav.tsx +3 -2
  138. package/src/wc/components/Table/Table.tsx +21 -16
  139. package/src/wc/components/Tag/Tag.tsx +4 -10
  140. package/src/wc/components/Text/Text.tsx +54 -59
  141. package/src/wc/components/Text/index.ts +12 -1
  142. package/src/wc/components/Text/text-types.ts +23 -0
  143. package/src/wc/components/Text/text-utils.ts +13 -0
  144. package/src/wc/components/Tooltip/Tooltip.tsx +64 -41
  145. package/src/wc/components/TooltipDataViz/TooltipDataViz.tsx +20 -12
  146. package/src/wc/components.d.ts +54 -19
  147. package/src/wc/utils/control-density.css +1 -0
  148. package/src/wc/utils/control-text.ts +11 -0
  149. package/src/wc/utils/index.ts +2 -0
  150. package/src/wc/utils/interaction-fill.css +2 -2
  151. package/src/wc/utils/typography.css +146 -0
  152. package/dist/components/p-1m8r0wFg.js +0 -2
  153. package/dist/components/p-1m8r0wFg.js.map +0 -1
  154. package/dist/components/p-B2_D4IDN.js +0 -2
  155. package/dist/components/p-B2_D4IDN.js.map +0 -1
  156. package/dist/components/p-B5NcWtpp.js +0 -2
  157. package/dist/components/p-B5NcWtpp.js.map +0 -1
  158. package/dist/components/p-BAeKw2pj.js +0 -2
  159. package/dist/components/p-BAeKw2pj.js.map +0 -1
  160. package/dist/components/p-B_g8X0eh.js +0 -2
  161. package/dist/components/p-B_g8X0eh.js.map +0 -1
  162. package/dist/components/p-CDHRJqea.js +0 -2
  163. package/dist/components/p-CDHRJqea.js.map +0 -1
  164. package/dist/components/p-CYmVDfd_.js +0 -2
  165. package/dist/components/p-CtwNUCnp.js +0 -2
  166. package/dist/components/p-CtwNUCnp.js.map +0 -1
  167. package/dist/components/p-CuRjYmBk.js +0 -2
  168. package/dist/components/p-CuRjYmBk.js.map +0 -1
  169. package/dist/components/p-Dlol7hPg.js +0 -2
  170. package/dist/components/p-Dlol7hPg.js.map +0 -1
  171. package/dist/components/p-EK1hOP19.js +0 -2
  172. package/dist/components/p-EK1hOP19.js.map +0 -1
  173. package/dist/components/p-XNHSe8Mg.js +0 -2
  174. package/dist/components/p-XNHSe8Mg.js.map +0 -1
  175. /package/dist/components/{p-B82KZX9D.js.map → p-9Qu1ls2y.js.map} +0 -0
@@ -150,9 +150,9 @@ export class Table {
150
150
  }}
151
151
  style={col.minWidth ? { minWidth: typeof col.minWidth === 'number' ? `${col.minWidth}px` : col.minWidth as string } : undefined}
152
152
  >
153
- <span class="text-body-small-emphasis" style={{ color: 'var(--color-foreground-secondary)' }}>
153
+ <ds-text as="span" variant="text-body-small" emphasis color="secondary">
154
154
  {col.header}
155
- </span>
155
+ </ds-text>
156
156
  {isSorted && (
157
157
  <span class="sort-indicator" aria-hidden="true">
158
158
  {sort.direction === 'asc' ? '↑' : '↓'}
@@ -181,11 +181,15 @@ export class Table {
181
181
 
182
182
  {/* Empty state */}
183
183
  {isEmpty && (
184
- <div class="empty-row" style={{ gridColumn: '1 / -1' }}>
185
- <p class="text-body-medium" style={{ margin: '0', color: 'var(--color-foreground-secondary)' }}>
186
- {this.emptyMessage}
187
- </p>
188
- </div>
184
+ <ds-text
185
+ class="empty-row"
186
+ as="p"
187
+ variant="text-body-medium"
188
+ color="secondary"
189
+ style={{ gridColumn: '1 / -1' }}
190
+ >
191
+ {this.emptyMessage}
192
+ </ds-text>
189
193
  )}
190
194
 
191
195
  {/* Data rows */}
@@ -211,19 +215,20 @@ export class Table {
211
215
  }}
212
216
  >
213
217
  {visibleCols.map(col => (
214
- <div
218
+ <ds-text
215
219
  key={col.id}
216
220
  class={{
217
221
  'cell': true,
218
222
  'align-right': col.align === 'right',
219
223
  'align-center': col.align === 'center',
220
224
  }}
225
+ as="span"
226
+ variant="text-body-medium"
227
+ lineTruncation={1}
221
228
  role="cell"
222
229
  >
223
- <span class="text-body-medium cell-value">
224
- {String(getCellValue(col, row) ?? '')}
225
- </span>
226
- </div>
230
+ {String(getCellValue(col, row) ?? '')}
231
+ </ds-text>
227
232
  ))}
228
233
  </div>
229
234
  );
@@ -243,11 +248,11 @@ export class Table {
243
248
  this.dsPageChange.emit({ pageIndex: this.pageIndex });
244
249
  }}
245
250
  >
246
- <span class="text-body-small">Previous</span>
251
+ <ds-text as="span" variant="text-body-small">Previous</ds-text>
247
252
  </button>
248
- <span class="text-body-small" style={{ color: 'var(--color-foreground-secondary)' }}>
253
+ <ds-text as="span" variant="text-body-small" color="secondary">
249
254
  Page {this.pageIndex! + 1} of {totalPages}
250
- </span>
255
+ </ds-text>
251
256
  <button
252
257
  type="button"
253
258
  class="page-button"
@@ -257,7 +262,7 @@ export class Table {
257
262
  this.dsPageChange.emit({ pageIndex: this.pageIndex });
258
263
  }}
259
264
  >
260
- <span class="text-body-small">Next</span>
265
+ <ds-text as="span" variant="text-body-small">Next</ds-text>
261
266
  </button>
262
267
  </div>
263
268
  )}
@@ -1,16 +1,10 @@
1
1
  import { Component, Prop, h, Host } from '@stencil/core';
2
+ import { CONTROL_TEXT_VARIANT } from '../../utils';
2
3
 
3
4
  export type TagIntent = 'neutral' | 'brand' | 'ai' | 'negative' | 'warning' | 'caution' | 'positive';
4
5
  export type TagContrast = 'strong' | 'bold' | 'medium' | 'faint';
5
6
  export type TagSize = 'md' | 'sm' | 'xs';
6
7
 
7
- /** Text variant per control-density recipe (md / sm / xs). */
8
- const TEXT_VARIANT: Record<TagSize, string> = {
9
- md: 'text-body-medium',
10
- sm: 'text-body-small',
11
- xs: 'text-caption',
12
- };
13
-
14
8
  /**
15
9
  * `ds-icon` size prop matching control-density icon metrics
16
10
  * (md→20 / sm→16 / xs→12 via `--dimension-iconography-*`).
@@ -35,7 +29,7 @@ export class Tag {
35
29
  @Prop() maxWidth: string | number | undefined;
36
30
 
37
31
  render() {
38
- const textVariant = TEXT_VARIANT[this.size];
32
+ const textVariant = CONTROL_TEXT_VARIANT[this.size];
39
33
  const iconSize = ICON_SIZE[this.size];
40
34
 
41
35
  const maxWidthStyle = this.maxWidth != null
@@ -60,9 +54,9 @@ export class Tag {
60
54
  <span class="tag__icon-slot" data-icon-size={iconSize}>
61
55
  <slot name="icon" />
62
56
  </span>
63
- <span class={{ 'tag__label': true, [textVariant]: true }}>
57
+ <ds-text class="tag__label" as="span" variant={textVariant} color="inherit">
64
58
  {this.label}
65
- </span>
59
+ </ds-text>
66
60
  </Host>
67
61
  );
68
62
  }
@@ -1,32 +1,15 @@
1
1
  import { Component, Prop, h, Host } from '@stencil/core';
2
-
3
- export type TextVariant =
4
- | 'text-display-medium'
5
- | 'text-display-small'
6
- | 'text-title-large'
7
- | 'text-title-medium'
8
- | 'text-title-small'
9
- | 'text-body-large'
10
- | 'text-body-large-emphasis'
11
- | 'text-body-medium'
12
- | 'text-body-medium-emphasis'
13
- | 'text-body-small'
14
- | 'text-body-small-emphasis'
15
- | 'text-caption'
16
- | 'text-caption-emphasis';
17
-
18
- export type TextColorToken =
19
- | 'primary' | 'secondary' | 'tertiary'
20
- | 'brand' | 'negative' | 'positive' | 'warning' | 'caution' | 'ai'
21
- | 'on-strong' | 'on-bold' | 'inherit';
22
-
23
- export type TextColor = TextColorToken | `var(--${string})`;
24
- export type TextDecoration = 'none' | 'underline' | 'dotted-underline';
25
- export type TextAlign = 'left' | 'center' | 'right';
26
- export type LineTruncation = 1 | 2 | 3 | 4 | 5 | 'none';
27
- export type TextWrap = 'wrap' | 'nowrap' | 'balance' | 'pretty';
28
- export type TextElement = 'p' | 'span' | 'div' | 'label' | 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6';
29
- export type TextFontFeature = 'normal' | 'tabular-nums';
2
+ import { textDisplayClass, textVariantClass } from './text-utils';
3
+ import type {
4
+ TextAlign,
5
+ TextColor,
6
+ TextDecoration,
7
+ TextElement,
8
+ TextFontFeature,
9
+ TextVariant,
10
+ LineTruncation,
11
+ TextWrap,
12
+ } from './text-types';
30
13
 
31
14
  @Component({
32
15
  tag: 'ds-text',
@@ -35,6 +18,12 @@ export type TextFontFeature = 'normal' | 'tabular-nums';
35
18
  })
36
19
  export class Text {
37
20
  @Prop() variant: TextVariant = 'text-body-medium';
21
+ /**
22
+ * Heavier weight + tighter letter-spacing for the variant.
23
+ * `false` (default): regular weight (caption stays medium).
24
+ * `true`: display bold, title/caption semibold, body medium.
25
+ */
26
+ @Prop() emphasis: boolean = false;
38
27
  @Prop() color: TextColor | undefined;
39
28
  @Prop() decoration: TextDecoration | undefined;
40
29
  @Prop() italic: boolean = false;
@@ -45,6 +34,8 @@ export class Text {
45
34
  @Prop() as: TextElement = 'p';
46
35
  /** Maps to `for` attribute on <label> elements. */
47
36
  @Prop() for: string | undefined;
37
+ /** Forwarded to the inner element (e.g. aria-labelledby targets). */
38
+ @Prop() textId: string | undefined;
48
39
 
49
40
  render() {
50
41
  // Polymorphic element: TSX treats a variable tag as a component, so a
@@ -54,35 +45,38 @@ export class Text {
54
45
  const isCustomColor = (c: TextColor) => typeof c === 'string' && c.startsWith('var(--');
55
46
 
56
47
  const cls: Record<string, boolean> = {
57
- [this.variant]: true,
58
- 'text': true,
59
- 'text--nowrap': this.wrap === 'nowrap',
60
- 'text--balance': this.wrap === 'balance',
61
- 'text--pretty': this.wrap === 'pretty',
62
- 'text--truncate-1': this.lineTruncation === 1 && this.wrap !== 'nowrap',
63
- 'text--truncate-2': this.lineTruncation === 2 && this.wrap !== 'nowrap',
64
- 'text--truncate-3': this.lineTruncation === 3 && this.wrap !== 'nowrap',
65
- 'text--truncate-4': this.lineTruncation === 4 && this.wrap !== 'nowrap',
66
- 'text--truncate-5': this.lineTruncation === 5 && this.wrap !== 'nowrap',
67
- 'text--color-primary': this.color === 'primary',
68
- 'text--color-secondary': this.color === 'secondary',
69
- 'text--color-tertiary': this.color === 'tertiary',
70
- 'text--color-brand': this.color === 'brand',
71
- 'text--color-negative': this.color === 'negative',
72
- 'text--color-positive': this.color === 'positive',
73
- 'text--color-warning': this.color === 'warning',
74
- 'text--color-caution': this.color === 'caution',
75
- 'text--color-ai': this.color === 'ai',
76
- 'text--color-on-strong': this.color === 'on-strong',
77
- 'text--color-on-bold': this.color === 'on-bold',
78
- 'text--color-inherit': this.color === 'inherit',
79
- 'text--decoration-underline': this.decoration === 'underline',
80
- 'text--decoration-dotted-underline': this.decoration === 'dotted-underline',
81
- 'text--italic': this.italic,
82
- 'text--align-left': this.align === 'left',
83
- 'text--align-center': this.align === 'center',
84
- 'text--align-right': this.align === 'right',
85
- 'text--font-feature-tabular-nums': this.fontFeature === 'tabular-nums',
48
+ 'ds-text': true,
49
+ [textDisplayClass(this.as)]: true,
50
+ [textVariantClass(this.variant)]: true,
51
+ 'ds-text--emphasis': this.emphasis,
52
+ 'ds-text--regular': !this.emphasis,
53
+ 'ds-text--nowrap': this.wrap === 'nowrap',
54
+ 'ds-text--balance': this.wrap === 'balance',
55
+ 'ds-text--pretty': this.wrap === 'pretty',
56
+ 'ds-text--truncate-1': this.lineTruncation === 1 && this.wrap !== 'nowrap',
57
+ 'ds-text--truncate-2': this.lineTruncation === 2 && this.wrap !== 'nowrap',
58
+ 'ds-text--truncate-3': this.lineTruncation === 3 && this.wrap !== 'nowrap',
59
+ 'ds-text--truncate-4': this.lineTruncation === 4 && this.wrap !== 'nowrap',
60
+ 'ds-text--truncate-5': this.lineTruncation === 5 && this.wrap !== 'nowrap',
61
+ 'ds-text--color-primary': this.color === 'primary',
62
+ 'ds-text--color-secondary': this.color === 'secondary',
63
+ 'ds-text--color-tertiary': this.color === 'tertiary',
64
+ 'ds-text--color-brand': this.color === 'brand',
65
+ 'ds-text--color-negative': this.color === 'negative',
66
+ 'ds-text--color-positive': this.color === 'positive',
67
+ 'ds-text--color-warning': this.color === 'warning',
68
+ 'ds-text--color-caution': this.color === 'caution',
69
+ 'ds-text--color-ai': this.color === 'ai',
70
+ 'ds-text--color-on-strong': this.color === 'on-strong',
71
+ 'ds-text--color-on-bold': this.color === 'on-bold',
72
+ 'ds-text--color-inherit': this.color === 'inherit',
73
+ 'ds-text--decoration-underline': this.decoration === 'underline',
74
+ 'ds-text--decoration-dotted-underline': this.decoration === 'dotted-underline',
75
+ 'ds-text--italic': this.italic,
76
+ 'ds-text--align-left': this.align === 'left',
77
+ 'ds-text--align-center': this.align === 'center',
78
+ 'ds-text--align-right': this.align === 'right',
79
+ 'ds-text--font-feature-tabular-nums': this.fontFeature === 'tabular-nums',
86
80
  };
87
81
 
88
82
  const style = this.color && isCustomColor(this.color)
@@ -91,10 +85,11 @@ export class Text {
91
85
 
92
86
  const extraProps: Record<string, string> = {};
93
87
  if (this.as === 'label' && this.for) extraProps['for'] = this.for;
88
+ if (this.textId) extraProps['id'] = this.textId;
94
89
 
95
90
  return (
96
- <Host style={{ display: 'contents' }}>
97
- <Tag class={cls} style={style} {...extraProps}>
91
+ <Host class={cls} style={style}>
92
+ <Tag class="ds-text__element" {...extraProps}>
98
93
  <slot />
99
94
  </Tag>
100
95
  </Host>
@@ -1 +1,12 @@
1
- export type { TextVariant, TextColor, TextColorToken, TextDecoration, TextAlign, LineTruncation, TextWrap, TextElement } from './Text';
1
+ export type {
2
+ TextVariant,
3
+ TextColor,
4
+ TextColorToken,
5
+ TextDecoration,
6
+ TextAlign,
7
+ LineTruncation,
8
+ TextWrap,
9
+ TextElement,
10
+ TextFontFeature,
11
+ } from './text-types';
12
+ export { textVariantClass } from './text-utils';
@@ -0,0 +1,23 @@
1
+ export type TextVariant =
2
+ | 'text-display-medium'
3
+ | 'text-display-small'
4
+ | 'text-title-large'
5
+ | 'text-title-medium'
6
+ | 'text-title-small'
7
+ | 'text-body-large'
8
+ | 'text-body-medium'
9
+ | 'text-body-small'
10
+ | 'text-caption';
11
+
12
+ export type TextColorToken =
13
+ | 'primary' | 'secondary' | 'tertiary'
14
+ | 'brand' | 'negative' | 'positive' | 'warning' | 'caution' | 'ai'
15
+ | 'on-strong' | 'on-bold' | 'inherit';
16
+
17
+ export type TextColor = TextColorToken | `var(--${string})`;
18
+ export type TextDecoration = 'none' | 'underline' | 'dotted-underline';
19
+ export type TextAlign = 'left' | 'center' | 'right';
20
+ export type LineTruncation = 1 | 2 | 3 | 4 | 5 | 'none';
21
+ export type TextWrap = 'wrap' | 'nowrap' | 'balance' | 'pretty';
22
+ export type TextElement = 'p' | 'span' | 'div' | 'label' | 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6';
23
+ export type TextFontFeature = 'normal' | 'tabular-nums';
@@ -0,0 +1,13 @@
1
+ import type { TextElement, TextVariant } from './text-types';
2
+
3
+ /** `text-body-medium` → `ds-text--body-medium` */
4
+ export function textVariantClass(variant: TextVariant): string {
5
+ return `ds-text--${variant.replace(/^text-/, '')}`;
6
+ }
7
+
8
+ /** Inline semantics get a measurable inline box; paragraphs/headings get a block box. */
9
+ export function textDisplayClass(element: TextElement): string {
10
+ return element === 'span' || element === 'label'
11
+ ? 'ds-text--inline-box'
12
+ : 'ds-text--block-box';
13
+ }
@@ -1,23 +1,19 @@
1
1
  import { Component, Prop, Element, Watch, h, Host } from '@stencil/core';
2
- import { resolveCssLengthPx, resolveCssTimeMs, TOKEN_CSS_LENGTHS, TOKEN_DEFAULTS } from '../../utils';
2
+ import {
3
+ CONTROL_TEXT_VARIANT,
4
+ resolveCssLengthPx,
5
+ resolveCssTimeMs,
6
+ TOKEN_CSS_LENGTHS,
7
+ TOKEN_DEFAULTS,
8
+ } from '../../utils';
9
+ import type { TextVariant } from '../Text/text-types';
10
+ // Side-effect: register `ds-text` — the portal builds it via createElement, not JSX.
11
+ import '../Text/Text';
3
12
 
4
13
  export type TooltipSide = 'top' | 'right' | 'bottom' | 'left';
5
14
  export type TooltipAlign = 'start' | 'center' | 'end';
6
15
  export type TooltipSize = 'md' | 'sm' | 'xs';
7
16
 
8
- /** Body text per control-density size (regular weight — not emphasis). */
9
- const TEXT_VARIANT: Record<TooltipSize, string> = {
10
- md: 'text-body-medium',
11
- sm: 'text-body-small',
12
- xs: 'text-caption',
13
- };
14
-
15
- const LINE_HEIGHT: Record<TooltipSize, string> = {
16
- md: 'var(--typography-lineheight-md)',
17
- sm: 'var(--typography-lineheight-sm)',
18
- xs: 'var(--typography-lineheight-xs)',
19
- };
20
-
21
17
  const FALLBACK_HEIGHT: Record<TooltipSize, string> = {
22
18
  md: TOKEN_DEFAULTS.size400,
23
19
  sm: TOKEN_DEFAULTS.size300,
@@ -29,6 +25,30 @@ let tooltipIdCounter = 0;
29
25
  /** Currently open tooltip — force-cleared on warm handoff so tips don't cross-fade. */
30
26
  let activeTooltip: Tooltip | null = null;
31
27
 
28
+ type DsTextEl = HTMLElement & {
29
+ as: string;
30
+ variant: TextVariant;
31
+ emphasis: boolean;
32
+ color: string;
33
+ };
34
+
35
+ function createDsText(opts: {
36
+ variant: TextVariant;
37
+ emphasis?: boolean;
38
+ color?: string;
39
+ className?: string;
40
+ text: string;
41
+ }): DsTextEl {
42
+ const el = document.createElement('ds-text') as DsTextEl;
43
+ if (opts.className) el.className = opts.className;
44
+ el.as = 'span';
45
+ el.variant = opts.variant;
46
+ el.emphasis = opts.emphasis ?? false;
47
+ el.color = opts.color ?? 'inherit';
48
+ el.textContent = opts.text;
49
+ return el;
50
+ }
51
+
32
52
  /**
33
53
  * Imperative body portal for the popup.
34
54
  * Stencil must not own the portaled node — moving a VDOM child to `document.body`
@@ -204,6 +224,7 @@ export class Tooltip {
204
224
  activeTooltip.hideInstant();
205
225
  instant = true;
206
226
  }
227
+ // eslint-disable-next-line @typescript-eslint/no-this-alias -- module singleton tracks the active instance
207
228
  activeTooltip = this;
208
229
  this.skipEnterAnimation = instant;
209
230
  if (instant) {
@@ -263,28 +284,39 @@ export class Tooltip {
263
284
 
264
285
  private renderPopupContent() {
265
286
  if (!this.popupEl) return;
266
- const textVariant = TEXT_VARIANT[this.size];
267
- const lineHeight = LINE_HEIGHT[this.size];
287
+ const textVariant = CONTROL_TEXT_VARIANT[this.size];
268
288
  const density =
269
289
  this.size === 'md' ? 'ds-control--md' : this.size === 'sm' ? 'ds-control--sm' : 'ds-control--xs';
270
- // Every node needs `sc-ds-tooltip` so scoped selectors (`.foo.sc-ds-tooltip`) match.
290
+ // Every layout node needs `sc-ds-tooltip` so scoped selectors (`.foo.sc-ds-tooltip`) match.
271
291
  const sc = 'sc-ds-tooltip';
272
292
 
273
- const startKey =
274
- this.shortcutKey && this.shortcutKeyPosition === 'start'
275
- ? `<div class="key-hint ${sc}" aria-hidden="true"><span class="text-caption-emphasis ${sc}">${escapeHtml(this.shortcutKey)}</span></div>`
276
- : '';
277
- const endKey =
278
- this.shortcutKey && this.shortcutKeyPosition === 'end'
279
- ? `<div class="key-hint ${sc}" aria-hidden="true"><span class="text-caption-emphasis ${sc}">${escapeHtml(this.shortcutKey)}</span></div>`
280
- : '';
281
-
282
- this.popupEl.innerHTML =
283
- `<div class="tooltip-inner ${density} ${sc}">` +
284
- startKey +
285
- `<span class="tooltip-label ${textVariant} ${sc}" style="line-height:${lineHeight}">${escapeHtml(this.label)}</span>` +
286
- endKey +
287
- `</div>`;
293
+ const inner = document.createElement('div');
294
+ inner.className = `tooltip-inner ${density} ${sc}`;
295
+
296
+ const appendShortcut = (position: 'start' | 'end') => {
297
+ if (!this.shortcutKey || this.shortcutKeyPosition !== position) return;
298
+ const hint = document.createElement('div');
299
+ hint.className = `key-hint ${sc}`;
300
+ hint.setAttribute('aria-hidden', 'true');
301
+ hint.appendChild(
302
+ createDsText({ variant: 'text-caption', emphasis: true, text: this.shortcutKey }),
303
+ );
304
+ inner.appendChild(hint);
305
+ };
306
+
307
+ appendShortcut('start');
308
+
309
+ inner.appendChild(
310
+ createDsText({
311
+ className: `tooltip-label ${sc}`,
312
+ variant: textVariant,
313
+ text: this.label,
314
+ }),
315
+ );
316
+
317
+ appendShortcut('end');
318
+
319
+ this.popupEl.replaceChildren(inner);
288
320
  }
289
321
 
290
322
  private calculatePosition() {
@@ -338,12 +370,3 @@ export class Tooltip {
338
370
  );
339
371
  }
340
372
  }
341
-
342
- function escapeHtml(value: string): string {
343
- return value
344
- .replace(/&/g, '&amp;')
345
- .replace(/</g, '&lt;')
346
- .replace(/>/g, '&gt;')
347
- .replace(/"/g, '&quot;')
348
- .replace(/'/g, '&#39;');
349
- }
@@ -8,8 +8,9 @@ export type TooltipDataVizSide = 'left' | 'right';
8
8
  export type TooltipDataVizAlign = 'top' | 'bottom';
9
9
 
10
10
  /**
11
- * Positioned value/label callout for chart hover interactions (donut slice, bar,
12
- * line point, ...). Unlike `ds-tooltip`, this doesn't bind to a slotted anchor element
11
+ * Positioned value/label callout for chart hover interactions (bar, line point, …).
12
+ * Donut charts skip this legend sync already surfaces the hovered slice.
13
+ * Unlike `ds-tooltip`, this doesn't bind to a slotted anchor element —
13
14
  * charts hover-highlight data that lives inside an SVG, so the chart itself computes
14
15
  * the anchor point (e.g. the cursor position while hovering) and passes it in as `x`/`y`.
15
16
  *
@@ -120,16 +121,23 @@ export class TooltipDataViz {
120
121
  style={{ left: `${this.x}px`, top: `${this.y}px`, transform: `translate(${translateX}, ${translateY})` }}
121
122
  >
122
123
  <div class="tooltip-data-viz__item ds-control--md">
123
- <span class="tooltip-data-viz__label">
124
- <ds-text as="span" variant="text-body-medium" color="var(--color-foreground-on-translucent-background-secondary)">
125
- {this.label}
126
- </ds-text>
127
- </span>
128
- <span class="tooltip-data-viz__value">
129
- <ds-text as="span" variant="text-body-medium-emphasis" color="var(--color-foreground-on-translucent-background-primary)">
130
- {this.value}
131
- </ds-text>
132
- </span>
124
+ <ds-text
125
+ class="tooltip-data-viz__label"
126
+ as="span"
127
+ variant="text-body-medium"
128
+ color="var(--color-foreground-on-translucent-background-secondary)"
129
+ >
130
+ {this.label}
131
+ </ds-text>
132
+ <ds-text
133
+ class="tooltip-data-viz__value"
134
+ as="span"
135
+ variant="text-body-medium"
136
+ emphasis
137
+ color="var(--color-foreground-on-translucent-background-primary)"
138
+ >
139
+ {this.value}
140
+ </ds-text>
133
141
  </div>
134
142
  </Host>
135
143
  );