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,17 +1,34 @@
1
+ # typed: strict
1
2
  # frozen_string_literal: true
2
3
 
3
4
  module HakumiComponents
4
5
  module FloatButton
5
6
  class Component < HakumiComponents::BaseComponent
6
- TYPES = [ :default, :primary ].freeze
7
- SHAPES = [ :circle, :square ].freeze
8
- PLACEMENTS = {
9
- bottom_right: { bottom: 24, right: 24 },
10
- bottom_left: { bottom: 24, left: 24 },
11
- top_right: { top: 24, right: 24 },
12
- top_left: { top: 24, left: 24 }
13
- }.freeze
14
-
7
+ extend T::Sig
8
+
9
+ IconValue = T.type_alias { T.nilable(T.any(String, Symbol, ViewComponent::Base, ViewComponent::Slot, ActiveSupport::SafeBuffer)) }
10
+ BadgeCount = T.type_alias { T.nilable(T.any(Integer, Float, String)) }
11
+ TYPES = T.let([ :default, :primary ].freeze, T::Array[Symbol])
12
+ SHAPES = T.let([ :circle, :square ].freeze, T::Array[Symbol])
13
+
14
+ sig do
15
+ params(
16
+ icon: IconValue,
17
+ description: T.nilable(String),
18
+ tooltip: T.nilable(String),
19
+ href: T.nilable(String),
20
+ target: T.nilable(String),
21
+ shape: Symbol,
22
+ type: Symbol,
23
+ danger: T::Boolean,
24
+ badge: T.nilable(Types::HtmlAttributes),
25
+ floating: T::Boolean,
26
+ placement: Symbol,
27
+ offset: T.nilable(HakumiComponents::FloatButton::PositionConfig::OffsetMap),
28
+ button_options: Types::HtmlAttributes,
29
+ html_options: Types::HtmlAttributeValue
30
+ ).void
31
+ end
15
32
  def initialize(
16
33
  icon: :plus,
17
34
  description: nil,
@@ -28,38 +45,38 @@ module HakumiComponents
28
45
  button_options: {},
29
46
  **html_options
30
47
  )
31
- @icon = icon
32
- @description = description
33
- @tooltip = tooltip
34
- @href = href
35
- @target = target
36
- @shape = shape
37
- @type = type
38
- @danger = danger
39
- @badge = badge
40
- @floating = floating
41
- @placement = placement
42
- @offset = offset
43
- @button_options = button_options
44
- @html_options = html_options
48
+ @icon = T.let(icon, IconValue)
49
+ @description = T.let(description, T.nilable(String))
50
+ @tooltip = T.let(tooltip, T.nilable(String))
51
+ @href = T.let(href, T.nilable(String))
52
+ @target = T.let(target, T.nilable(String))
53
+ @shape = T.let(shape, Symbol)
54
+ @type = T.let(type, Symbol)
55
+ @danger = T.let(danger, T::Boolean)
56
+ @badge = T.let(badge, T.nilable(Types::HtmlAttributes))
57
+ @position = T.let(HakumiComponents::FloatButton::PositionConfig.new(floating: floating, placement: placement, offset: offset), HakumiComponents::FloatButton::PositionConfig)
58
+ @button_options = T.let(button_options, Types::HtmlAttributes)
59
+ @html_options = T.let(html_options, Types::HtmlAttributes)
45
60
 
46
61
  validate_props!
47
62
  end
48
63
 
64
+ sig { returns(Types::RenderedContent) }
49
65
  def call
50
66
  wrap_with_badge(float_button_markup)
51
67
  end
52
68
 
53
69
  private
54
70
 
71
+ sig { void }
55
72
  def validate_props!
56
73
  validate_inclusion!(:shape, SHAPES)
57
74
  validate_inclusion!(:type, TYPES)
58
- validate_inclusion!(:placement, PLACEMENTS.keys)
59
75
  validate_description_support!
60
76
  end
61
77
 
62
- def float_button_markup(floating: @floating)
78
+ sig { params(floating: T::Boolean).returns(ActiveSupport::SafeBuffer) }
79
+ def float_button_markup(floating: @position.floating)
63
80
  body_tag = interactive_tag
