tyrell-components 1.0.0-TC51 → 1.0.0-TC52
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/tyrell.js +1 -1
- package/lib/components/tabs.d.ts +1 -0
- package/lib/components/tabs.js +43 -15
- package/lib/styles/tab.d.ts +1 -1
- package/lib/styles/tab.js +1 -1
- package/lib/styles/tabs.d.ts +1 -1
- package/lib/styles/tabs.js +32 -13
- package/lib/version.d.ts +1 -1
- package/lib/version.js +1 -1
- package/package.json +1 -1
package/lib/components/tabs.d.ts
CHANGED
package/lib/components/tabs.js
CHANGED
|
@@ -8,6 +8,7 @@ function getTabsAttributes(el) {
|
|
|
8
8
|
height: el.getAttribute('height') || '400px',
|
|
9
9
|
active: el.getAttribute('active'),
|
|
10
10
|
placement: (el.getAttribute('placement') || 'top'),
|
|
11
|
+
fixed: el.hasAttribute('fixed'),
|
|
11
12
|
};
|
|
12
13
|
}
|
|
13
14
|
function getChildTabs(el) {
|
|
@@ -216,6 +217,9 @@ function updateActiveTabState(el, tabId, previousId) {
|
|
|
216
217
|
updateAriaAttributes(el, shadowRoot, tabId);
|
|
217
218
|
updatePanelInteraction(el, tabId);
|
|
218
219
|
updateMarker(el, tabId);
|
|
220
|
+
shadowRoot.querySelectorAll('.tab-overflow-item').forEach((item) => {
|
|
221
|
+
item.setAttribute('data-active', String(item.dataset.tabId === tabId));
|
|
222
|
+
});
|
|
219
223
|
const newPanel = tabs[newIndex];
|
|
220
224
|
if (newPanel?.resetScroll) {
|
|
221
225
|
newPanel.resetScroll();
|
|
@@ -274,7 +278,7 @@ function renderTabButtons(tabsEl, tabs, activeId) {
|
|
|
274
278
|
data-active="${active}"
|
|
275
279
|
${disabled ? 'disabled aria-disabled="true"' : ''}
|
|
276
280
|
>
|
|
277
|
-
${labelType === 'slot' ? `<slot name="label-${tabId}"></slot>` : textLabel}
|
|
281
|
+
${labelType === 'slot' ? `<slot name="label-${tabId}"></slot>` : `<span class="tab-label">${textLabel}</span>`}
|
|
278
282
|
</button>`;
|
|
279
283
|
}).join('');
|
|
280
284
|
return `
|
|
@@ -308,7 +312,7 @@ function renderButtonsOnly(tabsEl, tabs, activeId) {
|
|
|
308
312
|
data-active="${active}"
|
|
309
313
|
${disabled ? 'disabled aria-disabled="true"' : ''}
|
|
310
314
|
>
|
|
311
|
-
${labelType === 'slot' ? `<slot name="label-${tabId}"></slot>` : textLabel}
|
|
315
|
+
${labelType === 'slot' ? `<slot name="label-${tabId}"></slot>` : `<span class="tab-label">${textLabel}</span>`}
|
|
312
316
|
</button>`;
|
|
313
317
|
}).join('');
|
|
314
318
|
}
|
|
@@ -343,6 +347,7 @@ function renderOverflowMenu(el, container, allTabs) {
|
|
|
343
347
|
item.type = 'button';
|
|
344
348
|
item.className = 'tab-overflow-item';
|
|
345
349
|
item.setAttribute('role', 'menuitem');
|
|
350
|
+
item.setAttribute('data-tab-id', tabId);
|
|
346
351
|
item.setAttribute('data-active', String(tabId === activeId));
|
|
347
352
|
item.textContent = getTabMenuLabel(el, tab);
|
|
348
353
|
if (isTabDisabled(tab)) {
|
|
@@ -368,23 +373,41 @@ function updateOverflow(el) {
|
|
|
368
373
|
const strip = shadowRoot.querySelector('.tab-strip');
|
|
369
374
|
if (!container || !strip)
|
|
370
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;
|
|
371
384
|
removeOverflowMenu(shadowRoot);
|
|
372
385
|
if (strip.clientWidth <= 0)
|
|
373
386
|
return;
|
|
374
|
-
updateEdgeFades(strip);
|
|
375
387
|
const buttons = strip.querySelectorAll('.tab-button');
|
|
376
388
|
const last = buttons[buttons.length - 1];
|
|
377
389
|
const contentWidth = last ? last.offsetLeft + last.offsetWidth : 0;
|
|
378
|
-
const
|
|
379
|
-
|
|
380
|
-
|
|
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);
|
|
381
396
|
return;
|
|
397
|
+
}
|
|
382
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);
|
|
383
405
|
}
|
|
384
406
|
function updateEdgeFades(strip) {
|
|
385
407
|
const maxScroll = strip.scrollWidth - strip.clientWidth;
|
|
386
|
-
|
|
387
|
-
strip.style.setProperty('--fade-
|
|
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));
|
|
388
411
|
}
|
|
389
412
|
function scrollActiveIntoView(el, activeId, smooth) {
|
|
390
413
|
const shadowRoot = el.shadowRoot;
|
|
@@ -405,7 +428,7 @@ function render(el) {
|
|
|
405
428
|
const shadowRoot = el.shadowRoot;
|
|
406
429
|
if (!shadowRoot)
|
|
407
430
|
return;
|
|
408
|
-
const { width, height, placement } = getTabsAttributes(el);
|
|
431
|
+
const { width, height, placement, fixed } = getTabsAttributes(el);
|
|
409
432
|
const tabs = getChildTabs(el);
|
|
410
433
|
const activeId = getActiveTabId(el, tabs);
|
|
411
434
|
const activeIndex = activeId ? (findTabIndex(tabs, activeId) ?? 0) : 0;
|
|
@@ -427,8 +450,9 @@ function render(el) {
|
|
|
427
450
|
el.style.setProperty('--active-index', String(activeIndex));
|
|
428
451
|
if (existingContainer && existingButtons && existingStrip && existingViewport) {
|
|
429
452
|
existingContainer.setAttribute('data-placement', placement);
|
|
430
|
-
|
|
453
|
+
existingContainer.toggleAttribute('data-fixed', fixed);
|
|
431
454
|
const savedScrollLeft = existingStrip.scrollLeft;
|
|
455
|
+
existingButtons.querySelector('.tab-overflow-trigger')?.remove();
|
|
432
456
|
const allButtons = existingStrip.querySelectorAll('.tab-button');
|
|
433
457
|
allButtons.forEach(button => button.remove());
|
|
434
458
|
const buttonsHtml = renderButtonsOnly(el, tabs, activeId);
|
|
@@ -459,7 +483,7 @@ function render(el) {
|
|
|
459
483
|
}
|
|
460
484
|
else {
|
|
461
485
|
shadowRoot.innerHTML = `
|
|
462
|
-
<div class="tabs-container" data-placement="${placement}">
|
|
486
|
+
<div class="tabs-container" data-placement="${placement}"${fixed ? ' data-fixed' : ''}>
|
|
463
487
|
${renderTabButtons(el, tabs, activeId)}
|
|
464
488
|
<div class="panels-viewport" part="panels-container">
|
|
465
489
|
<div class="panels-wrapper">
|
|
@@ -509,7 +533,7 @@ function cleanup(el) {
|
|
|
509
533
|
}
|
|
510
534
|
export class TyTabs extends HTMLElement {
|
|
511
535
|
static get observedAttributes() {
|
|
512
|
-
return ['width', 'height', 'active', 'placement'];
|
|
536
|
+
return ['width', 'height', 'active', 'placement', 'fixed'];
|
|
513
537
|
}
|
|
514
538
|
constructor() {
|
|
515
539
|
super();
|
|
@@ -538,10 +562,14 @@ export class TyTabs extends HTMLElement {
|
|
|
538
562
|
}
|
|
539
563
|
attributeChangedCallback(name, oldValue, newValue) {
|
|
540
564
|
if (name === 'active') {
|
|
541
|
-
if (newValue)
|
|
542
|
-
|
|
565
|
+
if (!newValue)
|
|
566
|
+
return;
|
|
567
|
+
if (!this.shadowRoot?.querySelector(`[data-tab-id='${newValue}']`)) {
|
|
568
|
+
render(this);
|
|
569
|
+
return;
|
|
543
570
|
}
|
|
544
|
-
|
|
571
|
+
updateActiveTabState(this, newValue, oldValue);
|
|
572
|
+
scrollActiveIntoView(this, newValue, true);
|
|
545
573
|
}
|
|
546
574
|
else {
|
|
547
575
|
render(this);
|
package/lib/styles/tab.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Tab Component Styles
|
|
3
3
|
*/
|
|
4
|
-
export declare const tabStyles = "\n:host {\n width: var(--tabs-width, 100%);\n height: calc(var(--tabs-height, 400px) - var(--buttons-height, 48px));\n display: block;\n box-sizing: border-box;\n flex-shrink: 0;\n transition: opacity var(--transition-duration, 400ms) var(--transition-easing, ease-in-out);\n}\n\n@media (prefers-reduced-motion: reduce) {\n :host {\n transition-duration: 0ms !important;\n }\n}\n\n.tab-wrapper {\n width: 100%;\n height: 100%;\n position: relative;\n overflow: hidden;\n}\n\n.tab-panel {\n width: 100%;\n height: 100%;\n overflow: auto;\n box-sizing: border-box;\n}\n\n.tab-panel.ty-custom-scroll {\n scrollbar-width: none;\n -ms-overflow-style: none;\n}\n\n.tab-panel.ty-custom-scroll::-webkit-scrollbar {\n display: none;\n}\n\n.tab-wrapper:hover .ty-scrollbar-track-y.has-overflow {\n opacity: 1;\n}\n\n\n/* Vertical track */\n.ty-scrollbar-track-y {\n position: absolute;\n top: 0;\n right: 0;\n bottom: 0;\n width: var(--ty-scrollbar-width, 8px);\n z-index: 3;\n opacity: 0;\n pointer-events: none;\n transition: opacity 0.25s ease-out;\n cursor: pointer;\n}\n\n.ty-scrollbar-track-y.has-overflow {\n pointer-events: auto;\n}\n\n.ty-scrollbar-track-y.has-overflow:hover,\n.ty-scrollbar-track-y.has-overflow.scrolling,\n.ty-scrollbar-track-y.dragging {\n opacity: 1;\n}\n\n.ty-scrollbar-track-y::before {\n content: '';\n position: absolute;\n inset: 0;\n background: var(--ty-scrollbar-track, transparent);\n border-radius: var(--ty-scrollbar-radius, 4px);\n opacity: 0;\n transition: opacity 0.15s ease-out;\n}\n\n.ty-scrollbar-track-y:hover::before,\n.ty-scrollbar-track-y.dragging::before {\n opacity: 1;\n background: var(--ty-scrollbar-track-hover, color-mix(in oklab, var(--ty-color-neutral-bold) 8%, transparent));\n}\n\n.ty-scrollbar-thumb-y {\n position: absolute;\n right: 0;\n width: 100%;\n min-height: var(--ty-scrollbar-thumb-min-height, 30px);\n background: var(--ty-scrollbar-thumb, color-mix(in oklab, var(--ty-color-neutral-bold) 40%, transparent));\n border-radius: var(--ty-scrollbar-radius, 4px);\n transition: var(--ty-local-transition, background 0.15s ease-out);\n box-sizing: border-box;\n border: 1px solid transparent;\n}\n\n.ty-scrollbar-thumb-y:hover,\n.ty-scrollbar-track-y.dragging .ty-scrollbar-thumb-y {\n background: var(--ty-scrollbar-thumb-hover, color-mix(in oklab, var(--ty-color-neutral-bold) 55%, transparent));\n}\n\n.ty-scrollbar-thumb-y:active,\n.ty-scrollbar-track-y.dragging .ty-scrollbar-thumb-y {\n background: var(--ty-scrollbar-thumb-active, color-mix(in oklab, var(--ty-color-neutral-bold) 70%, transparent));\n}\n\n/* Horizontal track */\n.ty-scrollbar-track-x {\n position: absolute;\n bottom: 0;\n left: 0;\n right: 0;\n height: var(--ty-scrollbar-width, 8px);\n z-index: 3;\n opacity: 0;\n pointer-events: none;\n transition: opacity 0.25s ease-out;\n cursor: pointer;\n}\n\n.ty-scrollbar-track-x.has-overflow {\n pointer-events: auto;\n}\n\n.ty-scrollbar-track-x.has-overflow:hover,\n.ty-scrollbar-track-x.has-overflow.scrolling,\n.ty-scrollbar-track-x.dragging {\n opacity: 1;\n}\n\n.ty-scrollbar-track-x::before {\n content: '';\n position: absolute;\n inset: 0;\n background: var(--ty-scrollbar-track, transparent);\n border-radius: var(--ty-scrollbar-radius, 4px);\n opacity: 0;\n transition: opacity 0.15s ease-out;\n}\n\n.ty-scrollbar-track-x:hover::before,\n.ty-scrollbar-track-x.dragging::before {\n opacity: 1;\n background: var(--ty-scrollbar-track-hover, color-mix(in oklab, var(--ty-color-neutral-bold) 8%, transparent));\n}\n\n.ty-scrollbar-thumb-x {\n position: absolute;\n bottom: 0;\n height: 100%;\n min-width: var(--ty-scrollbar-thumb-min-height, 30px);\n background: var(--ty-scrollbar-thumb, color-mix(in oklab, var(--ty-color-neutral-bold) 40%, transparent));\n border-radius: var(--ty-scrollbar-radius, 4px);\n transition: var(--ty-local-transition, background 0.15s ease-out);\n box-sizing: border-box;\n border: 1px solid transparent;\n}\n\n.ty-scrollbar-thumb-x:hover,\n.ty-scrollbar-track-x.dragging .ty-scrollbar-thumb-x {\n background: var(--ty-scrollbar-thumb-hover, color-mix(in oklab, var(--ty-color-neutral-bold) 55%, transparent));\n}\n\n.ty-scrollbar-thumb-x:active,\n.ty-scrollbar-track-x.dragging .ty-scrollbar-thumb-x {\n background: var(--ty-scrollbar-thumb-active, color-mix(in oklab, var(--ty-color-neutral-bold) 70%, transparent));\n}\n\n/* Touch devices: hide the custom scrollbar */\n@media (pointer: coarse) and (hover: none) {\n .ty-scrollbar-track-y,\n .ty-scrollbar-track-x {\n display: none !important;\n }\n}\n\n@media (prefers-reduced-motion: reduce) {\n .ty-scrollbar-track-y,\n .ty-scrollbar-track-x,\n .ty-scrollbar-thumb-y,\n .ty-scrollbar-thumb-x {\n transition-duration: 0ms !important;\n }\n}\n\n";
|
|
4
|
+
export declare const tabStyles = "\n:host {\n width: var(--tabs-width, 100%);\n height: calc(var(--tabs-height, 400px) - var(--buttons-height, 48px));\n display: block;\n box-sizing: border-box;\n flex-shrink: 0;\n transition: opacity var(--ty-tabs-transition-duration, 400ms) var(--ty-tabs-transition-easing, ease-in-out);\n}\n\n@media (prefers-reduced-motion: reduce) {\n :host {\n transition-duration: 0ms !important;\n }\n}\n\n.tab-wrapper {\n width: 100%;\n height: 100%;\n position: relative;\n overflow: hidden;\n}\n\n.tab-panel {\n width: 100%;\n height: 100%;\n overflow: auto;\n box-sizing: border-box;\n}\n\n.tab-panel.ty-custom-scroll {\n scrollbar-width: none;\n -ms-overflow-style: none;\n}\n\n.tab-panel.ty-custom-scroll::-webkit-scrollbar {\n display: none;\n}\n\n.tab-wrapper:hover .ty-scrollbar-track-y.has-overflow {\n opacity: 1;\n}\n\n\n/* Vertical track */\n.ty-scrollbar-track-y {\n position: absolute;\n top: 0;\n right: 0;\n bottom: 0;\n width: var(--ty-scrollbar-width, 8px);\n z-index: 3;\n opacity: 0;\n pointer-events: none;\n transition: opacity 0.25s ease-out;\n cursor: pointer;\n}\n\n.ty-scrollbar-track-y.has-overflow {\n pointer-events: auto;\n}\n\n.ty-scrollbar-track-y.has-overflow:hover,\n.ty-scrollbar-track-y.has-overflow.scrolling,\n.ty-scrollbar-track-y.dragging {\n opacity: 1;\n}\n\n.ty-scrollbar-track-y::before {\n content: '';\n position: absolute;\n inset: 0;\n background: var(--ty-scrollbar-track, transparent);\n border-radius: var(--ty-scrollbar-radius, 4px);\n opacity: 0;\n transition: opacity 0.15s ease-out;\n}\n\n.ty-scrollbar-track-y:hover::before,\n.ty-scrollbar-track-y.dragging::before {\n opacity: 1;\n background: var(--ty-scrollbar-track-hover, color-mix(in oklab, var(--ty-color-neutral-bold) 8%, transparent));\n}\n\n.ty-scrollbar-thumb-y {\n position: absolute;\n right: 0;\n width: 100%;\n min-height: var(--ty-scrollbar-thumb-min-height, 30px);\n background: var(--ty-scrollbar-thumb, color-mix(in oklab, var(--ty-color-neutral-bold) 40%, transparent));\n border-radius: var(--ty-scrollbar-radius, 4px);\n transition: var(--ty-local-transition, background 0.15s ease-out);\n box-sizing: border-box;\n border: 1px solid transparent;\n}\n\n.ty-scrollbar-thumb-y:hover,\n.ty-scrollbar-track-y.dragging .ty-scrollbar-thumb-y {\n background: var(--ty-scrollbar-thumb-hover, color-mix(in oklab, var(--ty-color-neutral-bold) 55%, transparent));\n}\n\n.ty-scrollbar-thumb-y:active,\n.ty-scrollbar-track-y.dragging .ty-scrollbar-thumb-y {\n background: var(--ty-scrollbar-thumb-active, color-mix(in oklab, var(--ty-color-neutral-bold) 70%, transparent));\n}\n\n/* Horizontal track */\n.ty-scrollbar-track-x {\n position: absolute;\n bottom: 0;\n left: 0;\n right: 0;\n height: var(--ty-scrollbar-width, 8px);\n z-index: 3;\n opacity: 0;\n pointer-events: none;\n transition: opacity 0.25s ease-out;\n cursor: pointer;\n}\n\n.ty-scrollbar-track-x.has-overflow {\n pointer-events: auto;\n}\n\n.ty-scrollbar-track-x.has-overflow:hover,\n.ty-scrollbar-track-x.has-overflow.scrolling,\n.ty-scrollbar-track-x.dragging {\n opacity: 1;\n}\n\n.ty-scrollbar-track-x::before {\n content: '';\n position: absolute;\n inset: 0;\n background: var(--ty-scrollbar-track, transparent);\n border-radius: var(--ty-scrollbar-radius, 4px);\n opacity: 0;\n transition: opacity 0.15s ease-out;\n}\n\n.ty-scrollbar-track-x:hover::before,\n.ty-scrollbar-track-x.dragging::before {\n opacity: 1;\n background: var(--ty-scrollbar-track-hover, color-mix(in oklab, var(--ty-color-neutral-bold) 8%, transparent));\n}\n\n.ty-scrollbar-thumb-x {\n position: absolute;\n bottom: 0;\n height: 100%;\n min-width: var(--ty-scrollbar-thumb-min-height, 30px);\n background: var(--ty-scrollbar-thumb, color-mix(in oklab, var(--ty-color-neutral-bold) 40%, transparent));\n border-radius: var(--ty-scrollbar-radius, 4px);\n transition: var(--ty-local-transition, background 0.15s ease-out);\n box-sizing: border-box;\n border: 1px solid transparent;\n}\n\n.ty-scrollbar-thumb-x:hover,\n.ty-scrollbar-track-x.dragging .ty-scrollbar-thumb-x {\n background: var(--ty-scrollbar-thumb-hover, color-mix(in oklab, var(--ty-color-neutral-bold) 55%, transparent));\n}\n\n.ty-scrollbar-thumb-x:active,\n.ty-scrollbar-track-x.dragging .ty-scrollbar-thumb-x {\n background: var(--ty-scrollbar-thumb-active, color-mix(in oklab, var(--ty-color-neutral-bold) 70%, transparent));\n}\n\n/* Touch devices: hide the custom scrollbar */\n@media (pointer: coarse) and (hover: none) {\n .ty-scrollbar-track-y,\n .ty-scrollbar-track-x {\n display: none !important;\n }\n}\n\n@media (prefers-reduced-motion: reduce) {\n .ty-scrollbar-track-y,\n .ty-scrollbar-track-x,\n .ty-scrollbar-thumb-y,\n .ty-scrollbar-thumb-x {\n transition-duration: 0ms !important;\n }\n}\n\n";
|
package/lib/styles/tab.js
CHANGED
|
@@ -6,7 +6,7 @@ export const tabStyles = `
|
|
|
6
6
|
display: block;
|
|
7
7
|
box-sizing: border-box;
|
|
8
8
|
flex-shrink: 0;
|
|
9
|
-
transition: opacity var(--transition-duration, 400ms) var(--transition-easing, ease-in-out);
|
|
9
|
+
transition: opacity var(--ty-tabs-transition-duration, 400ms) var(--ty-tabs-transition-easing, ease-in-out);
|
|
10
10
|
}
|
|
11
11
|
|
|
12
12
|
@media (prefers-reduced-motion: reduce) {
|
package/lib/styles/tabs.d.ts
CHANGED
|
@@ -2,4 +2,4 @@
|
|
|
2
2
|
* Tabs Component Styles — ty-tabs container: top/bottom placement, animated
|
|
3
3
|
* active marker, carousel viewport. Customizable via CSS Parts (::part).
|
|
4
4
|
*/
|
|
5
|
-
export declare const tabsStyles = "\n:host {\n display: block;\n width: var(--tabs-width, 100%);\n height: var(--tabs-height, 400px);\n box-sizing: border-box;\n}\n\n.tabs-container {\n width: 100%;\n height: 100%;\n display: flex;\n flex-direction: column;\n position: relative;\n box-sizing: border-box;\n}\n\n.tabs-container[data-placement=\"bottom\"] {\n flex-direction: column-reverse;\n}\n\n/* Tab buttons container \u2014 exposed as a CSS Part for full styling control */\n.tab-buttons {\n display: flex;\n gap: 0;\n flex-shrink: 0;\n position: relative;\n\n /* Default minimal styling - customize via ::part(buttons-container) */\n background: transparent;\n}\n\n/* Separator drawn as a pseudo-element, not a border: a border sits OUTSIDE the\n strip's box, so the marker (absolutely positioned inside the strip) could only\n ever stop just above it, reading as two stacked lines. As an overlaid line it\n lands inside the marker's range, and .tab-strip's z-index lets the marker\n cover it. */\n.tab-buttons::after {\n content: \"\";\n position: absolute;\n left: 0;\n right: 0;\n bottom: 0;\n height: 1px;\n /* --tabs-separator: transparent removes the line (e.g. with a pill marker) */\n background: var(--tabs-separator, var(--ty-border));\n pointer-events: none;\n}\n\n.tabs-container[data-placement=\"bottom\"] .tab-buttons::after {\n bottom: auto;\n top: 0;\n}\n\n/* Scrollable strip holding the buttons + marker. Tabs never collapse \u2014 the\n strip scrolls (scrollbar hidden; touch/wheel still work) and activation\n glides the active tab into view. The \"\u2026\" trigger sits OUTSIDE this strip,\n pinned at the edge. position:relative makes it the offsetParent for the\n marker, so the marker scrolls with the buttons and can't desync. */\n.tab-strip {\n display: flex;\n flex: 1 1 auto;\n min-width: 0;\n position: relative;\n /* Paints the marker + buttons over .tab-buttons::after (the separator line) */\n z-index: 1;\n overflow-x: auto;\n scrollbar-width: none;\n /* Edge fades \u2014 a mask, not overlays, so it works over any background.\n --fade-left/right are set from the scroll listener: 0px at a hard edge,\n 28px when more tabs continue past that side. */\n --fade-left: 0px;\n --fade-right: 0px;\n -webkit-mask-image: linear-gradient(to right,\n transparent, black var(--fade-left),\n black calc(100% - var(--fade-right)), transparent);\n mask-image: linear-gradient(to right,\n transparent, black var(--fade-left),\n black calc(100% - var(--fade-right)), transparent);\n}\n\n.tab-strip::-webkit-scrollbar {\n display: none;\n}\n\n/* When every tab fits there is nothing to scroll, so drop the clipping \u2014\n both overflow and the mask cut a custom marker's box-shadow off at the\n strip edge. Set by updateOverflow(); clipping stays the pre-measurement\n default so overflowing tabs can't spill for a frame on first render. */\n.tab-strip.unclipped {\n overflow: visible;\n -webkit-mask-image: none;\n mask-image: none;\n}\n\n/* Marker wrapper \u2014 exposed as a CSS Part for custom marker styling */\n.marker-wrapper {\n position: absolute;\n z-index: 0;\n /* Behind buttons */\n pointer-events: none;\n /* Don't block clicks */\n transition: left var(--transition-duration, 300ms) var(--transition-easing, ease-in-out),\n width var(--transition-duration, 300ms) var(--transition-easing, ease-in-out),\n height var(--transition-duration, 300ms) var(--transition-easing, ease-in-out),\n top var(--transition-duration, 300ms) var(--transition-easing, ease-in-out);\n}\n\n.default-marker {\n position: absolute;\n bottom: 0;\n left: 0;\n height: 3px;\n width: 100%;\n background: var(--ty-color-primary);\n pointer-events: none;\n}\n\n/* Bottom placement: marker rides the top edge, next to the content it marks */\n.tabs-container[data-placement=\"bottom\"] .default-marker {\n bottom: auto;\n top: 0;\n}\n\n
|
|
5
|
+
export declare const tabsStyles = "\n:host {\n display: block;\n width: var(--tabs-width, 100%);\n height: var(--tabs-height, 400px);\n box-sizing: border-box;\n}\n\n.tabs-container {\n width: 100%;\n height: 100%;\n display: flex;\n flex-direction: column;\n position: relative;\n box-sizing: border-box;\n}\n\n.tabs-container[data-placement=\"bottom\"] {\n flex-direction: column-reverse;\n}\n\n/* Tab buttons container \u2014 exposed as a CSS Part for full styling control */\n.tab-buttons {\n display: flex;\n gap: 0;\n flex-shrink: 0;\n position: relative;\n\n /* Default minimal styling - customize via ::part(buttons-container) */\n background: transparent;\n}\n\n/* Separator drawn as a pseudo-element, not a border: a border sits OUTSIDE the\n strip's box, so the marker (absolutely positioned inside the strip) could only\n ever stop just above it, reading as two stacked lines. As an overlaid line it\n lands inside the marker's range, and .tab-strip's z-index lets the marker\n cover it. */\n.tab-buttons::after {\n content: \"\";\n position: absolute;\n left: 0;\n right: 0;\n bottom: 0;\n height: 1px;\n /* --ty-tabs-separator: transparent removes the line (e.g. with a pill marker) */\n background: var(--ty-tabs-separator, var(--ty-border));\n pointer-events: none;\n}\n\n.tabs-container[data-placement=\"bottom\"] .tab-buttons::after {\n bottom: auto;\n top: 0;\n}\n\n/* Scrollable strip holding the buttons + marker. Tabs never collapse \u2014 the\n strip scrolls (scrollbar hidden; touch/wheel still work) and activation\n glides the active tab into view. The \"\u2026\" trigger sits OUTSIDE this strip,\n pinned at the edge. position:relative makes it the offsetParent for the\n marker, so the marker scrolls with the buttons and can't desync. */\n.tab-strip {\n display: flex;\n flex: 1 1 auto;\n min-width: 0;\n position: relative;\n /* Paints the marker + buttons over .tab-buttons::after (the separator line) */\n z-index: 1;\n overflow-x: auto;\n scrollbar-width: none;\n /* Edge fades \u2014 a mask, not overlays, so it works over any background.\n --fade-left/right are set from the scroll listener: 0px at a hard edge,\n 28px when more tabs continue past that side. */\n --fade-left: 0px;\n --fade-right: 0px;\n -webkit-mask-image: linear-gradient(to right,\n transparent, black var(--fade-left),\n black calc(100% - var(--fade-right)), transparent);\n mask-image: linear-gradient(to right,\n transparent, black var(--fade-left),\n black calc(100% - var(--fade-right)), transparent);\n}\n\n.tab-strip::-webkit-scrollbar {\n display: none;\n}\n\n/* When every tab fits there is nothing to scroll, so drop the clipping \u2014\n both overflow and the mask cut a custom marker's box-shadow off at the\n strip edge. Set by updateOverflow(); clipping stays the pre-measurement\n default so overflowing tabs can't spill for a frame on first render. */\n.tab-strip.unclipped {\n overflow: visible;\n -webkit-mask-image: none;\n mask-image: none;\n}\n\n/* Marker wrapper \u2014 exposed as a CSS Part for custom marker styling */\n.marker-wrapper {\n position: absolute;\n z-index: 0;\n /* Behind buttons */\n pointer-events: none;\n /* Don't block clicks */\n transition: left var(--ty-tabs-transition-duration, 300ms) var(--ty-tabs-transition-easing, ease-in-out),\n width var(--ty-tabs-transition-duration, 300ms) var(--ty-tabs-transition-easing, ease-in-out),\n height var(--ty-tabs-transition-duration, 300ms) var(--ty-tabs-transition-easing, ease-in-out),\n top var(--ty-tabs-transition-duration, 300ms) var(--ty-tabs-transition-easing, ease-in-out);\n}\n\n.default-marker {\n position: absolute;\n bottom: 0;\n left: 0;\n height: 3px;\n width: 100%;\n background: var(--ty-color-primary);\n pointer-events: none;\n}\n\n/* Bottom placement: marker rides the top edge, next to the content it marks */\n.tabs-container[data-placement=\"bottom\"] .default-marker {\n bottom: auto;\n top: 0;\n}\n\n::slotted([slot=\"marker\"]) {\n display: block;\n width: 100%;\n height: 100%;\n box-sizing: border-box;\n}\n\n.tab-button {\n /* Material's floor for SCROLLABLE tabs. 120px is the fixed-tab number and it\n manufactured overflow: five short labels claimed 600px whether they needed\n it or not. Equal-width rhythm is what the \"fixed\" attribute is for. */\n min-width: var(--ty-tab-min-width, 72px);\n /* Scrollable tabs keep their natural width and let the strip scroll \u2014 that is\n the whole contract. Without this they are flex items with the default\n shrink of 1, so they squeeze toward the floor and their labels ellipsize\n before the strip ever overflows. Squeezing is what \"fixed\" is for. */\n flex-shrink: 0;\n padding: 6px 12px;\n border: none;\n background: transparent;\n cursor: pointer;\n font: inherit;\n color: var(--ty-text-soft);\n transition: var(--ty-local-transition, color 200ms, background-color 200ms);\n white-space: nowrap;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n gap: 8px;\n font-weight: var(--ty-font-bold);\n font-size: var(--ty-font-sm);\n position: relative;\n /* Establish stacking context */\n z-index: 10;\n /* Above marker */\n}\n\n/* Fixed tabs: the bar is divided between them instead of scrolling. Material\n calls this \"fixed tabs\" \u2014 for a small, known set of short labels. No\n min-width floor, no overflow, no \"\u2026\". */\n.tabs-container[data-fixed] .tab-button {\n flex: 1 1 0;\n min-width: 0;\n}\n\n/* Fixed tabs only. A scrollable tab is never narrower than its label, so\n ellipsizing there would hide text the user could simply scroll to. */\n.tabs-container[data-fixed] .tab-label {\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.tab-button[aria-selected=true] {\n color: var(--ty-text-strong);\n}\n\n.tab-button[disabled] {\n opacity: 0.5;\n cursor: not-allowed;\n pointer-events: none;\n}\n\n.tab-button:focus-visible {\n outline: 2px solid var(--ty-color-primary);\n outline-offset: -2px;\n}\n\n/* Overflow \"more\" trigger (pill) + menu */\n.tab-overflow-trigger {\n flex-shrink: 0;\n align-self: center;\n min-width: 36px;\n height: 24px;\n margin: 0 8px;\n padding: 0 10px;\n border: 1px solid var(--ty-border);\n border-radius: 9999px;\n background: transparent;\n cursor: pointer;\n color: var(--ty-text-soft);\n font-size: 16px;\n line-height: 1;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n position: relative;\n z-index: 10;\n transition: color 200ms, border-color 200ms;\n}\n\n.tab-overflow-trigger:hover {\n color: var(--ty-text-strong);\n border-color: var(--ty-text-soft);\n}\n\n.tab-overflow-trigger:focus-visible {\n outline: 2px solid var(--ty-color-primary);\n outline-offset: -2px;\n}\n\n.tab-overflow-menu {\n display: flex;\n flex-direction: column;\n min-width: 160px;\n max-height: 300px;\n overflow-y: auto;\n padding: var(--ty-spacing-2, 4px);\n background: var(--ty-surface-floating);\n border-radius: var(--ty-radius-md);\n box-shadow: var(--ty-shadow-md, 0 1px 3px 0 rgba(0, 0, 0, 0.1), 0 1px 2px -1px rgba(0, 0, 0, 0.1));\n}\n\n.tab-overflow-item {\n padding: var(--ty-spacing-2, 6px) var(--ty-spacing-3, 12px);\n border: none;\n background: transparent;\n text-align: left;\n cursor: pointer;\n border-radius: var(--ty-radius-md);\n color: var(--ty-text);\n font: inherit;\n font-size: var(--ty-font-sm);\n white-space: nowrap;\n}\n\n.tab-overflow-item:hover {\n background: var(--ty-bg-neutral-soft);\n color: var(--ty-text-strong);\n}\n\n.tab-overflow-item[data-active=\"true\"] {\n color: var(--ty-color-primary);\n font-weight: var(--ty-font-bold);\n}\n\n.tab-overflow-item[disabled] {\n opacity: 0.5;\n cursor: not-allowed;\n}\n\n/* Panels viewport \u2014 exposed as a CSS Part */\n.panels-viewport {\n position: relative;\n flex: 1;\n overflow: hidden;\n /* Critical: hides off-screen panels */\n min-height: 0;\n /* Allows flex child to shrink */\n}\n\n/* Panels wrapper slides horizontally */\n.panels-wrapper {\n display: flex;\n height: 100%;\n transition: transform var(--ty-tabs-transition-duration, 300ms) var(--ty-tabs-transition-easing, ease-in-out);\n}\n\n@media (prefers-reduced-motion: reduce) {\n .panels-wrapper {\n transition-duration: 0ms !important;\n }\n\n .marker-wrapper {\n transition-duration: 0ms !important;\n }\n}\n\n::slotted(ty-tab) {\n /* Carousel page width. --tabs-page-width is the measured pixel width, set by\n the ResizeObserver for percentage-width tabs so every panel is exactly one\n page wide. It is deliberately NOT --tabs-width: that one sizes :host, and\n feeding a measured pixel value back into the host's own width is a\n self-referential loop (see setupResizeObserver). */\n width: var(--tabs-page-width, var(--tabs-width, 100%));\n height: 100%;\n flex-shrink: 0;\n}\n";
|
package/lib/styles/tabs.js
CHANGED
|
@@ -42,8 +42,8 @@ export const tabsStyles = `
|
|
|
42
42
|
right: 0;
|
|
43
43
|
bottom: 0;
|
|
44
44
|
height: 1px;
|
|
45
|
-
/* --tabs-separator: transparent removes the line (e.g. with a pill marker) */
|
|
46
|
-
background: var(--tabs-separator, var(--ty-border));
|
|
45
|
+
/* --ty-tabs-separator: transparent removes the line (e.g. with a pill marker) */
|
|
46
|
+
background: var(--ty-tabs-separator, var(--ty-border));
|
|
47
47
|
pointer-events: none;
|
|
48
48
|
}
|
|
49
49
|
|
|
@@ -100,10 +100,10 @@ export const tabsStyles = `
|
|
|
100
100
|
/* Behind buttons */
|
|
101
101
|
pointer-events: none;
|
|
102
102
|
/* Don't block clicks */
|
|
103
|
-
transition: left var(--transition-duration, 300ms) var(--transition-easing, ease-in-out),
|
|
104
|
-
width var(--transition-duration, 300ms) var(--transition-easing, ease-in-out),
|
|
105
|
-
height var(--transition-duration, 300ms) var(--transition-easing, ease-in-out),
|
|
106
|
-
top var(--transition-duration, 300ms) var(--transition-easing, ease-in-out);
|
|
103
|
+
transition: left var(--ty-tabs-transition-duration, 300ms) var(--ty-tabs-transition-easing, ease-in-out),
|
|
104
|
+
width var(--ty-tabs-transition-duration, 300ms) var(--ty-tabs-transition-easing, ease-in-out),
|
|
105
|
+
height var(--ty-tabs-transition-duration, 300ms) var(--ty-tabs-transition-easing, ease-in-out),
|
|
106
|
+
top var(--ty-tabs-transition-duration, 300ms) var(--ty-tabs-transition-easing, ease-in-out);
|
|
107
107
|
}
|
|
108
108
|
|
|
109
109
|
.default-marker {
|
|
@@ -122,11 +122,6 @@ export const tabsStyles = `
|
|
|
122
122
|
top: 0;
|
|
123
123
|
}
|
|
124
124
|
|
|
125
|
-
/* Hide default marker when custom marker is slotted */
|
|
126
|
-
.marker-wrapper:has(::slotted([slot="marker"])) .default-marker {
|
|
127
|
-
display: none;
|
|
128
|
-
}
|
|
129
|
-
|
|
130
125
|
::slotted([slot="marker"]) {
|
|
131
126
|
display: block;
|
|
132
127
|
width: 100%;
|
|
@@ -135,7 +130,15 @@ export const tabsStyles = `
|
|
|
135
130
|
}
|
|
136
131
|
|
|
137
132
|
.tab-button {
|
|
138
|
-
|
|
133
|
+
/* Material's floor for SCROLLABLE tabs. 120px is the fixed-tab number and it
|
|
134
|
+
manufactured overflow: five short labels claimed 600px whether they needed
|
|
135
|
+
it or not. Equal-width rhythm is what the "fixed" attribute is for. */
|
|
136
|
+
min-width: var(--ty-tab-min-width, 72px);
|
|
137
|
+
/* Scrollable tabs keep their natural width and let the strip scroll — that is
|
|
138
|
+
the whole contract. Without this they are flex items with the default
|
|
139
|
+
shrink of 1, so they squeeze toward the floor and their labels ellipsize
|
|
140
|
+
before the strip ever overflows. Squeezing is what "fixed" is for. */
|
|
141
|
+
flex-shrink: 0;
|
|
139
142
|
padding: 6px 12px;
|
|
140
143
|
border: none;
|
|
141
144
|
background: transparent;
|
|
@@ -156,6 +159,22 @@ export const tabsStyles = `
|
|
|
156
159
|
/* Above marker */
|
|
157
160
|
}
|
|
158
161
|
|
|
162
|
+
/* Fixed tabs: the bar is divided between them instead of scrolling. Material
|
|
163
|
+
calls this "fixed tabs" — for a small, known set of short labels. No
|
|
164
|
+
min-width floor, no overflow, no "…". */
|
|
165
|
+
.tabs-container[data-fixed] .tab-button {
|
|
166
|
+
flex: 1 1 0;
|
|
167
|
+
min-width: 0;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/* Fixed tabs only. A scrollable tab is never narrower than its label, so
|
|
171
|
+
ellipsizing there would hide text the user could simply scroll to. */
|
|
172
|
+
.tabs-container[data-fixed] .tab-label {
|
|
173
|
+
overflow: hidden;
|
|
174
|
+
text-overflow: ellipsis;
|
|
175
|
+
white-space: nowrap;
|
|
176
|
+
}
|
|
177
|
+
|
|
159
178
|
.tab-button[aria-selected=true] {
|
|
160
179
|
color: var(--ty-text-strong);
|
|
161
180
|
}
|
|
@@ -258,7 +277,7 @@ export const tabsStyles = `
|
|
|
258
277
|
.panels-wrapper {
|
|
259
278
|
display: flex;
|
|
260
279
|
height: 100%;
|
|
261
|
-
transition: transform var(--transition-duration, 300ms) var(--transition-easing, ease-in-out);
|
|
280
|
+
transition: transform var(--ty-tabs-transition-duration, 300ms) var(--ty-tabs-transition-easing, ease-in-out);
|
|
262
281
|
}
|
|
263
282
|
|
|
264
283
|
@media (prefers-reduced-motion: reduce) {
|
package/lib/version.d.ts
CHANGED
package/lib/version.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export const VERSION = '1.0.0-
|
|
1
|
+
export const VERSION = '1.0.0-TC52';
|