poetry-ui 0.0.2

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 (442) hide show
  1. checksums.yaml +7 -0
  2. data/.gitattributes +6 -0
  3. data/CHANGELOG.md +3 -0
  4. data/LICENSE.txt +21 -0
  5. data/README.md +87 -0
  6. data/THIRD_PARTY_NOTICES.md +201 -0
  7. data/app/components/poetry/ui/accordion/component.html.erb +3 -0
  8. data/app/components/poetry/ui/accordion/component.rb +197 -0
  9. data/app/components/poetry/ui/accordion/preview.rb +39 -0
  10. data/app/components/poetry/ui/accordion/style.rb +37 -0
  11. data/app/components/poetry/ui/alert/component.html.erb +6 -0
  12. data/app/components/poetry/ui/alert/component.rb +61 -0
  13. data/app/components/poetry/ui/alert/preview.rb +35 -0
  14. data/app/components/poetry/ui/alert/style.rb +24 -0
  15. data/app/components/poetry/ui/alert_dialog/component.html.erb +17 -0
  16. data/app/components/poetry/ui/alert_dialog/component.rb +208 -0
  17. data/app/components/poetry/ui/alert_dialog/preview.rb +48 -0
  18. data/app/components/poetry/ui/alert_dialog/style.rb +46 -0
  19. data/app/components/poetry/ui/aspect_ratio/component.rb +63 -0
  20. data/app/components/poetry/ui/aspect_ratio/preview.rb +29 -0
  21. data/app/components/poetry/ui/aspect_ratio/style.rb +13 -0
  22. data/app/components/poetry/ui/attachment/component.html.erb +13 -0
  23. data/app/components/poetry/ui/attachment/component.rb +139 -0
  24. data/app/components/poetry/ui/attachment/preview.rb +83 -0
  25. data/app/components/poetry/ui/attachment/style.rb +59 -0
  26. data/app/components/poetry/ui/autocomplete/component.html.erb +16 -0
  27. data/app/components/poetry/ui/autocomplete/component.rb +193 -0
  28. data/app/components/poetry/ui/autocomplete/preview.rb +39 -0
  29. data/app/components/poetry/ui/autocomplete/style.rb +18 -0
  30. data/app/components/poetry/ui/avatar/component.rb +103 -0
  31. data/app/components/poetry/ui/avatar/group_preview.html.erb +11 -0
  32. data/app/components/poetry/ui/avatar/preview.rb +50 -0
  33. data/app/components/poetry/ui/avatar/style.rb +34 -0
  34. data/app/components/poetry/ui/badge/component.rb +73 -0
  35. data/app/components/poetry/ui/badge/preview.rb +54 -0
  36. data/app/components/poetry/ui/badge/style.rb +35 -0
  37. data/app/components/poetry/ui/breadcrumb/component.html.erb +32 -0
  38. data/app/components/poetry/ui/breadcrumb/component.rb +102 -0
  39. data/app/components/poetry/ui/breadcrumb/dropdown_crumb_preview.html.erb +21 -0
  40. data/app/components/poetry/ui/breadcrumb/preview.rb +45 -0
  41. data/app/components/poetry/ui/breadcrumb/style.rb +25 -0
  42. data/app/components/poetry/ui/bubble/component.html.erb +4 -0
  43. data/app/components/poetry/ui/bubble/component.rb +106 -0
  44. data/app/components/poetry/ui/bubble/preview.rb +62 -0
  45. data/app/components/poetry/ui/bubble/style.rb +47 -0
  46. data/app/components/poetry/ui/button/component.html.erb +14 -0
  47. data/app/components/poetry/ui/button/component.rb +168 -0
  48. data/app/components/poetry/ui/button/preview.rb +94 -0
  49. data/app/components/poetry/ui/button/style.rb +41 -0
  50. data/app/components/poetry/ui/button_group/component.rb +70 -0
  51. data/app/components/poetry/ui/button_group/popup_triggers_preview.html.erb +21 -0
  52. data/app/components/poetry/ui/button_group/preview.rb +46 -0
  53. data/app/components/poetry/ui/button_group/style.rb +27 -0
  54. data/app/components/poetry/ui/button_group/text_prefix_preview.html.erb +7 -0
  55. data/app/components/poetry/ui/calendar/component.html.erb +72 -0
  56. data/app/components/poetry/ui/calendar/component.rb +366 -0
  57. data/app/components/poetry/ui/calendar/preview.rb +55 -0
  58. data/app/components/poetry/ui/calendar/style.rb +55 -0
  59. data/app/components/poetry/ui/card/component.html.erb +11 -0
  60. data/app/components/poetry/ui/card/component.rb +90 -0
  61. data/app/components/poetry/ui/card/preview.rb +29 -0
  62. data/app/components/poetry/ui/card/style.rb +23 -0
  63. data/app/components/poetry/ui/carousel/component.html.erb +17 -0
  64. data/app/components/poetry/ui/carousel/component.rb +179 -0
  65. data/app/components/poetry/ui/carousel/preview.rb +67 -0
  66. data/app/components/poetry/ui/carousel/style.rb +39 -0
  67. data/app/components/poetry/ui/checkbox/component.html.erb +22 -0
  68. data/app/components/poetry/ui/checkbox/component.rb +179 -0
  69. data/app/components/poetry/ui/checkbox/preview.rb +68 -0
  70. data/app/components/poetry/ui/checkbox/style.rb +28 -0
  71. data/app/components/poetry/ui/clipboard_text/component.html.erb +15 -0
  72. data/app/components/poetry/ui/clipboard_text/component.rb +154 -0
  73. data/app/components/poetry/ui/clipboard_text/preview.rb +24 -0
  74. data/app/components/poetry/ui/clipboard_text/style.rb +23 -0
  75. data/app/components/poetry/ui/code_block/component.html.erb +13 -0
  76. data/app/components/poetry/ui/code_block/component.rb +141 -0
  77. data/app/components/poetry/ui/code_block/preview.rb +40 -0
  78. data/app/components/poetry/ui/code_block/style.rb +38 -0
  79. data/app/components/poetry/ui/collapsible/component.html.erb +4 -0
  80. data/app/components/poetry/ui/collapsible/component.rb +134 -0
  81. data/app/components/poetry/ui/collapsible/preview.rb +24 -0
  82. data/app/components/poetry/ui/combobox/component.html.erb +49 -0
  83. data/app/components/poetry/ui/combobox/component.rb +1086 -0
  84. data/app/components/poetry/ui/combobox/preview.rb +164 -0
  85. data/app/components/poetry/ui/combobox/style.rb +148 -0
  86. data/app/components/poetry/ui/command/component.html.erb +20 -0
  87. data/app/components/poetry/ui/command/component.rb +526 -0
  88. data/app/components/poetry/ui/command/dialog_component.html.erb +14 -0
  89. data/app/components/poetry/ui/command/dialog_component.rb +206 -0
  90. data/app/components/poetry/ui/command/dialog_preview.rb +39 -0
  91. data/app/components/poetry/ui/command/preview.rb +121 -0
  92. data/app/components/poetry/ui/command/style.rb +78 -0
  93. data/app/components/poetry/ui/composable_trigger.rb +63 -0
  94. data/app/components/poetry/ui/context_menu/component.html.erb +6 -0
  95. data/app/components/poetry/ui/context_menu/component.rb +358 -0
  96. data/app/components/poetry/ui/context_menu/preview.rb +111 -0
  97. data/app/components/poetry/ui/context_menu/style.rb +59 -0
  98. data/app/components/poetry/ui/data_table/component.html.erb +78 -0
  99. data/app/components/poetry/ui/data_table/component.rb +277 -0
  100. data/app/components/poetry/ui/data_table/preview.rb +68 -0
  101. data/app/components/poetry/ui/data_table/state.rb +115 -0
  102. data/app/components/poetry/ui/data_table/style.rb +42 -0
  103. data/app/components/poetry/ui/date_field/component.html.erb +7 -0
  104. data/app/components/poetry/ui/date_field/component.rb +209 -0
  105. data/app/components/poetry/ui/date_field/preview.rb +42 -0
  106. data/app/components/poetry/ui/date_field/style.rb +35 -0
  107. data/app/components/poetry/ui/date_picker/component.html.erb +23 -0
  108. data/app/components/poetry/ui/date_picker/component.rb +242 -0
  109. data/app/components/poetry/ui/date_picker/preview.rb +31 -0
  110. data/app/components/poetry/ui/date_picker/style.rb +25 -0
  111. data/app/components/poetry/ui/deferred/component.html.erb +16 -0
  112. data/app/components/poetry/ui/deferred/component.rb +90 -0
  113. data/app/components/poetry/ui/deferred/preview.rb +24 -0
  114. data/app/components/poetry/ui/dialog/component.html.erb +21 -0
  115. data/app/components/poetry/ui/dialog/component.rb +194 -0
  116. data/app/components/poetry/ui/dialog/preview.rb +33 -0
  117. data/app/components/poetry/ui/dialog/style.rb +30 -0
  118. data/app/components/poetry/ui/drawer/component.html.erb +16 -0
  119. data/app/components/poetry/ui/drawer/component.rb +207 -0
  120. data/app/components/poetry/ui/drawer/preview.rb +69 -0
  121. data/app/components/poetry/ui/drawer/style.rb +105 -0
  122. data/app/components/poetry/ui/dropdown_menu/component.html.erb +6 -0
  123. data/app/components/poetry/ui/dropdown_menu/component.rb +350 -0
  124. data/app/components/poetry/ui/dropdown_menu/preview.rb +101 -0
  125. data/app/components/poetry/ui/dropdown_menu/style.rb +60 -0
  126. data/app/components/poetry/ui/empty/component.html.erb +10 -0
  127. data/app/components/poetry/ui/empty/component.rb +79 -0
  128. data/app/components/poetry/ui/empty/preview.rb +27 -0
  129. data/app/components/poetry/ui/empty/style.rb +27 -0
  130. data/app/components/poetry/ui/family_identity.rb +52 -0
  131. data/app/components/poetry/ui/field/component.html.erb +19 -0
  132. data/app/components/poetry/ui/field/component.rb +183 -0
  133. data/app/components/poetry/ui/field/preview.html.erb +30 -0
  134. data/app/components/poetry/ui/field/preview.rb +72 -0
  135. data/app/components/poetry/ui/field/style.rb +68 -0
  136. data/app/components/poetry/ui/field_group/component.rb +61 -0
  137. data/app/components/poetry/ui/field_group/preview.html.erb +30 -0
  138. data/app/components/poetry/ui/field_group/preview.rb +20 -0
  139. data/app/components/poetry/ui/field_group/style.rb +25 -0
  140. data/app/components/poetry/ui/field_separator/component.rb +70 -0
  141. data/app/components/poetry/ui/field_separator/preview.rb +18 -0
  142. data/app/components/poetry/ui/field_separator/style.rb +20 -0
  143. data/app/components/poetry/ui/fieldset/component.rb +90 -0
  144. data/app/components/poetry/ui/fieldset/preview.html.erb +22 -0
  145. data/app/components/poetry/ui/fieldset/preview.rb +25 -0
  146. data/app/components/poetry/ui/fieldset/style.rb +18 -0
  147. data/app/components/poetry/ui/file_input/component.html.erb +14 -0
  148. data/app/components/poetry/ui/file_input/component.rb +188 -0
  149. data/app/components/poetry/ui/file_input/preview.rb +30 -0
  150. data/app/components/poetry/ui/file_input/style.rb +35 -0
  151. data/app/components/poetry/ui/hover_card/component.html.erb +6 -0
  152. data/app/components/poetry/ui/hover_card/component.rb +201 -0
  153. data/app/components/poetry/ui/hover_card/preview.rb +60 -0
  154. data/app/components/poetry/ui/hover_card/style.rb +17 -0
  155. data/app/components/poetry/ui/icon/component.rb +127 -0
  156. data/app/components/poetry/ui/icon/preview.rb +21 -0
  157. data/app/components/poetry/ui/input/component.rb +80 -0
  158. data/app/components/poetry/ui/input/preview.rb +35 -0
  159. data/app/components/poetry/ui/input/style.rb +18 -0
  160. data/app/components/poetry/ui/input_group/button_preview.html.erb +12 -0
  161. data/app/components/poetry/ui/input_group/component.rb +69 -0
  162. data/app/components/poetry/ui/input_group/preview.rb +23 -0
  163. data/app/components/poetry/ui/input_group/search_preview.html.erb +13 -0
  164. data/app/components/poetry/ui/input_group/style.rb +40 -0
  165. data/app/components/poetry/ui/input_group/textarea_preview.html.erb +10 -0
  166. data/app/components/poetry/ui/input_group_field.rb +61 -0
  167. data/app/components/poetry/ui/input_otp/component.html.erb +25 -0
  168. data/app/components/poetry/ui/input_otp/component.rb +250 -0
  169. data/app/components/poetry/ui/input_otp/preview.rb +91 -0
  170. data/app/components/poetry/ui/input_otp/style.rb +36 -0
  171. data/app/components/poetry/ui/item/component.html.erb +13 -0
  172. data/app/components/poetry/ui/item/component.rb +99 -0
  173. data/app/components/poetry/ui/item/group_preview.html.erb +15 -0
  174. data/app/components/poetry/ui/item/preview.rb +52 -0
  175. data/app/components/poetry/ui/item/style.rb +45 -0
  176. data/app/components/poetry/ui/kbd/component.rb +47 -0
  177. data/app/components/poetry/ui/kbd/group_preview.html.erb +4 -0
  178. data/app/components/poetry/ui/kbd/preview.rb +23 -0
  179. data/app/components/poetry/ui/kbd/style.rb +13 -0
  180. data/app/components/poetry/ui/label/component.rb +46 -0
  181. data/app/components/poetry/ui/label/preview.html.erb +5 -0
  182. data/app/components/poetry/ui/label/preview.rb +16 -0
  183. data/app/components/poetry/ui/label/style.rb +13 -0
  184. data/app/components/poetry/ui/link/component.rb +62 -0
  185. data/app/components/poetry/ui/link/preview.rb +31 -0
  186. data/app/components/poetry/ui/link/style.rb +20 -0
  187. data/app/components/poetry/ui/marker/component.html.erb +4 -0
  188. data/app/components/poetry/ui/marker/component.rb +81 -0
  189. data/app/components/poetry/ui/marker/preview.rb +43 -0
  190. data/app/components/poetry/ui/marker/style.rb +24 -0
  191. data/app/components/poetry/ui/menubar/component.html.erb +3 -0
  192. data/app/components/poetry/ui/menubar/component.rb +493 -0
  193. data/app/components/poetry/ui/menubar/preview.rb +164 -0
  194. data/app/components/poetry/ui/menubar/style.rb +71 -0
  195. data/app/components/poetry/ui/menus.rb +386 -0
  196. data/app/components/poetry/ui/message/component.html.erb +8 -0
  197. data/app/components/poetry/ui/message/component.rb +64 -0
  198. data/app/components/poetry/ui/message/preview.rb +33 -0
  199. data/app/components/poetry/ui/message/style.rb +29 -0
  200. data/app/components/poetry/ui/message_scroller/component.html.erb +15 -0
  201. data/app/components/poetry/ui/message_scroller/component.rb +163 -0
  202. data/app/components/poetry/ui/message_scroller/preview.rb +33 -0
  203. data/app/components/poetry/ui/message_scroller/style.rb +39 -0
  204. data/app/components/poetry/ui/metadata_list/component.rb +88 -0
  205. data/app/components/poetry/ui/metadata_list/preview.rb +55 -0
  206. data/app/components/poetry/ui/metadata_list/style.rb +35 -0
  207. data/app/components/poetry/ui/meter/component.rb +117 -0
  208. data/app/components/poetry/ui/meter/preview.rb +28 -0
  209. data/app/components/poetry/ui/meter/style.rb +16 -0
  210. data/app/components/poetry/ui/native_select/component.rb +124 -0
  211. data/app/components/poetry/ui/native_select/labelled_preview.html.erb +7 -0
  212. data/app/components/poetry/ui/native_select/preview.rb +20 -0
  213. data/app/components/poetry/ui/native_select/style.rb +22 -0
  214. data/app/components/poetry/ui/navigation_menu/bar_preview.html.erb +14 -0
  215. data/app/components/poetry/ui/navigation_menu/component.html.erb +25 -0
  216. data/app/components/poetry/ui/navigation_menu/component.rb +254 -0
  217. data/app/components/poetry/ui/navigation_menu/preview.rb +20 -0
  218. data/app/components/poetry/ui/navigation_menu/style.rb +73 -0
  219. data/app/components/poetry/ui/navigation_menu/viewport_preview.html.erb +23 -0
  220. data/app/components/poetry/ui/number_field/component.html.erb +19 -0
  221. data/app/components/poetry/ui/number_field/component.rb +263 -0
  222. data/app/components/poetry/ui/number_field/preview.rb +66 -0
  223. data/app/components/poetry/ui/number_field/style.rb +14 -0
  224. data/app/components/poetry/ui/pagination/component.html.erb +40 -0
  225. data/app/components/poetry/ui/pagination/component.rb +182 -0
  226. data/app/components/poetry/ui/pagination/preview.rb +47 -0
  227. data/app/components/poetry/ui/pagination/style.rb +27 -0
  228. data/app/components/poetry/ui/popover/component.html.erb +17 -0
  229. data/app/components/poetry/ui/popover/component.rb +203 -0
  230. data/app/components/poetry/ui/popover/preview.html.erb +29 -0
  231. data/app/components/poetry/ui/popover/preview.rb +66 -0
  232. data/app/components/poetry/ui/popover/style.rb +26 -0
  233. data/app/components/poetry/ui/popper_consumer.rb +69 -0
  234. data/app/components/poetry/ui/progress/component.rb +95 -0
  235. data/app/components/poetry/ui/progress/preview.rb +22 -0
  236. data/app/components/poetry/ui/progress/style.rb +19 -0
  237. data/app/components/poetry/ui/questionnaire/component.html.erb +80 -0
  238. data/app/components/poetry/ui/questionnaire/component.rb +396 -0
  239. data/app/components/poetry/ui/questionnaire/preview.rb +99 -0
  240. data/app/components/poetry/ui/questionnaire/style.rb +53 -0
  241. data/app/components/poetry/ui/radio_group/component.html.erb +4 -0
  242. data/app/components/poetry/ui/radio_group/component.rb +323 -0
  243. data/app/components/poetry/ui/radio_group/preview.rb +109 -0
  244. data/app/components/poetry/ui/radio_group/style.rb +53 -0
  245. data/app/components/poetry/ui/resizable/component.html.erb +10 -0
  246. data/app/components/poetry/ui/resizable/component.rb +178 -0
  247. data/app/components/poetry/ui/resizable/preview.rb +42 -0
  248. data/app/components/poetry/ui/resizable/style.rb +30 -0
  249. data/app/components/poetry/ui/scroll_area/component.rb +76 -0
  250. data/app/components/poetry/ui/scroll_area/preview.rb +35 -0
  251. data/app/components/poetry/ui/scroll_area/style.rb +19 -0
  252. data/app/components/poetry/ui/search_field/component.html.erb +15 -0
  253. data/app/components/poetry/ui/search_field/component.rb +137 -0
  254. data/app/components/poetry/ui/search_field/preview.rb +23 -0
  255. data/app/components/poetry/ui/search_field/style.rb +18 -0
  256. data/app/components/poetry/ui/select/component.html.erb +15 -0
  257. data/app/components/poetry/ui/select/component.rb +639 -0
  258. data/app/components/poetry/ui/select/preview.rb +113 -0
  259. data/app/components/poetry/ui/select/style.rb +58 -0
  260. data/app/components/poetry/ui/sensitive_input/component.html.erb +29 -0
  261. data/app/components/poetry/ui/sensitive_input/component.rb +274 -0
  262. data/app/components/poetry/ui/sensitive_input/preview.rb +40 -0
  263. data/app/components/poetry/ui/sensitive_input/style.rb +49 -0
  264. data/app/components/poetry/ui/separator/component.rb +57 -0
  265. data/app/components/poetry/ui/separator/preview.rb +19 -0
  266. data/app/components/poetry/ui/separator/style.rb +15 -0
  267. data/app/components/poetry/ui/sheet/component.html.erb +21 -0
  268. data/app/components/poetry/ui/sheet/component.rb +97 -0
  269. data/app/components/poetry/ui/sheet/preview.rb +57 -0
  270. data/app/components/poetry/ui/sheet/style.rb +47 -0
  271. data/app/components/poetry/ui/sidebar/component.html.erb +20 -0
  272. data/app/components/poetry/ui/sidebar/component.rb +249 -0
  273. data/app/components/poetry/ui/sidebar/preview.rb +21 -0
  274. data/app/components/poetry/ui/sidebar/shell_preview.html.erb +47 -0
  275. data/app/components/poetry/ui/sidebar/style.rb +139 -0
  276. data/app/components/poetry/ui/skeleton/component.rb +39 -0
  277. data/app/components/poetry/ui/skeleton/preview.rb +22 -0
  278. data/app/components/poetry/ui/skeleton/style.rb +13 -0
  279. data/app/components/poetry/ui/slider/component.html.erb +13 -0
  280. data/app/components/poetry/ui/slider/component.rb +360 -0
  281. data/app/components/poetry/ui/slider/preview.rb +73 -0
  282. data/app/components/poetry/ui/slider/style.rb +55 -0
  283. data/app/components/poetry/ui/spinner/component.rb +58 -0
  284. data/app/components/poetry/ui/spinner/preview.rb +22 -0
  285. data/app/components/poetry/ui/spinner/style.rb +12 -0
  286. data/app/components/poetry/ui/stat/component.html.erb +13 -0
  287. data/app/components/poetry/ui/stat/component.rb +102 -0
  288. data/app/components/poetry/ui/stat/preview.rb +47 -0
  289. data/app/components/poetry/ui/stat/style.rb +29 -0
  290. data/app/components/poetry/ui/switch/component.html.erb +15 -0
  291. data/app/components/poetry/ui/switch/component.rb +136 -0
  292. data/app/components/poetry/ui/switch/preview.rb +66 -0
  293. data/app/components/poetry/ui/switch/style.rb +22 -0
  294. data/app/components/poetry/ui/table/component.html.erb +3 -0
  295. data/app/components/poetry/ui/table/component.rb +93 -0
  296. data/app/components/poetry/ui/table/preview.rb +96 -0
  297. data/app/components/poetry/ui/table/style.rb +26 -0
  298. data/app/components/poetry/ui/tabs/component.html.erb +10 -0
  299. data/app/components/poetry/ui/tabs/component.rb +253 -0
  300. data/app/components/poetry/ui/tabs/preview.rb +43 -0
  301. data/app/components/poetry/ui/tabs/style.rb +49 -0
  302. data/app/components/poetry/ui/tag_group/component.html.erb +8 -0
  303. data/app/components/poetry/ui/tag_group/component.rb +203 -0
  304. data/app/components/poetry/ui/tag_group/preview.rb +43 -0
  305. data/app/components/poetry/ui/tag_group/style.rb +29 -0
  306. data/app/components/poetry/ui/textarea/component.rb +64 -0
  307. data/app/components/poetry/ui/textarea/preview.rb +75 -0
  308. data/app/components/poetry/ui/textarea/style.rb +18 -0
  309. data/app/components/poetry/ui/time_field/component.html.erb +6 -0
  310. data/app/components/poetry/ui/time_field/component.rb +85 -0
  311. data/app/components/poetry/ui/time_field/preview.rb +40 -0
  312. data/app/components/poetry/ui/time_field/style.rb +30 -0
  313. data/app/components/poetry/ui/timeline/component.rb +134 -0
  314. data/app/components/poetry/ui/timeline/preview.rb +61 -0
  315. data/app/components/poetry/ui/timeline/style.rb +54 -0
  316. data/app/components/poetry/ui/toast/component.html.erb +24 -0
  317. data/app/components/poetry/ui/toast/component.rb +178 -0
  318. data/app/components/poetry/ui/toast/preview.rb +69 -0
  319. data/app/components/poetry/ui/toast/style.rb +33 -0
  320. data/app/components/poetry/ui/toast_trigger/addressed_preview.html.erb +12 -0
  321. data/app/components/poetry/ui/toast_trigger/component.html.erb +3 -0
  322. data/app/components/poetry/ui/toast_trigger/component.rb +82 -0
  323. data/app/components/poetry/ui/toast_trigger/default_preview.html.erb +11 -0
  324. data/app/components/poetry/ui/toast_trigger/preview.rb +19 -0
  325. data/app/components/poetry/ui/toaster/component.html.erb +1 -0
  326. data/app/components/poetry/ui/toaster/component.rb +99 -0
  327. data/app/components/poetry/ui/toaster/positions_preview.html.erb +24 -0
  328. data/app/components/poetry/ui/toaster/preview.rb +46 -0
  329. data/app/components/poetry/ui/toaster/stamp_preview.html.erb +15 -0
  330. data/app/components/poetry/ui/toaster/style.rb +26 -0
  331. data/app/components/poetry/ui/toggle/component.rb +120 -0
  332. data/app/components/poetry/ui/toggle/preview.rb +79 -0
  333. data/app/components/poetry/ui/toggle/style.rb +33 -0
  334. data/app/components/poetry/ui/toggle_group/component.html.erb +4 -0
  335. data/app/components/poetry/ui/toggle_group/component.rb +252 -0
  336. data/app/components/poetry/ui/toggle_group/preview.rb +143 -0
  337. data/app/components/poetry/ui/toggle_group/style.rb +33 -0
  338. data/app/components/poetry/ui/toolbar/component.rb +125 -0
  339. data/app/components/poetry/ui/toolbar/preview.rb +41 -0
  340. data/app/components/poetry/ui/toolbar/style.rb +20 -0
  341. data/app/components/poetry/ui/tooltip/component.html.erb +20 -0
  342. data/app/components/poetry/ui/tooltip/component.rb +185 -0
  343. data/app/components/poetry/ui/tooltip/preview.rb +74 -0
  344. data/app/components/poetry/ui/tooltip/previews/provider_row.html.erb +25 -0
  345. data/app/components/poetry/ui/tooltip/style.rb +24 -0
  346. data/app/components/poetry/ui/tree/component.html.erb +32 -0
  347. data/app/components/poetry/ui/tree/component.rb +233 -0
  348. data/app/components/poetry/ui/tree/preview.rb +55 -0
  349. data/app/components/poetry/ui/tree/style.rb +29 -0
  350. data/app/components/poetry/ui/typeset/component.rb +73 -0
  351. data/app/components/poetry/ui/typeset/preview.rb +45 -0
  352. data/app/components/poetry/ui/typeset/style.rb +15 -0
  353. data/app/controllers/poetry/ui/llms_controller.rb +28 -0
  354. data/app/helpers/poetry/ui/components_helper.rb +1794 -0
  355. data/app/helpers/poetry/ui/form_builder/type_inference.rb +117 -0
  356. data/app/helpers/poetry/ui/form_builder.rb +916 -0
  357. data/app/helpers/poetry/ui/optimistic_form_builder.rb +55 -0
  358. data/app/helpers/poetry/ui/toast_stream_actions.rb +27 -0
  359. data/config/component_descriptions.yml +96 -0
  360. data/config/component_registry.yml +12257 -0
  361. data/config/locales/en.yml +93 -0
  362. data/config/routes.rb +8 -0
  363. data/config/template_classes.txt +113 -0
  364. data/lib/generators/poetry/add/add_generator.rb +218 -0
  365. data/lib/generators/poetry/agent_rules/agent_rules_generator.rb +91 -0
  366. data/lib/generators/poetry/agents/agents_generator.rb +24 -0
  367. data/lib/generators/poetry/agents_section.rb +148 -0
  368. data/lib/generators/poetry/block/block_generator.rb +67 -0
  369. data/lib/generators/poetry/block/templates/action_bar.html.erb +45 -0
  370. data/lib/generators/poetry/block/templates/app_shell.html.erb +139 -0
  371. data/lib/generators/poetry/block/templates/data_index.html.erb +85 -0
  372. data/lib/generators/poetry/block/templates/destructive_panel.html.erb +26 -0
  373. data/lib/generators/poetry/block/templates/page_header.html.erb +25 -0
  374. data/lib/generators/poetry/block/templates/section_card.html.erb +42 -0
  375. data/lib/generators/poetry/block/templates/stepper.html.erb +79 -0
  376. data/lib/generators/poetry/block/templates/top_nav.html.erb +44 -0
  377. data/lib/generators/poetry/diff/diff_generator.rb +135 -0
  378. data/lib/generators/poetry/editor/editor_generator.rb +344 -0
  379. data/lib/generators/poetry/editor/templates/poetry-check-hook +81 -0
  380. data/lib/generators/poetry/install/install_generator.rb +364 -0
  381. data/lib/generators/poetry/pagination/pagination_generator.rb +76 -0
  382. data/lib/generators/poetry/pagination/templates/kaminari_paginator.html.erb +29 -0
  383. data/lib/generators/poetry/pagination/templates/poetry_link_renderer.rb +34 -0
  384. data/lib/generators/poetry/pagination/templates/poetry_pagy_helper.rb +28 -0
  385. data/lib/generators/poetry/recipes/agent_embed/app/javascript/controllers/agent_demo_controller.js +131 -0
  386. data/lib/generators/poetry/recipes/screen_data_index/app/controllers/orders_controller.rb +15 -0
  387. data/lib/generators/poetry/recipes/screen_data_index/app/views/orders/index.html.erb +7 -0
  388. data/lib/generators/poetry/recipes/screen_data_index/test/system/orders_index_test.rb +16 -0
  389. data/lib/generators/poetry/recipes/screen_settings/app/controllers/settings_controller.rb +14 -0
  390. data/lib/generators/poetry/recipes/screen_settings/app/views/settings/show.html.erb +9 -0
  391. data/lib/generators/poetry/recipes/screen_settings/test/system/settings_show_test.rb +16 -0
  392. data/lib/generators/poetry/scaffold_templates/scaffold_templates_generator.rb +60 -0
  393. data/lib/generators/poetry/scaffold_templates/templates/_form.html.erb.tt +77 -0
  394. data/lib/generators/poetry/scaffold_templates/templates/controller.rb.tt +78 -0
  395. data/lib/generators/poetry/scaffold_templates/templates/edit.html.erb.tt +14 -0
  396. data/lib/generators/poetry/scaffold_templates/templates/index.html.erb.tt +24 -0
  397. data/lib/generators/poetry/scaffold_templates/templates/new.html.erb.tt +11 -0
  398. data/lib/generators/poetry/scaffold_templates/templates/partial.html.erb.tt +13 -0
  399. data/lib/generators/poetry/scaffold_templates/templates/show.html.erb.tt +18 -0
  400. data/lib/generators/poetry/skill/skill_generator.rb +34 -0
  401. data/lib/generators/poetry/skill/templates/poetry-component/SKILL.md +58 -0
  402. data/lib/generators/poetry/skill/templates/poetry-component/references/anatomy.md +152 -0
  403. data/lib/generators/poetry/skill/templates/poetry-component/references/checklist.md +67 -0
  404. data/lib/generators/poetry/skill/templates/poetry-component/references/documentation.md +137 -0
  405. data/lib/generators/poetry/skill/templates/poetry-design/SKILL.md +56 -0
  406. data/lib/generators/poetry/skill/templates/poetry-design/references/audit.md +123 -0
  407. data/lib/generators/poetry/skill/templates/poetry-design/references/compose.md +73 -0
  408. data/lib/generators/poetry/skill/templates/poetry-design/references/figma.md +68 -0
  409. data/lib/generators/poetry/skill/templates/poetry-design/references/paper.md +56 -0
  410. data/lib/generators/poetry/skill/templates/poetry-design/references/study.md +47 -0
  411. data/lib/generators/poetry/skill/templates/poetry-design/references/theme.md +70 -0
  412. data/lib/generators/poetry/skills_section.rb +58 -0
  413. data/lib/poetry/ui/chat.rb +283 -0
  414. data/lib/poetry/ui/code_block_highlighter.rb +60 -0
  415. data/lib/poetry/ui/engine.rb +55 -0
  416. data/lib/poetry/ui/recipes.rb +157 -0
  417. data/lib/poetry/ui/testing/combobox.rb +167 -0
  418. data/lib/poetry/ui/testing/dialog.rb +73 -0
  419. data/lib/poetry/ui/testing/menu.rb +127 -0
  420. data/lib/poetry/ui/testing/registration.rb +58 -0
  421. data/lib/poetry/ui/testing/select.rb +147 -0
  422. data/lib/poetry/ui/testing/tester.rb +130 -0
  423. data/lib/poetry/ui/testing.rb +130 -0
  424. data/lib/poetry/ui/themes.rb +109 -0
  425. data/lib/poetry/ui/version.rb +8 -0
  426. data/lib/poetry/ui/webmcp.rb +91 -0
  427. data/lib/poetry/ui.rb +285 -0
  428. data/lib/poetry-ui.rb +5 -0
  429. data/lib/tasks/poetry/check.rake +73 -0
  430. data/lib/tasks/poetry/design.rake +293 -0
  431. data/lib/tasks/poetry/verify.rake +36 -0
  432. data/themes/default.css +1793 -0
  433. data/themes/luma.css +1764 -0
  434. data/themes/lyra.css +1768 -0
  435. data/themes/maia.css +1768 -0
  436. data/themes/mira.css +1760 -0
  437. data/themes/nova.css +1755 -0
  438. data/themes/rhea.css +1771 -0
  439. data/themes/sera.css +1757 -0
  440. data/themes/vega.css +1775 -0
  441. data/typeset/typeset.css +499 -0
  442. metadata +502 -0
