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,33 +1,41 @@
1
+ # typed: strict
1
2
  # frozen_string_literal: true
2
3
 
3
4
  module HakumiComponents
4
5
  module Input
5
- # Hakumi Input component
6
- # A basic widget for getting user input, supporting text, number, email, etc.
7
- #
8
- # @example Basic usage
9
- # <%= render HakumiComponents::Input::Component.new(name: "email", placeholder: "Enter email") %>
10
- #
11
- # @example With label and caption
12
- # <%= render HakumiComponents::Input::Component.new(
13
- # name: "username",
14
- # label: "Username",
15
- # caption: "Choose a unique username",
16
- # required: true
17
- # ) %>
18
- #
19
- # @example With prefix and suffix
20
- # <%= render HakumiComponents::Input::Component.new(
21
- # name: "amount",
22
- # prefix: "dollar",
23
- # suffix: "USD",
24
- # placeholder: "0.00"
25
- # ) %>
26
- class Component < HakumiComponents::BaseComponent
27
- include HakumiComponents::Concerns::FormField
28
-
29
- SIZES = [ :small, :middle, :large ].freeze
6
+ class Component < HakumiComponents::BaseComponent
7
+ extend T::Sig
30
8
 
9
+ include HakumiComponents::Concerns::InputControl
10
+
11
+ InputType = T.type_alias { Types::HtmlKey }
12
+ IconName = T.type_alias { BaseComponent::SymbolInput }
13
+ AffixContent = T.type_alias { Types::RenderedContent }
14
+
15
+ sig do
16
+ params(
17
+ name: Types::FormFieldName,
18
+ label: T.nilable(String),
19
+ caption: T.nilable(String),
20
+ value: Types::FormFieldValue,
21
+ placeholder: T.nilable(String),
22
+ type: InputType,
23
+ size: Symbol,
24
+ disabled: T::Boolean,
25
+ readonly: T::Boolean,
26
+ required: T::Boolean,
27
+ prefix: Types::Renderable,
28
+ suffix: Types::Renderable,
29
+ prefix_icon: IconName,
30
+ suffix_icon: IconName,
31
+ allow_clear: T::Boolean,
32
+ maxlength: T.nilable(Integer),
33
+ standalone: T::Boolean,
34
+ rules: T.nilable(T::Array[Types::ValidationRule]),
35
+ errors: Types::FormFieldErrors,
36
+ html_options: Types::HtmlAttributeValue
37
+ ).void
38
+ end
31
39
  def initialize(
32
40
  name:,
33
41
  label: nil,
@@ -35,7 +43,7 @@ module HakumiComponents
35
43
  value: nil,
36
44
  placeholder: nil,
37
45
  type: :text,
38
- size: :middle,
46
+ size: :default,
39
47
  disabled: false,
40
48
  readonly: false,
41
49
  required: false,
@@ -50,12 +58,12 @@ module HakumiComponents
50
58
  errors: [],
51
59
  **html_options
52
60
  )
53
- @name = name
61
+ @name = T.let(name, Types::FormFieldName)
54
62
  @label = label
55
63
  @caption = caption
56
64
  @value = value
57
65
  @placeholder = placeholder
58
- @type = type
66
+ @type = T.let(type, InputType)
59
67
  @size = size
60
68
  @disabled = disabled
61
69
  @readonly = readonly
@@ -67,111 +75,136 @@ module HakumiComponents
67
75
  @allow_clear = allow_clear
68
76
  @maxlength = maxlength
69
77
  @standalone = standalone
70
- @rules = rules
71
- @errors = Array(errors)
72
- @html_options = html_options
73
-
78
+ @rules = T.let(rules, T.nilable(T::Array[Types::ValidationRule]))
79
+ @errors = T.let(errors, Types::FormFieldErrors)
80
+ @html_options = T.let(html_options, Types::HtmlAttributes)
81
+ initialize_input_control_contract!(
82
+ name: @name,
83
+ label: @label,
84
+ caption: @caption,
85
+ value: @value,
86
+ placeholder: @placeholder,
87
+ size: @size,
88
+ disabled: @disabled,
89
+ readonly: @readonly,
90
+ errors: @errors,
91
+ standalone: @standalone,
92
+ required: @required,
93
+ html_options: @html_options,
94
+ rules: @rules
95
+ )
74
96
  validate_props!
75
97
  end
76
98
 
77
99
  private
