uikit_rails 0.1.1

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 (378) hide show
  1. checksums.yaml +7 -0
  2. data/README.md +301 -0
  3. data/Rakefile +12 -0
  4. data/app/controllers/uikit_rails/styleguide_controller.rb +72 -0
  5. data/app/views/layouts/uikit_rails/application.html.erb +412 -0
  6. data/app/views/uikit_rails/styleguide/index.html.erb +27 -0
  7. data/app/views/uikit_rails/styleguide/show.html.erb +56 -0
  8. data/config/routes.rb +6 -0
  9. data/lib/uikit_rails/engine.rb +13 -0
  10. data/lib/uikit_rails/generators/add_generator.rb +110 -0
  11. data/lib/uikit_rails/generators/install_generator.rb +62 -0
  12. data/lib/uikit_rails/generators/templates/base_component.rb.tt +32 -0
  13. data/lib/uikit_rails/generators/templates/uikit_rails.css.tt +69 -0
  14. data/lib/uikit_rails/templates/components/accordion/USAGE +30 -0
  15. data/lib/uikit_rails/templates/components/accordion/accordion.css +74 -0
  16. data/lib/uikit_rails/templates/components/accordion/component.html.erb +13 -0
  17. data/lib/uikit_rails/templates/components/accordion/component.rb +35 -0
  18. data/lib/uikit_rails/templates/components/accordion/preview.yml +81 -0
  19. data/lib/uikit_rails/templates/components/alert/USAGE +25 -0
  20. data/lib/uikit_rails/templates/components/alert/alert.css +60 -0
  21. data/lib/uikit_rails/templates/components/alert/component.html.erb +9 -0
  22. data/lib/uikit_rails/templates/components/alert/component.rb +27 -0
  23. data/lib/uikit_rails/templates/components/alert/preview.yml +53 -0
  24. data/lib/uikit_rails/templates/components/alert_dialog/USAGE +38 -0
  25. data/lib/uikit_rails/templates/components/alert_dialog/alert_dialog.css +108 -0
  26. data/lib/uikit_rails/templates/components/alert_dialog/component.html.erb +27 -0
  27. data/lib/uikit_rails/templates/components/alert_dialog/component.rb +23 -0
  28. data/lib/uikit_rails/templates/components/alert_dialog/preview.yml +94 -0
  29. data/lib/uikit_rails/templates/components/avatar/USAGE +20 -0
  30. data/lib/uikit_rails/templates/components/avatar/avatar.css +53 -0
  31. data/lib/uikit_rails/templates/components/avatar/component.html.erb +7 -0
  32. data/lib/uikit_rails/templates/components/avatar/component.rb +31 -0
  33. data/lib/uikit_rails/templates/components/avatar/preview.yml +42 -0
  34. data/lib/uikit_rails/templates/components/badge/USAGE +13 -0
  35. data/lib/uikit_rails/templates/components/badge/badge.css +61 -0
  36. data/lib/uikit_rails/templates/components/badge/component.rb +28 -0
  37. data/lib/uikit_rails/templates/components/badge/preview.yml +38 -0
  38. data/lib/uikit_rails/templates/components/breadcrumb/USAGE +23 -0
  39. data/lib/uikit_rails/templates/components/breadcrumb/breadcrumb.css +55 -0
  40. data/lib/uikit_rails/templates/components/breadcrumb/component.html.erb +14 -0
  41. data/lib/uikit_rails/templates/components/breadcrumb/component.rb +40 -0
  42. data/lib/uikit_rails/templates/components/breadcrumb/preview.yml +42 -0
  43. data/lib/uikit_rails/templates/components/button/USAGE +21 -0
  44. data/lib/uikit_rails/templates/components/button/button.css +125 -0
  45. data/lib/uikit_rails/templates/components/button/component.rb +44 -0
  46. data/lib/uikit_rails/templates/components/button/preview.yml +106 -0
  47. data/lib/uikit_rails/templates/components/card/USAGE +33 -0
  48. data/lib/uikit_rails/templates/components/card/card.css +63 -0
  49. data/lib/uikit_rails/templates/components/card/component.html.erb +16 -0
  50. data/lib/uikit_rails/templates/components/card/component.rb +26 -0
  51. data/lib/uikit_rails/templates/components/card/preview.yml +57 -0
  52. data/lib/uikit_rails/templates/components/checkbox/USAGE +19 -0
  53. data/lib/uikit_rails/templates/components/checkbox/checkbox.css +67 -0
  54. data/lib/uikit_rails/templates/components/checkbox/component.html.erb +6 -0
  55. data/lib/uikit_rails/templates/components/checkbox/component.rb +26 -0
  56. data/lib/uikit_rails/templates/components/checkbox/preview.yml +43 -0
  57. data/lib/uikit_rails/templates/components/collapsible/USAGE +31 -0
  58. data/lib/uikit_rails/templates/components/collapsible/collapsible.css +55 -0
  59. data/lib/uikit_rails/templates/components/collapsible/component.html.erb +8 -0
  60. data/lib/uikit_rails/templates/components/collapsible/component.rb +18 -0
  61. data/lib/uikit_rails/templates/components/collapsible/preview.yml +65 -0
  62. data/lib/uikit_rails/templates/components/dialog/USAGE +41 -0
  63. data/lib/uikit_rails/templates/components/dialog/component.html.erb +23 -0
  64. data/lib/uikit_rails/templates/components/dialog/component.rb +20 -0
  65. data/lib/uikit_rails/templates/components/dialog/dialog.css +133 -0
  66. data/lib/uikit_rails/templates/components/dialog/preview.yml +77 -0
  67. data/lib/uikit_rails/templates/components/dropdown/USAGE +40 -0
  68. data/lib/uikit_rails/templates/components/dropdown/component.html.erb +14 -0
  69. data/lib/uikit_rails/templates/components/dropdown/component.rb +62 -0
  70. data/lib/uikit_rails/templates/components/dropdown/dropdown.css +104 -0
  71. data/lib/uikit_rails/templates/components/dropdown/preview.yml +75 -0
  72. data/lib/uikit_rails/templates/components/form/USAGE +51 -0
  73. data/lib/uikit_rails/templates/components/form/builder.rb +233 -0
  74. data/lib/uikit_rails/templates/components/form/form.css +48 -0
  75. data/lib/uikit_rails/templates/components/form/preview.yml +95 -0
  76. data/lib/uikit_rails/templates/components/input/USAGE +21 -0
  77. data/lib/uikit_rails/templates/components/input/component.rb +25 -0
  78. data/lib/uikit_rails/templates/components/input/input.css +43 -0
  79. data/lib/uikit_rails/templates/components/input/preview.yml +58 -0
  80. data/lib/uikit_rails/templates/components/label/USAGE +16 -0
  81. data/lib/uikit_rails/templates/components/label/component.rb +25 -0
  82. data/lib/uikit_rails/templates/components/label/label.css +25 -0
  83. data/lib/uikit_rails/templates/components/label/preview.yml +34 -0
  84. data/lib/uikit_rails/templates/components/pagination/USAGE +45 -0
  85. data/lib/uikit_rails/templates/components/pagination/component.html.erb +7 -0
  86. data/lib/uikit_rails/templates/components/pagination/component.rb +90 -0
  87. data/lib/uikit_rails/templates/components/pagination/pagination.css +89 -0
  88. data/lib/uikit_rails/templates/components/pagination/preview.yml +61 -0
  89. data/lib/uikit_rails/templates/components/popover/USAGE +44 -0
  90. data/lib/uikit_rails/templates/components/popover/component.html.erb +8 -0
  91. data/lib/uikit_rails/templates/components/popover/component.rb +19 -0
  92. data/lib/uikit_rails/templates/components/popover/popover.css +94 -0
  93. data/lib/uikit_rails/templates/components/popover/preview.yml +102 -0
  94. data/lib/uikit_rails/templates/components/progress/USAGE +15 -0
  95. data/lib/uikit_rails/templates/components/progress/component.html.erb +3 -0
  96. data/lib/uikit_rails/templates/components/progress/component.rb +38 -0
  97. data/lib/uikit_rails/templates/components/progress/preview.yml +44 -0
  98. data/lib/uikit_rails/templates/components/progress/progress.css +20 -0
  99. data/lib/uikit_rails/templates/components/select/USAGE +19 -0
  100. data/lib/uikit_rails/templates/components/select/component.rb +38 -0
  101. data/lib/uikit_rails/templates/components/select/preview.yml +61 -0
  102. data/lib/uikit_rails/templates/components/select/select.css +46 -0
  103. data/lib/uikit_rails/templates/components/separator/USAGE +15 -0
  104. data/lib/uikit_rails/templates/components/separator/component.rb +34 -0
  105. data/lib/uikit_rails/templates/components/separator/preview.yml +32 -0
  106. data/lib/uikit_rails/templates/components/separator/separator.css +21 -0
  107. data/lib/uikit_rails/templates/components/sheet/USAGE +44 -0
  108. data/lib/uikit_rails/templates/components/sheet/component.html.erb +23 -0
  109. data/lib/uikit_rails/templates/components/sheet/component.rb +23 -0
  110. data/lib/uikit_rails/templates/components/sheet/preview.yml +105 -0
  111. data/lib/uikit_rails/templates/components/sheet/sheet.css +193 -0
  112. data/lib/uikit_rails/templates/components/skeleton/USAGE +19 -0
  113. data/lib/uikit_rails/templates/components/skeleton/component.rb +38 -0
  114. data/lib/uikit_rails/templates/components/skeleton/preview.yml +44 -0
  115. data/lib/uikit_rails/templates/components/skeleton/skeleton.css +25 -0
  116. data/lib/uikit_rails/templates/components/switch/USAGE +19 -0
  117. data/lib/uikit_rails/templates/components/switch/component.html.erb +19 -0
  118. data/lib/uikit_rails/templates/components/switch/component.rb +23 -0
  119. data/lib/uikit_rails/templates/components/switch/preview.yml +43 -0
  120. data/lib/uikit_rails/templates/components/switch/switch.css +81 -0
  121. data/lib/uikit_rails/templates/components/table/USAGE +40 -0
  122. data/lib/uikit_rails/templates/components/table/component.html.erb +14 -0
  123. data/lib/uikit_rails/templates/components/table/component.rb +25 -0
  124. data/lib/uikit_rails/templates/components/table/preview.yml +109 -0
  125. data/lib/uikit_rails/templates/components/table/table.css +86 -0
  126. data/lib/uikit_rails/templates/components/tabs/USAGE +24 -0
  127. data/lib/uikit_rails/templates/components/tabs/component.html.erb +10 -0
  128. data/lib/uikit_rails/templates/components/tabs/component.rb +35 -0
  129. data/lib/uikit_rails/templates/components/tabs/preview.yml +60 -0
  130. data/lib/uikit_rails/templates/components/tabs/tabs.css +72 -0
  131. data/lib/uikit_rails/templates/components/textarea/USAGE +19 -0
  132. data/lib/uikit_rails/templates/components/textarea/component.rb +25 -0
  133. data/lib/uikit_rails/templates/components/textarea/preview.yml +47 -0
  134. data/lib/uikit_rails/templates/components/textarea/textarea.css +39 -0
  135. data/lib/uikit_rails/templates/components/toggle/USAGE +25 -0
  136. data/lib/uikit_rails/templates/components/toggle/component.rb +39 -0
  137. data/lib/uikit_rails/templates/components/toggle/preview.yml +81 -0
  138. data/lib/uikit_rails/templates/components/toggle/toggle.css +89 -0
  139. data/lib/uikit_rails/templates/components/tooltip/USAGE +23 -0
  140. data/lib/uikit_rails/templates/components/tooltip/component.html.erb +8 -0
  141. data/lib/uikit_rails/templates/components/tooltip/component.rb +19 -0
  142. data/lib/uikit_rails/templates/components/tooltip/preview.yml +52 -0
  143. data/lib/uikit_rails/templates/components/tooltip/tooltip.css +78 -0
  144. data/lib/uikit_rails/templates/stimulus/accordion_controller.js +19 -0
  145. data/lib/uikit_rails/templates/stimulus/alert_dialog_controller.js +25 -0
  146. data/lib/uikit_rails/templates/stimulus/collapsible_controller.js +9 -0
  147. data/lib/uikit_rails/templates/stimulus/dialog_controller.js +19 -0
  148. data/lib/uikit_rails/templates/stimulus/dropdown_controller.js +47 -0
  149. data/lib/uikit_rails/templates/stimulus/popover_controller.js +47 -0
  150. data/lib/uikit_rails/templates/stimulus/sheet_controller.js +19 -0
  151. data/lib/uikit_rails/templates/stimulus/tabs_controller.js +24 -0
  152. data/lib/uikit_rails/templates/stimulus/tooltip_controller.js +13 -0
  153. data/lib/uikit_rails/version.rb +5 -0
  154. data/lib/uikit_rails.rb +59 -0
  155. data/sig/uikit_rails.rbs +4 -0
  156. data/test_app/.dockerignore +51 -0
  157. data/test_app/.gitattributes +9 -0
  158. data/test_app/.github/dependabot.yml +12 -0
  159. data/test_app/.github/workflows/ci.yml +124 -0
  160. data/test_app/.gitignore +35 -0
  161. data/test_app/.kamal/hooks/docker-setup.sample +3 -0
  162. data/test_app/.kamal/hooks/post-app-boot.sample +3 -0
  163. data/test_app/.kamal/hooks/post-deploy.sample +14 -0
  164. data/test_app/.kamal/hooks/post-proxy-reboot.sample +3 -0
  165. data/test_app/.kamal/hooks/pre-app-boot.sample +3 -0
  166. data/test_app/.kamal/hooks/pre-build.sample +51 -0
  167. data/test_app/.kamal/hooks/pre-connect.sample +47 -0
  168. data/test_app/.kamal/hooks/pre-deploy.sample +122 -0
  169. data/test_app/.kamal/hooks/pre-proxy-reboot.sample +3 -0
  170. data/test_app/.kamal/secrets +20 -0
  171. data/test_app/.rubocop.yml +8 -0
  172. data/test_app/.ruby-version +1 -0
  173. data/test_app/Dockerfile +77 -0
  174. data/test_app/Gemfile +68 -0
  175. data/test_app/Gemfile.lock +587 -0
  176. data/test_app/README.md +24 -0
  177. data/test_app/Rakefile +6 -0
  178. data/test_app/app/assets/images/.keep +0 -0
  179. data/test_app/app/assets/stylesheets/application.css +10 -0
  180. data/test_app/app/assets/stylesheets/ui/accordion.css +74 -0
  181. data/test_app/app/assets/stylesheets/ui/alert.css +60 -0
  182. data/test_app/app/assets/stylesheets/ui/alert_dialog.css +108 -0
  183. data/test_app/app/assets/stylesheets/ui/avatar.css +53 -0
  184. data/test_app/app/assets/stylesheets/ui/badge.css +61 -0
  185. data/test_app/app/assets/stylesheets/ui/breadcrumb.css +55 -0
  186. data/test_app/app/assets/stylesheets/ui/button.css +125 -0
  187. data/test_app/app/assets/stylesheets/ui/card.css +63 -0
  188. data/test_app/app/assets/stylesheets/ui/checkbox.css +67 -0
  189. data/test_app/app/assets/stylesheets/ui/collapsible.css +55 -0
  190. data/test_app/app/assets/stylesheets/ui/dialog.css +133 -0
  191. data/test_app/app/assets/stylesheets/ui/dropdown.css +104 -0
  192. data/test_app/app/assets/stylesheets/ui/form.css +48 -0
  193. data/test_app/app/assets/stylesheets/ui/input.css +43 -0
  194. data/test_app/app/assets/stylesheets/ui/label.css +25 -0
  195. data/test_app/app/assets/stylesheets/ui/pagination.css +89 -0
  196. data/test_app/app/assets/stylesheets/ui/popover.css +94 -0
  197. data/test_app/app/assets/stylesheets/ui/progress.css +20 -0
  198. data/test_app/app/assets/stylesheets/ui/select.css +46 -0
  199. data/test_app/app/assets/stylesheets/ui/separator.css +21 -0
  200. data/test_app/app/assets/stylesheets/ui/sheet.css +193 -0
  201. data/test_app/app/assets/stylesheets/ui/skeleton.css +25 -0
  202. data/test_app/app/assets/stylesheets/ui/switch.css +81 -0
  203. data/test_app/app/assets/stylesheets/ui/table.css +86 -0
  204. data/test_app/app/assets/stylesheets/ui/tabs.css +72 -0
  205. data/test_app/app/assets/stylesheets/ui/textarea.css +39 -0
  206. data/test_app/app/assets/stylesheets/ui/toggle.css +89 -0
  207. data/test_app/app/assets/stylesheets/ui/tooltip.css +78 -0
  208. data/test_app/app/assets/stylesheets/uikit_rails.css +69 -0
  209. data/test_app/app/components/ui/accordion/component.html.erb +13 -0
  210. data/test_app/app/components/ui/accordion/component.rb +35 -0
  211. data/test_app/app/components/ui/accordion/preview.yml +81 -0
  212. data/test_app/app/components/ui/alert/component.html.erb +9 -0
  213. data/test_app/app/components/ui/alert/component.rb +27 -0
  214. data/test_app/app/components/ui/alert/preview.yml +53 -0
  215. data/test_app/app/components/ui/alert_dialog/component.html.erb +27 -0
  216. data/test_app/app/components/ui/alert_dialog/component.rb +23 -0
  217. data/test_app/app/components/ui/alert_dialog/preview.yml +94 -0
  218. data/test_app/app/components/ui/avatar/component.html.erb +7 -0
  219. data/test_app/app/components/ui/avatar/component.rb +31 -0
  220. data/test_app/app/components/ui/avatar/preview.yml +42 -0
  221. data/test_app/app/components/ui/badge/component.rb +28 -0
  222. data/test_app/app/components/ui/badge/preview.yml +38 -0
  223. data/test_app/app/components/ui/base_component.rb +32 -0
  224. data/test_app/app/components/ui/breadcrumb/component.html.erb +14 -0
  225. data/test_app/app/components/ui/breadcrumb/component.rb +40 -0
  226. data/test_app/app/components/ui/breadcrumb/preview.yml +42 -0
  227. data/test_app/app/components/ui/button/component.rb +44 -0
  228. data/test_app/app/components/ui/button/preview.yml +106 -0
  229. data/test_app/app/components/ui/card/component.html.erb +16 -0
  230. data/test_app/app/components/ui/card/component.rb +26 -0
  231. data/test_app/app/components/ui/card/preview.yml +57 -0
  232. data/test_app/app/components/ui/checkbox/component.html.erb +6 -0
  233. data/test_app/app/components/ui/checkbox/component.rb +26 -0
  234. data/test_app/app/components/ui/checkbox/preview.yml +43 -0
  235. data/test_app/app/components/ui/collapsible/component.html.erb +8 -0
  236. data/test_app/app/components/ui/collapsible/component.rb +18 -0
  237. data/test_app/app/components/ui/collapsible/preview.yml +65 -0
  238. data/test_app/app/components/ui/dialog/component.html.erb +23 -0
  239. data/test_app/app/components/ui/dialog/component.rb +20 -0
  240. data/test_app/app/components/ui/dialog/preview.yml +77 -0
  241. data/test_app/app/components/ui/dropdown/component.html.erb +14 -0
  242. data/test_app/app/components/ui/dropdown/component.rb +62 -0
  243. data/test_app/app/components/ui/dropdown/preview.yml +75 -0
  244. data/test_app/app/components/ui/form/USAGE +51 -0
  245. data/test_app/app/components/ui/form/builder.rb +233 -0
  246. data/test_app/app/components/ui/form/component.rb +258 -0
  247. data/test_app/app/components/ui/form/form.css +48 -0
  248. data/test_app/app/components/ui/form/preview.yml +95 -0
  249. data/test_app/app/components/ui/input/component.rb +25 -0
  250. data/test_app/app/components/ui/input/preview.yml +58 -0
  251. data/test_app/app/components/ui/label/component.rb +25 -0
  252. data/test_app/app/components/ui/label/preview.yml +34 -0
  253. data/test_app/app/components/ui/pagination/component.html.erb +7 -0
  254. data/test_app/app/components/ui/pagination/component.rb +90 -0
  255. data/test_app/app/components/ui/pagination/preview.yml +61 -0
  256. data/test_app/app/components/ui/popover/component.html.erb +8 -0
  257. data/test_app/app/components/ui/popover/component.rb +19 -0
  258. data/test_app/app/components/ui/popover/preview.yml +102 -0
  259. data/test_app/app/components/ui/progress/component.html.erb +3 -0
  260. data/test_app/app/components/ui/progress/component.rb +38 -0
  261. data/test_app/app/components/ui/progress/preview.yml +44 -0
  262. data/test_app/app/components/ui/select/component.rb +38 -0
  263. data/test_app/app/components/ui/select/preview.yml +61 -0
  264. data/test_app/app/components/ui/separator/component.rb +34 -0
  265. data/test_app/app/components/ui/separator/preview.yml +32 -0
  266. data/test_app/app/components/ui/sheet/component.html.erb +23 -0
  267. data/test_app/app/components/ui/sheet/component.rb +23 -0
  268. data/test_app/app/components/ui/sheet/preview.yml +105 -0
  269. data/test_app/app/components/ui/skeleton/component.rb +38 -0
  270. data/test_app/app/components/ui/skeleton/preview.yml +44 -0
  271. data/test_app/app/components/ui/switch/component.html.erb +19 -0
  272. data/test_app/app/components/ui/switch/component.rb +23 -0
  273. data/test_app/app/components/ui/switch/preview.yml +43 -0
  274. data/test_app/app/components/ui/table/component.html.erb +14 -0
  275. data/test_app/app/components/ui/table/component.rb +25 -0
  276. data/test_app/app/components/ui/table/preview.yml +109 -0
  277. data/test_app/app/components/ui/tabs/component.html.erb +10 -0
  278. data/test_app/app/components/ui/tabs/component.rb +35 -0
  279. data/test_app/app/components/ui/tabs/preview.yml +60 -0
  280. data/test_app/app/components/ui/textarea/component.rb +25 -0
  281. data/test_app/app/components/ui/textarea/preview.yml +47 -0
  282. data/test_app/app/components/ui/toggle/component.rb +39 -0
  283. data/test_app/app/components/ui/toggle/preview.yml +81 -0
  284. data/test_app/app/components/ui/tooltip/component.html.erb +8 -0
  285. data/test_app/app/components/ui/tooltip/component.rb +19 -0
  286. data/test_app/app/components/ui/tooltip/preview.yml +52 -0
  287. data/test_app/app/controllers/application_controller.rb +7 -0
  288. data/test_app/app/controllers/concerns/.keep +0 -0
  289. data/test_app/app/helpers/application_helper.rb +2 -0
  290. data/test_app/app/javascript/application.js +3 -0
  291. data/test_app/app/javascript/controllers/application.js +9 -0
  292. data/test_app/app/javascript/controllers/hello_controller.js +7 -0
  293. data/test_app/app/javascript/controllers/index.js +4 -0
  294. data/test_app/app/javascript/controllers/ui/accordion_controller.js +19 -0
  295. data/test_app/app/javascript/controllers/ui/alert_dialog_controller.js +25 -0
  296. data/test_app/app/javascript/controllers/ui/collapsible_controller.js +9 -0
  297. data/test_app/app/javascript/controllers/ui/dialog_controller.js +19 -0
  298. data/test_app/app/javascript/controllers/ui/dropdown_controller.js +47 -0
  299. data/test_app/app/javascript/controllers/ui/popover_controller.js +47 -0
  300. data/test_app/app/javascript/controllers/ui/sheet_controller.js +19 -0
  301. data/test_app/app/javascript/controllers/ui/tabs_controller.js +24 -0
  302. data/test_app/app/javascript/controllers/ui/tooltip_controller.js +13 -0
  303. data/test_app/app/jobs/application_job.rb +7 -0
  304. data/test_app/app/mailers/application_mailer.rb +4 -0
  305. data/test_app/app/models/application_record.rb +3 -0
  306. data/test_app/app/models/concerns/.keep +0 -0
  307. data/test_app/app/views/layouts/application.html.erb +29 -0
  308. data/test_app/app/views/layouts/mailer.html.erb +13 -0
  309. data/test_app/app/views/layouts/mailer.text.erb +1 -0
  310. data/test_app/app/views/pwa/manifest.json.erb +22 -0
  311. data/test_app/app/views/pwa/service-worker.js +26 -0
  312. data/test_app/bin/brakeman +7 -0
  313. data/test_app/bin/bundler-audit +6 -0
  314. data/test_app/bin/ci +6 -0
  315. data/test_app/bin/dev +2 -0
  316. data/test_app/bin/docker-entrypoint +8 -0
  317. data/test_app/bin/importmap +4 -0
  318. data/test_app/bin/jobs +6 -0
  319. data/test_app/bin/kamal +16 -0
  320. data/test_app/bin/rails +4 -0
  321. data/test_app/bin/rake +4 -0
  322. data/test_app/bin/rubocop +8 -0
  323. data/test_app/bin/setup +35 -0
  324. data/test_app/bin/thrust +5 -0
  325. data/test_app/config/application.rb +27 -0
  326. data/test_app/config/boot.rb +4 -0
  327. data/test_app/config/bundler-audit.yml +5 -0
  328. data/test_app/config/cable.yml +17 -0
  329. data/test_app/config/cache.yml +16 -0
  330. data/test_app/config/ci.rb +24 -0
  331. data/test_app/config/credentials.yml.enc +1 -0
  332. data/test_app/config/database.yml +40 -0
  333. data/test_app/config/deploy.yml +119 -0
  334. data/test_app/config/environment.rb +5 -0
  335. data/test_app/config/environments/development.rb +78 -0
  336. data/test_app/config/environments/production.rb +90 -0
  337. data/test_app/config/environments/test.rb +53 -0
  338. data/test_app/config/importmap.rb +7 -0
  339. data/test_app/config/initializers/assets.rb +7 -0
  340. data/test_app/config/initializers/content_security_policy.rb +29 -0
  341. data/test_app/config/initializers/filter_parameter_logging.rb +8 -0
  342. data/test_app/config/initializers/inflections.rb +16 -0
  343. data/test_app/config/locales/en.yml +31 -0
  344. data/test_app/config/puma.rb +42 -0
  345. data/test_app/config/queue.yml +18 -0
  346. data/test_app/config/recurring.yml +15 -0
  347. data/test_app/config/routes.rb +13 -0
  348. data/test_app/config/storage.yml +27 -0
  349. data/test_app/config.ru +6 -0
  350. data/test_app/db/cable_schema.rb +11 -0
  351. data/test_app/db/cache_schema.rb +12 -0
  352. data/test_app/db/queue_schema.rb +129 -0
  353. data/test_app/db/seeds.rb +9 -0
  354. data/test_app/lib/tasks/.keep +0 -0
  355. data/test_app/log/.keep +0 -0
  356. data/test_app/public/400.html +135 -0
  357. data/test_app/public/404.html +135 -0
  358. data/test_app/public/406-unsupported-browser.html +135 -0
  359. data/test_app/public/422.html +135 -0
  360. data/test_app/public/500.html +135 -0
  361. data/test_app/public/icon.png +0 -0
  362. data/test_app/public/icon.svg +3 -0
  363. data/test_app/public/robots.txt +1 -0
  364. data/test_app/script/.keep +0 -0
  365. data/test_app/storage/.keep +0 -0
  366. data/test_app/test/controllers/.keep +0 -0
  367. data/test_app/test/fixtures/files/.keep +0 -0
  368. data/test_app/test/helpers/.keep +0 -0
  369. data/test_app/test/integration/.keep +0 -0
  370. data/test_app/test/mailers/.keep +0 -0
  371. data/test_app/test/models/.keep +0 -0
  372. data/test_app/test/test_helper.rb +15 -0
  373. data/test_app/tmp/.keep +0 -0
  374. data/test_app/tmp/pids/.keep +0 -0
  375. data/test_app/tmp/storage/.keep +0 -0
  376. data/test_app/vendor/.keep +0 -0
  377. data/test_app/vendor/javascript/.keep +0 -0
  378. metadata +448 -0
