@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,8 +4,8 @@ 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';
8
- import { property, state } from 'lit/decorators.js';
7
+ import { html, nothing } from 'lit';
8
+ import { property, state, query } from 'lit/decorators.js';
9
9
  import { LyraElement } from '../../internal/lyra-element.js';
10
10
  import { defineElement } from '../../internal/prefix.js';
11
11
  import { isRtl } from '../../internal/rtl.js';
@@ -20,11 +20,28 @@ const NO_MAX_PX = 1_000_000;
20
20
  * `<lyra-split>` — resizable panels for dashboard layouts. Direct light-DOM
21
21
  * children are the panels; a divider is auto-inserted between each pair.
22
22
  *
23
+ * Optionally, one pane can opt in to responsive collapse via `collapse`
24
+ * (`"start"`/`"end"`, default `"none"` — no behavior change when unset): as
25
+ * the split's own container narrows past `railBreakpoint` that pane clamps
26
+ * to a fixed `railWidth`, and past the narrower `floatBreakpoint` it instead
27
+ * becomes an absolutely-positioned overlay "floating card" above the other
28
+ * pane. This component only handles the width-collapse mechanics and
29
+ * signals the current state — via the `collapseState`-derived
30
+ * `data-collapse-state` attribute (set on both the host and the collapsing
31
+ * panel itself) and the `lyra-split-collapse-change` event — it renders no
32
+ * icon-only/collapsed UI of its own; slotted content is expected to adapt to
33
+ * its own clamped width (e.g. via its own container query).
34
+ *
23
35
  * @customElement lyra-split
24
36
  * @event lyra-resize - `detail: { sizes }`, fired on every drag step/release
25
37
  * and every keyboard step.
38
+ * @event lyra-split-collapse-change - `detail: { state }` (`SplitCollapseChangeDetail`),
39
+ * fired whenever the responsive `collapseState` actually transitions between
40
+ * `'wide'`/`'rail'`/`'floating'`. Only relevant when `collapse` isn't `"none"`;
41
+ * never fires otherwise.
26
42
  * @slot - Panels to arrange side by side (or stacked, when `orientation="vertical"`); each direct child becomes one resizable panel.
27
- * @csspart base, divider
43
+ * @csspart base - The flex layout wrapper (`position: relative`, so the `'floating'` collapse state can anchor to it).
44
+ * @csspart divider - Each divider between two panels; carries `aria-disabled="true"` and is drag/keyboard-inert while its adjacent panel is collapsed (`'rail'`/`'floating'`).
28
45
  */
