@iyulab/components 1.33.4 → 1.35.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/CHANGELOG.md CHANGED
@@ -1,5 +1,42 @@
1
1
  # Changelog
2
2
 
3
+ ## [1.35.0] - 2026-08-28
4
+
5
+ ### Added
6
+
7
+ - **`USelect` fires a `search` event on every search-input change when
8
+ `searchable`.** The search text was already computed internally to drive
9
+ the built-in local option filtering, but never surfaced — a consumer
10
+ binding `USelect` to a large server-paginated dataset had no way to hook
11
+ into it and had to rebuild the search UI from scratch to do remote
12
+ search. `search` (`detail: { query: string }`, non-cancelable) fires
13
+ alongside the existing local filtering, so a consumer can subscribe and
14
+ drive remote search without losing the built-in search input. Mapped
15
+ through to the React wrapper as `onSearch`.
16
+
17
+ ## [1.34.0] - 2026-08-28
18
+
19
+ ### Added
20
+
21
+ - **`UDatePicker` gains a `mode="datetime"` option for combined date+time
22
+ selection.** Previously the component had no concept of time of day at
23
+ all, so a date+time field had no picker UI and consumers fell back to
24
+ free-text input with no format guarantee. `mode="datetime"` adds a native
25
+ time input to the calendar popover; the resulting `value` is always a
26
+ complete ISO-8601 `DateTimeOffset` string — seconds and the local UTC
27
+ offset are filled in automatically regardless of how coarse the time
28
+ input was. Picking a day preserves whatever time was already set; only
29
+ the "today" quick action overwrites both to right now. 12/24-hour display
30
+ is left to the browser's native time input rather than a custom widget.
31
+ Adds the `time` locale key (14 locales).
32
+ - **`UDatePicker`'s calendar popover gains visible "today"/"clear"
33
+ quick-action buttons.** The calendar already computed today's date (for
34
+ the today-cell highlight) and already exposed value clearing via the
35
+ trigger's clear icon — these buttons just surface both as visible,
36
+ keyboard-accessible controls in the popover footer. Adds the `today`
37
+ locale key (14 locales), reusing the existing `clear` key for the reset
38
+ button label.
39
+
3
40
  ## [1.33.4] - 2026-08-27
4
41
 
5
42
  ### Fixed
@@ -11,6 +11,8 @@ var tooLong = "يجب ألا تتجاوز {max}";
11
11
  var increment = "زيادة القيمة";
12
12
  var decrement = "إنقاص القيمة";
13
13
  var clear = "مسح القيمة";
14
+ var today = "اليوم";
15
+ var time = "الوقت";
14
16
  var showPassword = "إظهار كلمة المرور";
15
17
  var hidePassword = "إخفاء كلمة المرور";
16
18
  var previousSlide = "الشريحة السابقة";