78
100
 
101
+ sig { void }
79
102
  def validate_props!
80
103
  validate_inclusion!(:size, SIZES)
81
104
  end
82
105
 
106
+ sig { returns(T::Boolean) }
83
107
  def has_affix?
84
- @prefix.present? || @suffix.present? || @prefix_icon.present? || @suffix_icon.present? || @allow_clear
108
+ value_present?(@prefix) || value_present?(@suffix) || !@prefix_icon.nil? || !@suffix_icon.nil? || @allow_clear
85
109
  end
86
110
 
111
+ sig { returns(String) }
87
112
  def wrapper_classes
88
113
  class_names(
89
114
  "input-affix-wrapper",
90
115
  {
91
- @size => (@size != :middle),
92
- "disabled": @disabled
116
+ input_control_size => input_control_size != :default,
117
+ disabled: input_control_disabled
93
118
  },
94
- [ @html_options[:wrapper_class] ]
119
+ [ wrapper_class_name ]
95
120
  )
96
121
  end
97
122
 
123
+ sig { returns(String) }
98
124
  def input_classes
99
125
  class_names(
100
126
  "input",
101
127
  {
102
- @size => (@size != :middle)
128
+ input_control_size => input_control_size != :default
103
129
  },
104
- [ @html_options[:class] ]
130
+ [ html_class_name ]
105
131
  )
106
132
  end
107
133
 
134
+ sig { returns(Types::HtmlAttributes) }
108
135
  def input_attributes
109
- attrs = {
136
+ attrs = T.let({
110
137
  type: @type,
111
- name: @name,
138
+ name: form_field_name,
112
139
  id: input_id,
113
- value: @value,
114
- placeholder: @placeholder,
140
+ value: input_control_value,
141
+ placeholder: input_control_placeholder,
142
+ maxlength: @maxlength,
115
143
  class: has_affix? ? nil : input_classes,
116
- disabled: @disabled ? true : nil,
117
- readonly: @readonly ? true : nil,
118
- "aria-invalid": has_error? ? "true" : nil,
144
+ disabled: input_control_disabled ? true : nil,
145
+ readonly: input_control_readonly ? true : nil,
146
+ "aria-invalid": error? ? "true" : nil,
119
147
  "aria-describedby": describedby_ids,
120
- data: { action: "input->hakumi--input#handleInput" }
121
- }.compact
122
-
123
- # Merge data attributes
124
- if @html_options[:data]
125
- attrs[:data] = attrs[:data].merge(@html_options[:data])
126
- end
148
+ data: merged_input_data_attributes
149
+ }.compact, Types::HtmlAttributes)
127
150
 
128
151
  attrs
129
152
  end
130
153
 
154
+ sig { returns(Types::HtmlAttributes) }
131
155
  def wrapper_attributes
132
156
  {
133
157
  class: wrapper_classes,
134
158
  data: {
135
159
  controller: "hakumi--input",
136
- hakumi__input_has_clear_value: @allow_clear ? "true" : "false"
160
+ hakumi__input_has_clear_value: @allow_clear
137
161
  }
138
162
  }
139
163
  end
140
164
 
165
+ sig { returns(T.nilable(ActiveSupport::SafeBuffer)) }
141
166
  def prefix_element
142
- return nil unless @prefix || @prefix_icon
167
+ prefix_content = if @prefix_icon
168
+ render(HakumiComponents::Icon::Component.new(name: @prefix_icon)).to_s
169
+ else
170
+ rendered_prefix = render_affix_content(@prefix)
171
+ return nil if rendered_prefix.nil?
172
+
173
+ rendered_prefix
174
+ end
143
175
 
144
176
  content_tag(:span, class: "hakumi-input-prefix") do
145
- if @prefix_icon
146
- render HakumiComponents::Icon::Component.new(name: @prefix_icon)
147
- elsif @prefix
148
- @prefix
149
- end
177
+ prefix_content
150
178
  end
151
179
  end
152
180
 
181
+ sig { returns(T.nilable(ActiveSupport::SafeBuffer)) }
153
182
  def suffix_element
154
- return nil unless @suffix || @suffix_icon || @allow_clear
183
+ suffix_content = suffix_content()
184
+ return nil if suffix_content.nil? && !@allow_clear
155
185
 
156
186
  content_tag(:span, class: "hakumi-input-suffix") do
157
187
  safe_join([ suffix_content, clear_button ].compact)
