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,41 +1,90 @@
1
+ # typed: strict
1
2
  # frozen_string_literal: true
2
3
 
3
4
  module HakumiComponents
4
5
  module Calendar
5
6
  class Component < HakumiComponents::BaseComponent
6
- MODES = %i[month year quarter decade].freeze
7
- WEEK_DAYS = %i[sunday monday tuesday wednesday thursday friday saturday].freeze
8
- LOCALES = {
9
- en: {
7
+ extend T::Sig
8
+
9
+ DateInput = T.type_alias { BaseComponent::DateInput }
10
+ DateRange = T.type_alias { T.nilable([ T.nilable(Date), T.nilable(Date) ]) }
11
+ YearRangeValue = T.type_alias { T.any(T::Range[Integer], T::Array[Integer]) }
12
+ YearRangeInput = T.type_alias { T.nilable(YearRangeValue) }
13
+ DateCellRenderer = T.type_alias { T.nilable(T.proc.params(date: Date).returns(Types::RenderedContent)) }
14
+ MonthCellRenderer = T.type_alias { T.nilable(T.proc.params(month: Integer).returns(Types::RenderedContent)) }
15
+ HeaderRendererProc = T.type_alias do
16
+ T.nilable(T.proc.params(
17
+ arg0: Date,
18
+ arg1: Symbol,
19
+ arg2: NilClass,
20
+ arg3: NilClass
21
+ ).returns(Types::RenderedContent))
22
+ end
23
+ HeaderRenderer = T.type_alias { T.nilable(T.any(FalseClass, HeaderRendererProc)) }
24
+ DisabledDatePredicate = T.type_alias { T.nilable(T.proc.params(date: Date).returns(T::Boolean)) }
25
+ ControllerLocalValue = T.type_alias { T.nilable(T.any(Types::HtmlPrimitive, Types::StringOrSymbolArray)) }
26
+ EventInputKey = T.type_alias { T.any(Date, Types::HtmlKey) }
27
+ EventInputMap = T.type_alias { T::Hash[EventInputKey, T::Array[HakumiComponents::Calendar::EventEntry::InputAttributes]] }
28
+ EventsByDate = T.type_alias { T::Hash[Date, T::Array[HakumiComponents::Calendar::EventEntry]] }
29
+
30
+ MODES = T.let(%i[month year quarter decade].freeze, T::Array[Symbol])
31
+ LOCALES = T.let({
32
+ en: HakumiComponents::Calendar::LocalePack.new(
10
33
  days_short: %w[Su Mo Tu We Th Fr Sa],
11
34
  days_full: %w[Sunday Monday Tuesday Wednesday Thursday Friday Saturday],
12
35
  months_short: %w[Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec],
13
36
  months_full: %w[January February March April May June July August September October November December]
14
- },
15
- es: {
16
- days_short: %w[Do Lu Ma Mi Ju Vi Sá],
17
- days_full: %w[Domingo Lunes Martes Miércoles Jueves Viernes Sábado],
37
+ ),
38
+ es: HakumiComponents::Calendar::LocalePack.new(
39
+ days_short: [ "Do", "Lu", "Ma", "Mi", "Ju", "Vi", "" ],
40
+ days_full: [ "Domingo", "Lunes", "Martes", "Miércoles", "Jueves", "Viernes", "Sábado" ],
18
41
  months_short: %w[Ene Feb Mar Abr May Jun Jul Ago Sep Oct Nov Dic],
19
- months_full: %w[Enero Febrero Marzo Abril Mayo Junio Julio Agosto Septiembre Octubre Noviembre Diciembre]
20
- }
21
- }.freeze
42
+ months_full: [ "Enero", "Febrero", "Marzo", "Abril", "Mayo", "Junio", "Julio", "Agosto", "Septiembre", "Octubre", "Noviembre", "Diciembre" ]
43
+ )
44
+ }.freeze, T::Hash[Symbol, HakumiComponents::Calendar::LocalePack])
22
45
 
