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,124 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Poetry
4
+ module Ui
5
+ # A styled native <select>.
6
+ module NativeSelect
7
+ # A styled REAL <select> - platform picker, form submission, and
8
+ # mobile UX for free - with a decorative chevron replacing the
9
+ # native arrow. The fast path is options: pairs; compose
10
+ # <option>/<optgroup> in the content block for anything richer
11
+ # (poetry_native_select_option / _optgroup stamp the classes).
12
+ #
13
+ # @example The options: fast path
14
+ # render Poetry::Ui::NativeSelect::Component.new(
15
+ # name: "sort", label: "Sort by",
16
+ # options: [["Newest first", "newest"], ["Oldest first", "oldest"]],
17
+ # selected: "newest"
18
+ # )
19
+ class Component < Poetry::Core::Component
20
+ # The closed vocabulary for the size axis.
21
+ SIZES = %i[default sm].freeze
22
+
23
+ # Projected into the registry, llms.txt, and the agent surface.
24
+ AGENT_RULES = [
25
+ "This is a REAL <select> - use it for plain picking; the JS Select is for styled options.",
26
+ "Pair it with a Label (for_id: its id) or a Field - a bare select has no accessible name.",
27
+ "The fast path is options: [[label, value], ...] + selected:; a content block overrides it."
28
+ ].freeze
29
+
30
+ option :name, :string, doc: "The submitted field name, forwarded to the native select."
31
+ option :id, :string, doc: "The select's dom id - the seam a Label's for_id: points at."
32
+ option :label, :string,
33
+ doc: "The accessible name for label-less placements (a visible Label paired via id:/for_id: is still " \
34
+ "the default pattern)."
35
+ option :size, :symbol, default: :default, doc: "The control size axis; :sm suits dense toolbars and table rows."
36
+ option :disabled, :boolean, default: false, doc: "Disables the native select; the wrapper dims the whole pair."
37
+ option :invalid, :boolean, default: false, doc: "Marks the select invalid (aria-invalid on the element itself)."
38
+ option :described_by, :string,
39
+ doc: "Space-separated hint/error ids wired to the SELECT itself - a raw aria-describedby in " \
40
+ "html_attributes would land on the wrapper div, unassociated for assistive technology."
41
+
42
+ validates :size, inclusion: { in: SIZES }
43
+
44
+ part "native-select-wrapper", "Relative shell around the select and the chevron - " \
45
+ "dims the pair when the select is disabled",
46
+ states: {
47
+ "data-size" => { condition: "always - the resolved size",
48
+ values: SIZES.map(&:to_s) }
49
+ }
50
+ part "native-select", "The real <select> - appearance-none (the chevron replaces the " \
51
+ "native arrow); platform picker and form submission stay native",
52
+ states: {
53
+ "data-size" => { condition: "always - the resolved size (mirrors the wrapper)",
54
+ values: SIZES.map(&:to_s) }
55
+ }
56
+ part "native-select-icon", "The decorative chevron wrapper - absolutely pinned, " \
57
+ "aria-hidden"
58
+ part "native-select-option", "An <option> from the options: fast path (or " \
59
+ "poetry_native_select_option) - Canvas system colors " \
60
+ "keep the native dropdown legible"
61
+
62
+ # @api private
63
+ def call
64
+ content_tag(:div, wrapper_attributes.to_attributes) do
65
+ safe_join([select_element, chevron])
66
+ end
67
+ end
68
+
69
+ # @api private
70
+ def wrapper_attributes
71
+ html_attributes.merge_if_not_set(
72
+ {
73
+ "data-slot" => "native-select-wrapper", "data-size" => size
74
+ }.merge(component_data_attributes)
75
+ )
76
+ end
77
+
78
+ # @api private
79
+ def select_attributes
80
+ attrs = { "data-slot" => "native-select", "data-size" => size, "class" => css(:select) }
81
+ attrs["name"] = name if name.present?
82
+ attrs["id"] = id if id.present?
83
+ attrs["aria-label"] = label if label.present?
84
+ attrs["aria-describedby"] = described_by if described_by.present?
85
+ attrs["disabled"] = true if disabled
86
+ attrs["aria-invalid"] = true if invalid
87
+ attrs
88
+ end
89
+
90
+ private
91
+
92
+ # options: pairs are structural data, not a typed option.
93
+ def initialize(options: nil, selected: nil, **)
94
+ super(**)
95
+ @options = options
96
+ @selected = selected
97
+ end
98
+
99
+ def select_element
100
+ content_tag(:select, select_content, select_attributes)
101
+ end
102
+
103
+ def select_content
104
+ return content if content.present?
105
+
106
+ safe_join(Array(@options).map do |entry|
107
+ label, value = entry.is_a?(Array) ? entry : [entry, entry]
108
+ tag.option(label, value: value, selected: value.to_s == @selected.to_s || nil,
109
+ "data-slot": "native-select-option", class: css(:option))
110
+ end)
111
+ end
112
+
113
+ # The chevron svg is the icon part itself (the source's shape) - the
114
+ # theme rule sizes and pins it; no wrapper span.
115
+ def chevron
116
+ render(Poetry::Ui::Icon::Component.new(name: :"chevron-down", class: css(:icon),
117
+ data: { slot: "native-select-icon" }))
118
+ end
119
+
120
+ private :wrapper_attributes, :select_attributes
121
+ end
122
+ end
123
+ end
124
+ end
@@ -0,0 +1,7 @@
1
+ <%# The labelled pair - a bare select has no accessible name. %>
2
+ <div class="flex flex-col gap-2 p-4">
3
+ <%= poetry_label(for_id: "native-select-sort") { "Sort by" } %>
4
+ <%= poetry_native_select(name: "sort", id: "native-select-sort",
5
+ options: [["Newest first", "newest"], ["Oldest first", "oldest"], ["Title", "title"]],
6
+ selected: "newest") %>
7
+ </div>
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Poetry
4
+ module Ui
5
+ module NativeSelect
6
+ # The NativeSelect preview: the labelled pair (its whole a11y
7
+ # contract), plus small/disabled states.
8
+ class Preview < Poetry::Core::Preview::Base
9
+ def default
10
+ render_with_template(template: "poetry/ui/native_select/labelled_preview")
11
+ end
12
+
13
+ def small_disabled
14
+ render_component(size: :sm, disabled: true, name: "plan", id: "native-select-plan",
15
+ label: "Plan", options: %w[Starter Team], selected: "Team")
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Poetry
4
+ module Ui
5
+ module NativeSelect
6
+ # Re-expressed through the cn-* theme layer. appearance-none
7
+ # stays inline as a mechanism guard (a swapped theme must never
8
+ # resurrect the double native arrow); the wrapper and the
9
+ # option/optgroup Canvas system-color guards are structural, no cn
10
+ # names. The chevron wrapper keeps position mechanics inline.
11
+ class Style < Poetry::Core::Style
12
+ base "relative w-fit has-[select:disabled]:opacity-50 group/native-select"
13
+
14
+ element :select, "cn-native-select w-full appearance-none outline-none " \
15
+ "disabled:pointer-events-none disabled:cursor-not-allowed"
16
+ element :icon, "cn-native-select-icon pointer-events-none absolute select-none"
17
+ element :option, "bg-[Canvas] text-[CanvasText]"
18
+ element :optgroup, "bg-[Canvas] text-[CanvasText]"
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,14 @@
1
+ <%# The site-nav bar: a trigger item with a link panel + plain links. %>
2
+ <div class="flex min-h-64 justify-center p-4">
3
+ <%= poetry_navigation_menu(label: "Main") do |nav| %>
4
+ <% nav.with_item("Products", value: "products") do %>
5
+ <div class="grid w-64 gap-1">
6
+ <%= poetry_navigation_menu_link(href: "#", active: true) { "Analytics" } %>
7
+ <%= poetry_navigation_menu_link(href: "#") { "Automation" } %>
8
+ <%= poetry_navigation_menu_link(href: "#") { "Reports" } %>
9
+ </div>
10
+ <% end %>
11
+ <% nav.with_link("Pricing", href: "#") %>
12
+ <% nav.with_link("Docs", href: "#") %>
13
+ <% end %>
14
+ </div>
@@ -0,0 +1,25 @@
1
+ <%= content_tag(:nav, **root_attributes.to_attributes) do %>
2
+ <div data-slot="navigation-menu-list" class="<%= css(:list) %>">
3
+ <% entries.each do |entry| %>
4
+ <%= content_tag(:div, item_attributes(entry)) do %>
5
+ <% if entry.panel %>
6
+ <%= content_tag(:button, trigger_attributes(entry)) do %>
7
+ <%= entry.title %>
8
+ <%= render Poetry::Ui::Icon::Component.new(name: :"chevron-down",
9
+ class: css(:chevron), "aria-hidden": "true") %>
10
+ <% end %>
11
+ <%= content_tag(:div, capture(&entry.panel), panel_attributes(entry)) %>
12
+ <% else %>
13
+ <a href="<%= entry.href %>" data-slot="navigation-menu-link" class="<%= css(:trigger) %>"><%= entry.title %></a>
14
+ <% end %>
15
+ <% end %>
16
+ <% end %>
17
+ </div>
18
+ <% if viewport %>
19
+ <%= content_tag(:div, positioner_attributes) do %>
20
+ <div data-slot="navigation-menu-popup" data-closed class="<%= css(:popup) %>">
21
+ <div data-slot="navigation-menu-viewport" class="<%= css(:viewport_shell) %>"></div>
22
+ </div>
23
+ <% end %>
24
+ <% end %>
25
+ <% end %>
@@ -0,0 +1,254 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Poetry
4
+ module Ui
5
+ # A site-navigation bar with disclosure panels.
6
+ module NavigationMenu
7
+ # A site-navigation bar - a <nav> landmark, never a menu role:
8
+ # top-level links plus triggers whose panels of links open on
9
+ # hover or click beneath their items. It is a DISCLOSURE bar,
10
+ # so Tab moves through it normally and nothing traps focus.
11
+ #
12
+ # By default each panel opens under its own item. viewport: true
13
+ # switches to one shared card that morphs its size and position
14
+ # between triggers - the fit for rich title-and-description panel
15
+ # grids.
16
+ #
17
+ # @example Disclosure bar with a panel and a link
18
+ # <%= poetry_navigation_menu(label: "Main") do |nav| %>
19
+ # <% nav.with_item("Products", value: "products") do %>
20
+ # <%= poetry_navigation_menu_link(href: products_path) { "All products" } %>
21
+ # <% end %>
22
+ # <% nav.with_link("Docs", href: docs_path) %>
23
+ # <% end %>
24
+ class Component < Poetry::Core::Component
25
+ # Projected into the registry, llms.txt, and the agent surface.
26
+ AGENT_RULES = [
27
+ "label: is REQUIRED (the nav landmark's accessible name).",
28
+ "with_item(title, value:) declares a trigger + panel; with_link(title, href:) is a " \
29
+ "top-level destination - use links for pages, panels for groups of links.",
30
+ "Panel content is poetry_navigation_menu_link entries (active: marks the current page) - " \
31
+ "never buttons; navigation navigates.",
32
+ "This is a DISCLOSURE bar: Tab moves through it normally and nothing traps - do not " \
33
+ "wire menu/menuitem roles.",
34
+ "Rich panels (title + description grids) want viewport: true - the shared morphing " \
35
+ "card contains and sizes them; the default per-item mode suits simple link lists " \
36
+ "(the top-nav block shows the viewport pattern)."
37
+ ].freeze
38
+
39
+ # One declared bar entry - a link (href:) or a trigger + panel pair.
40
+ # @api private
41
+ Entry = Data.define(:title, :value, :href, :panel)
42
+
43
+ renders_many :items,
44
+ doc: "The bar entries. with_item(title, value:) { panel } declares a trigger + panel; " \
45
+ "with_item(title, href:) a top-level link (with_link is the shorthand).",
46
+ renders: lambda { |title, value: nil, href: nil, &panel|
47
+ if href.nil? && panel.nil?
48
+ raise ArgumentError,
49
+ "NavigationMenu item #{title.inspect} needs href: (a link) or a panel block"
50
+ end
51
+
52
+ entries << Entry.new(title: title, value: value&.to_s || title.to_s.parameterize, href: href,
53
+ panel: panel)
54
+ nil
55
+ }
56
+
57
+ use_stimulus do
58
+ on :root do
59
+ controller :navigation_menu do
60
+ register
61
+ action :keydown, on: :keydown
62
+ action :focus_left, on: :focusout
63
+ end
64
+ # The whole positioning engine is viewport-gated.
65
+ controller :popper, if: :viewport do
66
+ register
67
+ value :side, "bottom"
68
+ value :align, "start"
69
+ value :side_offset, 6
70
+ value :strategy, "absolute"
71
+ end
72
+ end
73
+ # Hover intent per panel-bearing item (the call site gates
74
+ # per-entry - plain links carry no wiring).
75
+ on :item do
76
+ controller :navigation_menu do
77
+ action :schedule_open, on: :pointerenter
78
+ action :schedule_close, on: :pointerleave
79
+ end
80
+ end
81
+ on :trigger do
82
+ controller(:navigation_menu) { action :toggle, on: :click }
83
+ end
84
+ # BOTH controllers on ONE element, declared together - the wiring
85
+ # merges through the Builder, never as two separate Attributes
86
+ # joined with plain Hash#merge (that drops one controller's
87
+ # tokens).
88
+ on :positioner do
89
+ controller(:popper) { target :content }
90
+ controller :navigation_menu do
91
+ action :cancel_close, on: :pointerenter
92
+ action :schedule_close, on: :pointerleave
93
+ end
94
+ end
95
+ end
96
+
97
+ option :label, :string, required: true,
98
+ doc: "The nav landmark's accessible name - a page may hold more than one nav."
99
+ option :viewport, :boolean, default: false,
100
+ doc: "Opts into the shared morphing viewport: panels adopt into one positioned " \
101
+ "card that morphs size and position between triggers. Off, each panel opens " \
102
+ "under its own item (also the no-JS shape)."
103
+
104
+ part "navigation-menu", "The <nav> landmark around the whole disclosure bar",
105
+ states: {
106
+ "data-viewport" => "the mode marker (\"true\" = shared morphing viewport, \"false\" = " \
107
+ "per-item panels) - the dictionary's group-data chrome keys on it"
108
+ }
109
+ part "navigation-menu-list", "The bar row holding every item"
110
+ part "navigation-menu-item", "One bar entry - wraps a trigger + panel pair or a top-level link",
111
+ states: {
112
+ "data-value" => "the entry's value - the controller's open/close key"
113
+ }
114
+ part "navigation-menu-trigger", "The disclosure button opening its panel",
115
+ states: {
116
+ "data-popup-open" => "its panel is open (written with aria-expanded - the chevron " \
117
+ "rotation hook)",
118
+ "data-open" => "its panel is open (the controller writes both vocabularies)",
119
+ "data-closed" => "its panel is closed (written after the first close)"
120
+ }
121
+ part "navigation-menu-content", "One item's panel - presence-animated; in viewport mode it is " \
122
+ "adopted into the shared viewport on first activation",
123
+ states: {
124
+ "data-open" => "panel is open (presence flips the pair at runtime)",
125
+ "data-closed" => "panel is closed or animating out (the server-rendered state)",
126
+ "data-activation-direction" => "which way the activation traveled between triggers " \
127
+ "(left/right, viewport mode) - keys the slide styles",
128
+ "data-viewport-panel" => "stamped once the panel is adopted into the shared viewport"
129
+ }
130
+ part "navigation-menu-positioner", "The viewport-mode shell popper positions against the " \
131
+ "active trigger",
132
+ states: {
133
+ "data-instant" => "suppresses the morph transitions for one painted frame (cold opens)"
134
+ },
135
+ vars: {
136
+ "--positioner-width" => "the pinned morph width (reset to auto once the transition settles)",
137
+ "--positioner-height" => "the pinned morph height (reset to auto once the transition settles)"
138
+ }
139
+ part "navigation-menu-popup", "The morphing card inside the positioner - open state and the " \
140
+ "size transition ride here",
141
+ states: {
142
+ "data-open" => "a panel is showing (the controller flips the pair)",
143
+ "data-closed" => "the popup is closed (the server-rendered state)",
144
+ "data-instant" => "suppresses the morph transitions for one painted frame (cold opens)",
145
+ "data-starting-style" => "the enter transition's first frame (the presence module's " \
146
+ "two-frame trick)",
147
+ "data-ending-style" => "held through the exit transition before the popup hides"
148
+ },
149
+ vars: {
150
+ "--popup-width" => "the pinned morph width (reset to auto once the transition settles)",
151
+ "--popup-height" => "the pinned morph height (reset to auto once the transition settles)"
152
+ }
153
+ part "navigation-menu-viewport", "The adoption container inside the popup - adopted panels " \
154
+ "stack absolutely in it"
155
+ part "navigation-menu-link", "A REAL destination link - top-level (with_link) or a panel entry " \
156
+ "(poetry_navigation_menu_link)",
157
+ states: {
158
+ "data-active" => "the current page (active: true)"
159
+ }
160
+
161
+ # Enforces the required label and at least one entry.
162
+ # @api private
163
+ def before_render
164
+ raise ArgumentError, "NavigationMenu requires label: (the nav landmark's name)" if label.blank?
165
+ raise ArgumentError, "NavigationMenu requires at least one with_item or with_link" unless items?
166
+ end
167
+
168
+ # Declares a top-level destination link - shorthand for
169
+ # with_item(title, href:).
170
+ # @param title [String] the visible link text
171
+ # @param href [String] the destination URL
172
+ def with_link(title, href:)
173
+ with_item(title, href: href)
174
+ end
175
+
176
+ # The declared entries, in bar order.
177
+ # @api private
178
+ def entries
179
+ @entries ||= []
180
+ end
181
+
182
+ # @api private
183
+ def root_attributes
184
+ html_attributes.merge_if_not_set(
185
+ {
186
+ "aria-label" => label, "data-slot" => "navigation-menu",
187
+ # The mode marker the dictionary's group-data-[viewport=false]
188
+ # chrome keys on.
189
+ "data-viewport" => viewport.to_s
190
+ }.merge(stimulus_attributes_for(:root)).merge(component_data_attributes)
191
+ )
192
+ end
193
+
194
+ # @api private
195
+ def item_attributes(entry)
196
+ attrs = {
197
+ "data-slot" => "navigation-menu-item", "data-value" => entry.value,
198
+ "class" => css(:item)
199
+ }
200
+ attrs.merge!(stimulus_attributes_for(:item)) if entry.panel
201
+ attrs
202
+ end
203
+
204
+ # @api private
205
+ def trigger_attributes(entry)
206
+ {
207
+ "type" => "button", "data-slot" => "navigation-menu-trigger",
208
+ "aria-expanded" => "false", "aria-controls" => panel_id(entry),
209
+ "class" => "#{css(:trigger)} group"
210
+ }.merge(stimulus_attributes_for(:trigger))
211
+ end
212
+
213
+ # @api private
214
+ def panel_attributes(entry)
215
+ {
216
+ "id" => panel_id(entry), "data-slot" => "navigation-menu-content",
217
+ "data-closed" => "", "hidden" => true,
218
+ # Viewport mode: panels stack absolutely inside the shared viewport
219
+ # (adopted on first activation); per-item mode positions under the
220
+ # relative item.
221
+ "class" => "#{css(:content)} #{viewport ? css(:viewport_panel) : css(:content_position)}"
222
+ }
223
+ end
224
+
225
+ # The shared shell (viewport mode): the positioner is re-anchored
226
+ # against the ACTIVE trigger on each activation with the full
227
+ # positioning engine; the popup carries the morphing size vars;
228
+ # panels adopt into the viewport.
229
+ # @api private
230
+ def positioner_attributes
231
+ attrs = {
232
+ "data-slot" => "navigation-menu-positioner", "hidden" => true,
233
+ "class" => css(:positioner)
234
+ }
235
+ attrs.merge(stimulus_attributes_for(:positioner))
236
+ end
237
+
238
+ # @api private
239
+ def panel_id(entry)
240
+ "#{instance_id}-panel-#{entry.value}"
241
+ end
242
+
243
+ private
244
+
245
+ def instance_id
246
+ @instance_id ||= poetry_instance_id("poetry-nav")
247
+ end
248
+
249
+ private :entries, :root_attributes, :item_attributes, :trigger_attributes, :panel_attributes
250
+ private :positioner_attributes, :panel_id
251
+ end
252
+ end
253
+ end
254
+ end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Poetry
4
+ module Ui
5
+ module NavigationMenu
6
+ # The NavigationMenu preview: a bar with a panel item and plain links
7
+ # (the panel opens on hover/click - the dommy tier drives it; the
8
+ # sidecar template composes the panel's link grid).
9
+ class Preview < Poetry::Core::Preview::Base
10
+ def default
11
+ render_with_template(template: "poetry/ui/navigation_menu/bar_preview")
12
+ end
13
+
14
+ def viewport
15
+ render_with_template(template: "poetry/ui/navigation_menu/viewport_preview")
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,73 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Poetry
4
+ module Ui
5
+ module NavigationMenu
6
+ # Re-expressed through the cn-* theme layer. The morph
7
+ # machinery stays ENTIRELY inline (positioner insets + size-var
8
+ # transitions, panel adoption pins, activation-direction slides,
9
+ # data-instant suppressors) - a swapped theme restyles the popup
10
+ # surface, never the morph. The viewport=false chrome chain and the
11
+ # trigger/link treatments ride the theme.
12
+ class Style < Poetry::Core::Style
13
+ base "cn-navigation-menu group/navigation-menu relative flex max-w-max flex-1 items-center justify-center"
14
+
15
+ element :list, "cn-navigation-menu-list group flex flex-1 list-none items-center justify-center"
16
+ element :item, "relative"
17
+
18
+ element :trigger, "cn-navigation-menu-trigger group/navigation-menu-trigger inline-flex h-9 " \
19
+ "w-max items-center justify-center outline-none " \
20
+ "disabled:pointer-events-none disabled:opacity-50"
21
+ element :chevron, "cn-navigation-menu-trigger-icon relative top-px transition duration-300 " \
22
+ "group-data-popup-open/navigation-menu-trigger:rotate-180 " \
23
+ "group-data-open/navigation-menu-trigger:rotate-180"
24
+
25
+ # The activation-direction slides stay inline (authored as VALID
26
+ # Tailwind variants - upstream's own strings are malformed and
27
+ # compile to nothing): content slides
28
+ # in FROM the travel direction and out the other way.
29
+ # h-full is the VIEWPORT-mode contract (the panel fills the
30
+ # morphing popup box); inline panels size to their content - an
31
+ # unscoped h-full clamps them to the 36px list item and the
32
+ # popover chrome paints short of the rows.
33
+ element :content,
34
+ "cn-navigation-menu-content w-auto " \
35
+ "group-data-[viewport=true]/navigation-menu:h-full " \
36
+ "transition-[opacity,transform,translate] duration-[0.35s] " \
37
+ "ease-[cubic-bezier(0.22,1,0.36,1)] " \
38
+ "group-data-[viewport=false]/navigation-menu:duration-300 " \
39
+ "data-ending-style:opacity-0 data-starting-style:opacity-0 " \
40
+ "data-[activation-direction=right]:data-starting-style:translate-x-1/2 " \
41
+ "data-[activation-direction=left]:data-starting-style:-translate-x-1/2 " \
42
+ "data-[activation-direction=right]:data-ending-style:-translate-x-1/2 " \
43
+ "data-[activation-direction=left]:data-ending-style:translate-x-1/2 " \
44
+ "**:data-[slot=navigation-menu-link]:focus:ring-0 " \
45
+ "**:data-[slot=navigation-menu-link]:focus:outline-none"
46
+ element :content_position, "absolute top-full left-0 isolate z-50 mt-1.5 w-max min-w-48"
47
+
48
+ # The morphing shared viewport: the popper writes the positioner's
49
+ # insets (CSS transitions them - the position morph); the popup pins
50
+ # --popup-width/height old -> new so size transitions; panels stack
51
+ # absolutely inside the viewport and size intrinsically. data-instant
52
+ # (cold opens) suppresses both transitions for a frame.
53
+ element :positioner, "absolute isolate z-50 w-(--positioner-width) h-(--positioner-height) " \
54
+ "transition-[top,left,right,bottom] duration-[0.35s] " \
55
+ "ease-[cubic-bezier(0.22,1,0.36,1)] data-instant:transition-none"
56
+ element :popup, "cn-navigation-menu-popup relative h-(--popup-height) w-(--popup-width) " \
57
+ "overflow-hidden transition-[width,height] duration-[0.35s] " \
58
+ "ease-[cubic-bezier(0.22,1,0.36,1)] data-instant:transition-none"
59
+ # The ACTIVE panel sits in flow - after the settle reset returns the
60
+ # size vars to auto, IT is what sizes the popup (all-absolute panels
61
+ # would collapse the auto popup to 0x0). Only an EXITING panel lifts
62
+ # out of flow (data-ending-style rides the whole exit), overlaying
63
+ # the incoming one while both are visible - the crossfade, done
64
+ # declaratively.
65
+ element :viewport_shell, "relative overflow-hidden"
66
+ element :viewport_panel, "w-max min-w-48 data-ending-style:absolute " \
67
+ "data-ending-style:top-0 data-ending-style:left-0"
68
+
69
+ element :link, "cn-navigation-menu-link flex items-center outline-none"
70
+ end
71
+ end
72
+ end
73
+ end
@@ -0,0 +1,23 @@
1
+ <%# The morphing shared viewport: two panel items - switching between
2
+ them morphs the shared popup's size and position; panels slide by
3
+ activation direction. %>
4
+ <div class="flex min-h-72 justify-center p-4">
5
+ <%= poetry_navigation_menu(label: "Main", viewport: true) do |nav| %>
6
+ <% nav.with_item("Products", value: "products") do %>
7
+ <div class="grid w-64 gap-1">
8
+ <%= poetry_navigation_menu_link(href: "#", active: true) { "Analytics" } %>
9
+ <%= poetry_navigation_menu_link(href: "#") { "Automation" } %>
10
+ <%= poetry_navigation_menu_link(href: "#") { "Reports" } %>
11
+ </div>
12
+ <% end %>
13
+ <% nav.with_item("Company", value: "company") do %>
14
+ <div class="grid w-96 grid-cols-2 gap-1">
15
+ <%= poetry_navigation_menu_link(href: "#") { "About" } %>
16
+ <%= poetry_navigation_menu_link(href: "#") { "Careers" } %>
17
+ <%= poetry_navigation_menu_link(href: "#") { "Press" } %>
18
+ <%= poetry_navigation_menu_link(href: "#") { "Contact" } %>
19
+ </div>
20
+ <% end %>
21
+ <% nav.with_link("Docs", href: "#") %>
22
+ <% end %>
23
+ </div>
@@ -0,0 +1,19 @@
1
+ <%# Two-input architecture (Base UI): the group holds the formatted
2
+ visible text input between two stepper addons; the hidden number
3
+ input beside it is the form/validation truth. %>
4
+ <%= content_tag(:div, **root_attributes.to_attributes) do %>
5
+ <%= content_tag(:div, **Poetry::Core::HTML::Attributes.new(group_attributes).to_attributes) do %>
6
+ <%= content_tag(:div, **Poetry::Core::HTML::Attributes.new(addon_attributes(:start)).to_attributes) do %>
7
+ <%= render stepper(:decrement) do %>
8
+ <%= render Poetry::Ui::Icon::Component.new(name: stepper_icon(:decrement)) %>
9
+ <% end %>
10
+ <% end %>
11
+ <%= tag.input(**input_attributes.to_attributes) %>
12
+ <%= content_tag(:div, **Poetry::Core::HTML::Attributes.new(addon_attributes(:end)).to_attributes) do %>
13
+ <%= render stepper(:increment) do %>
14
+ <%= render Poetry::Ui::Icon::Component.new(name: stepper_icon(:increment)) %>
15
+ <% end %>
16
+ <% end %>
17
+ <% end %>
18
+ <%= tag.input(**hidden_attributes.to_attributes) %>
19
+ <% end %>