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,22 @@
1
+ # typed: strict
1
2
  # frozen_string_literal: true
2
3
 
3
4
  module HakumiComponents
4
5
  module Timeline
5
6
  class Component < HakumiComponents::BaseComponent
6
- DIRECTIONS = %i[vertical horizontal].freeze
7
- MODES = %i[left right alternate].freeze
8
- VARIANTS = %i[filled outlined].freeze
7
+ extend T::Sig
8
+
9
+ TitleSpan = T.type_alias { T.nilable(BaseComponent::DimensionInput) }
10
+ ItemPayload = T.type_alias { T::Hash[String, T.nilable(String)] }
11
+ ParsedItems = T.type_alias { T::Array[ItemPayload] }
12
+ ControllerOptions = T.type_alias { T.any(ActionController::Parameters, Types::HtmlAttributes) }
13
+ ControllerValue = T.type_alias { T.any(Types::HtmlAttributeValue, ParsedItems) }
14
+ TimelineItem = T.type_alias { T.any(HakumiComponents::Timeline::Item::Component, ViewComponent::Slot) }
15
+ ItemInput = T.type_alias { T.untyped }
16
+
17
+ DIRECTIONS = T.let(%i[vertical horizontal].freeze, T::Array[Symbol])
18
+ MODES = T.let(%i[left right alternate].freeze, T::Array[Symbol])
19
+ VARIANTS = T.let(%i[filled outlined].freeze, T::Array[Symbol])
9
20
 
10
21
  renders_many :items, ->(
11
22
  title: nil,
@@ -17,7 +28,6 @@ module HakumiComponents
17
28
  **options,
18
29
  &block
19
30
  ) do
20
-
21
31
  HakumiComponents::Timeline::Item::Component.new(
22
32
  title: title,
23
33
  body: body,
@@ -31,6 +41,19 @@ module HakumiComponents
31
41
  )
32
42
  end
33
43
 
44
+ sig do
45
+ params(
46
+ direction: Symbol,
47
+ mode: Symbol,
48
+ reverse: T::Boolean,
49
+ pending: Types::Renderable,
50
+ pending_dot: Types::Renderable,
51
+ title_span: TitleSpan,
52
+ variant: Symbol,
53
+ items: ItemInput,
54
+ html_options: Types::HtmlAttributeValue
55
+ ).void
56
+ end
34
57
  def initialize(
35
58
  direction: :vertical,
36
59
  mode: :left,
@@ -39,64 +62,77 @@ module HakumiComponents
39
62
  pending_dot: nil,
40
63
  title_span: nil,
41
64
  variant: :filled,
65
+ items: [],
42
66
  **html_options
43
67
  )
44
- @direction = direction
45
- @mode = mode
46
- @reverse = reverse
47
- @pending = pending
48
- @pending_dot = pending_dot
49
- @title_span = title_span
50
- @variant = variant
51
- @html_options = html_options
68
+ @direction = T.let(direction.to_sym, Symbol)
69
+ @mode = T.let(mode.to_sym, Symbol)
70
+ @reverse = T.let(cast_boolean(reverse) ? true : false, T::Boolean)
71
+ @pending = T.let(pending, Types::Renderable)
72
+ @pending_dot = T.let(pending_dot, Types::Renderable)
73
+ @title_span = T.let(title_span, TitleSpan)
74
+ @variant = T.let(variant.to_sym, Symbol)
75
+ @item_definitions = T.let(normalize_item_definitions(items), ParsedItems)
76
+ @html_options = T.let(html_options, Types::HtmlAttributes)
52
77
 
53
78
  validate_props!
54
79
  end
55
80
 
56
- attr_reader :direction, :mode, :reverse, :pending, :pending_dot, :title_span, :variant
57
-
81
+ sig do
82
+ params(
83
+ params: ControllerOptions
84
+ ).returns(T::Hash[Symbol, ControllerValue])
85
+ end
58
86
  def self.extract_controller_locals(params)
59
87
  {
60
- direction: params[:direction],
61
- mode: params[:mode],
62
- reverse: params[:reverse],
63
- pending: params[:pending],
64
- pending_dot: params[:pending_dot],
65
- title_span: params[:title_span],
66
- variant: params[:variant],
67
- items: parse_json_param(params[:items])
88
+ direction: symbol_html_param(html_param(params, :direction)),
89
+ mode: symbol_html_param(html_param(params, :mode)),
90
+ reverse: boolean_html_param(html_param(params, :reverse)),
91
+ pending: string_html_param(html_param(params, :pending)),
92
+ pending_dot: string_html_param(html_param(params, :pending_dot)),
93
+ title_span: html_primitive_param(html_param(params, :title_span)),
94
+ variant: symbol_html_param(html_param(params, :variant)),
95
+ items: parse_json_param(html_param(params, :items)) || []
68
96
  }
