@ds-mo/ui 2.2.1 → 2.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/.build-stamp +1 -1
- package/dist/components/ds-app-shell.js +1 -1
- package/dist/components/ds-app-shell.js.map +1 -1
- package/dist/components/ds-bar-nav.js +1 -1
- package/dist/components/ds-bar-nav.js.map +1 -1
- package/dist/components/ds-button-unfilled-icon.js +1 -1
- package/dist/components/ds-checkbox.js +1 -1
- package/dist/components/ds-field.js +1 -1
- package/dist/components/ds-field.js.map +1 -1
- package/dist/components/ds-panel-nav.js +1 -1
- package/dist/components/ds-panel-nav.js.map +1 -1
- package/dist/components/ds-panel-tools.js +1 -1
- package/dist/components/ds-panel-tools.js.map +1 -1
- package/dist/components/ds-slider.js +1 -1
- package/dist/components/ds-tab-group-nav.js +1 -1
- package/dist/components/ds-toggle.js +1 -1
- package/dist/components/index.js +1 -1
- package/dist/components/index.js.map +1 -1
- package/dist/components/{p-Bl-GTn9s.js → p-BTndnbR_.js} +2 -2
- package/dist/components/p-BTndnbR_.js.map +1 -0
- package/dist/components/p-D4zKc1RW.js +2 -0
- package/dist/components/p-D4zKc1RW.js.map +1 -0
- package/dist/types/components/AppShell/AppShell.d.ts +3 -0
- package/dist/types/components/BarNav/BarNav.d.ts +4 -0
- package/dist/types/components/ButtonUnfilledIcon/ButtonUnfilledIcon.d.ts +5 -0
- package/dist/types/components/PanelNav/PanelNav.d.ts +8 -2
- package/dist/types/components/PanelTools/PanelTools.d.ts +8 -0
- package/dist/types/components/PanelTools/panel-tools-utils.d.ts +7 -1
- package/dist/types/components/TabGroupNav/TabGroupNav.d.ts +22 -3
- package/dist/types/components.d.ts +62 -0
- package/dist/types/nav/shell-shortcuts.d.ts +10 -0
- package/package.json +2 -2
- package/src/angular/proxies.ts +17 -9
- package/src/react/ds-tab-group-nav.ts +8 -2
- package/src/wc/components/AppShell/AppShell.tsx +43 -8
- package/src/wc/components/BarNav/BarNav.tsx +33 -2
- package/src/wc/components/ButtonUnfilledIcon/ButtonUnfilledIcon.tsx +7 -0
- package/src/wc/components/PanelNav/PanelNav.tsx +116 -22
- package/src/wc/components/PanelTools/PanelTools.tsx +91 -23
- package/src/wc/components/PanelTools/panel-tools-utils.ts +13 -1
- package/src/wc/components/TabGroupNav/TabGroupNav.tsx +164 -22
- package/src/wc/components.d.ts +62 -0
- package/src/wc/nav/shell-shortcuts.ts +63 -0
- package/dist/components/p-Bl-GTn9s.js.map +0 -1
- package/dist/components/p-CZpCmCiq.js +0 -2
- 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
|
-
|
|
33
|
-
this.value
|
|
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
|
-
|
|
86
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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 (
|
|
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.
|
|
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={
|
|
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,
|
package/src/wc/components.d.ts
CHANGED
|
@@ -118,6 +118,11 @@ export namespace Components {
|
|
|
118
118
|
* @default 'dashboard'
|
|
119
119
|
*/
|
|
120
120
|
"navStyle": NavChromeStyle;
|
|
121
|
+
/**
|
|
122
|
+
* When `true` (default), registers global shell keyboard shortcuts. Tool chords (⌘/Ctrl+K/A/S/M/N) toggle their drawer open and closed; ⌘/Ctrl+[ toggles panel nav; ⌘/Ctrl+] closes any open tool drawer.
|
|
123
|
+
* @default true
|
|
124
|
+
*/
|
|
125
|
+
"shortcutsEnabled": boolean;
|
|
121
126
|
}
|
|
122
127
|
interface DsBadge {
|
|
123
128
|
/**
|
|
@@ -332,6 +337,10 @@ export namespace Components {
|
|
|
332
337
|
*/
|
|
333
338
|
"dot": boolean;
|
|
334
339
|
"expanded": boolean | undefined;
|
|
340
|
+
/**
|
|
341
|
+
* Native `tabindex` for roving keyboard groups in shell chrome. Omit for the default button tab stop (`0`).
|
|
342
|
+
*/
|
|
343
|
+
"focusTabIndex"?: number;
|
|
335
344
|
/**
|
|
336
345
|
* Show a 1px tertiary border without changing the interaction model.
|
|
337
346
|
* @default false
|
|
@@ -693,6 +702,10 @@ export namespace Components {
|
|
|
693
702
|
* @default ''
|
|
694
703
|
*/
|
|
695
704
|
"storageKey": string;
|
|
705
|
+
/**
|
|
706
|
+
* Toggle expanded/collapsed panel nav — used by shell keyboard shortcuts.
|
|
707
|
+
*/
|
|
708
|
+
"toggleCollapsed": () => Promise<void>;
|
|
696
709
|
/**
|
|
697
710
|
* Single character shown in the collapsed avatar
|
|
698
711
|
* @default ''
|
|
@@ -705,11 +718,19 @@ export namespace Components {
|
|
|
705
718
|
"userName": string;
|
|
706
719
|
}
|
|
707
720
|
interface DsPanelTools {
|
|
721
|
+
/**
|
|
722
|
+
* Toggle any rail tool open/closed — shell shortcuts ⌘/Ctrl+K/A/S/M/N call this.
|
|
723
|
+
*/
|
|
724
|
+
"activateTool": (id: PanelToolsToolId) => Promise<void>;
|
|
708
725
|
/**
|
|
709
726
|
* Active tool view — `search`, `agents`, `messages`, `stacks`, or `activity`.
|
|
710
727
|
* @default ''
|
|
711
728
|
*/
|
|
712
729
|
"activeTool": PanelToolsToolId | '';
|
|
730
|
+
/**
|
|
731
|
+
* Close the tools drawer when open — used by shell keyboard shortcuts.
|
|
732
|
+
*/
|
|
733
|
+
"closeDrawer": () => Promise<void>;
|
|
713
734
|
/**
|
|
714
735
|
* Rail items rendered in the right column. Set via JS property: `el.items = [...]`. Replace the array reference to update.
|
|
715
736
|
* @default []
|
|
@@ -890,10 +911,23 @@ export namespace Components {
|
|
|
890
911
|
"ariaLabel": string | undefined;
|
|
891
912
|
"ariaLabelledby": string | undefined;
|
|
892
913
|
"background": TabGroupNavBackground | undefined;
|
|
914
|
+
"focusFirstTab": () => Promise<void>;
|
|
915
|
+
"focusLastTab": () => Promise<void>;
|
|
916
|
+
"focusTab": (id: string) => Promise<void>;
|
|
893
917
|
/**
|
|
894
918
|
* @default 'horizontal'
|
|
895
919
|
*/
|
|
896
920
|
"orientation": 'horizontal' | 'vertical';
|
|
921
|
+
/**
|
|
922
|
+
* When `false`, every tab uses `tabindex="-1"` (another chrome control owns the tab stop).
|
|
923
|
+
* @default true
|
|
924
|
+
*/
|
|
925
|
+
"rovingEnabled": boolean;
|
|
926
|
+
/**
|
|
927
|
+
* 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.
|
|
928
|
+
* @default true
|
|
929
|
+
*/
|
|
930
|
+
"selectionFollowsFocus": boolean;
|
|
897
931
|
/**
|
|
898
932
|
* @default []
|
|
899
933
|
*/
|
|
@@ -1627,6 +1661,7 @@ declare global {
|
|
|
1627
1661
|
};
|
|
1628
1662
|
interface HTMLDsTabGroupNavElementEventMap {
|
|
1629
1663
|
"dsChange": string;
|
|
1664
|
+
"dsRovingExit": 'start' | 'end';
|
|
1630
1665
|
}
|
|
1631
1666
|
interface HTMLDsTabGroupNavElement extends Components.DsTabGroupNav, HTMLStencilElement {
|
|
1632
1667
|
addEventListener<K extends keyof HTMLDsTabGroupNavElementEventMap>(type: K, listener: (this: HTMLDsTabGroupNavElement, ev: DsTabGroupNavCustomEvent<HTMLDsTabGroupNavElementEventMap[K]>) => any, options?: boolean | AddEventListenerOptions): void;
|
|
@@ -1820,6 +1855,11 @@ declare namespace LocalJSX {
|
|
|
1820
1855
|
* @default 'dashboard'
|
|
1821
1856
|
*/
|
|
1822
1857
|
"navStyle"?: NavChromeStyle;
|
|
1858
|
+
/**
|
|
1859
|
+
* When `true` (default), registers global shell keyboard shortcuts. Tool chords (⌘/Ctrl+K/A/S/M/N) toggle their drawer open and closed; ⌘/Ctrl+[ toggles panel nav; ⌘/Ctrl+] closes any open tool drawer.
|
|
1860
|
+
* @default true
|
|
1861
|
+
*/
|
|
1862
|
+
"shortcutsEnabled"?: boolean;
|
|
1823
1863
|
}
|
|
1824
1864
|
interface DsBadge {
|
|
1825
1865
|
/**
|
|
@@ -2047,6 +2087,10 @@ declare namespace LocalJSX {
|
|
|
2047
2087
|
*/
|
|
2048
2088
|
"dot"?: boolean;
|
|
2049
2089
|
"expanded"?: boolean | undefined;
|
|
2090
|
+
/**
|
|
2091
|
+
* Native `tabindex` for roving keyboard groups in shell chrome. Omit for the default button tab stop (`0`).
|
|
2092
|
+
*/
|
|
2093
|
+
"focusTabIndex"?: number;
|
|
2050
2094
|
/**
|
|
2051
2095
|
* Show a 1px tertiary border without changing the interaction model.
|
|
2052
2096
|
* @default false
|
|
@@ -2665,10 +2709,24 @@ declare namespace LocalJSX {
|
|
|
2665
2709
|
"ariaLabelledby"?: string | undefined;
|
|
2666
2710
|
"background"?: TabGroupNavBackground | undefined;
|
|
2667
2711
|
"onDsChange"?: (event: DsTabGroupNavCustomEvent<string>) => void;
|
|
2712
|
+
/**
|
|
2713
|
+
* Fired when arrow navigation reaches the first/last tab in manual selection mode.
|
|
2714
|
+
*/
|
|
2715
|
+
"onDsRovingExit"?: (event: DsTabGroupNavCustomEvent<'start' | 'end'>) => void;
|
|
2668
2716
|
/**
|
|
2669
2717
|
* @default 'horizontal'
|
|
2670
2718
|
*/
|
|
2671
2719
|
"orientation"?: 'horizontal' | 'vertical';
|
|
2720
|
+
/**
|
|
2721
|
+
* When `false`, every tab uses `tabindex="-1"` (another chrome control owns the tab stop).
|
|
2722
|
+
* @default true
|
|
2723
|
+
*/
|
|
2724
|
+
"rovingEnabled"?: boolean;
|
|
2725
|
+
/**
|
|
2726
|
+
* 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.
|
|
2727
|
+
* @default true
|
|
2728
|
+
*/
|
|
2729
|
+
"selectionFollowsFocus"?: boolean;
|
|
2672
2730
|
/**
|
|
2673
2731
|
* @default []
|
|
2674
2732
|
*/
|
|
@@ -2906,6 +2964,7 @@ declare namespace LocalJSX {
|
|
|
2906
2964
|
"gradient": boolean;
|
|
2907
2965
|
"grid": boolean;
|
|
2908
2966
|
"gradientSrc": string;
|
|
2967
|
+
"shortcutsEnabled": boolean;
|
|
2909
2968
|
}
|
|
2910
2969
|
interface DsBadgeAttributes {
|
|
2911
2970
|
"variant": BadgeVariant;
|
|
@@ -2977,6 +3036,7 @@ declare namespace LocalJSX {
|
|
|
2977
3036
|
"expanded": boolean | undefined;
|
|
2978
3037
|
"haspopup": string | undefined;
|
|
2979
3038
|
"pressed": boolean | undefined;
|
|
3039
|
+
"focusTabIndex": number;
|
|
2980
3040
|
}
|
|
2981
3041
|
interface DsCardAttributes {
|
|
2982
3042
|
"elevation": CardElevation;
|
|
@@ -3153,6 +3213,8 @@ declare namespace LocalJSX {
|
|
|
3153
3213
|
"ariaLabel": string | undefined;
|
|
3154
3214
|
"ariaLabelledby": string | undefined;
|
|
3155
3215
|
"orientation": 'horizontal' | 'vertical';
|
|
3216
|
+
"selectionFollowsFocus": boolean;
|
|
3217
|
+
"rovingEnabled": boolean;
|
|
3156
3218
|
}
|
|
3157
3219
|
interface DsTableAttributes {
|
|
3158
3220
|
"loading": boolean;
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import type { PanelToolsToolId } from '../components/PanelTools/panel-tools-types';
|
|
2
|
+
|
|
3
|
+
export type ShellShortcutAction =
|
|
4
|
+
| 'toggle-panel-nav'
|
|
5
|
+
| 'close-panel-tools'
|
|
6
|
+
| `open-tool:${PanelToolsToolId}`;
|
|
7
|
+
|
|
8
|
+
const TOOL_SHORTCUT_KEYS: Record<string, PanelToolsToolId> = {
|
|
9
|
+
k: 'search',
|
|
10
|
+
a: 'agents',
|
|
11
|
+
s: 'stacks',
|
|
12
|
+
m: 'messages',
|
|
13
|
+
n: 'activity',
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
/** Tool shortcut chords (⌘/Ctrl+K/A/S/M/N) toggle open/closed via `ds-panel-tools.activateTool`. */
|
|
17
|
+
|
|
18
|
+
/** True when Cmd (macOS) or Ctrl (Windows/Linux) is held without Alt/Shift. */
|
|
19
|
+
export function isShellShortcutModifier(
|
|
20
|
+
e: Pick<KeyboardEvent, 'metaKey' | 'ctrlKey' | 'altKey' | 'shiftKey'>,
|
|
21
|
+
): boolean {
|
|
22
|
+
return (e.metaKey || e.ctrlKey) && !e.altKey && !e.shiftKey;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/** Skip shell shortcuts while typing in an editable control. */
|
|
26
|
+
export function isEditableShortcutTarget(target: EventTarget | null): boolean {
|
|
27
|
+
if (!target || typeof target !== 'object') return false;
|
|
28
|
+
if (!('closest' in target) || typeof target.closest !== 'function') return false;
|
|
29
|
+
|
|
30
|
+
const el = target as Element & { isContentEditable?: boolean };
|
|
31
|
+
|
|
32
|
+
const editable = el.closest(
|
|
33
|
+
'input, textarea, select, [contenteditable=""], [contenteditable="true"], [contenteditable="plaintext-only"]',
|
|
34
|
+
);
|
|
35
|
+
if (editable) return true;
|
|
36
|
+
|
|
37
|
+
const role = el.getAttribute('role');
|
|
38
|
+
if (role === 'textbox' || role === 'combobox' || role === 'searchbox') return true;
|
|
39
|
+
|
|
40
|
+
return Boolean(el.isContentEditable);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function normalizedShortcutKey(e: Pick<KeyboardEvent, 'key' | 'code'>): string {
|
|
44
|
+
if (e.key === '[' || e.code === 'BracketLeft') return '[';
|
|
45
|
+
if (e.key === ']' || e.code === 'BracketRight') return ']';
|
|
46
|
+
return e.key.length === 1 ? e.key.toLowerCase() : e.key.toLowerCase();
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/** Resolve a shell chrome shortcut, or `null` when the chord is not handled. */
|
|
50
|
+
export function resolveShellShortcut(
|
|
51
|
+
e: Pick<KeyboardEvent, 'key' | 'code' | 'metaKey' | 'ctrlKey' | 'altKey' | 'shiftKey'>,
|
|
52
|
+
): ShellShortcutAction | null {
|
|
53
|
+
if (!isShellShortcutModifier(e)) return null;
|
|
54
|
+
|
|
55
|
+
const key = normalizedShortcutKey(e);
|
|
56
|
+
if (key === '[') return 'toggle-panel-nav';
|
|
57
|
+
if (key === ']') return 'close-panel-tools';
|
|
58
|
+
|
|
59
|
+
const toolId = TOOL_SHORTCUT_KEYS[key];
|
|
60
|
+
if (toolId) return `open-tool:${toolId}`;
|
|
61
|
+
|
|
62
|
+
return null;
|
|
63
|
+
}
|
|
@@ -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 'ds-focus-ring-inset': 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":"qIAAA,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,CAoDrC,CA3DC,cAAMa,GACJf,KAAKS,UAAUO,O,CASjB,MAAAC,GACE,MAAMC,EAAKlB,KAAKmB,WAEhB,MAAMC,EAA+B,CACnC,cAAe,KACf,sBAAuB,KACvB,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":[]}
|