/* eslint-disable import/no-extraneous-dependencies */ import { LionCalendar } from '@lion/ui/calendar.js'; import { ScopedElementsMixin } from '@open-wc/scoped-elements'; import { html, css } from 'lit'; import { ifDefined } from 'lit/directives/if-defined.js'; import { LionInputDate } from '@lion/ui/input-date.js'; import { OverlayMixin, withBottomSheetConfig, withModalDialogConfig, ArrowMixin, } from '@lion/ui/overlays.js'; import { LocalizeMixin } from '@lion/ui/localize-no-side-effects.js'; import { localizeNamespaceLoader } from './localizeNamespaceLoader.js'; /** * @typedef {import('../../form-core/src/validate/Validator.js').Validator} Validator * @typedef {import('lit').RenderOptions} RenderOptions */ /** * @customElement lion-input-datepicker */ export class LionInputDatepicker extends ScopedElementsMixin( ArrowMixin(OverlayMixin(LocalizeMixin(LionInputDate))), ) { static get scopedElements() { return { ...super.scopedElements, 'lion-calendar': LionCalendar, }; } static get styles() { return [ ...super.styles, css` .calendar__overlay-frame { display: inline-block; background: white; position: relative; } .calendar-overlay__header { display: flex; } .calendar-overlay__heading { padding: 16px 16px 8px; flex: 1; } .calendar-overlay__heading > .calendar-overlay__close-button { flex: none; } .calendar-overlay__close-button { min-width: 40px; min-height: 32px; border-width: 0; padding: 0; font-size: 24px; } `, ]; } /** @type {any} */ static get properties() { return { /** * The heading to be added on top of the calendar overlay. * Naming chosen from an Application Developer perspective. * For a Subclasser 'calendarOverlayHeading' would be more appropriate. */ calendarHeading: { type: String, attribute: 'calendar-heading', }, /** * The slot to put the invoker button in. Can be 'prefix', 'suffix', 'before' and 'after'. * Default will be 'suffix'. */ _calendarInvokerSlot: { attribute: false, }, __calendarMinDate: { attribute: false, }, __calendarMaxDate: { attribute: false, }, __calendarDisableDates: { attribute: false, }, }; } get slots() { return { ...super.slots, [this._calendarInvokerSlot]: () => this._invokerTemplate(), }; } static get localizeNamespaces() { return [{ 'lion-input-datepicker': localizeNamespaceLoader }, ...super.localizeNamespaces]; } /** * @protected */ get _invokerNode() { return /** @type {HTMLElement} */ (this.querySelector(`#${this.__invokerId}`)); } /** * @type {LionCalendar} * @protected */ get _calendarNode() { return /** @type {LionCalendar} */ ( this._overlayCtrl.contentNode.querySelector('[slot="content"]') ); } constructor() { super(); /** @private */ this.__invokerId = this.__createUniqueIdForA11y(); /** @protected */ this._calendarInvokerSlot = 'suffix'; // Configuration flags for subclassers /** @protected */ this._focusCentralDateOnCalendarOpen = true; /** @protected */ this._hideOnUserSelect = true; /** @protected */ this._syncOnUserSelect = true; /** @protected */ this._isHandlingCalendarUserInput = false; /** @private */ this.__openCalendarOverlay = this.__openCalendarOverlay.bind(this); /** @protected */ this._onCalendarUserSelectedChanged = this._onCalendarUserSelectedChanged.bind(this); } /** @private */ __createUniqueIdForA11y() { return `${this.localName}-${Math.random().toString(36).substr(2, 10)}`; } /** * @param {string} [name] * @param {unknown} [oldValue] * @param {import('lit').PropertyDeclaration} [options] * @returns {void} */ requestUpdate(name, oldValue, options) { super.requestUpdate(name, oldValue, options); if (name === 'disabled' || name === 'readOnly') { this.__toggleInvokerDisabled(); } } /** @private */ __toggleInvokerDisabled() { if (this._invokerNode) { const invokerNode = /** @type {HTMLElement & {disabled: boolean}} */ (this._invokerNode); invokerNode.disabled = this.disabled || this.readOnly; } } /** @param {import('lit').PropertyValues } changedProperties */ firstUpdated(changedProperties) { super.firstUpdated(changedProperties); this.__toggleInvokerDisabled(); } /** @param {import('lit').PropertyValues } changedProperties */ updated(changedProperties) { super.updated(changedProperties); if (changedProperties.has('validators')) { const validators = [...(this.validators || [])]; this.__syncDisabledDates(validators); } if (changedProperties.has('label')) { this.calendarHeading = this.calendarHeading || this.label; } } /** * Defining this overlay as a templates from OverlayMixin * this is our source to give as .contentNode to OverlayController. * Important: do not change the name of this method. * @protected */ _overlayTemplate() { // TODO: add performance optimization to only render the calendar if needed // When not opened (usually on init), it does not need to be rendered. // This would make first paint quicker return html`