@@ -0,0 +1,48 @@
1
+ /* ============================================
2
+ * UikitRails — Form
3
+ *
4
+ * Form layout, field groups, description, and error text.
5
+ * ============================================ */
6
+
7
+ .ui-form {
8
+ display: flex;
9
+ flex-direction: column;
10
+ gap: 1.5rem;
11
+ }
12
+
13
+ .ui-form-field {
14
+ display: flex;
15
+ flex-direction: column;
16
+ gap: 0.375rem;
17
+ }
18
+
19
+ .ui-form-description {
20
+ font-size: var(--ui-font-size-sm);
21
+ color: var(--ui-muted-foreground);
22
+ margin: 0;
23
+ }
24
+
25
+ .ui-form-error {
26
+ font-size: var(--ui-font-size-sm);
27
+ color: var(--ui-destructive);
28
+ margin: 0;
29
+ }
30
+
31
+ .ui-form-field .ui-input:has(~ .ui-form-error),
32
+ .ui-form-field .ui-textarea:has(~ .ui-form-error),
33
+ .ui-form-field .ui-select:has(~ .ui-form-error) {
34
+ border-color: var(--ui-destructive);
35
+ }
36
+
37
+ .ui-form-field .ui-input:has(~ .ui-form-error):focus,
38
+ .ui-form-field .ui-textarea:has(~ .ui-form-error):focus,
39
+ .ui-form-field .ui-select:has(~ .ui-form-error):focus {
40
+ outline-color: var(--ui-destructive);
41
+ }
42
+
43
+ .ui-form-actions {
44
+ display: flex;
45
+ align-items: center;
46
+ gap: 0.75rem;
47
+ padding-top: 0.5rem;
48
+ }
@@ -0,0 +1,95 @@
1
+ description: Form builder that renders the same Ui::Input, Ui::Textarea, Ui::Select, Ui::Checkbox, Ui::Label, and Ui::Button components as standalone render calls. Styleguide previews use form_with with scope and GET for simplicity.
2
+
3
+ sections:
4
+ - title: Basic Usage
5
+ examples:
6
+ - title: Text fields
7
+ code: |
8
+ <%= form_with url: "#", scope: :user, method: :get, builder: Ui::Form::Builder,
9
+ html: { class: "ui-form", style: "max-width: 24rem;" },
10
+ data: { turbo: false } do |f| %>
11
+ <div class="ui-form-field">
12
+ <%= f.label :name %>
13
+ <%= f.text_field :name, placeholder: "Your name" %>
14
+ </div>
15
+ <div class="ui-form-field">
16
+ <%= f.label :email %>
17
+ <%= f.email_field :email, placeholder: "you@example.com" %>
18
+ </div>
19
+ <div class="ui-form-field">
20
+ <%= f.label :password %>
21
+ <%= f.password_field :password, placeholder: "••••••••" %>
22
+ </div>
23
+ <% end %>
24
+
25
+ - title: Textarea
26
+ code: |
27
+ <%= form_with url: "#", scope: :user, method: :get, builder: Ui::Form::Builder,
28
+ html: { class: "ui-form", style: "max-width: 24rem;" },
29
+ data: { turbo: false } do |f| %>
30
+ <div class="ui-form-field">
31
+ <%= f.label :bio %>
32
+ <%= f.text_area :bio, placeholder: "Tell us about yourself...", rows: 3 %>
33
+ </div>
34
+ <% end %>
35
+
36
+ - title: Select
37
+ code: |
38
+ <%= form_with url: "#", scope: :user, method: :get, builder: Ui::Form::Builder,
39
+ html: { class: "ui-form", style: "max-width: 24rem;" },
40
+ data: { turbo: false } do |f| %>
41
+ <div class="ui-form-field">
42
+ <%= f.label :role %>
43
+ <%= f.select :role, %w[Admin Editor Viewer] %>
44
+ </div>
45
+ <% end %>
46
+
47
+ - title: Checkbox
48
+ code: |
49
+ <%= form_with url: "#", scope: :settings, method: :get, builder: Ui::Form::Builder,
50
+ html: { class: "ui-form", style: "max-width: 24rem;" },
51
+ data: { turbo: false } do |f| %>
52
+ <%= f.check_box :terms, label: "I agree to the terms and conditions" %>
53
+ <%= f.check_box :newsletter, label: "Subscribe to newsletter" %>
54
+ <% end %>
55
+
56
+ - title: Submit
57
+ examples:
58
+ - title: Variants
59
+ code: |
60
+ <%= form_with url: "#", scope: :user, method: :get, builder: Ui::Form::Builder,
61
+ data: { turbo: false } do |f| %>
62
+ <div style="display: flex; gap: 0.5rem;">
63
+ <%= f.submit "Save changes" %>
64
+ <%= f.submit "Delete", variant: :destructive %>
65
+ <%= f.submit "Cancel", variant: :outline %>
66
+ </div>
67
+ <% end %>
68
+
69
+ - title: Description
70
+ examples:
71
+ - title: Help text
72
+ code: |
73
+ <%= form_with url: "#", scope: :user, method: :get, builder: Ui::Form::Builder,
74
+ html: { class: "ui-form", style: "max-width: 24rem;" },
75
+ data: { turbo: false } do |f| %>
76
+ <div class="ui-form-field">
77
+ <%= f.label :email %>
78
+ <%= f.email_field :email, placeholder: "you@example.com" %>
79
+ <p class="ui-form-description">We'll never share your email with anyone.</p>
80
+ </div>
81
+ <% end %>
82
+
83
+ - title: Error State
84
+ examples:
85
+ - title: Inline error
86
+ code: |
87
+ <%= form_with url: "#", scope: :user, method: :get, builder: Ui::Form::Builder,
88
+ html: { class: "ui-form", style: "max-width: 24rem;" },
89
+ data: { turbo: false } do |f| %>
90
+ <div class="ui-form-field">
91
+ <%= f.label :email %>
92
+ <%= f.email_field :email, placeholder: "you@example.com" %>
93
+ <p class="ui-form-error">Email is already taken</p>
94
+ </div>
95
+ <% end %>
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Ui
4
+ module Input
5
+ # Text input field with consistent styling.
6
+ class Component < Ui::BaseComponent
7
+ attr_reader :html_attrs
8
+
9
+ def initialize(**html_attrs)
10
+ @html_attrs = html_attrs
11
+ super()
12
+ end
13
+
14
+ def call
15
+ tag.input(**computed_attrs)
16
+ end
17
+
18
+ private
19
+
20
+ def computed_attrs
21
+ merge_attrs({ class: "ui-input", type: "text" }, html_attrs)
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,58 @@
1
+ description: A styled text input field. Supports all standard HTML input attributes including type, placeholder, disabled, and name.
2
+
3
+ sections:
4
+ - title: Basic Usage
5
+ examples:
6
+ - title: Default text input
7
+ code: |
8
+ <%= render Ui::Input::Component.new(placeholder: "Enter your name") %>
9
+
10
+ - title: With name attribute
11
+ code: |
12
+ <%= render Ui::Input::Component.new(name: "email", placeholder: "you@example.com") %>
13
+
14
+ - title: Input Types
15
+ examples:
16
+ - title: Email
17
+ code: |
18
+ <%= render Ui::Input::Component.new(type: "email", placeholder: "you@example.com") %>
19
+
20
+ - title: Password
21
+ code: |
22
+ <%= render Ui::Input::Component.new(type: "password", placeholder: "••••••••") %>
23
+
24
+ - title: Number
25
+ code: |
26
+ <%= render Ui::Input::Component.new(type: "number", placeholder: "0", min: 0, max: 100) %>
27
+
28
+ - title: Search
29
+ code: |
30
+ <%= render Ui::Input::Component.new(type: "search", placeholder: "Search...") %>
31
+
32
+ - title: File
33
+ code: |
34
+ <%= render Ui::Input::Component.new(type: "file") %>
35
+
36
+ - title: States
37
+ examples:
38
+ - title: Disabled
39
+ code: |
40
+ <%= render Ui::Input::Component.new(placeholder: "Disabled input", disabled: true) %>
41
+
42
+ - title: With value
43
+ code: |
44
+ <%= render Ui::Input::Component.new(value: "Pre-filled value") %>
45
+
46
+ - title: Required
47
+ code: |
48
+ <%= render Ui::Input::Component.new(placeholder: "Required field", required: true) %>
49
+
50
+ - title: With Custom Attributes
51
+ examples:
52
+ - title: Custom class and data attributes
53
+ code: |
54
+ <%= render Ui::Input::Component.new(
55
+ placeholder: "Custom",
56
+ class: "my-custom-class",
57
+ data: { controller: "input-validator" }
58
+ ) %>
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Ui
4
+ module Label
5
+ # Form label with consistent styling.
6
+ class Component < Ui::BaseComponent
7
+ attr_reader :html_attrs
8
+
9
+ def initialize(**html_attrs)
10
+ @html_attrs = html_attrs
11
+ super()
12
+ end
13
+
14
+ def call
15
+ content_tag(:label, content, **computed_attrs)
16
+ end
17
+
18
+ private
19
+
20
+ def computed_attrs
21
+ merge_attrs({ class: "ui-label" }, html_attrs)
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,34 @@
1
+ description: A form label element with consistent styling. Accepts a `for` attribute to associate with an input.
2
+
3
+ sections:
4
+ - title: Basic Label
5
+ examples:
6
+ - title: Default
7
+ code: |
8
+ <%= render Ui::Label::Component.new do %>Email<% end %>
9
+
10
+ - title: With for attribute
11
+ code: |
12
+ <%= render Ui::Label::Component.new(for: "email") do %>Email address<% end %>
13
+
14
+ - title: Usage with Form Inputs
15
+ examples:
16
+ - title: Label with text input
17
+ code: |
18
+ <%= render Ui::Label::Component.new(for: "username") do %>Username<% end %>
19
+ <input type="text" id="username" name="username" />
20
+
21
+ - title: Label with required field
22
+ code: |
23
+ <%= render Ui::Label::Component.new(for: "password") do %>Password <span aria-hidden="true">*</span><% end %>
24
+ <input type="password" id="password" name="password" required />
25
+
26
+ - title: With Custom Attributes
27
+ examples:
28
+ - title: Custom class and data attributes
29
+ code: |
30
+ <%= render Ui::Label::Component.new(
31
+ for: "name",
32
+ class: "my-custom-label",
33
+ data: { required: true }
34
+ ) do %>Full Name<% end %>
@@ -0,0 +1,7 @@
1
+ <nav class="ui-pagination" aria-label="Pagination" <%= tag.attributes(html_attrs) %>>
2
+ <ul class="ui-pagination__list">
3
+ <% items.each do |item| %>
4
+ <li><%= item %></li>
5
+ <% end %>
6
+ </ul>
7
+ </nav>
@@ -0,0 +1,90 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Ui
4
+ module Pagination
5
+ # Page navigation with previous, next, and page links.
6
+ class Component < Ui::BaseComponent
7
+ renders_many :items, types: {
8
+ page: { renders: lambda { |href: "#", active: false, **attrs|
9
+ PageItem.new(href: href, active: active, **attrs)
10
+ }, as: :page },
11
+ prev_page: { renders: lambda { |href: "#", disabled: false, **attrs|
12
+ PrevItem.new(href: href, disabled: disabled, **attrs)
13
+ }, as: :prev_page },
14
+ next_page: { renders: lambda { |href: "#", disabled: false, **attrs|
15
+ NextItem.new(href: href, disabled: disabled, **attrs)
16
+ }, as: :next_page },
17
+ ellipsis: { renders: -> { EllipsisItem.new }, as: :ellipsis }
18
+ }
19
+
20
+ attr_reader :html_attrs
21
+
22
+ def initialize(**html_attrs)
23
+ @html_attrs = html_attrs
24
+ super()
25
+ end
26
+
27
+ class PageItem < Ui::BaseComponent
28
+ attr_reader :href, :active, :html_attrs
29
+
30
+ def initialize(href: "#", active: false, **html_attrs)
31
+ @href = href
32
+ @active = active
33
+ @html_attrs = html_attrs
34
+ super()
35
+ end
36
+
37
+ def call
38
+ content_tag(:a, content, class: class_names("ui-pagination__item", "ui-pagination__item--active": active),
39
+ href: href, "aria-current": ("page" if active), **html_attrs)
40
+ end
41
+ end
42
+
43
+ class PrevItem < Ui::BaseComponent
44
+ attr_reader :href, :disabled, :html_attrs
45
+
46
+ def initialize(href: "#", disabled: false, **html_attrs)
47
+ @href = href
48
+ @disabled = disabled
49
+ @html_attrs = html_attrs
50
+ super()
51
+ end
52
+
53
+ def call
54
+ classes = class_names("ui-pagination__item ui-pagination__prev", "ui-pagination__item--disabled": disabled)
55
+ if disabled
56
+ content_tag(:span, content.presence || "Previous", class: classes, "aria-disabled": "true", **html_attrs)
57
+ else
58
+ content_tag(:a, content.presence || "Previous", class: classes, href: href, **html_attrs)
59
+ end
60
+ end
61
+ end
62
+
63
+ class NextItem < Ui::BaseComponent
64
+ attr_reader :href, :disabled, :html_attrs
65
+
66
+ def initialize(href: "#", disabled: false, **html_attrs)
67
+ @href = href
68
+ @disabled = disabled
69
+ @html_attrs = html_attrs
70
+ super()
71
+ end
72
+
73
+ def call
74
+ classes = class_names("ui-pagination__item ui-pagination__next", "ui-pagination__item--disabled": disabled)
75
+ if disabled
76
+ content_tag(:span, content.presence || "Next", class: classes, "aria-disabled": "true", **html_attrs)
77
+ else
78
+ content_tag(:a, content.presence || "Next", class: classes, href: href, **html_attrs)
79
+ end
80
+ end
81
+ end
82
+
83
+ class EllipsisItem < Ui::BaseComponent
84
+ def call
85
+ content_tag(:span, "…", class: "ui-pagination__ellipsis")
86
+ end
87
+ end
88
+ end
89
+ end
90
+ end
@@ -0,0 +1,61 @@
1
+ description: Page navigation with previous/next buttons, page numbers, and ellipsis indicators. Compose the pagination using slot items for full flexibility.
2
+
3
+ sections:
4
+ - title: Basic Pagination
5
+ examples:
6
+ - title: Simple page navigation
7
+ code: |
8
+ <%= render Ui::Pagination::Component.new do |pg| %>
9
+ <% pg.with_prev_page(href: "/page/1") %>
10
+ <% pg.with_page(href: "/page/1") do %>1<% end %>
11
+ <% pg.with_page(href: "/page/2", active: true) do %>2<% end %>
12
+ <% pg.with_page(href: "/page/3") do %>3<% end %>
13
+ <% pg.with_next_page(href: "/page/3") %>
14
+ <% end %>
15
+
16
+ - title: With Ellipsis
17
+ examples:
18
+ - title: Many pages with ellipsis
19
+ code: |
20
+ <%= render Ui::Pagination::Component.new do |pg| %>
21
+ <% pg.with_prev_page(href: "/page/3") %>
22
+ <% pg.with_page(href: "/page/1") do %>1<% end %>
23
+ <% pg.with_ellipsis %>
24
+ <% pg.with_page(href: "/page/3") do %>3<% end %>
25
+ <% pg.with_page(href: "/page/4", active: true) do %>4<% end %>
26
+ <% pg.with_page(href: "/page/5") do %>5<% end %>
27
+ <% pg.with_ellipsis %>
28
+ <% pg.with_page(href: "/page/10") do %>10<% end %>
29
+ <% pg.with_next_page(href: "/page/5") %>
30
+ <% end %>
31
+
32
+ - title: Disabled States
33
+ examples:
34
+ - title: First page (previous disabled)
35
+ code: |
36
+ <%= render Ui::Pagination::Component.new do |pg| %>
37
+ <% pg.with_prev_page(disabled: true) %>
38
+ <% pg.with_page(href: "/page/1", active: true) do %>1<% end %>
39
+ <% pg.with_page(href: "/page/2") do %>2<% end %>
40
+ <% pg.with_page(href: "/page/3") do %>3<% end %>
41
+ <% pg.with_next_page(href: "/page/2") %>
42
+ <% end %>
43
+
44
+ - title: Last page (next disabled)
45
+ code: |
46
+ <%= render Ui::Pagination::Component.new do |pg| %>
47
+ <% pg.with_prev_page(href: "/page/4") %>
48
+ <% pg.with_page(href: "/page/3") do %>3<% end %>
49
+ <% pg.with_page(href: "/page/4") do %>4<% end %>
50
+ <% pg.with_page(href: "/page/5", active: true) do %>5<% end %>
51
+ <% pg.with_next_page(disabled: true) %>
52
+ <% end %>
53
+
54
+ - title: Prev/Next Only
55
+ examples:
56
+ - title: Without page numbers
57
+ code: |
58
+ <%= render Ui::Pagination::Component.new do |pg| %>
59
+ <% pg.with_prev_page(href: "/page/2") do %>← Older<% end %>
60
+ <% pg.with_next_page(href: "/page/4") do %>Newer →<% end %>
61
+ <% end %>
@@ -0,0 +1,8 @@
1
+ <div data-controller="ui--popover" class="ui-popover" <%= tag.attributes(html_attrs) %>>
2
+ <% if trigger? %>
3
+ <div data-action="click->ui--popover#toggle" class="ui-popover__trigger"><%= trigger %></div>
4
+ <% end %>
5
+ <div data-ui--popover-target="content" class="ui-popover__content ui-popover__content--<%= side %> ui-popover__content--<%= align %>">
6
+ <%= content %>
7
+ </div>
8
+ </div>
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Ui
4
+ module Popover
5
+ # Floating content panel triggered by click.
6
+ class Component < Ui::BaseComponent
7
+ renders_one :trigger
8
+
9
+ attr_reader :align, :side, :html_attrs
10
+
11
+ def initialize(align: :center, side: :bottom, **html_attrs)
12
+ @align = align.to_sym
13
+ @side = side.to_sym
14
+ @html_attrs = html_attrs
15
+ super()
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,102 @@
1
+ description: A floating content panel that appears on click. Unlike dropdowns which are for menu items, popovers are for richer content like forms, info panels, and settings. Supports side and alignment positioning.
2
+
3
+ sections:
4
+ - title: Basic Popover
5
+ examples:
6
+ - title: Default (bottom, center)
7
+ code: |
8
+ <%= render Ui::Popover::Component.new do |popover| %>
9
+ <% popover.with_trigger do %>
10
+ <%= render Ui::Button::Component.new(variant: :outline) do %>Open Popover<% end %>
11
+ <% end %>
12
+ <div style="display:flex;flex-direction:column;gap:0.5rem;">
13
+ <h4 style="margin:0;font-size:var(--ui-font-size-sm);font-weight:600;">Dimensions</h4>
14
+ <p style="margin:0;font-size:var(--ui-font-size-xs);color:var(--ui-muted-foreground);">Set the dimensions for the layer.</p>
15
+ <div style="display:flex;gap:0.5rem;">
16
+ <input type="text" placeholder="Width" style="flex:1;padding:0.375rem 0.5rem;border:1px solid var(--ui-border);border-radius:var(--ui-radius);font-size:var(--ui-font-size-sm);" />
17
+ <input type="text" placeholder="Height" style="flex:1;padding:0.375rem 0.5rem;border:1px solid var(--ui-border);border-radius:var(--ui-radius);font-size:var(--ui-font-size-sm);" />
18
+ </div>
19
+ </div>
20
+ <% end %>
21
+
22
+ - title: Alignment
23
+ examples:
24
+ - title: Align start
25
+ code: |
26
+ <%= render Ui::Popover::Component.new(align: :start) do |popover| %>
27
+ <% popover.with_trigger do %>
28
+ <%= render Ui::Button::Component.new(variant: :outline, size: :sm) do %>Align Start<% end %>
29
+ <% end %>
30
+ <p style="margin:0;">This popover is aligned to the start (left edge) of the trigger.</p>
31
+ <% end %>
32
+
33
+ - title: Align end
34
+ code: |
35
+ <div style="display:flex;justify-content:flex-end;">
36
+ <%= render Ui::Popover::Component.new(align: :end) do |popover| %>
37
+ <% popover.with_trigger do %>
38
+ <%= render Ui::Button::Component.new(variant: :outline, size: :sm) do %>Align End<% end %>
39
+ <% end %>
40
+ <p style="margin:0;">This popover is aligned to the end (right edge) of the trigger.</p>
41
+ <% end %>
42
+ </div>
43
+
44
+ - title: Side Variants
45
+ examples:
46
+ - title: Top side
47
+ code: |
48
+ <div style="margin-top:12rem;">
49
+ <%= render Ui::Popover::Component.new(side: :top) do |popover| %>
50
+ <% popover.with_trigger do %>
51
+ <%= render Ui::Button::Component.new(variant: :outline) do %>Open Top<% end %>
52
+ <% end %>
53
+ <p style="margin:0;">This popover appears above the trigger.</p>
54
+ <% end %>
55
+ </div>
56
+
57
+ - title: Top with start alignment
58
+ code: |
59
+ <div style="margin-top:12rem;">
60
+ <%= render Ui::Popover::Component.new(side: :top, align: :start) do |popover| %>
61
+ <% popover.with_trigger do %>
62
+ <%= render Ui::Button::Component.new(variant: :outline) do %>Top Start<% end %>
63
+ <% end %>
64
+ <p style="margin:0;">Appears above, aligned to the left edge.</p>
65
+ <% end %>
66
+ </div>
67
+
68
+ - title: Popover with Form Content
69
+ examples:
70
+ - title: Settings popover
71
+ code: |
72
+ <%= render Ui::Popover::Component.new(align: :start) do |popover| %>
73
+ <% popover.with_trigger do %>
74
+ <%= render Ui::Button::Component.new do %>Settings<% end %>
75
+ <% end %>
76
+ <div style="display:flex;flex-direction:column;gap:0.75rem;">
77
+ <h4 style="margin:0;font-size:var(--ui-font-size-sm);font-weight:600;">Layout Settings</h4>
78
+ <div>
79
+ <label style="font-size:var(--ui-font-size-xs);font-weight:500;">Grid columns</label>
80
+ <input type="number" value="3" min="1" max="6" style="width:100%;padding:0.375rem 0.5rem;border:1px solid var(--ui-border);border-radius:var(--ui-radius);font-size:var(--ui-font-size-sm);margin-top:0.25rem;" />
81
+ </div>
82
+ <div>
83
+ <label style="font-size:var(--ui-font-size-xs);font-weight:500;">Spacing</label>
84
+ <select style="width:100%;padding:0.375rem 0.5rem;border:1px solid var(--ui-border);border-radius:var(--ui-radius);font-size:var(--ui-font-size-sm);margin-top:0.25rem;">
85
+ <option>Compact</option>
86
+ <option selected>Comfortable</option>
87
+ <option>Spacious</option>
88
+ </select>
89
+ </div>
90
+ </div>
91
+ <% end %>
92
+
93
+ - title: Custom Attributes
94
+ examples:
95
+ - title: Popover with custom class
96
+ code: |
97
+ <%= render Ui::Popover::Component.new(class: "my-custom-popover", data: { turbo_frame: "settings" }) do |popover| %>
98
+ <% popover.with_trigger do %>
99
+ <%= render Ui::Button::Component.new(variant: :secondary) do %>Custom<% end %>
100
+ <% end %>
101
+ <p style="margin:0;">Popover with custom wrapper attributes.</p>
102
+ <% end %>
@@ -0,0 +1,3 @@
1
+ <div <%= tag.attributes(computed_attrs) %>>
2
+ <div class="ui-progress__bar" style="width: <%= percentage %>%;"></div>
3
+ </div>
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Ui
4
+ module Progress
5
+ # Progress bar showing completion percentage.
6
+ class Component < Ui::BaseComponent
7
+ attr_reader :value, :max, :html_attrs
8
+
9
+ def initialize(value: 0, max: 100, **html_attrs)
10
+ @value = value.to_i
11
+ @max = max.to_i
12
+ @html_attrs = html_attrs
13
+ super()
14
+ end
15
+
16
+ def percentage
17
+ return 0 if max.zero?
18
+
19
+ [(value.to_f / max * 100).round, 100].min
20
+ end
21
+
22
+ private
23
+
24
+ def computed_attrs
25
+ merge_attrs(
26
+ {
27
+ class: "ui-progress",
28
+ role: "progressbar",
29
+ "aria-valuenow": value,
30
+ "aria-valuemin": 0,
31
+ "aria-valuemax": max
32
+ },
33
+ html_attrs
34
+ )
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,44 @@
1
+ description: An accessible progress bar that visually indicates completion percentage. Includes ARIA attributes for screen readers and smooth width transitions.
2
+
3
+ sections:
4
+ - title: Basic Usage
5
+ examples:
6
+ - title: Empty (0%)
7
+ code: |
8
+ <%= render Ui::Progress::Component.new(value: 0) %>
9
+
10
+ - title: Partial (45%)
11
+ code: |
12
+ <%= render Ui::Progress::Component.new(value: 45) %>
13
+
14
+ - title: Complete (100%)
15
+ code: |
16
+ <%= render Ui::Progress::Component.new(value: 100) %>
17
+
18
+ - title: Custom Max
19
+ examples:
20
+ - title: 3 of 5 steps
21
+ code: |
22
+ <%= render Ui::Progress::Component.new(value: 3, max: 5) %>
23
+
24
+ - title: 7 of 10 items
25
+ code: |
26
+ <%= render Ui::Progress::Component.new(value: 7, max: 10) %>
27
+
28
+ - title: With Labels
29
+ examples:
30
+ - title: Progress with percentage label
31
+ code: |
32
+ <div style="display: flex; flex-direction: column; gap: 0.25rem;">
33
+ <div style="display: flex; justify-content: space-between; font-size: 0.875rem;">
34
+ <span>Uploading…</span>
35
+ <span>65%</span>
36
+ </div>
37
+ <%= render Ui::Progress::Component.new(value: 65) %>
38
+ </div>
39
+
40
+ - title: With Custom Attributes
41
+ examples:
42
+ - title: Custom class and data attributes
43
+ code: |
44
+ <%= render Ui::Progress::Component.new(value: 50, class: "my-progress", data: { controller: "upload" }) %>