@@ -29,6 +31,8 @@ var ar_default = {
29
31
  increment,
30
32
  decrement,
31
33
  clear,
34
+ today,
35
+ time,
32
36
  showPassword,
33
37
  hidePassword,
34
38
  previousSlide,
@@ -36,4 +40,4 @@ var ar_default = {
36
40
  goToSlide
37
41
  };
38
42
  //#endregion
39
- export { badInput, clear, decrement, ar_default as default, goToSlide, hidePassword, increment, nextSlide, patternMismatch, previousSlide, rangeOverflow, rangeUnderflow, showPassword, stepMismatch, tooLong, tooShort, typeMismatch, valueMissing };
43
+ export { badInput, clear, decrement, ar_default as default, goToSlide, hidePassword, increment, nextSlide, patternMismatch, previousSlide, rangeOverflow, rangeUnderflow, showPassword, stepMismatch, time, today, tooLong, tooShort, typeMismatch, valueMissing };
@@ -11,6 +11,8 @@ var tooLong = "Darf höchstens {max} betragen";
11
11
  var increment = "Wert erhöhen";
12
12
  var decrement = "Wert verringern";
13
13
  var clear = "Wert löschen";
14
+ var today = "Heute";
15
+ var time = "Uhrzeit";
14
16
  var showPassword = "Passwort anzeigen";
15
17
  var hidePassword = "Passwort verbergen";
16
18
  var previousSlide = "Vorherige Folie";
@@ -29,6 +31,8 @@ var de_default = {
29
31
  increment,
30
32
  decrement,
31
33
  clear,
34
+ today,
35
+ time,
32
36
  showPassword,
33
37
  hidePassword,
34
38
  previousSlide,
@@ -36,4 +40,4 @@ var de_default = {
36
40
  goToSlide
37
41
  };
38
42
  //#endregion
39
- export { badInput, clear, decrement, de_default as default, goToSlide, hidePassword, increment, nextSlide, patternMismatch, previousSlide, rangeOverflow, rangeUnderflow, showPassword, stepMismatch, tooLong, tooShort, typeMismatch, valueMissing };
43
+ export { badInput, clear, decrement, de_default as default, goToSlide, hidePassword, increment, nextSlide, patternMismatch, previousSlide, rangeOverflow, rangeUnderflow, showPassword, stepMismatch, time, today, tooLong, tooShort, typeMismatch, valueMissing };
@@ -11,6 +11,8 @@ var tooLong = "Must be at most {max}";
11
11
  var increment = "Increase value";
12
12
  var decrement = "Decrease value";
13
13
  var clear = "Clear value";
14
+ var today = "Today";
15
+ var time = "Time";
14
16
  var showPassword = "Show password";
15
17
  var hidePassword = "Hide password";
16
18
  var previousSlide = "Previous slide";
@@ -29,6 +31,8 @@ var en_default = {
29
31
  increment,
30
32
  decrement,
31
33
  clear,
34
+ today,
35
+ time,
32
36
  showPassword,
33
37
  hidePassword,
34
38
  previousSlide,
@@ -36,4 +40,4 @@ var en_default = {
36
40
  goToSlide
37
41
  };
38
42
  //#endregion
39
- export { badInput, clear, decrement, en_default as default, goToSlide, hidePassword, increment, nextSlide, patternMismatch, previousSlide, rangeOverflow, rangeUnderflow, showPassword, stepMismatch, tooLong, tooShort, typeMismatch, valueMissing };
43
+ export { badInput, clear, decrement, en_default as default, goToSlide, hidePassword, increment, nextSlide, patternMismatch, previousSlide, rangeOverflow, rangeUnderflow, showPassword, stepMismatch, time, today, tooLong, tooShort, typeMismatch, valueMissing };
@@ -11,6 +11,7 @@ var tooLong = "Debe ser como máximo {max}";
11
11
  var increment = "Aumentar valor";
12
12
  var decrement = "Disminuir valor";
13
13
  var clear = "Borrar valor";
14
+ var time = "Hora";
14
15
  var showPassword = "Mostrar contraseña";
15
16
  var hidePassword = "Ocultar contraseña";
16
17
  var previousSlide = "Diapositiva anterior";
@@ -29,6 +30,8 @@ var es_default = {
29
30
  increment,
30
31
  decrement,
31
32
  clear,
33
+ today: "Hoy",
34
+ time,
32
35
  showPassword,
33
36
  hidePassword,
34
37
  previousSlide,
@@ -36,4 +39,4 @@ var es_default = {
36
39
  goToSlide
37
40
  };
38
41
  //#endregion
39
- export { badInput, clear, decrement, es_default as default, goToSlide, hidePassword, increment, nextSlide, patternMismatch, previousSlide, rangeOverflow, rangeUnderflow, showPassword, stepMismatch, tooLong, tooShort, typeMismatch, valueMissing };
42
+ export { badInput, clear, decrement, es_default as default, goToSlide, hidePassword, increment, nextSlide, patternMismatch, previousSlide, rangeOverflow, rangeUnderflow, showPassword, stepMismatch, time, tooLong, tooShort, typeMismatch, valueMissing };
@@ -11,6 +11,8 @@ var tooLong = "Doit être au plus {max}";
11
11
  var increment = "Augmenter la valeur";
12
12
  var decrement = "Diminuer la valeur";
13
13
  var clear = "Effacer la valeur";
14
+ var today = "Aujourd’hui";
15
+ var time = "Heure";
14
16
  var showPassword = "Afficher le mot de passe";
15
17
  var hidePassword = "Masquer le mot de passe";
16
18
  var previousSlide = "Diapositive précédente";
@@ -29,6 +31,8 @@ var fr_default = {
29
31
  increment,
30
32
  decrement,
31
33
  clear,
34
+ today,
35
+ time,
32
36
  showPassword,
33
37
  hidePassword,
34
38
  previousSlide,
@@ -36,4 +40,4 @@ var fr_default = {
36
40
  goToSlide
37
41
  };
38
42
  //#endregion
39
- export { badInput, clear, decrement, fr_default as default, goToSlide, hidePassword, increment, nextSlide, patternMismatch, previousSlide, rangeOverflow, rangeUnderflow, showPassword, stepMismatch, tooLong, tooShort, typeMismatch, valueMissing };
43
+ export { badInput, clear, decrement, fr_default as default, goToSlide, hidePassword, increment, nextSlide, patternMismatch, previousSlide, rangeOverflow, rangeUnderflow, showPassword, stepMismatch, time, today, tooLong, tooShort, typeMismatch, valueMissing };
@@ -11,6 +11,8 @@ var tooLong = "Harus maksimal {max}";
11
11
  var increment = "Tambah nilai";
12
12
  var decrement = "Kurangi nilai";
13
13
  var clear = "Hapus nilai";
14
+ var today = "Hari ini";
15
+ var time = "Waktu";
14
16
  var showPassword = "Tampilkan kata sandi";
15
17
  var hidePassword = "Sembunyikan kata sandi";
16
18
  var previousSlide = "Slide sebelumnya";
@@ -29,6 +31,8 @@ var id_default = {
29
31
  increment,
30
32
  decrement,
31
33
  clear,
34
+ today,
35
+ time,
32
36
  showPassword,
33
37
  hidePassword,
34
38
  previousSlide,
@@ -36,4 +40,4 @@ var id_default = {
36
40
  goToSlide
37
41
  };
38
42
  //#endregion
39
- export { badInput, clear, decrement, id_default as default, goToSlide, hidePassword, increment, nextSlide, patternMismatch, previousSlide, rangeOverflow, rangeUnderflow, showPassword, stepMismatch, tooLong, tooShort, typeMismatch, valueMissing };
43
+ export { badInput, clear, decrement, id_default as default, goToSlide, hidePassword, increment, nextSlide, patternMismatch, previousSlide, rangeOverflow, rangeUnderflow, showPassword, stepMismatch, time, today, tooLong, tooShort, typeMismatch, valueMissing };
@@ -11,6 +11,8 @@ var tooLong = "{max}以下である必要があります";
11
11
  var increment = "値を増やす";
12
12
  var decrement = "値を減らす";
13
13
  var clear = "値をクリア";
14
+ var today = "今日";
15
+ var time = "時刻";
14
16
  var showPassword = "パスワードを表示";
15
17
  var hidePassword = "パスワードを隠す";
16
18
  var previousSlide = "前のスライド";
@@ -29,6 +31,8 @@ var ja_default = {
29
31
  increment,
30
32
  decrement,
31
33
  clear,
34
+ today,
35
+ time,
32
36
  showPassword,
33
37
  hidePassword,
34
38
  previousSlide,
@@ -36,4 +40,4 @@ var ja_default = {
36
40
  goToSlide
37
41
  };
38
42
  //#endregion
39
- export { badInput, clear, decrement, ja_default as default, goToSlide, hidePassword, increment, nextSlide, patternMismatch, previousSlide, rangeOverflow, rangeUnderflow, showPassword, stepMismatch, tooLong, tooShort, typeMismatch, valueMissing };
43
+ export { badInput, clear, decrement, ja_default as default, goToSlide, hidePassword, increment, nextSlide, patternMismatch, previousSlide, rangeOverflow, rangeUnderflow, showPassword, stepMismatch, time, today, tooLong, tooShort, typeMismatch, valueMissing };
@@ -11,6 +11,8 @@ var tooLong = "최대 {max} 이하여야 합니다";
11
11
  var increment = "값 증가";
12
12
  var decrement = "값 감소";
13
13
  var clear = "값 지우기";
14
+ var today = "오늘";
15
+ var time = "시간";
14
16
  var showPassword = "비밀번호 표시";
15
17
  var hidePassword = "비밀번호 숨기기";
16
18
  var previousSlide = "이전 슬라이드";
@@ -29,6 +31,8 @@ var ko_default = {
29
31
  increment,
30
32
  decrement,
31
33
  clear,
34
+ today,
35
+ time,
32
36
  showPassword,
33
37
  hidePassword,
34
38
  previousSlide,
@@ -36,4 +40,4 @@ var ko_default = {
36
40
  goToSlide
37
41
  };
38
42
  //#endregion
39
- export { badInput, clear, decrement, ko_default as default, goToSlide, hidePassword, increment, nextSlide, patternMismatch, previousSlide, rangeOverflow, rangeUnderflow, showPassword, stepMismatch, tooLong, tooShort, typeMismatch, valueMissing };
43
+ export { badInput, clear, decrement, ko_default as default, goToSlide, hidePassword, increment, nextSlide, patternMismatch, previousSlide, rangeOverflow, rangeUnderflow, showPassword, stepMismatch, time, today, tooLong, tooShort, typeMismatch, valueMissing };
@@ -11,6 +11,8 @@ var tooLong = "Deve ser no máximo {max}";
11
11
  var increment = "Aumentar valor";
12
12
  var decrement = "Diminuir valor";
13
13
  var clear = "Limpar valor";
14
+ var today = "Hoje";
15
+ var time = "Hora";
14
16
  var showPassword = "Mostrar senha";
15
17
  var hidePassword = "Ocultar senha";
16
18
  var previousSlide = "Slide anterior";
@@ -29,6 +31,8 @@ var pt_BR_default = {
29
31
  increment,
30
32
  decrement,
31
33
  clear,
34
+ today,
35
+ time,
32
36
  showPassword,
33
37
  hidePassword,
34
38
  previousSlide,
@@ -36,4 +40,4 @@ var pt_BR_default = {
36
40
  goToSlide
37
41
  };
38
42
  //#endregion
39
- export { badInput, clear, decrement, pt_BR_default as default, goToSlide, hidePassword, increment, nextSlide, patternMismatch, previousSlide, rangeOverflow, rangeUnderflow, showPassword, stepMismatch, tooLong, tooShort, typeMismatch, valueMissing };
43
+ export { badInput, clear, decrement, pt_BR_default as default, goToSlide, hidePassword, increment, nextSlide, patternMismatch, previousSlide, rangeOverflow, rangeUnderflow, showPassword, stepMismatch, time, today, tooLong, tooShort, typeMismatch, valueMissing };
@@ -11,6 +11,8 @@ var tooLong = "Должно быть не более {max}";
11
11
  var increment = "Увеличить значение";
12
12
  var decrement = "Уменьшить значение";
13
13
  var clear = "Очистить значение";
14
+ var today = "Сегодня";
15
+ var time = "Время";
14
16
  var showPassword = "Показать пароль";
15
17
  var hidePassword = "Скрыть пароль";
16
18
  var previousSlide = "Предыдущий слайд";
@@ -29,6 +31,8 @@ var ru_default = {
29
31
  increment,
30
32
  decrement,
31
33
  clear,
34
+ today,
35
+ time,
32
36
  showPassword,
33
37
  hidePassword,
34
38
  previousSlide,
@@ -36,4 +40,4 @@ var ru_default = {
36
40
  goToSlide
37
41
  };
38
42
  //#endregion
39
- export { badInput, clear, decrement, ru_default as default, goToSlide, hidePassword, increment, nextSlide, patternMismatch, previousSlide, rangeOverflow, rangeUnderflow, showPassword, stepMismatch, tooLong, tooShort, typeMismatch, valueMissing };
43
+ export { badInput, clear, decrement, ru_default as default, goToSlide, hidePassword, increment, nextSlide, patternMismatch, previousSlide, rangeOverflow, rangeUnderflow, showPassword, stepMismatch, time, today, tooLong, tooShort, typeMismatch, valueMissing };
@@ -11,6 +11,8 @@ var tooLong = "ต้องไม่เกิน {max}";
11
11
  var increment = "เพิ่มค่า";
12
12
  var decrement = "ลดค่า";
13
13
  var clear = "ล้างค่า";
14
+ var today = "วันนี้";
15
+ var time = "เวลา";
14
16
  var showPassword = "แสดงรหัสผ่าน";
15
17
  var hidePassword = "ซ่อนรหัสผ่าน";
16
18
  var previousSlide = "สไลด์ก่อนหน้า";
@@ -29,6 +31,8 @@ var th_default = {
29
31
  increment,
30
32
  decrement,
31
33
  clear,
34
+ today,
35
+ time,
32
36
  showPassword,
33
37
  hidePassword,
34
38
  previousSlide,
@@ -36,4 +40,4 @@ var th_default = {
36
40
  goToSlide
37
41
  };
38
42
  //#endregion
39
- export { badInput, clear, decrement, th_default as default, goToSlide, hidePassword, increment, nextSlide, patternMismatch, previousSlide, rangeOverflow, rangeUnderflow, showPassword, stepMismatch, tooLong, tooShort, typeMismatch, valueMissing };
43
+ export { badInput, clear, decrement, th_default as default, goToSlide, hidePassword, increment, nextSlide, patternMismatch, previousSlide, rangeOverflow, rangeUnderflow, showPassword, stepMismatch, time, today, tooLong, tooShort, typeMismatch, valueMissing };
@@ -11,6 +11,8 @@ var tooLong = "Phải nhỏ hơn hoặc bằng {max}";
11
11
  var increment = "Tăng giá trị";
12
12
  var decrement = "Giảm giá trị";
13
13
  var clear = "Xóa giá trị";
14
+ var today = "Hôm nay";
15
+ var time = "Giờ";
14
16
  var showPassword = "Hiện mật khẩu";
15
17
  var hidePassword = "Ẩn mật khẩu";
16
18
  var previousSlide = "Slide trước";
@@ -29,6 +31,8 @@ var vi_default = {
29
31
  increment,
30
32
  decrement,
31
33
  clear,
34
+ today,
35
+ time,
32
36
  showPassword,
33
37
  hidePassword,
34
38
  previousSlide,
@@ -36,4 +40,4 @@ var vi_default = {
36
40
  goToSlide
37
41
  };
38
42
  //#endregion
39
- export { badInput, clear, decrement, vi_default as default, goToSlide, hidePassword, increment, nextSlide, patternMismatch, previousSlide, rangeOverflow, rangeUnderflow, showPassword, stepMismatch, tooLong, tooShort, typeMismatch, valueMissing };
43
+ export { badInput, clear, decrement, vi_default as default, goToSlide, hidePassword, increment, nextSlide, patternMismatch, previousSlide, rangeOverflow, rangeUnderflow, showPassword, stepMismatch, time, today, tooLong, tooShort, typeMismatch, valueMissing };
@@ -11,6 +11,8 @@ var tooLong = "最多不能超过{max}";
11
11
  var increment = "增加值";
12
12
  var decrement = "减少值";
13
13
  var clear = "清除值";
14
+ var today = "今天";
15
+ var time = "时间";
14
16
  var showPassword = "显示密码";
15
17
  var hidePassword = "隐藏密码";
16
18
  var previousSlide = "上一张幻灯片";
@@ -29,6 +31,8 @@ var zh_CN_default = {
29
31
  increment,
30
32
  decrement,
31
33
  clear,
34
+ today,
35
+ time,
32
36
  showPassword,
33
37
  hidePassword,
34
38
  previousSlide,
@@ -36,4 +40,4 @@ var zh_CN_default = {
36
40
  goToSlide
37
41
  };
38
42
  //#endregion
39
- export { badInput, clear, decrement, zh_CN_default as default, goToSlide, hidePassword, increment, nextSlide, patternMismatch, previousSlide, rangeOverflow, rangeUnderflow, showPassword, stepMismatch, tooLong, tooShort, typeMismatch, valueMissing };
43
+ export { badInput, clear, decrement, zh_CN_default as default, goToSlide, hidePassword, increment, nextSlide, patternMismatch, previousSlide, rangeOverflow, rangeUnderflow, showPassword, stepMismatch, time, today, tooLong, tooShort, typeMismatch, valueMissing };
@@ -11,6 +11,8 @@ var tooLong = "最多不能超過{max}";
11
11
  var increment = "增加值";
12
12
  var decrement = "減少值";
13
13
  var clear = "清除值";
14
+ var today = "今天";
15
+ var time = "時間";
14
16
  var showPassword = "顯示密碼";
15
17
  var hidePassword = "隱藏密碼";
16
18
  var previousSlide = "上一張投影片";
@@ -29,6 +31,8 @@ var zh_TW_default = {
29
31
  increment,
30
32
  decrement,
31
33
  clear,
34
+ today,
35
+ time,
32
36
  showPassword,
33
37
  hidePassword,
34
38
  previousSlide,
@@ -36,4 +40,4 @@ var zh_TW_default = {
36
40
  goToSlide
37
41
  };
38
42
  //#endregion
39
- export { badInput, clear, decrement, zh_TW_default as default, goToSlide, hidePassword, increment, nextSlide, patternMismatch, previousSlide, rangeOverflow, rangeUnderflow, showPassword, stepMismatch, tooLong, tooShort, typeMismatch, valueMissing };
43
+ export { badInput, clear, decrement, zh_TW_default as default, goToSlide, hidePassword, increment, nextSlide, patternMismatch, previousSlide, rangeOverflow, rangeUnderflow, showPassword, stepMismatch, time, today, tooLong, tooShort, typeMismatch, valueMissing };
@@ -1,9 +1,14 @@
1
1
  import { PropertyValues } from 'lit';
2
2
  import { UFormControlElement } from '../UFormControlElement.js';
3
3
  import { UPopover } from '../popover/UPopover.js';
4
+ export type DatePickerMode = 'date' | 'datetime';
4
5
  /**
5
- * A single-date-selection form control. The value follows the same convention as the
6
- * native `input[type=date]`: an ISO `YYYY-MM-DD` string.
6
+ * A single-date(-time)-selection form control. In `mode="date"` (default) the value follows
7
+ * the same convention as the native `input[type=date]`: an ISO `YYYY-MM-DD` string. In
8
+ * `mode="datetime"` the value is a complete ISO-8601 `DateTimeOffset` string
9
+ * (`YYYY-MM-DDTHH:mm:ss±HH:mm`) — the component always fills in seconds and the browser's
10
+ * local UTC offset, so the value is unconditionally valid regardless of how coarse the time
11
+ * input was.
7
12
  *
8
13
  * The calendar week always starts on Sunday, regardless of locale — harmless for the
9
14
  * locales this library currently ships (en/ko), but not correct for locales where Monday
@@ -20,17 +25,25 @@ import { UPopover } from '../popover/UPopover.js';
20
25
  * @csspart calendar-weekdays - the weekday header row
21
26
  * @csspart calendar-grid - the date grid
22
27
  * @csspart day - a date cell button
28
+ * @csspart calendar-footer - the row holding the "today"/"clear" quick-action buttons
29
+ * @csspart calendar-time - the row holding the time-of-day input (datetime mode only)
23
30
  *
24
31
  * @cssprop --date-picker-popover-width - width of the calendar popover (default: 296px, independent of trigger width — a fixed-width calendar reads more naturally)
25
32
  *
26
- * @event change - fires when the user clicks a date cell, confirms via keyboard, or clicks the clear button.
27
- * Programmatic value assignment does not fire it (same contract as native form controls).
33
+ * @event change - fires when the user clicks a date cell, confirms via keyboard, changes the
34
+ * time input (datetime mode, once a date is set), or clicks the clear button. Programmatic
35
+ * value assignment does not fire it (same contract as native form controls).
28
36
  */
29
37
  export declare class UDatePicker extends UFormControlElement<string> {
30
38
  static styles: import('lit').CSSResultGroup[];
31
- /** Minimum value (ISO YYYY-MM-DD) dates before this cannot be selected. */
39
+ /** `date` (default) selects a calendar day only. `datetime` also captures a time-of-day and
40
+ * the value becomes a complete ISO-8601 `DateTimeOffset` string. */
41
+ mode: DatePickerMode;
42
+ /** Minimum value (ISO YYYY-MM-DD) — dates before this cannot be selected. Date-only even in
43
+ * `mode="datetime"`; time-of-day is never range-checked. */
32
44
  min?: string;
33
- /** Maximum value (ISO YYYY-MM-DD) — dates after this cannot be selected. */
45
+ /** Maximum value (ISO YYYY-MM-DD) — dates after this cannot be selected. Date-only even in
46
+ * `mode="datetime"`; time-of-day is never range-checked. */
34
47
  max?: string;
35
48
  /** Whether to show the clear button */
36
49
  clearable: boolean;
@@ -43,14 +56,28 @@ export declare class UDatePicker extends UFormControlElement<string> {
43
56
  private open;
44
57
  private viewDate;
45
58
  private focusedDate;
59
+ /** Time-of-day for the next selection while no `value` exists yet (`mode="datetime"` only) —
60
+ * once `value` is set, the time input reads/writes its time portion directly instead. */
61
+ private pendingTime;
46
62
  private grabFocusOnUpdate;
47
63
  protected updated(changed: PropertyValues): void;
48
64
  render(): import('lit-html').TemplateResult<1>;
49
65
  private renderCalendar;
66
+ private renderTimeRow;
67
+ private renderFooter;
50
68
  private renderDay;
51
69
  private isOutOfRange;
52
70
  private focusDayButton;
71
+ /** `timeOverride` lets a caller force the time-of-day (the "today" quick action wants
72
+ * "right now", overriding whatever time was previously set) — a plain day-cell click omits
73
+ * it, which preserves the existing time-of-day (or `pendingTime`) so switching the date
74
+ * alone doesn't clobber a time the user already picked. */
53
75
  private selectDay;
76
+ /** Time input change — only commits into `value` once a date already exists (matches native
77
+ * `datetime-local`: a time alone isn't a complete value). Before that, it just remembers
78
+ * `pendingTime` for whenever a day gets picked. Doesn't close the popover or refocus the
79
+ * trigger — unlike selecting a day, adjusting the time doesn't conclude the interaction. */
80
+ private handleTimeChange;
54
81
  private handlePrevMonth;
55
82
  private handleNextMonth;
56
83
  private navigateMonth;
@@ -59,6 +86,16 @@ export declare class UDatePicker extends UFormControlElement<string> {
59
86
  private handlePopoverShow;
60
87
  private handlePopoverHide;
61
88
  private handleClearClick;
89
+ /** "오늘" 퀵액션 — `today` 셀이 이미 렌더에서 계산해 표시 중인 값(`renderDay`의
90
+ * `isSameDay(date, new Date())`)을 실제로 선택하는 것뿐이라 `selectDay`를 그대로 탄다
91
+ * (범위 밖이면 `selectDay`가 조용히 no-op — 클릭 불가 상태인 day 셀과 동일 규약).
92
+ * datetime 모드에서는 "지금"을 통째로 채우는 것이 요청의 본질(§D-28 항목 2 docket 코멘트)
93
+ * 이라 시간까지 `now`로 덮어쓴다 — 평범한 day 셀 클릭과 달리 기존 시각을 보존하지 않는다. */
94
+ private handleTodayClick;
95
+ /** 캘린더 팝오버 안 "초기화" 퀵액션 — 트리거의 clear 아이콘(`handleClearClick`)과 값을
96
+ * 비우는 로직은 같지만, 팝오버가 열린 채로 눌렸으므로 선택 완료와 동일하게 닫아 준다. */
97
+ private handleFooterResetClick;
98
+ private resetValue;
62
99
  /** suffix `u-icon`은 순수 표시 요소(버튼 아님)라 네이티브 키보드 활성화가 없다 —
63
100
  * `role="button"`+`tabindex="0"`로 포커스 가능하게 한 뒤, Enter/Space를 같은 클릭
64
101
  * 핸들러로 릴레이한다(`UInput`/`USelect`의 동일 패턴과 일치). */
@@ -1,6 +1,7 @@
1
1
  import __decorateMetadata from "../../_virtual/_@oxc-project_runtime@0.147.0/helpers/esm/decorateMetadata.js";
2
2
  import __decorate from "../../_virtual/_@oxc-project_runtime@0.147.0/helpers/esm/decorate.js";
3
3
  import { UFormControlElement } from "../UFormControlElement.js";
4
+ import "../button/UButton.js";
4
5
  import "../icon/UIcon.js";
5
6
  import "../icon-button/UIconButton.js";
6
7
  import { Locale } from "../../utilities/Locale.js";
@@ -34,6 +35,31 @@ function addDays(date, delta) {
34
35
  function daysInMonth(date) {
35
36
  return new Date(date.getFullYear(), date.getMonth() + 1, 0).getDate();
36
37
  }
38
+ /** `±HH:mm` for the browser's local timezone at `date` (DST-aware — recomputed per date,
39
+ * not cached — `getTimezoneOffset()`'s sign is the inverse of the ISO-8601 offset sign). */
40
+ function getLocalOffset(date) {
41
+ const minutes = -date.getTimezoneOffset();
42
+ const sign = minutes >= 0 ? "+" : "-";
43
+ const abs = Math.abs(minutes);
44
+ return `${sign}${String(Math.floor(abs / 60)).padStart(2, "0")}:${String(abs % 60).padStart(2, "0")}`;
45
+ }
46
+ /** Builds the `value` for the given mode — `datetime` always emits seconds + local offset so
47
+ * the result is unconditionally a valid, unambiguous ISO-8601 `DateTimeOffset` regardless of
48
+ * how coarse the UI input was (this is the guarantee the datetime mode request asked for). */
49
+ function buildValue(date, mode, time) {
50
+ return mode === "datetime" ? `${toISODate(date)}T${time}:00${getLocalOffset(date)}` : toISODate(date);
51
+ }
52
+ /** Splits a `value` into its date portion (as a `Date`, via the local `parseISODate` above —
53
+ * date-only, no timezone conversion) and its `HH:mm` time-of-day (`'00:00'` if absent —
54
+ * covers both plain date-mode values and a datetime value with no time captured yet). */
55
+ function splitValue(value) {
56
+ const [datePart, rest] = value.split("T");
57
+ const match = rest?.match(/^(\d{2}:\d{2})/);
58
+ return {
59
+ date: parseISODate(datePart),
60
+ time: match ? match[1] : "00:00"
61
+ };
62
+ }
37
63
  /** Cells to render for the calendar grid — leading `null`s pad the previous month's weekday offset. */
38
64
  function buildMonthGrid(viewDate) {
39
65
  const startOffset = startOfMonth(viewDate).getDay();
@@ -58,12 +84,23 @@ function getWeekdayLabels(locale) {
58
84
  var UDatePicker = class UDatePicker extends UFormControlElement {
59
85
  constructor(..._args) {
60
86
  super(..._args);
87
+ this.mode = "date";
61
88
  this.clearable = false;
62
89
  this.calendarId = `u-date-picker-calendar-${Math.random().toString(36).slice(2, 8)}`;
63
90
  this.open = false;
64
91
  this.viewDate = startOfMonth(/* @__PURE__ */ new Date());
65
92
  this.focusedDate = /* @__PURE__ */ new Date();
93
+ this.pendingTime = "00:00";
66
94
  this.grabFocusOnUpdate = false;
95
+ this.handleTimeChange = (e) => {
96
+ const time = e.target.value || "00:00";
97
+ this.pendingTime = time;
98
+ if (!this.value) return;
99
+ const iso = buildValue(splitValue(this.value).date, this.mode, time);
100
+ const changed = iso !== this.value;
101
+ this.value = iso;
102
+ if (changed) this.emitChange();
103
+ };
67
104
  this.handlePrevMonth = () => this.navigateMonth(-1);
68
105
  this.handleNextMonth = () => this.navigateMonth(1);
69
106
  this.handleDayKeydown = (e, date) => {
@@ -106,7 +143,7 @@ var UDatePicker = class UDatePicker extends UFormControlElement {
106
143
  this.handlePopoverShow = () => {
107
144
  this.open = true;
108
145
  this.grabFocusOnUpdate = true;
109
- const base = this.value ? parseISODate(this.value) : /* @__PURE__ */ new Date();
146
+ const base = this.value ? splitValue(this.value).date : /* @__PURE__ */ new Date();
110
147
  this.viewDate = startOfMonth(base);
111
148
  this.focusedDate = base;
112
149
  };
@@ -116,9 +153,17 @@ var UDatePicker = class UDatePicker extends UFormControlElement {
116
153
  this.handleClearClick = (e) => {
117
154
  e.preventDefault();
118
155
  e.stopPropagation();
119
- const hadValue = !!this.value;
120
- this.value = void 0;
121
- if (hadValue) this.emitChange();
156
+ this.resetValue();
157
+ this.containerEl?.focus();
158
+ };
159
+ this.handleTodayClick = () => {
160
+ const now = /* @__PURE__ */ new Date();
161
+ const time = this.mode === "datetime" ? `${String(now.getHours()).padStart(2, "0")}:${String(now.getMinutes()).padStart(2, "0")}` : void 0;
162
+ this.selectDay(now, time);
163
+ };
164
+ this.handleFooterResetClick = () => {
165
+ this.resetValue();
166
+ this.popoverEl?.hide();
122
167
  this.containerEl?.focus();
123
168
  };
124
169
  this.handleClearKeydown = (e) => {
@@ -139,7 +184,10 @@ var UDatePicker = class UDatePicker extends UFormControlElement {
139
184
  }
140
185
  }
141
186
  render() {
142
- const displayText = this.value ? formatDate(this.value) : "";
187
+ const displayText = this.value ? formatDate(this.value, this.mode === "datetime" ? {
188
+ dateStyle: "medium",
189
+ timeStyle: "short"
190
+ } : void 0) : "";
143
191
  return html`
144
192
  <u-field part="field"
145
193
  ?required=${this.required}
@@ -216,11 +264,37 @@ var UDatePicker = class UDatePicker extends UFormControlElement {
216
264
  </div>
217
265
  `)}
218
266
  </div>
267
+ ${this.renderTimeRow()}
268
+ ${this.renderFooter()}
269
+ </div>
270
+ `;
271
+ }
272
+ renderTimeRow() {
273
+ if (this.mode !== "datetime") return "";
274
+ const time = this.value ? splitValue(this.value).time : this.pendingTime;
275
+ return html`
276
+ <div class="calendar-time" part="calendar-time">
277
+ <input type="time" class="time-input" part="time-input"
278
+ aria-label=${Locale.getValue("time")}
279
+ .value=${time}
280
+ @change=${this.handleTimeChange}
281
+ />
282
+ </div>
283
+ `;
284
+ }
285
+ renderFooter() {
286
+ const todayDisabled = this.isOutOfRange(/* @__PURE__ */ new Date());
287
+ return html`
288
+ <div class="calendar-footer" part="calendar-footer">
289
+ <u-button variant="ghost" size="sm" ?disabled=${todayDisabled} @click=${this.handleTodayClick}>${Locale.getValue("today")}</u-button>
290
+ ${this.clearable && this.value ? html`
291
+ <u-button variant="ghost" size="sm" @click=${this.handleFooterResetClick}>${Locale.getValue("clear")}</u-button>
292
+ ` : ""}
219
293
  </div>
220
294
  `;
221
295
  }
222
296
  renderDay(date) {
223
- const selected = this.value ? isSameDay(date, parseISODate(this.value)) : false;
297
+ const selected = this.value ? isSameDay(date, splitValue(this.value).date) : false;
224
298
  const focused = isSameDay(date, this.focusedDate);
225
299
  const today = isSameDay(date, /* @__PURE__ */ new Date());
226
300
  const outOfRange = this.isOutOfRange(date);
@@ -249,11 +323,17 @@ var UDatePicker = class UDatePicker extends UFormControlElement {
249
323
  const iso = toISODate(date);
250
324
  this.renderRoot.querySelector(`button.day[data-iso="${iso}"]`)?.focus();
251
325
  }
252
- selectDay(date) {
326
+ /** `timeOverride` lets a caller force the time-of-day (the "today" quick action wants
327
+ * "right now", overriding whatever time was previously set) — a plain day-cell click omits
328
+ * it, which preserves the existing time-of-day (or `pendingTime`) so switching the date
329
+ * alone doesn't clobber a time the user already picked. */
330
+ selectDay(date, timeOverride) {
253
331
  if (this.isOutOfRange(date)) return;
254
- const iso = toISODate(date);
332
+ const time = timeOverride ?? (this.value ? splitValue(this.value).time : this.pendingTime);
333
+ const iso = buildValue(date, this.mode, time);
255
334
  const changed = iso !== this.value;
256
335
  this.value = iso;
336
+ if (this.mode === "datetime") this.pendingTime = time;
257
337
  if (changed) this.emitChange();
258
338
  this.popoverEl?.hide();
259
339
  this.containerEl?.focus();
@@ -271,6 +351,11 @@ var UDatePicker = class UDatePicker extends UFormControlElement {
271
351
  if (next.getMonth() !== this.viewDate.getMonth() || next.getFullYear() !== this.viewDate.getFullYear()) this.viewDate = startOfMonth(next);
272
352
  this.focusedDate = next;
273
353
  }
354
+ resetValue() {
355
+ const hadValue = !!this.value;
356
+ this.value = void 0;
357
+ if (hadValue) this.emitChange();
358
+ }
274
359
  emitChange() {
275
360
  if (!this.novalidate) this.validate();
276
361
  this.dispatchEvent(new Event("change", {
@@ -284,10 +369,10 @@ var UDatePicker = class UDatePicker extends UFormControlElement {
284
369
  if (this.required && !this.value) {
285
370
  flags = { valueMissing: true };
286
371
  message = Locale.getValue("valueMissing");
287
- } else if (this.value && this.min && parseISODate(this.value).getTime() < parseISODate(this.min).getTime()) {
372
+ } else if (this.value && this.min && splitValue(this.value).date.getTime() < parseISODate(this.min).getTime()) {
288
373
  flags = { rangeUnderflow: true };
289
374
  message = Locale.getValue("rangeUnderflow", { min: this.min });
290
- } else if (this.value && this.max && parseISODate(this.value).getTime() > parseISODate(this.max).getTime()) {
375
+ } else if (this.value && this.max && splitValue(this.value).date.getTime() > parseISODate(this.max).getTime()) {
291
376
  flags = { rangeOverflow: true };
292
377
  message = Locale.getValue("rangeOverflow", { max: this.max });
293
378
  }
@@ -308,6 +393,10 @@ var UDatePicker = class UDatePicker extends UFormControlElement {
308
393
  this.containerEl?.blur();
309
394
  }
310
395
  };
396
+ __decorate([property({
397
+ type: String,
398
+ reflect: true
399
+ }), __decorateMetadata("design:type", Object)], UDatePicker.prototype, "mode", void 0);
311
400
  __decorate([property({ type: String }), __decorateMetadata("design:type", String)], UDatePicker.prototype, "min", void 0);
312
401
  __decorate([property({ type: String }), __decorateMetadata("design:type", String)], UDatePicker.prototype, "max", void 0);
313
402
  __decorate([property({
@@ -320,6 +409,7 @@ __decorate([query("u-popover", true), __decorateMetadata("design:type", typeof U
320
409
  __decorate([state(), __decorateMetadata("design:type", Boolean)], UDatePicker.prototype, "open", void 0);
321
410
  __decorate([state(), __decorateMetadata("design:type", typeof Date === "undefined" ? Object : Date)], UDatePicker.prototype, "viewDate", void 0);
322
411
  __decorate([state(), __decorateMetadata("design:type", typeof Date === "undefined" ? Object : Date)], UDatePicker.prototype, "focusedDate", void 0);
412
+ __decorate([state(), __decorateMetadata("design:type", String)], UDatePicker.prototype, "pendingTime", void 0);
323
413
  UDatePicker = __decorate([customElement("u-date-picker")], UDatePicker);
324
414
  //#endregion
325
415
  export { UDatePicker };
@@ -142,6 +142,35 @@ var styles = css`
142
142
  color: var(--u-txt-color-disabled, #BDBDBD);
143
143
  cursor: not-allowed;
144
144
  }
145
+
146
+ .calendar-time {
147
+ display: flex;
148
+ justify-content: flex-end;
149
+ padding: 4px 4px 0;
150
+ }
151
+ .time-input {
152
+ font-family: inherit;
153
+ font-size: 0.9em;
154
+ padding: 2px 6px;
155
+ border: 1px solid var(--u-input-border-color, #E0E0E0);
156
+ border-radius: 0.25em;
157
+ background-color: var(--u-input-bg-color, #FFFFFF);
158
+ color: var(--u-txt-color, #212121);
159
+ }
160
+ .time-input:focus-visible {
161
+ outline: none;
162
+ box-shadow: 0 0 0 1px var(--u-input-border-color-focus, #1565C0);
163
+ }
164
+
165
+ .calendar-footer {
166
+ display: flex;
167
+ justify-content: space-between;
168
+ align-items: center;
169
+ gap: 8px;
170
+ padding-top: 8px;
171
+ margin-top: 4px;
172
+ border-top: 1px solid var(--u-border-color-weak, #EEEEEE);
173
+ }
145
174
  `;
146
175
  //#endregion
147
176
  export { styles };
@@ -24,6 +24,9 @@ export type SelectVariant = 'outlined' | 'filled' | 'underlined' | 'borderless';
24
24
  *
25
25
  * @event change - 사용자 상호작용(옵션 클릭·칩 제거·지우기)으로 선택 값이 변경될 때 발생.
26
26
  * 네이티브 select와 동일하게 프로그램적 value 세팅·옵션 등록으로는 발화하지 않는다.
27
+ * @event search - `searchable`일 때 검색 입력이 바뀔 때마다 발생(`detail: { query: string }`).
28
+ * 로컬 필터링(이미 렌더된 `u-option`의 `hidden` 토글)과 별개로 발행되므로, 서버/원격 검색이
29
+ * 필요한 소비자는 이 이벤트를 구독해 자체적으로 옵션을 갱신할 수 있다.
27
30
  */
28
31
  export declare class USelect extends UFormControlElement<string | string[]> {
29
32
  static styles: import('lit').CSSResultGroup[];
@@ -86,6 +86,10 @@ var USelect = class USelect extends UFormControlElement {
86
86
  const value = option.value.toLowerCase();
87
87
  option.hidden = !label.includes(query) && !value.includes(query);
88
88
  }
89
+ this.fire("search", {
90
+ detail: { query },
91
+ cancelable: false
92
+ });
89
93
  };
90
94
  this.handleSearchKeydown = (e) => {
91
95
  const options = this.options.filter((o) => !o.hidden && !o.disabled);
@@ -3,10 +3,11 @@ import { USelect as USelectElement } from '../components/select/USelect';
3
3
 
4
4
  export declare const USelect: React.ForwardRefExoticComponent<
5
5
  Omit<Partial<USelectElement>, keyof React.HTMLAttributes<USelectElement>>
6
- & Omit<React.HTMLAttributes<USelectElement>, 'onChange'>
6
+ & Omit<React.HTMLAttributes<USelectElement>, 'onChange' | 'onSearch'>
7
7
  & React.RefAttributes<USelectElement>
8
8
  & {
9
9
  onChange?: (event: CustomEvent) => void;
10
+ onSearch?: (event: CustomEvent<{ query: string }>) => void;
10
11
  }
11
12
  >;
12
13
 
@@ -8,5 +8,6 @@ export const USelect = createComponent({
8
8
  elementClass: USelectElement,
9
9
  events: {
10
10
  onChange: 'change',
11
+ onSearch: 'search',
11
12
  },
12
13
  });
@@ -12,7 +12,7 @@ export type SupportedLocale = 'en' | 'ko' | 'ja' | 'zh-CN' | 'zh-TW' | 'es' | 'f
12
12
  /** 내장 로케일은 자동완성되지만, 임의의 BCP47 태그도 그대로 받는다. */
13
13
  export type LocaleTag = SupportedLocale | (string & {});
14
14
  /** 라이브러리가 제공하는 chrome 문자열 키. 지금은 검증 메시지뿐이지만 이후 다른 UI 문구도 추가될 수 있다. */
15
- export type LocaleMessageKey = 'valueMissing' | 'badInput' | 'typeMismatch' | 'patternMismatch' | 'rangeUnderflow' | 'rangeOverflow' | 'stepMismatch' | 'tooShort' | 'tooLong' | 'increment' | 'decrement' | 'clear' | 'showPassword' | 'hidePassword' | 'previousSlide' | 'nextSlide' | 'goToSlide';
15
+ export type LocaleMessageKey = 'valueMissing' | 'badInput' | 'typeMismatch' | 'patternMismatch' | 'rangeUnderflow' | 'rangeOverflow' | 'stepMismatch' | 'tooShort' | 'tooLong' | 'increment' | 'decrement' | 'clear' | 'today' | 'time' | 'showPassword' | 'hidePassword' | 'previousSlide' | 'nextSlide' | 'goToSlide';
16
16
  type LocaleTable = Record<LocaleMessageKey, string>;
17
17
  /**
18
18
  * 한 패키지(또는 앱 영역)의 문자열 묶음.
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@iyulab/components",
3
3
  "description": "web-components library based on lit-element made by iyulab",
4
- "version": "1.33.4",
4
+ "version": "1.35.0",
5
5
  "keywords": [
6
6
  "iyulab",
7
7
  "components",
@@ -6,15 +6,28 @@ import '@iyulab/components/dist/components/date-picker/UDatePicker.js';
6
6
 
7
7
  **Tag:** `u-date-picker`
8
8
 
9
- Single-date selection with a popover calendar. The value follows the same convention as the native `input[type=date]`: an ISO `YYYY-MM-DD` string. Form-associated.
9
+ Single-date(-time) selection with a popover calendar. `mode="date"` (default) follows the same
10
+ convention as the native `input[type=date]`: an ISO `YYYY-MM-DD` string. `mode="datetime"` adds
11
+ a time-of-day input and the value becomes a complete ISO-8601 `DateTimeOffset` string
12
+ (`YYYY-MM-DDTHH:mm:ss±HH:mm`, seconds and the browser's local UTC offset always filled in — the
13
+ value is unconditionally valid regardless of how coarse the time input was). Form-associated.
10
14
 
11
15
  > The calendar week always starts on Sunday, regardless of locale.
12
16
 
17
+ The calendar popover has a footer with a "Today" quick-action button (selects today's date —
18
+ in `mode="datetime"` this also sets the time to right now — disabled when today falls outside
19
+ `min`/`max`) and, when `clearable` and a value is set, a "Clear" button next to it. Picking a
20
+ day preserves whatever time-of-day was already set; only the "Today" button overrides the time.
21
+ `min`/`max` are always date-only, even in `mode="datetime"` — time-of-day is never range-checked.
22
+
13
23
  ```html
14
24
  <u-date-picker name="start-date" label="Start date"></u-date-picker>
15
25
 
16
26
  <!-- Clearable with a bounded range -->
17
27
  <u-date-picker name="due-date" label="Due date" clearable min="2026-01-01" max="2026-12-31"></u-date-picker>
28
+
29
+ <!-- Date + time, value is a full ISO-8601 DateTimeOffset string -->
30
+ <u-date-picker name="sent-at" label="Sent at" mode="datetime"></u-date-picker>
18
31
  ```
19
32
 
20
33
  ---
@@ -23,9 +36,10 @@ Single-date selection with a popover calendar. The value follows the same conven
23
36
 
24
37
  | Property | Type | Default | Reflect | Description |
25
38
  |----------|------|---------|---------|-------------|
26
- | `value` | `string` | | | Selected date (ISO `YYYY-MM-DD`) |
27
- | `min` | `string` | — | — | Minimum selectable date (ISO `YYYY-MM-DD`) |
28
- | `max` | `string` | — | — | Maximum selectable date (ISO `YYYY-MM-DD`) |
39
+ | `mode` | `'date' \| 'datetime'` | `'date'` | | `datetime` adds a time input; value becomes a full ISO-8601 `DateTimeOffset` string |
40
+ | `value` | `string` | — | — | Selected date (ISO `YYYY-MM-DD`), or full ISO-8601 datetime in `mode="datetime"` |
41
+ | `min` | `string` | — | — | Minimum selectable date (ISO `YYYY-MM-DD`, date-only in both modes) |
42
+ | `max` | `string` | — | — | Maximum selectable date (ISO `YYYY-MM-DD`, date-only in both modes) |
29
43
  | `clearable` | `boolean` | `false` | ✓ | Show clear button |
30
44
  | `placeholder` | `string` | — | — | Placeholder text |
31
45
  | `disabled` | `boolean` | `false` | ✓ | Disable |
@@ -41,7 +55,7 @@ Single-date selection with a popover calendar. The value follows the same conven
41
55
 
42
56
  | Event | Description |
43
57
  |-------|-------------|
44
- | `change` | Fires when the user clicks a date cell, confirms via keyboard, or clicks the clear button. Programmatic value assignment does not fire it. |
58
+ | `change` | Fires when the user clicks a date cell, confirms via keyboard, changes the time input (`mode="datetime"`, once a date is set), or clicks the clear button. Programmatic value assignment does not fire it. |
45
59
 
46
60
  ## Methods
47
61
 
@@ -65,6 +79,8 @@ Single-date selection with a popover calendar. The value follows the same conven
65
79
  | `calendar-weekdays` | The weekday header row |
66
80
  | `calendar-grid` | The date grid |
67
81
  | `day` | A date cell button |
82
+ | `calendar-footer` | The row holding the "Today"/"Clear" quick-action buttons |
83
+ | `calendar-time` | The row holding the time-of-day input (`mode="datetime"` only) |
68
84
 
69
85
  ## CSS Custom Properties
70
86
 
@@ -59,6 +59,7 @@ Dropdown select with single or multiple selection, search, and clear support. Fo
59
59
  | Event | Description |
60
60
  |-------|-------------|
61
61
  | `change` | Fires when selection changes |
62
+ | `search` | Fires on every search input change when `searchable` (`detail: { query: string }`). Fires alongside the built-in local filtering — subscribe to it to drive remote/server-side search instead of (or in addition to) the local filter |
62
63
 
63
64
  ## Methods
64
65