hakumi_components 0.1.18.pre → 0.1.19.pre

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 (467) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +32 -2
  3. data/README.md +208 -381
  4. data/app/assets/javascripts/hakumi_components.js +40 -34
  5. data/app/assets/stylesheets/hakumi_components.css +1 -1
  6. data/app/components/hakumi_components/admin_panel/component.rb +19 -9
  7. data/app/components/hakumi_components/affix/component.rb +54 -30
  8. data/app/components/hakumi_components/alert/component.html.erb +2 -2
  9. data/app/components/hakumi_components/alert/component.rb +68 -18
  10. data/app/components/hakumi_components/anchor/component.rb +35 -27
  11. data/app/components/hakumi_components/anchor/link/component.html.erb +1 -1
  12. data/app/components/hakumi_components/anchor/link/component.rb +28 -9
  13. data/app/components/hakumi_components/autocomplete/component.html.erb +86 -66
  14. data/app/components/hakumi_components/autocomplete/component.rb +115 -89
  15. data/app/components/hakumi_components/avatar/component.rb +45 -12
  16. data/app/components/hakumi_components/badge/component.rb +99 -45
  17. data/app/components/hakumi_components/base_component.rb +290 -91
  18. data/app/components/hakumi_components/breadcrumb/component.rb +16 -5
  19. data/app/components/hakumi_components/breadcrumb/item/component.html.erb +5 -5
  20. data/app/components/hakumi_components/breadcrumb/item/component.rb +56 -8
  21. data/app/components/hakumi_components/breadcrumb/item/menu_entry.rb +71 -0
  22. data/app/components/hakumi_components/button/component.rb +62 -20
  23. data/app/components/hakumi_components/calendar/component.html.erb +19 -19
  24. data/app/components/hakumi_components/calendar/component.rb +225 -202
  25. data/app/components/hakumi_components/calendar/day_cell.rb +62 -0
  26. data/app/components/hakumi_components/calendar/display_model.rb +237 -0
  27. data/app/components/hakumi_components/calendar/event_entry.rb +54 -0
  28. data/app/components/hakumi_components/calendar/locale_pack.rb +28 -0
  29. data/app/components/hakumi_components/calendar/panel_entry.rb +48 -0
  30. data/app/components/hakumi_components/calendar/week_row.rb +28 -0
  31. data/app/components/hakumi_components/card/component.html.erb +2 -2
  32. data/app/components/hakumi_components/card/component.rb +76 -49
  33. data/app/components/hakumi_components/card/grid/component.html.erb +1 -1
  34. data/app/components/hakumi_components/card/grid/component.rb +23 -17
  35. data/app/components/hakumi_components/card/meta/component.html.erb +8 -8
  36. data/app/components/hakumi_components/card/meta/component.rb +42 -24
  37. data/app/components/hakumi_components/carousel/autoplay_config.rb +27 -0
  38. data/app/components/hakumi_components/carousel/component.rb +85 -67
  39. data/app/components/hakumi_components/carousel/dots_config.rb +36 -0
  40. data/app/components/hakumi_components/cascader/component.html.erb +54 -36
  41. data/app/components/hakumi_components/cascader/component.rb +111 -62
  42. data/app/components/hakumi_components/checkbox/component.html.erb +5 -5
  43. data/app/components/hakumi_components/checkbox/component.rb +40 -6
  44. data/app/components/hakumi_components/checkbox/group/component.html.erb +4 -4
  45. data/app/components/hakumi_components/checkbox/group/component.rb +40 -13
  46. data/app/components/hakumi_components/checkbox/group/option.rb +34 -0
  47. data/app/components/hakumi_components/collapse/component.rb +64 -48
  48. data/app/components/hakumi_components/collapse/panel/component.rb +64 -21
  49. data/app/components/hakumi_components/color_picker/component.html.erb +21 -22
  50. data/app/components/hakumi_components/color_picker/component.rb +159 -49
  51. data/app/components/hakumi_components/color_picker/preset_group.rb +27 -0
  52. data/app/components/hakumi_components/concerns/form_field.rb +169 -68
  53. data/app/components/hakumi_components/concerns/form_field_contract.rb +57 -0
  54. data/app/components/hakumi_components/concerns/form_field_interface.rb +16 -0
  55. data/app/components/hakumi_components/concerns/input_control.rb +111 -0
  56. data/app/components/hakumi_components/concerns/input_control_contract.rb +47 -0
  57. data/app/components/hakumi_components/concerns/input_control_interface.rb +34 -0
  58. data/app/components/hakumi_components/concerns/selection_control.rb +122 -0
  59. data/app/components/hakumi_components/concerns/selection_control_contract.rb +47 -0
  60. data/app/components/hakumi_components/concerns/selection_control_interface.rb +34 -0
  61. data/app/components/hakumi_components/container/component.rb +29 -14
  62. data/app/components/hakumi_components/date_picker/component.html.erb +1 -3
  63. data/app/components/hakumi_components/date_picker/component.rb +130 -16
  64. data/app/components/hakumi_components/date_picker/range_picker.rb +159 -28
  65. data/app/components/hakumi_components/date_picker/shared_rendering.rb +151 -61
  66. data/app/components/hakumi_components/descriptions/component.html.erb +6 -6
  67. data/app/components/hakumi_components/descriptions/component.rb +98 -61
  68. data/app/components/hakumi_components/descriptions/item/component.rb +32 -8
  69. data/app/components/hakumi_components/divider/component.rb +36 -16
  70. data/app/components/hakumi_components/drawer/component.html.erb +2 -2
  71. data/app/components/hakumi_components/drawer/component.rb +112 -29
  72. data/app/components/hakumi_components/dropdown/component.rb +32 -35
  73. data/app/components/hakumi_components/dropdown/divider/component.rb +4 -0
  74. data/app/components/hakumi_components/dropdown/item/component.rb +42 -16
  75. data/app/components/hakumi_components/empty/component.rb +66 -31
  76. data/app/components/hakumi_components/flex/component.rb +39 -21
  77. data/app/components/hakumi_components/float_button/back_top/component.rb +35 -28
  78. data/app/components/hakumi_components/float_button/component.rb +161 -75
  79. data/app/components/hakumi_components/float_button/group/component.rb +85 -98
  80. data/app/components/hakumi_components/float_button/group_cluster/component.rb +46 -54
  81. data/app/components/hakumi_components/float_button/group_spec.rb +259 -0
  82. data/app/components/hakumi_components/float_button/item_spec.rb +115 -0
  83. data/app/components/hakumi_components/float_button/position_config.rb +68 -0
  84. data/app/components/hakumi_components/form/item/component.rb +34 -27
  85. data/app/components/hakumi_components/grid/col/component.rb +88 -17
  86. data/app/components/hakumi_components/grid/row/component.rb +35 -17
  87. data/app/components/hakumi_components/icon/component.rb +49 -49
  88. data/app/components/hakumi_components/image/component.html.erb +7 -7
  89. data/app/components/hakumi_components/image/component.rb +103 -40
  90. data/app/components/hakumi_components/image/preview_group/component.html.erb +1 -1
  91. data/app/components/hakumi_components/image/preview_group/component.rb +41 -36
  92. data/app/components/hakumi_components/image/preview_group/item.rb +48 -0
  93. data/app/components/hakumi_components/input/component.html.erb +1 -1
  94. data/app/components/hakumi_components/input/component.rb +142 -66
  95. data/app/components/hakumi_components/input/password/component.rb +30 -41
  96. data/app/components/hakumi_components/input/text_area/component.html.erb +2 -2
  97. data/app/components/hakumi_components/input/text_area/component.rb +89 -42
  98. data/app/components/hakumi_components/input_number/component.html.erb +5 -5
  99. data/app/components/hakumi_components/input_number/component.rb +137 -55
  100. data/app/components/hakumi_components/layout/component.html.erb +1 -1
  101. data/app/components/hakumi_components/layout/component.rb +23 -16
  102. data/app/components/hakumi_components/layout/content/component.html.erb +1 -1
  103. data/app/components/hakumi_components/layout/content/component.rb +24 -14
  104. data/app/components/hakumi_components/layout/footer/component.html.erb +1 -1
  105. data/app/components/hakumi_components/layout/footer/component.rb +24 -14
  106. data/app/components/hakumi_components/layout/header/component.html.erb +1 -1
  107. data/app/components/hakumi_components/layout/header/component.rb +24 -14
  108. data/app/components/hakumi_components/layout/sider/component.html.erb +1 -1
  109. data/app/components/hakumi_components/layout/sider/component.rb +83 -49
  110. data/app/components/hakumi_components/mentions/coercion.rb +46 -0
  111. data/app/components/hakumi_components/mentions/component.html.erb +6 -6
  112. data/app/components/hakumi_components/mentions/component.rb +131 -67
  113. data/app/components/hakumi_components/mentions/option.rb +30 -0
  114. data/app/components/hakumi_components/menu/component.rb +50 -59
  115. data/app/components/hakumi_components/menu/divider/component.rb +4 -0
  116. data/app/components/hakumi_components/menu/group/component.rb +16 -15
  117. data/app/components/hakumi_components/menu/item/component.rb +42 -19
  118. data/app/components/hakumi_components/menu/sub_menu/component.rb +49 -37
  119. data/app/components/hakumi_components/message/component.rb +62 -27
  120. data/app/components/hakumi_components/modal/component.html.erb +6 -6
  121. data/app/components/hakumi_components/modal/component.rb +93 -40
  122. data/app/components/hakumi_components/modal/confirm/component.html.erb +2 -2
  123. data/app/components/hakumi_components/modal/confirm/component.rb +49 -8
  124. data/app/components/hakumi_components/modal/error/component.rb +8 -39
  125. data/app/components/hakumi_components/modal/info/component.rb +6 -40
  126. data/app/components/hakumi_components/modal/status_component.rb +150 -0
  127. data/app/components/hakumi_components/modal/success/component.rb +6 -40
  128. data/app/components/hakumi_components/modal/warning/component.rb +14 -44
  129. data/app/components/hakumi_components/notification/component.rb +72 -38
  130. data/app/components/hakumi_components/pagination/component.html.erb +7 -11
  131. data/app/components/hakumi_components/pagination/component.rb +105 -72
  132. data/app/components/hakumi_components/pagination/page_item.rb +49 -0
  133. data/app/components/hakumi_components/popconfirm/component.rb +67 -19
  134. data/app/components/hakumi_components/popover/component.html.erb +2 -2
  135. data/app/components/hakumi_components/popover/component.rb +102 -38
  136. data/app/components/hakumi_components/progress/attribute_renderer.rb +354 -0
  137. data/app/components/hakumi_components/progress/circle_geometry.rb +76 -0
  138. data/app/components/hakumi_components/progress/component.html.erb +18 -17
  139. data/app/components/hakumi_components/progress/component.rb +230 -402
  140. data/app/components/hakumi_components/progress/controller_locals_parser.rb +130 -0
  141. data/app/components/hakumi_components/progress/info_tooltip_policy.rb +39 -0
  142. data/app/components/hakumi_components/progress/info_value.rb +75 -0
  143. data/app/components/hakumi_components/progress/status_state.rb +31 -0
  144. data/app/components/hakumi_components/progress/steps_renderer.rb +149 -0
  145. data/app/components/hakumi_components/progress/stroke_gradient_value.rb +90 -0
  146. data/app/components/hakumi_components/qr_code/component.rb +138 -72
  147. data/app/components/hakumi_components/radio/component.html.erb +3 -3
  148. data/app/components/hakumi_components/radio/component.rb +42 -9
  149. data/app/components/hakumi_components/radio/group/component.html.erb +9 -9
  150. data/app/components/hakumi_components/radio/group/component.rb +76 -27
  151. data/app/components/hakumi_components/radio/group/option.rb +43 -0
  152. data/app/components/hakumi_components/rate/component.html.erb +1 -1
  153. data/app/components/hakumi_components/rate/component.rb +55 -9
  154. data/app/components/hakumi_components/result/component.rb +87 -37
  155. data/app/components/hakumi_components/segmented/component.html.erb +1 -1
  156. data/app/components/hakumi_components/segmented/component.rb +81 -118
  157. data/app/components/hakumi_components/segmented/option.rb +191 -0
  158. data/app/components/hakumi_components/select/component.html.erb +19 -19
  159. data/app/components/hakumi_components/select/component.rb +88 -37
  160. data/app/components/hakumi_components/selection_control/coercion.rb +161 -0
  161. data/app/components/hakumi_components/selection_control/entry.rb +22 -0
  162. data/app/components/hakumi_components/selection_control/option.rb +42 -0
  163. data/app/components/hakumi_components/selection_control/option_group.rb +34 -0
  164. data/app/components/hakumi_components/selection_control/tree_node.rb +123 -0
  165. data/app/components/hakumi_components/skeleton/avatar/component.rb +36 -14
  166. data/app/components/hakumi_components/skeleton/avatar_config.rb +79 -0
  167. data/app/components/hakumi_components/skeleton/button/component.rb +32 -14
  168. data/app/components/hakumi_components/skeleton/component.rb +79 -93
  169. data/app/components/hakumi_components/skeleton/image/component.rb +46 -22
  170. data/app/components/hakumi_components/skeleton/input/component.rb +25 -10
  171. data/app/components/hakumi_components/skeleton/node/component.rb +23 -7
  172. data/app/components/hakumi_components/skeleton/paragraph_config.rb +92 -0
  173. data/app/components/hakumi_components/skeleton/title_config.rb +31 -0
  174. data/app/components/hakumi_components/slider/component.html.erb +11 -11
  175. data/app/components/hakumi_components/slider/component.rb +172 -53
  176. data/app/components/hakumi_components/slider/mark.rb +27 -0
  177. data/app/components/hakumi_components/space/compact/component.html.erb +1 -3
  178. data/app/components/hakumi_components/space/compact/component.rb +35 -19
  179. data/app/components/hakumi_components/space/component.html.erb +1 -1
  180. data/app/components/hakumi_components/space/component.rb +75 -39
  181. data/app/components/hakumi_components/spin/component.rb +92 -31
  182. data/app/components/hakumi_components/splitter/component.html.erb +9 -3
  183. data/app/components/hakumi_components/splitter/component.rb +55 -22
  184. data/app/components/hakumi_components/splitter/panel/component.html.erb +1 -3
  185. data/app/components/hakumi_components/splitter/panel/component.rb +64 -39
  186. data/app/components/hakumi_components/statistic/component.rb +143 -59
  187. data/app/components/hakumi_components/steps/component.rb +87 -55
  188. data/app/components/hakumi_components/steps/item/component.rb +59 -20
  189. data/app/components/hakumi_components/switch/component.html.erb +2 -3
  190. data/app/components/hakumi_components/switch/component.rb +62 -36
  191. data/app/components/hakumi_components/table/column/component.rb +80 -23
  192. data/app/components/hakumi_components/table/column_definition.rb +181 -0
  193. data/app/components/hakumi_components/table/column_group/component.rb +32 -21
  194. data/app/components/hakumi_components/table/component.html.erb +22 -31
  195. data/app/components/hakumi_components/table/component.rb +248 -509
  196. data/app/components/hakumi_components/table/concerns/attribute_helpers.rb +22 -0
  197. data/app/components/hakumi_components/table/concerns/cell_rendering.rb +277 -0
  198. data/app/components/hakumi_components/table/concerns/columns.rb +293 -91
  199. data/app/components/hakumi_components/table/concerns/editable.rb +36 -89
  200. data/app/components/hakumi_components/table/concerns/ellipsis.rb +31 -30
  201. data/app/components/hakumi_components/table/concerns/fixed_columns.rb +26 -19
  202. data/app/components/hakumi_components/table/concerns/surface_rendering.rb +365 -0
  203. data/app/components/hakumi_components/table/configs.rb +518 -0
  204. data/app/components/hakumi_components/table/definition_types.rb +40 -0
  205. data/app/components/hakumi_components/table/display_rows.rb +56 -0
  206. data/app/components/hakumi_components/table/ellipsis_config.rb +143 -0
  207. data/app/components/hakumi_components/table/fixed_offset.rb +36 -0
  208. data/app/components/hakumi_components/table/header_cell.rb +23 -0
  209. data/app/components/hakumi_components/table/row_record.rb +60 -0
  210. data/app/components/hakumi_components/table/row_render_state.rb +85 -0
  211. data/app/components/hakumi_components/tabs/component.html.erb +8 -8
  212. data/app/components/hakumi_components/tabs/component.rb +114 -64
  213. data/app/components/hakumi_components/tabs/indicator_config.rb +34 -0
  214. data/app/components/hakumi_components/tabs/item/component.rb +73 -13
  215. data/app/components/hakumi_components/tag/component.rb +142 -47
  216. data/app/components/hakumi_components/tag/group/component.rb +18 -6
  217. data/app/components/hakumi_components/time_picker/component.html.erb +8 -8
  218. data/app/components/hakumi_components/time_picker/component.rb +71 -17
  219. data/app/components/hakumi_components/timeline/component.html.erb +3 -2
  220. data/app/components/hakumi_components/timeline/component.rb +188 -55
  221. data/app/components/hakumi_components/timeline/item/component.html.erb +2 -2
  222. data/app/components/hakumi_components/timeline/item/component.rb +121 -60
  223. data/app/components/hakumi_components/tooltip/component.html.erb +1 -1
  224. data/app/components/hakumi_components/tooltip/component.rb +89 -64
  225. data/app/components/hakumi_components/tour/component.rb +74 -49
  226. data/app/components/hakumi_components/tour/step.rb +86 -0
  227. data/app/components/hakumi_components/transfer/coercion.rb +66 -0
  228. data/app/components/hakumi_components/transfer/component.html.erb +171 -140
  229. data/app/components/hakumi_components/transfer/component.rb +232 -82
  230. data/app/components/hakumi_components/transfer/item.rb +54 -0
  231. data/app/components/hakumi_components/transfer/operations.rb +31 -0
  232. data/app/components/hakumi_components/tree/component.rb +219 -400
  233. data/app/components/hakumi_components/tree/concerns/node_rendering.rb +365 -0
  234. data/app/components/hakumi_components/tree/node.rb +276 -0
  235. data/app/components/hakumi_components/tree_select/component.html.erb +23 -9
  236. data/app/components/hakumi_components/tree_select/component.rb +116 -107
  237. data/app/components/hakumi_components/typography/base_component.rb +69 -36
  238. data/app/components/hakumi_components/typography/link/component.rb +63 -6
  239. data/app/components/hakumi_components/typography/paragraph/component.rb +6 -0
  240. data/app/components/hakumi_components/typography/text/component.rb +4 -0
  241. data/app/components/hakumi_components/typography/title/component.rb +61 -6
  242. data/app/components/hakumi_components/upload/component.html.erb +106 -88
  243. data/app/components/hakumi_components/upload/component.rb +222 -48
  244. data/app/components/hakumi_components/upload/file_entry.rb +53 -0
  245. data/app/controllers/hakumi_components/components_controller.rb +2 -1
  246. data/app/form_builders/hakumi_components/form_builder/contracts.rb +55 -0
  247. data/app/form_builders/hakumi_components/form_builder/field_context.rb +43 -0
  248. data/app/form_builders/hakumi_components/form_builder/model_binding.rb +163 -0
  249. data/app/form_builders/hakumi_components/form_builder.rb +238 -317
  250. data/app/helpers/hakumi_components/form_helper.rb +27 -23
  251. data/app/javascript/hakumi_components/controllers/hakumi/admin_panel_controller.js +5 -7
  252. data/app/javascript/hakumi_components/controllers/hakumi/back_top_controller.js +1 -1
  253. data/app/javascript/hakumi_components/controllers/hakumi/button_controller.js +108 -2
  254. data/app/javascript/hakumi_components/controllers/hakumi/calendar_controller.js +183 -95
  255. data/app/javascript/hakumi_components/controllers/hakumi/color_picker_controller.js +23 -285
  256. data/app/javascript/hakumi_components/controllers/hakumi/date_picker_controller.js +274 -262
  257. data/app/javascript/hakumi_components/controllers/hakumi/float_button_group_controller.js +2 -2
  258. data/app/javascript/hakumi_components/controllers/hakumi/message_controller.js +4 -2
  259. data/app/javascript/hakumi_components/controllers/hakumi/modal_controller.js +119 -125
  260. data/app/javascript/hakumi_components/controllers/hakumi/table/editable.js +291 -0
  261. data/app/javascript/hakumi_components/controllers/hakumi/table_controller.js +166 -366
  262. data/app/javascript/hakumi_components/controllers/hakumi/tabs_controller.js +8 -2
  263. data/app/javascript/hakumi_components/controllers/hakumi/tag_controller.js +27 -25
  264. data/app/javascript/hakumi_components/controllers/hakumi/tag_group_controller.js +19 -18
  265. data/app/javascript/hakumi_components/controllers/hakumi/theme_controller.js +116 -117
  266. data/app/javascript/hakumi_components/controllers/hakumi/transfer_controller.js +17 -1
  267. data/app/javascript/hakumi_components/controllers/hakumi/tree_controller.js +363 -78
  268. data/app/javascript/hakumi_components/controllers/hakumi/typography_controller.js +3 -3
  269. data/app/javascript/hakumi_components/controllers/hakumi/upload_controller.js +320 -204
  270. data/app/javascript/hakumi_components/core/render_component.js +37 -11
  271. data/app/javascript/hakumi_components/utils/color_helper.js +262 -0
  272. data/app/javascript/stylesheets/_base.scss +9 -0
  273. data/app/javascript/stylesheets/_hakumi_components.scss +1 -0
  274. data/app/javascript/stylesheets/components/_breadcrumb.scss +2 -2
  275. data/app/javascript/stylesheets/components/_calendar.scss +13 -13
  276. data/app/javascript/stylesheets/components/_cascader.scss +5 -5
  277. data/app/javascript/stylesheets/components/_checkbox.scss +9 -11
  278. data/app/javascript/stylesheets/components/_color_picker.scss +11 -11
  279. data/app/javascript/stylesheets/components/_date_picker.scss +4 -4
  280. data/app/javascript/stylesheets/components/_descriptions.scss +2 -2
  281. data/app/javascript/stylesheets/components/_drawer.scss +3 -3
  282. data/app/javascript/stylesheets/components/_dropdown.scss +2 -2
  283. data/app/javascript/stylesheets/components/_flex.scss +1 -1
  284. data/app/javascript/stylesheets/components/_float_button.scss +5 -5
  285. data/app/javascript/stylesheets/components/_form_item.scss +92 -0
  286. data/app/javascript/stylesheets/components/_image.scss +15 -15
  287. data/app/javascript/stylesheets/components/_input.scss +23 -113
  288. data/app/javascript/stylesheets/components/_layout.scss +27 -26
  289. data/app/javascript/stylesheets/components/_menu.scss +15 -15
  290. data/app/javascript/stylesheets/components/_modal.scss +13 -13
  291. data/app/javascript/stylesheets/components/_notification.scss +3 -3
  292. data/app/javascript/stylesheets/components/_popover.scss +1 -1
  293. data/app/javascript/stylesheets/components/_segmented.scss +3 -3
  294. data/app/javascript/stylesheets/components/_select.scss +6 -6
  295. data/app/javascript/stylesheets/components/_slider.scss +1 -1
  296. data/app/javascript/stylesheets/components/_spin.scss +2 -2
  297. data/app/javascript/stylesheets/components/_steps.scss +10 -10
  298. data/app/javascript/stylesheets/components/_switch.scss +11 -10
  299. data/app/javascript/stylesheets/components/_table.scss +6 -6
  300. data/app/javascript/stylesheets/components/_tag.scss +2 -2
  301. data/app/javascript/stylesheets/components/_tooltip.scss +4 -4
  302. data/app/javascript/stylesheets/components/_tree_select.scss +3 -3
  303. data/app/javascript/stylesheets/components/_typography.scss +3 -3
  304. data/app/javascript/stylesheets/components/_upload.scss +4 -4
  305. data/app/services/hakumi_components/component_handler.rb +95 -24
  306. data/app/services/hakumi_components/icon/loader.rb +84 -67
  307. data/app/services/hakumi_components/illustrations/loader.rb +35 -27
  308. data/app/views/hakumi/_alert.html.erb +4 -1
  309. data/app/views/hakumi/_drawer.html.erb +1 -1
  310. data/app/views/hakumi/_statistic.html.erb +11 -11
  311. data/lib/generators/hakumi_components/install_generator.rb +14 -1
  312. data/lib/hakumi_components/documentation/models.rb +290 -0
  313. data/lib/hakumi_components/documentation/node.rb +73 -0
  314. data/lib/hakumi_components/documentation.rb +139 -62
  315. data/lib/hakumi_components/engine.rb +17 -11
  316. data/lib/hakumi_components/file_size_checker.rb +215 -0
  317. data/lib/hakumi_components/locales/en.yml +115 -0
  318. data/lib/hakumi_components/rails/attribute_introspection.rb +39 -107
  319. data/lib/hakumi_components/rails/model_reflection.rb +154 -0
  320. data/lib/hakumi_components/rails/validation_introspection.rb +25 -73
  321. data/lib/hakumi_components/rails/validation_mapper.rb +148 -201
  322. data/lib/hakumi_components/rails.rb +2 -0
  323. data/lib/hakumi_components/stylesheet_ownership_checker.rb +191 -0
  324. data/lib/hakumi_components/types/form_field.rb +24 -0
  325. data/lib/hakumi_components/types/html.rb +28 -0
  326. data/lib/hakumi_components/types/rendering.rb +10 -0
  327. data/lib/hakumi_components/types/stimulus.rb +19 -0
  328. data/lib/hakumi_components/types/validation.rb +22 -0
  329. data/lib/hakumi_components/types.rb +13 -0
  330. data/lib/hakumi_components/version.rb +2 -1
  331. data/lib/hakumi_components.rb +26 -6
  332. data/lib/tasks/coverage.rake +1 -0
  333. metadata +100 -136
  334. data/sig/action_view/tag_builder.rbs +0 -24
  335. data/sig/active_support/concern_patch.rbs +0 -7
  336. data/sig/generators/hakumi_components/install_generator.rbs +0 -11
  337. data/sig/hakumi_components/admin_panel/component.rbs +0 -28
  338. data/sig/hakumi_components/affix/component.rbs +0 -44
  339. data/sig/hakumi_components/alert/component.rbs +0 -64
  340. data/sig/hakumi_components/anchor/component.rbs +0 -45
  341. data/sig/hakumi_components/anchor/link/component.rbs +0 -31
  342. data/sig/hakumi_components/autocomplete/component.rbs +0 -68
  343. data/sig/hakumi_components/avatar/component.rbs +0 -44
  344. data/sig/hakumi_components/badge/component.rbs +0 -41
  345. data/sig/hakumi_components/base_component.rbs +0 -82
  346. data/sig/hakumi_components/breadcrumb/component.rbs +0 -21
  347. data/sig/hakumi_components/breadcrumb/item/component.rbs +0 -36
  348. data/sig/hakumi_components/button/component.rbs +0 -56
  349. data/sig/hakumi_components/calendar/component.rbs +0 -41
  350. data/sig/hakumi_components/card/component.rbs +0 -50
  351. data/sig/hakumi_components/card/grid/component.rbs +0 -21
  352. data/sig/hakumi_components/card/meta/component.rbs +0 -25
  353. data/sig/hakumi_components/carousel/component.rbs +0 -88
  354. data/sig/hakumi_components/cascader/component.rbs +0 -52
  355. data/sig/hakumi_components/checkbox/component.rbs +0 -46
  356. data/sig/hakumi_components/checkbox/group/component.rbs +0 -34
  357. data/sig/hakumi_components/collapse/component.rbs +0 -57
  358. data/sig/hakumi_components/collapse/panel/component.rbs +0 -47
  359. data/sig/hakumi_components/color_picker/component.rbs +0 -80
  360. data/sig/hakumi_components/concerns/form_field.rbs +0 -89
  361. data/sig/hakumi_components/container/component.rbs +0 -37
  362. data/sig/hakumi_components/date_picker/component.rbs +0 -72
  363. data/sig/hakumi_components/date_picker/range_picker.rbs +0 -68
  364. data/sig/hakumi_components/date_picker/shared_rendering.rbs +0 -41
  365. data/sig/hakumi_components/descriptions/component.rbs +0 -59
  366. data/sig/hakumi_components/descriptions/item/component.rbs +0 -31
  367. data/sig/hakumi_components/divider/component.rbs +0 -39
  368. data/sig/hakumi_components/drawer/component.rbs +0 -66
  369. data/sig/hakumi_components/dropdown/component.rbs +0 -45
  370. data/sig/hakumi_components/dropdown/divider/component.rbs +0 -11
  371. data/sig/hakumi_components/dropdown/item/component.rbs +0 -45
  372. data/sig/hakumi_components/empty/component.rbs +0 -40
  373. data/sig/hakumi_components/flex/component.rbs +0 -47
  374. data/sig/hakumi_components/float_button/back_top/component.rbs +0 -45
  375. data/sig/hakumi_components/float_button/component.rbs +0 -80
  376. data/sig/hakumi_components/float_button/group/component.rbs +0 -95
  377. data/sig/hakumi_components/float_button/group_cluster/component.rbs +0 -60
  378. data/sig/hakumi_components/form/item/component.rbs +0 -39
  379. data/sig/hakumi_components/form_builder.rbs +0 -37
  380. data/sig/hakumi_components/grid/col/component.rbs +0 -50
  381. data/sig/hakumi_components/grid/row/component.rbs +0 -46
  382. data/sig/hakumi_components/icon/component.rbs +0 -56
  383. data/sig/hakumi_components/image/component.rbs +0 -56
  384. data/sig/hakumi_components/image/preview_group/component.rbs +0 -28
  385. data/sig/hakumi_components/input/component.rbs +0 -71
  386. data/sig/hakumi_components/input/password/component.rbs +0 -27
  387. data/sig/hakumi_components/input/text_area/component.rbs +0 -42
  388. data/sig/hakumi_components/input_number/component.rbs +0 -85
  389. data/sig/hakumi_components/layout/component.rbs +0 -21
  390. data/sig/hakumi_components/layout/content/component.rbs +0 -19
  391. data/sig/hakumi_components/layout/footer/component.rbs +0 -19
  392. data/sig/hakumi_components/layout/header/component.rbs +0 -19
  393. data/sig/hakumi_components/layout/sider/component.rbs +0 -19
  394. data/sig/hakumi_components/mentions/component.rbs +0 -75
  395. data/sig/hakumi_components/menu/component.rbs +0 -52
  396. data/sig/hakumi_components/menu/divider/component.rbs +0 -11
  397. data/sig/hakumi_components/menu/group/component.rbs +0 -20
  398. data/sig/hakumi_components/menu/item/component.rbs +0 -42
  399. data/sig/hakumi_components/menu/sub_menu/component.rbs +0 -37
  400. data/sig/hakumi_components/message/component.rbs +0 -50
  401. data/sig/hakumi_components/modal/component.rbs +0 -76
  402. data/sig/hakumi_components/modal/confirm/component.rbs +0 -32
  403. data/sig/hakumi_components/modal/error/component.rbs +0 -27
  404. data/sig/hakumi_components/modal/info/component.rbs +0 -27
  405. data/sig/hakumi_components/modal/success/component.rbs +0 -27
  406. data/sig/hakumi_components/modal/warning/component.rbs +0 -27
  407. data/sig/hakumi_components/notification/component.rbs +0 -64
  408. data/sig/hakumi_components/pagination/component.rbs +0 -113
  409. data/sig/hakumi_components/popconfirm/component.rbs +0 -52
  410. data/sig/hakumi_components/popover/component.rbs +0 -66
  411. data/sig/hakumi_components/progress/component.rbs +0 -201
  412. data/sig/hakumi_components/qr_code/component.rbs +0 -111
  413. data/sig/hakumi_components/radio/component.rbs +0 -55
  414. data/sig/hakumi_components/radio/group/component.rbs +0 -71
  415. data/sig/hakumi_components/rails/attribute_introspection.rbs +0 -16
  416. data/sig/hakumi_components/rails/validation_introspection.rbs +0 -18
  417. data/sig/hakumi_components/rails/validation_mapper.rbs +0 -53
  418. data/sig/hakumi_components/rails.rbs +0 -6
  419. data/sig/hakumi_components/rate/component.rbs +0 -56
  420. data/sig/hakumi_components/result/component.rbs +0 -65
  421. data/sig/hakumi_components/segmented/component.rbs +0 -72
  422. data/sig/hakumi_components/select/component.rbs +0 -73
  423. data/sig/hakumi_components/skeleton/avatar/component.rbs +0 -23
  424. data/sig/hakumi_components/skeleton/button/component.rbs +0 -24
  425. data/sig/hakumi_components/skeleton/component.rbs +0 -76
  426. data/sig/hakumi_components/skeleton/image/component.rbs +0 -22
  427. data/sig/hakumi_components/skeleton/input/component.rbs +0 -23
  428. data/sig/hakumi_components/skeleton/node/component.rbs +0 -21
  429. data/sig/hakumi_components/slider/component.rbs +0 -85
  430. data/sig/hakumi_components/space/compact/component.rbs +0 -21
  431. data/sig/hakumi_components/space/component.rbs +0 -47
  432. data/sig/hakumi_components/spin/component.rbs +0 -77
  433. data/sig/hakumi_components/splitter/component.rbs +0 -27
  434. data/sig/hakumi_components/splitter/panel/component.rbs +0 -36
  435. data/sig/hakumi_components/statistic/component.rbs +0 -97
  436. data/sig/hakumi_components/steps/component.rbs +0 -82
  437. data/sig/hakumi_components/steps/item/component.rbs +0 -29
  438. data/sig/hakumi_components/switch/component.rbs +0 -52
  439. data/sig/hakumi_components/table/column/component.rbs +0 -57
  440. data/sig/hakumi_components/table/column_group/component.rbs +0 -20
  441. data/sig/hakumi_components/table/component.rbs +0 -279
  442. data/sig/hakumi_components/table/concerns/columns.rbs +0 -95
  443. data/sig/hakumi_components/table/concerns/editable.rbs +0 -40
  444. data/sig/hakumi_components/table/concerns/ellipsis.rbs +0 -27
  445. data/sig/hakumi_components/table/concerns/fixed_columns.rbs +0 -33
  446. data/sig/hakumi_components/tabs/component.rbs +0 -66
  447. data/sig/hakumi_components/tabs/item/component.rbs +0 -29
  448. data/sig/hakumi_components/tag/component.rbs +0 -86
  449. data/sig/hakumi_components/tag/group/component.rbs +0 -24
  450. data/sig/hakumi_components/time_picker/component.rbs +0 -72
  451. data/sig/hakumi_components/timeline/component.rbs +0 -61
  452. data/sig/hakumi_components/timeline/item/component.rbs +0 -70
  453. data/sig/hakumi_components/tooltip/component.rbs +0 -73
  454. data/sig/hakumi_components/tour/component.rbs +0 -84
  455. data/sig/hakumi_components/transfer/component.rbs +0 -75
  456. data/sig/hakumi_components/tree/component.rbs +0 -126
  457. data/sig/hakumi_components/tree_select/component.rbs +0 -122
  458. data/sig/hakumi_components/typography/base_component.rbs +0 -57
  459. data/sig/hakumi_components/typography/link/component.rbs +0 -28
  460. data/sig/hakumi_components/typography/paragraph/component.rbs +0 -13
  461. data/sig/hakumi_components/typography/text/component.rbs +0 -10
  462. data/sig/hakumi_components/typography/title/component.rbs +0 -28
  463. data/sig/hakumi_components/upload/component.rbs +0 -78
  464. data/sig/hakumi_components.rbs +0 -96
  465. data/sig/rails/active_model/validations/comparison_validator.rbs +0 -6
  466. data/sig/rails.rbs +0 -18
  467. data/sig/view_component/base.rbs +0 -28
