@api-client/ui 0.5.6 → 0.5.8

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.
Files changed (82) hide show
  1. package/.cursor/rules/html-and-css-best-practices.mdc +63 -0
  2. package/.cursor/rules/lit-best-practices.mdc +78 -0
  3. package/.github/instructions/html-and-css-best-practices.instructions.md +70 -0
  4. package/.github/instructions/lit-best-practices.instructions.md +86 -0
  5. package/build/src/elements/currency/currency-picker.d.ts +10 -0
  6. package/build/src/elements/currency/currency-picker.d.ts.map +1 -0
  7. package/build/src/elements/currency/currency-picker.js +27 -0
  8. package/build/src/elements/currency/currency-picker.js.map +1 -0
  9. package/build/src/elements/currency/internals/Picker.d.ts +311 -0
  10. package/build/src/elements/currency/internals/Picker.d.ts.map +1 -0
  11. package/build/src/elements/currency/internals/Picker.js +857 -0
  12. package/build/src/elements/currency/internals/Picker.js.map +1 -0
  13. package/build/src/elements/currency/internals/Picker.styles.d.ts +3 -0
  14. package/build/src/elements/currency/internals/Picker.styles.d.ts.map +1 -0
  15. package/build/src/elements/currency/internals/Picker.styles.js +58 -0
  16. package/build/src/elements/currency/internals/Picker.styles.js.map +1 -0
  17. package/build/src/elements/mention-textarea/internals/MentionTextArea.d.ts +216 -0
  18. package/build/src/elements/mention-textarea/internals/MentionTextArea.d.ts.map +1 -0
  19. package/build/src/elements/mention-textarea/internals/MentionTextArea.js +1037 -0
  20. package/build/src/elements/mention-textarea/internals/MentionTextArea.js.map +1 -0
  21. package/build/src/elements/mention-textarea/internals/MentionTextArea.styles.d.ts +3 -0
  22. package/build/src/elements/mention-textarea/internals/MentionTextArea.styles.d.ts.map +1 -0
  23. package/build/src/elements/mention-textarea/internals/MentionTextArea.styles.js +274 -0
  24. package/build/src/elements/mention-textarea/internals/MentionTextArea.styles.js.map +1 -0
  25. package/build/src/elements/mention-textarea/ui-mention-textarea.d.ts +13 -0
  26. package/build/src/elements/mention-textarea/ui-mention-textarea.d.ts.map +1 -0
  27. package/build/src/elements/mention-textarea/ui-mention-textarea.js +28 -0
  28. package/build/src/elements/mention-textarea/ui-mention-textarea.js.map +1 -0
  29. package/build/src/md/button/internals/base.d.ts +1 -0
  30. package/build/src/md/button/internals/base.d.ts.map +1 -1
  31. package/build/src/md/button/internals/base.js +7 -0
  32. package/build/src/md/button/internals/base.js.map +1 -1
  33. package/build/src/md/chip/internals/Chip.styles.d.ts.map +1 -1
  34. package/build/src/md/chip/internals/Chip.styles.js +2 -0
  35. package/build/src/md/chip/internals/Chip.styles.js.map +1 -1
  36. package/build/src/md/date-picker/internals/DatePicker.styles.d.ts.map +1 -1
  37. package/build/src/md/date-picker/internals/DatePicker.styles.js +73 -0
  38. package/build/src/md/date-picker/internals/DatePicker.styles.js.map +1 -1
  39. package/build/src/md/date-picker/internals/DatePickerCalendar.d.ts +164 -51
  40. package/build/src/md/date-picker/internals/DatePickerCalendar.d.ts.map +1 -1
  41. package/build/src/md/date-picker/internals/DatePickerCalendar.js +660 -368
  42. package/build/src/md/date-picker/internals/DatePickerCalendar.js.map +1 -1
  43. package/build/src/md/date-picker/ui-date-picker-input.d.ts +65 -13
  44. package/build/src/md/date-picker/ui-date-picker-input.d.ts.map +1 -1
  45. package/build/src/md/date-picker/ui-date-picker-input.js +143 -76
  46. package/build/src/md/date-picker/ui-date-picker-input.js.map +1 -1
  47. package/build/src/md/date-picker/ui-date-picker-modal-input.d.ts +76 -17
  48. package/build/src/md/date-picker/ui-date-picker-modal-input.d.ts.map +1 -1
  49. package/build/src/md/date-picker/ui-date-picker-modal-input.js +192 -127
  50. package/build/src/md/date-picker/ui-date-picker-modal-input.js.map +1 -1
  51. package/build/src/md/date-picker/ui-date-picker-modal.d.ts +63 -15
  52. package/build/src/md/date-picker/ui-date-picker-modal.d.ts.map +1 -1
  53. package/build/src/md/date-picker/ui-date-picker-modal.js +143 -64
  54. package/build/src/md/date-picker/ui-date-picker-modal.js.map +1 -1
  55. package/demo/elements/currency/index.html +91 -0
  56. package/demo/elements/currency/index.ts +272 -0
  57. package/demo/elements/index.html +6 -0
  58. package/demo/elements/mention-textarea/index.html +19 -0
  59. package/demo/elements/mention-textarea/index.ts +205 -0
  60. package/demo/md/date-picker/date-picker.ts +138 -103
  61. package/package.json +2 -2
  62. package/src/elements/currency/currency-picker.ts +14 -0
  63. package/src/elements/currency/internals/Picker.styles.ts +58 -0
  64. package/src/elements/currency/internals/Picker.ts +846 -0
  65. package/src/elements/mention-textarea/internals/MentionTextArea.styles.ts +274 -0
  66. package/src/elements/mention-textarea/internals/MentionTextArea.ts +1036 -0
  67. package/src/elements/mention-textarea/ui-mention-textarea.ts +18 -0
  68. package/src/md/button/internals/base.ts +7 -0
  69. package/src/md/chip/internals/Chip.styles.ts +2 -0
  70. package/src/md/date-picker/internals/DatePicker.styles.ts +73 -0
  71. package/src/md/date-picker/internals/DatePickerCalendar.ts +643 -309
  72. package/src/md/date-picker/ui-date-picker-input.ts +110 -49
  73. package/src/md/date-picker/ui-date-picker-modal-input.ts +168 -99
  74. package/src/md/date-picker/ui-date-picker-modal.ts +136 -53
  75. package/test/README.md +3 -2
  76. package/test/elements/currency/CurrencyPicker.accessibility.test.ts +328 -0
  77. package/test/elements/currency/CurrencyPicker.core.test.ts +318 -0
  78. package/test/elements/currency/CurrencyPicker.integration.test.ts +482 -0
  79. package/test/elements/currency/CurrencyPicker.test.ts +486 -0
  80. package/test/elements/mention-textarea/MentionTextArea.basic.test.ts +63 -0
  81. package/test/elements/mention-textarea/MentionTextArea.test.ts +321 -0
  82. package/tsconfig.json +1 -1
@@ -3,13 +3,27 @@ import { LitElement, html, nothing } from 'lit';
3
3
  import { customElement, property, state } from 'lit/decorators.js';
4
4
  import { classMap } from 'lit/directives/class-map.js';
5
5
  import { calendarStyles } from './DatePicker.styles.js';
6
- import { generateCalendarMonth, addMonths, getMonthNames, isSameDay, formatDate, } from './DatePickerUtils.js';
6
+ import { generateCalendarMonth, addMonths, getMonthNames, isSameDay, formatDate, addDays, } from './DatePickerUtils.js';
7
7
  import '../../../md/icons/ui-icon.js';
8
8
  import '../../../md/button/ui-button.js';
9
9
  import '../../../md/icon-button/ui-icon-button.js';
10
10
  /**
11
11
  * A calendar grid component for date selection.
12
- * Supports single date selection and date range selection.
12
+ * Supports single date selection and date range selection with full keyboard navigation.
13
+ *
14
+ * ## Features
15
+ * - Single date and date range selection
16
+ * - Keyboard navigation support (arrow keys, home, end, page up/down)
17
+ * - Configurable date restrictions (min/max dates, disabled dates)
18
+ * - Localization support for date formatting and month/day names
19
+ * - Optional action buttons for pending selections
20
+ * - Accessible design with proper ARIA attributes
21
+ *
22
+ * ## Events
23
+ * - `date-select`: Fired when a single date is selected/confirmed
24
+ * - `date-range-select`: Fired when a date range is completed (immediate mode)
25
+ * - `date-range-confirm`: Fired when a date range is confirmed (pending mode)
26
+ * - `date-cancel`: Fired when a pending selection is cancelled
13
27
  *
14
28
  * ## Usage
15
29
  *
@@ -28,11 +42,24 @@ import '../../../md/icon-button/ui-icon-button.js';
28
42
  * ### Date range selection
29
43
  * ```html
30
44
  * <ui-date-picker-calendar
31
- * .rangeSelection=${true}
32
- * .selectedRange=${{ start: new Date(), end: null }}
45
+ * rangeSelection
46
+ * .rangeStart=${new Date()}
47
+ * .rangeEnd=${null}
33
48
  * @date-range-select=${this.handleRangeSelect}
34
49
  * ></ui-date-picker-calendar>
35
50
  * ```
51
+ *
52
+ * ### With action buttons and restrictions
53
+ * ```html
54
+ * <ui-date-picker-calendar
55
+ * rangeSelection
56
+ * showActions
57
+ * .minDate=${new Date()}
58
+ * .maxDate=${new Date(Date.now() + 365 * 24 * 60 * 60 * 1000)}
59
+ * @date-range-confirm=${this.handleRangeConfirm}
60
+ * @date-cancel=${this.handleCancel}
61
+ * ></ui-date-picker-calendar>
62
+ * ```
36
63
  */
