@db-ux/wc-core-components 4.6.1 → 4.7.0-tabs-34782eb

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 (168) hide show
  1. package/CHANGELOG.md +8 -0
  2. package/agent/Tabs.md +30 -30
  3. package/dist/cjs/db-accordion-item.cjs.entry.js +2 -2
  4. package/dist/cjs/db-accordion.cjs.entry.js +1 -1
  5. package/dist/cjs/db-badge.cjs.entry.js +1 -1
  6. package/dist/cjs/db-brand.cjs.entry.js +1 -1
  7. package/dist/cjs/db-button.cjs.entry.js +1 -1
  8. package/dist/cjs/db-card.cjs.entry.js +2 -2
  9. package/dist/cjs/db-checkbox.cjs.entry.js +2 -2
  10. package/dist/cjs/db-custom-button.cjs.entry.js +2 -2
  11. package/dist/cjs/db-custom-select-dropdown_5.cjs.entry.js +9 -9
  12. package/dist/cjs/db-custom-select-form-field.cjs.entry.js +2 -2
  13. package/dist/cjs/db-custom-select.cjs.entry.js +5 -5
  14. package/dist/cjs/db-divider.cjs.entry.js +1 -1
  15. package/dist/cjs/db-drawer.cjs.entry.js +3 -3
  16. package/dist/cjs/db-header.cjs.entry.js +2 -2
  17. package/dist/cjs/db-icon.cjs.entry.js +1 -1
  18. package/dist/cjs/db-infotext.cjs.entry.js +1 -1
  19. package/dist/cjs/db-link.cjs.entry.js +1 -1
  20. package/dist/cjs/db-navigation-item.cjs.entry.js +1 -1
  21. package/dist/cjs/db-navigation.cjs.entry.js +2 -2
  22. package/dist/cjs/db-notification.cjs.entry.js +2 -2
  23. package/dist/cjs/db-page.cjs.entry.js +2 -2
  24. package/dist/cjs/db-popover.cjs.entry.js +2 -2
  25. package/dist/cjs/db-radio.cjs.entry.js +1 -1
  26. package/dist/cjs/db-section.cjs.entry.js +2 -2
  27. package/dist/cjs/db-select.cjs.entry.js +2 -2
  28. package/dist/cjs/db-stack.cjs.entry.js +2 -2
  29. package/dist/cjs/db-switch.cjs.entry.js +2 -2
  30. package/dist/cjs/db-tab-item_3.cjs.entry.js +133 -67
  31. package/dist/cjs/db-tabs.cjs.entry.js +373 -123
  32. package/dist/cjs/db-textarea.cjs.entry.js +2 -2
  33. package/dist/cjs/db-tooltip.cjs.entry.js +3 -4
  34. package/dist/cjs/db-ux.cjs.js +1 -1
  35. package/dist/cjs/index.cjs.js +2 -2
  36. package/dist/cjs/loader.cjs.js +1 -1
  37. package/dist/collection/components/accordion/accordion.js +1 -1
  38. package/dist/collection/components/accordion-item/accordion-item.js +2 -2
  39. package/dist/collection/components/badge/badge.js +1 -1
  40. package/dist/collection/components/brand/brand.js +1 -1
  41. package/dist/collection/components/button/button.js +1 -1
  42. package/dist/collection/components/card/card.js +2 -2
  43. package/dist/collection/components/checkbox/checkbox.js +2 -2
  44. package/dist/collection/components/custom-button/custom-button.js +2 -2
  45. package/dist/collection/components/custom-select/custom-select.js +5 -5
  46. package/dist/collection/components/custom-select-dropdown/custom-select-dropdown.js +2 -2
  47. package/dist/collection/components/custom-select-form-field/custom-select-form-field.js +2 -2
  48. package/dist/collection/components/custom-select-list/custom-select-list.js +2 -2
  49. package/dist/collection/components/custom-select-list-item/custom-select-list-item.js +1 -1
  50. package/dist/collection/components/divider/divider.js +1 -1
  51. package/dist/collection/components/drawer/drawer.js +3 -3
  52. package/dist/collection/components/header/header.js +2 -2
  53. package/dist/collection/components/icon/icon.js +1 -1
  54. package/dist/collection/components/infotext/infotext.js +1 -1
  55. package/dist/collection/components/input/input.js +2 -2
  56. package/dist/collection/components/link/link.js +1 -1
  57. package/dist/collection/components/navigation/navigation.js +2 -2
  58. package/dist/collection/components/navigation-item/navigation-item.js +1 -1
  59. package/dist/collection/components/notification/notification.js +2 -2
  60. package/dist/collection/components/page/page.js +2 -2
  61. package/dist/collection/components/popover/popover.js +2 -2
  62. package/dist/collection/components/radio/radio.js +1 -1
  63. package/dist/collection/components/section/section.js +2 -2
  64. package/dist/collection/components/select/select.js +2 -2
  65. package/dist/collection/components/stack/stack.js +2 -2
  66. package/dist/collection/components/switch/switch.js +2 -2
  67. package/dist/collection/components/tab-item/tab-item.js +198 -129
  68. package/dist/collection/components/tab-list/tab-list.js +74 -12
  69. package/dist/collection/components/tab-panel/tab-panel.js +80 -2
  70. package/dist/collection/components/tabs/tabs.js +504 -167
  71. package/dist/collection/components/tag/tag.js +2 -2
  72. package/dist/collection/components/textarea/textarea.js +2 -2
  73. package/dist/collection/components/tooltip/tooltip.js +3 -4
  74. package/dist/collection/shared/model.js +1 -1
  75. package/dist/custom-elements.json +4021 -3053
  76. package/dist/db-ux/db-ux.esm.js +1 -1
  77. package/dist/db-ux/index.esm.js +1 -1
  78. package/dist/db-ux/{p-2355324e.entry.js → p-01c278b9.entry.js} +1 -1
  79. package/dist/db-ux/{p-5f5eb78d.entry.js → p-071ae423.entry.js} +1 -1
  80. package/dist/db-ux/{p-abb0885d.entry.js → p-07c7ecc9.entry.js} +1 -1
  81. package/dist/db-ux/p-10869bfa.entry.js +1 -0
  82. package/dist/db-ux/p-1427bb15.entry.js +1 -0
  83. package/dist/db-ux/{p-70823b80.entry.js → p-328faf6b.entry.js} +1 -1
  84. package/dist/db-ux/p-397bc590.entry.js +1 -0
  85. package/dist/db-ux/{p-d85b1c15.entry.js → p-43a9623f.entry.js} +1 -1
  86. package/dist/db-ux/{p-67d59d5b.entry.js → p-44a0407b.entry.js} +1 -1
  87. package/dist/db-ux/{p-12a2c502.entry.js → p-61784f7a.entry.js} +1 -1
  88. package/dist/db-ux/p-68835a75.entry.js +1 -0
  89. package/dist/db-ux/p-68a995df.entry.js +1 -0
  90. package/dist/db-ux/{p-a65d0d23.entry.js → p-6c26b0ba.entry.js} +1 -1
  91. package/dist/db-ux/p-6e898a58.entry.js +1 -0
  92. package/dist/db-ux/{p-761c9ddf.entry.js → p-70e6d32f.entry.js} +1 -1
  93. package/dist/db-ux/p-71868bb5.entry.js +1 -0
  94. package/dist/db-ux/{p-bed8d029.entry.js → p-74a45768.entry.js} +1 -1
  95. package/dist/db-ux/{p-ba8464d2.entry.js → p-9a895c18.entry.js} +1 -1
  96. package/dist/db-ux/p-9b4d0c18.entry.js +1 -0
  97. package/dist/db-ux/{p-0e0c3c72.entry.js → p-9b518eb4.entry.js} +1 -1
  98. package/dist/db-ux/p-a927fe88.entry.js +1 -0
  99. package/dist/db-ux/p-a93ca690.entry.js +1 -0
  100. package/dist/db-ux/{p-212bde1e.entry.js → p-b06fe221.entry.js} +1 -1
  101. package/dist/db-ux/{p-13157b54.entry.js → p-b7087f0b.entry.js} +1 -1
  102. package/dist/db-ux/{p-d8b38722.entry.js → p-d93b9d58.entry.js} +1 -1
  103. package/dist/db-ux/p-d97c0787.entry.js +1 -0
  104. package/dist/db-ux/{p-c76089f6.entry.js → p-ee43015b.entry.js} +1 -1
  105. package/dist/db-ux/p-efde1b6e.entry.js +1 -0
  106. package/dist/db-ux/{p-5cb74480.entry.js → p-f08b14d9.entry.js} +1 -1
  107. package/dist/db-ux/p-f92cbbae.entry.js +1 -0
  108. package/dist/db-ux/{p-980a6afe.entry.js → p-ffe614c8.entry.js} +1 -1
  109. package/dist/esm/db-accordion-item.entry.js +2 -2
  110. package/dist/esm/db-accordion.entry.js +1 -1
  111. package/dist/esm/db-badge.entry.js +1 -1
  112. package/dist/esm/db-brand.entry.js +1 -1
  113. package/dist/esm/db-button.entry.js +1 -1
  114. package/dist/esm/db-card.entry.js +2 -2
  115. package/dist/esm/db-checkbox.entry.js +2 -2
  116. package/dist/esm/db-custom-button.entry.js +2 -2
  117. package/dist/esm/db-custom-select-dropdown_5.entry.js +9 -9
  118. package/dist/esm/db-custom-select-form-field.entry.js +2 -2
  119. package/dist/esm/db-custom-select.entry.js +5 -5
  120. package/dist/esm/db-divider.entry.js +1 -1
  121. package/dist/esm/db-drawer.entry.js +3 -3
  122. package/dist/esm/db-header.entry.js +2 -2
  123. package/dist/esm/db-icon.entry.js +1 -1
  124. package/dist/esm/db-infotext.entry.js +1 -1
  125. package/dist/esm/db-link.entry.js +1 -1
  126. package/dist/esm/db-navigation-item.entry.js +1 -1
  127. package/dist/esm/db-navigation.entry.js +2 -2
  128. package/dist/esm/db-notification.entry.js +2 -2
  129. package/dist/esm/db-page.entry.js +2 -2
  130. package/dist/esm/db-popover.entry.js +2 -2
  131. package/dist/esm/db-radio.entry.js +1 -1
  132. package/dist/esm/db-section.entry.js +2 -2
  133. package/dist/esm/db-select.entry.js +2 -2
  134. package/dist/esm/db-stack.entry.js +2 -2
  135. package/dist/esm/db-switch.entry.js +2 -2
  136. package/dist/esm/db-tab-item_3.entry.js +134 -68
  137. package/dist/esm/db-tabs.entry.js +373 -123
  138. package/dist/esm/db-textarea.entry.js +2 -2
  139. package/dist/esm/db-tooltip.entry.js +4 -5
  140. package/dist/esm/db-ux.js +1 -1
  141. package/dist/esm/index.js +2 -2
  142. package/dist/esm/loader.js +1 -1
  143. package/dist/types/components/tab-item/model.d.ts +28 -11
  144. package/dist/types/components/tab-item/tab-item.d.ts +22 -20
  145. package/dist/types/components/tab-list/model.d.ts +15 -5
  146. package/dist/types/components/tab-list/tab-list.d.ts +4 -1
  147. package/dist/types/components/tab-panel/model.d.ts +13 -3
  148. package/dist/types/components/tab-panel/tab-panel.d.ts +3 -0
  149. package/dist/types/components/tabs/model.d.ts +51 -17
  150. package/dist/types/components/tabs/tabs.d.ts +35 -16
  151. package/dist/types/components.d.ts +80 -30
  152. package/dist/types/shared/model.d.ts +5 -5
  153. package/dist/vscode.html-custom-data.json +153 -59
  154. package/dist/web-types.json +297 -135
  155. package/package.json +3 -3
  156. package/dist/db-ux/p-202323c2.entry.js +0 -1
  157. package/dist/db-ux/p-21068628.entry.js +0 -1
  158. package/dist/db-ux/p-377987d0.entry.js +0 -1
  159. package/dist/db-ux/p-3d5d49ff.entry.js +0 -1
  160. package/dist/db-ux/p-452a8b6b.entry.js +0 -1
  161. package/dist/db-ux/p-4de2c697.entry.js +0 -1
  162. package/dist/db-ux/p-605013f0.entry.js +0 -1
  163. package/dist/db-ux/p-6f9e146e.entry.js +0 -1
  164. package/dist/db-ux/p-7fa91164.entry.js +0 -1
  165. package/dist/db-ux/p-8e84712d.entry.js +0 -1
  166. package/dist/db-ux/p-a9efbb0b.entry.js +0 -1
  167. package/dist/db-ux/p-bd66d144.entry.js +0 -1
  168. package/dist/db-ux/p-dd08f6e4.entry.js +0 -1
