@aceshooting/lyra-ui 2.0.0 → 2.1.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.
Files changed (224) hide show
  1. package/README.md +87 -14
  2. package/custom-elements.json +11555 -5539
  3. package/dist/components/app-rail/app-rail-item.d.ts +30 -0
  4. package/dist/components/app-rail/app-rail-item.js +68 -0
  5. package/dist/components/app-rail/app-rail-item.styles.d.ts +1 -0
  6. package/dist/components/app-rail/app-rail-item.styles.js +65 -0
  7. package/dist/components/app-rail/app-rail.d.ts +21 -24
  8. package/dist/components/app-rail/app-rail.js +68 -132
  9. package/dist/components/app-rail/app-rail.styles.js +5 -2
  10. package/dist/components/attachment-chip/attachment-chip.d.ts +38 -11
  11. package/dist/components/attachment-chip/attachment-chip.js +73 -29
  12. package/dist/components/attachment-trigger/attachment-trigger.d.ts +13 -0
  13. package/dist/components/attachment-trigger/attachment-trigger.js +13 -1
  14. package/dist/components/attachment-trigger/attachment-trigger.styles.js +28 -6
  15. package/dist/components/chart/bar-chart.js +2 -18
  16. package/dist/components/chart/box-plot.d.ts +25 -1
  17. package/dist/components/chart/box-plot.js +136 -6
  18. package/dist/components/chart/box-plot.styles.js +13 -0
  19. package/dist/components/chart/bubble-chart.js +2 -18
  20. package/dist/components/chart/chart.d.ts +54 -10
  21. package/dist/components/chart/chart.js +182 -39
  22. package/dist/components/chart/doughnut-chart.js +2 -18
  23. package/dist/components/chart/histogram-bin.d.ts +5 -2
  24. package/dist/components/chart/histogram-bin.js +12 -4
  25. package/dist/components/chart/histogram.js +8 -16
  26. package/dist/components/chart/line-chart.js +2 -18
  27. package/dist/components/chart/lite-chart.d.ts +80 -3
  28. package/dist/components/chart/lite-chart.js +250 -53
  29. package/dist/components/chart/pie-chart.js +2 -18
  30. package/dist/components/chart/polar-area-chart.js +2 -18
  31. package/dist/components/chart/radar-chart.js +2 -18
  32. package/dist/components/chart/scatter-chart.js +2 -18
  33. package/dist/components/chat-composer/chat-composer.d.ts +42 -1
  34. package/dist/components/chat-composer/chat-composer.js +84 -3
  35. package/dist/components/chat-composer/chat-composer.styles.js +11 -1
  36. package/dist/components/chat-message/chat-message.d.ts +32 -4
  37. package/dist/components/chat-message/chat-message.js +57 -17
  38. package/dist/components/checkbox/checkbox.d.ts +66 -8
  39. package/dist/components/checkbox/checkbox.js +173 -50
  40. package/dist/components/checkbox/checkbox.styles.js +9 -2
  41. package/dist/components/chip/chip-group.js +8 -2
  42. package/dist/components/chip/chip.js +9 -2
  43. package/dist/components/citation-badge/citation-badge.js +21 -3
  44. package/dist/components/code-block/code-block.d.ts +38 -0
  45. package/dist/components/code-block/code-block.js +72 -7
  46. package/dist/components/code-block/code-loader.d.ts +44 -1
  47. package/dist/components/code-block/code-loader.js +63 -0
  48. package/dist/components/combobox/combobox.d.ts +46 -5
  49. package/dist/components/combobox/combobox.js +175 -35
  50. package/dist/components/combobox/combobox.styles.js +3 -3
  51. package/dist/components/context-meter/context-meter.js +17 -8
  52. package/dist/components/context-meter/context-meter.styles.js +0 -6
  53. package/dist/components/conversation-item/conversation-item.d.ts +13 -16
  54. package/dist/components/conversation-item/conversation-item.js +29 -20
  55. package/dist/components/date-picker/calendar-core.d.ts +24 -0
  56. package/dist/components/date-picker/calendar-core.js +51 -4
  57. package/dist/components/date-picker/date-input.d.ts +77 -10
  58. package/dist/components/date-picker/date-input.js +278 -73
  59. package/dist/components/date-picker/date-picker.d.ts +28 -4
  60. package/dist/components/date-picker/date-picker.js +95 -37
  61. package/dist/components/dialog/confirm.js +3 -6
  62. package/dist/components/dialog/dialog.d.ts +75 -31
  63. package/dist/components/dialog/dialog.js +147 -131
  64. package/dist/components/dialog/dialog.styles.js +46 -2
  65. package/dist/components/dock-panel/dock-panel.d.ts +8 -0
  66. package/dist/components/dock-panel/dock-panel.js +15 -1
  67. package/dist/components/dock-panel/dock-panel.styles.js +15 -1
  68. package/dist/components/document-preview/document-preview.d.ts +15 -7
  69. package/dist/components/document-preview/document-preview.js +29 -13
  70. package/dist/components/empty/empty.d.ts +1 -2
  71. package/dist/components/empty/empty.js +18 -12
  72. package/dist/components/empty/empty.styles.js +0 -3
  73. package/dist/components/export-button/export-button.d.ts +12 -0
  74. package/dist/components/export-button/export-button.js +33 -5
  75. package/dist/components/export-button/export-button.styles.js +5 -1
  76. package/dist/components/file-input/accept.js +2 -0
  77. package/dist/components/file-input/file-input.d.ts +10 -1
  78. package/dist/components/file-input/file-input.js +36 -5
  79. package/dist/components/flag/flag.d.ts +1 -1
  80. package/dist/components/flag/flag.js +15 -1
  81. package/dist/components/flag/flag.styles.js +1 -1
  82. package/dist/components/flag/language-map.d.ts +4 -1
  83. package/dist/components/flag/language-map.js +6 -3
  84. package/dist/components/gauge/gauge.d.ts +10 -1
  85. package/dist/components/gauge/gauge.js +28 -9
  86. package/dist/components/generation-status/generation-status.d.ts +13 -5
  87. package/dist/components/generation-status/generation-status.js +59 -5
  88. package/dist/components/graph/graph.d.ts +37 -3
  89. package/dist/components/graph/graph.js +132 -20
  90. package/dist/components/heatmap/calendar-grid.d.ts +15 -6
  91. package/dist/components/heatmap/calendar-grid.js +17 -6
  92. package/dist/components/heatmap/heatmap-scale.d.ts +5 -1
  93. package/dist/components/heatmap/heatmap-scale.js +8 -2
  94. package/dist/components/heatmap/heatmap.d.ts +166 -25
  95. package/dist/components/heatmap/heatmap.js +372 -89
  96. package/dist/components/json-viewer/json-viewer.d.ts +22 -5
  97. package/dist/components/json-viewer/json-viewer.js +115 -37
  98. package/dist/components/json-viewer/json-viewer.styles.js +2 -1
  99. package/dist/components/kbd/kbd.js +24 -7
  100. package/dist/components/live-region/live-region.d.ts +10 -1
  101. package/dist/components/live-region/live-region.js +42 -6
  102. package/dist/components/map/map.d.ts +24 -1
  103. package/dist/components/map/map.js +89 -24
  104. package/dist/components/markdown/markdown-loader.d.ts +24 -0
  105. package/dist/components/markdown/markdown-loader.js +35 -1
  106. package/dist/components/markdown/markdown.d.ts +46 -8
  107. package/dist/components/markdown/markdown.js +136 -31
  108. package/dist/components/markdown/markdown.styles.js +7 -1
  109. package/dist/components/media-card/media-card.d.ts +25 -17
  110. package/dist/components/media-card/media-card.js +43 -65
  111. package/dist/components/mention-popover/mention-popover.d.ts +4 -1
  112. package/dist/components/mention-popover/mention-popover.js +23 -5
  113. package/dist/components/menu/menu-item.d.ts +32 -3
  114. package/dist/components/menu/menu-item.js +94 -7
  115. package/dist/components/menu/menu-item.styles.js +8 -0
  116. package/dist/components/menu/menu.d.ts +25 -1
  117. package/dist/components/menu/menu.js +99 -3
  118. package/dist/components/menu/menu.styles.js +3 -2
  119. package/dist/components/model-select/model-select.d.ts +48 -3
  120. package/dist/components/model-select/model-select.js +120 -22
  121. package/dist/components/model-select/model-select.styles.js +17 -3
  122. package/dist/components/model-settings-panel/model-settings-panel.d.ts +25 -1
  123. package/dist/components/model-settings-panel/model-settings-panel.js +65 -1
  124. package/dist/components/playback/playback.d.ts +6 -2
  125. package/dist/components/playback/playback.js +72 -29
  126. package/dist/components/playback/playback.styles.js +6 -1
  127. package/dist/components/responsive-panel/responsive-panel.d.ts +29 -16
  128. package/dist/components/responsive-panel/responsive-panel.js +130 -132
  129. package/dist/components/responsive-panel/responsive-panel.styles.js +1 -1
  130. package/dist/components/result-card/result-card.d.ts +8 -2
  131. package/dist/components/result-card/result-card.js +9 -3
  132. package/dist/components/result-card/result-card.styles.js +0 -1
  133. package/dist/components/select/select.d.ts +59 -20
  134. package/dist/components/select/select.js +125 -43
  135. package/dist/components/skeleton/skeleton.d.ts +3 -0
  136. package/dist/components/skeleton/skeleton.js +7 -1
  137. package/dist/components/skeleton/skeleton.styles.js +5 -4
  138. package/dist/components/slider/slider.d.ts +57 -5
  139. package/dist/components/slider/slider.js +195 -80
  140. package/dist/components/source-card/source-card.d.ts +17 -5
  141. package/dist/components/source-card/source-card.js +55 -8
  142. package/dist/components/source-card/source-card.styles.js +1 -1
  143. package/dist/components/source-list/source-list.js +9 -1
  144. package/dist/components/sparkline/sparkline.js +34 -10
  145. package/dist/components/split/split.d.ts +141 -14
  146. package/dist/components/split/split.js +357 -52
  147. package/dist/components/split/split.styles.js +18 -1
  148. package/dist/components/stat/stat.d.ts +10 -4
  149. package/dist/components/stat/stat.js +46 -11
  150. package/dist/components/stream-status/stream-status.d.ts +27 -11
  151. package/dist/components/stream-status/stream-status.js +95 -13
  152. package/dist/components/streaming-text/streaming-text.d.ts +9 -3
  153. package/dist/components/streaming-text/streaming-text.js +25 -10
  154. package/dist/components/streaming-text/streaming-text.styles.js +3 -2
  155. package/dist/components/switch/switch.d.ts +52 -7
  156. package/dist/components/switch/switch.js +111 -40
  157. package/dist/components/switch/switch.styles.js +1 -1
  158. package/dist/components/table/table.d.ts +18 -3
  159. package/dist/components/table/table.js +88 -20
  160. package/dist/components/tabs/tabs.d.ts +23 -0
  161. package/dist/components/tabs/tabs.js +76 -12
  162. package/dist/components/tabs/tabs.styles.js +11 -0
  163. package/dist/components/thinking-panel/thinking-panel.d.ts +3 -1
  164. package/dist/components/thinking-panel/thinking-panel.js +34 -9
  165. package/dist/components/thinking-panel/thinking-panel.styles.js +6 -0
  166. package/dist/components/time-range/time-range.d.ts +59 -2
  167. package/dist/components/time-range/time-range.js +163 -67
  168. package/dist/components/toast/toast-item.d.ts +12 -3
  169. package/dist/components/toast/toast-item.js +90 -53
  170. package/dist/components/toast/toast-item.styles.js +59 -28
  171. package/dist/components/toast/toast.js +17 -5
  172. package/dist/components/toast/toaster.js +10 -1
  173. package/dist/components/tool-approval-dialog/tool-approval-dialog.d.ts +11 -10
  174. package/dist/components/tool-approval-dialog/tool-approval-dialog.js +50 -124
  175. package/dist/components/tool-approval-dialog/tool-approval-dialog.styles.js +1 -1
  176. package/dist/components/tool-call-chip/tool-call-chip.d.ts +18 -2
  177. package/dist/components/tool-call-chip/tool-call-chip.js +86 -25
  178. package/dist/components/tool-param-form/tool-param-form.d.ts +89 -28
  179. package/dist/components/tool-param-form/tool-param-form.js +350 -106
  180. package/dist/components/tool-result-dialog/tool-result-dialog.d.ts +27 -7
  181. package/dist/components/tool-result-dialog/tool-result-dialog.js +80 -115
  182. package/dist/components/tool-result-dialog/tool-result-dialog.styles.js +1 -1
  183. package/dist/components/tool-result-view/registry.d.ts +0 -8
  184. package/dist/components/tool-result-view/registry.js +7 -2
  185. package/dist/components/tool-result-view/tool-result-view.d.ts +6 -3
  186. package/dist/components/tool-result-view/tool-result-view.js +18 -4
  187. package/dist/components/tool-select-dialog/tool-select-dialog.d.ts +11 -11
  188. package/dist/components/tool-select-dialog/tool-select-dialog.js +44 -133
  189. package/dist/components/tool-select-dialog/tool-select-dialog.styles.js +13 -4
  190. package/dist/components/tree/tree-node.d.ts +4 -1
  191. package/dist/components/tree/tree-node.js +4 -1
  192. package/dist/components/tree/tree.d.ts +17 -8
  193. package/dist/components/tree/tree.js +21 -20
  194. package/dist/components/typing-indicator/typing-indicator.d.ts +12 -2
  195. package/dist/components/typing-indicator/typing-indicator.js +16 -4
  196. package/dist/components/typing-indicator/typing-indicator.styles.js +2 -2
  197. package/dist/components/virtual-list/virtual-list.d.ts +56 -19
  198. package/dist/components/virtual-list/virtual-list.js +184 -38
  199. package/dist/components/virtual-list/virtual-list.styles.js +12 -0
  200. package/dist/components/widget/widget.d.ts +5 -4
  201. package/dist/components/widget/widget.js +54 -120
  202. package/dist/components/widget/widget.styles.js +8 -2
  203. package/dist/components/word-cloud/word-cloud-layout.d.ts +7 -0
  204. package/dist/components/word-cloud/word-cloud-layout.js +20 -6
  205. package/dist/components/word-cloud/word-cloud.d.ts +22 -2
  206. package/dist/components/word-cloud/word-cloud.js +45 -6
  207. package/dist/components/word-cloud/word-cloud.styles.js +13 -1
  208. package/dist/internal/anchored-validity.d.ts +1 -0
  209. package/dist/internal/anchored-validity.js +86 -0
  210. package/dist/internal/form-associated.d.ts +7 -0
  211. package/dist/internal/form-associated.js +43 -4
  212. package/dist/internal/numbers.d.ts +12 -0
  213. package/dist/internal/numbers.js +26 -0
  214. package/dist/internal/overlay-manager.d.ts +51 -0
  215. package/dist/internal/overlay-manager.js +523 -0
  216. package/dist/internal/positioner.js +27 -3
  217. package/dist/internal/safe-url.d.ts +12 -0
  218. package/dist/internal/safe-url.js +41 -0
  219. package/dist/internal/tokens.styles.js +17 -11
  220. package/dist/lyra.d.ts +5 -2
  221. package/dist/lyra.js +3 -1
  222. package/llms-full.txt +970 -426
  223. package/llms.txt +5 -4
  224. package/package.json +19 -2