23
46
  renders_one :header_content
24
47
 
48
+ sig do
49
+ params(params: BaseComponent::ControllerOptions).returns(T::Hash[Symbol, ControllerLocalValue])
50
+ end
25
51
  def self.extract_controller_locals(params)
52
+ value = html_param(params, :value)
53
+ default_value = html_param(params, :default_value)
54
+ mode = symbol_html_param(html_param(params, :mode))
55
+ locale = symbol_html_param(html_param(params, :locale))
56
+ valid_range_input = html_param(params, :valid_range)
57
+
26
58
  {
27
- value: params[:value],
28
- default_value: params[:default_value],
29
- mode: params[:mode],
30
- fullscreen: params[:fullscreen],
31
- show_week: params[:show_week],
32
- locale: params[:locale],
33
- valid_range: params[:valid_range]
59
+ value: value&.to_s,
60
+ default_value: default_value&.to_s,
61
+ mode: mode,
62
+ fullscreen: boolean_html_param(html_param(params, :fullscreen)),
63
+ show_week: boolean_html_param(html_param(params, :show_week)),
64
+ locale: locale,
65
+ valid_range: valid_range_input.is_a?(Array) ? valid_range_input : nil
34
66
  }
35
67
  end
36
68
 
37
- EVENT_TYPES = %i[success warning error default].freeze
38
-
69
+ sig do
70
+ params(
71
+ value: DateInput,
72
+ default_value: DateInput,
73
+ mode: BaseComponent::SymbolInput,
74
+ fullscreen: T.nilable(T::Boolean),
75
+ show_week: T::Boolean,
76
+ locale: BaseComponent::SymbolInput,
77
+ valid_range: T.nilable(T::Array[DateInput]),
78
+ year_range: YearRangeInput,
79
+ events: EventInputMap,
80
+ date_cell_render: DateCellRenderer,
81
+ month_cell_render: MonthCellRenderer,
82
+ header_render: HeaderRenderer,
83
+ disabled_date: DisabledDatePredicate,
84
+ static: T.nilable(T::Boolean),
85
+ html_options: Types::HtmlAttributeValue
86
+ ).void
87
+ end
39
88
  def initialize(
40
89
  value: nil,
41
90
  default_value: nil,
@@ -53,53 +102,59 @@ module HakumiComponents
53
102
  static: nil,
54
103
  **html_options
55
104
  )
56
- @value = parse_date(value)
57
- @default_value = parse_date(default_value)
58
- @mode = mode&.to_sym || :month
59
- @fullscreen = fullscreen.nil? ? true : fullscreen
60
- @show_week = show_week || false
61
- @locale = locale&.to_sym || :en
62
- @valid_range = normalize_valid_range(valid_range)
63
- @year_range = year_range
64
- @events = normalize_events(events)
65
- @date_cell_render = date_cell_render
66
- @month_cell_render = month_cell_render
67
- @header_render = header_render
68
- @disabled_date = disabled_date
69
- @static = static.nil? ? has_custom_cell_render? : static
70
- @html_options = html_options
105
+ @value = T.let(parse_date(value), T.nilable(Date))
106
+ @default_value = T.let(parse_date(default_value), T.nilable(Date))
107
+ @mode = T.let(normalize_symbol(mode, fallback: :month), Symbol)
108
+ @fullscreen = T.let(fullscreen.nil? ? true : fullscreen, T::Boolean)
109
+ @show_week = T.let(show_week, T::Boolean)
110
+ @locale = T.let(normalize_symbol(locale, fallback: :en), Symbol)
111
+ @valid_range = T.let(normalize_valid_range(valid_range), DateRange)
112
+ @year_range = T.let(year_range, YearRangeInput)
113
+ @events = T.let(normalize_events(events), EventsByDate)
114
+ @date_cell_render = T.let(date_cell_render, DateCellRenderer)
115
+ @month_cell_render = T.let(month_cell_render, MonthCellRenderer)
116
+ @header_render = T.let(header_render, HeaderRenderer)
117
+ @disabled_date = T.let(disabled_date, DisabledDatePredicate)
118
+ @static = T.let(static.nil? ? custom_cell_render? : static, T::Boolean)
119
+ @html_options = T.let(html_options, Types::HtmlAttributes)
120
+ @display_model = T.let(nil, T.nilable(HakumiComponents::Calendar::DisplayModel))
71
121
 
