@melodicdev/components 1.5.9 → 1.5.10

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.
@@ -2,6 +2,8 @@ import type { IElementRef, OnCreate, OnDestroy } from '@melodicdev/core';
2
2
  /**
3
3
  * ml-date-picker - Date input with calendar dropdown
4
4
  *
5
+ * Users can type a date directly into the input or pick from the calendar popover.
6
+ *
5
7
  * @example
6
8
  * ```html
7
9
  * <ml-date-picker label="Start date" value="2026-02-08"></ml-date-picker>
@@ -35,11 +37,14 @@ export declare class DatePickerComponent implements IElementRef, OnCreate, OnDes
35
37
  /** Whether the calendar popover is open */
36
38
  isOpen: boolean;
37
39
  private _cleanupAutoUpdate;
38
- get displayValue(): string;
39
40
  onCreate(): void;
40
41
  onDestroy(): void;
41
42
  toggleCalendar: () => void;
42
- /** Called when a day is clicked - selects immediately and closes */
43
+ /** Called when the user types a date into the input */
44
+ handleInput: (event: Event) => void;
45
+ /** Clicking the input opens the calendar */
46
+ handleInputClick: () => void;
47
+ /** Called when a day is clicked in the calendar - selects immediately and closes */
43
48
  handleDateSelect: (event: Event) => void;
44
49
  handleKeyDown: (event: KeyboardEvent) => void;
45
50
  private commitValue;
@@ -48,6 +53,7 @@ export declare class DatePickerComponent implements IElementRef, OnCreate, OnDes
48
53
  private startPositioning;
49
54
  private updatePosition;
50
55
  private returnFocus;
56
+ private getInputEl;
51
57
  private getTriggerEl;
52
58
  private getPopoverEl;
53
59
  }