@@ -0,0 +1,154 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Poetry
4
+ module Ui
5
+ # ClipboardText family: a read-only value with one copy affordance.
6
+ module ClipboardText
7
+ # A read-only value with one copy affordance - API keys, install
8
+ # commands, resource IDs. The value renders in a readonly monospace
9
+ # input (selectable, never editable) with a trailing copy button.
10
+ # A successful copy stamps data-copied on the root for a moment, so
11
+ # the stacked copy/check glyphs swap in CSS, and announces itself
12
+ # to screen readers; the fallback copy path restores the user's own
13
+ # selection and focus.
14
+ #
15
+ # @example An install command with a copy button
16
+ # render Poetry::Ui::ClipboardText::Component.new(value: "gem install poetry-ui",
17
+ # label: "Install command")
18
+ class Component < Poetry::Core::Component
19
+ # Projected into the registry, llms.txt, and the agent surface.
20
+ AGENT_RULES = [
21
+ "A read-only value with one copy affordance (poetry_clipboard_text) - API keys, install " \
22
+ "commands, IDs. Editable text is an Input; a secret that needs masking is a SensitiveInput.",
23
+ "value: is what SHOWS; text_to_copy: overrides what lands on the clipboard when the " \
24
+ "display truncates - never truncate the copied text itself.",
25
+ "Give it label: (or compose under a Field/Label) - the readonly input still needs its " \
26
+ "accessible name."
27
+ ].freeze
28
+
29
+ use_stimulus do
30
+ on :root do
31
+ controller :clipboard_text do
32
+ register
33
+ value :message, from: :copied_message_text
34
+ value :text, from: :text_to_copy, if: -> { text_to_copy.present? }
35
+ end
36
+ end
37
+ on :input do
38
+ controller(:clipboard_text) { target :input }
39
+ end
40
+ on :copy_button do
41
+ controller(:clipboard_text) { action :copy, on: :click }
42
+ end
43
+ end
44
+
45
+ option :value, :string, required: true,
46
+ doc: "The displayed text - also what copies, unless text_to_copy: overrides it."
47
+ option :text_to_copy, :string,
48
+ doc: "Overrides what lands on the clipboard when the displayed value truncates: display short, copy " \
49
+ "full."
50
+ option :id, :string,
51
+ doc: "The readonly input's DOM id (auto-generated when omitted) - the Field/Label for= target."
52
+ option :label, :string, doc: "The readonly input's accessible name when no Label/Field association exists."
53
+ option :described_by, :string, doc: "Ids for the input's aria-describedby (hint or error text)."
54
+ option :disabled, :boolean, default: false, doc: "Disables the input and the copy button together."
55
+
56
+ part "clipboard-text", "Root - the controller rides here",
57
+ states: {
58
+ "data-copied" => "stamped for a beat after a successful copy (the stacked " \
59
+ "copy/check glyphs swap off it)"
60
+ }
61
+ part "clipboard-text-group", "The bordered field surface - InputGroup's chrome"
62
+ part "input-group-control", "The readonly mono <input> showing the value - selectable, " \
63
+ "never editable; InputGroup's control slot"
64
+ part "input-group-addon", "The trailing cell holding the copy affordance - InputGroup's " \
65
+ "addon vocabulary",
66
+ states: {
67
+ "data-align" => { condition: "always - inline-end holds the copy button",
68
+ values: %w[inline-end] }
69
+ }
70
+ # The copy affordance renders as a composed ghost Button carrying
71
+ # data-slot=clipboard-text-copy on Button's root - ownership
72
+ # attributes it to Button (the NumberField stepper precedent), so it
73
+ # is documented here in prose only: a REAL tab stop (copying is the
74
+ # component's primary action), aria-controls to the input, stacked
75
+ # copy/check glyphs inside.
76
+
77
+ # The readonly input's id (given or auto-generated).
78
+ # @api private
79
+ def control_id
80
+ @control_id ||= id.presence || poetry_instance_id("poetry-clipboard-text")
81
+ end
82
+
83
+ # Attributes for the root wrapper.
84
+ # @api private
85
+ def root_attributes
86
+ attrs = {
87
+ "data-slot" => "clipboard-text",
88
+ "class" => css
89
+ }.merge(component_data_attributes)
90
+ html_attributes.merge_if_not_set(attrs.merge(stimulus_attributes_for(:root)))
91
+ end
92
+
93
+ # Attributes for the bordered field surface.
94
+ # @api private
95
+ def group_attributes
96
+ {
97
+ "role" => "group",
98
+ "data-slot" => "clipboard-text-group",
99
+ "class" => InputGroup::Style.css
100
+ }
101
+ end
102
+
103
+ # Attributes for the trailing addon cell.
104
+ # @api private
105
+ def addon_attributes
106
+ {
107
+ "data-slot" => "input-group-addon",
108
+ "data-align" => "inline-end",
109
+ "class" => InputGroup::Style.css(:addon, class: InputGroup::Style.css(:addon_inline_end))
110
+ }
111
+ end
112
+
113
+ # Attributes for the readonly value input.
114
+ # @api private
115
+ def input_attributes
116
+ attrs = Poetry::Core::HTML::Attributes.new(
117
+ "type" => "text",
118
+ "readonly" => "",
119
+ "id" => control_id,
120
+ "value" => value,
121
+ "data-slot" => "input-group-control",
122
+ "class" => "#{Input::Style.css(class: InputGroup::Style.css(:control_input))} #{css(:input)}",
123
+ "autocomplete" => "off",
124
+ "spellcheck" => "false"
125
+ )
126
+ attrs["aria-label"] = label if label.present?
127
+ attrs["aria-describedby"] = described_by if described_by.present?
128
+ attrs["disabled"] = "" if disabled
129
+ attrs.merge!(stimulus_attributes_for(:input))
130
+ attrs
131
+ end
132
+
133
+ # The copy affordance: a ghost icon Button, a real tab stop (it IS
134
+ # the component's action).
135
+ # @api private
136
+ def copy_button
137
+ Button::Component.new({
138
+ variant: :ghost, size: :"icon-xs", disabled: disabled,
139
+ label: t("poetry.clipboard_text.copy"),
140
+ class: InputGroup::Style.css(:button, class: InputGroup::Style.css(:button_icon_xs)),
141
+ "data-slot" => "clipboard-text-copy",
142
+ "aria-controls" => control_id
143
+ }.compact.merge(stimulus_attributes_for(:copy_button)))
144
+ end
145
+
146
+ private
147
+
148
+ def copied_message_text = t("poetry.clipboard_text.copied")
149
+
150
+ private :control_id, :root_attributes, :group_attributes, :addon_attributes, :input_attributes, :copy_button
151
+ end
152
+ end
153
+ end
154
+ end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Poetry
4
+ module Ui
5
+ module ClipboardText
6
+ class Preview < Poetry::Core::Preview::Base
7
+ def default
8
+ render_component(value: "gem install poetry-ui", label: "Install command")
9
+ end
10
+
11
+ # Display truncates, the clipboard gets the FULL value via
12
+ # text_to_copy (the ported source's textToCopy contract).
13
+ def truncated_copy
14
+ render_component(value: "pk_live_51Nx…9fQ2", label: "API key",
15
+ text_to_copy: "pk_live_51NxAbCdEfGhIjKlMnOpQrStUvWxYz0123456789fQ2")
16
+ end
17
+
18
+ def disabled
19
+ render_component(value: "rotated-out-key", label: "API key", disabled: true)
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Poetry
4
+ module Ui
5
+ module ClipboardText
6
+ # Utility-only: InputGroup + Input + Button wear the chrome (the
7
+ # SearchField composition - zero new theme CSS). The input goes mono
8
+ # for value legibility; the stacked copy/check glyphs swap on the
9
+ # root's data-copied stamp (structural visibility, not design).
10
+ class Style < Poetry::Core::Style
11
+ base "flex w-full flex-col"
12
+
13
+ element :input, "font-mono"
14
+
15
+ element :icon_stack, "relative flex size-4 items-center justify-center"
16
+ element :icon_copy, "absolute inset-0 m-auto transition-all duration-200 " \
17
+ "[[data-copied]_&]:scale-0 [[data-copied]_&]:opacity-0"
18
+ element :icon_check, "absolute inset-0 m-auto scale-0 opacity-0 transition-all duration-200 " \
19
+ "[[data-copied]_&]:scale-100 [[data-copied]_&]:opacity-100"
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,13 @@
1
+ <%# The pre/code nesting stays on one line - literal template whitespace
2
+ inside <pre> would render. %>
3
+ <%= content_tag(:div, **root_attributes.to_attributes) do %>
4
+ <%= content_tag(:pre, content_tag(:code, highlighted || code, **code_attributes.to_attributes), **Poetry::Core::HTML::Attributes.new(pre_attributes).to_attributes) %>
5
+ <% if copy %>
6
+ <%= render copy_button do %>
7
+ <span class="<%= css(:icon_stack) %>">
8
+ <%= render Poetry::Ui::Icon::Component.new(name: :copy, class: css(:icon_copy)) %>
9
+ <%= render Poetry::Ui::Icon::Component.new(name: :check, class: css(:icon_check)) %>
10
+ </span>
11
+ <% end %>
12
+ <% end %>
13
+ <% end %>
@@ -0,0 +1,141 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Poetry
4
+ module Ui
5
+ # CodeBlock family: the server-highlighted code panel.
6
+ module CodeBlock
7
+ # A highlighted code panel rendered entirely at request time via
8
+ # the rouge gem. Rouge is a SOFT dependency - without it the same
9
+ # markup ships with plain escaped code. Line numbers are CSS
10
+ # counters, so they are excluded from selection and from copied
11
+ # text by construction; highlight_lines: tints rows via the
12
+ # theme's highlight hook; the syntax palette is seven --syntax-*
13
+ # vars each theme owns. copy: adds a copy button reading the
14
+ # rendered code itself.
15
+ #
16
+ # @example
17
+ # render Poetry::Ui::CodeBlock::Component.new(
18
+ # code: "puts \"hello\"", language: "ruby"
19
+ # )
20
+ class Component < Poetry::Core::Component
21
+ # Projected into the registry, llms.txt, and the agent surface.
22
+ AGENT_RULES = [
23
+ "Blocks of code are a CodeBlock (poetry_code_block) - never a hand-rolled pre/code " \
24
+ "with utility classes; the syntax palette, line counters, and copy affordance ride it.",
25
+ "Highlighting needs `gem \"rouge\"` in the host Gemfile - without it the block renders " \
26
+ "plain (same markup, no colors). Inline code stays plain <code> typography.",
27
+ "highlight_lines: takes 1-based line numbers; line numbers are CSS counters and never " \
28
+ "pollute copied text."
29
+ ].freeze
30
+
31
+ # The whole copy surface is copy:-gated - element conditions keep
32
+ # the wiring out of copy: false renders entirely.
33
+ use_stimulus do
34
+ on :root, if: :copy do
35
+ controller :clipboard_text do
36
+ register
37
+ value :message, from: :copied_message_text
38
+ end
39
+ end
40
+ on :source, if: :copy do
41
+ controller(:clipboard_text) { target :source }
42
+ end
43
+ on :copy_button do
44
+ controller(:clipboard_text) { action :copy, on: :click }
45
+ end
46
+ end
47
+
48
+ option :code, :string, required: true, doc: "The source text to highlight - required."
49
+ option :language, :string, default: "text",
50
+ doc: "The lexer name (\"ruby\", \"js\", ...); unknown languages fall back to " \
51
+ "plain text."
52
+ option :label, :string,
53
+ doc: "The scroll region's accessible name; defaults to the localized \"Code\" (a focusable scrollable " \
54
+ "region must be named - axe)."
55
+ option :line_numbers, :boolean, default: false,
56
+ doc: "Renders CSS-counter line numbers - never part of selection or copied " \
57
+ "text."
58
+ option :highlight_lines, ActiveModel::Type::Value.new,
59
+ doc: "1-based line numbers to tint via the theme's highlight hook."
60
+ option :copy, :boolean, default: true, doc: "Renders the copy button in the panel's corner."
61
+
62
+ part "code-block", "Root - the syntax-palette surface (cn-code-block)",
63
+ states: {
64
+ "data-language" => "always - the lexer name, a styling/tooling hook",
65
+ "data-line-numbers" => "line_numbers: - turns on the ::before CSS counters",
66
+ "data-copied" => "copy: only - stamped for a beat after a successful copy " \
67
+ "(the clipboard-text engine)"
68
+ }
69
+ part "code-block-pre", "The scroll container - tabindex 0 + role region + label " \
70
+ "(a scrollable region must be keyboard-reachable and named)"
71
+ part "code-block-code", "The code element - rouge's .line/.hll spans and the seven " \
72
+ "--syntax-* token maps live under it; the copy affordance " \
73
+ "reads ITS textContent"
74
+ # The copy affordance renders as a composed ghost Button carrying
75
+ # data-slot=clipboard-text-copy on Button's root (the ClipboardText
76
+ # anatomy, prose not part): top-right of the panel, a real tab stop,
77
+ # stacked copy/check glyphs swapping on data-copied.
78
+
79
+ # The rouge-rendered (or plain-escaped) markup.
80
+ # @api private
81
+ def highlighted
82
+ @highlighted ||= CodeBlockHighlighter.highlight(
83
+ code, language: language, highlight_lines: highlight_lines || []
84
+ )
85
+ end
86
+
87
+ # Attributes for the panel root.
88
+ # @api private
89
+ def root_attributes
90
+ attrs = {
91
+ "data-slot" => "code-block",
92
+ "data-language" => language,
93
+ "class" => css
94
+ }.merge(component_data_attributes)
95
+ attrs["data-line-numbers"] = "" if line_numbers
96
+ html_attributes.merge_if_not_set(attrs.merge(stimulus_attributes_for(:root)))
97
+ end
98
+
99
+ # Attributes for the scrollable <pre> region.
100
+ # @api private
101
+ def pre_attributes
102
+ {
103
+ "data-slot" => "code-block-pre",
104
+ "tabindex" => "0",
105
+ "role" => "region",
106
+ "aria-label" => label.presence || t("poetry.code_block.label"),
107
+ "class" => css(:pre)
108
+ }
109
+ end
110
+
111
+ # Attributes for the <code> element.
112
+ # @api private
113
+ def code_attributes
114
+ attrs = Poetry::Core::HTML::Attributes.new(
115
+ "data-slot" => "code-block-code",
116
+ "class" => css(:code)
117
+ )
118
+ attrs.merge!(stimulus_attributes_for(:source))
119
+ attrs
120
+ end
121
+
122
+ # The corner copy affordance: a ghost icon Button.
123
+ # @api private
124
+ def copy_button
125
+ Button::Component.new({
126
+ variant: :ghost, size: :"icon-xs",
127
+ label: t("poetry.clipboard_text.copy"),
128
+ class: css(:copy),
129
+ "data-slot" => "clipboard-text-copy"
130
+ }.merge(stimulus_attributes_for(:copy_button)))
131
+ end
132
+
133
+ private
134
+
135
+ def copied_message_text = t("poetry.clipboard_text.copied")
136
+
137
+ private :highlighted, :root_attributes, :pre_attributes, :code_attributes, :copy_button
138
+ end
139
+ end
140
+ end
141
+ end
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Poetry
4
+ module Ui
5
+ module CodeBlock
6
+ # The CodeBlock preview: highlighted Ruby, line numbers with .hll
7
+ # tints, and the copy-less variant.
8
+ class Preview < Poetry::Core::Preview::Base
9
+ RUBY_SAMPLE = <<~RUBY
10
+ class Invoice < ApplicationRecord
11
+ belongs_to :customer
12
+
13
+ # Only settled invoices count toward revenue.
14
+ scope :settled, -> { where(status: "settled") }
15
+
16
+ def total
17
+ line_items.sum(&:amount)
18
+ end
19
+ end
20
+ RUBY
21
+
22
+ def default
23
+ render_component(code: RUBY_SAMPLE, language: "ruby", label: "Invoice model")
24
+ end
25
+
26
+ # CSS-counter line numbers (never in copied text) + .hll tints on
27
+ # the named lines.
28
+ def numbered_and_highlighted
29
+ render_component(code: RUBY_SAMPLE, language: "ruby", label: "Invoice model",
30
+ line_numbers: true, highlight_lines: [5, 7, 8, 9])
31
+ end
32
+
33
+ def without_copy
34
+ render_component(code: %(export POETRY_THEME=vega\nbin/rake test:visual), language: "shell",
35
+ label: "Gate commands", copy: false)
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Poetry
4
+ module Ui
5
+ module CodeBlock
6
+ # The panel look, syntax palette values, .hll tint, and line-number
7
+ # chrome are theme-owned (cn-code-block + descendant rules, all nine
8
+ # themes); the dictionary carries the STRUCTURE - scroll container,
9
+ # counters, and the token map wiring rouge's classes to the seven
10
+ # --syntax-* vars the theme defines.
11
+ class Style < Poetry::Core::Style
12
+ base "cn-code-block group/code relative"
13
+
14
+ element :pre, "overflow-x-auto p-4 [counter-reset:line]"
15
+
16
+ # The token map: rouge's short classes -> the theme's syntax vars.
17
+ # .line blocks make .hll tints span the full row.
18
+ element :code, "block min-w-max font-mono text-sm [&_.line]:block " \
19
+ "[&_:is(.k,.kd,.kn,.kp,.kr,.kt,.kv)]:text-(--syntax-keyword) " \
20
+ "[&_:is(.kc,.l,.ld,.m,.mb,.mf,.mh,.mi,.il,.mo,.mx)]:text-(--syntax-constant) " \
21
+ "[&_:is(.sb,.bp,.ne,.nl,.py,.nv,.vc,.vg,.vi,.vm,.o,.ow)]:text-(--syntax-constant) " \
22
+ "[&_:is(.s,.sa,.sc,.dl,.sd,.s2,.se,.sh,.sx,.s1,.ss)]:text-(--syntax-string) " \
23
+ "[&_:is(.nb,.nc,.no,.nn)]:text-(--syntax-entity) " \
24
+ "[&_:is(.na,.nt,.sr)]:text-(--syntax-markup) " \
25
+ "[&_:is(.nd,.nf,.fm)]:text-(--syntax-function) " \
26
+ "[&_:is(.c,.ch,.cd,.cm,.cp,.cpf,.c1,.cs)]:text-(--syntax-comment)"
27
+
28
+ element :copy, "absolute top-2 end-2"
29
+
30
+ element :icon_stack, "relative flex size-4 items-center justify-center"
31
+ element :icon_copy, "absolute inset-0 m-auto transition-all duration-200 " \
32
+ "[[data-copied]_&]:scale-0 [[data-copied]_&]:opacity-0"
33
+ element :icon_check, "absolute inset-0 m-auto scale-0 opacity-0 transition-all duration-200 " \
34
+ "[[data-copied]_&]:scale-100 [[data-copied]_&]:opacity-100"
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,4 @@
1
+ <%= content_tag(:div, **root_attributes.to_attributes) do %>
2
+ <%= trigger %>
3
+ <%= content_tag(:div, content, **Poetry::Core::HTML::Attributes.new(content_attributes).to_attributes) %>
4
+ <% end %>
@@ -0,0 +1,134 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Poetry
4
+ module Ui
5
+ # Collapsible family: the plain show/hide disclosure.
6
+ module Collapsible
7
+ # A disclosure: a trigger button that shows and hides one content
8
+ # panel. The server renders the initial open/closed state, the
9
+ # trigger mirrors aria-expanded, and closed content stays in the
10
+ # DOM (hidden) so it survives re-renders and stays findable -
11
+ # exit animations are awaited before hiding.
12
+ #
13
+ # @example
14
+ # render Poetry::Ui::Collapsible::Component.new do |collapsible|
15
+ # collapsible.with_trigger { "Show details" }
16
+ # tag.div("Hidden until disclosed.")
17
+ # end
18
+ class Component < Poetry::Core::Component
19
+ include Poetry::Ui::ComposableTrigger
20
+
21
+ # Projected into the registry, llms.txt, and the agent surface.
22
+ AGENT_RULES = [
23
+ ComposableTrigger::AGENT_RULE,
24
+ "The trigger is with_trigger { \"label\" } - a real button, wired for you (aria-expanded/controls).",
25
+ "Server-render the initial state via open: - never toggle data-open/data-closed by hand.",
26
+ "Content stays in the DOM when closed (hidden) - do not conditionally render it.",
27
+ "For URL-controlled disclosure without JS, render open: from params - the same markup serves both."
28
+ ].freeze
29
+
30
+ # The required slots, stated statically so static checks can flag
31
+ # a missing trigger without rendering.
32
+ REQUIRED_SLOTS = { trigger: "the disclosure control" }.freeze
33
+
34
+ renders_one :trigger,
35
+ doc: "The disclosure control - a real button, wired for you (aria-expanded, aria-controls); " \
36
+ "options merge onto it.",
37
+ renders: lambda { |**options, &block|
38
+ attrs = {
39
+ type: "button", "data-slot" => "collapsible-trigger",
40
+ "aria-expanded" => open.to_s, "aria-controls" => content_id
41
+ }.merge(stimulus_attributes_for(:trigger))
42
+ composed_trigger(attrs, options, &block) ||
43
+ content_tag(:button, Poetry::Core::HTML::Attributes.merged(attrs, options), &block)
44
+ }
45
+
46
+ use_stimulus do
47
+ on :root do
48
+ controller(:state) { register }
49
+ end
50
+ on :trigger do
51
+ controller :state do
52
+ target :trigger
53
+ action :toggle, on: :click
54
+ end
55
+ end
56
+ on :content do
57
+ controller(:state) { target :content }
58
+ end
59
+ end
60
+
61
+ option :open, :boolean, default: false,
62
+ doc: "The server-rendered initial state; the trigger toggles it client-side."
63
+
64
+ part "collapsible", "The disclosure root - the state controller flips the pair here",
65
+ states: {
66
+ "data-open" => "expanded (server-rendered from open:; the controller flips the pair at runtime)",
67
+ "data-closed" => "collapsed (the server-rendered default)"
68
+ }
69
+ part "collapsible-trigger", "The disclosure button - mirrors aria-expanded",
70
+ states: {
71
+ "data-panel-open" => "its content is open (controller-written; " \
72
+ "absent while closed)"
73
+ }
74
+ part "collapsible-content", "The disclosure panel - stays in the DOM when closed (hidden) " \
75
+ "and rides the presence helper on exit",
76
+ states: {
77
+ "data-open" => "content is open or entering",
78
+ "data-closed" => "content is closed or animating out (hidden lands after the exit finishes)"
79
+ }
80
+
81
+ # A missing body yields a trigger disclosing an empty panel.
82
+ requires_content "the disclosed panel body"
83
+
84
+ # Enforces the required trigger and panel body.
85
+ # @api private
86
+ def before_render
87
+ raise ArgumentError, "Collapsible requires with_trigger (the disclosure control)" unless trigger?
88
+
89
+ ensure_content!
90
+ end
91
+
92
+ # The state word behind the data-* stamps.
93
+ # @api private
94
+ def state
95
+ open ? "open" : "closed"
96
+ end
97
+
98
+ # The panel's id - the trigger's aria-controls target.
99
+ # @api private
100
+ def content_id
101
+ @content_id ||= "#{instance_id}-content"
102
+ end
103
+
104
+ # Attributes for the disclosure root.
105
+ # @api private
106
+ def root_attributes
107
+ html_attributes.merge_if_not_set(
108
+ { "data-slot" => "collapsible", "data-#{state}" => "" }
109
+ .merge(stimulus_attributes_for(:root))
110
+ .merge(component_data_attributes)
111
+ )
112
+ end
113
+
114
+ # Attributes for the content panel.
115
+ # @api private
116
+ def content_attributes
117
+ attrs = {
118
+ "id" => content_id, "data-slot" => "collapsible-content", "data-#{state}" => ""
119
+ }.merge(stimulus_attributes_for(:content))
120
+ attrs["hidden"] = true unless open
121
+ attrs
122
+ end
123
+
124
+ private
125
+
126
+ def instance_id
127
+ @instance_id ||= poetry_instance_id("poetry-collapsible")
128
+ end
129
+
130
+ private :state, :content_id, :root_attributes, :content_attributes
131
+ end
132
+ end
133
+ end
134
+ end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Poetry
4
+ module Ui
5
+ module Collapsible
6
+ # The Collapsible preview matrix.
7
+ class Preview < Poetry::Core::Preview::Base
8
+ def default
9
+ render_component do |collapsible|
10
+ collapsible.with_trigger(class: "text-sm font-medium underline-offset-4 hover:underline") { "Show details" }
11
+ tag.div("Hidden until disclosed.", class: "pt-2 text-sm text-muted-foreground")
12
+ end
13
+ end
14
+
15
+ def initially_open
16
+ render_component(open: true) do |collapsible|
17
+ collapsible.with_trigger(class: "text-sm font-medium") { "Hide details" }
18
+ tag.div("Server-rendered open.", class: "pt-2 text-sm text-muted-foreground")
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,49 @@
1
+ <%# The option union renders FIRST (into a capture) so the shared
2
+ OptionSet is populated - server-stable option ids assigned, the
3
+ initial highlight decided - before the native <select>, the trigger's
4
+ value display, and the input's aria-activedescendant derive from it:
5
+ one server-side truth, four surfaces (the Select + Command capture
6
+ patterns fused). %>
7
+ <% list_body = capture do %>
8
+ <% items.each do |item| %><%= item %><% end %>
9
+ <% end %>
10
+ <%= content_tag(:div, **root_attributes.to_attributes) do %>
11
+ <%= native_select %>
12
+ <% if show_clear %>
13
+ <%# The clear anchor shrink-wraps the trigger (the root is full-width
14
+ block); the X rides as the trigger's IMMEDIATE next sibling - the
15
+ chevron-swap CSS keys on that adjacency. %>
16
+ <span class="<%= css(:clear_anchor) %>"><%= trigger_button %><%= clear_button %></span>
17
+ <% else %>
18
+ <%= multiple ? chips_frame : trigger_button %>
19
+ <% end %>
20
+ <%= content_tag(:div, **Poetry::Core::HTML::Attributes.new(content_attributes).to_attributes) do %>
21
+ <% if multiple %>
22
+ <%# MULTIPLE: the popup holds ONLY the listbox (the input lives
23
+ inline in the chips frame; the command engine rides the ROOT so
24
+ it scopes over both - no command part in here). %>
25
+ <%= content_tag(:div, **Poetry::Core::HTML::Attributes.new(list_attributes).to_attributes) do %>
26
+ <%= empty_part %>
27
+ <%= loading_part %>
28
+ <%= list_body %>
29
+ <% end %>
30
+ <%= status_part %>
31
+ <% else %>
32
+ <%# The EMBEDDED Command anatomy (Command::Style + the engine
33
+ controller reused verbatim; chrome flattened - rounded/border ride
34
+ the content). %>
35
+ <%= content_tag(:div, **Poetry::Core::HTML::Attributes.new(command_attributes).to_attributes) do %>
36
+ <div data-slot="combobox-input-wrapper" class="<%= Poetry::Ui::Command::Style.css(:input_wrapper) %>">
37
+ <span data-slot="combobox-search-icon"><%= render(Poetry::Ui::Icon::Component.new(name: :search, class: Poetry::Ui::Command::Style.css(:search_icon))) %></span>
38
+ <%= tag.input(**Poetry::Core::HTML::Attributes.new(input_attributes).to_attributes) %>
39
+ </div>
40
+ <%= content_tag(:div, **Poetry::Core::HTML::Attributes.new(list_attributes).to_attributes) do %>
41
+ <%= empty_part %>
42
+ <%= loading_part %>
43
+ <%= list_body %>
44
+ <% end %>
45
+ <%= status_part %>
46
+ <% end %>
47
+ <% end %>
48
+ <% end %>
49
+ <% end %>