shadcn_phlexcomponents 0.1.5 → 0.1.11

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 (249) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +14 -0
  3. data/app/javascript/controllers/accordion_controller.ts +133 -0
  4. data/app/javascript/controllers/{avatar_controller.js → avatar_controller.ts} +4 -0
  5. data/app/javascript/controllers/checkbox_controller.ts +34 -0
  6. data/app/javascript/controllers/collapsible_controller.ts +45 -0
  7. data/app/javascript/controllers/combobox_controller.ts +145 -0
  8. data/app/javascript/controllers/command_controller.ts +129 -0
  9. data/app/javascript/controllers/command_root_controller.ts +355 -0
  10. data/app/javascript/controllers/date_picker_controller.ts +274 -0
  11. data/app/javascript/controllers/date_range_picker_controller.ts +243 -0
  12. data/app/javascript/controllers/dialog_controller.ts +113 -0
  13. data/app/javascript/controllers/dropdown_menu_controller.ts +133 -0
  14. data/app/javascript/controllers/dropdown_menu_root_controller.ts +234 -0
  15. data/app/javascript/controllers/dropdown_menu_sub_controller.ts +150 -0
  16. data/app/javascript/controllers/form_field_controller.ts +22 -0
  17. data/app/javascript/controllers/hover_card_controller.ts +93 -0
  18. data/app/javascript/controllers/{loading_button_controller.js → loading_button_controller.ts} +2 -2
  19. data/app/javascript/controllers/popover_controller.ts +141 -0
  20. data/app/javascript/controllers/progress_controller.ts +17 -0
  21. data/app/javascript/controllers/radio_group_controller.ts +106 -0
  22. data/app/javascript/controllers/select_controller.ts +200 -0
  23. data/app/javascript/controllers/{sidebar_controller.js → sidebar_controller.ts} +6 -2
  24. data/app/javascript/controllers/sidebar_trigger_controller.ts +21 -0
  25. data/app/javascript/controllers/slider_controller.ts +107 -0
  26. data/app/javascript/controllers/switch_controller.ts +30 -0
  27. data/app/javascript/controllers/tabs_controller.ts +79 -0
  28. data/app/javascript/controllers/{theme_switcher_controller.js → theme_switcher_controller.ts} +12 -9
  29. data/app/javascript/controllers/toast_container_controller.ts +62 -0
  30. data/app/javascript/controllers/toast_controller.ts +28 -0
  31. data/app/javascript/controllers/tooltip_controller.ts +98 -0
  32. data/app/javascript/shadcn_phlexcomponents.ts +57 -0
  33. data/app/javascript/utils.ts +437 -0
  34. data/app/stylesheets/date_picker.css +74 -0
  35. data/app/stylesheets/nouislider.css +173 -0
  36. data/app/stylesheets/tw-animate.css +486 -0
  37. data/lib/install/install_shadcn_phlexcomponents.rb +22 -9
  38. data/lib/shadcn_phlexcomponents/alias.rb +3 -1
  39. data/lib/shadcn_phlexcomponents/components/accordion.rb +129 -0
  40. data/lib/shadcn_phlexcomponents/components/alert.rb +59 -0
  41. data/lib/shadcn_phlexcomponents/components/alert_dialog.rb +276 -0
  42. data/lib/{components → shadcn_phlexcomponents/components}/aspect_ratio.rb +2 -2
  43. data/lib/shadcn_phlexcomponents/components/avatar.rb +63 -0
  44. data/lib/shadcn_phlexcomponents/components/badge.rb +35 -0
  45. data/lib/{components → shadcn_phlexcomponents/components}/base.rb +44 -7
  46. data/lib/shadcn_phlexcomponents/components/breadcrumb.rb +150 -0
  47. data/lib/shadcn_phlexcomponents/components/button.rb +49 -0
  48. data/lib/shadcn_phlexcomponents/components/card.rb +88 -0
  49. data/lib/{components → shadcn_phlexcomponents/components}/checkbox.rb +21 -17
  50. data/lib/{components → shadcn_phlexcomponents/components}/checkbox_group.rb +27 -16
  51. data/lib/shadcn_phlexcomponents/components/collapsible.rb +91 -0
  52. data/lib/shadcn_phlexcomponents/components/combobox.rb +398 -0
  53. data/lib/shadcn_phlexcomponents/components/command.rb +351 -0
  54. data/lib/shadcn_phlexcomponents/components/date_picker.rb +264 -0
  55. data/lib/shadcn_phlexcomponents/components/date_range_picker.rb +126 -0
  56. data/lib/shadcn_phlexcomponents/components/dialog.rb +234 -0
  57. data/lib/shadcn_phlexcomponents/components/dropdown_menu.rb +282 -0
  58. data/lib/shadcn_phlexcomponents/components/dropdown_menu_sub.rb +135 -0
  59. data/lib/shadcn_phlexcomponents/components/form/form_checkbox.rb +82 -0
  60. data/lib/shadcn_phlexcomponents/components/form/form_checkbox_group.rb +116 -0
  61. data/lib/shadcn_phlexcomponents/components/form/form_date_picker.rb +46 -0
  62. data/lib/shadcn_phlexcomponents/components/form/form_date_range_picker.rb +82 -0
  63. data/lib/{components → shadcn_phlexcomponents/components/form}/form_error.rb +7 -3
  64. data/lib/shadcn_phlexcomponents/components/form/form_helpers.rb +143 -0
  65. data/lib/shadcn_phlexcomponents/components/form/form_hint.rb +21 -0
  66. data/lib/{components → shadcn_phlexcomponents/components/form}/form_input.rb +3 -4
  67. data/lib/shadcn_phlexcomponents/components/form/form_radio_group.rb +106 -0
  68. data/lib/shadcn_phlexcomponents/components/form/form_select.rb +64 -0
  69. data/lib/shadcn_phlexcomponents/components/form/form_slider.rb +91 -0
  70. data/lib/shadcn_phlexcomponents/components/form/form_switch.rb +67 -0
  71. data/lib/shadcn_phlexcomponents/components/form/form_textarea.rb +59 -0
  72. data/lib/shadcn_phlexcomponents/components/form.rb +157 -0
  73. data/lib/shadcn_phlexcomponents/components/hover_card.rb +110 -0
  74. data/lib/shadcn_phlexcomponents/components/input.rb +31 -0
  75. data/lib/shadcn_phlexcomponents/components/label.rb +16 -0
  76. data/lib/{components → shadcn_phlexcomponents/components}/link.rb +10 -3
  77. data/lib/shadcn_phlexcomponents/components/loading_button.rb +28 -0
  78. data/lib/shadcn_phlexcomponents/components/pagination.rb +166 -0
  79. data/lib/shadcn_phlexcomponents/components/popover.rb +116 -0
  80. data/lib/{components → shadcn_phlexcomponents/components}/progress.rb +5 -5
  81. data/lib/shadcn_phlexcomponents/components/radio_group.rb +155 -0
  82. data/lib/shadcn_phlexcomponents/components/select.rb +421 -0
  83. data/lib/{components → shadcn_phlexcomponents/components}/separator.rb +9 -8
  84. data/lib/shadcn_phlexcomponents/components/sheet.rb +239 -0
  85. data/lib/{components → shadcn_phlexcomponents/components}/skeleton.rb +1 -1
  86. data/lib/shadcn_phlexcomponents/components/slider.rb +72 -0
  87. data/lib/shadcn_phlexcomponents/components/switch.rb +75 -0
  88. data/lib/shadcn_phlexcomponents/components/table.rb +140 -0
  89. data/lib/shadcn_phlexcomponents/components/tabs.rb +135 -0
  90. data/lib/shadcn_phlexcomponents/components/textarea.rb +24 -0
  91. data/lib/{components → shadcn_phlexcomponents/components}/theme_switcher.rb +2 -2
  92. data/lib/shadcn_phlexcomponents/components/toast.rb +153 -0
  93. data/lib/{components → shadcn_phlexcomponents/components}/toast_container.rb +24 -5
  94. data/lib/shadcn_phlexcomponents/components/tooltip.rb +131 -0
  95. data/lib/shadcn_phlexcomponents/initializers/shadcn_phlexcomponents.rb +25 -0
  96. data/lib/shadcn_phlexcomponents/version.rb +1 -1
  97. data/lib/tasks/install.rake +1 -1
  98. metadata +92 -168
  99. data/app/assets/tailwind/choices.css +0 -324
  100. data/app/assets/tailwind/tailwindcss-animate.css +0 -318
  101. data/app/assets/tailwind/vanilla-calendar-pro.css +0 -466
  102. data/app/javascript/controllers/accordion_controller.js +0 -133
  103. data/app/javascript/controllers/alert_dialog_controller.js +0 -157
  104. data/app/javascript/controllers/checkbox_controller.js +0 -28
  105. data/app/javascript/controllers/collapsible_controller.js +0 -35
  106. data/app/javascript/controllers/combobox_controller.js +0 -34
  107. data/app/javascript/controllers/date_picker_controller.js +0 -118
  108. data/app/javascript/controllers/date_range_picker_controller.js +0 -231
  109. data/app/javascript/controllers/dialog_controller.js +0 -159
  110. data/app/javascript/controllers/dropdown_menu_controller.js +0 -193
  111. data/app/javascript/controllers/hover_card_controller.js +0 -42
  112. data/app/javascript/controllers/popover_controller.js +0 -124
  113. data/app/javascript/controllers/progress_controller.js +0 -14
  114. data/app/javascript/controllers/radio_group_controller.js +0 -90
  115. data/app/javascript/controllers/select_controller.js +0 -294
  116. data/app/javascript/controllers/sheet_controller.js +0 -159
  117. data/app/javascript/controllers/sidebar_trigger_controller.js +0 -15
  118. data/app/javascript/controllers/switch_controller.js +0 -24
  119. data/app/javascript/controllers/tabs_controller.js +0 -73
  120. data/app/javascript/controllers/toast_container_controller.js +0 -22
  121. data/app/javascript/controllers/toast_controller.js +0 -45
  122. data/app/javascript/controllers/tooltip_controller.js +0 -41
  123. data/lib/components/accordion.rb +0 -38
  124. data/lib/components/accordion_content.rb +0 -30
  125. data/lib/components/accordion_item.rb +0 -26
  126. data/lib/components/accordion_trigger.rb +0 -45
  127. data/lib/components/alert.rb +0 -40
  128. data/lib/components/alert_description.rb +0 -11
  129. data/lib/components/alert_dialog.rb +0 -60
  130. data/lib/components/alert_dialog_action.rb +0 -22
  131. data/lib/components/alert_dialog_action_to.rb +0 -40
  132. data/lib/components/alert_dialog_cancel.rb +0 -22
  133. data/lib/components/alert_dialog_content.rb +0 -40
  134. data/lib/components/alert_dialog_description.rb +0 -22
  135. data/lib/components/alert_dialog_footer.rb +0 -11
  136. data/lib/components/alert_dialog_header.rb +0 -11
  137. data/lib/components/alert_dialog_title.rb +0 -22
  138. data/lib/components/alert_dialog_trigger.rb +0 -50
  139. data/lib/components/alert_title.rb +0 -11
  140. data/lib/components/avatar.rb +0 -31
  141. data/lib/components/avatar_fallback.rb +0 -21
  142. data/lib/components/avatar_image.rb +0 -19
  143. data/lib/components/badge.rb +0 -30
  144. data/lib/components/breadcrumb.rb +0 -51
  145. data/lib/components/breadcrumb_ellipsis.rb +0 -23
  146. data/lib/components/breadcrumb_item.rb +0 -11
  147. data/lib/components/breadcrumb_link.rb +0 -7
  148. data/lib/components/breadcrumb_page.rb +0 -21
  149. data/lib/components/breadcrumb_separator.rb +0 -26
  150. data/lib/components/button.rb +0 -53
  151. data/lib/components/card.rb +0 -31
  152. data/lib/components/card_content.rb +0 -11
  153. data/lib/components/card_description.rb +0 -11
  154. data/lib/components/card_footer.rb +0 -11
  155. data/lib/components/card_header.rb +0 -11
  156. data/lib/components/card_title.rb +0 -11
  157. data/lib/components/collapsible.rb +0 -31
  158. data/lib/components/collapsible_content.rb +0 -24
  159. data/lib/components/collapsible_trigger.rb +0 -50
  160. data/lib/components/combobox.rb +0 -57
  161. data/lib/components/combobox_item.rb +0 -9
  162. data/lib/components/date_picker.rb +0 -94
  163. data/lib/components/date_range_picker.rb +0 -113
  164. data/lib/components/dialog.rb +0 -52
  165. data/lib/components/dialog_close.rb +0 -42
  166. data/lib/components/dialog_content.rb +0 -54
  167. data/lib/components/dialog_description.rb +0 -22
  168. data/lib/components/dialog_footer.rb +0 -11
  169. data/lib/components/dialog_header.rb +0 -11
  170. data/lib/components/dialog_title.rb +0 -22
  171. data/lib/components/dialog_trigger.rb +0 -50
  172. data/lib/components/dropdown_menu.rb +0 -50
  173. data/lib/components/dropdown_menu_content.rb +0 -52
  174. data/lib/components/dropdown_menu_item.rb +0 -56
  175. data/lib/components/dropdown_menu_item_to.rb +0 -28
  176. data/lib/components/dropdown_menu_label.rb +0 -11
  177. data/lib/components/dropdown_menu_separator.rb +0 -20
  178. data/lib/components/dropdown_menu_trigger.rb +0 -57
  179. data/lib/components/form.rb +0 -59
  180. data/lib/components/form_hint.rb +0 -17
  181. data/lib/components/hover_card.rb +0 -33
  182. data/lib/components/hover_card_content.rb +0 -32
  183. data/lib/components/hover_card_trigger.rb +0 -44
  184. data/lib/components/input.rb +0 -32
  185. data/lib/components/label.rb +0 -14
  186. data/lib/components/loading_button.rb +0 -21
  187. data/lib/components/pagination.rb +0 -38
  188. data/lib/components/pagination_ellipsis.rb +0 -24
  189. data/lib/components/pagination_link.rb +0 -34
  190. data/lib/components/pagination_next.rb +0 -32
  191. data/lib/components/pagination_previous.rb +0 -32
  192. data/lib/components/popover.rb +0 -34
  193. data/lib/components/popover_content.rb +0 -40
  194. data/lib/components/popover_trigger.rb +0 -51
  195. data/lib/components/radio_group.rb +0 -62
  196. data/lib/components/radio_group_item.rb +0 -66
  197. data/lib/components/select.rb +0 -184
  198. data/lib/components/select_content.rb +0 -64
  199. data/lib/components/select_group.rb +0 -23
  200. data/lib/components/select_item.rb +0 -59
  201. data/lib/components/select_label.rb +0 -24
  202. data/lib/components/select_trigger.rb +0 -56
  203. data/lib/components/sheet.rb +0 -53
  204. data/lib/components/sheet_close.rb +0 -42
  205. data/lib/components/sheet_content.rb +0 -65
  206. data/lib/components/sheet_description.rb +0 -22
  207. data/lib/components/sheet_footer.rb +0 -11
  208. data/lib/components/sheet_header.rb +0 -11
  209. data/lib/components/sheet_title.rb +0 -22
  210. data/lib/components/sheet_trigger.rb +0 -50
  211. data/lib/components/sidebar.rb +0 -108
  212. data/lib/components/sidebar_container.rb +0 -11
  213. data/lib/components/sidebar_content.rb +0 -11
  214. data/lib/components/sidebar_footer.rb +0 -11
  215. data/lib/components/sidebar_group.rb +0 -11
  216. data/lib/components/sidebar_group_content.rb +0 -11
  217. data/lib/components/sidebar_group_label.rb +0 -16
  218. data/lib/components/sidebar_header.rb +0 -11
  219. data/lib/components/sidebar_inset.rb +0 -15
  220. data/lib/components/sidebar_menu.rb +0 -11
  221. data/lib/components/sidebar_menu_button.rb +0 -61
  222. data/lib/components/sidebar_menu_item.rb +0 -9
  223. data/lib/components/sidebar_menu_sub.rb +0 -14
  224. data/lib/components/sidebar_menu_sub_button.rb +0 -48
  225. data/lib/components/sidebar_menu_sub_item.rb +0 -9
  226. data/lib/components/sidebar_trigger.rb +0 -40
  227. data/lib/components/switch.rb +0 -66
  228. data/lib/components/table.rb +0 -75
  229. data/lib/components/table_body.rb +0 -11
  230. data/lib/components/table_caption.rb +0 -11
  231. data/lib/components/table_cell.rb +0 -11
  232. data/lib/components/table_footer.rb +0 -11
  233. data/lib/components/table_head.rb +0 -14
  234. data/lib/components/table_header.rb +0 -11
  235. data/lib/components/table_row.rb +0 -11
  236. data/lib/components/tabs.rb +0 -38
  237. data/lib/components/tabs_content.rb +0 -35
  238. data/lib/components/tabs_list.rb +0 -23
  239. data/lib/components/tabs_trigger.rb +0 -45
  240. data/lib/components/textarea.rb +0 -28
  241. data/lib/components/toast.rb +0 -101
  242. data/lib/components/toast_action.rb +0 -39
  243. data/lib/components/toast_action_to.rb +0 -28
  244. data/lib/components/toast_content.rb +0 -11
  245. data/lib/components/toast_description.rb +0 -11
  246. data/lib/components/toast_title.rb +0 -11
  247. data/lib/components/tooltip.rb +0 -34
  248. data/lib/components/tooltip_content.rb +0 -39
  249. data/lib/components/tooltip_trigger.rb +0 -48