37
64
  let UiDatePickerCalendar = (() => {
38
65
  let _classDecorators = [customElement('ui-date-picker-calendar')];
@@ -49,9 +76,12 @@ let UiDatePickerCalendar = (() => {
49
76
  let _selectedDate_decorators;
50
77
  let _selectedDate_initializers = [];
51
78
  let _selectedDate_extraInitializers = [];
52
- let _selectedRange_decorators;
53
- let _selectedRange_initializers = [];
54
- let _selectedRange_extraInitializers = [];
79
+ let _rangeStart_decorators;
80
+ let _rangeStart_initializers = [];
81
+ let _rangeStart_extraInitializers = [];
82
+ let _rangeEnd_decorators;
83
+ let _rangeEnd_initializers = [];
84
+ let _rangeEnd_extraInitializers = [];
55
85
  let _rangeSelection_decorators;
56
86
  let _rangeSelection_initializers = [];
57
87
  let _rangeSelection_extraInitializers = [];
@@ -79,27 +109,30 @@ let UiDatePickerCalendar = (() => {
79
109
  let _cancelButtonText_decorators;
80
110
  let _cancelButtonText_initializers = [];
81
111
  let _cancelButtonText_extraInitializers = [];
82
- let __calendarData_decorators;
83
- let __calendarData_initializers = [];
84
- let __calendarData_extraInitializers = [];
85
- let __rangeStart_decorators;
86
- let __rangeStart_initializers = [];
87
- let __rangeStart_extraInitializers = [];
88
- let __monthNames_decorators;
89
- let __monthNames_initializers = [];
90
- let __monthNames_extraInitializers = [];
91
- let __showMonthDropdown_decorators;
92
- let __showMonthDropdown_initializers = [];
93
- let __showMonthDropdown_extraInitializers = [];
94
- let __showYearDropdown_decorators;
95
- let __showYearDropdown_initializers = [];
96
- let __showYearDropdown_extraInitializers = [];
97
- let __pendingDate_decorators;
98
- let __pendingDate_initializers = [];
99
- let __pendingDate_extraInitializers = [];
100
- let __pendingRange_decorators;
101
- let __pendingRange_initializers = [];
102
- let __pendingRange_extraInitializers = [];
112
+ let _calendarData_decorators;
113
+ let _calendarData_initializers = [];
114
+ let _calendarData_extraInitializers = [];
115
+ let _monthNames_decorators;
116
+ let _monthNames_initializers = [];
117
+ let _monthNames_extraInitializers = [];
118
+ let _showMonthDropdown_decorators;
119
+ let _showMonthDropdown_initializers = [];
120
+ let _showMonthDropdown_extraInitializers = [];
121
+ let _showYearDropdown_decorators;
122
+ let _showYearDropdown_initializers = [];
123
+ let _showYearDropdown_extraInitializers = [];
124
+ let _pendingDate_decorators;
125
+ let _pendingDate_initializers = [];
126
+ let _pendingDate_extraInitializers = [];
127
+ let _pendingRangeStart_decorators;
128
+ let _pendingRangeStart_initializers = [];
129
+ let _pendingRangeStart_extraInitializers = [];
130
+ let _pendingRangeEnd_decorators;
131
+ let _pendingRangeEnd_initializers = [];
132
+ let _pendingRangeEnd_extraInitializers = [];
133
+ let _focusedDate_decorators;
134
+ let _focusedDate_initializers = [];
135
+ let _focusedDate_extraInitializers = [];
103
136
  var UiDatePickerCalendar = class extends _classSuper {
104
137
  static { _classThis = this; }
105
138
  static {
@@ -107,7 +140,8 @@ let UiDatePickerCalendar = (() => {
107
140
  _year_decorators = [property({ type: Number })];
108
141
  _month_decorators = [property({ type: Number })];
109
142
  _selectedDate_decorators = [property({ type: Object })];
110
- _selectedRange_decorators = [property({ type: Object })];
143
+ _rangeStart_decorators = [property({ type: Object })];
144
+ _rangeEnd_decorators = [property({ type: Object })];
111
145
  _rangeSelection_decorators = [property({ type: Boolean })];
112
146
  _minDate_decorators = [property({ type: Object })];
113
147
  _maxDate_decorators = [property({ type: Object })];
@@ -117,17 +151,19 @@ let UiDatePickerCalendar = (() => {
117
151
  _showActions_decorators = [property({ type: Boolean })];
118
152
  _okButtonText_decorators = [property({ type: String })];
119
153
  _cancelButtonText_decorators = [property({ type: String })];
120
- __calendarData_decorators = [state()];
121
- __rangeStart_decorators = [state()];
122
- __monthNames_decorators = [state()];
123
- __showMonthDropdown_decorators = [state()];
124
- __showYearDropdown_decorators = [state()];
125
- __pendingDate_decorators = [state()];
126
- __pendingRange_decorators = [state()];
154
+ _calendarData_decorators = [state()];
155
+ _monthNames_decorators = [state()];
156
+ _showMonthDropdown_decorators = [state()];
157
+ _showYearDropdown_decorators = [state()];
158
+ _pendingDate_decorators = [state()];
159
+ _pendingRangeStart_decorators = [state()];
160
+ _pendingRangeEnd_decorators = [state()];
161
+ _focusedDate_decorators = [state()];
127
162
  __esDecorate(this, null, _year_decorators, { kind: "accessor", name: "year", static: false, private: false, access: { has: obj => "year" in obj, get: obj => obj.year, set: (obj, value) => { obj.year = value; } }, metadata: _metadata }, _year_initializers, _year_extraInitializers);
128
163
  __esDecorate(this, null, _month_decorators, { kind: "accessor", name: "month", static: false, private: false, access: { has: obj => "month" in obj, get: obj => obj.month, set: (obj, value) => { obj.month = value; } }, metadata: _metadata }, _month_initializers, _month_extraInitializers);
129
164
  __esDecorate(this, null, _selectedDate_decorators, { kind: "accessor", name: "selectedDate", static: false, private: false, access: { has: obj => "selectedDate" in obj, get: obj => obj.selectedDate, set: (obj, value) => { obj.selectedDate = value; } }, metadata: _metadata }, _selectedDate_initializers, _selectedDate_extraInitializers);
130
- __esDecorate(this, null, _selectedRange_decorators, { kind: "accessor", name: "selectedRange", static: false, private: false, access: { has: obj => "selectedRange" in obj, get: obj => obj.selectedRange, set: (obj, value) => { obj.selectedRange = value; } }, metadata: _metadata }, _selectedRange_initializers, _selectedRange_extraInitializers);
165
+ __esDecorate(this, null, _rangeStart_decorators, { kind: "accessor", name: "rangeStart", static: false, private: false, access: { has: obj => "rangeStart" in obj, get: obj => obj.rangeStart, set: (obj, value) => { obj.rangeStart = value; } }, metadata: _metadata }, _rangeStart_initializers, _rangeStart_extraInitializers);
166
+ __esDecorate(this, null, _rangeEnd_decorators, { kind: "accessor", name: "rangeEnd", static: false, private: false, access: { has: obj => "rangeEnd" in obj, get: obj => obj.rangeEnd, set: (obj, value) => { obj.rangeEnd = value; } }, metadata: _metadata }, _rangeEnd_initializers, _rangeEnd_extraInitializers);
131
167
  __esDecorate(this, null, _rangeSelection_decorators, { kind: "accessor", name: "rangeSelection", static: false, private: false, access: { has: obj => "rangeSelection" in obj, get: obj => obj.rangeSelection, set: (obj, value) => { obj.rangeSelection = value; } }, metadata: _metadata }, _rangeSelection_initializers, _rangeSelection_extraInitializers);
132
168
  __esDecorate(this, null, _minDate_decorators, { kind: "accessor", name: "minDate", static: false, private: false, access: { has: obj => "minDate" in obj, get: obj => obj.minDate, set: (obj, value) => { obj.minDate = value; } }, metadata: _metadata }, _minDate_initializers, _minDate_extraInitializers);
133
169
  __esDecorate(this, null, _maxDate_decorators, { kind: "accessor", name: "maxDate", static: false, private: false, access: { has: obj => "maxDate" in obj, get: obj => obj.maxDate, set: (obj, value) => { obj.maxDate = value; } }, metadata: _metadata }, _maxDate_initializers, _maxDate_extraInitializers);
@@ -137,13 +173,14 @@ let UiDatePickerCalendar = (() => {
137
173
  __esDecorate(this, null, _showActions_decorators, { kind: "accessor", name: "showActions", static: false, private: false, access: { has: obj => "showActions" in obj, get: obj => obj.showActions, set: (obj, value) => { obj.showActions = value; } }, metadata: _metadata }, _showActions_initializers, _showActions_extraInitializers);
138
174
  __esDecorate(this, null, _okButtonText_decorators, { kind: "accessor", name: "okButtonText", static: false, private: false, access: { has: obj => "okButtonText" in obj, get: obj => obj.okButtonText, set: (obj, value) => { obj.okButtonText = value; } }, metadata: _metadata }, _okButtonText_initializers, _okButtonText_extraInitializers);
139
175
  __esDecorate(this, null, _cancelButtonText_decorators, { kind: "accessor", name: "cancelButtonText", static: false, private: false, access: { has: obj => "cancelButtonText" in obj, get: obj => obj.cancelButtonText, set: (obj, value) => { obj.cancelButtonText = value; } }, metadata: _metadata }, _cancelButtonText_initializers, _cancelButtonText_extraInitializers);
140
- __esDecorate(this, null, __calendarData_decorators, { kind: "accessor", name: "_calendarData", static: false, private: false, access: { has: obj => "_calendarData" in obj, get: obj => obj._calendarData, set: (obj, value) => { obj._calendarData = value; } }, metadata: _metadata }, __calendarData_initializers, __calendarData_extraInitializers);
141
- __esDecorate(this, null, __rangeStart_decorators, { kind: "accessor", name: "_rangeStart", static: false, private: false, access: { has: obj => "_rangeStart" in obj, get: obj => obj._rangeStart, set: (obj, value) => { obj._rangeStart = value; } }, metadata: _metadata }, __rangeStart_initializers, __rangeStart_extraInitializers);
142
- __esDecorate(this, null, __monthNames_decorators, { kind: "accessor", name: "_monthNames", static: false, private: false, access: { has: obj => "_monthNames" in obj, get: obj => obj._monthNames, set: (obj, value) => { obj._monthNames = value; } }, metadata: _metadata }, __monthNames_initializers, __monthNames_extraInitializers);
143
- __esDecorate(this, null, __showMonthDropdown_decorators, { kind: "accessor", name: "_showMonthDropdown", static: false, private: false, access: { has: obj => "_showMonthDropdown" in obj, get: obj => obj._showMonthDropdown, set: (obj, value) => { obj._showMonthDropdown = value; } }, metadata: _metadata }, __showMonthDropdown_initializers, __showMonthDropdown_extraInitializers);
144
- __esDecorate(this, null, __showYearDropdown_decorators, { kind: "accessor", name: "_showYearDropdown", static: false, private: false, access: { has: obj => "_showYearDropdown" in obj, get: obj => obj._showYearDropdown, set: (obj, value) => { obj._showYearDropdown = value; } }, metadata: _metadata }, __showYearDropdown_initializers, __showYearDropdown_extraInitializers);
145
- __esDecorate(this, null, __pendingDate_decorators, { kind: "accessor", name: "_pendingDate", static: false, private: false, access: { has: obj => "_pendingDate" in obj, get: obj => obj._pendingDate, set: (obj, value) => { obj._pendingDate = value; } }, metadata: _metadata }, __pendingDate_initializers, __pendingDate_extraInitializers);
146
- __esDecorate(this, null, __pendingRange_decorators, { kind: "accessor", name: "_pendingRange", static: false, private: false, access: { has: obj => "_pendingRange" in obj, get: obj => obj._pendingRange, set: (obj, value) => { obj._pendingRange = value; } }, metadata: _metadata }, __pendingRange_initializers, __pendingRange_extraInitializers);
176
+ __esDecorate(this, null, _calendarData_decorators, { kind: "accessor", name: "calendarData", static: false, private: false, access: { has: obj => "calendarData" in obj, get: obj => obj.calendarData, set: (obj, value) => { obj.calendarData = value; } }, metadata: _metadata }, _calendarData_initializers, _calendarData_extraInitializers);
177
+ __esDecorate(this, null, _monthNames_decorators, { kind: "accessor", name: "monthNames", static: false, private: false, access: { has: obj => "monthNames" in obj, get: obj => obj.monthNames, set: (obj, value) => { obj.monthNames = value; } }, metadata: _metadata }, _monthNames_initializers, _monthNames_extraInitializers);
178
+ __esDecorate(this, null, _showMonthDropdown_decorators, { kind: "accessor", name: "showMonthDropdown", static: false, private: false, access: { has: obj => "showMonthDropdown" in obj, get: obj => obj.showMonthDropdown, set: (obj, value) => { obj.showMonthDropdown = value; } }, metadata: _metadata }, _showMonthDropdown_initializers, _showMonthDropdown_extraInitializers);
179
+ __esDecorate(this, null, _showYearDropdown_decorators, { kind: "accessor", name: "showYearDropdown", static: false, private: false, access: { has: obj => "showYearDropdown" in obj, get: obj => obj.showYearDropdown, set: (obj, value) => { obj.showYearDropdown = value; } }, metadata: _metadata }, _showYearDropdown_initializers, _showYearDropdown_extraInitializers);
180
+ __esDecorate(this, null, _pendingDate_decorators, { kind: "accessor", name: "pendingDate", static: false, private: false, access: { has: obj => "pendingDate" in obj, get: obj => obj.pendingDate, set: (obj, value) => { obj.pendingDate = value; } }, metadata: _metadata }, _pendingDate_initializers, _pendingDate_extraInitializers);
181
+ __esDecorate(this, null, _pendingRangeStart_decorators, { kind: "accessor", name: "pendingRangeStart", static: false, private: false, access: { has: obj => "pendingRangeStart" in obj, get: obj => obj.pendingRangeStart, set: (obj, value) => { obj.pendingRangeStart = value; } }, metadata: _metadata }, _pendingRangeStart_initializers, _pendingRangeStart_extraInitializers);
182
+ __esDecorate(this, null, _pendingRangeEnd_decorators, { kind: "accessor", name: "pendingRangeEnd", static: false, private: false, access: { has: obj => "pendingRangeEnd" in obj, get: obj => obj.pendingRangeEnd, set: (obj, value) => { obj.pendingRangeEnd = value; } }, metadata: _metadata }, _pendingRangeEnd_initializers, _pendingRangeEnd_extraInitializers);
183
+ __esDecorate(this, null, _focusedDate_decorators, { kind: "accessor", name: "focusedDate", static: false, private: false, access: { has: obj => "focusedDate" in obj, get: obj => obj.focusedDate, set: (obj, value) => { obj.focusedDate = value; } }, metadata: _metadata }, _focusedDate_initializers, _focusedDate_extraInitializers);
147
184
  __esDecorate(null, _classDescriptor = { value: _classThis }, _classDecorators, { kind: "class", name: _classThis.name, metadata: _metadata }, null, _classExtraInitializers);
148
185
  UiDatePickerCalendar = _classThis = _classDescriptor.value;
149
186
  if (_metadata) Object.defineProperty(_classThis, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
@@ -151,7 +188,7 @@ let UiDatePickerCalendar = (() => {
151
188
  static styles = calendarStyles;
152
189
  #year_accessor_storage = __runInitializers(this, _year_initializers, new Date().getFullYear()
153
190
  /**
154
- * The currently displayed month (0-indexed)
191
+ * The currently displayed month (0-indexed, where 0 = January)
155
192
  */
156
193
  );
157
194
  /**
@@ -161,158 +198,228 @@ let UiDatePickerCalendar = (() => {
161
198
  set year(value) { this.#year_accessor_storage = value; }
162
199
  #month_accessor_storage = (__runInitializers(this, _year_extraInitializers), __runInitializers(this, _month_initializers, new Date().getMonth()
163
200
  /**
164
- * The currently selected date for single selection mode
201
+ * The currently selected date for single selection mode.
202
+ * Set to null for no selection.
165
203
  */
166
204
  ));
167
205
  /**
168
- * The currently displayed month (0-indexed)
206
+ * The currently displayed month (0-indexed, where 0 = January)
169
207
  */
170
208
  get month() { return this.#month_accessor_storage; }
171
209
  set month(value) { this.#month_accessor_storage = value; }
172
210
  #selectedDate_accessor_storage = (__runInitializers(this, _month_extraInitializers), __runInitializers(this, _selectedDate_initializers, null
173
211
  /**
174
- * The selected date range for range selection mode
212
+ * The start date of the selected range for range selection mode.
213
+ * Used in combination with rangeEnd to define a date range.
175
214
  */
176
215
  ));
177
216
  /**
178
- * The currently selected date for single selection mode
217
+ * The currently selected date for single selection mode.
218
+ * Set to null for no selection.
179
219
  */
180
220
  get selectedDate() { return this.#selectedDate_accessor_storage; }
181
221
  set selectedDate(value) { this.#selectedDate_accessor_storage = value; }
182
- #selectedRange_accessor_storage = (__runInitializers(this, _selectedDate_extraInitializers), __runInitializers(this, _selectedRange_initializers, null
222
+ #rangeStart_accessor_storage = (__runInitializers(this, _selectedDate_extraInitializers), __runInitializers(this, _rangeStart_initializers, null
183
223
  /**
184
- * Enable range selection mode
224
+ * The end date of the selected range for range selection mode.
225
+ * Used in combination with rangeStart to define a date range.
185
226
  */
186
227
  ));
187
228
  /**
188
- * The selected date range for range selection mode
229
+ * The start date of the selected range for range selection mode.
230
+ * Used in combination with rangeEnd to define a date range.
189
231
  */
190
- get selectedRange() { return this.#selectedRange_accessor_storage; }
191
- set selectedRange(value) { this.#selectedRange_accessor_storage = value; }
192
- #rangeSelection_accessor_storage = (__runInitializers(this, _selectedRange_extraInitializers), __runInitializers(this, _rangeSelection_initializers, false
232
+ get rangeStart() { return this.#rangeStart_accessor_storage; }
233
+ set rangeStart(value) { this.#rangeStart_accessor_storage = value; }
234
+ #rangeEnd_accessor_storage = (__runInitializers(this, _rangeStart_extraInitializers), __runInitializers(this, _rangeEnd_initializers, null
193
235
  /**
194
- * Minimum selectable date
236
+ * Enable range selection mode. When true, users can select date ranges
237
+ * instead of single dates. Affects event dispatching and UI behavior.
195
238
  */
196
239
  ));
197
240
  /**
198
- * Enable range selection mode
241
+ * The end date of the selected range for range selection mode.
242
+ * Used in combination with rangeStart to define a date range.
243
+ */
244
+ get rangeEnd() { return this.#rangeEnd_accessor_storage; }
245
+ set rangeEnd(value) { this.#rangeEnd_accessor_storage = value; }
246
+ #rangeSelection_accessor_storage = (__runInitializers(this, _rangeEnd_extraInitializers), __runInitializers(this, _rangeSelection_initializers, false
247
+ /**
248
+ * Minimum selectable date. Dates before this will be disabled.
249
+ * Set to undefined for no minimum restriction.
250
+ */
251
+ ));
252
+ /**
253
+ * Enable range selection mode. When true, users can select date ranges
254
+ * instead of single dates. Affects event dispatching and UI behavior.
199
255
  */
200
256
  get rangeSelection() { return this.#rangeSelection_accessor_storage; }
201
257
  set rangeSelection(value) { this.#rangeSelection_accessor_storage = value; }
202
258
  #minDate_accessor_storage = (__runInitializers(this, _rangeSelection_extraInitializers), __runInitializers(this, _minDate_initializers, undefined
203
259
  /**
204
- * Maximum selectable date
260
+ * Maximum selectable date. Dates after this will be disabled.
261
+ * Set to undefined for no maximum restriction.
205
262
  */
206
263
  ));
207
264
  /**
208
- * Minimum selectable date
265
+ * Minimum selectable date. Dates before this will be disabled.
266
+ * Set to undefined for no minimum restriction.
209
267
  */
210
268
  get minDate() { return this.#minDate_accessor_storage; }
211
269
  set minDate(value) { this.#minDate_accessor_storage = value; }
212
270
  #maxDate_accessor_storage = (__runInitializers(this, _minDate_extraInitializers), __runInitializers(this, _maxDate_initializers, undefined
213
271
  /**
214
- * Array of disabled dates
272
+ * Array of specific dates to disable. These dates will not be selectable
273
+ * regardless of minDate and maxDate settings.
215
274
  */
216
275
  ));
217
276
  /**
218
- * Maximum selectable date
277
+ * Maximum selectable date. Dates after this will be disabled.
278
+ * Set to undefined for no maximum restriction.
219
279
  */
220
280
  get maxDate() { return this.#maxDate_accessor_storage; }
221
281
  set maxDate(value) { this.#maxDate_accessor_storage = value; }
222
282
  #disabledDates_accessor_storage = (__runInitializers(this, _maxDate_extraInitializers), __runInitializers(this, _disabledDates_initializers, undefined
223
283
  /**
224
- * Locale for date formatting and month/day names
284
+ * Locale for date formatting and month/day names (e.g., 'en-US', 'fr-FR').
285
+ * Defaults to browser locale if not specified.
225
286
  */
226
287
  ));
227
288
  /**
228
- * Array of disabled dates
289
+ * Array of specific dates to disable. These dates will not be selectable
290
+ * regardless of minDate and maxDate settings.
229
291
  */
230
292
  get disabledDates() { return this.#disabledDates_accessor_storage; }
231
293
  set disabledDates(value) { this.#disabledDates_accessor_storage = value; }
232
294
  #locale_accessor_storage = (__runInitializers(this, _disabledDates_extraInitializers), __runInitializers(this, _locale_initializers, undefined
233
295
  /**
234
- * Whether to show navigation controls
296
+ * Whether to show navigation controls (previous/next month and year buttons).
297
+ * When false, users can only navigate using keyboard or programmatically.
235
298
  */
236
299
  ));
237
300
  /**
238
- * Locale for date formatting and month/day names
301
+ * Locale for date formatting and month/day names (e.g., 'en-US', 'fr-FR').
302
+ * Defaults to browser locale if not specified.
239
303
  */
240
304
  get locale() { return this.#locale_accessor_storage; }
241
305
  set locale(value) { this.#locale_accessor_storage = value; }
242
306
  #showNavigation_accessor_storage = (__runInitializers(this, _locale_extraInitializers), __runInitializers(this, _showNavigation_initializers, true
243
307
  /**
244
- * Whether to show action buttons (OK/Cancel)
308
+ * Whether to show action buttons (OK/Cancel). When true, selections are pending
309
+ * until confirmed with the OK button. When false, selections are immediate.
245
310
  */
246
311
  ));
247
312
  /**
248
- * Whether to show navigation controls
313
+ * Whether to show navigation controls (previous/next month and year buttons).
314
+ * When false, users can only navigate using keyboard or programmatically.
249
315
  */
250
316
  get showNavigation() { return this.#showNavigation_accessor_storage; }
251
317
  set showNavigation(value) { this.#showNavigation_accessor_storage = value; }
252
318
  #showActions_accessor_storage = (__runInitializers(this, _showNavigation_extraInitializers), __runInitializers(this, _showActions_initializers, false
253
319
  /**
254
- * Text for the OK button
320
+ * Text label for the OK/confirm button. Only visible when showActions is true.
255
321
  */
256
322
  ));
257
323
  /**
258
- * Whether to show action buttons (OK/Cancel)
324
+ * Whether to show action buttons (OK/Cancel). When true, selections are pending
325
+ * until confirmed with the OK button. When false, selections are immediate.
259
326
  */
260
327
  get showActions() { return this.#showActions_accessor_storage; }
261
328
  set showActions(value) { this.#showActions_accessor_storage = value; }
262
329
  #okButtonText_accessor_storage = (__runInitializers(this, _showActions_extraInitializers), __runInitializers(this, _okButtonText_initializers, 'OK'
263
330
  /**
264
- * Text for the Cancel button
331
+ * Text label for the Cancel button. Only visible when showActions is true.
265
332
  */
266
333
  ));
267
334
  /**
268
- * Text for the OK button
335
+ * Text label for the OK/confirm button. Only visible when showActions is true.
269
336
  */
270
337
  get okButtonText() { return this.#okButtonText_accessor_storage; }
271
338
  set okButtonText(value) { this.#okButtonText_accessor_storage = value; }
272
339
  #cancelButtonText_accessor_storage = (__runInitializers(this, _okButtonText_extraInitializers), __runInitializers(this, _cancelButtonText_initializers, 'Cancel'));
273
340
  /**
274
- * Text for the Cancel button
341
+ * Text label for the Cancel button. Only visible when showActions is true.
275
342
  */
276
343
  get cancelButtonText() { return this.#cancelButtonText_accessor_storage; }
277
344
  set cancelButtonText(value) { this.#cancelButtonText_accessor_storage = value; }
278
- #_calendarData_accessor_storage = (__runInitializers(this, _cancelButtonText_extraInitializers), __runInitializers(this, __calendarData_initializers, undefined));
279
- get _calendarData() { return this.#_calendarData_accessor_storage; }
280
- set _calendarData(value) { this.#_calendarData_accessor_storage = value; }
281
- #_rangeStart_accessor_storage = (__runInitializers(this, __calendarData_extraInitializers), __runInitializers(this, __rangeStart_initializers, undefined));
282
- get _rangeStart() { return this.#_rangeStart_accessor_storage; }
283
- set _rangeStart(value) { this.#_rangeStart_accessor_storage = value; }
284
- #_monthNames_accessor_storage = (__runInitializers(this, __rangeStart_extraInitializers), __runInitializers(this, __monthNames_initializers, []));
285
- get _monthNames() { return this.#_monthNames_accessor_storage; }
286
- set _monthNames(value) { this.#_monthNames_accessor_storage = value; }
287
- #_showMonthDropdown_accessor_storage = (__runInitializers(this, __monthNames_extraInitializers), __runInitializers(this, __showMonthDropdown_initializers, false));
288
- get _showMonthDropdown() { return this.#_showMonthDropdown_accessor_storage; }
289
- set _showMonthDropdown(value) { this.#_showMonthDropdown_accessor_storage = value; }
290
- #_showYearDropdown_accessor_storage = (__runInitializers(this, __showMonthDropdown_extraInitializers), __runInitializers(this, __showYearDropdown_initializers, false));
291
- get _showYearDropdown() { return this.#_showYearDropdown_accessor_storage; }
292
- set _showYearDropdown(value) { this.#_showYearDropdown_accessor_storage = value; }
293
- #_pendingDate_accessor_storage = (__runInitializers(this, __showYearDropdown_extraInitializers), __runInitializers(this, __pendingDate_initializers, null));
294
- get _pendingDate() { return this.#_pendingDate_accessor_storage; }
295
- set _pendingDate(value) { this.#_pendingDate_accessor_storage = value; }
296
- #_pendingRange_accessor_storage = (__runInitializers(this, __pendingDate_extraInitializers), __runInitializers(this, __pendingRange_initializers, null));
297
- get _pendingRange() { return this.#_pendingRange_accessor_storage; }
298
- set _pendingRange(value) { this.#_pendingRange_accessor_storage = value; }
345
+ #calendarData_accessor_storage = (__runInitializers(this, _cancelButtonText_extraInitializers), __runInitializers(this, _calendarData_initializers, undefined));
346
+ get calendarData() { return this.#calendarData_accessor_storage; }
347
+ set calendarData(value) { this.#calendarData_accessor_storage = value; }
348
+ #monthNames_accessor_storage = (__runInitializers(this, _calendarData_extraInitializers), __runInitializers(this, _monthNames_initializers, []));
349
+ get monthNames() { return this.#monthNames_accessor_storage; }
350
+ set monthNames(value) { this.#monthNames_accessor_storage = value; }
351
+ #showMonthDropdown_accessor_storage = (__runInitializers(this, _monthNames_extraInitializers), __runInitializers(this, _showMonthDropdown_initializers, false));
352
+ get showMonthDropdown() { return this.#showMonthDropdown_accessor_storage; }
353
+ set showMonthDropdown(value) { this.#showMonthDropdown_accessor_storage = value; }
354
+ #showYearDropdown_accessor_storage = (__runInitializers(this, _showMonthDropdown_extraInitializers), __runInitializers(this, _showYearDropdown_initializers, false));
355
+ get showYearDropdown() { return this.#showYearDropdown_accessor_storage; }
356
+ set showYearDropdown(value) { this.#showYearDropdown_accessor_storage = value; }
357
+ #pendingDate_accessor_storage = (__runInitializers(this, _showYearDropdown_extraInitializers), __runInitializers(this, _pendingDate_initializers, null));
358
+ get pendingDate() { return this.#pendingDate_accessor_storage; }
359
+ set pendingDate(value) { this.#pendingDate_accessor_storage = value; }
360
+ #pendingRangeStart_accessor_storage = (__runInitializers(this, _pendingDate_extraInitializers), __runInitializers(this, _pendingRangeStart_initializers, null));
361
+ get pendingRangeStart() { return this.#pendingRangeStart_accessor_storage; }
362
+ set pendingRangeStart(value) { this.#pendingRangeStart_accessor_storage = value; }
363
+ #pendingRangeEnd_accessor_storage = (__runInitializers(this, _pendingRangeStart_extraInitializers), __runInitializers(this, _pendingRangeEnd_initializers, null));
364
+ get pendingRangeEnd() { return this.#pendingRangeEnd_accessor_storage; }
365
+ set pendingRangeEnd(value) { this.#pendingRangeEnd_accessor_storage = value; }
366
+ #focusedDate_accessor_storage = (__runInitializers(this, _pendingRangeEnd_extraInitializers), __runInitializers(this, _focusedDate_initializers, null));
367
+ get focusedDate() { return this.#focusedDate_accessor_storage; }
368
+ set focusedDate(value) { this.#focusedDate_accessor_storage = value; }
299
369
  connectedCallback() {
300
370
  super.connectedCallback();
301
- this._updateCalendar();
302
- this._updateMonthNames();
303
- document.addEventListener('keydown', this._handleDocumentKeyDown.bind(this));
371
+ this.updateCalendar();
372
+ this.updateMonthNames();
373
+ this.initializeFocusedDate();
374
+ }
375
+ firstUpdated() {
376
+ // Set initial focus to the calendar container
377
+ this.updateFocus();
304
378
  }
305
379
  disconnectedCallback() {
306
380
  super.disconnectedCallback();
307
- document.removeEventListener('keydown', this._handleDocumentKeyDown.bind(this));
381
+ }
382
+ willUpdate(changedProperties) {
383
+ if (changedProperties.has('year') ||
384
+ changedProperties.has('month') ||
385
+ changedProperties.has('selectedDate') ||
386
+ changedProperties.has('rangeStart') ||
387
+ changedProperties.has('rangeEnd') ||
388
+ changedProperties.has('disabledDates') ||
389
+ changedProperties.has('locale')) {
390
+ this.updateCalendar();
391
+ }
392
+ if (changedProperties.has('locale')) {
393
+ this.updateMonthNames();
394
+ }
395
+ // Update focused date when month/year changes via navigation
396
+ if (changedProperties.has('year') || changedProperties.has('month')) {
397
+ this.updateFocusedDateForMonthChange();
398
+ }
308
399
  }
309
400
  updated(changedProperties) {
310
- if (changedProperties.has('_showYearDropdown') && this._showYearDropdown) {
401
+ if (changedProperties.has('showYearDropdown') && this.showYearDropdown) {
311
402
  // Scroll selected year into view
312
- this._scrollSelectedYearIntoView();
403
+ this.scrollSelectedYearIntoView();
404
+ }
405
+ if (changedProperties.has('focusedDate')) {
406
+ this.updateFocus();
407
+ }
408
+ }
409
+ updateFocus() {
410
+ if (!this.focusedDate)
411
+ return;
412
+ // Find the button for the focused date and set focus
413
+ const dateString = this.focusedDate.toISOString().split('T')[0];
414
+ const focusedButton = this.shadowRoot?.querySelector(`[data-date="${dateString}"]`);
415
+ if (focusedButton && !focusedButton.hasAttribute('disabled')) {
416
+ // Use requestAnimationFrame to ensure DOM is updated
417
+ requestAnimationFrame(() => {
418
+ focusedButton.focus();
419
+ });
313
420
  }
314
421
  }
315
- _scrollSelectedYearIntoView() {
422
+ scrollSelectedYearIntoView() {
316
423
  // Wait for next frame to ensure DOM is updated
317
424
  requestAnimationFrame(() => {
318
425
  const selectedYearButton = this.shadowRoot?.querySelector('.year-option.selected');
@@ -324,139 +431,273 @@ let UiDatePickerCalendar = (() => {
324
431
  }
325
432
  });
326
433
  }
327
- willUpdate(changedProperties) {
328
- if (changedProperties.has('year') ||
329
- changedProperties.has('month') ||
330
- changedProperties.has('selectedDate') ||
331
- changedProperties.has('selectedRange') ||
332
- changedProperties.has('disabledDates') ||
333
- changedProperties.has('locale')) {
334
- this._updateCalendar();
434
+ updateFocusedDateForMonthChange() {
435
+ if (!this.focusedDate) {
436
+ this.initializeFocusedDate();
437
+ return;
335
438
  }
336
- if (changedProperties.has('locale')) {
337
- this._updateMonthNames();
439
+ // Keep the same day of month if possible
440
+ const targetDay = this.focusedDate.getDate();
441
+ const newDate = new Date(this.year, this.month, targetDay);
442
+ // Check if the target date exists in the new month and is not disabled
443
+ if (newDate.getMonth() === this.month && !this.isDateDisabled(newDate)) {
444
+ this.focusedDate = newDate;
445
+ }
446
+ else {
447
+ // Find the closest available date
448
+ this.focusedDate = this.findFirstAvailableDate();
338
449
  }
339
450
  }
340
- _updateCalendar() {
341
- this._calendarData = generateCalendarMonth(this.year, this.month, this.selectedDate, this.selectedRange, this.disabledDates, this.locale);
451
+ updateCalendar() {
452
+ const selectedRange = this.rangeStart || this.rangeEnd ? { start: this.rangeStart, end: this.rangeEnd } : null;
453
+ this.calendarData = generateCalendarMonth(this.year, this.month, this.selectedDate, selectedRange, this.disabledDates, this.locale);
342
454
  }
343
- _updateMonthNames() {
344
- this._monthNames = getMonthNames(this.locale);
455
+ updateMonthNames() {
456
+ this.monthNames = getMonthNames(this.locale);
345
457
  }
346
- _navigateMonth(delta) {
458
+ navigateMonth(delta) {
347
459
  const newDate = addMonths(new Date(this.year, this.month), delta);
348
460
  this.year = newDate.getFullYear();
349
461
  this.month = newDate.getMonth();
350
462
  }
351
- _handlePrevMonth() {
352
- this._navigateMonth(-1);
463
+ handlePrevMonth() {
464
+ this.navigateMonth(-1);
353
465
  }
354
- _handleNextMonth() {
355
- this._navigateMonth(1);
466
+ handleNextMonth() {
467
+ this.navigateMonth(1);
356
468
  }
357
- _handlePrevYear() {
469
+ handlePrevYear() {
358
470
  this.year = this.year - 1;
359
471
  }
360
- _handleNextYear() {
472
+ handleNextYear() {
361
473
  this.year = this.year + 1;
362
474
  }
363
- _handleMonthClick() {
364
- this._showMonthDropdown = !this._showMonthDropdown;
365
- this._showYearDropdown = false;
475
+ handleMonthClick() {
476
+ this.showMonthDropdown = !this.showMonthDropdown;
477
+ this.showYearDropdown = false;
366
478
  }
367
- _handleYearClick() {
368
- this._showYearDropdown = !this._showYearDropdown;
369
- this._showMonthDropdown = false;
479
+ handleYearClick() {
480
+ this.showYearDropdown = !this.showYearDropdown;
481
+ this.showMonthDropdown = false;
370
482
  }
371
- _handleMonthSelect(selectedMonth) {
483
+ handleMonthSelect(selectedMonth) {
372
484
  this.month = selectedMonth;
373
- this._showMonthDropdown = false;
485
+ this.showMonthDropdown = false;
374
486
  }
375
- _handleYearSelect(selectedYear) {
487
+ handleYearSelect(selectedYear) {
376
488
  this.year = selectedYear;
377
- this._showYearDropdown = false;
489
+ this.showYearDropdown = false;
490
+ }
491
+ closeDropdowns() {
492
+ this.showMonthDropdown = false;
493
+ this.showYearDropdown = false;
494
+ }
495
+ navigateDate(delta) {
496
+ if (!this.focusedDate)
497
+ return;
498
+ const newDate = addDays(this.focusedDate, delta);
499
+ // Check if new date is in current month or if we should navigate to next/previous month
500
+ if (newDate.getMonth() !== this.month || newDate.getFullYear() !== this.year) {
501
+ // Navigate to next/previous month
502
+ if (delta > 0) {
503
+ this.navigateMonth(1);
504
+ }
505
+ else {
506
+ this.navigateMonth(-1);
507
+ }
508
+ // Set focused date to the target date in the new month
509
+ this.focusedDate = newDate;
510
+ return;
511
+ }
512
+ // Check if new date is disabled
513
+ if (this.isDateDisabled(newDate)) {
514
+ // Try to find next available date
515
+ const availableDate = this.findNextAvailableDate(newDate, delta > 0);
516
+ if (availableDate) {
517
+ this.focusedDate = availableDate;
518
+ }
519
+ }
520
+ else {
521
+ this.focusedDate = newDate;
522
+ }
378
523
  }
379
- _closeDropdowns() {
380
- this._showMonthDropdown = false;
381
- this._showYearDropdown = false;
524
+ findNextAvailableDate(startDate, forward) {
525
+ const direction = forward ? 1 : -1;
526
+ for (let i = 1; i <= 31; i++) {
527
+ const date = addDays(startDate, i * direction);
528
+ if (date.getMonth() !== this.month)
529
+ break; // Out of current month
530
+ if (!this.isDateDisabled(date)) {
531
+ return date;
532
+ }
533
+ }
534
+ return null;
382
535
  }
383
- _handleDocumentKeyDown(event) {
384
- if (event.key === 'Escape') {
385
- this._closeDropdowns();
536
+ focusFirstDayOfMonth() {
537
+ const firstDay = new Date(this.year, this.month, 1);
538
+ if (!this.isDateDisabled(firstDay)) {
539
+ this.focusedDate = firstDay;
540
+ }
541
+ else {
542
+ this.focusedDate = this.findFirstAvailableDate();
543
+ }
544
+ }
545
+ focusLastDayOfMonth() {
546
+ const lastDay = new Date(this.year, this.month + 1, 0);
547
+ if (!this.isDateDisabled(lastDay)) {
548
+ this.focusedDate = lastDay;
549
+ }
550
+ else {
551
+ // Find last available date in month
552
+ for (let i = lastDay.getDate(); i >= 1; i--) {
553
+ const date = new Date(this.year, this.month, i);
554
+ if (!this.isDateDisabled(date)) {
555
+ this.focusedDate = date;
556
+ break;
557
+ }
558
+ }
386
559
  }
387
560
  }
388
- _handleDayClick(day) {
561
+ selectFocusedDate() {
562
+ if (!this.focusedDate || this.isDateDisabled(this.focusedDate))
563
+ return;
564
+ if (this.rangeSelection) {
565
+ this.handleRangeSelection(this.focusedDate);
566
+ }
567
+ else {
568
+ this.handleSingleSelection(this.focusedDate);
569
+ }
570
+ }
571
+ handleDayClick(day) {
389
572
  if (day.isDisabled)
390
573
  return;
574
+ // Update focused date to clicked date
575
+ this.focusedDate = day.date;
391
576
  if (this.rangeSelection) {
392
- this._handleRangeSelection(day.date);
577
+ this.handleRangeSelection(day.date);
393
578
  }
394
579
  else {
395
- this._handleSingleSelection(day.date);
580
+ this.handleSingleSelection(day.date);
396
581
  }
397
582
  }
398
- _handleSingleSelection(date) {
583
+ handleSingleSelection(date) {
399
584
  if (this.showActions) {
400
585
  // Use pending state when actions are enabled
401
- this._pendingDate = date;
586
+ this.pendingDate = date;
402
587
  }
403
588
  else {
404
589
  // Immediate selection when no actions
405
590
  this.selectedDate = date;
406
- const event = {
407
- date,
408
- formattedDate: formatDate(date, this.locale),
409
- };
410
- this.dispatchEvent(new CustomEvent('date-select', {
411
- detail: event,
412
- bubbles: true,
413
- composed: true,
414
- }));
415
- }
416
- }
417
- _handleRangeSelection(date) {
418
- if (!this._rangeStart || (this.selectedRange?.start && this.selectedRange?.end)) {
419
- // Start new range
420
- this._rangeStart = date;
421
- const newRange = { start: date, end: null };
422
- if (this.showActions) {
423
- // Use pending state when actions are enabled
424
- this._pendingRange = newRange;
425
- }
426
- else {
427
- this.selectedRange = newRange;
591
+ this.dispatchDateEvent(date);
592
+ }
593
+ }
594
+ handleRangeSelection(date) {
595
+ const isImmediate = !this.showActions;
596
+ const { start, end } = this.getCurrentRange();
597
+ // If we have a complete range, start a new one
598
+ if (start && end) {
599
+ this.setRangeValues(date, null, isImmediate);
600
+ return;
601
+ }
602
+ // If we have a start but no end, complete the range
603
+ if (start && !end) {
604
+ const sortedRange = start <= date ? { start, end: date } : { start: date, end: start };
605
+ this.setRangeValues(sortedRange.start, sortedRange.end, isImmediate);
606
+ if (isImmediate) {
607
+ this.dispatchRangeEvent(sortedRange);
428
608
  }
609
+ return;
610
+ }
611
+ // Start new range
612
+ this.setRangeValues(date, null, isImmediate);
613
+ }
614
+ /**
615
+ * Helper to get the current range state (either immediate or pending)
616
+ */
617
+ getCurrentRange() {
618
+ const isImmediate = !this.showActions;
619
+ return {
620
+ start: isImmediate ? this.rangeStart : this.pendingRangeStart,
621
+ end: isImmediate ? this.rangeEnd : this.pendingRangeEnd,
622
+ };
623
+ }
624
+ /**
625
+ * Helper to check if we have a complete range in the current mode
626
+ */
627
+ hasCompleteRange() {
628
+ const { start, end } = this.getCurrentRange();
629
+ return !!(start && end);
630
+ }
631
+ setRangeValues(start, end, isImmediate) {
632
+ if (isImmediate) {
633
+ this.rangeStart = start;
634
+ this.rangeEnd = end;
429
635
  }
430
636
  else {
431
- // Complete range
432
- const start = this._rangeStart;
433
- const end = date;
434
- // Ensure start is before end
435
- const sortedRange = start <= end ? { start, end } : { start: end, end: start };
436
- this._rangeStart = undefined;
437
- if (this.showActions) {
438
- // Use pending state when actions are enabled
439
- this._pendingRange = sortedRange;
440
- }
441
- else {
442
- // Immediate selection when no actions
443
- this.selectedRange = sortedRange;
444
- const event = {
445
- range: sortedRange,
446
- formattedRange: {
447
- start: sortedRange.start ? formatDate(sortedRange.start, this.locale) : null,
448
- end: sortedRange.end ? formatDate(sortedRange.end, this.locale) : null,
449
- },
450
- };
451
- this.dispatchEvent(new CustomEvent('date-range-select', {
452
- detail: event,
453
- bubbles: true,
454
- composed: true,
455
- }));
456
- }
637
+ this.pendingRangeStart = start;
638
+ this.pendingRangeEnd = end;
457
639
  }
458
640
  }
459
- _isDateDisabled(date) {
641
+ /**
642
+ * Helper to dispatch date selection events
643
+ */
644
+ dispatchDateEvent(date) {
645
+ const event = {
646
+ date,
647
+ formattedDate: formatDate(date, this.locale),
648
+ };
649
+ this.dispatchEvent(new CustomEvent('date-select', {
650
+ detail: event,
651
+ bubbles: true,
652
+ composed: true,
653
+ }));
654
+ }
655
+ /**
656
+ * Helper to dispatch range selection events
657
+ */
658
+ dispatchRangeEvent(range) {
659
+ const event = {
660
+ range,
661
+ formattedRange: {
662
+ start: formatDate(range.start, this.locale),
663
+ end: formatDate(range.end, this.locale),
664
+ },
665
+ };
666
+ this.dispatchEvent(new CustomEvent('date-range-select', {
667
+ detail: event,
668
+ bubbles: true,
669
+ composed: true,
670
+ }));
671
+ }
672
+ /**
673
+ * Helper to dispatch range confirmation events
674
+ */
675
+ dispatchRangeConfirmEvent(range) {
676
+ const event = {
677
+ range,
678
+ formattedRange: {
679
+ start: range?.start ? formatDate(range.start, this.locale) : null,
680
+ end: range?.end ? formatDate(range.end, this.locale) : null,
681
+ },
682
+ };
683
+ this.dispatchEvent(new CustomEvent('date-range-confirm', {
684
+ detail: event,
685
+ bubbles: true,
686
+ composed: true,
687
+ }));
688
+ }
689
+ /**
690
+ * Helper to dispatch cancel events
691
+ */
692
+ dispatchCancelEvent(reason = 'user_cancelled') {
693
+ const event = { reason };
694
+ this.dispatchEvent(new CustomEvent('date-cancel', {
695
+ detail: event,
696
+ bubbles: true,
697
+ composed: true,
698
+ }));
699
+ }
700
+ isDateDisabled(date) {
460
701
  if (this.minDate && date < this.minDate)
461
702
  return true;
462
703
  if (this.maxDate && date > this.maxDate)
@@ -465,249 +706,223 @@ let UiDatePickerCalendar = (() => {
465
706
  return true;
466
707
  return false;
467
708
  }
468
- _handleConfirm() {
709
+ handleConfirm() {
469
710
  if (this.rangeSelection) {
470
- if (this._pendingRange) {
471
- this.selectedRange = this._pendingRange;
472
- const event = {
473
- range: this._pendingRange,
474
- formattedRange: {
475
- start: this._pendingRange.start ? formatDate(this._pendingRange.start, this.locale) : null,
476
- end: this._pendingRange.end ? formatDate(this._pendingRange.end, this.locale) : null,
477
- },
478
- };
479
- this.dispatchEvent(new CustomEvent('date-range-confirm', {
480
- detail: event,
481
- bubbles: true,
482
- composed: true,
483
- }));
711
+ if (this.pendingRangeStart || this.pendingRangeEnd) {
712
+ this.rangeStart = this.pendingRangeStart;
713
+ this.rangeEnd = this.pendingRangeEnd;
714
+ const range = this.rangeStart || this.rangeEnd ? { start: this.rangeStart, end: this.rangeEnd } : null;
715
+ // Reset pending state after confirmation
716
+ this.pendingRangeStart = null;
717
+ this.pendingRangeEnd = null;
718
+ this.dispatchRangeConfirmEvent(range);
484
719
  }
485
720
  }
486
721
  else {
487
- if (this._pendingDate) {
488
- this.selectedDate = this._pendingDate;
489
- const event = {
490
- date: this._pendingDate,
491
- formattedDate: formatDate(this._pendingDate, this.locale),
492
- };
493
- this.dispatchEvent(new CustomEvent('date-select', {
494
- detail: event,
495
- bubbles: true,
496
- composed: true,
497
- }));
722
+ if (this.pendingDate) {
723
+ this.selectedDate = this.pendingDate;
724
+ // Reset pending state after confirmation
725
+ this.pendingDate = null;
726
+ this.dispatchDateEvent(this.selectedDate);
498
727
  }
499
728
  }
500
729
  }
501
- _handleCancel() {
730
+ handleCancel() {
502
731
  // Reset pending state
503
- this._pendingDate = null;
504
- this._pendingRange = null;
505
- this._rangeStart = undefined;
506
- const event = {
507
- reason: 'user_cancelled',
508
- };
509
- this.dispatchEvent(new CustomEvent('date-cancel', {
510
- detail: event,
511
- bubbles: true,
512
- composed: true,
513
- }));
732
+ this.pendingDate = null;
733
+ this.pendingRangeStart = null;
734
+ this.pendingRangeEnd = null;
735
+ this.dispatchCancelEvent();
736
+ }
737
+ /**
738
+ * Helper to render navigation buttons
739
+ */
740
+ renderNavButton(direction, onClick, ariaLabel, icon) {
741
+ if (!this.showNavigation || this.showMonthDropdown || this.showYearDropdown) {
742
+ return nothing;
743
+ }
744
+ return html `<ui-icon-button
745
+ class="nav-button"
746
+ size="xs"
747
+ @click=${onClick}
748
+ aria-label=${ariaLabel}
749
+ title=${ariaLabel}
750
+ >
751
+ <ui-icon icon=${icon}></ui-icon>
752
+ </ui-icon-button>`;
514
753
  }
515
- _renderNavigation() {
516
- const monthName = this._monthNames[this.month] || '';
754
+ renderNavigation() {
755
+ const monthName = this.monthNames[this.month] || '';
517
756
  return html `
518
757
  <div class="header">
519
758
  <div class="month-year">
520
759
  <div class="month-selector">
521
- ${this.showNavigation && !this._showMonthDropdown && !this._showYearDropdown
522
- ? html `
523
- <ui-icon-button
524
- class="nav-button month-nav"
525
- size="xs"
526
- @click=${this._handlePrevMonth}
527
- aria-label="Previous month"
528
- title="Previous month"
529
- >
530
- <ui-icon icon="chevronLeft"></ui-icon>
531
- </ui-icon-button>
532
- `
533
- : ''}
760
+ ${this.renderNavButton('prev', this.handlePrevMonth, 'Previous month', 'chevronLeft')}
534
761
  <ui-button
535
762
  class="month-button"
536
763
  color="text"
537
764
  size="xs"
538
- @click=${this._handleMonthClick}
765
+ @click=${this.handleMonthClick}
539
766
  aria-label="Select month"
540
- aria-expanded=${this._showMonthDropdown}
767
+ aria-expanded=${this.showMonthDropdown}
541
768
  trailingIcon
542
769
  >
543
770
  ${monthName}
544
771
  <ui-icon icon="arrowDropDown" slot="icon"></ui-icon>
545
772
  </ui-button>
546
- ${this.showNavigation && !this._showMonthDropdown && !this._showYearDropdown
547
- ? html `
548
- <ui-icon-button
549
- class="nav-button month-nav"
550
- size="xs"
551
- @click=${this._handleNextMonth}
552
- aria-label="Next month"
553
- title="Next month"
554
- >
555
- <ui-icon icon="chevronRight"></ui-icon>
556
- </ui-icon-button>
557
- `
558
- : ''}
773
+ ${this.renderNavButton('next', this.handleNextMonth, 'Next month', 'chevronRight')}
559
774
  </div>
560
775
  <div class="year-selector">
561
- ${this.showNavigation && !this._showMonthDropdown && !this._showYearDropdown
562
- ? html `
563
- <ui-icon-button
564
- class="nav-button year-nav"
565
- size="xs"
566
- @click=${this._handlePrevYear}
567
- aria-label="Previous year"
568
- title="Previous year"
569
- >
570
- <ui-icon icon="chevronLeft"></ui-icon>
571
- </ui-icon-button>
572
- `
573
- : ''}
776
+ ${this.renderNavButton('prev', this.handlePrevYear, 'Previous year', 'chevronLeft')}
574
777
  <ui-button
575
778
  class="year-button"
576
779
  color="text"
577
780
  size="xs"
578
- @click=${this._handleYearClick}
781
+ @click=${this.handleYearClick}
579
782
  aria-label="Select year"
580
- aria-expanded=${this._showYearDropdown}
783
+ aria-expanded=${this.showYearDropdown}
581
784
  trailingIcon
582
785
  >
583
786
  ${this.year}
584
787
  <ui-icon icon="arrowDropDown" slot="icon"></ui-icon>
585
788
  </ui-button>
586
- ${this.showNavigation && !this._showMonthDropdown && !this._showYearDropdown
587
- ? html `
588
- <ui-icon-button
589
- class="nav-button year-nav"
590
- size="xs"
591
- @click=${this._handleNextYear}
592
- aria-label="Next year"
593
- title="Next year"
594
- >
595
- <ui-icon icon="chevronRight"></ui-icon>
596
- </ui-icon-button>
597
- `
598
- : ''}
789
+ ${this.renderNavButton('next', this.handleNextYear, 'Next year', 'chevronRight')}
599
790
  </div>
600
791
  </div>
601
792
  </div>
602
793
  `;
603
794
  }
604
- _renderWeekdays() {
605
- if (!this._calendarData)
795
+ renderWeekdays() {
796
+ if (!this.calendarData)
606
797
  return html ``;
607
798
  return html `
608
- <div class="weekdays">
609
- ${this._calendarData.weekdays.map((weekday) => html `<div class="weekday">${weekday}</div>`)}
799
+ <div class="weekdays" role="row">
800
+ ${this.calendarData.weekdays.map((weekday) => html `<div class="weekday" role="columnheader">${weekday}</div>`)}
610
801
  </div>
611
802
  `;
612
803
  }
613
- _renderDay(day) {
614
- const isPendingSelected = this.showActions && this._pendingDate && isSameDay(day.date, this._pendingDate);
615
- const isPendingRangeStart = this.showActions && this._pendingRange?.start && isSameDay(day.date, this._pendingRange.start);
616
- const isPendingRangeEnd = this.showActions && this._pendingRange?.end && isSameDay(day.date, this._pendingRange.end);
617
- const isPendingInRange = this.showActions &&
618
- this._pendingRange?.start &&
619
- this._pendingRange?.end &&
620
- day.date >= this._pendingRange.start &&
621
- day.date <= this._pendingRange.end &&
622
- !isPendingRangeStart &&
623
- !isPendingRangeEnd;
624
- // Determine button color based on selection state
625
- let color = 'text';
804
+ /**
805
+ * Helper to determine day selection state for rendering
806
+ */
807
+ getDaySelectionState(day) {
626
808
  if (this.showActions) {
627
- if (isPendingRangeStart || isPendingRangeEnd || isPendingSelected) {
628
- color = 'filled';
629
- }
630
- else if (isPendingInRange) {
631
- color = 'text';
632
- }
809
+ // Use pending state
810
+ const isPendingSelected = this.pendingDate && isSameDay(day.date, this.pendingDate);
811
+ const isPendingRangeStart = this.pendingRangeStart && isSameDay(day.date, this.pendingRangeStart);
812
+ const isPendingRangeEnd = this.pendingRangeEnd && isSameDay(day.date, this.pendingRangeEnd);
813
+ const isPendingInRange = this.pendingRangeStart &&
814
+ this.pendingRangeEnd &&
815
+ day.date >= this.pendingRangeStart &&
816
+ day.date <= this.pendingRangeEnd &&
817
+ !isPendingRangeStart &&
818
+ !isPendingRangeEnd;
819
+ return {
820
+ isSelected: !!isPendingSelected,
821
+ isRangeStart: !!isPendingRangeStart,
822
+ isRangeEnd: !!isPendingRangeEnd,
823
+ isInRange: !!isPendingInRange,
824
+ };
633
825
  }
634
826
  else {
635
- if (day.isRangeStart || day.isRangeEnd || day.isSelected) {
636
- color = 'filled';
637
- }
638
- else if (day.isInRange) {
639
- color = 'text';
640
- }
827
+ // Use immediate state
828
+ return {
829
+ isSelected: day.isSelected,
830
+ isRangeStart: day.isRangeStart,
831
+ isRangeEnd: day.isRangeEnd,
832
+ isInRange: day.isInRange,
833
+ };
834
+ }
835
+ }
836
+ /**
837
+ * Helper to determine button color for a day
838
+ */
839
+ getDayButtonColor(day, selectionState) {
840
+ if (selectionState.isRangeStart || selectionState.isRangeEnd || selectionState.isSelected) {
841
+ return 'filled';
842
+ }
843
+ if (selectionState.isInRange) {
844
+ return 'text';
641
845
  }
642
- if (day.isToday && color === 'text') {
643
- color = 'outlined';
846
+ if (day.isToday) {
847
+ return 'outlined';
644
848
  }
849
+ return 'text';
850
+ }
851
+ renderDay(day) {
852
+ const selectionState = this.getDaySelectionState(day);
853
+ const color = this.getDayButtonColor(day, selectionState);
645
854
  const classes = {
646
855
  'day-cell': true,
647
856
  'other-month': !day.isCurrentMonth,
648
857
  'today': day.isToday,
649
- 'in-range': this.showActions ? !!isPendingInRange : day.isInRange,
650
- 'range-start': this.showActions ? !!isPendingRangeStart : day.isRangeStart,
651
- 'range-end': this.showActions ? !!isPendingRangeEnd : day.isRangeEnd,
652
- 'has-complete-range': this.showActions
653
- ? !!(this._pendingRange?.start && this._pendingRange?.end)
654
- : !!(this.selectedRange?.start && this.selectedRange?.end),
858
+ 'in-range': selectionState.isInRange,
859
+ 'range-start': selectionState.isRangeStart,
860
+ 'range-end': selectionState.isRangeEnd,
861
+ 'has-complete-range': this.hasCompleteRange(),
655
862
  };
863
+ const isFocused = this.focusedDate && isSameDay(day.date, this.focusedDate);
656
864
  return html `
657
- <div class=${classMap(classes)}>
865
+ <div class=${classMap(classes)} role="gridcell">
658
866
  <ui-button
659
867
  class="day-button"
660
868
  color=${color}
661
869
  size="s"
662
870
  data-date=${day.date.toISOString().split('T')[0]}
663
- tabindex=${day.isToday && !(day.isDisabled || this._isDateDisabled(day.date)) ? '0' : '-1'}
871
+ tabindex=${isFocused && !(day.isDisabled || this.isDateDisabled(day.date)) ? '0' : '-1'}
664
872
  aria-label=${formatDate(day.date, this.locale)}
665
- @click=${() => this._handleDayClick(day)}
666
- ?disabled=${day.isDisabled || this._isDateDisabled(day.date)}
873
+ aria-selected=${selectionState.isSelected || selectionState.isRangeStart || selectionState.isRangeEnd}
874
+ @click=${() => this.handleDayClick(day)}
875
+ ?disabled=${day.isDisabled || this.isDateDisabled(day.date)}
667
876
  >
668
877
  ${day.date.getDate()}
669
878
  </ui-button>
670
879
  </div>
671
880
  `;
672
881
  }
673
- _renderDays() {
674
- if (!this._calendarData)
882
+ renderDays() {
883
+ if (!this.calendarData)
675
884
  return html ``;
676
- return html `<div class="days">${this._calendarData.days.map((day) => this._renderDay(day))}</div>`;
885
+ return html `<div class="days">${this.calendarData.days.map((day) => this.renderDay(day))}</div>`;
677
886
  }
678
- _renderActions() {
887
+ renderActions() {
679
888
  if (!this.showActions)
680
889
  return nothing;
681
- const hasSelection = this.rangeSelection ? this._pendingRange?.start : this._pendingDate;
890
+ const hasSelection = this.rangeSelection ? this.pendingRangeStart : this.pendingDate;
682
891
  return html `
683
892
  <div class="actions">
684
- <ui-button size="s" color="text" @click=${this._handleCancel}>${this.cancelButtonText}</ui-button>
685
- <ui-button size="s" color="text" @click=${this._handleConfirm} ?disabled=${!hasSelection}>
893
+ <ui-button size="s" color="text" @click=${this.handleCancel}>${this.cancelButtonText}</ui-button>
894
+ <ui-button size="s" color="text" @click=${this.handleConfirm} ?disabled=${!hasSelection}>
686
895
  ${this.okButtonText}
687
896
  </ui-button>
688
897
  </div>
689
898
  `;
690
899
  }
691
- _renderMonthDropdown() {
900
+ /**
901
+ * Helper to render dropdown option buttons
902
+ */
903
+ renderDropdownOption(value, label, isSelected, onClick, className = '') {
904
+ return html `
905
+ <ui-button
906
+ class="dropdown-option ${className} ${isSelected ? 'selected' : ''}"
907
+ color=${isSelected ? 'filled' : 'text'}
908
+ size="s"
909
+ @click=${onClick}
910
+ aria-label=${label}
911
+ >
912
+ ${label}
913
+ </ui-button>
914
+ `;
915
+ }
916
+ renderMonthDropdown() {
692
917
  return html `
693
918
  <div class="dropdown-view">
694
919
  <div class="month-list">
695
- ${this._monthNames.map((monthName, index) => html `
696
- <ui-button
697
- class="month-option ${index === this.month ? 'selected' : ''}"
698
- color=${index === this.month ? 'filled' : 'text'}
699
- size="s"
700
- @click=${() => this._handleMonthSelect(index)}
701
- aria-label=${monthName}
702
- >
703
- ${monthName}
704
- </ui-button>
705
- `)}
920
+ ${this.monthNames.map((monthName, index) => this.renderDropdownOption(index, monthName, index === this.month, () => this.handleMonthSelect(index)))}
706
921
  </div>
707
922
  </div>
708
923
  `;
709
924
  }
710
- _renderYearDropdown() {
925
+ renderYearDropdown() {
711
926
  const currentYear = this.year;
712
927
  const startYear = currentYear - 50;
713
928
  const endYear = currentYear + 50;
@@ -718,47 +933,124 @@ let UiDatePickerCalendar = (() => {
718
933
  return html `
719
934
  <div class="dropdown-view">
720
935
  <div class="year-grid">
721
- ${years.map((year) => html `
722
- <ui-button
723
- class="year-option ${year === this.year ? 'selected' : ''}"
724
- color=${year === this.year ? 'filled' : 'text'}
725
- size="s"
726
- @click=${() => this._handleYearSelect(year)}
727
- aria-label=${year.toString()}
728
- >
729
- ${year}
730
- </ui-button>
731
- `)}
936
+ ${years.map((year) => this.renderDropdownOption(year, year.toString(), year === this.year, () => this.handleYearSelect(year), 'year-option'))}
732
937
  </div>
733
938
  </div>
734
939
  `;
735
940
  }
941
+ findFirstAvailableDate() {
942
+ const firstDayOfMonth = new Date(this.year, this.month, 1);
943
+ for (let i = 0; i < 31; i++) {
944
+ const date = addDays(firstDayOfMonth, i);
945
+ if (date.getMonth() !== this.month)
946
+ break; // Next month
947
+ if (!this.isDateDisabled(date)) {
948
+ return date;
949
+ }
950
+ }
951
+ return firstDayOfMonth; // Fallback
952
+ }
953
+ handleKeyDown(event) {
954
+ // Prevent default behavior for navigation keys
955
+ const navigationKeys = ['ArrowLeft', 'ArrowRight', 'ArrowUp', 'ArrowDown', 'Home', 'End', 'PageUp', 'PageDown', ' '];
956
+ if (navigationKeys.includes(event.key)) {
957
+ event.preventDefault();
958
+ }
959
+ switch (event.key) {
960
+ case 'ArrowLeft':
961
+ this.navigateDate(-1);
962
+ break;
963
+ case 'ArrowRight':
964
+ this.navigateDate(1);
965
+ break;
966
+ case 'ArrowUp':
967
+ this.navigateDate(-7);
968
+ break;
969
+ case 'ArrowDown':
970
+ this.navigateDate(7);
971
+ break;
972
+ case 'Enter':
973
+ case ' ':
974
+ this.selectFocusedDate();
975
+ break;
976
+ case 'Home':
977
+ this.focusFirstDayOfMonth();
978
+ break;
979
+ case 'End':
980
+ this.focusLastDayOfMonth();
981
+ break;
982
+ case 'PageUp':
983
+ if (event.shiftKey) {
984
+ this.handlePrevYear();
985
+ }
986
+ else {
987
+ this.navigateMonth(-1);
988
+ }
989
+ break;
990
+ case 'PageDown':
991
+ if (event.shiftKey) {
992
+ this.handleNextYear();
993
+ }
994
+ else {
995
+ this.navigateMonth(1);
996
+ }
997
+ break;
998
+ case 'Escape':
999
+ this.closeDropdowns();
1000
+ break;
1001
+ }
1002
+ }
1003
+ /**
1004
+ * Helper to check if a date is in the current month and not disabled
1005
+ */
1006
+ isDateAvailable(date) {
1007
+ return date.getMonth() === this.month && date.getFullYear() === this.year && !this.isDateDisabled(date);
1008
+ }
1009
+ initializeFocusedDate() {
1010
+ // Priority: selectedDate, rangeStart, today, first available date
1011
+ const candidates = [this.selectedDate, this.rangeStart, new Date()].filter(Boolean);
1012
+ for (const candidate of candidates) {
1013
+ if (this.isDateAvailable(candidate)) {
1014
+ this.focusedDate = candidate;
1015
+ return;
1016
+ }
1017
+ }
1018
+ // Fallback to first available date in current month
1019
+ this.focusedDate = this.findFirstAvailableDate();
1020
+ }
736
1021
  render() {
737
1022
  // Show dropdown views instead of calendar when dropdowns are open
738
- if (this._showMonthDropdown) {
1023
+ if (this.showMonthDropdown) {
739
1024
  return html `
740
- <div class="calendar" role="grid" aria-label="Calendar">
741
- ${this._renderNavigation()} ${this._renderMonthDropdown()}
1025
+ <div class="calendar" role="grid" aria-label="Month selection for ${this.year}">
1026
+ ${this.renderNavigation()} ${this.renderMonthDropdown()}
742
1027
  </div>
743
1028
  `;
744
1029
  }
745
- if (this._showYearDropdown) {
1030
+ if (this.showYearDropdown) {
746
1031
  return html `
747
- <div class="calendar" role="grid" aria-label="Calendar">
748
- ${this._renderNavigation()} ${this._renderYearDropdown()}
1032
+ <div class="calendar" role="grid" aria-label="Year selection">
1033
+ ${this.renderNavigation()} ${this.renderYearDropdown()}
749
1034
  </div>
750
1035
  `;
751
1036
  }
752
1037
  // Default calendar view
753
1038
  return html `
754
- <div class="calendar" role="grid" aria-label="Calendar">
755
- ${this._renderNavigation()} ${this._renderWeekdays()} ${this._renderDays()} ${this._renderActions()}
1039
+ <div
1040
+ class="calendar"
1041
+ role="grid"
1042
+ aria-label="Calendar for ${this.monthNames[this.month]} ${this.year}"
1043
+ aria-roledescription="Calendar grid"
1044
+ tabindex="0"
1045
+ @keydown=${this.handleKeyDown}
1046
+ >
1047
+ ${this.renderNavigation()} ${this.renderWeekdays()} ${this.renderDays()} ${this.renderActions()}
756
1048
  </div>
757
1049
  `;
758
1050
  }
759
1051
  constructor() {
760
1052
  super(...arguments);
761
- __runInitializers(this, __pendingRange_extraInitializers);
1053
+ __runInitializers(this, _focusedDate_extraInitializers);
762
1054
  }
763
1055
  static {
764
1056
  __runInitializers(_classThis, _classExtraInitializers);