podo-ui 1.1.14 → 1.2.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.
- package/cdn/podo-datepicker.css +14 -1
- package/cdn/podo-datepicker.js +91 -12
- package/cdn/podo-datepicker.min.css +2 -2
- package/cdn/podo-datepicker.min.js +2 -2
- package/cdn/podo-ui.css +1 -1
- package/cdn/podo-ui.min.css +1 -1
- package/dist/react/molecule/datepicker.d.ts +27 -2
- package/dist/react/molecule/datepicker.d.ts.map +1 -1
- package/dist/react/molecule/datepicker.js +69 -7
- package/dist/react/molecule/datepicker.module.scss +14 -0
- package/dist/svelte/molecule/DatePicker.svelte +116 -8
- package/dist/svelte/molecule/DatePicker.svelte.d.ts +13 -3
- package/package.json +1 -1
- package/public/ai/components/datepicker.json +27 -3
- package/public/ai.json +1 -1
- package/vanilla/datepicker.css +13 -0
- package/vanilla/datepicker.js +90 -11
package/cdn/podo-datepicker.css
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/*!
|
|
2
|
-
* Podo UI DatePicker CSS v1.
|
|
2
|
+
* Podo UI DatePicker CSS v1.2.0
|
|
3
3
|
* https://podoui.com
|
|
4
4
|
* MIT License
|
|
5
5
|
*/
|
|
@@ -140,6 +140,19 @@
|
|
|
140
140
|
gap: 0;
|
|
141
141
|
}
|
|
142
142
|
|
|
143
|
+
/* hour-only 모드: 분 컬럼이 제거되므로 단일 select가 충분한 너비 확보 */
|
|
144
|
+
.podo-datepicker__hour-section {
|
|
145
|
+
flex: 1;
|
|
146
|
+
min-width: 0;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/* hour-only select: "오전 12시"~"오후 11시" 라벨 폭 확보 */
|
|
150
|
+
.podo-datepicker__hour-select {
|
|
151
|
+
min-width: 72px;
|
|
152
|
+
text-align: center;
|
|
153
|
+
text-align-last: center;
|
|
154
|
+
}
|
|
155
|
+
|
|
143
156
|
.podo-datepicker__time-select {
|
|
144
157
|
appearance: none;
|
|
145
158
|
-webkit-appearance: none;
|
package/cdn/podo-datepicker.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/*!
|
|
2
|
-
* Podo UI DatePicker v1.
|
|
2
|
+
* Podo UI DatePicker v1.2.0
|
|
3
3
|
* https://podoui.com
|
|
4
4
|
* MIT License
|
|
5
5
|
*/
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
* Podo UI DatePicker - Vanilla JS
|
|
8
8
|
* A pure JavaScript date picker component without dependencies
|
|
9
9
|
*
|
|
10
|
-
* @version
|
|
10
|
+
* @version 1.2.0
|
|
11
11
|
* @license MIT
|
|
12
12
|
*/
|
|
13
13
|
|
|
@@ -325,7 +325,10 @@
|
|
|
325
325
|
* @param {HTMLElement|string} container - Container element or selector
|
|
326
326
|
* @param {Object} options - DatePicker options
|
|
327
327
|
* @param {string} [options.mode='instant'] - 'instant' or 'period'
|
|
328
|
-
* @param {string} [options.type='date'] - 'date', 'time', or '
|
|
328
|
+
* @param {string} [options.type='date'] - 'date', 'time', 'datetime', or 'hour'
|
|
329
|
+
* @param {string} [options.hourFormat='24'] - Hour display format ('24' | '12') when type='hour'
|
|
330
|
+
* @param {number[]} [options.disabledHours] - Hours (0-23) that cannot be selected when type='hour'
|
|
331
|
+
* @param {number} [options.hourStep=1] - Hour step interval (1,2,3,4,6,12) when type='hour'
|
|
329
332
|
* @param {Object} [options.value] - Initial value { date, time, endDate, endTime }
|
|
330
333
|
* @param {Function} [options.onChange] - Change callback
|
|
331
334
|
* @param {string} [options.placeholder] - Placeholder text
|
|
@@ -364,7 +367,8 @@
|
|
|
364
367
|
this.onChange = options.onChange;
|
|
365
368
|
this.placeholder = options.placeholder;
|
|
366
369
|
this.disabled = options.disabled || false;
|
|
367
|
-
|
|
370
|
+
// hour-only는 native select 단일 입력이라 적용/초기화 액션 불필요 → 즉시 commit
|
|
371
|
+
this.showActions = options.showActions ?? (this.mode === 'period' && this.type !== 'hour');
|
|
368
372
|
this.align = options.align || 'left';
|
|
369
373
|
// Ensure disable and enable are always arrays
|
|
370
374
|
this.disable = Array.isArray(options.disable) ? options.disable : [];
|
|
@@ -372,6 +376,9 @@
|
|
|
372
376
|
this.minDate = options.minDate;
|
|
373
377
|
this.maxDate = options.maxDate;
|
|
374
378
|
this.minuteStep = options.minuteStep || 1;
|
|
379
|
+
this.hourFormat = options.hourFormat || '24';
|
|
380
|
+
this.disabledHours = Array.isArray(options.disabledHours) ? options.disabledHours : null;
|
|
381
|
+
this.hourStep = options.hourStep || 1;
|
|
375
382
|
this.texts = { ...DEFAULT_TEXTS, ...options.texts };
|
|
376
383
|
this.format = options.format;
|
|
377
384
|
this.initialCalendar = options.initialCalendar || {};
|
|
@@ -479,8 +486,8 @@
|
|
|
479
486
|
this.renderInputContent();
|
|
480
487
|
this.inputEl.appendChild(this.inputContentEl);
|
|
481
488
|
|
|
482
|
-
// Icon
|
|
483
|
-
const iconClass = this.type === 'time' ? 'icon-time' : 'icon-calendar';
|
|
489
|
+
// Icon (time/hour 모두 icon-time)
|
|
490
|
+
const iconClass = this.type === 'time' || this.type === 'hour' ? 'icon-time' : 'icon-calendar';
|
|
484
491
|
this.iconEl = createElement('i', `${PREFIX}__icon ${iconClass}`);
|
|
485
492
|
this.inputEl.appendChild(this.iconEl);
|
|
486
493
|
|
|
@@ -533,12 +540,73 @@
|
|
|
533
540
|
this.renderDateInput(displayValue);
|
|
534
541
|
} else if (this.type === 'time') {
|
|
535
542
|
this.renderTimeInput(displayValue);
|
|
543
|
+
} else if (this.type === 'hour') {
|
|
544
|
+
this.renderHourInput(displayValue);
|
|
536
545
|
} else {
|
|
537
546
|
// datetime
|
|
538
547
|
this.renderDateTimeInput(displayValue);
|
|
539
548
|
}
|
|
540
549
|
}
|
|
541
550
|
|
|
551
|
+
// hour-only: 분 컬럼/구분자 없이 시 select만 렌더
|
|
552
|
+
renderHourInput(displayValue) {
|
|
553
|
+
const startSection = this.createHourSection(displayValue.time, 'hour');
|
|
554
|
+
this.inputContentEl.appendChild(startSection);
|
|
555
|
+
|
|
556
|
+
if (this.mode === 'period') {
|
|
557
|
+
const sep = createElement('span', `${PREFIX}__separator`, '~');
|
|
558
|
+
this.inputContentEl.appendChild(sep);
|
|
559
|
+
|
|
560
|
+
const endSection = this.createHourSection(displayValue.endTime, 'endHour');
|
|
561
|
+
this.inputContentEl.appendChild(endSection);
|
|
562
|
+
}
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
createHourSection(time, part) {
|
|
566
|
+
const section = createElement('div', `${PREFIX}__time-section ${PREFIX}__hour-section`);
|
|
567
|
+
const select = this.createHourOnlySelect(time, part);
|
|
568
|
+
section.appendChild(select);
|
|
569
|
+
return section;
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
// hour-only 라벨 포맷
|
|
573
|
+
formatHourLabel(h) {
|
|
574
|
+
if (this.hourFormat === '12') {
|
|
575
|
+
const period = h < 12 ? '오전' : '오후';
|
|
576
|
+
const h12 = h % 12 === 0 ? 12 : h % 12;
|
|
577
|
+
return `${period} ${h12}시`;
|
|
578
|
+
}
|
|
579
|
+
return `${h}시`;
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
createHourOnlySelect(time, part) {
|
|
583
|
+
const select = createElement('select', `${PREFIX}__time-select ${PREFIX}__hour-select`);
|
|
584
|
+
if (!time) select.classList.add(`${PREFIX}__time-select--placeholder`);
|
|
585
|
+
if (this.disabled) select.disabled = true;
|
|
586
|
+
select.setAttribute('aria-label', part === 'endHour' ? '종료 시간 선택' : '시간 선택');
|
|
587
|
+
|
|
588
|
+
const isEnd = part === 'endHour';
|
|
589
|
+
const currentDate = isEnd ? this.tempValue.endDate : this.tempValue.date;
|
|
590
|
+
|
|
591
|
+
for (let h = 0; h < 24; h += this.hourStep) {
|
|
592
|
+
const opt = createElement('option', null, this.formatHourLabel(h));
|
|
593
|
+
opt.value = h;
|
|
594
|
+
|
|
595
|
+
// disabledHours 우선
|
|
596
|
+
if (this.disabledHours && this.disabledHours.includes(h)) {
|
|
597
|
+
opt.disabled = true;
|
|
598
|
+
} else if (this.isHourDisabled(h, currentDate)) {
|
|
599
|
+
opt.disabled = true;
|
|
600
|
+
}
|
|
601
|
+
|
|
602
|
+
select.appendChild(opt);
|
|
603
|
+
}
|
|
604
|
+
|
|
605
|
+
select.value = time?.hour ?? 0;
|
|
606
|
+
select.dataset.part = part;
|
|
607
|
+
return select;
|
|
608
|
+
}
|
|
609
|
+
|
|
542
610
|
renderDateInput(displayValue) {
|
|
543
611
|
// Start date button
|
|
544
612
|
this.startDateBtn = this.createDateButton(displayValue.date, 'date');
|
|
@@ -1395,8 +1463,12 @@
|
|
|
1395
1463
|
|
|
1396
1464
|
if (isHour) {
|
|
1397
1465
|
newTime.hour = value;
|
|
1466
|
+
// hour-only: 분을 0으로 정규화하고 min/max time 보정 건너뜀
|
|
1467
|
+
if (this.type === 'hour') {
|
|
1468
|
+
newTime.minute = 0;
|
|
1469
|
+
}
|
|
1398
1470
|
// Auto-adjust minute if needed
|
|
1399
|
-
const currentDate = isEnd ? this.tempValue.endDate : this.tempValue.date;
|
|
1471
|
+
const currentDate = this.type === 'hour' ? null : (isEnd ? this.tempValue.endDate : this.tempValue.date);
|
|
1400
1472
|
if (currentDate) {
|
|
1401
1473
|
const minLimit = this.minDate ? extractDateTimeLimit(this.minDate) : null;
|
|
1402
1474
|
const maxLimit = this.maxDate ? extractDateTimeLimit(this.maxDate) : null;
|
|
@@ -1417,15 +1489,14 @@
|
|
|
1417
1489
|
}
|
|
1418
1490
|
|
|
1419
1491
|
if (isEnd) {
|
|
1420
|
-
//
|
|
1421
|
-
if (!this.tempValue.endDate) {
|
|
1492
|
+
// hour-only는 date 자동 채움을 건너뜀 (value는 time만 가져야 함)
|
|
1493
|
+
if (this.type !== 'hour' && !this.tempValue.endDate) {
|
|
1422
1494
|
this.tempValue = { ...this.tempValue, endDate: new Date(), endTime: newTime };
|
|
1423
1495
|
} else {
|
|
1424
1496
|
this.tempValue = { ...this.tempValue, endTime: newTime };
|
|
1425
1497
|
}
|
|
1426
1498
|
} else {
|
|
1427
|
-
|
|
1428
|
-
if (!this.tempValue.date) {
|
|
1499
|
+
if (this.type !== 'hour' && !this.tempValue.date) {
|
|
1429
1500
|
this.tempValue = { ...this.tempValue, date: new Date(), time: newTime };
|
|
1430
1501
|
} else {
|
|
1431
1502
|
this.tempValue = { ...this.tempValue, time: newTime };
|
|
@@ -1543,8 +1614,16 @@
|
|
|
1543
1614
|
}
|
|
1544
1615
|
|
|
1545
1616
|
handleReset() {
|
|
1617
|
+
// 선택값만 비우고 팝오버는 열린 상태 유지
|
|
1618
|
+
// close()를 호출하면 사용자가 "적용"을 누를 기회 없이 닫혀버리는 문제 수정
|
|
1546
1619
|
this.tempValue = {};
|
|
1547
|
-
this.
|
|
1620
|
+
this._activePresetKey = null;
|
|
1621
|
+
this.navigationStep = null;
|
|
1622
|
+
this._navOffset = 0;
|
|
1623
|
+
// 캘린더 그리드/액션 영역을 다시 그려서 비워진 선택 상태를 반영
|
|
1624
|
+
if (this.isOpen) {
|
|
1625
|
+
this.renderDropdown();
|
|
1626
|
+
}
|
|
1548
1627
|
this.renderInputContent();
|
|
1549
1628
|
if (typeof this.options.onReset === 'function') {
|
|
1550
1629
|
this.options.onReset();
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
/*! Podo UI DatePicker CSS v1.
|
|
2
|
-
.podo-datepicker{--podo-bg-block:var(--color-bg-block,#ffffff);--podo-bg-modal:var(--color-bg-modal,#ffffff);--podo-bg-disabled:var(--color-bg-disabled,#f5f5f5);--podo-border:var(--color-border,#e0e0e0);--podo-border-hover:var(--color-border-hover,#bdbdbd);--podo-text-body:var(--color-text-body,#212121);--podo-text-sub:var(--color-text-sub,#757575);--podo-text-disabled:var(--color-text-disabled,#9e9e9e);--podo-primary:var(--color-primary,#7c3aed);--podo-primary-hover:var(--color-primary-hover,#6d28d9);--podo-primary-fill:var(--color-primary-fill,rgba(124,58,237,0.1));--podo-primary-reverse:var(--color-primary-reverse,#ffffff);--podo-default:var(--color-default,#f5f5f5);--podo-default-fill:var(--color-default-fill,#eeeeee);--podo-default-hover:var(--color-default-hover,#e0e0e0);--podo-default-reverse:var(--color-default-reverse,#212121);--podo-radius-sm:var(--radius-2,4px);--podo-radius-md:var(--radius-3,8px);--podo-spacing-1:var(--spacing-1,4px);--podo-spacing-2:var(--spacing-2,8px);--podo-spacing-3:var(--spacing-3,12px);--podo-spacing-4:var(--spacing-4,16px);--podo-spacing-5:var(--spacing-5,20px);--podo-font-size-sm:13px;--podo-font-size-md:14px;--podo-font-size-lg:16px;}.podo-datepicker{position:relative;display:inline-block;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,'Helvetica Neue',Arial,sans-serif;font-size:var(--podo-font-size-md);line-height:1.5;}.podo-datepicker__input{display:flex;align-items:center;gap:var(--podo-spacing-2);padding:var(--podo-spacing-3) var(--podo-spacing-4);background:var(--podo-bg-block);border:1px solid var(--podo-border);border-radius:var(--podo-radius-md);cursor:pointer;transition:border-color 0.2s;}.podo-datepicker__input:hover{border-color:var(--podo-border-hover);}.podo-datepicker__input--active{border-color:var(--podo-primary);}.podo-datepicker__input--disabled{background:var(--podo-bg-disabled);cursor:not-allowed;opacity:0.6;}.podo-datepicker__input-content{flex:1;display:flex;align-items:center;gap:var(--podo-spacing-2);}.podo-datepicker__part{display:flex;flex:1;align-items:center;justify-content:center;padding:0 var(--podo-spacing-2);background:none;border:none;border-radius:var(--podo-radius-sm);cursor:pointer;transition:background 0.2s;color:var(--podo-text-body);white-space:nowrap;font-variant-numeric:tabular-nums;font-family:inherit;font-size:var(--podo-font-size-md);}.podo-datepicker__part:hover{background:var(--podo-default);}.podo-datepicker__part--active{background:var(--podo-default-fill);}.podo-datepicker__part--placeholder{color:var(--podo-text-disabled);}.podo-datepicker__separator{color:var(--podo-text-body);padding:0 var(--podo-spacing-1);font-size:var(--podo-font-size-md);}.podo-datepicker__time-section{display:flex;align-items:center;justify-content:center;gap:0;}.podo-datepicker__time-select{appearance:none;-webkit-appearance:none;-moz-appearance:none;background:none;border:none;padding:0 var(--podo-spacing-2);flex:1;text-align:center;text-align-last:center;color:var(--podo-text-body);cursor:pointer;border-radius:var(--podo-radius-sm);transition:background 0.2s;font-variant-numeric:tabular-nums;font-family:inherit;font-size:var(--podo-font-size-md);}.podo-datepicker__time-select:hover{background:var(--podo-default);}.podo-datepicker__time-select:focus{outline:none;background:var(--podo-default-fill);}.podo-datepicker__time-select--placeholder{color:var(--podo-text-disabled);}.podo-datepicker__time-select:disabled{cursor:not-allowed;opacity:0.6;}.podo-datepicker__time-separator{color:var(--podo-text-body);font-size:var(--podo-font-size-md);}.podo-datepicker__icon{display:flex;align-items:center;justify-content:center;width:20px;height:20px;font-size:20px;line-height:1;color:var(--podo-text-sub);flex-shrink:0;}.podo-datepicker__input--with-nav{padding:0;}.podo-datepicker__nav-arrow{display:flex;align-items:center;justify-content:center;width:40px;min-height:100%;background:none;border:none;cursor:pointer;transition:background 0.2s;flex-shrink:0;}.podo-datepicker__nav-arrow:hover:not(:disabled){background:var(--podo-default-hover);}.podo-datepicker__nav-arrow:disabled{opacity:0.4;cursor:not-allowed;}.podo-datepicker__nav-arrow i{font-size:16px;color:var(--podo-text-body);}.podo-datepicker__nav-arrow--left{border-right:1px solid var(--podo-border);border-radius:var(--podo-radius-md) 0 0 var(--podo-radius-md);}.podo-datepicker__nav-arrow--right{border-left:1px solid var(--podo-border);border-radius:0 var(--podo-radius-md) var(--podo-radius-md) 0;}.podo-datepicker__preset-label{font-size:var(--podo-font-size-md);color:var(--podo-text-body);white-space:nowrap;flex-shrink:0;padding-left:var(--podo-spacing-3);font-weight:600;}.podo-datepicker__dropdown{position:absolute;top:calc(100%+var(--podo-spacing-2));left:0;z-index:1000;display:flex;flex-direction:column;gap:10px;padding:var(--podo-spacing-5);background:var(--podo-bg-modal);border-radius:var(--podo-radius-md);box-shadow:0 6px 18px -3px rgba(50,50,50,0.12);}.podo-datepicker__dropdown--right{left:auto;right:0;}.podo-datepicker__calendar{display:flex;flex-direction:column;gap:10px;min-width:280px;max-width:100%;}.podo-datepicker__calendar-nav{display:flex;align-items:center;gap:var(--podo-spacing-4);height:40px;}.podo-datepicker__nav-button{display:flex;align-items:center;justify-content:center;width:40px;height:40px;background:var(--podo-default);border:none;border-radius:var(--podo-radius-md);cursor:pointer;transition:background 0.2s;}.podo-datepicker__nav-button:hover{background:var(--podo-default-hover);}.podo-datepicker__nav-button:disabled{opacity:0.5;cursor:not-allowed;}.podo-datepicker__nav-button i{font-size:20px;color:var(--podo-text-body);}.podo-datepicker__nav-title{flex:1;display:flex;align-items:center;justify-content:center;gap:var(--podo-spacing-1);}.podo-datepicker__nav-select-wrapper{position:relative;display:flex;align-items:center;}.podo-datepicker__nav-select-wrapper::after{content:'';position:absolute;right:var(--podo-spacing-2);top:50%;transform:translateY(-50%);width:0;height:0;border-left:4px solid transparent;border-right:4px solid transparent;border-top:5px solid var(--podo-text-body);pointer-events:none;}.podo-datepicker__nav-select{appearance:none;-webkit-appearance:none;-moz-appearance:none;background:none;border:none;padding:var(--podo-spacing-2) 24px var(--podo-spacing-2) var(--podo-spacing-2);border-radius:var(--podo-radius-sm);cursor:pointer;transition:background 0.2s;color:var(--podo-text-body);font-family:inherit;font-size:var(--podo-font-size-lg);font-weight:600;}.podo-datepicker__nav-select:hover{background:var(--podo-default);}.podo-datepicker__nav-select:focus{outline:none;background:var(--podo-default-fill);}.podo-datepicker__calendar-grid{display:flex;flex-direction:column;gap:var(--podo-spacing-2);padding-top:var(--podo-spacing-3);border-top:1px solid var(--podo-border);}.podo-datepicker__calendar-row{display:flex;width:100%;min-width:0;}.podo-datepicker__calendar-cell{flex:1;min-width:0;display:flex;align-items:center;justify-content:center;height:40px;font-size:var(--podo-font-size-md);background:none;border:none;border-radius:var(--podo-radius-md);cursor:pointer;transition:background 0.2s;font-family:inherit;color:var(--podo-text-body);}.podo-datepicker__calendar-cell--header{color:var(--podo-text-sub);cursor:default;font-weight:500;}.podo-datepicker__calendar-cell--other{color:var(--podo-text-disabled);}.podo-datepicker__calendar-cell--today{font-weight:600;color:var(--podo-primary);}.podo-datepicker__calendar-cell--selected{background:var(--podo-primary);color:var(--podo-primary-reverse);font-weight:600;}.podo-datepicker__calendar-cell--in-range{background:var(--podo-primary-fill);border-radius:0;}.podo-datepicker__calendar-cell--range-start{background:var(--podo-primary);color:var(--podo-primary-reverse);font-weight:600;border-radius:var(--podo-radius-md);}.podo-datepicker__calendar-cell--range-end{background:var(--podo-primary);color:var(--podo-primary-reverse);font-weight:600;border-radius:var(--podo-radius-md);}.podo-datepicker__calendar-cell--disabled{color:var(--podo-text-disabled);background:var(--podo-default);border:none;border-radius:0;outline:none;cursor:not-allowed;}.podo-datepicker__calendar-cell--disabled:hover{background:var(--podo-default);}.podo-datepicker__calendar-cell:not(.podo-datepicker__calendar-cell--header):not(.podo-datepicker__calendar-cell--selected):not(.podo-datepicker__calendar-cell--disabled):not(.podo-datepicker__calendar-cell--range-start):not(.podo-datepicker__calendar-cell--range-end):not(.podo-datepicker__calendar-cell--in-range):hover{background:var(--podo-default);}.podo-datepicker__period-calendars{display:flex;gap:var(--podo-spacing-4);}.podo-datepicker__period-calendar-left,.podo-datepicker__period-calendar-right{flex:1;}.podo-datepicker__actions{display:flex;align-items:center;justify-content:space-between;gap:var(--podo-spacing-3);padding-top:var(--podo-spacing-3);border-top:1px solid var(--podo-border);}.podo-datepicker__period-text{font-size:var(--podo-font-size-md);color:var(--podo-primary);flex:1;}.podo-datepicker__action-buttons{display:flex;gap:var(--podo-spacing-3);flex-shrink:0;}.podo-datepicker__action-button{display:inline-flex;align-items:center;justify-content:center;gap:var(--podo-spacing-2);padding:var(--podo-spacing-3) var(--podo-spacing-4);border-radius:var(--podo-radius-md);cursor:pointer;transition:background 0.2s;white-space:nowrap;font-family:inherit;font-size:var(--podo-font-size-md);}.podo-datepicker__action-button i{display:flex;align-items:center;justify-content:center;width:18px;height:18px;font-size:18px;line-height:1;}.podo-datepicker__action-button--reset{background:var(--podo-default-fill);border:1px solid var(--podo-border);color:var(--podo-default-reverse);}.podo-datepicker__action-button--reset:hover{background:var(--podo-default-hover);}.podo-datepicker__action-button--apply{background:var(--podo-primary);border:none;color:var(--podo-primary-reverse);}.podo-datepicker__action-button--apply:hover{background:var(--podo-primary-hover);}.podo-datepicker__dropdown-body{display:flex;gap:var(--podo-spacing-4);min-width:0;}.podo-datepicker__quick-select-panel{display:flex;flex-direction:column;gap:var(--podo-spacing-1);min-width:100px;padding-right:var(--podo-spacing-4);border-right:1px solid var(--podo-border);flex-shrink:0;}.podo-datepicker__quick-select-item{display:flex;align-items:center;padding:var(--podo-spacing-2) var(--podo-spacing-3);background:none;border:none;border-radius:var(--podo-radius-sm);cursor:pointer;transition:background 0.2s,color 0.2s;color:var(--podo-text-body);white-space:nowrap;text-align:left;font-size:14px;line-height:1.5;}.podo-datepicker__quick-select-item:hover:not(.podo-datepicker__quick-select-item--disabled):not(.podo-datepicker__quick-select-item--active){background:var(--podo-default-hover);}.podo-datepicker__quick-select-item--active{color:var(--podo-primary);background:var(--podo-primary-fill,rgba(124,58,237,0.08));font-weight:600;}.podo-datepicker__quick-select-item--disabled{color:var(--podo-text-disabled,#adb5bd);cursor:not-allowed;}@media screen and (max-width:600px){.podo-datepicker__period-calendar-right{display:none;}.podo-datepicker__quick-select-panel{min-width:80px;padding-right:var(--podo-spacing-3);}.podo-datepicker__quick-select-item{font-size:13px;padding:var(--podo-spacing-2);}}@media screen and (max-width:480px){.podo-datepicker__dropdown{padding:var(--podo-spacing-4);gap:var(--podo-spacing-3);}.podo-datepicker__calendar{min-width:260px;gap:var(--podo-spacing-3);}.podo-datepicker__calendar-nav{height:36px;gap:var(--podo-spacing-3);}.podo-datepicker__nav-button{width:36px;height:36px;}.podo-datepicker__calendar-cell{height:36px;}}@media screen and (max-width:400px){.podo-datepicker__dropdown{padding:var(--podo-spacing-3);gap:var(--podo-spacing-2);max-width:calc(100vw - var(--podo-spacing-4));box-sizing:border-box;}.podo-datepicker__calendar{min-width:0;width:100%;gap:var(--podo-spacing-2);}.podo-datepicker__calendar-nav{height:32px;gap:var(--podo-spacing-2);}.podo-datepicker__nav-button{width:32px;height:32px;}.podo-datepicker__nav-button i{font-size:18px;}.podo-datepicker__calendar-cell{height:32px;}.podo-datepicker__actions{flex-wrap:wrap;gap:var(--podo-spacing-2);}.podo-datepicker__action-button{padding:var(--podo-spacing-2) var(--podo-spacing-3);}.podo-datepicker__action-button i{width:16px;height:16px;font-size:16px;}.podo-datepicker__quick-select-panel{display:none;}.podo-datepicker__nav-arrow{width:32px;}.podo-datepicker__nav-arrow i{font-size:14px;}.podo-datepicker__preset-label{display:none;}}
|
|
1
|
+
/*! Podo UI DatePicker CSS v1.2.0 | MIT License | https://podoui.com */
|
|
2
|
+
.podo-datepicker{--podo-bg-block:var(--color-bg-block,#ffffff);--podo-bg-modal:var(--color-bg-modal,#ffffff);--podo-bg-disabled:var(--color-bg-disabled,#f5f5f5);--podo-border:var(--color-border,#e0e0e0);--podo-border-hover:var(--color-border-hover,#bdbdbd);--podo-text-body:var(--color-text-body,#212121);--podo-text-sub:var(--color-text-sub,#757575);--podo-text-disabled:var(--color-text-disabled,#9e9e9e);--podo-primary:var(--color-primary,#7c3aed);--podo-primary-hover:var(--color-primary-hover,#6d28d9);--podo-primary-fill:var(--color-primary-fill,rgba(124,58,237,0.1));--podo-primary-reverse:var(--color-primary-reverse,#ffffff);--podo-default:var(--color-default,#f5f5f5);--podo-default-fill:var(--color-default-fill,#eeeeee);--podo-default-hover:var(--color-default-hover,#e0e0e0);--podo-default-reverse:var(--color-default-reverse,#212121);--podo-radius-sm:var(--radius-2,4px);--podo-radius-md:var(--radius-3,8px);--podo-spacing-1:var(--spacing-1,4px);--podo-spacing-2:var(--spacing-2,8px);--podo-spacing-3:var(--spacing-3,12px);--podo-spacing-4:var(--spacing-4,16px);--podo-spacing-5:var(--spacing-5,20px);--podo-font-size-sm:13px;--podo-font-size-md:14px;--podo-font-size-lg:16px;}.podo-datepicker{position:relative;display:inline-block;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,'Helvetica Neue',Arial,sans-serif;font-size:var(--podo-font-size-md);line-height:1.5;}.podo-datepicker__input{display:flex;align-items:center;gap:var(--podo-spacing-2);padding:var(--podo-spacing-3) var(--podo-spacing-4);background:var(--podo-bg-block);border:1px solid var(--podo-border);border-radius:var(--podo-radius-md);cursor:pointer;transition:border-color 0.2s;}.podo-datepicker__input:hover{border-color:var(--podo-border-hover);}.podo-datepicker__input--active{border-color:var(--podo-primary);}.podo-datepicker__input--disabled{background:var(--podo-bg-disabled);cursor:not-allowed;opacity:0.6;}.podo-datepicker__input-content{flex:1;display:flex;align-items:center;gap:var(--podo-spacing-2);}.podo-datepicker__part{display:flex;flex:1;align-items:center;justify-content:center;padding:0 var(--podo-spacing-2);background:none;border:none;border-radius:var(--podo-radius-sm);cursor:pointer;transition:background 0.2s;color:var(--podo-text-body);white-space:nowrap;font-variant-numeric:tabular-nums;font-family:inherit;font-size:var(--podo-font-size-md);}.podo-datepicker__part:hover{background:var(--podo-default);}.podo-datepicker__part--active{background:var(--podo-default-fill);}.podo-datepicker__part--placeholder{color:var(--podo-text-disabled);}.podo-datepicker__separator{color:var(--podo-text-body);padding:0 var(--podo-spacing-1);font-size:var(--podo-font-size-md);}.podo-datepicker__time-section{display:flex;align-items:center;justify-content:center;gap:0;}.podo-datepicker__hour-section{flex:1;min-width:0;}.podo-datepicker__hour-select{min-width:72px;text-align:center;text-align-last:center;}.podo-datepicker__time-select{appearance:none;-webkit-appearance:none;-moz-appearance:none;background:none;border:none;padding:0 var(--podo-spacing-2);flex:1;text-align:center;text-align-last:center;color:var(--podo-text-body);cursor:pointer;border-radius:var(--podo-radius-sm);transition:background 0.2s;font-variant-numeric:tabular-nums;font-family:inherit;font-size:var(--podo-font-size-md);}.podo-datepicker__time-select:hover{background:var(--podo-default);}.podo-datepicker__time-select:focus{outline:none;background:var(--podo-default-fill);}.podo-datepicker__time-select--placeholder{color:var(--podo-text-disabled);}.podo-datepicker__time-select:disabled{cursor:not-allowed;opacity:0.6;}.podo-datepicker__time-separator{color:var(--podo-text-body);font-size:var(--podo-font-size-md);}.podo-datepicker__icon{display:flex;align-items:center;justify-content:center;width:20px;height:20px;font-size:20px;line-height:1;color:var(--podo-text-sub);flex-shrink:0;}.podo-datepicker__input--with-nav{padding:0;}.podo-datepicker__nav-arrow{display:flex;align-items:center;justify-content:center;width:40px;min-height:100%;background:none;border:none;cursor:pointer;transition:background 0.2s;flex-shrink:0;}.podo-datepicker__nav-arrow:hover:not(:disabled){background:var(--podo-default-hover);}.podo-datepicker__nav-arrow:disabled{opacity:0.4;cursor:not-allowed;}.podo-datepicker__nav-arrow i{font-size:16px;color:var(--podo-text-body);}.podo-datepicker__nav-arrow--left{border-right:1px solid var(--podo-border);border-radius:var(--podo-radius-md) 0 0 var(--podo-radius-md);}.podo-datepicker__nav-arrow--right{border-left:1px solid var(--podo-border);border-radius:0 var(--podo-radius-md) var(--podo-radius-md) 0;}.podo-datepicker__preset-label{font-size:var(--podo-font-size-md);color:var(--podo-text-body);white-space:nowrap;flex-shrink:0;padding-left:var(--podo-spacing-3);font-weight:600;}.podo-datepicker__dropdown{position:absolute;top:calc(100%+var(--podo-spacing-2));left:0;z-index:1000;display:flex;flex-direction:column;gap:10px;padding:var(--podo-spacing-5);background:var(--podo-bg-modal);border-radius:var(--podo-radius-md);box-shadow:0 6px 18px -3px rgba(50,50,50,0.12);}.podo-datepicker__dropdown--right{left:auto;right:0;}.podo-datepicker__calendar{display:flex;flex-direction:column;gap:10px;min-width:280px;max-width:100%;}.podo-datepicker__calendar-nav{display:flex;align-items:center;gap:var(--podo-spacing-4);height:40px;}.podo-datepicker__nav-button{display:flex;align-items:center;justify-content:center;width:40px;height:40px;background:var(--podo-default);border:none;border-radius:var(--podo-radius-md);cursor:pointer;transition:background 0.2s;}.podo-datepicker__nav-button:hover{background:var(--podo-default-hover);}.podo-datepicker__nav-button:disabled{opacity:0.5;cursor:not-allowed;}.podo-datepicker__nav-button i{font-size:20px;color:var(--podo-text-body);}.podo-datepicker__nav-title{flex:1;display:flex;align-items:center;justify-content:center;gap:var(--podo-spacing-1);}.podo-datepicker__nav-select-wrapper{position:relative;display:flex;align-items:center;}.podo-datepicker__nav-select-wrapper::after{content:'';position:absolute;right:var(--podo-spacing-2);top:50%;transform:translateY(-50%);width:0;height:0;border-left:4px solid transparent;border-right:4px solid transparent;border-top:5px solid var(--podo-text-body);pointer-events:none;}.podo-datepicker__nav-select{appearance:none;-webkit-appearance:none;-moz-appearance:none;background:none;border:none;padding:var(--podo-spacing-2) 24px var(--podo-spacing-2) var(--podo-spacing-2);border-radius:var(--podo-radius-sm);cursor:pointer;transition:background 0.2s;color:var(--podo-text-body);font-family:inherit;font-size:var(--podo-font-size-lg);font-weight:600;}.podo-datepicker__nav-select:hover{background:var(--podo-default);}.podo-datepicker__nav-select:focus{outline:none;background:var(--podo-default-fill);}.podo-datepicker__calendar-grid{display:flex;flex-direction:column;gap:var(--podo-spacing-2);padding-top:var(--podo-spacing-3);border-top:1px solid var(--podo-border);}.podo-datepicker__calendar-row{display:flex;width:100%;min-width:0;}.podo-datepicker__calendar-cell{flex:1;min-width:0;display:flex;align-items:center;justify-content:center;height:40px;font-size:var(--podo-font-size-md);background:none;border:none;border-radius:var(--podo-radius-md);cursor:pointer;transition:background 0.2s;font-family:inherit;color:var(--podo-text-body);}.podo-datepicker__calendar-cell--header{color:var(--podo-text-sub);cursor:default;font-weight:500;}.podo-datepicker__calendar-cell--other{color:var(--podo-text-disabled);}.podo-datepicker__calendar-cell--today{font-weight:600;color:var(--podo-primary);}.podo-datepicker__calendar-cell--selected{background:var(--podo-primary);color:var(--podo-primary-reverse);font-weight:600;}.podo-datepicker__calendar-cell--in-range{background:var(--podo-primary-fill);border-radius:0;}.podo-datepicker__calendar-cell--range-start{background:var(--podo-primary);color:var(--podo-primary-reverse);font-weight:600;border-radius:var(--podo-radius-md);}.podo-datepicker__calendar-cell--range-end{background:var(--podo-primary);color:var(--podo-primary-reverse);font-weight:600;border-radius:var(--podo-radius-md);}.podo-datepicker__calendar-cell--disabled{color:var(--podo-text-disabled);background:var(--podo-default);border:none;border-radius:0;outline:none;cursor:not-allowed;}.podo-datepicker__calendar-cell--disabled:hover{background:var(--podo-default);}.podo-datepicker__calendar-cell:not(.podo-datepicker__calendar-cell--header):not(.podo-datepicker__calendar-cell--selected):not(.podo-datepicker__calendar-cell--disabled):not(.podo-datepicker__calendar-cell--range-start):not(.podo-datepicker__calendar-cell--range-end):not(.podo-datepicker__calendar-cell--in-range):hover{background:var(--podo-default);}.podo-datepicker__period-calendars{display:flex;gap:var(--podo-spacing-4);}.podo-datepicker__period-calendar-left,.podo-datepicker__period-calendar-right{flex:1;}.podo-datepicker__actions{display:flex;align-items:center;justify-content:space-between;gap:var(--podo-spacing-3);padding-top:var(--podo-spacing-3);border-top:1px solid var(--podo-border);}.podo-datepicker__period-text{font-size:var(--podo-font-size-md);color:var(--podo-primary);flex:1;}.podo-datepicker__action-buttons{display:flex;gap:var(--podo-spacing-3);flex-shrink:0;}.podo-datepicker__action-button{display:inline-flex;align-items:center;justify-content:center;gap:var(--podo-spacing-2);padding:var(--podo-spacing-3) var(--podo-spacing-4);border-radius:var(--podo-radius-md);cursor:pointer;transition:background 0.2s;white-space:nowrap;font-family:inherit;font-size:var(--podo-font-size-md);}.podo-datepicker__action-button i{display:flex;align-items:center;justify-content:center;width:18px;height:18px;font-size:18px;line-height:1;}.podo-datepicker__action-button--reset{background:var(--podo-default-fill);border:1px solid var(--podo-border);color:var(--podo-default-reverse);}.podo-datepicker__action-button--reset:hover{background:var(--podo-default-hover);}.podo-datepicker__action-button--apply{background:var(--podo-primary);border:none;color:var(--podo-primary-reverse);}.podo-datepicker__action-button--apply:hover{background:var(--podo-primary-hover);}.podo-datepicker__dropdown-body{display:flex;gap:var(--podo-spacing-4);min-width:0;}.podo-datepicker__quick-select-panel{display:flex;flex-direction:column;gap:var(--podo-spacing-1);min-width:100px;padding-right:var(--podo-spacing-4);border-right:1px solid var(--podo-border);flex-shrink:0;}.podo-datepicker__quick-select-item{display:flex;align-items:center;padding:var(--podo-spacing-2) var(--podo-spacing-3);background:none;border:none;border-radius:var(--podo-radius-sm);cursor:pointer;transition:background 0.2s,color 0.2s;color:var(--podo-text-body);white-space:nowrap;text-align:left;font-size:14px;line-height:1.5;}.podo-datepicker__quick-select-item:hover:not(.podo-datepicker__quick-select-item--disabled):not(.podo-datepicker__quick-select-item--active){background:var(--podo-default-hover);}.podo-datepicker__quick-select-item--active{color:var(--podo-primary);background:var(--podo-primary-fill,rgba(124,58,237,0.08));font-weight:600;}.podo-datepicker__quick-select-item--disabled{color:var(--podo-text-disabled,#adb5bd);cursor:not-allowed;}@media screen and (max-width:600px){.podo-datepicker__period-calendar-right{display:none;}.podo-datepicker__quick-select-panel{min-width:80px;padding-right:var(--podo-spacing-3);}.podo-datepicker__quick-select-item{font-size:13px;padding:var(--podo-spacing-2);}}@media screen and (max-width:480px){.podo-datepicker__dropdown{padding:var(--podo-spacing-4);gap:var(--podo-spacing-3);}.podo-datepicker__calendar{min-width:260px;gap:var(--podo-spacing-3);}.podo-datepicker__calendar-nav{height:36px;gap:var(--podo-spacing-3);}.podo-datepicker__nav-button{width:36px;height:36px;}.podo-datepicker__calendar-cell{height:36px;}}@media screen and (max-width:400px){.podo-datepicker__dropdown{padding:var(--podo-spacing-3);gap:var(--podo-spacing-2);max-width:calc(100vw - var(--podo-spacing-4));box-sizing:border-box;}.podo-datepicker__calendar{min-width:0;width:100%;gap:var(--podo-spacing-2);}.podo-datepicker__calendar-nav{height:32px;gap:var(--podo-spacing-2);}.podo-datepicker__nav-button{width:32px;height:32px;}.podo-datepicker__nav-button i{font-size:18px;}.podo-datepicker__calendar-cell{height:32px;}.podo-datepicker__actions{flex-wrap:wrap;gap:var(--podo-spacing-2);}.podo-datepicker__action-button{padding:var(--podo-spacing-2) var(--podo-spacing-3);}.podo-datepicker__action-button i{width:16px;height:16px;font-size:16px;}.podo-datepicker__quick-select-panel{display:none;}.podo-datepicker__nav-arrow{width:32px;}.podo-datepicker__nav-arrow i{font-size:14px;}.podo-datepicker__preset-label{display:none;}}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
/*! Podo UI DatePicker v1.
|
|
2
|
-
(function (global, factory) {typeof exports === 'object' && typeof module !== 'undefined'? (module.exports = factory()): typeof define === 'function' && define.amd? define(factory): ((global = typeof globalThis !== 'undefined' ? globalThis : global || self),(global.PodoDatePicker = factory()));})(this, function () {'use strict';const PREFIX = 'podo-datepicker';const DEFAULT_TEXTS = {weekDays: ['일', '월', '화', '수', '목', '금', '토'],months: ['1월', '2월', '3월', '4월', '5월', '6월', '7월', '8월', '9월', '10월', '11월', '12월'],yearSuffix: '년',reset: '초기화',apply: '적용',quickSelect: {today: '오늘',yesterday: '어제',thisWeek: '이번 주',lastWeek: '지난 주',last7Days: '최근 7일',last30Days: '최근 30일',thisMonth: '이번 달',lastMonth: '지난 달',},};const QUICK_SELECT_KEYS = ['today', 'yesterday', 'thisWeek', 'lastWeek','last7Days', 'last30Days', 'thisMonth', 'lastMonth',];function getPresetRange(key) {const today = new Date();const todayStart = new Date(today.getFullYear(), today.getMonth(), today.getDate());switch (key) {case 'today':return { start: new Date(todayStart), end: new Date(todayStart) };case 'yesterday': {const d = new Date(todayStart);d.setDate(d.getDate() - 1);return { start: d, end: new Date(d) };}case 'thisWeek': {const dayOfWeek = todayStart.getDay();const start = new Date(todayStart);start.setDate(start.getDate() - dayOfWeek);const end = new Date(start);end.setDate(end.getDate() + 6);return { start, end };}case 'lastWeek': {const dayOfWeek = todayStart.getDay();const thisWeekStart = new Date(todayStart);thisWeekStart.setDate(thisWeekStart.getDate() - dayOfWeek);const start = new Date(thisWeekStart);start.setDate(start.getDate() - 7);const end = new Date(start);end.setDate(end.getDate() + 6);return { start, end };}case 'last7Days': {const start = new Date(todayStart);start.setDate(start.getDate() - 6);return { start, end: new Date(todayStart) };}case 'last30Days': {const start = new Date(todayStart);start.setDate(start.getDate() - 29);return { start, end: new Date(todayStart) };}case 'thisMonth': {const start = new Date(todayStart.getFullYear(), todayStart.getMonth(), 1);const end = new Date(todayStart.getFullYear(), todayStart.getMonth() + 1, 0);return { start, end };}case 'lastMonth': {const start = new Date(todayStart.getFullYear(), todayStart.getMonth() - 1, 1);const end = new Date(todayStart.getFullYear(), todayStart.getMonth(), 0);return { start, end };}default:return { start: todayStart, end: todayStart };}}function getNavigationStepForPreset(key) {switch (key) {case 'today': case 'yesterday': return { type: 'days', count: 1 };case 'thisWeek': case 'lastWeek': case 'last7Days': return { type: 'days', count: 7 };case 'last30Days': return { type: 'days', count: 30 };case 'thisMonth': case 'lastMonth': return { type: 'month', count: 1 };default: return { type: 'days', count: 1 };}}function calculateNavigationStep(start, end) {const diffMs = new Date(end.getFullYear(), end.getMonth(), end.getDate()).getTime()- new Date(start.getFullYear(), start.getMonth(), start.getDate()).getTime();return { type: 'days', count: Math.round(diffMs / 86400000) + 1 };}function shiftDateRange(start, end, step, dir) {if (step.type === 'month') {const shift = step.count * dir;const newStart = new Date(start.getFullYear(), start.getMonth() + shift, 1);const newEnd = new Date(newStart.getFullYear(), newStart.getMonth() + 1, 0);return { start: newStart, end: newEnd };}const shift = step.count * dir;const s = new Date(start); s.setDate(s.getDate() + shift);const e = new Date(end); e.setDate(e.getDate() + shift);return { start: s, end: e };}function resolveCalendarInitial(initial, fallback) {if (!initial) return fallback;if (initial instanceof Date) return initial;const now = new Date();switch (initial) {case 'now':return new Date(now.getFullYear(), now.getMonth(), 1);case 'prevMonth':return new Date(now.getFullYear(), now.getMonth() - 1, 1);case 'nextMonth':return new Date(now.getFullYear(), now.getMonth() + 1, 1);default:return fallback;}}function formatWithPattern(date, time, pattern) {if (!date && !time) return '';let result = pattern;if (date) {result = result.replace(/y/g, String(date.getFullYear()));result = result.replace(/m/g, String(date.getMonth() + 1).padStart(2, '0'));result = result.replace(/d/g, String(date.getDate()).padStart(2, '0'));}if (time) {result = result.replace(/h/g, String(time.hour).padStart(2, '0'));result = result.replace(/i/g, String(time.minute).padStart(2, '0'));}return result;}function getDateOnlyFormat(format) {if (!format) return undefined;return format.replace(/\s*h[:\s]*i[분]?/g, '').replace(/\s*h시\s*i분/g, '').trim();}function formatDate(date) {const year = date.getFullYear();const month = String(date.getMonth() + 1).padStart(2, '0');const day = String(date.getDate()).padStart(2, '0');return `${year} - ${month} - ${day}`;}function formatTime(hour, minute) {return `${String(hour).padStart(2, '0')} : ${String(minute).padStart(2, '0')}`;}function isSameDay(date1, date2) {if (!date1 || !date2) return false;return (date1.getFullYear() === date2.getFullYear() &&date1.getMonth() === date2.getMonth() &&date1.getDate() === date2.getDate());}function isInRange(date, start, end) {const d = new Date(date.getFullYear(), date.getMonth(), date.getDate());const s = new Date(start.getFullYear(), start.getMonth(), start.getDate());const e = new Date(end.getFullYear(), end.getMonth(), end.getDate());return d >= s && d <= e;}function isInRangeExclusive(date, start, end) {const d = new Date(date.getFullYear(), date.getMonth(), date.getDate());const s = new Date(start.getFullYear(), start.getMonth(), start.getDate());const e = new Date(end.getFullYear(), end.getMonth(), end.getDate());return d > s && d < e;}function getDaysInMonth(year, month) {return new Date(year, month + 1, 0).getDate();}function getFirstDayOfMonth(year, month) {return new Date(year, month, 1).getDay();}function isDateRange(condition) {return typeof condition === 'object' && condition !== null && 'from' in condition && 'to' in condition;}function matchesCondition(date, condition) {if (typeof condition === 'function') {return condition(date);}if (isDateRange(condition)) {return isInRange(date, condition.from, condition.to);}return isSameDay(date, condition);}function isDateDisabled(date, disable, enable) {if (Array.isArray(enable) && enable.length > 0) {const isEnabled = enable.some((condition) => matchesCondition(date, condition));return !isEnabled;}if (Array.isArray(disable) && disable.length > 0) {return disable.some((condition) => matchesCondition(date, condition));}return false;}function isDateTimeLimit(value) {return typeof value === 'object' && value !== null && 'date' in value && !(value instanceof Date);}function extractDateTimeLimit(limit) {if (isDateTimeLimit(limit)) {return { date: limit.date, time: limit.time };}return { date: limit };}function isBeforeMinDate(date, minDate) {if (!minDate) return false;const { date: minDateValue } = extractDateTimeLimit(minDate);const d = new Date(date.getFullYear(), date.getMonth(), date.getDate());const m = new Date(minDateValue.getFullYear(), minDateValue.getMonth(), minDateValue.getDate());return d < m;}function isAfterMaxDate(date, maxDate) {if (!maxDate) return false;const { date: maxDateValue } = extractDateTimeLimit(maxDate);const d = new Date(date.getFullYear(), date.getMonth(), date.getDate());const m = new Date(maxDateValue.getFullYear(), maxDateValue.getMonth(), maxDateValue.getDate());return d > m;}function createElement(tag, className, content) {const el = document.createElement(tag);if (className) el.className = className;if (content !== undefined) {if (typeof content === 'string' || typeof content === 'number') {el.textContent = content;} else if (content instanceof HTMLElement) {el.appendChild(content);}}return el;}class PodoDatePicker {constructor(container, options = {}) {this.container =typeof container === 'string' ? document.querySelector(container) : container;if (!this.container) {throw new Error('PodoDatePicker: Container element not found');}if (this.container._podoDatePicker) {return this.container._podoDatePicker;}this.mode = options.mode || 'instant';this.type = options.type || 'date';this.value = options.value || {};this.tempValue = { ...this.value };this.onChange = options.onChange;this.placeholder = options.placeholder;this.disabled = options.disabled || false;this.showActions = options.showActions ?? this.mode === 'period';this.align = options.align || 'left';this.disable = Array.isArray(options.disable) ? options.disable : [];this.enable = Array.isArray(options.enable) ? options.enable : [];this.minDate = options.minDate;this.maxDate = options.maxDate;this.minuteStep = options.minuteStep || 1;this.texts = { ...DEFAULT_TEXTS, ...options.texts };this.format = options.format;this.initialCalendar = options.initialCalendar || {};this.yearRange = options.yearRange;this.quickSelect = options.quickSelect || false;if (options.texts?.quickSelect) {this.texts.quickSelect = { ...DEFAULT_TEXTS.quickSelect, ...options.texts.quickSelect };}this.isOpen = false;this.selectingPart = null;this.navigationStep = null;this._activePresetKey = null;this._navOffset = 0;if (this.quickSelect && this.mode === 'period' && this.value.date && this.value.endDate) {for (const key of QUICK_SELECT_KEYS) {const { start, end } = getPresetRange(key);if (isSameDay(this.value.date, start) && isSameDay(this.value.endDate, end)) {this._activePresetKey = key;this._navOffset = 0;this.navigationStep = getNavigationStepForPreset(key);break;}}}if (this.initialCalendar.start) {const fallback = this.value.date ? new Date(this.value.date) : new Date();this.viewDate = resolveCalendarInitial(this.initialCalendar.start, fallback);} else if (this.value.date) {this.viewDate = new Date(this.value.date);} else {this.viewDate = new Date();}if (this.initialCalendar.end) {const fallback = this.value.endDate? new Date(this.value.endDate.getFullYear(),this.value.endDate.getMonth() + 1,1): new Date();this.endViewDate = resolveCalendarInitial(this.initialCalendar.end, fallback);} else if (this.value.endDate) {this.endViewDate = new Date(this.value.endDate.getFullYear(),this.value.endDate.getMonth() + 1,1);} else {this.endViewDate = new Date(this.viewDate.getFullYear(),this.viewDate.getMonth() + 1,1);}this.render();this.bindEvents();this._lastMobileState = this.isMobileView();this._resizeHandler = () => {const isMobile = this.isMobileView();if (this.isOpen) {this.updateDropdownMaxWidth();if (this.mode === 'period' && isMobile !== this._lastMobileState) {this._lastMobileState = isMobile;this.renderDropdown();}}};window.addEventListener('resize', this._resizeHandler);this.container._podoDatePicker = this;}render() {this.container.innerHTML = '';this.container.className = PREFIX;const showNavigation = this.quickSelect && this.mode === 'period';this.inputEl = createElement('div', `${PREFIX}__input`);if (this.disabled) this.inputEl.classList.add(`${PREFIX}__input--disabled`);this.inputContentEl = createElement('div', `${PREFIX}__input-content`);this.renderInputContent();this.inputEl.appendChild(this.inputContentEl);const iconClass = this.type === 'time' ? 'icon-time' : 'icon-calendar';this.iconEl = createElement('i', `${PREFIX}__icon ${iconClass}`);this.inputEl.appendChild(this.iconEl);if (showNavigation) {this.inputEl.classList.add(`${PREFIX}__input--with-nav`);this._prevArrowBtn = createElement('button', `${PREFIX}__nav-arrow ${PREFIX}__nav-arrow--left`);this._prevArrowBtn.type = 'button';this._prevArrowBtn.innerHTML = '<i class="icon-expand-left"></i>';this._prevArrowBtn.disabled = this.disabled || this.isNavDisabled(-1);this._prevArrowBtn.addEventListener('click', (e) => { e.stopPropagation(); this.handleNavigate(-1); });this.inputEl.insertBefore(this._prevArrowBtn, this.inputContentEl);this._presetLabelEl = createElement('span', `${PREFIX}__preset-label`);this.updatePresetLabel();this.inputEl.insertBefore(this._presetLabelEl, this.inputContentEl);if (this.iconEl && this.iconEl.parentNode) {this.iconEl.parentNode.removeChild(this.iconEl);}this._nextArrowBtn = createElement('button', `${PREFIX}__nav-arrow ${PREFIX}__nav-arrow--right`);this._nextArrowBtn.type = 'button';this._nextArrowBtn.innerHTML = '<i class="icon-expand-right"></i>';this._nextArrowBtn.disabled = this.disabled || this.isNavDisabled(1);this._nextArrowBtn.addEventListener('click', (e) => { e.stopPropagation(); this.handleNavigate(1); });this.inputEl.appendChild(this._nextArrowBtn);}this.container.appendChild(this.inputEl);this.dropdownEl = createElement('div',`${PREFIX}__dropdown ${this.align === 'right' ? `${PREFIX}__dropdown--right` : ''}`);this.dropdownEl.style.display = 'none';this.container.appendChild(this.dropdownEl);}renderInputContent() {this.inputContentEl.innerHTML = '';const displayValue = this.showActions ? this.tempValue : this.value;if (this.type === 'date') {this.renderDateInput(displayValue);} else if (this.type === 'time') {this.renderTimeInput(displayValue);} else {this.renderDateTimeInput(displayValue);}}renderDateInput(displayValue) {this.startDateBtn = this.createDateButton(displayValue.date, 'date');this.inputContentEl.appendChild(this.startDateBtn);if (this.mode === 'period') {const sep = createElement('span', `${PREFIX}__separator`, '~');this.inputContentEl.appendChild(sep);this.endDateBtn = this.createDateButton(displayValue.endDate, 'endDate');this.inputContentEl.appendChild(this.endDateBtn);}}renderTimeInput(displayValue) {const startTimeSection = this.createTimeSection(displayValue.time, 'hour', 'minute');this.inputContentEl.appendChild(startTimeSection);if (this.mode === 'period') {const sep = createElement('span', `${PREFIX}__separator`, '~');this.inputContentEl.appendChild(sep);const endTimeSection = this.createTimeSection(displayValue.endTime, 'endHour', 'endMinute');this.inputContentEl.appendChild(endTimeSection);}}renderDateTimeInput(displayValue) {this.startDateBtn = this.createDateButton(displayValue.date, 'date');this.inputContentEl.appendChild(this.startDateBtn);const startTimeSection = this.createTimeSection(displayValue.time, 'hour', 'minute');this.inputContentEl.appendChild(startTimeSection);if (this.mode === 'period') {const sep = createElement('span', `${PREFIX}__separator`, '~');this.inputContentEl.appendChild(sep);this.endDateBtn = this.createDateButton(displayValue.endDate, 'endDate');this.inputContentEl.appendChild(this.endDateBtn);const endTimeSection = this.createTimeSection(displayValue.endTime, 'endHour', 'endMinute');this.inputContentEl.appendChild(endTimeSection);}}createDateButton(date, part) {const btn = createElement('button', `${PREFIX}__part`);btn.type = 'button';const dateFormat = getDateOnlyFormat(this.format);if (!date) {btn.classList.add(`${PREFIX}__part--placeholder`);const placeholderText = dateFormat? dateFormat.replace(/y/g, 'YYYY').replace(/m/g, 'MM').replace(/d/g, 'DD'): 'YYYY - MM - DD';btn.textContent = placeholderText;} else {const displayText = dateFormat? formatWithPattern(date, null, dateFormat): formatDate(date);btn.textContent = displayText;}btn.dataset.part = part;return btn;}createTimeSection(time, hourPart, minutePart) {const section = createElement('div', `${PREFIX}__time-section`);const hourSelect = this.createHourSelect(time, hourPart);section.appendChild(hourSelect);const sep = createElement('span', `${PREFIX}__time-separator`, ':');section.appendChild(sep);const minuteSelect = this.createMinuteSelect(time, minutePart);section.appendChild(minuteSelect);return section;}createHourSelect(time, part) {const select = createElement('select', `${PREFIX}__time-select`);if (!time) select.classList.add(`${PREFIX}__time-select--placeholder`);if (this.disabled) select.disabled = true;const isEnd = part === 'endHour';const currentDate = isEnd ? this.tempValue.endDate : this.tempValue.date;for (let h = 0; h < 24; h++) {const opt = createElement('option', null, String(h).padStart(2, '0'));opt.value = h;if (this.isHourDisabled(h, currentDate)) {opt.disabled = true;}select.appendChild(opt);}select.value = time?.hour ?? 0;select.dataset.part = part;return select;}createMinuteSelect(time, part) {const select = createElement('select', `${PREFIX}__time-select`);if (!time) select.classList.add(`${PREFIX}__time-select--placeholder`);if (this.disabled) select.disabled = true;const isEnd = part === 'endMinute';const currentDate = isEnd ? this.tempValue.endDate : this.tempValue.date;const currentTime = isEnd ? this.tempValue.endTime : this.tempValue.time;for (let m = 0; m < 60; m += this.minuteStep) {const opt = createElement('option', null, String(m).padStart(2, '0'));opt.value = m;if (this.isMinuteDisabled(m, currentDate, currentTime)) {opt.disabled = true;}select.appendChild(opt);}let minute = time?.minute ?? 0;if (minute % this.minuteStep !== 0) {minute = Math.floor(minute / this.minuteStep) * this.minuteStep;}select.value = minute;select.dataset.part = part;return select;}isHourDisabled(h, currentDate) {if (!currentDate) return false;const minLimit = this.minDate ? extractDateTimeLimit(this.minDate) : null;const maxLimit = this.maxDate ? extractDateTimeLimit(this.maxDate) : null;if (minLimit?.time && isSameDay(currentDate, minLimit.date)) {if (h < minLimit.time.hour) return true;}if (maxLimit?.time && isSameDay(currentDate, maxLimit.date)) {if (h > maxLimit.time.hour) return true;}return false;}isMinuteDisabled(m, currentDate, currentTime) {if (!currentDate || !currentTime) return false;const minLimit = this.minDate ? extractDateTimeLimit(this.minDate) : null;const maxLimit = this.maxDate ? extractDateTimeLimit(this.maxDate) : null;if (minLimit?.time && isSameDay(currentDate, minLimit.date) && currentTime.hour === minLimit.time.hour) {if (m < minLimit.time.minute) return true;}if (maxLimit?.time && isSameDay(currentDate, maxLimit.date) && currentTime.hour === maxLimit.time.hour) {if (m > maxLimit.time.minute) return true;}return false;}renderDropdown() {this.dropdownEl.innerHTML = '';if (this.mode === 'period') {const showQS = this.quickSelect && this.mode === 'period';if (showQS) {const body = createElement('div', `${PREFIX}__dropdown-body`);body.appendChild(this.renderQuickSelectPanel());const calWrapper = createElement('div', `${PREFIX}__period-calendars-wrapper`);this.renderPeriodCalendarsInto(calWrapper);body.appendChild(calWrapper);this.dropdownEl.appendChild(body);} else {this.renderPeriodCalendars();}} else {this.renderCalendar(this.viewDate, (date) => this.handleViewDateChange(date));}if (this.showActions) {this.renderActions();}}renderQuickSelectPanel() {const panel = createElement('div', `${PREFIX}__quick-select-panel`);QUICK_SELECT_KEYS.forEach((key) => {const label = this.texts.quickSelect?.[key] || key;const disabled = this.isQSPresetDisabled(key);const active = this.isQSPresetActive(key);const btn = document.createElement('button');btn.type = 'button';btn.textContent = label;btn.className = `${PREFIX}__quick-select-item`;if (active) btn.classList.add(`${PREFIX}__quick-select-item--active`);if (disabled) {btn.classList.add(`${PREFIX}__quick-select-item--disabled`);btn.disabled = true;}if (!disabled) {btn.addEventListener('mousedown', (e) => {e.preventDefault();e.stopPropagation();this.handleQuickSelect(key);});}panel.appendChild(btn);});return panel;}isQSPresetDisabled(key) {const { start, end } = getPresetRange(key);if (this.minDate) {const min = this.minDate instanceof Date ? this.minDate : this.minDate.date;const minDay = new Date(min.getFullYear(), min.getMonth(), min.getDate());if (end < minDay) return true;}if (this.maxDate) {const max = this.maxDate instanceof Date ? this.maxDate : this.maxDate.date;const maxDay = new Date(max.getFullYear(), max.getMonth(), max.getDate());if (start > maxDay) return true;}return false;}isQSPresetActive(key) {if (!this.tempValue.date || !this.tempValue.endDate) return false;const { start, end } = getPresetRange(key);return isSameDay(this.tempValue.date, start) && isSameDay(this.tempValue.endDate, end);}handleQuickSelect(key) {let { start, end } = getPresetRange(key);if (this.minDate) {const min = this.minDate instanceof Date ? this.minDate : this.minDate.date;const minDay = new Date(min.getFullYear(), min.getMonth(), min.getDate());if (start < minDay) start = minDay;}if (this.maxDate) {const max = this.maxDate instanceof Date ? this.maxDate : this.maxDate.date;const maxDay = new Date(max.getFullYear(), max.getMonth(), max.getDate());if (end > maxDay) end = maxDay;}this.tempValue = { date: start, endDate: end, time: this.tempValue.time, endTime: this.tempValue.endTime };this.viewDate = new Date(start.getFullYear(), start.getMonth(), 1);this.endViewDate = new Date(end.getFullYear(), end.getMonth(), 1);this.navigationStep = getNavigationStepForPreset(key);this._activePresetKey = key;this._navOffset = 0;if (!this.showActions) {this.value = { ...this.tempValue };this.onChange?.(this.value);this.close();}this.renderInputContent();this.updateNavArrows();this.renderDropdown();}renderPeriodCalendarsInto(container) {const wrapper = createElement('div', `${PREFIX}__period-calendars`);const isMobile = this.isMobileView();const leftCal = createElement('div', `${PREFIX}__period-calendar-left`);const leftCalendar = this.createCalendarElement(this.viewDate,(date) => this.handleViewDateChange(date),{ maxViewDate: isMobile ? undefined : this.endViewDate });leftCal.appendChild(leftCalendar);wrapper.appendChild(leftCal);const rightCal = createElement('div', `${PREFIX}__period-calendar-right`);const rightCalendar = this.createCalendarElement(this.endViewDate,(date) => this.handleEndViewDateChange(date),{ minViewDate: this.viewDate });rightCal.appendChild(rightCalendar);wrapper.appendChild(rightCal);container.appendChild(wrapper);}renderCalendar(viewDate, onViewDateChange, opts = {}) {const calendar = createElement('div', `${PREFIX}__calendar`);const nav = this.renderCalendarNav(viewDate, onViewDateChange, opts);calendar.appendChild(nav);const grid = this.renderCalendarGrid(viewDate);calendar.appendChild(grid);this.dropdownEl.appendChild(calendar);return calendar;}isMobileView() {return window.innerWidth <= 600;}renderPeriodCalendars() {const wrapper = createElement('div', `${PREFIX}__period-calendars`);const isMobile = this.isMobileView();const leftCal = createElement('div', `${PREFIX}__period-calendar-left`);const leftCalendar = this.createCalendarElement(this.viewDate,(date) => this.handleViewDateChange(date),{ maxViewDate: isMobile ? undefined : this.endViewDate });leftCal.appendChild(leftCalendar);wrapper.appendChild(leftCal);const rightCal = createElement('div', `${PREFIX}__period-calendar-right`);const rightCalendar = this.createCalendarElement(this.endViewDate,(date) => this.handleEndViewDateChange(date),{ minViewDate: this.viewDate });rightCal.appendChild(rightCalendar);wrapper.appendChild(rightCal);this.dropdownEl.appendChild(wrapper);}createCalendarElement(viewDate, onViewDateChange, opts = {}) {const calendar = createElement('div', `${PREFIX}__calendar`);const nav = this.renderCalendarNav(viewDate, onViewDateChange, opts);calendar.appendChild(nav);const grid = this.renderCalendarGrid(viewDate);calendar.appendChild(grid);return calendar;}calculateYearBounds(minViewYear, maxViewYear) {const currentYear = new Date().getFullYear();let minYearBound = currentYear - 100;let maxYearBound = currentYear + 100;if (this.minDate) {const { date } = extractDateTimeLimit(this.minDate);minYearBound = Math.max(minYearBound, date.getFullYear());}if (this.maxDate) {const { date } = extractDateTimeLimit(this.maxDate);maxYearBound = Math.min(maxYearBound, date.getFullYear());}if (this.yearRange?.min !== undefined) minYearBound = this.yearRange.min;if (this.yearRange?.max !== undefined) maxYearBound = this.yearRange.max;return { minYearBound, maxYearBound };}renderCalendarNav(viewDate, onViewDateChange, opts = {}) {const nav = createElement('div', `${PREFIX}__calendar-nav`);const year = viewDate.getFullYear();const month = viewDate.getMonth();const minViewDate = opts.minViewDate;const maxViewDate = opts.maxViewDate;const minYear = minViewDate?.getFullYear();const minMonth = minViewDate?.getMonth();const maxYear = maxViewDate?.getFullYear();const maxMonth = maxViewDate?.getMonth();const isPrevDisabled = minViewDate? year < minYear || (year === minYear && month <= minMonth): false;const isNextDisabled = maxViewDate? year > maxYear || (year === maxYear && month >= maxMonth): false;const prevBtn = createElement('button', `${PREFIX}__nav-button`);prevBtn.type = 'button';prevBtn.innerHTML = '<i class="icon-expand-left"></i>';if (isPrevDisabled) prevBtn.disabled = true;prevBtn.addEventListener('click', () => {if (!isPrevDisabled) {onViewDateChange(new Date(year, month - 1, 1));this.renderDropdown();}});nav.appendChild(prevBtn);const title = createElement('div', `${PREFIX}__nav-title`);const yearWrapper = createElement('div', `${PREFIX}__nav-select-wrapper`);const yearSelect = createElement('select', `${PREFIX}__nav-select`);const { minYearBound, maxYearBound } = this.calculateYearBounds(minYear, maxYear);for (let y = minYearBound; y <= maxYearBound; y++) {if (minYear !== undefined && y < minYear) continue;if (maxYear !== undefined && y > maxYear) continue;const opt = createElement('option', null, `${y}${this.texts.yearSuffix}`);opt.value = y;yearSelect.appendChild(opt);}yearSelect.value = year;yearSelect.addEventListener('change', (e) => {onViewDateChange(new Date(parseInt(e.target.value), month, 1));this.renderDropdown();});yearWrapper.appendChild(yearSelect);title.appendChild(yearWrapper);const monthWrapper = createElement('div', `${PREFIX}__nav-select-wrapper`);const monthSelect = createElement('select', `${PREFIX}__nav-select`);for (let m = 0; m < 12; m++) {if (minYear !== undefined && minMonth !== undefined && year === minYear && m < minMonth) continue;if (maxYear !== undefined && maxMonth !== undefined && year === maxYear && m > maxMonth) continue;const opt = createElement('option', null, this.texts.months[m]);opt.value = m;monthSelect.appendChild(opt);}monthSelect.value = month;monthSelect.addEventListener('change', (e) => {onViewDateChange(new Date(year, parseInt(e.target.value), 1));this.renderDropdown();});monthWrapper.appendChild(monthSelect);title.appendChild(monthWrapper);nav.appendChild(title);const nextBtn = createElement('button', `${PREFIX}__nav-button`);nextBtn.type = 'button';nextBtn.innerHTML = '<i class="icon-expand-right"></i>';if (isNextDisabled) nextBtn.disabled = true;nextBtn.addEventListener('click', () => {if (!isNextDisabled) {onViewDateChange(new Date(year, month + 1, 1));this.renderDropdown();}});nav.appendChild(nextBtn);return nav;}renderCalendarGrid(viewDate) {const grid = createElement('div', `${PREFIX}__calendar-grid`);const year = viewDate.getFullYear();const month = viewDate.getMonth();const today = new Date();const headerRow = createElement('div', `${PREFIX}__calendar-row`);this.texts.weekDays.forEach((day) => {const cell = createElement('div', `${PREFIX}__calendar-cell ${PREFIX}__calendar-cell--header`, day);headerRow.appendChild(cell);});grid.appendChild(headerRow);const daysInMonth = getDaysInMonth(year, month);const firstDay = getFirstDayOfMonth(year, month);const prevMonthDays = getDaysInMonth(year, month - 1);let days = [];for (let i = firstDay - 1; i >= 0; i--) {const day = prevMonthDays - i;const date = new Date(year, month - 1, day);days.push({ day, date, isOther: true });}for (let day = 1; day <= daysInMonth; day++) {const date = new Date(year, month, day);days.push({ day, date, isOther: false });}const totalCells = Math.ceil((firstDay + daysInMonth) / 7) * 7;const remainingDays = totalCells - (firstDay + daysInMonth);for (let day = 1; day <= remainingDays; day++) {const date = new Date(year, month + 1, day);days.push({ day, date, isOther: true });}for (let i = 0; i < days.length; i += 7) {const row = createElement('div', `${PREFIX}__calendar-row`);for (let j = 0; j < 7 && i + j < days.length; j++) {const { day, date, isOther } = days[i + j];const cell = this.createDayCell(day, date, isOther, today);row.appendChild(cell);}grid.appendChild(row);}return grid;}createDayCell(day, date, isOther, today) {const cell = createElement('button', `${PREFIX}__calendar-cell`);cell.type = 'button';cell.textContent = day;const isDisabled = this.checkDateDisabled(date);if (isOther) cell.classList.add(`${PREFIX}__calendar-cell--other`);if (isDisabled) {cell.classList.add(`${PREFIX}__calendar-cell--disabled`);cell.disabled = true;}const isToday = isSameDay(date, today);const isSelected = this.mode === 'instant' && isSameDay(date, this.tempValue.date);const isRangeStart = this.mode === 'period' && isSameDay(date, this.tempValue.date);const isRangeEnd = this.mode === 'period' && isSameDay(date, this.tempValue.endDate);const isInRangeDay =this.mode === 'period' &&this.tempValue.date &&this.tempValue.endDate &&isInRangeExclusive(date, this.tempValue.date, this.tempValue.endDate);if (isToday && !isSelected && !isRangeStart && !isRangeEnd) {cell.classList.add(`${PREFIX}__calendar-cell--today`);}if (isSelected) cell.classList.add(`${PREFIX}__calendar-cell--selected`);if (isRangeStart) cell.classList.add(`${PREFIX}__calendar-cell--range-start`);if (isRangeEnd) cell.classList.add(`${PREFIX}__calendar-cell--range-end`);if (isInRangeDay) cell.classList.add(`${PREFIX}__calendar-cell--in-range`);if (!isDisabled) {cell.addEventListener('click', () => this.handleDateSelect(date));}return cell;}checkDateDisabled(date) {if (isDateDisabled(date, this.disable, this.enable)) return true;if (isBeforeMinDate(date, this.minDate)) return true;if (isAfterMaxDate(date, this.maxDate)) return true;return false;}renderActions() {const actions = createElement('div', `${PREFIX}__actions`);const periodText = createElement('span', `${PREFIX}__period-text`);if (this.mode === 'period' && this.tempValue.date) {periodText.textContent = this.formatPeriodText();}actions.appendChild(periodText);const buttons = createElement('div', `${PREFIX}__action-buttons`);const resetBtn = createElement('button', `${PREFIX}__action-button ${PREFIX}__action-button--reset`);resetBtn.type = 'button';resetBtn.innerHTML = `<i class="icon-refresh"></i>${this.texts.reset}`;resetBtn.addEventListener('click', () => this.handleReset());buttons.appendChild(resetBtn);const applyBtn = createElement('button', `${PREFIX}__action-button ${PREFIX}__action-button--apply`);applyBtn.type = 'button';applyBtn.textContent = this.texts.apply;applyBtn.addEventListener('click', () => this.handleApply());buttons.appendChild(applyBtn);actions.appendChild(buttons);this.dropdownEl.appendChild(actions);}formatPeriodText() {if (!this.tempValue.date) return '';if (this.format) {const startText = formatWithPattern(this.tempValue.date, this.tempValue.time, this.format);if (this.tempValue.endDate) {const endText = formatWithPattern(this.tempValue.endDate, this.tempValue.endTime, this.format);return `${startText} ~ ${endText}`;}return startText;}const formatKoreanDateTime = (date, time) => {const year = date.getFullYear();const month = date.getMonth() + 1;const day = date.getDate();let dateStr = `${year}년 ${month}월 ${day}일`;if (this.type === 'datetime' && time) {const hours = String(time.hour).padStart(2, '0');const minutes = String(time.minute).padStart(2, '0');dateStr += ` ${hours}:${minutes}`;}return dateStr;};const startText = formatKoreanDateTime(this.tempValue.date, this.tempValue.time);if (this.tempValue.endDate) {const endText = formatKoreanDateTime(this.tempValue.endDate, this.tempValue.endTime);return `${startText} ~ ${endText}`;}return startText;}bindEvents() {this.inputEl.addEventListener('click', (e) => {if (this.disabled) return;const target = e.target;if (target.dataset.part === 'date' || target.dataset.part === 'endDate') {this.toggleDropdown(target.dataset.part);}});this.inputContentEl.addEventListener('change', (e) => {if (e.target.tagName !== 'SELECT') return;const part = e.target.dataset.part;const value = parseInt(e.target.value);this.handleTimeChange(part, value);});document.addEventListener('mousedown', (e) => {if (!this.container.contains(e.target) && this.isOpen) {this.close();}});}toggleDropdown(part) {if (this.selectingPart === part && this.isOpen) {this.close();} else {this.selectingPart = part;this.open();}}open() {this.isOpen = true;this.inputEl.classList.add(`${PREFIX}__input--active`);this.dropdownEl.style.display = 'flex';this.renderDropdown();this.updateDropdownMaxWidth();}updateDropdownMaxWidth() {if (!this.dropdownEl) return;const rect = this.dropdownEl.getBoundingClientRect();const viewportWidth = window.innerWidth;const padding = 8;const availableWidth = viewportWidth - rect.left - padding;if (availableWidth > 0) {this.dropdownEl.style.maxWidth = `${availableWidth}px`;} else {this.dropdownEl.style.maxWidth = '';}}close() {this.isOpen = false;this.selectingPart = null;this.inputEl.classList.remove(`${PREFIX}__input--active`);this.dropdownEl.style.display = 'none';}handleViewDateChange(date) {this.viewDate = date;}handleEndViewDateChange(date) {this.endViewDate = date;}handleDateSelect(date) {const newDate = new Date(date.getFullYear(), date.getMonth(), date.getDate());if (this.mode === 'instant') {const adjustedTime = this.adjustTimeForDate(newDate, this.tempValue.time);this.tempValue = { ...this.tempValue, date: newDate, time: adjustedTime };if (!this.showActions) {this.value = { ...this.tempValue };this.emitChange();}this.close();this.renderInputContent();return;}const existingStartDate = this.tempValue.date;const existingEndDate = this.tempValue.endDate;if (!existingStartDate) {const adjustedTime = this.adjustTimeForDate(newDate, this.tempValue.time);this.tempValue = { ...this.tempValue, date: newDate, time: adjustedTime };} else if (!existingEndDate) {const dateOnly = new Date(newDate.getFullYear(), newDate.getMonth(), newDate.getDate());const startDateOnly = new Date(existingStartDate.getFullYear(),existingStartDate.getMonth(),existingStartDate.getDate());if (dateOnly < startDateOnly) {const adjustedStartTime = this.adjustTimeForDate(newDate, this.tempValue.time);const adjustedEndTime = this.adjustTimeForDate(existingStartDate, this.tempValue.time);this.tempValue = {date: newDate,time: adjustedStartTime,endDate: existingStartDate,endTime: adjustedEndTime,};this.navigationStep = calculateNavigationStep(newDate, existingStartDate);this._activePresetKey = null;this._navOffset = 0;} else {const adjustedEndTime = this.adjustTimeForDate(newDate, this.tempValue.endTime);this.tempValue = { ...this.tempValue, endDate: newDate, endTime: adjustedEndTime };this.navigationStep = calculateNavigationStep(existingStartDate, newDate);this._activePresetKey = null;this._navOffset = 0;}} else {const adjustedTime = this.adjustTimeForDate(newDate, this.tempValue.time);this.tempValue = { date: newDate, time: adjustedTime, endDate: undefined, endTime: undefined };}this.renderDropdown();this.renderInputContent();}adjustTimeForDate(date, time) {if (!time) return time;const minLimit = this.minDate ? extractDateTimeLimit(this.minDate) : null;const maxLimit = this.maxDate ? extractDateTimeLimit(this.maxDate) : null;let adjustedHour = time.hour;let adjustedMinute = time.minute;if (minLimit?.time && isSameDay(date, minLimit.date)) {if (adjustedHour < minLimit.time.hour) {adjustedHour = minLimit.time.hour;adjustedMinute = Math.ceil(minLimit.time.minute / this.minuteStep) * this.minuteStep;} else if (adjustedHour === minLimit.time.hour && adjustedMinute < minLimit.time.minute) {adjustedMinute = Math.ceil(minLimit.time.minute / this.minuteStep) * this.minuteStep;}}if (maxLimit?.time && isSameDay(date, maxLimit.date)) {if (adjustedHour > maxLimit.time.hour) {adjustedHour = maxLimit.time.hour;adjustedMinute = Math.floor(maxLimit.time.minute / this.minuteStep) * this.minuteStep;} else if (adjustedHour === maxLimit.time.hour && adjustedMinute > maxLimit.time.minute) {adjustedMinute = Math.floor(maxLimit.time.minute / this.minuteStep) * this.minuteStep;}}if (adjustedHour !== time.hour || adjustedMinute !== time.minute) {return { hour: adjustedHour, minute: adjustedMinute };}return time;}handleTimeChange(part, value) {const isEnd = part === 'endHour' || part === 'endMinute';const isHour = part === 'hour' || part === 'endHour';const currentTime = isEnd ? this.tempValue.endTime : this.tempValue.time;let newTime = { hour: currentTime?.hour ?? 0, minute: currentTime?.minute ?? 0 };if (isHour) {newTime.hour = value;const currentDate = isEnd ? this.tempValue.endDate : this.tempValue.date;if (currentDate) {const minLimit = this.minDate ? extractDateTimeLimit(this.minDate) : null;const maxLimit = this.maxDate ? extractDateTimeLimit(this.maxDate) : null;if (minLimit?.time && isSameDay(currentDate, minLimit.date) && value === minLimit.time.hour) {if (newTime.minute < minLimit.time.minute) {newTime.minute = Math.ceil(minLimit.time.minute / this.minuteStep) * this.minuteStep;}}if (maxLimit?.time && isSameDay(currentDate, maxLimit.date) && value === maxLimit.time.hour) {if (newTime.minute > maxLimit.time.minute) {newTime.minute = Math.floor(maxLimit.time.minute / this.minuteStep) * this.minuteStep;}}}} else {newTime.minute = value;}if (isEnd) {if (!this.tempValue.endDate) {this.tempValue = { ...this.tempValue, endDate: new Date(), endTime: newTime };} else {this.tempValue = { ...this.tempValue, endTime: newTime };}} else {if (!this.tempValue.date) {this.tempValue = { ...this.tempValue, date: new Date(), time: newTime };} else {this.tempValue = { ...this.tempValue, time: newTime };}}this.value = { ...this.tempValue };this.emitChange();this.renderInputContent();}handleNavigate(direction) {const dv = this.showActions ? this.tempValue : this.value;if (!dv?.date || !dv?.endDate || !this.navigationStep) return;const { start, end } = shiftDateRange(dv.date, dv.endDate, this.navigationStep, direction);if (this.minDate) {const min = this.minDate instanceof Date ? this.minDate : this.minDate.date;if (start < new Date(min.getFullYear(), min.getMonth(), min.getDate())) return;}if (this.maxDate) {const max = this.maxDate instanceof Date ? this.maxDate : this.maxDate.date;if (end > new Date(max.getFullYear(), max.getMonth(), max.getDate())) return;}this._navOffset = (this._navOffset || 0) + direction;const newValue = { date: start, endDate: end, time: dv.time, endTime: dv.endTime };this.tempValue = newValue;this.value = { ...newValue };this.viewDate = new Date(start.getFullYear(), start.getMonth(), 1);this.endViewDate = new Date(end.getFullYear(), end.getMonth(), 1);this.emitChange();this.renderInputContent();this.updateNavArrows();if (this.isOpen) this.renderDropdown();}isNavDisabled(direction) {const dv = this.showActions ? this.tempValue : this.value;if (!dv?.date || !dv?.endDate || !this.navigationStep) return true;const { start, end } = shiftDateRange(dv.date, dv.endDate, this.navigationStep, direction);if (direction === -1 && this.minDate) {const min = this.minDate instanceof Date ? this.minDate : this.minDate.date;return start < new Date(min.getFullYear(), min.getMonth(), min.getDate());}if (direction === 1 && this.maxDate) {const max = this.maxDate instanceof Date ? this.maxDate : this.maxDate.date;return end > new Date(max.getFullYear(), max.getMonth(), max.getDate());}return false;}updateNavArrows() {if (this._prevArrowBtn) {this._prevArrowBtn.disabled = this.disabled || this.isNavDisabled(-1);}if (this._nextArrowBtn) {this._nextArrowBtn.disabled = this.disabled || this.isNavDisabled(1);}this.updatePresetLabel();}getActivePresetLabel() {const dv = this.showActions ? this.tempValue : this.value;if (!dv?.date || !dv?.endDate) return null;for (const key of QUICK_SELECT_KEYS) {const { start, end } = getPresetRange(key);if (isSameDay(dv.date, start) && isSameDay(dv.endDate, end)) {return this.texts.quickSelect?.[key] || key;}}if (this._activePresetKey && this._navOffset !== 0) {return this._getNavOffsetLabel();}return null;}_getNavOffsetLabel() {const offset = Math.abs(this._navOffset);const isFuture = this._navOffset > 0;const step = this.navigationStep;if (!step) return null;if (step.type === 'month') {return isFuture ? `${offset}개월 후` : `${offset}개월 전`;}if (step.count === 1) {return isFuture ? `${offset}일 후` : `${offset}일 전`;}if (step.count === 7) {return isFuture ? `${offset}주 후` : `${offset}주 전`;}if (step.count === 30) {return isFuture ? `${offset * 30}일 후` : `${offset * 30}일 전`;}return isFuture ? `${offset * step.count}일 후` : `${offset * step.count}일 전`;}updatePresetLabel() {if (!this._presetLabelEl) return;const label = this.getActivePresetLabel();if (label) {this._presetLabelEl.textContent = `${label} :`;this._presetLabelEl.style.display = '';} else {this._presetLabelEl.textContent = '';this._presetLabelEl.style.display = 'none';}}handleReset() {this.tempValue = {};this.close();this.renderInputContent();if (typeof this.options.onReset === 'function') {this.options.onReset();}}handleApply() {this.value = { ...this.tempValue };this.emitChange();this.close();this.renderInputContent();}emitChange() {if (this.onChange) {this.onChange(this.value);}}getValue() {return { ...this.value };}setValue(value) {this.value = value || {};this.tempValue = { ...this.value };if (value?.date) {this.viewDate = new Date(value.date);}if (value?.endDate) {this.endViewDate = new Date(value.endDate.getFullYear(), value.endDate.getMonth() + 1, 1);}if (this.quickSelect && this.mode === 'period' && value?.date && value?.endDate) {let matched = false;for (const key of QUICK_SELECT_KEYS) {const { start, end } = getPresetRange(key);if (isSameDay(value.date, start) && isSameDay(value.endDate, end)) {this._activePresetKey = key;this._navOffset = 0;this.navigationStep = getNavigationStepForPreset(key);matched = true;break;}}if (!matched) {this._activePresetKey = null;this._navOffset = 0;}}this.renderInputContent();this.updatePresetLabel();}clear() {this.value = {};this.tempValue = {};this.renderInputContent();this.emitChange();}enable() {this.disabled = false;this.inputEl.classList.remove(`${PREFIX}__input--disabled`);this.renderInputContent();}disable() {this.disabled = true;this.inputEl.classList.add(`${PREFIX}__input--disabled`);this.close();this.renderInputContent();}destroy() {if (this._resizeHandler) {window.removeEventListener('resize', this._resizeHandler);}if (this.container._podoDatePicker) {delete this.container._podoDatePicker;}this.container.innerHTML = '';}}return PodoDatePicker;});
|
|
1
|
+
/*! Podo UI DatePicker v1.2.0 | MIT License | https://podoui.com */
|
|
2
|
+
(function (global, factory) {typeof exports === 'object' && typeof module !== 'undefined'? (module.exports = factory()): typeof define === 'function' && define.amd? define(factory): ((global = typeof globalThis !== 'undefined' ? globalThis : global || self),(global.PodoDatePicker = factory()));})(this, function () {'use strict';const PREFIX = 'podo-datepicker';const DEFAULT_TEXTS = {weekDays: ['일', '월', '화', '수', '목', '금', '토'],months: ['1월', '2월', '3월', '4월', '5월', '6월', '7월', '8월', '9월', '10월', '11월', '12월'],yearSuffix: '년',reset: '초기화',apply: '적용',quickSelect: {today: '오늘',yesterday: '어제',thisWeek: '이번 주',lastWeek: '지난 주',last7Days: '최근 7일',last30Days: '최근 30일',thisMonth: '이번 달',lastMonth: '지난 달',},};const QUICK_SELECT_KEYS = ['today', 'yesterday', 'thisWeek', 'lastWeek','last7Days', 'last30Days', 'thisMonth', 'lastMonth',];function getPresetRange(key) {const today = new Date();const todayStart = new Date(today.getFullYear(), today.getMonth(), today.getDate());switch (key) {case 'today':return { start: new Date(todayStart), end: new Date(todayStart) };case 'yesterday': {const d = new Date(todayStart);d.setDate(d.getDate() - 1);return { start: d, end: new Date(d) };}case 'thisWeek': {const dayOfWeek = todayStart.getDay();const start = new Date(todayStart);start.setDate(start.getDate() - dayOfWeek);const end = new Date(start);end.setDate(end.getDate() + 6);return { start, end };}case 'lastWeek': {const dayOfWeek = todayStart.getDay();const thisWeekStart = new Date(todayStart);thisWeekStart.setDate(thisWeekStart.getDate() - dayOfWeek);const start = new Date(thisWeekStart);start.setDate(start.getDate() - 7);const end = new Date(start);end.setDate(end.getDate() + 6);return { start, end };}case 'last7Days': {const start = new Date(todayStart);start.setDate(start.getDate() - 6);return { start, end: new Date(todayStart) };}case 'last30Days': {const start = new Date(todayStart);start.setDate(start.getDate() - 29);return { start, end: new Date(todayStart) };}case 'thisMonth': {const start = new Date(todayStart.getFullYear(), todayStart.getMonth(), 1);const end = new Date(todayStart.getFullYear(), todayStart.getMonth() + 1, 0);return { start, end };}case 'lastMonth': {const start = new Date(todayStart.getFullYear(), todayStart.getMonth() - 1, 1);const end = new Date(todayStart.getFullYear(), todayStart.getMonth(), 0);return { start, end };}default:return { start: todayStart, end: todayStart };}}function getNavigationStepForPreset(key) {switch (key) {case 'today': case 'yesterday': return { type: 'days', count: 1 };case 'thisWeek': case 'lastWeek': case 'last7Days': return { type: 'days', count: 7 };case 'last30Days': return { type: 'days', count: 30 };case 'thisMonth': case 'lastMonth': return { type: 'month', count: 1 };default: return { type: 'days', count: 1 };}}function calculateNavigationStep(start, end) {const diffMs = new Date(end.getFullYear(), end.getMonth(), end.getDate()).getTime()- new Date(start.getFullYear(), start.getMonth(), start.getDate()).getTime();return { type: 'days', count: Math.round(diffMs / 86400000) + 1 };}function shiftDateRange(start, end, step, dir) {if (step.type === 'month') {const shift = step.count * dir;const newStart = new Date(start.getFullYear(), start.getMonth() + shift, 1);const newEnd = new Date(newStart.getFullYear(), newStart.getMonth() + 1, 0);return { start: newStart, end: newEnd };}const shift = step.count * dir;const s = new Date(start); s.setDate(s.getDate() + shift);const e = new Date(end); e.setDate(e.getDate() + shift);return { start: s, end: e };}function resolveCalendarInitial(initial, fallback) {if (!initial) return fallback;if (initial instanceof Date) return initial;const now = new Date();switch (initial) {case 'now':return new Date(now.getFullYear(), now.getMonth(), 1);case 'prevMonth':return new Date(now.getFullYear(), now.getMonth() - 1, 1);case 'nextMonth':return new Date(now.getFullYear(), now.getMonth() + 1, 1);default:return fallback;}}function formatWithPattern(date, time, pattern) {if (!date && !time) return '';let result = pattern;if (date) {result = result.replace(/y/g, String(date.getFullYear()));result = result.replace(/m/g, String(date.getMonth() + 1).padStart(2, '0'));result = result.replace(/d/g, String(date.getDate()).padStart(2, '0'));}if (time) {result = result.replace(/h/g, String(time.hour).padStart(2, '0'));result = result.replace(/i/g, String(time.minute).padStart(2, '0'));}return result;}function getDateOnlyFormat(format) {if (!format) return undefined;return format.replace(/\s*h[:\s]*i[분]?/g, '').replace(/\s*h시\s*i분/g, '').trim();}function formatDate(date) {const year = date.getFullYear();const month = String(date.getMonth() + 1).padStart(2, '0');const day = String(date.getDate()).padStart(2, '0');return `${year} - ${month} - ${day}`;}function formatTime(hour, minute) {return `${String(hour).padStart(2, '0')} : ${String(minute).padStart(2, '0')}`;}function isSameDay(date1, date2) {if (!date1 || !date2) return false;return (date1.getFullYear() === date2.getFullYear() &&date1.getMonth() === date2.getMonth() &&date1.getDate() === date2.getDate());}function isInRange(date, start, end) {const d = new Date(date.getFullYear(), date.getMonth(), date.getDate());const s = new Date(start.getFullYear(), start.getMonth(), start.getDate());const e = new Date(end.getFullYear(), end.getMonth(), end.getDate());return d >= s && d <= e;}function isInRangeExclusive(date, start, end) {const d = new Date(date.getFullYear(), date.getMonth(), date.getDate());const s = new Date(start.getFullYear(), start.getMonth(), start.getDate());const e = new Date(end.getFullYear(), end.getMonth(), end.getDate());return d > s && d < e;}function getDaysInMonth(year, month) {return new Date(year, month + 1, 0).getDate();}function getFirstDayOfMonth(year, month) {return new Date(year, month, 1).getDay();}function isDateRange(condition) {return typeof condition === 'object' && condition !== null && 'from' in condition && 'to' in condition;}function matchesCondition(date, condition) {if (typeof condition === 'function') {return condition(date);}if (isDateRange(condition)) {return isInRange(date, condition.from, condition.to);}return isSameDay(date, condition);}function isDateDisabled(date, disable, enable) {if (Array.isArray(enable) && enable.length > 0) {const isEnabled = enable.some((condition) => matchesCondition(date, condition));return !isEnabled;}if (Array.isArray(disable) && disable.length > 0) {return disable.some((condition) => matchesCondition(date, condition));}return false;}function isDateTimeLimit(value) {return typeof value === 'object' && value !== null && 'date' in value && !(value instanceof Date);}function extractDateTimeLimit(limit) {if (isDateTimeLimit(limit)) {return { date: limit.date, time: limit.time };}return { date: limit };}function isBeforeMinDate(date, minDate) {if (!minDate) return false;const { date: minDateValue } = extractDateTimeLimit(minDate);const d = new Date(date.getFullYear(), date.getMonth(), date.getDate());const m = new Date(minDateValue.getFullYear(), minDateValue.getMonth(), minDateValue.getDate());return d < m;}function isAfterMaxDate(date, maxDate) {if (!maxDate) return false;const { date: maxDateValue } = extractDateTimeLimit(maxDate);const d = new Date(date.getFullYear(), date.getMonth(), date.getDate());const m = new Date(maxDateValue.getFullYear(), maxDateValue.getMonth(), maxDateValue.getDate());return d > m;}function createElement(tag, className, content) {const el = document.createElement(tag);if (className) el.className = className;if (content !== undefined) {if (typeof content === 'string' || typeof content === 'number') {el.textContent = content;} else if (content instanceof HTMLElement) {el.appendChild(content);}}return el;}class PodoDatePicker {constructor(container, options = {}) {this.container =typeof container === 'string' ? document.querySelector(container) : container;if (!this.container) {throw new Error('PodoDatePicker: Container element not found');}if (this.container._podoDatePicker) {return this.container._podoDatePicker;}this.mode = options.mode || 'instant';this.type = options.type || 'date';this.value = options.value || {};this.tempValue = { ...this.value };this.onChange = options.onChange;this.placeholder = options.placeholder;this.disabled = options.disabled || false;this.showActions = options.showActions ?? (this.mode === 'period' && this.type !== 'hour');this.align = options.align || 'left';this.disable = Array.isArray(options.disable) ? options.disable : [];this.enable = Array.isArray(options.enable) ? options.enable : [];this.minDate = options.minDate;this.maxDate = options.maxDate;this.minuteStep = options.minuteStep || 1;this.hourFormat = options.hourFormat || '24';this.disabledHours = Array.isArray(options.disabledHours) ? options.disabledHours : null;this.hourStep = options.hourStep || 1;this.texts = { ...DEFAULT_TEXTS, ...options.texts };this.format = options.format;this.initialCalendar = options.initialCalendar || {};this.yearRange = options.yearRange;this.quickSelect = options.quickSelect || false;if (options.texts?.quickSelect) {this.texts.quickSelect = { ...DEFAULT_TEXTS.quickSelect, ...options.texts.quickSelect };}this.isOpen = false;this.selectingPart = null;this.navigationStep = null;this._activePresetKey = null;this._navOffset = 0;if (this.quickSelect && this.mode === 'period' && this.value.date && this.value.endDate) {for (const key of QUICK_SELECT_KEYS) {const { start, end } = getPresetRange(key);if (isSameDay(this.value.date, start) && isSameDay(this.value.endDate, end)) {this._activePresetKey = key;this._navOffset = 0;this.navigationStep = getNavigationStepForPreset(key);break;}}}if (this.initialCalendar.start) {const fallback = this.value.date ? new Date(this.value.date) : new Date();this.viewDate = resolveCalendarInitial(this.initialCalendar.start, fallback);} else if (this.value.date) {this.viewDate = new Date(this.value.date);} else {this.viewDate = new Date();}if (this.initialCalendar.end) {const fallback = this.value.endDate? new Date(this.value.endDate.getFullYear(),this.value.endDate.getMonth() + 1,1): new Date();this.endViewDate = resolveCalendarInitial(this.initialCalendar.end, fallback);} else if (this.value.endDate) {this.endViewDate = new Date(this.value.endDate.getFullYear(),this.value.endDate.getMonth() + 1,1);} else {this.endViewDate = new Date(this.viewDate.getFullYear(),this.viewDate.getMonth() + 1,1);}this.render();this.bindEvents();this._lastMobileState = this.isMobileView();this._resizeHandler = () => {const isMobile = this.isMobileView();if (this.isOpen) {this.updateDropdownMaxWidth();if (this.mode === 'period' && isMobile !== this._lastMobileState) {this._lastMobileState = isMobile;this.renderDropdown();}}};window.addEventListener('resize', this._resizeHandler);this.container._podoDatePicker = this;}render() {this.container.innerHTML = '';this.container.className = PREFIX;const showNavigation = this.quickSelect && this.mode === 'period';this.inputEl = createElement('div', `${PREFIX}__input`);if (this.disabled) this.inputEl.classList.add(`${PREFIX}__input--disabled`);this.inputContentEl = createElement('div', `${PREFIX}__input-content`);this.renderInputContent();this.inputEl.appendChild(this.inputContentEl);const iconClass = this.type === 'time' || this.type === 'hour' ? 'icon-time' : 'icon-calendar';this.iconEl = createElement('i', `${PREFIX}__icon ${iconClass}`);this.inputEl.appendChild(this.iconEl);if (showNavigation) {this.inputEl.classList.add(`${PREFIX}__input--with-nav`);this._prevArrowBtn = createElement('button', `${PREFIX}__nav-arrow ${PREFIX}__nav-arrow--left`);this._prevArrowBtn.type = 'button';this._prevArrowBtn.innerHTML = '<i class="icon-expand-left"></i>';this._prevArrowBtn.disabled = this.disabled || this.isNavDisabled(-1);this._prevArrowBtn.addEventListener('click', (e) => { e.stopPropagation(); this.handleNavigate(-1); });this.inputEl.insertBefore(this._prevArrowBtn, this.inputContentEl);this._presetLabelEl = createElement('span', `${PREFIX}__preset-label`);this.updatePresetLabel();this.inputEl.insertBefore(this._presetLabelEl, this.inputContentEl);if (this.iconEl && this.iconEl.parentNode) {this.iconEl.parentNode.removeChild(this.iconEl);}this._nextArrowBtn = createElement('button', `${PREFIX}__nav-arrow ${PREFIX}__nav-arrow--right`);this._nextArrowBtn.type = 'button';this._nextArrowBtn.innerHTML = '<i class="icon-expand-right"></i>';this._nextArrowBtn.disabled = this.disabled || this.isNavDisabled(1);this._nextArrowBtn.addEventListener('click', (e) => { e.stopPropagation(); this.handleNavigate(1); });this.inputEl.appendChild(this._nextArrowBtn);}this.container.appendChild(this.inputEl);this.dropdownEl = createElement('div',`${PREFIX}__dropdown ${this.align === 'right' ? `${PREFIX}__dropdown--right` : ''}`);this.dropdownEl.style.display = 'none';this.container.appendChild(this.dropdownEl);}renderInputContent() {this.inputContentEl.innerHTML = '';const displayValue = this.showActions ? this.tempValue : this.value;if (this.type === 'date') {this.renderDateInput(displayValue);} else if (this.type === 'time') {this.renderTimeInput(displayValue);} else if (this.type === 'hour') {this.renderHourInput(displayValue);} else {this.renderDateTimeInput(displayValue);}}renderHourInput(displayValue) {const startSection = this.createHourSection(displayValue.time, 'hour');this.inputContentEl.appendChild(startSection);if (this.mode === 'period') {const sep = createElement('span', `${PREFIX}__separator`, '~');this.inputContentEl.appendChild(sep);const endSection = this.createHourSection(displayValue.endTime, 'endHour');this.inputContentEl.appendChild(endSection);}}createHourSection(time, part) {const section = createElement('div', `${PREFIX}__time-section ${PREFIX}__hour-section`);const select = this.createHourOnlySelect(time, part);section.appendChild(select);return section;}formatHourLabel(h) {if (this.hourFormat === '12') {const period = h < 12 ? '오전' : '오후';const h12 = h % 12 === 0 ? 12 : h % 12;return `${period} ${h12}시`;}return `${h}시`;}createHourOnlySelect(time, part) {const select = createElement('select', `${PREFIX}__time-select ${PREFIX}__hour-select`);if (!time) select.classList.add(`${PREFIX}__time-select--placeholder`);if (this.disabled) select.disabled = true;select.setAttribute('aria-label', part === 'endHour' ? '종료 시간 선택' : '시간 선택');const isEnd = part === 'endHour';const currentDate = isEnd ? this.tempValue.endDate : this.tempValue.date;for (let h = 0; h < 24; h += this.hourStep) {const opt = createElement('option', null, this.formatHourLabel(h));opt.value = h;if (this.disabledHours && this.disabledHours.includes(h)) {opt.disabled = true;} else if (this.isHourDisabled(h, currentDate)) {opt.disabled = true;}select.appendChild(opt);}select.value = time?.hour ?? 0;select.dataset.part = part;return select;}renderDateInput(displayValue) {this.startDateBtn = this.createDateButton(displayValue.date, 'date');this.inputContentEl.appendChild(this.startDateBtn);if (this.mode === 'period') {const sep = createElement('span', `${PREFIX}__separator`, '~');this.inputContentEl.appendChild(sep);this.endDateBtn = this.createDateButton(displayValue.endDate, 'endDate');this.inputContentEl.appendChild(this.endDateBtn);}}renderTimeInput(displayValue) {const startTimeSection = this.createTimeSection(displayValue.time, 'hour', 'minute');this.inputContentEl.appendChild(startTimeSection);if (this.mode === 'period') {const sep = createElement('span', `${PREFIX}__separator`, '~');this.inputContentEl.appendChild(sep);const endTimeSection = this.createTimeSection(displayValue.endTime, 'endHour', 'endMinute');this.inputContentEl.appendChild(endTimeSection);}}renderDateTimeInput(displayValue) {this.startDateBtn = this.createDateButton(displayValue.date, 'date');this.inputContentEl.appendChild(this.startDateBtn);const startTimeSection = this.createTimeSection(displayValue.time, 'hour', 'minute');this.inputContentEl.appendChild(startTimeSection);if (this.mode === 'period') {const sep = createElement('span', `${PREFIX}__separator`, '~');this.inputContentEl.appendChild(sep);this.endDateBtn = this.createDateButton(displayValue.endDate, 'endDate');this.inputContentEl.appendChild(this.endDateBtn);const endTimeSection = this.createTimeSection(displayValue.endTime, 'endHour', 'endMinute');this.inputContentEl.appendChild(endTimeSection);}}createDateButton(date, part) {const btn = createElement('button', `${PREFIX}__part`);btn.type = 'button';const dateFormat = getDateOnlyFormat(this.format);if (!date) {btn.classList.add(`${PREFIX}__part--placeholder`);const placeholderText = dateFormat? dateFormat.replace(/y/g, 'YYYY').replace(/m/g, 'MM').replace(/d/g, 'DD'): 'YYYY - MM - DD';btn.textContent = placeholderText;} else {const displayText = dateFormat? formatWithPattern(date, null, dateFormat): formatDate(date);btn.textContent = displayText;}btn.dataset.part = part;return btn;}createTimeSection(time, hourPart, minutePart) {const section = createElement('div', `${PREFIX}__time-section`);const hourSelect = this.createHourSelect(time, hourPart);section.appendChild(hourSelect);const sep = createElement('span', `${PREFIX}__time-separator`, ':');section.appendChild(sep);const minuteSelect = this.createMinuteSelect(time, minutePart);section.appendChild(minuteSelect);return section;}createHourSelect(time, part) {const select = createElement('select', `${PREFIX}__time-select`);if (!time) select.classList.add(`${PREFIX}__time-select--placeholder`);if (this.disabled) select.disabled = true;const isEnd = part === 'endHour';const currentDate = isEnd ? this.tempValue.endDate : this.tempValue.date;for (let h = 0; h < 24; h++) {const opt = createElement('option', null, String(h).padStart(2, '0'));opt.value = h;if (this.isHourDisabled(h, currentDate)) {opt.disabled = true;}select.appendChild(opt);}select.value = time?.hour ?? 0;select.dataset.part = part;return select;}createMinuteSelect(time, part) {const select = createElement('select', `${PREFIX}__time-select`);if (!time) select.classList.add(`${PREFIX}__time-select--placeholder`);if (this.disabled) select.disabled = true;const isEnd = part === 'endMinute';const currentDate = isEnd ? this.tempValue.endDate : this.tempValue.date;const currentTime = isEnd ? this.tempValue.endTime : this.tempValue.time;for (let m = 0; m < 60; m += this.minuteStep) {const opt = createElement('option', null, String(m).padStart(2, '0'));opt.value = m;if (this.isMinuteDisabled(m, currentDate, currentTime)) {opt.disabled = true;}select.appendChild(opt);}let minute = time?.minute ?? 0;if (minute % this.minuteStep !== 0) {minute = Math.floor(minute / this.minuteStep) * this.minuteStep;}select.value = minute;select.dataset.part = part;return select;}isHourDisabled(h, currentDate) {if (!currentDate) return false;const minLimit = this.minDate ? extractDateTimeLimit(this.minDate) : null;const maxLimit = this.maxDate ? extractDateTimeLimit(this.maxDate) : null;if (minLimit?.time && isSameDay(currentDate, minLimit.date)) {if (h < minLimit.time.hour) return true;}if (maxLimit?.time && isSameDay(currentDate, maxLimit.date)) {if (h > maxLimit.time.hour) return true;}return false;}isMinuteDisabled(m, currentDate, currentTime) {if (!currentDate || !currentTime) return false;const minLimit = this.minDate ? extractDateTimeLimit(this.minDate) : null;const maxLimit = this.maxDate ? extractDateTimeLimit(this.maxDate) : null;if (minLimit?.time && isSameDay(currentDate, minLimit.date) && currentTime.hour === minLimit.time.hour) {if (m < minLimit.time.minute) return true;}if (maxLimit?.time && isSameDay(currentDate, maxLimit.date) && currentTime.hour === maxLimit.time.hour) {if (m > maxLimit.time.minute) return true;}return false;}renderDropdown() {this.dropdownEl.innerHTML = '';if (this.mode === 'period') {const showQS = this.quickSelect && this.mode === 'period';if (showQS) {const body = createElement('div', `${PREFIX}__dropdown-body`);body.appendChild(this.renderQuickSelectPanel());const calWrapper = createElement('div', `${PREFIX}__period-calendars-wrapper`);this.renderPeriodCalendarsInto(calWrapper);body.appendChild(calWrapper);this.dropdownEl.appendChild(body);} else {this.renderPeriodCalendars();}} else {this.renderCalendar(this.viewDate, (date) => this.handleViewDateChange(date));}if (this.showActions) {this.renderActions();}}renderQuickSelectPanel() {const panel = createElement('div', `${PREFIX}__quick-select-panel`);QUICK_SELECT_KEYS.forEach((key) => {const label = this.texts.quickSelect?.[key] || key;const disabled = this.isQSPresetDisabled(key);const active = this.isQSPresetActive(key);const btn = document.createElement('button');btn.type = 'button';btn.textContent = label;btn.className = `${PREFIX}__quick-select-item`;if (active) btn.classList.add(`${PREFIX}__quick-select-item--active`);if (disabled) {btn.classList.add(`${PREFIX}__quick-select-item--disabled`);btn.disabled = true;}if (!disabled) {btn.addEventListener('mousedown', (e) => {e.preventDefault();e.stopPropagation();this.handleQuickSelect(key);});}panel.appendChild(btn);});return panel;}isQSPresetDisabled(key) {const { start, end } = getPresetRange(key);if (this.minDate) {const min = this.minDate instanceof Date ? this.minDate : this.minDate.date;const minDay = new Date(min.getFullYear(), min.getMonth(), min.getDate());if (end < minDay) return true;}if (this.maxDate) {const max = this.maxDate instanceof Date ? this.maxDate : this.maxDate.date;const maxDay = new Date(max.getFullYear(), max.getMonth(), max.getDate());if (start > maxDay) return true;}return false;}isQSPresetActive(key) {if (!this.tempValue.date || !this.tempValue.endDate) return false;const { start, end } = getPresetRange(key);return isSameDay(this.tempValue.date, start) && isSameDay(this.tempValue.endDate, end);}handleQuickSelect(key) {let { start, end } = getPresetRange(key);if (this.minDate) {const min = this.minDate instanceof Date ? this.minDate : this.minDate.date;const minDay = new Date(min.getFullYear(), min.getMonth(), min.getDate());if (start < minDay) start = minDay;}if (this.maxDate) {const max = this.maxDate instanceof Date ? this.maxDate : this.maxDate.date;const maxDay = new Date(max.getFullYear(), max.getMonth(), max.getDate());if (end > maxDay) end = maxDay;}this.tempValue = { date: start, endDate: end, time: this.tempValue.time, endTime: this.tempValue.endTime };this.viewDate = new Date(start.getFullYear(), start.getMonth(), 1);this.endViewDate = new Date(end.getFullYear(), end.getMonth(), 1);this.navigationStep = getNavigationStepForPreset(key);this._activePresetKey = key;this._navOffset = 0;if (!this.showActions) {this.value = { ...this.tempValue };this.onChange?.(this.value);this.close();}this.renderInputContent();this.updateNavArrows();this.renderDropdown();}renderPeriodCalendarsInto(container) {const wrapper = createElement('div', `${PREFIX}__period-calendars`);const isMobile = this.isMobileView();const leftCal = createElement('div', `${PREFIX}__period-calendar-left`);const leftCalendar = this.createCalendarElement(this.viewDate,(date) => this.handleViewDateChange(date),{ maxViewDate: isMobile ? undefined : this.endViewDate });leftCal.appendChild(leftCalendar);wrapper.appendChild(leftCal);const rightCal = createElement('div', `${PREFIX}__period-calendar-right`);const rightCalendar = this.createCalendarElement(this.endViewDate,(date) => this.handleEndViewDateChange(date),{ minViewDate: this.viewDate });rightCal.appendChild(rightCalendar);wrapper.appendChild(rightCal);container.appendChild(wrapper);}renderCalendar(viewDate, onViewDateChange, opts = {}) {const calendar = createElement('div', `${PREFIX}__calendar`);const nav = this.renderCalendarNav(viewDate, onViewDateChange, opts);calendar.appendChild(nav);const grid = this.renderCalendarGrid(viewDate);calendar.appendChild(grid);this.dropdownEl.appendChild(calendar);return calendar;}isMobileView() {return window.innerWidth <= 600;}renderPeriodCalendars() {const wrapper = createElement('div', `${PREFIX}__period-calendars`);const isMobile = this.isMobileView();const leftCal = createElement('div', `${PREFIX}__period-calendar-left`);const leftCalendar = this.createCalendarElement(this.viewDate,(date) => this.handleViewDateChange(date),{ maxViewDate: isMobile ? undefined : this.endViewDate });leftCal.appendChild(leftCalendar);wrapper.appendChild(leftCal);const rightCal = createElement('div', `${PREFIX}__period-calendar-right`);const rightCalendar = this.createCalendarElement(this.endViewDate,(date) => this.handleEndViewDateChange(date),{ minViewDate: this.viewDate });rightCal.appendChild(rightCalendar);wrapper.appendChild(rightCal);this.dropdownEl.appendChild(wrapper);}createCalendarElement(viewDate, onViewDateChange, opts = {}) {const calendar = createElement('div', `${PREFIX}__calendar`);const nav = this.renderCalendarNav(viewDate, onViewDateChange, opts);calendar.appendChild(nav);const grid = this.renderCalendarGrid(viewDate);calendar.appendChild(grid);return calendar;}calculateYearBounds(minViewYear, maxViewYear) {const currentYear = new Date().getFullYear();let minYearBound = currentYear - 100;let maxYearBound = currentYear + 100;if (this.minDate) {const { date } = extractDateTimeLimit(this.minDate);minYearBound = Math.max(minYearBound, date.getFullYear());}if (this.maxDate) {const { date } = extractDateTimeLimit(this.maxDate);maxYearBound = Math.min(maxYearBound, date.getFullYear());}if (this.yearRange?.min !== undefined) minYearBound = this.yearRange.min;if (this.yearRange?.max !== undefined) maxYearBound = this.yearRange.max;return { minYearBound, maxYearBound };}renderCalendarNav(viewDate, onViewDateChange, opts = {}) {const nav = createElement('div', `${PREFIX}__calendar-nav`);const year = viewDate.getFullYear();const month = viewDate.getMonth();const minViewDate = opts.minViewDate;const maxViewDate = opts.maxViewDate;const minYear = minViewDate?.getFullYear();const minMonth = minViewDate?.getMonth();const maxYear = maxViewDate?.getFullYear();const maxMonth = maxViewDate?.getMonth();const isPrevDisabled = minViewDate? year < minYear || (year === minYear && month <= minMonth): false;const isNextDisabled = maxViewDate? year > maxYear || (year === maxYear && month >= maxMonth): false;const prevBtn = createElement('button', `${PREFIX}__nav-button`);prevBtn.type = 'button';prevBtn.innerHTML = '<i class="icon-expand-left"></i>';if (isPrevDisabled) prevBtn.disabled = true;prevBtn.addEventListener('click', () => {if (!isPrevDisabled) {onViewDateChange(new Date(year, month - 1, 1));this.renderDropdown();}});nav.appendChild(prevBtn);const title = createElement('div', `${PREFIX}__nav-title`);const yearWrapper = createElement('div', `${PREFIX}__nav-select-wrapper`);const yearSelect = createElement('select', `${PREFIX}__nav-select`);const { minYearBound, maxYearBound } = this.calculateYearBounds(minYear, maxYear);for (let y = minYearBound; y <= maxYearBound; y++) {if (minYear !== undefined && y < minYear) continue;if (maxYear !== undefined && y > maxYear) continue;const opt = createElement('option', null, `${y}${this.texts.yearSuffix}`);opt.value = y;yearSelect.appendChild(opt);}yearSelect.value = year;yearSelect.addEventListener('change', (e) => {onViewDateChange(new Date(parseInt(e.target.value), month, 1));this.renderDropdown();});yearWrapper.appendChild(yearSelect);title.appendChild(yearWrapper);const monthWrapper = createElement('div', `${PREFIX}__nav-select-wrapper`);const monthSelect = createElement('select', `${PREFIX}__nav-select`);for (let m = 0; m < 12; m++) {if (minYear !== undefined && minMonth !== undefined && year === minYear && m < minMonth) continue;if (maxYear !== undefined && maxMonth !== undefined && year === maxYear && m > maxMonth) continue;const opt = createElement('option', null, this.texts.months[m]);opt.value = m;monthSelect.appendChild(opt);}monthSelect.value = month;monthSelect.addEventListener('change', (e) => {onViewDateChange(new Date(year, parseInt(e.target.value), 1));this.renderDropdown();});monthWrapper.appendChild(monthSelect);title.appendChild(monthWrapper);nav.appendChild(title);const nextBtn = createElement('button', `${PREFIX}__nav-button`);nextBtn.type = 'button';nextBtn.innerHTML = '<i class="icon-expand-right"></i>';if (isNextDisabled) nextBtn.disabled = true;nextBtn.addEventListener('click', () => {if (!isNextDisabled) {onViewDateChange(new Date(year, month + 1, 1));this.renderDropdown();}});nav.appendChild(nextBtn);return nav;}renderCalendarGrid(viewDate) {const grid = createElement('div', `${PREFIX}__calendar-grid`);const year = viewDate.getFullYear();const month = viewDate.getMonth();const today = new Date();const headerRow = createElement('div', `${PREFIX}__calendar-row`);this.texts.weekDays.forEach((day) => {const cell = createElement('div', `${PREFIX}__calendar-cell ${PREFIX}__calendar-cell--header`, day);headerRow.appendChild(cell);});grid.appendChild(headerRow);const daysInMonth = getDaysInMonth(year, month);const firstDay = getFirstDayOfMonth(year, month);const prevMonthDays = getDaysInMonth(year, month - 1);let days = [];for (let i = firstDay - 1; i >= 0; i--) {const day = prevMonthDays - i;const date = new Date(year, month - 1, day);days.push({ day, date, isOther: true });}for (let day = 1; day <= daysInMonth; day++) {const date = new Date(year, month, day);days.push({ day, date, isOther: false });}const totalCells = Math.ceil((firstDay + daysInMonth) / 7) * 7;const remainingDays = totalCells - (firstDay + daysInMonth);for (let day = 1; day <= remainingDays; day++) {const date = new Date(year, month + 1, day);days.push({ day, date, isOther: true });}for (let i = 0; i < days.length; i += 7) {const row = createElement('div', `${PREFIX}__calendar-row`);for (let j = 0; j < 7 && i + j < days.length; j++) {const { day, date, isOther } = days[i + j];const cell = this.createDayCell(day, date, isOther, today);row.appendChild(cell);}grid.appendChild(row);}return grid;}createDayCell(day, date, isOther, today) {const cell = createElement('button', `${PREFIX}__calendar-cell`);cell.type = 'button';cell.textContent = day;const isDisabled = this.checkDateDisabled(date);if (isOther) cell.classList.add(`${PREFIX}__calendar-cell--other`);if (isDisabled) {cell.classList.add(`${PREFIX}__calendar-cell--disabled`);cell.disabled = true;}const isToday = isSameDay(date, today);const isSelected = this.mode === 'instant' && isSameDay(date, this.tempValue.date);const isRangeStart = this.mode === 'period' && isSameDay(date, this.tempValue.date);const isRangeEnd = this.mode === 'period' && isSameDay(date, this.tempValue.endDate);const isInRangeDay =this.mode === 'period' &&this.tempValue.date &&this.tempValue.endDate &&isInRangeExclusive(date, this.tempValue.date, this.tempValue.endDate);if (isToday && !isSelected && !isRangeStart && !isRangeEnd) {cell.classList.add(`${PREFIX}__calendar-cell--today`);}if (isSelected) cell.classList.add(`${PREFIX}__calendar-cell--selected`);if (isRangeStart) cell.classList.add(`${PREFIX}__calendar-cell--range-start`);if (isRangeEnd) cell.classList.add(`${PREFIX}__calendar-cell--range-end`);if (isInRangeDay) cell.classList.add(`${PREFIX}__calendar-cell--in-range`);if (!isDisabled) {cell.addEventListener('click', () => this.handleDateSelect(date));}return cell;}checkDateDisabled(date) {if (isDateDisabled(date, this.disable, this.enable)) return true;if (isBeforeMinDate(date, this.minDate)) return true;if (isAfterMaxDate(date, this.maxDate)) return true;return false;}renderActions() {const actions = createElement('div', `${PREFIX}__actions`);const periodText = createElement('span', `${PREFIX}__period-text`);if (this.mode === 'period' && this.tempValue.date) {periodText.textContent = this.formatPeriodText();}actions.appendChild(periodText);const buttons = createElement('div', `${PREFIX}__action-buttons`);const resetBtn = createElement('button', `${PREFIX}__action-button ${PREFIX}__action-button--reset`);resetBtn.type = 'button';resetBtn.innerHTML = `<i class="icon-refresh"></i>${this.texts.reset}`;resetBtn.addEventListener('click', () => this.handleReset());buttons.appendChild(resetBtn);const applyBtn = createElement('button', `${PREFIX}__action-button ${PREFIX}__action-button--apply`);applyBtn.type = 'button';applyBtn.textContent = this.texts.apply;applyBtn.addEventListener('click', () => this.handleApply());buttons.appendChild(applyBtn);actions.appendChild(buttons);this.dropdownEl.appendChild(actions);}formatPeriodText() {if (!this.tempValue.date) return '';if (this.format) {const startText = formatWithPattern(this.tempValue.date, this.tempValue.time, this.format);if (this.tempValue.endDate) {const endText = formatWithPattern(this.tempValue.endDate, this.tempValue.endTime, this.format);return `${startText} ~ ${endText}`;}return startText;}const formatKoreanDateTime = (date, time) => {const year = date.getFullYear();const month = date.getMonth() + 1;const day = date.getDate();let dateStr = `${year}년 ${month}월 ${day}일`;if (this.type === 'datetime' && time) {const hours = String(time.hour).padStart(2, '0');const minutes = String(time.minute).padStart(2, '0');dateStr += ` ${hours}:${minutes}`;}return dateStr;};const startText = formatKoreanDateTime(this.tempValue.date, this.tempValue.time);if (this.tempValue.endDate) {const endText = formatKoreanDateTime(this.tempValue.endDate, this.tempValue.endTime);return `${startText} ~ ${endText}`;}return startText;}bindEvents() {this.inputEl.addEventListener('click', (e) => {if (this.disabled) return;const target = e.target;if (target.dataset.part === 'date' || target.dataset.part === 'endDate') {this.toggleDropdown(target.dataset.part);}});this.inputContentEl.addEventListener('change', (e) => {if (e.target.tagName !== 'SELECT') return;const part = e.target.dataset.part;const value = parseInt(e.target.value);this.handleTimeChange(part, value);});document.addEventListener('mousedown', (e) => {if (!this.container.contains(e.target) && this.isOpen) {this.close();}});}toggleDropdown(part) {if (this.selectingPart === part && this.isOpen) {this.close();} else {this.selectingPart = part;this.open();}}open() {this.isOpen = true;this.inputEl.classList.add(`${PREFIX}__input--active`);this.dropdownEl.style.display = 'flex';this.renderDropdown();this.updateDropdownMaxWidth();}updateDropdownMaxWidth() {if (!this.dropdownEl) return;const rect = this.dropdownEl.getBoundingClientRect();const viewportWidth = window.innerWidth;const padding = 8;const availableWidth = viewportWidth - rect.left - padding;if (availableWidth > 0) {this.dropdownEl.style.maxWidth = `${availableWidth}px`;} else {this.dropdownEl.style.maxWidth = '';}}close() {this.isOpen = false;this.selectingPart = null;this.inputEl.classList.remove(`${PREFIX}__input--active`);this.dropdownEl.style.display = 'none';}handleViewDateChange(date) {this.viewDate = date;}handleEndViewDateChange(date) {this.endViewDate = date;}handleDateSelect(date) {const newDate = new Date(date.getFullYear(), date.getMonth(), date.getDate());if (this.mode === 'instant') {const adjustedTime = this.adjustTimeForDate(newDate, this.tempValue.time);this.tempValue = { ...this.tempValue, date: newDate, time: adjustedTime };if (!this.showActions) {this.value = { ...this.tempValue };this.emitChange();}this.close();this.renderInputContent();return;}const existingStartDate = this.tempValue.date;const existingEndDate = this.tempValue.endDate;if (!existingStartDate) {const adjustedTime = this.adjustTimeForDate(newDate, this.tempValue.time);this.tempValue = { ...this.tempValue, date: newDate, time: adjustedTime };} else if (!existingEndDate) {const dateOnly = new Date(newDate.getFullYear(), newDate.getMonth(), newDate.getDate());const startDateOnly = new Date(existingStartDate.getFullYear(),existingStartDate.getMonth(),existingStartDate.getDate());if (dateOnly < startDateOnly) {const adjustedStartTime = this.adjustTimeForDate(newDate, this.tempValue.time);const adjustedEndTime = this.adjustTimeForDate(existingStartDate, this.tempValue.time);this.tempValue = {date: newDate,time: adjustedStartTime,endDate: existingStartDate,endTime: adjustedEndTime,};this.navigationStep = calculateNavigationStep(newDate, existingStartDate);this._activePresetKey = null;this._navOffset = 0;} else {const adjustedEndTime = this.adjustTimeForDate(newDate, this.tempValue.endTime);this.tempValue = { ...this.tempValue, endDate: newDate, endTime: adjustedEndTime };this.navigationStep = calculateNavigationStep(existingStartDate, newDate);this._activePresetKey = null;this._navOffset = 0;}} else {const adjustedTime = this.adjustTimeForDate(newDate, this.tempValue.time);this.tempValue = { date: newDate, time: adjustedTime, endDate: undefined, endTime: undefined };}this.renderDropdown();this.renderInputContent();}adjustTimeForDate(date, time) {if (!time) return time;const minLimit = this.minDate ? extractDateTimeLimit(this.minDate) : null;const maxLimit = this.maxDate ? extractDateTimeLimit(this.maxDate) : null;let adjustedHour = time.hour;let adjustedMinute = time.minute;if (minLimit?.time && isSameDay(date, minLimit.date)) {if (adjustedHour < minLimit.time.hour) {adjustedHour = minLimit.time.hour;adjustedMinute = Math.ceil(minLimit.time.minute / this.minuteStep) * this.minuteStep;} else if (adjustedHour === minLimit.time.hour && adjustedMinute < minLimit.time.minute) {adjustedMinute = Math.ceil(minLimit.time.minute / this.minuteStep) * this.minuteStep;}}if (maxLimit?.time && isSameDay(date, maxLimit.date)) {if (adjustedHour > maxLimit.time.hour) {adjustedHour = maxLimit.time.hour;adjustedMinute = Math.floor(maxLimit.time.minute / this.minuteStep) * this.minuteStep;} else if (adjustedHour === maxLimit.time.hour && adjustedMinute > maxLimit.time.minute) {adjustedMinute = Math.floor(maxLimit.time.minute / this.minuteStep) * this.minuteStep;}}if (adjustedHour !== time.hour || adjustedMinute !== time.minute) {return { hour: adjustedHour, minute: adjustedMinute };}return time;}handleTimeChange(part, value) {const isEnd = part === 'endHour' || part === 'endMinute';const isHour = part === 'hour' || part === 'endHour';const currentTime = isEnd ? this.tempValue.endTime : this.tempValue.time;let newTime = { hour: currentTime?.hour ?? 0, minute: currentTime?.minute ?? 0 };if (isHour) {newTime.hour = value;if (this.type === 'hour') {newTime.minute = 0;}const currentDate = this.type === 'hour' ? null : (isEnd ? this.tempValue.endDate : this.tempValue.date);if (currentDate) {const minLimit = this.minDate ? extractDateTimeLimit(this.minDate) : null;const maxLimit = this.maxDate ? extractDateTimeLimit(this.maxDate) : null;if (minLimit?.time && isSameDay(currentDate, minLimit.date) && value === minLimit.time.hour) {if (newTime.minute < minLimit.time.minute) {newTime.minute = Math.ceil(minLimit.time.minute / this.minuteStep) * this.minuteStep;}}if (maxLimit?.time && isSameDay(currentDate, maxLimit.date) && value === maxLimit.time.hour) {if (newTime.minute > maxLimit.time.minute) {newTime.minute = Math.floor(maxLimit.time.minute / this.minuteStep) * this.minuteStep;}}}} else {newTime.minute = value;}if (isEnd) {if (this.type !== 'hour' && !this.tempValue.endDate) {this.tempValue = { ...this.tempValue, endDate: new Date(), endTime: newTime };} else {this.tempValue = { ...this.tempValue, endTime: newTime };}} else {if (this.type !== 'hour' && !this.tempValue.date) {this.tempValue = { ...this.tempValue, date: new Date(), time: newTime };} else {this.tempValue = { ...this.tempValue, time: newTime };}}this.value = { ...this.tempValue };this.emitChange();this.renderInputContent();}handleNavigate(direction) {const dv = this.showActions ? this.tempValue : this.value;if (!dv?.date || !dv?.endDate || !this.navigationStep) return;const { start, end } = shiftDateRange(dv.date, dv.endDate, this.navigationStep, direction);if (this.minDate) {const min = this.minDate instanceof Date ? this.minDate : this.minDate.date;if (start < new Date(min.getFullYear(), min.getMonth(), min.getDate())) return;}if (this.maxDate) {const max = this.maxDate instanceof Date ? this.maxDate : this.maxDate.date;if (end > new Date(max.getFullYear(), max.getMonth(), max.getDate())) return;}this._navOffset = (this._navOffset || 0) + direction;const newValue = { date: start, endDate: end, time: dv.time, endTime: dv.endTime };this.tempValue = newValue;this.value = { ...newValue };this.viewDate = new Date(start.getFullYear(), start.getMonth(), 1);this.endViewDate = new Date(end.getFullYear(), end.getMonth(), 1);this.emitChange();this.renderInputContent();this.updateNavArrows();if (this.isOpen) this.renderDropdown();}isNavDisabled(direction) {const dv = this.showActions ? this.tempValue : this.value;if (!dv?.date || !dv?.endDate || !this.navigationStep) return true;const { start, end } = shiftDateRange(dv.date, dv.endDate, this.navigationStep, direction);if (direction === -1 && this.minDate) {const min = this.minDate instanceof Date ? this.minDate : this.minDate.date;return start < new Date(min.getFullYear(), min.getMonth(), min.getDate());}if (direction === 1 && this.maxDate) {const max = this.maxDate instanceof Date ? this.maxDate : this.maxDate.date;return end > new Date(max.getFullYear(), max.getMonth(), max.getDate());}return false;}updateNavArrows() {if (this._prevArrowBtn) {this._prevArrowBtn.disabled = this.disabled || this.isNavDisabled(-1);}if (this._nextArrowBtn) {this._nextArrowBtn.disabled = this.disabled || this.isNavDisabled(1);}this.updatePresetLabel();}getActivePresetLabel() {const dv = this.showActions ? this.tempValue : this.value;if (!dv?.date || !dv?.endDate) return null;for (const key of QUICK_SELECT_KEYS) {const { start, end } = getPresetRange(key);if (isSameDay(dv.date, start) && isSameDay(dv.endDate, end)) {return this.texts.quickSelect?.[key] || key;}}if (this._activePresetKey && this._navOffset !== 0) {return this._getNavOffsetLabel();}return null;}_getNavOffsetLabel() {const offset = Math.abs(this._navOffset);const isFuture = this._navOffset > 0;const step = this.navigationStep;if (!step) return null;if (step.type === 'month') {return isFuture ? `${offset}개월 후` : `${offset}개월 전`;}if (step.count === 1) {return isFuture ? `${offset}일 후` : `${offset}일 전`;}if (step.count === 7) {return isFuture ? `${offset}주 후` : `${offset}주 전`;}if (step.count === 30) {return isFuture ? `${offset * 30}일 후` : `${offset * 30}일 전`;}return isFuture ? `${offset * step.count}일 후` : `${offset * step.count}일 전`;}updatePresetLabel() {if (!this._presetLabelEl) return;const label = this.getActivePresetLabel();if (label) {this._presetLabelEl.textContent = `${label} :`;this._presetLabelEl.style.display = '';} else {this._presetLabelEl.textContent = '';this._presetLabelEl.style.display = 'none';}}handleReset() {this.tempValue = {};this._activePresetKey = null;this.navigationStep = null;this._navOffset = 0;if (this.isOpen) {this.renderDropdown();}this.renderInputContent();if (typeof this.options.onReset === 'function') {this.options.onReset();}}handleApply() {this.value = { ...this.tempValue };this.emitChange();this.close();this.renderInputContent();}emitChange() {if (this.onChange) {this.onChange(this.value);}}getValue() {return { ...this.value };}setValue(value) {this.value = value || {};this.tempValue = { ...this.value };if (value?.date) {this.viewDate = new Date(value.date);}if (value?.endDate) {this.endViewDate = new Date(value.endDate.getFullYear(), value.endDate.getMonth() + 1, 1);}if (this.quickSelect && this.mode === 'period' && value?.date && value?.endDate) {let matched = false;for (const key of QUICK_SELECT_KEYS) {const { start, end } = getPresetRange(key);if (isSameDay(value.date, start) && isSameDay(value.endDate, end)) {this._activePresetKey = key;this._navOffset = 0;this.navigationStep = getNavigationStepForPreset(key);matched = true;break;}}if (!matched) {this._activePresetKey = null;this._navOffset = 0;}}this.renderInputContent();this.updatePresetLabel();}clear() {this.value = {};this.tempValue = {};this.renderInputContent();this.emitChange();}enable() {this.disabled = false;this.inputEl.classList.remove(`${PREFIX}__input--disabled`);this.renderInputContent();}disable() {this.disabled = true;this.inputEl.classList.add(`${PREFIX}__input--disabled`);this.close();this.renderInputContent();}destroy() {if (this._resizeHandler) {window.removeEventListener('resize', this._resizeHandler);}if (this.container._podoDatePicker) {delete this.container._podoDatePicker;}this.container.innerHTML = '';}}return PodoDatePicker;});
|
package/cdn/podo-ui.css
CHANGED