shadcn-phlex 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 (113) hide show
  1. checksums.yaml +7 -0
  2. data/README.md +195 -0
  3. data/app.css +20 -0
  4. data/css/shadcn-source.css +3 -0
  5. data/css/shadcn-tailwind.css +160 -0
  6. data/css/themes/mauve.css +62 -0
  7. data/css/themes/mist.css +62 -0
  8. data/css/themes/neutral.css +74 -0
  9. data/css/themes/olive.css +62 -0
  10. data/css/themes/stone.css +62 -0
  11. data/css/themes/taupe.css +62 -0
  12. data/css/themes/zinc.css +62 -0
  13. data/js/controllers/accordion_controller.js +135 -0
  14. data/js/controllers/checkbox_controller.js +52 -0
  15. data/js/controllers/collapsible_controller.js +85 -0
  16. data/js/controllers/combobox_controller.js +168 -0
  17. data/js/controllers/command_controller.js +171 -0
  18. data/js/controllers/context_menu_controller.js +132 -0
  19. data/js/controllers/dark_mode_controller.js +106 -0
  20. data/js/controllers/dialog_controller.js +205 -0
  21. data/js/controllers/drawer_controller.js +161 -0
  22. data/js/controllers/dropdown_menu_controller.js +189 -0
  23. data/js/controllers/hover_card_controller.js +85 -0
  24. data/js/controllers/index.js +89 -0
  25. data/js/controllers/menubar_controller.js +171 -0
  26. data/js/controllers/navigation_menu_controller.js +160 -0
  27. data/js/controllers/popover_controller.js +151 -0
  28. data/js/controllers/radio_group_controller.js +78 -0
  29. data/js/controllers/scroll_area_controller.js +117 -0
  30. data/js/controllers/select_controller.js +198 -0
  31. data/js/controllers/sheet_controller.js +130 -0
  32. data/js/controllers/slider_controller.js +142 -0
  33. data/js/controllers/switch_controller.js +40 -0
  34. data/js/controllers/tabs_controller.js +96 -0
  35. data/js/controllers/toast_controller.js +206 -0
  36. data/js/controllers/toggle_controller.js +30 -0
  37. data/js/controllers/toggle_group_controller.js +73 -0
  38. data/js/controllers/tooltip_controller.js +146 -0
  39. data/lib/generators/shadcn_phlex/component_generator.rb +79 -0
  40. data/lib/generators/shadcn_phlex/install_generator.rb +217 -0
  41. data/lib/shadcn/base.rb +27 -0
  42. data/lib/shadcn/engine.rb +24 -0
  43. data/lib/shadcn/kit.rb +1158 -0
  44. data/lib/shadcn/themes/accent_colors.rb +106 -0
  45. data/lib/shadcn/themes/base_colors.rb +313 -0
  46. data/lib/shadcn/ui/accordion.rb +135 -0
  47. data/lib/shadcn/ui/alert.rb +79 -0
  48. data/lib/shadcn/ui/alert_dialog.rb +220 -0
  49. data/lib/shadcn/ui/aspect_ratio.rb +35 -0
  50. data/lib/shadcn/ui/avatar.rb +134 -0
  51. data/lib/shadcn/ui/badge.rb +48 -0
  52. data/lib/shadcn/ui/breadcrumb.rb +180 -0
  53. data/lib/shadcn/ui/button.rb +63 -0
  54. data/lib/shadcn/ui/button_group.rb +58 -0
  55. data/lib/shadcn/ui/card.rb +133 -0
  56. data/lib/shadcn/ui/checkbox.rb +72 -0
  57. data/lib/shadcn/ui/collapsible.rb +76 -0
  58. data/lib/shadcn/ui/combobox.rb +229 -0
  59. data/lib/shadcn/ui/command.rb +256 -0
  60. data/lib/shadcn/ui/context_menu.rb +319 -0
  61. data/lib/shadcn/ui/dialog.rb +226 -0
  62. data/lib/shadcn/ui/direction.rb +23 -0
  63. data/lib/shadcn/ui/drawer.rb +217 -0
  64. data/lib/shadcn/ui/dropdown_menu.rb +384 -0
  65. data/lib/shadcn/ui/empty.rb +97 -0
  66. data/lib/shadcn/ui/field.rb +126 -0
  67. data/lib/shadcn/ui/hover_card.rb +75 -0
  68. data/lib/shadcn/ui/input.rb +36 -0
  69. data/lib/shadcn/ui/input_group.rb +32 -0
  70. data/lib/shadcn/ui/input_otp.rb +112 -0
  71. data/lib/shadcn/ui/item.rb +115 -0
  72. data/lib/shadcn/ui/kbd.rb +45 -0
  73. data/lib/shadcn/ui/label.rb +28 -0
  74. data/lib/shadcn/ui/menubar.rb +345 -0
  75. data/lib/shadcn/ui/native_select.rb +31 -0
  76. data/lib/shadcn/ui/navigation_menu.rb +238 -0
  77. data/lib/shadcn/ui/pagination.rb +224 -0
  78. data/lib/shadcn/ui/popover.rb +147 -0
  79. data/lib/shadcn/ui/progress.rb +40 -0
  80. data/lib/shadcn/ui/radio_group.rb +92 -0
  81. data/lib/shadcn/ui/resizable.rb +108 -0
  82. data/lib/shadcn/ui/scroll_area.rb +75 -0
  83. data/lib/shadcn/ui/select.rb +235 -0
  84. data/lib/shadcn/ui/separator.rb +36 -0
  85. data/lib/shadcn/ui/sheet.rb +231 -0
  86. data/lib/shadcn/ui/sidebar.rb +420 -0
  87. data/lib/shadcn/ui/skeleton.rb +23 -0
  88. data/lib/shadcn/ui/slider.rb +72 -0
  89. data/lib/shadcn/ui/sonner.rb +177 -0
  90. data/lib/shadcn/ui/spinner.rb +58 -0
  91. data/lib/shadcn/ui/switch.rb +75 -0
  92. data/lib/shadcn/ui/table.rb +154 -0
  93. data/lib/shadcn/ui/tabs.rb +154 -0
  94. data/lib/shadcn/ui/text_field.rb +146 -0
  95. data/lib/shadcn/ui/textarea.rb +32 -0
  96. data/lib/shadcn/ui/theme_toggle.rb +74 -0
  97. data/lib/shadcn/ui/toggle.rb +66 -0
  98. data/lib/shadcn/ui/toggle_group.rb +75 -0
  99. data/lib/shadcn/ui/tooltip.rb +78 -0
  100. data/lib/shadcn/ui/typography.rb +217 -0
  101. data/lib/shadcn/version.rb +5 -0
  102. data/lib/shadcn-phlex.rb +6 -0
  103. data/lib/shadcn.rb +80 -0
  104. data/package.json +14 -0
  105. data/skills/shadcn-phlex/SKILL.md +190 -0
  106. data/skills/shadcn-phlex/evals/evals.json +90 -0
  107. data/skills/shadcn-phlex/references/component-catalog.md +355 -0
  108. data/skills/shadcn-phlex/rules/composition.md +235 -0
  109. data/skills/shadcn-phlex/rules/forms.md +151 -0
  110. data/skills/shadcn-phlex/rules/helpers.md +54 -0
  111. data/skills/shadcn-phlex/rules/stimulus.md +61 -0
  112. data/skills/shadcn-phlex/rules/styling.md +177 -0
  113. metadata +209 -0