@@ -7,131 +7,322 @@ const DBTabs = class {
7
7
  constructor(hostRef) {
8
8
  index.registerInstance(this, hostRef);
9
9
  this.indexChange = index.createEvent(this, "indexChange");
10
- this.tabSelect = index.createEvent(this, "tabSelect");
11
- this._name = "";
10
+ this.valueChange = index.createEvent(this, "valueChange");
11
+ this.tabItemAlignment = "start";
12
+ this.tabItemWidth = "auto";
13
+ this.scrollStartLabel = "Scroll start";
14
+ this.scrollEndLabel = "Scroll end";
15
+ this._generatedId = "tabs-" + index$1.uuid();
16
+ this._generatedName = index$1.uuid();
17
+ this.activeTabIndex = 0;
12
18
  this.initialized = false;
13
- this.showScrollLeft = false;
14
- this.showScrollRight = false;
15
- this.scrollContainer = null;
16
- this._resizeObserver = undefined;
19
+ this.showScrollStart = false;
20
+ this.showScrollEnd = false;
21
+ this._resizeObserver = null;
22
+ this._observer = null;
23
+ this._pendingRafId = null;
24
+ this._scrollListener = null;
25
+ this._cachedTabs = [];
17
26
  }
18
- convertTabs() {
27
+ _id() {
28
+ return this.id || this._generatedId;
29
+ }
30
+ _name() {
31
+ return "tabs-" + (this.name || this._generatedName);
32
+ }
33
+ getTabId(index) {
34
+ return `${this._name()}-tab-${index}`;
35
+ }
36
+ getPanelId(index) {
37
+ return `${this._name()}-tab-panel-${index}`;
38
+ }
39
+ activateTab(index) {
40
+ var _a, _b, _c;
41
+ // Prevent activating a disabled tab
42
+ if (this._ref) {
43
+ const tabList = this._ref.querySelector('[role="tablist"]');
44
+ if (tabList) {
45
+ const tabs = Array.from(tabList.querySelectorAll('[role="tab"]'));
46
+ const tab = tabs[index];
47
+ if ((tab === null || tab === void 0 ? void 0 : tab.disabled) || (tab === null || tab === void 0 ? void 0 : tab.getAttribute("aria-disabled")) === "true") {
48
+ return;
49
+ }
50
+ }
51
+ }
52
+ if (this.activeTabIndex !== index) {
53
+ this.activeTabIndex = index;
54
+ if (this.indexChange) {
55
+ this.indexChange.emit(index);
56
+ }
57
+ // Emit value of the newly active tab item if value props are set
58
+ if (this.valueChange) {
59
+ const tabList = (_a = this._ref) === null || _a === void 0 ? void 0 : _a.querySelector('[role="tablist"]');
60
+ const tabs = tabList
61
+ ? Array.from(tabList.querySelectorAll('[role="tab"]'))
62
+ : [];
63
+ const value = (_c = (_b = tabs[index]) === null || _b === void 0 ? void 0 : _b.dataset) === null || _c === void 0 ? void 0 : _c["value"];
64
+ this.valueChange.emit(value);
65
+ }
66
+ this.initTabs(index);
67
+ }
68
+ }
69
+ handleClick(event) {
70
+ var _a;
71
+ // In props-mode (props.tabs), tab activation is handled via onClick on each DBTabItem directly.
72
+ // In slot-mode (!props.tabs), clicks bubble up and are handled here via DOM traversal.
73
+ if (this.tabs) {
74
+ return;
75
+ }
76
+ const target = event.target;
77
+ const button = target.closest('[role="tab"]');
78
+ if (!button || !this._ref)
79
+ return;
80
+ const tabList = (_a = this._ref) === null || _a === void 0 ? void 0 : _a.querySelector('[role="tablist"]');
81
+ if (!tabList)
82
+ return;
83
+ const buttons = Array.from(tabList.querySelectorAll('[role="tab"]'));
84
+ const index = buttons.indexOf(button);
85
+ if (index !== -1) {
86
+ event.preventDefault();
87
+ this.activateTab(index);
88
+ }
89
+ }
90
+ handleKeyDown(event) {
91
+ var _a;
92
+ if (!this._ref)
93
+ return;
94
+ const key = event.key;
95
+ const navigationKeys = [
96
+ "ArrowRight",
97
+ "ArrowDown",
98
+ "ArrowLeft",
99
+ "ArrowUp",
100
+ "Home",
101
+ "End",
102
+ "Enter",
103
+ " ",
104
+ ];
105
+ if (!navigationKeys.includes(key)) {
106
+ return;
107
+ }
108
+ const tabList = this._ref.querySelector('[role="tablist"]');
109
+ if (!tabList)
110
+ return;
111
+ const buttons = Array.from(tabList.querySelectorAll('[role="tab"]'));
112
+ // find currently focused element within the buttons list
113
+ let currentIndex = -1;
114
+ if (typeof document !== "undefined") {
115
+ // Traverse Shadow DOM boundaries to find the truly focused element.
116
+ // document.activeElement only returns the shadow host when focus is inside a Shadow DOM,
117
+ // so we must walk through each shadowRoot to reach the actual focused element.
118
+ let activeEl = document.activeElement;
119
+ while ((_a = activeEl === null || activeEl === void 0 ? void 0 : activeEl.shadowRoot) === null || _a === void 0 ? void 0 : _a.activeElement) {
120
+ activeEl = activeEl.shadowRoot.activeElement;
121
+ }
122
+ if (activeEl) {
123
+ const focusedButton = activeEl.closest('[role="tab"]');
124
+ if (focusedButton) {
125
+ currentIndex = buttons.indexOf(focusedButton);
126
+ }
127
+ }
128
+ }
129
+ if (currentIndex === -1) {
130
+ currentIndex = this.activeTabIndex;
131
+ }
132
+ if (buttons.length > 0) {
133
+ // handle activation (enter / space) -> change panel
134
+ if (key === "Enter" || key === " ") {
135
+ event.preventDefault();
136
+ this.activateTab(currentIndex);
137
+ return;
138
+ }
139
+ // handle navigation (arrows) -> moves focus
140
+ let nextIndex;
141
+ const length = buttons.length;
142
+ if (key === "ArrowRight" || key === "ArrowDown") {
143
+ nextIndex = (currentIndex + 1) % length;
144
+ }
145
+ else if (key === "ArrowLeft" || key === "ArrowUp") {
146
+ nextIndex = (currentIndex - 1 + length) % length;
147
+ }
148
+ else if (key === "Home") {
149
+ nextIndex = 0;
150
+ }
151
+ else if (key === "End") {
152
+ nextIndex = length - 1;
153
+ }
154
+ if (nextIndex !== undefined) {
155
+ event.preventDefault();
156
+ // Skip disabled tabs when navigating with arrow keys
157
+ const isForward = key === "ArrowRight" || key === "ArrowDown";
158
+ const maxAttempts = length;
159
+ for (let i = 0; i < maxAttempts; i++) {
160
+ const candidate = buttons[nextIndex];
161
+ if (!(candidate === null || candidate === void 0 ? void 0 : candidate.disabled) &&
162
+ (candidate === null || candidate === void 0 ? void 0 : candidate.getAttribute("aria-disabled")) !== "true") {
163
+ break;
164
+ }
165
+ if (isForward) {
166
+ nextIndex = (nextIndex + 1) % length;
167
+ }
168
+ else {
169
+ nextIndex = (nextIndex - 1 + length) % length;
170
+ }
171
+ }
172
+ // do not activateTab here for manual activation, just move the focus
173
+ const nextButton = buttons[nextIndex];
174
+ if (nextButton &&
175
+ !nextButton.disabled &&
176
+ nextButton.getAttribute("aria-disabled") !== "true") {
177
+ nextButton.focus();
178
+ }
179
+ }
180
+ }
181
+ }
182
+ isIndexActive(index) {
183
+ return this.activeTabIndex === Number(index);
184
+ }
185
+ getTabItemTabIndex(index) {
186
+ const i = Number(index);
187
+ // only the active tab should be reachable via Tab key
188
+ return this.activeTabIndex === i || (this.activeTabIndex === -1 && i === 0)
189
+ ? 0
190
+ : -1;
191
+ }
192
+ _updateCachedTabs() {
19
193
  try {
20
194
  if (typeof this.tabs === "string") {
21
- return JSON.parse(this.tabs);
195
+ this._cachedTabs = JSON.parse(this.tabs);
196
+ }
197
+ else if (this.tabs) {
198
+ this._cachedTabs = this.tabs;
199
+ }
200
+ else {
201
+ this._cachedTabs = [];
22
202
  }
23
- return this.tabs;
24
203
  }
25
204
  catch (error) {
26
205
  console.error(error);
206
+ this._cachedTabs = [];
27
207
  }
28
- return [];
208
+ }
209
+ _getScrollContainer() {
210
+ var _a, _b;
211
+ return (_b = (_a = this._ref) === null || _a === void 0 ? void 0 : _a.querySelector('[role="tablist"]')) !== null && _b !== void 0 ? _b : null;
212
+ }
213
+ _isRtl() {
214
+ const container = this._getScrollContainer();
215
+ return (!!container &&
216
+ typeof getComputedStyle !== "undefined" &&
217
+ getComputedStyle(container).direction === "rtl");
29
218
  }
30
219
  evaluateScrollButtons(tList) {
31
220
  const needsScroll = tList.scrollWidth > tList.clientWidth;
32
- this.showScrollLeft = needsScroll && tList.scrollLeft > 1;
33
- this.showScrollRight =
34
- needsScroll && tList.scrollLeft < tList.scrollWidth - tList.clientWidth;
221
+ if (!needsScroll) {
222
+ this.showScrollStart = false;
223
+ this.showScrollEnd = false;
224
+ return;
225
+ }
226
+ const scrollPos = Math.abs(tList.scrollLeft);
227
+ const maxScroll = tList.scrollWidth - tList.clientWidth;
228
+ const tolerance = 2;
229
+ // scrollPos=0 means "at inline-start" in both LTR and RTL
230
+ this.showScrollStart = scrollPos > tolerance;
231
+ this.showScrollEnd = scrollPos < maxScroll - tolerance;
35
232
  }
36
- scroll(left) {
37
- var _a;
38
- let step = Number(this.arrowScrollDistance) || 100;
39
- if (left) {
233
+ scroll(toStart) {
234
+ const container = this._getScrollContainer();
235
+ if (!container) {
236
+ return;
237
+ }
238
+ let step = Number(this.arrowScrollDistance) || 120;
239
+ const isLeft = !!toStart;
240
+ const isRtl = this._isRtl();
241
+ // Map logical direction (start/end) to physical direction.
242
+ // In LTR: toStart=true → scroll left (negative), toEnd → scroll right (positive).
243
+ // In RTL: directions are inverted physically.
244
+ if (isLeft !== isRtl) {
40
245
  step *= -1;
41
246
  }
42
- (_a = this.scrollContainer) === null || _a === void 0 ? void 0 : _a.scrollBy({
43
- top: 0,
247
+ container.scrollBy({
44
248
  left: step,
45
249
  behavior: "smooth",
46
250
  });
47
251
  }
48
252
  initTabList() {
253
+ var _a, _b;
49
254
  if (this._ref) {
50
- const tabList = this._ref.querySelector(".db-tab-list");
51
- if (tabList) {
52
- const container = tabList.querySelector('[role="tablist"]');
53
- if (container) {
54
- container.setAttribute("aria-orientation", this.orientation || "horizontal");
55
- if (this.behavior === "arrows") {
56
- this.scrollContainer = container;
57
- this.evaluateScrollButtons(container);
58
- container.addEventListener("scroll", () => {
255
+ const container = this._ref.querySelector('[role="tablist"]');
256
+ if (container) {
257
+ if (!container.getAttribute("aria-orientation")) {
258
+ container.setAttribute("aria-orientation", (_a = this.orientation) !== null && _a !== void 0 ? _a : "horizontal");
259
+ }
260
+ if (this.behavior === "arrows") {
261
+ this.evaluateScrollButtons(container);
262
+ const _listener = this._scrollListener;
263
+ if (_listener && container) {
264
+ container.removeEventListener("scroll", _listener.fn);
265
+ this._scrollListener = null;
266
+ }
267
+ const onScroll = () => this.evaluateScrollButtons(container);
268
+ this._scrollListener = {
269
+ fn: onScroll,
270
+ };
271
+ container.addEventListener("scroll", onScroll);
272
+ if (!this._resizeObserver) {
273
+ const observer = new ResizeObserver(() => {
59
274
  this.evaluateScrollButtons(container);
60
275
  });
61
- // Use ResizeObserver to re-evaluate scroll buttons because it provides more accurate, container-specific resize detection than global window resize events.
62
- if (!this._resizeObserver) {
63
- const observer = new ResizeObserver(() => {
64
- this.evaluateScrollButtons(container);
65
- });
66
- observer.observe(container);
67
- this._resizeObserver = observer;
68
- }
276
+ observer.observe(container);
277
+ this._resizeObserver = observer;
69
278
  }
70
279
  }
280
+ if (this.name && !container.getAttribute("aria-label")) {
281
+ container.setAttribute("aria-label", (_b = this.name) !== null && _b !== void 0 ? _b : "");
282
+ }
71
283
  }
72
284
  }
73
285
  }
74
- initTabs(init) {
286
+ initTabs(activeIndex) {
287
+ var _a, _b;
288
+ const currentIndex = activeIndex !== undefined ? activeIndex : this.activeTabIndex;
75
289
  if (this._ref) {
76
- const tabItems = Array.from(this._ref.getElementsByClassName("db-tab-item"));
77
- const tabPanels = Array.from(this._ref.querySelectorAll(":is(:scope > .db-tab-panel, :scope > db-tab-panel > .db-tab-panel)"));
78
- for (const tabItem of tabItems) {
79
- const index = tabItems.indexOf(tabItem);
80
- const label = tabItem.querySelector("label");
81
- const input = tabItem.querySelector("input");
82
- if (input && label) {
83
- if (!input.id) {
84
- const tabId = `${this._name}-tab-${index}`;
85
- label.setAttribute("for", tabId);
86
- input.id = tabId;
87
- input.setAttribute("name", this._name);
88
- if (tabPanels.length > index) {
89
- input.setAttribute("aria-controls", `${this._name}-tab-panel-${index}`);
90
- }
91
- }
92
- if (init) {
93
- // Auto select
94
- const autoSelect = !this.initialSelectedMode || this.initialSelectedMode === "auto";
95
- const shouldAutoSelect = (this.initialSelectedIndex == null && index === 0) ||
96
- Number(this.initialSelectedIndex) === index;
97
- if (autoSelect && shouldAutoSelect) {
98
- input.click();
99
- }
100
- }
290
+ const tabListEl = this._ref.querySelector('[role="tablist"]');
291
+ const panels = Array.from((_b = (_a = this._ref) === null || _a === void 0 ? void 0 : _a.querySelectorAll('[role="tabpanel"]')) !== null && _b !== void 0 ? _b : []).filter((panel) => panel.closest(".db-tabs") === this._ref);
292
+ if (!tabListEl)
293
+ return;
294
+ const buttons = Array.from(tabListEl.querySelectorAll('[role="tab"]'));
295
+ buttons.forEach((button, index) => {
296
+ const isSelected = currentIndex === index;
297
+ const panel = panels[index];
298
+ const tabId = button.id || this.getTabId(index);
299
+ const panelId = (panel === null || panel === void 0 ? void 0 : panel.id) || this.getPanelId(index);
300
+ if (!button.id) {
301
+ button.id = tabId;
101
302
  }
102
- }
103
- for (const panel of tabPanels) {
104
- if (panel.id)
105
- continue;
106
- const index = tabPanels.indexOf(panel);
107
- panel.id = `${this._name}-tab-panel-${index}`;
108
- panel.setAttribute("aria-labelledby", `${this._name}-tab-${index}`);
109
- }
110
- }
111
- }
112
- handleChange(event) {
113
- var _a;
114
- event.stopPropagation();
115
- if (event.target) {
116
- const target = event.target;
117
- const parent = target.parentElement;
118
- if (parent &&
119
- parent.parentElement &&
120
- ((_a = parent.parentElement) === null || _a === void 0 ? void 0 : _a.nodeName) === "LI") {
121
- const tabItem = parent.parentElement.parentElement;
122
- if (tabItem) {
123
- const list = tabItem.parentElement;
124
- if (list) {
125
- const tabIndex = Array.from(list.children).indexOf(tabItem);
126
- if (this.indexChange) {
127
- this.indexChange.emit(tabIndex);
128
- }
129
- if (this.tabSelect) {
130
- this.tabSelect.emit(event);
131
- }
303
+ if (!button.getAttribute("aria-controls")) {
304
+ button.setAttribute("aria-controls", panelId);
305
+ }
306
+ button.dispatchEvent(new CustomEvent("aria-selected-changed", {
307
+ detail: {
308
+ selected: isSelected,
309
+ tabIndex: currentIndex === index || (currentIndex === -1 && index === 0)
310
+ ? 0
311
+ : -1,
312
+ },
313
+ }));
314
+ if (panel) {
315
+ if (!panel.id) {
316
+ panel.id = panelId;
317
+ }
318
+ if (!panel.getAttribute("aria-label") &&
319
+ !panel.getAttribute("aria-labelledby")) {
320
+ panel.setAttribute("aria-labelledby", tabId);
132
321
  }
322
+ // toggle visibility
323
+ panel.hidden = !isSelected;
133
324
  }
134
- }
325
+ });
135
326
  }
136
327
  }
137
328
  /**
@@ -171,54 +362,113 @@ const DBTabs = class {
171
362
  }
172
363
  }
173
364
  watch0Fn() {
174
- if (this._ref && this.initialized) {
175
- this.initTabList();
176
- this.initTabs(true);
177
- const tabList = this._ref.querySelector(".db-tab-list");
178
- if (tabList) {
179
- const observer = new MutationObserver((mutations) => {
180
- mutations.forEach((mutation) => {
181
- if (mutation.removedNodes.length || mutation.addedNodes.length) {
182
- this.initTabList();
183
- this.initTabs();
184
- }
185
- });
186
- });
187
- observer.observe(tabList, {
188
- childList: true,
189
- subtree: true,
190
- });
191
- }
192
- this.initialized = false;
193
- }
365
+ this._updateCachedTabs();
194
366
  }
195
367
  watch0() {
196
368
  this.watch0Fn();
197
369
  }
370
+ watch1Fn() {
371
+ if (this.activeIndex !== undefined) {
372
+ const newIndex = Number(this.activeIndex);
373
+ if (!isNaN(newIndex) && newIndex !== this.activeTabIndex) {
374
+ this.activateTab(newIndex);
375
+ }
376
+ }
377
+ }
378
+ watch1() {
379
+ this.watch1Fn();
380
+ }
198
381
  componentDidLoad() {
199
382
  this.enableAttributePassing(this._ref, "db-tabs");
200
- this._name = `tabs-${this.name || index$1.uuid()}`;
383
+ // 1. Calculate final start index synchronously to avoid race conditions
384
+ let startIndex = 0;
385
+ if (this.initialSelectedIndex !== undefined) {
386
+ const parsedIndex = Number(this.initialSelectedIndex);
387
+ startIndex = isNaN(parsedIndex) ? 0 : parsedIndex;
388
+ }
389
+ else if (this.initialSelectedMode === "manually") {
390
+ startIndex = -1;
391
+ }
392
+ // 2. Support deep linking: URL hash takes precedence over initial index
393
+ if (typeof window !== "undefined" && window.location.hash) {
394
+ const hashId = window.location.hash.substring(1);
395
+ const name = this.name ? "tabs-" + this.name : this._name();
396
+ const prefix = `${name}-tab-`;
397
+ if (hashId.startsWith(prefix)) {
398
+ const indexStr = hashId.replace(prefix, "");
399
+ const index = parseInt(indexStr, 10);
400
+ if (!isNaN(index)) {
401
+ startIndex = index;
402
+ }
403
+ }
404
+ }
405
+ // 3. Set initial state synchronously
406
+ this.activeTabIndex = startIndex;
201
407
  this.initialized = true;
408
+ this._updateCachedTabs();
409
+ // 4. Trigger single initial DOM update after paint
410
+ if (typeof window !== "undefined") {
411
+ requestAnimationFrame(() => {
412
+ this.initTabList();
413
+ this.initTabs(startIndex);
414
+ });
415
+ }
416
+ if (this._ref) {
417
+ const tabListEl = this._ref.querySelector('[role="tablist"]');
418
+ if (tabListEl) {
419
+ const observer = new MutationObserver(() => {
420
+ const rafId = this._pendingRafId;
421
+ if (rafId !== null)
422
+ cancelAnimationFrame(rafId);
423
+ this._pendingRafId = requestAnimationFrame(() => {
424
+ this._pendingRafId = null;
425
+ this.initTabList();
426
+ this.initTabs(this.activeTabIndex);
427
+ });
428
+ });
429
+ // Observe only the tablist (not panel content) to avoid unnecessary
430
+ // re-evaluations when user content inside panels changes.
431
+ // childList only – attribute changes (set by initTabs) are not observed, preventing infinite loops.
432
+ observer.observe(tabListEl, {
433
+ childList: true,
434
+ subtree: true,
435
+ });
436
+ this._observer = observer;
437
+ }
438
+ }
202
439
  this.watch0Fn();
440
+ this.watch1Fn();
203
441
  }
204
442
  disconnectedCallback() {
205
- var _a;
443
+ var _a, _b;
444
+ const rafId = this._pendingRafId;
445
+ if (rafId !== null) {
446
+ cancelAnimationFrame(rafId);
447
+ this._pendingRafId = null;
448
+ }
449
+ const _listener = this._scrollListener;
450
+ const _container = this._getScrollContainer();
451
+ if (_listener && _container) {
452
+ _container.removeEventListener("scroll", _listener.fn);
453
+ }
206
454
  (_a = this._resizeObserver) === null || _a === void 0 ? void 0 : _a.disconnect();
207
- this._resizeObserver = undefined;
455
+ this._resizeObserver = null;
456
+ (_b = this._observer) === null || _b === void 0 ? void 0 : _b.disconnect();
457
+ this._observer = null;
208
458
  }
209
459
  render() {
210
- var _a, _b, _c, _d, _e, _f;
211
- return (index.h("div", { key: '20d8d662866ff60717de87ad4deb98a3b647672a', class: index$1.cls("db-tabs", this.className), ref: (el) => {
460
+ var _a, _b, _c, _d, _e;
461
+ return (index.h("div", { key: '8d9608e7d9255b3bc138ee4c50f1fdfb0b09ba72', class: index$1.cls("db-tabs", this.className), ref: (el) => {
212
462
  this._ref = el;
213
- }, id: (_a = this.id) !== null && _a !== void 0 ? _a : (_b = this.propOverrides) === null || _b === void 0 ? void 0 : _b.id, "data-orientation": this.orientation, "data-scroll-behavior": this.behavior, "data-alignment": (_c = this.alignment) !== null && _c !== void 0 ? _c : "start", "data-width": (_d = this.width) !== null && _d !== void 0 ? _d : "auto", onInput: (event) => this.handleChange(event), onChange: (event) => this.handleChange(event) }, this.showScrollLeft ? (index.h("db-button", { class: "tabs-scroll-left", variant: "ghost", icon: "chevron_left", type: "button", noText: true, onClick: () => this.scroll(true) }, "Scroll left")) : null, this.tabs ? (index.h(index.Fragment, null, index.h("db-tab-list", null, (_e = this.convertTabs()) === null || _e === void 0 ? void 0 : _e.map((tab, index$1) => (index.h("db-tab-item", { key: this.name + "tab-item" + index$1, active: tab.active, label: tab.label, iconTrailing: tab.iconTrailing, icon: tab.icon, noText: tab.noText })))), (_f = this.convertTabs()) === null || _f === void 0 ? void 0 :
214
- _f.map((tab, index$1) => (index.h("db-tab-panel", { key: this.name + "tab-panel" + index$1, content: tab.content }, tab.children))))) : null, this.showScrollRight ? (index.h("db-button", { class: "tabs-scroll-right", variant: "ghost", icon: "chevron_right", type: "button", noText: true, onClick: () => this.scroll() }, "Scroll right")) : null, index.h("slot", { key: 'f9803a647f9a01adc0b20eb6bdb999f38c3602af' })));
463
+ }, id: (_c = (_a = this.id) !== null && _a !== void 0 ? _a : (_b = this.propOverrides) === null || _b === void 0 ? void 0 : _b.id) !== null && _c !== void 0 ? _c : this._id(), "data-orientation": this.orientation, "data-scroll-behavior": this.behavior, "data-tab-item-alignment": this.tabItemAlignment, "data-tab-item-width": this.tabItemWidth, onClick: (event) => this.handleClick(event), onKeyDown: (event) => this.handleKeyDown(event) }, this.showScrollStart ? (index.h("db-button", { class: "tabs-scroll-start", variant: "ghost", icon: "chevron_left", type: "button", noText: true, onClick: () => this.scroll(true) }, this.scrollStartLabel)) : null, this.tabs ? (index.h(index.Fragment, null, index.h("db-tab-list", { orientation: this.orientation, ariaLabel: this.name }, (_d = this._cachedTabs) === null || _d === void 0 ? void 0 : _d.map((tab, index$1) => (index.h("db-tab-item", { key: this.name + "tab-item" + index$1, id: this.getTabId(index$1), ariaControls: this.getPanelId(index$1), active: this.isIndexActive(index$1), tabIndex: this.getTabItemTabIndex(index$1), label: tab.label, iconTrailing: tab.iconTrailing, icon: tab.icon, noText: tab.noText, onClick: () => this.activateTab(index$1) })))), (_e = this._cachedTabs) === null || _e === void 0 ? void 0 :
464
+ _e.map((tab, index$1) => (index.h("db-tab-panel", { key: this.name + "tab-panel" + index$1, id: this.getPanelId(index$1), ariaLabelledby: this.getTabId(index$1), content: tab.content, hidden: !this.isIndexActive(index$1) }, tab.children))))) : null, !this.tabs ? index.h("slot", null) : null, this.showScrollEnd ? (index.h("db-button", { class: "tabs-scroll-end", variant: "ghost", icon: "chevron_right", type: "button", noText: true, onClick: () => this.scroll(false) }, this.scrollEndLabel)) : null));
215
465
  }
216
466
  static get watchers() { return {
217
- "_ref": [{
467
+ "tabs": [{
218
468
  "watch0": 0
219
469
  }],
220
- "initialized": [{
221
- "watch0": 0
470
+ "activeIndex": [{
471
+ "watch1": 0
222
472
  }]
223
473
  }; }
224
474
  };
@@ -201,10 +201,10 @@ const DBTextarea = class {
201
201
  }
202
202
  render() {
203
203
  var _a, _b, _c, _d, _e, _f;
204
- return (index.h("div", { key: 'e6c64857826bf80558d0b402594a66418e62ef57', class: index$1.cls("db-textarea", this.className), "data-variant": this.variant, "data-hide-asterisk": index$1.getHideProp(this.showRequiredAsterisk), "data-hide-label": index$1.getHideProp(this.showLabel) }, index.h("label", { key: '38f6ce12d40275dd6fa5cfa64c6d2ca4c17fab8f', htmlFor: this._id }, (_a = this.label) !== null && _a !== void 0 ? _a : constants.DEFAULT_LABEL), index.h("textarea", { key: '6d11324a4cfce9eeed9e1bcafe0a7edb1ee6c8ce', "aria-invalid": this.validation === "invalid", "data-custom-validity": this.validation, "data-field-sizing": this.fieldSizing, ref: (el) => {
204
+ return (index.h("div", { key: '09bd0536df39649a7eb87c87f3402d0e626a4ffc', class: index$1.cls("db-textarea", this.className), "data-variant": this.variant, "data-hide-asterisk": index$1.getHideProp(this.showRequiredAsterisk), "data-hide-label": index$1.getHideProp(this.showLabel) }, index.h("label", { key: 'f30242db7f91eb79b9c3f5db090ce0ad2cfc9899', htmlFor: this._id }, (_a = this.label) !== null && _a !== void 0 ? _a : constants.DEFAULT_LABEL), index.h("textarea", { key: 'eec1ca9649b4daf2d8b6ba0cd9f09da21ddcd0b7', "aria-invalid": this.validation === "invalid", "data-custom-validity": this.validation, "data-field-sizing": this.fieldSizing, ref: (el) => {
205
205
  this._ref = el;
206
206
  }, id: this._id, "data-resize": this.resize, "data-hide-resizer": index$1.getHideProp((_b = this.showResizer) !== null && _b !== void 0 ? _b : true), disabled: index$1.getBoolean(this.disabled, "disabled"), required: index$1.getBoolean(this.required, "required"), readOnly: index$1.getBoolean(this.readOnly, "readOnly") ||
207
- index$1.getBoolean(this.readonly, "readonly"), form: this.form, maxLength: index$1.getNumber(this.maxLength, this.maxlength), minLength: index$1.getNumber(this.minLength, this.minlength), name: this.name, wrap: this.wrap, spellcheck: this.spellCheck, autocomplete: this.autocomplete, onInput: (event) => this.handleInput(event), onChange: (event) => this.handleChange(event), onBlur: (event) => this.handleBlur(event), onFocus: (event) => this.handleFocus(event), value: (_d = (_c = this.value) !== null && _c !== void 0 ? _c : this._value) !== null && _d !== void 0 ? _d : "", "aria-describedby": (_e = this.ariaDescribedBy) !== null && _e !== void 0 ? _e : this._descByIds, placeholder: (_f = this.placeholder) !== null && _f !== void 0 ? _f : constants.DEFAULT_PLACEHOLDER, rows: index$1.getNumber(this.rows, constants.DEFAULT_ROWS), cols: index$1.getNumber(this.cols) }), index$1.stringPropVisible(this.message, this.showMessage) ? (index.h("db-infotext", { size: "small", icon: this.messageIcon, id: this._messageId }, this.message)) : null, this.hasValidState() ? (index.h("db-infotext", { size: "small", semantic: "successful", id: this._validMessageId }, this.validMessage || constants.DEFAULT_VALID_MESSAGE)) : null, index.h("db-infotext", { key: '16d43aeea87f6e3597c95847c037faa194875b94', size: "small", semantic: "critical", id: this._invalidMessageId }, this._invalidMessage), index.h("span", { key: '8b407f9a60c61ac4af26d6ec5a9571d5f37ae915', "data-visually-hidden": "true", role: "status" }, this._voiceOverFallback)));
207
+ index$1.getBoolean(this.readonly, "readonly"), form: this.form, maxLength: index$1.getNumber(this.maxLength, this.maxlength), minLength: index$1.getNumber(this.minLength, this.minlength), name: this.name, wrap: this.wrap, spellcheck: this.spellCheck, autocomplete: this.autocomplete, onInput: (event) => this.handleInput(event), onChange: (event) => this.handleChange(event), onBlur: (event) => this.handleBlur(event), onFocus: (event) => this.handleFocus(event), value: (_d = (_c = this.value) !== null && _c !== void 0 ? _c : this._value) !== null && _d !== void 0 ? _d : "", "aria-describedby": (_e = this.ariaDescribedBy) !== null && _e !== void 0 ? _e : this._descByIds, placeholder: (_f = this.placeholder) !== null && _f !== void 0 ? _f : constants.DEFAULT_PLACEHOLDER, rows: index$1.getNumber(this.rows, constants.DEFAULT_ROWS), cols: index$1.getNumber(this.cols) }), index$1.stringPropVisible(this.message, this.showMessage) ? (index.h("db-infotext", { size: "small", icon: this.messageIcon, id: this._messageId }, this.message)) : null, this.hasValidState() ? (index.h("db-infotext", { size: "small", semantic: "successful", id: this._validMessageId }, this.validMessage || constants.DEFAULT_VALID_MESSAGE)) : null, index.h("db-infotext", { key: 'a23431e70fa69dfb3cc53f5c114802c586b76628', size: "small", semantic: "critical", id: this._invalidMessageId }, this._invalidMessage), index.h("span", { key: '936fc47a2da2c0590c5991df6ff6d2ba84342fd3', "data-visually-hidden": "true", role: "status" }, this._voiceOverFallback)));
208
208
  }
209
209
  static get watchers() { return {
210
210
  "id": [{
@@ -1,7 +1,6 @@
1
1
  'use strict';
2
2
 
3
3
  var index = require('./index-BoC-BWt4.js');
4
- var constants = require('./constants-bMY2_d8A.js');
5
4
  var index$1 = require('./index-2_9ESkQu.js');
6
5
  var documentScrollListener = require('./document-scroll-listener-ChQ7XZRk.js');
7
6
  var floatingComponents = require('./floating-components-DlstoCKH.js');
@@ -9,7 +8,7 @@ var floatingComponents = require('./floating-components-DlstoCKH.js');
9
8
  const DBTooltip = class {
10
9
  constructor(hostRef) {
11
10
  index.registerInstance(this, hostRef);
12
- this._id = constants.DEFAULT_ID;
11
+ this._id = "tooltip-" + index$1.uuid();
13
12
  this.initialized = false;
14
13
  this._documentScrollListenerCallbackId = undefined;
15
14
  this._observer = undefined;
@@ -158,9 +157,9 @@ const DBTooltip = class {
158
157
  }
159
158
  render() {
160
159
  var _a, _b;
161
- return (index.h("i", { key: 'df7b27f485e4029f51329dd73e50603f6965efb8', class: index$1.cls("db-tooltip", this.className), role: "tooltip", "aria-hidden": "true", "data-gap": "true", ref: (el) => {
160
+ return (index.h("i", { key: 'c96329202b5534b72d32091f25637fa0cd49e8bb', class: index$1.cls("db-tooltip", this.className), role: "tooltip", "aria-hidden": "true", "data-gap": "true", ref: (el) => {
162
161
  this._ref = el;
163
- }, id: this._id, "data-emphasis": this.emphasis, "data-wrap": index$1.getBooleanAsString(this.wrap), "data-animation": index$1.getBooleanAsString((_a = this.animation) !== null && _a !== void 0 ? _a : true), "data-delay": this.delay, "data-width": this.width, "data-show-arrow": index$1.getBooleanAsString((_b = this.showArrow) !== null && _b !== void 0 ? _b : true), "data-placement": this.placement, onClick: (event) => this.handleClick(event) }, index.h("slot", { key: '7c1aee92a530f8b0899231f55122f6109c72bc46' })));
162
+ }, id: this._id, "data-emphasis": this.emphasis, "data-wrap": index$1.getBooleanAsString(this.wrap), "data-animation": index$1.getBooleanAsString((_a = this.animation) !== null && _a !== void 0 ? _a : true), "data-delay": this.delay, "data-width": this.width, "data-show-arrow": index$1.getBooleanAsString((_b = this.showArrow) !== null && _b !== void 0 ? _b : true), "data-placement": this.placement, onClick: (event) => this.handleClick(event) }, index.h("slot", { key: 'ffbe2e72d2a2071c09c2dd66d694453be33c809c' })));
164
163
  }
165
164
  static get watchers() { return {
166
165
  "id": [{