UNPKG

@wizco/fenixds-ngx

Version:

Componentes fenix design system para Angular.

673 lines 132 kB
import { Component, EventEmitter, HostListener, Input, Output, ViewEncapsulation, forwardRef, } from '@angular/core'; import { CommonModule } from '@angular/common'; import { FormsModule, NG_VALIDATORS, NG_VALUE_ACCESSOR, ReactiveFormsModule, } from '@angular/forms'; import { buildDisabledPredicate, clampToMinute, coerceDate, formatDate, isValidDate, parseDate, toDateOnly, toIsoDateKey, } from './datepicker.utils'; import * as i0 from "@angular/core"; import * as i1 from "@angular/common"; export class DatepickerComponent { el; openedChange = new EventEmitter(); mode = 'single'; valueFormat = 'api'; inline = false; label = ' '; placeholder; placeholderStart = 'Data inicial'; placeholderEnd = 'Data final'; labelStart = ' '; labelEnd = ' '; inputId = 'datepicker_' + Math.random().toString(36).substring(2); inputName; required = false; readonly = false; showError = false; allowTyping = true; autoClose = true; showClear = true; highlightToday = true; disableFuture = false; disablePast = false; minDate; maxDate; disableWeekends = false; disableHolidays = false; holidays = []; dateFilter; locale = 'pt-BR'; weekStartsOn = 0; displayFormat; apiFormat; isMobile = window.innerWidth <= 768; isOpen = false; view = 'day'; control = null; disabled = false; selected = null; rangeStart = null; rangeEnd = null; activeMonth = toDateOnly(new Date()); activeYearPageStart = new Date().getFullYear() - 6; inputValue = ''; timeValue = '00:00'; manualInputError = null; onTouched = () => { }; propagateChange = () => { }; constructor(el) { this.el = el; } ngOnInit() { if (this.inline) { this.view = this.mode === 'month' ? 'month' : 'day'; } } writeValue(obj) { this.manualInputError = null; if (this.mode === 'range') { const range = this.coerceRange(obj); this.rangeStart = range?.start ? toDateOnly(range.start) : null; this.rangeEnd = range?.end ? toDateOnly(range.end) : null; this.activeMonth = toDateOnly(this.rangeStart || this.rangeEnd || new Date()); this.syncInputText(); return; } const date = this.coerceSingle(obj); if (!date) { this.selected = null; this.inputValue = ''; if (this.mode === 'datetime') this.timeValue = '00:00'; this.activeMonth = toDateOnly(new Date()); return; } if (this.mode === 'month') { this.selected = new Date(date.getFullYear(), date.getMonth(), 1); } else if (this.mode === 'datetime') { this.selected = clampToMinute(date); this.timeValue = `${String(this.selected.getHours()).padStart(2, '0')}:${String(this.selected.getMinutes()).padStart(2, '0')}`; } else { this.selected = toDateOnly(date); } this.activeMonth = toDateOnly(this.selected); this.syncInputText(); } registerOnChange(fn) { this.propagateChange = fn; } registerOnTouched(fn) { this.onTouched = fn; } setDisabledState(isDisabled) { this.disabled = isDisabled; if (isDisabled) this.close(); } validate(control) { this.control = control; if (this.manualInputError) { return { wcoDateInvalid: { reason: this.manualInputError } }; } const isDisabled = this.buildIsDisabled(); if (this.mode === 'range') { if (this.required && (!this.rangeStart || !this.rangeEnd)) { return { required: true }; } if (this.rangeStart && this.rangeEnd) { if (this.rangeStart.getTime() > this.rangeEnd.getTime()) { return { wcoDateRange: { reason: 'startAfterEnd' } }; } } if (this.rangeStart) { const result = isDisabled(this.rangeStart); if (result.disabled) { return { wcoDateDisabled: { part: 'start', reason: result.reason }, }; } } if (this.rangeEnd) { const result = isDisabled(this.rangeEnd); if (result.disabled) { return { wcoDateDisabled: { part: 'end', reason: result.reason } }; } } return null; } if (this.required && !this.selected) { return { required: true }; } if (this.selected) { const result = isDisabled(this.selected); if (result.disabled) return { wcoDateDisabled: { reason: result.reason } }; } return null; } get hasError() { if (this.showError) return true; return !!this.control && this.control.invalid && this.control.touched; } get canInteract() { return !this.disabled && !this.readonly; } get resolvedPlaceholder() { if (this.mode === 'range') { return (this.placeholder || `${this.placeholderStart || 'Data inicial'} - ${this.placeholderEnd || 'Data final'}`); } return this.placeholder || this.resolvedDisplayFormat(); } open() { if (!this.canInteract) return; this.isOpen = true; this.view = this.mode === 'month' ? 'month' : 'day'; this.activeYearPageStart = this.activeMonth.getFullYear() - 6; this.openedChange.emit(true); } close() { if (this.inline) return; this.isOpen = false; this.openedChange.emit(false); } toggleOpen() { if (this.isOpen) this.close(); else this.open(); } onInputChange(value) { this.inputValue = value; if (!this.allowTyping) return; this.manualInputError = null; } onInputBlur() { this.onTouched(); if (!this.allowTyping) return; if (this.mode === 'range') { const parsed = this.parseRangeText(this.inputValue); if (!parsed && this.inputValue.trim()) { this.manualInputError = 'format'; this.emitValue(); return; } this.manualInputError = null; this.rangeStart = parsed?.start ? toDateOnly(parsed.start) : null; this.rangeEnd = parsed?.end ? toDateOnly(parsed.end) : null; if (this.rangeStart && this.rangeEnd) { if (this.rangeStart.getTime() > this.rangeEnd.getTime()) { const tmp = this.rangeStart; this.rangeStart = this.rangeEnd; this.rangeEnd = tmp; } } const isDisabled = this.buildIsDisabled(); if (this.rangeStart && isDisabled(this.rangeStart).disabled) { this.manualInputError = 'disabled'; } if (this.rangeEnd && isDisabled(this.rangeEnd).disabled) { this.manualInputError = 'disabled'; } this.activeMonth = toDateOnly(this.rangeStart || this.rangeEnd || new Date()); this.syncInputText(); this.emitValue(); return; } const parsed = this.parseUserText(this.inputValue); if (!parsed && this.inputValue.trim()) { this.manualInputError = 'format'; this.emitValue(); return; } this.manualInputError = null; this.applySingleSelection(parsed); } onTimeChange(value) { this.timeValue = value; if (this.mode !== 'datetime') return; if (!this.selected) return; const match = /^(\d{2}):(\d{2})$/.exec(value); if (!match) return; const hour = Number.parseInt(match[1], 10); const minute = Number.parseInt(match[2], 10); if (hour > 23 || minute > 59) return; this.selected = new Date(this.selected.getFullYear(), this.selected.getMonth(), this.selected.getDate(), hour, minute, 0, 0); this.syncInputText(); this.emitValue(); } selectDay(day) { if (!this.canInteract) return; if (day.disabled) return; if (this.mode === 'range') { if (!this.rangeStart || (this.rangeStart && this.rangeEnd)) { this.rangeStart = toDateOnly(day.date); this.rangeEnd = null; } else { const end = toDateOnly(day.date); if (end.getTime() < this.rangeStart.getTime()) { this.rangeEnd = this.rangeStart; this.rangeStart = end; } else { this.rangeEnd = end; } if (this.autoClose) this.close(); } this.activeMonth = toDateOnly(this.rangeStart || day.date); this.syncInputText(); this.emitValue(); return; } if (this.mode === 'datetime') { const match = /^(\d{2}):(\d{2})$/.exec(this.timeValue); const hour = match ? Number.parseInt(match[1], 10) : 0; const minute = match ? Number.parseInt(match[2], 10) : 0; this.selected = new Date(day.date.getFullYear(), day.date.getMonth(), day.date.getDate(), hour, minute, 0, 0); this.syncInputText(); this.emitValue(); if (this.autoClose) this.close(); return; } this.selected = toDateOnly(day.date); this.activeMonth = toDateOnly(this.selected); this.syncInputText(); this.emitValue(); if (this.autoClose) this.close(); } selectMonth(monthIndex) { if (!this.canInteract) return; const candidate = new Date(this.activeMonth.getFullYear(), monthIndex, 1); const isDisabled = this.buildIsDisabled(); if (isDisabled(candidate).disabled) return; if (this.mode === 'month') { this.selected = candidate; this.activeMonth = toDateOnly(candidate); this.syncInputText(); this.emitValue(); if (this.autoClose) this.close(); return; } this.activeMonth = toDateOnly(candidate); this.view = 'day'; } selectYear(year) { if (!this.canInteract) return; this.activeMonth = new Date(year, this.activeMonth.getMonth(), 1); this.view = this.mode === 'month' ? 'month' : 'day'; } prev() { if (this.view === 'day') { this.activeMonth = new Date(this.activeMonth.getFullYear(), this.activeMonth.getMonth() - 1, 1); return; } if (this.view === 'month') { this.activeMonth = new Date(this.activeMonth.getFullYear() - 1, this.activeMonth.getMonth(), 1); return; } this.activeYearPageStart -= 12; } next() { if (this.view === 'day') { this.activeMonth = new Date(this.activeMonth.getFullYear(), this.activeMonth.getMonth() + 1, 1); return; } if (this.view === 'month') { this.activeMonth = new Date(this.activeMonth.getFullYear() + 1, this.activeMonth.getMonth(), 1); return; } this.activeYearPageStart += 12; } switchView() { if (this.view === 'day') { this.view = 'month'; return; } if (this.view === 'month') { this.view = 'year'; this.activeYearPageStart = this.activeMonth.getFullYear() - 6; return; } this.view = this.mode === 'month' ? 'month' : 'day'; } get headerLabel() { if (this.view === 'month' || this.view === 'year') { return String(this.activeMonth.getFullYear()); } const monthFormatter = new Intl.DateTimeFormat(this.locale, { month: 'long', year: 'numeric', }); const label = monthFormatter.format(new Date(this.activeMonth.getFullYear(), this.activeMonth.getMonth(), 1)); return label.charAt(0).toUpperCase() + label.slice(1); } get weekdayLabels() { const formatter = new Intl.DateTimeFormat(this.locale, { weekday: 'short' }); const base = new Date(2023, 0, 1); // Sunday const labels = []; for (let i = 0; i < 7; i++) { const day = new Date(base.getFullYear(), base.getMonth(), base.getDate() + i); labels.push(formatter.format(day)); } if (this.weekStartsOn === 1) { const sunday = labels.shift(); if (sunday) labels.push(sunday); } return labels.map((label) => { const clean = label.replace('.', '').trim(); const first = Array.from(clean)[0] ?? ''; return first ? first.toLocaleUpperCase(this.locale) : ''; }); } get days() { const year = this.activeMonth.getFullYear(); const month = this.activeMonth.getMonth(); const first = new Date(year, month, 1); const firstDow = first.getDay(); const offset = this.weekStartsOn === 1 ? (firstDow === 0 ? 6 : firstDow - 1) : firstDow; const start = new Date(year, month, 1 - offset); const isDisabled = this.buildIsDisabled(); const todayKey = toIsoDateKey(new Date()); const cells = []; for (let i = 0; i < 42; i++) { const date = new Date(start.getFullYear(), start.getMonth(), start.getDate() + i); const inMonth = date.getMonth() === month; const key = toIsoDateKey(date); const disabled = isDisabled(date).disabled; const isToday = this.highlightToday && key === todayKey; const selectedKey = this.selected ? toIsoDateKey(this.selected) : null; const startKey = this.rangeStart ? toIsoDateKey(this.rangeStart) : null; const endKey = this.rangeEnd ? toIsoDateKey(this.rangeEnd) : null; const isSelected = selectedKey ? key === selectedKey : false; const isRangeStart = startKey ? key === startKey : false; const isRangeEnd = endKey ? key === endKey : false; const isInRange = this.mode === 'range' && this.rangeStart && this.rangeEnd ? !disabled && key >= toIsoDateKey(this.rangeStart) && key <= toIsoDateKey(this.rangeEnd) : false; cells.push({ date, label: date.getDate(), inMonth, disabled, isToday, isSelected, isRangeStart, isRangeEnd, isInRange, }); } return cells; } isCurrentYear(year) { return year === new Date().getFullYear(); } isSelectedYear(year) { if (this.mode === 'range') return false; return !!this.selected && this.selected.getFullYear() === year; } isCurrentMonth(monthIndex) { const now = new Date(); return (monthIndex === now.getMonth() && this.activeMonth.getFullYear() === now.getFullYear()); } isSelectedMonth(monthIndex) { if (this.mode === 'range') return false; return (!!this.selected && this.selected.getFullYear() === this.activeMonth.getFullYear() && this.selected.getMonth() === monthIndex); } get monthLabels() { const formatter = new Intl.DateTimeFormat(this.locale, { month: 'short' }); const isDisabled = this.buildIsDisabled(); const year = this.activeMonth.getFullYear(); const items = []; for (let i = 0; i < 12; i++) { const date = new Date(year, i, 1); const label = formatter.format(date).replace('.', '').trim(); items.push({ index: i, label: label.toLocaleUpperCase(this.locale), disabled: isDisabled(date).disabled, }); } return items; } get years() { return Array.from({ length: 12 }, (_, i) => this.activeYearPageStart + i); } onDocumentClick(event) { if (!this.isOpen) return; const target = event.target; if (!target) return; if (!this.el.nativeElement.contains(target)) this.close(); } onResize() { this.isMobile = window.innerWidth <= 768; } getFixedContainingBlock(el) { // In some environments (e.g. Storybook), an ancestor may have `transform`, // which changes the containing block for `position: fixed`. // When that happens, we must position relative to that ancestor. let node = el.parentElement; while (node && node !== document.documentElement) { const style = window.getComputedStyle(node); const hasTransform = style.transform && style.transform !== 'none'; const hasPerspective = style.perspective && style.perspective !== 'none'; const hasFilter = style.filter && style.filter !== 'none'; const hasContainPaint = style.contain?.includes('paint') ?? false; if (hasTransform || hasPerspective || hasFilter || hasContainPaint) { return node; } node = node.parentElement; } return null; } emitValue() { this.propagateChange(this.buildOutgoingValue()); } buildOutgoingValue() { const resolvedApiFormat = this.resolvedApiFormat(); const resolvedDisplayFormat = this.resolvedDisplayFormat(); const format = this.valueFormat === 'api' ? resolvedApiFormat : resolvedDisplayFormat; if (this.mode === 'range') { const start = this.rangeStart ? formatDate(this.rangeStart, format) : null; const end = this.rangeEnd ? formatDate(this.rangeEnd, format) : null; return { start, end }; } if (!this.selected) return null; return formatDate(this.selected, format); } resolvedApiFormat() { if (this.apiFormat) return this.apiFormat; if (this.mode === 'datetime') return "yyyy-MM-dd'T'HH:mm"; if (this.mode === 'month') return 'yyyy-MM'; return 'yyyy-MM-dd'; } resolvedDisplayFormat() { if (this.displayFormat) return this.displayFormat; if (this.mode === 'datetime') return 'dd/MM/yyyy HH:mm'; if (this.mode === 'month') return 'MM/yyyy'; return 'dd/MM/yyyy'; } syncInputText() { const display = this.resolvedDisplayFormat(); if (this.mode === 'range') { const start = this.rangeStart ? formatDate(this.rangeStart, display) : ''; const end = this.rangeEnd ? formatDate(this.rangeEnd, display) : ''; this.inputValue = end ? `${start} - ${end}` : start; return; } this.inputValue = this.selected ? formatDate(this.selected, display) : ''; } parseUserText(value) { const display = this.resolvedDisplayFormat(); const api = this.resolvedApiFormat(); const trimmed = value.trim(); if (!trimmed) return null; const parsedDisplay = parseDate(trimmed, display); const parsedApi = parsedDisplay ? null : parseDate(trimmed, api); const parsed = parsedDisplay || parsedApi; if (!parsed) return null; if (this.mode === 'month') return new Date(parsed.getFullYear(), parsed.getMonth(), 1); if (this.mode === 'datetime') return clampToMinute(parsed); return toDateOnly(parsed); } parseRangeText(value) { const trimmed = value.trim(); if (!trimmed) return null; const separators = [' - ', ' até ', ' to ']; let sepFound = null; for (const sep of separators) { if (trimmed.includes(sep)) { sepFound = sep; break; } } if (!sepFound) { const only = this.parseUserText(trimmed); return { start: only, end: null }; } const parts = trimmed.split(sepFound).map((p) => p.trim()); const startText = parts[0] || ''; const endText = parts.slice(1).join(sepFound).trim(); const start = startText ? this.parseUserText(startText) : null; const end = endText ? this.parseUserText(endText) : null; if (!start && !end) return null; return { start, end }; } applySingleSelection(date) { if (this.mode === 'range') return; if (!date) { this.selected = null; this.syncInputText(); this.emitValue(); return; } const isDisabled = this.buildIsDisabled(); if (isDisabled(date).disabled) { this.manualInputError = 'disabled'; this.emitValue(); return; } this.manualInputError = null; if (this.mode === 'month') this.selected = new Date(date.getFullYear(), date.getMonth(), 1); else if (this.mode === 'datetime') this.selected = clampToMinute(date); else this.selected = toDateOnly(date); this.activeMonth = toDateOnly(this.selected); this.syncInputText(); this.emitValue(); } coerceSingle(obj) { if (obj === null || obj === undefined) return null; if (obj instanceof Date) return isValidDate(obj) ? obj : null; if (typeof obj === 'string') return coerceDate(obj); if (typeof obj === 'object' && 'start' in obj && 'end' in obj) return null; return null; } coerceRange(obj) { if (!obj) return null; if (typeof obj !== 'object') return null; if (!('start' in obj) || !('end' in obj)) return null; const start = coerceDate(obj.start); const end = coerceDate(obj.end); return { start, end }; } buildIsDisabled() { const min = coerceDate(this.minDate ?? null); const max = coerceDate(this.maxDate ?? null); const holidaySet = new Set(); for (const h of this.holidays || []) { const d = coerceDate(h); if (d) holidaySet.add(toIsoDateKey(d)); } return buildDisabledPredicate({ disableFuture: this.disableFuture, disablePast: this.disablePast, minDate: min, maxDate: max, disableWeekends: this.disableWeekends, disableHolidays: this.disableHolidays, holidaySet, dateFilter: this.dateFilter || null, }); } static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: DatepickerComponent, deps: [{ token: i0.ElementRef }], target: i0.ɵɵFactoryTarget.Component }); static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.13", type: DatepickerComponent, isStandalone: true, selector: "wco-datepicker", inputs: { mode: "mode", valueFormat: "valueFormat", inline: "inline", label: "label", placeholder: "placeholder", placeholderStart: "placeholderStart", placeholderEnd: "placeholderEnd", labelStart: "labelStart", labelEnd: "labelEnd", inputId: "inputId", inputName: "inputName", required: "required", readonly: "readonly", showError: "showError", allowTyping: "allowTyping", autoClose: "autoClose", showClear: "showClear", highlightToday: "highlightToday", disableFuture: "disableFuture", disablePast: "disablePast", minDate: "minDate", maxDate: "maxDate", disableWeekends: "disableWeekends", disableHolidays: "disableHolidays", holidays: "holidays", dateFilter: "dateFilter", locale: "locale", weekStartsOn: "weekStartsOn", displayFormat: "displayFormat", apiFormat: "apiFormat" }, outputs: { openedChange: "openedChange" }, host: { listeners: { "document:click": "onDocumentClick($event)", "window:resize": "onResize()" } }, providers: [ { provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => DatepickerComponent), multi: true, }, { provide: NG_VALIDATORS, useExisting: DatepickerComponent, multi: true, }, ], ngImport: i0, template: "<div\n class=\"wco-datepicker\"\n [class.wco-datepicker--open]=\"isOpen\"\n [class.wco-datepicker--isMobile]=\"isMobile\"\n>\n <ng-container *ngIf=\"mode !== 'range' && !inline\">\n <div\n class=\"form-field wco-datepicker__field\"\n [class.form-field--error]=\"hasError\"\n [class.form-field--readonly]=\"readonly\"\n >\n <input\n [id]=\"inputId\"\n [name]=\"inputName\"\n [attr.required]=\"required ? '' : null\"\n [placeholder]=\"resolvedPlaceholder\"\n [readOnly]=\"readonly || !allowTyping\"\n [disabled]=\"disabled\"\n [value]=\"inputValue\"\n (click)=\"open()\"\n (input)=\"onInputChange($any($event.target).value)\"\n (blur)=\"onInputBlur()\"\n (keydown.enter)=\"$event.preventDefault(); onInputBlur()\"\n />\n <label [for]=\"inputId\">{{ label }}</label>\n\n <span\n class=\"material-icons-round wco-datepicker__suffix wco-datepicker__toggle\"\n (click)=\"toggleOpen()\"\n >calendar_today</span\n >\n </div>\n </ng-container>\n\n <ng-container *ngIf=\"mode === 'range' && !inline\">\n <div\n class=\"form-field wco-datepicker__field\"\n [class.form-field--error]=\"hasError\"\n [class.form-field--readonly]=\"readonly\"\n >\n <input\n [id]=\"inputId\"\n [name]=\"inputName\"\n [attr.required]=\"required ? '' : null\"\n [placeholder]=\"resolvedPlaceholder\"\n [readOnly]=\"readonly || !allowTyping\"\n [disabled]=\"disabled\"\n [value]=\"inputValue\"\n (click)=\"open()\"\n (input)=\"onInputChange($any($event.target).value)\"\n (blur)=\"onInputBlur()\"\n (keydown.enter)=\"$event.preventDefault(); onInputBlur()\"\n />\n <label [for]=\"inputId\">{{ label }}</label>\n <span\n class=\"material-icons-round wco-datepicker__suffix wco-datepicker__toggle\"\n (click)=\"toggleOpen()\"\n >calendar_today</span\n >\n </div>\n </ng-container>\n\n <div\n class=\"wco-datepicker__box\"\n [class.wco-datepicker__box--open]=\"isOpen || inline\"\n [class.wco-datepicker__box--inline]=\"inline\"\n >\n <div class=\"wco-datepicker__panel\" (click)=\"$event.stopPropagation()\">\n <div class=\"wco-datepicker__header\">\n <button\n type=\"button\"\n class=\"wco-datepicker__title\"\n (click)=\"switchView()\"\n [disabled]=\"!canInteract\"\n >\n {{ headerLabel }}\n <span class=\"material-icons text-primary-600\">{{\n view === \"year\" ? \"arrow_drop_up\" : \"arrow_drop_down\"\n }}</span>\n </button>\n\n <button\n type=\"button\"\n class=\"wco-datepicker__nav\"\n (click)=\"prev()\"\n [disabled]=\"!canInteract\"\n >\n <span class=\"material-icons text-primary-600\">chevron_left</span>\n </button>\n\n <button\n type=\"button\"\n class=\"wco-datepicker__nav\"\n (click)=\"next()\"\n [disabled]=\"!canInteract\"\n >\n <span class=\"material-icons text-primary-600\">chevron_right</span>\n </button>\n </div>\n\n <div class=\"wco-datepicker__content\" *ngIf=\"view === 'day'\">\n <div class=\"wco-datepicker__weekdays\">\n <span *ngFor=\"let w of weekdayLabels\">{{ w }}</span>\n </div>\n <div class=\"wco-datepicker__grid\">\n <button\n type=\"button\"\n class=\"wco-datepicker__day\"\n *ngFor=\"let d of days\"\n [class.wco-datepicker__day--muted]=\"!d.inMonth\"\n [class.wco-datepicker__day--today]=\"d.isToday\"\n [class.wco-datepicker__day--selected]=\"d.isSelected\"\n [class.wco-datepicker__day--range]=\"d.isInRange\"\n [class.wco-datepicker__day--rangeStart]=\"d.isRangeStart\"\n [class.wco-datepicker__day--rangeEnd]=\"d.isRangeEnd\"\n [disabled]=\"d.disabled || !canInteract\"\n (click)=\"selectDay(d)\"\n >\n {{ d.label }}\n </button>\n </div>\n </div>\n\n <div class=\"wco-datepicker__content\" *ngIf=\"view === 'month'\">\n <div class=\"wco-datepicker__months\">\n <button\n type=\"button\"\n class=\"wco-datepicker__month\"\n *ngFor=\"let m of monthLabels\"\n [disabled]=\"m.disabled || !canInteract\"\n [class.wco-datepicker__month--active]=\"\n m.index === activeMonth.getMonth()\n \"\n [class.wco-datepicker__month--current]=\"isCurrentMonth(m.index)\"\n [class.wco-datepicker__month--selected]=\"isSelectedMonth(m.index)\"\n (click)=\"selectMonth(m.index)\"\n >\n {{ m.label }}\n </button>\n </div>\n </div>\n\n <div class=\"wco-datepicker__content\" *ngIf=\"view === 'year'\">\n <div class=\"wco-datepicker__years\">\n <button\n type=\"button\"\n class=\"wco-datepicker__year\"\n *ngFor=\"let y of years\"\n [class.wco-datepicker__year--active]=\"\n y === activeMonth.getFullYear()\n \"\n [class.wco-datepicker__year--current]=\"isCurrentYear(y)\"\n [class.wco-datepicker__year--selected]=\"isSelectedYear(y)\"\n [disabled]=\"!canInteract\"\n (click)=\"selectYear(y)\"\n >\n {{ y }}\n </button>\n </div>\n </div>\n\n <div class=\"wco-datepicker__time\" *ngIf=\"mode === 'datetime'\">\n <label class=\"wco-datepicker__timeLabel\">Hora</label>\n <input\n type=\"time\"\n class=\"wco-datepicker__timeInput\"\n [disabled]=\"disabled || readonly\"\n [value]=\"timeValue\"\n (input)=\"onTimeChange($any($event.target).value)\"\n />\n </div>\n </div>\n </div>\n</div>\n", styles: [":host{display:block;width:100%}.wco-datepicker{width:100%;position:relative;display:inline-block;--wco-datepicker-radius: var(--wco-radius-md, 8px);--wco-datepicker-shadow: var( --wco-shadow-level-1, 0px 2px 16px rgba(0, 0, 0, .2) );--wco-datepicker-bg: var(--wco-color-neutral-50, #fff);--wco-datepicker-border: 1px solid var(--wco-color-neutral-100, #e9ebec);--wco-datepicker-muted: var(--wco-color-neutral-500, #8d9399);--wco-datepicker-text: var(--wco-color-neutral-900, #080809);--wco-datepicker-primary: var(--wco-color-primary-600, #bf5600);--wco-datepicker-primary-weak: var(--wco-color-primary-50, #ffb578);--wco-datepicker-hover: var(--wco-color-neutral-100, #e9ebec)}.wco-datepicker .wco-datepicker__suffix{cursor:pointer;-webkit-user-select:none;user-select:none}.wco-datepicker .wco-datepicker__toggle{transition:transform .15s ease-in-out;color:var(--wco-color-primary-600)}.wco-datepicker--open .wco-datepicker__toggle{transform:rotate(180deg)}.wco-datepicker .wco-datepicker__box{position:absolute;max-width:270px;width:var(--wco-datepicker-overlay-width, 320px);z-index:2147483647;opacity:0;visibility:hidden;pointer-events:none;transition:opacity .12s ease-in-out,transform .12s ease-in-out;transform:translateY(-6px)}.wco-datepicker .wco-datepicker__box--open{opacity:1;visibility:visible;pointer-events:all;transform:translateY(0)}.wco-datepicker--isMobile .wco-datepicker__box{position:absolute;width:100%;padding:var(--wco-spacing-nano, 8px)}.wco-datepicker .wco-datepicker__box--inline{position:static;padding:0;transform:none;transition:none}.wco-datepicker .wco-datepicker__panel{background:var(--wco-datepicker-bg);border:var(--wco-datepicker-border);border-radius:var(--wco-datepicker-radius);box-shadow:var(--wco-datepicker-shadow);padding:var(--wco-spacing-nano, 8px)}.wco-datepicker .wco-datepicker__header{display:grid;grid-template-columns:1fr 40px 40px;gap:var(--wco-spacing-quark, 4px);align-items:center;margin-bottom:var(--wco-spacing-nano, 8px)}.wco-datepicker .wco-datepicker__nav{width:40px;height:40px;border:none;background:transparent;cursor:pointer;color:var(--wco-datepicker-text);display:flex;justify-content:center;align-items:center}.wco-datepicker .wco-datepicker__nav:hover:not(:disabled){background:var(--wco-datepicker-hover)}.wco-datepicker .wco-datepicker__nav:disabled{cursor:not-allowed;opacity:.6}.wco-datepicker .wco-datepicker__title{border:none;background:transparent;cursor:pointer;display:inline-flex;justify-content:flex-start;align-items:center;gap:var(--wco-spacing-quark, 4px);font-family:var(--wco-font-family);font-size:var(--wco-font-size-xs, 16px);color:var(--wco-datepicker-text);padding:var(--wco-spacing-quark, 4px) var(--wco-spacing-nano, 8px)}.wco-datepicker .wco-datepicker__title:hover:not(:disabled){background:var(--wco-datepicker-hover)}.wco-datepicker .wco-datepicker__title:disabled{cursor:not-allowed;opacity:.6}.wco-datepicker .wco-datepicker__weekdays{display:grid;grid-template-columns:repeat(7,1fr);gap:0;padding:0 var(--wco-spacing-quark, 4px);margin-bottom:var(--wco-spacing-quark, 4px);color:var(--wco-datepicker-muted);font-size:var(--wco-font-size-xxxs, 12px);text-align:center;-webkit-user-select:none;user-select:none}.wco-datepicker .wco-datepicker__weekdays>span{width:36px;justify-self:center}.wco-datepicker .wco-datepicker__grid{display:grid;grid-template-columns:repeat(7,1fr);gap:0}.wco-datepicker .wco-datepicker__day{width:36px;height:36px;justify-self:center;position:relative;isolation:isolate;border-radius:999px;border:1px solid transparent;background:transparent;cursor:pointer;font-family:var(--wco-font-family);font-size:var(--wco-font-size-xxxs, 12px);color:var(--wco-datepicker-text)}.wco-datepicker .wco-datepicker__day:hover:not(:disabled){background:var(--wco-datepicker-primary-weak);color:var(--wco-datepicker-primary)}.wco-datepicker .wco-datepicker__day:disabled{cursor:not-allowed;opacity:.35;background:transparent}.wco-datepicker .wco-datepicker__day--muted{color:var(--wco-datepicker-muted)}.wco-datepicker .wco-datepicker__day--today{border-color:var(--wco-datepicker-text)}.wco-datepicker .wco-datepicker__day--selected{background:var(--wco-datepicker-primary);border-color:var(--wco-datepicker-primary);color:var(--wco-color-primary-text, #fff)}.wco-datepicker .wco-datepicker__day--range{background:var(--wco-datepicker-primary-weak);color:var(--wco-datepicker-primary)}.wco-datepicker .wco-datepicker__day--range:not(.wco-datepicker .wco-datepicker__day--rangeStart):not(.wco-datepicker .wco-datepicker__day--rangeEnd){border-radius:0}.wco-datepicker .wco-datepicker__day--rangeStart,.wco-datepicker .wco-datepicker__day--rangeEnd{z-index:5;border-radius:999px;background:var(--wco-datepicker-primary);border-color:var(--wco-datepicker-primary);color:var(--wco-color-primary-text, #fff)}.wco-datepicker .wco-datepicker__months,.wco-datepicker .wco-datepicker__years{display:grid;grid-template-columns:repeat(4,1fr);gap:var(--wco-spacing-quark, 4px)}.wco-datepicker .wco-datepicker__month,.wco-datepicker .wco-datepicker__year{height:26px;border-radius:999px;border:1px solid transparent;background:transparent;cursor:pointer;display:inline-flex;align-items:center;justify-content:flex-start;padding:0 var(--wco-spacing-nano, 8px);width:max-content;justify-self:flex-start;font-family:var(--wco-font-family);font-size:var(--wco-font-size-xxxs, 12px);color:var(--wco-datepicker-text);text-align:left}.wco-datepicker .wco-datepicker__month:hover:not(:disabled),.wco-datepicker .wco-datepicker__year:hover:not(:disabled){background:var(--wco-datepicker-primary-weak);color:var(--wco-datepicker-primary)}.wco-datepicker .wco-datepicker__month:disabled,.wco-datepicker .wco-datepicker__year:disabled{cursor:not-allowed;opacity:.35;background:transparent}.wco-datepicker .wco-datepicker__month--active,.wco-datepicker .wco-datepicker__year--active{background:transparent;border-color:var(--wco-datepicker-text);font-weight:600}.wco-datepicker .wco-datepicker__month--current,.wco-datepicker .wco-datepicker__year--current{border-color:var(--wco-datepicker-text)}.wco-datepicker .wco-datepicker__month--selected,.wco-datepicker .wco-datepicker__year--selected{background:var(--wco-datepicker-primary);border-color:var(--wco-datepicker-primary);color:var(--wco-color-primary-text, #fff);font-weight:600}.wco-datepicker .wco-datepicker__time{margin-top:var(--wco-spacing-nano, 8px);display:grid;grid-template-columns:1fr auto;gap:var(--wco-spacing-nano, 8px);align-items:center}.wco-datepicker .wco-datepicker__timeLabel{font-family:var(--wco-font-family);font-size:var(--wco-font-size-xxxs, 12px);color:var(--wco-datepicker-muted)}.wco-datepicker .wco-datepicker__timeInput{border:var(--wco-datepicker-border);border-radius:999px;padding:var(--wco-spacing-quark, 4px) var(--wco-spacing-nano, 8px);font-family:var(--wco-font-family);font-size:var(--wco-font-size-xxs, 14px)}.wco-datepicker .wco-datepicker__actions{margin-top:var(--wco-spacing-nano, 8px);display:flex;justify-content:space-between;gap:var(--wco-spacing-nano, 8px)}.wco-datepicker .wco-datepicker__action{border:none;background:transparent;cursor:pointer;font-family:var(--wco-font-family);font-size:var(--wco-font-size-xxs, 14px);color:var(--wco-datepicker-primary);padding:var(--wco-spacing-quark, 4px) var(--wco-spacing-nano, 8px);border-radius:999px}.wco-datepicker .wco-datepicker__action:hover:not(:disabled){background:var(--wco-datepicker-hover)}.wco-datepicker .wco-datepicker__action:disabled{cursor:not-allowed;opacity:.6}.wco-datepicker input[readonly]+label+.material-icons-round,.wco-datepicker input:disabled+label+.material-icons-round{cursor:pointer;color:var(--wco-color-neutral-300)}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "ngmodule", type: FormsModule }], encapsulation: i0.ViewEncapsulation.None }); } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: DatepickerComponent, decorators: [{ type: Component, args: [{ selector: 'wco-datepicker', standalone: true, imports: [CommonModule, ReactiveFormsModule, FormsModule], encapsulation: ViewEncapsulation.None, providers: [ { provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => DatepickerComponent), multi: true, }, { provide: NG_VALIDATORS, useExisting: DatepickerComponent, multi: true, }, ], template: "<div\n class=\"wco-datepicker\"\n [class.wco-datepicker--open]=\"isOpen\"\n [class.wco-datepicker--isMobile]=\"isMobile\"\n>\n <ng-container *ngIf=\"mode !== 'range' && !inline\">\n <div\n class=\"form-field wco-datepicker__field\"\n [class.form-field--error]=\"hasError\"\n [class.form-field--readonly]=\"readonly\"\n >\n <input\n [id]=\"inputId\"\n [name]=\"inputName\"\n [attr.required]=\"required ? '' : null\"\n [placeholder]=\"resolvedPlaceholder\"\n [readOnly]=\"readonly || !allowTyping\"\n [disabled]=\"disabled\"\n [value]=\"inputValue\"\n (click)=\"open()\"\n (input)=\"onInputChange($any($event.target).value)\"\n (blur)=\"onInputBlur()\"\n (keydown.enter)=\"$event.preventDefault(); onInputBlur()\"\n />\n <label [for]=\"inputId\">{{ label }}</label>\n\n <span\n class=\"material-icons-round wco-datepicker__suffix wco-datepicker__toggle\"\n (click)=\"toggleOpen()\"\n >calendar_today</span\n >\n </div>\n </ng-container>\n\n <ng-container *ngIf=\"mode === 'range' && !inline\">\n <div\n class=\"form-field wco-datepicker__field\"\n [class.form-field--error]=\"hasError\"\n [class.form-field--readonly]=\"readonly\"\n >\n <input\n [id]=\"inputId\"\n [name]=\"inputName\"\n [attr.required]=\"required ? '' : null\"\n [placeholder]=\"resolvedPlaceholder\"\n [readOnly]=\"readonly || !allowTyping\"\n [disabled]=\"disabled\"\n [value]=\"inputValue\"\n (click)=\"open()\"\n (input)=\"onInputChange($any($event.target).value)\"\n (blur)=\"onInputBlur()\"\n (keydown.enter)=\"$event.preventDefault(); onInputBlur()\"\n />\n <label [for]=\"inputId\">{{ label }}</label>\n <span\n class=\"material-icons-round wco-datepicker__suffix wco-datepicker__toggle\"\n (click)=\"toggleOpen()\"\n >calendar_today</span\n >\n </div>\n </ng-container>\n\n <div\n class=\"wco-datepicker__box\"\n [class.wco-datepicker__box--open]=\"isOpen || inline\"\n [class.wco-datepicker__box--inline]=\"inline\"\n >\n <div class=\"wco-datepicker__panel\" (click)=\"$event.stopPropagation()\">\n <div class=\"wco-datepicker__header\">\n <button\n type=\"button\"\n class=\"wco-datepicker__title\"\n (click)=\"switchView()\"\n [disabled]=\"!canInteract\"\n >\n {{ headerLabel }}\n <span class=\"material-icons text-primary-600\">{{\n view === \"year\" ? \"arrow_drop_up\" : \"arrow_drop_down\"\n }}</span>\n </button>\n\n <button\n type=\"button\"\n class=\"wco-datepicker__nav\"\n (click)=\"prev()\"\n [disabled]=\"!canInteract\"\n >\n <span class=\"material-icons text-primary-600\">chevron_left</span>\n </button>\n\n <button\n type=\"button\"\n class=\"wco-datepicker__nav\"\n (click)=\"next()\"\n [disabled]=\"!canInteract\"\n >\n <span class=\"material-icons text-primary-600\">chevron_right</span>\n </button>\n </div>\n\n <div class=\"wco-datepicker__content\" *ngIf=\"view === 'day'\">\n <div class=\"wco-datepicker__weekdays\">\n <span *ngFor=\"let w of weekdayLabels\">{{ w }}</span>\n </div>\n <div class=\"wco-datepicker__grid\">\n <button\n type=\"button\"\n class=\"wco-datepicker__day\"\n *ngFor=\"let d of days\"\n [class.wco-datepicker__day--muted]=\"!d.inMonth\"\n [class.wco-datepicker__day--today]=\"d.isToday\"\n [class.wco-datepicker__day--selected]=\"d.isSelected\"\n [class.wco-datepicker__day--range]=\"d.isInRange\"\n [class.wco-datepicker__day--rangeStart]=\"d.isRangeStart\"\n [class.wco-datepicker__day--rangeEnd]=\"d.isRangeEnd\"\n [disabled]=\"d.disabled || !canInteract\"\n (click)=\"selectDay(d)\"\n >\n {{ d.label }}\n </button>\n </div>\n </div>\n\n <div class=\"wco-datepicker__content\" *ngIf=\"view === 'month'\">\n <div class=\"wco-datepicker__months\">\n <button\n type=\"button\"\n class=\"wco-datepicker__month\"\n *ngFor=\"let m of monthLabels\"\n [disabled]=\"m.disabled || !canInteract\"\n [class.wco-datepicker__month--active]=\"\n m.index === activeMonth.getMonth()\n \"\n [class.wco-datepicker__month--current]=\"isCurrentMonth(m.index)\"\n [class.wco-datepicker__month--selected]=\"isSelectedMonth(m.index)\"\n (click)=\"selectMonth(m.index)\"\n >\n {{ m.label }}\n </button>\n </div>\n </div>\n\n <div class=\"wco-datepicker__content\" *ngIf=\"view === 'year'\">\n <div class=\"wco-datepicker__years\">\n <button\n type=\"button\"\n class=\"wco-datepicker__year\"\n *ngFor=\"let y of years\"\n [class.wco-datepicker__year--active]=\"\n y === activeMonth.getFullYear()\n \"\n [class.wco-datepicker__year--current]=\"isCurrentYear(y)\"\n [class.wco-datepicker__year--selected]=\"isSelectedYear(y)\"\n [disabled]=\"!canInteract\"\n (click)=\"selectYear(y)\"\n >\n {{ y }}\n </button>\n </div>\n </div>\n\n <div class=\"wco-datepicker__time\" *ngIf=\"mode === 'datetime'\">\n <label class=\"wco-datepicker__timeLabel\">Hora</label>\n <input\n type=\"time\"\n class=\"wco-datepicker__timeInput\"\n [disabled]=\"disabled || readonly\"\n [value]=\"timeValue\"\n (input)=\"onTimeChange($any($event.target).value)\"\n />\n </div>\n </div>\n </div>\n</div>\n", styles: [":host{display:block;width:100%}.wco-datepicker{width:100%;position:relative;display:inline-block;--wco-datepicker-radius: var(--wco-radius-md, 8px);--wco-datepicker-shadow: var( --wco-shadow-level-1, 0px 2px 16px rgba(0, 0, 0, .2) );--wco-datepicker-bg: var(--wco-color-neutral-50, #fff);--wco-datepicker-border: 1px solid var(--wco-color-neutral-100, #e9ebec);--wco-datepicker-muted: var(--wco-color-neutral-500, #8d9399);--wco-datepicker-text: var(--wco-color-neutral-900, #080809);--wco-datepicker-primary: var(--wco-color-primary-600, #bf5600);--wco-datepicker-primary-weak: var(--wco-color-primary-50, #ffb578);--wco-datepicker-hover: var(--wco-color-neutral-100, #e9ebec)}.wco-datepicker .wco-datepicker__suffix{cursor:pointer;-webkit-user-select:none;user-select:none}.wco-datepicker .wco-datepicker__toggle{transition:transform .15s ease-in-out;color:var(--wco-color-primary-600)}.wco-datepicker--open .wco-datepicker__toggle{transform:rotate(180deg)}.wco-datepicker .wco-datepicker__box{position:absolute;max-width:270px;width:var(--wco-datepicker-overlay-width, 320px);z-index:2147483647;opacity:0;visibility:hidden;pointer-events:none;transition:opacity .12s ease-in-out,transform .12s ease-in-out;transform:translateY(-6px)}.wco-datepicker .wco-datepicker__box--open{opacity:1;visibility:visible;pointer-events:all;transform:translateY(0)}.wco-datepicker--isMobile .wco-datepicker__box{position:absolute;width:100%;padding:var(--wco-spacing-nano, 8px)}.wco-datepicker .wco-datepicker__box--inline{position:static;padding:0;transform:none;transition:none}.wco-datepicker .wco-datepicker__panel{background:var(--wco-datepicker-bg);border:var(--wco-datepicker-border);border-radius:var(--wco-datepicker-radius);box-shadow:var(--wco-datepicker-shadow);padding:var(--wco-spacing-nano, 8px)}.wco-datepicker .wco-datepicker__header{display:grid;grid-template-columns:1fr 40px 40px;gap:var(--wco-spacing-quark, 4px);align-items:center;margin-bottom:var(--wco-spacing-nano, 8px)}.wco-datepicker .wco-datepicker__nav{width:40px;height:40px;border:none;background:transparent;cursor:pointer;color:var(--wco-datepicker-text);display:flex;justify-content:center;align-items:center}.wco-datepicker .wco-datepicker__nav:hover:not(:disabled){background:var(--wco-datepicker-hover)}.wco-datepicker .wco-datepicker__nav:disabled{cursor:not-allowed;opacity:.6}.wco-datepicker .wco-datepicker__title{border:none;background:transparent;cursor:pointer;display:inline-flex;justify-content:flex-start;align-items:center;gap:var(--wco-spacing-quark, 4px);font-family:var(--wco-font-family);font-size:var(--wco-font-size-xs, 16px);color:var(--wco-datepicker-text);padding:var(--wco-spacing-quark, 4px) var(--wco-spacing-nano, 8px)}.wco-datepicker .wco-datepicker__title:hover:not(:disabled){background:var(--wco-datepicker-hover)}.wco-datepicker .wco-datepicker__title:disabled{cursor:not-allowed;opacity:.6}.wco-datepicker .wco-datepicker__weekdays{display:grid;grid-template-columns:repeat(7,1fr);gap:0;padding:0 var(--wco-spacing-quark, 4px);margin-bottom:var(--wco-spacing-quark, 4px);color:var(--wco-datepicker-muted);font-size:var(--wco-font-size-xxxs, 12px);text-align:center;-webkit-user-select:none;user-select:none}.wco-datepicker .wco-datepicker__weekdays>span{width:36px;justify-self:center}.wco-datepicker .wco-datepicker__grid{display:grid;grid-template-columns:repeat(7,1fr);gap:0}.wco-datepicker .wco-datepicker__day{width:36px;height:36px;justify-self:center;position:relative;isolation:isolate;border-radius:999px;border:1px solid transparent;background:transparent;cursor:pointer;font-family:var(--wco-font-family);font-size:var(--wco-font-size-xxxs, 12px);color:var(--wco-datepicker-text)}.wco-datepicker .wco-datepicker__day:hover:not(:disa