@@ -4,15 +4,17 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
4
4
  else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
5
5
  return c > 3 && r && Object.defineProperty(target, key, r), r;
6
6
  };
7
- import { html, nothing } from 'lit';
7
+ import { html, nothing, } from 'lit';
8
8
  import { property, state } from 'lit/decorators.js';
9
9
  import { LyraElement } from '../../internal/lyra-element.js';
10
10
  import { FormAssociated } from '../../internal/form-associated.js';
11
+ import { SET_ANCHORED_VALIDITY } from '../../internal/anchored-validity.js';
11
12
  import { defineElement } from '../../internal/prefix.js';
12
13
  import { place } from '../../internal/positioner.js';
13
14
  import { nextId } from '../../internal/a11y.js';
14
15
  import { closeIcon, calendarIcon } from '../../internal/icons.js';
15
- import { parseISO, formatISO } from './calendar-core.js';
16
+ import { composedContains, deepActiveElement } from '../../internal/overlay-manager.js';
17
+ import { dateTimeFormat, parseISO, formatISO, normalizeCalendarMonths, normalizeWeekdayFormat, } from './calendar-core.js';
16
18
  import { styles } from './date-input.styles.js';
17
19
  import { LyraDatePicker } from './date-picker.js';
18
20
  import './date-picker.js';
