@aceshooting/lyra-ui 1.2.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 (330) hide show
  1. package/README.md +11 -0
  2. package/custom-elements.json +23341 -11202
  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 +1 -2
  19. package/dist/components/chart/chart.js +67 -12
  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 +54 -0
  23. package/dist/components/chart/lite-chart.js +140 -22
  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 +29 -13
  88. package/dist/components/flag/flag.js +39 -13
  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 +74 -0
  99. package/dist/components/heatmap/heatmap.js +135 -23
  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 +43 -2
  159. package/dist/components/select/select.js +150 -17
  160. package/dist/components/slider/slider.d.ts +89 -0
  161. package/dist/components/slider/slider.js +352 -0
  162. package/dist/components/slider/slider.styles.d.ts +1 -0
  163. package/dist/components/slider/slider.styles.js +87 -0
  164. package/dist/components/source-card/source-card.d.ts +92 -0
  165. package/dist/components/source-card/source-card.js +148 -0
  166. package/dist/components/source-card/source-card.styles.d.ts +1 -0
  167. package/dist/components/source-card/source-card.styles.js +69 -0
  168. package/dist/components/source-list/source-list.d.ts +63 -0
  169. package/dist/components/source-list/source-list.js +122 -0
  170. package/dist/components/source-list/source-list.styles.d.ts +1 -0
  171. package/dist/components/source-list/source-list.styles.js +59 -0
  172. package/dist/components/sparkline/sparkline.js +27 -13
  173. package/dist/components/split/split.d.ts +93 -1
  174. package/dist/components/split/split.js +284 -15
  175. package/dist/components/split/split.styles.js +30 -0
  176. package/dist/components/stat/stat.d.ts +6 -1
  177. package/dist/components/stat/stat.js +8 -2
  178. package/dist/components/stream-status/stream-status.d.ts +110 -0
  179. package/dist/components/stream-status/stream-status.js +210 -0
  180. package/dist/components/stream-status/stream-status.styles.d.ts +1 -0
  181. package/dist/components/stream-status/stream-status.styles.js +103 -0
  182. package/dist/components/streaming-text/streaming-text.d.ts +98 -0
  183. package/dist/components/streaming-text/streaming-text.js +209 -0
  184. package/dist/components/streaming-text/streaming-text.styles.d.ts +1 -0
  185. package/dist/components/streaming-text/streaming-text.styles.js +62 -0
  186. package/dist/components/switch/switch.d.ts +58 -0
  187. package/dist/components/switch/switch.js +179 -0
  188. package/dist/components/switch/switch.styles.d.ts +1 -0
  189. package/dist/components/switch/switch.styles.js +75 -0
  190. package/dist/components/table/table.d.ts +73 -4
  191. package/dist/components/table/table.js +169 -20
  192. package/dist/components/table/table.styles.js +6 -1
  193. package/dist/components/tabs/tabs.d.ts +51 -0
  194. package/dist/components/tabs/tabs.js +190 -0
  195. package/dist/components/tabs/tabs.styles.d.ts +1 -0
  196. package/dist/components/tabs/tabs.styles.js +63 -0
  197. package/dist/components/thinking-panel/thinking-panel.d.ts +121 -0
  198. package/dist/components/thinking-panel/thinking-panel.js +235 -0
  199. package/dist/components/thinking-panel/thinking-panel.styles.d.ts +1 -0
  200. package/dist/components/thinking-panel/thinking-panel.styles.js +117 -0
  201. package/dist/components/toast/toast-item.d.ts +1 -0
  202. package/dist/components/toast/toast-item.js +31 -6
  203. package/dist/components/tool-approval-dialog/tool-approval-dialog.d.ts +130 -0
  204. package/dist/components/tool-approval-dialog/tool-approval-dialog.js +403 -0
  205. package/dist/components/tool-approval-dialog/tool-approval-dialog.styles.d.ts +1 -0
  206. package/dist/components/tool-approval-dialog/tool-approval-dialog.styles.js +160 -0
  207. package/dist/components/tool-call-chip/tool-call-chip.d.ts +89 -0
  208. package/dist/components/tool-call-chip/tool-call-chip.js +288 -0
  209. package/dist/components/tool-call-chip/tool-call-chip.styles.d.ts +1 -0
  210. package/dist/components/tool-call-chip/tool-call-chip.styles.js +203 -0
  211. package/dist/components/tool-param-form/tool-param-form.d.ts +145 -0
  212. package/dist/components/tool-param-form/tool-param-form.js +366 -0
  213. package/dist/components/tool-param-form/tool-param-form.styles.d.ts +1 -0
  214. package/dist/components/tool-param-form/tool-param-form.styles.js +75 -0
  215. package/dist/components/tool-result-dialog/tool-result-dialog.d.ts +102 -0
  216. package/dist/components/tool-result-dialog/tool-result-dialog.js +393 -0
  217. package/dist/components/tool-result-dialog/tool-result-dialog.styles.d.ts +1 -0
  218. package/dist/components/tool-result-dialog/tool-result-dialog.styles.js +176 -0
  219. package/dist/components/tool-result-view/registry.d.ts +81 -0
  220. package/dist/components/tool-result-view/registry.js +84 -0
  221. package/dist/components/tool-result-view/tool-result-view.d.ts +55 -0
  222. package/dist/components/tool-result-view/tool-result-view.js +138 -0
  223. package/dist/components/tool-result-view/tool-result-view.styles.d.ts +1 -0
  224. package/dist/components/tool-result-view/tool-result-view.styles.js +13 -0
  225. package/dist/components/tool-select-dialog/tool-select-dialog.d.ts +151 -0
  226. package/dist/components/tool-select-dialog/tool-select-dialog.js +439 -0
  227. package/dist/components/tool-select-dialog/tool-select-dialog.styles.d.ts +1 -0
  228. package/dist/components/tool-select-dialog/tool-select-dialog.styles.js +182 -0
  229. package/dist/components/tree/tree-node.d.ts +11 -2
  230. package/dist/components/tree/tree-node.js +16 -6
  231. package/dist/components/tree/tree.d.ts +8 -2
  232. package/dist/components/tree/tree.js +18 -11
  233. package/dist/components/typing-indicator/typing-indicator.d.ts +60 -0
  234. package/dist/components/typing-indicator/typing-indicator.js +96 -0
  235. package/dist/components/typing-indicator/typing-indicator.styles.d.ts +1 -0
  236. package/dist/components/typing-indicator/typing-indicator.styles.js +132 -0
  237. package/dist/components/virtual-list/virtual-list.d.ts +173 -0
  238. package/dist/components/virtual-list/virtual-list.js +461 -0
  239. package/dist/components/virtual-list/virtual-list.styles.d.ts +1 -0
  240. package/dist/components/virtual-list/virtual-list.styles.js +44 -0
  241. package/dist/components/word-cloud/word-cloud-layout.js +14 -1
  242. package/dist/components/word-cloud/word-cloud.d.ts +6 -0
  243. package/dist/components/word-cloud/word-cloud.js +25 -14
  244. package/dist/components/word-cloud/word-cloud.styles.js +3 -2
  245. package/dist/internal/announcer.d.ts +57 -0
  246. package/dist/internal/announcer.js +71 -0
  247. package/dist/internal/form-associated.d.ts +1 -0
  248. package/dist/internal/form-associated.js +55 -11
  249. package/dist/internal/group-by-recency.d.ts +50 -0
  250. package/dist/internal/group-by-recency.js +80 -0
  251. package/dist/internal/rtl.d.ts +23 -0
  252. package/dist/internal/rtl.js +28 -0
  253. package/dist/internal/scroll-lock.d.ts +10 -5
  254. package/dist/internal/scroll-lock.js +30 -14
  255. package/dist/internal/tokens.styles.js +6 -0
  256. package/dist/lyra.d.ts +130 -31
  257. package/dist/lyra.js +94 -26
  258. package/llms-full.txt +3836 -17
  259. package/llms.txt +16 -1
  260. package/package.json +23 -5
  261. package/dist/components/chart/bar-chart.stories.d.ts +0 -5
  262. package/dist/components/chart/bar-chart.stories.js +0 -20
  263. package/dist/components/chart/box-plot.stories.d.ts +0 -5
  264. package/dist/components/chart/box-plot.stories.js +0 -29
  265. package/dist/components/chart/bubble-chart.stories.d.ts +0 -5
  266. package/dist/components/chart/bubble-chart.stories.js +0 -26
  267. package/dist/components/chart/chart.stories.d.ts +0 -32
  268. package/dist/components/chart/chart.stories.js +0 -141
  269. package/dist/components/chart/doughnut-chart.stories.d.ts +0 -5
  270. package/dist/components/chart/doughnut-chart.stories.js +0 -26
  271. package/dist/components/chart/histogram.stories.d.ts +0 -5
  272. package/dist/components/chart/histogram.stories.js +0 -17
  273. package/dist/components/chart/line-chart.stories.d.ts +0 -22
  274. package/dist/components/chart/line-chart.stories.js +0 -99
  275. package/dist/components/chart/lite-chart.stories.d.ts +0 -14
  276. package/dist/components/chart/lite-chart.stories.js +0 -114
  277. package/dist/components/chart/pie-chart.stories.d.ts +0 -5
  278. package/dist/components/chart/pie-chart.stories.js +0 -26
  279. package/dist/components/chart/polar-area-chart.stories.d.ts +0 -5
  280. package/dist/components/chart/polar-area-chart.stories.js +0 -20
  281. package/dist/components/chart/radar-chart.stories.d.ts +0 -5
  282. package/dist/components/chart/radar-chart.stories.js +0 -20
  283. package/dist/components/chart/scatter-chart.stories.d.ts +0 -5
  284. package/dist/components/chart/scatter-chart.stories.js +0 -31
  285. package/dist/components/combobox/combobox.stories.d.ts +0 -24
  286. package/dist/components/combobox/combobox.stories.js +0 -102
  287. package/dist/components/date-picker/date-input.stories.d.ts +0 -10
  288. package/dist/components/date-picker/date-input.stories.js +0 -48
  289. package/dist/components/date-picker/date-picker.stories.d.ts +0 -10
  290. package/dist/components/date-picker/date-picker.stories.js +0 -25
  291. package/dist/components/empty/empty.stories.d.ts +0 -7
  292. package/dist/components/empty/empty.stories.js +0 -34
  293. package/dist/components/export-button/export-button.stories.d.ts +0 -7
  294. package/dist/components/export-button/export-button.stories.js +0 -37
  295. package/dist/components/file-input/file-input.stories.d.ts +0 -7
  296. package/dist/components/file-input/file-input.stories.js +0 -19
  297. package/dist/components/flag/flag.stories.d.ts +0 -6
  298. package/dist/components/flag/flag.stories.js +0 -31
  299. package/dist/components/gauge/gauge.stories.d.ts +0 -8
  300. package/dist/components/gauge/gauge.stories.js +0 -19
  301. package/dist/components/graph/graph.stories.d.ts +0 -8
  302. package/dist/components/graph/graph.stories.js +0 -68
  303. package/dist/components/heatmap/heatmap.stories.d.ts +0 -27
  304. package/dist/components/heatmap/heatmap.stories.js +0 -179
  305. package/dist/components/map/map.stories.d.ts +0 -15
  306. package/dist/components/map/map.stories.js +0 -135
  307. package/dist/components/playback/playback.stories.d.ts +0 -7
  308. package/dist/components/playback/playback.stories.js +0 -16
  309. package/dist/components/select/select.stories.d.ts +0 -21
  310. package/dist/components/select/select.stories.js +0 -91
  311. package/dist/components/skeleton/skeleton.stories.d.ts +0 -5
  312. package/dist/components/skeleton/skeleton.stories.js +0 -16
  313. package/dist/components/sparkline/sparkline.stories.d.ts +0 -11
  314. package/dist/components/sparkline/sparkline.stories.js +0 -39
  315. package/dist/components/split/split.stories.d.ts +0 -7
  316. package/dist/components/split/split.stories.js +0 -36
  317. package/dist/components/stat/stat.stories.d.ts +0 -9
  318. package/dist/components/stat/stat.stories.js +0 -81
  319. package/dist/components/table/table.stories.d.ts +0 -12
  320. package/dist/components/table/table.stories.js +0 -67
  321. package/dist/components/time-range/time-range.stories.d.ts +0 -8
  322. package/dist/components/time-range/time-range.stories.js +0 -26
  323. package/dist/components/toast/toast.stories.d.ts +0 -8
  324. package/dist/components/toast/toast.stories.js +0 -72
  325. package/dist/components/tree/tree.stories.d.ts +0 -9
  326. package/dist/components/tree/tree.stories.js +0 -52
  327. package/dist/components/widget/widget.stories.d.ts +0 -9
  328. package/dist/components/widget/widget.stories.js +0 -74
  329. package/dist/components/word-cloud/word-cloud.stories.d.ts +0 -10
  330. package/dist/components/word-cloud/word-cloud.stories.js +0 -55
