var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
    var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
    if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
    else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
    return c > 3 && r && Object.defineProperty(target, key, r), r;
};
import { WithDisposable } from '@blockeditor/lit';
import { isSameDay, isSameMonth, isToday } from 'date-fns';
import { html, LitElement, nothing, } from 'lit';
import { customElement, property } from 'lit/decorators.js';
import { classMap } from 'lit/directives/class-map.js';
import { styleMap } from 'lit/directives/style-map.js';
import { arrowLeftIcon } from './icons.js';
import { datePickerStyle } from './style.js';
import { clamp, getMonthMatrix, toDate } from './utils.js';
const days = ['Su', 'Mo', 'Tu', 'We', 'Th', 'Fr', 'Sa'];
const months = [
    'Jan',
    'Feb',
    'Mar',
    'Apr',
    'May',
    'Jun',
    'Jul',
    'Aug',
    'Sep',
    'Oct',
    'Nov',
    'Dec',
];
/**
 * Date picker
 */
let DatePicker = class DatePicker extends WithDisposable(LitElement) {
    constructor() {
        super(...arguments);
        /** card padding in px */
        this.padding = 20;
        /** cell size in px */
        this.size = 28;
        /** horizontal gap between cells in px */
        this.gapH = 10;
        /** vertical gap between cells in px */
        this.gapV = 8;
        /** current active month */
        this._cursor = new Date();
        /** web-accessibility for month select */
        this._monthCursor = 0;
        this._yearCursor = 0;
        this._monthPickYearCursor = 0;
        /** date matrix */
        this._matrix = [];
        this._yearMatrix = [];
        this._mode = 'date';
        this._maxYear = 2099;
        this._minYear = 1970;
    }
    get year() {
        return this._cursor.getFullYear();
    }
    get month() {
        return this._cursor.getMonth();
    }
    get date() {
        return this._cursor.getDate();
    }
    get day() {
        return this._cursor.getDay();
    }
    get yearLabel() {
        return this.year;
    }
    get monthLabel() {
        return months[this.month];
    }
    get dayLabel() {
        return days[this.day];
    }
    get cardWidth() {
        const colNum = 7;
        return this.size * colNum + this.padding * 2 + this.gapH * (colNum - 1);
    }
    get cardHeight() {
        const rowNum = 7;
        return this.size * rowNum + this.padding * 2 + this.gapV * (rowNum - 1) - 2;
    }
    get minHeight() {
        const rowNum = 8;
        return this.size * rowNum + this.padding * 2 + this.gapV * (rowNum - 1) - 2;
    }
    get _cardStyle() {
        return {
            '--cell-size': `${this.size}px`,
            '--gap-h': `${this.gapH}px`,
            '--gap-v': `${this.gapV}px`,
            'min-width': `${this.cardWidth}px`,
            'min-height': `${this.cardHeight}px`,
            padding: `${this.padding}px`,
        };
    }
    /**
     * Focus on date-cell
     */
    focusDateCell() {
        const lastEl = this.shadowRoot?.querySelector('button.date-cell[tabindex="0"]');
        lastEl?.focus();
    }
    /**
     * check if date-cell is focused
     * @returns
     */
    isDateCellFocused() {
        const focused = this.shadowRoot?.activeElement;
        return focused?.classList.contains('date-cell');
    }
    focusMonthCell() {
        const lastEl = this.shadowRoot?.querySelector('button.month-cell[tabindex="0"]');
        lastEl?.focus();
    }
    isMonthCellFocused() {
        const focused = this.shadowRoot?.activeElement;
        return focused?.classList.contains('month-cell');
    }
    focusYearCell() {
        const lastEl = this.shadowRoot?.querySelector('button.year-cell[tabindex="0"]');
        lastEl?.focus();
    }
    isYearCellFocused() {
        const focused = this.shadowRoot?.activeElement;
        return focused?.classList.contains('year-cell');
    }
    openMonthSelector() {
        this._monthCursor = this.month;
        this._monthPickYearCursor = this.year;
        this._mode = 'month';
    }
    closeMonthSelector() {
        this._mode = 'date';
    }
    toggleMonthSelector() {
        if (this._mode === 'month')
            this.closeMonthSelector();
        else
            this.openMonthSelector();
    }
    openYearSelector() {
        this._yearCursor = clamp(this._minYear, this._maxYear, this.year);
        this._mode = 'year';
        this._getYearMatrix();
    }
    closeYearSelector() {
        this._mode = 'date';
    }
    toggleYearSelector() {
        if (this._mode === 'year')
            this.closeYearSelector();
        else
            this.openYearSelector();
    }
    _moveMonth(offset) {
        this._cursor.setMonth(this._cursor.getMonth() + offset);
        this._getMatrix();
    }
    _modeDecade(offset) {
        this._yearCursor = clamp(this._minYear, this._maxYear, this._yearCursor + offset);
        this._getYearMatrix();
    }
    _onChange(date, emit = true) {
        this._cursor = date;
        this.value = date.getTime();
        this._getMatrix();
        emit && this.onChange?.(date);
    }
    _getMatrix() {
        this._matrix = getMonthMatrix(this._cursor).map(row => {
            return row.map(date => {
                const tabIndex = isSameDay(date, this._cursor) ? 0 : -1;
                return {
                    date,
                    label: date.getDate().toString(),
                    isToday: isToday(date),
                    notCurrentMonth: !isSameMonth(date, this._cursor),
                    selected: this.value ? isSameDay(date, toDate(this.value)) : false,
                    tabIndex,
                };
            });
        });
    }
    _getYearMatrix() {
        // every decade has 12 years
        const no = Math.floor((this._yearCursor - this._minYear) / 12);
        const decade = no * 12;
        const start = this._minYear + decade;
        const end = start + 12;
        this._yearMatrix = Array.from({ length: end - start }, (_, i) => start + i).filter(v => v >= this._minYear && v <= this._maxYear);
    }
    firstUpdated() {
        this._disposables.addFromEvent(this, 'keydown', e => {
            e.stopPropagation();
            const directions = ['ArrowLeft', 'ArrowRight', 'ArrowUp', 'ArrowDown'];
            if (directions.includes(e.key) && this.isDateCellFocused()) {
                e.preventDefault();
                if (e.key === 'ArrowLeft') {
                    this._cursor.setDate(this._cursor.getDate() - 1);
                }
                else if (e.key === 'ArrowRight') {
                    this._cursor.setDate(this._cursor.getDate() + 1);
                }
                else if (e.key === 'ArrowUp') {
                    this._cursor.setDate(this._cursor.getDate() - 7);
                }
                else if (e.key === 'ArrowDown') {
                    this._cursor.setDate(this._cursor.getDate() + 7);
                }
                this._getMatrix();
                setTimeout(this.focusDateCell.bind(this));
            }
            if (directions.includes(e.key) && this.isMonthCellFocused()) {
                e.preventDefault();
                if (e.key === 'ArrowLeft') {
                    this._monthCursor = (this._monthCursor - 1 + 12) % 12;
                }
                else if (e.key === 'ArrowRight') {
                    this._monthCursor = (this._monthCursor + 1) % 12;
                }
                else if (e.key === 'ArrowUp') {
                    this._monthCursor = (this._monthCursor - 3 + 12) % 12;
                }
                else if (e.key === 'ArrowDown') {
                    this._monthCursor = (this._monthCursor + 3) % 12;
                }
                setTimeout(this.focusMonthCell.bind(this));
            }
            if (directions.includes(e.key) && this.isYearCellFocused()) {
                e.preventDefault();
                if (e.key === 'ArrowLeft') {
                    this._modeDecade(-1);
                }
                else if (e.key === 'ArrowRight') {
                    this._modeDecade(1);
                }
                else if (e.key === 'ArrowUp') {
                    this._modeDecade(-3);
                }
                else if (e.key === 'ArrowDown') {
                    this._modeDecade(3);
                }
                setTimeout(this.focusYearCell.bind(this));
            }
            if (e.key === 'Tab') {
                setTimeout(() => {
                    const focused = this.shadowRoot?.activeElement;
                    const firstEl = this.shadowRoot?.querySelector('button');
                    // check if focus the last element, then focus the first element
                    if (!e.shiftKey && !focused)
                        firstEl?.focus();
                    // check if focused element is inside current date-picker
                    if (e.shiftKey && !this.shadowRoot?.contains(focused))
                        this.focusDateCell();
                });
            }
        }, true);
    }
    _switchMode(map) {
        return map[this._mode] ?? nothing;
    }
    updated(_changedProperties) {
        if (_changedProperties.has('value')) {
            // this._getMatrix();
            if (this.value)
                this._onChange(toDate(this.value), false);
            else
                this._getMatrix();
        }
    }
    connectedCallback() {
        super.connectedCallback();
        if (this.value)
            this._cursor = toDate(this.value);
        this._getMatrix();
    }
    /** Actions */
    _navAction(prev, curr, slot) {
        const onPrev = typeof prev === 'function' ? prev : prev.action;
        const onNext = typeof curr === 'function' ? curr : curr.action;
        const prevDisable = typeof prev === 'function' ? false : prev.disable;
        const nextDisable = typeof curr === 'function' ? false : curr.disable;
        const classes = classMap({
            'date-picker-header__action': true,
            'with-slot': !!slot,
        });
        return html `<div class=${classes}>
      <button
        aria-label="previous month"
        class="date-picker-small-action interactive left"
        @click=${onPrev}
        ?disabled=${prevDisable}
      >
        ${arrowLeftIcon}
      </button>
      ${slot ?? nothing}
      <button
        aria-label="next month"
        class="date-picker-small-action interactive right"
        @click=${onNext}
        ?disabled=${nextDisable}
      >
        ${arrowLeftIcon}
      </button>
    </div>`;
    }
    /** Week header */
    _dayHeaderRenderer() {
        return html `<div class="days-header">
      ${days.map(day => html `<div class="date-cell">${day}</div>`)}
    </div>`;
    }
    /** Cell */
    _cellRenderer(cell) {
        const classes = classMap({
            interactive: true,
            'date-cell': true,
            'date-cell--today': cell.isToday,
            'date-cell--not-curr-month': cell.notCurrentMonth,
            'date-cell--selected': !!cell.selected,
        });
        const dateRaw = `${cell.date.getFullYear()}-${cell.date.getMonth()}-${cell.date.getDate()}(${cell.date.getDay()})`;
        return html `<button
      tabindex=${cell.tabIndex ?? -1}
      aria-label=${dateRaw}
      data-date=${dateRaw}
      class=${classes}
      @click=${() => {
            this._onChange(cell.date);
        }}
    >
      ${cell.label}
    </button>`;
    }
    _dateContent() {
        return html ` <div class="date-picker-header">
        <div class="date-picker-header__buttons">
          <button
            class="date-picker-header__date interactive"
            @click=${() => this.toggleMonthSelector()}
          >
            <div>${this.monthLabel}</div>
          </button>

          <button
            class="date-picker-header__date interactive"
            @click=${() => this.toggleYearSelector()}
          >
            <div>${this.yearLabel}</div>
          </button>
        </div>

        ${this._navAction(() => this._moveMonth(-1), () => this._moveMonth(1), html `<button
            tabindex="0"
            aria-label="today"
            class="action-label interactive today"
            @click=${() => {
            this._onChange(new Date());
        }}
          >
            <span>TODAY</span>
          </button>`)}
      </div>
      ${this._dayHeaderRenderer()}
      <div class="date-picker-weeks">
        ${this._matrix.map(week => html `<div class="date-picker-week">
              ${week.map(cell => this._cellRenderer(cell))}
            </div>`)}
      </div>`;
    }
    _monthContent() {
        return html ` <div class="date-picker-header">
        <button
          class="date-picker-header__date interactive"
          @click=${() => this.toggleMonthSelector()}
        >
          <div>${this._monthPickYearCursor}</div>
        </button>

        ${this._navAction({
            action: () => this._monthPickYearCursor--,
            disable: this._monthPickYearCursor <= this._minYear,
        }, {
            action: () => this._monthPickYearCursor++,
            disable: this._monthPickYearCursor >= this._maxYear,
        })}
      </div>
      <div class="date-picker-month">
        ${months.map((month, index) => {
            const isActive = this.value
                ? isSameMonth(this.value, new Date(this._monthPickYearCursor, index, 1))
                : false;
            const classes = classMap({
                'month-cell': true,
                interactive: true,
                active: isActive,
            });
            return html `<button
            tabindex=${this._monthCursor === index ? 0 : -1}
            aria-label=${month}
            class=${classes}
            @click=${() => {
                this._cursor.setMonth(index);
                this._cursor.setFullYear(this._monthPickYearCursor);
                this._mode = 'date';
                this._getMatrix();
            }}
          >
            ${month}
          </button>`;
        })}
      </div>`;
    }
    _yearContent() {
        const startYear = this._yearMatrix[0];
        const endYear = this._yearMatrix[this._yearMatrix.length - 1];
        return html `<div class="date-picker-header">
        <button
          class="date-picker-header__date interactive"
          @click=${() => this.toggleYearSelector()}
        >
          <div>${startYear}-${endYear}</div>
        </button>
        ${this._navAction({
            action: () => this._modeDecade(-12),
            disable: startYear <= this._minYear,
        }, {
            action: () => this._modeDecade(12),
            disable: endYear >= this._maxYear,
        })}
      </div>
      <div class="date-picker-year">
        ${this._yearMatrix.map(year => {
            const isActive = year === this._cursor.getFullYear();
            const classes = classMap({
                'year-cell': true,
                interactive: true,
                active: isActive,
            });
            return html `<button
            tabindex=${this._yearCursor === year ? 0 : -1}
            aria-label=${year}
            class=${classes}
            @click=${() => {
                this._cursor.setFullYear(year);
                this._mode = 'date';
                this._getMatrix();
            }}
          >
            ${year}
          </button>`;
        })}
      </div>`;
    }
    render() {
        const classes = classMap({
            'date-picker': true,
            [`date-picker--mode-${this._mode}`]: true,
        });
        const wrapperStyle = styleMap({
            'min-height': `${this.minHeight}px`,
        });
        return html `<div style=${wrapperStyle} class="date-picker-height-wrapper">
      <div class=${classes} style=${styleMap(this._cardStyle)}>
        ${this._switchMode({
            date: this._dateContent(),
            month: this._monthContent(),
            year: this._yearContent(),
        })}
      </div>
    </div>`;
    }
};
DatePicker.styles = datePickerStyle;
__decorate([
    property({ type: Number })
], DatePicker.prototype, "value", void 0);
__decorate([
    property({ attribute: false })
], DatePicker.prototype, "onChange", void 0);
__decorate([
    property({ type: Number })
], DatePicker.prototype, "padding", void 0);
__decorate([
    property({ type: Number })
], DatePicker.prototype, "size", void 0);
__decorate([
    property({ type: Number })
], DatePicker.prototype, "gapH", void 0);
__decorate([
    property({ type: Number })
], DatePicker.prototype, "gapV", void 0);
__decorate([
    property({ attribute: false })
], DatePicker.prototype, "_monthCursor", void 0);
__decorate([
    property({ attribute: false })
], DatePicker.prototype, "_yearCursor", void 0);
__decorate([
    property({ attribute: false })
], DatePicker.prototype, "_monthPickYearCursor", void 0);
__decorate([
    property({ attribute: false })
], DatePicker.prototype, "_matrix", void 0);
__decorate([
    property({ attribute: false })
], DatePicker.prototype, "_yearMatrix", void 0);
__decorate([
    property({ attribute: false })
], DatePicker.prototype, "_mode", void 0);
DatePicker = __decorate([
    customElement('date-picker')
], DatePicker);
export { DatePicker };
//# sourceMappingURL=date-picker.js.map