@ds-mo/ui 2.11.2 → 2.12.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 (226) 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.js +1 -1
  12. package/dist/components/ds-card-data-viz.js.map +1 -1
  13. package/dist/components/ds-card-setting.js +1 -1
  14. package/dist/components/ds-card-setting.js.map +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-checkbox.js +1 -1
  23. package/dist/components/ds-checkbox.js.map +1 -1
  24. package/dist/components/ds-chip.js +1 -1
  25. package/dist/components/ds-chip.js.map +1 -1
  26. package/dist/components/ds-divider.js +1 -1
  27. package/dist/components/ds-empty-state.js +1 -1
  28. package/dist/components/ds-empty-state.js.map +1 -1
  29. package/dist/components/ds-field.js +1 -1
  30. package/dist/components/ds-field.js.map +1 -1
  31. package/dist/components/ds-header.js +1 -1
  32. package/dist/components/ds-header.js.map +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-scrollbar.js +1 -1
  49. package/dist/components/ds-select.js +1 -1
  50. package/dist/components/ds-select.js.map +1 -1
  51. package/dist/components/ds-shell-gradient-picker.js +1 -1
  52. package/dist/components/ds-shell-gradient-swatch.js +1 -1
  53. package/dist/components/ds-skeleton.js +1 -1
  54. package/dist/components/ds-skeleton.js.map +1 -1
  55. package/dist/components/ds-slider.js +1 -1
  56. package/dist/components/ds-slider.js.map +1 -1
  57. package/dist/components/ds-tab-group-nav.js +1 -1
  58. package/dist/components/ds-tab-group.js +1 -1
  59. package/dist/components/ds-tab-group.js.map +1 -1
  60. package/dist/components/ds-table.js +1 -1
  61. package/dist/components/ds-table.js.map +1 -1
  62. package/dist/components/ds-tag.js +1 -1
  63. package/dist/components/ds-tag.js.map +1 -1
  64. package/dist/components/ds-text.js +1 -1
  65. package/dist/components/ds-toggle.js +1 -1
  66. package/dist/components/ds-toggle.js.map +1 -1
  67. package/dist/components/ds-tooltip-data-viz.js +1 -1
  68. package/dist/components/ds-tooltip-data-viz.js.map +1 -1
  69. package/dist/components/ds-tooltip.js +1 -1
  70. package/dist/components/ds-tooltip.js.map +1 -1
  71. package/dist/components/p-B9dHCgY-.js +2 -0
  72. package/dist/components/p-B9dHCgY-.js.map +1 -0
  73. package/dist/components/p-BPK76IsW.js +2 -0
  74. package/dist/components/p-BPK76IsW.js.map +1 -0
  75. package/dist/components/p-BQq26pt9.js.map +1 -1
  76. package/dist/components/p-BTC9Njj6.js +2 -0
  77. package/dist/components/p-BTC9Njj6.js.map +1 -0
  78. package/dist/components/p-BZIOTcnM.js +2 -0
  79. package/dist/components/p-BZIOTcnM.js.map +1 -0
  80. package/dist/components/{p-DdFUTZ3y.js → p-C3ORQUOp.js} +2 -2
  81. package/dist/components/{p-8Nt92j1-.js → p-CyFVn2Sc.js} +2 -2
  82. package/dist/components/p-DKdI8T8v.js +2 -0
  83. package/dist/components/p-DKdI8T8v.js.map +1 -0
  84. package/dist/components/{p-DOU4cbpc.js → p-DRqxVEye.js} +2 -2
  85. package/dist/components/p-DkcH8mRl.js +2 -0
  86. package/dist/components/p-DkcH8mRl.js.map +1 -0
  87. package/dist/components/p-DusYu-if.js +2 -0
  88. package/dist/components/p-DusYu-if.js.map +1 -0
  89. package/dist/components/p-EK1hOP19.js.map +1 -1
  90. package/dist/components/p-UbKTMQjv.js +2 -0
  91. package/dist/components/p-UbKTMQjv.js.map +1 -0
  92. package/dist/lib/nav/index.js +3 -0
  93. package/dist/lib/nav/index.js.map +2 -2
  94. package/dist/lib/utils/index.js +6 -0
  95. package/dist/lib/utils/index.js.map +2 -2
  96. package/dist/lib/utils/token-defaults.d.ts +3 -0
  97. package/dist/types/components/ButtonFilled/ButtonFilled.d.ts +17 -3
  98. package/dist/types/components/ButtonFilled/index.d.ts +1 -1
  99. package/dist/types/components/ButtonUnfilled/ButtonUnfilled.d.ts +66 -0
  100. package/dist/types/components/ButtonUnfilled/index.d.ts +2 -0
  101. package/dist/types/components/CardDataViz/CardDataViz.d.ts +1 -1
  102. package/dist/types/components/CardSetting/CardSetting.d.ts +1 -1
  103. package/dist/types/components/ChartDonut/ChartDonut.d.ts +7 -0
  104. package/dist/types/components/Checkbox/Checkbox.d.ts +1 -1
  105. package/dist/types/components/Chip/Chip.d.ts +18 -14
  106. package/dist/types/components/Chip/index.d.ts +1 -1
  107. package/dist/types/components/Input/Input.d.ts +1 -1
  108. package/dist/types/components/Pagination/Pagination.d.ts +1 -1
  109. package/dist/types/components/PanelNav/PanelNav.d.ts +2 -0
  110. package/dist/types/components/PanelTools/panel-tools-types.d.ts +1 -1
  111. package/dist/types/components/RadioGroup/RadioGroup.d.ts +2 -2
  112. package/dist/types/components/Select/Select.d.ts +2 -2
  113. package/dist/types/components/ShellGradientSwatch/ShellGradientSwatch.d.ts +1 -1
  114. package/dist/types/components/Slider/Slider.d.ts +1 -1
  115. package/dist/types/components/TabGroup/TabGroup.d.ts +1 -1
  116. package/dist/types/components/TabGroup/tab-item-utils.d.ts +2 -1
  117. package/dist/types/components/TabGroupNav/TabGroupNav.d.ts +1 -1
  118. package/dist/types/components/Tag/Tag.d.ts +0 -2
  119. package/dist/types/components/Tag/index.d.ts +1 -1
  120. package/dist/types/components/Toggle/Toggle.d.ts +1 -1
  121. package/dist/types/components/Tooltip/Tooltip.d.ts +25 -6
  122. package/dist/types/components/TooltipDataViz/TooltipDataViz.d.ts +18 -0
  123. package/dist/types/components.d.ts +277 -476
  124. package/dist/types/utils/token-defaults.d.ts +3 -0
  125. package/package.json +6 -3
  126. package/src/angular/index.ts +1 -6
  127. package/src/angular/proxies.ts +33 -170
  128. package/src/react/components.ts +1 -6
  129. package/src/react/ds-button-unfilled.ts +33 -0
  130. package/src/react/ds-chip.ts +2 -10
  131. package/src/wc/components/BarNav/BarNav.tsx +3 -2
  132. package/src/wc/components/BarNav/bar-nav-tabs-menu-utils.ts +1 -1
  133. package/src/wc/components/ButtonFilled/ButtonFilled.tsx +90 -11
  134. package/src/wc/components/ButtonFilled/index.ts +6 -1
  135. package/src/wc/components/ButtonUnfilled/ButtonUnfilled.tsx +223 -0
  136. package/src/wc/components/ButtonUnfilled/index.ts +6 -0
  137. package/src/wc/components/CardDataViz/CardDataViz.tsx +1 -2
  138. package/src/wc/components/CardSetting/CardSetting.tsx +4 -6
  139. package/src/wc/components/ChartDonut/ChartDonut.tsx +50 -3
  140. package/src/wc/components/ChartLegend/ChartLegend.tsx +3 -0
  141. package/src/wc/components/Checkbox/Checkbox.tsx +5 -5
  142. package/src/wc/components/Chip/Chip.tsx +50 -67
  143. package/src/wc/components/Chip/index.ts +1 -1
  144. package/src/wc/components/Input/Input.tsx +4 -4
  145. package/src/wc/components/Menu/Menu.tsx +7 -4
  146. package/src/wc/components/Pagination/Pagination.tsx +6 -6
  147. package/src/wc/components/PanelNav/PanelNav.tsx +77 -48
  148. package/src/wc/components/PanelTools/PanelTools.tsx +19 -16
  149. package/src/wc/components/PanelTools/panel-tools-types.ts +1 -1
  150. package/src/wc/components/PanelTools/panel-tools-utils.ts +1 -1
  151. package/src/wc/components/RadioGroup/RadioGroup.tsx +7 -7
  152. package/src/wc/components/Select/Select.tsx +5 -5
  153. package/src/wc/components/ShellGradientSwatch/ShellGradientSwatch.tsx +4 -3
  154. package/src/wc/components/Slider/Slider.tsx +7 -3
  155. package/src/wc/components/TabGroup/TabGroup.tsx +13 -7
  156. package/src/wc/components/TabGroup/tab-item-utils.ts +2 -1
  157. package/src/wc/components/TabGroupNav/TabGroupNav.tsx +14 -13
  158. package/src/wc/components/Tag/Tag.tsx +25 -24
  159. package/src/wc/components/Tag/index.ts +1 -1
  160. package/src/wc/components/Toggle/Toggle.tsx +6 -6
  161. package/src/wc/components/Tooltip/Tooltip.tsx +166 -64
  162. package/src/wc/components/TooltipDataViz/TooltipDataViz.tsx +57 -2
  163. package/src/wc/components.d.ts +277 -476
  164. package/src/wc/utils/control-density.css +42 -0
  165. package/src/wc/utils/control-inactive.css +20 -0
  166. package/src/wc/utils/interaction-fill.css +162 -0
  167. package/src/wc/utils/scroll-edge-fade.css +5 -0
  168. package/src/wc/utils/token-defaults.ts +6 -0
  169. package/dist/components/ds-accordion.js +0 -2
  170. package/dist/components/ds-accordion.js.map +0 -1
  171. package/dist/components/ds-breadcrumb.d.ts +0 -11
  172. package/dist/components/ds-breadcrumb.js +0 -2
  173. package/dist/components/ds-breadcrumb.js.map +0 -1
  174. package/dist/components/ds-button-unfilled-icon.d.ts +0 -11
  175. package/dist/components/ds-button-unfilled-icon.js +0 -2
  176. package/dist/components/ds-fade.d.ts +0 -11
  177. package/dist/components/ds-fade.js +0 -2
  178. package/dist/components/ds-fade.js.map +0 -1
  179. package/dist/components/ds-surface.d.ts +0 -11
  180. package/dist/components/ds-surface.js +0 -2
  181. package/dist/components/ds-surface.js.map +0 -1
  182. package/dist/components/ds-toast-provider.d.ts +0 -11
  183. package/dist/components/ds-toast-provider.js +0 -2
  184. package/dist/components/ds-toast-provider.js.map +0 -1
  185. package/dist/components/p-BMQbEIrQ.js +0 -2
  186. package/dist/components/p-BMQbEIrQ.js.map +0 -1
  187. package/dist/components/p-BSFgy5r6.js +0 -2
  188. package/dist/components/p-BSFgy5r6.js.map +0 -1
  189. package/dist/components/p-CBUIn8dk.js +0 -2
  190. package/dist/components/p-CBUIn8dk.js.map +0 -1
  191. package/dist/components/p-ChVwCVHy.js +0 -2
  192. package/dist/components/p-ChVwCVHy.js.map +0 -1
  193. package/dist/components/p-CpNIQXz6.js +0 -2
  194. package/dist/components/p-CpNIQXz6.js.map +0 -1
  195. package/dist/components/p-CvgHQfvT.js +0 -2
  196. package/dist/components/p-CvgHQfvT.js.map +0 -1
  197. package/dist/components/p-bDf6yBQd.js +0 -2
  198. package/dist/components/p-bDf6yBQd.js.map +0 -1
  199. package/dist/types/components/Accordion/Accordion.d.ts +0 -28
  200. package/dist/types/components/Breadcrumb/Breadcrumb.d.ts +0 -16
  201. package/dist/types/components/ButtonUnfilledIcon/ButtonUnfilledIcon.d.ts +0 -45
  202. package/dist/types/components/ButtonUnfilledIcon/index.d.ts +0 -2
  203. package/dist/types/components/Fade/Fade.d.ts +0 -22
  204. package/dist/types/components/Fade/index.d.ts +0 -2
  205. package/dist/types/components/Surface/Surface.d.ts +0 -22
  206. package/dist/types/components/Toast/Toast.d.ts +0 -16
  207. package/dist/types/components/Toast/toast-service.d.ts +0 -24
  208. package/src/react/ds-accordion.ts +0 -27
  209. package/src/react/ds-breadcrumb.ts +0 -27
  210. package/src/react/ds-button-unfilled-icon.ts +0 -33
  211. package/src/react/ds-fade.ts +0 -26
  212. package/src/react/ds-surface.ts +0 -26
  213. package/src/react/ds-toast-provider.ts +0 -26
  214. package/src/wc/components/Accordion/Accordion.tsx +0 -144
  215. package/src/wc/components/Breadcrumb/Breadcrumb.tsx +0 -61
  216. package/src/wc/components/ButtonUnfilledIcon/ButtonUnfilledIcon.tsx +0 -129
  217. package/src/wc/components/ButtonUnfilledIcon/index.ts +0 -1
  218. package/src/wc/components/Fade/Fade.tsx +0 -58
  219. package/src/wc/components/Fade/index.ts +0 -1
  220. package/src/wc/components/Surface/Surface.tsx +0 -81
  221. package/src/wc/components/Toast/Toast.tsx +0 -179
  222. package/src/wc/components/Toast/toast-service.ts +0 -67
  223. /package/dist/components/{ds-button-unfilled-icon.js.map → ds-button-unfilled.js.map} +0 -0
  224. /package/dist/components/{p-DdFUTZ3y.js.map → p-C3ORQUOp.js.map} +0 -0
  225. /package/dist/components/{p-8Nt92j1-.js.map → p-CyFVn2Sc.js.map} +0 -0
  226. /package/dist/components/{p-DOU4cbpc.js.map → p-DRqxVEye.js.map} +0 -0
