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/vanilla/datepicker.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* Podo UI DatePicker - Vanilla JS
|
|
3
3
|
* A pure JavaScript date picker component without dependencies
|
|
4
4
|
*
|
|
5
|
-
* @version
|
|
5
|
+
* @version 1.2.0
|
|
6
6
|
* @license MIT
|
|
7
7
|
*/
|
|
8
8
|
|
|
@@ -320,7 +320,10 @@
|
|
|
320
320
|
* @param {HTMLElement|string} container - Container element or selector
|
|
321
321
|
* @param {Object} options - DatePicker options
|
|
322
322
|
* @param {string} [options.mode='instant'] - 'instant' or 'period'
|
|
323
|
-
* @param {string} [options.type='date'] - 'date', 'time', or '
|
|
323
|
+
* @param {string} [options.type='date'] - 'date', 'time', 'datetime', or 'hour'
|
|
324
|
+
* @param {string} [options.hourFormat='24'] - Hour display format ('24' | '12') when type='hour'
|
|
325
|
+
* @param {number[]} [options.disabledHours] - Hours (0-23) that cannot be selected when type='hour'
|
|
326
|
+
* @param {number} [options.hourStep=1] - Hour step interval (1,2,3,4,6,12) when type='hour'
|
|
324
327
|
* @param {Object} [options.value] - Initial value { date, time, endDate, endTime }
|
|
325
328
|
* @param {Function} [options.onChange] - Change callback
|
|
326
329
|
* @param {string} [options.placeholder] - Placeholder text
|
|
@@ -359,7 +362,8 @@
|
|
|
359
362
|
this.onChange = options.onChange;
|
|
360
363
|
this.placeholder = options.placeholder;
|
|
361
364
|
this.disabled = options.disabled || false;
|
|
362
|
-
|
|
365
|
+
// hour-only는 native select 단일 입력이라 적용/초기화 액션 불필요 → 즉시 commit
|
|
366
|
+
this.showActions = options.showActions ?? (this.mode === 'period' && this.type !== 'hour');
|
|
363
367
|
this.align = options.align || 'left';
|
|
364
368
|
// Ensure disable and enable are always arrays
|
|
365
369
|
this.disable = Array.isArray(options.disable) ? options.disable : [];
|
|
@@ -367,6 +371,9 @@
|
|
|
367
371
|
this.minDate = options.minDate;
|
|
368
372
|
this.maxDate = options.maxDate;
|
|
369
373
|
this.minuteStep = options.minuteStep || 1;
|
|
374
|
+
this.hourFormat = options.hourFormat || '24';
|
|
375
|
+
this.disabledHours = Array.isArray(options.disabledHours) ? options.disabledHours : null;
|
|
376
|
+
this.hourStep = options.hourStep || 1;
|
|
370
377
|
this.texts = { ...DEFAULT_TEXTS, ...options.texts };
|
|
371
378
|
this.format = options.format;
|
|
372
379
|
this.initialCalendar = options.initialCalendar || {};
|
|
@@ -474,8 +481,8 @@
|
|
|
474
481
|
this.renderInputContent();
|
|
475
482
|
this.inputEl.appendChild(this.inputContentEl);
|
|
476
483
|
|
|
477
|
-
// Icon
|
|
478
|
-
const iconClass = this.type === 'time' ? 'icon-time' : 'icon-calendar';
|
|
484
|
+
// Icon (time/hour 모두 icon-time)
|
|
485
|
+
const iconClass = this.type === 'time' || this.type === 'hour' ? 'icon-time' : 'icon-calendar';
|
|
479
486
|
this.iconEl = createElement('i', `${PREFIX}__icon ${iconClass}`);
|
|
480
487
|
this.inputEl.appendChild(this.iconEl);
|
|
481
488
|
|
|
@@ -528,12 +535,73 @@
|
|
|
528
535
|
this.renderDateInput(displayValue);
|
|
529
536
|
} else if (this.type === 'time') {
|
|
530
537
|
this.renderTimeInput(displayValue);
|
|
538
|
+
} else if (this.type === 'hour') {
|
|
539
|
+
this.renderHourInput(displayValue);
|
|
531
540
|
} else {
|
|
532
541
|
// datetime
|
|
533
542
|
this.renderDateTimeInput(displayValue);
|
|
534
543
|
}
|
|
535
544
|
}
|
|
536
545
|
|
|
546
|
+
// hour-only: 분 컬럼/구분자 없이 시 select만 렌더
|
|
547
|
+
renderHourInput(displayValue) {
|
|
548
|
+
const startSection = this.createHourSection(displayValue.time, 'hour');
|
|
549
|
+
this.inputContentEl.appendChild(startSection);
|
|
550
|
+
|
|
551
|
+
if (this.mode === 'period') {
|
|
552
|
+
const sep = createElement('span', `${PREFIX}__separator`, '~');
|
|
553
|
+
this.inputContentEl.appendChild(sep);
|
|
554
|
+
|
|
555
|
+
const endSection = this.createHourSection(displayValue.endTime, 'endHour');
|
|
556
|
+
this.inputContentEl.appendChild(endSection);
|
|
557
|
+
}
|
|
558
|
+
}
|
|
559
|
+
|
|
560
|
+
createHourSection(time, part) {
|
|
561
|
+
const section = createElement('div', `${PREFIX}__time-section ${PREFIX}__hour-section`);
|
|
562
|
+
const select = this.createHourOnlySelect(time, part);
|
|
563
|
+
section.appendChild(select);
|
|
564
|
+
return section;
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
// hour-only 라벨 포맷
|
|
568
|
+
formatHourLabel(h) {
|
|
569
|
+
if (this.hourFormat === '12') {
|
|
570
|
+
const period = h < 12 ? '오전' : '오후';
|
|
571
|
+
const h12 = h % 12 === 0 ? 12 : h % 12;
|
|
572
|
+
return `${period} ${h12}시`;
|
|
573
|
+
}
|
|
574
|
+
return `${h}시`;
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
createHourOnlySelect(time, part) {
|
|
578
|
+
const select = createElement('select', `${PREFIX}__time-select ${PREFIX}__hour-select`);
|
|
579
|
+
if (!time) select.classList.add(`${PREFIX}__time-select--placeholder`);
|
|
580
|
+
if (this.disabled) select.disabled = true;
|
|
581
|
+
select.setAttribute('aria-label', part === 'endHour' ? '종료 시간 선택' : '시간 선택');
|
|
582
|
+
|
|
583
|
+
const isEnd = part === 'endHour';
|
|
584
|
+
const currentDate = isEnd ? this.tempValue.endDate : this.tempValue.date;
|
|
585
|
+
|
|
586
|
+
for (let h = 0; h < 24; h += this.hourStep) {
|
|
587
|
+
const opt = createElement('option', null, this.formatHourLabel(h));
|
|
588
|
+
opt.value = h;
|
|
589
|
+
|
|
590
|
+
// disabledHours 우선
|
|
591
|
+
if (this.disabledHours && this.disabledHours.includes(h)) {
|
|
592
|
+
opt.disabled = true;
|
|
593
|
+
} else if (this.isHourDisabled(h, currentDate)) {
|
|
594
|
+
opt.disabled = true;
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
select.appendChild(opt);
|
|
598
|
+
}
|
|
599
|
+
|
|
600
|
+
select.value = time?.hour ?? 0;
|
|
601
|
+
select.dataset.part = part;
|
|
602
|
+
return select;
|
|
603
|
+
}
|
|
604
|
+
|
|
537
605
|
renderDateInput(displayValue) {
|
|
538
606
|
// Start date button
|
|
539
607
|
this.startDateBtn = this.createDateButton(displayValue.date, 'date');
|
|
@@ -1390,8 +1458,12 @@
|
|
|
1390
1458
|
|
|
1391
1459
|
if (isHour) {
|
|
1392
1460
|
newTime.hour = value;
|
|
1461
|
+
// hour-only: 분을 0으로 정규화하고 min/max time 보정 건너뜀
|
|
1462
|
+
if (this.type === 'hour') {
|
|
1463
|
+
newTime.minute = 0;
|
|
1464
|
+
}
|
|
1393
1465
|
// Auto-adjust minute if needed
|
|
1394
|
-
const currentDate = isEnd ? this.tempValue.endDate : this.tempValue.date;
|
|
1466
|
+
const currentDate = this.type === 'hour' ? null : (isEnd ? this.tempValue.endDate : this.tempValue.date);
|
|
1395
1467
|
if (currentDate) {
|
|
1396
1468
|
const minLimit = this.minDate ? extractDateTimeLimit(this.minDate) : null;
|
|
1397
1469
|
const maxLimit = this.maxDate ? extractDateTimeLimit(this.maxDate) : null;
|
|
@@ -1412,15 +1484,14 @@
|
|
|
1412
1484
|
}
|
|
1413
1485
|
|
|
1414
1486
|
if (isEnd) {
|
|
1415
|
-
//
|
|
1416
|
-
if (!this.tempValue.endDate) {
|
|
1487
|
+
// hour-only는 date 자동 채움을 건너뜀 (value는 time만 가져야 함)
|
|
1488
|
+
if (this.type !== 'hour' && !this.tempValue.endDate) {
|
|
1417
1489
|
this.tempValue = { ...this.tempValue, endDate: new Date(), endTime: newTime };
|
|
1418
1490
|
} else {
|
|
1419
1491
|
this.tempValue = { ...this.tempValue, endTime: newTime };
|
|
1420
1492
|
}
|
|
1421
1493
|
} else {
|
|
1422
|
-
|
|
1423
|
-
if (!this.tempValue.date) {
|
|
1494
|
+
if (this.type !== 'hour' && !this.tempValue.date) {
|
|
1424
1495
|
this.tempValue = { ...this.tempValue, date: new Date(), time: newTime };
|
|
1425
1496
|
} else {
|
|
1426
1497
|
this.tempValue = { ...this.tempValue, time: newTime };
|
|
@@ -1538,8 +1609,16 @@
|
|
|
1538
1609
|
}
|
|
1539
1610
|
|
|
1540
1611
|
handleReset() {
|
|
1612
|
+
// 선택값만 비우고 팝오버는 열린 상태 유지
|
|
1613
|
+
// close()를 호출하면 사용자가 "적용"을 누를 기회 없이 닫혀버리는 문제 수정
|
|
1541
1614
|
this.tempValue = {};
|
|
1542
|
-
this.
|
|
1615
|
+
this._activePresetKey = null;
|
|
1616
|
+
this.navigationStep = null;
|
|
1617
|
+
this._navOffset = 0;
|
|
1618
|
+
// 캘린더 그리드/액션 영역을 다시 그려서 비워진 선택 상태를 반영
|
|
1619
|
+
if (this.isOpen) {
|
|
1620
|
+
this.renderDropdown();
|
|
1621
|
+
}
|
|
1543
1622
|
this.renderInputContent();
|
|
1544
1623
|
if (typeof this.options.onReset === 'function') {
|
|
1545
1624
|
this.options.onReset();
|