69
97
  end
70
98
 
71
- def self.parse_json_param(value)
72
- return value unless value.is_a?(String)
99
+ sig { returns(Symbol) }
100
+ attr_reader :direction, :mode, :variant
73
101
 
74
- JSON.parse(value)
75
- rescue JSON::ParserError
76
- value
77
- end
102
+ sig { returns(T::Boolean) }
103
+ attr_reader :reverse
78
104
 
79
- def wrapper_attributes
80
- base_attrs = {
81
- class: timeline_classes,
82
- style: timeline_style,
83
- data: stimulus_data
84
- }
105
+ sig { returns(Types::Renderable) }
106
+ attr_reader :pending, :pending_dot
107
+
108
+ sig { returns(TitleSpan) }
109
+ attr_reader :title_span
85
110
 
86
- merge_attributes(base_attrs, @html_options.except(:class))
111
+ sig { returns(Types::HtmlAttributes) }
112
+ def wrapper_attributes
113
+ merge_attributes(
114
+ {
115
+ class: timeline_classes,
116
+ style: timeline_style,
117
+ data: stimulus_attrs("hakumi--timeline", { reverse: reverse })
118
+ },
119
+ @html_options.except(:class)
120
+ )
87
121
  end
88
122
 
123
+ sig { returns(T::Array[TimelineItem]) }
89
124
  def timeline_items
90
- base_items = items.dup
91
- base_items << pending_item if pending_item
125
+ base_items = T.let(programmatic_items + items.dup, T::Array[TimelineItem])
126
+ pending = pending_item
127
+ base_items << pending if pending
92
128
 
93
- return base_items.reverse if reverse
94
-
95
- base_items
129
+ reverse ? base_items.reverse : base_items
96
130
  end
97
131
 
132
+ sig { params(index: Integer, item: TimelineItem).returns(Symbol) }
98
133
  def item_position(index, item)
99
- return item.position if item.position
134
+ existing_position = slot_position(item)
135
+ return existing_position unless existing_position.nil?
100
136
 
101
137
  case mode
102
138
  when :right
@@ -108,12 +144,39 @@ module HakumiComponents
108
144
  end
109
145
  end
110
146
 
147
+ sig do
148
+ params(
149
+ item: TimelineItem,
150
+ index: Integer,
151
+ total: Integer
152
+ ).returns(TimelineItem)
153
+ end
154
+ def contextualized_item(item, index, total)
155
+ if item.is_a?(HakumiComponents::Timeline::Item::Component)
156
+ return item.with_context(index: index, total: total, position: item_position(index, item), reverse: reverse)
157
+ end
158
+
159
+ raw_item = item.public_send(
160
+ :with_context,
161
+ index: index,
162
+ total: total,
163
+ position: item_position(index, item),
164
+ reverse: reverse
165
+ )
166
+
167
+ return raw_item if raw_item.is_a?(ViewComponent::Slot)
168
+ return raw_item if raw_item.is_a?(HakumiComponents::Timeline::Item::Component)
169
+
170
+ item
171
+ end
172
+
173
+ sig { returns(T.nilable(HakumiComponents::Timeline::Item::Component)) }
111
174
  def pending_item
112
- return if pending.nil?
175
+ return nil if pending.nil?
113
176
 