@@ -0,0 +1,135 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ShadcnPhlexcomponents
4
+ class DropdownMenuSub < Base
5
+ def initialize(aria_id: nil, **attributes)
6
+ @aria_id = aria_id
7
+ super(**attributes)
8
+ end
9
+
10
+ def trigger(**attributes, &)
11
+ DropdownMenuSubTrigger(aria_id: @aria_id, **attributes, &)
12
+ end
13
+
14
+ def content(**attributes, &)
15
+ DropdownMenuSubContent(aria_id: @aria_id, **attributes, &)
16
+ end
17
+
18
+ def default_attributes
19
+ {
20
+ data: {
21
+ controller: "dropdown-menu-sub",
22
+ action: "keydown.left->dropdown-menu-sub#closeOnLeftKeydown:prevent",
23
+ },
24
+ }
25
+ end
26
+
27
+ def view_template(&)
28
+ div(**@attributes, &)
29
+ end
30
+ end
31
+
32
+ class DropdownMenuSubTrigger < Base
33
+ class_variants(
34
+ base: <<~HEREDOC,
35
+ focus:bg-accent focus:text-accent-foreground data-[state=open]:bg-accent data-[state=open]:text-accent-foreground
36
+ flex cursor-default items-center rounded-sm px-2 py-1.5 text-sm outline-hidden select-none data-[inset]:pl-8
37
+ HEREDOC
38
+ )
39
+
40
+ def initialize(aria_id: nil, **attributes)
41
+ @aria_id = aria_id
42
+ super(**attributes)
43
+ end
44
+
45
+ def view_template(&)
46
+ div(**@attributes) do
47
+ yield
48
+
49
+ icon("chevron-right", class: "ml-auto size-4")
50
+ end
51
+ end
52
+
53
+ def default_attributes
54
+ {
55
+ id: "#{@aria_id}-trigger",
56
+ role: "menuitem",
57
+ aria: {
58
+ haspopup: "menu",
59
+ expanded: false,
60
+ controls: "#{@aria_id}-content",
61
+ },
62
+ tabindex: -1,
63
+ data: {
64
+ state: "closed",
65
+ dropdown_menu_sub_target: "trigger",
66
+ dropdown_menu_target: "item",
67
+ action: <<~HEREDOC,
68
+ focus->dropdown-menu#onItemFocus
69
+ blur->dropdown-menu#onItemBlur
70
+ keydown.up->dropdown-menu#focusItem:prevent
71
+ keydown.down->dropdown-menu#focusItem:prevent
72
+ mouseover->dropdown-menu#focusItem
73
+ mouseover->dropdown-menu-sub#open
74
+ click->dropdown-menu-sub#open
75
+ keydown.right->dropdown-menu-sub#open:prevent
76
+ keydown.space->dropdown-menu-sub#open:prevent
77
+ keydown.enter->dropdown-menu-sub#open:prevent
78
+ keydown.left->dropdown-menu-sub#closeParentSubMenu
79
+ HEREDOC
80
+ },
81
+ }
82
+ end
83
+ end
84
+
85
+ class DropdownMenuSubContent < Base
86
+ class_variants(
87
+ base: <<~HEREDOC,
88
+ bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out
89
+ data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95
90
+ data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2
91
+ data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 min-w-[8rem]
92
+ origin-(--radix-popper-transform-origin) overflow-hidden rounded-md border p-1 shadow-lg outline-none
93
+ HEREDOC
94
+ )
95
+
96
+ def initialize(aria_id: nil, side: :right, align: :start, **attributes)
97
+ @side = side
98
+ @align = align
99
+ @aria_id = aria_id
100
+ super(**attributes)
101
+ end
102
+
103
+ def default_attributes
104
+ {
105
+ id: "#{@aria_id}-content",
106
+ tabindex: -1,
107
+ role: "menu",
108
+ aria: {
109
+ labelledby: "#{@aria_id}-trigger",
110
+ orientation: "vertical",
111
+ },
112
+ data: {
113
+ state: "closed",
114
+ side: @side,
115
+ align: @align,
116
+ dropdown_menu_sub_target: "content",
117
+ action: <<~HEREDOC,
118
+ mouseover->dropdown-menu-sub#open#{" "}
119
+ keydown.up->dropdown-menu-sub#focusItemByIndex:prevent:self
120
+ keydown.down->dropdown-menu-sub#focusItemByIndex:prevent:self
121
+ HEREDOC
122
+ },
123
+ }
124
+ end
125
+
126
+ def view_template(&)
127
+ div(
128
+ class: "hidden fixed top-0 left-0 w-max z-50",
129
+ data: { dropdown_menu_sub_target: "contentContainer" },
130
+ ) do
131
+ div(**@attributes, &)
132
+ end
133
+ end
134
+ end
135
+ end
@@ -0,0 +1,82 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ShadcnPhlexcomponents
4
+ class FormCheckbox < Base
5
+ include FormHelpers
6
+
7
+ def initialize(
8
+ method = nil,
9
+ model: false,
10
+ object_name: nil,
11
+ value: nil,
12
+ name: nil,
13
+ id: nil,
14
+ label: nil,
15
+ error: nil,
16
+ hint: nil,
17
+ checked: nil,
18
+ **attributes
19
+ )
20
+ @method = method
21
+ @model = model
22
+ @object_name = object_name
23
+ @value = value || "1"
24
+ @name = name
25
+ @id = id
26
+ @label = label
27
+ @error = default_error(error, method)
28
+ @hint = hint
29
+ @aria_id = "form-field-#{SecureRandom.hex(5)}"
30
+ @checked = default_checked(checked, method)
31
+ super(**attributes)
32
+ end
33
+
34
+ def label_attributes(use_label_styles: false, **attributes)
35
+ attributes[:class] = [
36
+ use_label_styles ? Label.new.class_variants : nil,
37
+ "ml-6",
38
+ attributes[:class],
39
+ ].compact.join(" ")
40
+ attributes[:for] ||= @id
41
+ attributes
42
+ end
43
+
44
+ def hint_attributes(**attributes)
45
+ attributes[:class] = [
46
+ "ml-6",
47
+ attributes[:class],
48
+ ].compact.join(" ")
49
+ attributes
50
+ end
51
+
52
+ def view_template(&)
53
+ vanish(&)
54
+
55
+ @id ||= field_id(@object_name, @method)
56
+ @name ||= field_name(@object_name, @method)
57
+
58
+ div(class: "space-y-2") do
59
+ div(class: "flex items-top space-x-2") do
60
+ div(class: "grid gap-1.5 relative", data: label_and_hint_container_attributes) do
61
+ @attributes[:class] = "#{@attributes[:class]} -mt-[1.5px] absolute top-0 left-0"
62
+
63
+ Checkbox(
64
+ id: @id,
65
+ name: @name,
66
+ value: @value,
67
+ checked: @checked,
68
+ aria: aria_attributes,
69
+ disabled: @disabled,
70
+ **@attributes,
71
+ )
72
+
73
+ render_label(&)
74
+ render_hint(&)
75
+ end
76
+ end
77
+
78
+ render_error
79
+ end
80
+ end
81
+ end
82
+ end
@@ -0,0 +1,116 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ShadcnPhlexcomponents
4
+ class FormCheckboxGroup < Base
5
+ include FormHelpers
6
+
7
+ def initialize(
8
+ method = nil,
9
+ model: false,
10
+ object_name: nil,
11
+ collection: [],
12
+ value_method: nil,
13
+ text_method: nil,
14
+ value: nil,
15
+ name: nil,
16
+ id: nil,
17
+ label: nil,
18
+ error: nil,
19
+ hint: nil,
20
+ disabled_items: nil,
21
+ **attributes
22
+ )
23
+ @method = method
24
+ @model = model
25
+ @object_name = object_name
26
+
27
+ @collection = if collection.first&.is_a?(Hash)
28
+ convert_collection_hash_to_struct(collection, value_method: value_method, text_method: text_method)
29
+ else
30
+ collection
31
+ end
32
+
33
+ @value_method = value_method
34
+ @text_method = text_method
35
+
36
+ @value = value
37
+
38
+ @model_value = if model
39
+ model_collection = model.public_send(method)
40
+
41
+ if model_collection.respond_to?(:map)
42
+ model_collection.map { |item| item.public_send(value_method) }
43
+ end
44
+ end
45
+
46
+ @name = name
47
+ @id = id
48
+ @label = label
49
+ @error = default_error(error, method)
50
+ @hint = hint
51
+ @disabled_items = disabled_items
52
+ @aria_id = "form-field-#{SecureRandom.hex(5)}"
53
+ super(**attributes)
54
+ end
55
+
56
+ def aria_attributes
57
+ attrs = super
58
+ attrs[:labelledby] = "#{@aria_id}-label"
59
+ attrs
60
+ end
61
+
62
+ def label_attributes(use_label_styles: false, **attributes)
63
+ attrs = super(use_label_styles: use_label_styles, **attributes)
64
+ attrs[:id] = "#{@aria_id}-label"
65
+ attrs
66
+ end
67
+
68
+ def checkbox(**attributes)
69
+ @checkbox_attributes = attributes
70
+ nil
71
+ end
72
+
73
+ def checkbox_label(**attributes)
74
+ @checkbox_label_attributes = attributes
75
+ nil
76
+ end
77
+
78
+ def view_template(&)
79
+ vanish(&)
80
+
81
+ @id ||= field_id(@object_name, @method)
82
+ @name ||= field_name(@object_name, @method)
83
+
84
+ div(class: "space-y-2", data: label_and_hint_container_attributes) do
85
+ render_label(&)
86
+
87
+ CheckboxGroup(
88
+ id: @id,
89
+ name: @name,
90
+ value: @value || @model_value || [],
91
+ aria: aria_attributes,
92
+ **@attributes,
93
+ ) do |c|
94
+ c.items(
95
+ @collection,
96
+ value_method: @value_method,
97
+ text_method: @text_method,
98
+ disabled_items: @disabled_items,
99
+ id_prefix: @id,
100
+ ) do
101
+ if @checkbox_attributes
102
+ c.checkbox(**@checkbox_attributes)
103
+ end
104
+
105
+ if @checkbox_label_attributes
106
+ c.label(**@checkbox_label_attributes)
107
+ end
108
+ end
109
+ end
110
+
111
+ render_hint(&)
112
+ render_error
113
+ end
114
+ end
115
+ end
116
+ end
@@ -0,0 +1,46 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ShadcnPhlexcomponents
4
+ class FormDatePicker < Base
5
+ include FormHelpers
6
+
7
+ def initialize(
8
+ method = nil,
9
+ model: false,
10
+ object_name: nil,
11
+ value: nil,
12
+ name: nil,
13
+ id: nil,
14
+ label: nil,
15
+ error: nil,
16
+ hint: nil,
17
+ **attributes
18
+ )
19
+ @method = method
20
+ @model = model
21
+ @object_name = object_name
22
+ @value = default_value(value, method)
23
+ @name = name
24
+ @id = id
25
+ @label = label
26
+ @error = default_error(error, method)
27
+ @hint = hint
28
+ @aria_id = "form-field-#{SecureRandom.hex(5)}"
29
+ super(**attributes)
30
+ end
31
+
32
+ def view_template(&)
33
+ vanish(&)
34
+
35
+ @id ||= field_id(@object_name, @method)
36
+ @name ||= field_name(@object_name, @method)
37
+
38
+ div(class: "space-y-2", data: label_and_hint_container_attributes) do
39
+ render_label(&)
40
+ DatePicker(id: @id, name: @name, value: @value, aria: aria_attributes, **@attributes)
41
+ render_hint(&)
42
+ render_error
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,82 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ShadcnPhlexcomponents
4
+ class FormDateRangePicker < Base
5
+ include FormHelpers
6
+
7
+ def initialize(
8
+ method,
9
+ end_method,
10
+ model: false,
11
+ object_name: nil,
12
+ value: nil,
13
+ name: nil,
14
+ id: nil,
15
+ label: nil,
16
+ error: nil,
17
+ hint: nil,
18
+ **attributes
19
+ )
20
+ @method = method
21
+ @end_method = end_method
22
+ @model = model
23
+ @object_name = object_name
24
+
25
+ @value = [
26
+ default_value(value ? value[0] : nil, method),
27
+ default_value(value ? value[1] : nil, end_method),
28
+ ]
29
+ @error = [
30
+ default_error(error ? error[0] : nil, method),
31
+ default_error(error ? error[1] : nil, end_method),
32
+ ].compact
33
+
34
+ @name = name
35
+ @id = id
36
+ @label = label
37
+ @hint = hint
38
+ @aria_id = "form-field-#{SecureRandom.hex(5)}"
39
+ super(**attributes)
40
+ end
41
+
42
+ def render_label(&)
43
+ # It's currently not possible to separate the content of the yield in Phlex.
44
+ # So we use Javascript to remove the duplicated hint or label.
45
+ if @yield_label && @yield_hint
46
+ div(data: { remove_hint: true }, &)
47
+ elsif @yield_label
48
+ yield
49
+ elsif @label
50
+ attrs = label_attributes(use_label_styles: false)
51
+ Label(**attrs) { @label }
52
+ elsif @label != false
53
+ attrs = label_attributes(use_label_styles: true)
54
+ rails_label(@object_name, [@method, @end_method].to_sentence, nil, **attrs)
55
+ end
56
+ end
57
+
58
+ def view_template(&)
59
+ vanish(&)
60
+
61
+ @id ||= field_id(@object_name, @method)
62
+ @name ||=
63
+ [
64
+ field_name(@object_name, @method),
65
+ field_name(@object_name, @end_method),
66
+ ]
67
+
68
+ div(class: "space-y-2", data: label_and_hint_container_attributes) do
69
+ render_label(&)
70
+ DateRangePicker(
71
+ id: @id,
72
+ name: @name,
73
+ value: @value,
74
+ aria: aria_attributes,
75
+ **@attributes,
76
+ )
77
+ render_hint(&)
78
+ render_error
79
+ end
80
+ end
81
+ end
82
+ end
@@ -2,16 +2,20 @@
2
2
 
