import { queryOne } from '@ecl/dom-utils';
/**
* @param {HTMLElement} element DOM element for component instantiation and scope
* @param {Object} options
* @param {String} options.translationToggleSelector Selector for toggling translatoins section
* @param {Boolean} options.attachClickListener Whether or not to bind click events on toggle
*/
export class FileDownload {
/**
* @static
* Shorthand for instance creation and initialisation.
*
* @param {HTMLElement} root DOM element for component instantiation and scope
*
* @return {FileDownload} An instance of FileDownload.
*/
static autoInit(root, { FILE_DOWNLOAD: defaultOptions = {} } = {}) {
const fileDownload = new FileDownload(root, defaultOptions);
fileDownload.init();
root.ECLFileDownload = fileDownload;
return fileDownload;
}
constructor(
element,
{
translationToggleSelector = '[data-ecl-file-translation-toggle]',
attachClickListener = true,
} = {},
) {
// Check element
if (!element || element.nodeType !== Node.ELEMENT_NODE) {
throw new TypeError(
'DOM element should be given to initialize this widget.',
);
}
this.element = element;
// Options
this.translationToggleSelector = translationToggleSelector;
this.attachClickListener = attachClickListener;
// Private variables
this.translationToggle = null;
this.translationContainer = null;
// Bind `this` for use in callbacks
this.handleClickOnToggle = this.handleClickOnToggle.bind(this);
this.handleKeyboard = this.handleKeyboard.bind(this);
}
/**
* Initialise component.
*/
init() {
if (!ECL) {
throw new TypeError('Called init but ECL is not present');
}
ECL.components = ECL.components || new Map();
this.translationToggle = queryOne(
this.translationToggleSelector,
this.element,
);
// Get target element
if (this.translationToggle) {
this.translationContainer = document.querySelector(
`#${this.translationToggle.getAttribute('aria-controls')}`,
);
}
// Bind click event on toggle
if (this.attachClickListener && this.translationToggle) {
this.translationToggle.addEventListener(
'click',
this.handleClickOnToggle,
);
this.element.addEventListener('keyup', this.handleKeyboard);
}
// Set ecl initialized attribute
this.element.setAttribute('data-ecl-auto-initialized', 'true');
ECL.components.set(this.element, this);
}
/**
* Destroy component.
*/
destroy() {
if (this.attachClickListener && this.translationToggle) {
this.translationToggle.removeEventListener(
'click',
this.handleClickOnToggle,
);
this.element.removeEventListener('keyup', this.handleKeyboard);
}
if (this.element) {
this.element.removeAttribute('data-ecl-auto-initialized');
ECL.components.delete(this.element);
}
}
/**
* Adjusts translation item padding to keep actions aligned with the footer
* when the list is tall enough to show a scrollbar.
*/
alignTranslationActions() {
const scrollbarWidth =
this.translationContainer.offsetWidth -
this.translationContainer.clientWidth;
const items = this.translationContainer.querySelectorAll(
'.ecl-file__translation-item',
);
if (items.length === 0) return;
if (scrollbarWidth > 0) {
const naturalPadding = parseFloat(
getComputedStyle(items[0]).paddingInlineEnd,
);
const newPadding = Math.max(0, naturalPadding - scrollbarWidth);
items.forEach((item) => {
item.style.paddingInlineEnd = `${newPadding}px`;
});
} else {
items.forEach((item) => {
item.style.paddingInlineEnd = '';
});
}
}
/**
* @param {Event} e
*/
handleClickOnToggle(e) {
e.preventDefault();
// Exit if no target found
if (!this.translationContainer) {
throw new TypeError(
'Target has to be provided for file download (aria-controls)',
);
}
if (this.translationToggle.getAttribute('aria-expanded') === 'true') {
this.closeTranslation();
} else {
this.openTranslation();
}
return this;
}
/**
* Open the translation dropdown.
*/
openTranslation() {
this.element.classList.add('ecl-file--open');
this.translationContainer.hidden = false;
this.translationToggle.setAttribute('aria-expanded', 'true');
this.alignTranslationActions();
}
/**
* Close the translation dropdown.
*/
closeTranslation() {
this.element.classList.remove('ecl-file--open');
this.translationContainer.hidden = true;
this.translationToggle.setAttribute('aria-expanded', 'false');
this.translationContainer
.querySelectorAll('.ecl-file__translation-item')
.forEach((item) => {
item.style.paddingInlineEnd = '';
});
}
/**
* Handles keyboard events.
*
* @param {Event} e
*/
handleKeyboard(e) {
if (!this.translationToggle) return;
// When pressing Esc close the translation dropdown, if open
if (
(e.key === 'Escape' || e.key === 'Esc') &&
this.translationToggle.getAttribute('aria-expanded') === 'true'
) {
this.closeTranslation();
this.translationToggle.focus();
}
}
}
export default FileDownload;