@@ -1,19 +1,35 @@
1
1
  import { Component, Prop, Event, EventEmitter, h, Host } from '@stencil/core';
2
2
 
3
- export type ChipIntent = 'neutral' | 'brand' | 'ai' | 'negative' | 'warning' | 'caution' | 'positive';
4
- export type ChipContrast = 'strong' | 'bold' | 'medium' | 'faint';
5
- export type ChipElevation = 'none' | 'flat' | 'elevated';
6
- export type ChipSize = 'md' | 'sm' | 'xs';
3
+ /**
4
+ * Semantic chip state replaces Tag’s intent × contrast matrix.
5
+ * - `default` neutral faint
6
+ * - `active` brand faint
7
+ * - `error` — negative faint
8
+ * - `caution` — caution faint
9
+ */
10
+ export type ChipState = 'default' | 'active' | 'error' | 'caution';
11
+ export type ChipSize = 'md' | 'sm' | 'xs';
7
12
  export type ChipBackground = 'faint' | 'medium' | 'bold' | 'strong' | 'always-dark';
8
13
 
14
+ /** Text variant per control-density recipe (md / sm / xs). */
9
15
  const TEXT_VARIANT: Record<ChipSize, string> = {
10
16
  md: 'text-body-medium',
11
17
  sm: 'text-body-small',
12
18
  xs: 'text-caption',
13
19
  };