29
46
  export class LyraSplit extends LyraElement {
30
47
  constructor() {
@@ -38,21 +55,55 @@ export class LyraSplit extends LyraElement {
38
55
  * localStorage persistence stay percent-based regardless — only the
39
56
  * effective clamp bounds change for a constrained panel. */
40
57
  this.panelConstraints = [];
58
+ /** Opts a pane in to responsive collapse: `'start'` is the first light-DOM
59
+ * panel (index 0), `'end'` is the last. Both are LOGICAL positions, same
60
+ * as CSS `inset-inline-start`/`-end` — see the `collapsingIndex` getter
61
+ * for why that already resolves to the same physical index under RTL for
62
+ * this component (panels are never re-`order`ed for RTL, only the drag
63
+ * delta sign mirrors). Default `'none'`: none of the collapse behavior
64
+ * below applies, and rendering/behavior is identical to before this
65
+ * property existed. */
66
+ this.collapse = 'none';
67
+ /** Fixed CSS length the collapsing pane clamps to in the `'rail'` state. */
68
+ this.railWidth = '3.5rem';
69
+ /** Container width (px, measured on `[part="base"]`) below which the
70
+ * collapsing pane switches from its normal percent width to the fixed
71
+ * `railWidth` (`'rail'` state). Must stay above `floatBreakpoint`. */
72
+ this.railBreakpoint = 640;
73
+ /** Container width (px) below which the collapsing pane instead becomes an
74
+ * absolutely-positioned overlay above the other pane (`'floating'` state). */
75
+ this.floatBreakpoint = 400;
41
76
  this.panelCount = 0;
77
+ /** The collapsing pane's current responsive state; always `'wide'` (a
78
+ * no-op) while `collapse === 'none'`. */
79
+ this.collapseState = 'wide';
42
80
  // Keyed by pointerId so an interrupted or concurrent (multi-touch) drag on
43
81
  // one divider never reads or clobbers another pointer's drag state.
44
82
  this.drags = new Map();
83
+ // Panels `updated()` last applied inline flex/order (and, while collapsing,
84
+ // position/inset/inline-size) styles to — tracked so a panel removed from
85
+ // the slot (e.g. a DOM node later reused elsewhere) gets those
86
+ // lyra-split-authored styles cleared instead of carrying them around stale
87
+ // forever.
88
+ this.stylizedPanels = new Set();
45
89
  this.onSlotChange = () => {
46
90
  this.panelCount = this.children.length;
47
91
  this.ensureSizes();
48
92
  };
49
93
  this.onPointerDown = (e, index) => {
94
+ // The divider adjacent to a currently rail/floating-collapsed pane isn't
95
+ // draggable — mirrors the `[part="divider"][aria-disabled="true"]`
96
+ // `pointer-events: none` in split.styles.ts (belt-and-suspenders: this
97
+ // guard also covers a synthetic/programmatic pointerdown that CSS
98
+ // wouldn't stop).
99
+ if (this.isDividerDisabled(index))
100
+ return;
50
101
  const base = this.renderRoot.querySelector('[part="base"]');
51
102
  this.drags.set(e.pointerId, {
52
103
  index,
53
104
  startPos: this.orientation === 'vertical' ? e.clientY : e.clientX,
54
- startSizes: [...this.sizes],
55
105
  base,
106
+ appliedDelta: 0,
56
107
  });
57
108
  e.target.setPointerCapture(e.pointerId);
58
109
  window.addEventListener('pointermove', this.onPointerMove);
@@ -70,14 +121,40 @@ export class LyraSplit extends LyraElement {
70
121
  return;
71
122
  const total = this.orientation === 'vertical' ? drag.base.clientHeight : drag.base.clientWidth;
72
123
  const pos = this.orientation === 'vertical' ? e.clientY : e.clientX;
73
- let deltaPercent = ((pos - drag.startPos) / total) * 100;
124
+ let cumulativeDelta = ((pos - drag.startPos) / total) * 100;
74
125
  // Panels are ordered along the inline axis via CSS `order`, so under RTL
75
126
  // `flex-direction: row` already renders panel[i] to the *right* of
76
127
  // panel[i+1] — a physically-rightward drag has to shrink index instead
77
128
  // of growing it to keep matching the visible panel under the pointer.
78
129
  if (this.orientation === 'horizontal' && isRtl(this))
79
- deltaPercent = -deltaPercent;
80
- const paired = this.clampPair(drag.startSizes, drag.index, deltaPercent, total);
130
+ cumulativeDelta = -cumulativeDelta;
131
+ // Clamp against the *current* live sizes, not this pointer's own drag-start
132
+ // snapshot -- two adjacent dividers dragged concurrently share one panel
133
+ // between them, and each clamp pass must see whatever the other pointer's
134
+ // move has *just* written, or the two independently-clamped pairs can each
135
+ // stay individually valid while their shared panel drifts past what either
136
+ // pair alone would allow, letting the total exceed 100%. Since the clamp
137
+ // basis is now the live, already-partially-applied `this.sizes` instead of
138
+ // a fixed drag-start snapshot, only the *incremental* delta since the last
139
+ // move may be applied here (not the cumulative-since-drag-start delta a
140
+ // snapshot-based clamp would use).
141
+ const incremental = cumulativeDelta - drag.appliedDelta;
142
+ const priorValue = this.sizes[drag.index];
143
+ const paired = this.clampPair(this.sizes, drag.index, incremental, total);
144
+ // Accumulate this move's own *realized* increment (post-clamp) onto the
145
+ // running total, rather than recomputing an absolute "total since
146
+ // drag-start" diff against a fixed startSizes snapshot. clampPair can
147
+ // cap the actual move short of what was requested (e.g. a drag
148
+ // saturating a panel's min/panelConstraints bound), so the realized
149
+ // portion still has to be tracked instead of the raw request -- that
150
+ // part of round 1's fix stands. But an absolute since-start diff also
151
+ // silently absorbs any change a DIFFERENT concurrent pointer's drag
152
+ // makes to this same shared panel between this pointer's own moves
153
+ // (adjacent dividers share one panel: divider i's index+1 panel is
154
+ // divider i+1's index panel), corrupting this pointer's next incremental
155
+ // calculation. Summing only this move's own delta (paired vs. the value
156
+ // immediately prior to *this* clamp) avoids both bugs at once.
157
+ drag.appliedDelta += paired[drag.index] - priorValue;
81
158
  // Merge only this drag's pair into the live sizes so a concurrent drag
82
159
  // on another divider (different pointerId) isn't clobbered.
83
160
  const next = [...this.sizes];
@@ -99,6 +176,9 @@ export class LyraSplit extends LyraElement {
99
176
  }
100
177
  };
101
178
  this.onDividerKeyDown = (e, index) => {
179
+ // Same rail/floating-adjacent guard as onPointerDown.
180
+ if (this.isDividerDisabled(index))
181
+ return;
102
182
  // Mirror the same swap as onPointerMove for horizontal+RTL.
103
183
  const rtl = this.orientation === 'horizontal' && isRtl(this);
104
184
  const forwardKey = this.orientation === 'vertical' ? 'ArrowDown' : rtl ? 'ArrowLeft' : 'ArrowRight';
@@ -119,6 +199,13 @@ export class LyraSplit extends LyraElement {
119
199
  this.panelCount = this.children.length;
120
200
  this.ensureSizes();
121
201
  this.loadPersisted();
202
+ // No-op on first mount (`baseEl` doesn't exist until the first render —
203
+ // `firstUpdated()` below arms it then) but does the real work on a
204
+ // reconnect, whose shadow DOM content survives disconnect. Mirrors
205
+ // lite-chart.ts's identical connectedCallback/firstUpdated split for its
206
+ // own ResizeObserver.
207
+ if (this.collapse !== 'none')
208
+ this.armCollapseObserver();
122
209
  }
123
210
  disconnectedCallback() {
124
211
  super.disconnectedCallback();
@@ -128,6 +215,11 @@ export class LyraSplit extends LyraElement {
128
215
  window.removeEventListener('pointerup', this.onPointerUp);
129
216
  window.removeEventListener('pointercancel', this.onPointerUp);
130
217
  window.removeEventListener('lostpointercapture', this.onPointerUp);
218
+ this.collapseResizeObserver?.disconnect();
219
+ }
220
+ firstUpdated() {
221
+ if (this.collapse !== 'none')
222
+ this.armCollapseObserver();
131
223
  }
132
224
  get storageFullKey() {
133
225
  return this.storageKey ? `lyra-split:${this.storageKey}:${this.panelCount}` : undefined;
@@ -211,6 +303,84 @@ export class LyraSplit extends LyraElement {
211
303
  return 0;
212
304
  return this.orientation === 'vertical' ? base.clientHeight : base.clientWidth;
213
305
  }
306
+ /** The physical panel index `collapse: 'start' | 'end'` resolves to, or
307
+ * `-1` when collapse is off (`'none'`) or there are fewer than 2 panels
308
+ * to collapse one of. Panels are laid out via ascending inline `order`
309
+ * (see `updated()`) and that ordering is never re-swapped for RTL — only
310
+ * the drag/keyboard *delta sign* mirrors for RTL, exactly like
311
+ * `onPointerMove`/`onDividerKeyDown` — so panel index 0 already renders
312
+ * at the logical inline-start edge under both LTR and RTL (confirmed by
313
+ * the pointer-drag RTL test elsewhere in this file: panel 0 renders on
314
+ * the visual *right*, i.e. the RTL inline-start side). `'start'` therefore
315
+ * always resolves to index 0 and `'end'` to `panelCount - 1`, regardless
316
+ * of `isRtl(this)` — consulting it here would swap collapse onto the
317
+ * panel that visually sits at the *other* logical edge, which would be a
318
+ * bug against this component's own RTL rendering, not a fix for one.
319
+ */
320
+ get collapsingIndex() {
321
+ if (this.collapse === 'none' || this.panelCount < 2)
322
+ return -1;
323
+ return this.collapse === 'start' ? 0 : this.panelCount - 1;
324
+ }
325
+ /** Whether a pane is actually collapsed (rail or floating) right now —
326
+ * `false` whenever `collapse === 'none'`, since `collapseState` then
327
+ * never leaves its `'wide'` default. */
328
+ get collapseActive() {
329
+ return this.collapsingIndex !== -1 && this.collapseState !== 'wide';
330
+ }
331
+ /** Dragging/keyboard-resizing is disabled on the one divider immediately
332
+ * adjacent to the currently-collapsed pane (its other side has nothing
333
+ * meaningful to resize against while the pane is rail/floating-width). */
334
+ isDividerDisabled(index) {
335
+ if (!this.collapseActive)
336
+ return false;
337
+ const adjacent = this.collapsingIndex === 0 ? 0 : this.panelCount - 2;
338
+ return index === adjacent;
339
+ }
340
+ /** Classifies a measured container width into the collapsing pane's
341
+ * responsive state and, only on an actual transition, updates
342
+ * `collapseState` and fires `lyra-split-collapse-change`. */
343
+ updateCollapseState(width) {
344
+ if (this.collapse === 'none')
345
+ return;
346
+ const next = width < this.floatBreakpoint ? 'floating' : width < this.railBreakpoint ? 'rail' : 'wide';
347
+ if (next === this.collapseState)
348
+ return;
349
+ this.collapseState = next;
350
+ this.emit('lyra-split-collapse-change', { state: next });
351
+ }
352
+ /** Creates (idempotently) and (re-)observes `[part="base"]` with the
353
+ * collapse-state `ResizeObserver` — a no-op until `baseEl` exists (see
354
+ * `firstUpdated()`/`connectedCallback()`). Mirrors lite-chart.ts's own
355
+ * ResizeObserver setup, including reading the box size synchronously
356
+ * once so the very first render doesn't sit at the stale `'wide'` default
357
+ * until the (async) first callback fires — same rationale as
358
+ * lyra-virtual-list's `containerResizeObserver` synchronous initial read. */
359
+ armCollapseObserver() {
360
+ if (!this.collapseResizeObserver) {
361
+ this.collapseResizeObserver = new ResizeObserver((entries) => {
362
+ const box = entries[0]?.contentBoxSize?.[0];
363
+ const width = box ? box.inlineSize : (this.baseEl?.getBoundingClientRect().width ?? 0);
364
+ this.updateCollapseState(width);
365
+ });
366
+ }
367
+ if (this.baseEl) {
368
+ this.collapseResizeObserver.observe(this.baseEl);
369
+ this.updateCollapseState(this.baseEl.clientWidth);
370
+ }
371
+ }
372
+ /** Reacts to a live `collapse` property change (as opposed to the
373
+ * connect/first-render arming above): turns the observer on/off, and
374
+ * resets `collapseState` back to its `'wide'` default when collapse is
375
+ * switched off so no stale rail/floating styling survives it. */
376
+ syncCollapseObserver() {
377
+ if (this.collapse === 'none') {
378
+ this.collapseResizeObserver?.disconnect();
379
+ this.collapseState = 'wide';
380
+ return;
381
+ }
382
+ this.armCollapseObserver();
383
+ }
214
384
  /** Resolves panel `index`'s effective percent min/max for this clamp pass:
215
385
  * a `panelConstraints` entry's px bound converted against the live
216
386
  * `containerSize`, or the component's plain percent `min` (no upper
@@ -257,19 +427,98 @@ export class LyraSplit extends LyraElement {
257
427
  updated(changed) {
258
428
  if (changed.has('sizes') || this.sizes.length === 0)
259
429
  this.ensureSizes();
430
+ if (changed.has('collapse'))
431
+ this.syncCollapseObserver();
260
432
  const panels = [...this.children];
433
+ const current = new Set(panels);
434
+ // A panel that dropped out of the slot (removed from the light DOM)
435
+ // still carries whatever inline flex/order (and, while collapsing,
436
+ // position/inset/inline-size) this method last applied to it — clear
437
+ // those before that DOM node potentially gets reused elsewhere (e.g.
438
+ // re-slotted into another split/container).
439
+ for (const stale of this.stylizedPanels) {
440
+ if (!current.has(stale)) {
441
+ stale.style.removeProperty('flex');
442
+ stale.style.removeProperty('order');
443
+ stale.style.removeProperty('position');
444
+ stale.style.removeProperty('inset-block');
445
+ stale.style.removeProperty('inset-inline-start');
446
+ stale.style.removeProperty('inset-inline-end');
447
+ stale.style.removeProperty('inline-size');
448
+ delete stale.dataset.collapseState;
449
+ }
450
+ }
451
+ // Resolved once per pass rather than per panel: which physical index (if
452
+ // any) is actually collapsed right now — `-1` covers both `collapse ===
453
+ // 'none'` (the default) and a `collapse !== 'none'` pane that's still in
454
+ // its normal `'wide'` state, so every panel below falls straight through
455
+ // to the exact pre-existing (non-collapse) styling in either case.
456
+ const collapsingIndex = this.collapseActive ? this.collapsingIndex : -1;
261
457
  panels.forEach((panel, i) => {
262
458
  const percent = this.sizes[i] ?? 0;
263
459
  const constraint = this.panelConstraints[i];
264
- // clamp() mixes units natively, so a constrained panel stays pinned
265
- // between its px bounds across container resizes with no extra
266
- // ResizeObserver the browser re-evaluates it on every layout pass.
267
- panel.style.flex =
268
- constraint && (constraint.minPx != null || constraint.maxPx != null)
269
- ? `0 0 clamp(${constraint.minPx ?? NO_MIN_PX}px, ${percent}%, ${constraint.maxPx ?? NO_MAX_PX}px)`
270
- : `0 0 ${percent}%`;
460
+ // Collapse-only inline styles are always cleared first, then
461
+ // re-applied below only for the panel(s) that need them this pass —
462
+ // simpler than diffing which of the 5 properties changed.
463
+ panel.style.removeProperty('position');
464
+ panel.style.removeProperty('inset-block');
465
+ panel.style.removeProperty('inset-inline-start');
466
+ panel.style.removeProperty('inset-inline-end');
467
+ panel.style.removeProperty('inline-size');
468
+ if (collapsingIndex === i && this.collapseState === 'rail') {
469
+ // Fixed rail width instead of the normal clamp()/percent flex-basis.
470
+ panel.style.flex = `0 0 ${this.railWidth}`;
471
+ }
472
+ else if (collapsingIndex === i && this.collapseState === 'floating') {
473
+ // Lifted out of the flex flow entirely as an overlay card, anchored
474
+ // to its own logical start/end edge, spanning the full cross-axis
475
+ // extent — [part="base"] carries `position: relative` for this to
476
+ // anchor against (see split.styles.ts). Sized at its own normal
477
+ // percent width (i.e. what it would render at in the `'wide'`
478
+ // state), so there's no visual size jump the moment it un-floats.
479
+ panel.style.flex = 'none';
480
+ panel.style.position = 'absolute';
481
+ panel.style.setProperty('inset-block', '0');
482
+ panel.style.setProperty(collapsingIndex === 0 ? 'inset-inline-start' : 'inset-inline-end', '0');
483
+ panel.style.setProperty('inline-size', `${percent}%`);
484
+ }
485
+ else if (collapsingIndex !== -1 && i !== collapsingIndex) {
486
+ // The pane(s) sharing the split with a currently rail/floating
487
+ // collapsed pane: grow to fill whatever space that pane no longer
488
+ // occupies (its full percent-basis space while floating, or
489
+ // percent-basis-minus-railWidth while railed), proportionally to
490
+ // their own relative `sizes` share for a 3+-panel split.
491
+ panel.style.flex = `${percent} 1 0%`;
492
+ }
493
+ else {
494
+ // clamp() mixes units natively, so a constrained panel stays pinned
495
+ // between its px bounds across container resizes with no extra
496
+ // ResizeObserver — the browser re-evaluates it on every layout pass.
497
+ panel.style.flex =
498
+ constraint && (constraint.minPx != null || constraint.maxPx != null)
499
+ ? `0 0 clamp(${constraint.minPx ?? NO_MIN_PX}px, ${percent}%, ${constraint.maxPx ?? NO_MAX_PX}px)`
500
+ : `0 0 ${percent}%`;
501
+ }
271
502
  panel.style.order = String(i * 2);
503
+ if (collapsingIndex === i) {
504
+ panel.dataset.collapseState = this.collapseState;
505
+ }
506
+ else {
507
+ delete panel.dataset.collapseState;
508
+ }
272
509
  });
510
+ this.stylizedPanels = current;
511
+ // Host-level marker (mirrors the per-panel `dataset.collapseState`
512
+ // above) for simple external CSS targeting of the whole component's
513
+ // current state, e.g. `lyra-split[data-collapse-state="rail"] + aside`.
514
+ // Absent whenever there's nothing collapsed to report (`collapse ===
515
+ // 'none'` or still `'wide'`), same as the per-panel marker.
516
+ if (collapsingIndex !== -1) {
517
+ this.setAttribute('data-collapse-state', this.collapseState);
518
+ }
519
+ else {
520
+ this.removeAttribute('data-collapse-state');
521
+ }
273
522
  }
274
523
  render() {
275
524
  const dividers = [];
@@ -278,6 +527,7 @@ export class LyraSplit extends LyraElement {
278
527
  // panels, not the whole track — pushing past it would starve a panel
279
528
  // further down the line even though this pair still has room.
280
529
  const valueMax = (this.sizes[i] ?? 0) + (this.sizes[i + 1] ?? 0) - this.min;
530
+ const disabled = this.isDividerDisabled(i);
281
531
  dividers.push(html `<div
282
532
  part="divider"
283
533
  role="separator"
@@ -286,7 +536,8 @@ export class LyraSplit extends LyraElement {
286
536
  aria-valuenow=${Math.round(this.sizes[i] ?? 0)}
287
537
  aria-valuemin=${this.min}
288
538
  aria-valuemax=${Math.round(valueMax)}
289
- tabindex="0"
539
+ aria-disabled=${disabled ? 'true' : nothing}
540
+ tabindex=${disabled ? '-1' : '0'}
290
541
  style=${`order:${i * 2 + 1}`}
291
542
  @pointerdown=${(e) => this.onPointerDown(e, i)}
292
543
  @keydown=${(e) => this.onDividerKeyDown(e, i)}
@@ -310,7 +561,25 @@ __decorate([
310
561
  __decorate([
311
562
  property({ attribute: false })
312
563
  ], LyraSplit.prototype, "panelConstraints", void 0);
564
+ __decorate([
565
+ property({ reflect: true })
566
+ ], LyraSplit.prototype, "collapse", void 0);
567
+ __decorate([
568
+ property({ attribute: 'rail-width' })
569
+ ], LyraSplit.prototype, "railWidth", void 0);
570
+ __decorate([
571
+ property({ type: Number, attribute: 'rail-breakpoint' })
572
+ ], LyraSplit.prototype, "railBreakpoint", void 0);
573
+ __decorate([
574
+ property({ type: Number, attribute: 'float-breakpoint' })
575
+ ], LyraSplit.prototype, "floatBreakpoint", void 0);
313
576
  __decorate([
314
577
  state()
315
578
  ], LyraSplit.prototype, "panelCount", void 0);
579
+ __decorate([
580
+ state()
581
+ ], LyraSplit.prototype, "collapseState", void 0);
582
+ __decorate([
583
+ query('[part="base"]')
584
+ ], LyraSplit.prototype, "baseEl", void 0);
316
585
  defineElement('split', LyraSplit);
@@ -7,6 +7,9 @@ export const styles = css `
7
7
  }
8
8
  [part='base'] {
9
9
  display: flex;
10
+ /* Anchor for the 'floating' collapse state's absolutely-positioned
11
+ overlay panel (set inline by split.ts's updated()). */
12
+ position: relative;
10
13
  inline-size: 100%;
11
14
  block-size: 100%;
12
15
  }
@@ -48,4 +51,31 @@ export const styles = css `
48
51
  outline: var(--lyra-focus-ring-width) solid var(--lyra-focus-ring-color);
49
52
  outline-offset: var(--lyra-focus-ring-offset);
50
53
  }
54
+ /* The divider adjacent to a rail/floating-collapsed pane (see split.ts's
55
+ isDividerDisabled()) — geometry (position/inset/inline-size) for the
56
+ collapsing panel itself is set inline by updated(), same as the
57
+ pre-existing flex/order styling; this only covers the divider's own
58
+ drag/hover affordance and the floating panel's elevation, which read
59
+ more naturally as stylesheet rules than one-off inline styles. */
60
+ [part='divider'][aria-disabled='true'] {
61
+ cursor: default;
62
+ pointer-events: none;
63
+ }
64
+ /* The 'floating' collapse state's overlay "card" look -- geometry
65
+ (position/inset-*/inline-size) is set inline by split.ts's updated(),
66
+ matching how the ordinary flex/order styling is applied; only the
67
+ visual/stacking treatment lives here. */
68
+ ::slotted([data-collapse-state='floating']) {
69
+ z-index: 1;
70
+ background: var(--lyra-color-surface);
71
+ border-radius: var(--lyra-radius);
72
+ box-shadow: var(--lyra-shadow);
73
+ }
74
+ /* Rail-clamped content can easily overflow the fixed rail-width — clip it
75
+ rather than letting it blow out the layout; the panel's own content is
76
+ expected to adapt to the narrower width itself (e.g. via a container
77
+ query), this is just a safety net. */
78
+ ::slotted([data-collapse-state='rail']) {
79
+ overflow: hidden;
80
+ }
51
81
  `;
@@ -5,6 +5,10 @@ export type StatGoodDirection = 'up' | 'down';
5
5
  export interface StatRow {
6
6
  label: string;
7
7
  value: string;
8
+ /** Exact value shown as a hover/focus tooltip on this row's `row-value` (mirrors the headline
9
+ * `exactValue`/`exact-value` behavior). Also makes this row's `[part='row-value']`
10
+ * keyboard-focusable so the tooltip is reachable without a pointer. */
11
+ exactValue?: string;
8
12
  }
9
13
  /**
10
14
  * `<lyra-stat>` — a KPI/stat card. First-party invention consolidating the
@@ -15,6 +19,7 @@ export interface StatRow {
15
19
  * @slot caption - Rich caption content (overrides the `caption` attribute).
16
20
  * @slot spark - A sparkline (e.g. `<lyra-sparkline>`) or other compact trend
17
21
  * visual. `lyra-stat` only reserves the slot; it doesn't render one itself.
22
+ * @slot sub - Rich sub-line content (overrides the `sub` attribute).
18
23
  * @csspart base - The component's root wrapper.
19
24
  * @csspart icon - Container for the leading icon slot.
20
25
  * @csspart label - The label text.
@@ -22,12 +27,14 @@ export interface StatRow {
22
27
  * @csspart value - The value text.
23
28
  * @csspart unit - The unit text.
24
29
  * @csspart trend - The trend pill.
30
+ * @csspart sub - Container for the `sub` attribute/slot.
25
31
  * @csspart spark - Container for the `spark` slot.
26
32
  * @csspart caption - Container for the caption attribute/slot.
27
33
  * @csspart rows - Container for the `rows` breakdown list.
28
34
  * @csspart row - A single breakdown row (one per `rows` entry).
29
35
  * @csspart row-label - The label text of a breakdown row.
30
- * @csspart row-value - The value text of a breakdown row.
36
+ * @csspart row-value - The value text of a breakdown row. Shows the row's `exactValue` (if any) as
37
+ * a hover/focus tooltip, same as the headline `value`.
31
38
  */
32
39
  export declare class LyraStat extends LyraElement {
33
40
  static styles: import("lit").CSSResultGroup[];
@@ -46,13 +53,27 @@ export declare class LyraStat extends LyraElement {
46
53
  * the status `variant`; see the `[part='value']` selector below for how
47
54
  * the two combine. */
48
55
  emphasis: boolean;
56
+ /** Exact value shown as a hover/focus tooltip on the headline `value` (e.g. `value="$1.2K"
57
+ * exact-value="$1,204.37"`). Also makes `[part='value']` keyboard-focusable so the tooltip is
58
+ * reachable without a pointer. */
59
+ exactValue: string;
60
+ /** A secondary line distinct from `caption` (e.g. a comparison-period label), rendered between the
61
+ * trend pill and the caption. */
62
+ sub: string;
63
+ /** Renders `value` as smaller/lighter prose (e.g. a loading/status message) instead of the bold
64
+ * numeric headline style, and hides `unit`. */
65
+ prose: boolean;
66
+ /** Tighter padding for constrained spaces — same convention as `lyra-empty`'s `compact`. */
67
+ compact: boolean;
49
68
  private hasIcon;
50
69
  private hasCaptionSlot;
51
70
  private hasSparkSlot;
71
+ private hasSubSlot;
52
72
  protected willUpdate(): void;
53
73
  private onIconSlotChange;
54
74
  private onCaptionSlotChange;
55
75
  private onSparkSlotChange;
76
+ private onSubSlotChange;
56
77
  render(): TemplateResult;
57
78
  }
58
79
  declare global {
@@ -20,6 +20,7 @@ import { styles } from './stat.styles.js';
20
20
  * @slot caption - Rich caption content (overrides the `caption` attribute).
21
21
  * @slot spark - A sparkline (e.g. `<lyra-sparkline>`) or other compact trend
22
22
  * visual. `lyra-stat` only reserves the slot; it doesn't render one itself.
23
+ * @slot sub - Rich sub-line content (overrides the `sub` attribute).
23
24
  * @csspart base - The component's root wrapper.
24
25
  * @csspart icon - Container for the leading icon slot.
25
26
  * @csspart label - The label text.
@@ -27,12 +28,14 @@ import { styles } from './stat.styles.js';
27
28
  * @csspart value - The value text.
28
29
  * @csspart unit - The unit text.
29
30
  * @csspart trend - The trend pill.
31
+ * @csspart sub - Container for the `sub` attribute/slot.
30
32
  * @csspart spark - Container for the `spark` slot.
31
33
  * @csspart caption - Container for the caption attribute/slot.
32
34
  * @csspart rows - Container for the `rows` breakdown list.
33
35
  * @csspart row - A single breakdown row (one per `rows` entry).
34
36
  * @csspart row-label - The label text of a breakdown row.
35
- * @csspart row-value - The value text of a breakdown row.
37
+ * @csspart row-value - The value text of a breakdown row. Shows the row's `exactValue` (if any) as
38
+ * a hover/focus tooltip, same as the headline `value`.
36
39
  */
37
40
  export class LyraStat extends LyraElement {
38
41
  constructor() {
@@ -52,12 +55,25 @@ export class LyraStat extends LyraElement {
52
55
  * the status `variant`; see the `[part='value']` selector below for how
53
56
  * the two combine. */
54
57
  this.emphasis = false;
58
+ /** Exact value shown as a hover/focus tooltip on the headline `value` (e.g. `value="$1.2K"
59
+ * exact-value="$1,204.37"`). Also makes `[part='value']` keyboard-focusable so the tooltip is
60
+ * reachable without a pointer. */
61
+ this.exactValue = '';
62
+ /** A secondary line distinct from `caption` (e.g. a comparison-period label), rendered between the
63
+ * trend pill and the caption. */
64
+ this.sub = '';
65
+ /** Renders `value` as smaller/lighter prose (e.g. a loading/status message) instead of the bold
66
+ * numeric headline style, and hides `unit`. */
67
+ this.prose = false;
68
+ /** Tighter padding for constrained spaces — same convention as `lyra-empty`'s `compact`. */
69
+ this.compact = false;
55
70
  // Same fix `lyra-empty` already established: `[part]:empty` never matches
56
71
  // because the part always contains a literal `<slot>` child. Track real
57
72
  // slot assignment in JS instead.
58
73
  this.hasIcon = false;
59
74
  this.hasCaptionSlot = false;
60
75
  this.hasSparkSlot = false;
76
+ this.hasSubSlot = false;
61
77
  this.onIconSlotChange = (e) => {
62
78
  this.hasIcon = e.target.assignedElements({ flatten: true }).length > 0;
63
79
  };
@@ -67,6 +83,9 @@ export class LyraStat extends LyraElement {
67
83
  this.onSparkSlotChange = (e) => {
68
84
  this.hasSparkSlot = e.target.assignedElements({ flatten: true }).length > 0;
69
85
  };
86
+ this.onSubSlotChange = (e) => {
87
+ this.hasSubSlot = e.target.assignedElements({ flatten: true }).length > 0;
88
+ };
70
89
  }
71
90
  static { this.styles = [LyraElement.styles, styles, srOnly]; }
72
91
  willUpdate() {
@@ -74,6 +93,7 @@ export class LyraStat extends LyraElement {
74
93
  this.hasIcon = Array.from(this.children).some((el) => !el.hasAttribute('slot'));
75
94
  this.hasCaptionSlot = Array.from(this.children).some((el) => el.getAttribute('slot') === 'caption');
76
95
  this.hasSparkSlot = Array.from(this.children).some((el) => el.getAttribute('slot') === 'spark');
96
+ this.hasSubSlot = Array.from(this.children).some((el) => el.getAttribute('slot') === 'sub');
77
97
  }
78
98
  }
79
99
  render() {
@@ -86,6 +106,7 @@ export class LyraStat extends LyraElement {
86
106
  // [part='trend'].
87
107
  const arrow = rawDirection === 'flat' ? '–' : chevronIcon();
88
108
  const hasCaption = this.hasCaptionSlot || this.caption.length > 0;
109
+ const hasSub = this.hasSubSlot || this.sub.length > 0;
89
110
  // The visible pill only ever shows the icon rotation + color to convey
90
111
  // direction and good/bad polarity; both are invisible to screen readers,
91
112
  // so mirror them into a plain-language sr-only announcement.
@@ -99,7 +120,9 @@ export class LyraStat extends LyraElement {
99
120
  ></span>
100
121
  <span part="label">${this.label}</span>
101
122
  <div part="value-row">
102
- <span part="value">${this.value}</span>
123
+ <span part="value" title=${this.exactValue || nothing} tabindex=${this.exactValue ? '0' : nothing}
124
+ >${this.value}</span
125
+ >
103
126
  <span part="unit">${this.unit}</span>
104
127
  </div>
105
128
  ${hasTrend
@@ -112,6 +135,9 @@ export class LyraStat extends LyraElement {
112
135
  <span class="sr-only">${trendAnnouncement}</span>
113
136
  </span>`
114
137
  : nothing}
138
+ <div part="sub" ?hidden=${!hasSub}>
139
+ ${this.sub}<slot name="sub" @slotchange=${this.onSubSlotChange}></slot>
140
+ </div>
115
141
  <div part="spark" ?hidden=${!this.hasSparkSlot}>
116
142
  <slot name="spark" @slotchange=${this.onSparkSlotChange}></slot>
117
143
  </div>
@@ -122,7 +148,12 @@ export class LyraStat extends LyraElement {
122
148
  ${this.rows.map((row) => html `
123
149
  <div part="row">
124
150
  <span part="row-label">${row.label}</span>
125
- <span part="row-value">${row.value}</span>
151
+ <span
152
+ part="row-value"
153
+ title=${row.exactValue || nothing}
154
+ tabindex=${row.exactValue ? '0' : nothing}
155
+ >${row.value}</span
156
+ >
126
157
  </div>
127
158
  `)}
128
159
  </div>
@@ -157,6 +188,18 @@ __decorate([
157
188
  __decorate([
158
189
  property({ type: Boolean, reflect: true })
159
190
  ], LyraStat.prototype, "emphasis", void 0);
191
+ __decorate([
192
+ property({ attribute: 'exact-value' })
193
+ ], LyraStat.prototype, "exactValue", void 0);
194
+ __decorate([
195
+ property()
196
+ ], LyraStat.prototype, "sub", void 0);
197
+ __decorate([
198
+ property({ type: Boolean, reflect: true })
199
+ ], LyraStat.prototype, "prose", void 0);
200
+ __decorate([
201
+ property({ type: Boolean, reflect: true })
202
+ ], LyraStat.prototype, "compact", void 0);
160
203
  __decorate([
161
204
  state()
162
205
  ], LyraStat.prototype, "hasIcon", void 0);
@@ -166,4 +209,7 @@ __decorate([
166
209
  __decorate([
167
210
  state()
168
211
  ], LyraStat.prototype, "hasSparkSlot", void 0);
212
+ __decorate([
213
+ state()
214
+ ], LyraStat.prototype, "hasSubSlot", void 0);
169
215
  defineElement('stat', LyraStat);