@aceshooting/lyra-ui 1.1.0 → 2.0.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 (335) hide show
  1. package/README.md +12 -1
  2. package/custom-elements.json +21460 -8489
  3. package/dist/components/app-rail/app-rail.d.ts +171 -0
  4. package/dist/components/app-rail/app-rail.js +483 -0
  5. package/dist/components/app-rail/app-rail.styles.d.ts +1 -0
  6. package/dist/components/app-rail/app-rail.styles.js +125 -0
  7. package/dist/components/attachment-chip/attachment-chip.d.ts +114 -0
  8. package/dist/components/attachment-chip/attachment-chip.js +312 -0
  9. package/dist/components/attachment-chip/attachment-chip.styles.d.ts +1 -0
  10. package/dist/components/attachment-chip/attachment-chip.styles.js +184 -0
  11. package/dist/components/attachment-trigger/attachment-trigger.d.ts +83 -0
  12. package/dist/components/attachment-trigger/attachment-trigger.js +258 -0
  13. package/dist/components/attachment-trigger/attachment-trigger.styles.d.ts +1 -0
  14. package/dist/components/attachment-trigger/attachment-trigger.styles.js +68 -0
  15. package/dist/components/chart/box-plot.js +11 -1
  16. package/dist/components/chart/chart-loader.d.ts +44 -13
  17. package/dist/components/chart/chart-loader.js +65 -18
  18. package/dist/components/chart/chart.d.ts +12 -2
  19. package/dist/components/chart/chart.js +121 -14
  20. package/dist/components/chart/histogram-bin.d.ts +4 -1
  21. package/dist/components/chart/histogram-bin.js +24 -14
  22. package/dist/components/chart/lite-chart.d.ts +63 -0
  23. package/dist/components/chart/lite-chart.js +181 -23
  24. package/dist/components/chart/lite-chart.styles.js +9 -0
  25. package/dist/components/chat-composer/chat-composer.d.ts +92 -0
  26. package/dist/components/chat-composer/chat-composer.js +285 -0
  27. package/dist/components/chat-composer/chat-composer.styles.d.ts +1 -0
  28. package/dist/components/chat-composer/chat-composer.styles.js +135 -0
  29. package/dist/components/chat-message/chat-message.d.ts +113 -0
  30. package/dist/components/chat-message/chat-message.js +291 -0
  31. package/dist/components/chat-message/chat-message.styles.d.ts +1 -0
  32. package/dist/components/chat-message/chat-message.styles.js +203 -0
  33. package/dist/components/checkbox/checkbox.d.ts +59 -0
  34. package/dist/components/checkbox/checkbox.js +230 -0
  35. package/dist/components/checkbox/checkbox.styles.d.ts +1 -0
  36. package/dist/components/checkbox/checkbox.styles.js +70 -0
  37. package/dist/components/chip/chip-group.d.ts +56 -0
  38. package/dist/components/chip/chip-group.js +124 -0
  39. package/dist/components/chip/chip-group.styles.d.ts +1 -0
  40. package/dist/components/chip/chip-group.styles.js +57 -0
  41. package/dist/components/chip/chip.d.ts +64 -0
  42. package/dist/components/chip/chip.js +115 -0
  43. package/dist/components/chip/chip.styles.d.ts +1 -0
  44. package/dist/components/chip/chip.styles.js +115 -0
  45. package/dist/components/citation-badge/citation-badge.d.ts +113 -0
  46. package/dist/components/citation-badge/citation-badge.js +300 -0
  47. package/dist/components/citation-badge/citation-badge.styles.d.ts +1 -0
  48. package/dist/components/citation-badge/citation-badge.styles.js +96 -0
  49. package/dist/components/code-block/code-block.d.ts +89 -0
  50. package/dist/components/code-block/code-block.js +317 -0
  51. package/dist/components/code-block/code-block.styles.d.ts +1 -0
  52. package/dist/components/code-block/code-block.styles.js +187 -0
  53. package/dist/components/code-block/code-loader.d.ts +44 -0
  54. package/dist/components/code-block/code-loader.js +79 -0
  55. package/dist/components/combobox/combobox.d.ts +19 -1
  56. package/dist/components/combobox/combobox.js +84 -14
  57. package/dist/components/combobox/option.d.ts +5 -1
  58. package/dist/components/combobox/option.js +35 -0
  59. package/dist/components/context-meter/context-meter.d.ts +53 -0
  60. package/dist/components/context-meter/context-meter.js +150 -0
  61. package/dist/components/context-meter/context-meter.styles.d.ts +1 -0
  62. package/dist/components/context-meter/context-meter.styles.js +120 -0
  63. package/dist/components/conversation-item/conversation-item.d.ts +128 -0
  64. package/dist/components/conversation-item/conversation-item.js +322 -0
  65. package/dist/components/conversation-item/conversation-item.styles.d.ts +1 -0
  66. package/dist/components/conversation-item/conversation-item.styles.js +155 -0
  67. package/dist/components/date-picker/date-input.d.ts +24 -0
  68. package/dist/components/date-picker/date-input.js +134 -22
  69. package/dist/components/date-picker/date-picker.d.ts +7 -0
  70. package/dist/components/date-picker/date-picker.js +85 -16
  71. package/dist/components/dialog/confirm.d.ts +40 -0
  72. package/dist/components/dialog/confirm.js +79 -0
  73. package/dist/components/dialog/dialog.d.ts +85 -0
  74. package/dist/components/dialog/dialog.js +272 -0
  75. package/dist/components/dialog/dialog.styles.d.ts +1 -0
  76. package/dist/components/dialog/dialog.styles.js +52 -0
  77. package/dist/components/dock-panel/dock-panel.d.ts +134 -0
  78. package/dist/components/dock-panel/dock-panel.js +345 -0
  79. package/dist/components/dock-panel/dock-panel.styles.d.ts +1 -0
  80. package/dist/components/dock-panel/dock-panel.styles.js +145 -0
  81. package/dist/components/document-preview/document-preview.d.ts +135 -0
  82. package/dist/components/document-preview/document-preview.js +372 -0
  83. package/dist/components/document-preview/document-preview.styles.d.ts +1 -0
  84. package/dist/components/document-preview/document-preview.styles.js +191 -0
  85. package/dist/components/export-button/csv.js +1 -1
  86. package/dist/components/export-button/export-button.js +2 -0
  87. package/dist/components/flag/flag.d.ts +42 -5
  88. package/dist/components/flag/flag.js +70 -3
  89. package/dist/components/gauge/gauge.js +19 -10
  90. package/dist/components/generation-status/generation-status.d.ts +109 -0
  91. package/dist/components/generation-status/generation-status.js +273 -0
  92. package/dist/components/generation-status/generation-status.styles.d.ts +1 -0
  93. package/dist/components/generation-status/generation-status.styles.js +84 -0
  94. package/dist/components/heatmap/calendar-grid.d.ts +4 -1
  95. package/dist/components/heatmap/calendar-grid.js +25 -12
  96. package/dist/components/heatmap/heatmap-scale.d.ts +6 -0
  97. package/dist/components/heatmap/heatmap-scale.js +7 -1
  98. package/dist/components/heatmap/heatmap.d.ts +89 -3
  99. package/dist/components/heatmap/heatmap.js +158 -29
  100. package/dist/components/json-viewer/json-viewer.d.ts +53 -0
  101. package/dist/components/json-viewer/json-viewer.js +283 -0
  102. package/dist/components/json-viewer/json-viewer.styles.d.ts +1 -0
  103. package/dist/components/json-viewer/json-viewer.styles.js +176 -0
  104. package/dist/components/kbd/kbd.d.ts +77 -0
  105. package/dist/components/kbd/kbd.js +214 -0
  106. package/dist/components/kbd/kbd.styles.d.ts +1 -0
  107. package/dist/components/kbd/kbd.styles.js +52 -0
  108. package/dist/components/live-region/live-region.d.ts +81 -0
  109. package/dist/components/live-region/live-region.js +153 -0
  110. package/dist/components/live-region/live-region.styles.d.ts +1 -0
  111. package/dist/components/live-region/live-region.styles.js +10 -0
  112. package/dist/components/map/map.d.ts +8 -6
  113. package/dist/components/map/map.js +44 -8
  114. package/dist/components/markdown/markdown-loader.d.ts +34 -0
  115. package/dist/components/markdown/markdown-loader.js +43 -0
  116. package/dist/components/markdown/markdown.d.ts +81 -0
  117. package/dist/components/markdown/markdown.js +284 -0
  118. package/dist/components/markdown/markdown.styles.d.ts +1 -0
  119. package/dist/components/markdown/markdown.styles.js +82 -0
  120. package/dist/components/media-card/media-card.d.ts +122 -0
  121. package/dist/components/media-card/media-card.js +295 -0
  122. package/dist/components/media-card/media-card.styles.d.ts +1 -0
  123. package/dist/components/media-card/media-card.styles.js +132 -0
  124. package/dist/components/mention-popover/mention-popover.d.ts +200 -0
  125. package/dist/components/mention-popover/mention-popover.js +475 -0
  126. package/dist/components/mention-popover/mention-popover.styles.d.ts +1 -0
  127. package/dist/components/mention-popover/mention-popover.styles.js +101 -0
  128. package/dist/components/menu/menu-item.d.ts +62 -0
  129. package/dist/components/menu/menu-item.js +119 -0
  130. package/dist/components/menu/menu-item.styles.d.ts +1 -0
  131. package/dist/components/menu/menu-item.styles.js +67 -0
  132. package/dist/components/menu/menu.d.ts +143 -0
  133. package/dist/components/menu/menu.js +365 -0
  134. package/dist/components/menu/menu.styles.d.ts +1 -0
  135. package/dist/components/menu/menu.styles.js +66 -0
  136. package/dist/components/model-select/model-select.d.ts +125 -0
  137. package/dist/components/model-select/model-select.js +490 -0
  138. package/dist/components/model-select/model-select.styles.d.ts +1 -0
  139. package/dist/components/model-select/model-select.styles.js +189 -0
  140. package/dist/components/model-settings-panel/model-settings-panel.d.ts +69 -0
  141. package/dist/components/model-settings-panel/model-settings-panel.js +145 -0
  142. package/dist/components/model-settings-panel/model-settings-panel.styles.d.ts +1 -0
  143. package/dist/components/model-settings-panel/model-settings-panel.styles.js +82 -0
  144. package/dist/components/playback/playback.d.ts +13 -1
  145. package/dist/components/playback/playback.js +47 -14
  146. package/dist/components/responsive-panel/responsive-panel.d.ts +150 -0
  147. package/dist/components/responsive-panel/responsive-panel.js +365 -0
  148. package/dist/components/responsive-panel/responsive-panel.styles.d.ts +1 -0
  149. package/dist/components/responsive-panel/responsive-panel.styles.js +103 -0
  150. package/dist/components/result-card/result-card.d.ts +44 -0
  151. package/dist/components/result-card/result-card.js +83 -0
  152. package/dist/components/result-card/result-card.styles.d.ts +1 -0
  153. package/dist/components/result-card/result-card.styles.js +50 -0
  154. package/dist/components/result-card/result-field.d.ts +36 -0
  155. package/dist/components/result-card/result-field.js +87 -0
  156. package/dist/components/result-card/result-field.styles.d.ts +1 -0
  157. package/dist/components/result-card/result-field.styles.js +32 -0
  158. package/dist/components/select/select.d.ts +45 -1
  159. package/dist/components/select/select.js +114 -17
  160. package/dist/components/select/select.styles.js +26 -2
  161. package/dist/components/slider/slider.d.ts +89 -0
  162. package/dist/components/slider/slider.js +352 -0
  163. package/dist/components/slider/slider.styles.d.ts +1 -0
  164. package/dist/components/slider/slider.styles.js +87 -0
  165. package/dist/components/source-card/source-card.d.ts +92 -0
  166. package/dist/components/source-card/source-card.js +148 -0
  167. package/dist/components/source-card/source-card.styles.d.ts +1 -0
  168. package/dist/components/source-card/source-card.styles.js +69 -0
  169. package/dist/components/source-list/source-list.d.ts +63 -0
  170. package/dist/components/source-list/source-list.js +122 -0
  171. package/dist/components/source-list/source-list.styles.d.ts +1 -0
  172. package/dist/components/source-list/source-list.styles.js +59 -0
  173. package/dist/components/sparkline/sparkline.js +27 -13
  174. package/dist/components/split/split.d.ts +93 -1
  175. package/dist/components/split/split.js +284 -15
  176. package/dist/components/split/split.styles.js +30 -0
  177. package/dist/components/stat/stat.d.ts +22 -1
  178. package/dist/components/stat/stat.js +49 -3
  179. package/dist/components/stat/stat.styles.js +20 -0
  180. package/dist/components/stream-status/stream-status.d.ts +110 -0
  181. package/dist/components/stream-status/stream-status.js +210 -0
  182. package/dist/components/stream-status/stream-status.styles.d.ts +1 -0
  183. package/dist/components/stream-status/stream-status.styles.js +103 -0
  184. package/dist/components/streaming-text/streaming-text.d.ts +98 -0
  185. package/dist/components/streaming-text/streaming-text.js +209 -0
  186. package/dist/components/streaming-text/streaming-text.styles.d.ts +1 -0
  187. package/dist/components/streaming-text/streaming-text.styles.js +62 -0
  188. package/dist/components/switch/switch.d.ts +58 -0
  189. package/dist/components/switch/switch.js +179 -0
  190. package/dist/components/switch/switch.styles.d.ts +1 -0
  191. package/dist/components/switch/switch.styles.js +75 -0
  192. package/dist/components/table/table.d.ts +73 -4
  193. package/dist/components/table/table.js +169 -20
  194. package/dist/components/table/table.styles.js +6 -1
  195. package/dist/components/tabs/tabs.d.ts +51 -0
  196. package/dist/components/tabs/tabs.js +190 -0
  197. package/dist/components/tabs/tabs.styles.d.ts +1 -0
  198. package/dist/components/tabs/tabs.styles.js +63 -0
  199. package/dist/components/thinking-panel/thinking-panel.d.ts +121 -0
  200. package/dist/components/thinking-panel/thinking-panel.js +235 -0
  201. package/dist/components/thinking-panel/thinking-panel.styles.d.ts +1 -0
  202. package/dist/components/thinking-panel/thinking-panel.styles.js +117 -0
  203. package/dist/components/toast/toast-item.d.ts +1 -0
  204. package/dist/components/toast/toast-item.js +31 -6
  205. package/dist/components/tool-approval-dialog/tool-approval-dialog.d.ts +130 -0
  206. package/dist/components/tool-approval-dialog/tool-approval-dialog.js +403 -0
  207. package/dist/components/tool-approval-dialog/tool-approval-dialog.styles.d.ts +1 -0
  208. package/dist/components/tool-approval-dialog/tool-approval-dialog.styles.js +160 -0
  209. package/dist/components/tool-call-chip/tool-call-chip.d.ts +89 -0
  210. package/dist/components/tool-call-chip/tool-call-chip.js +288 -0
  211. package/dist/components/tool-call-chip/tool-call-chip.styles.d.ts +1 -0
  212. package/dist/components/tool-call-chip/tool-call-chip.styles.js +203 -0
  213. package/dist/components/tool-param-form/tool-param-form.d.ts +145 -0
  214. package/dist/components/tool-param-form/tool-param-form.js +366 -0
  215. package/dist/components/tool-param-form/tool-param-form.styles.d.ts +1 -0
  216. package/dist/components/tool-param-form/tool-param-form.styles.js +75 -0
  217. package/dist/components/tool-result-dialog/tool-result-dialog.d.ts +102 -0
  218. package/dist/components/tool-result-dialog/tool-result-dialog.js +393 -0
  219. package/dist/components/tool-result-dialog/tool-result-dialog.styles.d.ts +1 -0
  220. package/dist/components/tool-result-dialog/tool-result-dialog.styles.js +176 -0
  221. package/dist/components/tool-result-view/registry.d.ts +81 -0
  222. package/dist/components/tool-result-view/registry.js +84 -0
  223. package/dist/components/tool-result-view/tool-result-view.d.ts +55 -0
  224. package/dist/components/tool-result-view/tool-result-view.js +138 -0
  225. package/dist/components/tool-result-view/tool-result-view.styles.d.ts +1 -0
  226. package/dist/components/tool-result-view/tool-result-view.styles.js +13 -0
  227. package/dist/components/tool-select-dialog/tool-select-dialog.d.ts +151 -0
  228. package/dist/components/tool-select-dialog/tool-select-dialog.js +439 -0
  229. package/dist/components/tool-select-dialog/tool-select-dialog.styles.d.ts +1 -0
  230. package/dist/components/tool-select-dialog/tool-select-dialog.styles.js +182 -0
  231. package/dist/components/tree/tree-node.d.ts +11 -2
  232. package/dist/components/tree/tree-node.js +16 -6
  233. package/dist/components/tree/tree.d.ts +8 -2
  234. package/dist/components/tree/tree.js +18 -11
  235. package/dist/components/typing-indicator/typing-indicator.d.ts +60 -0
  236. package/dist/components/typing-indicator/typing-indicator.js +96 -0
  237. package/dist/components/typing-indicator/typing-indicator.styles.d.ts +1 -0
  238. package/dist/components/typing-indicator/typing-indicator.styles.js +132 -0
  239. package/dist/components/virtual-list/virtual-list.d.ts +173 -0
  240. package/dist/components/virtual-list/virtual-list.js +461 -0
  241. package/dist/components/virtual-list/virtual-list.styles.d.ts +1 -0
  242. package/dist/components/virtual-list/virtual-list.styles.js +44 -0
  243. package/dist/components/widget/widget.d.ts +10 -0
  244. package/dist/components/widget/widget.js +24 -1
  245. package/dist/components/widget/widget.styles.js +9 -2
  246. package/dist/components/word-cloud/word-cloud-layout.js +14 -1
  247. package/dist/components/word-cloud/word-cloud.d.ts +6 -0
  248. package/dist/components/word-cloud/word-cloud.js +25 -14
  249. package/dist/components/word-cloud/word-cloud.styles.js +4 -2
  250. package/dist/internal/announcer.d.ts +57 -0
  251. package/dist/internal/announcer.js +71 -0
  252. package/dist/internal/form-associated.d.ts +1 -0
  253. package/dist/internal/form-associated.js +55 -11
  254. package/dist/internal/group-by-recency.d.ts +50 -0
  255. package/dist/internal/group-by-recency.js +80 -0
  256. package/dist/internal/rtl.d.ts +23 -0
  257. package/dist/internal/rtl.js +28 -0
  258. package/dist/internal/scroll-lock.d.ts +10 -5
  259. package/dist/internal/scroll-lock.js +30 -14
  260. package/dist/internal/tokens.styles.js +6 -0
  261. package/dist/lyra.d.ts +130 -31
  262. package/dist/lyra.js +94 -26
  263. package/llms-full.txt +3921 -32
  264. package/llms.txt +16 -1
  265. package/package.json +23 -5
  266. package/dist/components/chart/bar-chart.stories.d.ts +0 -5
  267. package/dist/components/chart/bar-chart.stories.js +0 -20
  268. package/dist/components/chart/box-plot.stories.d.ts +0 -5
  269. package/dist/components/chart/box-plot.stories.js +0 -29
  270. package/dist/components/chart/bubble-chart.stories.d.ts +0 -5
  271. package/dist/components/chart/bubble-chart.stories.js +0 -26
  272. package/dist/components/chart/chart.stories.d.ts +0 -32
  273. package/dist/components/chart/chart.stories.js +0 -141
  274. package/dist/components/chart/doughnut-chart.stories.d.ts +0 -5
  275. package/dist/components/chart/doughnut-chart.stories.js +0 -26
  276. package/dist/components/chart/histogram.stories.d.ts +0 -5
  277. package/dist/components/chart/histogram.stories.js +0 -17
  278. package/dist/components/chart/line-chart.stories.d.ts +0 -22
  279. package/dist/components/chart/line-chart.stories.js +0 -99
  280. package/dist/components/chart/lite-chart.stories.d.ts +0 -12
  281. package/dist/components/chart/lite-chart.stories.js +0 -98
  282. package/dist/components/chart/pie-chart.stories.d.ts +0 -5
  283. package/dist/components/chart/pie-chart.stories.js +0 -26
  284. package/dist/components/chart/polar-area-chart.stories.d.ts +0 -5
  285. package/dist/components/chart/polar-area-chart.stories.js +0 -20
  286. package/dist/components/chart/radar-chart.stories.d.ts +0 -5
  287. package/dist/components/chart/radar-chart.stories.js +0 -20
  288. package/dist/components/chart/scatter-chart.stories.d.ts +0 -5
  289. package/dist/components/chart/scatter-chart.stories.js +0 -31
  290. package/dist/components/combobox/combobox.stories.d.ts +0 -24
  291. package/dist/components/combobox/combobox.stories.js +0 -102
  292. package/dist/components/date-picker/date-input.stories.d.ts +0 -10
  293. package/dist/components/date-picker/date-input.stories.js +0 -48
  294. package/dist/components/date-picker/date-picker.stories.d.ts +0 -10
  295. package/dist/components/date-picker/date-picker.stories.js +0 -25
  296. package/dist/components/empty/empty.stories.d.ts +0 -7
  297. package/dist/components/empty/empty.stories.js +0 -34
  298. package/dist/components/export-button/export-button.stories.d.ts +0 -7
  299. package/dist/components/export-button/export-button.stories.js +0 -37
  300. package/dist/components/file-input/file-input.stories.d.ts +0 -7
  301. package/dist/components/file-input/file-input.stories.js +0 -19
  302. package/dist/components/flag/flag.stories.d.ts +0 -5
  303. package/dist/components/flag/flag.stories.js +0 -17
  304. package/dist/components/gauge/gauge.stories.d.ts +0 -8
  305. package/dist/components/gauge/gauge.stories.js +0 -19
  306. package/dist/components/graph/graph.stories.d.ts +0 -8
  307. package/dist/components/graph/graph.stories.js +0 -68
  308. package/dist/components/heatmap/heatmap.stories.d.ts +0 -22
  309. package/dist/components/heatmap/heatmap.stories.js +0 -154
  310. package/dist/components/map/map.stories.d.ts +0 -15
  311. package/dist/components/map/map.stories.js +0 -135
  312. package/dist/components/playback/playback.stories.d.ts +0 -7
  313. package/dist/components/playback/playback.stories.js +0 -16
  314. package/dist/components/select/select.stories.d.ts +0 -19
  315. package/dist/components/select/select.stories.js +0 -75
  316. package/dist/components/skeleton/skeleton.stories.d.ts +0 -5
  317. package/dist/components/skeleton/skeleton.stories.js +0 -16
  318. package/dist/components/sparkline/sparkline.stories.d.ts +0 -11
  319. package/dist/components/sparkline/sparkline.stories.js +0 -39
  320. package/dist/components/split/split.stories.d.ts +0 -7
  321. package/dist/components/split/split.stories.js +0 -36
  322. package/dist/components/stat/stat.stories.d.ts +0 -8
  323. package/dist/components/stat/stat.stories.js +0 -64
  324. package/dist/components/table/table.stories.d.ts +0 -12
  325. package/dist/components/table/table.stories.js +0 -67
  326. package/dist/components/time-range/time-range.stories.d.ts +0 -8
  327. package/dist/components/time-range/time-range.stories.js +0 -26
  328. package/dist/components/toast/toast.stories.d.ts +0 -8
  329. package/dist/components/toast/toast.stories.js +0 -72
  330. package/dist/components/tree/tree.stories.d.ts +0 -9
  331. package/dist/components/tree/tree.stories.js +0 -52
  332. package/dist/components/widget/widget.stories.d.ts +0 -7
  333. package/dist/components/widget/widget.stories.js +0 -41
  334. package/dist/components/word-cloud/word-cloud.stories.d.ts +0 -10
  335. package/dist/components/word-cloud/word-cloud.stories.js +0 -55
