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