@ussebastian/kitdigital
Version:
Kit Digital de la Universidad San Sebastián
151 lines (128 loc) • 4.84 kB
JavaScript
export class Collapsable {
constructor(element, key) {
this.key = key;
this.element = element;
this.triggerElement = null;
this.contentElement = null;
this.collapsableId = null;
this.contentElementHeight = null;
this.groupId = null;
this.animationDuration = 250;
this.isOpen = false;
this.element = element;
this.triggerElement = this.element;
}
// get the list of the group of collapsables
setKeyboardNavigation() {
if (this.groupId) {
const currentGroup =
window.USSKitDigital.componentsSharedState.Collapsable.groups[this.groupId];
// make the focus go to the previous collapsable, if its the first one go to the last
this.element.addEventListener('keydown', (e) => {
if (e.key === 'ArrowUp' || e.key === 'ArrowLeft') {
e.preventDefault();
const index = currentGroup.indexOf(this);
if (index > 0) {
currentGroup[index - 1].triggerElement.focus();
} else {
currentGroup[currentGroup.length - 1].triggerElement.focus();
}
}
if (e.key === 'ArrowDown' || e.key === 'ArrowRight') {
e.preventDefault();
const index = currentGroup.indexOf(this);
if (index < currentGroup.length - 1) {
currentGroup[index + 1].triggerElement.focus();
} else {
currentGroup[0].triggerElement.focus();
}
}
if (e.key === 'Home' || e.keyCode === 36) {
e.preventDefault();
currentGroup[0].triggerElement.focus();
}
if (e.key === 'End' || e.keyCode === 35) {
e.preventDefault();
currentGroup[currentGroup.length - 1].triggerElement.focus();
}
});
}
}
closeGroup() {
if (
window.USSKitDigital?.componentsSharedState?.Collapsable?.groups[this.groupId] === undefined
) {
window.USSKitDigital.componentsSharedState = {
Collapsable: {
groups: {
[this.groupId]: [],
},
},
};
}
window.USSKitDigital.componentsSharedState.Collapsable.groups[this.groupId].push(this);
}
closeAllGroupCollapsables() {
window.USSKitDigital.componentsSharedState.Collapsable.groups[this.groupId].forEach(
(collapsable) => {
if (collapsable !== this) {
collapsable.close();
}
},
);
}
mount() {
this.collapsableId = this.triggerElement.dataset.ussCollapsableTriggerFor;
this.animationDuration = this.triggerElement.dataset.ussCollapsableAnimationDuration || 250;
this.groupId = this.triggerElement.dataset.ussCollapsableCloseGroup;
if (this.groupId) this.closeGroup();
this.contentElement = document.querySelector(
`[data-uss-collapsable-id="${this.collapsableId}"]`,
);
this.contentElement.setAttribute('id', this.element.id || this.collapsableId);
this.contentElement.style.setProperty('--animation-duration', `${this.animationDuration}ms`);
if (!this.contentElement) {
throw new Error('Collapsable: Content element not found.');
}
// Initial state
this.contentElementHeight = this.contentElement.offsetHeight;
this.contentElement.style.setProperty(
'--uss-collapsable-height',
`${this.contentElementHeight}px`,
);
this.contentElement.dataset.ussCollapsableOpen = 'false';
this.contentElement.setAttribute('aria-labelledby', `trigger-of-${this.collapsableId}`);
this.triggerElement.setAttribute('aria-expanded', 'false');
this.triggerElement.setAttribute('aria-controls', this.element.id || this.collapsableId);
this.triggerElement.setAttribute('id', `trigger-of-${this.collapsableId}`);
this.toggleTabIndexes();
this.triggerElement.addEventListener('click', () => this.toggleOpenState());
this.setKeyboardNavigation();
}
toggleOpenState() {
this.contentElement.dataset.ussCollapsableOpen = String(!this.isOpen);
this.triggerElement.setAttribute('aria-expanded', !this.isOpen);
if (!this.isOpen) {
if (this.groupId) this.closeAllGroupCollapsables();
setTimeout(() => {
if (this.isOpen) this.contentElement.style.overflow = 'visible';
}, this.animationDuration);
} else {
this.contentElement.style.overflow = 'hidden';
}
this.isOpen = !this.isOpen;
this.toggleTabIndexes();
}
close() {
this.contentElement.dataset.ussCollapsableOpen = 'false';
this.triggerElement.setAttribute('aria-expanded', 'false');
this.contentElement.style.overflow = 'hidden';
this.isOpen = false;
this.toggleTabIndexes();
}
toggleTabIndexes() {
this.contentElement.querySelectorAll('a, button, input, select, textarea').forEach((el) => {
el.setAttribute('tabindex', this.isOpen ? '0' : '-1');
});
}
}