@@ -0,0 +1,238 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Shadcn
4
+ module UI
5
+ # Port of shadcn/ui NavigationMenu
6
+ class NavigationMenu < Base
7
+ def initialize(viewport: true, **attrs)
8
+ @viewport = viewport
9
+ @attrs = attrs
10
+ end
11
+
12
+ def view_template(&block)
13
+ nav(**build_attrs) do
14
+ yield if block_given?
15
+ render NavigationMenuViewport.new if @viewport
16
+ end
17
+ end
18
+
19
+ private
20
+
21
+ def build_attrs
22
+ classes = cn(
23
+ "group/navigation-menu relative flex max-w-max flex-1 items-center justify-center",
24
+ @attrs.delete(:class)
25
+ )
26
+ @attrs.merge(
27
+ data_slot: "navigation-menu",
28
+ data_controller: "shadcn--navigation-menu",
29
+ data_viewport: @viewport,
30
+ class: classes
31
+ )
32
+ end
33
+ end
34
+
35
+ class NavigationMenuList < Base
36
+ def initialize(**attrs)
37
+ @attrs = attrs
38
+ end
39
+
40
+ def view_template(&block)
41
+ ul(**build_attrs, &block)
42
+ end
43
+
44
+ private
45
+
46
+ def build_attrs
47
+ classes = cn(
48
+ "group flex flex-1 list-none items-center justify-center gap-1",
49
+ @attrs.delete(:class)
50
+ )
51
+ @attrs.merge(data_slot: "navigation-menu-list", class: classes)
52
+ end
53
+ end
54
+
55
+ class NavigationMenuItem < Base
56
+ def initialize(**attrs)
57
+ @attrs = attrs
58
+ end
59
+
60
+ def view_template(&block)
61
+ li(**build_attrs, &block)
62
+ end
63
+
64
+ private
65
+
66
+ def build_attrs
67
+ classes = cn("relative", @attrs.delete(:class))
68
+ @attrs.merge(data_slot: "navigation-menu-item", class: classes)
69
+ end
70
+ end
71
+
72
+ class NavigationMenuTrigger < Base
73
+ def initialize(**attrs)
74
+ @attrs = attrs
75
+ end
76
+
77
+ def view_template(&block)
78
+ button(**build_attrs) do
79
+ yield if block_given?
80
+ plain " "
81
+ svg(
82
+ xmlns: "http://www.w3.org/2000/svg",
83
+ width: "12", height: "12",
84
+ viewbox: "0 0 24 24",
85
+ fill: "none",
86
+ stroke: "currentColor",
87
+ stroke_width: "2",
88
+ stroke_linecap: "round",
89
+ stroke_linejoin: "round",
90
+ class: "relative top-[1px] ml-1 size-3 transition duration-300 group-data-[state=open]:rotate-180",
91
+ aria_hidden: "true"
92
+ ) do |s|
93
+ s.path(d: "m6 9 6 6 6-6")
94
+ end
95
+ end
96
+ end
97
+
98
+ private
99
+
100
+ def build_attrs
101
+ classes = cn(
102
+ "group inline-flex h-9 w-max items-center justify-center rounded-md bg-background px-4 py-2 text-sm font-medium",
103
+ "transition-[color,box-shadow] outline-none",
104
+ "hover:bg-accent hover:text-accent-foreground",
105
+ "focus:bg-accent focus:text-accent-foreground",
106
+ "focus-visible:ring-[3px] focus-visible:ring-ring/50 focus-visible:outline-1",
107
+ "disabled:pointer-events-none disabled:opacity-50",
108
+ "data-[state=open]:bg-accent/50 data-[state=open]:text-accent-foreground",
109
+ @attrs.delete(:class)
110
+ )
111
+ @attrs.merge(
112
+ data_slot: "navigation-menu-trigger",
113
+ data_shadcn__navigation_menu_target: "trigger",
114
+ data_action: "mouseenter->shadcn--navigation-menu#enterTrigger mouseleave->shadcn--navigation-menu#leaveTrigger click->shadcn--navigation-menu#clickTrigger keydown->shadcn--navigation-menu#keydown",
115
+ type: "button",
116
+ class: classes
117
+ )
118
+ end
119
+ end
120
+
121
+ class NavigationMenuContent < Base
122
+ def initialize(**attrs)
123
+ @attrs = attrs
124
+ end
125
+
126
+ def view_template(&block)
127
+ div(**build_attrs, &block)
128
+ end
129
+
130
+ private
131
+
132
+ def build_attrs
133
+ classes = cn(
134
+ "top-0 left-0 w-full p-2 pr-2.5",
135
+ "data-[motion=from-end]:slide-in-from-right-52 data-[motion=from-start]:slide-in-from-left-52",
136
+ "data-[motion=to-end]:slide-out-to-right-52 data-[motion=to-start]:slide-out-to-left-52",
137
+ "data-[motion^=from-]:animate-in data-[motion^=from-]:fade-in",
138
+ "data-[motion^=to-]:animate-out data-[motion^=to-]:fade-out",
139
+ "md:absolute md:w-auto",
140
+ @attrs.delete(:class)
141
+ )
142
+ @attrs.merge(
143
+ data_slot: "navigation-menu-content",
144
+ data_shadcn__navigation_menu_target: "content",
145
+ data_action: "mouseenter->shadcn--navigation-menu#enterContent mouseleave->shadcn--navigation-menu#leaveContent keydown->shadcn--navigation-menu#contentKeydown",
146
+ hidden: true,
147
+ class: classes
148
+ )
149
+ end
150
+ end
151
+
152
+ class NavigationMenuViewport < Base
153
+ def initialize(**attrs)
154
+ @attrs = attrs
155
+ end
156
+
157
+ def view_template
158
+ div(class: "absolute top-full left-0 isolate z-50 flex justify-center") do
159
+ div(**build_attrs)
160
+ end
161
+ end
162
+
163
+ private
164
+
165
+ def build_attrs
166
+ classes = cn(
167
+ "origin-top-center relative mt-1.5 w-full overflow-hidden rounded-md border bg-popover text-popover-foreground shadow",
168
+ "data-[state=closed]:animate-out data-[state=closed]:zoom-out-95",
169
+ "data-[state=open]:animate-in data-[state=open]:zoom-in-90",
170
+ @attrs.delete(:class)
171
+ )
172
+ @attrs.merge(
173
+ data_slot: "navigation-menu-viewport",
174
+ data_shadcn__navigation_menu_target: "viewport",
175
+ hidden: true,
176
+ class: classes
177
+ )
178
+ end
179
+ end
180
+
181
+ class NavigationMenuLink < Base
182
+ def initialize(href: "#", active: false, **attrs)
183
+ @href = href
184
+ @active = active
185
+ @attrs = attrs
186
+ end
187
+
188
+ def view_template(&block)
189
+ a(**build_attrs, &block)
190
+ end
191
+
192
+ private
193
+
194
+ def build_attrs
195
+ classes = cn(
196
+ "flex flex-col gap-1 rounded-sm p-2 text-sm transition-all outline-none",
197
+ "hover:bg-accent hover:text-accent-foreground",
198
+ "focus:bg-accent focus:text-accent-foreground",
199
+ "focus-visible:ring-[3px] focus-visible:ring-ring/50 focus-visible:outline-1",
200
+ "data-[active=true]:bg-accent/50 data-[active=true]:text-accent-foreground",
201
+ "[&_svg:not([class*='size-'])]:size-4 [&_svg:not([class*='text-'])]:text-muted-foreground",
202
+ @attrs.delete(:class)
203
+ )
204
+ @attrs.merge(
205
+ data_slot: "navigation-menu-link",
206
+ data_shadcn__navigation_menu_target: "link",
207
+ data_active: @active,
208
+ href: @href,
209
+ class: classes
210
+ )
211
+ end
212
+ end
213
+
214
+ class NavigationMenuIndicator < Base
215
+ def initialize(**attrs)
216
+ @attrs = attrs
217
+ end
218
+
219
+ def view_template
220
+ div(**build_attrs) do
221
+ div(class: "relative top-[60%] h-2 w-2 rotate-45 rounded-tl-sm bg-border shadow-md")
222
+ end
223
+ end
224
+
225
+ private
226
+
227
+ def build_attrs
228
+ classes = cn(
229
+ "top-full z-[1] flex h-1.5 items-end justify-center overflow-hidden",
230
+ "data-[state=hidden]:animate-out data-[state=hidden]:fade-out",
231
+ "data-[state=visible]:animate-in data-[state=visible]:fade-in",
232
+ @attrs.delete(:class)
233
+ )
234
+ @attrs.merge(data_slot: "navigation-menu-indicator", class: classes)
235
+ end
236
+ end
237
+ end
238
+ end
@@ -0,0 +1,224 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Shadcn
4
+ module UI
5
+ # Port of shadcn/ui Pagination
6
+ class Pagination < Base
7
+ def initialize(**attrs)
8
+ @attrs = attrs
9
+ end
10
+
11
+ def view_template(&block)
12
+ nav(**build_attrs, &block)
13
+ end
14
+
15
+ private
16
+
17
+ def build_attrs
18
+ classes = cn("mx-auto flex w-full justify-center", @attrs.delete(:class))
19
+ @attrs.merge(
20
+ data_slot: "pagination",
21
+ role: "navigation",
22
+ aria_label: "pagination",
23
+ class: classes
24
+ )
25
+ end
26
+ end
27
+
28
+ class PaginationContent < Base
29
+ def initialize(**attrs)
30
+ @attrs = attrs
31
+ end
32
+
33
+ def view_template(&block)
34
+ ul(**build_attrs, &block)
35
+ end
36
+
37
+ private
38
+
39
+ def build_attrs
40
+ classes = cn("flex flex-row items-center gap-1", @attrs.delete(:class))
41
+ @attrs.merge(data_slot: "pagination-content", class: classes)
42
+ end
43
+ end
44
+
45
+ class PaginationItem < Base
46
+ def initialize(**attrs)
47
+ @attrs = attrs
48
+ end
49
+
50
+ def view_template(&block)
51
+ li(**build_attrs, &block)
52
+ end
53
+
54
+ private
55
+
56
+ def build_attrs
57
+ @attrs.merge(data_slot: "pagination-item")
58
+ end
59
+ end
60
+
61
+ class PaginationLink < Base
62
+ def initialize(href: "#", active: false, size: :icon, **attrs)
63
+ @href = href
64
+ @active = active
65
+ @size = size
66
+ @attrs = attrs
67
+ end
68
+
69
+ def view_template(&block)
70
+ a(**build_attrs, &block)
71
+ end
72
+
73
+ private
74
+
75
+ def build_attrs
76
+ size_class = case @size
77
+ when :icon then "size-9"
78
+ when :default then "h-9 px-4 py-2"
79
+ else "h-9 px-4 py-2"
80
+ end
81
+
82
+ classes = cn(
83
+ "inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium",
84
+ "transition-all cursor-pointer border border-input bg-background shadow-xs",
85
+ "hover:bg-accent hover:text-accent-foreground",
86
+ size_class,
87
+ @active ? "border-primary bg-primary text-primary-foreground" : "",
88
+ @attrs.delete(:class)
89
+ )
90
+ result = @attrs.merge(
91
+ data_slot: "pagination-link",
92
+ href: @href,
93
+ class: classes
94
+ )
95
+ result[:aria_current] = "page" if @active
96
+ result
97
+ end
98
+ end
99
+
100
+ class PaginationPrevious < Base
101
+ def initialize(href: "#", **attrs)
102
+ @href = href
103
+ @attrs = attrs
104
+ end
105
+
106
+ def view_template(&block)
107
+ a(**build_attrs) do
108
+ svg(
109
+ xmlns: "http://www.w3.org/2000/svg",
110
+ width: "16", height: "16",
111
+ viewbox: "0 0 24 24",
112
+ fill: "none",
113
+ stroke: "currentColor",
114
+ stroke_width: "2",
115
+ stroke_linecap: "round",
116
+ stroke_linejoin: "round",
117
+ class: "size-4"
118
+ ) do |s|
119
+ s.path(d: "m15 18-6-6 6-6")
120
+ end
121
+ span { "Previous" }
122
+ end
123
+ end
124
+
125
+ private
126
+
127
+ def build_attrs
128
+ classes = cn(
129
+ "inline-flex items-center justify-center gap-1 whitespace-nowrap rounded-md text-sm font-medium",
130
+ "h-9 px-4 py-2 cursor-pointer border border-input bg-background shadow-xs",
131
+ "hover:bg-accent hover:text-accent-foreground",
132
+ @attrs.delete(:class)
133
+ )
134
+ @attrs.merge(
135
+ data_slot: "pagination-previous",
136
+ href: @href,
137
+ aria_label: "Go to previous page",
138
+ class: classes
139
+ )
140
+ end
141
+ end
142
+
143
+ class PaginationNext < Base
144
+ def initialize(href: "#", **attrs)
145
+ @href = href
146
+ @attrs = attrs
147
+ end
148
+
149
+ def view_template(&block)
150
+ a(**build_attrs) do
151
+ span { "Next" }
152
+ svg(
153
+ xmlns: "http://www.w3.org/2000/svg",
154
+ width: "16", height: "16",
155
+ viewbox: "0 0 24 24",
156
+ fill: "none",
157
+ stroke: "currentColor",
158
+ stroke_width: "2",
159
+ stroke_linecap: "round",
160
+ stroke_linejoin: "round",
161
+ class: "size-4"
162
+ ) do |s|
163
+ s.path(d: "m9 18 6-6-6-6")
164
+ end
165
+ end
166
+ end
167
+
168
+ private
169
+
170
+ def build_attrs
171
+ classes = cn(
172
+ "inline-flex items-center justify-center gap-1 whitespace-nowrap rounded-md text-sm font-medium",
173
+ "h-9 px-4 py-2 cursor-pointer border border-input bg-background shadow-xs",
174
+ "hover:bg-accent hover:text-accent-foreground",
175
+ @attrs.delete(:class)
176
+ )
177
+ @attrs.merge(
178
+ data_slot: "pagination-next",
179
+ href: @href,
180
+ aria_label: "Go to next page",
181
+ class: classes
182
+ )
183
+ end
184
+ end
185
+
186
+ class PaginationEllipsis < Base
187
+ def initialize(**attrs)
188
+ @attrs = attrs
189
+ end
190
+
191
+ def view_template
192
+ span(**build_attrs) do
193
+ svg(
194
+ xmlns: "http://www.w3.org/2000/svg",
195
+ width: "16", height: "16",
196
+ viewbox: "0 0 24 24",
197
+ fill: "none",
198
+ stroke: "currentColor",
199
+ stroke_width: "2",
200
+ stroke_linecap: "round",
201
+ stroke_linejoin: "round",
202
+ class: "size-4"
203
+ ) do |s|
204
+ s.circle(cx: "12", cy: "12", r: "1")
205
+ s.circle(cx: "19", cy: "12", r: "1")
206
+ s.circle(cx: "5", cy: "12", r: "1")
207
+ end
208
+ span(class: "sr-only") { "More pages" }
209
+ end
210
+ end
211
+
212
+ private
213
+
214
+ def build_attrs
215
+ classes = cn("flex size-9 items-center justify-center", @attrs.delete(:class))
216
+ @attrs.merge(
217
+ data_slot: "pagination-ellipsis",
218
+ aria_hidden: "true",
219
+ class: classes
220
+ )
221
+ end
222
+ end
223
+ end
224
+ end
@@ -0,0 +1,147 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Shadcn
4
+ module UI
5
+ # Port of shadcn/ui Popover
6
+ class Popover < Base
7
+ def initialize(**attrs)
8
+ @attrs = attrs
9
+ end
10
+
11
+ def view_template(&block)
12
+ div(**build_attrs, &block)
13
+ end
14
+
15
+ private
16
+
17
+ def build_attrs
18
+ @attrs.merge(
19
+ data_slot: "popover",
20
+ data_controller: "shadcn--popover",
21
+ data_action: "click@window->shadcn--popover#hide keydown.esc@window->shadcn--popover#hideOnEscape"
22
+ )
23
+ end
24
+ end
25
+
26
+ class PopoverTrigger < Base
27
+ def initialize(**attrs)
28
+ @attrs = attrs
29
+ end
30
+
31
+ def view_template(&block)
32
+ div(**build_attrs, &block)
33
+ end
34
+
35
+ private
36
+
37
+ def build_attrs
38
+ @attrs.merge(
39
+ data_slot: "popover-trigger",
40
+ data_shadcn__popover_target: "trigger",
41
+ data_action: "click->shadcn--popover#toggle",
42
+ role: "button",
43
+ style: "display: inline-block"
44
+ )
45
+ end
46
+ end
47
+
48
+ class PopoverAnchor < Base
49
+ def initialize(**attrs)
50
+ @attrs = attrs
51
+ end
52
+
53
+ def view_template(&block)
54
+ div(**build_attrs, &block)
55
+ end
56
+
57
+ private
58
+
59
+ def build_attrs
60
+ @attrs.merge(
61
+ data_slot: "popover-anchor",
62
+ data_shadcn__popover_target: "anchor"
63
+ )
64
+ end
65
+ end
66
+
67
+ class PopoverContent < Base
68
+ def initialize(**attrs)
69
+ @attrs = attrs
70
+ end
71
+
72
+ def view_template(&block)
73
+ div(**build_attrs, &block)
74
+ end
75
+
76
+ private
77
+
78
+ def build_attrs
79
+ classes = cn(
80
+ "z-50 w-72 rounded-md border bg-popover p-4 text-popover-foreground shadow-md outline-hidden",
81
+ "data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2",
82
+ "data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
83
+ "data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95",
84
+ "data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95",
85
+ @attrs.delete(:class)
86
+ )
87
+ @attrs.merge(
88
+ data_slot: "popover-content",
89
+ data_shadcn__popover_target: "content",
90
+ hidden: true,
91
+ class: classes
92
+ )
93
+ end
94
+ end
95
+
96
+ class PopoverHeader < Base
97
+ def initialize(**attrs)
98
+ @attrs = attrs
99
+ end
100
+
101
+ def view_template(&block)
102
+ div(**build_attrs, &block)
103
+ end
104
+
105
+ private
106
+
107
+ def build_attrs
108
+ classes = cn("flex flex-col gap-1", @attrs.delete(:class))
109
+ @attrs.merge(data_slot: "popover-header", class: classes)
110
+ end
111
+ end
112
+
113
+ class PopoverTitle < Base
114
+ def initialize(**attrs)
115
+ @attrs = attrs
116
+ end
117
+
118
+ def view_template(&block)
119
+ h4(**build_attrs, &block)
120
+ end
121
+
122
+ private
123
+
124
+ def build_attrs
125
+ classes = cn("text-sm font-medium leading-none", @attrs.delete(:class))
126
+ @attrs.merge(data_slot: "popover-title", class: classes)
127
+ end
128
+ end
129
+
130
+ class PopoverDescription < Base
131
+ def initialize(**attrs)
132
+ @attrs = attrs
133
+ end
134
+
135
+ def view_template(&block)
136
+ p(**build_attrs, &block)
137
+ end
138
+
139
+ private
140
+
141
+ def build_attrs
142
+ classes = cn("text-sm text-muted-foreground", @attrs.delete(:class))
143
+ @attrs.merge(data_slot: "popover-description", class: classes)
144
+ end
145
+ end
146
+ end
147
+ end
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Shadcn
4
+ module UI
5
+ # Port of shadcn/ui Progress
6
+ class Progress < Base
7
+ def initialize(value: 0, **attrs)
8
+ @value = value.to_i.clamp(0, 100)
9
+ @attrs = attrs
10
+ end
11
+
12
+ def view_template
13
+ div(**build_attrs) do
14
+ div(
15
+ data_slot: "progress-indicator",
16
+ class: "h-full w-full flex-1 bg-primary transition-all",
17
+ style: "transform: translateX(-#{100 - @value}%)"
18
+ )
19
+ end
20
+ end
21
+
22
+ private
23
+
24
+ def build_attrs
25
+ classes = cn(
26
+ "relative h-2 w-full overflow-hidden rounded-full bg-primary/20",
27
+ @attrs.delete(:class)
28
+ )
29
+ @attrs.merge(
30
+ data_slot: "progress",
31
+ role: "progressbar",
32
+ aria_valuemin: 0,
33
+ aria_valuemax: 100,
34
+ aria_valuenow: @value,
35
+ class: classes
36
+ )
37
+ end
38
+ end
39
+ end
40
+ end