tyrell-components 1.0.0-TC52 → 1.0.0-TC54
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/css/tyrell-theme.css +25 -2
- package/css/tyrell.css +4 -0
- package/dist/tyrell-colors-static.css +1 -316
- package/dist/tyrell-theme.css +1 -1443
- package/dist/tyrell.css +1 -1390
- package/dist/tyrell.js +1 -1
- package/lib/base/ty-component.js +196 -196
- package/lib/components/button.d.ts +1 -1
- package/lib/components/button.js +260 -259
- package/lib/components/calendar-month.d.ts +2 -2
- package/lib/components/calendar-month.js +426 -416
- package/lib/components/calendar-navigation.js +238 -238
- package/lib/components/calendar.js +605 -602
- package/lib/components/checkbox.d.ts +1 -1
- package/lib/components/checkbox.js +273 -272
- package/lib/components/copy.d.ts +1 -1
- package/lib/components/copy.js +279 -278
- package/lib/components/date-picker.d.ts +1 -1
- package/lib/components/date-picker.js +1103 -1108
- package/lib/components/file-upload.js +404 -404
- package/lib/components/icon.js +141 -141
- package/lib/components/input.d.ts +1 -1
- package/lib/components/input.js +593 -596
- package/lib/components/modal.js +319 -319
- package/lib/components/option.js +167 -167
- package/lib/components/popup.js +233 -233
- package/lib/components/radio.d.ts +2 -2
- package/lib/components/radio.js +407 -406
- package/lib/components/resize-observer.js +67 -67
- package/lib/components/scroll-container.js +294 -294
- package/lib/components/select.d.ts +33 -2
- package/lib/components/select.js +1543 -1532
- package/lib/components/selected-tags.js +188 -184
- package/lib/components/step.js +62 -62
- package/lib/components/switch.d.ts +1 -1
- package/lib/components/switch.js +214 -213
- package/lib/components/tab.js +62 -62
- package/lib/components/tabs.js +507 -509
- package/lib/components/tag.d.ts +1 -1
- package/lib/components/tag.js +236 -235
- package/lib/components/textarea.d.ts +1 -1
- package/lib/components/textarea.js +457 -456
- package/lib/components/tooltip.js +283 -283
- package/lib/components/wizard.js +384 -384
- package/lib/icons/heroicons/micro.js +392 -392
- package/lib/icons/heroicons/mini.js +382 -382
- package/lib/icons/heroicons/outline.js +337 -337
- package/lib/icons/heroicons/solid.js +407 -407
- package/lib/icons/lucide.js +22843 -22843
- package/lib/icons/material/filled.js +9653 -9653
- package/lib/icons/material/outlined.js +10271 -10271
- package/lib/icons/material/round.js +10495 -10495
- package/lib/icons/material/sharp.js +9905 -9905
- package/lib/icons/material/two-tone.js +12792 -12792
- package/lib/index.js +21 -21
- package/lib/styles/button.js +176 -288
- package/lib/styles/calendar-month.js +123 -183
- package/lib/styles/calendar-navigation.js +60 -79
- package/lib/styles/calendar.js +8 -9
- package/lib/styles/checkbox.js +45 -72
- package/lib/styles/copy.js +67 -91
- package/lib/styles/custom-scrollbar.js +80 -100
- package/lib/styles/date-picker.js +261 -331
- package/lib/styles/field-size.d.ts +6 -0
- package/lib/styles/field-size.js +91 -0
- package/lib/styles/file-upload.js +139 -169
- package/lib/styles/icon.js +92 -156
- package/lib/styles/input.js +125 -241
- package/lib/styles/modal.js +67 -102
- package/lib/styles/option.d.ts +1 -1
- package/lib/styles/option.js +47 -67
- package/lib/styles/popup.js +34 -65
- package/lib/styles/radio.js +69 -96
- package/lib/styles/resize-observer.js +2 -5
- package/lib/styles/scroll-container.js +100 -130
- package/lib/styles/select-base.js +525 -778
- package/lib/styles/select.js +140 -228
- package/lib/styles/step.js +22 -29
- package/lib/styles/switch.js +34 -55
- package/lib/styles/tab.js +22 -29
- package/lib/styles/tabs.js +171 -252
- package/lib/styles/tag.js +135 -188
- package/lib/styles/textarea.d.ts +1 -1
- package/lib/styles/textarea.js +205 -271
- package/lib/styles/tooltip.js +3 -3
- package/lib/styles/wizard.js +170 -226
- package/lib/types/common.d.ts +2 -0
- package/lib/types/common.js +6 -5
- package/lib/utils/calendar-utils.js +100 -100
- package/lib/utils/custom-scrollbar.js +272 -272
- package/lib/utils/flavor-sheet.js +14 -14
- package/lib/utils/icon-registry.js +127 -127
- package/lib/utils/loader-registry.js +3 -3
- package/lib/utils/locale.js +34 -34
- package/lib/utils/mobile.js +1 -1
- package/lib/utils/number-format.js +54 -54
- package/lib/utils/positioning.js +212 -212
- package/lib/utils/property-capture.js +36 -36
- package/lib/utils/property-manager.js +125 -125
- package/lib/utils/resize-observer.js +23 -23
- package/lib/utils/scroll-lock.js +57 -57
- package/lib/utils/styles.js +17 -17
- package/lib/version.d.ts +1 -1
- package/lib/version.js +1 -1
- package/package.json +4 -4
package/lib/components/tabs.js
CHANGED
|
@@ -2,580 +2,578 @@ import { ensureStyles } from '../utils/styles.js';
|
|
|
2
2
|
import { tabsStyles } from '../styles/tabs.js';
|
|
3
3
|
const eventHandlers = new WeakMap();
|
|
4
4
|
const resizeObservers = new WeakMap();
|
|
5
|
+
const pendingInitialSnap = new WeakSet();
|
|
5
6
|
function getTabsAttributes(el) {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
7
|
+
return {
|
|
8
|
+
width: el.getAttribute('width') || '100%',
|
|
9
|
+
height: el.getAttribute('height') || '400px',
|
|
10
|
+
active: el.getAttribute('active'),
|
|
11
|
+
placement: (el.getAttribute('placement') || 'top'),
|
|
12
|
+
fixed: el.hasAttribute('fixed'),
|
|
13
|
+
};
|
|
13
14
|
}
|
|
14
15
|
function getChildTabs(el) {
|
|
15
|
-
|
|
16
|
+
return Array.from(el.querySelectorAll('ty-tab'));
|
|
16
17
|
}
|
|
17
18
|
function getTabId(tab) {
|
|
18
|
-
|
|
19
|
+
return tab.getAttribute('id');
|
|
19
20
|
}
|
|
20
21
|
function getSlotLabel(tabsEl, tabId) {
|
|
21
|
-
|
|
22
|
+
return tabsEl.querySelector(`:scope > [slot='label-${tabId}']`);
|
|
22
23
|
}
|
|
23
24
|
function hasSlotLabel(tabsEl, tabId) {
|
|
24
|
-
|
|
25
|
+
return getSlotLabel(tabsEl, tabId) !== null;
|
|
25
26
|
}
|
|
26
27
|
function getTabLabelType(tabsEl, tab) {
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
28
|
+
const tabId = getTabId(tab);
|
|
29
|
+
if (tabId && hasSlotLabel(tabsEl, tabId)) {
|
|
30
|
+
return 'slot';
|
|
31
|
+
}
|
|
32
|
+
return 'text';
|
|
32
33
|
}
|
|
33
34
|
function isTabDisabled(tab) {
|
|
34
|
-
|
|
35
|
+
return tab.hasAttribute('disabled');
|
|
35
36
|
}
|
|
36
37
|
function hasCustomMarker(el) {
|
|
37
|
-
|
|
38
|
+
return el.querySelector('[slot="marker"]') !== null;
|
|
38
39
|
}
|
|
39
40
|
function findTabIndex(tabs, tabId) {
|
|
40
|
-
|
|
41
|
-
|
|
41
|
+
const index = tabs.findIndex(tab => getTabId(tab) === tabId);
|
|
42
|
+
return index >= 0 ? index : undefined;
|
|
42
43
|
}
|
|
43
44
|
function getActiveTabId(el, tabs) {
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
45
|
+
const activeAttr = el.getAttribute('active');
|
|
46
|
+
if (activeAttr && findTabIndex(tabs, activeAttr) !== undefined) {
|
|
47
|
+
return activeAttr;
|
|
48
|
+
}
|
|
49
|
+
if (tabs.length > 0) {
|
|
50
|
+
return getTabId(tabs[0]);
|
|
51
|
+
}
|
|
52
|
+
return null;
|
|
52
53
|
}
|
|
53
54
|
function setActiveTab(el, tabId) {
|
|
54
|
-
|
|
55
|
+
el.setAttribute('active', tabId);
|
|
55
56
|
}
|
|
56
57
|
function dispatchTabChangeEvent(el, activeId, activeIndex, previousId, previousIndex) {
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
58
|
+
const event = new CustomEvent('ty-tab-change', {
|
|
59
|
+
detail: {
|
|
60
|
+
activeId,
|
|
61
|
+
activeIndex,
|
|
62
|
+
previousId,
|
|
63
|
+
previousIndex,
|
|
64
|
+
},
|
|
65
|
+
bubbles: true,
|
|
66
|
+
cancelable: false,
|
|
67
|
+
});
|
|
68
|
+
el.dispatchEvent(event);
|
|
68
69
|
}
|
|
69
70
|
function handleTabClick(el, tabId, event) {
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
71
|
+
event.preventDefault();
|
|
72
|
+
event.stopPropagation();
|
|
73
|
+
setActiveTab(el, tabId);
|
|
73
74
|
}
|
|
74
75
|
function cleanupEventListeners(el) {
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
76
|
+
const handlers = eventHandlers.get(el);
|
|
77
|
+
if (!handlers)
|
|
78
|
+
return;
|
|
79
|
+
const shadowRoot = el.shadowRoot;
|
|
80
|
+
if (!shadowRoot)
|
|
81
|
+
return;
|
|
82
|
+
handlers.tabClickHandlers.forEach((handler, tabId) => {
|
|
83
|
+
const button = shadowRoot.querySelector(`[data-tab-id='${tabId}']`);
|
|
84
|
+
if (button) {
|
|
85
|
+
button.removeEventListener('pointerdown', handler);
|
|
86
|
+
}
|
|
87
|
+
});
|
|
88
|
+
handlers.tabKeyClickHandlers.forEach((handler, tabId) => {
|
|
89
|
+
const button = shadowRoot.querySelector(`[data-tab-id='${tabId}']`);
|
|
90
|
+
if (button) {
|
|
91
|
+
button.removeEventListener('click', handler);
|
|
92
|
+
}
|
|
93
|
+
});
|
|
94
|
+
handlers.tabClickHandlers.clear();
|
|
95
|
+
handlers.tabKeyClickHandlers.clear();
|
|
95
96
|
}
|
|
96
97
|
function setupEventListeners(el, shadowRoot, tabs) {
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
98
|
+
cleanupEventListeners(el);
|
|
99
|
+
const handlers = {
|
|
100
|
+
tabClickHandlers: new Map(),
|
|
101
|
+
tabKeyClickHandlers: new Map(),
|
|
102
|
+
};
|
|
103
|
+
tabs.forEach((tab) => {
|
|
104
|
+
const tabId = getTabId(tab);
|
|
105
|
+
if (!tabId)
|
|
106
|
+
return;
|
|
107
|
+
const button = shadowRoot.querySelector(`[data-tab-id='${tabId}']`);
|
|
108
|
+
if (button) {
|
|
109
|
+
const handler = (e) => handleTabClick(el, tabId, e);
|
|
110
|
+
const keyClickHandler = (e) => {
|
|
111
|
+
if (e.detail === 0)
|
|
112
|
+
handleTabClick(el, tabId, e);
|
|
113
|
+
};
|
|
114
|
+
button.addEventListener('pointerdown', handler);
|
|
115
|
+
button.addEventListener('click', keyClickHandler);
|
|
116
|
+
handlers.tabClickHandlers.set(tabId, handler);
|
|
117
|
+
handlers.tabKeyClickHandlers.set(tabId, keyClickHandler);
|
|
118
|
+
}
|
|
119
|
+
});
|
|
120
|
+
eventHandlers.set(el, handlers);
|
|
120
121
|
}
|
|
121
122
|
function updateTransform(el, activeIndex) {
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
123
|
+
const shadowRoot = el.shadowRoot;
|
|
124
|
+
if (!shadowRoot)
|
|
125
|
+
return;
|
|
126
|
+
const panelsWrapper = shadowRoot.querySelector('.panels-wrapper');
|
|
127
|
+
if (!panelsWrapper)
|
|
128
|
+
return;
|
|
129
|
+
const containerWidth = el.offsetWidth;
|
|
130
|
+
const offsetPx = activeIndex * containerWidth;
|
|
131
|
+
panelsWrapper.style.transform = `translateX(-${offsetPx}px)`;
|
|
131
132
|
}
|
|
132
133
|
function updateAriaAttributes(el, shadowRoot, activeId) {
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
134
|
+
const tabs = getChildTabs(el);
|
|
135
|
+
tabs.forEach((tab, idx) => {
|
|
136
|
+
const tabId = getTabId(tab);
|
|
137
|
+
if (!tabId)
|
|
138
|
+
return;
|
|
139
|
+
const button = shadowRoot.querySelector(`[data-tab-id='${tabId}']`);
|
|
140
|
+
const isActive = tabId === activeId;
|
|
141
|
+
if (button) {
|
|
142
|
+
button.setAttribute('aria-selected', String(isActive));
|
|
143
|
+
button.setAttribute('tabindex', isActive ? '0' : '-1');
|
|
144
|
+
button.setAttribute('data-active', String(isActive));
|
|
145
|
+
const slottedLabel = getSlotLabel(el, tabId);
|
|
146
|
+
if (slottedLabel) {
|
|
147
|
+
slottedLabel.setAttribute('data-active', String(isActive));
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
});
|
|
150
151
|
}
|
|
151
152
|
function updatePanelInteraction(el, activeId) {
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
153
|
+
const tabs = getChildTabs(el);
|
|
154
|
+
tabs.forEach((tab) => {
|
|
155
|
+
const tabId = getTabId(tab);
|
|
156
|
+
if (!tabId)
|
|
157
|
+
return;
|
|
158
|
+
const isActive = tabId === activeId;
|
|
159
|
+
tab.setAttribute('data-active', String(isActive));
|
|
160
|
+
if (isActive) {
|
|
161
|
+
tab.style.pointerEvents = 'auto';
|
|
162
|
+
tab.style.opacity = '1';
|
|
163
|
+
}
|
|
164
|
+
else {
|
|
165
|
+
tab.style.pointerEvents = 'none';
|
|
166
|
+
tab.style.opacity = '0';
|
|
167
|
+
}
|
|
168
|
+
});
|
|
168
169
|
}
|
|
169
170
|
function calculateMarkerPosition(el, shadowRoot, activeId) {
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
if (snap) {
|
|
202
|
-
marker.offsetWidth;
|
|
203
|
-
marker.style.transition = '';
|
|
204
|
-
}
|
|
171
|
+
const button = shadowRoot.querySelector(`[data-tab-id='${activeId}']`);
|
|
172
|
+
const buttonsContainer = shadowRoot.querySelector('.tab-buttons');
|
|
173
|
+
if (!button || !buttonsContainer)
|
|
174
|
+
return null;
|
|
175
|
+
const left = button.offsetLeft;
|
|
176
|
+
const top = button.offsetTop;
|
|
177
|
+
const width = button.offsetWidth;
|
|
178
|
+
const height = button.offsetHeight;
|
|
179
|
+
return { left, top, width, height };
|
|
180
|
+
}
|
|
181
|
+
function updateMarker(el, activeId, forceSnap = false) {
|
|
182
|
+
const shadowRoot = el.shadowRoot;
|
|
183
|
+
if (!shadowRoot)
|
|
184
|
+
return;
|
|
185
|
+
const marker = shadowRoot.querySelector('.marker-wrapper');
|
|
186
|
+
if (!marker)
|
|
187
|
+
return;
|
|
188
|
+
const position = calculateMarkerPosition(el, shadowRoot, activeId);
|
|
189
|
+
if (!position)
|
|
190
|
+
return;
|
|
191
|
+
const snap = forceSnap || !marker.style.left || marker.offsetWidth === 0;
|
|
192
|
+
if (snap)
|
|
193
|
+
marker.style.transition = 'none';
|
|
194
|
+
marker.style.left = `${position.left}px`;
|
|
195
|
+
marker.style.top = `${position.top}px`;
|
|
196
|
+
marker.style.width = `${position.width}px`;
|
|
197
|
+
marker.style.height = `${position.height}px`;
|
|
198
|
+
if (snap) {
|
|
199
|
+
marker.offsetWidth;
|
|
200
|
+
marker.style.transition = '';
|
|
201
|
+
}
|
|
205
202
|
}
|
|
206
203
|
function updateActiveTabState(el, tabId, previousId) {
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
204
|
+
const tabs = getChildTabs(el);
|
|
205
|
+
const shadowRoot = el.shadowRoot;
|
|
206
|
+
if (!shadowRoot)
|
|
207
|
+
return;
|
|
208
|
+
const currentIndex = previousId ? findTabIndex(tabs, previousId) : undefined;
|
|
209
|
+
const newIndex = findTabIndex(tabs, tabId);
|
|
210
|
+
if (previousId === tabId || newIndex === undefined)
|
|
211
|
+
return;
|
|
212
|
+
el.style.setProperty('--active-index', String(newIndex));
|
|
213
|
+
updateTransform(el, newIndex);
|
|
214
|
+
updateAriaAttributes(el, shadowRoot, tabId);
|
|
215
|
+
updatePanelInteraction(el, tabId);
|
|
216
|
+
updateMarker(el, tabId, pendingInitialSnap.delete(el));
|
|
217
|
+
shadowRoot.querySelectorAll('.tab-overflow-item').forEach((item) => {
|
|
218
|
+
item.setAttribute('data-active', String(item.dataset.tabId === tabId));
|
|
219
|
+
});
|
|
220
|
+
const newPanel = tabs[newIndex];
|
|
221
|
+
if (newPanel?.resetScroll) {
|
|
222
|
+
newPanel.resetScroll();
|
|
223
|
+
}
|
|
224
|
+
dispatchTabChangeEvent(el, tabId, newIndex, previousId, currentIndex ?? null);
|
|
228
225
|
}
|
|
229
226
|
function setupResizeObserver(el) {
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
227
|
+
const oldObserver = resizeObservers.get(el);
|
|
228
|
+
if (oldObserver) {
|
|
229
|
+
oldObserver.disconnect();
|
|
230
|
+
}
|
|
231
|
+
const observer = new ResizeObserver((entries) => {
|
|
232
|
+
const entry = entries[0];
|
|
233
|
+
const { width } = getTabsAttributes(el);
|
|
234
|
+
const tabs = getChildTabs(el);
|
|
235
|
+
const activeId = getActiveTabId(el, tabs);
|
|
236
|
+
const activeIndex = activeId ? findTabIndex(tabs, activeId) : 0;
|
|
237
|
+
if (width.includes('%')) {
|
|
238
|
+
el.style.setProperty('--tabs-page-width', `${entry.contentRect.width}px`);
|
|
239
|
+
}
|
|
240
|
+
if (activeIndex !== undefined) {
|
|
241
|
+
updateTransform(el, activeIndex);
|
|
242
|
+
}
|
|
243
|
+
updateOverflow(el);
|
|
244
|
+
if (activeId) {
|
|
245
|
+
updateMarker(el, activeId);
|
|
246
|
+
scrollActiveIntoView(el, activeId, false);
|
|
247
|
+
}
|
|
248
|
+
});
|
|
249
|
+
observer.observe(el);
|
|
250
|
+
resizeObservers.set(el, observer);
|
|
254
251
|
}
|
|
255
252
|
function cleanupResizeObserver(el) {
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
253
|
+
const observer = resizeObservers.get(el);
|
|
254
|
+
if (observer) {
|
|
255
|
+
observer.disconnect();
|
|
256
|
+
resizeObservers.delete(el);
|
|
257
|
+
}
|
|
261
258
|
}
|
|
262
259
|
function renderTabButtons(tabsEl, tabs, activeId) {
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
260
|
+
const buttons = tabs.map((tab) => {
|
|
261
|
+
const tabId = getTabId(tab);
|
|
262
|
+
if (!tabId)
|
|
263
|
+
return '';
|
|
264
|
+
const labelType = getTabLabelType(tabsEl, tab);
|
|
265
|
+
const textLabel = labelType === 'text' ? (tab.getAttribute('label') || 'Tab') : '';
|
|
266
|
+
const disabled = isTabDisabled(tab);
|
|
267
|
+
const active = tabId === activeId;
|
|
268
|
+
return `<button
|
|
269
|
+
class="tab-button"
|
|
270
|
+
role="tab"
|
|
271
|
+
data-tab-id="${tabId}"
|
|
272
|
+
id="tab-${tabId}"
|
|
273
|
+
aria-selected="${active}"
|
|
274
|
+
tabindex="${active ? '0' : '-1'}"
|
|
275
|
+
data-active="${active}"
|
|
276
|
+
${disabled ? 'disabled aria-disabled="true"' : ''}
|
|
277
|
+
>
|
|
278
|
+
${labelType === 'slot' ? `<slot name="label-${tabId}"></slot>` : `<span class="tab-label">${textLabel}</span>`}
|
|
279
|
+
</button>`;
|
|
280
|
+
}).join('');
|
|
281
|
+
return `
|
|
282
|
+
<div class="tab-buttons" role="tablist" part="buttons-container">
|
|
283
|
+
<div class="tab-strip" part="strip">
|
|
284
|
+
<div class="marker-wrapper" part="marker-wrapper">
|
|
285
|
+
${!hasCustomMarker(tabsEl) ? '<div class="default-marker"></div>' : ''}
|
|
286
|
+
<slot name="marker"></slot>
|
|
287
|
+
</div>
|
|
288
|
+
${buttons}
|
|
289
|
+
</div>
|
|
290
|
+
</div>
|
|
291
|
+
`;
|
|
295
292
|
}
|
|
296
293
|
function renderButtonsOnly(tabsEl, tabs, activeId) {
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
294
|
+
return tabs.map((tab) => {
|
|
295
|
+
const tabId = getTabId(tab);
|
|
296
|
+
if (!tabId)
|
|
297
|
+
return '';
|
|
298
|
+
const labelType = getTabLabelType(tabsEl, tab);
|
|
299
|
+
const textLabel = labelType === 'text' ? (tab.getAttribute('label') || 'Tab') : '';
|
|
300
|
+
const disabled = isTabDisabled(tab);
|
|
301
|
+
const active = tabId === activeId;
|
|
302
|
+
return `<button
|
|
303
|
+
class="tab-button"
|
|
304
|
+
role="tab"
|
|
305
|
+
data-tab-id="${tabId}"
|
|
306
|
+
id="tab-${tabId}"
|
|
307
|
+
aria-selected="${active}"
|
|
308
|
+
tabindex="${active ? '0' : '-1'}"
|
|
309
|
+
data-active="${active}"
|
|
310
|
+
${disabled ? 'disabled aria-disabled="true"' : ''}
|
|
311
|
+
>
|
|
312
|
+
${labelType === 'slot' ? `<slot name="label-${tabId}"></slot>` : `<span class="tab-label">${textLabel}</span>`}
|
|
313
|
+
</button>`;
|
|
314
|
+
}).join('');
|
|
318
315
|
}
|
|
319
316
|
function getTabMenuLabel(tabsEl, tab) {
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
317
|
+
const tabId = getTabId(tab);
|
|
318
|
+
if (tabId && getTabLabelType(tabsEl, tab) === 'slot') {
|
|
319
|
+
const slotted = getSlotLabel(tabsEl, tabId);
|
|
320
|
+
return slotted?.textContent?.trim() || tabId;
|
|
321
|
+
}
|
|
322
|
+
return tab.getAttribute('label') || 'Tab';
|
|
326
323
|
}
|
|
327
324
|
function removeOverflowMenu(shadowRoot) {
|
|
328
|
-
|
|
325
|
+
shadowRoot.querySelector('.tab-overflow-trigger')?.remove();
|
|
329
326
|
}
|
|
330
327
|
function renderOverflowMenu(el, container, allTabs) {
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
328
|
+
const activeId = getActiveTabId(el, allTabs);
|
|
329
|
+
const trigger = document.createElement('button');
|
|
330
|
+
trigger.className = 'tab-overflow-trigger';
|
|
331
|
+
trigger.type = 'button';
|
|
332
|
+
trigger.setAttribute('aria-label', `${allTabs.length} tabs`);
|
|
333
|
+
trigger.textContent = '⋯';
|
|
334
|
+
const popup = document.createElement('ty-popup');
|
|
335
|
+
popup.setAttribute('placement', 'bottom');
|
|
336
|
+
const menu = document.createElement('div');
|
|
337
|
+
menu.className = 'tab-overflow-menu';
|
|
338
|
+
menu.setAttribute('role', 'menu');
|
|
339
|
+
allTabs.forEach((tab) => {
|
|
340
|
+
const tabId = getTabId(tab);
|
|
341
|
+
if (!tabId)
|
|
342
|
+
return;
|
|
343
|
+
const item = document.createElement('button');
|
|
344
|
+
item.type = 'button';
|
|
345
|
+
item.className = 'tab-overflow-item';
|
|
346
|
+
item.setAttribute('role', 'menuitem');
|
|
347
|
+
item.setAttribute('data-tab-id', tabId);
|
|
348
|
+
item.setAttribute('data-active', String(tabId === activeId));
|
|
349
|
+
item.textContent = getTabMenuLabel(el, tab);
|
|
350
|
+
if (isTabDisabled(tab)) {
|
|
351
|
+
item.disabled = true;
|
|
352
|
+
}
|
|
353
|
+
else {
|
|
354
|
+
item.addEventListener('click', () => {
|
|
355
|
+
setActiveTab(el, tabId);
|
|
356
|
+
popup.closePopup?.();
|
|
357
|
+
});
|
|
358
|
+
}
|
|
359
|
+
menu.appendChild(item);
|
|
360
|
+
});
|
|
361
|
+
popup.appendChild(menu);
|
|
362
|
+
trigger.appendChild(popup);
|
|
363
|
+
container.appendChild(trigger);
|
|
367
364
|
}
|
|
368
365
|
function updateOverflow(el) {
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
366
|
+
const shadowRoot = el.shadowRoot;
|
|
367
|
+
if (!shadowRoot)
|
|
368
|
+
return;
|
|
369
|
+
const container = shadowRoot.querySelector('.tab-buttons');
|
|
370
|
+
const strip = shadowRoot.querySelector('.tab-strip');
|
|
371
|
+
if (!container || !strip)
|
|
372
|
+
return;
|
|
373
|
+
if (getTabsAttributes(el).fixed) {
|
|
374
|
+
removeOverflowMenu(shadowRoot);
|
|
375
|
+
strip.classList.add('unclipped');
|
|
376
|
+
strip.style.removeProperty('--fade-left');
|
|
377
|
+
strip.style.removeProperty('--fade-right');
|
|
378
|
+
return;
|
|
379
|
+
}
|
|
380
|
+
const savedScrollLeft = strip.scrollLeft;
|
|
381
|
+
removeOverflowMenu(shadowRoot);
|
|
382
|
+
if (strip.clientWidth <= 0)
|
|
383
|
+
return;
|
|
384
|
+
const buttons = strip.querySelectorAll('.tab-button');
|
|
385
|
+
const last = buttons[buttons.length - 1];
|
|
386
|
+
const contentWidth = last ? last.offsetLeft + last.offsetWidth : 0;
|
|
387
|
+
const stripWidth = strip.clientWidth;
|
|
388
|
+
const hidden = contentWidth - stripWidth;
|
|
389
|
+
strip.classList.toggle('unclipped', hidden <= 1);
|
|
390
|
+
if (hidden <= 1) {
|
|
391
|
+
strip.scrollLeft = savedScrollLeft;
|
|
392
|
+
updateEdgeFades(strip);
|
|
393
|
+
return;
|
|
394
|
+
}
|
|
395
|
+
renderOverflowMenu(el, container, getChildTabs(el));
|
|
396
|
+
const triggerCost = container.clientWidth - strip.clientWidth;
|
|
397
|
+
if (hidden <= triggerCost) {
|
|
398
|
+
removeOverflowMenu(shadowRoot);
|
|
399
|
+
}
|
|
400
|
+
strip.scrollLeft = savedScrollLeft;
|
|
401
|
+
updateEdgeFades(strip);
|
|
405
402
|
}
|
|
406
403
|
function updateEdgeFades(strip) {
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
404
|
+
const maxScroll = strip.scrollWidth - strip.clientWidth;
|
|
405
|
+
const fade = (distance) => `${distance > 1 ? Math.min(28, distance) : 0}px`;
|
|
406
|
+
strip.style.setProperty('--fade-left', fade(strip.scrollLeft));
|
|
407
|
+
strip.style.setProperty('--fade-right', fade(maxScroll - strip.scrollLeft));
|
|
411
408
|
}
|
|
412
409
|
function scrollActiveIntoView(el, activeId, smooth) {
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
410
|
+
const shadowRoot = el.shadowRoot;
|
|
411
|
+
if (!shadowRoot)
|
|
412
|
+
return;
|
|
413
|
+
const strip = shadowRoot.querySelector('.tab-strip');
|
|
414
|
+
const button = shadowRoot.querySelector(`[data-tab-id='${activeId}']`);
|
|
415
|
+
if (!strip || !button || strip.clientWidth <= 0)
|
|
416
|
+
return;
|
|
417
|
+
const centered = button.offsetLeft + button.offsetWidth / 2 - strip.clientWidth / 2;
|
|
418
|
+
const target = Math.max(0, Math.min(centered, strip.scrollWidth - strip.clientWidth));
|
|
419
|
+
if (Math.round(target) !== Math.round(strip.scrollLeft)) {
|
|
420
|
+
const reduce = window.matchMedia('(prefers-reduced-motion: reduce)').matches;
|
|
421
|
+
strip.scrollTo({ left: target, behavior: smooth && !reduce ? 'smooth' : 'auto' });
|
|
422
|
+
}
|
|
426
423
|
}
|
|
427
424
|
function render(el) {
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
425
|
+
const shadowRoot = el.shadowRoot;
|
|
426
|
+
if (!shadowRoot)
|
|
427
|
+
return;
|
|
428
|
+
const { width, height, placement, fixed } = getTabsAttributes(el);
|
|
429
|
+
const tabs = getChildTabs(el);
|
|
430
|
+
const activeId = getActiveTabId(el, tabs);
|
|
431
|
+
const activeIndex = activeId ? (findTabIndex(tabs, activeId) ?? 0) : 0;
|
|
432
|
+
tabs.forEach((tab) => {
|
|
433
|
+
const tabId = getTabId(tab);
|
|
434
|
+
if (!tabId)
|
|
435
|
+
return;
|
|
436
|
+
tab.setAttribute('role', 'tabpanel');
|
|
437
|
+
tab.setAttribute('aria-labelledby', `tab-${tabId}`);
|
|
438
|
+
tab.setAttribute('tabindex', '0');
|
|
439
|
+
});
|
|
440
|
+
const existingContainer = shadowRoot.querySelector('.tabs-container');
|
|
441
|
+
const existingButtons = shadowRoot.querySelector('.tab-buttons');
|
|
442
|
+
const existingStrip = shadowRoot.querySelector('.tab-strip');
|
|
443
|
+
const existingViewport = shadowRoot.querySelector('.panels-viewport');
|
|
444
|
+
ensureStyles(shadowRoot, { css: tabsStyles, id: 'ty-tabs' });
|
|
445
|
+
el.style.setProperty('--tabs-width', width.includes('%') ? '100%' : width);
|
|
446
|
+
el.style.setProperty('--tabs-height', height);
|
|
447
|
+
el.style.setProperty('--active-index', String(activeIndex));
|
|
448
|
+
if (existingContainer && existingButtons && existingStrip && existingViewport) {
|
|
449
|
+
existingContainer.setAttribute('data-placement', placement);
|
|
450
|
+
existingContainer.toggleAttribute('data-fixed', fixed);
|
|
451
|
+
const savedScrollLeft = existingStrip.scrollLeft;
|
|
452
|
+
existingButtons.querySelector('.tab-overflow-trigger')?.remove();
|
|
453
|
+
const allButtons = existingStrip.querySelectorAll('.tab-button');
|
|
454
|
+
allButtons.forEach(button => button.remove());
|
|
455
|
+
const buttonsHtml = renderButtonsOnly(el, tabs, activeId);
|
|
456
|
+
const tempDiv = document.createElement('div');
|
|
457
|
+
tempDiv.innerHTML = buttonsHtml;
|
|
458
|
+
Array.from(tempDiv.children).forEach(button => {
|
|
459
|
+
existingStrip.appendChild(button);
|
|
460
|
+
});
|
|
461
|
+
existingStrip.scrollLeft = savedScrollLeft;
|
|
462
|
+
setupEventListeners(el, shadowRoot, tabs);
|
|
463
|
+
updateAriaAttributes(el, shadowRoot, activeId || '');
|
|
464
|
+
requestAnimationFrame(() => {
|
|
465
|
+
const buttons = shadowRoot.querySelector('.tab-buttons');
|
|
466
|
+
if (buttons) {
|
|
467
|
+
const buttonsHeight = buttons.offsetHeight;
|
|
468
|
+
el.style.setProperty('--buttons-height', `${buttonsHeight}px`);
|
|
469
|
+
}
|
|
470
|
+
updateTransform(el, activeIndex);
|
|
471
|
+
updateOverflow(el);
|
|
472
|
+
if (activeId) {
|
|
473
|
+
updateMarker(el, activeId);
|
|
474
|
+
scrollActiveIntoView(el, activeId, true);
|
|
475
|
+
}
|
|
476
|
+
});
|
|
477
|
+
if (activeId) {
|
|
478
|
+
updatePanelInteraction(el, activeId);
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
else {
|
|
482
|
+
pendingInitialSnap.add(el);
|
|
483
|
+
shadowRoot.innerHTML = `
|
|
484
|
+
<div class="tabs-container" data-placement="${placement}"${fixed ? ' data-fixed' : ''}>
|
|
485
|
+
${renderTabButtons(el, tabs, activeId)}
|
|
486
|
+
<div class="panels-viewport" part="panels-container">
|
|
487
|
+
<div class="panels-wrapper">
|
|
488
|
+
<slot></slot>
|
|
489
|
+
</div>
|
|
490
|
+
</div>
|
|
491
|
+
</div>
|
|
492
|
+
`;
|
|
493
|
+
requestAnimationFrame(() => {
|
|
494
|
+
const buttons = shadowRoot.querySelector('.tab-buttons');
|
|
495
|
+
if (buttons) {
|
|
496
|
+
const buttonsHeight = buttons.offsetHeight;
|
|
497
|
+
el.style.setProperty('--buttons-height', `${buttonsHeight}px`);
|
|
498
|
+
}
|
|
499
|
+
updateTransform(el, activeIndex);
|
|
500
|
+
updateOverflow(el);
|
|
501
|
+
if (activeId) {
|
|
502
|
+
updateMarker(el, activeId);
|
|
503
|
+
scrollActiveIntoView(el, activeId, false);
|
|
504
|
+
}
|
|
505
|
+
});
|
|
506
|
+
setupEventListeners(el, shadowRoot, tabs);
|
|
507
|
+
updateAriaAttributes(el, shadowRoot, activeId || '');
|
|
508
|
+
setupResizeObserver(el);
|
|
509
|
+
const strip = shadowRoot.querySelector('.tab-strip');
|
|
510
|
+
strip?.addEventListener('scroll', () => updateEdgeFades(strip), { passive: true });
|
|
511
|
+
if (activeId) {
|
|
512
|
+
updatePanelInteraction(el, activeId);
|
|
513
|
+
}
|
|
514
|
+
const markerSlot = shadowRoot.querySelector('slot[name="marker"]');
|
|
515
|
+
if (markerSlot) {
|
|
516
|
+
const updateDefaultMarkerVisibility = () => {
|
|
517
|
+
const defaultMarker = shadowRoot.querySelector('.default-marker');
|
|
518
|
+
if (defaultMarker) {
|
|
519
|
+
const hasCustom = markerSlot.assignedElements().length > 0;
|
|
520
|
+
defaultMarker.style.display = hasCustom ? 'none' : '';
|
|
521
|
+
}
|
|
522
|
+
};
|
|
523
|
+
markerSlot.addEventListener('slotchange', updateDefaultMarkerVisibility);
|
|
524
|
+
updateDefaultMarkerVisibility();
|
|
525
|
+
}
|
|
526
|
+
}
|
|
529
527
|
}
|
|
530
528
|
function cleanup(el) {
|
|
531
|
-
|
|
532
|
-
|
|
529
|
+
cleanupEventListeners(el);
|
|
530
|
+
cleanupResizeObserver(el);
|
|
533
531
|
}
|
|
534
532
|
export class TyTabs extends HTMLElement {
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
533
|
+
static get observedAttributes() {
|
|
534
|
+
return ['width', 'height', 'active', 'placement', 'fixed'];
|
|
535
|
+
}
|
|
536
|
+
constructor() {
|
|
537
|
+
super();
|
|
538
|
+
this._childObserver = null;
|
|
539
|
+
this._childRenderQueued = false;
|
|
540
|
+
this.attachShadow({ mode: 'open' });
|
|
541
|
+
}
|
|
542
|
+
connectedCallback() {
|
|
543
|
+
render(this);
|
|
544
|
+
this._childObserver = new MutationObserver(() => {
|
|
545
|
+
if (this._childRenderQueued)
|
|
546
|
+
return;
|
|
547
|
+
this._childRenderQueued = true;
|
|
548
|
+
queueMicrotask(() => {
|
|
549
|
+
this._childRenderQueued = false;
|
|
550
|
+
if (this.isConnected)
|
|
551
|
+
render(this);
|
|
552
|
+
});
|
|
553
|
+
});
|
|
554
|
+
this._childObserver.observe(this, { childList: true });
|
|
555
|
+
}
|
|
556
|
+
disconnectedCallback() {
|
|
557
|
+
this._childObserver?.disconnect();
|
|
558
|
+
this._childObserver = null;
|
|
559
|
+
cleanup(this);
|
|
560
|
+
}
|
|
561
|
+
attributeChangedCallback(name, oldValue, newValue) {
|
|
562
|
+
if (name === 'active') {
|
|
563
|
+
if (!newValue)
|
|
564
|
+
return;
|
|
565
|
+
if (!this.shadowRoot?.querySelector(`[data-tab-id='${newValue}']`)) {
|
|
566
|
+
render(this);
|
|
567
|
+
return;
|
|
568
|
+
}
|
|
569
|
+
updateActiveTabState(this, newValue, oldValue);
|
|
570
|
+
scrollActiveIntoView(this, newValue, true);
|
|
571
|
+
}
|
|
572
|
+
else {
|
|
573
|
+
render(this);
|
|
574
|
+
}
|
|
575
|
+
}
|
|
578
576
|
}
|
|
579
577
|
if (!customElements.get('ty-tabs')) {
|
|
580
|
-
|
|
578
|
+
customElements.define('ty-tabs', TyTabs);
|
|
581
579
|
}
|