@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.
- package/.cursor/rules/html-and-css-best-practices.mdc +63 -0
- package/.cursor/rules/lit-best-practices.mdc +78 -0
- package/.github/instructions/html-and-css-best-practices.instructions.md +70 -0
- package/.github/instructions/lit-best-practices.instructions.md +86 -0
- package/build/src/elements/currency/currency-picker.d.ts +10 -0
- package/build/src/elements/currency/currency-picker.d.ts.map +1 -0
- package/build/src/elements/currency/currency-picker.js +27 -0
- package/build/src/elements/currency/currency-picker.js.map +1 -0
- package/build/src/elements/currency/internals/Picker.d.ts +311 -0
- package/build/src/elements/currency/internals/Picker.d.ts.map +1 -0
- package/build/src/elements/currency/internals/Picker.js +857 -0
- package/build/src/elements/currency/internals/Picker.js.map +1 -0
- package/build/src/elements/currency/internals/Picker.styles.d.ts +3 -0
- package/build/src/elements/currency/internals/Picker.styles.d.ts.map +1 -0
- package/build/src/elements/currency/internals/Picker.styles.js +58 -0
- package/build/src/elements/currency/internals/Picker.styles.js.map +1 -0
- package/build/src/elements/mention-textarea/internals/MentionTextArea.d.ts +216 -0
- package/build/src/elements/mention-textarea/internals/MentionTextArea.d.ts.map +1 -0
- package/build/src/elements/mention-textarea/internals/MentionTextArea.js +1037 -0
- package/build/src/elements/mention-textarea/internals/MentionTextArea.js.map +1 -0
- package/build/src/elements/mention-textarea/internals/MentionTextArea.styles.d.ts +3 -0
- package/build/src/elements/mention-textarea/internals/MentionTextArea.styles.d.ts.map +1 -0
- package/build/src/elements/mention-textarea/internals/MentionTextArea.styles.js +274 -0
- package/build/src/elements/mention-textarea/internals/MentionTextArea.styles.js.map +1 -0
- package/build/src/elements/mention-textarea/ui-mention-textarea.d.ts +13 -0
- package/build/src/elements/mention-textarea/ui-mention-textarea.d.ts.map +1 -0
- package/build/src/elements/mention-textarea/ui-mention-textarea.js +28 -0
- package/build/src/elements/mention-textarea/ui-mention-textarea.js.map +1 -0
- package/build/src/md/button/internals/base.d.ts +1 -0
- package/build/src/md/button/internals/base.d.ts.map +1 -1
- package/build/src/md/button/internals/base.js +7 -0
- package/build/src/md/button/internals/base.js.map +1 -1
- package/build/src/md/chip/internals/Chip.styles.d.ts.map +1 -1
- package/build/src/md/chip/internals/Chip.styles.js +2 -0
- package/build/src/md/chip/internals/Chip.styles.js.map +1 -1
- package/build/src/md/date-picker/internals/DatePicker.styles.d.ts.map +1 -1
- package/build/src/md/date-picker/internals/DatePicker.styles.js +73 -0
- package/build/src/md/date-picker/internals/DatePicker.styles.js.map +1 -1
- package/build/src/md/date-picker/internals/DatePickerCalendar.d.ts +164 -51
- package/build/src/md/date-picker/internals/DatePickerCalendar.d.ts.map +1 -1
- package/build/src/md/date-picker/internals/DatePickerCalendar.js +660 -368
- package/build/src/md/date-picker/internals/DatePickerCalendar.js.map +1 -1
- package/build/src/md/date-picker/ui-date-picker-input.d.ts +65 -13
- package/build/src/md/date-picker/ui-date-picker-input.d.ts.map +1 -1
- package/build/src/md/date-picker/ui-date-picker-input.js +143 -76
- package/build/src/md/date-picker/ui-date-picker-input.js.map +1 -1
- package/build/src/md/date-picker/ui-date-picker-modal-input.d.ts +76 -17
- package/build/src/md/date-picker/ui-date-picker-modal-input.d.ts.map +1 -1
- package/build/src/md/date-picker/ui-date-picker-modal-input.js +192 -127
- package/build/src/md/date-picker/ui-date-picker-modal-input.js.map +1 -1
- package/build/src/md/date-picker/ui-date-picker-modal.d.ts +63 -15
- package/build/src/md/date-picker/ui-date-picker-modal.d.ts.map +1 -1
- package/build/src/md/date-picker/ui-date-picker-modal.js +143 -64
- package/build/src/md/date-picker/ui-date-picker-modal.js.map +1 -1
- package/demo/elements/currency/index.html +91 -0
- package/demo/elements/currency/index.ts +272 -0
- package/demo/elements/index.html +6 -0
- package/demo/elements/mention-textarea/index.html +19 -0
- package/demo/elements/mention-textarea/index.ts +205 -0
- package/demo/md/date-picker/date-picker.ts +138 -103
- package/package.json +2 -2
- package/src/elements/currency/currency-picker.ts +14 -0
- package/src/elements/currency/internals/Picker.styles.ts +58 -0
- package/src/elements/currency/internals/Picker.ts +846 -0
- package/src/elements/mention-textarea/internals/MentionTextArea.styles.ts +274 -0
- package/src/elements/mention-textarea/internals/MentionTextArea.ts +1036 -0
- package/src/elements/mention-textarea/ui-mention-textarea.ts +18 -0
- package/src/md/button/internals/base.ts +7 -0
- package/src/md/chip/internals/Chip.styles.ts +2 -0
- package/src/md/date-picker/internals/DatePicker.styles.ts +73 -0
- package/src/md/date-picker/internals/DatePickerCalendar.ts +643 -309
- package/src/md/date-picker/ui-date-picker-input.ts +110 -49
- package/src/md/date-picker/ui-date-picker-modal-input.ts +168 -99
- package/src/md/date-picker/ui-date-picker-modal.ts +136 -53
- package/test/README.md +3 -2
- package/test/elements/currency/CurrencyPicker.accessibility.test.ts +328 -0
- package/test/elements/currency/CurrencyPicker.core.test.ts +318 -0
- package/test/elements/currency/CurrencyPicker.integration.test.ts +482 -0
- package/test/elements/currency/CurrencyPicker.test.ts +486 -0
- package/test/elements/mention-textarea/MentionTextArea.basic.test.ts +63 -0
- package/test/elements/mention-textarea/MentionTextArea.test.ts +321 -0
- 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
|
-
*
|
|
32
|
-
* .
|
|
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
|
|
53
|
-
let
|
|
54
|
-
let
|
|
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
|
|
83
|
-
let
|
|
84
|
-
let
|
|
85
|
-
let
|
|
86
|
-
let
|
|
87
|
-
let
|
|
88
|
-
let
|
|
89
|
-
let
|
|
90
|
-
let
|
|
91
|
-
let
|
|
92
|
-
let
|
|
93
|
-
let
|
|
94
|
-
let
|
|
95
|
-
let
|
|
96
|
-
let
|
|
97
|
-
let
|
|
98
|
-
let
|
|
99
|
-
let
|
|
100
|
-
let
|
|
101
|
-
let
|
|
102
|
-
let
|
|
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
|
-
|
|
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
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
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,
|
|
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,
|
|
141
|
-
__esDecorate(this, null,
|
|
142
|
-
__esDecorate(this, null,
|
|
143
|
-
__esDecorate(this, null,
|
|
144
|
-
__esDecorate(this, null,
|
|
145
|
-
__esDecorate(this, null,
|
|
146
|
-
__esDecorate(this, null,
|
|
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
|
|
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
|
-
#
|
|
222
|
+
#rangeStart_accessor_storage = (__runInitializers(this, _selectedDate_extraInitializers), __runInitializers(this, _rangeStart_initializers, null
|
|
183
223
|
/**
|
|
184
|
-
*
|
|
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
|
|
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
|
|
191
|
-
set
|
|
192
|
-
#
|
|
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
|
-
*
|
|
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
|
-
*
|
|
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
|
|
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
|
|
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
|
-
#
|
|
279
|
-
get
|
|
280
|
-
set
|
|
281
|
-
#
|
|
282
|
-
get
|
|
283
|
-
set
|
|
284
|
-
#
|
|
285
|
-
get
|
|
286
|
-
set
|
|
287
|
-
#
|
|
288
|
-
get
|
|
289
|
-
set
|
|
290
|
-
#
|
|
291
|
-
get
|
|
292
|
-
set
|
|
293
|
-
#
|
|
294
|
-
get
|
|
295
|
-
set
|
|
296
|
-
#
|
|
297
|
-
get
|
|
298
|
-
set
|
|
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.
|
|
302
|
-
this.
|
|
303
|
-
|
|
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
|
-
|
|
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('
|
|
401
|
+
if (changedProperties.has('showYearDropdown') && this.showYearDropdown) {
|
|
311
402
|
// Scroll selected year into view
|
|
312
|
-
this.
|
|
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
|
-
|
|
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
|
-
|
|
328
|
-
if (
|
|
329
|
-
|
|
330
|
-
|
|
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
|
|
337
|
-
|
|
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
|
-
|
|
341
|
-
|
|
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
|
-
|
|
344
|
-
this.
|
|
455
|
+
updateMonthNames() {
|
|
456
|
+
this.monthNames = getMonthNames(this.locale);
|
|
345
457
|
}
|
|
346
|
-
|
|
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
|
-
|
|
352
|
-
this.
|
|
463
|
+
handlePrevMonth() {
|
|
464
|
+
this.navigateMonth(-1);
|
|
353
465
|
}
|
|
354
|
-
|
|
355
|
-
this.
|
|
466
|
+
handleNextMonth() {
|
|
467
|
+
this.navigateMonth(1);
|
|
356
468
|
}
|
|
357
|
-
|
|
469
|
+
handlePrevYear() {
|
|
358
470
|
this.year = this.year - 1;
|
|
359
471
|
}
|
|
360
|
-
|
|
472
|
+
handleNextYear() {
|
|
361
473
|
this.year = this.year + 1;
|
|
362
474
|
}
|
|
363
|
-
|
|
364
|
-
this.
|
|
365
|
-
this.
|
|
475
|
+
handleMonthClick() {
|
|
476
|
+
this.showMonthDropdown = !this.showMonthDropdown;
|
|
477
|
+
this.showYearDropdown = false;
|
|
366
478
|
}
|
|
367
|
-
|
|
368
|
-
this.
|
|
369
|
-
this.
|
|
479
|
+
handleYearClick() {
|
|
480
|
+
this.showYearDropdown = !this.showYearDropdown;
|
|
481
|
+
this.showMonthDropdown = false;
|
|
370
482
|
}
|
|
371
|
-
|
|
483
|
+
handleMonthSelect(selectedMonth) {
|
|
372
484
|
this.month = selectedMonth;
|
|
373
|
-
this.
|
|
485
|
+
this.showMonthDropdown = false;
|
|
374
486
|
}
|
|
375
|
-
|
|
487
|
+
handleYearSelect(selectedYear) {
|
|
376
488
|
this.year = selectedYear;
|
|
377
|
-
this.
|
|
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
|
-
|
|
380
|
-
|
|
381
|
-
|
|
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
|
-
|
|
384
|
-
|
|
385
|
-
|
|
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
|
-
|
|
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.
|
|
577
|
+
this.handleRangeSelection(day.date);
|
|
393
578
|
}
|
|
394
579
|
else {
|
|
395
|
-
this.
|
|
580
|
+
this.handleSingleSelection(day.date);
|
|
396
581
|
}
|
|
397
582
|
}
|
|
398
|
-
|
|
583
|
+
handleSingleSelection(date) {
|
|
399
584
|
if (this.showActions) {
|
|
400
585
|
// Use pending state when actions are enabled
|
|
401
|
-
this.
|
|
586
|
+
this.pendingDate = date;
|
|
402
587
|
}
|
|
403
588
|
else {
|
|
404
589
|
// Immediate selection when no actions
|
|
405
590
|
this.selectedDate = date;
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
if (
|
|
419
|
-
|
|
420
|
-
this.
|
|
421
|
-
|
|
422
|
-
|
|
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
|
-
|
|
432
|
-
|
|
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
|
-
|
|
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
|
-
|
|
709
|
+
handleConfirm() {
|
|
469
710
|
if (this.rangeSelection) {
|
|
470
|
-
if (this.
|
|
471
|
-
this.
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
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.
|
|
488
|
-
this.selectedDate = this.
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
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
|
-
|
|
730
|
+
handleCancel() {
|
|
502
731
|
// Reset pending state
|
|
503
|
-
this.
|
|
504
|
-
this.
|
|
505
|
-
this.
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
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
|
-
|
|
516
|
-
const monthName = this.
|
|
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.
|
|
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.
|
|
765
|
+
@click=${this.handleMonthClick}
|
|
539
766
|
aria-label="Select month"
|
|
540
|
-
aria-expanded=${this.
|
|
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.
|
|
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.
|
|
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.
|
|
781
|
+
@click=${this.handleYearClick}
|
|
579
782
|
aria-label="Select year"
|
|
580
|
-
aria-expanded=${this.
|
|
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.
|
|
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
|
-
|
|
605
|
-
if (!this.
|
|
795
|
+
renderWeekdays() {
|
|
796
|
+
if (!this.calendarData)
|
|
606
797
|
return html ``;
|
|
607
798
|
return html `
|
|
608
|
-
<div class="weekdays">
|
|
609
|
-
${this.
|
|
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
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
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
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
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
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
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
|
|
643
|
-
|
|
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':
|
|
650
|
-
'range-start':
|
|
651
|
-
'range-end':
|
|
652
|
-
'has-complete-range': this.
|
|
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=${
|
|
871
|
+
tabindex=${isFocused && !(day.isDisabled || this.isDateDisabled(day.date)) ? '0' : '-1'}
|
|
664
872
|
aria-label=${formatDate(day.date, this.locale)}
|
|
665
|
-
|
|
666
|
-
|
|
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
|
-
|
|
674
|
-
if (!this.
|
|
882
|
+
renderDays() {
|
|
883
|
+
if (!this.calendarData)
|
|
675
884
|
return html ``;
|
|
676
|
-
return html `<div class="days">${this.
|
|
885
|
+
return html `<div class="days">${this.calendarData.days.map((day) => this.renderDay(day))}</div>`;
|
|
677
886
|
}
|
|
678
|
-
|
|
887
|
+
renderActions() {
|
|
679
888
|
if (!this.showActions)
|
|
680
889
|
return nothing;
|
|
681
|
-
const hasSelection = this.rangeSelection ? this.
|
|
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.
|
|
685
|
-
<ui-button size="s" color="text" @click=${this.
|
|
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
|
-
|
|
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.
|
|
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
|
-
|
|
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) =>
|
|
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.
|
|
1023
|
+
if (this.showMonthDropdown) {
|
|
739
1024
|
return html `
|
|
740
|
-
<div class="calendar" role="grid" aria-label="
|
|
741
|
-
${this.
|
|
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.
|
|
1030
|
+
if (this.showYearDropdown) {
|
|
746
1031
|
return html `
|
|
747
|
-
<div class="calendar" role="grid" aria-label="
|
|
748
|
-
${this.
|
|
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
|
|
755
|
-
|
|
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,
|
|
1053
|
+
__runInitializers(this, _focusedDate_extraInitializers);
|
|
762
1054
|
}
|
|
763
1055
|
static {
|
|
764
1056
|
__runInitializers(_classThis, _classExtraInitializers);
|