@duskmoon-dev/el-datepicker 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,625 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __getOwnPropNames = Object.getOwnPropertyNames;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
5
+ var __moduleCache = /* @__PURE__ */ new WeakMap;
6
+ var __toCommonJS = (from) => {
7
+ var entry = __moduleCache.get(from), desc;
8
+ if (entry)
9
+ return entry;
10
+ entry = __defProp({}, "__esModule", { value: true });
11
+ if (from && typeof from === "object" || typeof from === "function")
12
+ __getOwnPropNames(from).map((key) => !__hasOwnProp.call(entry, key) && __defProp(entry, key, {
13
+ get: () => from[key],
14
+ enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
15
+ }));
16
+ __moduleCache.set(from, entry);
17
+ return entry;
18
+ };
19
+ var __export = (target, all) => {
20
+ for (var name in all)
21
+ __defProp(target, name, {
22
+ get: all[name],
23
+ enumerable: true,
24
+ configurable: true,
25
+ set: (newValue) => all[name] = () => newValue
26
+ });
27
+ };
28
+
29
+ // src/index.ts
30
+ var exports_src = {};
31
+ __export(exports_src, {
32
+ register: () => register,
33
+ ElDmDatepicker: () => ElDmDatepicker
34
+ });
35
+ module.exports = __toCommonJS(exports_src);
36
+
37
+ // src/el-dm-datepicker.ts
38
+ var import_el_core = require("@duskmoon-dev/el-core");
39
+ var import_datepicker = require("@duskmoon-dev/core/components/datepicker");
40
+ var strippedCss = import_datepicker.css.replace(/@layer\s+components\s*\{/, "").replace(/\}[\s]*$/, "");
41
+ var styles = import_el_core.css`
42
+ ${strippedCss}
43
+
44
+ :host {
45
+ display: block;
46
+ }
47
+
48
+ :host([hidden]) {
49
+ display: none;
50
+ }
51
+
52
+ .datepicker {
53
+ width: 100%;
54
+ }
55
+ `;
56
+ var WEEKDAYS = ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"];
57
+ var MONTHS = [
58
+ "January",
59
+ "February",
60
+ "March",
61
+ "April",
62
+ "May",
63
+ "June",
64
+ "July",
65
+ "August",
66
+ "September",
67
+ "October",
68
+ "November",
69
+ "December"
70
+ ];
71
+
72
+ class ElDmDatepicker extends import_el_core.BaseElement {
73
+ static properties = {
74
+ value: { type: String, reflect: true, default: "" },
75
+ disabled: { type: Boolean, reflect: true, default: false },
76
+ placeholder: { type: String, reflect: true, default: "Select date" },
77
+ format: { type: String, reflect: true, default: "YYYY-MM-DD" },
78
+ minDate: { type: String, reflect: true, default: "" },
79
+ maxDate: { type: String, reflect: true, default: "" },
80
+ range: { type: Boolean, reflect: true, default: false },
81
+ showTime: { type: Boolean, reflect: true, default: false },
82
+ size: { type: String, reflect: true, default: "md" }
83
+ };
84
+ value;
85
+ disabled;
86
+ placeholder;
87
+ format;
88
+ minDate;
89
+ maxDate;
90
+ range;
91
+ showTime;
92
+ size;
93
+ _isOpen = false;
94
+ _viewMode = "days";
95
+ _viewDate = new Date;
96
+ _selectedDate = null;
97
+ _rangeStart = null;
98
+ _rangeEnd = null;
99
+ _hours = 12;
100
+ _minutes = 0;
101
+ _period = "AM";
102
+ constructor() {
103
+ super();
104
+ this.attachStyles(styles);
105
+ }
106
+ connectedCallback() {
107
+ super.connectedCallback();
108
+ this._parseValue();
109
+ document.addEventListener("click", this._handleOutsideClick);
110
+ }
111
+ disconnectedCallback() {
112
+ super.disconnectedCallback();
113
+ document.removeEventListener("click", this._handleOutsideClick);
114
+ }
115
+ _handleOutsideClick = (e) => {
116
+ if (!this.contains(e.target)) {
117
+ this._close();
118
+ }
119
+ };
120
+ _parseValue() {
121
+ if (!this.value)
122
+ return;
123
+ if (this.range) {
124
+ const [start, end] = this.value.split(" - ");
125
+ if (start)
126
+ this._rangeStart = new Date(start);
127
+ if (end)
128
+ this._rangeEnd = new Date(end);
129
+ if (this._rangeStart) {
130
+ this._viewDate = new Date(this._rangeStart);
131
+ }
132
+ } else {
133
+ const date = new Date(this.value);
134
+ if (!isNaN(date.getTime())) {
135
+ this._selectedDate = date;
136
+ this._viewDate = new Date(date);
137
+ if (this.showTime) {
138
+ this._hours = date.getHours() % 12 || 12;
139
+ this._minutes = date.getMinutes();
140
+ this._period = date.getHours() >= 12 ? "PM" : "AM";
141
+ }
142
+ }
143
+ }
144
+ }
145
+ _open() {
146
+ if (this.disabled)
147
+ return;
148
+ this._isOpen = true;
149
+ this._viewMode = "days";
150
+ this.emit("open");
151
+ this.update();
152
+ }
153
+ _close() {
154
+ if (!this._isOpen)
155
+ return;
156
+ this._isOpen = false;
157
+ this.emit("close");
158
+ this.update();
159
+ }
160
+ _toggle() {
161
+ if (this._isOpen) {
162
+ this._close();
163
+ } else {
164
+ this._open();
165
+ }
166
+ }
167
+ _formatDate(date) {
168
+ if (!date)
169
+ return "";
170
+ const year = date.getFullYear();
171
+ const month = String(date.getMonth() + 1).padStart(2, "0");
172
+ const day = String(date.getDate()).padStart(2, "0");
173
+ let formatted = this.format.replace("YYYY", String(year)).replace("MM", month).replace("DD", day);
174
+ if (this.showTime) {
175
+ const hours = String(this._hours).padStart(2, "0");
176
+ const minutes = String(this._minutes).padStart(2, "0");
177
+ formatted += ` ${hours}:${minutes} ${this._period}`;
178
+ }
179
+ return formatted;
180
+ }
181
+ _getDisplayValue() {
182
+ if (this.range) {
183
+ if (this._rangeStart && this._rangeEnd) {
184
+ return `${this._formatDate(this._rangeStart)} - ${this._formatDate(this._rangeEnd)}`;
185
+ } else if (this._rangeStart) {
186
+ return this._formatDate(this._rangeStart);
187
+ }
188
+ return "";
189
+ }
190
+ return this._formatDate(this._selectedDate);
191
+ }
192
+ _prevMonth() {
193
+ this._viewDate.setMonth(this._viewDate.getMonth() - 1);
194
+ this.update();
195
+ }
196
+ _nextMonth() {
197
+ this._viewDate.setMonth(this._viewDate.getMonth() + 1);
198
+ this.update();
199
+ }
200
+ _prevYear() {
201
+ this._viewDate.setFullYear(this._viewDate.getFullYear() - 1);
202
+ this.update();
203
+ }
204
+ _nextYear() {
205
+ this._viewDate.setFullYear(this._viewDate.getFullYear() + 1);
206
+ this.update();
207
+ }
208
+ _setViewMode(mode) {
209
+ this._viewMode = mode;
210
+ this.update();
211
+ }
212
+ _selectMonth(month) {
213
+ this._viewDate.setMonth(month);
214
+ this._viewMode = "days";
215
+ this.update();
216
+ }
217
+ _selectYear(year) {
218
+ this._viewDate.setFullYear(year);
219
+ this._viewMode = "months";
220
+ this.update();
221
+ }
222
+ _isDateDisabled(date) {
223
+ if (this.minDate) {
224
+ const min = new Date(this.minDate);
225
+ if (date < min)
226
+ return true;
227
+ }
228
+ if (this.maxDate) {
229
+ const max = new Date(this.maxDate);
230
+ if (date > max)
231
+ return true;
232
+ }
233
+ return false;
234
+ }
235
+ _isToday(date) {
236
+ const today = new Date;
237
+ return date.getDate() === today.getDate() && date.getMonth() === today.getMonth() && date.getFullYear() === today.getFullYear();
238
+ }
239
+ _isSelected(date) {
240
+ if (this.range) {
241
+ if (this._rangeStart) {
242
+ if (this._isSameDay(date, this._rangeStart))
243
+ return true;
244
+ }
245
+ if (this._rangeEnd) {
246
+ if (this._isSameDay(date, this._rangeEnd))
247
+ return true;
248
+ }
249
+ return false;
250
+ }
251
+ return this._selectedDate ? this._isSameDay(date, this._selectedDate) : false;
252
+ }
253
+ _isInRange(date) {
254
+ if (!this.range || !this._rangeStart || !this._rangeEnd)
255
+ return false;
256
+ return date > this._rangeStart && date < this._rangeEnd;
257
+ }
258
+ _isRangeStart(date) {
259
+ return this._rangeStart ? this._isSameDay(date, this._rangeStart) : false;
260
+ }
261
+ _isRangeEnd(date) {
262
+ return this._rangeEnd ? this._isSameDay(date, this._rangeEnd) : false;
263
+ }
264
+ _isSameDay(a, b) {
265
+ return a.getDate() === b.getDate() && a.getMonth() === b.getMonth() && a.getFullYear() === b.getFullYear();
266
+ }
267
+ _selectDate(date) {
268
+ if (this._isDateDisabled(date))
269
+ return;
270
+ if (this.range) {
271
+ if (!this._rangeStart || this._rangeStart && this._rangeEnd) {
272
+ this._rangeStart = date;
273
+ this._rangeEnd = null;
274
+ } else {
275
+ if (date < this._rangeStart) {
276
+ this._rangeEnd = this._rangeStart;
277
+ this._rangeStart = date;
278
+ } else {
279
+ this._rangeEnd = date;
280
+ }
281
+ this._updateValue();
282
+ if (!this.showTime) {
283
+ this._close();
284
+ }
285
+ }
286
+ } else {
287
+ this._selectedDate = date;
288
+ this._updateValue();
289
+ if (!this.showTime) {
290
+ this._close();
291
+ }
292
+ }
293
+ this.update();
294
+ }
295
+ _updateValue() {
296
+ if (this.range) {
297
+ if (this._rangeStart && this._rangeEnd) {
298
+ this.value = `${this._formatDate(this._rangeStart)} - ${this._formatDate(this._rangeEnd)}`;
299
+ } else if (this._rangeStart) {
300
+ this.value = this._formatDate(this._rangeStart);
301
+ }
302
+ } else if (this._selectedDate) {
303
+ if (this.showTime) {
304
+ const hours24 = this._period === "PM" ? this._hours === 12 ? 12 : this._hours + 12 : this._hours === 12 ? 0 : this._hours;
305
+ this._selectedDate.setHours(hours24, this._minutes);
306
+ }
307
+ this.value = this._selectedDate.toISOString();
308
+ }
309
+ this.emit("change", {
310
+ value: this.value,
311
+ date: this._selectedDate,
312
+ rangeStart: this._rangeStart,
313
+ rangeEnd: this._rangeEnd
314
+ });
315
+ }
316
+ _getCalendarDays() {
317
+ const year = this._viewDate.getFullYear();
318
+ const month = this._viewDate.getMonth();
319
+ const firstDay = new Date(year, month, 1);
320
+ const lastDay = new Date(year, month + 1, 0);
321
+ const days = [];
322
+ const startPadding = firstDay.getDay();
323
+ for (let i = startPadding - 1;i >= 0; i--) {
324
+ const date = new Date(year, month, -i);
325
+ days.push({ date, isOtherMonth: true });
326
+ }
327
+ for (let i = 1;i <= lastDay.getDate(); i++) {
328
+ const date = new Date(year, month, i);
329
+ days.push({ date, isOtherMonth: false });
330
+ }
331
+ const remaining = 42 - days.length;
332
+ for (let i = 1;i <= remaining; i++) {
333
+ const date = new Date(year, month + 1, i);
334
+ days.push({ date, isOtherMonth: true });
335
+ }
336
+ return days;
337
+ }
338
+ _renderDays() {
339
+ const days = this._getCalendarDays();
340
+ return `
341
+ <div class="datepicker-weekdays">
342
+ ${WEEKDAYS.map((d) => `<div class="datepicker-weekday">${d}</div>`).join("")}
343
+ </div>
344
+ <div class="datepicker-days">
345
+ ${days.map(({ date, isOtherMonth }) => {
346
+ const classes = [
347
+ "datepicker-day",
348
+ isOtherMonth ? "datepicker-day-other-month" : "",
349
+ this._isToday(date) ? "datepicker-day-today" : "",
350
+ this._isSelected(date) ? "datepicker-day-selected" : "",
351
+ this._isInRange(date) ? "datepicker-day-in-range" : "",
352
+ this._isRangeStart(date) ? "datepicker-day-range-start" : "",
353
+ this._isRangeEnd(date) ? "datepicker-day-range-end" : ""
354
+ ].filter(Boolean).join(" ");
355
+ return `
356
+ <button
357
+ type="button"
358
+ class="${classes}"
359
+ data-date="${date.toISOString()}"
360
+ ${this._isDateDisabled(date) ? "disabled" : ""}
361
+ >
362
+ ${date.getDate()}
363
+ </button>
364
+ `;
365
+ }).join("")}
366
+ </div>
367
+ `;
368
+ }
369
+ _renderMonths() {
370
+ const currentMonth = this._viewDate.getMonth();
371
+ return `
372
+ <div class="datepicker-months">
373
+ ${MONTHS.map((name, i) => `
374
+ <button
375
+ type="button"
376
+ class="datepicker-month ${i === currentMonth ? "selected" : ""}"
377
+ data-month="${i}"
378
+ >
379
+ ${name.slice(0, 3)}
380
+ </button>
381
+ `).join("")}
382
+ </div>
383
+ `;
384
+ }
385
+ _renderYears() {
386
+ const currentYear = this._viewDate.getFullYear();
387
+ const startYear = currentYear - 5;
388
+ return `
389
+ <div class="datepicker-years">
390
+ ${Array.from({ length: 12 }, (_, i) => `
391
+ <button
392
+ type="button"
393
+ class="datepicker-year ${i + startYear === currentYear ? "selected" : ""}"
394
+ data-year="${i + startYear}"
395
+ >
396
+ ${i + startYear}
397
+ </button>
398
+ `).join("")}
399
+ </div>
400
+ `;
401
+ }
402
+ _renderTime() {
403
+ if (!this.showTime)
404
+ return "";
405
+ return `
406
+ <div class="datepicker-time">
407
+ <input
408
+ type="text"
409
+ class="datepicker-time-input"
410
+ value="${String(this._hours).padStart(2, "0")}"
411
+ data-time="hours"
412
+ maxlength="2"
413
+ />
414
+ <span class="datepicker-time-separator">:</span>
415
+ <input
416
+ type="text"
417
+ class="datepicker-time-input"
418
+ value="${String(this._minutes).padStart(2, "0")}"
419
+ data-time="minutes"
420
+ maxlength="2"
421
+ />
422
+ <div class="datepicker-time-period">
423
+ <button
424
+ type="button"
425
+ class="datepicker-time-period-btn ${this._period === "AM" ? "active" : ""}"
426
+ data-period="AM"
427
+ >AM</button>
428
+ <button
429
+ type="button"
430
+ class="datepicker-time-period-btn ${this._period === "PM" ? "active" : ""}"
431
+ data-period="PM"
432
+ >PM</button>
433
+ </div>
434
+ </div>
435
+ `;
436
+ }
437
+ _renderHeader() {
438
+ const title = this._viewMode === "days" ? `${MONTHS[this._viewDate.getMonth()]} ${this._viewDate.getFullYear()}` : this._viewMode === "months" ? String(this._viewDate.getFullYear()) : `${this._viewDate.getFullYear() - 5} - ${this._viewDate.getFullYear() + 6}`;
439
+ return `
440
+ <div class="datepicker-header">
441
+ <div class="datepicker-nav">
442
+ <button type="button" class="datepicker-nav-btn" data-nav="prev">&lt;</button>
443
+ </div>
444
+ <button type="button" class="datepicker-title" data-action="toggle-view">
445
+ ${title}
446
+ </button>
447
+ <div class="datepicker-nav">
448
+ <button type="button" class="datepicker-nav-btn" data-nav="next">&gt;</button>
449
+ </div>
450
+ </div>
451
+ `;
452
+ }
453
+ render() {
454
+ const sizeClass = this.size !== "md" ? `datepicker-${this.size}` : "";
455
+ const openClass = this._isOpen ? "datepicker-open" : "";
456
+ let calendarContent = "";
457
+ switch (this._viewMode) {
458
+ case "days":
459
+ calendarContent = this._renderDays();
460
+ break;
461
+ case "months":
462
+ calendarContent = this._renderMonths();
463
+ break;
464
+ case "years":
465
+ calendarContent = this._renderYears();
466
+ break;
467
+ }
468
+ return `
469
+ <div class="datepicker ${sizeClass} ${openClass}">
470
+ <input
471
+ type="text"
472
+ class="datepicker-input"
473
+ placeholder="${this.placeholder}"
474
+ value="${this._getDisplayValue()}"
475
+ ${this.disabled ? "disabled" : ""}
476
+ readonly
477
+ />
478
+ <div class="datepicker-icon">
479
+ <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
480
+ <rect x="3" y="4" width="18" height="18" rx="2" ry="2"></rect>
481
+ <line x1="16" y1="2" x2="16" y2="6"></line>
482
+ <line x1="8" y1="2" x2="8" y2="6"></line>
483
+ <line x1="3" y1="10" x2="21" y2="10"></line>
484
+ </svg>
485
+ </div>
486
+ <div class="datepicker-dropdown">
487
+ ${this._renderHeader()}
488
+ <div class="datepicker-calendar">
489
+ ${calendarContent}
490
+ </div>
491
+ ${this._renderTime()}
492
+ </div>
493
+ </div>
494
+ `;
495
+ }
496
+ update() {
497
+ super.update();
498
+ this._attachEventListeners();
499
+ }
500
+ _attachEventListeners() {
501
+ const input = this.shadowRoot?.querySelector(".datepicker-input");
502
+ if (input) {
503
+ input.removeEventListener("click", this._toggle.bind(this));
504
+ input.addEventListener("click", this._toggle.bind(this));
505
+ }
506
+ const prevBtn = this.shadowRoot?.querySelector('[data-nav="prev"]');
507
+ const nextBtn = this.shadowRoot?.querySelector('[data-nav="next"]');
508
+ if (prevBtn) {
509
+ prevBtn.removeEventListener("click", this._handlePrev);
510
+ prevBtn.addEventListener("click", this._handlePrev);
511
+ }
512
+ if (nextBtn) {
513
+ nextBtn.removeEventListener("click", this._handleNext);
514
+ nextBtn.addEventListener("click", this._handleNext);
515
+ }
516
+ const titleBtn = this.shadowRoot?.querySelector('[data-action="toggle-view"]');
517
+ if (titleBtn) {
518
+ titleBtn.removeEventListener("click", this._handleToggleView);
519
+ titleBtn.addEventListener("click", this._handleToggleView);
520
+ }
521
+ const dayBtns = this.shadowRoot?.querySelectorAll(".datepicker-day");
522
+ dayBtns?.forEach((btn) => {
523
+ btn.removeEventListener("click", this._handleDayClick);
524
+ btn.addEventListener("click", this._handleDayClick);
525
+ });
526
+ const monthBtns = this.shadowRoot?.querySelectorAll(".datepicker-month");
527
+ monthBtns?.forEach((btn) => {
528
+ btn.removeEventListener("click", this._handleMonthClick);
529
+ btn.addEventListener("click", this._handleMonthClick);
530
+ });
531
+ const yearBtns = this.shadowRoot?.querySelectorAll(".datepicker-year");
532
+ yearBtns?.forEach((btn) => {
533
+ btn.removeEventListener("click", this._handleYearClick);
534
+ btn.addEventListener("click", this._handleYearClick);
535
+ });
536
+ const timeInputs = this.shadowRoot?.querySelectorAll(".datepicker-time-input");
537
+ timeInputs?.forEach((input2) => {
538
+ input2.removeEventListener("change", this._handleTimeChange);
539
+ input2.addEventListener("change", this._handleTimeChange);
540
+ });
541
+ const periodBtns = this.shadowRoot?.querySelectorAll(".datepicker-time-period-btn");
542
+ periodBtns?.forEach((btn) => {
543
+ btn.removeEventListener("click", this._handlePeriodClick);
544
+ btn.addEventListener("click", this._handlePeriodClick);
545
+ });
546
+ }
547
+ _handlePrev = () => {
548
+ if (this._viewMode === "days") {
549
+ this._prevMonth();
550
+ } else if (this._viewMode === "months") {
551
+ this._prevYear();
552
+ } else {
553
+ this._viewDate.setFullYear(this._viewDate.getFullYear() - 12);
554
+ this.update();
555
+ }
556
+ };
557
+ _handleNext = () => {
558
+ if (this._viewMode === "days") {
559
+ this._nextMonth();
560
+ } else if (this._viewMode === "months") {
561
+ this._nextYear();
562
+ } else {
563
+ this._viewDate.setFullYear(this._viewDate.getFullYear() + 12);
564
+ this.update();
565
+ }
566
+ };
567
+ _handleToggleView = () => {
568
+ if (this._viewMode === "days") {
569
+ this._setViewMode("months");
570
+ } else if (this._viewMode === "months") {
571
+ this._setViewMode("years");
572
+ } else {
573
+ this._setViewMode("days");
574
+ }
575
+ };
576
+ _handleDayClick = (e) => {
577
+ const btn = e.currentTarget;
578
+ const dateStr = btn.dataset.date;
579
+ if (dateStr) {
580
+ this._selectDate(new Date(dateStr));
581
+ }
582
+ };
583
+ _handleMonthClick = (e) => {
584
+ const btn = e.currentTarget;
585
+ const month = btn.dataset.month;
586
+ if (month !== undefined) {
587
+ this._selectMonth(parseInt(month, 10));
588
+ }
589
+ };
590
+ _handleYearClick = (e) => {
591
+ const btn = e.currentTarget;
592
+ const year = btn.dataset.year;
593
+ if (year !== undefined) {
594
+ this._selectYear(parseInt(year, 10));
595
+ }
596
+ };
597
+ _handleTimeChange = (e) => {
598
+ const input = e.target;
599
+ const type = input.dataset.time;
600
+ const value = parseInt(input.value, 10);
601
+ if (type === "hours") {
602
+ this._hours = Math.max(1, Math.min(12, value || 12));
603
+ } else if (type === "minutes") {
604
+ this._minutes = Math.max(0, Math.min(59, value || 0));
605
+ }
606
+ this._updateValue();
607
+ this.update();
608
+ };
609
+ _handlePeriodClick = (e) => {
610
+ const btn = e.currentTarget;
611
+ const period = btn.dataset.period;
612
+ if (period) {
613
+ this._period = period;
614
+ this._updateValue();
615
+ this.update();
616
+ }
617
+ };
618
+ }
619
+
620
+ // src/index.ts
621
+ function register() {
622
+ if (!customElements.get("el-dm-datepicker")) {
623
+ customElements.define("el-dm-datepicker", ElDmDatepicker);
624
+ }
625
+ }