igniteui-webcomponents
Version:
Ignite UI for Web Components is a complete library of UI components, giving you the ability to build modern web applications using encapsulation and the concept of reusable components in a dependency-free approach.
330 lines • 12.7 kB
JavaScript
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var IgcTabsComponent_1;
import { LitElement, html, nothing } from 'lit';
import { eventOptions, property, query, queryAssignedElements, state, } from 'lit/decorators.js';
import { createRef, ref } from 'lit/directives/ref.js';
import { themes } from '../../theming/theming-decorator.js';
import IgcIconButtonComponent from '../button/icon-button.js';
import { addKeybindings, arrowLeft, arrowRight, endKey, homeKey, } from '../common/controllers/key-bindings.js';
import { createMutationController, } from '../common/controllers/mutation-observer.js';
import { blazorAdditionalDependencies } from '../common/decorators/blazorAdditionalDependencies.js';
import { watch } from '../common/decorators/watch.js';
import { registerComponent } from '../common/definitions/register.js';
import { EventEmitterMixin } from '../common/mixins/event-emitter.js';
import { createCounter, getOffset, isLTR, wrap } from '../common/util.js';
import IgcTabPanelComponent from './tab-panel.js';
import IgcTabComponent from './tab.js';
import { styles as shared } from './themes/shared/tabs/tabs.common.css.js';
import { all } from './themes/tabs-themes.js';
import { styles } from './themes/tabs.base.css.js';
let IgcTabsComponent = IgcTabsComponent_1 = class IgcTabsComponent extends EventEmitterMixin(LitElement) {
static register() {
registerComponent(IgcTabsComponent_1, IgcTabComponent, IgcTabPanelComponent, IgcIconButtonComponent);
}
get _closestActiveTabIndex() {
const root = this.getRootNode();
const tabs = this.enabledTabs;
const tab = root.activeElement
? root.activeElement.closest(IgcTabComponent.tagName)
: null;
return tabs.indexOf(tab);
}
_mutationCallback({ changes: { attributes, added, removed }, }) {
const ownAttributes = attributes.filter((tab) => tab.closest(this.tagName) === this);
const ownAdded = added.filter(({ target }) => target.closest(this.tagName) === this);
const ownRemoved = removed.filter(({ target }) => target.closest(this.tagName) === this);
this.setSelectedTab(ownAttributes.find((tab) => tab.selected));
if (ownRemoved.length || ownAdded.length) {
for (const { node: tab } of ownRemoved) {
this.resizeObserver?.unobserve(tab);
if (tab.selected && tab === this.activeTab) {
this.activeTab = undefined;
}
}
for (const { node: tab } of ownAdded) {
this.resizeObserver?.observe(tab);
if (tab.selected) {
this.setSelectedTab(tab);
}
}
this.syncAttributes();
}
this.updateSelectedTab();
this.activeTab?.scrollIntoView({ block: 'nearest' });
this.alignIndicator();
}
get enabledTabs() {
return this.tabs.filter((tab) => !tab.disabled);
}
get selected() {
return this.activeTab?.panel ?? '';
}
alignIndicator() {
const styles = {
visibility: this.activeTab ? 'visible' : 'hidden',
transitionDuration: '0.3s',
};
if (this.activeTab && this.wrapper) {
Object.assign(styles, {
width: `${this.activeTab.offsetWidth}px`,
transform: `translate(${isLTR(this)
? getOffset(this.activeTab, this.wrapper).left
: getOffset(this.activeTab, this.wrapper).right}px)`,
});
}
Object.assign(this.selectedIndicator?.style ?? {}, styles);
}
constructor() {
super();
this.headerRef = createRef();
this.showScrollButtons = false;
this.disableStartScrollButton = true;
this.disableEndScrollButton = false;
this.alignment = 'start';
this.activation = 'auto';
addKeybindings(this, {
ref: this.headerRef,
bindingDefaults: { preventDefault: true },
})
.set(arrowLeft, this.onArrowLeft)
.set(arrowRight, this.onArrowRight)
.set(homeKey, this.onHomeKey)
.set(endKey, this.onEndKey)
.setActivateHandler(this.onActivationKey);
createMutationController(this, {
callback: this._mutationCallback,
config: {
attributeFilter: ['selected'],
childList: true,
subtree: true,
},
filter: [IgcTabComponent.tagName],
});
}
async firstUpdated() {
this.showScrollButtons =
this.container.scrollWidth > this.container.clientWidth;
await this.updateComplete;
this.syncAttributes();
this.setupObserver();
this.setSelectedTab(this.tabs.filter((tab) => tab.selected).at(-1) ?? this.enabledTabs.at(0));
this.updateSelectedTab();
}
disconnectedCallback() {
this.resizeObserver?.disconnect();
super.disconnectedCallback();
}
updateButtonsOnResize() {
this.showScrollButtons = false;
this.performUpdate();
this.showScrollButtons =
this.container.scrollWidth > this.container.clientWidth;
this.updateScrollButtons();
}
updateScrollButtons() {
const { scrollLeft, offsetWidth } = this.container;
const { scrollWidth } = this.wrapper;
this.disableEndScrollButton =
scrollWidth <= Math.abs(scrollLeft) + offsetWidth;
this.disableStartScrollButton = scrollLeft === 0;
}
setupObserver() {
this.resizeObserver = new ResizeObserver(() => {
this.updateButtonsOnResize();
this.alignIndicator();
});
[this.container, this.wrapper, ...this.tabs].forEach((element) => this.resizeObserver.observe(element));
}
updateSelectedTab() {
this.tabs.forEach((tab) => {
tab.selected = tab === this.activeTab;
});
this.panels.forEach((panel) => {
panel.hidden = panel.id !== this.activeTab?.panel;
});
}
syncAttributes() {
const prefix = this.id ? `${this.id}-` : '';
this.tabs.forEach((tab, index) => {
if (!tab.panel) {
tab.panel =
this.panels.at(index)?.id ??
`${prefix}tab-${IgcTabsComponent_1.increment()}`;
}
this.panels
.find((panel) => panel.id === tab.panel)
?.setAttribute('aria-labelledby', tab.id);
});
}
setSelectedTab(tab) {
if (!tab || tab === this.activeTab) {
return;
}
if (this.activeTab) {
this.activeTab.selected = false;
}
this.activeTab = tab;
this.activeTab.selected = true;
}
scrollByTabOffset(direction) {
const { scrollLeft, offsetWidth } = this.container;
const LTR = isLTR(this);
const next = direction === 'end';
const pivot = Math.abs(next ? offsetWidth + scrollLeft : scrollLeft);
let amount = this.tabs
.map((tab) => ({
start: LTR
? getOffset(tab, this.wrapper).left
: Math.abs(getOffset(tab, this.wrapper).right),
width: tab.offsetWidth,
}))
.filter((offset) => next ? offset.start + offset.width > pivot : offset.start < pivot)
.at(next ? 0 : -1).width;
amount *= next ? 1 : -1;
this.container.scrollBy({ left: LTR ? amount : -amount });
}
handleClick(event) {
const target = event.target;
const tab = target.closest('igc-tab');
if (!(tab && this.contains(tab)) || tab.disabled) {
return;
}
tab.focus();
this.setSelectedTab(tab);
this.emitEvent('igcChange', { detail: tab });
}
_scrollToAndFocus(tab) {
tab.scrollIntoView({ behavior: 'auto', block: 'nearest' });
tab.focus();
}
_kbActivateTab(tab) {
this._scrollToAndFocus(tab);
if (this.activation === 'auto') {
this.setSelectedTab(tab);
this.emitEvent('igcChange', { detail: tab });
}
}
onArrowLeft() {
const tabs = this.enabledTabs;
const delta = isLTR(this) ? -1 : 1;
this._kbActivateTab(tabs[wrap(0, tabs.length - 1, this._closestActiveTabIndex + delta)]);
}
onArrowRight() {
const tabs = this.enabledTabs;
const delta = isLTR(this) ? 1 : -1;
this._kbActivateTab(tabs[wrap(0, tabs.length - 1, this._closestActiveTabIndex + delta)]);
}
onHomeKey() {
this._kbActivateTab(this.enabledTabs.at(0));
}
onEndKey() {
this._kbActivateTab(this.enabledTabs.at(-1));
}
onActivationKey() {
const tabs = this.enabledTabs;
const index = this._closestActiveTabIndex;
this.setSelectedTab(tabs[index]);
this._kbActivateTab(tabs[index]);
}
handleScroll() {
this.updateScrollButtons();
}
select(name) {
this.setSelectedTab(this.tabs.find((el) => el.panel === name));
}
renderScrollButton(direction) {
const start = direction === 'start';
return this.showScrollButtons
? html `<igc-icon-button
tabindex="-1"
aria-hidden="true"
variant="flat"
collection="default"
part="${direction}-scroll-button"
exportparts="icon"
name="${start ? 'prev' : 'next'}"
.disabled=${start
? this.disableStartScrollButton
: this.disableEndScrollButton}
=${() => this.scrollByTabOffset(direction)}
></igc-icon-button>`
: nothing;
}
render() {
return html `
<div part="headers">
${this.renderScrollButton('start')}
<div part="headers-content" =${this.handleScroll}>
<div part="headers-wrapper">
<div
${ref(this.headerRef)}
part="headers-scroll"
role="tablist"
=${this.handleClick}
>
<slot></slot>
</div>
<div part="selected-indicator"></div>
</div>
</div>
${this.renderScrollButton('end')}
</div>
<div part="content">
<slot name="panel"></slot>
</div>
`;
}
};
IgcTabsComponent.tagName = 'igc-tabs';
IgcTabsComponent.styles = [styles, shared];
IgcTabsComponent.increment = createCounter();
__decorate([
queryAssignedElements({ selector: IgcTabComponent.tagName })
], IgcTabsComponent.prototype, "tabs", void 0);
__decorate([
queryAssignedElements({ slot: 'panel' })
], IgcTabsComponent.prototype, "panels", void 0);
__decorate([
query('[part="headers-wrapper"]')
], IgcTabsComponent.prototype, "wrapper", void 0);
__decorate([
query('[part="headers-content"]')
], IgcTabsComponent.prototype, "container", void 0);
__decorate([
query('[part="selected-indicator"]')
], IgcTabsComponent.prototype, "selectedIndicator", void 0);
__decorate([
state()
], IgcTabsComponent.prototype, "showScrollButtons", void 0);
__decorate([
state()
], IgcTabsComponent.prototype, "disableStartScrollButton", void 0);
__decorate([
state()
], IgcTabsComponent.prototype, "disableEndScrollButton", void 0);
__decorate([
state()
], IgcTabsComponent.prototype, "activeTab", void 0);
__decorate([
property({ reflect: true })
], IgcTabsComponent.prototype, "alignment", void 0);
__decorate([
property()
], IgcTabsComponent.prototype, "activation", void 0);
__decorate([
watch('alignment', { waitUntilFirstUpdate: true })
], IgcTabsComponent.prototype, "alignIndicator", null);
__decorate([
eventOptions({ passive: true })
], IgcTabsComponent.prototype, "handleScroll", null);
IgcTabsComponent = IgcTabsComponent_1 = __decorate([
themes(all),
blazorAdditionalDependencies('IgcTabComponent, IgcTabPanelComponent')
], IgcTabsComponent);
export default IgcTabsComponent;
//# sourceMappingURL=tabs.js.map