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,113 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Poetry
4
+ module Ui
5
+ module Select
6
+ # The Select preview matrix: the ported select-demo composition, the
7
+ # scrollable grouped list (scroll-button case), placeholder vs
8
+ # valued, both trigger sizes, disabled, invalid (the Field
9
+ # aria-invalid chain), RTL, and the server-rendered open state.
10
+ class Preview < Poetry::Core::Preview::Base
11
+ # The select-demo port: grouped fruit, placeholder shown until a
12
+ # commit writes the native select + the value display together.
13
+ def default
14
+ render_component(placeholder: "Select a fruit", "aria-label": "Fruit") do |select|
15
+ select.with_group(label: "Fruits") do |group|
16
+ group.with_item(value: "apple") { "Apple" }
17
+ group.with_item(value: "banana") { "Banana" }
18
+ group.with_item(value: "blueberry") { "Blueberry" }
19
+ group.with_item(value: "grapes", disabled: true) { "Grapes" }
20
+ group.with_item(value: "pineapple") { "Pineapple" }
21
+ end
22
+ end
23
+ end
24
+
25
+ # select-scrollable parity: grouped timezones behind max-h - the
26
+ # scroll buttons appear per-direction at the extremes.
27
+ def scrollable
28
+ render_component(placeholder: "Select a timezone", "aria-label": "Timezone") do |select|
29
+ select.with_group(label: "North America") do |group|
30
+ group.with_item(value: "est") { "Eastern Standard Time (EST)" }
31
+ group.with_item(value: "cst") { "Central Standard Time (CST)" }
32
+ group.with_item(value: "mst") { "Mountain Standard Time (MST)" }
33
+ group.with_item(value: "pst") { "Pacific Standard Time (PST)" }
34
+ end
35
+ select.with_separator
36
+ select.with_group(label: "Europe & Africa") do |group|
37
+ group.with_item(value: "gmt") { "Greenwich Mean Time (GMT)" }
38
+ group.with_item(value: "cet") { "Central European Time (CET)" }
39
+ group.with_item(value: "eet") { "Eastern European Time (EET)" }
40
+ group.with_item(value: "west") { "Western European Summer Time (WEST)" }
41
+ end
42
+ select.with_separator
43
+ select.with_group(label: "Asia") do |group|
44
+ group.with_item(value: "ist") { "India Standard Time (IST)" }
45
+ group.with_item(value: "cst_china") { "China Standard Time (CST)" }
46
+ group.with_item(value: "jst") { "Japan Standard Time (JST)" }
47
+ end
48
+ end
49
+ end
50
+
51
+ # The server-rendered value: display label + native option selected
52
+ # + aria-selected/data-selected on the option, all in one pass.
53
+ def valued
54
+ render_component(value: "banana", placeholder: "Select a fruit", "aria-label": "Fruit") do |select|
55
+ select.with_item(value: "apple") { "Apple" }
56
+ select.with_item(value: "banana") { "Banana" }
57
+ select.with_item(value: "blueberry") { "Blueberry" }
58
+ end
59
+ end
60
+
61
+ # data-size=sm - h-8, matching Input's dense form rows.
62
+ def small
63
+ render_component(size: :sm, placeholder: "Per page", "aria-label": "Per page") do |select|
64
+ select.with_item(value: "10") { "10" }
65
+ select.with_item(value: "25") { "25" }
66
+ select.with_item(value: "50") { "50" }
67
+ end
68
+ end
69
+
70
+ # Disables the trigger AND the native select together.
71
+ def disabled
72
+ render_component(disabled: true, placeholder: "Select a fruit", "aria-label": "Fruit") do |select|
73
+ select.with_item(value: "apple") { "Apple" }
74
+ select.with_item(value: "banana") { "Banana" }
75
+ end
76
+ end
77
+
78
+ # The Field error chain lands aria-invalid on the trigger - the
79
+ # source aria-invalid: classes fire with zero Select-side code.
80
+ def invalid
81
+ render_component(placeholder: "Choose department", required: true,
82
+ "aria-label": "Department", "aria-invalid": "true") do |select|
83
+ select.with_item(value: "eng") { "Engineering" }
84
+ select.with_item(value: "design") { "Design" }
85
+ end
86
+ end
87
+
88
+ # dir=rtl flips popper alignment via the direction helper; the
89
+ # listbox itself is flat (Left/Right stay no-ops).
90
+ def rtl
91
+ render_component(dir: :rtl, value: "ar", "aria-label": "اللغة") do |select|
92
+ select.with_item(value: "ar") { "العربية" }
93
+ select.with_item(value: "he") { "עברית" }
94
+ end
95
+ end
96
+
97
+ # Server-rendered open state (controllable-state: the attributes
98
+ # are the store; the controller reconciles on connect).
99
+ def open
100
+ render_component(open: true, value: "apple", placeholder: "Select a fruit",
101
+ "aria-label": "Fruit") do |select|
102
+ select.with_group(label: "Fruits") do |group|
103
+ group.with_item(value: "apple") { "Apple" }
104
+ group.with_item(value: "banana") { "Banana" }
105
+ end
106
+ select.with_separator
107
+ select.with_item(value: "other") { "Other" }
108
+ end
109
+ end
110
+ end
111
+ end
112
+ end
113
+ end
@@ -0,0 +1,58 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Poetry
4
+ module Ui
5
+ module Select
6
+ # Style dictionary for the Select family.
7
+ class Style < Poetry::Core::Style
8
+ element :trigger, "cn-select-trigger flex w-fit items-center justify-between whitespace-nowrap " \
9
+ "outline-none disabled:cursor-not-allowed disabled:opacity-50 " \
10
+ "*:data-[slot=select-value]:line-clamp-1 *:data-[slot=select-value]:flex " \
11
+ "*:data-[slot=select-value]:items-center " \
12
+ "[&_svg]:pointer-events-none [&_svg]:shrink-0"
13
+
14
+ element :content, "cn-select-content cn-menu-translucent relative z-50 max-h-(--available-height) " \
15
+ "origin-(--transform-origin) overflow-x-hidden overflow-y-auto " \
16
+ "isolate w-(--anchor-width)"
17
+
18
+ # Padding/scroll-margin are theme-owned via cn-select-viewport
19
+ # (sera runs p-1.5). Sizing vars stay structural.
20
+ # min-h on purpose, never a hard h: a hard h collapses the whole
21
+ # popup to trigger height (62px for a five-item list) the moment
22
+ # the trigger-height var is fed (goldens never see open popups, so
23
+ # only a human catches it).
24
+ element :viewport, "cn-select-viewport min-h-[var(--radix-select-trigger-height)] w-full " \
25
+ "min-w-[var(--radix-select-trigger-width)]"
26
+
27
+ element :item, "cn-select-item relative flex w-full cursor-default items-center outline-hidden " \
28
+ "select-none [&_svg]:pointer-events-none [&_svg]:shrink-0 data-disabled:opacity-50 " \
29
+ "data-disabled:pointer-events-none"
30
+
31
+ element :label, "cn-select-label"
32
+
33
+ element :separator, "cn-select-separator pointer-events-none"
34
+
35
+ element :scroll_button, ""
36
+
37
+ # The indicator wrapper span - the RIGHT-2 gutter, the mirror image
38
+ # of dropdown-menu's left gutter.
39
+ element :item_indicator, "cn-select-item-indicator absolute flex items-center justify-center"
40
+
41
+ # The check stays in the DOM always; the parent item's bare
42
+ # data-selected drives visibility (unselected = attribute ABSENCE -
43
+ # no data-unselected exists), so the controller's
44
+ # aria-selected/data-selected twin-flip is the whole toggle.
45
+ element :item_indicator_state, "[:not([data-selected])>&]:hidden"
46
+
47
+ # The form bubble: visually hidden but PAINTED (sr-only clips, never
48
+ # display:none - browser autofill heuristics skip unpainted controls).
49
+ element :native, "sr-only"
50
+
51
+ # The source's inline lucide icon classes, named per part.
52
+ element :trigger_icon, "cn-select-trigger-icon"
53
+ element :indicator_check, "size-4"
54
+ element :scroll_icon, ""
55
+ end
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,29 @@
1
+ <%# InputGroup anatomy: the real input (inert + transparent while masked),
2
+ the aria-hidden mask overlay (dots swapping to the reveal hint on
3
+ hover/focus), the trailing eye (revealed-only) and copy: affordance,
4
+ and the sr hint the masked group is described by. %>
5
+ <%= content_tag(:div, **root_attributes.to_attributes) do %>
6
+ <%= content_tag(:div, **group_attributes.to_attributes) do %>
7
+ <%= tag.input(**input_attributes.to_attributes) %>
8
+ <%= content_tag(:span, **mask_attributes.to_attributes) do %>
9
+ <span class="relative">
10
+ <span class="<%= css(:mask_dots) %>">••••••••</span>
11
+ <span class="<%= css(:mask_reveal) %>"><%= t("poetry.sensitive_input.reveal_hint") %></span>
12
+ </span>
13
+ <% end %>
14
+ <%= content_tag(:div, **Poetry::Core::HTML::Attributes.new(addon_attributes).to_attributes) do %>
15
+ <%= render toggle_button do %>
16
+ <%= render Poetry::Ui::Icon::Component.new(name: :"eye-off") %>
17
+ <% end %>
18
+ <% if copy %>
19
+ <%= render copy_button do %>
20
+ <span class="<%= css(:icon_stack) %>">
21
+ <%= render Poetry::Ui::Icon::Component.new(name: :copy, class: css(:icon_copy)) %>
22
+ <%= render Poetry::Ui::Icon::Component.new(name: :check, class: css(:icon_check)) %>
23
+ </span>
24
+ <% end %>
25
+ <% end %>
26
+ <% end %>
27
+ <% end %>
28
+ <%= content_tag(:span, t("poetry.sensitive_input.hint"), **hint_attributes.to_attributes) %>
29
+ <% end %>
@@ -0,0 +1,274 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Poetry
4
+ module Ui
5
+ # A secret field that stays masked until deliberately revealed.
6
+ module SensitiveInput
7
+ # A secret field - API keys, tokens, credentials - shown masked
8
+ # until deliberately revealed. Three states ride data-state on the
9
+ # root: masked (the value hidden behind an overlay that IS the
10
+ # reveal button, while the real input stays rendered for layout but
11
+ # goes inert), revealed (type=text, editable, the eye re-masks), and
12
+ # empty (a plain password input; the first character typed
13
+ # auto-reveals so composition happens visibly). Re-mask via Escape
14
+ # (focus returns to the group), leaving the component, or the eye.
15
+ # copy: adds a copy button - a real keyboard tab stop - that copies
16
+ # the value WITHOUT revealing it. The no-JS story is a plain
17
+ # password input that still submits.
18
+ #
19
+ # @example An API key with copy-without-reveal
20
+ # render Poetry::Ui::SensitiveInput::Component.new(name: "api_key", label: "API key",
21
+ # value: token, copy: true)
22
+ class Component < Poetry::Core::Component
23
+ include Poetry::Ui::InputGroupField
24
+
25
+ # Projected into the registry, llms.txt, and the agent surface.
26
+ AGENT_RULES = [
27
+ "Secrets shown-on-demand are a SensitiveInput (poetry_sensitive_input) - never a bare " \
28
+ "password Input with a hand-rolled eye; the masked-container contract (role=button, " \
29
+ "focus discipline, blur re-mask) rides the controller.",
30
+ "label: feeds the masked announcement (\"{label}, masked.\") - pair with a Label/Field " \
31
+ "for the visible caption.",
32
+ "copy: true adds copy-without-revealing; leave it off for password-change forms.",
33
+ "Values re-mask on blur BY DESIGN - do not fight it with reveal-state persistence."
34
+ ].freeze
35
+
36
+ # Both engines declare on the root; the clipboard-text controller
37
+ # rides along only when copy: is set.
38
+ use_stimulus do
39
+ on :root do
40
+ controller :sensitive_input do
41
+ register
42
+ value :masked_label
43
+ value :hidden_message, from: :hidden_message_text
44
+ value :read_only, "true", if: :readonly
45
+ action :blurred, on: :focusout
46
+ end
47
+ controller :clipboard_text, if: :copy do
48
+ register
49
+ value :message, from: :copied_message_text
50
+ end
51
+ end
52
+ # Clicks anywhere on the bordered surface reveal (the mask's own
53
+ # clicks bubble here); Enter/Space ride the mask button.
54
+ on :group do
55
+ controller(:sensitive_input) { action :reveal, on: :click }
56
+ end
57
+ on :mask do
58
+ controller :sensitive_input do
59
+ target :mask
60
+ action :maskKeydown, on: :keydown
61
+ end
62
+ end
63
+ on :input do
64
+ controller :sensitive_input do
65
+ target :input
66
+ action :changed, on: :input
67
+ action :inputKeydown, on: :keydown
68
+ end
69
+ controller(:clipboard_text, if: :copy) { target :input }
70
+ end
71
+ on :toggle do
72
+ controller :sensitive_input do
73
+ target :toggle
74
+ action :toggle, on: :click
75
+ end
76
+ end
77
+ on :copy_button do
78
+ controller(:clipboard_text) { action :copy, on: :click }
79
+ end
80
+ # The sr hint the masked group is described by.
81
+ on :hint do
82
+ controller(:sensitive_input) { target :hint }
83
+ end
84
+ end
85
+
86
+ option :name, :string, required: true, doc: "The form field name on the real input."
87
+ option :value, :string,
88
+ doc: "The secret's current value; present = first paint is masked, blank = the empty state."
89
+ option :id, :string, doc: "The input's DOM id - what a Field label's for: must reference."
90
+ option :label, :string,
91
+ doc: "The accessible name - feeds the input's aria-label and the masked announcement (\"{label}, " \
92
+ "masked.\"); pair with a visible Label/Field caption."
93
+ option :placeholder, :string, doc: "Hint text shown while the field is empty."
94
+ option :described_by, :string, doc: "aria-describedby on the input - Field hint/error wiring."
95
+ option :copy, :boolean, default: false, doc: "Adds the copy-without-revealing button in the trailing cell."
96
+ option :disabled, :boolean, default: false, doc: "Disables the input and drops the masked group's tab stop."
97
+ option :readonly, :boolean, default: false, doc: "The value can be revealed and copied but not edited."
98
+ option :required, :boolean, default: false, doc: "Marks the real input required."
99
+ option :invalid, :boolean, default: false,
100
+ doc: "aria-invalid on the input - set by Field/FormBuilder from model errors."
101
+
102
+ part "sensitive-input", "Root - the state machine rides here",
103
+ states: {
104
+ "data-state" => { condition: "always - masked (value hidden, group is the reveal " \
105
+ "button), revealed, or empty",
106
+ values: %w[masked revealed empty] },
107
+ "data-copied" => "copy: only - stamped for a beat after a successful copy " \
108
+ "(the clipboard-text engine)",
109
+ "data-disabled" => "disabled: - the masked group loses its tab stop and pointer " \
110
+ "affordances"
111
+ }
112
+ part "sensitive-input-group", "The bordered field surface (InputGroup's chrome) - " \
113
+ "clicks anywhere on it reveal; wears the focus ring when " \
114
+ "the mask button inside holds focus"
115
+ part "input-group-control", "The real input - rendered in every state for layout " \
116
+ "stability; inert while masked (aria-hidden, tabindex -1, " \
117
+ "readonly, transparent); type=password unless revealed"
118
+ part "sensitive-input-mask", "The overlay painting the masked state - while masked it " \
119
+ "IS the reveal button (role=button, tabindex 0, " \
120
+ "\"{label}, masked.\", described by the sr hint; only text " \
121
+ "spans inside, so no nested-interactive); bullet dots swap " \
122
+ "to the reveal hint on hover/focus with no layout shift"
123
+ part "input-group-addon", "The trailing cell holding the eye (and copy: affordance)",
124
+ states: {
125
+ "data-align" => { condition: "always - inline-end holds the actions",
126
+ values: %w[inline-end] }
127
+ }
128
+ # The eye and copy affordances render as composed ghost Buttons
129
+ # carrying data-slot=sensitive-input-toggle / clipboard-text-copy
130
+ # on Button's root - part ownership attributes them to Button. The
131
+ # eye exists ONLY while revealed (hidden otherwise; the masked
132
+ # group is the reveal path), re-masks and hands focus back to the
133
+ # group; copy is a real tab stop wired to the clipboard-text
134
+ # engine.
135
+
136
+ # @api private
137
+ def hint_id
138
+ "#{control_id}-hint"
139
+ end
140
+
141
+ # @api private
142
+ def state
143
+ value.present? ? "masked" : "empty"
144
+ end
145
+
146
+ # @api private
147
+ def masked?
148
+ state == "masked"
149
+ end
150
+
151
+ # @api private
152
+ def root_attributes
153
+ attrs = {
154
+ "data-slot" => "sensitive-input",
155
+ "data-state" => state,
156
+ "class" => css
157
+ }.merge(component_data_attributes)
158
+ attrs["data-disabled"] = "" if disabled
159
+ html_attributes.merge_if_not_set(attrs.merge(stimulus_attributes_for(:root)))
160
+ end
161
+
162
+ # @api private
163
+ def group_attributes
164
+ attrs = Poetry::Core::HTML::Attributes.new(
165
+ "data-slot" => "sensitive-input-group",
166
+ "class" => InputGroup::Style.css(class: css(:group))
167
+ )
168
+ attrs.merge!(stimulus_attributes_for(:group))
169
+ attrs
170
+ end
171
+
172
+ # Fixed inline-end - the reveal/copy cell.
173
+ # @api private
174
+ def addon_attributes
175
+ super(:end)
176
+ end
177
+
178
+ # The mask overlay IS the reveal button while masked (never the
179
+ # group: a role=button ancestor around the inert input trips axe
180
+ # nested-interactive). Display:none in every other state keeps it
181
+ # out of the a11y tree.
182
+ # @api private
183
+ def mask_attributes
184
+ attrs = Poetry::Core::HTML::Attributes.new(
185
+ "data-slot" => "sensitive-input-mask",
186
+ "class" => css(:mask)
187
+ )
188
+ if masked?
189
+ attrs["role"] = "button"
190
+ attrs["tabindex"] = disabled ? "-1" : "0"
191
+ attrs["aria-label"] = masked_label
192
+ attrs["aria-describedby"] = hint_id
193
+ end
194
+ attrs.merge!(stimulus_attributes_for(:mask))
195
+ attrs
196
+ end
197
+
198
+ # @api private
199
+ def input_attributes
200
+ attrs = Poetry::Core::HTML::Attributes.new(
201
+ "type" => "password",
202
+ "name" => name,
203
+ "id" => control_id,
204
+ "data-slot" => "input-group-control",
205
+ "class" => "#{Input::Style.css(class: InputGroup::Style.css(:control_input))} #{css(:input)}",
206
+ "autocomplete" => "off",
207
+ "spellcheck" => "false"
208
+ )
209
+ attrs["value"] = value if value.present?
210
+ attrs["placeholder"] = placeholder if placeholder.present?
211
+ attrs["aria-label"] = label if label.present?
212
+ attrs["aria-invalid"] = "true" if invalid && !disabled
213
+ attrs["aria-describedby"] = described_by if described_by.present?
214
+ attrs["disabled"] = "" if disabled
215
+ attrs["required"] = "" if required
216
+ if masked?
217
+ attrs["aria-hidden"] = "true"
218
+ attrs["tabindex"] = "-1"
219
+ attrs["readonly"] = ""
220
+ elsif readonly
221
+ attrs["readonly"] = ""
222
+ end
223
+ attrs.merge!(stimulus_attributes_for(:input))
224
+ attrs
225
+ end
226
+
227
+ # The eye: exists only while revealed (the masked group is the
228
+ # reveal path), so the server always renders it hidden.
229
+ # @api private
230
+ def toggle_button
231
+ group_tool_button(slot: "sensitive-input-toggle",
232
+ label: t("poetry.sensitive_input.hide"),
233
+ tabindex: nil, extra: { "hidden" => "" },
234
+ wiring: stimulus_attributes_for(:toggle))
235
+ end
236
+
237
+ # A REAL tab stop (no tabindex -1): copy must be keyboard-reachable.
238
+ # @api private
239
+ def copy_button
240
+ group_tool_button(slot: "clipboard-text-copy",
241
+ label: t("poetry.clipboard_text.copy"),
242
+ tabindex: nil,
243
+ wiring: stimulus_attributes_for(:copy_button))
244
+ end
245
+
246
+ # @api private
247
+ def masked_label
248
+ if label.present?
249
+ t("poetry.sensitive_input.masked_label", label: label)
250
+ else
251
+ t("poetry.sensitive_input.masked_fallback")
252
+ end
253
+ end
254
+
255
+ # The sr hint the masked group is described by (always rendered;
256
+ # only referenced while masked).
257
+ # @api private
258
+ def hint_attributes
259
+ attrs = Poetry::Core::HTML::Attributes.new("id" => hint_id, "class" => "sr-only")
260
+ attrs.merge!(stimulus_attributes_for(:hint))
261
+ attrs
262
+ end
263
+
264
+ private
265
+
266
+ def hidden_message_text = t("poetry.sensitive_input.hidden")
267
+ def copied_message_text = t("poetry.clipboard_text.copied")
268
+
269
+ private :hint_id, :state, :masked?, :root_attributes, :group_attributes, :addon_attributes, :mask_attributes
270
+ private :input_attributes, :toggle_button, :copy_button, :masked_label, :hint_attributes
271
+ end
272
+ end
273
+ end
274
+ end
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Poetry
4
+ module Ui
5
+ module SensitiveInput
6
+ class Preview < Poetry::Core::Preview::Base
7
+ # Masked: the group IS the reveal affordance (click/Enter/Space);
8
+ # the value re-masks on blur, Escape, or the eye.
9
+ def default
10
+ render_component(name: "api_key", label: "API key",
11
+ value: "sk_demo_4eC39HqLyjWDarjtT1zdp7dc")
12
+ end
13
+
14
+ # copy: adds copy-WITHOUT-revealing (the clipboard-text engine).
15
+ def with_copy
16
+ render_component(name: "api_key", label: "API key",
17
+ value: "sk_demo_4eC39HqLyjWDarjtT1zdp7dc", copy: true)
18
+ end
19
+
20
+ # readonly: reveal and copy still work; editing never does.
21
+ def readonly
22
+ render_component(name: "api_key", label: "API key", readonly: true,
23
+ value: "sk_demo_4eC39HqLyjWDarjtT1zdp7dc", copy: true)
24
+ end
25
+
26
+ # Empty: a plain password field; the first character typed
27
+ # auto-reveals so composition happens visibly.
28
+ def empty
29
+ render_component(name: "new_secret", label: "New secret",
30
+ placeholder: "Paste the token…")
31
+ end
32
+
33
+ def disabled
34
+ render_component(name: "api_key", label: "API key",
35
+ value: "rotated-out", disabled: true)
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,49 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Poetry
4
+ module Ui
5
+ module SensitiveInput
6
+ # InputGroup + Input + Button wear the chrome (the SearchField
7
+ # composition); the machine's rendering is structural utilities keyed
8
+ # on the root's data-state. The one theme-owned name is the group's
9
+ # OWN focus ring (cn-sensitive-input-group): while masked the group
10
+ # is the focusable button, and every theme keys its input ring on the
11
+ # CONTROL's focus-visible - the group needs each theme's treatment in
12
+ # that theme's own vocabulary (sera underlines, mira rings at 2).
13
+ class Style < Poetry::Core::Style
14
+ base "flex w-full flex-col"
15
+
16
+ # group/sensitive powers the mask's hover swap; disabled kills the
17
+ # pointer affordances at the root.
18
+ element :group, "cn-sensitive-input-group group/sensitive relative " \
19
+ "[[data-state=masked]:not([data-disabled])_&]:cursor-pointer " \
20
+ "[[data-disabled]_&]:pointer-events-none"
21
+
22
+ # The real input goes transparent (not hidden - it defines the
23
+ # width) and inert while masked.
24
+ element :input, "[[data-state=masked]_&]:pointer-events-none " \
25
+ "[[data-state=masked]_&]:text-transparent " \
26
+ "[[data-state=masked]_&]:select-none"
27
+
28
+ # The overlay paints the masked state only - and while masked it IS
29
+ # the reveal button (pointer target + focusable); display:none in
30
+ # every other state keeps it out of the a11y tree. Both texts render
31
+ # stacked so the hover swap never shifts layout.
32
+ element :mask, "pointer-events-none absolute inset-y-0 start-0 hidden items-center " \
33
+ "px-3 text-base outline-none md:text-sm [[data-state=masked]_&]:flex " \
34
+ "[[data-state=masked]:not([data-disabled])_&]:pointer-events-auto " \
35
+ "[[data-state=masked]:not([data-disabled])_&]:cursor-pointer"
36
+ element :mask_dots, "group-focus-within/sensitive:invisible group-hover/sensitive:invisible"
37
+ element :mask_reveal, "invisible absolute start-0 top-0 whitespace-nowrap " \
38
+ "text-muted-foreground group-focus-within/sensitive:visible " \
39
+ "group-hover/sensitive:visible"
40
+
41
+ element :icon_stack, "relative flex size-4 items-center justify-center"
42
+ element :icon_copy, "absolute inset-0 m-auto transition-all duration-200 " \
43
+ "[[data-copied]_&]:scale-0 [[data-copied]_&]:opacity-0"
44
+ element :icon_check, "absolute inset-0 m-auto scale-0 opacity-0 transition-all duration-200 " \
45
+ "[[data-copied]_&]:scale-100 [[data-copied]_&]:opacity-100"
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,57 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Poetry
4
+ module Ui
5
+ # A thin divider line between content regions.
6
+ module Separator
7
+ # A thin divider. Decorative by default (aria-hidden;
8
+ # it separates visually but adds nothing for AT). Set decorative: false
9
+ # for a semantic boundary (role=separator with the orientation), e.g.
10
+ # between toolbar groups.
11
+ #
12
+ # @example Horizontal divider between sections
13
+ # render Poetry::Ui::Separator::Component.new
14
+ class Component < Poetry::Core::Component
15
+ # Projected into the registry, llms.txt, and the agent surface.
16
+ AGENT_RULES = [
17
+ "A purely visual divider stays decorative (the default): aria-hidden, role absent.",
18
+ "Set decorative: false only when the divide is semantically meaningful (role=separator)."
19
+ ].freeze
20
+
21
+ option :orientation, :symbol, default: :horizontal, doc: "The divider's axis."
22
+ option :decorative, :boolean, default: true,
23
+ doc: "Whether the divide is purely visual (aria-hidden) or a semantic boundary " \
24
+ "(role=separator)."
25
+
26
+ validates :orientation, inclusion: { in: %i[horizontal vertical] }
27
+
28
+ part "separator", "The divider itself - decorative (aria-hidden) by default, " \
29
+ "role=separator when decorative: false",
30
+ states: {
31
+ "data-orientation" => { condition: "always - the resolved orientation",
32
+ values: %w[horizontal vertical] }
33
+ }
34
+
35
+ # @api private
36
+ def call
37
+ content_tag(:div, nil, **root_attributes.to_attributes)
38
+ end
39
+
40
+ # @api private
41
+ def root_attributes
42
+ attrs = { "data-slot" => "separator", "data-orientation" => orientation }
43
+ if decorative
44
+ attrs["aria-hidden"] = "true"
45
+ else
46
+ attrs["role"] = "separator"
47
+ # ARIA default orientation is horizontal; only mark the exception.
48
+ attrs["aria-orientation"] = "vertical" if orientation == :vertical
49
+ end
50
+ html_attributes.merge_if_not_set(attrs.merge(component_data_attributes))
51
+ end
52
+
53
+ private :root_attributes
54
+ end
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Poetry
4
+ module Ui
5
+ module Separator
6
+ # The Separator preview: a decorative horizontal rule, and a semantic
7
+ # vertical divider (role=separator).
8
+ class Preview < Poetry::Core::Preview::Base
9
+ def horizontal
10
+ render_component(class: "my-2")
11
+ end
12
+
13
+ def vertical_semantic
14
+ render_component(orientation: :vertical, decorative: false, class: "h-8")
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Poetry
4
+ module Ui
5
+ module Separator
6
+ # Style dictionary for the Separator family. The thickness/length
7
+ # flips on data-horizontal / data-vertical, driven by the rendered
8
+ # data-orientation.
9
+ class Style < Poetry::Core::Style
10
+ base "shrink-0 bg-border data-horizontal:h-px data-horizontal:w-full " \
11
+ "data-vertical:w-px data-vertical:self-stretch"
12
+ end
13
+ end
14
+ end
15
+ end