64
81
 
65
82
  content_tag(:div, root_attributes(floating)) do
@@ -77,30 +94,30 @@ module HakumiComponents
77
94
  end
78
95
  end
79
96
 
97
+ sig { returns(Symbol) }
80
98
  def interactive_tag
81
99
  @href.present? ? :a : :button
82
100
  end
83
101
 
102
+ sig { returns(T.nilable(ActiveSupport::SafeBuffer)) }
84
103
  def icon_element
85
- return nil unless @icon
86
-
87
- icon_markup = if @icon.is_a?(String) || @icon.is_a?(Symbol)
88
- render HakumiComponents::Icon::Component.new(name: @icon)
89
- else
90
- @icon
91
- end
104
+ icon_markup = resolved_icon_markup
105
+ return nil if icon_markup.nil?
92
106
 
93
- content_tag(:span, icon_markup, class: "hakumi-float-button-icon")
107
+ content_tag(:span, class: "hakumi-float-button-icon") { icon_markup }
94
108
  end
95
109
 
110
+ sig { returns(T.nilable(ActiveSupport::SafeBuffer)) }
96
111
  def description_element
97
112
  return nil if @description.blank?
98
113
 
99
114
  tag.span(@description, class: "hakumi-float-button-description")
100
115
  end
101
116
 
117
+ sig { params(floating: T::Boolean).returns(Types::HtmlAttributes) }
102
118
  def root_attributes(floating)
103
- base_attrs = {
119
+ position_style = floating ? @position.style : nil
120
+ base_attrs = T.let({
104
121
  class: class_names(
105
122
  "float-button",
106
123
  {
@@ -109,24 +126,25 @@ module HakumiComponents
109
126
  primary: (@type == :primary),
110
127
  danger: @danger,
111
128
  static: !floating,
112
- "with-description": @description.present?
129
+ 'with-description': @description.present?
113
130
  }
114
131
  ),
115
- style: floating ? floating_style : nil
116
- }
132
+ style: position_style
133
+ }, Types::HtmlAttributes)
117
134
 
118
135
  custom_style = @html_options[:style]
119
136
  merged = merge_attributes(base_attrs, @html_options.except(:style))
120
- merged[:style] = merge_styles(base_attrs[:style], custom_style)
137
+ merged[:style] = merge_styles(position_style, custom_style)
121
138
  merged
122
139
  end
123
140
 
141
+ sig { params(tag_name: Symbol).returns(Types::HtmlAttributes) }
124
142
  def body_attributes(tag_name)
125
- attrs = {
143
+ attrs = T.let({
126
144
  class: "hakumi-float-button-button",
127
- "aria-label": @tooltip || @description,
145
+ 'aria-label': @tooltip || @description,
128
146
  title: @tooltip
129
- }
147
+ }, Types::HtmlAttributes)
130
148
 
131
149
  if tag_name == :button
132
150
  attrs[:type] = "button"
@@ -143,34 +161,12 @@ module HakumiComponents
143
161
  merged
144
162
  end
145
163
 
146
- def floating_style
147
- return nil unless @floating
148
-
149
- resolved = resolved_offset
150
- styles = [ "position: fixed", "z-index: 1000" ]
151
- resolved.each do |key, value|
152
- styles << "#{key}: #{dimension(value)}"
153
- end
154
-
155
- styles.join("; ")
156
- end
157
-
158
- def resolved_offset
159
- placement_offset = PLACEMENTS[@placement] || PLACEMENTS[:bottom_right]
160
- custom_offset = @offset || {}
161
- placement_offset.merge(custom_offset).compact
162
- end
163
-
164
- def dimension(value)
165
- return value if value.is_a?(String)
166
-
167
- "#{value}px"
168
- end
169
-
164
+ sig { returns(Types::RenderedContent) }
170
165
  def slot_content
171
166
  content if content?
172
167
  end
173
168
 
169
+ sig { void }
174
170
  def validate_description_support!
175
171
  return if @description.blank?
176
172
  return if @shape == :square
