carbon-custom-elements
Version:
A Carbon Design System variant that's as easy to use as native HTML elements, with no framework tax, no framework silo.
1 lines • 18.7 kB
Source Map (JSON)
{"version":3,"sources":["components/date-picker/date-picker.ts"],"names":[],"mappings":"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","file":"date-picker.d.ts","sourcesContent":["/**\n * @license\n *\n * Copyright IBM Corp. 2019, 2020\n *\n * This source code is licensed under the Apache-2.0 license found in the\n * LICENSE file in the root directory of this source tree.\n */\n\nimport { html, property, query, customElement, LitElement } from 'lit-element';\nimport flatpickr from 'flatpickr';\nimport { Instance as FlatpickrInstance } from 'flatpickr/dist/types/instance';\nimport { Locale as FlatpickrLocale } from 'flatpickr/dist/types/locale';\nimport { Options as FlatpickrOptions, Plugin as FlatpickrPlugin } from 'flatpickr/dist/types/options';\nimport settings from 'carbon-components/es/globals/js/settings';\nimport on from 'carbon-components/es/globals/js/misc/on';\nimport Handle from '../../globals/internal/handle';\nimport { getISODateString, parseISODateString } from './iso-date';\nimport BXDatePickerInput from './date-picker-input';\nimport appendToPlugin from './append-to-plugin';\nimport cssClassPlugin from './css-class-plugin';\nimport fixEventsPlugin from './fix-events-plugin';\nimport focusPlugin from './focus-plugin';\nimport iconPlugin from './icon-plugin';\nimport monthSelectPlugin from './month-select-plugin';\nimport rangePlugin from './range-plugin';\nimport shadowDOMEventPlugin from './shadow-dom-events-plugin';\nimport stateHandshakePlugin from './state-handshake-plugin';\nimport styles from './date-picker.scss';\n\nconst { prefix } = settings;\n\n/**\n * Date picker modes.\n */\nenum DATE_PICKER_MODE {\n /**\n * Simple mode, without calendar dropdown.\n */\n SIMPLE = 'simple',\n\n /**\n * Single date mode.\n */\n SINGLE = 'single',\n\n /**\n * Range mode.\n */\n RANGE = 'range',\n}\n\n/**\n * Date picker.\n * @element bx-date-picker\n * @fires bx-date-picker-changed - The custom event fired on this element when Flatpickr updates its value.\n */\n@customElement(`${prefix}-date-picker`)\nclass BXDatePicker extends LitElement {\n /**\n * The handle for the listener of `${prefix}-date-picker-changed` event.\n */\n private _hAfterChange: Handle | null = null;\n\n /**\n * The slotted `<bx-date-input kind=\"from\">`.\n */\n private _dateInteractNode: BXDatePickerInput | null = null;\n\n /**\n * The element to put calendar dropdown in.\n */\n @query('#floating-menu-container')\n private _floatingMenuContainerNode!: HTMLDivElement;\n\n /**\n * The internal placeholder for the `value` property.\n */\n private _value!: string;\n\n /**\n * @returns The effective date picker mode, determined by the child `<bx-date-picker-input>`.\n */\n private get _mode() {\n const { selectorInputFrom, selectorInputTo } = this.constructor as typeof BXDatePicker;\n if (this.querySelector(selectorInputTo)) {\n return DATE_PICKER_MODE.RANGE;\n }\n if (this.querySelector(selectorInputFrom)) {\n return DATE_PICKER_MODE.SINGLE;\n }\n return DATE_PICKER_MODE.SIMPLE;\n }\n\n /**\n * @returns The Flatpickr plugins to use.\n */\n private get _datePickerPlugins(): FlatpickrPlugin[] {\n const {\n classCalendarContainer,\n classMonth,\n classWeekdays,\n classDays,\n classWeekday,\n classDay,\n classNoBorder,\n selectorInputFrom,\n selectorInputTo,\n _selectorFlatpickrMonthYearContainer: selectorFlatpickrMonthYearContainer,\n _selectorFlatpickrYearContainer: selectorFlatpickrYearContainer,\n _selectorFlatpickrCurrentMonth: selectorFlatpickrCurrentMonth,\n _selectorFlatpickrMonth: selectorFlatpickrMonth,\n _selectorFlatpickrWeekdays: selectorFlatpickrWeekdays,\n _selectorFlatpickrDays: selectorFlatpickrDays,\n _selectorFlatpickrWeekday: selectorFlatpickrWeekday,\n _selectorFlatpickrDay: selectorFlatpickrDay,\n _classFlatpickrCurrentMonth: classFlatpickrCurrentMonth,\n _classFlatpickrToday: classFlatpickrToday,\n } = this.constructor as typeof BXDatePicker;\n const { _floatingMenuContainerNode: floatingMenuContainerNode, _mode: mode } = this;\n const inputFrom = this.querySelector(selectorInputFrom);\n const inputTo = this.querySelector(selectorInputTo);\n const plugins = [\n appendToPlugin({ appendTo: floatingMenuContainerNode }),\n cssClassPlugin({\n classCalendarContainer,\n classMonth,\n classWeekdays,\n classDays,\n classWeekday,\n classDay,\n classNoBorder,\n selectorFlatpickrMonth,\n selectorFlatpickrWeekdays,\n selectorFlatpickrDays,\n selectorFlatpickrWeekday,\n selectorFlatpickrDay,\n classFlatpickrToday,\n }),\n fixEventsPlugin({ inputFrom: inputFrom as BXDatePickerInput, inputTo: inputTo as BXDatePickerInput }),\n focusPlugin({ inputFrom: inputFrom as BXDatePickerInput, inputTo: inputTo as BXDatePickerInput }),\n iconPlugin(),\n monthSelectPlugin({\n selectorFlatpickrMonthYearContainer,\n selectorFlatpickrYearContainer,\n selectorFlatpickrCurrentMonth,\n classFlatpickrCurrentMonth,\n }),\n shadowDOMEventPlugin(),\n stateHandshakePlugin(this),\n ];\n if (mode === DATE_PICKER_MODE.RANGE) {\n // Flatpickr runs event handlers of last registered plugins first.\n // Ensures `onValueUpdate` of `rangePlugin` runs first\n // given Flatpickr puts `01/01/1970 to 01/02/1970` to from date\n // where `rangePlugin` overrides it to separate them to from/to dates.\n // We want to ensure our handler of `onValueUpdate` (notably one in `<bx-date-picker-input>`)\n // gets the `<input>` value set by `rangePlugin` instead of Flatpickr core.\n plugins.push(rangePlugin({ input: inputTo as HTMLInputElement }));\n }\n return plugins;\n }\n\n /**\n * @returns The options to instantiate Flatpickr with.\n */\n private get _datePickerOptions(): FlatpickrOptions {\n const {\n locale = (this.constructor as typeof BXDatePicker).defaultLocale,\n enabledRange,\n _dateInteractNode: dateInteractNode,\n _datePickerPlugins: plugins,\n _handleFlatpickrError: handleFlatpickrError,\n } = this;\n // We use `<bx-date-picker-input>` to communicate values/events with Flatpickr,\n // but want to use `<input>` in shadow DOM to base the calendar dropdown's position on\n const { input: positionElement } = dateInteractNode!;\n const [minDate = undefined, maxDate = undefined] = !enabledRange ? [] : enabledRange.split('/');\n return {\n allowInput: true,\n dateFormat: this.dateFormat ?? (this.constructor as typeof BXDatePicker).defaultDateFormat,\n disableMobile: true,\n errorHandler: handleFlatpickrError,\n locale,\n maxDate,\n minDate,\n positionElement,\n plugins,\n };\n }\n\n /**\n * Handles `${prefix}-date-picker-changed` event on this element.\n */\n private _handleChange = ({ detail }: CustomEvent) => {\n this._value = detail.selectedDates.map(date => getISODateString(date)).join('/');\n };\n\n /**\n * Handles `slotchange` event in the `<slot>`.\n */\n private _handleSlotChange({ target }: Event) {\n const { _dateInteractNode: oldDateInteractNode } = this;\n const dateInteractNode = (target as HTMLSlotElement)\n .assignedNodes()\n .find(\n node =>\n node.nodeType === Node.ELEMENT_NODE &&\n (node as HTMLElement).matches((this.constructor as typeof BXDatePicker).selectorInputFrom)\n );\n if (oldDateInteractNode !== dateInteractNode) {\n this._dateInteractNode = dateInteractNode as BXDatePickerInput;\n this._instantiateDatePicker();\n }\n }\n\n /**\n * Fires a custom event to notify an error in Flatpickr.\n */\n private _handleFlatpickrError = (error: Error) => {\n this.dispatchEvent(\n new CustomEvent((this.constructor as typeof BXDatePicker).eventFlatpickrError, {\n bubbles: true,\n cancelable: false,\n composed: true,\n detail: {\n error,\n },\n })\n );\n };\n\n /**\n * Instantiates Flatpickr.\n * @returns The Flatpickr instance.\n */\n private _instantiateDatePicker() {\n this._releaseDatePicker();\n const { _dateInteractNode: dateInteractNode } = this;\n // `this._dateInteractNode` won't be there unless there is a slotted `<bx-date-input type=\"from\">`,\n // which means Flatpickr will never be instantiated in \"simple\" mode.\n if (dateInteractNode && dateInteractNode.input) {\n this.calendar = flatpickr(dateInteractNode as any, this._datePickerOptions);\n }\n return this.calendar;\n }\n\n /**\n * Releases Flatpickr instances.\n */\n private _releaseDatePicker() {\n if (this.calendar) {\n this.calendar.destroy();\n this.calendar = null;\n }\n return this.calendar;\n }\n\n /**\n * The Flatpickr instance.\n */\n calendar: FlatpickrInstance | null = null;\n\n /**\n * The date format to let Flatpickr use.\n */\n @property({ attribute: 'date-format' })\n dateFormat!: string;\n\n /**\n * The localization data.\n */\n @property({ attribute: false })\n locale!: FlatpickrLocale;\n\n /**\n * The date range that a user can pick in calendar dropdown.\n */\n @property({ attribute: 'enabled-range' })\n enabledRange!: string;\n\n /**\n * `true` if the date picker should be open.\n */\n @property({ type: Boolean, reflect: true })\n open = false;\n\n /**\n * The date(s) in ISO8601 format (date portion only), for range mode, '/' is used for separate start/end dates.\n */\n @property()\n get value() {\n return this._value;\n }\n\n set value(value: string) {\n const { _value: oldValue } = this;\n this._value = value;\n this.requestUpdate('value', oldValue);\n }\n\n connectedCallback() {\n super.connectedCallback();\n this._instantiateDatePicker();\n // Manually hooks the event listeners on the host element to make the event names configurable\n this._hAfterChange = on(this, (this.constructor as typeof BXDatePicker).eventChange, this._handleChange as EventListener);\n }\n\n disconnectedCallback() {\n if (this._hAfterChange) {\n this._hAfterChange = this._hAfterChange.release();\n }\n this._releaseDatePicker();\n super.disconnectedCallback();\n }\n\n updated(changedProperties) {\n const { calendar, open } = this;\n if (calendar && changedProperties.has('dateFormat')) {\n const { dateFormat } = this;\n calendar.set({ dateFormat });\n }\n if (changedProperties.has('enabledRange')) {\n const { enabledRange } = this;\n const dates = enabledRange.split('/').map(item => (!item ? undefined : parseISODateString(item))); // Allows empty start/end\n if (dates.some(item => Boolean(item && isNaN(Number(item))))) {\n // Allows empty start/end\n throw new Error(`Wrong date format found in \\`enabledRange\\` property: ${enabledRange}`);\n }\n const [minDate, maxDate] = dates;\n if (minDate && maxDate && minDate > maxDate) {\n throw new Error(\n `In \\`enabledRange\\` property, the end date shouldn't be smaller than the start date. You have: ${enabledRange}`\n );\n }\n if (calendar) {\n calendar.set({ minDate, maxDate });\n }\n }\n if (changedProperties.has('open') && calendar) {\n if (open) {\n calendar.open();\n } else {\n calendar.close();\n }\n }\n if (changedProperties.has('value')) {\n const { value } = this;\n const dates = value\n .split('/')\n .filter(Boolean)\n .map(item => parseISODateString(item));\n if (dates.some(item => isNaN(Number(item)))) {\n throw new Error(`Wrong date format found in \\`value\\` property: ${value}`);\n }\n const [startDate, endDate] = dates;\n if (startDate && endDate && startDate > endDate) {\n throw new Error(`In \\`value\\` property, the end date shouldn't be smaller than the start date. You have: ${value}`);\n }\n if (calendar) {\n calendar.setDate(dates);\n const { selectorInputFrom, selectorInputTo } = this.constructor as typeof BXDatePicker;\n const inputFrom = this.querySelector(selectorInputFrom) as BXDatePickerInput;\n const inputTo = this.querySelector(selectorInputTo) as BXDatePickerInput;\n [inputFrom, inputTo].forEach((input, i) => {\n if (input) {\n input.value = !dates[i] ? '' : calendar.formatDate(new Date(dates[i]), calendar.config.dateFormat);\n }\n });\n }\n }\n }\n\n render() {\n const { _handleSlotChange: handleSlotChange } = this;\n return html`\n <a class=\"${prefix}--visually-hidden\" href=\"javascript:void 0\" role=\"navigation\"></a>\n <slot @slotchange=\"${handleSlotChange}\"></slot>\n <div id=\"floating-menu-container\"></div>\n <a class=\"${prefix}--visually-hidden\" href=\"javascript:void 0\" role=\"navigation\"></a>\n `;\n }\n\n /**\n * The CSS selector for Flatpickr's month/year portion.\n */\n private static _selectorFlatpickrMonthYearContainer = '.flatpickr-current-month';\n\n /**\n * The CSS selector for Flatpickr's year portion.\n */\n private static _selectorFlatpickrYearContainer = '.numInputWrapper';\n\n /**\n * The CSS selector for the inner element of Flatpickr's month portion.\n */\n private static _selectorFlatpickrCurrentMonth = '.cur-month';\n\n /**\n * The CSS selector for Flatpickr's month navigator.\n */\n private static _selectorFlatpickrMonth = '.flatpickr-month';\n\n /**\n * The CSS selector for Flatpickr's container of the weekdays.\n */\n private static _selectorFlatpickrWeekdays = '.flatpickr-weekdays';\n\n /**\n * The CSS selector for Flatpickr's container of the days.\n */\n private static _selectorFlatpickrDays = '.flatpickr-days';\n\n /**\n * The CSS selector applied to Flatpickr's each weekdays.\n */\n private static _selectorFlatpickrWeekday = '.flatpickr-weekday';\n\n /**\n * The CSS selector applied to Flatpickr's each days.\n */\n private static _selectorFlatpickrDay = '.flatpickr-day';\n\n /**\n * The CSS class for the inner element of Flatpickr's month portion.\n */\n private static _classFlatpickrCurrentMonth = 'cur-month';\n\n /**\n * The CSS class applied to Flatpickr's \"today\" highlight.\n */\n private static _classFlatpickrToday = 'today';\n\n /**\n * The CSS class for the calendar dropdown.\n */\n static get classCalendarContainer() {\n return `${prefix}--date-picker__calendar`;\n }\n\n /**\n * The CSS class for the month navigator.\n */\n static get classMonth() {\n return `${prefix}--date-picker__month`;\n }\n\n /**\n * The CSS class for the container of the weekdays.\n */\n static get classWeekdays() {\n return `${prefix}--date-picker__weekdays`;\n }\n\n /**\n * The CSS class for the container of the days.\n */\n static get classDays() {\n return `${prefix}--date-picker__days`;\n }\n\n /**\n * The CSS class applied to each weekdays.\n */\n static get classWeekday() {\n return `${prefix}--date-picker__weekday`;\n }\n\n /**\n * The CSS class applied to each days.\n */\n static get classDay() {\n return `${prefix}--date-picker__day`;\n }\n\n /**\n * The CSS class applied to the \"today\" highlight if there are any dates selected.\n */\n static classNoBorder = 'no-border';\n\n /**\n * The default date format.\n */\n static defaultDateFormat = 'm/d/Y';\n\n /**\n * The default localization data.\n */\n static defaultLocale = flatpickr.l10ns.default;\n\n /**\n * A selector that will return the `<input>` to enter starting date.\n */\n static get selectorInputFrom() {\n return `${prefix}-date-picker-input[kind=\"single\"],${prefix}-date-picker-input[kind=\"from\"]`;\n }\n\n /**\n * A selector that will return the `<input>` to enter end date.\n */\n static get selectorInputTo() {\n return `${prefix}-date-picker-input[kind=\"to\"]`;\n }\n\n /**\n * The name of the custom event when Flatpickr throws an error.\n */\n static get eventFlatpickrError() {\n return `${prefix}-date-picker-flatpickr-error`;\n }\n\n /**\n * The name of the custom event fired on this element when Flatpickr updates its value.\n */\n static get eventChange() {\n return `${prefix}-date-picker-changed`;\n }\n\n static styles = styles;\n}\n\nexport default BXDatePicker;\n"]}