@@ -36,6 +38,14 @@ function localeDateOrder(locale) {
36
38
  return ['month', 'day', 'year']; // Date.parse()'s own bias, as a last-resort fallback
37
39
  }
38
40
  }
41
+ const monthsConverter = {
42
+ fromAttribute: normalizeCalendarMonths,
43
+ toAttribute: normalizeCalendarMonths,
44
+ };
45
+ const weekdayFormatConverter = {
46
+ fromAttribute: normalizeWeekdayFormat,
47
+ toAttribute: normalizeWeekdayFormat,
48
+ };
39
49
  /**
40
50
  * `<lyra-date-input>` — a date field with an attached calendar popover.
41
51
  * Mirrors the core `<wa-date-input>` API under `lyra-`. Value is ISO 8601
@@ -49,31 +59,52 @@ function localeDateOrder(locale) {
49
59
  * @event change - Fired on committed date transitions.
50
60
  * @event lyra-show / lyra-hide - Calendar popover lifecycle.
51
61
  * @event lyra-clear - The clear button was used.
52
- * @csspart form-control, form-control-label, input-wrapper, input, clear-button, expand-button, expand-icon, popup, date-picker, hint, error
62
+ * @csspart form-control - The outer form-control wrapper.
63
+ * @csspart form-control-label - The label wrapper.
64
+ * @csspart input-wrapper - The input and button wrapper.
65
+ * @csspart input - The text input.
66
+ * @csspart clear-button - The clear control.
67
+ * @csspart expand-button - The calendar popup toggle.
68
+ * @csspart expand-icon - The calendar icon.
69
+ * @csspart popup - The positioned calendar popup.
70
+ * @csspart date-picker - The nested date picker.
71
+ * @csspart hint - The hint message.
72
+ * @csspart error - The validation message.
53
73
  * @slot label - Custom label content.
54
74
  * @slot error - Custom error content.
55
75
  * @slot hint - Custom hint content.
56
76
  */