@@ -4,7 +4,7 @@ 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 } 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';
@@ -16,6 +16,26 @@ import { parseISO, formatISO } from './calendar-core.js';
16
16
  import { styles } from './date-input.styles.js';
17
17
  import { LyraDatePicker } from './date-picker.js';
18
18
  import './date-picker.js';
19
+ /** Determines the locale's day/month/year field order from a real formatted
20
+ * sample (Jan 2, 2026 -- a date where day/month/year are all numerically
21
+ * distinguishable), instead of relying on Date.parse()'s implementation-defined
22
+ * (commonly mm/dd/yyyy-biased) heuristics for an ambiguous separated date. */
23
+ function localeDateOrder(locale) {
24
+ try {
25
+ const parts = new Intl.DateTimeFormat(locale || undefined, {
26
+ year: 'numeric',
27
+ month: '2-digit',
28
+ day: '2-digit',
29
+ }).formatToParts(new Date(2026, 0, 2));
30
+ const order = parts
31
+ .filter((p) => p.type === 'day' || p.type === 'month' || p.type === 'year')
32
+ .map((p) => p.type);
33
+ return order.length === 3 ? order : ['month', 'day', 'year'];
34
+ }
35
+ catch {
36
+ return ['month', 'day', 'year']; // Date.parse()'s own bias, as a last-resort fallback
37
+ }
38
+ }
19
39
  /**
20
40
  * `<lyra-date-input>` — a date field with an attached calendar popover.
21
41
  * Mirrors the core `<wa-date-input>` API under `lyra-`. Value is ISO 8601
@@ -84,24 +104,18 @@ export class LyraDateInput extends FormAssociated(LyraElement) {
84
104
  this.emit('change');
85
105
  return;
86
106
  }
87
- // A string that already looks like strict ISO (YYYY-MM-DD) must be judged
88
- // by parseISO alone: it validates calendar correctness (rejects e.g.
89
- // "2026-02-30"), but Date.parse() doesn't -- falling through to it for an
90
- // ISO-shaped string would silently resurrect the same rollover parseISO
91
- // guards against (Date.parse('2026-02-30') also normalizes to March 2).
92
- const looksLikeISO = /^\d{4}-\d{2}-\d{2}$/.test(raw);
93
- const parsed = looksLikeISO ? parseISO(raw) : isNaN(Date.parse(raw)) ? null : new Date(Date.parse(raw));
94
- if (parsed) {
95
- this.value = formatISO(parsed);
107
+ const parsed = this.mode === 'range' ? this.parseRangeText(raw) : this.parseSingleText(raw);
108
+ if (parsed && this.isWithinBounds(parsed)) {
109
+ this.value = parsed;
96
110
  this.emit('input');
97
111
  this.emit('change');
98
112
  }
99
113
  else {
100
- // Unparseable text: don't silently keep the committed value while the
101
- // field still shows garbage -- revert the displayed text back to the
102
- // last valid commit and flag the constraint-validation state (mirrors
103
- // how `required` is already wired via internals.setValidity(), see
104
- // form-associated.ts).
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).
105
119
  target.value = this.displayText;
106
120
  this.internals.setValidity({ badInput: true }, 'Enter a valid date');
107
121
  }
@@ -175,7 +189,7 @@ export class LyraDateInput extends FormAssociated(LyraElement) {
175
189
  }
176
190
  /** Open the calendar popover. */