@@ -178,25 +174,115 @@ module HakumiComponents
178
174
  raise ArgumentError, "description is only supported when shape is :square"
179
175
  end
180
176
 
177
+ sig { params(markup: Types::RenderedContent).returns(Types::RenderedContent) }
181
178
  def wrap_with_badge(markup)
182
- return markup unless @badge.present?
183
-
184
- badge_options = @badge.is_a?(Hash) ? @badge.deep_dup : {}
179
+ return markup if @badge.nil? || @badge.empty?
185
180
 
186
- if @floating
187
- badge_options[:style] = merge_styles(badge_options[:style], floating_style)
181
+ if @position.floating
188
182
  button_markup = float_button_markup(floating: false)
189
- render(HakumiComponents::Badge::Component.new(**badge_options)) { button_markup }
183
+ render(badge_component(style: merge_styles(badge_html_style, @position.style))) { button_markup }
190
184
  else
191
- render(HakumiComponents::Badge::Component.new(**badge_options)) { markup }
185
+ render(badge_component(style: badge_html_style)) { markup }
192
186
  end
193
187
  end
194
188
 
189
+ sig { params(styles: T.nilable(Types::HtmlAttributeValue)).returns(T.nilable(String)) }
195
190
  def merge_styles(*styles)
196
- styles.compact!
197
- return nil if styles.empty?
191
+ entries = styles.compact.map(&:to_s)
192
+ return nil if entries.empty?
193
+
194
+ entries.join("; ")
195
+ end
196
+
197
+ sig { returns(T.nilable(Types::RenderedContent)) }
198
+ def resolved_icon_markup
199
+ return nil if @icon.nil?
200
+ return render HakumiComponents::Icon::Component.new(name: @icon) if @icon.is_a?(String) || @icon.is_a?(Symbol)
201
+
202
+ render_value(@icon)
203
+ end
204
+
205
+ sig { params(style: T.nilable(String)).returns(HakumiComponents::Badge::Component) }
206
+ def badge_component(style:)
207
+ HakumiComponents::Badge::Component.new(
208
+ count: badge_count,
209
+ dot: badge_dot?,
210
+ overflow_count: badge_overflow_count,
211
+ status: badge_status,
212
+ text: badge_text,
213
+ color: badge_color,
214
+ show_zero: badge_show_zero?,
215
+ icon: badge_icon,
216
+ **badge_html_options(style)
217
+ )
218
+ end
219
+
220
+ sig { returns(BadgeCount) }
221
+ def badge_count
222
+ value = @badge&.fetch(:count, nil)
223
+ return value if value.is_a?(Integer) || value.is_a?(Float) || value.is_a?(String)
224
+
225
+ nil
226
+ end
227
+
228
+ sig { returns(T::Boolean) }
229
+ def badge_dot?
230
+ self.class.boolean_html_param(@badge&.fetch(:dot, nil)) == true
231
+ end
232
+
233
+ sig { returns(Integer) }
234
+ def badge_overflow_count
235
+ self.class.integer_html_param(@badge&.fetch(:overflow_count, nil)) || 99
236
+ end
237
+
238
+ sig { returns(HakumiComponents::Badge::Component::Status) }
239
+ def badge_status
240
+ value = @badge&.fetch(:status, nil)
241
+ return value if value.is_a?(String) || value.is_a?(Symbol)
242
+
243
+ nil
244
+ end
245
+
246
+ sig { returns(T.nilable(String)) }
247
+ def badge_text
248
+ self.class.string_html_param(@badge&.fetch(:text, nil))
249
+ end
250
+
251
+ sig { returns(T.nilable(String)) }
252
+ def badge_color
253
+ self.class.string_html_param(@badge&.fetch(:color, nil))
254
+ end
255
+
256
+ sig { returns(T::Boolean) }
257
+ def badge_show_zero?
258
+ self.class.boolean_html_param(@badge&.fetch(:show_zero, nil)) == true
259
+ end
260
+
261
+ sig { returns(HakumiComponents::Badge::Component::IconName) }
262
+ def badge_icon
263
+ value = @badge&.fetch(:icon, nil)
264
+ return value if value.is_a?(String) || value.is_a?(Symbol)
265
+
266
+ nil
267
+ end
268
+
269
+ sig { returns(T.nilable(String)) }
270
+ def badge_html_style
271
+ @badge.nil? ? nil : html_style(@badge)
272
+ end
198
273
 
