@ds-mo/ui 2.2.0 → 2.3.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 (40) hide show
  1. package/dist/.build-stamp +1 -1
  2. package/dist/components/ds-app-shell.js +1 -1
  3. package/dist/components/ds-app-shell.js.map +1 -1
  4. package/dist/components/ds-bar-nav.js +1 -1
  5. package/dist/components/ds-bar-nav.js.map +1 -1
  6. package/dist/components/ds-button-unfilled-icon.js +1 -1
  7. package/dist/components/ds-checkbox.js +1 -1
  8. package/dist/components/ds-field.js +1 -1
  9. package/dist/components/ds-field.js.map +1 -1
  10. package/dist/components/ds-panel-nav.js +1 -1
  11. package/dist/components/ds-panel-nav.js.map +1 -1
  12. package/dist/components/ds-panel-tools.js +1 -1
  13. package/dist/components/ds-panel-tools.js.map +1 -1
  14. package/dist/components/ds-slider.js +1 -1
  15. package/dist/components/ds-tab-group-nav.js +1 -1
  16. package/dist/components/ds-toggle.js +1 -1
  17. package/dist/components/p-BTndnbR_.js +2 -0
  18. package/dist/components/p-BTndnbR_.js.map +1 -0
  19. package/dist/components/p-D4zKc1RW.js +2 -0
  20. package/dist/components/p-D4zKc1RW.js.map +1 -0
  21. package/dist/types/components/BarNav/BarNav.d.ts +4 -0
  22. package/dist/types/components/ButtonUnfilledIcon/ButtonUnfilledIcon.d.ts +5 -0
  23. package/dist/types/components/PanelNav/PanelNav.d.ts +7 -2
  24. package/dist/types/components/PanelTools/PanelTools.d.ts +4 -0
  25. package/dist/types/components/TabGroupNav/TabGroupNav.d.ts +22 -3
  26. package/dist/types/components.d.ts +39 -0
  27. package/package.json +1 -1
  28. package/src/angular/proxies.ts +11 -5
  29. package/src/react/ds-tab-group-nav.ts +8 -2
  30. package/src/wc/components/AppShell/AppShell.tsx +5 -7
  31. package/src/wc/components/BarNav/BarNav.tsx +33 -2
  32. package/src/wc/components/ButtonUnfilledIcon/ButtonUnfilledIcon.tsx +8 -0
  33. package/src/wc/components/PanelNav/PanelNav.tsx +116 -20
  34. package/src/wc/components/PanelTools/PanelTools.tsx +64 -14
  35. package/src/wc/components/TabGroupNav/TabGroupNav.tsx +164 -22
  36. package/src/wc/components.d.ts +39 -0
  37. package/dist/components/p-CSc44rQs.js +0 -2
  38. package/dist/components/p-CSc44rQs.js.map +0 -1
  39. package/dist/components/p-CZpCmCiq.js +0 -2
  40. package/dist/components/p-CZpCmCiq.js.map +0 -1