@@ -1,11 +1,15 @@
1
+ # typed: strict
1
2
  # frozen_string_literal: true
2
3
 
3
4
  module HakumiComponents
4
- class InstallGenerator < Rails::Generators::Base
5
+ class InstallGenerator < ::Rails::Generators::Base
6
+ extend T::Sig
7
+
5
8
  source_root File.expand_path("templates", __dir__)
6
9
 
7
10
  desc "Install Hakumi Components in your Rails application"
8
11
 
12
+ sig { void }
9
13
  def add_npm_package
10
14
  say "Adding @hakumi-dev/hakumi-components to package.json...", :green
11
15
 
@@ -17,6 +21,7 @@ module HakumiComponents
17
21
  end
18
22
  end
19
23
 
24
+ sig { void }
20
25
  def setup_javascript
21
26
  say "Setting up JavaScript...", :green
22
27
 
@@ -80,6 +85,7 @@ module HakumiComponents
80
85
  end
81
86
  end
82
87
 
88
+ sig { void }
83
89
  def setup_stylesheets
84
90
  say "Setting up stylesheets...", :green
85
91
 
@@ -117,6 +123,7 @@ module HakumiComponents
117
123
  end
118
124
  end
119
125
 
126
+ sig { void }
120
127
  def setup_form_builder
