@financial-times/ft-concept-button
Version:
ft concept button (includes the ft follow button)
113 lines (100 loc) • 3.07 kB
JavaScript
export default class FtConceptButton {
/**
* Class constructor.
*
* @param {HTMLElement=} [element] The component element in the DOM
* @param {object} [options={}] An options object for configuring the component
* @property {HTMLElement} element The component element in the DOM
* @property {object} options The merged options
*/
constructor(element, options) {
this.element = element;
this.button = this.element.querySelector('.ft-concept-button__button');
this.live = this.element.querySelector('.ft-concept-button__announcement');
this.options = Object.assign({}, options || this.getOptionsFromDom());
// Set the correct initial state of the button
this._initializeState();
}
_initializeState() {
const state = this.isPressed;
this.isPressed = state;
}
get isPressed() {
return this.button.getAttribute('aria-pressed') === 'true';
}
/**
* Set the followed state
* Updates the UI and adds text to the a11y box.
*
* @param {boolean} [state] the state to set
*/
set isPressed(state) {
this.button.setAttribute('aria-pressed', state ? 'true' : 'false');
if (
this.options.ariaLivePressedText &&
this.options.ariaLiveUnpressedText &&
this.live
) {
this.live.textContent = state
? this.options.ariaLivePressedText
: this.options.ariaLiveUnpressedText;
}
if (this.options.pressedText && this.options.unpressedText) {
this.button.textContent = state
? this.options.pressedText
: this.options.unpressedText;
}
if (
this.options.ariaLabelPressedText &&
this.options.ariaLabelUnpressedText
) {
this.button.setAttribute('aria-label', state
? this.options.ariaLabelPressedText
: this.options.ariaLabelUnpressedText
);
}
}
getOptionsFromDom() {
const options = {};
if (this.element instanceof HTMLElement) {
for (const [key, value] of Object.entries(this.element.dataset)) {
if (key.startsWith('ftConceptButton')) {
const option = key.replace(
/^ftConceptButton(\w)(\w+)$/,
(m, m1, m2) => m1.toLowerCase() + m2
);
options[option] = value;
try {
options[option] = JSON.parse(value);
} catch (_) {
// empty
}
}
}
}
return options;
}
/**
* Initialize components on the page
*
* @param {(Element | HTMLElement | string)} rootEl - The root element to initialize the component in, or a CSS-style selector for the root element
* @param {object} [options={}] - An options object for configuring the component
* @returns {(FtConceptButton|Array<FtConceptButton>)} - ft-concept-button instance(s)
*/
static init(rootEl, options) {
if (!rootEl) {
rootEl = document.body;
}
if (typeof rootEl === 'string') {
rootEl = document.querySelector(rootEl);
}
const selector = '[data-o-component="ft-concept-button"]';
if (rootEl instanceof HTMLElement && rootEl.matches(selector)) {
return new FtConceptButton(rootEl, options);
}
return Array.from(
rootEl.querySelectorAll(selector),
rootEl => new FtConceptButton(rootEl, options)
);
}
}