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,358 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Poetry
4
+ module Ui
5
+ # ContextMenu family: the right-click/long-press menu.
6
+ module ContextMenu
7
+ # Shared vocabularies, declared once at module level so the root
8
+ # Component and the nested menu-level classes read the same lists.
9
+ DIRS = %i[ltr rtl].freeze
10
+ # The closed vocabulary for the side axis.
11
+ SIDES = %i[top right bottom left].freeze
12
+
13
+ # The shared menus-family kernel wearing this family identity -
14
+ # SLOT_BUILDERS (reopened at the bottom, once the classes exist) keeps
15
+ # the registry recursion on the family classes.
16
+ #
17
+ # @api private
18
+ module ItemSlots
19
+ extend ActiveSupport::Concern
20
+ include Poetry::Ui::Menus::ItemSlots
21
+ end
22
+
23
+ # A context menu: the same menu anatomy as DropdownMenu, opened by
24
+ # right-click or long-press on a SURFACE instead of a button. The
25
+ # surface is NOT a widget - no role, no aria-haspopup, not in the
26
+ # tab order (unless focusable_surface: opts in) - and with no JS
27
+ # the browser-native context menu appears untouched. The menu
28
+ # anchors at the pointer position; side: picks which side of the
29
+ # pointer it opens toward (default right), and collisions can
30
+ # still flip it.
31
+ #
32
+ # @example Right-click surface with actions
33
+ # render Poetry::Ui::ContextMenu::Component.new do |menu|
34
+ # menu.with_trigger(tag: :div) { "Right-click this card" }
35
+ # menu.with_item { "Rename" }
36
+ # menu.with_item(variant: :destructive) { "Delete" }
37
+ # end
38
+ class Component < Poetry::Core::Component
39
+ include ItemSlots
40
+
41
+ # Projected into the registry, llms.txt, and the agent surface.
42
+ AGENT_RULES = [
43
+ "NEVER make a context menu the only path to an action - it is an invisible affordance; every " \
44
+ "item needs a visible equivalent (a '...' DropdownMenu button, a toolbar, a detail page).",
45
+ "Choose ContextMenu only for right-click-on-an-object semantics; a visible button opening a " \
46
+ "menu is DropdownMenu.",
47
+ "Do not add aria-haspopup or a role to the trigger surface; do not make it focusable except " \
48
+ "via focusable_surface: true.",
49
+ "side: picks which side of the pointer the menu opens toward (top/right/bottom/left, default " \
50
+ ":right); align and offsets are not API - collisions still flip the side.",
51
+ "Wrap the whole logical object (row/card) as the trigger surface, not a fragment.",
52
+ "Destructive items use variant: :destructive AND still confirm irreversible actions via a dialog.",
53
+ "shortcut: is a visual hint only - it does NOT bind the key.",
54
+ "Do not nest a ContextMenu trigger surface inside another ContextMenu trigger surface."
55
+ ].freeze
56
+
57
+ renders_one :trigger,
58
+ doc: "The right-click/long-press SURFACE: wraps arbitrary content (a card, a row, a region); " \
59
+ "polymorphic tag: (default :span, set tag: :div to wrap block content). NOT a button: no " \
60
+ "role, no aria-haspopup, no tabindex by default. The inline -webkit-touch-callout " \
61
+ "suppresses the iOS callout so long-press can run (iOS never fires contextmenu; the timer " \
62
+ "is the only touch path there).",
63
+ renders: lambda { |**options, &block|
64
+ tag_name = options.delete(:tag) || :span
65
+ attrs = {
66
+ "id" => trigger_id, "data-slot" => "context-menu-trigger",
67
+ "aria-controls" => content_id,
68
+ "style" => ["-webkit-touch-callout: none", options.delete(:style)].compact.join("; ")
69
+ }.merge(stimulus_attributes_for(:trigger))
70
+ # The surface state: bare data-popup-open while open, NO
71
+ # attribute while closed (absence IS the state).
72
+ attrs["data-popup-open"] = "" if open
73
+ attrs["data-disabled"] = "" if disabled
74
+ if focusable_surface
75
+ attrs["tabindex"] = "0"
76
+ attrs["aria-keyshortcuts"] = "Shift+F10"
77
+ end
78
+ content_tag(tag_name, Poetry::Core::HTML::Attributes.merged(attrs, options)) { capture(&block) }
79
+ }
80
+
81
+ use_stimulus do
82
+ on :root do
83
+ controller :context_menu do
84
+ register
85
+ value :long_press_delay
86
+ value :disabled
87
+ end
88
+ controller :menu do
89
+ register
90
+ value :open
91
+ value :modal
92
+ value :loop
93
+ end
94
+ controller :popper do
95
+ register
96
+ # side: is the one placement knob; align start / side_offset
97
+ # 2 stay fixed; collisions still flip the side.
98
+ value :side
99
+ value :align, :start
100
+ value :side_offset, 2
101
+ value :avoid_collisions, true
102
+ end
103
+ end
104
+ # The surface's wiring: contextmenu + the long-press pointer set;
105
+ # popper's anchor target is the FALLBACK rect for positionless
106
+ # opens (the stored virtual anchor point wins).
107
+ on :trigger do
108
+ controller :context_menu do
109
+ action :open, on: :contextmenu
110
+ action :press_start, on: :pointerdown
111
+ action :press_cancel, on: %i[pointermove pointerup pointercancel]
112
+ end
113
+ controller(:popper) { target :anchor }
114
+ end
115
+ on :content do
116
+ controller(:popper) { target :content }
117
+ end
118
+ on :item do
119
+ controller(:menu) { action :activate, on: :click }
120
+ end
121
+ on :sub_trigger do
122
+ controller :menu do
123
+ action :sub_enter, on: :pointerenter
124
+ action :sub_leave, on: :pointerleave
125
+ action :open_sub, on: :click
126
+ end
127
+ controller(:popper) { target :anchor }
128
+ end
129
+ end
130
+
131
+ option :open, :boolean, default: false,
132
+ doc: "Server-renders the menu open (rare - context menus normally open from the " \
133
+ "gesture)."
134
+ option :modal, :boolean, default: true, doc: "Traps focus in the open menu; false keeps the page interactive."
135
+ option :loop, :boolean, default: false, doc: "Wraps arrow-key movement past either end of the menu."
136
+ option :long_press_delay, :integer, default: 700, doc: "Touch long-press duration in ms before the menu opens."
137
+ option :disabled, :boolean, default: false, doc: "Inerts the surface - no gesture opens the menu."
138
+ option :label, :string, doc: "The menu's accessible name (localized fallback when omitted)."
139
+ option :focusable_surface, :boolean, default: false,
140
+ doc: "Puts the surface in the tab order and advertises Shift+F10."
141
+ option :dir, :symbol, doc: "Writing-direction override (ltr/rtl) stamped on the root."
142
+ option :side, :symbol, default: :right,
143
+ doc: "Which side of the pointer the menu opens toward; collisions may still flip it."
144
+
145
+ validates :dir, inclusion: { in: DIRS }, allow_nil: true
146
+ validates :side, inclusion: { in: SIDES }
147
+
148
+ part "context-menu", "Root wrapper hosting the context-menu + menu + popper controllers " \
149
+ "around the surface and content"
150
+ part "context-menu-trigger", "The right-click/long-press SURFACE wrapping the logical object " \
151
+ "- not a widget: no role, no aria-haspopup",
152
+ states: {
153
+ "data-popup-open" => "the menu is open (absence is the closed state - no aria-expanded " \
154
+ "on a role-less surface)",
155
+ "data-disabled" => "the surface is inert (disabled: true)"
156
+ }
157
+ part "context-menu-content", "The role=menu popup panel - anchored at the pointer via popper's " \
158
+ "virtual-anchor mode; open state and animation ride here",
159
+ states: {
160
+ "data-open" => "menu is open (presence flips the pair at runtime)",
161
+ "data-closed" => "menu is closed or animating out (the server-rendered state)",
162
+ "data-side" => { condition: "the placement side (the side: option, default right; popper " \
163
+ "re-writes it after collision flips)",
164
+ values: %w[top right bottom left] },
165
+ "data-align" => { condition: "the alignment (forced start initially; popper re-resolves it)",
166
+ values: %w[start center end] }
167
+ },
168
+ vars: {
169
+ "--transform-origin" => "popper's anchor-facing animation origin",
170
+ "--available-width" => "popper: viewport space left for the panel (post-flip)",
171
+ "--available-height" => "popper: viewport space left for the panel (post-flip)",
172
+ "--anchor-width" => "popper: the anchor rect's measured width",
173
+ "--anchor-height" => "popper: the anchor rect's measured height"
174
+ }
175
+ part "context-menu-group", "role=group semantic grouping between separators"
176
+ part "context-menu-label", "Non-interactive heading for a run of items",
177
+ states: {
178
+ "data-inset" => "indented to align with checkbox/radio item text (inset: true)"
179
+ }
180
+ part "context-menu-item", "One role=menuitem action row",
181
+ states: {
182
+ "data-variant" => "default or destructive (the danger treatment)",
183
+ "data-inset" => "indented to align with checkbox/radio item text (inset: true)",
184
+ "data-disabled" => "item is disabled (always written together with aria-disabled)"
185
+ }
186
+ part "context-menu-checkbox-item", "A role=menuitemcheckbox toggle row",
187
+ states: {
188
+ "data-checked" => "checked (the controller re-writes the pair with aria-checked on " \
189
+ "activation)",
190
+ "data-unchecked" => "unchecked",
191
+ "data-disabled" => "item is disabled (always written together with aria-disabled)",
192
+ "data-close-on-select" => "per-item override of the menu's close-on-select default " \
193
+ "(\"false\" keeps the menu open)"
194
+ }
195
+ part "context-menu-radio-group", "role=group scoping one single-select value",
196
+ states: {
197
+ "data-value" => "the selected radio value (the controller re-writes it on change)"
198
+ }
199
+ part "context-menu-radio-item", "A role=menuitemradio row inside a radio group",
200
+ states: {
201
+ "data-checked" => "the selected radio (the controller re-writes the pair with aria-checked)",
202
+ "data-unchecked" => "not selected",
203
+ "data-value" => "the radio's value",
204
+ "data-disabled" => "item is disabled (always written together with aria-disabled)"
205
+ }
206
+ part "context-menu-checkbox-item-indicator",
207
+ "The check glyph inside checkbox items (aria-hidden; the item carries the checked state)"
208
+ part "context-menu-radio-item-indicator",
209
+ "The circle glyph inside radio items (aria-hidden; the item carries the checked state)"
210
+ part "context-menu-separator", "role=separator rule between groups"
211
+ part "context-menu-shortcut", "The trailing keybinding HINT - aria-hidden, never binds the key"
212
+ part "context-menu-sub", "A submenu scope - hosts its own popper around the sub trigger/" \
213
+ "content pair"
214
+ part "context-menu-sub-trigger", "The role=menuitem row opening its submenu",
215
+ states: {
216
+ "data-popup-open" => "its submenu is open (written with aria-expanded; absence is the " \
217
+ "closed state)",
218
+ "data-inset" => "indented to align with checkbox/radio item text (inset: true)"
219
+ }
220
+ part "context-menu-sub-content", "The nested role=menu panel - its own popper content on the " \
221
+ "same presence machinery",
222
+ states: {
223
+ "data-open" => "submenu is open (presence flips the pair at runtime)",
224
+ "data-closed" => "submenu is closed (the server-rendered state)",
225
+ "data-side" => { condition: "the placement side (right/left by direction; popper resolves " \
226
+ "it at runtime)",
227
+ values: %w[top right bottom left] },
228
+ "data-align" => { condition: "the alignment against the sub-trigger (popper resolves it " \
229
+ "at runtime)",
230
+ values: %w[start center end] }
231
+ },
232
+ vars: {
233
+ "--transform-origin" => "popper's anchor-facing animation origin",
234
+ "--available-width" => "popper: viewport space left for the panel (post-flip)",
235
+ "--available-height" => "popper: viewport space left for the panel (post-flip)",
236
+ "--anchor-width" => "popper: the sub-trigger's measured width",
237
+ "--anchor-height" => "popper: the sub-trigger's measured height"
238
+ }
239
+
240
+ # Enforces the required surface and at least one item.
241
+ # @api private
242
+ def before_render
243
+ raise ArgumentError, "ContextMenu requires with_trigger (the right-click surface)" unless trigger?
244
+ raise ArgumentError, "ContextMenu requires at least one item" unless items?
245
+ end
246
+
247
+ # The surface's id.
248
+ # @api private
249
+ def trigger_id
250
+ "#{instance_id}-trigger"
251
+ end
252
+
253
+ # The menu panel's id - the surface's aria-controls target.
254
+ # @api private
255
+ def content_id
256
+ "#{instance_id}-content"
257
+ end
258
+
259
+ # Attributes for the root wrapper.
260
+ # @api private
261
+ def root_attributes
262
+ root = { "data-slot" => "context-menu" }
263
+ root["dir"] = dir.to_s if dir
264
+ html_attributes.merge_if_not_set(
265
+ root.merge(stimulus_attributes_for(:root)).merge(component_data_attributes)
266
+ )
267
+ end
268
+
269
+ # Attributes for the role=menu panel.
270
+ # @api private
271
+ def content_attributes
272
+ attrs = {
273
+ "id" => content_id, "role" => "menu", "aria-orientation" => "vertical",
274
+ # No aria-labelledby to the trigger (it is not a widget - the
275
+ # delta): the menu's name is label: -> the i18n fallback.
276
+ "aria-label" => label.presence || t("poetry.context_menu.menu_label_fallback"),
277
+ "tabindex" => "-1",
278
+ "data-slot" => "context-menu-content", (open ? "data-open" : "data-closed") => "",
279
+ # The initial placement (side: option; re-resolved live by popper).
280
+ "data-side" => side.to_s, "data-align" => "start",
281
+ "class" => css(:content)
282
+ }.merge(stimulus_attributes_for(:content))
283
+ attrs["hidden"] = true unless open
284
+ attrs
285
+ end
286
+
287
+ private
288
+
289
+ def menu_dir
290
+ dir
291
+ end
292
+
293
+ # Server-stable unique id pair (aria-controls is the controller's
294
+ # portal-safe trigger->content seam).
295
+ def instance_id
296
+ @instance_id ||= poetry_instance_id("poetry-context-menu")
297
+ end
298
+
299
+ private :trigger_id, :content_id, :root_attributes, :content_attributes
300
+ end
301
+
302
+ # role=group semantic grouping between separators - the shared kernel
303
+ # anatomy wearing this family identity.
304
+ #
305
+ # @api private
306
+ class Group < Poetry::Ui::Menus::Group
307
+ include ItemSlots
308
+ end
309
+
310
+ # role=group scoping the single-select value for its radio items -
311
+ # the shared kernel anatomy wearing this family identity.
312
+ #
313
+ # @api private
314
+ class RadioGroup < Poetry::Ui::Menus::RadioGroup
315
+ end
316
+
317
+ # A submenu scope (recursive item union on its own popper) - the
318
+ # shared kernel anatomy wearing this family identity.
319
+ #
320
+ # @api private
321
+ class Sub < Poetry::Ui::Menus::Sub
322
+ include ItemSlots
323
+ end
324
+
325
+ # The builder classes behind lambda-wrapped slot types: a
326
+ # lambda hides its return class from introspection, so the owner
327
+ # declares it and the registry walker recurses into the builder's own
328
+ # call surface (with_sub yields a Sub with its own items).
329
+ # REQUIRED_SLOTS states the same facts the before_render
330
+ # raises enforce, so static checks can flag omissions without
331
+ # rendering.
332
+ module ItemSlots
333
+ # Nested-slot recursion map for the registry projection.
334
+ SLOT_BUILDERS = { sub: Sub, group: Group, radio_group: RadioGroup }.freeze
335
+ end
336
+
337
+ class Component
338
+ # The required slots, stated statically for static checks.
339
+ REQUIRED_SLOTS = { trigger: "the right-click surface", item: "at least one item" }.freeze
340
+ end
341
+
342
+ class Group
343
+ # The required slots, stated statically for static checks.
344
+ REQUIRED_SLOTS = { item: "at least one item" }.freeze
345
+ end
346
+
347
+ class RadioGroup
348
+ # The required slots, stated statically for static checks.
349
+ REQUIRED_SLOTS = { radio_item: "at least one radio item" }.freeze
350
+ end
351
+
352
+ class Sub
353
+ # The required slots, stated statically for static checks.
354
+ REQUIRED_SLOTS = { trigger: "the sub-menu item", item: "at least one item" }.freeze
355
+ end
356
+ end
357
+ end
358
+ end
@@ -0,0 +1,111 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Poetry
4
+ module Ui
5
+ module ContextMenu
6
+ # The ContextMenu preview matrix: the ported context-menu-demo port
7
+ # (items + shortcuts, a submenu, checkbox items, a radio group) on a
8
+ # right-click surface, plus inset/disabled items, the disabled
9
+ # surface (native menu returns), RTL, the focusable-surface keyboard
10
+ # mode, and the server-rendered open state. Long-press/pointer
11
+ # anchoring is runtime behavior - the browser pass drives it.
12
+ class Preview < Poetry::Core::Preview::Base
13
+ SURFACE_CLASSES = "flex h-[150px] w-[300px] items-center justify-center rounded-md " \
14
+ "border border-dashed text-sm select-none"
15
+
16
+ # The context-menu-demo port: right-click the dashed surface.
17
+ def default
18
+ render_component do |menu|
19
+ menu.with_trigger(tag: :div, class: SURFACE_CLASSES) { "Right-click here" }
20
+ menu.with_item(inset: true, shortcut: "⌘[") { "Back" }
21
+ menu.with_item(inset: true, disabled: true, shortcut: "⌘]") { "Forward" }
22
+ menu.with_item(inset: true, shortcut: "⌘R") { "Reload" }
23
+ menu.with_sub do |sub|
24
+ sub.with_trigger(inset: true) { "More Tools" }
25
+ sub.with_item(shortcut: "⇧⌘S") { "Save Page As..." }
26
+ sub.with_item { "Create Shortcut..." }
27
+ sub.with_item { "Name Window..." }
28
+ sub.with_separator
29
+ sub.with_item { "Developer Tools" }
30
+ end
31
+ menu.with_separator
32
+ menu.with_checkbox_item(checked: true, close_on_select: false, shortcut: "⌘⇧B") { "Show Bookmarks Bar" }
33
+ menu.with_checkbox_item(checked: false, close_on_select: false) { "Show Full URLs" }
34
+ menu.with_separator
35
+ menu.with_radio_group(value: "pedro") do |group|
36
+ group.with_radio_item(value: "pedro") { "Pedro Duarte" }
37
+ group.with_radio_item(value: "colm") { "Colm Tuite" }
38
+ end
39
+ end
40
+ end
41
+
42
+ # A destructive tail + a named menu (label: -> aria-label).
43
+ def destructive
44
+ render_component(label: "File actions") do |menu|
45
+ menu.with_trigger(tag: :div, class: SURFACE_CLASSES) { "quarterly-report.pdf" }
46
+ menu.with_item { "Open" }
47
+ menu.with_item { "Rename" }
48
+ menu.with_separator
49
+ menu.with_item(variant: :destructive, shortcut: "⌘⌫") { "Delete" }
50
+ end
51
+ end
52
+
53
+ # disabled: true stands the handlers down - right-click here shows
54
+ # the BROWSER-NATIVE context menu (never a dead surface).
55
+ def disabled_surface
56
+ render_component(disabled: true) do |menu|
57
+ menu.with_trigger(tag: :div, class: SURFACE_CLASSES) { "Native menu here (disabled)" }
58
+ menu.with_item { "Unreachable" }
59
+ end
60
+ end
61
+
62
+ # focusable_surface: true opts the surface into the tab order with
63
+ # the aria-keyshortcuts hint - Shift+F10 / the Menu key open it.
64
+ def focusable_surface
65
+ render_component(focusable_surface: true, label: "Row actions") do |menu|
66
+ menu.with_trigger(tag: :div, class: SURFACE_CLASSES) { "Focusable row (Shift+F10)" }
67
+ menu.with_item { "Rename" }
68
+ menu.with_item { "Duplicate" }
69
+ end
70
+ end
71
+
72
+ # The submenu side flips under RTL.
73
+ def rtl
74
+ render_component(dir: :rtl) do |menu|
75
+ menu.with_trigger(tag: :div, class: SURFACE_CLASSES) { "انقر بزر الماوس الأيمن" }
76
+ menu.with_item { "فتح" }
77
+ menu.with_sub do |sub|
78
+ sub.with_trigger { "المزيد" }
79
+ sub.with_item { "استيراد" }
80
+ end
81
+ end
82
+ end
83
+
84
+ # A grouped run - the role=group wrapper between separators.
85
+ def grouped
86
+ render_component do |menu|
87
+ menu.with_trigger(tag: :div, class: SURFACE_CLASSES) { "Surface" }
88
+ menu.with_group do |group|
89
+ group.with_item { "Cut" }
90
+ group.with_item { "Copy" }
91
+ end
92
+ menu.with_separator
93
+ menu.with_checkbox_item(checked: true, disabled: true) { "Locked toggle" }
94
+ end
95
+ end
96
+
97
+ # Server-rendered open state (controllable-state: the attributes
98
+ # are the store; positionless open anchors to the trigger rect).
99
+ def open
100
+ render_component(open: true) do |menu|
101
+ menu.with_trigger(tag: :div, class: SURFACE_CLASSES) { "Surface" }
102
+ menu.with_label { "Row" }
103
+ menu.with_item { "Rename" }
104
+ menu.with_separator
105
+ menu.with_item(variant: :destructive) { "Delete" }
106
+ end
107
+ end
108
+ end
109
+ end
110
+ end
111
+ end
@@ -0,0 +1,59 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Poetry
4
+ module Ui
5
+ module ContextMenu
6
+ # Re-expressed through the cn-* theme layer. The family deltas
7
+ # vs DropdownMenu now read off the theme rules (context-menu label
8
+ # adds text-foreground, the sub-trigger omits gap-2, the sub chevron
9
+ # is a bare ml-auto); the trigger SURFACE still ships no classes.
10
+ class Style < Poetry::Core::Style
11
+ element :content, "cn-context-menu-content cn-menu-translucent z-50 max-h-(--available-height) " \
12
+ "origin-(--transform-origin) overflow-x-hidden overflow-y-auto outline-none"
13
+
14
+ # group/context-menu-item: bare marker, no CSS - the vega theme's
15
+ # shortcut re-color (group-focus/context-menu-item) keys on it.
16
+ element :item, "cn-context-menu-item group/context-menu-item relative flex cursor-default " \
17
+ "items-center outline-hidden select-none [&_svg]:pointer-events-none [&_svg]:shrink-0 " \
18
+ "data-disabled:opacity-50 data-disabled:pointer-events-none"
19
+
20
+ element :checkbox_item, "cn-context-menu-checkbox-item relative flex cursor-default items-center " \
21
+ "outline-hidden select-none [&_svg]:pointer-events-none [&_svg]:shrink-0 " \
22
+ "data-disabled:opacity-50 data-disabled:pointer-events-none"
23
+
24
+ element :radio_item, "cn-context-menu-radio-item relative flex cursor-default items-center outline-hidden " \
25
+ "select-none [&_svg]:pointer-events-none [&_svg]:shrink-0 data-disabled:opacity-50 " \
26
+ "data-disabled:pointer-events-none"
27
+
28
+ element :label, "cn-context-menu-label"
29
+
30
+ element :separator, "cn-context-menu-separator"
31
+
32
+ element :shortcut, "cn-context-menu-shortcut"
33
+
34
+ element :sub_trigger, "cn-context-menu-sub-trigger flex cursor-default items-center " \
35
+ "outline-hidden select-none [&_svg]:pointer-events-none [&_svg]:shrink-0"
36
+
37
+ element :sub_content, "cn-context-menu-sub-content z-50 " \
38
+ "origin-(--transform-origin) overflow-hidden"
39
+
40
+ # Source-exact wrapper span (anonymous in the ported source; poetry names
41
+ # it context-menu-item-indicator - the self-identification rule).
42
+ element :item_indicator, "cn-context-menu-item-indicator pointer-events-none absolute " \
43
+ "flex items-center justify-center"
44
+
45
+ # POETRY ADDITION (family convention): the indicator stays in the
46
+ # DOM; the parent item's data-checked/data-unchecked pair drives
47
+ # visibility.
48
+ element :item_indicator_state, "[[data-unchecked]>&]:hidden"
49
+
50
+ # The source's inline lucide icon classes, named per part. DELTA:
51
+ # the context sub chevron is a bare ml-auto (the item's [&_svg]
52
+ # rules provide size-4).
53
+ element :indicator_check, "size-4"
54
+ element :indicator_circle, "size-2 fill-current"
55
+ element :sub_indicator, "ml-auto"
56
+ end
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,78 @@
1
+ <% surface = capture do %>
2
+ <%= content_tag(:div, **root_attributes.to_attributes) do %>
3
+ <% if filter %>
4
+ <div data-slot="data-table-toolbar" class="<%= css(:toolbar) %>">
5
+ <form method="get" action="<%= filter_form_action %>" role="search" aria-label="<%= filter_label %>" data-slot="data-table-filter">
6
+ <% state.filter_params.each do |name, value| %>
7
+ <input type="hidden" name="<%= name %>" value="<%= value %>">
8
+ <% end %>
9
+ <label for="<%= filter_id %>" class="sr-only"><%= filter_label %></label>
10
+ <%= render Poetry::Ui::Input::Component.new(id: filter_id, name: filter_name,
11
+ value: state.q, placeholder: filter_placeholder,
12
+ class: "max-w-sm") %>
13
+ </form>
14
+ </div>
15
+ <% end %>
16
+ <div class="<%= css(:container) %>">
17
+ <%= render Poetry::Ui::Table::Component.new(sticky_header: sticky_header, container_class: container_class,
18
+ scroll_label: scroll_label.presence || caption) do %>
19
+ <% if caption.present? %>
20
+ <caption data-slot="table-caption" class="<%= Poetry::Ui::Table::Style.css(:caption, class: css(:caption)) %>"><%= caption %></caption>
21
+ <% end %>
22
+ <thead data-slot="table-header" class="<%= Poetry::Ui::Table::Style.css(:header) %>">
23
+ <tr data-slot="table-row" class="<%= Poetry::Ui::Table::Style.css(:row) %>">
24
+ <% if selectable? %>
25
+ <th data-slot="table-head" class="<%= Poetry::Ui::Table::Style.css(:head) %> <%= css(:select_cell) %>">
26
+ <%= tag.input(**Poetry::Core::HTML::Attributes.new(select_all_attributes).to_attributes) %>
27
+ </th>
28
+ <% end %>
29
+ <% column_defs.each do |column| %>
30
+ <%= content_tag(:th, head_attributes(column)) do %>
31
+ <% if column.sortable %>
32
+ <%= render Poetry::Ui::Button::Component.new(**sort_link_options(column)) do |button| %>
33
+ <% button.with_trailing { render Poetry::Ui::Icon::Component.new(name: sort_icon(column)) } %>
34
+ <%= column.label %>
35
+ <% end %>
36
+ <% else %>
37
+ <%= column.label %>
38
+ <% end %>
39
+ <% end %>
40
+ <% end %>
41
+ </tr>
42
+ </thead>
43
+ <tbody data-slot="table-body" class="<%= Poetry::Ui::Table::Style.css(:body) %>">
44
+ <% if rows.any? %>
45
+ <% rows.each do |row| %>
46
+ <tr data-slot="table-row" class="<%= Poetry::Ui::Table::Style.css(:row) %>">
47
+ <% if selectable? %>
48
+ <td data-slot="table-cell" class="<%= Poetry::Ui::Table::Style.css(:cell) %> <%= css(:select_cell) %>">
49
+ <%= tag.input(**Poetry::Core::HTML::Attributes.new(select_row_attributes(row)).to_attributes) %>
50
+ </td>
51
+ <% end %>
52
+ <% column_defs.each do |column| %>
53
+ <%= content_tag(:td, column.cell.call(row), cell_attributes(column)) %>
54
+ <% end %>
55
+ </tr>
56
+ <% end %>
57
+ <% else %>
58
+ <tr data-slot="table-row" class="<%= Poetry::Ui::Table::Style.css(:row) %>">
59
+ <td data-slot="table-cell" colspan="<%= column_defs.size + (selectable? ? 1 : 0) %>"
60
+ class="<%= Poetry::Ui::Table::Style.css(:cell) %> <%= css(:empty) %>"><%= empty_text %></td>
61
+ </tr>
62
+ <% end %>
63
+ </tbody>
64
+ <% end %>
65
+ </div>
66
+ <% if pagination? %>
67
+ <div data-slot="data-table-footer" class="<%= css(:footer) %>">
68
+ <%= render Poetry::Ui::Pagination::Component.new(current: state.page, total: total,
69
+ path: ->(page) { path_for(state.page_params(page)) }) %>
70
+ </div>
71
+ <% end %>
72
+ <% end %>
73
+ <% end %>
74
+ <% if frame.present? %>
75
+ <turbo-frame id="<%= frame %>" data-turbo-action="advance"><%= surface %></turbo-frame>
76
+ <% else %>
77
+ <%= surface %>
78
+ <% end %>