121
128
  say "Setting up FormBuilder (optional)...", :green
122
129
 
@@ -136,6 +143,7 @@ module HakumiComponents
136
143
  end
137
144
  end
138
145
 
146
+ sig { void }
139
147
  def show_post_install
140
148
  say ""
141
149
  say "=" * 70, :green
@@ -170,6 +178,7 @@ module HakumiComponents
170
178
 
171
179
  private
172
180
 
181
+ sig { returns(T.nilable(String)) }
173
182
  def detect_js_entry_point
174
183
  # Check common JavaScript entry points in order of preference
175
184
  candidates = [
@@ -181,6 +190,7 @@ module HakumiComponents
181
190
  candidates.find { |file| File.exist?(file) }
182
191
  end
183
192
 
193
+ sig { returns(T.nilable(String)) }
184
194
  def detect_scss_file
185
195
  # Check common SCSS file locations
186
196
  candidates = [
@@ -192,6 +202,7 @@ module HakumiComponents
192
202
  candidates.find { |file| File.exist?(file) }
193
203
  end
194
204
 
205
+ sig { returns(T::Boolean) }
195
206
  def using_modern_bundler?
196
207
  # Detect if using Vite, Webpack, or esbuild (modern bundlers)
197
208
  File.exist?("vite.config.js") ||
@@ -202,12 +213,14 @@ module HakumiComponents
202
213
  (File.exist?("Gemfile") && File.read("Gemfile").include?("vite_rails"))
203
214
  end
204
215
 
216
+ sig { returns(Symbol) }
205
217
  def package_manager
206
218
  return :yarn if File.exist?("yarn.lock")
207
219
  return :npm if File.exist?("package-lock.json")
208
220
  :yarn # default
209
221
  end
210
222
 
223
+ sig { returns(T::Boolean) }
211
224
  def needs_manual_setup?
212
225
  !detect_js_entry_point || !detect_scss_file
213
226
  end
@@ -0,0 +1,290 @@
1
+ # typed: strict
2
+ # frozen_string_literal: true
3
+
4
+ module HakumiComponents
5
+ module Documentation
6
+ MemberHash = T.type_alias { T::Hash[String, T.nilable(T.any(String, T::Boolean))] }
7
+ SectionHash = T.type_alias { T::Hash[String, T.any(T.nilable(String), T::Array[MemberHash])] }
8
+ ExampleHash = T.type_alias { T::Hash[String, T.nilable(T.any(String, T::Boolean))] }
9
+ ComponentHash = T.type_alias do
10
+ T::Hash[String, T.any(
11
+ T.nilable(String),
12
+ T::Array[String],
13
+ T::Array[ExampleHash],
14
+ T::Array[SectionHash]
15
+ )]
16
+ end
17
+
18
+ class Member
19
+ extend T::Sig
20
+
21
+ sig do
22
+ params(
23
+ name: String,
24
+ description: T.nilable(String),
25
+ type: T.nilable(String),
26
+ default_value: T.nilable(String),
27
+ required: T.nilable(T::Boolean),
28
+ values: T.nilable(String),
29
+ example: T.nilable(String),
30
+ returns: T.nilable(String),
31
+ detail: T.nilable(String)
32
+ ).void
33
+ end
34
+ def initialize(
35
+ name:,
36
+ description: nil,
37
+ type: nil,
38
+ default_value: nil,
39
+ required: nil,
40
+ values: nil,
41
+ example: nil,
42
+ returns: nil,
43
+ detail: nil
44
+ )
45
+ @name = name
46
+ @description = description
47
+ @type = type
48
+ @default_value = default_value
49
+ @required = required
50
+ @values = values
51
+ @example = example
52
+ @returns = returns
53
+ @detail = detail
54
+ end
55
+
56
+ sig { returns(String) }
57
+ attr_reader :name
58
+
59
+ sig { returns(T.nilable(String)) }
60
+ attr_reader :description
61
+
62
+ sig { returns(T.nilable(String)) }
63
+ attr_reader :type
64
+
65
+ sig { returns(T.nilable(String)) }
66
+ attr_reader :default_value
67
+
68
+ sig { returns(T.nilable(T::Boolean)) }
69
+ attr_reader :required
70
+
71
+ sig { returns(T.nilable(String)) }
72
+ attr_reader :values
73
+
74
+ sig { returns(T.nilable(String)) }
75
+ attr_reader :example
76
+
77
+ sig { returns(T.nilable(String)) }
78
+ attr_reader :returns
79
+
80
+ sig { returns(T.nilable(String)) }
81
+ attr_reader :detail
82
+
83
+ sig { returns(MemberHash) }
84
+ def to_h
85
+ {
86
+ "name" => @name,
87
+ "description" => @description,
88
+ "type" => @type,
89
+ "default" => @default_value,
90
+ "required" => @required,
91
+ "values" => @values,
92
+ "example" => @example,
93
+ "returns" => @returns,
94
+ "detail" => @detail
95
+ }
96
+ end
97
+ end
98
+
99
+ class Section
100
+ extend T::Sig
101
+
102
+ sig do
103
+ params(
104
+ title: String,
105
+ description: T.nilable(String),
106
+ props: T::Array[Member],
107
+ methods: T::Array[Member],
108
+ events: T::Array[Member],
109
+ slots: T::Array[Member]
110
+ ).void
111
+ end
112
+ def initialize(title:, description: nil, props: [], methods: [], events: [], slots: [])
113
+ @title = title
114
+ @description = description
115
+ @props = props
116
+ @methods = methods
117
+ @events = events
118
+ @slots = slots
119
+ end
120
+
121
+ sig { returns(String) }
122
+ attr_reader :title
123
+
124
+ sig { returns(T.nilable(String)) }
125
+ attr_reader :description
126
+
127
+ sig { returns(T::Array[Member]) }
128
+ attr_reader :props
129
+
130
+ sig { returns(T::Array[Member]) }
131
+ attr_reader :methods
132
+
133
+ sig { returns(T::Array[Member]) }
134
+ attr_reader :events
135
+
136
+ sig { returns(T::Array[Member]) }
137
+ attr_reader :slots
138
+
139
+ sig { returns(SectionHash) }
140
+ def to_h
141
+ {
142
+ "title" => @title,
143
+ "description" => @description,
144
+ "props" => @props.map(&:to_h),
145
+ "methods" => @methods.map(&:to_h),
146
+ "events" => @events.map(&:to_h),
147
+ "slots" => @slots.map(&:to_h)
148
+ }
149
+ end
150
+ end
151
+
152
+ class Example
153
+ extend T::Sig
154
+
155
+ sig do
156
+ params(
157
+ name: String,
158
+ title: String,
159
+ description: T.nilable(String),
160
+ sandbox: T::Boolean,
161
+ content: T.nilable(String)
162
+ ).void
163
+ end
164
+ def initialize(name:, title:, description: nil, sandbox: false, content: nil)
165
+ @name = name
166
+ @title = title
167
+ @description = description
168
+ @sandbox = sandbox
169
+ @content = content
170
+ end
171
+
172
+ sig { returns(String) }
173
+ attr_reader :name
174
+
175
+ sig { returns(String) }
176
+ attr_reader :title
177
+
178
+ sig { returns(T.nilable(String)) }
179
+ attr_reader :description
180
+
181
+ sig { returns(T::Boolean) }
182
+ attr_reader :sandbox
183
+
184
+ sig { returns(T.nilable(String)) }
185
+ attr_reader :content
186
+
187
+ sig { params(content: T.nilable(String)).returns(Example) }
188
+ def with_content(content)
189
+ self.class.new(
190
+ name: @name,
191
+ title: @title,
192
+ description: @description,
193
+ sandbox: @sandbox,
194
+ content: content
195
+ )
196
+ end
197
+
198
+ sig { returns(ExampleHash) }
199
+ def to_h
200
+ {
201
+ "name" => @name,
202
+ "title" => @title,
203
+ "description" => @description,
204
+ "sandbox" => @sandbox,
205
+ "content" => @content
206
+ }
207
+ end
208
+ end
209
+
210
+ class Component
211
+ extend T::Sig
212
+
213
+ sig do
214
+ params(
215
+ name: String,
216
+ description: T.nilable(String),
217
+ category: T.nilable(String),
218
+ when_to_use: T::Array[String],
219
+ when_not_to_use: T::Array[String],
220
+ examples: T::Array[Example],
221
+ api_sections: T::Array[Section]
222
+ ).void
223
+ end
224
+ def initialize(
225
+ name:,
226
+ description: nil,
227
+ category: nil,
228
+ when_to_use: [],
229
+ when_not_to_use: [],
230
+ examples: [],
231
+ api_sections: []
232
+ )
233
+ @name = name
234
+ @description = description
235
+ @category = category
236
+ @when_to_use = when_to_use
237
+ @when_not_to_use = when_not_to_use
238
+ @examples = examples
239
+ @api_sections = api_sections
240
+ end
241
+
242
+ sig { returns(String) }
243
+ attr_reader :name
244
+
245
+ sig { returns(T.nilable(String)) }
246
+ attr_reader :description
247
+
248
+ sig { returns(T.nilable(String)) }
249
+ attr_reader :category
250
+
251
+ sig { returns(T::Array[String]) }
252
+ attr_reader :when_to_use
253
+
254
+ sig { returns(T::Array[String]) }
255
+ attr_reader :when_not_to_use
256
+
257
+ sig { returns(T::Array[Example]) }
258
+ attr_reader :examples
259
+
260
+ sig { returns(T::Array[Section]) }
261
+ attr_reader :api_sections
262
+
263
+ sig { params(examples: T::Array[Example]).returns(Component) }
264
+ def with_examples(examples)
265
+ self.class.new(
266
+ name: @name,
267
+ description: @description,
268
+ category: @category,
269
+ when_to_use: @when_to_use,
270
+ when_not_to_use: @when_not_to_use,
271
+ examples: examples,
272
+ api_sections: @api_sections
273
+ )
274
+ end
275
+
276
+ sig { returns(ComponentHash) }
277
+ def to_h
278
+ {
279
+ "name" => @name,
280
+ "description" => @description,
281
+ "category" => @category,
282
+ "when_to_use" => @when_to_use,
283
+ "when_not_to_use" => @when_not_to_use,
284
+ "examples" => @examples.map(&:to_h),
285
+ "api_sections" => @api_sections.map(&:to_h)
286
+ }
287
+ end
288
+ end
289
+ end
290
+ end
@@ -0,0 +1,73 @@
1
+ # typed: strict
2
+ # frozen_string_literal: true
3
+
4
+ module HakumiComponents
5
+ module Documentation
6
+ class Node
7
+ extend T::Sig
8
+
9
+ # T.untyped is intentional: wraps arbitrary parsed data (Hash, String, Array,
10
+ # Integer, Boolean, nil) from external sources like JSON config files.
11
+ sig { params(value: T.untyped).returns(Node) }
12
+ def self.wrap(value)
13
+ new(value)
14
+ end
15
+
16
+ sig { params(value: T.untyped).void }
17
+ def initialize(value)
18
+ @value = T.let(value, T.untyped)
19
+ end
20
+
21
+ sig { params(key: String).returns(Node) }
22
+ def fetch(key)
23
+ return self.class.wrap(nil) unless @value.is_a?(Hash)
24
+
25
+ value = @value[key]
26
+ self.class.wrap(value)
27
+ end
28
+
29
+ sig { returns(T.nilable(String)) }
30
+ def string
31
+ return @value if @value.is_a?(String)
32
+
33
+ nil
34
+ end
35
+
36
+ sig { returns(T.nilable(T::Boolean)) }
37
+ def boolean
38
+ return @value if @value == true || @value == false
39
+
40
+ nil
41
+ end
42
+
43
+ sig { returns(T.nilable(String)) }
44
+ def scalar_to_string
45
+ return nil if @value.nil?
46
+ return @value if @value.is_a?(String)
47
+ return @value.to_s if @value.is_a?(Symbol) || @value.is_a?(Numeric) || @value == true || @value == false
48
+
49
+ nil
50
+ end
51
+
52
+ sig { returns(T::Array[String]) }
53
+ def string_array
54
+ return [] unless @value.is_a?(Array)
55
+
56
+ @value.filter_map do |entry|
57
+ entry if entry.is_a?(String)
58
+ end
59
+ end
60
+
61
+ sig { returns(T::Array[Node]) }
62
+ def object_array
63
+ return [] unless @value.is_a?(Array)
64
+
65
+ @value.filter_map do |entry|
66
+ next unless entry.is_a?(Hash)
67
+
68
+ self.class.wrap(entry)
69
+ end
70
+ end
71
+ end
72
+ end
73
+ end
@@ -1,41 +1,28 @@
1
+ # typed: strict
1
2
  # frozen_string_literal: true
2
3
 
3
4
  require "yaml"
5
+ require_relative "documentation/models"
6
+ require_relative "documentation/node"
4
7
 
5
8
  module HakumiComponents
6
- # Provides access to component documentation and examples.
7
- # Documentation lives in app/components/hakumi/[component]/docs/ and includes:
8
- # - meta.yml: Component metadata, API documentation, and example definitions
9
- # - examples/*.erb: Example partials that demonstrate component usage
10
- #
11
- # NOTE: Documentation files are NOT included in the published gem.
12
- # This module is primarily useful for:
13
- # - Development playgrounds
14
- # - Documentation generators
15
- # - IDE tooling
16
- #
17
- # @example List all documented components
18
- # HakumiComponents::Documentation.components
19
- # # => ["button", "alert", "modal", ...]
20
- #
21
- # @example Get component metadata
22
- # HakumiComponents::Documentation.metadata("button")
23
- # # => { "name" => "Button", "description" => "...", "api" => [...] }
24
- #
25
- # @example Get example content
26
- # HakumiComponents::Documentation.example("button", "basic_types")
27
- # # => "<%= render(HakumiComponents::Button::Component.new..."
28
- #
29
9
  module Documentation
10
+ INTERNAL_COMPONENTS = T.let(%w[
11
+ admin_panel
12
+ container
13
+ selection_control
14
+ ].freeze, T::Array[String])
15
+
30
16
  class << self
31
- # Root path for component files
32
- # @return [Pathname]
17
+ extend T::Sig
18
+
19
+ sig { returns(Pathname) }
33
20
  def components_root
21
+ @components_root = T.let(@components_root, T.nilable(Pathname)) unless instance_variable_defined?(:@components_root)
34
22
  @components_root ||= Pathname.new(File.expand_path("../../app/components/hakumi_components", __dir__.to_s))
35
23
  end
36
24
 
37
- # List all components that have documentation
38
- # @return [Array<String>] component names
25
+ sig { returns(T::Array[String]) }
39
26
  def components
40
27
  return [] unless components_root.exist?
41
28
 
@@ -45,30 +32,59 @@ module HakumiComponents
45
32
  .sort
46
33
  end
47
34
 
48
- # Get metadata for a component
49
- # @param component_name [String] the component name (e.g., "button")
50
- # @return [Hash, nil] parsed meta.yml content or nil if not found
51
- def metadata(component_name)
52
- meta_path = meta_path_for(component_name)
53
- return nil unless meta_path.exist?
35
+ sig { returns(T::Array[String]) }
36
+ def component_directories
37
+ return [] unless components_root.exist?
54
38
 
55
- YAML.load_file(meta_path)
39
+ components_root.children
40
+ .select(&:directory?)
41
+ .map { |path| path.basename.to_s }
42
+ .reject { |name| name == "concerns" }
43
+ .sort
56
44
  end
57
45
 
58
- # Get list of examples for a component
59
- # @param component_name [String] the component name
60
- # @return [Array<Hash>] example definitions from meta.yml
46
+ sig { returns(T::Array[String]) }
47
+ def internal_components
48
+ INTERNAL_COMPONENTS
49
+ end
50
+
51
+ sig { returns(T::Array[String]) }
52
+ def public_components
53
+ components - internal_components
54
+ end
55
+
56
+ sig { params(component_name: String).returns(T::Boolean) }
57
+ def internal_component?(component_name)
58
+ internal_components.include?(component_name)
59
+ end
60
+
61
+ sig { params(component_name: String).returns(T.nilable(Component)) }
62
+ def metadata(component_name)
63
+ meta = raw_metadata(component_name)
64
+ return nil unless meta
65
+
66
+ Component.new(
67
+ name: meta.fetch("name").string || component_name.tr("_", " ").split.map(&:capitalize).join(" "),
68
+ description: meta.fetch("description").string,
69
+ category: meta.fetch("category").string,
70
+ when_to_use: meta.fetch("when_to_use").string_array,
71
+ when_not_to_use: meta.fetch("when_not_to_use").string_array,
72
+ examples: build_examples(meta),
73
+ api_sections: build_sections(meta)
74
+ )
75
+ end
76
+
77
+ sig { params(component_name: String).returns(T::Array[Example]) }
61
78
  def examples(component_name)
62
79
  meta = metadata(component_name)
63
80
  return [] unless meta
64
81
 
65
- meta["examples"] || []
82
+ meta.examples.map do |doc_example|
83
+ doc_example.with_content(example(component_name, doc_example.name))
84
+ end
66
85
  end
67
86
 
68
- # Get the content of an example partial
69
- # @param component_name [String] the component name
70
- # @param example_name [String] the example name (without underscore or extension)
71
- # @return [String, nil] the example ERB content or nil if not found
87
+ sig { params(component_name: String, example_name: String).returns(T.nilable(String)) }
72
88
  def example(component_name, example_name)
73
89
  example_path = example_path_for(component_name, example_name)
74
90
  return nil unless example_path
@@ -76,52 +92,113 @@ module HakumiComponents
76
92
  example_path.read
77
93
  end
78
94
 
79
- # Get the path to an example partial (for rendering in Rails)
80
- # @param component_name [String] the component name
81
- # @param example_name [String] the example name
82
- # @return [Pathname, nil] the path to the example file or nil if not found
95
+ sig { params(component_name: String, example_name: String).returns(T.nilable(Pathname)) }
83
96
  def example_path_for(component_name, example_name)
84
97
  path = components_root / component_name / "docs" / "examples" / "_#{example_name}.html.erb"
85
98
  path.exist? ? path : nil
86
99
  end
87
100
 
88
- # Get the API documentation for a component
89
- # @param component_name [String] the component name
90
- # @return [Array<Hash>] API property definitions
101
+ sig { params(component_name: String).returns(T::Array[Section]) }
91
102
  def api(component_name)
92
103
  meta = metadata(component_name)
93
104
  return [] unless meta
94
105
 
95
- meta["api"] || []
106
+ meta.api_sections
96
107
  end
97
108
 
98
- # Get all documentation as a hash (useful for JSON export)
99
- # @return [Hash] all component documentation
109
+ sig { returns(T::Hash[String, Component]) }
100
110
  def all
101
- result = Hash.new
111
+ result = T.let({}, T::Hash[String, Component])
102
112
  components.each do |name|
103
- result[name] = {
104
- metadata: metadata(name),
105
- examples: examples(name).map do |ex|
106
- ex.merge("content" => example(name, ex["name"]))
107
- end
108
- }
113
+ documentation = metadata(name)
114
+ next unless documentation
115
+
116
+ result[name] = documentation.with_examples(examples(name))
109
117
  end
110
118
  result
111
119
  end
112
120
 
113
- # Export all documentation to JSON
114
- # @return [String] JSON representation
121
+ sig { params(_args: String).returns(String) }
115
122
  def to_json(*_args)
116
123
  require "json"
117
- JSON.pretty_generate(all)
124
+
125
+ serialized = T.let({}, T::Hash[String, ComponentHash])
126
+ all.each do |name, documentation|
127
+ serialized[name] = documentation.to_h
128
+ end
129
+
130
+ JSON.pretty_generate(serialized)
118
131
  end
119
132
 
120
133
  private
121
134
 
135
+ sig { params(component_name: String).returns(T.nilable(Node)) }
136
+ def raw_metadata(component_name)
137
+ meta_path = meta_path_for(component_name)
138
+ return nil unless meta_path.exist?
139
+
140
+ raw = YAML.load_file(meta_path)
141
+ return nil unless raw.is_a?(Hash)
142
+
143
+ Node.wrap(raw)
144
+ end
145
+
146
+ sig { params(component_name: String).returns(Pathname) }
122
147
  def meta_path_for(component_name)
123
148
  components_root / component_name / "docs" / "meta.yml"
124
149
  end
150
+
151
+ sig { params(meta: Node).returns(T::Array[Example]) }
152
+ def build_examples(meta)
153
+ meta.fetch("examples").object_array.filter_map do |example|
154
+ name = example.fetch("name").string
155
+ next unless name
156
+
157
+ Example.new(
158
+ name: name,
159
+ title: example.fetch("title").string || name.tr("_", " ").split.map(&:capitalize).join(" "),
160
+ description: example.fetch("description").string,
161
+ sandbox: example.fetch("sandbox").boolean || false
162
+ )
163
+ end
164
+ end
165
+
166
+ sig { params(meta: Node).returns(T::Array[Section]) }
167
+ def build_sections(meta)
168
+ meta.fetch("api_sections").object_array.filter_map do |section|
169
+ title = section.fetch("title").string
170
+ next unless title
171
+
172
+ Section.new(
173
+ title: title,
174
+ description: section.fetch("description").string,
175
+ props: build_members(section, "props"),
176
+ methods: build_members(section, "methods"),
177
+ events: build_members(section, "events"),
178
+ slots: build_members(section, "slots")
179
+ )
180
+ end
181
+ end
182
+
183
+ sig { params(section: Node, key: String).returns(T::Array[Member]) }
184
+ def build_members(section, key)
185
+ section.fetch(key).object_array.filter_map do |member|
186
+ name = member.fetch("name").string
187
+ next unless name
188
+
189
+ Member.new(
190
+ name: name,
191
+ description: member.fetch("description").string,
192
+ type: member.fetch("type").string,
193
+ default_value: member.fetch("default").scalar_to_string,
194
+ required: member.fetch("required").boolean,
195
+ values: member.fetch("values").scalar_to_string,
196
+ example: member.fetch("example").string,
197
+ returns: member.fetch("returns").string,
198
+ detail: member.fetch("detail").string
199
+ )
200
+ end
201
+ end
125
202
  end
126
203
  end
127
204
  end