72
122
  validate_props!
73
123
  end
74
124
 
75
- def has_custom_cell_render?
76
- @date_cell_render.respond_to?(:call) || @month_cell_render.respond_to?(:call)
125
+ sig { returns(T::Boolean) }
126
+ def custom_cell_render?
127
+ !@date_cell_render.nil? || !@month_cell_render.nil?
77
128
  end
78
129
 
130
+ sig { returns(T::Boolean) }
79
131
  def static?
80
132
  @static
81
133
  end
82
134
 
135
+ sig { returns(Date) }
83
136
  def current_date
84
137
  @value || @default_value || Date.today
85
138
  end
86
139
 
87
- # Value that is actually selected (without Date.today fallback)
88
- # Used for determining visual selection, not navigation
140
+ sig { returns(T.nilable(Date)) }
89
141
  def selected_value
90
142
  @value || @default_value
91
143
  end
92
144
 
145
+ sig { returns(Integer) }
93
146
  def current_year
94
147
  current_date.year
95
148
  end
96
149
 
150
+ sig { returns(Integer) }
97
151
  def current_month
98
152
  current_date.month
99
153
  end
100
154
 
155
+ sig { returns(String) }
101
156
  def calendar_classes
102
- class_names(
157
+ component_classes(
103
158
  "calendar",
104
159
  {
105
160
  fullscreen: @fullscreen,
@@ -110,20 +165,24 @@ module HakumiComponents
110
165
  "mode-decade": decade_mode?,
111
166
  "show-week": @show_week
112
167
  },
113
- @html_options[:class]
168
+ @html_options
114
169
  )
115
170
  end
116
171
 
172
+ sig { returns(Types::HtmlAttributes) }
117
173
  def wrapper_attributes
118
- base = {
119
- class: calendar_classes,
120
- data: data_attributes
121
- }
122
- merge_attributes(base, @html_options.except(:class))
174
+ merge_attributes(
175
+ {
176
+ class: calendar_classes,
177
+ data: data_attributes
178
+ },
179
+ @html_options.except(:class)
180
+ )
123
181
  end
124
182
 
183
+ sig { returns(Types::DataAttributes) }
125
184
  def data_attributes
126
- attrs = {
185
+ attrs = T.let({
127
186
  controller: "hakumi--calendar",
128
187
  "hakumi--calendar-mode-value": @mode,
129
188
  "hakumi--calendar-year-value": current_year,
@@ -131,317 +190,281 @@ module HakumiComponents
131
190
  "hakumi--calendar-fullscreen-value": @fullscreen,
132
191
  "hakumi--calendar-locale-value": @locale,
133
192
  "hakumi--calendar-static-value": @static
134
- }
135
- attrs["hakumi--calendar-events-value"] = events_to_json if @events.present?
193
+ }, Types::DataAttributes)
194
+ attrs["hakumi--calendar-events-value"] = events_to_json if events?
195
+ attrs["hakumi--calendar-valid-range-value"] = valid_range_to_json if @valid_range
136
196
  attrs
137
197
  end
138
198
 
199
+ sig { returns(String) }
200
+ def valid_range_to_json
201
+ start_date, end_date = T.must(@valid_range)
202
+ [ start_date&.iso8601, end_date&.iso8601 ].to_json
203
+ end
204
+
205
+ sig { returns(String) }
139
206
  def events_to_json
140
- @events.transform_keys { |date| date.to_s }.transform_values do |events|
207
+ @events.transform_keys(&:iso8601).transform_values do |events|
141
208
  events.map do |event|
142
209
  {
143
- id: event[:id],
144
- type: event[:type]&.to_s || "default",
145
- content: event[:content],
210
+ id: event.id,
211
+ type: event.type.to_s,
212
+ content: event.content,
146
213
  html: render_event_badge(event)
147
214
  }
148
215
  end
149
216
  end.to_json
150
217
  end
151
218
 
219
+ sig { params(event: HakumiComponents::Calendar::EventEntry).returns(String) }
152
220
  def render_event_badge(event)
153
221
  helpers.render(
154
222
  HakumiComponents::Badge::Component.new(
155
- status: event[:type],
156
- text: event[:content]
223
+ status: event.type,
224
+ text: event.content
157
225
  )
158
226
  ).to_s
159
227
  end
160
228
 
229
+ sig { returns(T::Boolean) }
161
230
  def month_mode?
162
231
  @mode == :month
163
232
  end
164
233
 
234
+ sig { returns(T::Boolean) }
165
235
  def year_mode?
166
236
  @mode == :year
167
237
  end
168
238
 
239
+ sig { returns(T::Boolean) }
169
240
  def quarter_mode?
170
241
  @mode == :quarter
171
242
  end
172
243
 
244
+ sig { returns(T::Boolean) }
173
245
  def decade_mode?
174
246
  @mode == :decade
175
247
  end
176
248
 
249
+ sig { returns(T::Boolean) }
177
250
  def fullscreen?
178
251
  @fullscreen
179
252
  end
180
253
 
254
+ sig { returns(T::Boolean) }
181
255
  def show_week?
182
256
  @show_week
183
257
  end
184
258
 
259
+ sig { returns(T::Boolean) }
185
260
  def show_header?
186
261
  @header_render != false
187
262
  end
188
263
 
264
+ sig { returns(T::Boolean) }
189
265
  def custom_header?
190
- @header_render.respond_to?(:call)
266
+ @header_render.is_a?(Proc)
191
267
  end
192
268
 
193
- # Show year select in month, year, and quarter modes (to change year)
269
+ sig { returns(T::Boolean) }
194
270
  def show_year_select?
195
271
  month_mode? || year_mode? || quarter_mode?
196
272
  end
197
273
 
198
- # Show month select only in month mode (to change month)
274
+ sig { returns(T::Boolean) }
199
275
  def show_month_select?
200
276
  month_mode?
201
277
  end
202
278
 
203
- # Show mode switch only for standalone calendar (not in date pickers)
279
+ sig { returns(T::Boolean) }
204
280
  def show_mode_switch?
205
281
  fullscreen? && !static?
206
282
  end
207
283
 
284
+ sig { returns(Types::RenderedContent) }
208
285
  def render_custom_header
209
- return unless custom_header?
286
+ return nil unless @header_render.is_a?(Proc)
210
287
 
211
- @header_render.call(
212
- value: current_date,
213
- type: @mode,
214
- on_change: nil,
215
- on_type_change: nil
216
- )
288
+ @header_render.call(current_date, @mode, nil, nil)
289
+ end
290
+
291
+ sig { returns(HakumiComponents::Calendar::LocalePack) }
292
+ def locale_data
293
+ LOCALES.fetch(@locale, LOCALES.fetch(:en))
217
294
  end
218
295
 
296
+ sig { returns(T::Array[String]) }
219
297
  def weekday_names
220
- locale_data[:days_short]
298
+ display_model.weekday_names
221
299
  end
222
300
 
301
+ sig { returns(T::Array[String]) }
223
302
  def month_names
224
- locale_data[:months_full]
303
+ display_model.month_names
225
304
  end
226
305
 
306
+ sig { returns(T::Array[String]) }
227
307
  def month_names_short
228
- locale_data[:months_short]
229
- end
230
-
231
- def locale_data
232
- LOCALES[@locale] || LOCALES[:en]
308
+ display_model.month_names_short
233
309
  end
234
310
 
311
+ sig { returns(T::Array[HakumiComponents::Calendar::WeekRow]) }
235
312
  def weeks_in_month
236
- @weeks_in_month ||= build_weeks_for_month(current_year, current_month)
313
+ display_model.weeks_in_month
237
314
  end
238
315
 
316
+ sig { returns(T::Array[HakumiComponents::Calendar::PanelEntry]) }
239
317
  def months_in_year
240
- (1..12).map do |month|
241
- {
242
- month: month,
243
- name: month_names[month - 1],
244
- short_name: month_names_short[month - 1],
245
- current: month == current_month && current_year == Date.today.year,
246
- selected: selected_value && month == selected_value.month && current_year == selected_value.year
247
- }
248
- end
318
+ display_model.months_in_year
249
319
  end
250
320
 
321
+ sig { returns(T::Array[HakumiComponents::Calendar::PanelEntry]) }
251
322
  def quarters_in_year
252
- today = Date.today
253
- current_quarter = ((current_month - 1) / 3) + 1
254
- selected_quarter = selected_value ? ((selected_value.month - 1) / 3) + 1 : nil
255
-
256
- (1..4).map do |quarter|
257
- {
258
- quarter: quarter,
259
- label: "Q#{quarter}",
260
- current: quarter == current_quarter && current_year == today.year,
261
- selected: selected_value && quarter == selected_quarter && current_year == selected_value.year
262
- }
263
- end
323
+ display_model.quarters_in_year
264
324
  end
265
325
 
326
+ sig { returns(T::Array[HakumiComponents::Calendar::PanelEntry]) }
266
327
  def years_in_decade
267
- # Show 12 years: current decade (10 years) + 1 year before + 1 year after
268
- decade_start = (current_year / 10) * 10
269
- start_year = decade_start - 1
270
- end_year = decade_start + 10
271
- today = Date.today
272
-
273
- (start_year..end_year).map do |year|
274
- {
275
- year: year,
276
- current: year == today.year,
277
- selected: selected_value && year == selected_value.year,
278
- in_decade: year >= decade_start && year < decade_start + 10
279
- }
280
- end
328
+ display_model.years_in_decade
281
329
  end
282
330
 
331
+ sig { returns(String) }
283
332
  def decade_range_label
284
- decade_start = (current_year / 10) * 10
285
- "#{decade_start}-#{decade_start + 9}"
333
+ display_model.decade_range_label
286
334
  end
287
335
 
336
+ sig { returns(T::Array[Integer]) }
288
337
  def years_range
289
- return @year_range.to_a if @year_range.is_a?(Range)
290
- return @year_range if @year_range.is_a?(Array)
291
-
292
- base_year = current_year
293
- ((base_year - 10)..(base_year + 10)).to_a
338
+ display_model.years_range
294
339
  end
295
340
 
341
+ sig { params(date: Date).returns(T::Boolean) }
296
342
  def date_disabled?(date)
297
- return false unless @disabled_date.respond_to?(:call)
298
-
299
- @disabled_date.call(date)
343
+ display_model.date_disabled?(date)
300
344
  end
301
345
 
346
+ sig { params(date: Date).returns(T::Boolean) }
302
347
  def date_in_valid_range?(date)
303
- return true unless @valid_range
304
-
305
- start_date, end_date = @valid_range
306
- (start_date.nil? || date >= start_date) && (end_date.nil? || date <= end_date)
348
+ display_model.date_in_valid_range?(date)
307
349
  end
308
350
 
351
+ sig { params(date: Date).returns(Types::RenderedContent) }
309
352
  def render_date_cell(date)
310
- return @date_cell_render.call(date) if @date_cell_render.respond_to?(:call)
353
+ return @date_cell_render.call(date) if @date_cell_render
311
354
 
312
355
  events_for_date(date)
313
356
  end
314
357
 
358
+ sig { params(date: Date).returns(T.nilable(ActiveSupport::SafeBuffer)) }
315
359
  def events_for_date(date)
316
360
  items = @events[date]
317
361
  return nil if items.blank?
318
362
 
319
363
  safe_join(items.map do |item|
320
- content_tag(:div, class: "hakumi-calendar-event hakumi-calendar-event-#{item[:type] || :default}") do
364
+ content_tag(:div, class: "hakumi-calendar-event hakumi-calendar-event-#{item.type}") do
321
365
  render HakumiComponents::Badge::Component.new(
322
- status: item[:type] || :default,
323
- text: item[:content]
366
+ status: item.type,
367
+ text: item.content
324
368
  )
325
369
  end
326
370
  end)
327
371
  end
328
372
 
329
- def has_events?
330
- @events.present? && @events.any?
373
+ sig { returns(T::Boolean) }
374
+ def events?
375
+ @events.any?
331
376
  end
332
377
 
378
+ sig { params(month: Integer).returns(Types::RenderedContent) }
333
379
  def render_month_cell(month)
334
- return @month_cell_render.call(month) if @month_cell_render.respond_to?(:call)
380
+ return nil unless @month_cell_render
335
381
 
336
- nil
382
+ @month_cell_render.call(month)
337
383
  end
338
384
 
339
- def cell_classes(date)
340
- classes = [ "hakumi-calendar-cell" ]
341
- classes << "hakumi-calendar-cell-in-view" if date.month == current_month
342
- classes << "hakumi-calendar-cell-today" if date == Date.today
343
- classes << "hakumi-calendar-cell-selected" if selected_value && date == selected_value
344
- classes << "hakumi-calendar-cell-disabled" if date_disabled?(date) || !date_in_valid_range?(date)
345
- classes << "hakumi-calendar-cell-weekend" if date.saturday? || date.sunday?
346
- classes.join(" ")
385
+ sig { params(cell: HakumiComponents::Calendar::DayCell).returns(String) }
386
+ def cell_classes(cell)
387
+ display_model.cell_classes(cell)
347
388
  end
348
389
 
349
- def month_cell_classes(month_info)
350
- classes = [ "hakumi-calendar-cell", "hakumi-calendar-cell-month" ]
351
- classes << "hakumi-calendar-cell-selected" if month_info[:selected]
352
- classes << "hakumi-calendar-cell-current" if month_info[:current]
353
- classes.join(" ")
390
+ sig { params(entry: HakumiComponents::Calendar::PanelEntry).returns(String) }
391
+ def month_cell_classes(entry)
392
+ display_model.month_cell_classes(entry)
354
393
  end
355
394
 
356
- def quarter_cell_classes(quarter_info)
357
- classes = [ "hakumi-calendar-cell", "hakumi-calendar-cell-quarter" ]
358
- classes << "hakumi-calendar-cell-selected" if quarter_info[:selected]
359
- classes << "hakumi-calendar-cell-current" if quarter_info[:current]
360
- classes.join(" ")
395
+ sig { params(entry: HakumiComponents::Calendar::PanelEntry).returns(String) }
396
+ def quarter_cell_classes(entry)
397
+ display_model.quarter_cell_classes(entry)
361
398
  end
362
399
 
363
- def year_cell_classes(year_info)
364
- classes = [ "hakumi-calendar-cell", "hakumi-calendar-cell-year" ]
365
- classes << "hakumi-calendar-cell-selected" if year_info[:selected]
366
- classes << "hakumi-calendar-cell-current" if year_info[:current]
367
- classes << "hakumi-calendar-cell-disabled" unless year_info[:in_decade]
368
- classes.join(" ")
400
+ sig { params(entry: HakumiComponents::Calendar::PanelEntry).returns(String) }
401
+ def year_cell_classes(entry)
402
+ display_model.year_cell_classes(entry)
369
403
  end
370
404
 
405
+ sig { params(date: Date).returns(Integer) }
371
406
  def week_number(date)
372
- date.cweek
407
+ display_model.week_number(date)
373
408
  end
374
409
 
375
410
  private
376
411
 
412
+ sig { returns(HakumiComponents::Calendar::DisplayModel) }
413
+ def display_model
414
+ @display_model ||= HakumiComponents::Calendar::DisplayModel.new(
415
+ current_date: current_date,
416
+ selected_value: selected_value,
417
+ show_week: @show_week,
418
+ year_range: @year_range,
419
+ valid_range: @valid_range,
420
+ disabled_date: @disabled_date,
421
+ locale_data: locale_data
422
+ )
423
+ end
424
+
425
+ sig { void }
377
426
  def validate_props!
378
427
  validate_inclusion!(:mode, MODES)
379
428
  validate_inclusion!(:locale, LOCALES.keys)
380
429
  end
381
430
 
431
+ sig { params(value: DateInput).returns(T.nilable(Date)) }
382
432
  def parse_date(value)
383
433
  return nil if value.nil?
384
434
  return value if value.is_a?(Date)
385
- return value.to_date if value.respond_to?(:to_date)
386
-
435
+ return value.to_date if value.is_a?(Time)
387
436
  Date.parse(value.to_s)
388
437
  rescue ArgumentError, TypeError
389
438
  nil
390
439
  end
391
440
 
441
+ sig { params(value: BaseComponent::SymbolInput, fallback: Symbol).returns(Symbol) }
442
+ def normalize_symbol(value, fallback:)
443
+ return fallback if value.nil?
444
+
445
+ symbol = value.to_sym
446
+ symbol.empty? ? fallback : symbol
447
+ rescue NoMethodError
448
+ fallback
449
+ end
450
+
451
+ sig { params(range: T.nilable(T::Array[DateInput])).returns(DateRange) }
392
452
  def normalize_valid_range(range)
393
453
  return nil unless range.is_a?(Array) && range.size == 2
394
454
 
395
455
  [ parse_date(range[0]), parse_date(range[1]) ]
396
456
  end
397
457
 
458
+ sig { params(events: EventInputMap).returns(EventsByDate) }
398
459
  def normalize_events(events)
399
- return {} unless events.is_a?(Hash)
400
-
401
- events.transform_keys { |k| parse_date(k) }.compact
402
- end
403
-
404
- def build_weeks_for_month(year, month)
405
- first_day = Date.new(year, month, 1)
406
- last_day = Date.new(year, month, -1)
407
-
408
- start_date = first_day - first_day.wday
409
- end_date = last_day + (6 - last_day.wday)
410
-
411
- selected_week_number = selected_value&.cweek
412
- selected_week_year = selected_value&.cwyear # Use ISO week year, not calendar year
460
+ normalized = T.let({}, EventsByDate)
461
+ events.each do |key, items|
462
+ date = parse_date(key)
463
+ next if date.nil?
413
464
 
414
- weeks = []
415
- current = start_date
416
-
417
- while current <= end_date
418
- # Calculate Monday of this week (for ISO week number)
419
- # current is Sunday (wday=0), so Monday is current + 1
420
- monday = current.wday == 0 ? current + 1 : current - current.wday + 1
421
- week_number = monday.cweek
422
- week_year = monday.cwyear # Use ISO week year, not calendar year
423
-
424
- week = (0..6).map do |day_offset|
425
- date = current + day_offset
426
- {
427
- date: date,
428
- day: date.day,
429
- in_month: date.month == month,
430
- today: date == Date.today,
431
- selected: selected_value && date == selected_value,
432
- disabled: date_disabled?(date) || !date_in_valid_range?(date),
433
- weekend: date.saturday? || date.sunday?
434
- }
435
- end
436
- weeks << {
437
- week_number: week_number,
438
- week_selected: @show_week && selected_week_number == week_number && selected_week_year == week_year,
439
- days: week
440
- }
441
- current += 7
465
+ normalized[date] = HakumiComponents::Calendar::EventEntry.coerce_all(items)
442
466
  end
443
-
444
- weeks
467
+ normalized
445
468
  end
446
469
  end
447
470
  end