3
3
  module ShadcnPhlexcomponents
4
4
  class FormError < Base
5
- STYLES = "text-[0.8rem] font-medium text-destructive"
5
+ class_variants(base: "text-destructive text-sm")
6
6
 
7
- def initialize(message: nil, aria_id: nil, **attributes)
7
+ def initialize(message, aria_id: nil, **attributes)
8
8
  @message = message
9
9
  @id = aria_id ? "#{aria_id}-message" : nil
10
10
  super(**attributes)
11
11
  end
12
12
 
13
13
  def view_template(&)
14
- p(id: @id, **@attributes) { @message }
14
+ if @message
15
+ p(id: @id, **@attributes) { @message }
16
+ else
17
+ p(id: @id, **@attributes, &)
18
+ end
15
19
  end
16
20
  end
17
21
  end
@@ -0,0 +1,143 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ShadcnPhlexcomponents
4
+ module FormHelpers
5
+ module AliasedLabel
6
+ include Phlex::Rails::Helpers::Label
7
+
8
+ alias_method :rails_label, :label
9
+ end
10
+
11
+ include AliasedLabel
12
+ include Phlex::Rails::Helpers::FieldID
13
+ include Phlex::Rails::Helpers::FieldName
14
+
15
+ def label(text = nil, **attributes, &)
16
+ @yield_label = true
17
+ attrs = label_attributes(use_label_styles: false, **attributes)
18
+
19
+ if text
20
+ Label(**attrs) { text }
21
+ else
22
+ Label(**attrs, &)
23
+ end
24
+ end
25
+
26
+ def hint(text = nil, **attributes, &)
27
+ @yield_hint = true
28
+ attrs = hint_attributes(**attributes)
29
+
30
+ if text
31
+ FormHint(text, **attrs)
32
+ else
33
+ FormHint(**attrs, &)
34
+ end
35
+ end
36
+
37
+ def render_label(&)
38
+ # It's currently not possible to separate the content of the yield in Phlex.
39
+ # So we use Javascript to remove the duplicated hint or label.
40
+ if @yield_label && @yield_hint
41
+ div(data: { remove_hint: true }, &)
42
+ elsif @yield_label
43
+ yield
44
+ elsif @label
45
+ attrs = label_attributes(use_label_styles: false)
46
+ Label(**attrs) { @label }
47
+ elsif @label != false
48
+ attrs = label_attributes(use_label_styles: true)
49
+ rails_label(@object_name, @method, nil, **attrs)
50
+ end
51
+ end
52
+
53
+ def render_hint(&)
54
+ # It's currently not possible to separate the content of the yield in Phlex.
55
+ # So we use Javascript to remove the duplicated hint or label.
56
+ if @yield_label && @yield_hint
57
+ div(data: { remove_label: true }, &)
58
+ elsif @yield_hint
59
+ yield
60
+ elsif @hint
61
+ attrs = hint_attributes
62
+ FormHint(@hint, aria_id: @aria_id, **attrs)
63
+ end
64
+ end
65
+
66
+ def render_error
67
+ if @error.present?
68
+ if @error.is_a?(Array)
69
+ FormError(nil, aria_id: @aria_id, class: "space-y-0.5") do
70
+ @error.each do |error|
71
+ span(class: "block") { error }
72
+ end
73
+ end
74
+ else
75
+ FormError(@error, aria_id: @aria_id)
76
+ end
77
+ end
78
+ end
79
+
80
+ def label_attributes(use_label_styles: false, **attributes)
81
+ attributes[:class] = [
82
+ use_label_styles ? Label.new.class_variants : nil,
83
+ @error.present? ? "text-destructive" : nil,
84
+ attributes[:class],
85
+ ].compact.join(" ")
86
+ attributes[:for] ||= @id
87
+ attributes
88
+ end
89
+
90
+ def hint_attributes(**attributes)
91
+ attributes
92
+ end
93
+
94
+ def label_and_hint_container_attributes
95
+ {
96
+ controller: @yield_label && @yield_hint ? "form-field" : nil,
97
+ }.compact
98
+ end
99
+
100
+ def aria_attributes
101
+ {
102
+ describedby: describedby,
103
+ invalid: @error.present?.to_s,
104
+ }
105
+ end
106
+
107
+ def describedby
108
+ return if !@hint && !@error.present?
109
+
110
+ [
111
+ @hint ? "#{@aria_id}-description" : nil,
112
+ @error.present? ? "#{@aria_id}-message" : nil,
113
+ ].compact.join(" ")
114
+ end
115
+
116
+ def default_value(value, method)
117
+ return value unless value.nil?
118
+ return unless @model
119
+
120
+ if @model.respond_to?(method)
121
+ @model.public_send(method)
122
+ end
123
+ end
124
+
125
+ def default_checked(checked, method)
126
+ return checked if [true, false].include?(checked)
127
+ return unless @model
128
+
129
+ if @model.respond_to?(method)
130
+ !!@model.public_send(method)
131
+ end
132
+ end
133
+
134
+ def default_error(error, method)
135
+ return error unless error.nil?
136
+ return unless @model
137
+
138
+ if @model.respond_to?(:errors)
139
+ @model.errors.full_messages_for(method).first
140
+ end
141
+ end
142
+ end
143
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ShadcnPhlexcomponents
4
+ class FormHint < Base
5
+ class_variants(base: "text-muted-foreground text-sm")
6
+
7
+ def initialize(message = nil, aria_id: nil, **attributes)
8
+ @message = message
9
+ @id = aria_id ? "#{aria_id}-description" : nil
10
+ super(**attributes)
11
+ end
12
+
13
+ def view_template(&)
14
+ if @message
15
+ p(id: @id, **@attributes) { @message }
16
+ else
17
+ p(id: @id, **@attributes, &)
18
+ end
19
+ end
20
+ end
21
+ end
@@ -21,12 +21,11 @@ module ShadcnPhlexcomponents
21
21
  @model = model
22
22
  @object_name = object_name
23
23
  @type = type.to_sym
24
- @value = value
25
- @model_value = model&.public_send(method)
24
+ @value = default_value(value, method)
26
25
  @name = name
27
26
  @id = id
28
27
  @label = label
29
- @error = error || (model ? model.errors.full_messages_for(method).first : nil)
28
+ @error = default_error(error, method)
30
29
  @hint = hint
31
30
  @aria_id = "form-field-#{SecureRandom.hex(5)}"
32
31
  super(**attributes)
@@ -40,7 +39,7 @@ module ShadcnPhlexcomponents
40
39
 
41
40
  div(class: "space-y-2", data: label_and_hint_container_attributes) do
42
41
  render_label(&)
43
- Input(type: @type, id: @id, name: @name, value: @value || @model_value, aria: aria_attributes, **@attributes)
42
+ Input(type: @type, id: @id, name: @name, value: @value, aria: aria_attributes, **@attributes)
44
43
  render_hint(&)
45
44
  render_error
46
45
  end