199
- styles.join("; ")
274
+ sig { params(style: T.nilable(String)).returns(Types::HtmlAttributes) }
275
+ def badge_html_options(style)
276
+ return {} if @badge.nil?
277
+
278
+ options = T.let({}, Types::HtmlAttributes)
279
+ extra_classes = html_classes(@badge)
280
+ options[:class] = extra_classes unless extra_classes.nil?
281
+ options[:style] = style unless style.nil?
282
+ data = data_attributes_from(@badge[:data])
283
+ options[:data] = data unless data.empty?
284
+ options[:id] = @badge[:id] if @badge[:id].is_a?(String)
285
+ options
200
286
  end
201
287
  end
202
288
  end
@@ -1,15 +1,35 @@
1
+ # typed: strict
1
2
  # frozen_string_literal: true
2
3
 
3
4
  module HakumiComponents
4
5
  module FloatButton
5
6
  module Group
6
7
  class Component < HakumiComponents::BaseComponent
8
+ extend T::Sig
9
+
7
10
  SHAPES = HakumiComponents::FloatButton::Component::SHAPES
8
11
  TYPES = HakumiComponents::FloatButton::Component::TYPES
9
- TRIGGERS = [ :click, :hover ].freeze
10
- EXPAND_DIRECTIONS = [ :top, :right, :bottom, :left ].freeze
11
- PLACEMENTS = HakumiComponents::FloatButton::Component::PLACEMENTS
12
-
12
+ TRIGGERS = T.let([ :click, :hover ].freeze, T::Array[Symbol])
13
+ EXPAND_DIRECTIONS = T.let([ :top, :right, :bottom, :left ].freeze, T::Array[Symbol])
14
+
15
+ sig do
16
+ params(
17
+ icon: HakumiComponents::FloatButton::Component::IconValue,
18
+ tooltip: T.nilable(String),
19
+ shape: Symbol,
20
+ type: Symbol,
21
+ trigger: Symbol,
22
+ open: T.nilable(T::Boolean),
23
+ default_open: T::Boolean,
24
+ expand_direction: Symbol,
25
+ items: T::Array[T.any(HakumiComponents::FloatButton::ItemSpec::InputAttributes, HakumiComponents::FloatButton::ItemSpec)],
26
+ floating: T::Boolean,
27
+ placement: Symbol,
28
+ offset: T.nilable(HakumiComponents::FloatButton::PositionConfig::OffsetMap),
29
+ button_options: Types::HtmlAttributes,
30
+ html_options: Types::HtmlAttributeValue
31
+ ).void
32
+ end
13
33
  def initialize(
14
34
  icon: :plus,
15
35
  tooltip: nil,
@@ -26,24 +46,23 @@ module HakumiComponents
26
46
  button_options: {},
27
47
  **html_options
28
48
  )
29
- @icon = icon
30
- @tooltip = tooltip
31
- @shape = shape
32
- @type = type
33
- @trigger = trigger
34
- @open = open
35
- @default_open = default_open
36
- @expand_direction = expand_direction
37
- @items = items
38
- @floating = floating
39
- @placement = placement
40
- @offset = offset
41
- @button_options = button_options
42
- @html_options = html_options
49
+ @icon = T.let(icon, HakumiComponents::FloatButton::Component::IconValue)
50
+ @tooltip = T.let(tooltip, T.nilable(String))
51
+ @shape = T.let(shape, Symbol)
52
+ @type = T.let(type, Symbol)
53
+ @trigger = T.let(trigger, Symbol)
54
+ @open = T.let(open, T.nilable(T::Boolean))
55
+ @default_open = T.let(default_open, T::Boolean)
56
+ @expand_direction = T.let(expand_direction, Symbol)
57
+ @items = T.let(HakumiComponents::FloatButton::ItemSpec.coerce_all(items), T::Array[HakumiComponents::FloatButton::ItemSpec])
58
+ @position = T.let(HakumiComponents::FloatButton::PositionConfig.new(floating: floating, placement: placement, offset: offset), HakumiComponents::FloatButton::PositionConfig)
59
+ @button_options = T.let(button_options, Types::HtmlAttributes)
60
+ @html_options = T.let(html_options, Types::HtmlAttributes)
43
61
 
