shortcut-buttons-flatpickr
Version:
Shortcut buttons is a plugin for flatpickr that provides users an alternative way to interact with the datetime picker.
186 lines (156 loc) • 5.96 kB
text/typescript
/*
* Copyright (c) 2018 João Morais under the MIT license.
* https://github.com/jcsmorais/shortcut-buttons-flatpickr/
*/
import { Instance as Flatpickr } from 'flatpickr/dist/types/instance.d';
export namespace ShortcutButtonsFlatpickr {
export type OnClickSignature = (index: number, fp: Flatpickr) => void;
export type Attributes = { [name: string]: string };
export type Button = {
attributes?: Attributes,
label: string,
};
export type Config = {
button: Button | Button[],
label?: string,
onClick?: OnClickSignature | OnClickSignature[],
theme?: string,
};
}
const defaultConfig: Partial<ShortcutButtonsFlatpickr.Config> = {
theme: 'light',
};
/**
* List of attributes that can be set through button's options.
*/
const supportedAttributes = new Set([
'accesskey',
'aria-label',
'class',
'tabindex',
]);
/**
* Adds shortcut buttons to flatpickr providing users an alternative way to interact with the datetime picker.
*
* Example usage:
*
* ```ts
* flatpickr('.target-input-element', {
* // ...
* plugins: [ShortcutButtonsPlugin({
* button: {
* label: 'The Beginning Of Time',
* },
* onClick: (index: number, fp: Flatpickr) => {
* // Do something when a button is clicked
* },
* theme: 'light',
* })],
* })
* ```
*
* @param config Configuration options.
*
* Supported options are:
* `button`: button(s).
* `button.attributes`: button's attributes.
* `button.label`: button's label.
* `label`: label including a sentence stating that the user can use the calendar controls or one of the buttons.
* `onClick`: callback(s) invoked when plugin's buttons are clicked.
* `theme`: flatpickr's theme.
*/
export function ShortcutButtonsPlugin(config: ShortcutButtonsFlatpickr.Config) {
const cfg = { ...defaultConfig, ...config };
return (fp: Flatpickr) => {
/**
* Element that wraps this plugin's dependent elements.
*/
let wrapper: HTMLElement;
/**
* Handles click events on plugin's button.
*/
function onClick(event: Event) {
event.stopPropagation();
event.preventDefault();
const target = event.target as HTMLButtonElement;
if (target.tagName.toLowerCase() !== 'button' || typeof cfg.onClick === 'undefined') {
return;
}
const index = parseInt(target.dataset.index, 10);
const callbacks: ShortcutButtonsFlatpickr.OnClickSignature[] = Array.isArray(cfg.onClick) ?
cfg.onClick :
[cfg.onClick];
for (const callback of callbacks) {
if (typeof callback === 'function') {
callback(index, fp);
}
}
}
/**
* Handles key down events on plugin's button.
*/
function onKeyDown(event: KeyboardEvent) {
const target = event.target as HTMLButtonElement;
if (event.key !== 'Tab' || target.tagName.toLowerCase() !== 'button') {
return;
}
if ((event.shiftKey && !target.previousSibling) || (!event.shiftKey && !target.nextSibling)) {
event.preventDefault();
fp.element.focus();
}
}
/**
* Set given button's attributes.
*/
function setButtonsAttributes(button: HTMLButtonElement, attributes?: ShortcutButtonsFlatpickr.Attributes) {
Object.keys(attributes).filter((attribute) => supportedAttributes.has(attribute)).forEach((key) => {
if (key === 'class') {
button.classList.add(...attributes[key].split(' '));
return;
}
button.setAttribute(key, attributes[key])
});
}
return {
/**
* Initialize plugin.
*/
onReady: () => {
wrapper = document.createElement('div');
wrapper.classList.add('shortcut-buttons-flatpickr-wrapper', cfg.theme);
if (typeof cfg.label !== 'undefined' && cfg.label.length) {
const label = document.createElement('div');
label.classList.add('shortcut-buttons-flatpickr-label');
label.textContent = cfg.label;
wrapper.appendChild(label);
}
const buttons = document.createElement('div');
buttons.classList.add('shortcut-buttons-flatpickr-buttons');
(Array.isArray(cfg.button) ? cfg.button : [cfg.button]).forEach((b, index) => {
const button = document.createElement('button');
button.type = 'button';
button.classList.add('shortcut-buttons-flatpickr-button');
button.textContent = b.label;
button.dataset.index = String(index);
if (typeof b.attributes !== 'undefined') {
setButtonsAttributes(button, b.attributes);
}
buttons.appendChild(button);
fp.pluginElements.push(button);
});
wrapper.appendChild(buttons);
fp.calendarContainer.appendChild(wrapper);
wrapper.addEventListener('click', onClick);
wrapper.addEventListener('keydown', onKeyDown);
},
/**
* Clean up before flatpickr is destroyed.
*/
onDestroy: () => {
wrapper.removeEventListener('keydown', onKeyDown);
wrapper.removeEventListener('click', onClick);
wrapper = undefined;
},
};
};
}