@carbon/react
Version:
React components for the Carbon Design System
156 lines (145 loc) • 5.72 kB
JavaScript
/**
* Copyright IBM Corp. 2016, 2023
*
* This source code is licensed under the Apache-2.0 license found in the
* LICENSE file in the root directory of this source tree.
*/
import { Enter, ArrowLeft, ArrowRight, ArrowDown } from '../../../internal/keyboard/keys.js';
import { match } from '../../../internal/keyboard/match.js';
/**
* @param {object} config Plugin configuration.
* @returns {Plugin} A Flatpickr plugin to fix Flatpickr's behavior of certain events.
*/
var carbonFlatpickrFixEventsPlugin = config => fp => {
const {
inputFrom,
inputTo,
lastStartValue
} = config;
/**
* Handles `click` outside to close calendar
*/
const handleClickOutside = event => {
if (!fp.isOpen || fp.calendarContainer.contains(event.target) || event.target === inputFrom || event.target === inputTo) {
return;
}
fp.close();
};
/**
* Handles `keydown` event.
*/
const handleKeydown = event => {
const {
target
} = event;
if (inputFrom === target || inputTo === target) {
if (match(event, Enter)) {
// Makes sure the hitting enter key picks up pending values of both `<input>`
// Workaround for: https://github.com/flatpickr/flatpickr/issues/1942
fp.setDate([inputFrom.value, inputTo && inputTo.value], true, fp.config.dateFormat);
event.stopPropagation();
} else if (match(event, ArrowLeft) || match(event, ArrowRight)) {
// Prevents Flatpickr code from canceling the event if left/right arrow keys are hit on `<input>`,
// so user can move the keyboard cursor for editing dates
// Workaround for: https://github.com/flatpickr/flatpickr/issues/1943
event.stopPropagation();
} else if (match(event, ArrowDown)) {
event.preventDefault();
fp.open();
} else if (!fp.config.allowInput) {
// We override the default behaviour of Flatpickr, ideally when allowInput is set to false,
// the Delete/Backspace button clears all of the date, which we don't want, hence
// we stop event bubbling and the default Flatpickr's onChange behaviour here itself
event.stopPropagation();
event.preventDefault();
}
}
};
const parseDateWithFormat = dateStr => fp.parseDate(dateStr, fp.config.dateFormat);
/**
* Handles `blur` event.
*
* For whatever reason, manual changes within the `to` input do not update the
* calendar on blur. If a manual change is made within the input, this block will
* set the date again, triggering the calendar to update.
*/
const handleBlur = event => {
const {
target
} = event;
// Only fall into this logic if the event is on the `to` input and there is a
// `to` date selected
if (inputTo === target && fp.selectedDates[1]) {
// Using getTime() enables the ability to more readily compare the date currently
// selected in the calendar and the date currently in the value of the input
const withoutTime = date => date?.setHours(0, 0, 0, 0);
const selectedToDate = withoutTime(new Date(fp.selectedDates[1]));
const currentValueToDate = withoutTime(parseDateWithFormat(inputTo.value));
// The date should only be set if both dates are valid dates, and they don't match.
// When they don't match, this indicates that the date selected in the calendar is stale,
// and the current value in the input should be set for the calendar to update.
if (selectedToDate && currentValueToDate && selectedToDate !== currentValueToDate) {
// Update the calendar with the value of the `to` date input
fp.setDate([inputFrom.value, inputTo && inputTo.value], true, fp.config.dateFormat);
}
}
const isValidDate = date => date?.toString() !== 'Invalid Date';
// save end date in calendar immediately after it's been written down
if (inputTo === target && fp.selectedDates.length === 1 && inputTo.value) {
if (isValidDate(parseDateWithFormat(inputTo.value))) {
fp.setDate([inputFrom.value, inputTo.value], true, fp.config.dateFormat);
}
}
// overriding the flatpickr bug where the startDate gets deleted on blur
if (inputTo === target && !inputFrom.value && lastStartValue.current) {
if (isValidDate(parseDateWithFormat(lastStartValue.current))) {
inputFrom.value = lastStartValue.current;
if (inputTo.value) {
fp.setDate([inputFrom.value, inputTo.value], true, fp.config.dateFormat);
}
}
}
};
/**
* Releases event listeners used in this Flatpickr plugin.
*/
const release = () => {
const {
inputFrom,
inputTo
} = config;
if (inputTo) {
inputTo.removeEventListener('keydown', handleKeydown, true);
inputTo.removeEventListener('blur', handleBlur, true);
}
inputFrom.removeEventListener('keydown', handleKeydown, true);
document.removeEventListener('click', handleClickOutside, true);
};
/**
* Sets up event listeners used for this Flatpickr plugin.
*/
const init = () => {
release();
const {
inputFrom,
inputTo
} = config;
inputFrom.addEventListener('keydown', handleKeydown, true);
if (inputTo) {
inputTo.addEventListener('keydown', handleKeydown, true);
inputTo.addEventListener('blur', handleBlur, true);
}
document.addEventListener('click', handleClickOutside, true);
};
/**
* Registers this Flatpickr plugin.
*/
const register = () => {
fp.loadedPlugins.push('carbonFlatpickrFixEventsPlugin');
};
return {
onReady: [register, init],
onDestroy: [release]
};
};
export { carbonFlatpickrFixEventsPlugin as default };