14
20
 
15
- const ICON_SIZE: Record<ChipSize, number> = { md: 20, sm: 16, xs: 12 };
21
+ /** Dismiss `ds-icon` size same iconography metrics as Tag’s leading icon. */
22
+ const ICON_SIZE: Record<ChipSize, 'md' | 'sm' | 'xs'> = {
23
+ md: 'md',
24
+ sm: 'sm',
25
+ xs: 'xs',
26
+ };
16
27
 
28
+ /**
29
+ * Removable chip — same density recipe as Tag, but colored by semantic `state`
30
+ * (not intent × contrast). Not a toggle/select control; the only intentional
31
+ * action is remove.
32
+ */
17
33
  @Component({
18
34
  tag: 'ds-chip',
19
35
  styleUrl: 'Chip.css',
@@ -21,46 +37,27 @@ const ICON_SIZE: Record<ChipSize, number> = { md: 20, sm: 16, xs: 12 };
21
37
  })
22
38
  export class Chip {
23
39
  @Prop() label!: string;
24
- @Prop() intent: ChipIntent = 'neutral';
25
- @Prop() contrast: ChipContrast = 'faint';
26
- @Prop() elevation: ChipElevation = 'none';
40
+ /** Semantic color state. @default 'default' */
41
+ @Prop() state: ChipState = 'default';
27
42
  @Prop() size: ChipSize = 'md';
28
43
  @Prop() rounded: boolean = false;
29
- @Prop() removable: boolean = false;
44
+ /** When true (default), shows the trailing dismiss control. */
45
+ @Prop() removable: boolean = true;
30
46
  @Prop() maxWidth: string | number | undefined;
31
- @Prop() inactive: boolean = false;
47
+ @Prop() isInactive: boolean = false;
48
+ /** Surface context for interaction-fill tokens when the chip sits on a non-default surface. */
32
49
  @Prop() background: ChipBackground | undefined;
33
- @Prop({ mutable: true }) pressed: boolean = false;
34
50
 
35
51
  /** Fired when the remove button is clicked. */
36
52
  @Event() dsRemove!: EventEmitter<void>;
37
- /** Fired when an interactive chip is clicked. */
38
- @Event() dsClick!: EventEmitter<void>;
39
- /** Fired when the pressed state toggles. */
40
- @Event() dsPressedChange!: EventEmitter<boolean>;
41
-
42
- private handleClick = () => {
43
- if (this.inactive) return;
44
- this.pressed = !this.pressed;
45
- this.dsPressedChange.emit(this.pressed);
46
- this.dsClick.emit();
47
- };
48
-
49
- private handleKeyDown = (e: KeyboardEvent) => {
50
- if (this.inactive) return;
51
- if (e.key === 'Enter' || e.key === ' ') {
52
- e.preventDefault();
53
- this.handleClick();
54
- }
55
- };
56
53
 
57
54
  private handleRemove = (e: MouseEvent) => {
58
55
  e.stopPropagation();
56
+ if (this.isInactive) return;
59
57
  this.dsRemove.emit();
60
58
  };
61
59
 
62
60
  render() {
63
- const sz = this.size.toUpperCase() as 'MD' | 'SM' | 'XS';
64
61
  const textVariant = TEXT_VARIANT[this.size];
65
62
  const iconSize = ICON_SIZE[this.size];
66
63
 
@@ -68,56 +65,42 @@ export class Chip {
68
65
  ? { maxWidth: typeof this.maxWidth === 'number' ? `${this.maxWidth}px` : this.maxWidth }
69
66
  : undefined;
70
67
 
71
- const hostCls: Record<string, boolean> = {
72
- 'tag': true,
73
- [`tag--intent-${this.intent}`]: true,
74
- [`tag--contrast-${this.contrast}`]: true,
75
- [`tag--elevation-${this.elevation}`]: true,
76
- [`tag--size-${this.size}`]: true,
77
- 'tag--rounded': this.rounded,
78
- 'tag--rounded-no-icon-left': this.rounded,
79
- 'tag--rounded-no-remove-right': this.rounded && !this.removable,
80
- [`tag--icon-left-${sz}`]: false,
81
- [`tag--icon-right-${sz}`]: this.removable,
82
- 'tag--removable': this.removable,
83
- 'tag--interactive': true,
84
- 'tag--inactive': this.inactive,
85
- 'tag--pressed': this.pressed,
86
- 'tag--on-medium': this.background === 'medium',
87
- 'tag--on-bold': this.background === 'bold',
88
- 'tag--on-strong': this.background === 'strong',
89
- 'tag--on-always-dark': this.background === 'always-dark',
90
- };
91
-
92
68
  return (
93
69
  <Host
94
- class={hostCls}
70
+ class={{
71
+ 'tag': true,
72
+ 'chip': true,
73
+ [`chip--${this.state}`]: true,
74
+ [`tag--size-${this.size}`]: true,
75
+ 'ds-control--md': this.size === 'md',
76
+ 'ds-control--sm': this.size === 'sm',
77
+ 'ds-control--xs': this.size === 'xs',
78
+ 'tag--rounded': this.rounded,
79
+ 'tag--removable': this.removable,
80
+ /* Hover overlay on the chip surface — dismiss is the only action. */
81
+ 'ds-interaction-fill': !this.isInactive && this.removable,
82
+ 'ds-interaction-fill--on-medium': this.background === 'medium',
83
+ 'ds-interaction-fill--on-bold': this.background === 'bold',
84
+ 'ds-interaction-fill--on-strong': this.background === 'strong',
85
+ 'ds-interaction-fill--on-always-dark': this.background === 'always-dark',
86
+ 'ds-control-inactive': this.isInactive,
87
+ }}
95
88
  style={maxWidthStyle}
96
- onClick={this.handleClick}
97
- onKeyDown={this.handleKeyDown}
98
- role="button"
99
- tabIndex={this.inactive ? -1 : 0}
100
- aria-pressed={String(this.pressed)}
101
- aria-disabled={this.inactive || undefined}
89
+ aria-disabled={this.isInactive || undefined}
102
90
  >
103
- <span class="tag__icon-slot" style={{ fontSize: `${iconSize}px`, lineHeight: '0' }}>
104
- <slot name="icon" />
105
- </span>
106
91
  <span class={{ 'tag__label': true, [textVariant]: true }}>
107
92
  {this.label}
108
93
  </span>
109
94
  {this.removable && (
110
95
  <button
111
96
  type="button"
112
- class="tag__remove"
97
+ class="tag__remove ds-focus-ring-inset"
113
98
  onClick={this.handleRemove}
114
99
  aria-label={`Remove ${this.label}`}
115
- tabIndex={-1}
100
+ disabled={this.isInactive || undefined}
116
101
  >
117
102
  <slot name="remove-icon">
118
- <span class="tag__remove-x" style={{ fontSize: `${iconSize}px`, lineHeight: '0' }}>
119
-
120
- </span>
103
+ <ds-icon class="tag__remove-x" name="Cross" size={iconSize} color="inherit"></ds-icon>
121
104
  </slot>
122
105
  </button>
123
106
  )}
@@ -1 +1 @@
1
- export type { ChipIntent, ChipContrast, ChipElevation, ChipSize, ChipBackground } from './Chip';
1
+ export type { ChipState, ChipSize, ChipBackground } from './Chip';
@@ -18,7 +18,7 @@ export class Input {
18
18
  @Prop() value: string = '';
19
19
  @Prop() placeholder: string | undefined;
20
20
  @Prop() type: InputType = 'text';
21
- @Prop() inactive: boolean = false;
21
+ @Prop() isInactive: boolean = false;
22
22
  @Prop() autoFocus: boolean = false;
23
23
  @Prop() error: boolean = false;
24
24
  @Prop() errorMessage: string | undefined;
@@ -48,7 +48,7 @@ export class Input {
48
48
 
49
49
  render() {
50
50
  const inputId = this.inputId ?? this.generatedId;
51
- const showClear = this.type === 'search' && this.value.length > 0 && !this.inactive;
51
+ const showClear = this.type === 'search' && this.value.length > 0 && !this.isInactive;
52
52
  const showError = this.error && Boolean(this.errorMessage);
53
53
 
54
54
  const describedBy = [
@@ -57,7 +57,7 @@ export class Input {
57
57
  ].filter(Boolean).join(' ') || undefined;
58
58
 
59
59
  return (
60
- <Host class="input-container">
60
+ <Host class={{ 'input-container': true, 'ds-control-inactive': this.isInactive }}>
61
61
  <div class={{ wrapper: true, 'wrapper--error': this.error }}>
62
62
  <div class="row">
63
63
  <input
@@ -65,7 +65,7 @@ export class Input {
65
65
  id={inputId}
66
66
  value={this.value}
67
67
  placeholder={this.placeholder}
68
- disabled={this.inactive}
68
+ disabled={this.isInactive}
69
69
  autoFocus={this.autoFocus}
70
70
  class="native-input"
71
71
  aria-label={this.ariaLabel}
@@ -357,7 +357,7 @@ export class Menu {
357
357
  aria-label={section.header}
358
358
  >
359
359
  {section.header && (
360
- <div class="section-header" aria-hidden="true">
360
+ <div class="section-header ds-control--md" aria-hidden="true">
361
361
  <span class="text-body-small-emphasis section-label">{section.header}</span>
362
362
  </div>
363
363
  )}
@@ -379,10 +379,13 @@ export class Menu {
379
379
  type="button"
380
380
  class={{
381
381
  'menu-item': true,
382
+ 'ds-control--md': true,
382
383
  'ds-focus-ring-inset': true,
383
384
  'ds-focus-ring--visible': isFocused && this.focusRingVisible,
385
+ 'ds-interaction-fill': !item.isInactive,
386
+ 'ds-interaction-fill--selected': !!item.isSelected && !item.isInactive,
384
387
  'menu-item--selected': !!item.isSelected,
385
- 'menu-item--inactive': !!item.isInactive,
388
+ 'ds-control-inactive': !!item.isInactive,
386
389
  'menu-item--destructive': !!item.isDestructive,
387
390
  'menu-item--focused': isFocused,
388
391
  }}
@@ -394,7 +397,7 @@ export class Menu {
394
397
  onClick={() => this.handleItemClick(item)}
395
398
  onFocus={() => { this.focusedIndex = idx; }}
396
399
  >
397
- <div class="menu-item__content">
400
+ <div class="menu-item__content ds-interaction-fill__content">
398
401
  <span class={item.isSelected ? 'text-body-medium-emphasis menu-item__label' : 'text-body-medium menu-item__label'}>
399
402
  {item.label}
400
403
  </span>
@@ -403,7 +406,7 @@ export class Menu {
403
406
  )}
404
407
  </div>
405
408
  {item.showToggle && (
406
- <div class={{ 'menu-item__toggle': true, 'menu-item__toggle--on': !!item.toggleValue }} aria-hidden="true">
409
+ <div class={{ 'menu-item__toggle': true, 'menu-item__toggle--on': !!item.toggleValue, 'ds-interaction-fill__content': true }} aria-hidden="true">
407
410
  <div class="menu-item__toggle-thumb" />
408
411
  </div>
409
412
  )}
@@ -29,12 +29,12 @@ export class Pagination {
29
29
  @Prop({ mutable: true }) page: number = 1;
30
30
  @Prop() totalPages: number = 1;
31
31
  @Prop() siblingCount: number = 1;
32
- @Prop() inactive: boolean = false;
32
+ @Prop() isInactive: boolean = false;
33
33
 
34
34
  @Event() dsPageChange!: EventEmitter<number>;
35
35
 
36
36
  private go(p: number) {
37
- if (this.inactive) return;
37
+ if (this.isInactive) return;
38
38
  this.page = p;
39
39
  this.dsPageChange.emit(p);
40
40
  }
@@ -47,13 +47,13 @@ export class Pagination {
47
47
  return (
48
48
  <Host>
49
49
  <nav
50
- class={{ pagination: true, 'pagination--inactive': this.inactive }}
50
+ class={{ pagination: true, 'ds-control-inactive': this.isInactive }}
51
51
  aria-label="Pagination"
52
52
  >
53
53
  <button
54
54
  type="button"
55
55
  class="button button--nav"
56
- disabled={this.page <= 1 || this.inactive}
56
+ disabled={this.page <= 1 || this.isInactive}
57
57
  onClick={() => this.go(this.page - 1)}
58
58
  aria-label="Previous page"
59
59
  >
@@ -72,7 +72,7 @@ export class Pagination {
72
72
  class={{ button: true, 'button--active': item === this.page }}
73
73
  onClick={() => this.go(item)}
74
74
  aria-current={item === this.page ? 'page' : undefined}
75
- disabled={this.inactive}
75
+ disabled={this.isInactive}
76
76
  >
77
77
  <span class={item === this.page ? 'text-body-small-emphasis' : 'text-body-small'}>
78
78
  {item}
@@ -84,7 +84,7 @@ export class Pagination {
84
84
  <button
85
85
  type="button"
86
86
  class="button button--nav"
87
- disabled={this.page >= this.totalPages || this.inactive}
87
+ disabled={this.page >= this.totalPages || this.isInactive}
88
88
  onClick={() => this.go(this.page + 1)}
89
89
  aria-label="Next page"
90
90
  >
@@ -480,6 +480,70 @@ export class PanelNav {
480
480
  window.addEventListener('mouseup', onUp);
481
481
  }
482
482
 
483
+ private renderFooterAction(isDashboardChrome: boolean) {
484
+ const footerLabel = isDashboardChrome ? 'Open settings' : 'Go to dashboard';
485
+ return (
486
+ <ds-tooltip label={footerLabel} side="right" size="sm">
487
+ <ds-button-unfilled variant="icon"
488
+ class="panel-nav__footer-btn"
489
+ icon={isDashboardChrome ? 'Gear' : 'Dashboard'}
490
+ activeFill={false}
491
+ hasBorder={false}
492
+ focusTabIndex={this.rovingIndex === this.getFooterRovingIndex() ? 0 : -1}
493
+ aria-label={footerLabel}
494
+ onDsClick={() => this.handleFooterAction()}
495
+ onKeyDown={(e: KeyboardEvent) => this.handleRovingKeyDown(e, this.getFooterRovingIndex())}
496
+ onFocusin={() => { this.rovingIndex = this.getFooterRovingIndex(); }}
497
+ />
498
+ </ds-tooltip>
499
+ );
500
+ }
501
+
502
+ private renderFooterUser(collapsed: boolean, userName: string, userInitial: string) {
503
+ const userLabel = collapsed ? `User: ${userName}` : `User menu for ${userName}`;
504
+ const userButton = (
505
+ <button
506
+ type="button"
507
+ id={PANEL_NAV_USER_MENU_ANCHOR_ID}
508
+ class="panel-nav__item panel-nav__footer-user ds-focus-ring-inset ds-interaction-fill"
509
+ tabIndex={this.rovingIndex === this.getUserRovingIndex() ? 0 : -1}
510
+ aria-label={userLabel}
511
+ onClick={(e) => this.handleUserAction(e)}
512
+ onKeyDown={(e: KeyboardEvent) => this.handleRovingKeyDown(e, this.getUserRovingIndex())}
513
+ onFocus={() => { this.rovingIndex = this.getUserRovingIndex(); }}
514
+ >
515
+ <span class="panel-nav__item-label panel-nav__footer-user-label">
516
+ <span class="panel-nav__item-label-text">
517
+ <ds-text as="span" variant="text-body-medium-emphasis" color="inherit">
518
+ {userName}
519
+ </ds-text>
520
+ </span>
521
+ </span>
522
+ <span class="panel-nav__item-icon panel-nav__footer-user-icon" aria-hidden="true">
523
+ <span class="panel-nav__footer-icon-expanded">
524
+ <ds-icon name="ChevronUpDown" size="md" color="inherit" />
525
+ </span>
526
+ <span class="panel-nav__footer-icon-collapsed">
527
+ <ds-icon name="Circle" size="md" color="inherit" />
528
+ <span class="panel-nav__user-initial">
529
+ <ds-text as="span" variant="text-caption-emphasis" color="inherit">
530
+ {userInitial}
531
+ </ds-text>
532
+ </span>
533
+ </span>
534
+ </span>
535
+ </button>
536
+ );
537
+
538
+ if (!collapsed) return userButton;
539
+
540
+ return (
541
+ <ds-tooltip label={userLabel} side="right" size="sm">
542
+ {userButton}
543
+ </ds-tooltip>
544
+ );
545
+ }
546
+
483
547
  private renderNavItem(item: PanelNavItem, idx: number, collapsed: boolean) {
484
548
  const isActive = item.id === this.effectiveActiveId;
485
549
  const rovingPosition = idx + 1;
@@ -515,9 +579,9 @@ export class PanelNav {
515
579
  'panel-nav__item': true,
516
580
  'panel-nav__item--active': isActive,
517
581
  'ds-focus-ring-inset': true,
582
+ 'ds-interaction-fill': true,
518
583
  },
519
584
  'aria-current': isActive ? ('page' as const) : undefined,
520
- title: collapsed ? item.label : undefined,
521
585
  tabIndex: rovingPosition === this.rovingIndex ? 0 : -1,
522
586
  onClick: () => this.handleItemClick(item.id),
523
587
  onKeyDown: (e: KeyboardEvent) => this.handleRovingKeyDown(e, rovingPosition),
@@ -525,10 +589,17 @@ export class PanelNav {
525
589
  };
526
590
 
527
591
  const useAnchor = this.routerMode === 'anchor' && item.href;
528
-
529
- return useAnchor
592
+ const control = useAnchor
530
593
  ? <a {...sharedProps} href={item.href}>{itemContent}</a>
531
594
  : <button {...sharedProps} type="button">{itemContent}</button>;
595
+
596
+ if (!collapsed) return control;
597
+
598
+ return (
599
+ <ds-tooltip label={item.label} side="right" size="sm">
600
+ {control}
601
+ </ds-tooltip>
602
+ );
532
603
  }
533
604
 
534
605
  render() {
@@ -557,7 +628,7 @@ export class PanelNav {
557
628
  <div class="panel-nav__header">
558
629
  <button
559
630
  type="button"
560
- class="panel-nav__header-btn ds-focus-ring-inset"
631
+ class="panel-nav__header-btn ds-focus-ring-inset ds-interaction-fill"
561
632
  tabIndex={this.rovingIndex === 0 ? 0 : -1}
562
633
  onClick={() => this.handleToggle()}
563
634
  onKeyDown={(e: KeyboardEvent) => this.handleRovingKeyDown(e, 0)}
@@ -610,50 +681,8 @@ export class PanelNav {
610
681
 
611
682
  {/* ── Footer ── */}
612
683
  <div class="panel-nav__footer">
613
- <ds-button-unfilled-icon
614
- class="panel-nav__footer-btn"
615
- icon={isDashboardChrome ? 'Gear' : 'Dashboard'}
616
- activeFill={false}
617
- focusTabIndex={this.rovingIndex === this.getFooterRovingIndex() ? 0 : -1}
618
- title={isDashboardChrome ? 'Settings' : 'Dashboard'}
619
- aria-label={isDashboardChrome ? 'Open settings' : 'Go to dashboard'}
620
- onDsClick={() => this.handleFooterAction()}
621
- onKeyDown={(e: KeyboardEvent) => this.handleRovingKeyDown(e, this.getFooterRovingIndex())}
622
- onFocusin={() => { this.rovingIndex = this.getFooterRovingIndex(); }}
623
- />
624
-
625
- {/* Right user — label fades like nav items; right icon cross-fades chevron ↔ circle+initial */}
626
- <button
627
- type="button"
628
- id={PANEL_NAV_USER_MENU_ANCHOR_ID}
629
- class="panel-nav__item panel-nav__footer-user ds-focus-ring-inset"
630
- tabIndex={this.rovingIndex === this.getUserRovingIndex() ? 0 : -1}
631
- aria-label={collapsed ? `User: ${userName}` : `User menu for ${userName}`}
632
- onClick={(e) => this.handleUserAction(e)}
633
- onKeyDown={(e: KeyboardEvent) => this.handleRovingKeyDown(e, this.getUserRovingIndex())}
634
- onFocus={() => { this.rovingIndex = this.getUserRovingIndex(); }}
635
- >
636
- <span class="panel-nav__item-label panel-nav__footer-user-label">
637
- <span class="panel-nav__item-label-text">
638
- <ds-text as="span" variant="text-body-medium-emphasis" color="inherit">
639
- {userName}
640
- </ds-text>
641
- </span>
642
- </span>
643
- <span class="panel-nav__item-icon panel-nav__footer-user-icon" aria-hidden="true">
644
- <span class="panel-nav__footer-icon-expanded">
645
- <ds-icon name="ChevronUpDown" size="md" color="inherit" />
646
- </span>
647
- <span class="panel-nav__footer-icon-collapsed">
648
- <ds-icon name="Circle" size="md" color="inherit" />
649
- <span class="panel-nav__user-initial">
650
- <ds-text as="span" variant="text-caption-emphasis" color="inherit">
651
- {userInitial}
652
- </ds-text>
653
- </span>
654
- </span>
655
- </span>
656
- </button>
684
+ {this.renderFooterAction(isDashboardChrome)}
685
+ {this.renderFooterUser(collapsed, userName, userInitial)}
657
686
  </div>
658
687
 
659
688
  </nav>
@@ -152,7 +152,7 @@ export class PanelTools {
152
152
  @Method()
153
153
  async activateTool(id: PanelToolsToolId) {
154
154
  const item = this.railItems.find(entry => entry.id === id);
155
- if (!item || item.inactive) return;
155
+ if (!item || item.isInactive) return;
156
156
  this.handleToolChange(id);
157
157
  }
158
158
 
@@ -204,22 +204,25 @@ export class PanelTools {
204
204
  };
205
205
 
206
206
  private renderRailAction(item: PanelToolsItem, index: number) {
207
+ const label = item.ariaLabel ?? PANEL_TOOLS_LABELS[item.id];
207
208
  return (
208
- <ds-button-unfilled-icon
209
- key={item.id}
210
- class="panel-tools__rail-action"
211
- icon={item.icon}
212
- isActive={this.isRailSelected(item.id)}
213
- activeFill={false}
214
- dot={item.dot ?? false}
215
- inactive={item.inactive}
216
- focusTabIndex={index === this.rovingIndex ? 0 : -1}
217
- aria-label={item.ariaLabel ?? PANEL_TOOLS_LABELS[item.id]}
218
- pressed={this.isRailSelected(item.id)}
219
- onFocusin={() => { this.rovingIndex = index; }}
220
- onKeyDown={(e: KeyboardEvent) => this.handleRailKeyDown(e, index)}
221
- onDsClick={() => this.handleToolChange(item.id)}
222
- />
209
+ <ds-tooltip key={item.id} label={label} side="left" size="sm">
210
+ <ds-button-unfilled variant="icon"
211
+ class="panel-tools__rail-action"
212
+ icon={item.icon}
213
+ isActive={this.isRailSelected(item.id)}
214
+ activeFill={false}
215
+ hasBorder={false}
216
+ dot={item.dot ?? false}
217
+ isInactive={item.isInactive}
218
+ focusTabIndex={index === this.rovingIndex ? 0 : -1}
219
+ aria-label={label}
220
+ pressed={this.isRailSelected(item.id)}
221
+ onFocusin={() => { this.rovingIndex = index; }}
222
+ onKeyDown={(e: KeyboardEvent) => this.handleRailKeyDown(e, index)}
223
+ onDsClick={() => this.handleToolChange(item.id)}
224
+ />
225
+ </ds-tooltip>
223
226
  );
224
227
  }
225
228
 
@@ -28,6 +28,6 @@ export interface PanelToolsItem {
28
28
  selected?: boolean;
29
29
  /** Show a notification dot. */
30
30
  dot?: boolean;
31
- inactive?: boolean;
31
+ isInactive?: boolean;
32
32
  ariaLabel?: string;
33
33
  }
@@ -24,7 +24,7 @@ export function shouldResyncPanelToolsItems(
24
24
  prior.icon !== item.icon ||
25
25
  prior.selected !== item.selected ||
26
26
  prior.dot !== item.dot ||
27
- prior.inactive !== item.inactive ||
27
+ prior.isInactive !== item.isInactive ||
28
28
  prior.ariaLabel !== item.ariaLabel
29
29
  );
30
30
  });
@@ -3,7 +3,7 @@ import { Component, Prop, Event, EventEmitter, Element, Listen, h, Host } from '
3
3
  export interface RadioOption {
4
4
  label: string;
5
5
  value: string;
6
- inactive?: boolean;
6
+ isInactive?: boolean;
7
7
  }
8
8
 
9
9
  @Component({
@@ -17,7 +17,7 @@ export class RadioGroup {
17
17
  @Prop() options: RadioOption[] = [];
18
18
  @Prop({ mutable: true }) value: string = '';
19
19
  @Prop() direction: 'vertical' | 'horizontal' = 'vertical';
20
- @Prop() inactive: boolean = false;
20
+ @Prop() isInactive: boolean = false;
21
21
  @Prop({ attribute: 'aria-label' }) ariaLabel: string | undefined;
22
22
  @Prop({ attribute: 'aria-labelledby' }) ariaLabelledby: string | undefined;
23
23
 
@@ -70,7 +70,7 @@ export class RadioGroup {
70
70
  }
71
71
 
72
72
  private selectItem(optValue: string) {
73
- if (this.inactive) return;
73
+ if (this.isInactive) return;
74
74
  if (optValue !== this.value) {
75
75
  this.value = optValue;
76
76
  this.dsChange.emit(optValue);
@@ -79,8 +79,8 @@ export class RadioGroup {
79
79
 
80
80
  render() {
81
81
  const selectedIdx = this.options.findIndex(o => o.value === this.value);
82
- const firstActiveIdx = this.options.findIndex(o => !this.inactive && !o.inactive);
83
- const focusableIdx = selectedIdx >= 0 && !this.inactive && !this.options[selectedIdx]?.inactive
82
+ const firstActiveIdx = this.options.findIndex(o => !this.isInactive && !o.isInactive);
83
+ const focusableIdx = selectedIdx >= 0 && !this.isInactive && !this.options[selectedIdx]?.isInactive
84
84
  ? selectedIdx
85
85
  : firstActiveIdx;
86
86
 
@@ -92,7 +92,7 @@ export class RadioGroup {
92
92
  class={{ group: true, 'group--horizontal': this.direction === 'horizontal' }}
93
93
  >
94
94
  {this.options.map((opt, i) => {
95
- const isItemInactive = this.inactive || !!opt.inactive;
95
+ const isItemInactive = this.isInactive || !!opt.isInactive;
96
96
  const isChecked = opt.value === this.value;
97
97
  const tabIdx = isItemInactive ? -1 : (i === focusableIdx ? 0 : -1);
98
98
 
@@ -106,7 +106,7 @@ export class RadioGroup {
106
106
  data-radio-item
107
107
  data-value={opt.value}
108
108
  data-inactive={isItemInactive || undefined}
109
- class={{ 'radio-item': true, 'radio-item--inactive': isItemInactive }}
109
+ class={{ 'radio-item': true, 'ds-control-inactive': isItemInactive }}
110
110
  onClick={() => !isItemInactive && this.selectItem(opt.value)}
111
111
  onKeyDown={(e: KeyboardEvent) => {
112
112
  if ((e.key === ' ' || e.key === 'Enter') && !isItemInactive) {
@@ -26,8 +26,8 @@ export class Select {
26
26
  /** Placeholder shown when no value is selected. */
27
27
  @Prop() placeholder: string = 'Select option';
28
28
 
29
- /** Disables interaction. */
30
- @Prop() inactive: boolean = false;
29
+ /** Disables interaction (25% opacity via ds-control-inactive). */
30
+ @Prop() isInactive: boolean = false;
31
31
 
32
32
  @Prop({ attribute: 'aria-label' }) ariaLabel: string | undefined;
33
33
  @Prop({ attribute: 'aria-labelledby' }) ariaLabelledby: string | undefined;
@@ -87,7 +87,7 @@ export class Select {
87
87
  }
88
88
 
89
89
  private open() {
90
- if (this.inactive || !this.options.length) return;
90
+ if (this.isInactive || !this.options.length) return;
91
91
  // Sync menu width to trigger width
92
92
  if (this.menuEl && this.triggerEl) {
93
93
  this.menuEl.menuWidth = `${this.triggerEl.offsetWidth}px`;
@@ -147,14 +147,14 @@ export class Select {
147
147
  const showPlaceholder = !this.hasSelection;
148
148
 
149
149
  return (
150
- <Host class={{ 'select-host': true, inactive: this.inactive }}>
150
+ <Host class={{ 'select-host': true, 'ds-control-inactive': this.isInactive }}>
151
151
  <button
152
152
  ref={el => {
153
153
  this.triggerEl = (el as HTMLButtonElement) ?? null;
154
154
  }}
155
155
  type="button"
156
156
  class={{ trigger: true, 'trigger--open': this.isOpen }}
157
- disabled={this.inactive}
157
+ disabled={this.isInactive}
158
158
  role="combobox"
159
159
  aria-haspopup="listbox"
160
160
  aria-expanded={this.isOpen}
@@ -19,14 +19,14 @@ export class ShellGradientSwatch {
19
19
  /** Selected — brand stroke + halo over inner fill/border/interaction stack. */
20
20
  @Prop({ reflect: true }) selected = false;
21
21
 
22
- @Prop() inactive = false;
22
+ @Prop() isInactive = false;
23
23
 
24
24
  @Prop({ attribute: 'aria-label' }) ariaLabel: string | undefined;
25
25
 
26
26
  @Event() dsSelect!: EventEmitter<ShellGradientPreset>;
27
27
 
28
28
  private handleClick = () => {
29
- if (this.inactive) return;
29
+ if (this.isInactive) return;
30
30
  this.dsSelect.emit(this.preset);
31
31
  };
32
32
 
@@ -40,6 +40,7 @@ export class ShellGradientSwatch {
40
40
  class={{
41
41
  'shell-gradient-swatch': true,
42
42
  'ds-focus-ring-inset': true,
43
+ 'ds-control-inactive': this.isInactive,
43
44
  'shell-gradient-swatch--selected': this.selected,
44
45
  'shell-gradient-swatch--none': this.preset === 'none',
45
46
  }}
@@ -53,7 +54,7 @@ export class ShellGradientSwatch {
53
54
  }
54
55
  aria-label={label}
55
56
  aria-pressed={this.selected ? 'true' : 'false'}
56
- disabled={this.inactive}
57
+ disabled={this.isInactive}
57
58
  onClick={this.handleClick}
58
59
  >
59
60
  <span class="shell-gradient-swatch__fill" aria-hidden="true" />