44
62
  validate_props!
45
63
  end
46
64
 
65
+ sig { returns(Types::RenderedContent) }
47
66
  def call
48
67
  content_tag(:div, root_attributes) do
49
68
  safe_join([
@@ -55,47 +74,46 @@ module HakumiComponents
55
74
 
56
75
  private
57
76
 
77
+ sig { void }
58
78
  def validate_props!
59
79
  validate_inclusion!(:shape, SHAPES)
60
80
  validate_inclusion!(:type, TYPES)
61
81
  validate_inclusion!(:trigger, TRIGGERS)
62
82
  validate_inclusion!(:expand_direction, EXPAND_DIRECTIONS)
63
- validate_inclusion!(:placement, PLACEMENTS.keys)
64
83
  end
65
84
 
85
+ sig { returns(Types::HtmlAttributes) }
66
86
  def root_attributes
67
- base_attrs = {
87
+ base_data = T.let({}, Types::DataAttributes)
88
+ apply_controller_data!(base_data) if use_controller?
89
+
90
+ base_attrs = T.let({
68
91
  class: class_names(
69
92
  "float-button-group",
70
93
  {
71
94
  open: open_for_click?,
72
- "trigger-hover": (@trigger == :hover),
95
+ 'trigger-hover': (@trigger == :hover),
73
96
  "expand-#{@expand_direction}": true,
74
- static: !@floating
97
+ static: !@position.floating
75
98
  }
76
99
  ),
77
- style: floating_style
78
- }
100
+ style: @position.style,
101
+ data: base_data
102
+ }, Types::HtmlAttributes)
79
103
 
80
- merged = merge_attributes(base_attrs, @html_options.except(:data))
81
- merged[:data] = base_attrs_data.merge(@html_options[:data] || {})
82
- apply_controller_data!(merged[:data]) if use_controller?
83
- merged
84
- end
85
-
86
- def base_attrs_data
87
- {
88
- trigger: @trigger,
89
- "expand-direction": @expand_direction
90
- }
104
+ merge_attributes(base_attrs, @html_options)
91
105
  end
92
106
 
107
+ sig { params(data: Types::DataAttributes).void }
93
108
  def apply_controller_data!(data)
94
- data[:controller] = append_controller(data[:controller], "hakumi--float-button-group")
95
- data["hakumi--float-button-group-default-open-value"] = @default_open
96
- data["hakumi--float-button-group-trigger-value"] = @trigger
109
+ controller_data = stimulus_attrs("hakumi--float-button-group", {
110
+ default_open: @default_open,
111
+ trigger: @trigger
112
+ })
113
+ controller_data.each { |key, value| data[key] = value }
97
114
  end
98
115
 
116
+ sig { returns(Types::RenderedContent) }
99
117
  def trigger_button
100
118
  render HakumiComponents::FloatButton::Component.new(
101
119
  icon: @icon,
@@ -109,118 +127,87 @@ module HakumiComponents
109
127
  )
110
128
  end
111
129
 
130
+ sig { returns(Types::HtmlAttributes) }
112
131
  def trigger_button_options
113
132
  options = @button_options.deep_dup
114
133
  return options unless attach_click_action?
115
134
 
116
- data = options[:data] || {}
117
- data[:action] = append_action(data[:action], "click->hakumi--float-button-group#toggle")
135
+ data = data_attributes_from(options[:data])
136
+ data[:action] = append_data_token(data[:action], "click->hakumi--float-button-group#toggle")
118
137
  options[:data] = data
119
138
  options
120
139
  end
121
140
 
141
+ sig { returns(ActiveSupport::SafeBuffer) }
122
142
  def actions_wrapper
123
- content_tag(:div, actions_attributes) do
124
- actions_content
125
- end
143
+ content_tag(:div, actions_content, actions_attributes)
126
144
  end
127
145
 
146
+ sig { returns(Types::HtmlAttributes) }
128
147
  def actions_attributes
129
- attrs = {
148
+ attrs = T.let({
130
149
  class: class_names(
131
150
  "float-button-group-actions",
132
151
  {
133
152
  open: open_for_click?,
134
- "has-content": content?,
153
+ 'has-content': content?,
135
154
  "expand-#{@expand_direction}": true
136
155
  }
137
156
  )
138
- }
139
-
140
- attrs[:data] = {
141
- "hakumi--float-button-group-target": "actions"
142
- } if use_controller?
157
+ }, Types::HtmlAttributes)
143
158
 
159
+ attrs[:data] = { 'hakumi--float-button-group-target': "actions" } if use_controller?
144
160
  attrs
145
161
  end
146
162
 
163
+ sig { returns(Types::RenderedContent) }
147
164
  def actions_content
148
165
  return content if content?
149
166
 
150
167
  safe_join(@items.map { |item| render_item(item) })
151
168
  end
152
169
 
170
+ sig { params(item: HakumiComponents::FloatButton::ItemSpec).returns(Types::RenderedContent) }
153
171
  def render_item(item)
154
- item_options = item.deep_symbolize_keys
155
- item_html_options = item_options.delete(:html_options) { {} }
156
-
157
- component_options = {
172
+ render HakumiComponents::FloatButton::Component.new(
173
+ icon: item.icon,
174
+ description: item.description,
175
+ tooltip: item.tooltip,
176
+ href: item.href,
177
+ target: item.target,
158
178
  shape: @shape,
159
- type: item_options[:type] || @type,
179
+ type: item.type,
180
+ danger: item.danger,
160
181
  floating: false,
161
- placement: :bottom_right
162
- }.merge(item_options)
163
-
164
- merged_html_options = { class: "hakumi-float-button-group-item" }.merge(item_html_options)
165
-
166
- render HakumiComponents::FloatButton::Component.new(**component_options, **merged_html_options)
167
- end
168
-
169
- def floating_style
170
- return nil unless @floating
171
-
172
- resolved = resolved_offset
173
- styles = [ "position: fixed", "z-index: 1000" ]
174
- resolved.each do |key, value|
175
- styles << "#{key}: #{dimension(value)}"
176
- end
177
-
178
- styles.join("; ")
179
- end
180
-
181
- def resolved_offset
182
- placement_offset = PLACEMENTS[@placement] || PLACEMENTS[:bottom_right]
183
- custom_offset = @offset || {}
184
- placement_offset.merge(custom_offset).compact
185
- end
186
-
187
- def dimension(value)
188
- return value if value.is_a?(String)
189
-
190
- "#{value}px"
182
+ placement: :bottom_right,
183
+ **{ class: "hakumi-float-button-group-item" }.merge(item.html_options)
184
+ )
191
185
  end
192
186
 
187
+ sig { returns(T::Boolean) }
193
188
  def open_state?
194
- controlled? ? !!@open : @default_open
189
+ controlled? ? @open == true : @default_open
195
190
  end
196
191
 
192
+ sig { returns(T::Boolean) }
197
193
  def controlled?
198
194
  !@open.nil?
199
195
  end
200
196
 
197
+ sig { returns(T::Boolean) }
201
198
  def use_controller?
202
199
  (@trigger == :click && !controlled?) || (@trigger == :hover)
203
200
  end
204
201
 
202
+ sig { returns(T::Boolean) }
205
203
  def attach_click_action?
206
204
  @trigger == :click && !controlled?
207
205
  end
208
206
 
207
+ sig { returns(T::Boolean) }
209
208
  def open_for_click?
210
209
  @trigger == :click && open_state?
211
210
  end
212
-
213
- def append_controller(existing, name)
214
- return name unless existing.present?
215
-
216
- [ existing, name ].join(" ")
217
- end
218
-
219
- def append_action(existing, action)
220
- return action unless existing.present?
221
-
222
- [ existing, action ].join(" ")
223
- end
224
211
  end
225
212
  end
226
213
  end