177
191
  show() {
178
- if (this.open || this.disabled || this.readonly)
192
+ if (this.open || this.effectiveDisabled || this.readonly)
179
193
  return;
180
194
  this.open = true;
181
195
  this.emit('lyra-show');
@@ -199,7 +213,14 @@ export class LyraDateInput extends FormAssociated(LyraElement) {
199
213
  disconnectedCallback() {
200
214
  super.disconnectedCallback();
201
215
  this.cleanupFn?.();
216
+ this.cleanupFn = undefined;
202
217
  document.removeEventListener('pointerdown', this.onDocPointer);
218
+ // Reset so a reconnect (e.g. a drag-drop reparent) re-triggers
219
+ // `updated()`'s `open`-driven branch -- without this, `open` stays
220
+ // stuck `true` across the disconnect, `updated()` never sees it
221
+ // *change*, and `place()` never gets called again to re-bind
222
+ // positioning to the (possibly relocated) anchor.
223
+ this.open = false;
203
224
  }
204
225
  updated(changed) {
205
226
  super.updated(changed);
@@ -217,11 +238,100 @@ export class LyraDateInput extends FormAssociated(LyraElement) {
217
238
  this.toggleAttribute('data-invalid', this.touched && !this.internals.validity.valid);
218
239
  }
219
240
  }
241
+ /** Parses one date, ISO-first (so a calendar-invalid ISO string like
242
+ * "2026-02-30" is rejected rather than silently rolled over by Date.parse()).
243
+ * A 3-part, ambiguous slash/dot/dash-separated date (e.g. "15/07/2026") is
244
+ * parsed according to the locale's own day/month/year order (via a real
245
+ * Intl.DateTimeFormat sample, see localeDateOrder()) rather than
246
+ * Date.parse()'s implementation-defined heuristics -- this is what prevents
247
+ * e.g. an en-GB "15/07/2026" from being silently misread as some other day.
248
+ * A 4-digit first group (e.g. "2026-7-15") is never locale-guessed, though:
249
+ * it's unambiguously a year regardless of locale/separator (ISO's own
250
+ * year-first convention, just without zero-padding), so it's routed
251
+ * straight through parseISO() instead -- this is what lets a non-padded
252
+ * ISO-ish date keep parsing correctly (it did via Date.parse() before the
253
+ * ambiguous-date regex below existed at all).
254
+ * Anything else (e.g. "July 15, 2026") still falls through to Date.parse(). */
255
+ parseOneDate(raw) {
256
+ const looksLikeISO = /^\d{4}-\d{2}-\d{2}$/.test(raw);
257
+ if (looksLikeISO)
258
+ return parseISO(raw);
259
+ const ambiguous = /^(\d{1,4})[/.-](\d{1,4})[/.-](\d{1,4})$/.exec(raw);
260
+ if (ambiguous) {
261
+ const [, g1, g2, g3] = ambiguous;
262
+ if (g1.length === 4) {
263
+ return parseISO(`${g1}-${g2.padStart(2, '0')}-${g3.padStart(2, '0')}`);
264
+ }
265
+ const order = localeDateOrder(this.locale);
266
+ const values = [Number(g1), Number(g2), Number(g3)];
267
+ const fields = {};
268
+ order.forEach((type, i) => {
269
+ fields[type] = values[i];
270
+ });
271
+ if (fields.day != null && fields.month != null && fields.year != null) {
272
+ const year = fields.year < 100 ? 2000 + fields.year : fields.year;
273
+ return parseISO(`${String(year).padStart(4, '0')}-${String(fields.month).padStart(2, '0')}-${String(fields.day).padStart(2, '0')}`);
274
+ }
275
+ }
276
+ return isNaN(Date.parse(raw)) ? null : new Date(Date.parse(raw));
277
+ }
278
+ parseSingleText(raw) {
279
+ const parsed = this.parseOneDate(raw);
280
+ return parsed ? formatISO(parsed) : null;
281
+ }
282
+ /** Only round-trips the exact `displayText` shape this component itself
283
+ * renders (`"<locale from> – <locale to>"`) — a raw ISO range typed
284
+ * directly (`"2026-05-01/2026-05-15"`) is also accepted as a convenience.
285
+ * A reversed typed range (`to` before `from`) is normalized into
286
+ * from-before-to order, matching what the date-picker's own UI-driven
287
+ * `commit()` already does for a UI-picked range. */
288
+ parseRangeText(raw) {
289
+ if (/^\d{4}-\d{2}-\d{2}\/\d{4}-\d{2}-\d{2}$/.test(raw)) {
290
+ const [a, b] = raw.split('/');
291
+ const from = parseISO(a);
292
+ const to = parseISO(b);
293
+ if (!from || !to)
294
+ return null;
295
+ return from <= to ? raw : `${b}/${a}`;
296
+ }
297
+ const separator = ' – '; // matches displayText's ` – ` join exactly
298
+ const idx = raw.indexOf(separator);
299
+ if (idx === -1)
300
+ return null;
301
+ const from = this.parseOneDate(raw.slice(0, idx).trim());
302
+ const to = this.parseOneDate(raw.slice(idx + separator.length).trim());
303
+ if (!from || !to)
304
+ return null;
305
+ return from <= to ? `${formatISO(from)}/${formatISO(to)}` : `${formatISO(to)}/${formatISO(from)}`;
306
+ }
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
+ });
326
+ }
220
327
  render() {
221
328
  const hasValue = this.value.length > 0;
222
329
  const hasHint = this.hasHintSlot || this.hint.length > 0;
223
330
  const hasError = this.hasErrorSlot || this.errorText.length > 0;
224
331
  const hasLabel = this.hasLabelSlot || this.label.length > 0;
332
+ const describedBy = [hasError ? 'date-input-error' : '', hasHint ? 'date-input-hint' : '']
333
+ .filter(Boolean)
334
+ .join(' ');
225
335
  return html `
226
336
  <div part="form-control" @keydown=${this.onFormControlKey}>
227
337
  <label part="form-control-label" for=${this.inputId} ?hidden=${!hasLabel}>
@@ -232,10 +342,11 @@ export class LyraDateInput extends FormAssociated(LyraElement) {
232
342
  id=${this.inputId}
233
343
  part="input"
234
344
  type="text"
235
- aria-label=${this.label || this.placeholder || 'Date'}
345
+ aria-label=${hasLabel ? nothing : this.placeholder || 'Date'}
346
+ aria-describedby=${describedBy || nothing}
236
347
  .value=${this.displayText}
237
348
  placeholder=${this.placeholder}
238
- ?disabled=${this.disabled}
349
+ ?disabled=${this.effectiveDisabled}
239
350
  ?readonly=${this.readonly}
240
351
  @change=${this.onInputChange}
241
352
  @keydown=${this.onInputKey}
@@ -245,6 +356,7 @@ export class LyraDateInput extends FormAssociated(LyraElement) {
245
356
  ? html `<button
246
357
  part="clear-button"
247
358
  type="button"
359
+ ?disabled=${this.effectiveDisabled || this.readonly}
248
360
  aria-label="Clear"
249
361
  @click=${() => this.clear()}
250
362
  >
@@ -258,7 +370,7 @@ export class LyraDateInput extends FormAssociated(LyraElement) {
258
370
  aria-haspopup="dialog"
259
371
  aria-expanded=${this.open ? 'true' : 'false'}
260
372
  aria-controls=${this.popupId}
261
- ?disabled=${this.disabled || this.readonly}
373
+ ?disabled=${this.effectiveDisabled || this.readonly}
262
374
  @click=${() => (this.open ? this.hide() : this.show())}
263
375
  >
264
376
  <span part="expand-icon" aria-hidden="true">${calendarIcon()}</span>
@@ -273,7 +385,7 @@ export class LyraDateInput extends FormAssociated(LyraElement) {
273
385
  .max=${this.max}
274
386
  .months=${this.months}
275
387
  .locale=${this.locale}
276
- .disabled=${this.disabled}
388
+ .disabled=${this.effectiveDisabled}
277
389
  .readonly=${this.readonly}
278
390
  .disablePast=${this.disablePast}
279
391
  .disableFuture=${this.disableFuture}
@@ -284,10 +396,10 @@ export class LyraDateInput extends FormAssociated(LyraElement) {
284
396
  @change=${this.onPickerChange}
285
397
  ></lyra-date-picker>
286
398
  </div>
287
- <div part="error" ?hidden=${!hasError}>
399
+ <div id="date-input-error" part="error" ?hidden=${!hasError}>
288
400
  ${this.errorText}<slot name="error" @slotchange=${this.onErrorSlotChange}></slot>
289
401
  </div>
290
- <div part="hint" ?hidden=${!hasHint}>
402
+ <div id="date-input-hint" part="hint" ?hidden=${!hasHint}>
291
403
  ${this.hint}<slot name="hint" @slotchange=${this.onHintSlotChange}></slot>
292
404
  </div>
293
405
  </div>
@@ -43,6 +43,13 @@ export declare class LyraDatePicker extends LyraElement {
43
43
  protected willUpdate(changed: PropertyValues): void;
44
44
  private get fdow();
45
45
  private isDisabled;
46
+ /** The first enabled day at/after the first visible month's start, scanning
47
+ * forward across all visible months -- used as the sole focusable day when
48
+ * there's no selection and no prior keyboard focus, so the empty-grid case
49
+ * never defaults to a possibly-disabled day 1. Returns null if every
50
+ * visible day is disabled (pathological but possible with a very narrow
51
+ * min/max window). */
52
+ private firstEnabledDate;
46
53
  private commit;
47
54
  private selectDate;
48
55
  /** Clear the selection and emit input + change. */
@@ -50,32 +50,54 @@ export class LyraDatePicker extends LyraElement {
50
50
  // `selection.from`'s month -- only an externally-set `value` should do that.
51
51
  this.suppressViewSync = false;
52
52
  this.onGridKey = (e) => {
53
- const current = this.focusedDate ?? this.selection.from ?? new Date();
53
+ const min = parseISO(this.min);
54
+ const max = parseISO(this.max);
55
+ const today = new Date();
56
+ today.setHours(0, 0, 0, 0);
57
+ const current = this.focusedDate ??
58
+ this.selection.from ??
59
+ this.firstEnabledDate(min, max, today) ??
60
+ new Date(this.viewDate.getFullYear(), this.viewDate.getMonth(), 1);
61
+ const step = (base, days) => new Date(base.getFullYear(), base.getMonth(), base.getDate() + days);
62
+ // Walks in the requested direction until an enabled day is found, bounded
63
+ // by a step cap (366 days -- more than a year) so an all-disabled range
64
+ // (e.g. min/max entirely in the past with disable-past set) can't loop
65
+ // forever; returns null rather than landing focus on a disabled cell,
66
+ // whose `.focus()` would be a silent no-op anyway.
67
+ const firstEnabledFrom = (base, dayStep) => {
68
+ let d = base;
69
+ for (let i = 0; i < 366; i++) {
70
+ if (!this.isDisabled(d, min, max, today))
71
+ return d;
72
+ d = step(d, dayStep);
73
+ }
74
+ return null;
75
+ };
54
76
  let next = null;
55
77
  switch (e.key) {
56
78
  case 'ArrowLeft':
57
- next = new Date(current.getFullYear(), current.getMonth(), current.getDate() - 1);
79
+ next = firstEnabledFrom(step(current, -1), -1);
58
80
  break;
59
81
  case 'ArrowRight':
60
- next = new Date(current.getFullYear(), current.getMonth(), current.getDate() + 1);
82
+ next = firstEnabledFrom(step(current, 1), 1);
61
83
  break;
62
84
  case 'ArrowUp':
63
- next = new Date(current.getFullYear(), current.getMonth(), current.getDate() - 7);
85
+ next = firstEnabledFrom(step(current, -7), -1);
64
86
  break;
65
87
  case 'ArrowDown':
66
- next = new Date(current.getFullYear(), current.getMonth(), current.getDate() + 7);
88
+ next = firstEnabledFrom(step(current, 7), 1);
67
89
  break;
68
90
  case 'PageUp':
69
- next = new Date(current.getFullYear(), current.getMonth() - 1, current.getDate());
91
+ next = firstEnabledFrom(new Date(current.getFullYear(), current.getMonth() - 1, current.getDate()), 1);
70
92
  break;
71
93
  case 'PageDown':
72
- next = new Date(current.getFullYear(), current.getMonth() + 1, current.getDate());
94
+ next = firstEnabledFrom(new Date(current.getFullYear(), current.getMonth() + 1, current.getDate()), 1);
73
95
  break;
74
96
  case 'Home':
75
- next = new Date(current.getFullYear(), current.getMonth(), 1);
97
+ next = firstEnabledFrom(new Date(current.getFullYear(), current.getMonth(), 1), 1);
76
98
  break;
77
99
  case 'End':
78
- next = new Date(current.getFullYear(), current.getMonth() + 1, 0);
100
+ next = firstEnabledFrom(new Date(current.getFullYear(), current.getMonth() + 1, 0), -1);
79
101
  break;
80
102
  case 'Enter':
81
103
  case ' ':
@@ -86,6 +108,8 @@ export class LyraDatePicker extends LyraElement {
86
108
  return;
87
109
  }
88
110
  e.preventDefault();
111
+ if (!next)
112
+ return;
89
113
  this.focusedDate = next;
90
114
  this.viewDate = this.viewDateForFocus(next);
91
115
  this.focusPending = true;
@@ -127,6 +151,23 @@ export class LyraDatePicker extends LyraElement {
127
151
  return true;
128
152
  return false;
129
153
  }
154
+ /** The first enabled day at/after the first visible month's start, scanning
155
+ * forward across all visible months -- used as the sole focusable day when
156
+ * there's no selection and no prior keyboard focus, so the empty-grid case
157
+ * never defaults to a possibly-disabled day 1. Returns null if every
158
+ * visible day is disabled (pathological but possible with a very narrow
159
+ * min/max window). */
160
+ firstEnabledDate(min, max, today) {
161
+ let d = new Date(this.viewDate.getFullYear(), this.viewDate.getMonth(), 1);
162
+ const lastVisibleMonth = addMonths(d, this.months - 1);
163
+ const boundEnd = new Date(lastVisibleMonth.getFullYear(), lastVisibleMonth.getMonth() + 1, 0);
164
+ for (let i = 0; i < 732 && d <= boundEnd; i++) {
165
+ if (!this.isDisabled(d, min, max, today))
166
+ return d;
167
+ d = new Date(d.getFullYear(), d.getMonth(), d.getDate() + 1);
168
+ }
169
+ return null;
170
+ }
130
171
  commit(from, to, fire) {
131
172
  const next = this.mode === 'range'
132
173
  ? from && to
@@ -217,7 +258,7 @@ export class LyraDatePicker extends LyraElement {
217
258
  cell?.focus();
218
259
  }
219
260
  }
220
- renderDay(date, shownMonth, selection, min, max, today, rowHasVisibleDay) {
261
+ renderDay(date, shownMonth, selection, min, max, today, rowHasVisibleDay, fallbackFocusDate) {
221
262
  const outside = date.getMonth() !== shownMonth;
222
263
  // Outside-month days are empty placeholders by default (matches WA), keeping the
223
264
  // 6-row grid aligned without low-contrast faded numbers.
@@ -240,7 +281,19 @@ export class LyraDatePicker extends LyraElement {
240
281
  const isEnd = to && isSameDay(date, to);
241
282
  const selected = this.mode === 'single' ? isStart : isStart || isEnd;
242
283
  const inRange = this.mode === 'range' && from && to && date > from && date < to;
243
- const focused = this.focusedDate && isSameDay(date, this.focusedDate) ? true : !this.focusedDate && isStart;
284
+ // Falls back to the first *enabled* day across the visible month(s) (see
285
+ // firstEnabledDate()) when there's neither a focusedDate nor a selection,
286
+ // so the grid always has exactly one tabbable cell -- otherwise every
287
+ // cell computes `false` and keyboard users tabbing into an empty picker
288
+ // land nowhere. Must not just be "day 1 of the shown month" unconditionally:
289
+ // day 1 can itself be disabled (e.g. disable-past opened on any day other
290
+ // than the 1st), which would land the sole tabindex="0" cell on a button
291
+ // that can never actually receive focus.
292
+ const focused = this.focusedDate != null
293
+ ? isSameDay(date, this.focusedDate)
294
+ : isStart
295
+ ? true
296
+ : !from && fallbackFocusDate != null && isSameDay(date, fallbackFocusDate);
244
297
  const parts = ['day'];
245
298
  if (outside)
246
299
  parts.push('day-outside');
@@ -272,7 +325,7 @@ export class LyraDatePicker extends LyraElement {
272
325
  ${date.getDate()}
273
326
  </button>`;
274
327
  }
275
- renderMonth(offset, selection, min, max, today, fdow, labels) {
328
+ renderMonth(offset, selection, min, max, today, fdow, labels, fallbackFocusDate) {
276
329
  const base = addMonths(this.viewDate, offset);
277
330
  const year = base.getFullYear();
278
331
  const month = base.getMonth();
@@ -282,13 +335,25 @@ export class LyraDatePicker extends LyraElement {
282
335
  return html `<div part="month">
283
336
  <div part="header">
284
337
  ${isFirst
285
- ? html `<button part="previous" type="button" aria-label="Previous month" @click=${() => this.nav(-1)}>
338
+ ? html `<button
339
+ part="previous"
340
+ type="button"
341
+ aria-label="Previous month"
342
+ ?disabled=${this.disabled || this.readonly}
343
+ @click=${() => this.nav(-1)}
344
+ >
286
345
  ${chevronIcon()}
287
346
  </button>`
288
347
  : html `<span></span>`}
289
348
  <div part="title">${monthTitle(year, month, this.locale)}</div>
290
349
  ${isLast
291
- ? html `<button part="next" type="button" aria-label="Next month" @click=${() => this.nav(1)}>
350
+ ? html `<button
351
+ part="next"
352
+ type="button"
353
+ aria-label="Next month"
354
+ ?disabled=${this.disabled || this.readonly}
355
+ @click=${() => this.nav(1)}
356
+ >
292
357
  ${chevronIcon()}
293
358
  </button>`
294
359
  : html `<span></span>`}
@@ -297,7 +362,7 @@ export class LyraDatePicker extends LyraElement {
297
362
  <div part="grid" role="grid" @keydown=${this.onGridKey}>
298
363
  ${matrix.map((week) => {
299
364
  const rowHasVisibleDay = week.some((d) => d.getMonth() === month);
300
- return html `<div part="week" role="row">${week.map((d) => this.renderDay(d, month, selection, min, max, today, rowHasVisibleDay))}</div>`;
365
+ return html `<div part="week" role="row">${week.map((d) => this.renderDay(d, month, selection, min, max, today, rowHasVisibleDay, fallbackFocusDate))}</div>`;
301
366
  })}
302
367
  </div>
303
368
  </div>`;
@@ -310,9 +375,13 @@ export class LyraDatePicker extends LyraElement {
310
375
  today.setHours(0, 0, 0, 0);
311
376
  const fdow = this.fdow;
312
377
  const labels = weekdayLabels(fdow, this.weekdayFormat, this.locale);
378
+ // Only bother scanning for a fallback focus day when it's actually
379
+ // needed: there's no point walking the visible days if a focusedDate or
380
+ // an existing selection already determines the sole tabbable cell.
381
+ const fallbackFocusDate = this.focusedDate || selection.from ? null : this.firstEnabledDate(min, max, today);
313
382
  const monthEls = [];
314
383
  for (let i = 0; i < this.months; i++) {
315
- monthEls.push(this.renderMonth(i, selection, min, max, today, fdow, labels));
384
+ monthEls.push(this.renderMonth(i, selection, min, max, today, fdow, labels, fallbackFocusDate));
316
385
  }
317
386
  return html `<div part="base">${monthEls}</div>`;
318
387
  }
@@ -0,0 +1,40 @@
1
+ import './dialog.js';
2
+ export interface ConfirmOptions {
3
+ /** Dialog title -- rendered as a slotted `<h2>`, which also drives the dialog's accessible name (see dialog.ts). */
4
+ title: string;
5
+ /** Optional supporting text rendered below the title. */
6
+ description?: string;
7
+ /** Confirm button label. Defaults to `'Confirm'`. */
8
+ confirmLabel?: string;
9
+ /** Cancel button label. Defaults to `'Cancel'`. */
10
+ cancelLabel?: string;
11
+ /** `'danger'` fills the confirm button with `--lyra-color-danger` instead of `--lyra-color-brand` -- for destructive actions. */
12
+ tone?: 'neutral' | 'danger';
13
+ }
14
+ /**
15
+ * Show a confirmation modal, resolving `true` only when the confirm button
16
+ * is pressed -- Escape, a backdrop click, and the cancel button all resolve
17
+ * `false`. A drop-in replacement for `window.confirm()`.
18
+ *
19
+ * Mounts a transient `<lyra-dialog>` for the duration of the call and
20
+ * removes it once settled, rather than reusing a persistent page-level
21
+ * region (contrast `lyra-toast`'s `toaster.ts`): a confirmation modal has no
22
+ * stacking/queueing concerns -- only one is ever meant to be open at a time
23
+ * -- so a mount-and-remove per call keeps its lifetime trivially tied to the
24
+ * returned promise, with nothing left mounted between calls.
25
+ *
26
+ * Every dismissal path (confirm button, cancel button, Escape, backdrop
27
+ * click) funnels through `<lyra-dialog>`'s own `close()`/`lyra-dialog-close`
28
+ * event, so there is exactly one place that resolves the promise and tears
29
+ * the dialog down.
30
+ *
31
+ * @example
32
+ * const ok = await confirm({
33
+ * title: 'Delete conversation?',
34
+ * description: 'This cannot be undone.',
35
+ * confirmLabel: 'Delete',
36
+ * tone: 'danger',
37
+ * });
38
+ * if (ok) deleteConversation();
39
+ */
40
+ export declare function confirm(options: ConfirmOptions): Promise<boolean>;
@@ -0,0 +1,79 @@
1
+ import './dialog.js';
2
+ // Plain inline-styled <button>s, not a shared lyra-button component -- none
3
+ // exists in this library yet. Every value below is still a --lyra-* token
4
+ // reference, never a raw literal, same requirement as a component's own
5
+ // styles.ts. --lyra-color-on-brand is documented (tokens.styles.ts) as the
6
+ // text color for *brand*-fill content specifically, but it's white in both
7
+ // light and dark themes and reads fine over the danger fill too, so the
8
+ // confirm button reuses it rather than introducing a redundant --lyra-color-
9
+ // on-danger token for one call site.
10
+ const BUTTON_BASE_STYLE = 'font: inherit; font-size: 0.875rem; padding: var(--lyra-space-xs) var(--lyra-space-m); ' +
11
+ 'border-radius: var(--lyra-radius); cursor: pointer; border: 1px solid var(--lyra-color-border);';
12
+ const CANCEL_STYLE = 'background: var(--lyra-color-surface); color: var(--lyra-color-text);';
13
+ const CONFIRM_TONE_STYLE = {
14
+ neutral: 'background: var(--lyra-color-brand); color: var(--lyra-color-on-brand); border-color: var(--lyra-color-brand);',
15
+ danger: 'background: var(--lyra-color-danger); color: var(--lyra-color-on-brand); border-color: var(--lyra-color-danger);',
16
+ };
17
+ function createButton(label, style, onClick) {
18
+ const button = document.createElement('button');
19
+ button.type = 'button';
20
+ button.textContent = label;
21
+ button.style.cssText = `${BUTTON_BASE_STYLE} ${style}`;
22
+ button.addEventListener('click', onClick);
23
+ return button;
24
+ }
25
+ /**
26
+ * Show a confirmation modal, resolving `true` only when the confirm button
27
+ * is pressed -- Escape, a backdrop click, and the cancel button all resolve
28
+ * `false`. A drop-in replacement for `window.confirm()`.
29
+ *
30
+ * Mounts a transient `<lyra-dialog>` for the duration of the call and
31
+ * removes it once settled, rather than reusing a persistent page-level
32
+ * region (contrast `lyra-toast`'s `toaster.ts`): a confirmation modal has no
33
+ * stacking/queueing concerns -- only one is ever meant to be open at a time
34
+ * -- so a mount-and-remove per call keeps its lifetime trivially tied to the
35
+ * returned promise, with nothing left mounted between calls.
36
+ *
37
+ * Every dismissal path (confirm button, cancel button, Escape, backdrop
38
+ * click) funnels through `<lyra-dialog>`'s own `close()`/`lyra-dialog-close`
39
+ * event, so there is exactly one place that resolves the promise and tears
40
+ * the dialog down.
41
+ *
42
+ * @example
43
+ * const ok = await confirm({
44
+ * title: 'Delete conversation?',
45
+ * description: 'This cannot be undone.',
46
+ * confirmLabel: 'Delete',
47
+ * tone: 'danger',
48
+ * });
49
+ * if (ok) deleteConversation();
50
+ */
51
+ export function confirm(options) {
52
+ const { title, description, confirmLabel = 'Confirm', cancelLabel = 'Cancel', tone = 'neutral' } = options;
53
+ return new Promise((resolve) => {
54
+ const dialog = document.createElement('lyra-dialog');
55
+ const heading = document.createElement('h2');
56
+ heading.textContent = title;
57
+ heading.style.cssText = 'margin: 0 0 var(--lyra-space-s) 0; font-size: 1.0625rem; font-weight: 600;';
58
+ dialog.appendChild(heading);
59
+ if (description) {
60
+ const desc = document.createElement('p');
61
+ desc.textContent = description;
62
+ desc.style.cssText = 'margin: var(--lyra-space-xs) 0 0 0; color: var(--lyra-color-text-quiet);';
63
+ dialog.appendChild(desc);
64
+ }
65
+ dialog.addEventListener('lyra-dialog-close', (e) => {
66
+ const reason = e.detail;
67
+ resolve(reason === 'confirm');
68
+ dialog.remove();
69
+ });
70
+ const cancelButton = createButton(cancelLabel, CANCEL_STYLE, () => dialog.close('cancel'));
71
+ cancelButton.slot = 'footer';
72
+ const confirmButton = createButton(confirmLabel, CONFIRM_TONE_STYLE[tone], () => dialog.close('confirm'));
73
+ confirmButton.slot = 'footer';
74
+ dialog.appendChild(cancelButton);
75
+ dialog.appendChild(confirmButton);
76
+ dialog.open = true;
77
+ document.body.appendChild(dialog);
78
+ });
79
+ }
@@ -0,0 +1,85 @@
1
+ import { type TemplateResult, type PropertyValues } from 'lit';
2
+ import { LyraElement } from '../../internal/lyra-element.js';
3
+ /**
4
+ * Reason a dialog was dismissed, forwarded as the `lyra-dialog-close` event
5
+ * detail. `'escape'` and `'backdrop'` are emitted by the dialog's own built-in
6
+ * dismiss triggers; any other string is whatever a caller passes to
7
+ * `close()` (e.g. a consumer's own footer close button, or confirm.ts's
8
+ * `'confirm'`/`'cancel'`).
9
+ */
10
+ export type DialogCloseReason = 'escape' | 'backdrop' | 'api' | string;
11
+ /**
12
+ * `<lyra-dialog>` — a general-purpose modal/overlay. `role="dialog"`,
13
+ * focus-trapped while open, dismissible via Escape or a backdrop click, and
14
+ * scroll-locks the document for as long as it's open. Chrome stays minimal —
15
+ * no built-in title bar or close button; a consumer supplies a heading and
16
+ * any close affordance itself via the default/`footer` slots.
17
+ *
18
+ * Accessible name: if a heading element (`h1`–`h6` or `[role="heading"]`) is
19
+ * a *direct child* (not inside `slot="footer"`), its text content becomes
20
+ * `aria-label` on the panel. Otherwise, when `label` is set, an invisible
21
+ * (`.sr-only`, exposed as the `label` part) element carrying that text is
22
+ * rendered inside the panel and `aria-labelledby` points at it instead.
23
+ * Either way `label` itself never renders visible chrome — a slotted heading
24
+ * is what a sighted user sees; `::part(label)` can be restyled to make the
25
+ * sr-only text visible too, if a consumer wants that instead of slotting a
26
+ * heading.
27
+ *
28
+ * The heading case deliberately uses `aria-label` (a copied string) rather
29
+ * than `aria-labelledby` pointing at the heading's `id`: the heading is
30
+ * *light-DOM* content while `[part="panel"]` lives in this element's
31
+ * *shadow* tree, and an ID-reference attribute can't resolve across that
32
+ * boundary (verified against axe's `aria-dialog-name` rule) — unlike the
33
+ * `label`-prop case above, where the sr-only element is rendered inside the
34
+ * same shadow root it labels, so `aria-labelledby` there is safe.
35
+ *
36
+ * @customElement lyra-dialog
37
+ * @slot - The dialog body.
38
+ * @slot footer - Action buttons, rendered in a bottom row.
39
+ * @event lyra-dialog-close - `detail: DialogCloseReason`. Fired whenever the
40
+ * dialog is dismissed via Escape, a backdrop click, or a `close()` call.
41
+ * @csspart backdrop - The full-viewport scrim behind the panel.
42
+ * @csspart panel - The dialog panel itself (`role="dialog"` while open).
43
+ * @csspart label - The invisible `label`-text element used for
44
+ * `aria-labelledby` when no heading is slotted.
45
+ * @csspart body - The wrapper around the default slot.
46
+ * @csspart footer - The wrapper around the `footer` slot.
47
+ */
48
+ export declare class LyraDialog extends LyraElement {
49
+ static styles: import("lit").CSSResultGroup[];
50
+ /** Whether the dialog is open. Set this (or call `close()`) — there is no separate `show()`/`hide()` pair. */
51
+ open: boolean;
52
+ /** Accessible name used when no heading is slotted — see the class doc for the full fallback order. */
53
+ label: string;
54
+ private hasFooterSlot;
55
+ private headingText?;
56
+ private releaseScrollLock?;
57
+ private lastTrigger?;
58
+ private readonly srLabelId;
59
+ protected willUpdate(changed: PropertyValues): void;
60
+ protected updated(changed: PropertyValues): void;
61
+ connectedCallback(): void;
62
+ disconnectedCallback(): void;
63
+ private onDefaultSlotChange;
64
+ private onFooterSlotChange;
65
+ private detectHeading;
66
+ /**
67
+ * Close the dialog and return focus to whatever had it before the dialog
68
+ * opened. `reason` is forwarded as the `lyra-dialog-close` detail --
69
+ * built-in triggers pass `'escape'`/`'backdrop'`; a consumer's own close
70
+ * affordance (e.g. a footer Cancel button) should call this directly with
71
+ * its own reason string, so every dismissal path funnels through the same
72
+ * event instead of the consumer having to also toggle `open` itself.
73
+ */
74
+ close(reason?: DialogCloseReason): void;
75
+ private onBackdropClick;
76
+ private onDocKeyDown;
77
+ private getFocusableElements;
78
+ private getActiveElement;
79
+ render(): TemplateResult;
80
+ }
81
+ declare global {
82
+ interface HTMLElementTagNameMap {
83
+ 'lyra-dialog': LyraDialog;
84
+ }
85
+ }