shadcn_phlexcomponents 0.1.0

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 (168) hide show
  1. checksums.yaml +7 -0
  2. data/README.md +39 -0
  3. data/Rakefile +12 -0
  4. data/app/assets/tailwind/tailwindcss-animate.css +318 -0
  5. data/app/assets/tailwind/vanilla-calendar-pro.css +461 -0
  6. data/app/javascript/controllers/accordion_controller.js +133 -0
  7. data/app/javascript/controllers/alert_dialog_controller.js +157 -0
  8. data/app/javascript/controllers/avatar_controller.js +15 -0
  9. data/app/javascript/controllers/checkbox_controller.js +28 -0
  10. data/app/javascript/controllers/collapsible_controller.js +35 -0
  11. data/app/javascript/controllers/combobox_controller.js +291 -0
  12. data/app/javascript/controllers/datepicker_controller.js +47 -0
  13. data/app/javascript/controllers/dialog_controller.js +159 -0
  14. data/app/javascript/controllers/dropdown_menu_controller.js +193 -0
  15. data/app/javascript/controllers/hover_card_controller.js +135 -0
  16. data/app/javascript/controllers/loading_button_controller.js +15 -0
  17. data/app/javascript/controllers/popover_controller.js +124 -0
  18. data/app/javascript/controllers/progress_controller.js +14 -0
  19. data/app/javascript/controllers/radio_group_controller.js +90 -0
  20. data/app/javascript/controllers/select_controller.js +294 -0
  21. data/app/javascript/controllers/sheet_controller.js +159 -0
  22. data/app/javascript/controllers/sidebar_controller.js +36 -0
  23. data/app/javascript/controllers/sidebar_trigger_controller.js +15 -0
  24. data/app/javascript/controllers/switch_controller.js +24 -0
  25. data/app/javascript/controllers/tabs_controller.js +73 -0
  26. data/app/javascript/controllers/theme_switcher_controller.js +32 -0
  27. data/app/javascript/controllers/toast_container_controller.js +22 -0
  28. data/app/javascript/controllers/toast_controller.js +45 -0
  29. data/app/javascript/controllers/tooltip_controller.js +135 -0
  30. data/lib/components/accordion.rb +38 -0
  31. data/lib/components/accordion_content.rb +28 -0
  32. data/lib/components/accordion_item.rb +26 -0
  33. data/lib/components/accordion_trigger.rb +45 -0
  34. data/lib/components/alert.rb +40 -0
  35. data/lib/components/alert_description.rb +11 -0
  36. data/lib/components/alert_dialog.rb +60 -0
  37. data/lib/components/alert_dialog_action.rb +22 -0
  38. data/lib/components/alert_dialog_action_to.rb +37 -0
  39. data/lib/components/alert_dialog_cancel.rb +22 -0
  40. data/lib/components/alert_dialog_content.rb +40 -0
  41. data/lib/components/alert_dialog_description.rb +22 -0
  42. data/lib/components/alert_dialog_footer.rb +11 -0
  43. data/lib/components/alert_dialog_header.rb +11 -0
  44. data/lib/components/alert_dialog_title.rb +22 -0
  45. data/lib/components/alert_dialog_trigger.rb +50 -0
  46. data/lib/components/alert_title.rb +11 -0
  47. data/lib/components/aspect_ratio.rb +19 -0
  48. data/lib/components/avatar.rb +31 -0
  49. data/lib/components/avatar_fallback.rb +21 -0
  50. data/lib/components/avatar_image.rb +20 -0
  51. data/lib/components/badge.rb +36 -0
  52. data/lib/components/base.rb +108 -0
  53. data/lib/components/breadcrumb.rb +51 -0
  54. data/lib/components/breadcrumb_ellipsis.rb +23 -0
  55. data/lib/components/breadcrumb_item.rb +11 -0
  56. data/lib/components/breadcrumb_link.rb +7 -0
  57. data/lib/components/breadcrumb_page.rb +21 -0
  58. data/lib/components/breadcrumb_separator.rb +26 -0
  59. data/lib/components/button.rb +53 -0
  60. data/lib/components/card.rb +31 -0
  61. data/lib/components/card_content.rb +11 -0
  62. data/lib/components/card_description.rb +11 -0
  63. data/lib/components/card_footer.rb +11 -0
  64. data/lib/components/card_header.rb +11 -0
  65. data/lib/components/card_title.rb +11 -0
  66. data/lib/components/checkbox.rb +65 -0
  67. data/lib/components/checkbox_group.rb +48 -0
  68. data/lib/components/collapsible.rb +32 -0
  69. data/lib/components/collapsible_content.rb +25 -0
  70. data/lib/components/collapsible_trigger.rb +50 -0
  71. data/lib/components/datepicker.rb +38 -0
  72. data/lib/components/dialog.rb +52 -0
  73. data/lib/components/dialog_close.rb +42 -0
  74. data/lib/components/dialog_content.rb +54 -0
  75. data/lib/components/dialog_description.rb +22 -0
  76. data/lib/components/dialog_footer.rb +11 -0
  77. data/lib/components/dialog_header.rb +11 -0
  78. data/lib/components/dialog_title.rb +22 -0
  79. data/lib/components/dialog_trigger.rb +50 -0
  80. data/lib/components/dropdown_menu.rb +50 -0
  81. data/lib/components/dropdown_menu_content.rb +49 -0
  82. data/lib/components/dropdown_menu_item.rb +57 -0
  83. data/lib/components/dropdown_menu_item_to.rb +25 -0
  84. data/lib/components/dropdown_menu_label.rb +12 -0
  85. data/lib/components/dropdown_menu_separator.rb +20 -0
  86. data/lib/components/dropdown_menu_trigger.rb +58 -0
  87. data/lib/components/hover_card.rb +33 -0
  88. data/lib/components/hover_card_content.rb +36 -0
  89. data/lib/components/hover_card_trigger.rb +50 -0
  90. data/lib/components/input.rb +32 -0
  91. data/lib/components/label.rb +15 -0
  92. data/lib/components/link.rb +26 -0
  93. data/lib/components/loading_button.rb +21 -0
  94. data/lib/components/pagination.rb +38 -0
  95. data/lib/components/pagination_ellipsis.rb +24 -0
  96. data/lib/components/pagination_link.rb +34 -0
  97. data/lib/components/pagination_next.rb +32 -0
  98. data/lib/components/pagination_previous.rb +32 -0
  99. data/lib/components/popover.rb +35 -0
  100. data/lib/components/popover_content.rb +37 -0
  101. data/lib/components/popover_trigger.rb +52 -0
  102. data/lib/components/progress.rb +37 -0
  103. data/lib/components/radio_group.rb +62 -0
  104. data/lib/components/radio_group_item.rb +66 -0
  105. data/lib/components/select.rb +189 -0
  106. data/lib/components/select_content.rb +59 -0
  107. data/lib/components/select_group.rb +23 -0
  108. data/lib/components/select_item.rb +58 -0
  109. data/lib/components/select_label.rb +23 -0
  110. data/lib/components/select_trigger.rb +54 -0
  111. data/lib/components/separator.rb +29 -0
  112. data/lib/components/sheet.rb +53 -0
  113. data/lib/components/sheet_close.rb +42 -0
  114. data/lib/components/sheet_content.rb +67 -0
  115. data/lib/components/sheet_description.rb +22 -0
  116. data/lib/components/sheet_footer.rb +11 -0
  117. data/lib/components/sheet_header.rb +11 -0
  118. data/lib/components/sheet_title.rb +22 -0
  119. data/lib/components/sheet_trigger.rb +50 -0
  120. data/lib/components/sidebar.rb +103 -0
  121. data/lib/components/sidebar_container.rb +11 -0
  122. data/lib/components/sidebar_content.rb +11 -0
  123. data/lib/components/sidebar_footer.rb +11 -0
  124. data/lib/components/sidebar_group.rb +11 -0
  125. data/lib/components/sidebar_group_content.rb +11 -0
  126. data/lib/components/sidebar_group_label.rb +16 -0
  127. data/lib/components/sidebar_header.rb +11 -0
  128. data/lib/components/sidebar_inset.rb +15 -0
  129. data/lib/components/sidebar_menu.rb +11 -0
  130. data/lib/components/sidebar_menu_button.rb +61 -0
  131. data/lib/components/sidebar_menu_item.rb +9 -0
  132. data/lib/components/sidebar_menu_sub.rb +14 -0
  133. data/lib/components/sidebar_menu_sub_button.rb +48 -0
  134. data/lib/components/sidebar_menu_sub_item.rb +9 -0
  135. data/lib/components/sidebar_trigger.rb +40 -0
  136. data/lib/components/skeleton.rb +11 -0
  137. data/lib/components/switch.rb +65 -0
  138. data/lib/components/table.rb +73 -0
  139. data/lib/components/table_body.rb +11 -0
  140. data/lib/components/table_caption.rb +11 -0
  141. data/lib/components/table_cell.rb +11 -0
  142. data/lib/components/table_footer.rb +11 -0
  143. data/lib/components/table_head.rb +14 -0
  144. data/lib/components/table_header.rb +11 -0
  145. data/lib/components/table_row.rb +11 -0
  146. data/lib/components/tabs.rb +38 -0
  147. data/lib/components/tabs_content.rb +35 -0
  148. data/lib/components/tabs_list.rb +23 -0
  149. data/lib/components/tabs_trigger.rb +45 -0
  150. data/lib/components/textarea.rb +28 -0
  151. data/lib/components/theme_switcher.rb +21 -0
  152. data/lib/components/toast.rb +100 -0
  153. data/lib/components/toast_action.rb +38 -0
  154. data/lib/components/toast_action_to.rb +25 -0
  155. data/lib/components/toast_container.rb +44 -0
  156. data/lib/components/toast_content.rb +11 -0
  157. data/lib/components/toast_description.rb +11 -0
  158. data/lib/components/toast_title.rb +11 -0
  159. data/lib/components/tooltip.rb +34 -0
  160. data/lib/components/tooltip_content.rb +42 -0
  161. data/lib/components/tooltip_trigger.rb +50 -0
  162. data/lib/install/install_shadcn_phlexcomponents.rb +12 -0
  163. data/lib/shadcn_phlexcomponents/alias.rb +132 -0
  164. data/lib/shadcn_phlexcomponents/engine.rb +11 -0
  165. data/lib/shadcn_phlexcomponents/version.rb +5 -0
  166. data/lib/shadcn_phlexcomponents.rb +9 -0
  167. data/lib/tasks/install.rake +10 -0
  168. metadata +264 -0
