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
@@ -169,45 +169,19 @@ export default class extends RegistryController {
169
169
  initializeTimeFromInput() {
170
170
  // Use hidden input for ISO value (24-hour format)
171
171
  const currentValue = this.hasHiddenInputTarget ? this.hiddenInputTarget.value : ""
172
- const timePart = currentValue.match(/(\d{2}):(\d{2})(?::(\d{2}))?$/)
172
+ const timePart = this.extractTimePart(currentValue)
173
173
 
174
174
  if (timePart) {
175
- let hour = parseInt(timePart[1], 10)
176
- let period = undefined
177
-
178
- // Convert 24h to 12h if needed
179
- if (this.hasPeriodOptionTarget) {
180
- if (hour === 0) {
181
- hour = 12
182
- period = "AM"
183
- } else if (hour < 12) {
184
- period = "AM"
185
- } else if (hour === 12) {
186
- period = "PM"
187
- } else {
188
- hour -= 12
189
- period = "PM"
190
- }
191
- }
192
-
193
- this.selectedTime = {
194
- hour: String(hour).padStart(2, "0"),
195
- minute: timePart[2],
196
- second: timePart[3] || "00"
197
- }
198
-
199
- if (period) {
200
- this.selectedTime.period = period
201
- }
175
+ this.selectedTime = this.selectedTimeFromTimePart(timePart, this.hasPeriodOptionTarget)
202
176
  } else {
203
- this.selectedTime = { hour: "00", minute: "00", second: "00" }
177
+ this.selectedTime = this.defaultSelectedTime()
204
178
  }
205
179
 
206
180
  // Also initialize selectedDate if we have a date in the input
207
181
  if (currentValue) {
208
- const datePart = currentValue.match(/^(\d{4}-\d{2}-\d{2})/)
182
+ const datePart = this.extractDatePart(currentValue)
209
183
  if (datePart) {
210
- this.selectedDate = DateHelper.parseDataDate(datePart[1]).toDate()
184
+ this.selectedDate = DateHelper.parseDataDate(datePart).toDate()
211
185
  }
212
186
  }
213
187
 
@@ -257,7 +231,7 @@ export default class extends RegistryController {
257
231
  const inputValue = this.hasHiddenInputTarget ? this.hiddenInputTarget.value : ""
258
232
 
259
233
 
260
- const calendars = this.panelTarget.querySelectorAll(".hakumi-calendar")
234
+ const calendars = this.calendarElements()
261
235
 
262
236
  calendars.forEach(calendarEl => {
263
237
 
@@ -287,41 +261,60 @@ export default class extends RegistryController {
287
261
 
288
262
  try {
289
263
  switch (picker) {
290
- case "quarter": {
291
- const match = value.match(/Q(\d) (\d{4})/)
292
- if (!match) return null
293
- const quarter = parseInt(match[1], 10)
294
- const year = parseInt(match[2], 10)
295
- const month = (quarter - 1) * 3 + 1
296
- return DateHelper.parseDataDate(`${year}-${String(month).padStart(2, "0")}-01`).toDate()
297
- }
298
- case "year": {
299
- const year = parseInt(value, 10)
300
- if (isNaN(year)) return null
301
- return DateHelper.parseDataDate(`${year}-01-01`).toDate()
302
- }
303
- case "month": {
304
- const [year, month] = value.split("-").map(n => parseInt(n, 10))
305
- if (isNaN(year) || isNaN(month)) return null
306
- return DateHelper.parseDataDate(`${year}-${String(month).padStart(2, "0")}-01`).toDate()
307
- }
308
- case "week": {
309
- const parts = value.split("-W")
310
- const year = parseInt(parts[0], 10)
311
- const week = parseInt(parts[1], 10)
312
- if (isNaN(year) || isNaN(week)) return null
313
- return DateHelper.getMondayOfISOWeek(year, week).toDate()
314
- }
315
- default: {
316
- const parsed = DateHelper.parseDataDate(value)
317
- return parsed.isValid() ? parsed.toDate() : null
318
- }
264
+ case "quarter":
265
+ return this.parseQuarterInputValue(value)
266
+ case "year":
267
+ return this.parseYearInputValue(value)
268
+ case "month":
269
+ return this.parseMonthInputValue(value)
270
+ case "week":
271
+ return this.parseWeekInputValue(value)
272
+ default:
273
+ return this.parseDateInputValue(value)
319
274
  }
320
275
  } catch (e) {
321
276
  return null
322
277
  }
323
278
  }
324
279
 
280
+ parseQuarterInputValue(value) {
281
+ const match = value.match(/Q(\d) (\d{4})/)
282
+ if (!match) return null
283
+
284
+ const quarter = parseInt(match[1], 10)
285
+ const year = parseInt(match[2], 10)
286
+ const month = (quarter - 1) * 3 + 1
287
+ return DateHelper.parseDataDate(`${year}-${String(month).padStart(2, "0")}-01`).toDate()
288
+ }
289
+
290
+ parseYearInputValue(value) {
291
+ const year = parseInt(value, 10)
292
+ if (isNaN(year)) return null
293
+
294
+ return DateHelper.parseDataDate(`${year}-01-01`).toDate()
295
+ }
296
+
297
+ parseMonthInputValue(value) {
298
+ const [year, month] = value.split("-").map(n => parseInt(n, 10))
299
+ if (isNaN(year) || isNaN(month)) return null
300
+
301
+ return DateHelper.parseDataDate(`${year}-${String(month).padStart(2, "0")}-01`).toDate()
302
+ }
303
+
304
+ parseWeekInputValue(value) {
305
+ const parts = value.split("-W")
306
+ const year = parseInt(parts[0], 10)
307
+ const week = parseInt(parts[1], 10)
308
+ if (isNaN(year) || isNaN(week)) return null
309
+
310
+ return DateHelper.getMondayOfISOWeek(year, week).toDate()
311
+ }
312
+
313
+ parseDateInputValue(value) {
314
+ const parsed = DateHelper.parseDataDate(value)
315
+ return parsed.isValid() ? parsed.toDate() : null
316
+ }
317
+
325
318
  close() {
326
319
  if (!this.isOpen) return
327
320
 
@@ -348,7 +341,7 @@ export default class extends RegistryController {
348
341
  updateRangeVisuals() {
349
342
  if (!this.rangeValue || !this.hasPanelTarget) return
350
343
 
351
- const calendars = this.panelTarget.querySelectorAll(".hakumi-calendar")
344
+ const calendars = this.calendarElements()
352
345
 
353
346
  calendars.forEach(calendar => {
354
347
  const api = calendar.hakumiComponent?.api
@@ -370,7 +363,7 @@ export default class extends RegistryController {
370
363
  clearRangeVisuals() {
371
364
  if (!this.hasPanelTarget) return
372
365
 
373
- const calendars = this.panelTarget.querySelectorAll(".hakumi-calendar")
366
+ const calendars = this.calendarElements()
374
367
  calendars.forEach(calendar => {
375
368
  const api = calendar.hakumiComponent?.api
376
369
  if (api) api.clearRange()
@@ -380,44 +373,25 @@ export default class extends RegistryController {
380
373
  syncRangeCalendars() {
381
374
  if (!this.rangeValue || !this.hasPanelTarget) return
382
375
 
383
- const calendars = this.panelTarget.querySelectorAll(".hakumi-calendar")
384
- if (calendars.length !== 2) return
385
-
386
- const startCalendar = calendars[0]
387
- const endCalendar = calendars[1]
388
-
389
- const startApi = startCalendar.hakumiComponent?.api
390
- const endApi = endCalendar.hakumiComponent?.api
376
+ const rangeCalendars = this.rangeCalendarElements()
377
+ if (!rangeCalendars) return
391
378
 
379
+ const [startCalendar, endCalendar] = rangeCalendars
380
+ const [startApi, endApi] = this.calendarApis(startCalendar, endCalendar)
392
381
  if (!startApi || !endApi) return
393
382
 
394
383
  // Get current month/year from first calendar
395
384
  const startDate = startApi.getValue()
396
- const startMonth = startDate.getMonth()
397
- const startYear = startDate.getFullYear()
398
-
399
- // Calculate next month for second calendar
400
- let endMonth = startMonth + 1
401
- let endYear = startYear
402
-
403
- if (endMonth > 11) {
404
- endMonth = 0
405
- endYear += 1
406
- }
407
-
408
- // Set second calendar to next month
409
- const endDate = new Date(endYear, endMonth, 1)
410
- endApi.setValue(endDate, { navigate: true })
385
+ endApi.setValue(this.monthOffsetDate(startDate, 1), { navigate: true })
411
386
  }
412
387
 
413
388
  setupRangeCalendarListeners() {
414
389
  if (!this.rangeValue || !this.hasPanelTarget) return
415
390
 
416
- const calendars = this.panelTarget.querySelectorAll(".hakumi-calendar")
417
- if (calendars.length !== 2) return
391
+ const rangeCalendars = this.rangeCalendarElements()
392
+ if (!rangeCalendars) return
418
393
 
419
- const startCalendar = calendars[0]
420
- const endCalendar = calendars[1]
394
+ const [startCalendar, endCalendar] = rangeCalendars
421
395
 
422
396
  // Remove previous listeners if they exist
423
397
  if (this.boundSyncFromStart) {
@@ -428,68 +402,15 @@ export default class extends RegistryController {
428
402
  }
429
403
 
430
404
  // Create listeners
431
- this.boundSyncFromStart = (event) => {
432
- const startApi = startCalendar.hakumiComponent?.api
433
- const endApi = endCalendar.hakumiComponent?.api
434
- if (!startApi || !endApi) return
435
-
436
- const startDate = startApi.getValue()
437
- const startMonth = startDate.getMonth()
438
- const startYear = startDate.getFullYear()
439
-
440
- // Calculate next month
441
- let endMonth = startMonth + 1
442
- let endYear = startYear
443
-
444
- if (endMonth > 11) {
445
- endMonth = 0
446
- endYear += 1
447
- }
448
-
449
- const endDate = endApi.getValue()
450
- const currentEndMonth = endDate.getMonth()
451
- const currentEndYear = endDate.getFullYear()
452
-
453
- // Only sync if second calendar is not already on the correct month
454
- if (currentEndMonth !== endMonth || currentEndYear !== endYear) {
455
- endApi.setValue(new Date(endYear, endMonth, 1), { navigate: true })
456
- }
457
- }
458
-
459
- this.boundSyncFromEnd = (event) => {
460
- const startApi = startCalendar.hakumiComponent?.api
461
- const endApi = endCalendar.hakumiComponent?.api
462
- if (!startApi || !endApi) return
463
-
464
- const endDate = endApi.getValue()
465
- const endMonth = endDate.getMonth()
466
- const endYear = endDate.getFullYear()
467
-
468
- // Calculate previous month
469
- let startMonth = endMonth - 1
470
- let startYear = endYear
471
-
472
- if (startMonth < 0) {
473
- startMonth = 11
474
- startYear -= 1
475
- }
476
-
477
- const startDate = startApi.getValue()
478
- const currentStartMonth = startDate.getMonth()
479
- const currentStartYear = startDate.getFullYear()
480
-
481
- // Only sync if first calendar is not already on the correct month
482
- if (currentStartMonth !== startMonth || currentStartYear !== startYear) {
483
- startApi.setValue(new Date(startYear, startMonth, 1), { navigate: true })
484
- }
485
- }
405
+ this.boundSyncFromStart = () => this.syncAdjacentRangeCalendar(startCalendar, endCalendar, 1)
406
+ this.boundSyncFromEnd = () => this.syncAdjacentRangeCalendar(endCalendar, startCalendar, -1)
486
407
 
487
408
  // Hover listener
488
409
  this.boundHandleHover = (event) => {
489
410
  const hoveredDate = event.detail?.date
490
411
  if (!hoveredDate) return
491
412
 
492
- const calendars = this.panelTarget.querySelectorAll(".hakumi-calendar")
413
+ const calendars = this.calendarElements()
493
414
  calendars.forEach(calendar => {
494
415
  const api = calendar.hakumiComponent?.api
495
416
  if (api) {
@@ -498,13 +419,61 @@ export default class extends RegistryController {
498
419
  })
499
420
  }
500
421
 
501
- // Agregar los listeners
422
+ // Add the listeners
502
423
  startCalendar.addEventListener("hakumi--calendar:change", this.boundSyncFromStart)
503
424
  endCalendar.addEventListener("hakumi--calendar:change", this.boundSyncFromEnd)
504
425
  startCalendar.addEventListener("hakumi--calendar:cell-hover", this.boundHandleHover)
505
426
  endCalendar.addEventListener("hakumi--calendar:cell-hover", this.boundHandleHover)
506
427
  }
507
428
 
429
+ calendarElements() {
430
+ if (!this.hasPanelTarget) return []
431
+
432
+ return this.panelTarget.querySelectorAll(".hakumi-calendar")
433
+ }
434
+
435
+ rangeCalendarElements() {
436
+ const calendars = this.calendarElements()
437
+ if (calendars.length !== 2) return null
438
+
439
+ return [calendars[0], calendars[1]]
440
+ }
441
+
442
+ calendarApis(...calendars) {
443
+ return calendars.map(calendar => calendar.hakumiComponent?.api)
444
+ }
445
+
446
+ monthOffsetDate(date, offset) {
447
+ let month = date.getMonth() + offset
448
+ let year = date.getFullYear()
449
+
450
+ if (month > 11) {
451
+ month = 0
452
+ year += 1
453
+ } else if (month < 0) {
454
+ month = 11
455
+ year -= 1
456
+ }
457
+
458
+ return new Date(year, month, 1)
459
+ }
460
+
461
+ syncAdjacentRangeCalendar(sourceCalendar, targetCalendar, offset) {
462
+ const [sourceApi, targetApi] = this.calendarApis(sourceCalendar, targetCalendar)
463
+ if (!sourceApi || !targetApi) return
464
+
465
+ const targetDate = this.monthOffsetDate(sourceApi.getValue(), offset)
466
+ const currentTargetDate = targetApi.getValue()
467
+
468
+ // Only sync if paired calendar is not already on the correct month
469
+ if (
470
+ currentTargetDate.getMonth() !== targetDate.getMonth() ||
471
+ currentTargetDate.getFullYear() !== targetDate.getFullYear()
472
+ ) {
473
+ targetApi.setValue(targetDate, { navigate: true })
474
+ }
475
+ }
476
+
508
477
  clear(event) {
509
478
  if (event) event.stopPropagation()
510
479
  if (this.disabledValue) return
@@ -527,6 +496,8 @@ export default class extends RegistryController {
527
496
  if (this.disabledValue) return
528
497
  if (this.rangeValue && this.hasStartInputTarget) {
529
498
  this.startInputTarget.focus()
499
+ } else if (this.hasDisplayInputTarget) {
500
+ this.displayInputTarget.focus()
530
501
  } else if (this.hasInputTarget) {
531
502
  this.inputTarget.focus()
532
503
  }
@@ -536,6 +507,8 @@ export default class extends RegistryController {
536
507
  if (this.rangeValue) {
537
508
  if (this.hasStartInputTarget) this.startInputTarget.blur()
538
509
  if (this.hasEndInputTarget) this.endInputTarget.blur()
510
+ } else if (this.hasDisplayInputTarget) {
511
+ this.displayInputTarget.blur()
539
512
  } else if (this.hasInputTarget) {
540
513
  this.inputTarget.blur()
541
514
  }
@@ -577,124 +550,96 @@ export default class extends RegistryController {
577
550
  if (!(date instanceof Date)) return
578
551
  if (this.isDateDisabled(date)) return
579
552
 
580
- // When show_time is enabled, don't close panel or update input immediately
581
- // Store the selected date and wait for OK button
582
553
  if (this.showTimeValue) {
583
- this.selectedDate = date
584
- // Initialize time from current value or defaults
585
- if (!this.selectedTime) {
586
- const currentValue = this.hasHiddenInputTarget ? this.hiddenInputTarget.value : ""
587
- const timePart = currentValue.match(/(\d{2}):(\d{2})(?::(\d{2}))?$/)
588
- if (timePart) {
589
- this.selectedTime = {
590
- hour: timePart[1],
591
- minute: timePart[2],
592
- second: timePart[3] || "00"
593
- }
594
- } else {
595
- this.selectedTime = { hour: "00", minute: "00", second: "00" }
596
- }
597
- this.updateTimeSelection()
598
- }
554
+ this.handleDateTimeDateSelection(date)
599
555
  return
600
556
  }
601
557
 
602
558
  const formatted = this.formatDate(date)
603
559
 
604
560
  if (this.rangeValue) {
605
- // New visual range selection logic
606
- if (!this.tempRangeStart) {
607
- // First selection: set range start
608
- this.tempRangeStart = date
609
- this.tempRangeEnd = null
610
-
611
- // Update start input
612
- if (this.hasStartInputTarget) {
613
- this.startInputTarget.value = formatted
614
- }
615
-
616
- // Clear end input
617
- if (this.hasEndInputTarget) {
618
- this.endInputTarget.value = ""
619
- }
561
+ this.handleRangeDateSelection(date, formatted)
562
+ return // Early return for ranges
563
+ }
620
564
 
621
- // Update visual in both calendars
622
- this.updateRangeVisuals()
565
+ // Code for non-range pickers
566
+ // Use setValue to update both hidden and display inputs
567
+ this.setValue(formatted)
568
+ this.syncCalendarWithInput()
569
+ this.updateClearVisibility()
570
+ this.close()
571
+ }
623
572
 
624
- this.dispatchChangeEvent({
625
- startValue: formatted,
626
- endValue: ""
627
- })
628
- } else if (!this.tempRangeEnd) {
629
- // Second selection: set range end
630
- const clickedDate = date
573
+ handleDateTimeDateSelection(date) {
574
+ // Store the selected date and wait for OK button.
575
+ this.selectedDate = date
631
576
 
632
- // If clicked date is before start, restart selection
633
- if (clickedDate < this.tempRangeStart) {
634
- this.tempRangeStart = clickedDate
635
- this.tempRangeEnd = null
577
+ if (!this.selectedTime) {
578
+ const currentValue = this.hasHiddenInputTarget ? this.hiddenInputTarget.value : ""
579
+ const timePart = this.extractTimePart(currentValue)
580
+ if (timePart) {
581
+ this.selectedTime = this.selectedTimeFromTimePart(timePart)
582
+ } else {
583
+ this.selectedTime = this.defaultSelectedTime()
584
+ }
585
+ this.updateTimeSelection()
586
+ }
587
+ }
636
588
 
637
- if (this.hasStartInputTarget) {
638
- this.startInputTarget.value = formatted
639
- }
640
- if (this.hasEndInputTarget) {
641
- this.endInputTarget.value = ""
642
- }
589
+ handleRangeDateSelection(date, formatted) {
590
+ if (!this.tempRangeStart) {
591
+ this.startRangeSelection(date, formatted)
592
+ } else if (!this.tempRangeEnd) {
593
+ if (date < this.tempRangeStart) {
594
+ this.restartRangeSelection(date, formatted)
595
+ } else {
596
+ this.completeRangeSelection(date, formatted)
597
+ }
598
+ } else {
599
+ this.restartRangeSelection(date, formatted)
600
+ }
643
601
 
644
- this.updateRangeVisuals()
602
+ this.updateClearVisibility()
603
+ }
645
604
 
646
- this.dispatchChangeEvent({
647
- startValue: formatted,
648
- endValue: ""
649
- })
650
- } else {
651
- // Complete the range
652
- this.tempRangeEnd = clickedDate
605
+ startRangeSelection(date, formatted) {
606
+ this.tempRangeStart = date
607
+ this.tempRangeEnd = null
653
608
 
654
- if (this.hasEndInputTarget) {
655
- this.endInputTarget.value = formatted
656
- }
609
+ if (this.hasStartInputTarget) {
610
+ this.startInputTarget.value = formatted
611
+ }
657
612
 
658
- this.updateRangeVisuals()
613
+ if (this.hasEndInputTarget) {
614
+ this.endInputTarget.value = ""
615
+ }
659
616
 
660
- this.dispatchChangeEvent({
661
- startValue: this.hasStartInputTarget ? this.startInputTarget.value : "",
662
- endValue: formatted
663
- })
617
+ this.updateRangeVisuals()
664
618
 
665
- this.updateClearVisibility()
619
+ this.dispatchChangeEvent({
620
+ startValue: formatted,
621
+ endValue: ""
622
+ })
623
+ }
666
624
 
667
- // Close picker after completing selection
668
- this.close()
669
- }
670
- } else {
671
- // Already have a complete range, restart with new selection
672
- this.tempRangeStart = date
673
- this.tempRangeEnd = null
625
+ restartRangeSelection(date, formatted) {
626
+ this.startRangeSelection(date, formatted)
627
+ }
674
628
 
675
- if (this.hasStartInputTarget) {
676
- this.startInputTarget.value = formatted
677
- }
678
- if (this.hasEndInputTarget) {
679
- this.endInputTarget.value = ""
680
- }
629
+ completeRangeSelection(date, formatted) {
630
+ this.tempRangeEnd = date
681
631
 
682
- this.updateRangeVisuals()
632
+ if (this.hasEndInputTarget) {
633
+ this.endInputTarget.value = formatted
634
+ }
683
635
 
684
- this.dispatchChangeEvent({
685
- startValue: formatted,
686
- endValue: ""
687
- })
688
- }
636
+ this.updateRangeVisuals()
689
637
 
690
- this.updateClearVisibility()
691
- return // Early return for ranges
692
- }
638
+ this.dispatchChangeEvent({
639
+ startValue: this.hasStartInputTarget ? this.startInputTarget.value : "",
640
+ endValue: formatted
641
+ })
693
642
 
694
- // Code for non-range pickers
695
- // Use setValue to update both hidden and display inputs
696
- this.setValue(formatted)
697
- this.syncCalendarWithInput()
698
643
  this.updateClearVisibility()
699
644
  this.close()
700
645
  }
@@ -738,33 +683,98 @@ export default class extends RegistryController {
738
683
 
739
684
  switch (picker) {
740
685
  case "year":
741
- return String(date.getFullYear())
686
+ return this.formatYearValue(date)
742
687
 
743
688
  case "month":
744
- return `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, "0")}`
689
+ return this.formatMonthValue(date)
745
690
 
746
- case "week": {
747
- const week = DateHelper.getISOWeek(date)
748
- const weekYear = DateHelper.getISOWeekYear(date)
749
- return `${weekYear}-W${String(week).padStart(2, "0")}`
750
- }
691
+ case "week":
692
+ return this.formatWeekValue(date)
751
693
 
752
- case "quarter": {
753
- const quarter = Math.floor(date.getMonth() / 3) + 1
754
- return `Q${quarter} ${date.getFullYear()}`
755
- }
694
+ case "quarter":
695
+ return this.formatQuarterValue(date)
756
696
 
757
- default: {
758
- const dateStr = DateHelper.formatDataDate(date)
759
- if (time && this.showTimeValue) {
760
- const timeStr = this.showSecondsValue
761
- ? `${time.hour}:${time.minute}:${time.second}`
762
- : `${time.hour}:${time.minute}`
763
- return `${dateStr} ${timeStr}`
764
- }
765
- return dateStr
697
+ default:
698
+ return this.formatDateValue(date, time)
699
+ }
700
+ }
701
+
702
+ formatYearValue(date) {
703
+ return String(date.getFullYear())
704
+ }
705
+
706
+ formatMonthValue(date) {
707
+ return `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, "0")}`
708
+ }
709
+
710
+ formatWeekValue(date) {
711
+ const week = DateHelper.getISOWeek(date)
712
+ const weekYear = DateHelper.getISOWeekYear(date)
713
+ return `${weekYear}-W${String(week).padStart(2, "0")}`
714
+ }
715
+
716
+ formatQuarterValue(date) {
717
+ const quarter = Math.floor(date.getMonth() / 3) + 1
718
+ return `Q${quarter} ${date.getFullYear()}`
719
+ }
720
+
721
+ formatDateValue(date, time = null) {
722
+ const dateStr = DateHelper.formatDataDate(date)
723
+ if (time && this.showTimeValue) {
724
+ return `${dateStr} ${this.formatTimeValue(time)}`
725
+ }
726
+ return dateStr
727
+ }
728
+
729
+ formatTimeValue(time) {
730
+ return this.showSecondsValue
731
+ ? `${time.hour}:${time.minute}:${time.second}`
732
+ : `${time.hour}:${time.minute}`
733
+ }
734
+
735
+ extractDatePart(value) {
736
+ const datePart = value.match(/^(\d{4}-\d{2}-\d{2})/)
737
+ return datePart?.[1] || null
738
+ }
739
+
740
+ extractTimePart(value) {
741
+ return value.match(/(\d{2}):(\d{2})(?::(\d{2}))?$/)
742
+ }
743
+
744
+ selectedTimeFromTimePart(timePart, convertToPeriod = false) {
745
+ let hour = parseInt(timePart[1], 10)
746
+ let period = undefined
747
+
748
+ // Convert 24h to 12h if needed
749
+ if (convertToPeriod) {
750
+ if (hour === 0) {
751
+ hour = 12
752
+ period = "AM"
753
+ } else if (hour < 12) {
754
+ period = "AM"
755
+ } else if (hour === 12) {
756
+ period = "PM"
757
+ } else {
758
+ hour -= 12
759
+ period = "PM"
766
760
  }
767
761
  }
762
+
763
+ const selectedTime = {
764
+ hour: String(hour).padStart(2, "0"),
765
+ minute: timePart[2],
766
+ second: timePart[3] || "00"
767
+ }
768
+
769
+ if (period) {
770
+ selectedTime.period = period
771
+ }
772
+
773
+ return selectedTime
774
+ }
775
+
776
+ defaultSelectedTime() {
777
+ return { hour: "00", minute: "00", second: "00" }
768
778
  }
769
779
 
770
780
  handleClickOutside(event) {
@@ -838,6 +848,7 @@ export default class extends RegistryController {
838
848
 
839
849
  setNow() {
840
850
  const now = new Date()
851
+ if (this.isDateDisabled(now)) return
841
852
 
842
853
  // Set both selected date and time
843
854
  this.selectedDate = now
@@ -869,6 +880,7 @@ export default class extends RegistryController {
869
880
  if (!selectedDateStr) return
870
881
  date = DateHelper.parseDataDate(selectedDateStr).toDate()
871
882
  }
883
+ if (this.isDateDisabled(date)) return
872
884
 
873
885
  // Combine date and time
874
886
  const time = this.selectedTime || { hour: "00", minute: "00", second: "00" }