@@ -1 +1 @@
1
- {"version":3,"file":"date-picker.component.d.ts","sourceRoot":"","sources":["../../../../src/components/forms/date-picker/date-picker.component.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAiBzE;;;;;;;;;;GAUG;AACH,qBAMa,mBAAoB,YAAW,WAAW,EAAE,QAAQ,EAAE,SAAS;IACpE,UAAU,EAAG,WAAW,CAAC;IAEhC,+CAA+C;IACxC,KAAK,SAAM;IAElB,uBAAuB;IAChB,WAAW,SAAiB;IAEnC,kBAAkB;IACX,KAAK,SAAM;IAElB,gBAAgB;IACT,IAAI,SAAM;IAEjB,oBAAoB;IACb,KAAK,SAAM;IAElB,iBAAiB;IACV,IAAI,EAAE,IAAI,GAAG,IAAI,GAAG,IAAI,CAAQ;IAEvC,qBAAqB;IACd,QAAQ,UAAS;IAExB,qBAAqB;IACd,QAAQ,UAAS;IAExB,2CAA2C;IACpC,GAAG,SAAM;IAEhB,2CAA2C;IACpC,GAAG,SAAM;IAEhB,2CAA2C;IACpC,MAAM,UAAS;IAEtB,OAAO,CAAC,kBAAkB,CAA6B;IAEvD,IAAW,YAAY,IAAI,MAAM,CAEhC;IAEM,QAAQ,IAAI,IAAI;IAOhB,SAAS,IAAI,IAAI;IAQjB,cAAc,QAAO,IAAI,CAM9B;IAEF,oEAAoE;IAC7D,gBAAgB,GAAI,OAAO,KAAK,KAAG,IAAI,CAK5C;IAEK,aAAa,GAAI,OAAO,aAAa,KAAG,IAAI,CAQjD;IAEF,OAAO,CAAC,WAAW;IAWnB,OAAO,CAAC,QAAQ,CAAC,aAAa,CAW5B;IAEF,OAAO,CAAC,YAAY;IAOpB,OAAO,CAAC,gBAAgB;IAUxB,OAAO,CAAC,cAAc;IAUtB,OAAO,CAAC,WAAW;IAOnB,OAAO,CAAC,YAAY;IAIpB,OAAO,CAAC,YAAY;CAGpB"}
1
+ {"version":3,"file":"date-picker.component.d.ts","sourceRoot":"","sources":["../../../../src/components/forms/date-picker/date-picker.component.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAKzE;;;;;;;;;;;;GAYG;AACH,qBAMa,mBAAoB,YAAW,WAAW,EAAE,QAAQ,EAAE,SAAS;IACpE,UAAU,EAAG,WAAW,CAAC;IAEhC,+CAA+C;IACxC,KAAK,SAAM;IAElB,uBAAuB;IAChB,WAAW,SAAiB;IAEnC,kBAAkB;IACX,KAAK,SAAM;IAElB,gBAAgB;IACT,IAAI,SAAM;IAEjB,oBAAoB;IACb,KAAK,SAAM;IAElB,iBAAiB;IACV,IAAI,EAAE,IAAI,GAAG,IAAI,GAAG,IAAI,CAAQ;IAEvC,qBAAqB;IACd,QAAQ,UAAS;IAExB,qBAAqB;IACd,QAAQ,UAAS;IAExB,2CAA2C;IACpC,GAAG,SAAM;IAEhB,2CAA2C;IACpC,GAAG,SAAM;IAEhB,2CAA2C;IACpC,MAAM,UAAS;IAEtB,OAAO,CAAC,kBAAkB,CAA6B;IAEhD,QAAQ,IAAI,IAAI;IAOhB,SAAS,IAAI,IAAI;IAQjB,cAAc,QAAO,IAAI,CAM9B;IAEF,uDAAuD;IAChD,WAAW,GAAI,OAAO,KAAK,KAAG,IAAI,CAGvC;IAEF,4CAA4C;IACrC,gBAAgB,QAAO,IAAI,CAIhC;IAEF,oFAAoF;IAC7E,gBAAgB,GAAI,OAAO,KAAK,KAAG,IAAI,CAK5C;IAEK,aAAa,GAAI,OAAO,aAAa,KAAG,IAAI,CAmBjD;IAEF,OAAO,CAAC,WAAW;IAWnB,OAAO,CAAC,QAAQ,CAAC,aAAa,CAW5B;IAEF,OAAO,CAAC,YAAY;IAOpB,OAAO,CAAC,gBAAgB;IAUxB,OAAO,CAAC,cAAc;IAUtB,OAAO,CAAC,WAAW;IAOnB,OAAO,CAAC,UAAU;IAIlB,OAAO,CAAC,YAAY;IAIpB,OAAO,CAAC,YAAY;CAGpB"}
@@ -8,21 +8,11 @@ import { MelodicComponent } from '@melodicdev/core';
8
8
  import { computePosition, autoUpdate, offset, flip, shift } from '../../../utils/positioning/index.js';
9
9
  import { datePickerTemplate } from './date-picker.template.js';
10
10
  import { datePickerStyles } from './date-picker.styles.js';
11
- const MONTH_SHORT = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
12
- function formatDisplay(iso) {
13
- if (!iso)
14
- return '';
15
- const parts = iso.split('-');
16
- if (parts.length !== 3)
17
- return iso;
18
- const month = parseInt(parts[1], 10) - 1;
19
- const day = parseInt(parts[2], 10);
20
- const year = parseInt(parts[0], 10);
21
- return `${MONTH_SHORT[month]} ${day}, ${year}`;
22
- }
23
11
  /**
24
12
  * ml-date-picker - Date input with calendar dropdown
25
13
  *
14
+ * Users can type a date directly into the input or pick from the calendar popover.
15
+ *
26
16
  * @example
27
17
  * ```html
28
18
  * <ml-date-picker label="Start date" value="2026-02-08"></ml-date-picker>
@@ -64,7 +54,18 @@ let DatePickerComponent = class DatePickerComponent {
64
54
  popoverEl.togglePopover();
65
55
  }
66
56
  };
67
- /** Called when a day is clicked - selects immediately and closes */
57
+ /** Called when the user types a date into the input */
58
+ this.handleInput = (event) => {
59
+ const input = event.target;
60
+ this.commitValue(input.value);
61
+ };
62
+ /** Clicking the input opens the calendar */
63
+ this.handleInputClick = () => {
64
+ if (!this.isOpen) {
65
+ this.toggleCalendar();
66
+ }
67
+ };
68
+ /** Called when a day is clicked in the calendar - selects immediately and closes */
68
69
  this.handleDateSelect = (event) => {
69
70
  event.stopPropagation();
70
71
  const detail = event.detail;
@@ -76,9 +77,19 @@ let DatePickerComponent = class DatePickerComponent {
76
77
  event.preventDefault();
77
78
  this.closePopover();
78
79
  }
79
- else if ((event.key === 'Enter' || event.key === ' ' || event.key === 'ArrowDown') && !this.isOpen) {
80
+ // Prevent Space from triggering the native date picker
81
+ if (event.key === ' ') {
80
82
  event.preventDefault();
81
- this.toggleCalendar();
83
+ if (!this.isOpen) {
84
+ this.toggleCalendar();
85
+ }
86
+ }
87
+ // Prevent F4 / Alt+Down from triggering the native picker in some browsers
88
+ if (event.key === 'F4' || (event.altKey && event.key === 'ArrowDown')) {
89
+ event.preventDefault();
90
+ if (!this.isOpen) {
91
+ this.toggleCalendar();
92
+ }
82
93
  }
83
94
  };
84
95
  this._handleToggle = (event) => {
@@ -95,9 +106,6 @@ let DatePickerComponent = class DatePickerComponent {
95
106
  }
96
107
  };
97
108
  }
98
- get displayValue() {
99
- return formatDisplay(this.value);
100
- }
101
109
  onCreate() {
102
110
  const popoverEl = this.getPopoverEl();
103
111
  if (popoverEl) {
@@ -144,11 +152,14 @@ let DatePickerComponent = class DatePickerComponent {
144
152
  popoverEl.style.top = `${y}px`;
145
153
  }
146
154
  returnFocus() {
147
- const triggerEl = this.getTriggerEl();
148
- if (triggerEl) {
149
- triggerEl.focus();
155
+ const inputEl = this.getInputEl();
156
+ if (inputEl) {
157
+ inputEl.focus();
150
158
  }
151
159
  }
160
+ getInputEl() {
161
+ return this.elementRef.shadowRoot?.querySelector('.ml-date-picker__input');
162
+ }
152
163
  getTriggerEl() {
153
164
  return this.elementRef.shadowRoot?.querySelector('.ml-date-picker__trigger');
154
165
  }
@@ -1 +1 @@
1
- {"version":3,"file":"date-picker.styles.d.ts","sourceRoot":"","sources":["../../../../src/components/forms/date-picker/date-picker.styles.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,gBAAgB,iDAiN5B,CAAC"}
1
+ {"version":3,"file":"date-picker.styles.d.ts","sourceRoot":"","sources":["../../../../src/components/forms/date-picker/date-picker.styles.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,gBAAgB,iDA2O5B,CAAC"}
@@ -75,32 +75,24 @@ export const datePickerStyles = () => css `
75
75
  margin-left: var(--ml-space-0-5);
76
76
  }
77
77
 
78
- /* Trigger button */
78
+ /* Trigger wrapper */
79
79
  .ml-date-picker__trigger {
80
80
  display: flex;
81
81
  align-items: center;
82
- gap: var(--ml-date-picker-gap);
83
82
  width: 100%;
84
- padding: var(--ml-date-picker-padding);
85
83
  border: var(--ml-date-picker-border-width) solid var(--ml-date-picker-border-color);
86
84
  border-radius: var(--ml-date-picker-border-radius);
87
85
  background-color: var(--ml-date-picker-bg);
88
- color: var(--ml-date-picker-color);
89
- cursor: pointer;
90
- font-family: var(--ml-date-picker-font-family);
91
- font-size: var(--ml-date-picker-font-size);
92
- text-align: left;
93
86
  transition:
94
87
  border-color var(--ml-date-picker-transition-duration) var(--ml-date-picker-transition-easing),
95
88
  box-shadow var(--ml-date-picker-transition-duration) var(--ml-date-picker-transition-easing);
96
89
  }
97
90
 
98
- .ml-date-picker__trigger:hover:not(:disabled) {
91
+ .ml-date-picker__trigger:hover:not(:has(:disabled)) {
99
92
  border-color: var(--ml-date-picker-hover-border-color);
100
93
  }
101
94
 
102
- .ml-date-picker__trigger:focus-visible {
103
- outline: none;
95
+ .ml-date-picker__trigger:focus-within {
104
96
  border-color: var(--ml-date-picker-focus-border-color);
105
97
  box-shadow: var(--ml-date-picker-focus-shadow);
106
98
  }
@@ -114,7 +106,7 @@ export const datePickerStyles = () => css `
114
106
  border-color: var(--ml-date-picker-error-border-color);
115
107
  }
116
108
 
117
- .ml-date-picker--error .ml-date-picker__trigger:focus-visible,
109
+ .ml-date-picker--error .ml-date-picker__trigger:focus-within,
118
110
  .ml-date-picker--error.ml-date-picker--open .ml-date-picker__trigger {
119
111
  box-shadow: var(--ml-date-picker-error-focus-shadow);
120
112
  }
@@ -125,39 +117,73 @@ export const datePickerStyles = () => css `
125
117
  background-color: var(--ml-date-picker-disabled-bg);
126
118
  }
127
119
 
120
+ /* Date input */
121
+ .ml-date-picker__input {
122
+ flex: 1;
123
+ min-width: 0;
124
+ border: none;
125
+ outline: none;
126
+ background: transparent;
127
+ color: var(--ml-date-picker-color);
128
+ font-family: var(--ml-date-picker-font-family);
129
+ font-size: var(--ml-date-picker-font-size);
130
+ padding: var(--ml-date-picker-padding);
131
+ }
132
+
133
+ /* Hide native date picker indicator across browsers */
134
+ .ml-date-picker__input::-webkit-calendar-picker-indicator {
135
+ display: none;
136
+ -webkit-appearance: none;
137
+ }
138
+
139
+ .ml-date-picker__input::-webkit-date-and-time-value {
140
+ text-align: left;
141
+ }
142
+
143
+ .ml-date-picker__input:disabled {
144
+ cursor: not-allowed;
145
+ }
146
+
128
147
  /* Sizes */
129
- .ml-date-picker--sm .ml-date-picker__trigger {
148
+ .ml-date-picker--sm .ml-date-picker__input {
130
149
  padding: var(--ml-space-2) var(--ml-space-3);
131
150
  font-size: var(--ml-text-sm);
132
151
  }
133
152
 
134
- .ml-date-picker--md .ml-date-picker__trigger {
153
+ .ml-date-picker--md .ml-date-picker__input {
135
154
  padding: var(--ml-space-2-5) var(--ml-space-3-5);
136
155
  font-size: var(--ml-text-sm);
137
156
  }
138
157
 
139
- .ml-date-picker--lg .ml-date-picker__trigger {
158
+ .ml-date-picker--lg .ml-date-picker__input {
140
159
  padding: var(--ml-space-3) var(--ml-space-3-5);
141
160
  font-size: var(--ml-text-base);
142
161
  }
143
162
 
144
- /* Icon */
145
- .ml-date-picker__icon {
146
- flex-shrink: 0;
163
+ /* Calendar button */
164
+ .ml-date-picker__calendar-btn {
165
+ display: flex;
166
+ align-items: center;
167
+ justify-content: center;
168
+ border: none;
169
+ background: transparent;
170
+ cursor: pointer;
171
+ padding: 0 var(--ml-space-3) 0 0;
147
172
  color: var(--ml-date-picker-icon-color);
173
+ transition: color var(--ml-date-picker-transition-duration) var(--ml-date-picker-transition-easing);
148
174
  }
149
175
 
150
- /* Value */
151
- .ml-date-picker__value {
152
- flex: 1;
153
- min-width: 0;
154
- overflow: hidden;
155
- text-overflow: ellipsis;
156
- white-space: nowrap;
176
+ .ml-date-picker__calendar-btn:hover:not(:disabled) {
177
+ color: var(--ml-date-picker-color);
157
178
  }
158
179
 
159
- .ml-date-picker__value--placeholder {
160
- color: var(--ml-date-picker-placeholder-color);
180
+ .ml-date-picker__calendar-btn:disabled {
181
+ cursor: not-allowed;
182
+ }
183
+
184
+ /* Icon */
185
+ .ml-date-picker__icon {
186
+ flex-shrink: 0;
161
187
  }
162
188
 
163
189
  /* Popover */
@@ -1 +1 @@
1
- {"version":3,"file":"date-picker.template.d.ts","sourceRoot":"","sources":["../../../../src/components/forms/date-picker/date-picker.template.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,4BAA4B,CAAC;AAEtE,wBAAgB,kBAAkB,CAAC,CAAC,EAAE,mBAAmB,6CAkDxD"}
1
+ {"version":3,"file":"date-picker.template.d.ts","sourceRoot":"","sources":["../../../../src/components/forms/date-picker/date-picker.template.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,4BAA4B,CAAC;AAEtE,wBAAgB,kBAAkB,CAAC,CAAC,EAAE,mBAAmB,6CA4DxD"}
@@ -15,23 +15,33 @@ export function datePickerTemplate(c) {
15
15
  </label>
16
16
  `)}
17
17
 
18
- <button
19
- type="button"
20
- class="ml-date-picker__trigger"
21
- ?disabled=${c.disabled}
22
- aria-haspopup="dialog"
23
- aria-expanded=${c.isOpen ? 'true' : 'false'}
24
- @click=${c.toggleCalendar}
25
- @keydown=${c.handleKeyDown}
26
- >
27
- <ml-icon icon="calendar-blank" size="sm" class="ml-date-picker__icon"></ml-icon>
28
- <span class=${classMap({
29
- 'ml-date-picker__value': true,
30
- 'ml-date-picker__value--placeholder': !c.value
31
- })}>
32
- ${c.value ? c.displayValue : c.placeholder}
33
- </span>
34
- </button>
18
+ <div class="ml-date-picker__trigger">
19
+ <input
20
+ type="date"
21
+ class="ml-date-picker__input"
22
+ .value=${c.value}
23
+ min=${c.min}
24
+ max=${c.max}
25
+ placeholder=${c.placeholder}
26
+ ?disabled=${c.disabled}
27
+ ?required=${c.required}
28
+ aria-haspopup="dialog"
29
+ aria-expanded=${c.isOpen ? 'true' : 'false'}
30
+ @change=${c.handleInput}
31
+ @click=${c.handleInputClick}
32
+ @keydown=${c.handleKeyDown}
33
+ />
34
+ <button
35
+ type="button"
36
+ class="ml-date-picker__calendar-btn"
37
+ ?disabled=${c.disabled}
38
+ aria-label="Open calendar"
39
+ tabindex="-1"
40
+ @click=${c.toggleCalendar}
41
+ >
42
+ <ml-icon icon="calendar-blank" size="sm" class="ml-date-picker__icon"></ml-icon>
43
+ </button>
44
+ </div>
35
45
 
36
46
  <div class="ml-date-picker__popover" popover="auto">
37
47
  <ml-calendar
@@ -1 +1 @@
1
- {"version":3,"file":"date-time-picker.component.d.ts","sourceRoot":"","sources":["../../../../src/components/forms/date-time-picker/date-time-picker.component.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAKzE,OAAO,yCAAyC,CAAC;AACjD,OAAO,yCAAyC,CAAC;AAiCjD;;;;;;;;;;;;;GAaG;AACH,qBAMa,uBAAwB,YAAW,WAAW,EAAE,QAAQ,EAAE,SAAS;IACxE,UAAU,EAAG,WAAW,CAAC;IAEhC,yDAAyD;IAClD,KAAK,SAAM;IAElB,uBAAuB;IAChB,WAAW,SAA0B;IAE5C,kBAAkB;IACX,KAAK,SAAM;IAElB,gBAAgB;IACT,IAAI,SAAM;IAEjB,oBAAoB;IACb,KAAK,SAAM;IAElB,iBAAiB;IACV,IAAI,EAAE,IAAI,GAAG,IAAI,GAAG,IAAI,CAAQ;IAEvC,qBAAqB;IACd,QAAQ,UAAS;IAExB,qBAAqB;IACd,QAAQ,UAAS;IAExB,2CAA2C;IACpC,OAAO,SAAM;IAEpB,2CAA2C;IACpC,OAAO,SAAM;IAEpB,sCAAsC;IAC/B,OAAO,SAAM;IAEpB,sCAAsC;IAC/B,OAAO,SAAM;IAEpB,2BAA2B;IACpB,IAAI,SAAM;IAEjB,yCAAyC;IAClC,UAAU,UAAQ;IAEzB,4BAA4B;IACrB,SAAS,SAAM;IAEtB,4BAA4B;IACrB,SAAS,SAAM;IAEtB,OAAO,CAAC,UAAU,CAAyB;IAE3C,IAAW,SAAS,IAAI,OAAO,CAE9B;IAED,IAAW,YAAY,IAAI,MAAM,CAEhC;IAEM,QAAQ,IAAI,IAAI;IAKhB,SAAS,IAAI,IAAI;IAKxB,OAAO,CAAC,aAAa;IAOrB,OAAO,CAAC,oBAAoB;IA4B5B,OAAO,CAAC,UAAU;CAqBlB"}
1
+ {"version":3,"file":"date-time-picker.component.d.ts","sourceRoot":"","sources":["../../../../src/components/forms/date-time-picker/date-time-picker.component.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAKzE,OAAO,yCAAyC,CAAC;AACjD,OAAO,yCAAyC,CAAC;AAiCjD;;;;;;;;;;;;;GAaG;AACH,qBAMa,uBAAwB,YAAW,WAAW,EAAE,QAAQ,EAAE,SAAS;IACxE,UAAU,EAAG,WAAW,CAAC;IAEhC,yDAAyD;IAClD,KAAK,SAAM;IAElB,uBAAuB;IAChB,WAAW,SAA0B;IAE5C,kBAAkB;IACX,KAAK,SAAM;IAElB,gBAAgB;IACT,IAAI,SAAM;IAEjB,oBAAoB;IACb,KAAK,SAAM;IAElB,iBAAiB;IACV,IAAI,EAAE,IAAI,GAAG,IAAI,GAAG,IAAI,CAAQ;IAEvC,qBAAqB;IACd,QAAQ,UAAS;IAExB,qBAAqB;IACd,QAAQ,UAAS;IAExB,2CAA2C;IACpC,OAAO,SAAM;IAEpB,2CAA2C;IACpC,OAAO,SAAM;IAEpB,sCAAsC;IAC/B,OAAO,SAAM;IAEpB,sCAAsC;IAC/B,OAAO,SAAM;IAEpB,2BAA2B;IACpB,IAAI,SAAM;IAEjB,yCAAyC;IAClC,UAAU,UAAQ;IAEzB,4BAA4B;IACrB,SAAS,SAAM;IAEtB,4BAA4B;IACrB,SAAS,SAAM;IAEtB,OAAO,CAAC,UAAU,CAAyB;IAE3C,IAAW,SAAS,IAAI,OAAO,CAE9B;IAED,IAAW,YAAY,IAAI,MAAM,CAEhC;IAEM,QAAQ,IAAI,IAAI;IAKhB,SAAS,IAAI,IAAI;IAKxB,OAAO,CAAC,aAAa;IAOrB,OAAO,CAAC,oBAAoB;IA+E5B,OAAO,CAAC,UAAU;CAqBlB"}
@@ -140,6 +140,53 @@ let DateTimePickerComponent = class DateTimePickerComponent {
140
140
  timePicker.addEventListener('ml:change', handler);
141
141
  this._listeners.push(() => timePicker.removeEventListener('ml:change', handler));
142
142
  }
143
+ // Bridge Tab focus between date input and time input across shadow boundaries.
144
+ // Internal segment navigation (month→day→year, hour→minute→am/pm) doesn't
145
+ // fire focusout on the <input>, so we only intercept when focus actually leaves.
146
+ if (datePicker && timePicker) {
147
+ const dateInput = datePicker.shadowRoot?.querySelector('input');
148
+ const timeInput = timePicker.shadowRoot?.querySelector('input');
149
+ if (dateInput && timeInput) {
150
+ let tabForward = false;
151
+ let tabBackward = false;
152
+ const dateKeydown = (e) => {
153
+ if (e.key === 'Tab' && !e.shiftKey) {
154
+ tabForward = true;
155
+ requestAnimationFrame(() => { tabForward = false; });
156
+ }
157
+ };
158
+ const dateFocusOut = () => {
159
+ if (tabForward) {
160
+ tabForward = false;
161
+ timeInput.focus();
162
+ }
163
+ };
164
+ dateInput.addEventListener('keydown', dateKeydown);
165
+ dateInput.addEventListener('focusout', dateFocusOut);
166
+ this._listeners.push(() => {
167
+ dateInput.removeEventListener('keydown', dateKeydown);
168
+ dateInput.removeEventListener('focusout', dateFocusOut);
169
+ });
170
+ const timeKeydown = (e) => {
171
+ if (e.key === 'Tab' && e.shiftKey) {
172
+ tabBackward = true;
173
+ requestAnimationFrame(() => { tabBackward = false; });
174
+ }
175
+ };
176
+ const timeFocusOut = () => {
177
+ if (tabBackward) {
178
+ tabBackward = false;
179
+ dateInput.focus();
180
+ }
181
+ };
182
+ timeInput.addEventListener('keydown', timeKeydown);
183
+ timeInput.addEventListener('focusout', timeFocusOut);
184
+ this._listeners.push(() => {
185
+ timeInput.removeEventListener('keydown', timeKeydown);
186
+ timeInput.removeEventListener('focusout', timeFocusOut);
187
+ });
188
+ }
189
+ }
143
190
  }
144
191
  emitChange() {
145
192
  if (this.dateValue && this.timeValue) {
@@ -66,6 +66,10 @@ export declare class TimePickerComponent implements IElementRef, OnCreate, OnDes
66
66
  togglePeriod: () => void;
67
67
  confirmSelection: () => void;
68
68
  handleNowClick: () => void;
69
+ /** Called when the user types a time into the input */
70
+ handleTimeInput: (event: Event) => void;
71
+ /** Clicking the input opens the popover */
72
+ handleInputClick: () => void;
69
73
  handleKeyDown: (event: KeyboardEvent) => void;
70
74
  handleHourInput: (event: Event) => void;
71
75
  handleMinuteInput: (event: Event) => void;
@@ -83,6 +87,7 @@ export declare class TimePickerComponent implements IElementRef, OnCreate, OnDes
83
87
  private startPositioning;
84
88
  private updatePosition;
85
89
  private returnFocus;
90
+ private getInputEl;
86
91
  private getTriggerEl;
87
92
  private getPopoverEl;
88
93
  }
@@ -1 +1 @@
1
- {"version":3,"file":"time-picker.component.d.ts","sourceRoot":"","sources":["../../../../src/components/forms/time-picker/time-picker.component.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AA8BzE;;;;;;;;;;;GAWG;AACH,qBAMa,mBAAoB,YAAW,WAAW,EAAE,QAAQ,EAAE,SAAS;IACpE,UAAU,EAAG,WAAW,CAAC;IAEhC,gDAAgD;IACzC,KAAK,SAAM;IAElB,uBAAuB;IAChB,WAAW,SAAiB;IAEnC,kBAAkB;IACX,KAAK,SAAM;IAElB,gBAAgB;IACT,IAAI,SAAM;IAEjB,oBAAoB;IACb,KAAK,SAAM;IAElB,iBAAiB;IACV,IAAI,EAAE,IAAI,GAAG,IAAI,GAAG,IAAI,CAAQ;IAEvC,qBAAqB;IACd,QAAQ,UAAS;IAExB,qBAAqB;IACd,QAAQ,UAAS;IAExB,sCAAsC;IAC/B,GAAG,SAAM;IAEhB,sCAAsC;IAC/B,GAAG,SAAM;IAEhB,uFAAuF;IAChF,IAAI,SAAM;IAEjB,oDAAoD;IAC7C,UAAU,UAAQ;IAEzB,kCAAkC;IAC3B,MAAM,UAAS;IAEtB,2BAA2B;IACpB,QAAQ,SAAK;IAEpB,6BAA6B;IACtB,UAAU,SAAK;IAEtB,6BAA6B;IACtB,UAAU,SAAK;IAEtB,oCAAoC;IAC7B,UAAU,EAAE,IAAI,GAAG,IAAI,CAAQ;IAEtC,OAAO,CAAC,kBAAkB,CAA6B;IAEvD,IAAW,SAAS,IAAI,OAAO,CAE9B;IAED,IAAW,WAAW,IAAI,OAAO,CAEhC;IAED,IAAW,YAAY,IAAI,MAAM,CAEhC;IAED,IAAW,WAAW,IAAI,MAAM,CAQ/B;IAED,IAAW,aAAa,IAAI,MAAM,CAEjC;IAED,IAAW,aAAa,IAAI,MAAM,CAEjC;IAEM,QAAQ,IAAI,IAAI;IAOhB,SAAS,IAAI,IAAI;IAQjB,aAAa,QAAO,IAAI,CAM7B;IAEK,aAAa,QAAO,IAAI,CAS7B;IAEK,aAAa,QAAO,IAAI,CAQ7B;IAEK,eAAe,QAAO,IAAI,CAE/B;IAEK,eAAe,QAAO,IAAI,CAE/B;IAEK,eAAe,QAAO,IAAI,CAE/B;IAEK,eAAe,QAAO,IAAI,CAE/B;IAEK,YAAY,QAAO,IAAI,CAI5B;IAEK,gBAAgB,QAAO,IAAI,CAOhC;IAEK,cAAc,QAAO,IAAI,CAM9B;IAEK,aAAa,GAAI,OAAO,aAAa,KAAG,IAAI,CAQjD;IAEK,eAAe,GAAI,OAAO,KAAK,KAAG,IAAI,CAa3C;IAEK,iBAAiB,GAAI,OAAO,KAAK,KAAG,IAAI,CAO7C;IAEK,iBAAiB,GAAI,OAAO,KAAK,KAAG,IAAI,CAO7C;IAEK,cAAc,QAAO,IAAI,CAK9B;IAEK,gBAAgB,QAAO,IAAI,CAEhC;IAEK,gBAAgB,QAAO,IAAI,CAEhC;IAEK,gBAAgB,GAAI,OAAO,KAAK,KAAG,IAAI,CAE5C;IAEK,oBAAoB,GAAI,OAAO,aAAa,KAAG,IAAI,CAkBxD;IAEF,OAAO,CAAC,QAAQ;IAKhB,OAAO,CAAC,iBAAiB;IAezB,OAAO,CAAC,WAAW;IAWnB,OAAO,CAAC,QAAQ,CAAC,aAAa,CAY5B;IAEF,OAAO,CAAC,YAAY;IAOpB,OAAO,CAAC,gBAAgB;IAUxB,OAAO,CAAC,cAAc;IAUtB,OAAO,CAAC,WAAW;IAOnB,OAAO,CAAC,YAAY;IAIpB,OAAO,CAAC,YAAY;CAGpB"}
1
+ {"version":3,"file":"time-picker.component.d.ts","sourceRoot":"","sources":["../../../../src/components/forms/time-picker/time-picker.component.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AA8BzE;;;;;;;;;;;GAWG;AACH,qBAMa,mBAAoB,YAAW,WAAW,EAAE,QAAQ,EAAE,SAAS;IACpE,UAAU,EAAG,WAAW,CAAC;IAEhC,gDAAgD;IACzC,KAAK,SAAM;IAElB,uBAAuB;IAChB,WAAW,SAAiB;IAEnC,kBAAkB;IACX,KAAK,SAAM;IAElB,gBAAgB;IACT,IAAI,SAAM;IAEjB,oBAAoB;IACb,KAAK,SAAM;IAElB,iBAAiB;IACV,IAAI,EAAE,IAAI,GAAG,IAAI,GAAG,IAAI,CAAQ;IAEvC,qBAAqB;IACd,QAAQ,UAAS;IAExB,qBAAqB;IACd,QAAQ,UAAS;IAExB,sCAAsC;IAC/B,GAAG,SAAM;IAEhB,sCAAsC;IAC/B,GAAG,SAAM;IAEhB,uFAAuF;IAChF,IAAI,SAAM;IAEjB,oDAAoD;IAC7C,UAAU,UAAQ;IAEzB,kCAAkC;IAC3B,MAAM,UAAS;IAEtB,2BAA2B;IACpB,QAAQ,SAAK;IAEpB,6BAA6B;IACtB,UAAU,SAAK;IAEtB,6BAA6B;IACtB,UAAU,SAAK;IAEtB,oCAAoC;IAC7B,UAAU,EAAE,IAAI,GAAG,IAAI,CAAQ;IAEtC,OAAO,CAAC,kBAAkB,CAA6B;IAEvD,IAAW,SAAS,IAAI,OAAO,CAE9B;IAED,IAAW,WAAW,IAAI,OAAO,CAEhC;IAED,IAAW,YAAY,IAAI,MAAM,CAEhC;IAED,IAAW,WAAW,IAAI,MAAM,CAQ/B;IAED,IAAW,aAAa,IAAI,MAAM,CAEjC;IAED,IAAW,aAAa,IAAI,MAAM,CAEjC;IAEM,QAAQ,IAAI,IAAI;IAOhB,SAAS,IAAI,IAAI;IAQjB,aAAa,QAAO,IAAI,CAM7B;IAEK,aAAa,QAAO,IAAI,CAS7B;IAEK,aAAa,QAAO,IAAI,CAQ7B;IAEK,eAAe,QAAO,IAAI,CAE/B;IAEK,eAAe,QAAO,IAAI,CAE/B;IAEK,eAAe,QAAO,IAAI,CAE/B;IAEK,eAAe,QAAO,IAAI,CAE/B;IAEK,YAAY,QAAO,IAAI,CAI5B;IAEK,gBAAgB,QAAO,IAAI,CAOhC;IAEK,cAAc,QAAO,IAAI,CAM9B;IAEF,uDAAuD;IAChD,eAAe,GAAI,OAAO,KAAK,KAAG,IAAI,CAG3C;IAEF,2CAA2C;IACpC,gBAAgB,QAAO,IAAI,CAIhC;IAEK,aAAa,GAAI,OAAO,aAAa,KAAG,IAAI,CAmBjD;IAEK,eAAe,GAAI,OAAO,KAAK,KAAG,IAAI,CAa3C;IAEK,iBAAiB,GAAI,OAAO,KAAK,KAAG,IAAI,CAO7C;IAEK,iBAAiB,GAAI,OAAO,KAAK,KAAG,IAAI,CAO7C;IAEK,cAAc,QAAO,IAAI,CAK9B;IAEK,gBAAgB,QAAO,IAAI,CAEhC;IAEK,gBAAgB,QAAO,IAAI,CAEhC;IAEK,gBAAgB,GAAI,OAAO,KAAK,KAAG,IAAI,CAE5C;IAEK,oBAAoB,GAAI,OAAO,aAAa,KAAG,IAAI,CAkBxD;IAEF,OAAO,CAAC,QAAQ;IAKhB,OAAO,CAAC,iBAAiB;IAezB,OAAO,CAAC,WAAW;IAWnB,OAAO,CAAC,QAAQ,CAAC,aAAa,CAY5B;IAEF,OAAO,CAAC,YAAY;IAOpB,OAAO,CAAC,gBAAgB;IAUxB,OAAO,CAAC,cAAc;IAUtB,OAAO,CAAC,WAAW;IAOnB,OAAO,CAAC,UAAU;IAIlB,OAAO,CAAC,YAAY;IAIpB,OAAO,CAAC,YAAY;CAGpB"}
@@ -147,14 +147,35 @@ let TimePickerComponent = class TimePickerComponent {
147
147
  this.editSecond = now.getSeconds();
148
148
  this.editPeriod = this.editHour >= 12 ? 'PM' : 'AM';
149
149
  };
150
+ /** Called when the user types a time into the input */
151
+ this.handleTimeInput = (event) => {
152
+ const input = event.target;
153
+ this.commitValue(input.value);
154
+ };
155
+ /** Clicking the input opens the popover */
156
+ this.handleInputClick = () => {
157
+ if (!this.isOpen) {
158
+ this.togglePopover();
159
+ }
160
+ };
150
161
  this.handleKeyDown = (event) => {
151
162
  if (event.key === 'Escape' && this.isOpen) {
152
163
  event.preventDefault();
153
164
  this.closePopover();
154
165
  }
155
- else if ((event.key === 'Enter' || event.key === ' ' || event.key === 'ArrowDown') && !this.isOpen) {
166
+ // Prevent Space from triggering the native time picker
167
+ if (event.key === ' ') {
156
168
  event.preventDefault();
157
- this.togglePopover();
169
+ if (!this.isOpen) {
170
+ this.togglePopover();
171
+ }
172
+ }
173
+ // Prevent F4 / Alt+Down from triggering the native picker in some browsers
174
+ if (event.key === 'F4' || (event.altKey && event.key === 'ArrowDown')) {
175
+ event.preventDefault();
176
+ if (!this.isOpen) {
177
+ this.togglePopover();
178
+ }
158
179
  }
159
180
  };
160
181
  this.handleHourInput = (event) => {
@@ -346,11 +367,14 @@ let TimePickerComponent = class TimePickerComponent {
346
367
  popoverEl.style.top = `${y}px`;
347
368
  }
348
369
  returnFocus() {
349
- const triggerEl = this.getTriggerEl();
350
- if (triggerEl) {
351
- triggerEl.focus();
370
+ const inputEl = this.getInputEl();
371
+ if (inputEl) {
372
+ inputEl.focus();
352
373
  }
353
374
  }
375
+ getInputEl() {
376
+ return this.elementRef.shadowRoot?.querySelector('.ml-time-picker__input');
377
+ }
354
378
  getTriggerEl() {
355
379
  return this.elementRef.shadowRoot?.querySelector('.ml-time-picker__trigger');
356
380
  }
@@ -1 +1 @@
1
- {"version":3,"file":"time-picker.styles.d.ts","sourceRoot":"","sources":["../../../../src/components/forms/time-picker/time-picker.styles.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,gBAAgB,iDAqa5B,CAAC"}
1
+ {"version":3,"file":"time-picker.styles.d.ts","sourceRoot":"","sources":["../../../../src/components/forms/time-picker/time-picker.styles.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,gBAAgB,iDA+b5B,CAAC"}
@@ -122,32 +122,24 @@ export const timePickerStyles = () => css `
122
122
  margin-left: var(--ml-space-0-5);
123
123
  }
124
124
 
125
- /* Trigger button */
125
+ /* Trigger wrapper */
126
126
  .ml-time-picker__trigger {
127
127
  display: flex;
128
128
  align-items: center;
129
- gap: var(--ml-time-picker-gap);
130
129
  width: 100%;
131
- padding: var(--ml-time-picker-padding);
132
130
  border: var(--ml-time-picker-border-width) solid var(--ml-time-picker-border-color);
133
131
  border-radius: var(--ml-time-picker-border-radius);
134
132
  background-color: var(--ml-time-picker-bg);
135
- color: var(--ml-time-picker-color);
136
- cursor: pointer;
137
- font-family: var(--ml-time-picker-font-family);
138
- font-size: var(--ml-time-picker-font-size);
139
- text-align: left;
140
133
  transition:
141
134
  border-color var(--ml-time-picker-transition-duration) var(--ml-time-picker-transition-easing),
142
135
  box-shadow var(--ml-time-picker-transition-duration) var(--ml-time-picker-transition-easing);
143
136
  }
144
137
 
145
- .ml-time-picker__trigger:hover:not(:disabled) {
138
+ .ml-time-picker__trigger:hover:not(:has(:disabled)) {
146
139
  border-color: var(--ml-time-picker-hover-border-color);
147
140
  }
148
141
 
149
- .ml-time-picker__trigger:focus-visible {
150
- outline: none;
142
+ .ml-time-picker__trigger:focus-within {
151
143
  border-color: var(--ml-time-picker-focus-border-color);
152
144
  box-shadow: var(--ml-time-picker-focus-shadow);
153
145
  }
@@ -161,7 +153,7 @@ export const timePickerStyles = () => css `
161
153
  border-color: var(--ml-time-picker-error-border-color);
162
154
  }
163
155
 
164
- .ml-time-picker--error .ml-time-picker__trigger:focus-visible,
156
+ .ml-time-picker--error .ml-time-picker__trigger:focus-within,
165
157
  .ml-time-picker--error.ml-time-picker--open .ml-time-picker__trigger {
166
158
  box-shadow: var(--ml-time-picker-error-focus-shadow);
167
159
  }
@@ -172,39 +164,73 @@ export const timePickerStyles = () => css `
172
164
  background-color: var(--ml-time-picker-disabled-bg);
173
165
  }
174
166
 
167
+ /* Time input */
168
+ .ml-time-picker__input {
169
+ flex: 1;
170
+ min-width: 0;
171
+ border: none;
172
+ outline: none;
173
+ background: transparent;
174
+ color: var(--ml-time-picker-color);
175
+ font-family: var(--ml-time-picker-font-family);
176
+ font-size: var(--ml-time-picker-font-size);
177
+ padding: var(--ml-time-picker-padding);
178
+ }
179
+
180
+ /* Hide native time picker indicator across browsers */
181
+ .ml-time-picker__input::-webkit-calendar-picker-indicator {
182
+ display: none;
183
+ -webkit-appearance: none;
184
+ }
185
+
186
+ .ml-time-picker__input::-webkit-date-and-time-value {
187
+ text-align: left;
188
+ }
189
+
190
+ .ml-time-picker__input:disabled {
191
+ cursor: not-allowed;
192
+ }
193
+
175
194
  /* Sizes */
176
- .ml-time-picker--sm .ml-time-picker__trigger {
195
+ .ml-time-picker--sm .ml-time-picker__input {
177
196
  padding: var(--ml-space-2) var(--ml-space-3);
178
197
  font-size: var(--ml-text-sm);
179
198
  }
180
199
 
181
- .ml-time-picker--md .ml-time-picker__trigger {
200
+ .ml-time-picker--md .ml-time-picker__input {
182
201
  padding: var(--ml-space-2-5) var(--ml-space-3-5);
183
202
  font-size: var(--ml-text-sm);
184
203
  }
185
204
 
186
- .ml-time-picker--lg .ml-time-picker__trigger {
205
+ .ml-time-picker--lg .ml-time-picker__input {
187
206
  padding: var(--ml-space-3) var(--ml-space-3-5);
188
207
  font-size: var(--ml-text-base);
189
208
  }
190
209
 
191
- /* Icon */
192
- .ml-time-picker__icon {
193
- flex-shrink: 0;
210
+ /* Clock button */
211
+ .ml-time-picker__clock-btn {
212
+ display: flex;
213
+ align-items: center;
214
+ justify-content: center;
215
+ border: none;
216
+ background: transparent;
217
+ cursor: pointer;
218
+ padding: 0 var(--ml-space-3) 0 0;
194
219
  color: var(--ml-time-picker-icon-color);
220
+ transition: color var(--ml-time-picker-transition-duration) var(--ml-time-picker-transition-easing);
195
221
  }
196
222
 
197
- /* Value */
198
- .ml-time-picker__value {
199
- flex: 1;
200
- min-width: 0;
201
- overflow: hidden;
202
- text-overflow: ellipsis;
203
- white-space: nowrap;
223
+ .ml-time-picker__clock-btn:hover:not(:disabled) {
224
+ color: var(--ml-time-picker-color);
225
+ }
226
+
227
+ .ml-time-picker__clock-btn:disabled {
228
+ cursor: not-allowed;
204
229
  }
205
230
 
206
- .ml-time-picker__value--placeholder {
207
- color: var(--ml-time-picker-placeholder-color);
231
+ /* Icon */
232
+ .ml-time-picker__icon {
233
+ flex-shrink: 0;
208
234
  }
209
235
 
210
236
  /* Popover */
@@ -1 +1 @@
1
- {"version":3,"file":"time-picker.template.d.ts","sourceRoot":"","sources":["../../../../src/components/forms/time-picker/time-picker.template.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,4BAA4B,CAAC;AAEtE,wBAAgB,kBAAkB,CAAC,CAAC,EAAE,mBAAmB,6CA6HxD"}
1
+ {"version":3,"file":"time-picker.template.d.ts","sourceRoot":"","sources":["../../../../src/components/forms/time-picker/time-picker.template.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,4BAA4B,CAAC;AAEtE,wBAAgB,kBAAkB,CAAC,CAAC,EAAE,mBAAmB,6CAwIxD"}