@api-client/ui 0.5.6 → 0.5.8

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.
Files changed (82) hide show
  1. package/.cursor/rules/html-and-css-best-practices.mdc +63 -0
  2. package/.cursor/rules/lit-best-practices.mdc +78 -0
  3. package/.github/instructions/html-and-css-best-practices.instructions.md +70 -0
  4. package/.github/instructions/lit-best-practices.instructions.md +86 -0
  5. package/build/src/elements/currency/currency-picker.d.ts +10 -0
  6. package/build/src/elements/currency/currency-picker.d.ts.map +1 -0
  7. package/build/src/elements/currency/currency-picker.js +27 -0
  8. package/build/src/elements/currency/currency-picker.js.map +1 -0
  9. package/build/src/elements/currency/internals/Picker.d.ts +311 -0
  10. package/build/src/elements/currency/internals/Picker.d.ts.map +1 -0
  11. package/build/src/elements/currency/internals/Picker.js +857 -0
  12. package/build/src/elements/currency/internals/Picker.js.map +1 -0
  13. package/build/src/elements/currency/internals/Picker.styles.d.ts +3 -0
  14. package/build/src/elements/currency/internals/Picker.styles.d.ts.map +1 -0
  15. package/build/src/elements/currency/internals/Picker.styles.js +58 -0
  16. package/build/src/elements/currency/internals/Picker.styles.js.map +1 -0
  17. package/build/src/elements/mention-textarea/internals/MentionTextArea.d.ts +216 -0
  18. package/build/src/elements/mention-textarea/internals/MentionTextArea.d.ts.map +1 -0
  19. package/build/src/elements/mention-textarea/internals/MentionTextArea.js +1037 -0
  20. package/build/src/elements/mention-textarea/internals/MentionTextArea.js.map +1 -0
  21. package/build/src/elements/mention-textarea/internals/MentionTextArea.styles.d.ts +3 -0
  22. package/build/src/elements/mention-textarea/internals/MentionTextArea.styles.d.ts.map +1 -0
  23. package/build/src/elements/mention-textarea/internals/MentionTextArea.styles.js +274 -0
  24. package/build/src/elements/mention-textarea/internals/MentionTextArea.styles.js.map +1 -0
  25. package/build/src/elements/mention-textarea/ui-mention-textarea.d.ts +13 -0
  26. package/build/src/elements/mention-textarea/ui-mention-textarea.d.ts.map +1 -0
  27. package/build/src/elements/mention-textarea/ui-mention-textarea.js +28 -0
  28. package/build/src/elements/mention-textarea/ui-mention-textarea.js.map +1 -0
  29. package/build/src/md/button/internals/base.d.ts +1 -0
  30. package/build/src/md/button/internals/base.d.ts.map +1 -1
  31. package/build/src/md/button/internals/base.js +7 -0
  32. package/build/src/md/button/internals/base.js.map +1 -1
  33. package/build/src/md/chip/internals/Chip.styles.d.ts.map +1 -1
  34. package/build/src/md/chip/internals/Chip.styles.js +2 -0
  35. package/build/src/md/chip/internals/Chip.styles.js.map +1 -1
  36. package/build/src/md/date-picker/internals/DatePicker.styles.d.ts.map +1 -1
  37. package/build/src/md/date-picker/internals/DatePicker.styles.js +73 -0
  38. package/build/src/md/date-picker/internals/DatePicker.styles.js.map +1 -1
  39. package/build/src/md/date-picker/internals/DatePickerCalendar.d.ts +164 -51
  40. package/build/src/md/date-picker/internals/DatePickerCalendar.d.ts.map +1 -1
  41. package/build/src/md/date-picker/internals/DatePickerCalendar.js +660 -368
  42. package/build/src/md/date-picker/internals/DatePickerCalendar.js.map +1 -1
  43. package/build/src/md/date-picker/ui-date-picker-input.d.ts +65 -13
  44. package/build/src/md/date-picker/ui-date-picker-input.d.ts.map +1 -1
  45. package/build/src/md/date-picker/ui-date-picker-input.js +143 -76
  46. package/build/src/md/date-picker/ui-date-picker-input.js.map +1 -1
  47. package/build/src/md/date-picker/ui-date-picker-modal-input.d.ts +76 -17
  48. package/build/src/md/date-picker/ui-date-picker-modal-input.d.ts.map +1 -1
  49. package/build/src/md/date-picker/ui-date-picker-modal-input.js +192 -127
  50. package/build/src/md/date-picker/ui-date-picker-modal-input.js.map +1 -1
  51. package/build/src/md/date-picker/ui-date-picker-modal.d.ts +63 -15
  52. package/build/src/md/date-picker/ui-date-picker-modal.d.ts.map +1 -1
  53. package/build/src/md/date-picker/ui-date-picker-modal.js +143 -64
  54. package/build/src/md/date-picker/ui-date-picker-modal.js.map +1 -1
  55. package/demo/elements/currency/index.html +91 -0
  56. package/demo/elements/currency/index.ts +272 -0
  57. package/demo/elements/index.html +6 -0
  58. package/demo/elements/mention-textarea/index.html +19 -0
  59. package/demo/elements/mention-textarea/index.ts +205 -0
  60. package/demo/md/date-picker/date-picker.ts +138 -103
  61. package/package.json +2 -2
  62. package/src/elements/currency/currency-picker.ts +14 -0
  63. package/src/elements/currency/internals/Picker.styles.ts +58 -0
  64. package/src/elements/currency/internals/Picker.ts +846 -0
  65. package/src/elements/mention-textarea/internals/MentionTextArea.styles.ts +274 -0
  66. package/src/elements/mention-textarea/internals/MentionTextArea.ts +1036 -0
  67. package/src/elements/mention-textarea/ui-mention-textarea.ts +18 -0
  68. package/src/md/button/internals/base.ts +7 -0
  69. package/src/md/chip/internals/Chip.styles.ts +2 -0
  70. package/src/md/date-picker/internals/DatePicker.styles.ts +73 -0
  71. package/src/md/date-picker/internals/DatePickerCalendar.ts +643 -309
  72. package/src/md/date-picker/ui-date-picker-input.ts +110 -49
  73. package/src/md/date-picker/ui-date-picker-modal-input.ts +168 -99
  74. package/src/md/date-picker/ui-date-picker-modal.ts +136 -53
  75. package/test/README.md +3 -2
  76. package/test/elements/currency/CurrencyPicker.accessibility.test.ts +328 -0
  77. package/test/elements/currency/CurrencyPicker.core.test.ts +318 -0
  78. package/test/elements/currency/CurrencyPicker.integration.test.ts +482 -0
  79. package/test/elements/currency/CurrencyPicker.test.ts +486 -0
  80. package/test/elements/mention-textarea/MentionTextArea.basic.test.ts +63 -0
  81. package/test/elements/mention-textarea/MentionTextArea.test.ts +321 -0
  82. package/tsconfig.json +1 -1