@@ -0,0 +1,66 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ShadcnPhlexcomponents
4
+ class RadioGroupItem < Base
5
+ STYLES = <<~HEREDOC.freeze
6
+ aspect-square h-4 w-4 rounded-full border border-primary text-primary shadow
7
+ focus:outline-none focus-visible:ring-1 focus-visible:ring-ring
8
+ disabled:cursor-not-allowed disabled:opacity-50 relative cursor-pointer
9
+ group/radio
10
+ HEREDOC
11
+
12
+ def initialize(name: nil, value: nil, checked: false, id: nil, **attributes)
13
+ @value = value
14
+ @name = name
15
+ @checked = checked
16
+ @id = id || name
17
+ super(**attributes)
18
+ end
19
+
20
+ def view_template(&)
21
+ button(**@attributes) do
22
+ span(
23
+ class: "items-center justify-center hidden group-data-[checked=true]/radio:flex"
24
+ ) do
25
+ icon("circle", class: "size-2.5 fill-primary")
26
+ end
27
+
28
+ input(
29
+ type: "radio",
30
+ value: @value,
31
+ class: "-translate-x-full pointer-events-none absolute top-0 left-0 size-4 opacity-0",
32
+ name: @name,
33
+ tabindex: -1,
34
+ checked: @checked,
35
+ aria: { hidden: true },
36
+ data: { input: "" }
37
+ )
38
+ end
39
+ end
40
+
41
+ def default_attributes
42
+ {
43
+ id: @id,
44
+ type: "button",
45
+ tabindex: -1,
46
+ role: "radio",
47
+ aria: {
48
+ checked: @checked.to_s,
49
+ },
50
+ data: {
51
+ checked: @checked.to_s,
52
+ value: @value,
53
+ "shadcn-phlexcomponents--radio-group-target": "item",
54
+ action: <<~HEREDOC
55
+ click->shadcn-phlexcomponents--radio-group#setChecked
56
+ keydown.right->shadcn-phlexcomponents--radio-group#setCheckedToNext:prevent
57
+ keydown.down->shadcn-phlexcomponents--radio-group#setCheckedToNext:prevent
58
+ keydown.up->shadcn-phlexcomponents--radio-group#setCheckedToPrev:prevent
59
+ keydown.left->shadcn-phlexcomponents--radio-group#setCheckedToPrev:prevent
60
+ keydown.enter->shadcn-phlexcomponents--radio-group#preventDefault
61
+ HEREDOC
62
+ }
63
+ }
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,189 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ShadcnPhlexcomponents
4
+ class Select < Base
5
+ STYLES = "w-full".freeze
6
+
7
+ NATIVE_STYLES = <<~HEREDOC.freeze
8
+ flex h-9 w-full rounded-md border border-input bg-transparent px-3 py-2 pr-8
9
+ text-base shadow-sm transition-colors placeholder:text-muted-foreground
10
+ focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring
11
+ disabled:cursor-not-allowed disabled:opacity-50 md:text-sm appearance-none
12
+ relative
13
+ HEREDOC
14
+
15
+ NATIVE_OPTION_STYLES = "bg-popover text-popover-foreground".freeze
16
+
17
+ def initialize(id: nil,
18
+ name: nil,
19
+ value: nil,
20
+ placeholder: nil,
21
+ side: :bottom,
22
+ native: false,
23
+ dir: 'ltr',
24
+ include_blank: false,
25
+ disabled: false,
26
+ aria_id: "select-#{SecureRandom.hex(5)}",
27
+ **attributes)
28
+ @id = id || name
29
+ @name = name
30
+ @value = value
31
+ @placeholder = placeholder
32
+ @side = side
33
+ @native = native
34
+ @dir = dir
35
+ @include_blank = include_blank
36
+ @disabled = disabled
37
+ @aria_id = aria_id
38
+ super(**attributes)
39
+ end
40
+
41
+ def trigger(**attributes)
42
+ SelectTrigger(
43
+ id: @id,
44
+ aria_id: @aria_id,
45
+ dir: @dir,
46
+ value: @value,
47
+ placeholder: @placeholder,
48
+ disabled: @disabled,
49
+ **attributes)
50
+ end
51
+
52
+ def content(**attributes, &)
53
+ SelectContent(side: @side, aria_id: @aria_id, dir: @dir, include_blank: @include_blank, native: @native, **attributes, &)
54
+ end
55
+
56
+ def item(**attributes, &)
57
+ SelectItem(aria_id: @aria_id, **attributes, &)
58
+ end
59
+
60
+ def label(**attributes, &)
61
+ SelectLabel(aria_id: @aria_id, **attributes, &)
62
+ end
63
+
64
+ def group(**attributes, &)
65
+ SelectGroup(aria_id: @aria_id, **attributes, &)
66
+ end
67
+
68
+ def items(collection)
69
+ SelectTrigger(
70
+ id: @id,
71
+ aria_id: @aria_id,
72
+ dir: @dir,
73
+ value: @value,
74
+ placeholder: @placeholder,
75
+ disabled: @disabled)
76
+
77
+ SelectContent(aria_id: @aria_id, dir: @dir, include_blank: @include_blank, native: @native) do
78
+ collection.each do |option|
79
+ SelectItem(value: option[:value], aria_id: @aria_id, disabled: option[:disabled]) { option[:name] }
80
+ end
81
+ end
82
+ end
83
+
84
+ def view_template(&)
85
+ content = capture(&)
86
+ element = Nokogiri::HTML.fragment(content.to_s)
87
+ content_element = element.css('[data-shadcn-phlexcomponents--select-target="content"]')
88
+
89
+ if @native
90
+ div(class: "relative") do
91
+ select(**@attributes) do
92
+ if @placeholder || @include_blank
93
+ option(value: "", class: NATIVE_OPTION_STYLES) { @placeholder }
94
+ end
95
+
96
+ build_native_options(content_element)
97
+ end
98
+
99
+ icon("chevron-down", class: "size-4 absolute opacity-50 top-1/2 -translate-y-1/2 right-3 pointer-events-none")
100
+ end
101
+ else
102
+ div(**@attributes) do
103
+ yield
104
+
105
+ select(
106
+ name: @name,
107
+ disabled: @disabled,
108
+ style: {
109
+ position: 'absolute',
110
+ border: '0px',
111
+ width: '1px',
112
+ height: '1px',
113
+ padding: '0px',
114
+ margin: '-1px',
115
+ overflow: 'hidden',
116
+ clip: 'rect(0px, 0px, 0px, 0px)',
117
+ 'white-space': 'nowrap',
118
+ 'overflow-wrap': 'normal'
119
+ },
120
+ data: {
121
+ "shadcn-phlexcomponents--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_styles
132
+ if @native
133
+ NATIVE_STYLES
134
+ else
135
+ STYLES
136
+ end
137
+ end
138
+
139
+
140
+ def default_attributes
141
+ if @native
142
+ {
143
+ id: @id,
144
+ name:@name,
145
+ disabled: @disabled
146
+ }
147
+ else
148
+ {
149
+ data: {
150
+ side: @side,
151
+ aria_id: @aria_id,
152
+ controller: "shadcn-phlexcomponents--select",
153
+ "shadcn-phlexcomponents--select-selected-value": @value
154
+ }
155
+ }
156
+ end
157
+ end
158
+
159
+ def build_native_options(content_element)
160
+ content_element.children.each do |content_child|
161
+ next if content_child.is_a?(Nokogiri::XML::Text) || content_child.is_a?(Nokogiri::XML::Comment)
162
+
163
+ if content_child.attributes['data-shadcn-phlexcomponents--select-target']&.value == 'group'
164
+ group_label = content_child.at_css('[data-shadcn-phlexcomponents--select-target="label"]')&.text
165
+
166
+ optgroup(label: group_label, class: NATIVE_OPTION_STYLES) do
167
+ content_child.css('[data-shadcn-phlexcomponents--select-target="item"]').each do |i|
168
+ option(
169
+ value: i.attributes['data-value'].value,
170
+ class: NATIVE_OPTION_STYLES,
171
+ selected: i.attributes['data-value'].value === @value,
172
+ disabled: i.attributes['data-disabled']&.value === '') do
173
+ i.text
174
+ end
175
+ end
176
+ end
177
+ elsif content_child.attributes['data-shadcn-phlexcomponents--select-target']&.value == 'item'
178
+
179
+ option(value: content_child.attributes['data-value'].value,
180
+ class: NATIVE_OPTION_STYLES,
181
+ selected: content_child.attributes['data-value'].value === @value,
182
+ disabled: content_child.attributes['data-disabled']&.value === '') do
183
+ content_child.text
184
+ end
185
+ end
186
+ end
187
+ end
188
+ end
189
+ end
@@ -0,0 +1,59 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ShadcnPhlexcomponents
4
+ class SelectContent < Base
5
+ STYLES = <<~HEREDOC.freeze
6
+ relative z-50 min-w-[8rem] max-h-108 overflow-y-auto overflow-x-hidden rounded-md border
7
+ bg-popover text-popover-foreground p-1 shadow-md outline-none
8
+ data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0
9
+ data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95
10
+ data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2
11
+ data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2
12
+ data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1
13
+ data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1
14
+ HEREDOC
15
+
16
+ def initialize(side: :bottom, include_blank: false, native: false, dir: "ltr", aria_id: nil, **attributes)
17
+ @side = side
18
+ @include_blank = include_blank
19
+ @native = native
20
+ @dir = dir
21
+ @aria_id = aria_id
22
+ super(**attributes)
23
+ end
24
+
25
+ def view_template(&)
26
+ div(class: "hidden fixed top-0 left-0 w-max z-50", data: { "shadcn-phlexcomponents--select-target": "contentWrapper" }) do
27
+ div(**@attributes) do
28
+ if @include_blank && !@native
29
+ SelectItem(aria_id: @aria_id, value: "", class: "h-8", hide_icon: true) { @include_blank.is_a?(String) ? @include_blank : "" }
30
+ end
31
+
32
+ yield
33
+ end
34
+ end
35
+ end
36
+
37
+ def default_attributes
38
+ {
39
+ id: "#{@aria_id}-content",
40
+ dir: @dir,
41
+ tabindex: -1,
42
+ role: "listbox",
43
+ aria: {
44
+ labelledby: "#{@aria_id}-trigger",
45
+ orientation: "vertical"
46
+ },
47
+ data: {
48
+ state: "closed",
49
+ side: @side,
50
+ "shadcn-phlexcomponents--select-target": "content",
51
+ action: <<~HEREDOC,
52
+ keydown.up->shadcn-phlexcomponents--select#focusLastItem:prevent
53
+ keydown.down->shadcn-phlexcomponents--select#focusFirstItem:prevent
54
+ HEREDOC
55
+ }
56
+ }
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ShadcnPhlexcomponents
4
+ class SelectGroup < Base
5
+ def initialize(aria_id:, **attributes)
6
+ @aria_id = aria_id
7
+ super(**attributes)
8
+ end
9
+
10
+ def view_template(&)
11
+ div(**@attributes, &)
12
+ end
13
+
14
+ def default_attributes
15
+ {
16
+ role: "group",
17
+ data: {
18
+ "shadcn-phlexcomponents--select-target": "group"
19
+ }
20
+ }
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,58 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ShadcnPhlexcomponents
4
+ class SelectItem < Base
5
+ STYLES = <<~HEREDOC.freeze
6
+ group/item relative flex w-full cursor-default select-none items-center
7
+ rounded-sm py-1.5 pl-2 pr-8 text-sm outline-none focus:bg-accent
8
+ focus:text-accent-foreground data-[disabled]:pointer-events-none
9
+ data-[disabled]:opacity-50
10
+ HEREDOC
11
+
12
+ def initialize(value: nil, disabled: false, hide_icon: false, aria_id: nil, **attributes)
13
+ @value = value
14
+ @disabled = disabled
15
+ @aria_id = aria_id
16
+ @hide_icon = hide_icon
17
+ @aria_labelledby = "#{@aria_id}-#{@value.dasherize.parameterize}"
18
+ super(**attributes)
19
+ end
20
+
21
+ def view_template(&)
22
+ div(**@attributes) do
23
+ span(id: @aria_labelledby, &)
24
+
25
+ unless @hide_icon
26
+ span(class: "absolute right-2 h-3.5 w-3.5 items-center hidden justify-center group-aria-[selected=true]/item:flex") do
27
+ icon("check", class: "size-4")
28
+ end
29
+ end
30
+ end
31
+ end
32
+
33
+ def default_attributes
34
+ {
35
+ role: "option",
36
+ tabindex: -1,
37
+ aria: {
38
+ selected: false,
39
+ labelledby: @aria_labelledby
40
+ },
41
+ data: {
42
+ disabled: @disabled,
43
+ value: @value,
44
+ action: <<~HEREDOC,
45
+ click->shadcn-phlexcomponents--select#selectItem
46
+ keydown.up->shadcn-phlexcomponents--select#focusPrevItem:prevent:stop
47
+ keydown.down->shadcn-phlexcomponents--select#focusNextItem:prevent:stop
48
+ keydown.enter->shadcn-phlexcomponents--select#selectItem:prevent
49
+ keydown.space->shadcn-phlexcomponents--select#selectItem:prevent
50
+ mouseover->shadcn-phlexcomponents--select#focusItem
51
+ mouseout->shadcn-phlexcomponents--select#focusContent
52
+ HEREDOC
53
+ "shadcn-phlexcomponents--select-target": "item"
54
+ }
55
+ }
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+ module ShadcnPhlexcomponents
3
+ class SelectLabel < Base
4
+ STYLES = "px-2 py-1.5 text-sm font-semibold".freeze
5
+
6
+ def initialize(aria_id: nil, **attributes)
7
+ @aria_id = aria_id
8
+ super(**attributes)
9
+ end
10
+
11
+ def view_template(&)
12
+ div(**@attributes, &)
13
+ end
14
+
15
+ def default_attributes
16
+ {
17
+ data: {
18
+ "shadcn-phlexcomponents--select-target": "label"
19
+ }
20
+ }
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,54 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ShadcnPhlexcomponents
4
+ class SelectTrigger < Base
5
+ STYLES = <<~HEREDOC.freeze
6
+ flex h-9 items-center justify-between whitespace-nowrap rounded-md border
7
+ border-input bg-transparent px-3 py-2 text-sm shadow-sm ring-offset-background
8
+ data-[placeholder]:text-muted-foreground focus:outline-none focus:ring-1
9
+ focus:ring-ring disabled:cursor-not-allowed disabled:opacity-50 [&>span]:line-clamp-1
10
+ w-full cursor-pointer
11
+ HEREDOC
12
+
13
+ def initialize(id: nil, value: nil, placeholder: nil, dir: "ltr", aria_id: nil, **attributes)
14
+ @id = id
15
+ @value = value
16
+ @placeholder = placeholder
17
+ @dir = dir
18
+ @aria_id = aria_id
19
+ super(**attributes)
20
+ end
21
+
22
+ def view_template
23
+ button(**@attributes) do
24
+ span(class: "pointer-events-none", data: { "shadcn-phlexcomponents--select-target": "triggerText" }) { @value || @placeholder }
25
+
26
+ icon("chevron-down", class: "size-4 opacity-50")
27
+ end
28
+ end
29
+
30
+ def default_attributes
31
+ {
32
+ type: "button",
33
+ id: @id,
34
+ dir: @dir,
35
+ role: "combobox",
36
+ aria: {
37
+ autocomplete: "none",
38
+ expanded: false,
39
+ controls: "#{@aria_id}-content"
40
+ },
41
+ data: {
42
+ placeholder: @placeholder.present?,
43
+ placeholder_text: @placeholder,
44
+ action: <<~HEREDOC,
45
+ click->shadcn-phlexcomponents--select#toggle
46
+ keydown.down->shadcn-phlexcomponents--select#toggle:prevent
47
+ keydown.up->shadcn-phlexcomponents--select#toggle:prevent
48
+ HEREDOC
49
+ "shadcn-phlexcomponents--select-target": "trigger"
50
+ }
51
+ }
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ShadcnPhlexcomponents
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
9
+
10
+ def initialize(orientation: :horizontal, **attributes)
11
+ @orientation = orientation
12
+ super(**attributes)
13
+ end
14
+
15
+ def default_styles
16
+ "#{ORIENTATIONS[@orientation]}"
17
+ end
18
+
19
+ def default_attributes
20
+ {
21
+ role: "none"
22
+ }
23
+ end
24
+
25
+ def view_template
26
+ div(**@attributes)
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,53 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ShadcnPhlexcomponents
4
+ class Sheet < Base
5
+ STYLES = "inline-block".freeze
6
+
7
+ def initialize(side: :right, aria_id: "sheet-#{SecureRandom.hex(5)}", **attributes)
8
+ @side = side
9
+ @aria_id = aria_id
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(side: @side, 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 view_template(&)
42
+ div(**@attributes, &)
43
+ end
44
+
45
+ def default_attributes
46
+ {
47
+ data: {
48
+ controller: "shadcn-phlexcomponents--sheet"
49
+ }
50
+ }
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ShadcnPhlexcomponents
4
+ class SheetClose < Base
5
+ def initialize(as_child: false, **attributes)
6
+ @as_child = as_child
7
+ super(**attributes)
8
+ end
9
+
10
+ def view_template(&)
11
+ if @as_child
12
+ content = capture(&)
13
+ element = find_as_child(content.to_s)
14
+
15
+ vanish(&)
16
+ element_attributes = nokogiri_attributes_to_hash(element)
17
+ styles = TAILWIND_MERGER.merge("#{@attributes[:class]} #{element_attributes[:class]}")
18
+ merged_attributes = mix(@attributes, element_attributes)
19
+ merged_attributes[:class] = styles
20
+
21
+ if element.name == "button"
22
+ merged_attributes.delete(:role)
23
+ end
24
+
25
+ send(element.name, **merged_attributes) do
26
+ sanitize_as_child(element.children.to_s)
27
+ end
28
+ else
29
+ div(**@attributes, &)
30
+ end
31
+ end
32
+
33
+ def default_attributes
34
+ {
35
+ role: "button",
36
+ data: {
37
+ action: "click->shadcn-phlexcomponents--sheet#close"
38
+ }
39
+ }
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,67 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ShadcnPhlexcomponents
4
+
5
+ class SheetContent < Base
6
+ STYLES = <<~HEREDOC.freeze
7
+ fixed z-50 gap-4 bg-background p-6 shadow-lg transition ease-in-out
8
+ data-[state=closed]:duration-300 data-[state=open]:duration-500
9
+ data-[state=open]:animate-in data-[state=closed]:animate-out
10
+ HEREDOC
11
+
12
+ CLOSE_BUTTON_STYLES = <<~HEREDOC.freeze
13
+ absolute right-4 top-4 rounded-sm opacity-70 ring-offset-background
14
+ transition-opacity hover:opacity-100 focus:outline-none focus:ring-2
15
+ focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none
16
+ cursor-pointer
17
+ HEREDOC
18
+
19
+ SIDES = {
20
+ left: "inset-y-0 left-0 h-full w-3/4 border-r data-[state=closed]:slide-out-to-left data-[state=open]:slide-in-from-left sm:max-w-sm",
21
+ right: "inset-y-0 right-0 h-full w-3/4 border-l data-[state=closed]:slide-out-to-right data-[state=open]:slide-in-from-right sm:max-w-sm",
22
+ top: "inset-x-0 top-0 border-b data-[state=closed]:slide-out-to-top data-[state=open]:slide-in-from-top",
23
+ bottom: "inset-x-0 bottom-0 border-t data-[state=closed]:slide-out-to-bottom data-[state=open]:slide-in-from-bottom"
24
+ }
25
+
26
+ def self.default_styles(side)
27
+ "#{STYLES} #{SIDES[side]}"
28
+ end
29
+
30
+ def initialize(side: :right, aria_id: nil, **attributes)
31
+ @side = side
32
+ @aria_id = aria_id
33
+ super(**attributes)
34
+ end
35
+
36
+ def view_template(&)
37
+ @class = @attributes.delete(:class)
38
+ div(class: "#{@class} hidden", **@attributes) do
39
+ yield
40
+
41
+ button(class: CLOSE_BUTTON_STYLES, data: { action: "click->shadcn-phlexcomponents--sheet#close" }) do
42
+ icon("x", class: "size-4")
43
+ span(class: "sr-only") { "close" }
44
+ end
45
+ end
46
+ end
47
+
48
+ def default_attributes
49
+ {
50
+ id: "#{@aria_id}-content",
51
+ tabindex: -1,
52
+ role: "dialog",
53
+ aria: {
54
+ describedby: "#{@aria_id}-description",
55
+ labelledby: "#{@aria_id}-title",
56
+ },
57
+ data: {
58
+ "shadcn-phlexcomponents--sheet-target": "content"
59
+ }
60
+ }
61
+ end
62
+
63
+ def default_styles
64
+ self.class.default_styles(@side)
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ShadcnPhlexcomponents
4
+ class SheetDescription < Base
5
+ STYLES = "text-sm text-muted-foreground".freeze
6
+
7
+ def initialize(aria_id: nil, **attributes)
8
+ @aria_id = aria_id
9
+ super(**attributes)
10
+ end
11
+
12
+ def default_attributes
13
+ {
14
+ id: "#{@aria_id}-description"
15
+ }
16
+ end
17
+
18
+ def view_template(&)
19
+ p(**@attributes, &)
20
+ end
21
+ end
22
+ end