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,421 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ShadcnPhlexcomponents
4
+ NATIVE_OPTION_STYLES = "bg-popover text-popover-foreground"
5
+
6
+ class Select < Base
7
+ def initialize(
8
+ id: nil,
9
+ name: nil,
10
+ value: nil,
11
+ placeholder: nil,
12
+ native: false,
13
+ dir: "ltr",
14
+ include_blank: false,
15
+ disabled: false,
16
+ **attributes
17
+ )
18
+ @id = id
19
+ @name = name
20
+ @value = value
21
+ @placeholder = placeholder
22
+ @native = native
23
+ @dir = dir
24
+ @include_blank = include_blank
25
+ @disabled = disabled
26
+ @aria_id = "select-#{SecureRandom.hex(5)}"
27
+ super(**attributes)
28
+ end
29
+
30
+ def class_variants(**args)
31
+ if @native
32
+ Input.new.class_variants(class: "appearance-none #{args[:class]}")
33
+ else
34
+ TAILWIND_MERGER.merge("w-full #{args[:class]}")
35
+ end
36
+ end
37
+
38
+ def trigger(**attributes)
39
+ SelectTrigger(
40
+ id: @id,
41
+ aria_id: @aria_id,
42
+ dir: @dir,
43
+ value: @value,
44
+ placeholder: @placeholder,
45
+ disabled: @disabled,
46
+ **attributes,
47
+ )
48
+ end
49
+
50
+ def content(**attributes, &)
51
+ SelectContent(
52
+ aria_id: @aria_id, dir: @dir, include_blank: @include_blank, native: @native, **attributes, &
53
+ )
54
+ end
55
+
56
+ def item(**attributes, &)
57
+ SelectItem(aria_id: @aria_id, **attributes, &)
58
+ end
59
+
60
+ def label(**attributes, &)
61
+ SelectLabel(**attributes, &)
62
+ end
63
+
64
+ def group(**attributes, &)
65
+ SelectGroup(aria_id: @aria_id, **attributes, &)
66
+ end
67
+
68
+ def items(collection, value_method:, text_method:, disabled_items: nil, &)
69
+ vanish(&)
70
+
71
+ if collection.first&.is_a?(Hash)
72
+ collection = convert_collection_hash_to_struct(collection, value_method: value_method, text_method: text_method)
73
+ end
74
+
75
+ SelectTrigger(
76
+ id: @id,
77
+ aria_id: @aria_id,
78
+ dir: @dir,
79
+ value: @value,
80
+ placeholder: @placeholder,
81
+ disabled: @disabled,
82
+ )
83
+
84
+ SelectContent(aria_id: @aria_id, dir: @dir, include_blank: @include_blank, native: @native) do
85
+ collection.each do |item|
86
+ value = item.public_send(value_method)
87
+ text = item.public_send(text_method)
88
+
89
+ SelectItem(value: value, aria_id: @aria_id, disabled: item_disabled?(disabled_items, value)) { text }
90
+ end
91
+ end
92
+ end
93
+
94
+ def view_template(&)
95
+ content = capture(&)
96
+ element = Nokogiri::HTML.fragment(content.to_s)
97
+ content_element = element.css('[data-select-target="content"]')
98
+
99
+ if @native
100
+ div(class: "relative") do
101
+ select(**@attributes) do
102
+ if @placeholder || @include_blank
103
+ option(value: "", class: NATIVE_OPTION_STYLES) { @placeholder }
104
+ end
105
+
106
+ build_native_options(content_element)
107
+ end
108
+
109
+ icon("chevron-down", class: "size-4 absolute opacity-50 top-1/2 -translate-y-1/2 right-3 pointer-events-none")
110
+ end
111
+ else
112
+ div(**@attributes) do
113
+ yield
114
+
115
+ select(
116
+ name: @name,
117
+ disabled: @disabled,
118
+ class: "sr-only",
119
+ tabindex: -1,
120
+ data: {
121
+ select_target: "select",
122
+ },
123
+ ) do
124
+ option(value: "")
125
+ build_native_options(content_element)
126
+ end
127
+ end
128
+ end
129
+ end
130
+
131
+ def default_attributes
132
+ if @native
133
+ {
134
+ id: @id,
135
+ name: @name,
136
+ disabled: @disabled,
137
+ }
138
+ else
139
+ {
140
+ data: {
141
+ aria_id: @aria_id,
142
+ controller: "select",
143
+ select_selected_value: @value,
144
+ },
145
+ }
146
+ end
147
+ end
148
+
149
+ def build_native_options(content_element)
150
+ content_element.children.each do |content_child|
151
+ next if content_child.is_a?(Nokogiri::XML::Text) || content_child.is_a?(Nokogiri::XML::Comment)
152
+
153
+ if content_child.attributes["data-select-target"]&.value == "group"
154
+ group_label = content_child.at_css('[data-select-target="label"]')&.text
155
+
156
+ optgroup(label: group_label, class: NATIVE_OPTION_STYLES) do
157
+ content_child.css('[data-select-target="item"]').each do |i|
158
+ option(
159
+ value: i.attributes["data-value"].value,
160
+ class: NATIVE_OPTION_STYLES,
161
+ selected: i.attributes["data-value"].value == @value,
162
+ disabled: i.attributes["data-disabled"]&.value == "",
163
+ ) do
164
+ i.text
165
+ end
166
+ end
167
+ end
168
+ elsif content_child.attributes["data-select-target"]&.value == "item"
169
+
170
+ option(
171
+ value: content_child.attributes["data-value"].value,
172
+ class: NATIVE_OPTION_STYLES,
173
+ selected: content_child.attributes["data-value"].value == @value,
174
+ disabled: content_child.attributes["data-disabled"]&.value == "",
175
+ ) do
176
+ content_child.text
177
+ end
178
+ end
179
+ end
180
+ end
181
+ end
182
+
183
+ class SelectTrigger < Base
184
+ class_variants(
185
+ base: <<~HEREDOC,
186
+ border-input [&_svg:not([class*='text-'])]:text-muted-foreground
187
+ focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40
188
+ aria-invalid:border-destructive dark:bg-input/30 dark:hover:bg-input/50 flex items-center
189
+ justify-between gap-2 rounded-md border bg-transparent px-3 py-2 text-sm whitespace-nowrap shadow-xs
190
+ transition-[color,box-shadow] outline-none focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50
191
+ data-[size=default]:h-9 data-[size=sm]:h-8 *:data-[select-target=triggerText]:line-clamp-1#{" "}
192
+ *:data-[select-target=triggerText]:flex *:data-[select-target=triggerText]:items-center *:data-[select-target=triggerText]:gap-2
193
+ [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4
194
+ data-[placeholder]:data-[has-value=false]:text-muted-foreground w-full
195
+ HEREDOC
196
+ )
197
+
198
+ def initialize(id: nil, value: nil, placeholder: nil, dir: "ltr", aria_id: nil, **attributes)
199
+ @id = id
200
+ @value = value
201
+ @placeholder = placeholder
202
+ @dir = dir
203
+ @aria_id = aria_id
204
+ super(**attributes)
205
+ end
206
+
207
+ def view_template
208
+ button(**@attributes) do
209
+ span(class: "pointer-events-none", data: { select_target: "triggerText" }) do
210
+ @value || @placeholder
211
+ end
212
+
213
+ icon("chevron-down", class: "size-4 opacity-50 text-foreground")
214
+ end
215
+ end
216
+
217
+ def default_attributes
218
+ {
219
+ type: "button",
220
+ id: @id,
221
+ dir: @dir,
222
+ role: "combobox",
223
+ aria: {
224
+ autocomplete: "none",
225
+ expanded: false,
226
+ controls: "#{@aria_id}-content",
227
+ },
228
+ data: {
229
+ placeholder: @placeholder,
230
+ has_value: @value.present?.to_s,
231
+ action: <<~HEREDOC,
232
+ click->select#toggle
233
+ keydown.space->select#open
234
+ keydown.enter->select#open
235
+ keydown.down->select#open:prevent
236
+ HEREDOC
237
+ select_target: "trigger",
238
+ },
239
+ }
240
+ end
241
+ end
242
+
243
+ class SelectContent < Base
244
+ class_variants(
245
+ base: <<~HEREDOC,
246
+ bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out
247
+ data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95
248
+ data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2
249
+ data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 relative z-50
250
+ max-h-(--radix-popper-available-height) min-w-[8rem] origin-(--radix-popper-transform-origin)
251
+ overflow-x-hidden overflow-y-auto rounded-md border shadow-md p-1 pointer-events-auto outline-none
252
+ HEREDOC
253
+ )
254
+
255
+ def initialize(include_blank: false, native: false, dir: "ltr", side: :bottom, align: :center, aria_id: nil, **attributes)
256
+ @include_blank = include_blank
257
+ @native = native
258
+ @dir = dir
259
+ @side = side
260
+ @align = align
261
+ @aria_id = aria_id
262
+ super(**attributes)
263
+ end
264
+
265
+ def view_template(&)
266
+ div(
267
+ class: "hidden fixed top-0 left-0 w-max z-50",
268
+ data: { select_target: "contentContainer" },
269
+ ) do
270
+ div(**@attributes) do
271
+ if @include_blank && !@native
272
+ SelectItem(aria_id: @aria_id, value: "", class: "h-8", hide_icon: true) do
273
+ @include_blank.is_a?(String) ? @include_blank : ""
274
+ end
275
+ end
276
+
277
+ yield
278
+ end
279
+ end
280
+ end
281
+
282
+ def default_attributes
283
+ {
284
+ id: "#{@aria_id}-content",
285
+ dir: @dir,
286
+ tabindex: -1,
287
+ role: "listbox",
288
+ aria: {
289
+ labelledby: "#{@aria_id}-trigger",
290
+ orientation: "vertical",
291
+ },
292
+ data: {
293
+ side: @side,
294
+ align: @align,
295
+ state: "closed",
296
+ select_target: "content",
297
+ action: <<~HEREDOC,
298
+ select:click:outside->select#clickOutside
299
+ keydown.up->select#focusItemByIndex:prevent:self
300
+ keydown.down->select#focusItemByIndex:prevent:self
301
+ HEREDOC
302
+ },
303
+ }
304
+ end
305
+ end
306
+
307
+ class SelectLabel < Base
308
+ class_variants(
309
+ base: "text-muted-foreground px-2 py-1.5 text-xs",
310
+ )
311
+
312
+ def initialize(**attributes)
313
+ super(**attributes)
314
+ end
315
+
316
+ def view_template(&)
317
+ div(**@attributes, &)
318
+ end
319
+
320
+ def default_attributes
321
+ {
322
+ data: {
323
+ select_target: "label",
324
+ },
325
+ }
326
+ end
327
+ end
328
+
329
+ class SelectItem < Base
330
+ class_variants(
331
+ base: <<~HEREDOC,
332
+ focus:bg-accent focus:text-accent-foreground [&_svg:not([class*='text-'])]:text-muted-foreground
333
+ relative flex w-full cursor-default items-center gap-2 rounded-sm py-1.5 pr-8 pl-2 text-sm
334
+ outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50
335
+ [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4
336
+ *:[span]:last:items-center *:[span]:last:gap-2 group/item
337
+ HEREDOC
338
+ )
339
+
340
+ def initialize(value: nil, disabled: false, aria_id: nil, **attributes)
341
+ @value = value
342
+ @disabled = disabled
343
+ @aria_id = aria_id
344
+ @aria_labelledby = "#{@aria_id}-#{@value.dasherize.parameterize}"
345
+ super(**attributes)
346
+ end
347
+
348
+ def view_template(&)
349
+ div(**@attributes) do
350
+ span(id: @aria_labelledby, &)
351
+
352
+ span(class: "absolute right-2 h-3.5 w-3.5 items-center hidden justify-center
353
+ group-aria-[selected=true]/item:flex group-data-[value='']/item:hidden") do
354
+ icon("check", class: "size-4")
355
+ end
356
+ end
357
+ end
358
+
359
+ def default_attributes
360
+ {
361
+ role: "option",
362
+ tabindex: -1,
363
+ aria: {
364
+ selected: false,
365
+ labelledby: @aria_labelledby,
366
+ },
367
+ data: {
368
+ disabled: @disabled,
369
+ value: @value,
370
+ action: <<~HEREDOC,
371
+ click->select#select
372
+ mouseover->select#focusItem
373
+ keydown.up->select#focusItem:prevent
374
+ keydown.down->select#focusItem:prevent
375
+ focus->select#onItemFocus
376
+ blur->select#onItemBlur
377
+ keydown.enter->select#select:prevent
378
+ keydown.space->select#select:prevent
379
+ mouseout->select#focusContent
380
+ HEREDOC
381
+ select_target: "item",
382
+ },
383
+ }
384
+ end
385
+ end
386
+
387
+ class SelectGroup < Base
388
+ def initialize(aria_id: nil, **attributes)
389
+ @aria_id = aria_id
390
+ super(**attributes)
391
+ end
392
+
393
+ def view_template(&)
394
+ div(**@attributes, &)
395
+ end
396
+
397
+ def default_attributes
398
+ {
399
+ role: "group",
400
+ aria: {
401
+ labelledby: "#{@aria_id}-group-#{SecureRandom.hex(5)}",
402
+ },
403
+ data: {
404
+ select_target: "group",
405
+ },
406
+ }
407
+ end
408
+ end
409
+
410
+ class SelectSeparator < Base
411
+ class_variants(base: "bg-border pointer-events-none -mx-1 my-1 h-px")
412
+
413
+ def view_template(&)
414
+ div(**@attributes, &)
415
+ end
416
+
417
+ def default_attributes
418
+ { aria: { hidden: "true" } }
419
+ end
420
+ end
421
+ end
@@ -2,23 +2,24 @@
2
2
 
3
3
  module ShadcnPhlexcomponents
4
4
  class Separator < Base
5
- ORIENTATIONS = {
6
- horizontal: "shrink-0 bg-border h-[1px] w-full",
7
- vertical: "shrink-0 bg-border h-full w-[1px]",
8
- }.freeze
5
+ class_variants(
6
+ base: <<~HEREDOC,
7
+ bg-border shrink-0 data-[orientation=horizontal]:h-px data-[orientation=horizontal]:w-full
8
+ data-[orientation=vertical]:h-full data-[orientation=vertical]:w-px
9
+ HEREDOC
10
+ )
9
11
 
10
12
  def initialize(orientation: :horizontal, **attributes)
11
13
  @orientation = orientation
12
14
  super(**attributes)
13
15
  end
14
16
 
15
- def default_styles
16
- ORIENTATIONS[@orientation].to_s
17
- end
18
-
19
17
  def default_attributes
20
18
  {
21
19
  role: "none",
20
+ data: {
21
+ orientation: @orientation,
22
+ },
22
23
  }
23
24
  end
24
25
 
@@ -0,0 +1,239 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ShadcnPhlexcomponents
4
+ class Sheet < Base
5
+ class_variants(base: "inline-block max-w-fit")
6
+
7
+ def initialize(open: false, **attributes)
8
+ @open = open
9
+ @aria_id = "sheet-#{SecureRandom.hex(5)}"
10
+ super(**attributes)
11
+ end
12
+
13
+ def trigger(**attributes, &)
14
+ SheetTrigger(aria_id: @aria_id, **attributes, &)
15
+ end
16
+
17
+ def content(**attributes, &)
18
+ SheetContent(aria_id: @aria_id, **attributes, &)
19
+ end
20
+
21
+ def header(**attributes, &)
22
+ SheetHeader(**attributes, &)
23
+ end
24
+
25
+ def title(**attributes, &)
26
+ SheetTitle(aria_id: @aria_id, **attributes, &)
27
+ end
28
+
29
+ def description(**attributes, &)
30
+ SheetDescription(aria_id: @aria_id, **attributes, &)
31
+ end
32
+
33
+ def footer(**attributes, &)
34
+ SheetFooter(**attributes, &)
35
+ end
36
+
37
+ def close(**attributes, &)
38
+ SheetClose(**attributes, &)
39
+ end
40
+
41
+ def default_attributes
42
+ {
43
+ data: {
44
+ controller: "dialog",
45
+ },
46
+ }
47
+ end
48
+
49
+ def view_template(&)
50
+ div(**@attributes, &)
51
+ end
52
+ end
53
+
54
+ class SheetTrigger < Base
55
+ def initialize(as_child: false, aria_id: nil, **attributes)
56
+ @as_child = as_child
57
+ @aria_id = aria_id
58
+ super(**attributes)
59
+ end
60
+
61
+ def default_attributes
62
+ {
63
+ role: "button",
64
+ aria: {
65
+ haspopup: "dialog",
66
+ expanded: false,
67
+ controls: "#{@aria_id}-content",
68
+ },
69
+ data: {
70
+ as_child: @as_child.to_s,
71
+ action: "click->dialog#open",
72
+ dialog_target: "trigger",
73
+ },
74
+ }
75
+ end
76
+
77
+ def view_template(&)
78
+ if @as_child
79
+ content = capture(&)
80
+ element = find_as_child(content.to_s)
81
+ vanish(&)
82
+ merged_attributes = merged_as_child_attributes(element, @attributes)
83
+
84
+ send(element.name, **merged_attributes) do
85
+ sanitize_as_child(element.children.to_s)
86
+ end
87
+ else
88
+ div(**@attributes, &)
89
+ end
90
+ end
91
+ end
92
+
93
+ class SheetContent < Base
94
+ class_variants(
95
+ base: <<~HEREDOC,
96
+ bg-background data-[state=open]:animate-in data-[state=closed]:animate-out fixed z-50 flex flex-col gap-4
97
+ shadow-lg transition ease-in-out data-[state=closed]:duration-300 data-[state=open]:duration-500
98
+ pointer-events-auto
99
+ HEREDOC
100
+ variants: {
101
+ side: {
102
+ top: "data-[state=closed]:slide-out-to-top data-[state=open]:slide-in-from-top inset-x-0 top-0 h-auto border-b",
103
+ left: "h-screen data-[state=closed]:slide-out-to-left data-[state=open]:slide-in-from-left inset-y-0 left-0 w-3/4 border-r sm:max-w-sm",
104
+ right: "h-screen data-[state=closed]:slide-out-to-right data-[state=open]:slide-in-from-right inset-y-0 right-0 w-3/4 border-l sm:max-w-sm",
105
+ bottom: "data-[state=closed]:slide-out-to-bottom data-[state=open]:slide-in-from-bottom inset-x-0 bottom-0 h-auto border-t",
106
+ },
107
+ },
108
+ defaults: {
109
+ side: :right,
110
+ },
111
+ )
112
+
113
+ def initialize(side: :right, aria_id: nil, **attributes)
114
+ @class_variants = { side: side }
115
+ @aria_id = aria_id
116
+ super(**attributes)
117
+ end
118
+
119
+ def view_template(&)
120
+ @class = @attributes.delete(:class)
121
+ div(class: "#{@class} hidden", **@attributes) do
122
+ yield
123
+
124
+ button(
125
+ class: <<~HEREDOC,
126
+ ring-offset-background focus:ring-ring data-[state=open]:bg-secondary absolute top-4 right-4 rounded-xs
127
+ opacity-70 transition-opacity hover:opacity-100 focus:ring-2 focus:ring-offset-2 focus:outline-hidden
128
+ disabled:pointer-events-none
129
+ HEREDOC
130
+ data: { action: "click->dialog#close" },
131
+ ) do
132
+ icon("x", class: "size-4")
133
+ span(class: "sr-only") { "close" }
134
+ end
135
+ end
136
+ end
137
+
138
+ def default_attributes
139
+ {
140
+ id: "#{@aria_id}-content",
141
+ tabindex: -1,
142
+ role: "dialog",
143
+ aria: {
144
+ describedby: "#{@aria_id}-description",
145
+ labelledby: "#{@aria_id}-title",
146
+ },
147
+ data: {
148
+ dialog_target: "content",
149
+ action: "dialog:click:outside->dialog#close",
150
+ },
151
+ }
152
+ end
153
+ end
154
+
155
+ class SheetHeader < Base
156
+ class_variants(base: "flex flex-col gap-1.5 p-4")
157
+
158
+ def view_template(&)
159
+ div(**@attributes, &)
160
+ end
161
+ end
162
+
163
+ class SheetTitle < Base
164
+ class_variants(base: "text-foreground font-semibold")
165
+
166
+ def initialize(aria_id: nil, **attributes)
167
+ @aria_id = aria_id
168
+ super(**attributes)
169
+ end
170
+
171
+ def default_attributes
172
+ {
173
+ id: "#{@aria_id}-title",
174
+ }
175
+ end
176
+
177
+ def view_template(&)
178
+ h2(**@attributes, &)
179
+ end
180
+ end
181
+
182
+ class SheetDescription < Base
183
+ class_variants(base: "text-muted-foreground text-sm")
184
+
185
+ def initialize(aria_id: nil, **attributes)
186
+ @aria_id = aria_id
187
+ super(**attributes)
188
+ end
189
+
190
+ def default_attributes
191
+ {
192
+ id: "#{@aria_id}-description",
193
+ }
194
+ end
195
+
196
+ def view_template(&)
197
+ p(**@attributes, &)
198
+ end
199
+ end
200
+
201
+ class SheetFooter < Base
202
+ class_variants(base: "mt-auto flex flex-col gap-2 p-4")
203
+
204
+ def view_template(&)
205
+ div(**@attributes, &)
206
+ end
207
+ end
208
+
209
+ class SheetClose < Base
210
+ def initialize(as_child: false, **attributes)
211
+ @as_child = as_child
212
+ super(**attributes)
213
+ end
214
+
215
+ def view_template(&)
216
+ if @as_child
217
+ content = capture(&)
218
+ element = find_as_child(content.to_s)
219
+ vanish(&)
220
+ merged_attributes = merged_as_child_attributes(element, @attributes)
221
+
222
+ send(element.name, **merged_attributes) do
223
+ sanitize_as_child(element.children.to_s)
224
+ end
225
+ else
226
+ div(**@attributes, &)
227
+ end
228
+ end
229
+
230
+ def default_attributes
231
+ {
232
+ role: "button",
233
+ data: {
234
+ action: "click->dialog#close",
235
+ },
236
+ }
237
+ end
238
+ end
239
+ end
@@ -2,7 +2,7 @@
2
2
 
3
3
  module ShadcnPhlexcomponents
4
4
  class Skeleton < Base
5
- STYLES = "animate-pulse rounded-md bg-primary/10"
5
+ class_variants(base: "bg-accent animate-pulse rounded-md")
6
6
 
7
7
  def view_template(&)
8
8
  div(**@attributes, &)