@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,475 @@
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 } 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 { place } from '../../internal/positioner.js';
12
+ import { nextId } from '../../internal/a11y.js';
13
+ import { styles } from './mention-popover.styles.js';
14
+ function defaultFilter(item, query) {
15
+ return item.label.toLowerCase().includes(query) || (item.description ?? '').toLowerCase().includes(query);
16
+ }
17
+ function isTextControl(el) {
18
+ if (el instanceof HTMLTextAreaElement)
19
+ return true;
20
+ return el instanceof HTMLInputElement && (el.type === 'text' || el.type === 'search');
21
+ }
22
+ // Computed-style properties that affect text layout/measurement, copied
23
+ // verbatim from the real control onto the hidden mirror in caretClientRect()
24
+ // below so wrapped-line offsets inside the mirror land exactly where they do
25
+ // in the real control. Deliberately excludes paint-only properties (color,
26
+ // background, etc.) -- those never affect where text/lines break.
27
+ const MIRROR_CSS_PROPS = [
28
+ 'box-sizing',
29
+ 'width',
30
+ 'padding-top',
31
+ 'padding-right',
32
+ 'padding-bottom',
33
+ 'padding-left',
34
+ 'border-top-width',
35
+ 'border-right-width',
36
+ 'border-bottom-width',
37
+ 'border-left-width',
38
+ 'border-style',
39
+ 'font-family',
40
+ 'font-size',
41
+ 'font-style',
42
+ 'font-variant',
43
+ 'font-weight',
44
+ 'font-stretch',
45
+ 'letter-spacing',
46
+ 'word-spacing',
47
+ 'line-height',
48
+ 'text-align',
49
+ 'text-indent',
50
+ 'text-transform',
51
+ 'tab-size',
52
+ 'direction',
53
+ ];
54
+ /**
55
+ * Measures exactly where `el`'s caret (`selectionStart`) currently paints,
56
+ * in viewport coordinates, via the classic "hidden mirror div + marker span"
57
+ * technique: there is no native DOM API for this -- `getBoundingClientRect()`
58
+ * on a text control only reports the control's own box, never a cursor
59
+ * position inside it. A throwaway off-screen clone of `el` is built (font/
60
+ * box metrics copied via `MIRROR_CSS_PROPS` above), a marker `<span>` is
61
+ * inserted at the caret's text offset, its rect is measured relative to the
62
+ * mirror's own rect (so the absolute off-screen position of the mirror
63
+ * itself never matters), and that local offset is applied to `el`'s real
64
+ * `getBoundingClientRect()` minus its current scroll offset. Returns `null`
65
+ * for a zero-size (e.g. `display: none`) control.
66
+ */
67
+ function caretClientRect(el) {
68
+ const elRect = el.getBoundingClientRect();
69
+ if (elRect.width === 0 || elRect.height === 0)
70
+ return null;
71
+ const computed = window.getComputedStyle(el);
72
+ const mirror = document.createElement('div');
73
+ mirror.style.position = 'absolute';
74
+ mirror.style.visibility = 'hidden';
75
+ mirror.style.top = '0';
76
+ mirror.style.left = '-9999px';
77
+ // <textarea> soft-wraps; a single-line <input> never does -- mismatching
78
+ // this makes the mirror wrap where the real control wouldn't (or vice
79
+ // versa), throwing off every offset past the first line/character run.
80
+ mirror.style.whiteSpace = el instanceof HTMLTextAreaElement ? 'pre-wrap' : 'pre';
81
+ mirror.style.overflowWrap = 'break-word';
82
+ for (const prop of MIRROR_CSS_PROPS) {
83
+ mirror.style.setProperty(prop, computed.getPropertyValue(prop));
84
+ }
85
+ const index = el.selectionStart ?? el.value.length;
86
+ mirror.append(document.createTextNode(el.value.slice(0, index)));
87
+ const marker = document.createElement('span');
88
+ // A marker with no content at all collapses to zero width -- a hair of
89
+ // content keeps it reliably measurable even for a caret sitting at the
90
+ // very end of the value.
91
+ marker.textContent = el.value.slice(index) || '​';
92
+ mirror.appendChild(marker);
93
+ document.body.appendChild(mirror);
94
+ const mirrorRect = mirror.getBoundingClientRect();
95
+ const markerRect = marker.getBoundingClientRect();
96
+ mirror.remove();
97
+ const lineHeight = parseFloat(computed.lineHeight) || markerRect.height || 16;
98
+ return new DOMRect(elRect.left + (markerRect.left - mirrorRect.left) - el.scrollLeft, elRect.top + (markerRect.top - mirrorRect.top) - el.scrollTop, 1, lineHeight);
99
+ }
100
+ /**
101
+ * `<lyra-mention-popover>` — a caret-anchored, keyboard-navigable popover for
102
+ * `@`-mention and `/`-slash-command autocomplete inside a plain-text
103
+ * `<textarea>`/`<input>` the host owns (e.g. `<lyra-chat-composer>`'s own
104
+ * textarea, though this component has no dependency on that or any other
105
+ * specific input). It never takes DOM focus itself — the same "focus stays
106
+ * put, `aria-activedescendant` conveys the active row" pattern
107
+ * `<lyra-select>`/`<lyra-combobox>` use for their own listbox — so a host
108
+ * must apply `aria-activedescendant` to its *own* input element, pointing at
109
+ * whatever `activeDescendantId` currently returns.
110
+ *
111
+ * Integration contract (entirely the host's responsibility — this component
112
+ * never inspects the text control's value or listens to it directly):
113
+ * 1. Detect a mention/command trigger (e.g. `@`/`/` at the start of a word)
114
+ * in the host's own `input` handling.
115
+ * 2. Set `anchor` (the `<textarea>`/`<input>` itself, or any element for
116
+ * plain whole-element anchoring — see "Positioning" below), `items`, and
117
+ * `query` (the text typed since the trigger character), then flip
118
+ * `open = true`.
119
+ * 3. Forward every `keydown` the input receives, while `open`, through
120
+ * `handleKeyDown()` — it returns `true` when it consumed the key (so the
121
+ * host's own handler should stop, e.g. skip submitting the message on an
122
+ * Enter that actually picked a mention) and `false` otherwise.
123
+ * 4. Set `open = false` whenever the query stops looking like an active
124
+ * mention context (a space typed, the trigger character deleted, the
125
+ * input blurred, …) — `lyra-mention-close` fires automatically from that
126
+ * (see below), there is no separate "tell it to close" call needed.
127
+ * 5. Keep the host's own input's `aria-activedescendant` (and, optionally,
128
+ * `aria-controls`, via `listboxId`) in sync with `activeDescendantId` —
129
+ * this component has no DOM of the host's to attach that to itself.
130
+ *
131
+ * Positioning: when `anchor` is a plain `<textarea>` or single-line text
132
+ * `<input>`, this component measures exactly where the caret currently
133
+ * paints (`caretClientRect()`, the standard hidden-mirror-element technique
134
+ * — see that function's own doc) and positions against that single point
135
+ * with `internal/positioner.js`'s `place()`, so the popup tracks the caret
136
+ * rather than sitting under the whole textarea. Any other `anchor` element
137
+ * (or a text control this component fails to measure, e.g. one with
138
+ * `display: none`) falls back to `place(anchor, popup)` against the whole
139
+ * element — the same whole-element anchoring `<lyra-combobox>`/
140
+ * `<lyra-select>` use for their own popups. Re-measures on every `anchor`/
141
+ * `query` change while open (a keystroke moves the caret, so a fresh `query`
142
+ * is the proxy for "the caret may have moved"); a caret that moves for a
143
+ * reason other than typing (e.g. a mouse click elsewhere in the text while
144
+ * the popover happens to still be open) is not separately tracked — the
145
+ * host can force a re-measure by toggling `open` or reassigning `anchor`.
146
+ *
147
+ * Filtering happens internally against `items` (mirroring `<lyra-combobox>`'s
148
+ * filter-predicate convention via `filter`, rather than requiring the host to
149
+ * pre-filter): the default predicate is a case-insensitive substring match
150
+ * against `label`/`description`, overridable via `filter`.
151
+ *
152
+ * There is no persisted "selection" the way a real listbox has one — a
153
+ * mention is either committed (closing the popover) or the popover is
154
+ * dismissed with nothing chosen — so `aria-selected="true"` marks whichever
155
+ * row is currently *active* (what Enter/Tab would commit right now), per the
156
+ * WAI-ARIA combobox-with-list-autocomplete pattern, rather than tracking a
157
+ * separate persisted value the way `<lyra-combobox>`'s own `aria-selected`
158
+ * does.
159
+ *
160
+ * @example
161
+ * ```html
162
+ * <textarea id="composer"></textarea>
163
+ * <lyra-mention-popover id="mentions"></lyra-mention-popover>
164
+ * <script>
165
+ * const textarea = document.getElementById('composer');
166
+ * const popover = document.getElementById('mentions');
167
+ *
168
+ * textarea.addEventListener('keydown', (e) => {
169
+ * if (popover.open && popover.handleKeyDown(e)) return; // consumed
170
+ * // ...the host's own Enter-to-send handling, etc.
171
+ * });
172
+ * textarea.addEventListener('input', () => {
173
+ * // host's own '@'/'/' + query detection, then:
174
+ * popover.anchor = textarea;
175
+ * popover.items = candidates;
176
+ * popover.query = detectedQuery;
177
+ * popover.open = detectedQuery !== null;
178
+ * });
179
+ * textarea.addEventListener('blur', () => (popover.open = false));
180
+ *
181
+ * popover.addEventListener('lyra-mention-select', (e) => {
182
+ * // splice `${e.detail.label}` into the textarea at the trigger offset
183
+ * });
184
+ * popover.addEventListener('lyra-mention-close', () => {
185
+ * textarea.removeAttribute('aria-activedescendant');
186
+ * });
187
+ *
188
+ * // Whenever the popover re-renders its active row (e.g. after every
189
+ * // ArrowUp/ArrowDown handleKeyDown() call above), sync the host's own
190
+ * // input so assistive tech announces the current candidate:
191
+ * const syncActiveDescendant = () => {
192
+ * if (popover.open && popover.activeDescendantId) {
193
+ * textarea.setAttribute('aria-activedescendant', popover.activeDescendantId);
194
+ * } else {
195
+ * textarea.removeAttribute('aria-activedescendant');
196
+ * }
197
+ * };
198
+ * </script>
199
+ * ```
200
+ *
201
+ * @customElement lyra-mention-popover
202
+ * @event lyra-mention-select - An item was committed (Enter/Tab/click). `detail: { id, label }`.
203
+ * @event lyra-mention-close - The popover was dismissed with no selection — Escape, or `open`
204
+ * transitioning to `false` by any other means (a direct host assignment included). Never fires
205
+ * for a close that followed a `lyra-mention-select` commit.
206
+ * @csspart listbox - The popover's root element (`role="listbox"`).
207
+ * @csspart option - A candidate row (`role="option"`).
208
+ * @csspart option-icon - A row's leading icon glyph, when `icon` is set.
209
+ * @csspart option-label - Wrapper around a row's label/description.
210
+ * @csspart option-description - A row's optional secondary line, when `description` is set.
211
+ * @csspart empty - The "no matches" message, shown when `items`/`query` produce zero rows.
212
+ */
213
+ export class LyraMentionPopover extends LyraElement {
214
+ constructor() {
215
+ super(...arguments);
216
+ /** The full candidate set, pre-`query`-filtering. */
217
+ this.items = [];
218
+ /** The text typed since the trigger character (`@`/`/`/…) — drives the
219
+ * built-in internal filtering (see `filter` to override it). */
220
+ this.query = '';
221
+ /** Whether the popover is shown. */
222
+ this.open = false;
223
+ /** Overrides the built-in case-insensitive label/description substring match. */
224
+ this.filter = null;
225
+ /** Message shown when `items` (post-`query`-filtering) is empty. */
226
+ this.emptyText = 'No matches';
227
+ /** Accessible name for the `role="listbox"` popup. */
228
+ this.label = 'Suggestions';
229
+ // Highlighted row, opens pre-highlighted on the top match (index 0) so a
230
+ // bare Enter right after opening commits immediately -- the same "first
231
+ // result is pre-selected" UX every mainstream @-mention/slash-command
232
+ // picker (Slack, GitHub, Notion, …) uses, unlike lyra-combobox's own
233
+ // listbox which opens with nothing highlighted (-1) since a combobox's
234
+ // typed text can itself already equal a full, deliberately-typed value.
235
+ this.activeIndex = 0;
236
+ this._listboxId = nextId('mention-popover-listbox');
237
+ // A synthetic zero-size point element, positioned at the measured caret
238
+ // rect and handed to place() in caret-precision mode -- place()/Floating
239
+ // UI only understand a real HTMLElement anchor, so caret positioning goes
240
+ // through this rather than a bespoke non-place()-based positioning path.
241
+ this.virtualAnchor = null;
242
+ this._isFirstUpdate = true;
243
+ // Set by commit() immediately before it flips `open` false, so updated()'s
244
+ // open-transition handling below (which otherwise fires lyra-mention-close
245
+ // on every true->false transition, matching lyra-combobox's/lyra-select's
246
+ // identical lyra-hide handling) can tell a successful-selection close
247
+ // apart from every other close and skip the event for that one case.
248
+ this._suppressCloseEvent = false;
249
+ // Delegated onto [part="listbox"] rather than one closure pair allocated
250
+ // per row per render -- same pattern as lyra-combobox's/lyra-select's
251
+ // identical onListboxMouseDown/onListboxClick.
252
+ this.onListboxMouseDown = (e) => {
253
+ // Focus must never leave the host's own text control -- preventing the
254
+ // default here is what stops a plain (non-focusable) row click from
255
+ // blurring it.
256
+ if (e.target.closest('[part="option"]'))
257
+ e.preventDefault();
258
+ };
259
+ this.onListboxClick = (e) => {
260
+ const optionEl = e.target.closest('[part="option"]');
261
+ const id = optionEl?.dataset.id;
262
+ if (id === undefined)
263
+ return;
264
+ const item = this.filteredItems.find((i) => i.id === id);
265
+ if (item)
266
+ this.commit(item);
267
+ };
268
+ }
269
+ static { this.styles = [LyraElement.styles, styles]; }
270
+ willUpdate(changed) {
271
+ this._isFirstUpdate = !this.hasUpdated;
272
+ // A fresh query or candidate set re-highlights the top match, mirroring
273
+ // how a filtering text field's own suggestion list re-anchors to the
274
+ // first result on every keystroke rather than preserving a highlight
275
+ // that may no longer even be in the filtered set.
276
+ if (changed.has('query') || changed.has('items'))
277
+ this.activeIndex = 0;
278
+ }
279
+ updated(changed) {
280
+ if (changed.has('open')) {
281
+ if (this.open) {
282
+ this.reposition();
283
+ }
284
+ else {
285
+ this.cleanup?.();
286
+ this.cleanup = undefined;
287
+ this.virtualAnchor?.remove();
288
+ // Don't fire for markup that's simply rendering open="false" for the
289
+ // first time, and don't fire for the commit() path (see
290
+ // _suppressCloseEvent's own doc above) -- every other true->false
291
+ // transition (Escape, or a direct host assignment) does fire.
292
+ if (!this._isFirstUpdate && !this._suppressCloseEvent)
293
+ this.emit('lyra-mention-close');
294
+ this._suppressCloseEvent = false;
295
+ }
296
+ }
297
+ else if (this.open && (changed.has('anchor') || changed.has('query'))) {
298
+ this.reposition();
299
+ }
300
+ }
301
+ disconnectedCallback() {
302
+ super.disconnectedCallback();
303
+ this.cleanup?.();
304
+ this.cleanup = undefined;
305
+ this.virtualAnchor?.remove();
306
+ }
307
+ /** The current candidate set: `items` filtered by `query` via `filter`
308
+ * (or the built-in default). Empty `query` returns `items` unfiltered. */
309
+ get filteredItems() {
310
+ const q = this.query.trim().toLowerCase();
311
+ if (!q)
312
+ return this.items;
313
+ const fn = this.filter ?? defaultFilter;
314
+ return this.items.filter((item) => fn(item, q));
315
+ }
316
+ /** The `id` of the currently-highlighted row for the host to apply as its
317
+ * own input's `aria-activedescendant` — `null` while closed or when
318
+ * `filteredItems` is empty (nothing to point at). See the class doc's
319
+ * `@example` for the full wiring. */
320
+ get activeDescendantId() {
321
+ if (!this.open)
322
+ return null;
323
+ const idx = this.clampedIndex(this.filteredItems);
324
+ return idx >= 0 ? this.rowId(idx) : null;
325
+ }
326
+ /** The `id` of the `role="listbox"` popup, for a host that also wants to
327
+ * wire `aria-controls` on its own input (screen-reader support for
328
+ * `aria-controls` is inconsistent, so `activeDescendantId` remains the
329
+ * load-bearing piece; this is a supplementary nicety). */
330
+ get listboxId() {
331
+ return this._listboxId;
332
+ }
333
+ /**
334
+ * The host's own text-control keydown handler calls this while the
335
+ * popover is open. Returns `true` when the key was intercepted
336
+ * (`preventDefault()` already called) and the host should not also act on
337
+ * it; `false` when the host's normal handling should proceed untouched.
338
+ */
339
+ handleKeyDown(e) {
340
+ if (!this.open)
341
+ return false;
342
+ const rows = this.filteredItems;
343
+ switch (e.key) {
344
+ case 'ArrowDown':
345
+ e.preventDefault();
346
+ if (rows.length)
347
+ this.activeIndex = Math.min(rows.length - 1, this.clampedIndex(rows) + 1);
348
+ return true;
349
+ case 'ArrowUp':
350
+ e.preventDefault();
351
+ if (rows.length)
352
+ this.activeIndex = Math.max(0, this.clampedIndex(rows) - 1);
353
+ return true;
354
+ case 'Enter':
355
+ case 'Tab': {
356
+ const idx = this.clampedIndex(rows);
357
+ // Nothing to commit -- let Enter submit / Tab move focus normally,
358
+ // matching a native combobox with no highlighted suggestion.
359
+ if (idx < 0)
360
+ return false;
361
+ e.preventDefault();
362
+ this.commit(rows[idx]);
363
+ return true;
364
+ }
365
+ case 'Escape':
366
+ e.preventDefault();
367
+ this.open = false;
368
+ return true;
369
+ default:
370
+ return false;
371
+ }
372
+ }
373
+ clampedIndex(rows) {
374
+ if (!rows.length)
375
+ return -1;
376
+ return Math.min(Math.max(this.activeIndex, 0), rows.length - 1);
377
+ }
378
+ rowId(index) {
379
+ return `${this._listboxId}-opt-${index}`;
380
+ }
381
+ commit(item) {
382
+ this.emit('lyra-mention-select', { id: item.id, label: item.label });
383
+ this._suppressCloseEvent = true;
384
+ this.open = false;
385
+ }
386
+ reposition() {
387
+ this.cleanup?.();
388
+ this.cleanup = undefined;
389
+ const anchorEl = this.resolveAnchorElement();
390
+ const popup = this.renderRoot.querySelector('[part="listbox"]');
391
+ if (anchorEl && popup)
392
+ this.cleanup = place(anchorEl, popup, { placement: 'bottom-start' });
393
+ }
394
+ /** Resolves what to actually hand `place()` -- a caret-precise virtual
395
+ * point for a measurable text control, `anchor` itself otherwise. See the
396
+ * class doc's "Positioning" section. */
397
+ resolveAnchorElement() {
398
+ const anchor = this.anchor;
399
+ if (!anchor)
400
+ return null;
401
+ if (isTextControl(anchor)) {
402
+ const rect = caretClientRect(anchor);
403
+ if (rect) {
404
+ const virtual = this.virtualAnchor ?? (this.virtualAnchor = document.createElement('div'));
405
+ virtual.style.position = 'fixed';
406
+ virtual.style.left = `${rect.left}px`;
407
+ virtual.style.top = `${rect.top}px`;
408
+ virtual.style.width = '0';
409
+ virtual.style.height = `${rect.height}px`;
410
+ virtual.style.pointerEvents = 'none';
411
+ if (!virtual.isConnected)
412
+ document.body.appendChild(virtual);
413
+ return virtual;
414
+ }
415
+ }
416
+ return anchor;
417
+ }
418
+ renderRow(item, index, activeId) {
419
+ const id = this.rowId(index);
420
+ const active = id === activeId;
421
+ return html `
422
+ <div part="option" id=${id} role="option" data-id=${item.id} aria-selected=${active ? 'true' : 'false'} ?data-active=${active}>
423
+ ${item.icon ? html `<span part="option-icon" aria-hidden="true">${item.icon}</span>` : nothing}
424
+ <span part="option-label">
425
+ <span>${item.label}</span>
426
+ ${item.description ? html `<span part="option-description">${item.description}</span>` : nothing}
427
+ </span>
428
+ </div>
429
+ `;
430
+ }
431
+ render() {
432
+ const rows = this.filteredItems;
433
+ const idx = this.clampedIndex(rows);
434
+ const activeId = idx >= 0 ? this.rowId(idx) : '';
435
+ return html `
436
+ <div
437
+ part="listbox"
438
+ id=${this._listboxId}
439
+ role="listbox"
440
+ aria-label=${this.label}
441
+ @mousedown=${this.onListboxMouseDown}
442
+ @click=${this.onListboxClick}
443
+ >
444
+ ${rows.length === 0
445
+ ? html `<div part="empty" role="option" aria-selected="false" aria-disabled="true">${this.emptyText}</div>`
446
+ : rows.map((item, i) => this.renderRow(item, i, activeId))}
447
+ </div>
448
+ `;
449
+ }
450
+ }
451
+ __decorate([
452
+ property({ attribute: false })
453
+ ], LyraMentionPopover.prototype, "anchor", void 0);
454
+ __decorate([
455
+ property({ attribute: false })
456
+ ], LyraMentionPopover.prototype, "items", void 0);
457
+ __decorate([
458
+ property()
459
+ ], LyraMentionPopover.prototype, "query", void 0);
460
+ __decorate([
461
+ property({ type: Boolean, reflect: true })
462
+ ], LyraMentionPopover.prototype, "open", void 0);
463
+ __decorate([
464
+ property({ attribute: false })
465
+ ], LyraMentionPopover.prototype, "filter", void 0);
466
+ __decorate([
467
+ property({ attribute: 'empty-text' })
468
+ ], LyraMentionPopover.prototype, "emptyText", void 0);
469
+ __decorate([
470
+ property()
471
+ ], LyraMentionPopover.prototype, "label", void 0);
472
+ __decorate([
473
+ state()
474
+ ], LyraMentionPopover.prototype, "activeIndex", void 0);
475
+ defineElement('mention-popover', LyraMentionPopover);
@@ -0,0 +1 @@
1
+ export declare const styles: import("lit").CSSResult;
@@ -0,0 +1,101 @@
1
+ import { css } from 'lit';
2
+ export const styles = css `
3
+ :host {
4
+ display: contents;
5
+ }
6
+
7
+ /* Positioned by internal/positioner.js's place() -- same fixed/z-index
8
+ shape as lyra-combobox's/lyra-select's own [part='listbox']. Closed
9
+ state: invisible + slightly raised, transitioning in on :host([open]),
10
+ same as those two. */
11
+ [part='listbox'] {
12
+ position: fixed;
13
+ z-index: 900;
14
+ box-sizing: border-box;
15
+ max-block-size: 16rem;
16
+ overflow-y: auto;
17
+ inline-size: max-content;
18
+ min-inline-size: 14rem;
19
+ max-inline-size: min(92vw, 24rem);
20
+ padding: var(--lyra-space-xs);
21
+ background: var(--lyra-color-surface);
22
+ border: 1px solid var(--lyra-color-border);
23
+ border-radius: var(--lyra-radius);
24
+ box-shadow: var(--lyra-shadow);
25
+ visibility: hidden;
26
+ opacity: 0;
27
+ transform: translateY(-0.25rem);
28
+ transition:
29
+ opacity var(--lyra-transition-fast),
30
+ transform var(--lyra-transition-fast),
31
+ visibility var(--lyra-transition-fast);
32
+ }
33
+ :host([open]) [part='listbox'] {
34
+ visibility: visible;
35
+ opacity: 1;
36
+ transform: translateY(0);
37
+ }
38
+ @media (prefers-reduced-motion: reduce) {
39
+ [part='listbox'] {
40
+ transition: none !important;
41
+ }
42
+ }
43
+
44
+ [part='option'] {
45
+ display: flex;
46
+ align-items: flex-start;
47
+ gap: var(--lyra-space-xs);
48
+ inline-size: 100%;
49
+ padding: var(--lyra-space-xs) var(--lyra-space-s);
50
+ border-radius: var(--lyra-radius);
51
+ cursor: pointer;
52
+ }
53
+ [part='option'][data-active] {
54
+ background: var(--lyra-color-brand-quiet);
55
+ }
56
+ [part='option'][aria-selected='true'] {
57
+ color: var(--lyra-color-brand);
58
+ }
59
+
60
+ [part='option-icon'] {
61
+ flex: 0 0 auto;
62
+ line-height: 1.4;
63
+ }
64
+
65
+ [part='option-label'] {
66
+ display: flex;
67
+ flex-direction: column;
68
+ min-inline-size: 0;
69
+ line-height: 1.4;
70
+ }
71
+ [part='option-label'] span:first-child {
72
+ overflow: hidden;
73
+ text-overflow: ellipsis;
74
+ white-space: nowrap;
75
+ }
76
+
77
+ [part='option-description'] {
78
+ font-size: 0.75rem;
79
+ color: var(--lyra-color-text-quiet);
80
+ overflow: hidden;
81
+ text-overflow: ellipsis;
82
+ white-space: nowrap;
83
+ }
84
+ /* --lyra-color-text-quiet-on-brand-quiet fails WCAG AA (~4.24:1, needs
85
+ 4.5:1) -- unlike lyra-combobox's/lyra-select's identical-looking
86
+ [part='option'][data-active] background (which never has this problem
87
+ in practice, since those default their active index to -1/none until a
88
+ user actually arrows through the list), this component always opens
89
+ with row 0 pre-highlighted (see the activeIndex field's own doc), so the
90
+ active/quiet-text combination is the default state, not an edge case --
91
+ it must pass contrast on its own. */
92
+ [part='option'][data-active] [part='option-description'] {
93
+ color: var(--lyra-color-text);
94
+ }
95
+
96
+ [part='empty'] {
97
+ padding: var(--lyra-space-s) var(--lyra-space-m);
98
+ color: var(--lyra-color-text-quiet);
99
+ font-size: 0.875rem;
100
+ }
101
+ `;
@@ -0,0 +1,62 @@
1
+ import { type TemplateResult } from 'lit';
2
+ import { LyraElement } from '../../internal/lyra-element.js';
3
+ /**
4
+ * `<lyra-menu-item>` — a single action row inside `<lyra-menu>`'s default
5
+ * slot. Not meaningful on its own (there is no standalone "click a menu item"
6
+ * use case) — it exists purely as `<lyra-menu>`'s light-DOM child, the same
7
+ * relationship `<lyra-option>` has to `<lyra-combobox>`/`<lyra-select>`.
8
+ *
9
+ * `role="menuitem"` and the roving `tabindex` both live on *this host
10
+ * element*, not an internal shadow-DOM button — mirroring `<lyra-tree-node>`'s
11
+ * identical choice (see that class's doc). `<lyra-menu>` is the sole owner of
12
+ * this element's `tabIndex`: it flips exactly one navigable item's `tabIndex`
13
+ * to `0` (the rest sit at `-1`) as its roving-tabindex highlight moves, and
14
+ * calls `.focus()` directly on this host to move real DOM focus there.
15
+ * `[part="base"]` is purely a visual box with no interactive semantics of its
16
+ * own — see the class doc on `<lyra-menu>` for why real DOM focus (rather
17
+ * than `aria-activedescendant`) was chosen for this pair.
18
+ *
19
+ * Enter/Space activation is handled by `<lyra-menu>`'s own delegated
20
+ * `keydown` listener calling `select()` on whichever item is currently
21
+ * roving-focused (mirrors `<lyra-tree>` calling `current.select()` from its
22
+ * own keydown handler) — this element only wires a plain `click` listener
23
+ * itself, so `select()` fires identically whether the item was reached by
24
+ * mouse or keyboard.
25
+ *
26
+ * @customElement lyra-menu-item
27
+ * @slot - The item's label content.
28
+ * @slot icon - Optional leading icon.
29
+ * @event lyra-menu-item-select - This item was activated (click, or the
30
+ * parent `<lyra-menu>`'s own Enter/Space handling of the roving-focused
31
+ * item). No detail payload — a listener already has `event.target` (this
32
+ * element) to read `value` off of, and `<lyra-menu>` itself consumes this
33
+ * event to close and re-fire it as its own consolidated `lyra-menu-select`
34
+ * (`detail: { value }`) rather than requiring a consumer to listen on every
35
+ * individual item — listen there instead unless you specifically need a
36
+ * per-item handler.
37
+ * @csspart base - The row (`role` lives on the host — see the class doc).
38
+ * @csspart icon - Wrapper around the `icon` slot. Not rendered at all when the slot is empty.
39
+ * @csspart label - Wrapper around the default slot.
40
+ */
41
+ export declare class LyraMenuItem extends LyraElement {
42
+ static styles: import("lit").CSSResultGroup[];
43
+ /** An id/value the parent `<lyra-menu>`'s `lyra-menu-select` detail keys off of. */
44
+ value: string;
45
+ /** Disables selection and excludes this item from `<lyra-menu>`'s roving-tabindex nav entirely. */
46
+ disabled: boolean;
47
+ /** Visual treatment for a dangerous action (e.g. "Delete") — tints the row with `--lyra-color-danger`. */
48
+ destructive: boolean;
49
+ private hasIconSlot;
50
+ connectedCallback(): void;
51
+ protected willUpdate(): void;
52
+ /** Fires `lyra-menu-item-select` (no-op while `disabled`). Called by this element's own
53
+ * click handler, and by `<lyra-menu>`'s Enter/Space keydown handling of the active item. */
54
+ select(): void;
55
+ private onIconSlotChange;
56
+ render(): TemplateResult;
57
+ }
58
+ declare global {
59
+ interface HTMLElementTagNameMap {
60
+ 'lyra-menu-item': LyraMenuItem;
61
+ }
62
+ }