@@ -1,4 +1,4 @@
1
- import { Component, Prop, Event, EventEmitter, Element, Listen, Watch, h, Host } from '@stencil/core';
1
+ import { Component, Prop, Event, EventEmitter, Element, Listen, Watch, Method, State, h, Host } from '@stencil/core';
2
2
  import {
3
3
  getSelectableTabs,
4
4
  isTabDivider,
@@ -23,28 +23,39 @@ export class TabGroupNav {
23
23
  @Prop({ attribute: 'aria-labelledby' }) ariaLabelledby: string | undefined;
24
24
  @Prop() orientation: 'horizontal' | 'vertical' = 'horizontal';
25
25
 
26
+ /**
27
+ * When `false`, arrow keys move focus only — Space/Enter (or click) commits selection.
28
+ * Used by bar nav where each tab is a full page transition.
29
+ */
30
+ @Prop() selectionFollowsFocus: boolean = true;
31
+
32
+ /** When `false`, every tab uses `tabindex="-1"` (another chrome control owns the tab stop). */
33
+ @Prop() rovingEnabled: boolean = true;
34
+
26
35
  @Event() dsChange!: EventEmitter<string>;
27
36
 
37
+ /** Fired when arrow navigation reaches the first/last tab in manual selection mode. */
38
+ @Event() dsRovingExit!: EventEmitter<'start' | 'end'>;
39
+
40
+ @State() private focusedTabId: string = '';
41
+
28
42
  private get selectableTabs(): TabItemTab[] {
29
43
  return getSelectableTabs(this.tabs);
30
44
  }
31
45
 
32
- private selectTab(id: string, options?: { focus?: boolean }) {
33
- this.value = id;
34
- this.dsChange.emit(id);
35
- if (options?.focus !== false) {
36
- this.focusTab(id);
37
- }
38
- }
39
-
40
- private focusTab(id: string) {
41
- const btn = this.el.querySelector(`[data-tab-id="${id}"]`) as HTMLElement | null;
42
- btn?.focus({ preventScroll: true });
46
+ componentWillLoad() {
47
+ this.syncFocusedTabId(this.value);
43
48
  }
44
49
 
45
- /** Keep focus on the selected tab when value changes externally (e.g. BarNav menu). */
46
50
  @Watch('value')
47
51
  onValueChange(next: string, prev: string | undefined) {
52
+ if (!this.selectionFollowsFocus) {
53
+ if (prev === undefined || next !== this.focusedTabId) {
54
+ this.syncFocusedTabId(next);
55
+ }
56
+ return;
57
+ }
58
+
48
59
  if (prev === undefined || next === prev) return;
49
60
 
50
61
  requestAnimationFrame(() => {
@@ -63,6 +74,64 @@ export class TabGroupNav {
63
74
  });
64
75
  }
65
76
 
77
+ @Watch('tabs')
78
+ onTabsChange() {
79
+ if (!this.selectableTabs.some(tab => tab.id === this.focusedTabId)) {
80
+ this.syncFocusedTabId(this.value);
81
+ }
82
+ }
83
+
84
+ @Method()
85
+ async focusTab(id: string) {
86
+ this.focusedTabId = id;
87
+ const btn = this.el.querySelector(`[data-tab-id="${id}"]`) as HTMLElement | null;
88
+ btn?.focus({ preventScroll: true });
89
+ }
90
+
91
+ @Method()
92
+ async focusLastTab() {
93
+ const tabs = this.selectableTabs;
94
+ const last = [...tabs].reverse().find(tab => !tab.disabled);
95
+ if (last) await this.focusTab(last.id);
96
+ }
97
+
98
+ @Method()
99
+ async focusFirstTab() {
100
+ const first = this.selectableTabs.find(tab => !tab.disabled);
101
+ if (first) await this.focusTab(first.id);
102
+ }
103
+
104
+ private syncFocusedTabId(preferred: string) {
105
+ const tabs = this.selectableTabs;
106
+ if (tabs.some(tab => tab.id === preferred && !tab.disabled)) {
107
+ this.focusedTabId = preferred;
108
+ return;
109
+ }
110
+ const first = tabs.find(tab => !tab.disabled);
111
+ this.focusedTabId = first?.id ?? '';
112
+ }
113
+
114
+ private selectTab(id: string, options?: { focus?: boolean }) {
115
+ this.value = id;
116
+ this.focusedTabId = id;
117
+ this.dsChange.emit(id);
118
+ if (options?.focus !== false) {
119
+ void this.focusTab(id);
120
+ }
121
+ }
122
+
123
+ private getFocusedIndex(): number {
124
+ return this.selectableTabs.findIndex(tab => tab.id === this.focusedTabId);
125
+ }
126
+
127
+ private findEnabledLinear(from: number, step: 1 | -1): number | null {
128
+ const tabs = this.selectableTabs;
129
+ for (let i = from + step; i >= 0 && i < tabs.length; i += step) {
130
+ if (!tabs[i]?.disabled) return i;
131
+ }
132
+ return null;
133
+ }
134
+
66
135
  /**
67
136
  * Find the next non-disabled tab index relative to `from`, stepping by `step`.
68
137
  * Returns `from` unchanged if no other enabled tab exists.
@@ -77,27 +146,77 @@ export class TabGroupNav {
77
146
  return from;
78
147
  }
79
148
 
149
+ private moveFocusToIndex(nextIdx: number) {
150
+ const next = this.selectableTabs[nextIdx];
151
+ if (!next) return;
152
+ this.focusedTabId = next.id;
153
+ void this.focusTab(next.id);
154
+ }
155
+
156
+ private activateFocusedTab(e: KeyboardEvent) {
157
+ if (e.key !== 'Enter' && e.key !== ' ') return;
158
+ const tab = this.selectableTabs.find(item => item.id === this.focusedTabId);
159
+ if (!tab || tab.disabled) return;
160
+ e.preventDefault();
161
+ this.selectTab(tab.id, { focus: true });
162
+ }
163
+
80
164
  @Listen('keydown')
81
165
  handleKeyDown(e: KeyboardEvent) {
82
166
  const tabs = this.selectableTabs;
83
167
  if (!tabs.length) return;
84
168
 
85
- const currentIdx = tabs.findIndex(t => t.id === this.value);
86
- const isVertical = this.orientation === 'vertical';
169
+ if (!this.selectionFollowsFocus) {
170
+ if (e.key === 'Enter' || e.key === ' ') {
171
+ this.activateFocusedTab(e);
172
+ return;
173
+ }
174
+ }
175
+
176
+ const currentIdx = this.selectionFollowsFocus
177
+ ? tabs.findIndex(t => t.id === this.value)
178
+ : this.getFocusedIndex();
179
+ if (currentIdx < 0) return;
87
180
 
181
+ const isVertical = this.orientation === 'vertical';
88
182
  let nextIdx: number | null = null;
183
+ let exit: 'start' | 'end' | null = null;
89
184
 
90
185
  if (!isVertical) {
91
186
  if (e.key === 'ArrowRight') {
92
- nextIdx = this.findEnabled(currentIdx, 1);
187
+ if (this.selectionFollowsFocus) {
188
+ nextIdx = this.findEnabled(currentIdx, 1);
189
+ } else {
190
+ const candidate = this.findEnabledLinear(currentIdx, 1);
191
+ if (candidate === null) exit = 'end';
192
+ else nextIdx = candidate;
193
+ }
93
194
  } else if (e.key === 'ArrowLeft') {
94
- nextIdx = this.findEnabled(currentIdx, -1);
195
+ if (this.selectionFollowsFocus) {
196
+ nextIdx = this.findEnabled(currentIdx, -1);
197
+ } else {
198
+ const candidate = this.findEnabledLinear(currentIdx, -1);
199
+ if (candidate === null) exit = 'start';
200
+ else nextIdx = candidate;
201
+ }
95
202
  }
96
203
  } else {
97
204
  if (e.key === 'ArrowDown') {
98
- nextIdx = this.findEnabled(currentIdx, 1);
205
+ if (this.selectionFollowsFocus) {
206
+ nextIdx = this.findEnabled(currentIdx, 1);
207
+ } else {
208
+ const candidate = this.findEnabledLinear(currentIdx, 1);
209
+ if (candidate === null) exit = 'end';
210
+ else nextIdx = candidate;
211
+ }
99
212
  } else if (e.key === 'ArrowUp') {
100
- nextIdx = this.findEnabled(currentIdx, -1);
213
+ if (this.selectionFollowsFocus) {
214
+ nextIdx = this.findEnabled(currentIdx, -1);
215
+ } else {
216
+ const candidate = this.findEnabledLinear(currentIdx, -1);
217
+ if (candidate === null) exit = 'start';
218
+ else nextIdx = candidate;
219
+ }
101
220
  }
102
221
  }
103
222
 
@@ -109,10 +228,20 @@ export class TabGroupNav {
109
228
  nextIdx = lastEnabled === -1 ? null : tabs.length - 1 - lastEnabled;
110
229
  }
111
230
 
112
- if (nextIdx !== null) {
231
+ if (exit) {
232
+ e.preventDefault();
233
+ this.dsRovingExit.emit(exit);
234
+ return;
235
+ }
236
+
237
+ if (nextIdx !== null && nextIdx !== currentIdx) {
113
238
  e.preventDefault();
114
239
  const next = tabs[nextIdx];
115
- this.selectTab(next.id);
240
+ if (this.selectionFollowsFocus) {
241
+ this.selectTab(next.id);
242
+ } else {
243
+ this.moveFocusToIndex(nextIdx);
244
+ }
116
245
  }
117
246
  }
118
247
 
@@ -123,6 +252,14 @@ export class TabGroupNav {
123
252
  return `on-${this.background}`;
124
253
  }
125
254
 
255
+ private tabIndexForTab(tab: TabItemTab): number {
256
+ if (!this.rovingEnabled) return -1;
257
+ if (this.selectionFollowsFocus) {
258
+ return tab.id === this.value ? 0 : -1;
259
+ }
260
+ return tab.id === this.focusedTabId ? 0 : -1;
261
+ }
262
+
126
263
  render() {
127
264
  const bgClass = this.getBgClass();
128
265
  const isVertical = this.orientation === 'vertical';
@@ -174,8 +311,13 @@ export class TabGroupNav {
174
311
  aria-disabled={tab.disabled || undefined}
175
312
  aria-controls={tab.panelId ?? undefined}
176
313
  disabled={tab.disabled}
177
- tabIndex={isSelected ? 0 : -1}
314
+ tabIndex={this.tabIndexForTab(tab)}
178
315
  onClick={() => !tab.disabled && this.selectTab(tab.id)}
316
+ onFocus={() => {
317
+ if (!this.selectionFollowsFocus) {
318
+ this.focusedTabId = tab.id;
319
+ }
320
+ }}
179
321
  >
180
322
  <span class={{
181
323
  tab__label: true,
@@ -332,6 +332,10 @@ export namespace Components {
332
332
  */
333
333
  "dot": boolean;
334
334
  "expanded": boolean | undefined;
335
+ /**
336
+ * Native `tabindex` for roving keyboard groups in shell chrome. Omit for the default button tab stop (`0`).
337
+ */
338
+ "focusTabIndex"?: number;
335
339
  /**
336
340
  * Show a 1px tertiary border without changing the interaction model.
337
341
  * @default false
@@ -890,10 +894,23 @@ export namespace Components {
890
894
  "ariaLabel": string | undefined;
891
895
  "ariaLabelledby": string | undefined;
892
896
  "background": TabGroupNavBackground | undefined;
897
+ "focusFirstTab": () => Promise<void>;
898
+ "focusLastTab": () => Promise<void>;
899
+ "focusTab": (id: string) => Promise<void>;
893
900
  /**
894
901
  * @default 'horizontal'
895
902
  */
896
903
  "orientation": 'horizontal' | 'vertical';
904
+ /**
905
+ * When `false`, every tab uses `tabindex="-1"` (another chrome control owns the tab stop).
906
+ * @default true
907
+ */
908
+ "rovingEnabled": boolean;
909
+ /**
910
+ * When `false`, arrow keys move focus only — Space/Enter (or click) commits selection. Used by bar nav where each tab is a full page transition.
911
+ * @default true
912
+ */
913
+ "selectionFollowsFocus": boolean;
897
914
  /**
898
915
  * @default []
899
916
  */
@@ -1627,6 +1644,7 @@ declare global {
1627
1644
  };
1628
1645
  interface HTMLDsTabGroupNavElementEventMap {
1629
1646
  "dsChange": string;
1647
+ "dsRovingExit": 'start' | 'end';
1630
1648
  }
1631
1649
  interface HTMLDsTabGroupNavElement extends Components.DsTabGroupNav, HTMLStencilElement {
1632
1650
  addEventListener<K extends keyof HTMLDsTabGroupNavElementEventMap>(type: K, listener: (this: HTMLDsTabGroupNavElement, ev: DsTabGroupNavCustomEvent<HTMLDsTabGroupNavElementEventMap[K]>) => any, options?: boolean | AddEventListenerOptions): void;
@@ -2047,6 +2065,10 @@ declare namespace LocalJSX {
2047
2065
  */
2048
2066
  "dot"?: boolean;
2049
2067
  "expanded"?: boolean | undefined;
2068
+ /**
2069
+ * Native `tabindex` for roving keyboard groups in shell chrome. Omit for the default button tab stop (`0`).
2070
+ */
2071
+ "focusTabIndex"?: number;
2050
2072
  /**
2051
2073
  * Show a 1px tertiary border without changing the interaction model.
2052
2074
  * @default false
@@ -2665,10 +2687,24 @@ declare namespace LocalJSX {
2665
2687
  "ariaLabelledby"?: string | undefined;
2666
2688
  "background"?: TabGroupNavBackground | undefined;
2667
2689
  "onDsChange"?: (event: DsTabGroupNavCustomEvent<string>) => void;
2690
+ /**
2691
+ * Fired when arrow navigation reaches the first/last tab in manual selection mode.
2692
+ */
2693
+ "onDsRovingExit"?: (event: DsTabGroupNavCustomEvent<'start' | 'end'>) => void;
2668
2694
  /**
2669
2695
  * @default 'horizontal'
2670
2696
  */
2671
2697
  "orientation"?: 'horizontal' | 'vertical';
2698
+ /**
2699
+ * When `false`, every tab uses `tabindex="-1"` (another chrome control owns the tab stop).
2700
+ * @default true
2701
+ */
2702
+ "rovingEnabled"?: boolean;
2703
+ /**
2704
+ * When `false`, arrow keys move focus only — Space/Enter (or click) commits selection. Used by bar nav where each tab is a full page transition.
2705
+ * @default true
2706
+ */
2707
+ "selectionFollowsFocus"?: boolean;
2672
2708
  /**
2673
2709
  * @default []
2674
2710
  */
@@ -2977,6 +3013,7 @@ declare namespace LocalJSX {
2977
3013
  "expanded": boolean | undefined;
2978
3014
  "haspopup": string | undefined;
2979
3015
  "pressed": boolean | undefined;
3016
+ "focusTabIndex": number;
2980
3017
  }
2981
3018
  interface DsCardAttributes {
2982
3019
  "elevation": CardElevation;
@@ -3153,6 +3190,8 @@ declare namespace LocalJSX {
3153
3190
  "ariaLabel": string | undefined;
3154
3191
  "ariaLabelledby": string | undefined;
3155
3192
  "orientation": 'horizontal' | 'vertical';
3193
+ "selectionFollowsFocus": boolean;
3194
+ "rovingEnabled": boolean;
3156
3195
  }
3157
3196
  interface DsTableAttributes {
3158
3197
  "loading": boolean;
@@ -1,2 +0,0 @@
1
- import{p as o,H as n,c as i,h as t,a as e,t as r}from"./index.js";import{d as s}from"./p-CiEc8Nwx.js";import{d as c}from"./p-DqBCQRRd.js";const d=()=>`.sc-ds-button-unfilled-icon-h{--ds-focus-ring-width:var(--dimension-stroke-width-025, 2px);--ds-focus-ring-offset:var(--dimension-space-025, 2px);--ds-focus-ring-color:var(--color-interaction-focus)}.ds-focus-ring.sc-ds-button-unfilled-icon:focus-visible{outline:var(--ds-focus-ring-width) solid var(--ds-focus-ring-color);outline-offset:var(--ds-focus-ring-offset)}.ds-focus-ring.ds-focus-ring--visible.sc-ds-button-unfilled-icon{outline:var(--ds-focus-ring-width) solid var(--ds-focus-ring-color);outline-offset:var(--ds-focus-ring-offset)}.ds-focus-ring-inset.sc-ds-button-unfilled-icon:focus-visible{outline:none}.ds-focus-ring-inset.sc-ds-button-unfilled-icon:focus-visible::after{outline:var(--ds-focus-ring-width) solid var(--ds-focus-ring-color);outline-offset:calc(-1 * var(--ds-focus-ring-width))}.ds-focus-ring-inset.ds-focus-ring--visible.sc-ds-button-unfilled-icon{outline:none}.ds-focus-ring-inset.ds-focus-ring--visible.sc-ds-button-unfilled-icon::after{outline:var(--ds-focus-ring-width) solid var(--ds-focus-ring-color);outline-offset:calc(-1 * var(--ds-focus-ring-width))}.sc-ds-button-unfilled-icon-h{display:inline-flex;flex:0 0 auto;width:var(--dimension-size-400);height:var(--dimension-size-400);outline:none;--ds-button-unfilled-icon-fg:var(--color-foreground-secondary);--ds-button-unfilled-icon-fg-active:var(--color-foreground-primary);--ds-button-unfilled-icon-active-bg:var(--color-interaction-active);--ds-button-unfilled-icon-hover-bg:var(--color-interaction-hover);--ds-button-unfilled-icon-pressed-bg:var(--color-interaction-pressed);--ds-button-unfilled-icon-dot-ring:var(--color-background-secondary);--ds-button-unfilled-icon-border:var(--color-border-tertiary)}.button-icon.sc-ds-button-unfilled-icon{display:inline-flex;align-items:center;justify-content:center;width:var(--dimension-size-400);height:var(--dimension-size-400);padding:var(--dimension-space-075);background:transparent;border:var(--dimension-stroke-width-012, 1px) solid transparent;border-radius:var(--dimension-radius-025);color:var(--ds-button-unfilled-icon-fg);cursor:pointer;box-sizing:border-box;-webkit-user-select:none;user-select:none;position:relative;overflow:hidden;transition:color var(--effect-motion-short-2)}.button-icon.sc-ds-button-unfilled-icon::before,.button-icon.sc-ds-button-unfilled-icon::after{content:'';position:absolute;inset:0;border-radius:inherit;background:transparent;pointer-events:none;transition:none;z-index:1}.button-icon.sc-ds-button-unfilled-icon::before{z-index:0}.button-icon--active-fill.sc-ds-button-unfilled-icon::before{background:var(--ds-button-unfilled-icon-active-bg)}.button-icon.sc-ds-button-unfilled-icon:hover:not(.button-icon--inactive)::after{background:var(--ds-button-unfilled-icon-hover-bg);transition:none}.button-icon.sc-ds-button-unfilled-icon:active:not(.button-icon--inactive)::after{background:var(--ds-button-unfilled-icon-pressed-bg);transition:none}.button-icon--active.sc-ds-button-unfilled-icon{color:var(--ds-button-unfilled-icon-fg-active)}.button-icon--bordered.sc-ds-button-unfilled-icon{border-color:var(--ds-button-unfilled-icon-border)}.button-icon--bordered.sc-ds-button-unfilled-icon::after{inset:var(--dimension-stroke-width-012, 1px)}.button-icon--bordered.sc-ds-button-unfilled-icon::before{inset:var(--dimension-stroke-width-012, 1px)}.button-icon--inactive.sc-ds-button-unfilled-icon{opacity:0.5;cursor:not-allowed;pointer-events:none}.button-icon--on-medium.sc-ds-button-unfilled-icon{--ds-button-unfilled-icon-active-bg:var(--color-interaction-on-medium-background-active);--ds-button-unfilled-icon-hover-bg:var(--color-interaction-on-medium-background-hover);--ds-button-unfilled-icon-pressed-bg:var(--color-interaction-on-medium-background-pressed);--ds-button-unfilled-icon-dot-ring:var(--color-background-medium-neutral);--ds-focus-ring-color:var(--color-interaction-on-medium-background-focus)}.button-icon--on-bold.sc-ds-button-unfilled-icon{--ds-button-unfilled-icon-fg:var(--color-foreground-on-bold-background-secondary);--ds-button-unfilled-icon-fg-active:var(--color-foreground-on-bold-background-primary);--ds-button-unfilled-icon-active-bg:var(--color-interaction-on-bold-background-active);--ds-button-unfilled-icon-hover-bg:var(--color-interaction-on-bold-background-hover);--ds-button-unfilled-icon-pressed-bg:var(--color-interaction-on-bold-background-pressed);--ds-button-unfilled-icon-dot-ring:var(--color-background-bold-neutral);--ds-focus-ring-color:var(--color-interaction-on-bold-background-focus)}.button-icon--on-strong.sc-ds-button-unfilled-icon{--ds-button-unfilled-icon-fg:var(--color-foreground-on-bold-background-secondary);--ds-button-unfilled-icon-fg-active:var(--color-foreground-on-bold-background-primary);--ds-button-unfilled-icon-active-bg:var(--color-interaction-on-strong-background-active);--ds-button-unfilled-icon-hover-bg:var(--color-interaction-on-strong-background-hover);--ds-button-unfilled-icon-pressed-bg:var(--color-interaction-on-strong-background-pressed);--ds-button-unfilled-icon-dot-ring:var(--color-background-strong-neutral);--ds-focus-ring-color:var(--color-interaction-on-strong-background-focus)}.button-icon--on-always-dark.sc-ds-button-unfilled-icon{--ds-button-unfilled-icon-fg:var(--color-always-dark-foreground-secondary);--ds-button-unfilled-icon-fg-active:var(--color-always-dark-foreground-primary);--ds-button-unfilled-icon-active-bg:var(--color-always-dark-interaction-active);--ds-button-unfilled-icon-hover-bg:var(--color-always-dark-interaction-hover);--ds-button-unfilled-icon-pressed-bg:var(--color-always-dark-interaction-pressed);--ds-button-unfilled-icon-dot-ring:var(--color-always-dark-background);--ds-focus-ring-color:var(--color-always-dark-interaction-focus)}.button-icon--on-navigation.sc-ds-button-unfilled-icon{--ds-button-unfilled-icon-fg:var(--color-navigation-foreground-secondary);--ds-button-unfilled-icon-fg-active:var(--color-navigation-foreground-primary);--ds-button-unfilled-icon-active-bg:var(--color-navigation-interaction-active);--ds-button-unfilled-icon-hover-bg:var(--color-navigation-interaction-hover);--ds-button-unfilled-icon-pressed-bg:var(--color-navigation-interaction-pressed);--ds-button-unfilled-icon-dot-ring:var(--color-navigation-background);--ds-focus-ring-color:var(--color-navigation-interaction-focus)}.button-icon__icon-wrap.sc-ds-button-unfilled-icon{position:relative;width:var(--dimension-iconography-md);height:var(--dimension-iconography-md);display:inline-flex;align-items:center;justify-content:center;flex-shrink:0;z-index:2}.button-icon__dot.sc-ds-button-unfilled-icon{position:absolute;top:0;right:0;pointer-events:none}`;const a=o(class o extends n{constructor(o){super();if(o!==false){this.__registerHost()}this.dsClick=i(this,"dsClick");this.dsChange=i(this,"dsChange");this.icon="";this.isActive=false;this.activeFill=true;this.hasBorder=false;this.dot=false;this.inactive=false;this.type="button";this.ariaLabel="action";this.buttonEl=null;this.handleClick=o=>{if(this.inactive)return;this.dsClick.emit(o);this.dsChange.emit(!this.isActive)}}async setFocus(){this.buttonEl?.focus()}render(){const o=this.background;const n={"button-icon":true,"button-icon--active":this.isActive,"button-icon--active-fill":this.isActive&&this.activeFill,"button-icon--bordered":this.hasBorder,"button-icon--inactive":this.inactive,"button-icon--on-medium":o==="medium","button-icon--on-bold":o==="bold","button-icon--on-strong":o==="strong","button-icon--on-always-dark":o==="always-dark","button-icon--on-navigation":o==="navigation"};return t(e,{key:"6abdc966f8bc8f651bbfa533faa71850a4f44dc4",tabIndex:-1},t("button",{key:"d3b3c1759f9fabb7f0e94611c6c3cb3a0e0d9e77",ref:o=>{this.buttonEl=o??null},type:this.type,class:n,disabled:this.inactive,"aria-label":this.ariaLabel,"aria-controls":this.controls,"aria-expanded":this.expanded===undefined?undefined:String(this.expanded),"aria-haspopup":this.haspopup,"aria-pressed":this.pressed===undefined?undefined:String(this.pressed),onClick:this.handleClick},t("span",{key:"731bc71690891edb9386a2c4a62afae34fa5fb17",class:"button-icon__icon-wrap"},t("ds-icon",{key:"8af219906a6e5ffacce764d725a0e783c4361f10",name:this.icon,size:"md",color:"inherit"}),this.dot&&t("ds-badge",{key:"3d19f7b87b208fe9f2619e34ff8cc114f27745c8",class:"button-icon__dot",variant:"dot",background:"var(--ds-button-unfilled-icon-dot-ring)",label:"","aria-hidden":"true"}))))}get el(){return this}static get style(){return d()}},[2,"ds-button-unfilled-icon",{icon:[1],isActive:[4,"is-active"],activeFill:[4,"active-fill"],hasBorder:[4,"has-border"],dot:[4],inactive:[4],type:[1],background:[1],ariaLabel:[1,"aria-label"],controls:[1],expanded:[4],haspopup:[1],pressed:[4],setFocus:[64]}]);function u(){if(typeof customElements==="undefined"){return}const o=["ds-button-unfilled-icon","ds-badge","ds-icon"];o.forEach((o=>{switch(o){case"ds-button-unfilled-icon":if(!customElements.get(r(o))){customElements.define(r(o),a)}break;case"ds-badge":if(!customElements.get(r(o))){s()}break;case"ds-icon":if(!customElements.get(r(o))){c()}break}}))}u();export{a as B,u as d};
2
- //# sourceMappingURL=p-CSc44rQs.js.map
@@ -1 +0,0 @@
1
- {"version":3,"names":["buttonUnfilledIconCss","ButtonUnfilledIcon","__stencil_proxyCustomElement","HTMLElement","constructor","registerHost","this","icon","isActive","activeFill","hasBorder","dot","inactive","type","ariaLabel","buttonEl","handleClick","event","dsClick","emit","dsChange","setFocus","focus","render","bg","background","cls","h","Host","key","tabIndex","ref","el","class","disabled","controls","expanded","undefined","String","haspopup","pressed","onClick","name","size","color","variant","label"],"sources":["src/wc/components/ButtonUnfilledIcon/ButtonUnfilledIcon.css?tag=ds-button-unfilled-icon&encapsulation=scoped","src/wc/components/ButtonUnfilledIcon/ButtonUnfilledIcon.tsx"],"sourcesContent":["@import '../../utils/focus-ring.css';\n\n:host {\n display: inline-flex;\n flex: 0 0 auto;\n width: var(--dimension-size-400);\n height: var(--dimension-size-400);\n outline: none;\n --ds-button-unfilled-icon-fg: var(--color-foreground-secondary);\n --ds-button-unfilled-icon-fg-active: var(--color-foreground-primary);\n --ds-button-unfilled-icon-active-bg: var(--color-interaction-active);\n --ds-button-unfilled-icon-hover-bg: var(--color-interaction-hover);\n --ds-button-unfilled-icon-pressed-bg: var(--color-interaction-pressed);\n --ds-button-unfilled-icon-dot-ring: var(--color-background-secondary);\n --ds-button-unfilled-icon-border: var(--color-border-tertiary);\n}\n\n.button-icon {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: var(--dimension-size-400);\n height: var(--dimension-size-400);\n padding: var(--dimension-space-075);\n background: transparent;\n border: var(--dimension-stroke-width-012, 1px) solid transparent;\n border-radius: var(--dimension-radius-025);\n color: var(--ds-button-unfilled-icon-fg);\n cursor: pointer;\n box-sizing: border-box;\n -webkit-user-select: none;\n user-select: none;\n position: relative;\n overflow: hidden;\n transition: color var(--effect-motion-short-2);\n}\n\n.button-icon::before,\n.button-icon::after {\n content: '';\n position: absolute;\n inset: 0;\n border-radius: inherit;\n background: transparent;\n pointer-events: none;\n transition: none;\n z-index: 1;\n}\n\n.button-icon::before {\n z-index: 0;\n}\n\n.button-icon--active-fill::before {\n background: var(--ds-button-unfilled-icon-active-bg);\n}\n\n.button-icon:hover:not(.button-icon--inactive)::after {\n background: var(--ds-button-unfilled-icon-hover-bg);\n transition: none;\n}\n\n.button-icon:active:not(.button-icon--inactive)::after {\n background: var(--ds-button-unfilled-icon-pressed-bg);\n transition: none;\n}\n\n.button-icon--active {\n color: var(--ds-button-unfilled-icon-fg-active);\n}\n\n.button-icon--bordered {\n border-color: var(--ds-button-unfilled-icon-border);\n}\n\n.button-icon--bordered::after {\n inset: var(--dimension-stroke-width-012, 1px);\n}\n\n.button-icon--bordered::before {\n inset: var(--dimension-stroke-width-012, 1px);\n}\n\n.button-icon--inactive {\n opacity: 0.5;\n cursor: not-allowed;\n pointer-events: none;\n}\n\n.button-icon--on-medium {\n --ds-button-unfilled-icon-active-bg: var(--color-interaction-on-medium-background-active);\n --ds-button-unfilled-icon-hover-bg: var(--color-interaction-on-medium-background-hover);\n --ds-button-unfilled-icon-pressed-bg: var(--color-interaction-on-medium-background-pressed);\n --ds-button-unfilled-icon-dot-ring: var(--color-background-medium-neutral);\n --ds-focus-ring-color: var(--color-interaction-on-medium-background-focus);\n}\n\n.button-icon--on-bold {\n --ds-button-unfilled-icon-fg: var(--color-foreground-on-bold-background-secondary);\n --ds-button-unfilled-icon-fg-active: var(--color-foreground-on-bold-background-primary);\n --ds-button-unfilled-icon-active-bg: var(--color-interaction-on-bold-background-active);\n --ds-button-unfilled-icon-hover-bg: var(--color-interaction-on-bold-background-hover);\n --ds-button-unfilled-icon-pressed-bg: var(--color-interaction-on-bold-background-pressed);\n --ds-button-unfilled-icon-dot-ring: var(--color-background-bold-neutral);\n --ds-focus-ring-color: var(--color-interaction-on-bold-background-focus);\n}\n\n.button-icon--on-strong {\n --ds-button-unfilled-icon-fg: var(--color-foreground-on-bold-background-secondary);\n --ds-button-unfilled-icon-fg-active: var(--color-foreground-on-bold-background-primary);\n --ds-button-unfilled-icon-active-bg: var(--color-interaction-on-strong-background-active);\n --ds-button-unfilled-icon-hover-bg: var(--color-interaction-on-strong-background-hover);\n --ds-button-unfilled-icon-pressed-bg: var(--color-interaction-on-strong-background-pressed);\n --ds-button-unfilled-icon-dot-ring: var(--color-background-strong-neutral);\n --ds-focus-ring-color: var(--color-interaction-on-strong-background-focus);\n}\n\n.button-icon--on-always-dark {\n --ds-button-unfilled-icon-fg: var(--color-always-dark-foreground-secondary);\n --ds-button-unfilled-icon-fg-active: var(--color-always-dark-foreground-primary);\n --ds-button-unfilled-icon-active-bg: var(--color-always-dark-interaction-active);\n --ds-button-unfilled-icon-hover-bg: var(--color-always-dark-interaction-hover);\n --ds-button-unfilled-icon-pressed-bg: var(--color-always-dark-interaction-pressed);\n --ds-button-unfilled-icon-dot-ring: var(--color-always-dark-background);\n --ds-focus-ring-color: var(--color-always-dark-interaction-focus);\n}\n\n.button-icon--on-navigation {\n --ds-button-unfilled-icon-fg: var(--color-navigation-foreground-secondary);\n --ds-button-unfilled-icon-fg-active: var(--color-navigation-foreground-primary);\n --ds-button-unfilled-icon-active-bg: var(--color-navigation-interaction-active);\n --ds-button-unfilled-icon-hover-bg: var(--color-navigation-interaction-hover);\n --ds-button-unfilled-icon-pressed-bg: var(--color-navigation-interaction-pressed);\n --ds-button-unfilled-icon-dot-ring: var(--color-navigation-background);\n --ds-focus-ring-color: var(--color-navigation-interaction-focus);\n}\n\n.button-icon__icon-wrap {\n position: relative;\n width: var(--dimension-iconography-md);\n height: var(--dimension-iconography-md);\n display: inline-flex;\n align-items: center;\n justify-content: center;\n flex-shrink: 0;\n z-index: 2;\n}\n\n.button-icon__dot {\n position: absolute;\n top: 0;\n right: 0;\n pointer-events: none;\n}\n","import { Component, Element, Event, EventEmitter, h, Host, Method, Prop } from '@stencil/core';\n\nexport type ButtonUnfilledIconBackground =\n | 'faint'\n | 'medium'\n | 'bold'\n | 'strong'\n | 'always-dark'\n | 'navigation';\n\n@Component({\n tag: 'ds-button-unfilled-icon',\n styleUrl: 'ButtonUnfilledIcon.css',\n scoped: true,\n})\nexport class ButtonUnfilledIcon {\n @Element() el!: HTMLElement;\n\n /** Icon name passed to <ds-icon>. */\n @Prop() icon: string = '';\n\n /** Active/selected visual state. */\n @Prop() isActive: boolean = false;\n\n /** When active, render the active interaction fill. Shell chrome can disable this while keeping active icon colour. */\n @Prop() activeFill: boolean = true;\n\n /** Show a 1px tertiary border without changing the interaction model. */\n @Prop() hasBorder: boolean = false;\n\n /** Show a notification dot at the top-right of the icon zone. */\n @Prop() dot: boolean = false;\n\n /** Disables interaction. */\n @Prop() inactive: boolean = false;\n\n /** Native button type. */\n @Prop() type: 'button' | 'submit' | 'reset' = 'button';\n\n /** Parent surface context — adjusts hover/press/focus colours for coloured backgrounds. */\n @Prop() background: ButtonUnfilledIconBackground | undefined;\n\n @Prop({ attribute: 'aria-label' }) ariaLabel: string = 'action';\n @Prop() controls: string | undefined;\n @Prop() expanded: boolean | undefined;\n @Prop() haspopup: string | undefined;\n @Prop() pressed: boolean | undefined;\n\n @Event() dsClick!: EventEmitter<MouseEvent>;\n @Event() dsChange!: EventEmitter<boolean>;\n\n private buttonEl: HTMLButtonElement | null = null;\n\n @Method()\n async setFocus() {\n this.buttonEl?.focus();\n }\n\n private handleClick = (event: MouseEvent) => {\n if (this.inactive) return;\n this.dsClick.emit(event);\n this.dsChange.emit(!this.isActive);\n };\n\n render() {\n const bg = this.background;\n\n const cls: Record<string, boolean> = {\n 'button-icon': true,\n 'button-icon--active': this.isActive,\n 'button-icon--active-fill': this.isActive && this.activeFill,\n 'button-icon--bordered': this.hasBorder,\n 'button-icon--inactive': this.inactive,\n 'button-icon--on-medium': bg === 'medium',\n 'button-icon--on-bold': bg === 'bold',\n 'button-icon--on-strong': bg === 'strong',\n 'button-icon--on-always-dark': bg === 'always-dark',\n 'button-icon--on-navigation': bg === 'navigation',\n };\n\n return (\n <Host tabIndex={-1}>\n <button\n ref={el => {\n this.buttonEl = el ?? null;\n }}\n type={this.type}\n class={cls}\n disabled={this.inactive}\n aria-label={this.ariaLabel}\n aria-controls={this.controls}\n aria-expanded={this.expanded === undefined ? undefined : String(this.expanded)}\n aria-haspopup={this.haspopup}\n aria-pressed={this.pressed === undefined ? undefined : String(this.pressed)}\n onClick={this.handleClick}\n >\n <span class=\"button-icon__icon-wrap\">\n <ds-icon name={this.icon} size=\"md\" color=\"inherit\" />\n {this.dot && (\n <ds-badge\n class=\"button-icon__dot\"\n variant=\"dot\"\n background=\"var(--ds-button-unfilled-icon-dot-ring)\"\n label=\"\"\n aria-hidden=\"true\"\n />\n )}\n </span>\n </button>\n </Host>\n );\n }\n}\n"],"mappings":"0IAAA,MAAMA,EAAwB,IAAM,q/M,MCevBC,EAAkBC,EAAA,MAAAD,UAAAE,EAL/B,WAAAC,CAAAC,G,4GASUC,KAAAC,KAAe,GAGfD,KAAAE,SAAoB,MAGpBF,KAAAG,WAAsB,KAGtBH,KAAAI,UAAqB,MAGrBJ,KAAAK,IAAe,MAGfL,KAAAM,SAAoB,MAGpBN,KAAAO,KAAsC,SAKXP,KAAAQ,UAAoB,SAS/CR,KAAAS,SAAqC,KAOrCT,KAAAU,YAAeC,IACrB,GAAIX,KAAKM,SAAU,OACnBN,KAAKY,QAAQC,KAAKF,GAClBX,KAAKc,SAASD,MAAMb,KAAKE,SAAS,CAmDrC,CA1DC,cAAMa,GACJf,KAAKS,UAAUO,O,CASjB,MAAAC,GACE,MAAMC,EAAKlB,KAAKmB,WAEhB,MAAMC,EAA+B,CACnC,cAAe,KACf,sBAAuBpB,KAAKE,SAC5B,2BAA4BF,KAAKE,UAAYF,KAAKG,WAClD,wBAAyBH,KAAKI,UAC9B,wBAAyBJ,KAAKM,SAC9B,yBAA0BY,IAAO,SACjC,uBAAwBA,IAAO,OAC/B,yBAA0BA,IAAO,SACjC,8BAA+BA,IAAO,cACtC,6BAA8BA,IAAO,cAGvC,OACEG,EAACC,EAAI,CAAAC,IAAA,2CAACC,UAAU,GACdH,EAAA,UAAAE,IAAA,2CACEE,IAAKC,IACH1B,KAAKS,SAAWiB,GAAM,IAAI,EAE5BnB,KAAMP,KAAKO,KACXoB,MAAOP,EACPQ,SAAU5B,KAAKM,SAAQ,aACXN,KAAKQ,UAAS,gBACXR,KAAK6B,SAAQ,gBACb7B,KAAK8B,WAAaC,UAAYA,UAAYC,OAAOhC,KAAK8B,UAAS,gBAC/D9B,KAAKiC,SAAQ,eACdjC,KAAKkC,UAAYH,UAAYA,UAAYC,OAAOhC,KAAKkC,SACnEC,QAASnC,KAAKU,aAEdW,EAAA,QAAAE,IAAA,2CAAMI,MAAM,0BACVN,EAAA,WAAAE,IAAA,2CAASa,KAAMpC,KAAKC,KAAMoC,KAAK,KAAKC,MAAM,YACzCtC,KAAKK,KACJgB,EAAA,YAAAE,IAAA,2CACEI,MAAM,mBACNY,QAAQ,MACRpB,WAAW,0CACXqB,MAAM,GAAE,cACI,W","ignoreList":[]}
@@ -1,2 +0,0 @@
1
- import{p as o,H as r,c as a,h as n,a as t,t as s}from"./index.js";import{g as e,i}from"./p-BQq26pt9.js";import{d}from"./p-CiEc8Nwx.js";const c=()=>`.sc-ds-tab-group-nav-h{--ds-focus-ring-width:var(--dimension-stroke-width-025, 2px);--ds-focus-ring-offset:var(--dimension-space-025, 2px);--ds-focus-ring-color:var(--color-interaction-focus)}.ds-focus-ring.sc-ds-tab-group-nav:focus-visible{outline:var(--ds-focus-ring-width) solid var(--ds-focus-ring-color);outline-offset:var(--ds-focus-ring-offset)}.ds-focus-ring.ds-focus-ring--visible.sc-ds-tab-group-nav{outline:var(--ds-focus-ring-width) solid var(--ds-focus-ring-color);outline-offset:var(--ds-focus-ring-offset)}.ds-focus-ring-inset.sc-ds-tab-group-nav:focus-visible{outline:none}.ds-focus-ring-inset.sc-ds-tab-group-nav:focus-visible::after{outline:var(--ds-focus-ring-width) solid var(--ds-focus-ring-color);outline-offset:calc(-1 * var(--ds-focus-ring-width))}.ds-focus-ring-inset.ds-focus-ring--visible.sc-ds-tab-group-nav{outline:none}.ds-focus-ring-inset.ds-focus-ring--visible.sc-ds-tab-group-nav::after{outline:var(--ds-focus-ring-width) solid var(--ds-focus-ring-color);outline-offset:calc(-1 * var(--ds-focus-ring-width))}.sc-ds-tab-group-nav-h{display:block}.tab-list.sc-ds-tab-group-nav{display:flex;align-items:center;gap:var(--dimension-space-050);position:relative}.tab-list--vertical.sc-ds-tab-group-nav{flex-direction:column;align-items:stretch}.tab.sc-ds-tab-group-nav{display:flex;align-items:center;justify-content:center;gap:var(--dimension-space-050);padding:var(--dimension-space-075);background-color:transparent;border:none;border-radius:var(--dimension-radius-025);cursor:pointer;-webkit-user-select:none;user-select:none;color:var(--color-foreground-secondary);position:relative;transition:color var(--effect-motion-short-2);flex-shrink:0;height:var(--dimension-size-400);min-width:var(--dimension-size-400);box-sizing:border-box;--_hover-bg:var(--color-interaction-hover);--_pressed-bg:var(--color-interaction-pressed);--_dot:var(--color-foreground-bold-brand);--_dot-ring:var(--color-background-secondary)}.tab.sc-ds-tab-group-nav::after{content:'';position:absolute;inset:0;border-radius:var(--dimension-radius-025, 2px);background:transparent;pointer-events:none;transition:none}.tab.sc-ds-tab-group-nav:hover::after{background:var(--_hover-bg);transition:none}.tab.sc-ds-tab-group-nav:active::after{background:var(--_pressed-bg);transition:none}.tab--selected.sc-ds-tab-group-nav{color:var(--color-foreground-primary)}.tab.sc-ds-tab-group-nav:disabled,.tab[aria-disabled="true"].sc-ds-tab-group-nav{opacity:0.5;cursor:not-allowed;pointer-events:none}.tab-list--vertical.sc-ds-tab-group-nav .tab.sc-ds-tab-group-nav{justify-content:flex-start}.tab__label.sc-ds-tab-group-nav{position:relative;display:inline-flex;align-items:center;padding:0 var(--dimension-space-025);white-space:nowrap}.tab__label--dot.sc-ds-tab-group-nav{padding-right:calc(var(--dimension-space-025) + var(--dimension-space-050))}.tab__dot.sc-ds-tab-group-nav{position:absolute;top:0;right:0;pointer-events:none}.on-medium.sc-ds-tab-group-nav{--_hover-bg:var(--color-interaction-on-medium-background-hover);--_pressed-bg:var(--color-interaction-on-medium-background-pressed);--_dot-ring:var(--color-background-medium-neutral);--ds-focus-ring-color:var(--color-interaction-on-medium-background-focus)}.on-bold.sc-ds-tab-group-nav{--_hover-bg:var(--color-interaction-on-bold-background-hover);--_pressed-bg:var(--color-interaction-on-bold-background-pressed);--_dot-ring:var(--color-background-bold-neutral);--ds-focus-ring-color:var(--color-interaction-on-bold-background-focus);color:var(--color-foreground-on-bold-background-secondary)}.on-bold.tab--selected.sc-ds-tab-group-nav{color:var(--color-foreground-on-bold-background-primary)}.on-strong.sc-ds-tab-group-nav{--_hover-bg:var(--color-interaction-on-strong-background-hover);--_pressed-bg:var(--color-interaction-on-strong-background-pressed);--_dot-ring:var(--color-background-strong-neutral);--ds-focus-ring-color:var(--color-interaction-on-strong-background-focus);color:var(--color-foreground-on-bold-background-secondary)}.on-strong.tab--selected.sc-ds-tab-group-nav{color:var(--color-foreground-on-bold-background-primary)}.on-always-dark.sc-ds-tab-group-nav{--_hover-bg:var(--color-always-dark-interaction-hover);--_pressed-bg:var(--color-always-dark-interaction-pressed);--_dot-ring:var(--color-always-dark-background);--ds-focus-ring-color:var(--color-always-dark-interaction-focus);color:var(--color-always-dark-foreground-secondary)}.on-always-dark.tab--selected.sc-ds-tab-group-nav{color:var(--color-always-dark-foreground-primary)}.on-navigation.sc-ds-tab-group-nav{color:var(--color-navigation-foreground-secondary);--_hover-bg:var(--color-navigation-interaction-hover);--_pressed-bg:var(--color-navigation-interaction-pressed);--_dot:var(--color-navigation-foreground-brand);--_dot-ring:var(--color-navigation-background);--ds-focus-ring-color:var(--color-navigation-interaction-focus)}.on-navigation.tab--selected.sc-ds-tab-group-nav{color:var(--color-navigation-foreground-primary)}.tab-list--on-navigation.sc-ds-tab-group-nav .tab-divider__line.sc-ds-tab-group-nav{background-color:var(--color-navigation-border-tertiary)}.tab-divider.sc-ds-tab-group-nav{display:flex;align-items:center;justify-content:center;box-sizing:border-box;width:var(--dimension-space-100);height:var(--dimension-size-400);padding-block:var(--dimension-space-075);flex-shrink:0;pointer-events:none}.tab-divider__line.sc-ds-tab-group-nav{width:var(--dimension-stroke-width-012);align-self:stretch;background-color:var(--color-border-secondary)}.tab-divider--vertical.sc-ds-tab-group-nav{width:auto;height:var(--dimension-space-100);align-self:stretch;padding-block:0;padding-inline:var(--dimension-space-075)}.tab-divider--vertical.sc-ds-tab-group-nav .tab-divider__line.sc-ds-tab-group-nav{width:auto;height:var(--dimension-stroke-width-012);align-self:stretch}`;const l=o(class o extends r{constructor(o){super();if(o!==false){this.__registerHost()}this.dsChange=a(this,"dsChange");this.value="";this.tabs=[];this.orientation="horizontal"}get selectableTabs(){return e(this.tabs)}selectTab(o,r){this.value=o;this.dsChange.emit(o);if(r?.focus!==false){this.focusTab(o)}}focusTab(o){const r=this.el.querySelector(`[data-tab-id="${o}"]`);r?.focus({preventScroll:true})}onValueChange(o,r){if(r===undefined||o===r)return;requestAnimationFrame((()=>{const r=document.activeElement;if(!r||!this.el.contains(r))return;const a=r.getAttribute("data-tab-id");if(a===o)return;const n=this.el.querySelector(`[data-tab-id="${o}"]`);if(n){n.focus({preventScroll:true})}else{r.blur()}}))}findEnabled(o,r){const a=this.selectableTabs;const n=a.length;for(let t=0;t<n;t++){const s=(o+r*(t+1)+n*(t+1))%n;if(!a[s]?.disabled)return s}return o}handleKeyDown(o){const r=this.selectableTabs;if(!r.length)return;const a=r.findIndex((o=>o.id===this.value));const n=this.orientation==="vertical";let t=null;if(!n){if(o.key==="ArrowRight"){t=this.findEnabled(a,1)}else if(o.key==="ArrowLeft"){t=this.findEnabled(a,-1)}}else{if(o.key==="ArrowDown"){t=this.findEnabled(a,1)}else if(o.key==="ArrowUp"){t=this.findEnabled(a,-1)}}if(o.key==="Home"){const o=r.findIndex((o=>!o.disabled));t=o===-1?null:o}else if(o.key==="End"){const o=[...r].reverse().findIndex((o=>!o.disabled));t=o===-1?null:r.length-1-o}if(t!==null){o.preventDefault();const a=r[t];this.selectTab(a.id)}}getBgClass(){if(!this.background||this.background==="faint")return"";if(this.background==="always-dark")return"on-always-dark";if(this.background==="navigation")return"on-navigation";return`on-${this.background}`}render(){const o=this.getBgClass();const r=this.orientation==="vertical";const a=this.background==="navigation";return n(t,{key:"c68d7a69a3c05c7e85e6ab4cf2be6d54351c55c1",class:"tab-group-nav-host"},n("div",{key:"4bf5c4ebc83ca93c0d7da6783f446de04a74854d",role:"tablist",class:{"tab-list":true,"tab-list--vertical":r,"tab-list--on-navigation":a},"aria-label":this.ariaLabel,"aria-labelledby":this.ariaLabelledby,"aria-orientation":r?"vertical":undefined},this.tabs.map(((a,t)=>{if(i(a)){return n("div",{key:`divider-${t}`,class:{"tab-divider":true,"tab-divider--vertical":r},"aria-hidden":"true"},n("div",{class:"tab-divider__line"}))}const s=a.id===this.value;return n("button",{key:a.id,type:"button",role:"tab","data-tab-id":a.id,class:{tab:true,"tab--selected":s,"ds-focus-ring-inset":true,[o]:!!o},"aria-selected":s,"aria-disabled":a.disabled||undefined,"aria-controls":a.panelId??undefined,disabled:a.disabled,tabIndex:s?0:-1,onClick:()=>!a.disabled&&this.selectTab(a.id)},n("span",{class:{tab__label:true,"tab__label--dot":!!a.dot,[s?"text-body-medium-emphasis":"text-body-medium"]:true}},a.label,a.dot&&n("ds-badge",{class:"tab__dot",variant:"dot",background:"var(--_dot-ring)",label:"","aria-hidden":"true"})))}))))}get el(){return this}static get watchers(){return{value:[{onValueChange:0}]}}static get style(){return c()}},[2,"ds-tab-group-nav",{value:[1025],tabs:[16],background:[1],ariaLabel:[1,"aria-label"],ariaLabelledby:[1,"aria-labelledby"],orientation:[1]},[[0,"keydown","handleKeyDown"]],{value:[{onValueChange:0}]}]);function u(){if(typeof customElements==="undefined"){return}const o=["ds-tab-group-nav","ds-badge"];o.forEach((o=>{switch(o){case"ds-tab-group-nav":if(!customElements.get(s(o))){customElements.define(s(o),l)}break;case"ds-badge":if(!customElements.get(s(o))){d()}break}}))}u();export{l as T,u as d};
2
- //# sourceMappingURL=p-CZpCmCiq.js.map
@@ -1 +0,0 @@
1
- {"version":3,"names":["tabGroupNavCss","TabGroupNav","__stencil_proxyCustomElement","HTMLElement","constructor","registerHost","this","value","tabs","orientation","selectableTabs","getSelectableTabs","selectTab","id","options","dsChange","emit","focus","focusTab","btn","el","querySelector","preventScroll","onValueChange","next","prev","undefined","requestAnimationFrame","active","document","activeElement","contains","activeId","getAttribute","nextTab","blur","findEnabled","from","step","len","length","n","i","disabled","handleKeyDown","e","currentIdx","findIndex","t","isVertical","nextIdx","key","first","lastEnabled","reverse","preventDefault","getBgClass","background","render","bgClass","isNavigation","h","Host","class","role","ariaLabel","ariaLabelledby","map","tab","index","isTabDivider","isSelected","type","panelId","tabIndex","onClick","tab__label","dot","label","variant"],"sources":["src/wc/components/TabGroupNav/TabGroupNav.css?tag=ds-tab-group-nav&encapsulation=scoped","src/wc/components/TabGroupNav/TabGroupNav.tsx"],"sourcesContent":["@import '../../utils/focus-ring.css';\n\n:host {\n display: block;\n}\n\n.tab-list {\n display: flex;\n align-items: center;\n gap: var(--dimension-space-050);\n position: relative;\n}\n\n/* Vertical orientation */\n.tab-list--vertical {\n flex-direction: column;\n align-items: stretch;\n}\n\n.tab {\n display: flex;\n align-items: center;\n justify-content: center;\n gap: var(--dimension-space-050);\n padding: var(--dimension-space-075);\n background-color: transparent;\n border: none;\n border-radius: var(--dimension-radius-025);\n cursor: pointer;\n -webkit-user-select: none;\n user-select: none;\n color: var(--color-foreground-secondary);\n position: relative;\n /* Only transition color — hover bg is instant (see ::after below) */\n transition: color var(--effect-motion-short-2);\n flex-shrink: 0;\n height: var(--dimension-size-400);\n min-width: var(--dimension-size-400);\n box-sizing: border-box;\n --_hover-bg: var(--color-interaction-hover);\n --_pressed-bg: var(--color-interaction-pressed);\n --_dot: var(--color-foreground-bold-brand);\n --_dot-ring: var(--color-background-secondary);\n}\n\n/* Interaction layer — same pattern as PanelNav */\n.tab::after {\n content: '';\n position: absolute;\n inset: 0;\n border-radius: var(--dimension-radius-025, 2px);\n background: transparent;\n pointer-events: none;\n transition: none;\n}\n\n.tab:hover::after { background: var(--_hover-bg); transition: none; }\n.tab:active::after { background: var(--_pressed-bg); transition: none; }\n\n.tab--selected { color: var(--color-foreground-primary); }\n\n.tab:disabled,\n.tab[aria-disabled=\"true\"] {\n opacity: 0.5;\n cursor: not-allowed;\n pointer-events: none;\n}\n\n/* Vertical tabs read as a list — left-align the label */\n.tab-list--vertical .tab { justify-content: flex-start; }\n\n/* Label — dot anchors top-right like BarNav action icon zone */\n.tab__label {\n position: relative;\n display: inline-flex;\n align-items: center;\n padding: 0 var(--dimension-space-025);\n white-space: nowrap;\n}\n\n/* Clearance for top-right dot — slightly less than space-025 + space-075 */\n.tab__label--dot {\n padding-right: calc(var(--dimension-space-025) + var(--dimension-space-050));\n}\n\n/* Notification dot — top-right corner of the label zone */\n.tab__dot {\n position: absolute;\n top: 0;\n right: 0;\n pointer-events: none;\n}\n\n/* Background context */\n.on-medium {\n --_hover-bg: var(--color-interaction-on-medium-background-hover);\n --_pressed-bg: var(--color-interaction-on-medium-background-pressed);\n --_dot-ring: var(--color-background-medium-neutral);\n --ds-focus-ring-color: var(--color-interaction-on-medium-background-focus);\n}\n\n.on-bold {\n --_hover-bg: var(--color-interaction-on-bold-background-hover);\n --_pressed-bg: var(--color-interaction-on-bold-background-pressed);\n --_dot-ring: var(--color-background-bold-neutral);\n --ds-focus-ring-color: var(--color-interaction-on-bold-background-focus);\n color: var(--color-foreground-on-bold-background-secondary);\n}\n\n.on-bold.tab--selected { color: var(--color-foreground-on-bold-background-primary); }\n\n.on-strong {\n --_hover-bg: var(--color-interaction-on-strong-background-hover);\n --_pressed-bg: var(--color-interaction-on-strong-background-pressed);\n --_dot-ring: var(--color-background-strong-neutral);\n --ds-focus-ring-color: var(--color-interaction-on-strong-background-focus);\n color: var(--color-foreground-on-bold-background-secondary);\n}\n\n.on-strong.tab--selected { color: var(--color-foreground-on-bold-background-primary); }\n\n.on-always-dark {\n --_hover-bg: var(--color-always-dark-interaction-hover);\n --_pressed-bg: var(--color-always-dark-interaction-pressed);\n --_dot-ring: var(--color-always-dark-background);\n --ds-focus-ring-color: var(--color-always-dark-interaction-focus);\n color: var(--color-always-dark-foreground-secondary);\n}\n\n.on-always-dark.tab--selected { color: var(--color-always-dark-foreground-primary); }\n\n.on-navigation {\n color: var(--color-navigation-foreground-secondary);\n --_hover-bg: var(--color-navigation-interaction-hover);\n --_pressed-bg: var(--color-navigation-interaction-pressed);\n --_dot: var(--color-navigation-foreground-brand);\n --_dot-ring: var(--color-navigation-background);\n --ds-focus-ring-color: var(--color-navigation-interaction-focus);\n}\n\n.on-navigation.tab--selected { color: var(--color-navigation-foreground-primary); }\n\n.tab-list--on-navigation .tab-divider__line {\n background-color: var(--color-navigation-border-tertiary);\n}\n\n/* In-tablist visual group separator — not focusable, skipped by keyboard nav */\n.tab-divider {\n display: flex;\n align-items: center;\n justify-content: center;\n box-sizing: border-box;\n width: var(--dimension-space-100);\n height: var(--dimension-size-400);\n padding-block: var(--dimension-space-075);\n flex-shrink: 0;\n pointer-events: none;\n}\n\n.tab-divider__line {\n width: var(--dimension-stroke-width-012);\n align-self: stretch;\n background-color: var(--color-border-secondary);\n}\n\n.tab-divider--vertical {\n width: auto;\n height: var(--dimension-space-100);\n align-self: stretch;\n padding-block: 0;\n padding-inline: var(--dimension-space-075);\n}\n\n.tab-divider--vertical .tab-divider__line {\n width: auto;\n height: var(--dimension-stroke-width-012);\n align-self: stretch;\n}\n","import { Component, Prop, Event, EventEmitter, Element, Listen, Watch, h, Host } from '@stencil/core';\nimport {\n getSelectableTabs,\n isTabDivider,\n type TabItem,\n type TabItemTab,\n} from '../TabGroup/tab-item-utils';\n\nexport type TabGroupNavBackground = 'faint' | 'medium' | 'bold' | 'strong' | 'always-dark' | 'navigation';\n\n@Component({\n tag: 'ds-tab-group-nav',\n styleUrl: 'TabGroupNav.css',\n scoped: true,\n})\nexport class TabGroupNav {\n @Element() el!: HTMLElement;\n\n @Prop({ mutable: true }) value: string = '';\n @Prop() tabs: TabItem[] = [];\n @Prop() background: TabGroupNavBackground | undefined;\n @Prop({ attribute: 'aria-label' }) ariaLabel: string | undefined;\n @Prop({ attribute: 'aria-labelledby' }) ariaLabelledby: string | undefined;\n @Prop() orientation: 'horizontal' | 'vertical' = 'horizontal';\n\n @Event() dsChange!: EventEmitter<string>;\n\n private get selectableTabs(): TabItemTab[] {\n return getSelectableTabs(this.tabs);\n }\n\n private selectTab(id: string, options?: { focus?: boolean }) {\n this.value = id;\n this.dsChange.emit(id);\n if (options?.focus !== false) {\n this.focusTab(id);\n }\n }\n\n private focusTab(id: string) {\n const btn = this.el.querySelector(`[data-tab-id=\"${id}\"]`) as HTMLElement | null;\n btn?.focus({ preventScroll: true });\n }\n\n /** Keep focus on the selected tab when value changes externally (e.g. BarNav menu). */\n @Watch('value')\n onValueChange(next: string, prev: string | undefined) {\n if (prev === undefined || next === prev) return;\n\n requestAnimationFrame(() => {\n const active = document.activeElement as HTMLElement | null;\n if (!active || !this.el.contains(active)) return;\n\n const activeId = active.getAttribute('data-tab-id');\n if (activeId === next) return;\n\n const nextTab = this.el.querySelector(`[data-tab-id=\"${next}\"]`) as HTMLElement | null;\n if (nextTab) {\n nextTab.focus({ preventScroll: true });\n } else {\n active.blur();\n }\n });\n }\n\n /**\n * Find the next non-disabled tab index relative to `from`, stepping by `step`.\n * Returns `from` unchanged if no other enabled tab exists.\n */\n private findEnabled(from: number, step: 1 | -1): number {\n const tabs = this.selectableTabs;\n const len = tabs.length;\n for (let n = 0; n < len; n++) {\n const i = (from + step * (n + 1) + len * (n + 1)) % len;\n if (!tabs[i]?.disabled) return i;\n }\n return from;\n }\n\n @Listen('keydown')\n handleKeyDown(e: KeyboardEvent) {\n const tabs = this.selectableTabs;\n if (!tabs.length) return;\n\n const currentIdx = tabs.findIndex(t => t.id === this.value);\n const isVertical = this.orientation === 'vertical';\n\n let nextIdx: number | null = null;\n\n if (!isVertical) {\n if (e.key === 'ArrowRight') {\n nextIdx = this.findEnabled(currentIdx, 1);\n } else if (e.key === 'ArrowLeft') {\n nextIdx = this.findEnabled(currentIdx, -1);\n }\n } else {\n if (e.key === 'ArrowDown') {\n nextIdx = this.findEnabled(currentIdx, 1);\n } else if (e.key === 'ArrowUp') {\n nextIdx = this.findEnabled(currentIdx, -1);\n }\n }\n\n if (e.key === 'Home') {\n const first = tabs.findIndex(t => !t.disabled);\n nextIdx = first === -1 ? null : first;\n } else if (e.key === 'End') {\n const lastEnabled = [...tabs].reverse().findIndex(t => !t.disabled);\n nextIdx = lastEnabled === -1 ? null : tabs.length - 1 - lastEnabled;\n }\n\n if (nextIdx !== null) {\n e.preventDefault();\n const next = tabs[nextIdx];\n this.selectTab(next.id);\n }\n }\n\n private getBgClass(): string {\n if (!this.background || this.background === 'faint') return '';\n if (this.background === 'always-dark') return 'on-always-dark';\n if (this.background === 'navigation') return 'on-navigation';\n return `on-${this.background}`;\n }\n\n render() {\n const bgClass = this.getBgClass();\n const isVertical = this.orientation === 'vertical';\n const isNavigation = this.background === 'navigation';\n\n return (\n <Host class=\"tab-group-nav-host\">\n <div\n role=\"tablist\"\n class={{\n 'tab-list': true,\n 'tab-list--vertical': isVertical,\n 'tab-list--on-navigation': isNavigation,\n }}\n aria-label={this.ariaLabel}\n aria-labelledby={this.ariaLabelledby}\n aria-orientation={isVertical ? 'vertical' : undefined}\n >\n {this.tabs.map((tab, index) => {\n if (isTabDivider(tab)) {\n return (\n <div\n key={`divider-${index}`}\n class={{\n 'tab-divider': true,\n 'tab-divider--vertical': isVertical,\n }}\n aria-hidden=\"true\"\n >\n <div class=\"tab-divider__line\" />\n </div>\n );\n }\n\n const isSelected = tab.id === this.value;\n return (\n <button\n key={tab.id}\n type=\"button\"\n role=\"tab\"\n data-tab-id={tab.id}\n class={{\n tab: true,\n 'tab--selected': isSelected,\n 'ds-focus-ring-inset': true,\n [bgClass]: !!bgClass,\n }}\n aria-selected={isSelected}\n aria-disabled={tab.disabled || undefined}\n aria-controls={tab.panelId ?? undefined}\n disabled={tab.disabled}\n tabIndex={isSelected ? 0 : -1}\n onClick={() => !tab.disabled && this.selectTab(tab.id)}\n >\n <span class={{\n tab__label: true,\n 'tab__label--dot': !!tab.dot,\n [isSelected ? 'text-body-medium-emphasis' : 'text-body-medium']: true,\n }}>\n {tab.label}\n {tab.dot && (\n <ds-badge\n class=\"tab__dot\"\n variant=\"dot\"\n background=\"var(--_dot-ring)\"\n label=\"\"\n aria-hidden=\"true\"\n />\n )}\n </span>\n </button>\n );\n })}\n </div>\n </Host>\n );\n }\n}\n"],"mappings":"uIAAA,MAAMA,EAAiB,IAAM,isL,MCehBC,EAAWC,EAAA,MAAAD,UAAAE,EALxB,WAAAC,CAAAC,G,6EAQ2BC,KAAAC,MAAgB,GACjCD,KAAAE,KAAkB,GAIlBF,KAAAG,YAAyC,YAmLlD,CA/KC,kBAAYC,GACV,OAAOC,EAAkBL,KAAKE,K,CAGxB,SAAAI,CAAUC,EAAYC,GAC5BR,KAAKC,MAAQM,EACbP,KAAKS,SAASC,KAAKH,GACnB,GAAIC,GAASG,QAAU,MAAO,CAC5BX,KAAKY,SAASL,E,EAIV,QAAAK,CAASL,GACf,MAAMM,EAAMb,KAAKc,GAAGC,cAAc,iBAAiBR,OACnDM,GAAKF,MAAM,CAAEK,cAAe,M,CAK9B,aAAAC,CAAcC,EAAcC,GAC1B,GAAIA,IAASC,WAAaF,IAASC,EAAM,OAEzCE,uBAAsB,KACpB,MAAMC,EAASC,SAASC,cACxB,IAAKF,IAAWtB,KAAKc,GAAGW,SAASH,GAAS,OAE1C,MAAMI,EAAWJ,EAAOK,aAAa,eACrC,GAAID,IAAaR,EAAM,OAEvB,MAAMU,EAAU5B,KAAKc,GAAGC,cAAc,iBAAiBG,OACvD,GAAIU,EAAS,CACXA,EAAQjB,MAAM,CAAEK,cAAe,M,KAC1B,CACLM,EAAOO,M,KASL,WAAAC,CAAYC,EAAcC,GAChC,MAAM9B,EAAOF,KAAKI,eAClB,MAAM6B,EAAM/B,EAAKgC,OACjB,IAAK,IAAIC,EAAI,EAAGA,EAAIF,EAAKE,IAAK,CAC5B,MAAMC,GAAKL,EAAOC,GAAQG,EAAI,GAAKF,GAAOE,EAAI,IAAMF,EACpD,IAAK/B,EAAKkC,IAAIC,SAAU,OAAOD,C,CAEjC,OAAOL,C,CAIT,aAAAO,CAAcC,GACZ,MAAMrC,EAAOF,KAAKI,eAClB,IAAKF,EAAKgC,OAAQ,OAElB,MAAMM,EAAatC,EAAKuC,WAAUC,GAAKA,EAAEnC,KAAOP,KAAKC,QACrD,MAAM0C,EAAa3C,KAAKG,cAAgB,WAExC,IAAIyC,EAAyB,KAE7B,IAAKD,EAAY,CACf,GAAIJ,EAAEM,MAAQ,aAAc,CAC1BD,EAAU5C,KAAK8B,YAAYU,EAAY,E,MAClC,GAAID,EAAEM,MAAQ,YAAa,CAChCD,EAAU5C,KAAK8B,YAAYU,GAAY,E,MAEpC,CACL,GAAID,EAAEM,MAAQ,YAAa,CACzBD,EAAU5C,KAAK8B,YAAYU,EAAY,E,MAClC,GAAID,EAAEM,MAAQ,UAAW,CAC9BD,EAAU5C,KAAK8B,YAAYU,GAAY,E,EAI3C,GAAID,EAAEM,MAAQ,OAAQ,CACpB,MAAMC,EAAQ5C,EAAKuC,WAAUC,IAAMA,EAAEL,WACrCO,EAAUE,KAAU,EAAK,KAAOA,C,MAC3B,GAAIP,EAAEM,MAAQ,MAAO,CAC1B,MAAME,EAAc,IAAI7C,GAAM8C,UAAUP,WAAUC,IAAMA,EAAEL,WAC1DO,EAAUG,KAAgB,EAAK,KAAO7C,EAAKgC,OAAS,EAAIa,C,CAG1D,GAAIH,IAAY,KAAM,CACpBL,EAAEU,iBACF,MAAM/B,EAAOhB,EAAK0C,GAClB5C,KAAKM,UAAUY,EAAKX,G,EAIhB,UAAA2C,GACN,IAAKlD,KAAKmD,YAAcnD,KAAKmD,aAAe,QAAS,MAAO,GAC5D,GAAInD,KAAKmD,aAAe,cAAe,MAAO,iBAC9C,GAAInD,KAAKmD,aAAe,aAAc,MAAO,gBAC7C,MAAO,MAAMnD,KAAKmD,Y,CAGpB,MAAAC,GACE,MAAMC,EAAUrD,KAAKkD,aACrB,MAAMP,EAAa3C,KAAKG,cAAgB,WACxC,MAAMmD,EAAetD,KAAKmD,aAAe,aAEzC,OACEI,EAACC,EAAI,CAAAX,IAAA,2CAACY,MAAM,sBACVF,EAAA,OAAAV,IAAA,2CACEa,KAAK,UACLD,MAAO,CACL,WAAY,KACZ,qBAAsBd,EACtB,0BAA2BW,GAC5B,aACWtD,KAAK2D,UAAS,kBACT3D,KAAK4D,eAAc,mBAClBjB,EAAa,WAAavB,WAE3CpB,KAAKE,KAAK2D,KAAI,CAACC,EAAKC,KACnB,GAAIC,EAAaF,GAAM,CACrB,OACEP,EAAA,OACEV,IAAK,WAAWkB,IAChBN,MAAO,CACL,cAAe,KACf,wBAAyBd,GAC1B,cACW,QAEZY,EAAA,OAAKE,MAAM,sB,CAKjB,MAAMQ,EAAaH,EAAIvD,KAAOP,KAAKC,MACnC,OACEsD,EAAA,UACEV,IAAKiB,EAAIvD,GACT2D,KAAK,SACLR,KAAK,MAAK,cACGI,EAAIvD,GACjBkD,MAAO,CACLK,IAAK,KACL,gBAAiBG,EACjB,sBAAuB,KACvBZ,CAACA,KAAYA,GACd,gBACcY,EAAU,gBACVH,EAAIzB,UAAYjB,UAAS,gBACzB0C,EAAIK,SAAW/C,UAC9BiB,SAAUyB,EAAIzB,SACd+B,SAAUH,EAAa,GAAI,EAC3BI,QAAS,KAAOP,EAAIzB,UAAYrC,KAAKM,UAAUwD,EAAIvD,KAEnDgD,EAAA,QAAME,MAAO,CACXa,WAAY,KACZ,oBAAqBR,EAAIS,IACzB,CAACN,EAAa,4BAA8B,oBAAqB,OAEhEH,EAAIU,MACJV,EAAIS,KACHhB,EAAA,YACEE,MAAM,WACNgB,QAAQ,MACRtB,WAAW,mBACXqB,MAAM,GAAE,cACI,UAIX,K","ignoreList":[]}