57
77
  export class LyraDateInput extends FormAssociated(LyraElement) {
78
+ static { this.styles = [LyraElement.styles, styles]; }
79
+ static { this.properties = {
80
+ mode: { noAccessor: true },
81
+ min: { noAccessor: true },
82
+ max: { noAccessor: true },
83
+ readonly: { type: Boolean, reflect: true, noAccessor: true },
84
+ disablePast: { type: Boolean, attribute: 'disable-past', noAccessor: true },
85
+ disableFuture: { type: Boolean, attribute: 'disable-future', noAccessor: true },
86
+ }; }
58
87
  constructor() {
59
- super(...arguments);
60
- this.mode = 'single';
61
- this.min = '';
62
- this.max = '';
63
- this.readonly = false;
88
+ super();
64
89
  this.open = false;
65
90
  this.withClear = false;
66
91
  this.label = '';
67
92
  this.hint = '';
68
93
  this.errorText = '';
69
94
  this.placeholder = '';
95
+ this.accessibleLabel = null;
70
96
  this.locale = '';
71
97
  this.months = 1;
72
98
  this.firstDayOfWeek = 'auto';
73
99
  this.weekdayFormat = 'short';
74
- this.disablePast = false;
75
- this.disableFuture = false;
76
100
  this.withOutsideDays = false;
101
+ /** Accessible label for the clear button. Override for a non-English `locale`. */
102
+ this.clearLabel = 'Clear';
103
+ /** Accessible label for the calendar-toggle button. Override for a non-English `locale`. */
104
+ this.openLabel = 'Open calendar';
105
+ /** Accessible name for the calendar popover dialog. Override for a non-English `locale`. */
106
+ this.dialogLabel = 'Choose date';
107
+ this.restorePopupFocusOnClose = false;
77
108
  this.inputId = nextId('date-input');
78
109
  this.popupId = nextId('date-popup');
79
110
  // Set on the date input's first `blur`; gates the `data-invalid`
@@ -89,6 +120,13 @@ export class LyraDateInput extends FormAssociated(LyraElement) {
89
120
  this.hasHintSlot = false;
90
121
  this.hasErrorSlot = false;
91
122
  this.hasLabelSlot = false;
123
+ this._mode = 'single';
124
+ this._min = '';
125
+ this._max = '';
126
+ this._readonly = false;
127
+ this._disablePast = false;
128
+ this._disableFuture = false;
129
+ this.typedBadInput = false;
92
130
  this.onDocPointer = (e) => {
93
131
  if (!e.composedPath().includes(this))
94
132
  this.hide();
@@ -105,19 +143,19 @@ export class LyraDateInput extends FormAssociated(LyraElement) {
105
143
  return;
106
144
  }
107
145
  const parsed = this.mode === 'range' ? this.parseRangeText(raw) : this.parseSingleText(raw);
108
- if (parsed && this.isWithinBounds(parsed)) {
146
+ if (parsed) {
109
147
  this.value = parsed;
110
148
  this.emit('input');
111
149
  this.emit('change');
112
150
  }
113
151
  else {
114
- // Unparseable (or out-of-bounds) text: don't silently keep the
115
- // committed value while the field still shows garbage -- revert the
116
- // displayed text back to the last valid commit and flag the
117
- // constraint-validation state (mirrors how `required` is already
118
- // wired via internals.setValidity(), see form-associated.ts).
152
+ // Unparseable text: don't silently keep the committed value while the
153
+ // field still shows garbage -- revert the display to the last commit
154
+ // and flag bad input. Parseable dates outside an active bound instead
155
+ // commit above and expose their precise range validity state.
119
156
  target.value = this.displayText;
120
- this.internals.setValidity({ badInput: true }, 'Enter a valid date');
157
+ this.setTypedBadInput(true);
158
+ this.updateValidity();
121
159
  }
122
160
  };
123
161
  this.onInputKey = (e) => {
@@ -133,8 +171,7 @@ export class LyraDateInput extends FormAssociated(LyraElement) {
133
171
  this.onFormControlKey = (e) => {
134
172
  if (e.key === 'Escape' && this.open) {
135
173
  e.preventDefault();
136
- this.hide();
137
- this.renderRoot.querySelector('[part="expand-button"]')?.focus();
174
+ this.hide(true);
138
175
  }
139
176
  };
140
177
  this.onInputBlur = () => {
@@ -167,25 +204,193 @@ export class LyraDateInput extends FormAssociated(LyraElement) {
167
204
  // The picker only fires `change` once a selection is finalized (a single
168
205
  // pick, or the second click of a range), so this is always the right
169
206
  // moment to close, in either mode.
170
- this.hide();
207
+ this.hide(true);
171
208
  };
209
+ this.addEventListener('invalid', () => {
210
+ this.touched = true;
211
+ });
212
+ }
213
+ get mode() {
214
+ return this._mode;
215
+ }
216
+ set mode(next) {
217
+ const old = this._mode;
218
+ this._mode = next === 'range' ? 'range' : 'single';
219
+ this.updateValidity();
220
+ this.requestUpdate('mode', old);
221
+ }
222
+ get min() {
223
+ return this._min;
224
+ }
225
+ set min(next) {
226
+ const old = this._min;
227
+ this._min = next ?? '';
228
+ this.updateValidity();
229
+ this.requestUpdate('min', old);
230
+ }
231
+ get max() {
232
+ return this._max;
233
+ }
234
+ set max(next) {
235
+ const old = this._max;
236
+ this._max = next ?? '';
237
+ this.updateValidity();
238
+ this.requestUpdate('max', old);
239
+ }
240
+ get readonly() {
241
+ return this._readonly;
242
+ }
243
+ set readonly(next) {
244
+ const old = this._readonly;
245
+ this._readonly = Boolean(next);
246
+ this.toggleAttribute('readonly', this._readonly);
247
+ this.updateValidity();
248
+ this.requestUpdate('readonly', old);
249
+ }
250
+ get disablePast() {
251
+ return this._disablePast;
252
+ }
253
+ set disablePast(next) {
254
+ const old = this._disablePast;
255
+ this._disablePast = Boolean(next);
256
+ this.updateValidity();
257
+ this.requestUpdate('disablePast', old);
258
+ }
259
+ get disableFuture() {
260
+ return this._disableFuture;
261
+ }
262
+ set disableFuture(next) {
263
+ const old = this._disableFuture;
264
+ this._disableFuture = Boolean(next);
265
+ this.updateValidity();
266
+ this.requestUpdate('disableFuture', old);
267
+ }
268
+ get value() {
269
+ return super.value;
270
+ }
271
+ set value(next) {
272
+ this.setTypedBadInput(false);
273
+ super.value = this.normalizeCommittedValue(next ?? '');
274
+ }
275
+ setTypedBadInput(next) {
276
+ const old = this.typedBadInput;
277
+ this.typedBadInput = next;
278
+ if (old !== next)
279
+ this.requestUpdate('typedBadInput', old);
280
+ }
281
+ parseStrictISO(value) {
282
+ if (!/^\d{4}-\d{2}-\d{2}$/.test(value))
283
+ return null;
284
+ return parseISO(value);
285
+ }
286
+ normalizeCommittedValue(value) {
287
+ if (value === '')
288
+ return '';
289
+ const parts = value.split('/');
290
+ if (parts.length !== 1 && parts.length !== 2)
291
+ return '';
292
+ const dates = parts.map((part) => this.parseStrictISO(part));
293
+ if (dates.some((date) => date === null))
294
+ return '';
295
+ if (parts.length === 2 && dates[0] > dates[1])
296
+ return `${parts[1]}/${parts[0]}`;
297
+ return value;
298
+ }
299
+ valueDates(value) {
300
+ const parts = value.split('/');
301
+ const expectedParts = this.mode === 'range' ? 2 : 1;
302
+ if (parts.length !== expectedParts)
303
+ return null;
304
+ const dates = parts.map((part) => this.parseStrictISO(part));
305
+ return dates.some((date) => date === null) ? null : dates;
306
+ }
307
+ updateValidity() {
308
+ if (this.readonly) {
309
+ this[SET_ANCHORED_VALIDITY]({});
310
+ return;
311
+ }
312
+ const flags = {};
313
+ let underflowMessage = '';
314
+ let overflowMessage = '';
315
+ if (this.required && this.value === '')
316
+ flags.valueMissing = true;
317
+ if (this.typedBadInput)
318
+ flags.badInput = true;
319
+ if (this.value !== '') {
320
+ const dates = this.valueDates(this.value);
321
+ if (!dates) {
322
+ flags.badInput = true;
323
+ }
324
+ else {
325
+ const min = this.parseStrictISO(this.min);
326
+ const max = this.parseStrictISO(this.max);
327
+ const today = new Date();
328
+ today.setHours(0, 0, 0, 0);
329
+ if (min !== null && dates.some((date) => date < min)) {
330
+ flags.rangeUnderflow = true;
331
+ underflowMessage = `Date must be on or after ${this.min}.`;
332
+ }
333
+ if (this.disablePast && dates.some((date) => date < today)) {
334
+ flags.rangeUnderflow = true;
335
+ underflowMessage ||= 'Date cannot be in the past.';
336
+ }
337
+ if (max !== null && dates.some((date) => date > max)) {
338
+ flags.rangeOverflow = true;
339
+ overflowMessage = `Date must be on or before ${this.max}.`;
340
+ }
341
+ if (this.disableFuture && dates.some((date) => date > today)) {
342
+ flags.rangeOverflow = true;
343
+ overflowMessage ||= 'Date cannot be in the future.';
344
+ }
345
+ }
346
+ }
347
+ let message = '';
348
+ if (flags.badInput)
349
+ message = 'Enter a valid date.';
350
+ else if (flags.rangeUnderflow)
351
+ message = underflowMessage;
352
+ else if (flags.rangeOverflow)
353
+ message = overflowMessage;
354
+ else if (flags.valueMissing)
355
+ message = 'Please fill out this field.';
356
+ this[SET_ANCHORED_VALIDITY](flags, message);
172
357
  }
173
- static { this.styles = [LyraElement.styles, styles]; }
174
358
  get displayText() {
175
359
  const parts = this.value.split('/');
176
360
  const from = parseISO(parts[0] ?? '');
177
361
  const to = parseISO(parts[1] ?? '');
178
362
  if (!from)
179
363
  return '';
180
- const fmt = (d) => d.toLocaleDateString(this.locale || undefined);
364
+ const formatter = dateTimeFormat(this.locale, {});
365
+ const fmt = (d) => formatter.format(d);
181
366
  return this.mode === 'range' && to ? `${fmt(from)} – ${fmt(to)}` : fmt(from);
182
367
  }
183
- willUpdate() {
368
+ willUpdate(changed) {
184
369
  if (!this.hasUpdated) {
185
370
  this.hasHintSlot = Array.from(this.children).some((el) => el.getAttribute('slot') === 'hint');
186
371
  this.hasErrorSlot = Array.from(this.children).some((el) => el.getAttribute('slot') === 'error');
187
372
  this.hasLabelSlot = Array.from(this.children).some((el) => el.getAttribute('slot') === 'label');
188
373
  }
374
+ if (changed.has('open')) {
375
+ if (this.open) {
376
+ if (!this.popupTrigger) {
377
+ const active = deepActiveElement(this.ownerDocument);
378
+ this.popupTrigger = active && typeof active.focus === 'function' ? active : undefined;
379
+ }
380
+ this.ownerDocument.addEventListener('pointerdown', this.onDocPointer);
381
+ }
382
+ else {
383
+ this.ownerDocument.removeEventListener('pointerdown', this.onDocPointer);
384
+ const active = deepActiveElement(this.ownerDocument);
385
+ const popup = this.renderRoot.querySelector('[part="popup"]');
386
+ const restoreFocus = this.restorePopupFocusOnClose || (popup !== null && composedContains(popup, active));
387
+ const trigger = this.popupTrigger;
388
+ this.popupTrigger = undefined;
389
+ this.restorePopupFocusOnClose = false;
390
+ if (restoreFocus && trigger?.isConnected)
391
+ trigger.focus();
392
+ }
393
+ }
189
394
  }
190
395
  /** Open the calendar popover. */
191
396
  show() {
@@ -193,18 +398,22 @@ export class LyraDateInput extends FormAssociated(LyraElement) {
193
398
  return;
194
399
  this.open = true;
195
400
  this.emit('lyra-show');
196
- document.addEventListener('pointerdown', this.onDocPointer);
197
401
  }
198
402
  /** Close the calendar popover. */
199
- hide() {
403
+ hide(restoreFocus = false) {
200
404
  if (!this.open)
201
405
  return;
406
+ this.restorePopupFocusOnClose ||= restoreFocus;
202
407
  this.open = false;
203
408
  this.emit('lyra-hide');
204
- document.removeEventListener('pointerdown', this.onDocPointer);
205
409
  }
206
410
  /** Clear the value. */
207
411
  clear() {
412
+ // Matches how `onInputBlur` already flips this on the first blur -- an
413
+ // explicit user-initiated clear() is itself an interaction, so a
414
+ // required-and-now-empty field must surface its invalid state right
415
+ // away instead of silently looking valid until some later blur.
416
+ this.touched = true;
208
417
  this.value = '';
209
418
  this.emit('input');
210
419
  this.emit('change');
@@ -214,7 +423,9 @@ export class LyraDateInput extends FormAssociated(LyraElement) {
214
423
  super.disconnectedCallback();
215
424
  this.cleanupFn?.();
216
425
  this.cleanupFn = undefined;
217
- document.removeEventListener('pointerdown', this.onDocPointer);
426
+ this.ownerDocument.removeEventListener('pointerdown', this.onDocPointer);
427
+ this.popupTrigger = undefined;
428
+ this.restorePopupFocusOnClose = false;
218
429
  // Reset so a reconnect (e.g. a drag-drop reparent) re-triggers
219
430
  // `updated()`'s `open`-driven branch -- without this, `open` stays
220
431
  // stuck `true` across the disconnect, `updated()` never sees it
@@ -234,7 +445,16 @@ export class LyraDateInput extends FormAssociated(LyraElement) {
234
445
  this.cleanupFn = place(anchor, popup);
235
446
  }
236
447
  }
237
- if (changed.has('touched') || changed.has('required') || changed.has('value')) {
448
+ if (changed.has('touched') ||
449
+ changed.has('required') ||
450
+ changed.has('value') ||
451
+ changed.has('mode') ||
452
+ changed.has('min') ||
453
+ changed.has('max') ||
454
+ changed.has('readonly') ||
455
+ changed.has('disablePast') ||
456
+ changed.has('disableFuture') ||
457
+ changed.has('typedBadInput')) {
238
458
  this.toggleAttribute('data-invalid', this.touched && !this.internals.validity.valid);
239
459
  }
240
460
  }
@@ -304,31 +524,19 @@ export class LyraDateInput extends FormAssociated(LyraElement) {
304
524
  return null;
305
525
  return from <= to ? `${formatISO(from)}/${formatISO(to)}` : `${formatISO(to)}/${formatISO(from)}`;
306
526
  }
307
- isWithinBounds(isoValue) {
308
- const min = parseISO(this.min);
309
- const max = parseISO(this.max);
310
- const today = new Date();
311
- today.setHours(0, 0, 0, 0);
312
- return isoValue.split('/').every((part) => {
313
- const d = parseISO(part);
314
- if (!d)
315
- return false;
316
- if (min && d < min)
317
- return false;
318
- if (max && d > max)
319
- return false;
320
- if (this.disablePast && d < today)
321
- return false;
322
- if (this.disableFuture && d > today)
323
- return false;
324
- return true;
325
- });
527
+ formStateRestoreCallback(state, _mode) {
528
+ this.value = typeof state === 'string' ? state : '';
529
+ }
530
+ formResetCallback() {
531
+ super.formResetCallback();
532
+ this.touched = false;
326
533
  }
327
534
  render() {
328
535
  const hasValue = this.value.length > 0;
329
536
  const hasHint = this.hasHintSlot || this.hint.length > 0;
330
537
  const hasError = this.hasErrorSlot || this.errorText.length > 0;
331
538
  const hasLabel = this.hasLabelSlot || this.label.length > 0;
539
+ const invalid = this.touched && !this.internals.validity.valid;
332
540
  const describedBy = [hasError ? 'date-input-error' : '', hasHint ? 'date-input-hint' : '']
333
541
  .filter(Boolean)
334
542
  .join(' ');
@@ -342,10 +550,13 @@ export class LyraDateInput extends FormAssociated(LyraElement) {
342
550
  id=${this.inputId}
343
551
  part="input"
344
552
  type="text"
345
- aria-label=${hasLabel ? nothing : this.placeholder || 'Date'}
553
+ aria-label=${this.accessibleLabel || (hasLabel ? nothing : this.placeholder || 'Date')}
346
554
  aria-describedby=${describedBy || nothing}
555
+ aria-required=${this.required ? 'true' : 'false'}
556
+ aria-invalid=${invalid ? 'true' : 'false'}
347
557
  .value=${this.displayText}
348
558
  placeholder=${this.placeholder}
559
+ ?required=${this.required}
349
560
  ?disabled=${this.effectiveDisabled}
350
561
  ?readonly=${this.readonly}
351
562
  @change=${this.onInputChange}
@@ -357,7 +568,7 @@ export class LyraDateInput extends FormAssociated(LyraElement) {
357
568
  part="clear-button"
358
569
  type="button"
359
570
  ?disabled=${this.effectiveDisabled || this.readonly}
360
- aria-label="Clear"
571
+ aria-label=${this.clearLabel}
361
572
  @click=${() => this.clear()}
362
573
  >
363
574
  ${closeIcon()}
@@ -366,7 +577,7 @@ export class LyraDateInput extends FormAssociated(LyraElement) {
366
577
  <button
367
578
  part="expand-button"
368
579
  type="button"
369
- aria-label="Open calendar"
580
+ aria-label=${this.openLabel}
370
581
  aria-haspopup="dialog"
371
582
  aria-expanded=${this.open ? 'true' : 'false'}
372
583
  aria-controls=${this.popupId}
@@ -376,14 +587,14 @@ export class LyraDateInput extends FormAssociated(LyraElement) {
376
587
  <span part="expand-icon" aria-hidden="true">${calendarIcon()}</span>
377
588
  </button>
378
589
  </div>
379
- <div id=${this.popupId} part="popup" role="dialog" aria-label="Choose date">
590
+ <div id=${this.popupId} part="popup" role="dialog" aria-label=${this.dialogLabel}>
380
591
  <lyra-date-picker
381
592
  part="date-picker"
382
593
  .value=${this.value}
383
594
  .mode=${this.mode}
384
595
  .min=${this.min}
385
596
  .max=${this.max}
386
- .months=${this.months}
597
+ .months=${normalizeCalendarMonths(this.months)}
387
598
  .locale=${this.locale}
388
599
  .disabled=${this.effectiveDisabled}
389
600
  .readonly=${this.readonly}
@@ -391,7 +602,7 @@ export class LyraDateInput extends FormAssociated(LyraElement) {
391
602
  .disableFuture=${this.disableFuture}
392
603
  .withOutsideDays=${this.withOutsideDays}
393
604
  first-day-of-week=${this.firstDayOfWeek}
394
- weekday-format=${this.weekdayFormat}
605
+ .weekdayFormat=${normalizeWeekdayFormat(this.weekdayFormat)}
395
606
  @input=${this.onPickerInput}
396
607
  @change=${this.onPickerChange}
397
608
  ></lyra-date-picker>
@@ -406,18 +617,6 @@ export class LyraDateInput extends FormAssociated(LyraElement) {
406
617
  `;
407
618
  }
408
619
  }
409
- __decorate([
410
- property()
411
- ], LyraDateInput.prototype, "mode", void 0);
412
- __decorate([
413
- property()
414
- ], LyraDateInput.prototype, "min", void 0);
415
- __decorate([
416
- property()
417
- ], LyraDateInput.prototype, "max", void 0);
418
- __decorate([
419
- property({ type: Boolean, reflect: true })
420
- ], LyraDateInput.prototype, "readonly", void 0);
421
620
  __decorate([
422
621
  property({ type: Boolean, reflect: true })
423
622
  ], LyraDateInput.prototype, "open", void 0);
@@ -436,27 +635,33 @@ __decorate([
436
635
  __decorate([
437
636
  property()
438
637
  ], LyraDateInput.prototype, "placeholder", void 0);
638
+ __decorate([
639
+ property({ attribute: 'aria-label' })
640
+ ], LyraDateInput.prototype, "accessibleLabel", void 0);
439
641
  __decorate([
440
642
  property()
441
643
  ], LyraDateInput.prototype, "locale", void 0);
442
644
  __decorate([
443
- property({ type: Number })
645
+ property({ converter: monthsConverter })
444
646
  ], LyraDateInput.prototype, "months", void 0);
445
647
  __decorate([
446
648
  property({ attribute: 'first-day-of-week' })
447
649
  ], LyraDateInput.prototype, "firstDayOfWeek", void 0);
448
650
  __decorate([
449
- property({ attribute: 'weekday-format' })
651
+ property({ attribute: 'weekday-format', converter: weekdayFormatConverter })
450
652
  ], LyraDateInput.prototype, "weekdayFormat", void 0);
451
- __decorate([
452
- property({ type: Boolean, attribute: 'disable-past' })
453
- ], LyraDateInput.prototype, "disablePast", void 0);
454
- __decorate([
455
- property({ type: Boolean, attribute: 'disable-future' })
456
- ], LyraDateInput.prototype, "disableFuture", void 0);
457
653
  __decorate([
458
654
  property({ type: Boolean, attribute: 'with-outside-days' })
459
655
  ], LyraDateInput.prototype, "withOutsideDays", void 0);
656
+ __decorate([
657
+ property({ attribute: 'clear-label' })
658
+ ], LyraDateInput.prototype, "clearLabel", void 0);
659
+ __decorate([
660
+ property({ attribute: 'open-label' })
661
+ ], LyraDateInput.prototype, "openLabel", void 0);
662
+ __decorate([
663
+ property({ attribute: 'dialog-label' })
664
+ ], LyraDateInput.prototype, "dialogLabel", void 0);
460
665
  __decorate([
461
666
  state()
462
667
  ], LyraDateInput.prototype, "touched", void 0);
@@ -1,6 +1,6 @@
1
1
  import { type TemplateResult, type PropertyValues } from 'lit';
2
2
  import { LyraElement } from '../../internal/lyra-element.js';
3
- import { type WeekdayFormat } from './calendar-core.js';
3
+ import { type CalendarMode, type WeekdayFormat } from './calendar-core.js';
4
4
  export interface DateRange {
5
5
  from: Date | null;
6
6
  to: Date | null;
@@ -14,14 +14,30 @@ export interface DateRange {
14
14
  * @customElement lyra-date-picker
15
15
  * @event change - The user committed a value.
16
16
  * @event input - The value changed during interaction (range: after the first click).
17
- * @csspart base, month, header, title, previous, next, weekdays, weekday, grid, week
18
- * @csspart day, day-today, day-outside, day-selected, day-range-start, day-range-end, day-range-inner, day-placeholder
17
+ * @csspart base - The date-picker wrapper.
18
+ * @csspart month - A visible month wrapper.
19
+ * @csspart header - The month header.
20
+ * @csspart title - The month title.
21
+ * @csspart previous - The previous-month button.
22
+ * @csspart next - The next-month button.
23
+ * @csspart weekdays - The weekday header row.
24
+ * @csspart weekday - A weekday label.
25
+ * @csspart grid - A month date grid.
26
+ * @csspart week - A calendar week row.
27
+ * @csspart day - A calendar day button.
28
+ * @csspart day-today - A day representing today.
29
+ * @csspart day-outside - A day outside the active month.
30
+ * @csspart day-selected - A selected day.
31
+ * @csspart day-range-start - The start of a selected range.
32
+ * @csspart day-range-end - The end of a selected range.
33
+ * @csspart day-range-inner - An interior day in a selected range.
34
+ * @csspart day-placeholder - A non-day grid placeholder.
19
35
  */
20
36
  export declare class LyraDatePicker extends LyraElement {
21
37
  static styles: import("lit").CSSResultGroup[];
22
38
  /** ISO value: `YYYY-MM-DD` or `YYYY-MM-DD/YYYY-MM-DD`. */
23
39
  value: string;
24
- mode: 'single' | 'range';
40
+ mode: CalendarMode;
25
41
  min: string;
26
42
  max: string;
27
43
  disabled: boolean;
@@ -33,10 +49,18 @@ export declare class LyraDatePicker extends LyraElement {
33
49
  disablePast: boolean;
34
50
  disableFuture: boolean;
35
51
  withOutsideDays: boolean;
52
+ /** Accessible label for the previous-month button. Override for a non-English `locale`. */
53
+ previousLabel: string;
54
+ /** Accessible label for the next-month button. Override for a non-English `locale`. */
55
+ nextLabel: string;
36
56
  private viewDate;
37
57
  private focusedDate;
38
58
  private focusPending;
59
+ private readonly titleIds;
39
60
  private suppressViewSync;
61
+ private get effectiveMode();
62
+ private get visibleMonths();
63
+ private get effectiveWeekdayFormat();
40
64
  get selection(): DateRange;
41
65
  /** Read-only Date view of a single-mode value. */
42
66
  get valueAsDate(): Date | null;