158
188
  end
159
189
  end
160
190
 
191
+ sig { returns(AffixContent) }
161
192
  def suffix_content
162
- return nil unless @suffix || @suffix_icon
193
+ return nil unless value_present?(@suffix) || @suffix_icon
163
194
 
164
195
  if @suffix_icon
165
- render HakumiComponents::Icon::Component.new(name: @suffix_icon)
166
- elsif @suffix
167
- @suffix
196
+ render(HakumiComponents::Icon::Component.new(name: @suffix_icon)).to_s
197
+ else
198
+ render_affix_content(@suffix)
168
199
  end
169
200
  end
170
201
 
202
+ sig { returns(T.nilable(ActiveSupport::SafeBuffer)) }
171
203
  def clear_button
172
204
  return nil unless @allow_clear
173
205
 
174
- content_tag(:span,
206
+ content_tag(
207
+ :span,
175
208
  class: "hakumi-input-clear-icon hakumi-input-clear-icon-hidden",
176
209
  role: "button",
177
210
  tabindex: "-1",
@@ -180,6 +213,49 @@ module HakumiComponents
180
213
  render HakumiComponents::Icon::Component.new(name: :close_circle, theme: :filled)
181
214
  end
182
215
  end
183
- end
216
+
217
+ sig { returns(Types::DataAttributes) }
218
+ def merged_input_data_attributes
219
+ {
220
+ action: "input->hakumi--input#handleInput"
221
+ }.merge(coerce_data_attributes(form_field_html_options[:data]))
222
+ end
223
+
224
+ sig { params(raw_data: Types::HtmlAttributeValue).returns(Types::DataAttributes) }
225
+ def coerce_data_attributes(raw_data)
226
+ data_attributes = T.let({}, Types::DataAttributes)
227
+ return data_attributes unless raw_data.is_a?(Hash)
228
+
229
+ raw_data.each do |key, value|
230
+ next unless key.is_a?(String) || key.is_a?(Symbol)
231
+ next unless value.nil? || value.is_a?(String) || value.is_a?(Symbol) || value.is_a?(TrueClass) || value.is_a?(FalseClass) || value.is_a?(Numeric)
232
+
233
+ data_attributes[key] = value
234
+ end
235
+
236
+ data_attributes
237
+ end
238
+
239
+ sig { params(value: Types::Renderable).returns(AffixContent) }
240
+ def render_affix_content(value)
241
+ return nil unless value_present?(value)
242
+ return value if value.is_a?(ActiveSupport::SafeBuffer) || value.is_a?(String)
243
+ return render(value).to_s if value.is_a?(ViewComponent::Base)
244
+
245
+ nil
246
+ end
247
+
248
+ sig { returns(T.nilable(String)) }
249
+ def html_class_name
250
+ css_class = form_field_html_options[:class]
251
+ css_class.is_a?(String) ? css_class : nil
252
+ end
253
+
254
+ sig { returns(T.nilable(String)) }
255
+ def wrapper_class_name
256
+ wrapper_class = form_field_html_options[:wrapper_class]
257
+ wrapper_class.is_a?(String) ? wrapper_class : nil
258
+ end
259
+ end
184
260
  end
185
261
  end
@@ -1,51 +1,36 @@
1
+ # typed: strict
1
2
  # frozen_string_literal: true
2
3
 
3
4
  module HakumiComponents
4
5
  module Input
5
6
  module Password
6
- # Hakumi Password Input component
7
- # A password input with visibility toggle functionality
8
- #
9
- # @example Basic usage
10
- # <%= render HakumiComponents::Input::Password::Component.new(
11
- # name: "password",
12
- # label: "Password",
13
- # placeholder: "Enter password"
14
- # ) %>
15
- #
16
- # @example With size and validation
17
- # <%= render HakumiComponents::Input::Password::Component.new(
18
- # name: "password",
19
- # label: "Password",
20
- # size: :large,
21
- # required: true,
22
- # errors: ["Password is too short"]
23
- # ) %>
24
7
  class Component < HakumiComponents::Input::Component
25
- def initialize(**options)
26
- # Force type to password
27
- options[:type] = :password
8
+ extend T::Sig
28
9
 
29
- # Password fields always have a suffix (visibility toggle)
30
- # Store the user's suffix separately if provided
31
- @user_suffix = options.delete(:suffix)
10
+ private
32
11
 
33
- super(**options)
12
+ sig { void }
13
+ def before_render
14
+ @user_suffix = T.let(@suffix, Types::Renderable)
15
+ @suffix = T.let(nil, Types::Renderable)
16
+ super
34
17
  end
35
18
 
36
- private
37
-
19
+ sig { returns(Types::HtmlAttributes) }
38
20
  def wrapper_attributes
39
21
  base_attrs = super
40
-
41
- # Merge password-specific data attributes
42
- base_attrs[:data] ||= {}
43
- base_attrs[:data][:controller] = [ base_attrs[:data][:controller], "hakumi--input-password" ].compact.join(" ")
44
- base_attrs[:data][:hakumi__input_password_visible_value] = "false"
45
-
22
+ data_attributes = coerce_data_attributes(base_attrs[:data])
23
+ controller_names = T.let([], T::Array[String])
24
+ current_controller = data_attributes[:controller]
25
+ controller_names << current_controller if current_controller.is_a?(String)
26
+ controller_names << "hakumi--input-password"
27
+ data_attributes[:controller] = controller_names.join(" ")
28
+ data_attributes[:hakumi__input_password_visible_value] = "false"
29
+ base_attrs[:data] = data_attributes
46
30
  base_attrs
47
31
  end
48
32
 
33
+ sig { returns(ActiveSupport::SafeBuffer) }
49
34
  def suffix_element
50
35
  content_tag(:span, class: "hakumi-input-suffix") do
51
36
  safe_join([
@@ -55,18 +40,20 @@ module HakumiComponents
55
40
  end
56
41
  end
57
42
 
43
+ sig { returns(T::Boolean) }
58
44
  def has_affix?
59
- true # Password always has affix (visibility toggle)
45
+ true
60
46
  end
61
47
 
48
+ sig { returns(T.nilable(T.any(String, ActiveSupport::SafeBuffer))) }
62
49
  def user_suffix_content
63
- return nil unless @user_suffix
64
-
65
- @user_suffix
50
+ render_affix_content(@user_suffix)
66
51
  end
67
52
 
53
+ sig { returns(ActiveSupport::SafeBuffer) }
68
54
  def visibility_toggle_button
69
- content_tag(:span,
55
+ content_tag(
56
+ :span,
70
57
  class: "hakumi-input-password-icon",
71
58
  role: "button",
72
59
  tabindex: "-1",
@@ -82,11 +69,13 @@ module HakumiComponents
82
69
  end
83
70
  end
84
71
 
72
+ sig { returns(Types::HtmlAttributes) }
85
73
  def input_attributes
86
74
  attrs = super
87
- # Add target for Stimulus controller
88
- attrs[:data] ||= {}
89
- attrs[:data][:hakumi__input_password_target] = "input"
75
+ attrs[:type] = :password
76
+ data_attributes = coerce_data_attributes(attrs[:data])
77
+ data_attributes[:hakumi__input_password_target] = "input"
78
+ attrs[:data] = data_attributes
90
79
  attrs
91
80
  end
92
81
  end
@@ -1,10 +1,10 @@
1
1
  <% textarea_control = capture do %>
2
2
  <span <%= tag.attributes(wrapper_attributes).to_s.html_safe %>>
3
- <%= content_tag(:textarea, @value, **textarea_attributes) %>
3
+ <%= content_tag(:textarea, input_control_value, **textarea_attributes) %>
4
4
  </span>
5
5
  <% end %>
6
6
 
7
- <% if @standalone %>
7
+ <% if standalone? %>
8
8
  <%= textarea_control %>
9
9
  <% else %>
10
10
  <div <%= tag.attributes(form_item_attributes).to_s.html_safe %>>
@@ -1,37 +1,41 @@
1
+ # typed: strict
1
2
  # frozen_string_literal: true
2
3
 
3
4
  module HakumiComponents
4
5
  module Input
5
6
  module TextArea
6
- # Hakumi TextArea component
7
- # Multi-line text input with optional character count and auto-resize
8
- #
9
- # @example Basic usage
10
- # <%= render HakumiComponents::Input::TextArea::Component.new(
11
- # name: "bio",
12
- # label: "Biography",
13
- # rows: 4
14
- # ) %>
15
- #
16
- # @example With character count
17
- # <%= render HakumiComponents::Input::TextArea::Component.new(
18
- # name: "description",
19
- # label: "Description",
20
- # show_count: true,
21
- # maxlength: 200
22
- # ) %>
23
7
  class Component < HakumiComponents::BaseComponent
24
- include HakumiComponents::Concerns::FormField
8
+ extend T::Sig
25
9
 
26
- SIZES = [ :small, :middle, :large ].freeze
10
+ include HakumiComponents::Concerns::InputControl
27
11
 
12
+ sig do
13
+ params(
14
+ name: Types::FormFieldName,
15
+ label: T.nilable(String),
16
+ caption: T.nilable(String),
17
+ value: Types::FormFieldValue,
18
+ placeholder: T.nilable(String),
19
+ size: Symbol,
20
+ rows: Integer,
21
+ disabled: T::Boolean,
22
+ readonly: T::Boolean,
23
+ required: T::Boolean,
24
+ show_count: T::Boolean,
25
+ maxlength: T.nilable(Integer),
26
+ autosize: T::Boolean,
27
+ standalone: T::Boolean,
28
+ errors: Types::FormFieldErrors,
29
+ html_options: Types::HtmlAttributeValue
30
+ ).void
31
+ end
28
32
  def initialize(
29
33
  name:,
30
34
  label: nil,
31
35
  caption: nil,
32
36
  value: nil,
33
37
  placeholder: nil,
34
- size: :middle,
38
+ size: :default,
35
39
  rows: 4,
36
40
  disabled: false,
37
41
  readonly: false,
@@ -43,7 +47,7 @@ module HakumiComponents
43
47
  errors: [],
44
48
  **html_options
45
49
  )
46
- @name = name
50
+ @name = T.let(name, Types::FormFieldName)
47
51
  @label = label
48
52
  @caption = caption
49
53
  @value = value
@@ -57,34 +61,52 @@ module HakumiComponents
57
61
  @maxlength = maxlength
58
62
  @autosize = autosize
59
63
  @standalone = standalone
60
- @errors = Array(errors)
61
- @html_options = html_options
64
+ @errors = T.let(errors, Types::FormFieldErrors)
65
+ @html_options = T.let(html_options, Types::HtmlAttributes)
66
+ initialize_input_control_contract!(
67
+ name: @name,
68
+ label: @label,
69
+ caption: @caption,
70
+ value: @value,
71
+ placeholder: @placeholder,
72
+ size: @size,
73
+ disabled: @disabled,
74
+ readonly: @readonly,
75
+ errors: @errors,
76
+ standalone: @standalone,
77
+ required: @required,
78
+ html_options: @html_options
79
+ )
62
80
 
63
81
  validate_props!
64
82
  end
65
83
 
66
84
  private
67
85
 
86
+ sig { void }
68
87
  def validate_props!
69
88
  validate_inclusion!(:size, SIZES)
70
89
  end
71
90
 
91
+ sig { returns(String) }
72
92
  def textarea_classes
73
93
  class_names(
74
94
  "input",
75
95
  {
76
- @size => (@size != :middle)
96
+ input_control_size => input_control_size != :default
77
97
  },
78
- [ @html_options[:class] ]
98
+ [ html_class_name ]
79
99
  )
80
100
  end
81
101
 
102
+ sig { returns(String) }
82
103
  def wrapper_classes
83
- classes = [ "hakumi-input-textarea" ]
104
+ classes = T.let([ "hakumi-input-textarea" ], T::Array[String])
84
105
  classes << "hakumi-input-textarea-show-count" if @show_count
85
106
  classes.join(" ")
86
107
  end
87
108
 
109
+ sig { returns(Types::HtmlAttributes) }
88
110
  def wrapper_attributes
89
111
  {
90
112
  class: wrapper_classes,
@@ -97,34 +119,59 @@ module HakumiComponents
97
119
  }
98
120
  end
99
121
 
122
+ sig { returns(Types::HtmlAttributes) }
100
123
  def textarea_attributes
101
- attrs = {
102
- name: @name,
124
+ {
125
+ name: form_field_name,
103
126
  id: input_id,
104
127
  class: textarea_classes,
105
128
  rows: @rows,
106
- placeholder: @placeholder,
107
- disabled: @disabled ? true : nil,
108
- readonly: @readonly ? true : nil,
109
- "aria-invalid": has_error? ? "true" : nil,
129
+ placeholder: input_control_placeholder,
130
+ maxlength: @maxlength,
131
+ disabled: input_control_disabled ? true : nil,
132
+ readonly: input_control_readonly ? true : nil,
133
+ "aria-invalid": error? ? "true" : nil,
110
134
  "aria-describedby": describedby_ids,
111
- data: { action: "input->hakumi--input-textarea#handleInput" }
135
+ data: merged_textarea_data_attributes
112
136
  }.compact
113
-
114
- # Merge user data attributes
115
- if @html_options[:data]
116
- attrs[:data].merge!(@html_options[:data])
117
- end
118
-
119
- attrs
120
137
  end
121
138
 
139
+ sig { returns(String) }
122
140
  def character_count
123
- current = @value.to_s.length
124
- return current.to_s unless @maxlength
141
+ current = input_control_value.to_s.length
142
+ return current.to_s if @maxlength.nil?
125
143
 
126
144
  "#{current} / #{@maxlength}"
127
145
  end
146
+
147
+ sig { returns(Types::DataAttributes) }
148
+ def merged_textarea_data_attributes
149
+ {
150
+ action: "input->hakumi--input-textarea#handleInput"
151
+ }.merge(html_data_attributes)
152
+ end
153
+
154
+ sig { returns(Types::DataAttributes) }
155
+ def html_data_attributes
156
+ data_attributes = T.let({}, Types::DataAttributes)
157
+ html_data = form_field_html_options[:data]
158
+ return data_attributes unless html_data.is_a?(Hash)
159
+
160
+ html_data.each do |key, value|
161
+ next unless key.is_a?(String) || key.is_a?(Symbol)
162
+ next unless value.nil? || value.is_a?(String) || value.is_a?(Symbol) || value.is_a?(TrueClass) || value.is_a?(FalseClass) || value.is_a?(Numeric)
163
+
164
+ data_attributes[key] = value
165
+ end
166
+
167
+ data_attributes
168
+ end
169
+
170
+ sig { returns(T.nilable(String)) }
171
+ def html_class_name
172
+ css_class = form_field_html_options[:class]
173
+ css_class.is_a?(String) ? css_class : nil
174
+ end
128
175
  end
129
176
  end
130
177
  end
@@ -1,4 +1,4 @@
1
- <% if @standalone %>
1
+ <% if standalone? %>
2
2
  <%= content_tag(:span, wrapper_attributes) do %>
3
3
  <%= prefix_element %>
4
4
  <%= tag.input(**input_attributes) %>
@@ -12,7 +12,7 @@
12
12
  aria-label="Increase value"
13
13
  data-hakumi--input-number-target="up"
14
14
  data-action="mousedown->hakumi--input-number#handleStepUp"
15
- <%= "disabled" if @disabled || @readonly %>
15
+ <%= "disabled" if input_control_disabled || input_control_readonly %>
16
16
  >
17
17
  <%= render HakumiComponents::Icon::Component.new(name: :caret_up, size: 10) %>
18
18
  </button>
@@ -22,7 +22,7 @@
22
22
  aria-label="Decrease value"
23
23
  data-hakumi--input-number-target="down"
24
24
  data-action="mousedown->hakumi--input-number#handleStepDown"
25
- <%= "disabled" if @disabled || @readonly %>
25
+ <%= "disabled" if input_control_disabled || input_control_readonly %>
26
26
  >
27
27
  <%= render HakumiComponents::Icon::Component.new(name: :caret_down, size: 10) %>
28
28
  </button>
@@ -48,7 +48,7 @@
48
48
  aria-label="Increase value"
49
49
  data-hakumi--input-number-target="up"
50
50
  data-action="mousedown->hakumi--input-number#handleStepUp"
51
- <%= "disabled" if @disabled || @readonly %>
51
+ <%= "disabled" if input_control_disabled || input_control_readonly %>
52
52
  >
53
53
  <%= render HakumiComponents::Icon::Component.new(name: :caret_up, size: 10) %>
54
54
  </button>
@@ -58,7 +58,7 @@
58
58
  aria-label="Decrease value"
59
59
  data-hakumi--input-number-target="down"
60
60
  data-action="mousedown->hakumi--input-number#handleStepDown"
61
- <%= "disabled" if @disabled || @readonly %>
61
+ <%= "disabled" if input_control_disabled || input_control_readonly %>
62
62
  >
63
63
  <%= render HakumiComponents::Icon::Component.new(name: :caret_down, size: 10) %>
64
64
  </button>