mirror of
https://github.com/BookStackApp/BookStack.git
synced 2025-07-30 04:23:11 +03:00
Comments: Moved to tab UI, Converted tabs component to ts
This commit is contained in:
95
resources/js/components/tabs.ts
Normal file
95
resources/js/components/tabs.ts
Normal file
@ -0,0 +1,95 @@
|
||||
import {Component} from './component';
|
||||
|
||||
/**
|
||||
* Tabs
|
||||
* Uses accessible attributes to drive its functionality.
|
||||
* On tab wrapping element:
|
||||
* - role=tablist
|
||||
* On tabs (Should be a button):
|
||||
* - id
|
||||
* - role=tab
|
||||
* - aria-selected=true/false
|
||||
* - aria-controls=<id-of-panel-section>
|
||||
* On panels:
|
||||
* - id
|
||||
* - tabindex=0
|
||||
* - role=tabpanel
|
||||
* - aria-labelledby=<id-of-tab-for-panel>
|
||||
* - hidden (If not shown by default).
|
||||
*/
|
||||
export class Tabs extends Component {
|
||||
|
||||
protected container: HTMLElement;
|
||||
protected tabList: HTMLElement;
|
||||
protected tabs: HTMLElement[];
|
||||
protected panels: HTMLElement[];
|
||||
|
||||
protected activeUnder: number;
|
||||
protected active: null|boolean = null;
|
||||
|
||||
setup() {
|
||||
this.container = this.$el;
|
||||
this.tabList = this.container.querySelector('[role="tablist"]') as HTMLElement;
|
||||
this.tabs = Array.from(this.tabList.querySelectorAll('[role="tab"]'));
|
||||
this.panels = Array.from(this.container.querySelectorAll(':scope > [role="tabpanel"], :scope > * > [role="tabpanel"]'));
|
||||
this.activeUnder = this.$opts.activeUnder ? Number(this.$opts.activeUnder) : 10000;
|
||||
|
||||
this.container.addEventListener('click', event => {
|
||||
const tab = (event.target as HTMLElement).closest('[role="tab"]');
|
||||
if (tab instanceof HTMLElement && this.tabs.includes(tab)) {
|
||||
this.show(tab.getAttribute('aria-controls') || '');
|
||||
}
|
||||
});
|
||||
|
||||
window.addEventListener('resize', this.updateActiveState.bind(this), {
|
||||
passive: true,
|
||||
});
|
||||
this.updateActiveState();
|
||||
}
|
||||
|
||||
public show(sectionId: string): void {
|
||||
for (const panel of this.panels) {
|
||||
panel.toggleAttribute('hidden', panel.id !== sectionId);
|
||||
}
|
||||
|
||||
for (const tab of this.tabs) {
|
||||
const tabSection = tab.getAttribute('aria-controls');
|
||||
const selected = tabSection === sectionId;
|
||||
tab.setAttribute('aria-selected', selected ? 'true' : 'false');
|
||||
}
|
||||
|
||||
this.$emit('change', {showing: sectionId});
|
||||
}
|
||||
|
||||
protected updateActiveState(): void {
|
||||
const active = window.innerWidth < this.activeUnder;
|
||||
if (active === this.active) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (active) {
|
||||
this.activate();
|
||||
} else {
|
||||
this.deactivate();
|
||||
}
|
||||
|
||||
this.active = active;
|
||||
}
|
||||
|
||||
protected activate(): void {
|
||||
const panelToShow = this.panels.find(p => !p.hasAttribute('hidden')) || this.panels[0];
|
||||
this.show(panelToShow.id);
|
||||
this.tabList.toggleAttribute('hidden', false);
|
||||
}
|
||||
|
||||
protected deactivate(): void {
|
||||
for (const panel of this.panels) {
|
||||
panel.removeAttribute('hidden');
|
||||
}
|
||||
for (const tab of this.tabs) {
|
||||
tab.setAttribute('aria-selected', 'false');
|
||||
}
|
||||
this.tabList.toggleAttribute('hidden', true);
|
||||
}
|
||||
|
||||
}
|
Reference in New Issue
Block a user