@@ -18,16 +18,63 @@ export interface ModalInputDatePickerChangeEvent {
18
18
 
19
19
  /**
20
20
  * A modal date input picker for manual date entry using keyboard.
21
- * Ideal for compact layouts and precise date entry.
21
+ * Ideal for compact layouts and precise date entry. Supports both single
22
+ * date selection and date range selection modes.
23
+ *
24
+ * ## Features
25
+ * - Manual date entry with keyboard input
26
+ * - Single date and date range modes
27
+ * - Input validation with error messages
28
+ * - Min/max date constraints
29
+ * - Multiple date format support (MM/DD/YYYY, MM-DD-YYYY, YYYY-MM-DD)
30
+ * - Accessible design with proper ARIA labels and semantic HTML
31
+ * - Real-time validation feedback
32
+ *
33
+ * ## Events
34
+ *
35
+ * ### `date-input-change`
36
+ * Fired when a valid date is entered in the input fields.
37
+ *
38
+ * **Detail for range mode:**
39
+ * ```typescript
40
+ * {
41
+ * range: DateRange,
42
+ * formattedRange: {
43
+ * start: string | null,
44
+ * end: string | null
45
+ * }
46
+ * }
47
+ * ```
48
+ *
49
+ * **Detail for single date mode:**
50
+ * ```typescript
51
+ * {
52
+ * date: Date,
53
+ * formattedDate: string
54
+ * }
55
+ * ```
56
+ *
57
+ * ### `modal-input-close`
58
+ * Fired when the modal is closed, either by confirmation or cancellation.
59
+ *
60
+ * **Detail:**
61
+ * ```typescript
62
+ * {
63
+ * confirmed: boolean,
64
+ * date?: Date | null, // Available in single date mode
65
+ * range?: DateRange | null // Available in range mode
66
+ * }
67
+ * ```
22
68
  *
23
69
  * ## Usage
24
70
  *
71
+ * ### Range mode (default)
25
72
  * ```html
26
73
  * <ui-date-picker-modal-input
27
74
  * .open=${true}
28
75
  * .selectedRange=${{ start: new Date(), end: null }}
29
76
  * @date-input-change=${this.handleRangeChange}
30
- * @close=${this.handleClose}
77
+ * @modal-input-close=${this.handleClose}
31
78
  * ></ui-date-picker-modal-input>
32
79
  * ```
33
80
  *
@@ -36,6 +83,17 @@ export interface ModalInputDatePickerChangeEvent {
36
83
  * <ui-date-picker-modal-input
37
84
  * .rangeMode=${false}
38
85
  * .selectedDate=${new Date()}
86
+ * @date-input-change=${this.handleDateChange}
87
+ * @modal-input-close=${this.handleClose}
88
+ * ></ui-date-picker-modal-input>
89
+ * ```
90
+ *
91
+ * ### With constraints
92
+ * ```html
93
+ * <ui-date-picker-modal-input
94
+ * .minDate=${new Date('2024-01-01')}
95
+ * .maxDate=${new Date('2024-12-31')}
96
+ * .locale=${'en-US'}
39
97
  * ></ui-date-picker-modal-input>
40
98
  * ```
41
99
  */
@@ -56,7 +114,7 @@ export class UiDatePickerModalInput extends LitElement {
56
114
  /**
57
115
  * Whether to use range mode (two date inputs) or single date mode
58
116
  */
59
- @property({ type: Boolean }) accessor rangeMode = true
117
+ @property({ type: Boolean }) accessor rangeMode = false
60
118
 
61
119
  /**
62
120
  * Label for the start date (or single date)
@@ -98,35 +156,42 @@ export class UiDatePickerModalInput extends LitElement {
98
156
  */
99
157
  @property({ type: String }) accessor locale: string | undefined = undefined
100
158
 
101
- @state() private accessor _startInput = ''
159
+ @state() private accessor startInput = ''
160
+
161
+ @state() private accessor endInput = ''
102
162
 
103
- @state() private accessor _endInput = ''
163
+ @state() private accessor startError = ''
104
164
 
105
- @state() private accessor _startError = ''
165
+ @state() private accessor endError = ''
106
166
 
107
- @state() private accessor _endError = ''
167
+ constructor() {
168
+ super()
169
+ // Initialize boolean properties to false first, then set intended defaults
170
+ this.open = false
171
+ this.rangeMode = true // Default to range mode as documented
172
+ }
108
173
 
109
174
  override connectedCallback(): void {
110
175
  super.connectedCallback()
111
- this._updateInputValues()
176
+ this.updateInputValues()
112
177
  }
113
178
 
114
179
  override willUpdate(changedProperties: Map<string | number | symbol, unknown>): void {
115
180
  if (changedProperties.has('selectedDate') || changedProperties.has('selectedRange')) {
116
- this._updateInputValues()
181
+ this.updateInputValues()
117
182
  }
118
183
  }
119
184
 
120
- private _updateInputValues(): void {
185
+ private updateInputValues(): void {
121
186
  if (this.rangeMode && this.selectedRange) {
122
- this._startInput = this.selectedRange.start ? formatDate(this.selectedRange.start, this.locale) : ''
123
- this._endInput = this.selectedRange.end ? formatDate(this.selectedRange.end, this.locale) : ''
187
+ this.startInput = this.selectedRange.start ? formatDate(this.selectedRange.start, this.locale) : ''
188
+ this.endInput = this.selectedRange.end ? formatDate(this.selectedRange.end, this.locale) : ''
124
189
  } else if (!this.rangeMode && this.selectedDate) {
125
- this._startInput = formatDate(this.selectedDate, this.locale)
190
+ this.startInput = formatDate(this.selectedDate, this.locale)
126
191
  }
127
192
  }
128
193
 
129
- private _validateDate(dateString: string, isEndDate = false): Date | null {
194
+ private validateDate(dateString: string, isEndDate = false): Date | null {
130
195
  if (!dateString.trim()) return null
131
196
 
132
197
  const parsedDate = parseDate(dateString)
@@ -138,22 +203,22 @@ export class UiDatePickerModalInput extends LitElement {
138
203
 
139
204
  // For range mode, validate end date is after start date
140
205
  if (this.rangeMode && isEndDate) {
141
- const startDate = parseDate(this._startInput)
206
+ const startDate = parseDate(this.startInput)
142
207
  if (startDate && parsedDate < startDate) return null
143
208
  }
144
209
 
145
210
  return parsedDate
146
211
  }
147
212
 
148
- private _handleStartInput(event: Event): void {
213
+ private handleStartInput(event: Event): void {
149
214
  const target = event.target as HTMLInputElement
150
- this._startInput = target.value
151
- this._startError = ''
215
+ this.startInput = target.value
216
+ this.startError = ''
152
217
 
153
- if (this._startInput.trim()) {
154
- const date = this._validateDate(this._startInput)
218
+ if (this.startInput.trim()) {
219
+ const date = this.validateDate(this.startInput)
155
220
  if (!date) {
156
- this._startError = 'Invalid date format or out of range'
221
+ this.startError = 'Invalid date format or out of range'
157
222
  return
158
223
  }
159
224
 
@@ -164,68 +229,68 @@ export class UiDatePickerModalInput extends LitElement {
164
229
  this.selectedDate = date
165
230
  }
166
231
 
167
- this._dispatchChangeEvent()
232
+ this.dispatchChangeEvent()
168
233
  }
169
234
  }
170
235
 
171
- private _handleEndInput(event: Event): void {
236
+ private handleEndInput(event: Event): void {
172
237
  const target = event.target as HTMLInputElement
173
- this._endInput = target.value
174
- this._endError = ''
238
+ this.endInput = target.value
239
+ this.endError = ''
175
240
 
176
- if (this._endInput.trim()) {
177
- const date = this._validateDate(this._endInput, true)
241
+ if (this.endInput.trim()) {
242
+ const date = this.validateDate(this.endInput, true)
178
243
  if (!date) {
179
- this._endError = 'Invalid date format, out of range, or before start date'
244
+ this.endError = 'Invalid date format, out of range, or before start date'
180
245
  return
181
246
  }
182
247
 
183
248
  const currentRange = this.selectedRange || { start: null, end: null }
184
249
  this.selectedRange = { ...currentRange, end: date }
185
- this._dispatchChangeEvent()
250
+ this.dispatchChangeEvent()
186
251
  }
187
252
  }
188
253
 
189
- private _handleCancel(): void {
190
- this._dispatchCloseEvent(false)
254
+ private handleCancel(): void {
255
+ this.dispatchCloseEvent(false)
191
256
  }
192
257
 
193
- private _handleConfirm(): void {
258
+ private handleConfirm(): void {
194
259
  // Validate all inputs before confirming
195
260
  let hasErrors = false
196
261
 
197
262
  if (this.rangeMode) {
198
- if (!this._startInput.trim()) {
199
- this._startError = 'Start date is required'
263
+ if (!this.startInput.trim()) {
264
+ this.startError = 'Start date is required'
200
265
  hasErrors = true
201
- } else if (!this._validateDate(this._startInput)) {
202
- this._startError = 'Invalid start date'
266
+ } else if (!this.validateDate(this.startInput)) {
267
+ this.startError = 'Invalid start date'
203
268
  hasErrors = true
204
269
  }
205
270
 
206
- if (!this._endInput.trim()) {
207
- this._endError = 'End date is required'
271
+ if (!this.endInput.trim()) {
272
+ this.endError = 'End date is required'
208
273
  hasErrors = true
209
- } else if (!this._validateDate(this._endInput, true)) {
210
- this._endError = 'Invalid end date'
274
+ } else if (!this.validateDate(this.endInput, true)) {
275
+ this.endError = 'Invalid end date'
211
276
  hasErrors = true
212
277
  }
213
278
  } else {
214
- if (!this._startInput.trim()) {
215
- this._startError = 'Date is required'
279
+ if (!this.startInput.trim()) {
280
+ this.startError = 'Date is required'
216
281
  hasErrors = true
217
- } else if (!this._validateDate(this._startInput)) {
218
- this._startError = 'Invalid date'
282
+ } else if (!this.validateDate(this.startInput)) {
283
+ this.startError = 'Invalid date'
219
284
  hasErrors = true
220
285
  }
221
286
  }
222
287
 
223
288
  if (!hasErrors) {
224
- this._dispatchCloseEvent(true)
289
+ this.dispatchCloseEvent(true)
225
290
  }
226
291
  }
227
292
 
228
- private _dispatchChangeEvent(): void {
293
+ private dispatchChangeEvent(): void {
229
294
  if (this.rangeMode && this.selectedRange) {
230
295
  const event: ModalInputDatePickerChangeEvent = {
231
296
  range: this.selectedRange,
@@ -256,7 +321,7 @@ export class UiDatePickerModalInput extends LitElement {
256
321
  }
257
322
  }
258
323
 
259
- private _dispatchCloseEvent(confirmed: boolean): void {
324
+ private dispatchCloseEvent(confirmed: boolean): void {
260
325
  this.dispatchEvent(
261
326
  new CustomEvent('modal-input-close', {
262
327
  detail: {
@@ -270,80 +335,84 @@ export class UiDatePickerModalInput extends LitElement {
270
335
  )
271
336
  }
272
337
 
273
- private _renderHeader(): TemplateResult {
338
+ private renderHeader(): TemplateResult {
274
339
  return html`
275
- <div class="modal-header">
276
- <h2 class="modal-title">${this.title}</h2>
277
- <ui-icon-button @click=${this._handleCancel} aria-label="Close" title="Close">
340
+ <header class="modal-header">
341
+ <h2 id="modal-title" class="modal-title">${this.title}</h2>
342
+ <ui-icon-button @click=${this.handleCancel} aria-label="Close" title="Close">
278
343
  <ui-icon icon="close"></ui-icon>
279
344
  </ui-icon-button>
280
- </div>
345
+ </header>
281
346
  `
282
347
  }
283
348
 
284
- private _renderInputs(): TemplateResult {
349
+ private renderInputs(): TemplateResult {
285
350
  return html`
286
- <div class="modal-content">
287
- <div style="display: flex; flex-direction: column; gap: 16px; width: 100%; max-width: 400px;">
288
- <ui-outlined-text-field
289
- .label=${this.rangeMode ? this.startLabel : 'Date'}
290
- .placeholder=${this.placeholder}
291
- .value=${this._startInput}
292
- .errorMessage=${this._startError}
293
- .error=${!!this._startError}
294
- @input=${this._handleStartInput}
295
- >
296
- <ui-icon slot="trailing-icon" icon="calendarToday"></ui-icon>
297
- </ui-outlined-text-field>
298
-
299
- ${this.rangeMode
300
- ? html`
301
- <ui-outlined-text-field
302
- .label=${this.endLabel}
303
- .placeholder=${this.placeholder}
304
- .value=${this._endInput}
305
- .errorMessage=${this._endError}
306
- .error=${!!this._endError}
307
- @input=${this._handleEndInput}
308
- >
309
- <ui-icon slot="trailing-icon" icon="calendarToday"></ui-icon>
310
- </ui-outlined-text-field>
311
- `
312
- : ''}
313
-
314
- <div
315
- style="margin-top: 16px; padding: 16px; background: var(--ui-color-surface-variant, #e7e0ec); border-radius: 12px;"
316
- >
317
- <p style="margin: 0; font-size: 14px; color: var(--ui-color-on-surface-variant, #49454f);">
351
+ <main class="modal-content">
352
+ <div class="input-container" role="form" aria-labelledby="modal-title">
353
+ <fieldset>
354
+ <legend class="visually-hidden">${this.rangeMode ? 'Enter date range' : 'Enter date'}</legend>
355
+
356
+ <ui-outlined-text-field
357
+ .label=${this.rangeMode ? this.startLabel : 'Date'}
358
+ .placeholder=${this.placeholder}
359
+ .value=${this.startInput}
360
+ .invalidText=${this.startError}
361
+ ?invalid=${!!this.startError}
362
+ @input=${this.handleStartInput}
363
+ required
364
+ aria-describedby="format-help"
365
+ >
366
+ <ui-icon slot="suffix" icon="calendarToday"></ui-icon>
367
+ </ui-outlined-text-field>
368
+
369
+ ${this.rangeMode
370
+ ? html`
371
+ <ui-outlined-text-field
372
+ .label=${this.endLabel}
373
+ .placeholder=${this.placeholder}
374
+ .value=${this.endInput}
375
+ .invalidText=${this.endError}
376
+ ?invalid=${!!this.endError}
377
+ @input=${this.handleEndInput}
378
+ required
379
+ aria-describedby="format-help"
380
+ >
381
+ <ui-icon slot="suffix" icon="calendarToday"></ui-icon>
382
+ </ui-outlined-text-field>
383
+ `
384
+ : ''}
385
+ </fieldset>
386
+
387
+ <aside id="format-help" class="format-help" role="note" aria-label="Date format information">
388
+ <p class="help-title">
318
389
  <strong>Supported formats:</strong><br />
319
390
  MM/DD/YYYY, MM-DD-YYYY, YYYY-MM-DD
320
391
  </p>
321
- <p style="margin: 8px 0 0; font-size: 14px; color: var(--ui-color-on-surface-variant, #49454f);">
322
- <strong>Examples:</strong> 12/25/2024, 12-25-2024, 2024-12-25
323
- </p>
324
- </div>
392
+ <p class="help-examples"><strong>Examples:</strong> 12/25/2024, 12-25-2024, 2024-12-25</p>
393
+ </aside>
325
394
  </div>
326
- </div>
395
+ </main>
327
396
  `
328
397
  }
329
398
 
330
- private _renderActions(): TemplateResult {
399
+ private renderActions(): TemplateResult {
331
400
  const hasValidInput = this.rangeMode
332
- ? this._startInput.trim() && this._endInput.trim() && !this._startError && !this._endError
333
- : this._startInput.trim() && !this._startError
401
+ ? this.startInput.trim() && this.endInput.trim() && !this.startError && !this.endError
402
+ : this.startInput.trim() && !this.startError
334
403
 
335
404
  return html`
336
- <div class="modal-actions">
337
- <ui-button color="text" @click=${this._handleCancel}>Cancel</ui-button>
338
- <ui-button color="filled" @click=${this._handleConfirm} .disabled=${!hasValidInput}> Confirm </ui-button>
339
- </div>
405
+ <footer class="modal-actions">
406
+ <ui-button color="text" @click=${this.handleCancel}>Cancel</ui-button>
407
+ <ui-button color="filled" @click=${this.handleConfirm} .disabled=${!hasValidInput}> Confirm </ui-button>
408
+ </footer>
340
409
  `
341
410
  }
342
411
 
343
412
  override render(): TemplateResult {
344
413
  return html`
345
- <ui-dialog .open=${this.open} @close=${this._handleCancel}>
346
- ${this._renderHeader()} ${this._renderInputs()} ${this._renderActions()}
414
+ <ui-dialog .open=${this.open} @close=${this.handleCancel}>
415
+ ${this.renderHeader()} ${this.renderInputs()} ${this.renderActions()}
347
416
  </ui-dialog>
348
417
  `
349
418
  }