@@ -0,0 +1,171 @@
1
+ import { type TemplateResult, type PropertyValues } from 'lit';
2
+ import { LyraElement } from '../../internal/lyra-element.js';
3
+ /** The rail's effective presentation -- see the class doc for what each renders. */
4
+ export type AppRailMode = 'full' | 'icon-only' | 'mobile';
5
+ /** What can be *assigned* to `mode` -- `'auto'` is a write-only sentinel; see the `mode` accessor doc. */
6
+ export type AppRailModeInput = AppRailMode | 'auto';
7
+ export interface AppRailModeChangeDetail {
8
+ mode: AppRailMode;
9
+ }
10
+ export interface AppRailToggleDetail {
11
+ open: boolean;
12
+ }
13
+ /**
14
+ * Pure breakpoint-to-mode resolver, kept separate from the `matchMedia`
15
+ * wiring below so it's directly unit-testable without resizing a real
16
+ * browser window. `mobileMatches` wins over `iconOnlyMatches` when both are
17
+ * true (the viewport is narrower than both breakpoints at once).
18
+ */
19
+ export declare function computeAppRailMode(iconOnlyMatches: boolean, mobileMatches: boolean): AppRailMode;
20
+ /**
21
+ * `<lyra-app-rail>` — a responsive navigation rail that adapts across three
22
+ * presentations as the *viewport* narrows (not this element's own inline
23
+ * size — see the `mode` accessor doc for why): `'full'` (nav items show
24
+ * icon + label, inline), `'icon-only'` (a narrower inline rail, icons only),
25
+ * and `'mobile'` (hidden behind a toggle button; opening it shows a
26
+ * focus-trapped floating overlay over the page).
27
+ *
28
+ * Breakpoints are viewport-width `matchMedia()` queries against
29
+ * `icon-only-breakpoint`/`mobile-breakpoint`, not a `ResizeObserver` on this
30
+ * element — a nav rail's presentation should track the actual device/window
31
+ * width the way a native OS shell's navigation does, not however much
32
+ * horizontal space a particular layout happens to give it.
33
+ *
34
+ * This is its own standalone focus-trapped overlay implementation for the
35
+ * `'mobile'` state (`role="dialog"`, Escape/backdrop-dismissible,
36
+ * scroll-locking while open) rather than nesting a `<lyra-dialog>` in its
37
+ * shadow template — see `<lyra-dialog>`'s own header comment for why a new
38
+ * overlay-shaped component in this library duplicates that pattern locally
39
+ * instead of composing the previous one. `[part="base"]` (the inline
40
+ * `'full'`/`'icon-only'` presentation) and `[part="panel"]` (the mobile
41
+ * overlay) are the *same* element promoted in place across modes (mirrors
42
+ * `<lyra-widget>`'s fullscreen mode) — never both at once, and never two
43
+ * separate copies of the slotted content, which slot projection can't
44
+ * produce anyway (a light-DOM node is only ever assigned to one `<slot>`).
45
+ * It's a plain `<div>` with an explicit `role="navigation"` rather than a
46
+ * literal `<nav>` tag: a `<nav>`'s implicit role can't be overridden to
47
+ * `role="dialog"` while the overlay is modal without an `aria-allowed-role`
48
+ * violation (verified against axe), whereas an explicit `role="navigation"`
49
+ * on a generic element can be swapped for `role="dialog"` freely.
50
+ *
51
+ * @customElement lyra-app-rail
52
+ * @slot - Nav items — generic slotted content (e.g. `<a>`/`<button>`
53
+ * elements) the consumer builds with its own icon+label structure; this
54
+ * component only lays them out and manages the responsive chrome around
55
+ * them. See the `@example` below for the accessible-name expectation this
56
+ * places on the slotted content in `'icon-only'` mode. While the mobile
57
+ * overlay is open, clicking anywhere inside this slot closes it (see
58
+ * `onNavItemClick`) — every slotted item is assumed to be a navigation
59
+ * trigger, not necessarily a real link, so this fires on any click rather
60
+ * than trying to detect one.
61
+ * @slot header - Logo/brand content, shown above the nav items in every mode.
62
+ * @slot footer - A trailing user/settings trigger, shown below the nav items.
63
+ * @event lyra-mode-change - The effective mode changed, whether from a
64
+ * breakpoint crossing or an explicit `mode` assignment. Not fired for a
65
+ * redundant reassignment to the mode already in effect.
66
+ * `detail: AppRailModeChangeDetail`.
67
+ * @event lyra-toggle - The mobile overlay opened or closed — via the
68
+ * built-in toggle button, Escape, a backdrop click, a nav-item click while
69
+ * open, or a breakpoint/forced mode change leaving `'mobile'` while open.
70
+ * Not fired when a consumer sets `open` directly (mirrors `<lyra-dialog>`'s
71
+ * `open`/`close()` split). `detail: AppRailToggleDetail`.
72
+ * @csspart base - The rail root while inline (`'full'`/`'icon-only'` modes).
73
+ * @csspart header - The wrapper around the `header` slot.
74
+ * @csspart nav - The wrapper around the default (nav items) slot.
75
+ * @csspart footer - The wrapper around the `footer` slot.
76
+ * @csspart toggle - The mobile hamburger/close toggle button. Hidden via
77
+ * CSS outside `'mobile'` mode.
78
+ * @csspart backdrop - The mobile overlay's scrim. Only rendered while open.
79
+ * @csspart panel - The mobile overlay's floating panel — see the class doc
80
+ * for why it's the same element as `base`, never both at once.
81
+ *
82
+ * @example
83
+ * In `'icon-only'` mode, slotted nav items lose their visible text label.
84
+ * Give each one a real accessible name regardless — `aria-label`, visually
85
+ * hidden text, or a `title` — since this component only lays out whatever is
86
+ * slotted and has no way to inspect or fix up a consumer's own markup:
87
+ * ```html
88
+ * <lyra-app-rail>
89
+ * <a href="/inbox" aria-label="Inbox"><svg aria-hidden="true">...</svg><span>Inbox</span></a>
90
+ * </lyra-app-rail>
91
+ * ```
92
+ */
93
+ export declare class LyraAppRail extends LyraElement {
94
+ static styles: import("lit").CSSResultGroup[];
95
+ static properties: {
96
+ mode: {
97
+ reflect: boolean;
98
+ noAccessor: boolean;
99
+ };
100
+ };
101
+ /** Below this viewport width, the rail switches from `'full'` to
102
+ * `'icon-only'`. Any valid CSS length, used directly in a `max-width`
103
+ * media query. */
104
+ iconOnlyBreakpoint: string;
105
+ /** Below this viewport width, the rail switches from `'icon-only'` to
106
+ * `'mobile'`. Should be smaller than `iconOnlyBreakpoint` to produce all
107
+ * three states as the viewport narrows. */
108
+ mobileBreakpoint: string;
109
+ /** Whether the mobile floating overlay is shown. Only meaningful while
110
+ * `mode` is `'mobile'` — the value is preserved (not reset) while another
111
+ * mode is active, but no overlay chrome renders until `mode` is
112
+ * `'mobile'` again. Set this directly, or use the built-in toggle button
113
+ * — there is no separate `show()`/`hide()` pair. */
114
+ open: boolean;
115
+ /** Accessible name for the rail's navigation landmark, and for its dialog
116
+ * role while the mobile overlay is open. */
117
+ label: string;
118
+ private hasHeaderSlot;
119
+ private hasFooterSlot;
120
+ private _mode;
121
+ private forced;
122
+ private iconOnlyMatches;
123
+ private mobileMatches;
124
+ private mqIconOnly?;
125
+ private mqMobile?;
126
+ private overlayActive;
127
+ private justOpened;
128
+ private releaseScrollLock?;
129
+ private lastTrigger?;
130
+ private readonly navId;
131
+ /**
132
+ * The rail's current effective presentation. Always one of the three real
133
+ * modes — never `'auto'` — reflecting either the live breakpoint match or,
134
+ * once forced, whatever was last assigned.
135
+ *
136
+ * Assigning `'full'`/`'icon-only'`/`'mobile'` forces that mode and stops
137
+ * this element from responding to `icon-only-breakpoint`/
138
+ * `mobile-breakpoint` matches. Assigning the sentinel `'auto'` releases
139
+ * the force and immediately re-syncs to the current viewport width,
140
+ * resuming automatic tracking. `'auto'` is a write-only instruction, not a
141
+ * value this getter ever returns — there is no way to observe "currently
142
+ * forced" from the property itself.
143
+ */
144
+ get mode(): AppRailMode;
145
+ set mode(next: AppRailModeInput);
146
+ protected willUpdate(changed: PropertyValues): void;
147
+ protected updated(): void;
148
+ connectedCallback(): void;
149
+ disconnectedCallback(): void;
150
+ private setupMediaQueries;
151
+ private teardownMediaQueries;
152
+ private onIconOnlyChange;
153
+ private onMobileChange;
154
+ private applyComputedMode;
155
+ private setEffectiveMode;
156
+ private setOpen;
157
+ private onToggleClick;
158
+ private onBackdropClick;
159
+ private onNavItemClick;
160
+ private onHeaderSlotChange;
161
+ private onFooterSlotChange;
162
+ private onDocKeyDown;
163
+ private getFocusableElements;
164
+ private getActiveElement;
165
+ render(): TemplateResult;
166
+ }
167
+ declare global {
168
+ interface HTMLElementTagNameMap {
169
+ 'lyra-app-rail': LyraAppRail;
170
+ }
171
+ }
@@ -0,0 +1,483 @@
1
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
2
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
3
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
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
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
6
+ };
7
+ import { html, nothing, svg } from 'lit';
8
+ import { property, state } from 'lit/decorators.js';
9
+ import { LyraElement } from '../../internal/lyra-element.js';
10
+ import { defineElement } from '../../internal/prefix.js';
11
+ import { lockScroll } from '../../internal/scroll-lock.js';
12
+ import { nextId } from '../../internal/a11y.js';
13
+ import { closeIcon } from '../../internal/icons.js';
14
+ import { styles } from './app-rail.styles.js';
15
+ const FOCUSABLE_SELECTOR = 'a[href], button:not([disabled]), input:not([disabled]), select:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex="-1"])';
16
+ // Shadow-piercing so a slotted custom element's real focusable target (e.g.
17
+ // an <input> inside its own shadow root) is found even though the host tag
18
+ // itself doesn't match FOCUSABLE_SELECTOR. Deliberately duplicated from
19
+ // lyra-dialog's identical helper rather than imported/shared -- this
20
+ // component is its own standalone overlay for the mobile state and must not
21
+ // depend on lyra-dialog (see this file's class doc).
22
+ function collectFocusable(el) {
23
+ const result = [];
24
+ if (el.matches(FOCUSABLE_SELECTOR)) {
25
+ result.push(el);
26
+ }
27
+ if (el instanceof HTMLSlotElement) {
28
+ for (const assigned of el.assignedElements({ flatten: true })) {
29
+ result.push(...collectFocusable(assigned));
30
+ }
31
+ return result;
32
+ }
33
+ const container = el.shadowRoot ?? el;
34
+ for (const child of Array.from(container.children)) {
35
+ result.push(...collectFocusable(child));
36
+ }
37
+ return result;
38
+ }
39
+ /** Whether `el` is actually laid out/paintable -- `checkVisibility()` (falling
40
+ * back to `getClientRects().length` where unsupported) correctly follows
41
+ * flattened-tree/slot assignment, unlike `offsetParent`. Mirrors
42
+ * lyra-dialog's/lyra-widget's identical helper. */
43
+ function isRendered(el) {
44
+ return el.checkVisibility ? el.checkVisibility() : el.getClientRects().length > 0;
45
+ }
46
+ // icons.ts has no hamburger/menu glyph and this component must not modify
47
+ // that shared module -- inlined here instead, matching its 24x24
48
+ // viewBox/stroke-width/currentColor convention so it reads as part of the
49
+ // same icon set.
50
+ function menuIcon() {
51
+ return svg `
52
+ <svg
53
+ width="1em"
54
+ height="1em"
55
+ viewBox="0 0 24 24"
56
+ fill="none"
57
+ stroke="currentColor"
58
+ stroke-width="1.75"
59
+ stroke-linecap="round"
60
+ stroke-linejoin="round"
61
+ aria-hidden="true"
62
+ focusable="false"
63
+ >
64
+ <line x1="4" y1="7" x2="20" y2="7"></line>
65
+ <line x1="4" y1="12" x2="20" y2="12"></line>
66
+ <line x1="4" y1="17" x2="20" y2="17"></line>
67
+ </svg>
68
+ `;
69
+ }
70
+ /**
71
+ * Pure breakpoint-to-mode resolver, kept separate from the `matchMedia`
72
+ * wiring below so it's directly unit-testable without resizing a real
73
+ * browser window. `mobileMatches` wins over `iconOnlyMatches` when both are
74
+ * true (the viewport is narrower than both breakpoints at once).
75
+ */
76
+ export function computeAppRailMode(iconOnlyMatches, mobileMatches) {
77
+ if (mobileMatches)
78
+ return 'mobile';
79
+ if (iconOnlyMatches)
80
+ return 'icon-only';
81
+ return 'full';
82
+ }
83
+ /**
84
+ * `<lyra-app-rail>` — a responsive navigation rail that adapts across three
85
+ * presentations as the *viewport* narrows (not this element's own inline
86
+ * size — see the `mode` accessor doc for why): `'full'` (nav items show
87
+ * icon + label, inline), `'icon-only'` (a narrower inline rail, icons only),
88
+ * and `'mobile'` (hidden behind a toggle button; opening it shows a
89
+ * focus-trapped floating overlay over the page).
90
+ *
91
+ * Breakpoints are viewport-width `matchMedia()` queries against
92
+ * `icon-only-breakpoint`/`mobile-breakpoint`, not a `ResizeObserver` on this
93
+ * element — a nav rail's presentation should track the actual device/window
94
+ * width the way a native OS shell's navigation does, not however much
95
+ * horizontal space a particular layout happens to give it.
96
+ *
97
+ * This is its own standalone focus-trapped overlay implementation for the
98
+ * `'mobile'` state (`role="dialog"`, Escape/backdrop-dismissible,
99
+ * scroll-locking while open) rather than nesting a `<lyra-dialog>` in its
100
+ * shadow template — see `<lyra-dialog>`'s own header comment for why a new
101
+ * overlay-shaped component in this library duplicates that pattern locally
102
+ * instead of composing the previous one. `[part="base"]` (the inline
103
+ * `'full'`/`'icon-only'` presentation) and `[part="panel"]` (the mobile
104
+ * overlay) are the *same* element promoted in place across modes (mirrors
105
+ * `<lyra-widget>`'s fullscreen mode) — never both at once, and never two
106
+ * separate copies of the slotted content, which slot projection can't
107
+ * produce anyway (a light-DOM node is only ever assigned to one `<slot>`).
108
+ * It's a plain `<div>` with an explicit `role="navigation"` rather than a
109
+ * literal `<nav>` tag: a `<nav>`'s implicit role can't be overridden to
110
+ * `role="dialog"` while the overlay is modal without an `aria-allowed-role`
111
+ * violation (verified against axe), whereas an explicit `role="navigation"`
112
+ * on a generic element can be swapped for `role="dialog"` freely.
113
+ *
114
+ * @customElement lyra-app-rail
115
+ * @slot - Nav items — generic slotted content (e.g. `<a>`/`<button>`
116
+ * elements) the consumer builds with its own icon+label structure; this
117
+ * component only lays them out and manages the responsive chrome around
118
+ * them. See the `@example` below for the accessible-name expectation this
119
+ * places on the slotted content in `'icon-only'` mode. While the mobile
120
+ * overlay is open, clicking anywhere inside this slot closes it (see
121
+ * `onNavItemClick`) — every slotted item is assumed to be a navigation
122
+ * trigger, not necessarily a real link, so this fires on any click rather
123
+ * than trying to detect one.
124
+ * @slot header - Logo/brand content, shown above the nav items in every mode.
125
+ * @slot footer - A trailing user/settings trigger, shown below the nav items.
126
+ * @event lyra-mode-change - The effective mode changed, whether from a
127
+ * breakpoint crossing or an explicit `mode` assignment. Not fired for a
128
+ * redundant reassignment to the mode already in effect.
129
+ * `detail: AppRailModeChangeDetail`.
130
+ * @event lyra-toggle - The mobile overlay opened or closed — via the
131
+ * built-in toggle button, Escape, a backdrop click, a nav-item click while
132
+ * open, or a breakpoint/forced mode change leaving `'mobile'` while open.
133
+ * Not fired when a consumer sets `open` directly (mirrors `<lyra-dialog>`'s
134
+ * `open`/`close()` split). `detail: AppRailToggleDetail`.
135
+ * @csspart base - The rail root while inline (`'full'`/`'icon-only'` modes).
136
+ * @csspart header - The wrapper around the `header` slot.
137
+ * @csspart nav - The wrapper around the default (nav items) slot.
138
+ * @csspart footer - The wrapper around the `footer` slot.
139
+ * @csspart toggle - The mobile hamburger/close toggle button. Hidden via
140
+ * CSS outside `'mobile'` mode.
141
+ * @csspart backdrop - The mobile overlay's scrim. Only rendered while open.
142
+ * @csspart panel - The mobile overlay's floating panel — see the class doc
143
+ * for why it's the same element as `base`, never both at once.
144
+ *
145
+ * @example
146
+ * In `'icon-only'` mode, slotted nav items lose their visible text label.
147
+ * Give each one a real accessible name regardless — `aria-label`, visually
148
+ * hidden text, or a `title` — since this component only lays out whatever is
149
+ * slotted and has no way to inspect or fix up a consumer's own markup:
150
+ * ```html
151
+ * <lyra-app-rail>
152
+ * <a href="/inbox" aria-label="Inbox"><svg aria-hidden="true">...</svg><span>Inbox</span></a>
153
+ * </lyra-app-rail>
154
+ * ```
155
+ */
156
+ export class LyraAppRail extends LyraElement {
157
+ constructor() {
158
+ super(...arguments);
159
+ /** Below this viewport width, the rail switches from `'full'` to
160
+ * `'icon-only'`. Any valid CSS length, used directly in a `max-width`
161
+ * media query. */
162
+ this.iconOnlyBreakpoint = '960px';
163
+ /** Below this viewport width, the rail switches from `'icon-only'` to
164
+ * `'mobile'`. Should be smaller than `iconOnlyBreakpoint` to produce all
165
+ * three states as the viewport narrows. */
166
+ this.mobileBreakpoint = '600px';
167
+ /** Whether the mobile floating overlay is shown. Only meaningful while
168
+ * `mode` is `'mobile'` — the value is preserved (not reset) while another
169
+ * mode is active, but no overlay chrome renders until `mode` is
170
+ * `'mobile'` again. Set this directly, or use the built-in toggle button
171
+ * — there is no separate `show()`/`hide()` pair. */
172
+ this.open = false;
173
+ /** Accessible name for the rail's navigation landmark, and for its dialog
174
+ * role while the mobile overlay is open. */
175
+ this.label = 'Navigation';
176
+ this.hasHeaderSlot = false;
177
+ this.hasFooterSlot = false;
178
+ this._mode = 'full';
179
+ // Whether matchMedia changes are currently ignored because a consumer
180
+ // forced a specific mode -- see the `mode` accessor doc.
181
+ this.forced = false;
182
+ this.iconOnlyMatches = false;
183
+ this.mobileMatches = false;
184
+ // Derived from mode === 'mobile' && open -- tracked as its own field
185
+ // (rather than recomputed inline everywhere) so willUpdate can detect the
186
+ // specific false->true/true->false transition regardless of which of the
187
+ // two source properties changed.
188
+ this.overlayActive = false;
189
+ this.justOpened = false;
190
+ this.navId = nextId('app-rail-nav');
191
+ // Split into two single-query listeners (rather than one shared handler
192
+ // reading both live MediaQueryLists) specifically so a test can invoke
193
+ // either directly with a fabricated { matches } event -- exercising the
194
+ // full breakpoint-response wiring without needing to actually resize the
195
+ // browser window a test runs in.
196
+ this.onIconOnlyChange = (e) => {
197
+ this.iconOnlyMatches = e.matches;
198
+ if (!this.forced)
199
+ this.applyComputedMode();
200
+ };
201
+ this.onMobileChange = (e) => {
202
+ this.mobileMatches = e.matches;
203
+ if (!this.forced)
204
+ this.applyComputedMode();
205
+ };
206
+ this.onToggleClick = (e) => {
207
+ this.lastTrigger = e.currentTarget;
208
+ this.setOpen(!this.open);
209
+ };
210
+ this.onBackdropClick = () => {
211
+ this.setOpen(false);
212
+ };
213
+ // See the default-slot @slot doc -- any click inside the nav items while
214
+ // the overlay is open closes it, without trying to distinguish a real
215
+ // navigation trigger from incidental slotted content.
216
+ this.onNavItemClick = () => {
217
+ if (this.overlayActive)
218
+ this.setOpen(false);
219
+ };
220
+ this.onHeaderSlotChange = (e) => {
221
+ this.hasHeaderSlot = e.target.assignedElements({ flatten: true }).length > 0;
222
+ };
223
+ this.onFooterSlotChange = (e) => {
224
+ this.hasFooterSlot = e.target.assignedElements({ flatten: true }).length > 0;
225
+ };
226
+ this.onDocKeyDown = (e) => {
227
+ if (!this.overlayActive)
228
+ return;
229
+ if (e.key === 'Escape') {
230
+ e.preventDefault();
231
+ this.setOpen(false);
232
+ return;
233
+ }
234
+ if (e.key !== 'Tab')
235
+ return;
236
+ const focusable = this.getFocusableElements();
237
+ if (focusable.length === 0) {
238
+ e.preventDefault();
239
+ return;
240
+ }
241
+ const first = focusable[0];
242
+ const last = focusable[focusable.length - 1];
243
+ const active = this.getActiveElement();
244
+ if (e.shiftKey && active === first) {
245
+ e.preventDefault();
246
+ last.focus();
247
+ }
248
+ else if (!e.shiftKey && active === last) {
249
+ e.preventDefault();
250
+ first.focus();
251
+ }
252
+ };
253
+ }
254
+ static { this.styles = [LyraElement.styles, styles]; }
255
+ // `mode` needs a custom accessor (force/auto semantics below) rather than
256
+ // the usual @property()-generated one -- registered here, alongside the
257
+ // decorator-declared properties below, the same way lyra-model-select's
258
+ // form-associated `value` accessor is. `reflect: true` still applies:
259
+ // LitElement's update loop reflects any `reflect`-flagged property to its
260
+ // attribute generically by reading `this[name]` after render, regardless
261
+ // of whether that property has a custom or auto-generated accessor.
262
+ static { this.properties = {
263
+ mode: { reflect: true, noAccessor: true },
264
+ }; }
265
+ /**
266
+ * The rail's current effective presentation. Always one of the three real
267
+ * modes — never `'auto'` — reflecting either the live breakpoint match or,
268
+ * once forced, whatever was last assigned.
269
+ *
270
+ * Assigning `'full'`/`'icon-only'`/`'mobile'` forces that mode and stops
271
+ * this element from responding to `icon-only-breakpoint`/
272
+ * `mobile-breakpoint` matches. Assigning the sentinel `'auto'` releases
273
+ * the force and immediately re-syncs to the current viewport width,
274
+ * resuming automatic tracking. `'auto'` is a write-only instruction, not a
275
+ * value this getter ever returns — there is no way to observe "currently
276
+ * forced" from the property itself.
277
+ */
278
+ get mode() {
279
+ return this._mode;
280
+ }
281
+ set mode(next) {
282
+ if (next === 'auto') {
283
+ if (!this.forced)
284
+ return;
285
+ this.forced = false;
286
+ this.applyComputedMode();
287
+ return;
288
+ }
289
+ if (next !== 'full' && next !== 'icon-only' && next !== 'mobile')
290
+ return;
291
+ this.forced = true;
292
+ this.setEffectiveMode(next);
293
+ }
294
+ willUpdate(changed) {
295
+ if (!this.hasUpdated) {
296
+ this.hasHeaderSlot = Array.from(this.children).some((el) => el.getAttribute('slot') === 'header');
297
+ this.hasFooterSlot = Array.from(this.children).some((el) => el.getAttribute('slot') === 'footer');
298
+ }
299
+ if (this.hasUpdated && (changed.has('iconOnlyBreakpoint') || changed.has('mobileBreakpoint'))) {
300
+ this.teardownMediaQueries();
301
+ this.setupMediaQueries();
302
+ }
303
+ if (changed.has('open') || changed.has('mode')) {
304
+ const next = this._mode === 'mobile' && this.open;
305
+ if (next !== this.overlayActive) {
306
+ this.overlayActive = next;
307
+ if (next) {
308
+ this.justOpened = true;
309
+ this.releaseScrollLock = lockScroll();
310
+ document.addEventListener('keydown', this.onDocKeyDown);
311
+ }
312
+ else {
313
+ this.releaseScrollLock?.();
314
+ this.releaseScrollLock = undefined;
315
+ document.removeEventListener('keydown', this.onDocKeyDown);
316
+ }
317
+ }
318
+ }
319
+ }
320
+ // Runs after render (not willUpdate) so [part="panel"] and its slotted
321
+ // content have already landed in the DOM before the focus call below can
322
+ // rely on them -- mirrors lyra-dialog's/lyra-widget's identical ordering
323
+ // rationale.
324
+ updated() {
325
+ if (this.justOpened) {
326
+ this.justOpened = false;
327
+ const first = this.getFocusableElements()[0];
328
+ if (first) {
329
+ first.focus();
330
+ }
331
+ else {
332
+ this.shadowRoot?.querySelector('[part="panel"]')?.focus();
333
+ }
334
+ }
335
+ }
336
+ connectedCallback() {
337
+ super.connectedCallback();
338
+ this.setupMediaQueries();
339
+ // A reconnect (e.g. a drag-and-drop reparent keeping this same element
340
+ // instance) fires disconnectedCallback then connectedCallback
341
+ // synchronously with no update in between, so willUpdate never reruns to
342
+ // notice the overlay is still active -- restore the scroll lock/trap it
343
+ // dropped. Mirrors lyra-dialog's/lyra-widget's identical reconnect handling.
344
+ if (this.hasUpdated && this.overlayActive && !this.releaseScrollLock) {
345
+ this.releaseScrollLock = lockScroll();
346
+ document.addEventListener('keydown', this.onDocKeyDown);
347
+ }
348
+ }
349
+ disconnectedCallback() {
350
+ super.disconnectedCallback();
351
+ this.teardownMediaQueries();
352
+ this.releaseScrollLock?.();
353
+ this.releaseScrollLock = undefined;
354
+ document.removeEventListener('keydown', this.onDocKeyDown);
355
+ }
356
+ setupMediaQueries() {
357
+ if (typeof window === 'undefined' || !window.matchMedia)
358
+ return;
359
+ this.mqIconOnly = window.matchMedia(`(max-width: ${this.iconOnlyBreakpoint})`);
360
+ this.mqMobile = window.matchMedia(`(max-width: ${this.mobileBreakpoint})`);
361
+ this.mqIconOnly.addEventListener('change', this.onIconOnlyChange);
362
+ this.mqMobile.addEventListener('change', this.onMobileChange);
363
+ this.iconOnlyMatches = this.mqIconOnly.matches;
364
+ this.mobileMatches = this.mqMobile.matches;
365
+ if (!this.forced)
366
+ this.applyComputedMode();
367
+ }
368
+ teardownMediaQueries() {
369
+ this.mqIconOnly?.removeEventListener('change', this.onIconOnlyChange);
370
+ this.mqMobile?.removeEventListener('change', this.onMobileChange);
371
+ this.mqIconOnly = undefined;
372
+ this.mqMobile = undefined;
373
+ }
374
+ applyComputedMode() {
375
+ this.setEffectiveMode(computeAppRailMode(this.iconOnlyMatches, this.mobileMatches));
376
+ }
377
+ setEffectiveMode(next) {
378
+ if (this._mode === next)
379
+ return;
380
+ const old = this._mode;
381
+ this._mode = next;
382
+ this.requestUpdate('mode', old);
383
+ this.emit('lyra-mode-change', { mode: next });
384
+ // 'open' is only meaningful in 'mobile' mode -- leaving it while open
385
+ // closes the overlay as a side effect (through setOpen, so it still
386
+ // emits lyra-toggle and releases the scroll lock/focus trap normally)
387
+ // rather than leaving a now-invisible overlay primed to reappear the
388
+ // next time mode returns to 'mobile'.
389
+ if (next !== 'mobile' && this.open)
390
+ this.setOpen(false);
391
+ }
392
+ setOpen(next) {
393
+ if (this.open === next)
394
+ return;
395
+ this.open = next;
396
+ this.emit('lyra-toggle', { open: next });
397
+ if (!next)
398
+ this.lastTrigger?.focus();
399
+ }
400
+ // Bounds Tab/Shift+Tab to the panel while the overlay is open. Order
401
+ // follows the header slot, then the nav (default) slot, then the footer
402
+ // slot -- the same order the flattened tree already tabs through. The
403
+ // toggle button itself is deliberately excluded, same as a dialog trigger
404
+ // living outside its own trap.
405
+ getFocusableElements() {
406
+ const root = this.shadowRoot;
407
+ if (!root)
408
+ return [];
409
+ const fromSlot = (selector) => {
410
+ const slot = root.querySelector(selector);
411
+ return slot ? slot.assignedElements({ flatten: true }).flatMap(collectFocusable) : [];
412
+ };
413
+ return [
414
+ ...fromSlot('slot[name="header"]'),
415
+ ...fromSlot('slot:not([name])'),
416
+ ...fromSlot('slot[name="footer"]'),
417
+ ].filter(isRendered);
418
+ }
419
+ getActiveElement() {
420
+ let active = document.activeElement;
421
+ while (active) {
422
+ const inner = active.shadowRoot?.activeElement ?? null;
423
+ if (!inner)
424
+ break;
425
+ active = inner;
426
+ }
427
+ return active;
428
+ }
429
+ render() {
430
+ const mobile = this._mode === 'mobile';
431
+ return html `
432
+ <button
433
+ part="toggle"
434
+ type="button"
435
+ aria-expanded=${this.open ? 'true' : 'false'}
436
+ aria-controls=${this.navId}
437
+ aria-label=${this.open ? 'Close navigation' : 'Open navigation'}
438
+ @click=${this.onToggleClick}
439
+ >
440
+ ${this.open ? closeIcon() : menuIcon()}
441
+ </button>
442
+ ${mobile && this.open ? html `<div part="backdrop" @click=${this.onBackdropClick}></div>` : nothing}
443
+ <div
444
+ id=${this.navId}
445
+ part=${mobile ? 'panel' : 'base'}
446
+ aria-label=${this.label}
447
+ role=${this.overlayActive ? 'dialog' : 'navigation'}
448
+ aria-modal=${this.overlayActive ? 'true' : nothing}
449
+ tabindex=${this.overlayActive ? '-1' : nothing}
450
+ ?inert=${mobile && !this.open}
451
+ >
452
+ <div part="header" ?hidden=${!this.hasHeaderSlot}>
453
+ <slot name="header" @slotchange=${this.onHeaderSlotChange}></slot>
454
+ </div>
455
+ <div part="nav">
456
+ <slot @click=${this.onNavItemClick}></slot>
457
+ </div>
458
+ <div part="footer" ?hidden=${!this.hasFooterSlot}>
459
+ <slot name="footer" @slotchange=${this.onFooterSlotChange}></slot>
460
+ </div>
461
+ </div>
462
+ `;
463
+ }
464
+ }
465
+ __decorate([
466
+ property({ attribute: 'icon-only-breakpoint' })
467
+ ], LyraAppRail.prototype, "iconOnlyBreakpoint", void 0);
468
+ __decorate([
469
+ property({ attribute: 'mobile-breakpoint' })
470
+ ], LyraAppRail.prototype, "mobileBreakpoint", void 0);
471
+ __decorate([
472
+ property({ type: Boolean, reflect: true })
473
+ ], LyraAppRail.prototype, "open", void 0);
474
+ __decorate([
475
+ property()
476
+ ], LyraAppRail.prototype, "label", void 0);
477
+ __decorate([
478
+ state()
479
+ ], LyraAppRail.prototype, "hasHeaderSlot", void 0);
480
+ __decorate([
481
+ state()
482
+ ], LyraAppRail.prototype, "hasFooterSlot", void 0);
483
+ defineElement('app-rail', LyraAppRail);
@@ -0,0 +1 @@
1
+ export declare const styles: import("lit").CSSResult;