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,47 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Poetry
4
+ module Ui
5
+ module Bubble
6
+ # Re-expressed through the cn-* theme layer. The variant rules
7
+ # (root classes styling the content child via *:data-[slot] selectors,
8
+ # incl. the tinted relative-color math flagged to the contrast gate)
9
+ # ride the theme; the align choreography and the reactions' side/align
10
+ # translate positioning stay inline (mechanism).
11
+ class Style < Poetry::Core::Style
12
+ base "cn-bubble group/bubble relative flex w-fit min-w-0 flex-col " \
13
+ "group-data-[align=end]/message:self-end data-[align=end]:self-end"
14
+
15
+ variant :variant, {
16
+ default: "cn-bubble-variant-default",
17
+ secondary: "cn-bubble-variant-secondary",
18
+ muted: "cn-bubble-variant-muted",
19
+ tinted: "cn-bubble-variant-tinted",
20
+ outline: "cn-bubble-variant-outline",
21
+ ghost: "cn-bubble-variant-ghost",
22
+ destructive: "cn-bubble-variant-destructive"
23
+ }
24
+
25
+ element :group, "cn-bubble-group flex min-w-0 flex-col"
26
+
27
+ element :content, "cn-bubble-content w-fit max-w-full min-w-0 overflow-hidden wrap-break-word " \
28
+ "group-data-[align=end]/bubble:self-end " \
29
+ "[button]:text-left [button,a]:transition-colors [button,a]:outline-none"
30
+
31
+ # The side x align positioning stays inline (mechanism); the pill
32
+ # treatment rides .cn-bubble-reactions. Fixed offsets, NOT translate:
33
+ # translate makes the pill the CONTAINING BLOCK for fixed-position
34
+ # descendants, so a tooltip or popover composed into the reactions
35
+ # slot anchored to the pill and clamped to its width (libraries that
36
+ # portal overlay content to <body> survive this; poetry positions in
37
+ # place). -4.5 = 18px = 75% of the one-line pill height the themes
38
+ # pin - pixel-identical to a 3/4 translate for every pill poetry
39
+ # renders.
40
+ element :reactions, "cn-bubble-reactions absolute z-10 flex w-fit shrink-0 items-center " \
41
+ "justify-center data-[side=top]:-top-4.5 " \
42
+ "data-[side=bottom]:-bottom-4.5 " \
43
+ "data-[align=start]:left-3 data-[align=end]:right-3"
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,14 @@
1
+ <%= content_tag(root_tag, **root_attributes.to_attributes) do %>
2
+ <% if loading %>
3
+ <span data-slot="spinner" aria-hidden="true"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" class="animate-spin"><path d="M21 12a9 9 0 1 1-6.219-8.56" /></svg></span>
4
+ <% end %>
5
+ <% if leading? %><span data-slot="icon" aria-hidden="true"><%= leading %></span><% end %>
6
+ <%# contents: the label span must not create an inline box - a block-level
7
+ svg passed IN the content (icon + text + kbd) would force a line break
8
+ inside it (Tailwind preflight makes svg display:block). display:contents
9
+ promotes the content to flex items of the button, so items-center and
10
+ gap apply exactly as upstream (where children are direct flex items). %>
11
+ <% if icon_only? %><%= content %><% else %><span data-slot="label" class="contents"><%= content %></span><% end %>
12
+ <% if trailing? %><span data-slot="icon" aria-hidden="true"><%= trailing %></span><% end %>
13
+ <% if loading %><span class="sr-only"><%= t("poetry.button.loading") %></span><% end %>
14
+ <% end %>
@@ -0,0 +1,168 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Poetry
4
+ module Ui
5
+ # Action buttons and button-styled links.
6
+ module Button
7
+ # A button. Variants carry semantic intent (one default-variant
8
+ # action per view); size covers text and icon-only forms. Renders a
9
+ # real <a> when href: is given, and a no-JS loading state
10
+ # (aria-busy + spinner) via loading:.
11
+ #
12
+ # Icon-only sizes require label: - an icon button without an
13
+ # accessible name raises.
14
+ #
15
+ # @example
16
+ # render Poetry::Ui::Button::Component.new(variant: :default) { "Save" }
17
+ class Component < Poetry::Core::Component
18
+ # The closed vocabulary for the variant axis.
19
+ VARIANTS = %i[default destructive outline secondary ghost link].freeze
20
+ # The closed vocabulary for the size axis; icon* are the square icon-only forms.
21
+ SIZES = %i[default xs sm lg icon icon-xs icon-sm icon-lg].freeze
22
+ # The closed vocabulary for the native type: attribute.
23
+ TYPES = %i[button submit reset].freeze
24
+
25
+ # The contract's agent-rules section (projected into the registry,
26
+ # llms.txt, and the generated agent-rules.md).
27
+ AGENT_RULES = [
28
+ "Use poetry_button - never a raw <button> with hand-written Tailwind.",
29
+ "The visible text is the content block: poetry_button { \"Save\" }. label: is ONLY the accessible name.",
30
+ "Icon-only buttons (size: :icon*) MUST pass label: (the accessible name).",
31
+ "Link-styled actions use variant: :link - not <a> with button classes.",
32
+ "Navigation wearing button styling: pass href: (renders a real <a>; tag: :a is implied) - " \
33
+ "never onclick navigation.",
34
+ "Loading via loading: - never a manual disabled + spinner.",
35
+ "Never nest an interactive element inside a Button.",
36
+ "Pick the variant by intent; one primary (default) action per view."
37
+ ].freeze
38
+
39
+ # At least one of these must be present or nothing visible renders;
40
+ # static checks read this without rendering.
41
+ REQUIRES_ANY = [
42
+ { hint: "nothing visible renders without one - label: is only the accessible name",
43
+ content: true, slots: %w[leading trailing], options: %w[loading] }
44
+ ].freeze
45
+
46
+ renders_one :leading, doc: "Optional leading visual, rendered inside the icon span."
47
+ renders_one :trailing, doc: "Optional trailing visual, rendered inside the icon span."
48
+
49
+ style :variant, default: :default, required: true, variants: VARIANTS,
50
+ doc: "The visual intent axis; :destructive marks irreversible actions."
51
+ style :size, default: :default, required: true, variants: SIZES,
52
+ doc: "The size axis; the icon* sizes are square icon-only forms (label: required)."
53
+
54
+ option :type, :symbol, default: :button,
55
+ doc: "The native button type; ignored when the button renders as an anchor."
56
+ option :tag, :symbol, default: :button,
57
+ doc: "Renders the same styling on an <a> when :a - navigation wearing button clothes."
58
+ option :disabled, :boolean, default: false,
59
+ doc: "Disables the control (native disabled; aria-disabled on the anchor form)."
60
+ option :loading, :boolean, default: false,
61
+ doc: "The no-JS loading state: aria-busy, a spinner, and the control disabled."
62
+ option :label, :string, doc: "The accessible name for icon-only usage - not visible text."
63
+ option :href, :string, doc: "The link target; implies the anchor form."
64
+
65
+ validates :type, inclusion: { in: TYPES }
66
+ validates :tag, inclusion: { in: %i[button a] }
67
+
68
+ part "button", "The rendered control itself (<button>, or <a> when tag: :a) - " \
69
+ "every visual state rides here",
70
+ states: {
71
+ "data-variant" => { condition: "always - the resolved variant",
72
+ values: VARIANTS.map(&:to_s) },
73
+ "data-size" => { condition: "always - the resolved size", values: SIZES.map(&:to_s) },
74
+ "data-loading" => "loading: is set (aria-busy rides along)"
75
+ }
76
+ part "icon", "Wrapper span around leading/trailing slot content - sizes and centers " \
77
+ "whatever it holds"
78
+ part "label", "The content block's span (display: contents - children join the root's " \
79
+ "flex row directly)"
80
+ part "spinner", "The loading indicator, swapped in for the leading icon while loading:"
81
+
82
+ # Raises when an icon-only size lacks label:.
83
+ # @api private
84
+ def initialize(...)
85
+ super
86
+ return unless icon_only? && label.blank?
87
+
88
+ # The accessible-icon rule: an icon-only control without an
89
+ # accessible name never ships.
90
+ raise ArgumentError, "icon-only Button requires label: (the accessible name)"
91
+ end
92
+
93
+ # Enforces that something visible renders.
94
+ # @api private
95
+ def before_render
96
+ return if content? || leading? || trailing? || loading
97
+
98
+ raise ArgumentError,
99
+ "Button renders nothing visible: pass a content block (the visible text), " \
100
+ "an icon slot (with_leading/with_trailing), or loading: - label: is only the accessible name"
101
+ end
102
+
103
+ # Whether size: is one of the square icon-only forms.
104
+ # @api private
105
+ def icon_only?
106
+ size.to_s.start_with?("icon")
107
+ end
108
+
109
+ # href: implies the anchor: an href on a native <button> would be
110
+ # silently dropped, leaving a dead control.
111
+ # @api private
112
+ def link_tag?
113
+ tag == :a || href.present?
114
+ end
115
+
116
+ # @api private
117
+ def root_tag
118
+ link_tag? ? :a : :button
119
+ end
120
+
121
+ # @api private
122
+ def root_attributes
123
+ html_attributes.merge_if_not_set(base_attributes.merge(tag_attributes))
124
+ end
125
+
126
+ private
127
+
128
+ def base_attributes
129
+ attrs = { "data-slot" => "button", "data-variant" => variant, "data-size" => size }
130
+ .merge(component_data_attributes)
131
+ attrs["aria-label"] = label if label.present?
132
+ if loading
133
+ attrs["data-loading"] = true
134
+ attrs["aria-busy"] = true
135
+ end
136
+ attrs
137
+ end
138
+
139
+ # Native <button> gets native semantics (type, real disabled);
140
+ # `tag: :a` is navigation-styled-as-button: role="button" + the
141
+ # faux-disabled convention (aria-disabled, href withheld).
142
+ def tag_attributes
143
+ link_tag? ? link_attributes : button_attributes
144
+ end
145
+
146
+ def link_attributes
147
+ attrs = { "role" => "button" }
148
+ if disabled || loading
149
+ attrs["aria-disabled"] = true
150
+ elsif href.present?
151
+ attrs["href"] = href
152
+ end
153
+ attrs
154
+ end
155
+
156
+ def button_attributes
157
+ attrs = { "type" => type }
158
+ attrs["disabled"] = true if disabled || loading
159
+ attrs
160
+ end
161
+
162
+ private :icon_only?, :link_tag?, :root_tag, :root_attributes
163
+
164
+ private :icon_only?, :link_tag?, :root_tag, :root_attributes
165
+ end
166
+ end
167
+ end
168
+ end
@@ -0,0 +1,94 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Poetry
4
+ module Ui
5
+ module Button
6
+ # The Button preview matrix - one corpus, three uses: Lookbook docs,
7
+ # test fixtures (smoke-rendered in CI), and the agent visual loop.
8
+ class Preview < Poetry::Core::Preview::Base
9
+ # @!group Variants
10
+
11
+ def default
12
+ render_component(variant: :default) { "Save changes" }
13
+ end
14
+
15
+ def destructive
16
+ render_component(variant: :destructive) { "Delete account" }
17
+ end
18
+
19
+ def outline
20
+ render_component(variant: :outline) { "Cancel" }
21
+ end
22
+
23
+ def secondary
24
+ render_component(variant: :secondary) { "Duplicate" }
25
+ end
26
+
27
+ def ghost
28
+ render_component(variant: :ghost) { "Dismiss" }
29
+ end
30
+
31
+ def link
32
+ render_component(variant: :link) { "Learn more" }
33
+ end
34
+
35
+ # @!endgroup
36
+
37
+ # @!group Sizes
38
+
39
+ def size_xs
40
+ render_component(size: :xs) { "Tag" }
41
+ end
42
+
43
+ def size_sm
44
+ render_component(size: :sm) { "Small" }
45
+ end
46
+
47
+ def size_lg
48
+ render_component(size: :lg) { "Continue" }
49
+ end
50
+
51
+ def icon_only
52
+ render_component(size: :icon, label: "Add item") do |component|
53
+ component.with_leading { embed(Icon::Component.new(name: :plus)) }
54
+ end
55
+ end
56
+
57
+ def icon_large
58
+ render_component(size: :"icon-lg", label: "Open settings") do |component|
59
+ component.with_leading { embed(Icon::Component.new(name: :settings)) }
60
+ end
61
+ end
62
+
63
+ # @!endgroup
64
+
65
+ # @!group States
66
+
67
+ def disabled
68
+ render_component(disabled: true) { "Unavailable" }
69
+ end
70
+
71
+ def loading
72
+ render_component(loading: true) { "Saving" }
73
+ end
74
+
75
+ # @!endgroup
76
+
77
+ # @!group Composition
78
+
79
+ def with_icons
80
+ render_component(variant: :destructive) do |component|
81
+ component.with_leading { embed(Icon::Component.new(name: :trash)) }
82
+ "Delete"
83
+ end
84
+ end
85
+
86
+ def as_link
87
+ render_component(tag: :a, href: "#", variant: :outline) { "View pricing" }
88
+ end
89
+
90
+ # @!endgroup
91
+ end
92
+ end
93
+ end
94
+ end
@@ -0,0 +1,41 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Poetry
4
+ module Ui
5
+ module Button
6
+ # The Button dictionary, re-expressed through the cn-* theme layer:
7
+ # entries carry the stable `cn-button*` names plus the
8
+ # structural/behavioral inline set; the visual classes live in
9
+ # themes/default.css. NOTE dark destructive: the dark treatment
10
+ # paints bg-destructive/60 - the composite the contrast gate
11
+ # measures (6.5:1); solid dark destructive is 2.9:1 and must never
12
+ # carry white text undiluted (the rule lives in the theme file's
13
+ # .cn-button-variant-destructive).
14
+ class Style < Poetry::Core::Style
15
+ base "cn-button inline-flex shrink-0 items-center justify-center whitespace-nowrap " \
16
+ "transition-all outline-none disabled:pointer-events-none disabled:opacity-50 " \
17
+ "[&_svg]:pointer-events-none [&_svg]:shrink-0 group/button select-none"
18
+
19
+ variant :variant, {
20
+ default: "cn-button-variant-default",
21
+ destructive: "cn-button-variant-destructive",
22
+ outline: "cn-button-variant-outline",
23
+ secondary: "cn-button-variant-secondary",
24
+ ghost: "cn-button-variant-ghost",
25
+ link: "cn-button-variant-link"
26
+ }
27
+
28
+ variant :size, {
29
+ default: "cn-button-size-default",
30
+ xs: "cn-button-size-xs",
31
+ sm: "cn-button-size-sm",
32
+ lg: "cn-button-size-lg",
33
+ icon: "cn-button-size-icon",
34
+ "icon-xs": "cn-button-size-icon-xs",
35
+ "icon-sm": "cn-button-size-icon-sm",
36
+ "icon-lg": "cn-button-size-icon-lg"
37
+ }
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,70 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Poetry
4
+ module Ui
5
+ # Segmented groups of adjacent controls.
6
+ module ButtonGroup
7
+ # Visually joins adjacent controls (buttons, inputs, select
8
+ # triggers) into one segmented unit: outer corners stay rounded,
9
+ # inner corners and doubled borders collapse. Compose the members in
10
+ # the content block; poetry_button_group_text and
11
+ # poetry_button_group_separator are the non-button parts.
12
+ #
13
+ # @example A segmented pair
14
+ # render Poetry::Ui::ButtonGroup::Component.new("aria-label": "Alignment") do
15
+ # safe_join([
16
+ # render(Poetry::Ui::Button::Component.new(variant: :outline).with_content("Left")),
17
+ # render(Poetry::Ui::Button::Component.new(variant: :outline).with_content("Right"))
18
+ # ])
19
+ # end
20
+ class Component < Poetry::Core::Component
21
+ requires_content "its member controls"
22
+
23
+ # The closed vocabulary for the orientation axis.
24
+ ORIENTATIONS = %i[horizontal vertical].freeze
25
+
26
+ # Projected into the registry, llms.txt, and the agent surface.
27
+ AGENT_RULES = [
28
+ "Members go in the content block - the group's selectors join ANY data-slot children " \
29
+ "(buttons, inputs, select triggers); never hand-round the inner corners.",
30
+ "Give the group an aria-label when the page has more than one (role=group is unnamed by default).",
31
+ "A visual divider between members is poetry_button_group_separator, not a styled border."
32
+ ].freeze
33
+
34
+ style :orientation, default: :horizontal, required: true, variants: ORIENTATIONS,
35
+ doc: "The join axis - a horizontal row or a vertical stack."
36
+
37
+ part "button-group", "The role=group root - its selectors join ANY data-slot children into " \
38
+ "the segmented unit",
39
+ states: {
40
+ "data-orientation" => { condition: "the join axis", values: ORIENTATIONS.map(&:to_s) }
41
+ }
42
+ part "button-group-text", "A non-button member (the poetry_button_group_text helper's div) - " \
43
+ "a text affix joined like a button"
44
+
45
+ # Enforces the member-controls content block.
46
+ # @api private
47
+ def before_render
48
+ ensure_content!
49
+ end
50
+
51
+ # @api private
52
+ def call
53
+ content_tag(:div, content, **root_attributes.to_attributes)
54
+ end
55
+
56
+ # @api private
57
+ def root_attributes
58
+ html_attributes.merge_if_not_set(
59
+ {
60
+ "role" => "group", "data-slot" => "button-group",
61
+ "data-orientation" => orientation
62
+ }.merge(component_data_attributes)
63
+ )
64
+ end
65
+
66
+ private :root_attributes
67
+ end
68
+ end
69
+ end
70
+ end
@@ -0,0 +1,21 @@
1
+ <%# Popup wrappers at every group position: select FIRST (rounded left,
2
+ square right), dropdown-menu MIDDLE (square both, collapsed border),
3
+ popover LAST (square left, rounded right). %>
4
+ <%= poetry_button_group("aria-label": "Popup trigger corners") do %>
5
+ <%= poetry_select(placeholder: "USD", "aria-label": "Currency") do |select| %>
6
+ <% select.with_item(value: "usd") { "USD" } %>
7
+ <% select.with_item(value: "eur") { "EUR" } %>
8
+ <% end %>
9
+ <%= poetry_dropdown_menu do |menu| %>
10
+ <% menu.with_trigger(variant: :outline) { "Actions" } %>
11
+ <% menu.with_item { "Duplicate" } %>
12
+ <% menu.with_item { "Archive" } %>
13
+ <% end %>
14
+ <%= poetry_popover do |popover| %>
15
+ <% popover.with_trigger(variant: :outline, size: :icon, label: "Open details") do %>
16
+ <%= poetry_icon(name: :"chevron-down") %>
17
+ <% end %>
18
+ <% popover.with_title { "Details" } %>
19
+ <% popover.with_description { "The popover trigger sits last in the group." } %>
20
+ <% end %>
21
+ <% end %>
@@ -0,0 +1,46 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Poetry
4
+ module Ui
5
+ module ButtonGroup
6
+ # The ButtonGroup preview: a segmented pair, a prefix text member,
7
+ # and the vertical orientation.
8
+ class Preview < Poetry::Core::Preview::Base
9
+ def default
10
+ render_component("aria-label": "Text alignment") do
11
+ embed(button("Left", variant: :outline)) +
12
+ embed(button("Center", variant: :outline)) +
13
+ embed(button("Right", variant: :outline))
14
+ end
15
+ end
16
+
17
+ def with_text_prefix
18
+ # Part helpers need a real view context - a per-example template
19
+ # (a class-level preview.html.erb would hijack every example).
20
+ render_with_template(template: "poetry/ui/button_group/text_prefix_preview")
21
+ end
22
+
23
+ def vertical
24
+ render_component(orientation: :vertical, "aria-label": "Vertical actions") do
25
+ embed(button("Top", variant: :outline)) +
26
+ embed(button("Bottom", variant: :outline))
27
+ end
28
+ end
29
+
30
+ # The corner rig for popup wrappers (select / dropdown-menu /
31
+ # popover render a Stimulus wrapper around their trigger, unlike
32
+ # upstream fragments): first, middle, and last positions each
33
+ # prove the theme chains reach the trigger THROUGH its wrapper.
34
+ def popup_triggers
35
+ render_with_template(template: "poetry/ui/button_group/popup_triggers_preview")
36
+ end
37
+
38
+ private
39
+
40
+ def button(text, **)
41
+ Poetry::Ui::Button::Component.new(**).with_content(text)
42
+ end
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Poetry
4
+ module Ui
5
+ module ButtonGroup
6
+ # Re-expressed through the cn-* theme layer. One documented
7
+ # deviation from upstream's literal split: upstream ships the
8
+ # orientation corner-chains inline AND under cn names; poetry puts
9
+ # them theme-side only (pure radius/border design - the theme owns
10
+ # them, nothing is emitted twice).
11
+ class Style < Poetry::Core::Style
12
+ base "cn-button-group flex w-fit items-stretch *:focus-visible:relative *:focus-visible:z-10 " \
13
+ "[&>[data-slot=select-trigger]:not([class*='w-'])]:w-fit [&>input]:flex-1"
14
+
15
+ variant :orientation, {
16
+ horizontal: "cn-button-group-orientation-horizontal",
17
+ vertical: "cn-button-group-orientation-vertical"
18
+ }
19
+
20
+ element :text, "cn-button-group-text flex items-center [&_svg]:pointer-events-none"
21
+ element :separator, "cn-button-group-separator relative self-stretch " \
22
+ "data-horizontal:mx-px data-horizontal:w-auto " \
23
+ "data-vertical:my-px data-vertical:h-auto"
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,7 @@
1
+ <%# A text member joined to an input: the group collapses the seam. %>
2
+ <div class="p-4">
3
+ <%= poetry_button_group("aria-label": "Site address") do %>
4
+ <%= poetry_button_group_text { "https://" } %>
5
+ <%= poetry_input(name: "domain", placeholder: "example.com") %>
6
+ <% end %>
7
+ </div>
@@ -0,0 +1,72 @@
1
+ <%= content_tag(:div, **root_attributes.to_attributes) do %>
2
+ <div data-slot="calendar-nav" class="<%= css(:nav) %>">
3
+ <%= render Poetry::Ui::Button::Component.new(**previous_options) do |button| %>
4
+ <% button.with_leading { render Poetry::Ui::Icon::Component.new(name: :"chevron-left") } %>
5
+ <% end %>
6
+ <% if dropdown_caption? %>
7
+ <%# No caption target here: the controller reflects navigation into
8
+ the selects and their visible labels (data-calendar-unit).
9
+ Upstream's overlay pattern: the label span
10
+ is what you SEE (aria-hidden - the select carries the value);
11
+ the real select sits invisibly on top. %>
12
+ <div data-slot="calendar-caption" class="<%= css(:caption_dropdowns) %>">
13
+ <% { "month" => [month_options, @month.month, t("poetry.calendar.month"), month_names_list[@month.month - 1]],
14
+ "year" => [year_options, @month.year, t("poetry.calendar.year"), @month.year.to_s] }.each do |unit, (options, current, label, text)| %>
15
+ <span data-slot="calendar-dropdown" data-calendar-unit="<%= unit %>" class="<%= css(:dropdown_root) %>"
16
+ <%= tag.attributes(**stimulus_attributes_for(:dropdown)) %>>
17
+ <span data-slot="calendar-caption-label" aria-hidden="true" class="<%= css(:dropdown_label) %>">
18
+ <span data-slot="calendar-dropdown-value"><%= text %></span>
19
+ <%= render Poetry::Ui::Icon::Component.new(name: :"chevron-down") %>
20
+ </span>
21
+ <select aria-label="<%= label %>" class="<%= css(:dropdown_select) %>">
22
+ <% options.each do |(option_label, value)| %>
23
+ <option value="<%= value %>" <% if value == current %>selected<% end %>><%= option_label %></option>
24
+ <% end %>
25
+ </select>
26
+ </span>
27
+ <% end %>
28
+ </div>
29
+ <% else %>
30
+ <div data-slot="calendar-caption" class="<%= css(:caption) %>" <%= tag.attributes(**stimulus_attributes_for(:caption)) %>><%= caption %></div>
31
+ <% end %>
32
+ <%= render Poetry::Ui::Button::Component.new(**next_options) do |button| %>
33
+ <% button.with_leading { render Poetry::Ui::Icon::Component.new(name: :"chevron-right") } %>
34
+ <% end %>
35
+ </div>
36
+ <div role="grid" data-slot="calendar-grid" class="<%= css(:grid) %>"
37
+ <%= tag.attributes(**stimulus_attributes_for(:grid)) %>>
38
+ <div role="row" data-slot="calendar-weekdays" class="<%= css(:weekdays) %>">
39
+ <% if week_numbers %>
40
+ <div role="columnheader" data-slot="calendar-week-number" class="<%= css(:weekday) %>"><span class="sr-only">Week</span></div>
41
+ <% end %>
42
+ <% weekday_labels.each do |label| %>
43
+ <div role="columnheader" data-slot="calendar-weekday" class="<%= css(:weekday) %>"><%= label %></div>
44
+ <% end %>
45
+ </div>
46
+ <% cells.each_slice(7) do |week| %>
47
+ <div role="row" data-slot="calendar-week" class="<%= css(:week) %>">
48
+ <% if week_numbers %>
49
+ <div role="rowheader" data-slot="calendar-week-number" class="<%= css(:weekday) %>"><%= iso_week(week) %></div>
50
+ <% end %>
51
+ <% week.each do |date| %>
52
+ <%= content_tag(:div, cell_attributes(date)) do %>
53
+ <%= content_tag(:button, day_attributes(date)) do %>
54
+ <span data-slot="calendar-day-label"><%= date.day %></span>
55
+ <% end %>
56
+ <% end %>
57
+ <% end %>
58
+ </div>
59
+ <% end %>
60
+ </div>
61
+ <% if name.present? %>
62
+ <% if range? %>
63
+ <input type="hidden" name="<%= name %>[start]" value="<%= range_start&.iso8601 %>"
64
+ <%= tag.attributes(**stimulus_attributes_for(:start_input)) %>>
65
+ <input type="hidden" name="<%= name %>[end]" value="<%= range_end&.iso8601 %>"
66
+ <%= tag.attributes(**stimulus_attributes_for(:end_input)) %>>
67
+ <% else %>
68
+ <input type="hidden" name="<%= name %>" value="<%= selected&.iso8601 %>"
69
+ <%= tag.attributes(**stimulus_attributes_for(:hidden_input)) %>>
70
+ <% end %>
71
+ <% end %>
72
+ <% end %>