@triptease/tt-multi-date-picker 0.3.0 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -156,15 +156,19 @@ class TtMultiDatePicker extends lit_1.LitElement {
156
156
  ${(0, unsafe_svg_js_1.unsafeSVG)(icons_1.chevron)}
157
157
  </button>
158
158
  </div>
159
+ <p class="instructions">Click a date to select it. Shift+click to select a range.</p>
159
160
  <div class="calendar-grid" @click=${this._handleDayClick} @keydown=${this._handleKeyDown} tabindex="0">
160
161
  ${monthList.map((month) => (0, lit_1.html) `
161
- <tt-calendar
162
- display-only
163
- month="${month.toFormat('yyyy-MM')}"
164
- .highlightedRanges=${this.value}
165
- min-date="${(0, if_defined_js_1.ifDefined)(this.minDate)}"
166
- max-date="${(0, if_defined_js_1.ifDefined)(this.maxDate)}"
167
- ></tt-calendar>
162
+ <div class="month-panel">
163
+ <h3 class="month-label">${month.toFormat('MMMM yyyy')}</h3>
164
+ <tt-calendar
165
+ display-only
166
+ month="${month.toFormat('yyyy-MM')}"
167
+ .highlightedRanges=${this.value}
168
+ min-date="${(0, if_defined_js_1.ifDefined)(this.minDate)}"
169
+ max-date="${(0, if_defined_js_1.ifDefined)(this.maxDate)}"
170
+ ></tt-calendar>
171
+ </div>
168
172
  `)}
169
173
  </div>
170
174
  ${this.value.length > 0
@@ -1 +1 @@
1
- {"version":3,"file":"TtMultiDatePicker.js","sourceRoot":"","sources":["../../../src/TtMultiDatePicker.ts"],"names":[],"mappings":";;;;;;;;;AAAA,6BAAgD;AAChD,qDAAoD;AACpD,gEAAyD;AACzD,iCAAiC;AACjC,gEAAyD;AACzD,4CAA2C;AAC3C,iDAA+C;AAC/C,2CAAqC;AACrC,6DAA+D;AAG/D,MAAa,iBAAkB,SAAQ,gBAAU;IA4B/C;QACE,KAAK,EAAE,CAAC;QAtBV,WAAM,GAAe,CAAC,CAAC;QAMvB,UAAK,GAAc,EAAE,CAAC;QAEO,SAAI,GAAW,EAAE,CAAC;QACH,aAAQ,GAAY,KAAK,CAAC;QAK9D,uBAAkB,GAAa,gBAAQ,CAAC,KAAK,EAAE,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;QAKjE,iBAAY,GAAa,gBAAQ,CAAC,KAAK,EAAE,CAAC;QAIhD,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC,eAAe,EAAE,CAAC;IAC3C,CAAC;IAEQ,iBAAiB;QACxB,KAAK,CAAC,iBAAiB,EAAE,CAAC;QAC1B,IAAI,IAAI,CAAC,UAAU,EAAE,CAAC;YACpB,IAAI,CAAC,kBAAkB,GAAG,gBAAQ,CAAC,OAAO,CAAC,IAAI,CAAC,UAAU,GAAG,KAAK,CAAC,CAAC;QACtE,CAAC;QACD,IAAI,CAAC,SAAS,EAAE,CAAC;IACnB,CAAC;IAEO,SAAS;QACf,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;QACtE,IAAI,CAAC,UAAU,CAAC,YAAY,CAAC,GAAG,CAAC,CAAC;QAClC,IAAI,IAAI,CAAC,QAAQ,IAAI,IAAI,CAAC,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAC7C,IAAI,CAAC,UAAU,CAAC,WAAW,CAAC,EAAE,YAAY,EAAE,IAAI,EAAE,EAAE,iCAAiC,CAAC,CAAC;QACzF,CAAC;aAAM,CAAC;YACN,IAAI,CAAC,UAAU,CAAC,WAAW,CAAC,EAAE,CAAC,CAAC;QAClC,CAAC;IACH,CAAC;IAED,iBAAiB;QACf,IAAI,CAAC,KAAK,GAAG,EAAE,CAAC;QAChB,IAAI,CAAC,gBAAgB,GAAG,SAAS,CAAC;QAClC,IAAI,CAAC,SAAS,EAAE,CAAC;IACnB,CAAC;IAEO,aAAa;QACnB,OAAO,KAAK,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,kBAAkB,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC;IACpG,CAAC;IAEO,aAAa;QACnB,IAAI,CAAC,kBAAkB,GAAG,IAAI,CAAC,kBAAkB,CAAC,KAAK,CAAC,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE,CAAC,CAAC;IACnF,CAAC;IAEO,SAAS;QACf,IAAI,CAAC,kBAAkB,GAAG,IAAI,CAAC,kBAAkB,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE,CAAC,CAAC;IAClF,CAAC;IAEO,kBAAkB;QACxB,MAAM,KAAK,GAAG,IAAI,CAAC,kBAAkB,CAAC;QACtC,MAAM,IAAI,GAAG,IAAI,CAAC,kBAAkB,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC,CAAC;QACvE,OAAO,GAAG,KAAK,CAAC,QAAQ,CAAC,WAAW,CAAC,MAAM,IAAI,CAAC,QAAQ,CAAC,WAAW,CAAC,EAAE,CAAC;IAC1E,CAAC;IAEO,mBAAmB,CAAC,IAAc;QACxC,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAC;QAC1C,MAAM,SAAS,GAAG,IAAI,CAAC,UAAW,CAAC,gBAAgB,CAAC,aAAa,CAAC,CAAC;QACnE,KAAK,MAAM,GAAG,IAAI,SAAS,EAAE,CAAC;YAC5B,IAAI,GAAG,CAAC,YAAY,CAAC,OAAO,CAAC,KAAK,QAAQ;gBAAE,OAAO,GAAG,CAAC;QACzD,CAAC;QACD,OAAO,SAAS,CAAC;IACnB,CAAC;IAEO,UAAU,CAAC,IAAc;QAC/B,IAAI,CAAC,YAAY,GAAG,IAAI,CAAC;QACzB,IAAI,CAAC,cAAc,CAAC,IAAI,CAAC,GAAG,EAAE;YAC5B,MAAM,GAAG,GAAG,IAAI,CAAC,mBAAmB,CAAC,IAAI,CAAgE,CAAC;YAC1G,GAAG,EAAE,QAAQ,CAAC,IAAI,CAAC,CAAC;QACtB,CAAC,CAAC,CAAC;IACL,CAAC;IAEO,eAAe,CAAC,OAAe,EAAE,QAAiB;QACxD,IAAI,QAAQ,IAAI,IAAI,CAAC,gBAAgB,EAAE,CAAC;YACtC,IAAI,CAAC,KAAK,GAAG,IAAA,gCAAW,EAAC,IAAI,CAAC,KAAK,EAAE,IAAI,CAAC,gBAAgB,EAAE,OAAO,CAAC,CAAC;QACvE,CAAC;aAAM,CAAC;YACN,IAAI,CAAC,KAAK,GAAG,IAAA,+BAAU,EAAC,IAAI,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC;QAC/C,CAAC;QAED,IAAI,CAAC,gBAAgB,GAAG,OAAO,CAAC;QAChC,IAAI,CAAC,SAAS,EAAE,CAAC;QACjB,IAAI,CAAC,aAAa,CAAC,IAAI,KAAK,CAAC,QAAQ,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;IAC7E,CAAC;IAEO,eAAe,CAAC,KAAiB;QACvC,MAAM,MAAM,GAAG,KAAK,CAAC,YAAY,EAAE,CAAC,CAAC,CAAgB,CAAC;QACtD,MAAM,OAAO,GAAG,MAAM,CAAC,OAAO,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,SAAS,EAAE,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;QAChG,MAAM,OAAO,GAAG,OAAO,EAAE,YAAY,CAAC,WAAW,CAAC,CAAC;QACnD,IAAI,CAAC,OAAO;YAAE,OAAO;QAErB,IAAI,CAAC,YAAY,GAAG,gBAAQ,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;QAC9C,IAAI,CAAC,eAAe,CAAC,OAAO,EAAE,KAAK,CAAC,QAAQ,CAAC,CAAC;IAChD,CAAC;IAEO,cAAc,CAAC,KAAoB;QACzC,QAAQ,KAAK,CAAC,GAAG,EAAE,CAAC;YAClB,KAAK,YAAY;gBACf,KAAK,CAAC,cAAc,EAAE,CAAC;gBACvB,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC;gBACrD,MAAM;YACR,KAAK,WAAW;gBACd,KAAK,CAAC,cAAc,EAAE,CAAC;gBACvB,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,YAAY,CAAC,KAAK,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC;gBACtD,MAAM;YACR,KAAK,WAAW;gBACd,KAAK,CAAC,cAAc,EAAE,CAAC;gBACvB,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC;gBACtD,MAAM;YACR,KAAK,SAAS;gBACZ,KAAK,CAAC,cAAc,EAAE,CAAC;gBACvB,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,YAAY,CAAC,KAAK,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC;gBACvD,MAAM;YACR,KAAK,MAAM;gBACT,KAAK,CAAC,cAAc,EAAE,CAAC;gBACvB,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,YAAY,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC;gBACnD,MAAM;YACR,KAAK,KAAK;gBACR,KAAK,CAAC,cAAc,EAAE,CAAC;gBACvB,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,YAAY,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC;gBACjD,MAAM;YACR,KAAK,UAAU;gBACb,KAAK,CAAC,cAAc,EAAE,CAAC;gBACvB,IAAI,CAAC,SAAS,EAAE,CAAC;gBACjB,MAAM;YACR,KAAK,QAAQ;gBACX,KAAK,CAAC,cAAc,EAAE,CAAC;gBACvB,IAAI,CAAC,aAAa,EAAE,CAAC;gBACrB,MAAM;YACR,KAAK,OAAO,CAAC;YACb,KAAK,GAAG;gBACN,KAAK,CAAC,cAAc,EAAE,CAAC;gBACvB,IAAI,CAAC,eAAe,CAAC,IAAI,CAAC,YAAY,CAAC,SAAS,EAAG,EAAE,KAAK,CAAC,QAAQ,CAAC,CAAC;gBACrE,MAAM;QACV,CAAC;IACH,CAAC;IAEO,SAAS;QACf,IAAI,CAAC,KAAK,GAAG,EAAE,CAAC;QAChB,IAAI,CAAC,gBAAgB,GAAG,SAAS,CAAC;QAClC,IAAI,CAAC,SAAS,EAAE,CAAC;QACjB,IAAI,CAAC,aAAa,CAAC,IAAI,KAAK,CAAC,QAAQ,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;IAC7E,CAAC;IAEQ,MAAM;QACb,MAAM,SAAS,GAAG,IAAI,CAAC,aAAa,EAAE,CAAC;QAEvC,OAAO,IAAA,UAAI,EAAA;;yBAEU,IAAI,CAAC,aAAa,yBAAyB,IAAI,CAAC,MAAM,YAAY,IAAA,yBAAS,EAAC,eAAO,CAAC;cAC/F,IAAI,CAAC,kBAAkB,EAAE;yBACd,IAAI,CAAC,SAAS,qBAAqB,IAAI,CAAC,MAAM;YAC3D,IAAA,yBAAS,EAAC,eAAO,CAAC;;;0CAGY,IAAI,CAAC,eAAe,aAAa,IAAI,CAAC,cAAc;UACpF,SAAS,CAAC,GAAG,CACb,CAAC,KAAK,EAAE,EAAE,CAAC,IAAA,UAAI,EAAA;;;uBAGF,KAAK,CAAC,QAAQ,CAAC,SAAS,CAAC;mCACb,IAAI,CAAC,KAAK;0BACnB,IAAA,yBAAS,EAAC,IAAI,CAAC,OAAO,CAAC;0BACvB,IAAA,yBAAS,EAAC,IAAI,CAAC,OAAO,CAAC;;WAEtC,CACF;;QAED,IAAI,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC;YACrB,CAAC,CAAC,IAAA,UAAI,EAAA;;gDAEkC,IAAI,CAAC,KAAK,CAAC,MAAM,aAAa,IAAI,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE;0EACpC,IAAI,CAAC,SAAS;;WAE7E;YACH,CAAC,CAAC,aAAO;KACZ,CAAC;IACJ,CAAC;;AApMH,8CAqMC;AApMQ,gCAAc,GAAG,IAAI,AAAP,CAAQ;AACb,wBAAM,GAAG,kBAAM,AAAT,CAAU;AAKhC;IADC,IAAA,wBAAQ,EAAC,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;iDACJ;AAGvB;IADC,IAAA,wBAAQ,EAAC,EAAE,SAAS,EAAE,aAAa,EAAE,CAAC;qDACnB;AAGpB;IADC,IAAA,wBAAQ,EAAC,EAAE,SAAS,EAAE,KAAK,EAAE,CAAC;gDACT;AAEO;IAA5B,IAAA,wBAAQ,EAAC,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;+CAAmB;AACH;IAA3C,IAAA,wBAAQ,EAAC,EAAE,IAAI,EAAE,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;mDAA2B;AACjC;IAApC,IAAA,wBAAQ,EAAC,EAAE,SAAS,EAAE,UAAU,EAAE,CAAC;kDAAkB;AACjB;IAApC,IAAA,wBAAQ,EAAC,EAAE,SAAS,EAAE,UAAU,EAAE,CAAC;kDAAkB;AAG9C;IADP,IAAA,qBAAK,GAAE;6DACiE;AAKjE;IADP,IAAA,qBAAK,GAAE;uDAC0C","sourcesContent":["import { html, LitElement, nothing } from 'lit';\nimport { property, state } from 'lit/decorators.js';\nimport { ifDefined } from 'lit/directives/if-defined.js';\nimport { DateTime } from 'luxon';\nimport { unsafeSVG } from 'lit/directives/unsafe-svg.js';\nimport { chevron } from '@triptease/icons';\nimport '@triptease/tt-calendar/tt-calendar.js';\nimport { styles } from './styles.js';\nimport { toggleDate, extendRange } from './selection-model.js';\nimport type { Selection } from './selection-model.js';\n\nexport class TtMultiDatePicker extends LitElement {\n static formAssociated = true;\n static override styles = styles;\n\n private _internals: ElementInternals;\n\n @property({ type: Number })\n months: 3 | 6 | 12 = 6;\n\n @property({ attribute: 'start-month' })\n startMonth?: string;\n\n @property({ attribute: false })\n value: Selection = [];\n\n @property({ reflect: true }) name: string = '';\n @property({ type: Boolean, reflect: true }) required: boolean = false;\n @property({ attribute: 'min-date' }) minDate?: string;\n @property({ attribute: 'max-date' }) maxDate?: string;\n\n @state()\n private _currentStartMonth: DateTime = DateTime.local().startOf('month');\n\n private _lastClickedDate?: string;\n\n @state()\n private _focusedDate: DateTime = DateTime.local();\n\n constructor() {\n super();\n this._internals = this.attachInternals();\n }\n\n override connectedCallback() {\n super.connectedCallback();\n if (this.startMonth) {\n this._currentStartMonth = DateTime.fromISO(this.startMonth + '-01');\n }\n this._syncForm();\n }\n\n private _syncForm() {\n const val = this.value.length > 0 ? JSON.stringify(this.value) : null;\n this._internals.setFormValue(val);\n if (this.required && this.value.length === 0) {\n this._internals.setValidity({ valueMissing: true }, 'Please select at least one date');\n } else {\n this._internals.setValidity({});\n }\n }\n\n formResetCallback() {\n this.value = [];\n this._lastClickedDate = undefined;\n this._syncForm();\n }\n\n private _getMonthList(): DateTime[] {\n return Array.from({ length: this.months }, (_, i) => this._currentStartMonth.plus({ months: i }));\n }\n\n private _previousPage() {\n this._currentStartMonth = this._currentStartMonth.minus({ months: this.months });\n }\n\n private _nextPage() {\n this._currentStartMonth = this._currentStartMonth.plus({ months: this.months });\n }\n\n private _getDateRangeLabel(): string {\n const first = this._currentStartMonth;\n const last = this._currentStartMonth.plus({ months: this.months - 1 });\n return `${first.toFormat('MMMM yyyy')} — ${last.toFormat('MMMM yyyy')}`;\n }\n\n private _getCalendarForDate(date: DateTime): Element | undefined {\n const monthStr = date.toFormat('yyyy-MM');\n const calendars = this.shadowRoot!.querySelectorAll('tt-calendar');\n for (const cal of calendars) {\n if (cal.getAttribute('month') === monthStr) return cal;\n }\n return undefined;\n }\n\n private _moveFocus(date: DateTime) {\n this._focusedDate = date;\n this.updateComplete.then(() => {\n const cal = this._getCalendarForDate(date) as (Element & { focusDay: (d: DateTime) => void }) | undefined;\n cal?.focusDay(date);\n });\n }\n\n private _applySelection(dateStr: string, shiftKey: boolean) {\n if (shiftKey && this._lastClickedDate) {\n this.value = extendRange(this.value, this._lastClickedDate, dateStr);\n } else {\n this.value = toggleDate(this.value, dateStr);\n }\n\n this._lastClickedDate = dateStr;\n this._syncForm();\n this.dispatchEvent(new Event('change', { bubbles: true, composed: true }));\n }\n\n private _handleDayClick(event: MouseEvent) {\n const target = event.composedPath()[0] as HTMLElement;\n const dayCell = target.closest?.('.day') ?? (target.classList?.contains('day') ? target : null);\n const dateStr = dayCell?.getAttribute('data-date');\n if (!dateStr) return;\n\n this._focusedDate = DateTime.fromISO(dateStr);\n this._applySelection(dateStr, event.shiftKey);\n }\n\n private _handleKeyDown(event: KeyboardEvent) {\n switch (event.key) {\n case 'ArrowRight':\n event.preventDefault();\n this._moveFocus(this._focusedDate.plus({ days: 1 }));\n break;\n case 'ArrowLeft':\n event.preventDefault();\n this._moveFocus(this._focusedDate.minus({ days: 1 }));\n break;\n case 'ArrowDown':\n event.preventDefault();\n this._moveFocus(this._focusedDate.plus({ weeks: 1 }));\n break;\n case 'ArrowUp':\n event.preventDefault();\n this._moveFocus(this._focusedDate.minus({ weeks: 1 }));\n break;\n case 'Home':\n event.preventDefault();\n this._moveFocus(this._focusedDate.startOf('week'));\n break;\n case 'End':\n event.preventDefault();\n this._moveFocus(this._focusedDate.endOf('week'));\n break;\n case 'PageDown':\n event.preventDefault();\n this._nextPage();\n break;\n case 'PageUp':\n event.preventDefault();\n this._previousPage();\n break;\n case 'Enter':\n case ' ':\n event.preventDefault();\n this._applySelection(this._focusedDate.toISODate()!, event.shiftKey);\n break;\n }\n }\n\n private _clearAll() {\n this.value = [];\n this._lastClickedDate = undefined;\n this._syncForm();\n this.dispatchEvent(new Event('change', { bubbles: true, composed: true }));\n }\n\n override render() {\n const monthList = this._getMonthList();\n\n return html`\n <div class=\"navigation\">\n <button @click=${this._previousPage} aria-label=\"Previous ${this.months} months\">${unsafeSVG(chevron)}</button>\n <h2>${this._getDateRangeLabel()}</h2>\n <button @click=${this._nextPage} aria-label=\"Next ${this.months} months\" class=\"right\">\n ${unsafeSVG(chevron)}\n </button>\n </div>\n <div class=\"calendar-grid\" @click=${this._handleDayClick} @keydown=${this._handleKeyDown} tabindex=\"0\">\n ${monthList.map(\n (month) => html`\n <tt-calendar\n display-only\n month=\"${month.toFormat('yyyy-MM')}\"\n .highlightedRanges=${this.value}\n min-date=\"${ifDefined(this.minDate)}\"\n max-date=\"${ifDefined(this.maxDate)}\"\n ></tt-calendar>\n `\n )}\n </div>\n ${this.value.length > 0\n ? html`\n <div class=\"footer\">\n <span class=\"selection-summary\">${this.value.length} selection${this.value.length > 1 ? 's' : ''}</span>\n <button data-action=\"clear\" data-theme=\"secondary\" @click=${this._clearAll}>Clear all</button>\n </div>\n `\n : nothing}\n `;\n }\n}\n"]}
1
+ {"version":3,"file":"TtMultiDatePicker.js","sourceRoot":"","sources":["../../../src/TtMultiDatePicker.ts"],"names":[],"mappings":"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","sourcesContent":["import { html, LitElement, nothing } from 'lit';\nimport { property, state } from 'lit/decorators.js';\nimport { ifDefined } from 'lit/directives/if-defined.js';\nimport { DateTime } from 'luxon';\nimport { unsafeSVG } from 'lit/directives/unsafe-svg.js';\nimport { chevron } from '@triptease/icons';\nimport '@triptease/tt-calendar/tt-calendar.js';\nimport { styles } from './styles.js';\nimport { toggleDate, extendRange } from './selection-model.js';\nimport type { Selection } from './selection-model.js';\n\nexport class TtMultiDatePicker extends LitElement {\n static formAssociated = true;\n static override styles = styles;\n\n private _internals: ElementInternals;\n\n @property({ type: Number })\n months: 3 | 6 | 12 = 6;\n\n @property({ attribute: 'start-month' })\n startMonth?: string;\n\n @property({ attribute: false })\n value: Selection = [];\n\n @property({ reflect: true }) name: string = '';\n @property({ type: Boolean, reflect: true }) required: boolean = false;\n @property({ attribute: 'min-date' }) minDate?: string;\n @property({ attribute: 'max-date' }) maxDate?: string;\n\n @state()\n private _currentStartMonth: DateTime = DateTime.local().startOf('month');\n\n private _lastClickedDate?: string;\n\n @state()\n private _focusedDate: DateTime = DateTime.local();\n\n constructor() {\n super();\n this._internals = this.attachInternals();\n }\n\n override connectedCallback() {\n super.connectedCallback();\n if (this.startMonth) {\n this._currentStartMonth = DateTime.fromISO(this.startMonth + '-01');\n }\n this._syncForm();\n }\n\n private _syncForm() {\n const val = this.value.length > 0 ? JSON.stringify(this.value) : null;\n this._internals.setFormValue(val);\n if (this.required && this.value.length === 0) {\n this._internals.setValidity({ valueMissing: true }, 'Please select at least one date');\n } else {\n this._internals.setValidity({});\n }\n }\n\n formResetCallback() {\n this.value = [];\n this._lastClickedDate = undefined;\n this._syncForm();\n }\n\n private _getMonthList(): DateTime[] {\n return Array.from({ length: this.months }, (_, i) => this._currentStartMonth.plus({ months: i }));\n }\n\n private _previousPage() {\n this._currentStartMonth = this._currentStartMonth.minus({ months: this.months });\n }\n\n private _nextPage() {\n this._currentStartMonth = this._currentStartMonth.plus({ months: this.months });\n }\n\n private _getDateRangeLabel(): string {\n const first = this._currentStartMonth;\n const last = this._currentStartMonth.plus({ months: this.months - 1 });\n return `${first.toFormat('MMMM yyyy')} — ${last.toFormat('MMMM yyyy')}`;\n }\n\n private _getCalendarForDate(date: DateTime): Element | undefined {\n const monthStr = date.toFormat('yyyy-MM');\n const calendars = this.shadowRoot!.querySelectorAll('tt-calendar');\n for (const cal of calendars) {\n if (cal.getAttribute('month') === monthStr) return cal;\n }\n return undefined;\n }\n\n private _moveFocus(date: DateTime) {\n this._focusedDate = date;\n this.updateComplete.then(() => {\n const cal = this._getCalendarForDate(date) as (Element & { focusDay: (d: DateTime) => void }) | undefined;\n cal?.focusDay(date);\n });\n }\n\n private _applySelection(dateStr: string, shiftKey: boolean) {\n if (shiftKey && this._lastClickedDate) {\n this.value = extendRange(this.value, this._lastClickedDate, dateStr);\n } else {\n this.value = toggleDate(this.value, dateStr);\n }\n\n this._lastClickedDate = dateStr;\n this._syncForm();\n this.dispatchEvent(new Event('change', { bubbles: true, composed: true }));\n }\n\n private _handleDayClick(event: MouseEvent) {\n const target = event.composedPath()[0] as HTMLElement;\n const dayCell = target.closest?.('.day') ?? (target.classList?.contains('day') ? target : null);\n const dateStr = dayCell?.getAttribute('data-date');\n if (!dateStr) return;\n\n this._focusedDate = DateTime.fromISO(dateStr);\n this._applySelection(dateStr, event.shiftKey);\n }\n\n private _handleKeyDown(event: KeyboardEvent) {\n switch (event.key) {\n case 'ArrowRight':\n event.preventDefault();\n this._moveFocus(this._focusedDate.plus({ days: 1 }));\n break;\n case 'ArrowLeft':\n event.preventDefault();\n this._moveFocus(this._focusedDate.minus({ days: 1 }));\n break;\n case 'ArrowDown':\n event.preventDefault();\n this._moveFocus(this._focusedDate.plus({ weeks: 1 }));\n break;\n case 'ArrowUp':\n event.preventDefault();\n this._moveFocus(this._focusedDate.minus({ weeks: 1 }));\n break;\n case 'Home':\n event.preventDefault();\n this._moveFocus(this._focusedDate.startOf('week'));\n break;\n case 'End':\n event.preventDefault();\n this._moveFocus(this._focusedDate.endOf('week'));\n break;\n case 'PageDown':\n event.preventDefault();\n this._nextPage();\n break;\n case 'PageUp':\n event.preventDefault();\n this._previousPage();\n break;\n case 'Enter':\n case ' ':\n event.preventDefault();\n this._applySelection(this._focusedDate.toISODate()!, event.shiftKey);\n break;\n }\n }\n\n private _clearAll() {\n this.value = [];\n this._lastClickedDate = undefined;\n this._syncForm();\n this.dispatchEvent(new Event('change', { bubbles: true, composed: true }));\n }\n\n override render() {\n const monthList = this._getMonthList();\n\n return html`\n <div class=\"navigation\">\n <button @click=${this._previousPage} aria-label=\"Previous ${this.months} months\">${unsafeSVG(chevron)}</button>\n <h2>${this._getDateRangeLabel()}</h2>\n <button @click=${this._nextPage} aria-label=\"Next ${this.months} months\" class=\"right\">\n ${unsafeSVG(chevron)}\n </button>\n </div>\n <p class=\"instructions\">Click a date to select it. Shift+click to select a range.</p>\n <div class=\"calendar-grid\" @click=${this._handleDayClick} @keydown=${this._handleKeyDown} tabindex=\"0\">\n ${monthList.map(\n (month) => html`\n <div class=\"month-panel\">\n <h3 class=\"month-label\">${month.toFormat('MMMM yyyy')}</h3>\n <tt-calendar\n display-only\n month=\"${month.toFormat('yyyy-MM')}\"\n .highlightedRanges=${this.value}\n min-date=\"${ifDefined(this.minDate)}\"\n max-date=\"${ifDefined(this.maxDate)}\"\n ></tt-calendar>\n </div>\n `\n )}\n </div>\n ${this.value.length > 0\n ? html`\n <div class=\"footer\">\n <span class=\"selection-summary\">${this.value.length} selection${this.value.length > 1 ? 's' : ''}</span>\n <button data-action=\"clear\" data-theme=\"secondary\" @click=${this._clearAll}>Clear all</button>\n </div>\n `\n : nothing}\n `;\n }\n}\n"]}
@@ -32,8 +32,16 @@ exports.styles = [
32
32
  color: var(--color-text-500);
33
33
  }
34
34
 
35
+ .instructions {
36
+ margin: 0;
37
+ padding: var(--space-scale-1) var(--space-scale-3);
38
+ font-size: var(--font-size-100);
39
+ color: var(--color-text-400);
40
+ }
41
+
35
42
  tt-calendar {
36
43
  --tt-calendar-range-bg: var(--color-primary-400);
44
+ --tt-calendar-range-color: var(--color-text-100);
37
45
  }
38
46
 
39
47
  .calendar-grid {
@@ -43,6 +51,14 @@ exports.styles = [
43
51
  padding: var(--space-scale-3);
44
52
  }
45
53
 
54
+ .month-label {
55
+ font-size: var(--font-size-200);
56
+ font-weight: var(--font-weight-semibold);
57
+ color: var(--color-text-500);
58
+ margin: 0 0 var(--space-scale-1) 0;
59
+ text-align: center;
60
+ }
61
+
46
62
  .footer {
47
63
  display: flex;
48
64
  justify-content: space-between;
@@ -1 +1 @@
1
- {"version":3,"file":"styles.js","sourceRoot":"","sources":["../../../src/styles.ts"],"names":[],"mappings":";;;AAAA,6BAA0B;AAC1B,mDAAiE;AAEpD,QAAA,MAAM,GAAG;IACpB,YAAU;IACV,IAAA,SAAG,EAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkDF;CACF,CAAC","sourcesContent":["import { css } from 'lit';\nimport { styles as baseStyles } from '@triptease/stylesheet/lit';\n\nexport const styles = [\n baseStyles,\n css`\n :host {\n display: block;\n }\n\n .navigation {\n display: flex;\n justify-content: space-between;\n align-items: center;\n padding: var(--space-scale-2) var(--space-scale-3);\n border-bottom: 1px solid var(--color-border-200);\n }\n\n .navigation button svg {\n display: block;\n }\n\n .navigation button.right svg {\n transform: rotate(180deg);\n }\n\n .navigation h2 {\n font-weight: var(--font-weight-semibold);\n font-size: var(--font-size-400);\n color: var(--color-text-500);\n }\n\n tt-calendar {\n --tt-calendar-range-bg: var(--color-primary-400);\n }\n\n .calendar-grid {\n display: grid;\n grid-template-columns: repeat(3, 1fr);\n gap: var(--space-scale-3);\n padding: var(--space-scale-3);\n }\n\n .footer {\n display: flex;\n justify-content: space-between;\n align-items: center;\n padding: var(--space-scale-2) var(--space-scale-3);\n border-top: 1px solid var(--color-border-200);\n }\n\n .selection-summary {\n font-size: var(--font-size-100);\n color: var(--color-text-400);\n }\n `,\n];\n"]}
1
+ {"version":3,"file":"styles.js","sourceRoot":"","sources":["../../../src/styles.ts"],"names":[],"mappings":";;;AAAA,6BAA0B;AAC1B,mDAAiE;AAEpD,QAAA,MAAM,GAAG;IACpB,YAAU;IACV,IAAA,SAAG,EAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkEF;CACF,CAAC","sourcesContent":["import { css } from 'lit';\nimport { styles as baseStyles } from '@triptease/stylesheet/lit';\n\nexport const styles = [\n baseStyles,\n css`\n :host {\n display: block;\n }\n\n .navigation {\n display: flex;\n justify-content: space-between;\n align-items: center;\n padding: var(--space-scale-2) var(--space-scale-3);\n border-bottom: 1px solid var(--color-border-200);\n }\n\n .navigation button svg {\n display: block;\n }\n\n .navigation button.right svg {\n transform: rotate(180deg);\n }\n\n .navigation h2 {\n font-weight: var(--font-weight-semibold);\n font-size: var(--font-size-400);\n color: var(--color-text-500);\n }\n\n .instructions {\n margin: 0;\n padding: var(--space-scale-1) var(--space-scale-3);\n font-size: var(--font-size-100);\n color: var(--color-text-400);\n }\n\n tt-calendar {\n --tt-calendar-range-bg: var(--color-primary-400);\n --tt-calendar-range-color: var(--color-text-100);\n }\n\n .calendar-grid {\n display: grid;\n grid-template-columns: repeat(3, 1fr);\n gap: var(--space-scale-3);\n padding: var(--space-scale-3);\n }\n\n .month-label {\n font-size: var(--font-size-200);\n font-weight: var(--font-weight-semibold);\n color: var(--color-text-500);\n margin: 0 0 var(--space-scale-1) 0;\n text-align: center;\n }\n\n .footer {\n display: flex;\n justify-content: space-between;\n align-items: center;\n padding: var(--space-scale-2) var(--space-scale-3);\n border-top: 1px solid var(--color-border-200);\n }\n\n .selection-summary {\n font-size: var(--font-size-100);\n color: var(--color-text-400);\n }\n `,\n];\n"]}
@@ -153,15 +153,19 @@ export class TtMultiDatePicker extends LitElement {
153
153
  ${unsafeSVG(chevron)}
154
154
  </button>
155
155
  </div>
156
+ <p class="instructions">Click a date to select it. Shift+click to select a range.</p>
156
157
  <div class="calendar-grid" @click=${this._handleDayClick} @keydown=${this._handleKeyDown} tabindex="0">
157
158
  ${monthList.map((month) => html `
158
- <tt-calendar
159
- display-only
160
- month="${month.toFormat('yyyy-MM')}"
161
- .highlightedRanges=${this.value}
162
- min-date="${ifDefined(this.minDate)}"
163
- max-date="${ifDefined(this.maxDate)}"
164
- ></tt-calendar>
159
+ <div class="month-panel">
160
+ <h3 class="month-label">${month.toFormat('MMMM yyyy')}</h3>
161
+ <tt-calendar
162
+ display-only
163
+ month="${month.toFormat('yyyy-MM')}"
164
+ .highlightedRanges=${this.value}
165
+ min-date="${ifDefined(this.minDate)}"
166
+ max-date="${ifDefined(this.maxDate)}"
167
+ ></tt-calendar>
168
+ </div>
165
169
  `)}
166
170
  </div>
167
171
  ${this.value.length > 0
@@ -1 +1 @@
1
- {"version":3,"file":"TtMultiDatePicker.js","sourceRoot":"","sources":["../../../src/TtMultiDatePicker.ts"],"names":[],"mappings":"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","sourcesContent":["import { html, LitElement, nothing } from 'lit';\nimport { property, state } from 'lit/decorators.js';\nimport { ifDefined } from 'lit/directives/if-defined.js';\nimport { DateTime } from 'luxon';\nimport { unsafeSVG } from 'lit/directives/unsafe-svg.js';\nimport { chevron } from '@triptease/icons';\nimport '@triptease/tt-calendar/tt-calendar.js';\nimport { styles } from './styles.js';\nimport { toggleDate, extendRange } from './selection-model.js';\nimport type { Selection } from './selection-model.js';\n\nexport class TtMultiDatePicker extends LitElement {\n static formAssociated = true;\n static override styles = styles;\n\n private _internals: ElementInternals;\n\n @property({ type: Number })\n months: 3 | 6 | 12 = 6;\n\n @property({ attribute: 'start-month' })\n startMonth?: string;\n\n @property({ attribute: false })\n value: Selection = [];\n\n @property({ reflect: true }) name: string = '';\n @property({ type: Boolean, reflect: true }) required: boolean = false;\n @property({ attribute: 'min-date' }) minDate?: string;\n @property({ attribute: 'max-date' }) maxDate?: string;\n\n @state()\n private _currentStartMonth: DateTime = DateTime.local().startOf('month');\n\n private _lastClickedDate?: string;\n\n @state()\n private _focusedDate: DateTime = DateTime.local();\n\n constructor() {\n super();\n this._internals = this.attachInternals();\n }\n\n override connectedCallback() {\n super.connectedCallback();\n if (this.startMonth) {\n this._currentStartMonth = DateTime.fromISO(this.startMonth + '-01');\n }\n this._syncForm();\n }\n\n private _syncForm() {\n const val = this.value.length > 0 ? JSON.stringify(this.value) : null;\n this._internals.setFormValue(val);\n if (this.required && this.value.length === 0) {\n this._internals.setValidity({ valueMissing: true }, 'Please select at least one date');\n } else {\n this._internals.setValidity({});\n }\n }\n\n formResetCallback() {\n this.value = [];\n this._lastClickedDate = undefined;\n this._syncForm();\n }\n\n private _getMonthList(): DateTime[] {\n return Array.from({ length: this.months }, (_, i) => this._currentStartMonth.plus({ months: i }));\n }\n\n private _previousPage() {\n this._currentStartMonth = this._currentStartMonth.minus({ months: this.months });\n }\n\n private _nextPage() {\n this._currentStartMonth = this._currentStartMonth.plus({ months: this.months });\n }\n\n private _getDateRangeLabel(): string {\n const first = this._currentStartMonth;\n const last = this._currentStartMonth.plus({ months: this.months - 1 });\n return `${first.toFormat('MMMM yyyy')} — ${last.toFormat('MMMM yyyy')}`;\n }\n\n private _getCalendarForDate(date: DateTime): Element | undefined {\n const monthStr = date.toFormat('yyyy-MM');\n const calendars = this.shadowRoot!.querySelectorAll('tt-calendar');\n for (const cal of calendars) {\n if (cal.getAttribute('month') === monthStr) return cal;\n }\n return undefined;\n }\n\n private _moveFocus(date: DateTime) {\n this._focusedDate = date;\n this.updateComplete.then(() => {\n const cal = this._getCalendarForDate(date) as (Element & { focusDay: (d: DateTime) => void }) | undefined;\n cal?.focusDay(date);\n });\n }\n\n private _applySelection(dateStr: string, shiftKey: boolean) {\n if (shiftKey && this._lastClickedDate) {\n this.value = extendRange(this.value, this._lastClickedDate, dateStr);\n } else {\n this.value = toggleDate(this.value, dateStr);\n }\n\n this._lastClickedDate = dateStr;\n this._syncForm();\n this.dispatchEvent(new Event('change', { bubbles: true, composed: true }));\n }\n\n private _handleDayClick(event: MouseEvent) {\n const target = event.composedPath()[0] as HTMLElement;\n const dayCell = target.closest?.('.day') ?? (target.classList?.contains('day') ? target : null);\n const dateStr = dayCell?.getAttribute('data-date');\n if (!dateStr) return;\n\n this._focusedDate = DateTime.fromISO(dateStr);\n this._applySelection(dateStr, event.shiftKey);\n }\n\n private _handleKeyDown(event: KeyboardEvent) {\n switch (event.key) {\n case 'ArrowRight':\n event.preventDefault();\n this._moveFocus(this._focusedDate.plus({ days: 1 }));\n break;\n case 'ArrowLeft':\n event.preventDefault();\n this._moveFocus(this._focusedDate.minus({ days: 1 }));\n break;\n case 'ArrowDown':\n event.preventDefault();\n this._moveFocus(this._focusedDate.plus({ weeks: 1 }));\n break;\n case 'ArrowUp':\n event.preventDefault();\n this._moveFocus(this._focusedDate.minus({ weeks: 1 }));\n break;\n case 'Home':\n event.preventDefault();\n this._moveFocus(this._focusedDate.startOf('week'));\n break;\n case 'End':\n event.preventDefault();\n this._moveFocus(this._focusedDate.endOf('week'));\n break;\n case 'PageDown':\n event.preventDefault();\n this._nextPage();\n break;\n case 'PageUp':\n event.preventDefault();\n this._previousPage();\n break;\n case 'Enter':\n case ' ':\n event.preventDefault();\n this._applySelection(this._focusedDate.toISODate()!, event.shiftKey);\n break;\n }\n }\n\n private _clearAll() {\n this.value = [];\n this._lastClickedDate = undefined;\n this._syncForm();\n this.dispatchEvent(new Event('change', { bubbles: true, composed: true }));\n }\n\n override render() {\n const monthList = this._getMonthList();\n\n return html`\n <div class=\"navigation\">\n <button @click=${this._previousPage} aria-label=\"Previous ${this.months} months\">${unsafeSVG(chevron)}</button>\n <h2>${this._getDateRangeLabel()}</h2>\n <button @click=${this._nextPage} aria-label=\"Next ${this.months} months\" class=\"right\">\n ${unsafeSVG(chevron)}\n </button>\n </div>\n <div class=\"calendar-grid\" @click=${this._handleDayClick} @keydown=${this._handleKeyDown} tabindex=\"0\">\n ${monthList.map(\n (month) => html`\n <tt-calendar\n display-only\n month=\"${month.toFormat('yyyy-MM')}\"\n .highlightedRanges=${this.value}\n min-date=\"${ifDefined(this.minDate)}\"\n max-date=\"${ifDefined(this.maxDate)}\"\n ></tt-calendar>\n `\n )}\n </div>\n ${this.value.length > 0\n ? html`\n <div class=\"footer\">\n <span class=\"selection-summary\">${this.value.length} selection${this.value.length > 1 ? 's' : ''}</span>\n <button data-action=\"clear\" data-theme=\"secondary\" @click=${this._clearAll}>Clear all</button>\n </div>\n `\n : nothing}\n `;\n }\n}\n"]}
1
+ {"version":3,"file":"TtMultiDatePicker.js","sourceRoot":"","sources":["../../../src/TtMultiDatePicker.ts"],"names":[],"mappings":"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","sourcesContent":["import { html, LitElement, nothing } from 'lit';\nimport { property, state } from 'lit/decorators.js';\nimport { ifDefined } from 'lit/directives/if-defined.js';\nimport { DateTime } from 'luxon';\nimport { unsafeSVG } from 'lit/directives/unsafe-svg.js';\nimport { chevron } from '@triptease/icons';\nimport '@triptease/tt-calendar/tt-calendar.js';\nimport { styles } from './styles.js';\nimport { toggleDate, extendRange } from './selection-model.js';\nimport type { Selection } from './selection-model.js';\n\nexport class TtMultiDatePicker extends LitElement {\n static formAssociated = true;\n static override styles = styles;\n\n private _internals: ElementInternals;\n\n @property({ type: Number })\n months: 3 | 6 | 12 = 6;\n\n @property({ attribute: 'start-month' })\n startMonth?: string;\n\n @property({ attribute: false })\n value: Selection = [];\n\n @property({ reflect: true }) name: string = '';\n @property({ type: Boolean, reflect: true }) required: boolean = false;\n @property({ attribute: 'min-date' }) minDate?: string;\n @property({ attribute: 'max-date' }) maxDate?: string;\n\n @state()\n private _currentStartMonth: DateTime = DateTime.local().startOf('month');\n\n private _lastClickedDate?: string;\n\n @state()\n private _focusedDate: DateTime = DateTime.local();\n\n constructor() {\n super();\n this._internals = this.attachInternals();\n }\n\n override connectedCallback() {\n super.connectedCallback();\n if (this.startMonth) {\n this._currentStartMonth = DateTime.fromISO(this.startMonth + '-01');\n }\n this._syncForm();\n }\n\n private _syncForm() {\n const val = this.value.length > 0 ? JSON.stringify(this.value) : null;\n this._internals.setFormValue(val);\n if (this.required && this.value.length === 0) {\n this._internals.setValidity({ valueMissing: true }, 'Please select at least one date');\n } else {\n this._internals.setValidity({});\n }\n }\n\n formResetCallback() {\n this.value = [];\n this._lastClickedDate = undefined;\n this._syncForm();\n }\n\n private _getMonthList(): DateTime[] {\n return Array.from({ length: this.months }, (_, i) => this._currentStartMonth.plus({ months: i }));\n }\n\n private _previousPage() {\n this._currentStartMonth = this._currentStartMonth.minus({ months: this.months });\n }\n\n private _nextPage() {\n this._currentStartMonth = this._currentStartMonth.plus({ months: this.months });\n }\n\n private _getDateRangeLabel(): string {\n const first = this._currentStartMonth;\n const last = this._currentStartMonth.plus({ months: this.months - 1 });\n return `${first.toFormat('MMMM yyyy')} — ${last.toFormat('MMMM yyyy')}`;\n }\n\n private _getCalendarForDate(date: DateTime): Element | undefined {\n const monthStr = date.toFormat('yyyy-MM');\n const calendars = this.shadowRoot!.querySelectorAll('tt-calendar');\n for (const cal of calendars) {\n if (cal.getAttribute('month') === monthStr) return cal;\n }\n return undefined;\n }\n\n private _moveFocus(date: DateTime) {\n this._focusedDate = date;\n this.updateComplete.then(() => {\n const cal = this._getCalendarForDate(date) as (Element & { focusDay: (d: DateTime) => void }) | undefined;\n cal?.focusDay(date);\n });\n }\n\n private _applySelection(dateStr: string, shiftKey: boolean) {\n if (shiftKey && this._lastClickedDate) {\n this.value = extendRange(this.value, this._lastClickedDate, dateStr);\n } else {\n this.value = toggleDate(this.value, dateStr);\n }\n\n this._lastClickedDate = dateStr;\n this._syncForm();\n this.dispatchEvent(new Event('change', { bubbles: true, composed: true }));\n }\n\n private _handleDayClick(event: MouseEvent) {\n const target = event.composedPath()[0] as HTMLElement;\n const dayCell = target.closest?.('.day') ?? (target.classList?.contains('day') ? target : null);\n const dateStr = dayCell?.getAttribute('data-date');\n if (!dateStr) return;\n\n this._focusedDate = DateTime.fromISO(dateStr);\n this._applySelection(dateStr, event.shiftKey);\n }\n\n private _handleKeyDown(event: KeyboardEvent) {\n switch (event.key) {\n case 'ArrowRight':\n event.preventDefault();\n this._moveFocus(this._focusedDate.plus({ days: 1 }));\n break;\n case 'ArrowLeft':\n event.preventDefault();\n this._moveFocus(this._focusedDate.minus({ days: 1 }));\n break;\n case 'ArrowDown':\n event.preventDefault();\n this._moveFocus(this._focusedDate.plus({ weeks: 1 }));\n break;\n case 'ArrowUp':\n event.preventDefault();\n this._moveFocus(this._focusedDate.minus({ weeks: 1 }));\n break;\n case 'Home':\n event.preventDefault();\n this._moveFocus(this._focusedDate.startOf('week'));\n break;\n case 'End':\n event.preventDefault();\n this._moveFocus(this._focusedDate.endOf('week'));\n break;\n case 'PageDown':\n event.preventDefault();\n this._nextPage();\n break;\n case 'PageUp':\n event.preventDefault();\n this._previousPage();\n break;\n case 'Enter':\n case ' ':\n event.preventDefault();\n this._applySelection(this._focusedDate.toISODate()!, event.shiftKey);\n break;\n }\n }\n\n private _clearAll() {\n this.value = [];\n this._lastClickedDate = undefined;\n this._syncForm();\n this.dispatchEvent(new Event('change', { bubbles: true, composed: true }));\n }\n\n override render() {\n const monthList = this._getMonthList();\n\n return html`\n <div class=\"navigation\">\n <button @click=${this._previousPage} aria-label=\"Previous ${this.months} months\">${unsafeSVG(chevron)}</button>\n <h2>${this._getDateRangeLabel()}</h2>\n <button @click=${this._nextPage} aria-label=\"Next ${this.months} months\" class=\"right\">\n ${unsafeSVG(chevron)}\n </button>\n </div>\n <p class=\"instructions\">Click a date to select it. Shift+click to select a range.</p>\n <div class=\"calendar-grid\" @click=${this._handleDayClick} @keydown=${this._handleKeyDown} tabindex=\"0\">\n ${monthList.map(\n (month) => html`\n <div class=\"month-panel\">\n <h3 class=\"month-label\">${month.toFormat('MMMM yyyy')}</h3>\n <tt-calendar\n display-only\n month=\"${month.toFormat('yyyy-MM')}\"\n .highlightedRanges=${this.value}\n min-date=\"${ifDefined(this.minDate)}\"\n max-date=\"${ifDefined(this.maxDate)}\"\n ></tt-calendar>\n </div>\n `\n )}\n </div>\n ${this.value.length > 0\n ? html`\n <div class=\"footer\">\n <span class=\"selection-summary\">${this.value.length} selection${this.value.length > 1 ? 's' : ''}</span>\n <button data-action=\"clear\" data-theme=\"secondary\" @click=${this._clearAll}>Clear all</button>\n </div>\n `\n : nothing}\n `;\n }\n}\n"]}
@@ -29,8 +29,16 @@ export const styles = [
29
29
  color: var(--color-text-500);
30
30
  }
31
31
 
32
+ .instructions {
33
+ margin: 0;
34
+ padding: var(--space-scale-1) var(--space-scale-3);
35
+ font-size: var(--font-size-100);
36
+ color: var(--color-text-400);
37
+ }
38
+
32
39
  tt-calendar {
33
40
  --tt-calendar-range-bg: var(--color-primary-400);
41
+ --tt-calendar-range-color: var(--color-text-100);
34
42
  }
35
43
 
36
44
  .calendar-grid {
@@ -40,6 +48,14 @@ export const styles = [
40
48
  padding: var(--space-scale-3);
41
49
  }
42
50
 
51
+ .month-label {
52
+ font-size: var(--font-size-200);
53
+ font-weight: var(--font-weight-semibold);
54
+ color: var(--color-text-500);
55
+ margin: 0 0 var(--space-scale-1) 0;
56
+ text-align: center;
57
+ }
58
+
43
59
  .footer {
44
60
  display: flex;
45
61
  justify-content: space-between;
@@ -1 +1 @@
1
- {"version":3,"file":"styles.js","sourceRoot":"","sources":["../../../src/styles.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,MAAM,KAAK,CAAC;AAC1B,OAAO,EAAE,MAAM,IAAI,UAAU,EAAE,MAAM,2BAA2B,CAAC;AAEjE,MAAM,CAAC,MAAM,MAAM,GAAG;IACpB,UAAU;IACV,GAAG,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkDF;CACF,CAAC","sourcesContent":["import { css } from 'lit';\nimport { styles as baseStyles } from '@triptease/stylesheet/lit';\n\nexport const styles = [\n baseStyles,\n css`\n :host {\n display: block;\n }\n\n .navigation {\n display: flex;\n justify-content: space-between;\n align-items: center;\n padding: var(--space-scale-2) var(--space-scale-3);\n border-bottom: 1px solid var(--color-border-200);\n }\n\n .navigation button svg {\n display: block;\n }\n\n .navigation button.right svg {\n transform: rotate(180deg);\n }\n\n .navigation h2 {\n font-weight: var(--font-weight-semibold);\n font-size: var(--font-size-400);\n color: var(--color-text-500);\n }\n\n tt-calendar {\n --tt-calendar-range-bg: var(--color-primary-400);\n }\n\n .calendar-grid {\n display: grid;\n grid-template-columns: repeat(3, 1fr);\n gap: var(--space-scale-3);\n padding: var(--space-scale-3);\n }\n\n .footer {\n display: flex;\n justify-content: space-between;\n align-items: center;\n padding: var(--space-scale-2) var(--space-scale-3);\n border-top: 1px solid var(--color-border-200);\n }\n\n .selection-summary {\n font-size: var(--font-size-100);\n color: var(--color-text-400);\n }\n `,\n];\n"]}
1
+ {"version":3,"file":"styles.js","sourceRoot":"","sources":["../../../src/styles.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,MAAM,KAAK,CAAC;AAC1B,OAAO,EAAE,MAAM,IAAI,UAAU,EAAE,MAAM,2BAA2B,CAAC;AAEjE,MAAM,CAAC,MAAM,MAAM,GAAG;IACpB,UAAU;IACV,GAAG,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkEF;CACF,CAAC","sourcesContent":["import { css } from 'lit';\nimport { styles as baseStyles } from '@triptease/stylesheet/lit';\n\nexport const styles = [\n baseStyles,\n css`\n :host {\n display: block;\n }\n\n .navigation {\n display: flex;\n justify-content: space-between;\n align-items: center;\n padding: var(--space-scale-2) var(--space-scale-3);\n border-bottom: 1px solid var(--color-border-200);\n }\n\n .navigation button svg {\n display: block;\n }\n\n .navigation button.right svg {\n transform: rotate(180deg);\n }\n\n .navigation h2 {\n font-weight: var(--font-weight-semibold);\n font-size: var(--font-size-400);\n color: var(--color-text-500);\n }\n\n .instructions {\n margin: 0;\n padding: var(--space-scale-1) var(--space-scale-3);\n font-size: var(--font-size-100);\n color: var(--color-text-400);\n }\n\n tt-calendar {\n --tt-calendar-range-bg: var(--color-primary-400);\n --tt-calendar-range-color: var(--color-text-100);\n }\n\n .calendar-grid {\n display: grid;\n grid-template-columns: repeat(3, 1fr);\n gap: var(--space-scale-3);\n padding: var(--space-scale-3);\n }\n\n .month-label {\n font-size: var(--font-size-200);\n font-weight: var(--font-weight-semibold);\n color: var(--color-text-500);\n margin: 0 0 var(--space-scale-1) 0;\n text-align: center;\n }\n\n .footer {\n display: flex;\n justify-content: space-between;\n align-items: center;\n padding: var(--space-scale-2) var(--space-scale-3);\n border-top: 1px solid var(--color-border-200);\n }\n\n .selection-summary {\n font-size: var(--font-size-100);\n color: var(--color-text-400);\n }\n `,\n];\n"]}
@@ -29,13 +29,15 @@ describe('tt-multi-date-picker', () => {
29
29
  const el = await getPicker(screen.container);
30
30
  expect(getCalendars(el)).toHaveLength(12);
31
31
  });
32
- it('renders prev/next navigation buttons', async () => {
32
+ it('renders prev/next navigation buttons and selection instructions', async () => {
33
33
  const screen = render(html `<tt-multi-date-picker></tt-multi-date-picker>`);
34
34
  const el = await getPicker(screen.container);
35
35
  const prevBtn = el.shadowRoot.querySelector('[aria-label*="Previous"]');
36
36
  const nextBtn = el.shadowRoot.querySelector('[aria-label*="Next"]');
37
+ const instructions = el.shadowRoot.querySelector('.instructions');
37
38
  expect(prevBtn).not.toBeNull();
38
39
  expect(nextBtn).not.toBeNull();
40
+ expect(instructions.textContent).toContain('Shift+click');
39
41
  });
40
42
  });
41
43
  describe('selection', () => {
@@ -1 +1 @@
1
- {"version":3,"file":"tt-multi-date-picker.test.js","sourceRoot":"","sources":["../../../test/tt-multi-date-picker.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;AAC9C,OAAO,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAC;AAC5C,OAAO,EAAE,IAAI,EAAE,MAAM,KAAK,CAAC;AAC3B,OAAO,gCAAgC,CAAC;AAGxC,KAAK,UAAU,SAAS,CAAC,SAAkB;IACzC,MAAM,EAAE,GAAG,SAAS,CAAC,aAAa,CAAoB,sBAAsB,CAAC,CAAC;IAC9E,IAAI,CAAC,EAAE;QAAE,MAAM,IAAI,KAAK,CAAC,gCAAgC,CAAC,CAAC;IAC3D,MAAM,EAAE,CAAC,cAAc,CAAC;IACxB,OAAO,EAAE,CAAC;AACZ,CAAC;AAED,SAAS,YAAY,CAAC,EAAqB;IACzC,OAAO,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,UAAW,CAAC,gBAAgB,CAAC,aAAa,CAAC,CAAC,CAAC;AACpE,CAAC;AAED,QAAQ,CAAC,sBAAsB,EAAE,GAAG,EAAE;IACpC,QAAQ,CAAC,WAAW,EAAE,GAAG,EAAE;QACzB,EAAE,CAAC,gCAAgC,EAAE,KAAK,IAAI,EAAE;YAC9C,MAAM,MAAM,GAAG,MAAM,CAAC,IAAI,CAAA,+CAA+C,CAAC,CAAC;YAC3E,MAAM,EAAE,GAAG,MAAM,SAAS,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;YAC7C,MAAM,CAAC,YAAY,CAAC,EAAE,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;QAC3C,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,qCAAqC,EAAE,KAAK,IAAI,EAAE;YACnD,MAAM,MAAM,GAAG,MAAM,CAAC,IAAI,CAAA,0DAA0D,CAAC,CAAC;YACtF,MAAM,EAAE,GAAG,MAAM,SAAS,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;YAC7C,MAAM,CAAC,YAAY,CAAC,EAAE,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;QAC3C,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,uCAAuC,EAAE,KAAK,IAAI,EAAE;YACrD,MAAM,MAAM,GAAG,MAAM,CAAC,IAAI,CAAA,2DAA2D,CAAC,CAAC;YACvF,MAAM,EAAE,GAAG,MAAM,SAAS,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;YAC7C,MAAM,CAAC,YAAY,CAAC,EAAE,CAAC,CAAC,CAAC,YAAY,CAAC,EAAE,CAAC,CAAC;QAC5C,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,sCAAsC,EAAE,KAAK,IAAI,EAAE;YACpD,MAAM,MAAM,GAAG,MAAM,CAAC,IAAI,CAAA,+CAA+C,CAAC,CAAC;YAC3E,MAAM,EAAE,GAAG,MAAM,SAAS,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;YAC7C,MAAM,OAAO,GAAG,EAAE,CAAC,UAAW,CAAC,aAAa,CAAC,0BAA0B,CAAC,CAAC;YACzE,MAAM,OAAO,GAAG,EAAE,CAAC,UAAW,CAAC,aAAa,CAAC,sBAAsB,CAAC,CAAC;YACrE,MAAM,CAAC,OAAO,CAAC,CAAC,GAAG,CAAC,QAAQ,EAAE,CAAC;YAC/B,MAAM,CAAC,OAAO,CAAC,CAAC,GAAG,CAAC,QAAQ,EAAE,CAAC;QACjC,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,QAAQ,CAAC,WAAW,EAAE,GAAG,EAAE;QACzB,EAAE,CAAC,gDAAgD,EAAE,KAAK,IAAI,EAAE;YAC9D,MAAM,MAAM,GAAG,MAAM,CAAC,IAAI,CAAA,gFAAgF,CAAC,CAAC;YAC5G,MAAM,EAAE,GAAG,MAAM,SAAS,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;YAE7C,IAAI,YAAqB,CAAC;YAC1B,EAAE,CAAC,gBAAgB,CAAC,QAAQ,EAAE,CAAC,CAAQ,EAAE,EAAE;gBACzC,YAAY,GAAI,CAAC,CAAC,MAA4B,CAAC,KAAK,CAAC;YACvD,CAAC,CAAC,CAAC;YAEH,MAAM,SAAS,GAAG,YAAY,CAAC,EAAE,CAAC,CAAC;YACnC,MAAM,GAAG,GAAG,SAAS,CAAC,CAAC,CAAC,CAAC,UAAW,CAAC,aAAa,CAAC,8BAA8B,CAAgB,CAAC;YAClG,GAAG,CAAC,KAAK,EAAE,CAAC;YACZ,MAAM,EAAE,CAAC,cAAc,CAAC;YAExB,MAAM,CAAC,EAAE,CAAC,KAAK,CAAC,CAAC,OAAO,CAAC,CAAC,EAAE,SAAS,EAAE,YAAY,EAAE,OAAO,EAAE,YAAY,EAAE,CAAC,CAAC,CAAC;YAC/E,MAAM,CAAC,YAAY,CAAC,CAAC,OAAO,CAAC,CAAC,EAAE,SAAS,EAAE,YAAY,EAAE,OAAO,EAAE,YAAY,EAAE,CAAC,CAAC,CAAC;QACrF,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,yCAAyC,EAAE,KAAK,IAAI,EAAE;YACvD,MAAM,MAAM,GAAG,MAAM,CAAC,IAAI,CAAA,gFAAgF,CAAC,CAAC;YAC5G,MAAM,EAAE,GAAG,MAAM,SAAS,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;YAE7C,MAAM,GAAG,GAAG,YAAY,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,UAAW,CAAC,aAAa,CAAC,8BAA8B,CAAgB,CAAC;YACzG,GAAG,CAAC,KAAK,EAAE,CAAC;YACZ,MAAM,EAAE,CAAC,cAAc,CAAC;YACxB,GAAG,CAAC,KAAK,EAAE,CAAC;YACZ,MAAM,EAAE,CAAC,cAAc,CAAC;YAExB,MAAM,CAAC,EAAE,CAAC,KAAK,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;QAC/B,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,kCAAkC,EAAE,KAAK,IAAI,EAAE;YAChD,MAAM,MAAM,GAAG,MAAM,CAAC,IAAI,CAAA,gFAAgF,CAAC,CAAC;YAC5G,MAAM,EAAE,GAAG,MAAM,SAAS,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;YAE7C,MAAM,SAAS,GAAG,YAAY,CAAC,EAAE,CAAC,CAAC;YACnC,MAAM,IAAI,GAAG,SAAS,CAAC,CAAC,CAAC,CAAC,UAAW,CAAC,aAAa,CAAC,8BAA8B,CAAgB,CAAC;YACnG,MAAM,KAAK,GAAG,SAAS,CAAC,CAAC,CAAC,CAAC,UAAW,CAAC,aAAa,CAAC,8BAA8B,CAAgB,CAAC;YAEpG,IAAI,CAAC,KAAK,EAAE,CAAC;YACb,MAAM,EAAE,CAAC,cAAc,CAAC;YACxB,KAAK,CAAC,aAAa,CAAC,IAAI,UAAU,CAAC,OAAO,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;YAChG,MAAM,EAAE,CAAC,cAAc,CAAC;YAExB,MAAM,CAAC,EAAE,CAAC,KAAK,CAAC,CAAC,OAAO,CAAC,CAAC,EAAE,SAAS,EAAE,YAAY,EAAE,OAAO,EAAE,YAAY,EAAE,CAAC,CAAC,CAAC;QACjF,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,QAAQ,CAAC,kBAAkB,EAAE,GAAG,EAAE;QAChC,EAAE,CAAC,4BAA4B,EAAE,KAAK,IAAI,EAAE;YAC1C,MAAM,MAAM,GAAG,MAAM,CAAC,IAAI,CAAA;;;;OAIzB,CAAC,CAAC;YACH,MAAM,EAAE,GAAG,MAAM,SAAS,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;YAE7C,MAAM,GAAG,GAAG,YAAY,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,UAAW,CAAC,aAAa,CAAC,8BAA8B,CAAgB,CAAC;YACzG,GAAG,CAAC,KAAK,EAAE,CAAC;YACZ,MAAM,EAAE,CAAC,cAAc,CAAC;YAExB,MAAM,IAAI,GAAG,MAAM,CAAC,SAAS,CAAC,aAAa,CAAC,MAAM,CAAE,CAAC;YACrD,MAAM,QAAQ,GAAG,IAAI,QAAQ,CAAC,IAAI,CAAC,CAAC;YACpC,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,GAAG,CAAC,OAAO,CAAW,CAAC,CAAC;YAC1D,MAAM,CAAC,KAAK,CAAC,CAAC,OAAO,CAAC,CAAC,EAAE,SAAS,EAAE,YAAY,EAAE,OAAO,EAAE,YAAY,EAAE,CAAC,CAAC,CAAC;QAC9E,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,0CAA0C,EAAE,KAAK,IAAI,EAAE;YACxD,MAAM,MAAM,GAAG,MAAM,CAAC,IAAI,CAAA,wDAAwD,CAAC,CAAC;YACpF,MAAM,EAAE,GAAG,MAAM,SAAS,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;YAC7C,MAAM,CAAC,EAAE,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC5C,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,QAAQ,CAAC,QAAQ,EAAE,GAAG,EAAE;QACtB,EAAE,CAAC,kDAAkD,EAAE,KAAK,IAAI,EAAE;YAChE,MAAM,MAAM,GAAG,MAAM,CAAC,IAAI,CAAA,gFAAgF,CAAC,CAAC;YAC5G,MAAM,EAAE,GAAG,MAAM,SAAS,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;YAE7C,MAAM,GAAG,GAAG,YAAY,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,UAAW,CAAC,aAAa,CAAC,8BAA8B,CAAgB,CAAC;YACzG,GAAG,CAAC,KAAK,EAAE,CAAC;YACZ,MAAM,EAAE,CAAC,cAAc,CAAC;YAExB,MAAM,QAAQ,GAAG,EAAE,CAAC,UAAW,CAAC,aAAa,CAAC,uBAAuB,CAAC,CAAC;YACvE,MAAM,CAAC,QAAQ,CAAC,CAAC,GAAG,CAAC,QAAQ,EAAE,CAAC;QAClC,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,oDAAoD,EAAE,KAAK,IAAI,EAAE;YAClE,MAAM,MAAM,GAAG,MAAM,CAAC,IAAI,CAAA,gFAAgF,CAAC,CAAC;YAC5G,MAAM,EAAE,GAAG,MAAM,SAAS,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;YAE7C,MAAM,GAAG,GAAG,YAAY,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,UAAW,CAAC,aAAa,CAAC,8BAA8B,CAAgB,CAAC;YACzG,GAAG,CAAC,KAAK,EAAE,CAAC;YACZ,MAAM,EAAE,CAAC,cAAc,CAAC;YAExB,MAAM,QAAQ,GAAG,EAAE,CAAC,UAAW,CAAC,aAAa,CAAC,uBAAuB,CAAgB,CAAC;YACtF,QAAQ,CAAC,KAAK,EAAE,CAAC;YACjB,MAAM,EAAE,CAAC,cAAc,CAAC;YAExB,MAAM,CAAC,EAAE,CAAC,KAAK,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;QAC/B,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,QAAQ,CAAC,qBAAqB,EAAE,GAAG,EAAE;QACnC,EAAE,CAAC,sCAAsC,EAAE,KAAK,IAAI,EAAE;YACpD,MAAM,MAAM,GAAG,MAAM,CAAC,IAAI,CAAA,gFAAgF,CAAC,CAAC;YAC5G,MAAM,EAAE,GAAG,MAAM,SAAS,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;YAE7C,MAAM,QAAQ,GAAG,EAAE,CAAC,UAAW,CAAC,aAAa,CAAC,gBAAgB,CAAgB,CAAC;YAC/E,QAAQ,CAAC,aAAa,CAAC,IAAI,aAAa,CAAC,SAAS,EAAE,EAAE,GAAG,EAAE,UAAU,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;YACzF,MAAM,EAAE,CAAC,cAAc,CAAC;YAExB,MAAM,SAAS,GAAG,YAAY,CAAC,EAAE,CAAC,CAAC;YACnC,MAAM,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,YAAY,CAAC,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;QAChE,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,wCAAwC,EAAE,KAAK,IAAI,EAAE;YACtD,MAAM,MAAM,GAAG,MAAM,CAAC,IAAI,CAAA,gFAAgF,CAAC,CAAC;YAC5G,MAAM,EAAE,GAAG,MAAM,SAAS,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;YAE7C,MAAM,QAAQ,GAAG,EAAE,CAAC,UAAW,CAAC,aAAa,CAAC,gBAAgB,CAAgB,CAAC;YAC/E,QAAQ,CAAC,aAAa,CAAC,IAAI,aAAa,CAAC,SAAS,EAAE,EAAE,GAAG,EAAE,QAAQ,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;YACvF,MAAM,EAAE,CAAC,cAAc,CAAC;YAExB,MAAM,SAAS,GAAG,YAAY,CAAC,EAAE,CAAC,CAAC;YACnC,MAAM,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,YAAY,CAAC,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;QAChE,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,+BAA+B,EAAE,KAAK,IAAI,EAAE;YAC7C,MAAM,MAAM,GAAG,MAAM,CAAC,IAAI,CAAA,gFAAgF,CAAC,CAAC;YAC5G,MAAM,EAAE,GAAG,MAAM,SAAS,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;YAE7C,MAAM,GAAG,GAAG,YAAY,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,UAAW,CAAC,aAAa,CAAC,8BAA8B,CAAgB,CAAC;YACzG,GAAG,CAAC,KAAK,EAAE,CAAC;YACZ,MAAM,EAAE,CAAC,cAAc,CAAC;YAExB,EAAE,CAAC,KAAK,GAAG,EAAE,CAAC;YACd,MAAM,EAAE,CAAC,cAAc,CAAC;YAExB,MAAM,QAAQ,GAAG,EAAE,CAAC,UAAW,CAAC,aAAa,CAAC,gBAAgB,CAAgB,CAAC;YAC/E,QAAQ,CAAC,aAAa,CAAC,IAAI,aAAa,CAAC,SAAS,EAAE,EAAE,GAAG,EAAE,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;YACtF,MAAM,EAAE,CAAC,cAAc,CAAC;YAExB,MAAM,CAAC,EAAE,CAAC,KAAK,CAAC,CAAC,OAAO,CAAC,CAAC,EAAE,SAAS,EAAE,YAAY,EAAE,OAAO,EAAE,YAAY,EAAE,CAAC,CAAC,CAAC;QACjF,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,QAAQ,CAAC,eAAe,EAAE,GAAG,EAAE;QAC7B,EAAE,CAAC,oDAAoD,EAAE,KAAK,IAAI,EAAE;YAClE,MAAM,MAAM,GAAG,MAAM,CAAC,IAAI,CAAA;;;;;;;OAOzB,CAAC,CAAC;YACH,MAAM,EAAE,GAAG,MAAM,SAAS,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;YAC7C,MAAM,SAAS,GAAG,YAAY,CAAC,EAAE,CAAC,CAAC;YAEnC,MAAM,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,YAAY,CAAC,UAAU,CAAC,CAAC,CAAC,OAAO,CAAC,YAAY,CAAC,CAAC;YACpE,MAAM,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,YAAY,CAAC,UAAU,CAAC,CAAC,CAAC,OAAO,CAAC,YAAY,CAAC,CAAC;QACtE,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC","sourcesContent":["import { describe, expect, it } from 'vitest';\nimport { render } from 'vitest-browser-lit';\nimport { html } from 'lit';\nimport '../src/tt-multi-date-picker.js';\nimport type { TtMultiDatePicker } from '../src/TtMultiDatePicker.js';\n\nasync function getPicker(container: Element): Promise<TtMultiDatePicker> {\n const el = container.querySelector<TtMultiDatePicker>('tt-multi-date-picker');\n if (!el) throw new Error('tt-multi-date-picker not found');\n await el.updateComplete;\n return el;\n}\n\nfunction getCalendars(el: TtMultiDatePicker): Element[] {\n return Array.from(el.shadowRoot!.querySelectorAll('tt-calendar'));\n}\n\ndescribe('tt-multi-date-picker', () => {\n describe('rendering', () => {\n it('renders 6 calendars by default', async () => {\n const screen = render(html`<tt-multi-date-picker></tt-multi-date-picker>`);\n const el = await getPicker(screen.container);\n expect(getCalendars(el)).toHaveLength(6);\n });\n\n it('renders 3 calendars when months=\"3\"', async () => {\n const screen = render(html`<tt-multi-date-picker months=\"3\"></tt-multi-date-picker>`);\n const el = await getPicker(screen.container);\n expect(getCalendars(el)).toHaveLength(3);\n });\n\n it('renders 12 calendars when months=\"12\"', async () => {\n const screen = render(html`<tt-multi-date-picker months=\"12\"></tt-multi-date-picker>`);\n const el = await getPicker(screen.container);\n expect(getCalendars(el)).toHaveLength(12);\n });\n\n it('renders prev/next navigation buttons', async () => {\n const screen = render(html`<tt-multi-date-picker></tt-multi-date-picker>`);\n const el = await getPicker(screen.container);\n const prevBtn = el.shadowRoot!.querySelector('[aria-label*=\"Previous\"]');\n const nextBtn = el.shadowRoot!.querySelector('[aria-label*=\"Next\"]');\n expect(prevBtn).not.toBeNull();\n expect(nextBtn).not.toBeNull();\n });\n });\n\n describe('selection', () => {\n it('selects a date on click and emits change event', async () => {\n const screen = render(html`<tt-multi-date-picker months=\"3\" start-month=\"2026-03\"></tt-multi-date-picker>`);\n const el = await getPicker(screen.container);\n\n let emittedValue: unknown;\n el.addEventListener('change', (e: Event) => {\n emittedValue = (e.target as TtMultiDatePicker).value;\n });\n\n const calendars = getCalendars(el);\n const day = calendars[0].shadowRoot!.querySelector('.day[data-date=\"2026-03-05\"]') as HTMLElement;\n day.click();\n await el.updateComplete;\n\n expect(el.value).toEqual([{ startDate: '2026-03-05', endDate: '2026-03-05' }]);\n expect(emittedValue).toEqual([{ startDate: '2026-03-05', endDate: '2026-03-05' }]);\n });\n\n it('deselects a single date on second click', async () => {\n const screen = render(html`<tt-multi-date-picker months=\"3\" start-month=\"2026-03\"></tt-multi-date-picker>`);\n const el = await getPicker(screen.container);\n\n const day = getCalendars(el)[0].shadowRoot!.querySelector('.day[data-date=\"2026-03-05\"]') as HTMLElement;\n day.click();\n await el.updateComplete;\n day.click();\n await el.updateComplete;\n\n expect(el.value).toEqual([]);\n });\n\n it('creates a range with shift+click', async () => {\n const screen = render(html`<tt-multi-date-picker months=\"3\" start-month=\"2026-03\"></tt-multi-date-picker>`);\n const el = await getPicker(screen.container);\n\n const calendars = getCalendars(el);\n const day5 = calendars[0].shadowRoot!.querySelector('.day[data-date=\"2026-03-05\"]') as HTMLElement;\n const day10 = calendars[0].shadowRoot!.querySelector('.day[data-date=\"2026-03-10\"]') as HTMLElement;\n\n day5.click();\n await el.updateComplete;\n day10.dispatchEvent(new MouseEvent('click', { shiftKey: true, bubbles: true, composed: true }));\n await el.updateComplete;\n\n expect(el.value).toEqual([{ startDate: '2026-03-05', endDate: '2026-03-10' }]);\n });\n });\n\n describe('form integration', () => {\n it('submits JSON value in form', async () => {\n const screen = render(html`\n <form>\n <tt-multi-date-picker name=\"dates\" start-month=\"2026-03\"></tt-multi-date-picker>\n </form>\n `);\n const el = await getPicker(screen.container);\n\n const day = getCalendars(el)[0].shadowRoot!.querySelector('.day[data-date=\"2026-03-05\"]') as HTMLElement;\n day.click();\n await el.updateComplete;\n\n const form = screen.container.querySelector('form')!;\n const formData = new FormData(form);\n const value = JSON.parse(formData.get('dates') as string);\n expect(value).toEqual([{ startDate: '2026-03-05', endDate: '2026-03-05' }]);\n });\n\n it('reports validity when required and empty', async () => {\n const screen = render(html`<tt-multi-date-picker required></tt-multi-date-picker>`);\n const el = await getPicker(screen.container);\n expect(el.matches(':invalid')).toBe(true);\n });\n });\n\n describe('footer', () => {\n it('shows clear all button when there are selections', async () => {\n const screen = render(html`<tt-multi-date-picker months=\"3\" start-month=\"2026-03\"></tt-multi-date-picker>`);\n const el = await getPicker(screen.container);\n\n const day = getCalendars(el)[0].shadowRoot!.querySelector('.day[data-date=\"2026-03-05\"]') as HTMLElement;\n day.click();\n await el.updateComplete;\n\n const clearBtn = el.shadowRoot!.querySelector('[data-action=\"clear\"]');\n expect(clearBtn).not.toBeNull();\n });\n\n it('clears all selections when clear button is clicked', async () => {\n const screen = render(html`<tt-multi-date-picker months=\"3\" start-month=\"2026-03\"></tt-multi-date-picker>`);\n const el = await getPicker(screen.container);\n\n const day = getCalendars(el)[0].shadowRoot!.querySelector('.day[data-date=\"2026-03-05\"]') as HTMLElement;\n day.click();\n await el.updateComplete;\n\n const clearBtn = el.shadowRoot!.querySelector('[data-action=\"clear\"]') as HTMLElement;\n clearBtn.click();\n await el.updateComplete;\n\n expect(el.value).toEqual([]);\n });\n });\n\n describe('keyboard navigation', () => {\n it('navigates to next page with PageDown', async () => {\n const screen = render(html`<tt-multi-date-picker months=\"3\" start-month=\"2026-03\"></tt-multi-date-picker>`);\n const el = await getPicker(screen.container);\n\n const gridArea = el.shadowRoot!.querySelector('.calendar-grid') as HTMLElement;\n gridArea.dispatchEvent(new KeyboardEvent('keydown', { key: 'PageDown', bubbles: true }));\n await el.updateComplete;\n\n const calendars = getCalendars(el);\n expect(calendars[0].getAttribute('month')).toEqual('2026-06');\n });\n\n it('navigates to previous page with PageUp', async () => {\n const screen = render(html`<tt-multi-date-picker months=\"3\" start-month=\"2026-03\"></tt-multi-date-picker>`);\n const el = await getPicker(screen.container);\n\n const gridArea = el.shadowRoot!.querySelector('.calendar-grid') as HTMLElement;\n gridArea.dispatchEvent(new KeyboardEvent('keydown', { key: 'PageUp', bubbles: true }));\n await el.updateComplete;\n\n const calendars = getCalendars(el);\n expect(calendars[0].getAttribute('month')).toEqual('2025-12');\n });\n\n it('selects a date with Enter key', async () => {\n const screen = render(html`<tt-multi-date-picker months=\"3\" start-month=\"2026-03\"></tt-multi-date-picker>`);\n const el = await getPicker(screen.container);\n\n const day = getCalendars(el)[0].shadowRoot!.querySelector('.day[data-date=\"2026-03-05\"]') as HTMLElement;\n day.click();\n await el.updateComplete;\n\n el.value = [];\n await el.updateComplete;\n\n const gridArea = el.shadowRoot!.querySelector('.calendar-grid') as HTMLElement;\n gridArea.dispatchEvent(new KeyboardEvent('keydown', { key: 'Enter', bubbles: true }));\n await el.updateComplete;\n\n expect(el.value).toEqual([{ startDate: '2026-03-05', endDate: '2026-03-05' }]);\n });\n });\n\n describe('min/max dates', () => {\n it('passes min-date and max-date to calendar instances', async () => {\n const screen = render(html`\n <tt-multi-date-picker\n months=\"3\"\n start-month=\"2026-03\"\n min-date=\"2026-03-10\"\n max-date=\"2026-05-20\"\n ></tt-multi-date-picker>\n `);\n const el = await getPicker(screen.container);\n const calendars = getCalendars(el);\n\n expect(calendars[0].getAttribute('min-date')).toEqual('2026-03-10');\n expect(calendars[0].getAttribute('max-date')).toEqual('2026-05-20');\n });\n });\n});\n"]}
1
+ {"version":3,"file":"tt-multi-date-picker.test.js","sourceRoot":"","sources":["../../../test/tt-multi-date-picker.test.ts"],"names":[],"mappings":"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","sourcesContent":["import { describe, expect, it } from 'vitest';\nimport { render } from 'vitest-browser-lit';\nimport { html } from 'lit';\nimport '../src/tt-multi-date-picker.js';\nimport type { TtMultiDatePicker } from '../src/TtMultiDatePicker.js';\n\nasync function getPicker(container: Element): Promise<TtMultiDatePicker> {\n const el = container.querySelector<TtMultiDatePicker>('tt-multi-date-picker');\n if (!el) throw new Error('tt-multi-date-picker not found');\n await el.updateComplete;\n return el;\n}\n\nfunction getCalendars(el: TtMultiDatePicker): Element[] {\n return Array.from(el.shadowRoot!.querySelectorAll('tt-calendar'));\n}\n\ndescribe('tt-multi-date-picker', () => {\n describe('rendering', () => {\n it('renders 6 calendars by default', async () => {\n const screen = render(html`<tt-multi-date-picker></tt-multi-date-picker>`);\n const el = await getPicker(screen.container);\n expect(getCalendars(el)).toHaveLength(6);\n });\n\n it('renders 3 calendars when months=\"3\"', async () => {\n const screen = render(html`<tt-multi-date-picker months=\"3\"></tt-multi-date-picker>`);\n const el = await getPicker(screen.container);\n expect(getCalendars(el)).toHaveLength(3);\n });\n\n it('renders 12 calendars when months=\"12\"', async () => {\n const screen = render(html`<tt-multi-date-picker months=\"12\"></tt-multi-date-picker>`);\n const el = await getPicker(screen.container);\n expect(getCalendars(el)).toHaveLength(12);\n });\n\n it('renders prev/next navigation buttons and selection instructions', async () => {\n const screen = render(html`<tt-multi-date-picker></tt-multi-date-picker>`);\n const el = await getPicker(screen.container);\n const prevBtn = el.shadowRoot!.querySelector('[aria-label*=\"Previous\"]');\n const nextBtn = el.shadowRoot!.querySelector('[aria-label*=\"Next\"]');\n const instructions = el.shadowRoot!.querySelector('.instructions');\n expect(prevBtn).not.toBeNull();\n expect(nextBtn).not.toBeNull();\n expect(instructions!.textContent).toContain('Shift+click');\n });\n });\n\n describe('selection', () => {\n it('selects a date on click and emits change event', async () => {\n const screen = render(html`<tt-multi-date-picker months=\"3\" start-month=\"2026-03\"></tt-multi-date-picker>`);\n const el = await getPicker(screen.container);\n\n let emittedValue: unknown;\n el.addEventListener('change', (e: Event) => {\n emittedValue = (e.target as TtMultiDatePicker).value;\n });\n\n const calendars = getCalendars(el);\n const day = calendars[0].shadowRoot!.querySelector('.day[data-date=\"2026-03-05\"]') as HTMLElement;\n day.click();\n await el.updateComplete;\n\n expect(el.value).toEqual([{ startDate: '2026-03-05', endDate: '2026-03-05' }]);\n expect(emittedValue).toEqual([{ startDate: '2026-03-05', endDate: '2026-03-05' }]);\n });\n\n it('deselects a single date on second click', async () => {\n const screen = render(html`<tt-multi-date-picker months=\"3\" start-month=\"2026-03\"></tt-multi-date-picker>`);\n const el = await getPicker(screen.container);\n\n const day = getCalendars(el)[0].shadowRoot!.querySelector('.day[data-date=\"2026-03-05\"]') as HTMLElement;\n day.click();\n await el.updateComplete;\n day.click();\n await el.updateComplete;\n\n expect(el.value).toEqual([]);\n });\n\n it('creates a range with shift+click', async () => {\n const screen = render(html`<tt-multi-date-picker months=\"3\" start-month=\"2026-03\"></tt-multi-date-picker>`);\n const el = await getPicker(screen.container);\n\n const calendars = getCalendars(el);\n const day5 = calendars[0].shadowRoot!.querySelector('.day[data-date=\"2026-03-05\"]') as HTMLElement;\n const day10 = calendars[0].shadowRoot!.querySelector('.day[data-date=\"2026-03-10\"]') as HTMLElement;\n\n day5.click();\n await el.updateComplete;\n day10.dispatchEvent(new MouseEvent('click', { shiftKey: true, bubbles: true, composed: true }));\n await el.updateComplete;\n\n expect(el.value).toEqual([{ startDate: '2026-03-05', endDate: '2026-03-10' }]);\n });\n });\n\n describe('form integration', () => {\n it('submits JSON value in form', async () => {\n const screen = render(html`\n <form>\n <tt-multi-date-picker name=\"dates\" start-month=\"2026-03\"></tt-multi-date-picker>\n </form>\n `);\n const el = await getPicker(screen.container);\n\n const day = getCalendars(el)[0].shadowRoot!.querySelector('.day[data-date=\"2026-03-05\"]') as HTMLElement;\n day.click();\n await el.updateComplete;\n\n const form = screen.container.querySelector('form')!;\n const formData = new FormData(form);\n const value = JSON.parse(formData.get('dates') as string);\n expect(value).toEqual([{ startDate: '2026-03-05', endDate: '2026-03-05' }]);\n });\n\n it('reports validity when required and empty', async () => {\n const screen = render(html`<tt-multi-date-picker required></tt-multi-date-picker>`);\n const el = await getPicker(screen.container);\n expect(el.matches(':invalid')).toBe(true);\n });\n });\n\n describe('footer', () => {\n it('shows clear all button when there are selections', async () => {\n const screen = render(html`<tt-multi-date-picker months=\"3\" start-month=\"2026-03\"></tt-multi-date-picker>`);\n const el = await getPicker(screen.container);\n\n const day = getCalendars(el)[0].shadowRoot!.querySelector('.day[data-date=\"2026-03-05\"]') as HTMLElement;\n day.click();\n await el.updateComplete;\n\n const clearBtn = el.shadowRoot!.querySelector('[data-action=\"clear\"]');\n expect(clearBtn).not.toBeNull();\n });\n\n it('clears all selections when clear button is clicked', async () => {\n const screen = render(html`<tt-multi-date-picker months=\"3\" start-month=\"2026-03\"></tt-multi-date-picker>`);\n const el = await getPicker(screen.container);\n\n const day = getCalendars(el)[0].shadowRoot!.querySelector('.day[data-date=\"2026-03-05\"]') as HTMLElement;\n day.click();\n await el.updateComplete;\n\n const clearBtn = el.shadowRoot!.querySelector('[data-action=\"clear\"]') as HTMLElement;\n clearBtn.click();\n await el.updateComplete;\n\n expect(el.value).toEqual([]);\n });\n });\n\n describe('keyboard navigation', () => {\n it('navigates to next page with PageDown', async () => {\n const screen = render(html`<tt-multi-date-picker months=\"3\" start-month=\"2026-03\"></tt-multi-date-picker>`);\n const el = await getPicker(screen.container);\n\n const gridArea = el.shadowRoot!.querySelector('.calendar-grid') as HTMLElement;\n gridArea.dispatchEvent(new KeyboardEvent('keydown', { key: 'PageDown', bubbles: true }));\n await el.updateComplete;\n\n const calendars = getCalendars(el);\n expect(calendars[0].getAttribute('month')).toEqual('2026-06');\n });\n\n it('navigates to previous page with PageUp', async () => {\n const screen = render(html`<tt-multi-date-picker months=\"3\" start-month=\"2026-03\"></tt-multi-date-picker>`);\n const el = await getPicker(screen.container);\n\n const gridArea = el.shadowRoot!.querySelector('.calendar-grid') as HTMLElement;\n gridArea.dispatchEvent(new KeyboardEvent('keydown', { key: 'PageUp', bubbles: true }));\n await el.updateComplete;\n\n const calendars = getCalendars(el);\n expect(calendars[0].getAttribute('month')).toEqual('2025-12');\n });\n\n it('selects a date with Enter key', async () => {\n const screen = render(html`<tt-multi-date-picker months=\"3\" start-month=\"2026-03\"></tt-multi-date-picker>`);\n const el = await getPicker(screen.container);\n\n const day = getCalendars(el)[0].shadowRoot!.querySelector('.day[data-date=\"2026-03-05\"]') as HTMLElement;\n day.click();\n await el.updateComplete;\n\n el.value = [];\n await el.updateComplete;\n\n const gridArea = el.shadowRoot!.querySelector('.calendar-grid') as HTMLElement;\n gridArea.dispatchEvent(new KeyboardEvent('keydown', { key: 'Enter', bubbles: true }));\n await el.updateComplete;\n\n expect(el.value).toEqual([{ startDate: '2026-03-05', endDate: '2026-03-05' }]);\n });\n });\n\n describe('min/max dates', () => {\n it('passes min-date and max-date to calendar instances', async () => {\n const screen = render(html`\n <tt-multi-date-picker\n months=\"3\"\n start-month=\"2026-03\"\n min-date=\"2026-03-10\"\n max-date=\"2026-05-20\"\n ></tt-multi-date-picker>\n `);\n const el = await getPicker(screen.container);\n const calendars = getCalendars(el);\n\n expect(calendars[0].getAttribute('min-date')).toEqual('2026-03-10');\n expect(calendars[0].getAttribute('max-date')).toEqual('2026-05-20');\n });\n });\n});\n"]}
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "description": "Multi-date and date range selection calendar web component",
4
4
  "license": "MIT",
5
5
  "author": "@triptease",
6
- "version": "0.3.0",
6
+ "version": "0.4.0",
7
7
  "type": "module",
8
8
  "main": "dist/esm/src/index.js",
9
9
  "module": "dist/esm/src/index.js",
@@ -37,8 +37,8 @@
37
37
  },
38
38
  "dependencies": {
39
39
  "@triptease/icons": "1.4.1",
40
- "@triptease/stylesheet": "2.1.8",
41
- "@triptease/tt-calendar": "6.3.0",
40
+ "@triptease/stylesheet": "2.1.9",
41
+ "@triptease/tt-calendar": "6.4.0",
42
42
  "lit": "^3.1.4",
43
43
  "luxon": "^3.6.1"
44
44
  },