114
177
  HakumiComponents::Timeline::Item::Component.new(
115
178
  title: nil,
116
- body: pending.is_a?(String) ? pending : nil,
179
+ body: pending,
117
180
  loading: true,
118
181
  dot: pending_dot,
119
182
  parent: self,
@@ -123,43 +186,113 @@ module HakumiComponents
123
186
 
124
187
  private
125
188
 
126
- def stimulus_controller
127
- "hakumi--timeline"
189
+ sig { returns(T::Array[HakumiComponents::Timeline::Item::Component]) }
190
+ def programmatic_items
191
+ @item_definitions.map do |item|
192
+ HakumiComponents::Timeline::Item::Component.new(
193
+ title: item["title"],
194
+ body: item["body"] || item["content"],
195
+ color: item_color(item["color"]),
196
+ dot: item["dot"],
197
+ position: item_position_value(item["position"]),
198
+ loading: cast_boolean(item["loading"]) ? true : false,
199
+ parent: self
200
+ )
201
+ end
128
202
  end
129
203
 
130
- def stimulus_data
131
- {
132
- controller: stimulus_controller,
133
- "#{stimulus_controller}-reverse-value" => reverse
134
- }
204
+ sig { params(value: ItemInput).returns(ParsedItems) }
205
+ def normalize_item_definitions(value)
206
+ return [] unless value.is_a?(Array)
207
+
208
+ value.filter_map do |entry|
209
+ next unless entry.is_a?(Hash)
210
+
211
+ item = T.let({}, ItemPayload)
212
+ entry.each do |key, raw_value|
213
+ item[key.to_s] = raw_value&.to_s
214
+ end
215
+ item
216
+ end
217
+ end
218
+
219
+ sig { params(value: T.nilable(String)).returns(Types::HtmlKey) }
220
+ def item_color(value)
221
+ return :blue if value.blank?
222
+
223
+ symbol = value.to_sym
224
+ return symbol if HakumiComponents::Timeline::Item::Component::COLORS.key?(symbol)
225
+
226
+ value
227
+ end
228
+
229
+ sig { params(value: T.nilable(String)).returns(T.nilable(Symbol)) }
230
+ def item_position_value(value)
231
+ return nil if value.blank?
232
+
233
+ value.to_sym
234
+ end
235
+
236
+ sig { params(value: Types::HtmlAttributeValue).returns(T.nilable(ParsedItems)) }
237
+ def self.parse_json_param(value)
238
+ return nil unless value.is_a?(String) && value.present?
239
+
240
+ parsed = JSON.parse(value)
241
+ return nil unless parsed.is_a?(Array)
242
+
243
+ items = T.let([], ParsedItems)
244
+ parsed.each do |entry|
245
+ next unless entry.is_a?(Hash)
246
+
247
+ item = T.let({}, T::Hash[String, T.nilable(String)])
248
+ entry.each do |key, raw_value|
249
+ item[key.to_s] = raw_value&.to_s
250
+ end
251
+ items << item
252
+ end
253
+ items
254
+ rescue JSON::ParserError
255
+ nil
256
+ end
257
+
258
+ sig { params(item: TimelineItem).returns(T.nilable(Symbol)) }
259
+ def slot_position(item)
260
+ return item.position if item.is_a?(HakumiComponents::Timeline::Item::Component)
261
+
262
+ raw_position = item.public_send(:position)
263
+ raw_position.is_a?(Symbol) ? raw_position : nil
135
264
  end
136
265
 
266
+ sig { returns(String) }
137
267
  def timeline_classes
138
- class_names(
268
+ component_classes(
139
269
  "timeline",
140
270
  {
141
271
  direction => true,
142
272
  mode => mode != :left,
143
- "reverse" => reverse,
144
- "outlined" => variant == :outlined
273
+ reverse: reverse,
274
+ outlined: variant == :outlined
145
275
  },
146
- [ @html_options[:class] ]
276
+ @html_options
147
277
  )
148
278
  end
149
279
 
280
+ sig { returns(T.nilable(String)) }
150
281
  def timeline_style
151
282
  span = normalized_title_span
152
- return if span.blank?
283
+ return nil if span.blank?
153
284
 
154
285
  "--hakumi-timeline-title-span: #{span};"
155
286
  end
156
287
 
288
+ sig { void }
157
289
  def validate_props!
158
290
  validate_inclusion!(:direction, DIRECTIONS)
159
291
  validate_inclusion!(:mode, MODES)
160
292
  validate_inclusion!(:variant, VARIANTS)
161
293
  end
162
294
 
295
+ sig { returns(T.nilable(String)) }
163
296
  def normalized_title_span
164
297
  dimension_to_css(title_span)
165
298
  end
@@ -1,6 +1,6 @@
1
1
  <%= tag.div(**wrapper_attributes) do %>
2
- <% if title.present? %>
3
- <div class="hakumi-timeline-item-title"><%= title %></div>
2
+ <% if title_content.present? %>
3
+ <div class="hakumi-timeline-item-title"><%= title_content %></div>
4
4
  <% else %>
5
5
  <div class="hakumi-timeline-item-title hakumi-timeline-item-title-empty"></div>
6
6
  <% end %>
@@ -1,17 +1,40 @@
1
+ # typed: strict
1
2
  # frozen_string_literal: true
2
3
 
3
4
  module HakumiComponents
4
5
  module Timeline
5
6
  module Item
6
7
  class Component < HakumiComponents::BaseComponent
7
- COLORS = {
8
- blue: "var(--color-primary)",
9
- red: "var(--color-error)",
10
- green: "var(--color-success)",
11
- gray: "var(--color-text-secondary)"
12
- }.freeze
13
- POSITIONS = %i[left right].freeze
14
-
8
+ extend T::Sig
9
+
10
+ ColorValue = T.type_alias { Types::HtmlKey }
11
+ ContentBlock = T.type_alias { T.nilable(T.proc.returns(Types::RenderedContent)) }
12
+
13
+ COLORS = T.let(
14
+ {
15
+ blue: "var(--color-primary)",
16
+ red: "var(--color-error)",
17
+ green: "var(--color-success)",
18
+ gray: "var(--color-text-secondary)"
19
+ }.freeze,
20
+ T::Hash[Symbol, String]
21
+ )
22
+ POSITIONS = T.let(%i[left right].freeze, T::Array[Symbol])
23
+
24
+ sig do
25
+ params(
26
+ title: Types::Renderable,
27
+ body: Types::Renderable,
28
+ color: ColorValue,
29
+ dot: Types::Renderable,
30
+ position: T.nilable(Symbol),
31
+ loading: T::Boolean,
32
+ parent: T.nilable(HakumiComponents::Timeline::Component),
33
+ pending: T::Boolean,
34
+ html_options: Types::HtmlAttributeValue,
35
+ block: ContentBlock
36
+ ).void
37
+ end
15
38
  def initialize(
16
39
  title: nil,
17
40
  body: nil,
@@ -24,22 +47,47 @@ module HakumiComponents
24
47
  **html_options,
25
48
  &block
26
49
  )
27
- @title = title
28
- @body = body
29
- @color = color
30
- @dot = dot
31
- @position = position
32
- @loading = loading
33
- @parent = parent
34
- @pending = pending
35
- @html_options = html_options
36
- @content_block = block
50
+ @title = T.let(title, Types::Renderable)
51
+ @body = T.let(body, Types::Renderable)
52
+ @color = T.let(color.is_a?(String) ? color : color.to_sym, ColorValue)
53
+ @dot = T.let(dot, Types::Renderable)
54
+ @position = T.let(position, T.nilable(Symbol))
55
+ @loading = T.let(cast_boolean(loading) ? true : false, T::Boolean)
56
+ @parent = T.let(parent, T.nilable(HakumiComponents::Timeline::Component))
57
+ @pending = T.let(cast_boolean(pending) ? true : false, T::Boolean)
58
+ @html_options = T.let(html_options, Types::HtmlAttributes)
59
+ @content_block = T.let(block, ContentBlock)
60
+ @index = T.let(nil, T.nilable(Integer))
61
+ @total = T.let(nil, T.nilable(Integer))
62
+ @resolved_position = T.let(nil, T.nilable(Symbol))
63
+ @reverse = T.let(false, T::Boolean)
37
64
  validate_props!
38
65
  end
39
66
 
40
- attr_reader :title, :body, :color, :dot, :position, :loading, :parent, :pending
67
+ sig { returns(Types::Renderable) }
68
+ attr_reader :title, :body, :dot
69
+
70
+ sig { returns(ColorValue) }
71
+ attr_reader :color
72
+
73
+ sig { returns(T.nilable(Symbol)) }
74
+ attr_reader :position
41
75
 
42
- def with_context(index:, total:, position: nil, reverse: false)
76
+ sig { returns(T::Boolean) }
77
+ attr_reader :loading, :pending
78
+
79
+ sig { returns(T.nilable(HakumiComponents::Timeline::Component)) }
80
+ attr_reader :parent
81
+
82
+ sig do
83
+ params(
84
+ index: Integer,
85
+ total: Integer,
86
+ position: Symbol,
87
+ reverse: T::Boolean
88
+ ).returns(HakumiComponents::Timeline::Item::Component)
89
+ end
90
+ def with_context(index:, total:, position:, reverse: false)
43
91
  @index = index
44
92
  @total = total
45
93
  @resolved_position = position
@@ -47,88 +95,101 @@ module HakumiComponents
47
95
  self
48
96
  end
49
97
 
98
+ sig { returns(Types::HtmlAttributes) }
50
99
  def wrapper_attributes
51
- base_attrs = {
52
- class: item_classes,
53
- data: item_data,
54
- style: dot_style
55
- }
100
+ merge_attributes(
101
+ {
102
+ class: item_classes,
103
+ data: item_data,
104
+ style: dot_style
105
+ },
106
+ @html_options
107
+ )
108
+ end
56
109
 
57
- merge_attributes(base_attrs, @html_options)
110
+ sig { returns(Types::RenderedContent) }
111
+ def title_content
112
+ render_value(@title)
58
113
  end
59
114
 
115
+ sig { returns(Types::RenderedContent) }
60
116
  def item_content
61
- return @body if @body.present?
117
+ rendered_body = render_value(@body)
118
+ return rendered_body if rendered_body.present?
62
119
  return content if content.present?
63
- return view_context.capture(&@content_block) if @content_block && view_context
120
+
121
+ if @content_block && view_context
122
+ captured = view_context.capture(&@content_block)
123
+ return captured if captured.present?
124
+ end
64
125
 
65
126
  nil
66
127
  end
67
128
 
129
+ sig { returns(Symbol) }
68
130
  def resolved_position
69
- @resolved_position || @position
131
+ @resolved_position || @position || :left
70
132
  end
71
133
 
134
+ sig { returns(T::Boolean) }
72
135
  def last_item?
73
- # When reversed, the first item in the array (index 0) is the last visual item
74
- # When not reversed, the last item in the array is the last visual item
75
- if @reverse
76
- @index == 0
77
- else
78
- @index == (@total.to_i - 1)
79
- end
80
- end
136
+ index = @index
137
+ total = @total
138
+ return false if index.nil? || total.nil?
81
139
 
82
- def dot_component?
83
- dot.respond_to?(:render_in)
140
+ @reverse ? index.zero? : index == (total - 1)
84
141
  end
85
142
 
143
+ sig { returns(Types::RenderedContent) }
86
144
  def dot_content
87
- @dot_content ||= begin
88
- if dot_component?
89
- render(dot)
90
- elsif dot.present?
91
- dot
92
- elsif loading
93
- render(HakumiComponents::Icon::Component.new(name: :loading, spin: true, size: 12))
94
- end
95
- end
145
+ rendered_dot = render_value(@dot)
146
+ return rendered_dot if rendered_dot.present?
147
+ return render(HakumiComponents::Icon::Component.new(name: :loading, spin: true, size: 12)) if loading
148
+
149
+ nil
96
150
  end
97
151
 
98
152
  private
99
153
 
154
+ sig { returns(String) }
100
155
  def item_classes
101
- classes = [ "hakumi-timeline-item" ]
102
- classes << "hakumi-timeline-item-#{resolved_position}" if resolved_position
103
- classes << "hakumi-timeline-item-last" if last_item?
104
- classes << "hakumi-timeline-item-loading" if loading
105
- classes << "hakumi-timeline-item-pending" if pending
106
- classes << "hakumi-timeline-item-custom" if dot.present?
107
- classes.join(" ")
156
+ component_classes(
157
+ "timeline-item",
158
+ {
159
+ resolved_position => true,
160
+ last: last_item?,
161
+ loading: loading,
162
+ pending: pending,
163
+ custom: value_present?(@dot)
164
+ },
165
+ @html_options
166
+ )
108
167
  end
109
168
 
169
+ sig { returns(Types::DataAttributes) }
110
170
  def item_data
111
171
  { timeline_index: @index || 0 }
112
172
  end
113
173
 
174
+ sig { returns(T.nilable(String)) }
114
175
  def dot_style
115
- return unless color
116
-
117
176
  color_value = color_value_for(color)
118
- return unless color_value
177
+ return nil if color_value.nil?
119
178
 
120
179
  "--hakumi-timeline-dot-color: #{color_value};"
121
180
  end
122
181
 
182
+ sig { params(value: ColorValue).returns(T.nilable(String)) }
123
183
  def color_value_for(value)
124
- return COLORS[value.to_sym] if value.is_a?(Symbol) && COLORS.key?(value.to_sym)
184
+ return COLORS[value] if value.is_a?(Symbol) && COLORS.key?(value)
125
185
  return value if value.is_a?(String)
126
186
 
127
187
  nil
128
188
  end
129
189
 
190
+ sig { void }
130
191
  def validate_props!
131
- validate_inclusion!(:position, POSITIONS) if @position
192
+ validate_inclusion!(:position, POSITIONS) unless @position.nil?
132
193
  validate_inclusion!(:color, COLORS.keys) if @color.is_a?(Symbol)
133
194
  end
134
195
  end
@@ -4,7 +4,7 @@
4
4
  data-<%= stimulus_controller %>-target="trigger">
5
5
  <%= content %>
6
6
  </div>
7
- <% if has_tooltip_content? %>
7
+ <% if tooltip_content? %>
8
8
  <div
9
9
  class="<%= tooltip_classes %>"
10
10
  role="tooltip"