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.
- checksums.yaml +7 -0
- data/README.md +195 -0
- data/app.css +20 -0
- data/css/shadcn-source.css +3 -0
- data/css/shadcn-tailwind.css +160 -0
- data/css/themes/mauve.css +62 -0
- data/css/themes/mist.css +62 -0
- data/css/themes/neutral.css +74 -0
- data/css/themes/olive.css +62 -0
- data/css/themes/stone.css +62 -0
- data/css/themes/taupe.css +62 -0
- data/css/themes/zinc.css +62 -0
- data/js/controllers/accordion_controller.js +135 -0
- data/js/controllers/checkbox_controller.js +52 -0
- data/js/controllers/collapsible_controller.js +85 -0
- data/js/controllers/combobox_controller.js +168 -0
- data/js/controllers/command_controller.js +171 -0
- data/js/controllers/context_menu_controller.js +132 -0
- data/js/controllers/dark_mode_controller.js +106 -0
- data/js/controllers/dialog_controller.js +205 -0
- data/js/controllers/drawer_controller.js +161 -0
- data/js/controllers/dropdown_menu_controller.js +189 -0
- data/js/controllers/hover_card_controller.js +85 -0
- data/js/controllers/index.js +89 -0
- data/js/controllers/menubar_controller.js +171 -0
- data/js/controllers/navigation_menu_controller.js +160 -0
- data/js/controllers/popover_controller.js +151 -0
- data/js/controllers/radio_group_controller.js +78 -0
- data/js/controllers/scroll_area_controller.js +117 -0
- data/js/controllers/select_controller.js +198 -0
- data/js/controllers/sheet_controller.js +130 -0
- data/js/controllers/slider_controller.js +142 -0
- data/js/controllers/switch_controller.js +40 -0
- data/js/controllers/tabs_controller.js +96 -0
- data/js/controllers/toast_controller.js +206 -0
- data/js/controllers/toggle_controller.js +30 -0
- data/js/controllers/toggle_group_controller.js +73 -0
- data/js/controllers/tooltip_controller.js +146 -0
- data/lib/generators/shadcn_phlex/component_generator.rb +79 -0
- data/lib/generators/shadcn_phlex/install_generator.rb +217 -0
- data/lib/shadcn/base.rb +27 -0
- data/lib/shadcn/engine.rb +24 -0
- data/lib/shadcn/kit.rb +1158 -0
- data/lib/shadcn/themes/accent_colors.rb +106 -0
- data/lib/shadcn/themes/base_colors.rb +313 -0
- data/lib/shadcn/ui/accordion.rb +135 -0
- data/lib/shadcn/ui/alert.rb +79 -0
- data/lib/shadcn/ui/alert_dialog.rb +220 -0
- data/lib/shadcn/ui/aspect_ratio.rb +35 -0
- data/lib/shadcn/ui/avatar.rb +134 -0
- data/lib/shadcn/ui/badge.rb +48 -0
- data/lib/shadcn/ui/breadcrumb.rb +180 -0
- data/lib/shadcn/ui/button.rb +63 -0
- data/lib/shadcn/ui/button_group.rb +58 -0
- data/lib/shadcn/ui/card.rb +133 -0
- data/lib/shadcn/ui/checkbox.rb +72 -0
- data/lib/shadcn/ui/collapsible.rb +76 -0
- data/lib/shadcn/ui/combobox.rb +229 -0
- data/lib/shadcn/ui/command.rb +256 -0
- data/lib/shadcn/ui/context_menu.rb +319 -0
- data/lib/shadcn/ui/dialog.rb +226 -0
- data/lib/shadcn/ui/direction.rb +23 -0
- data/lib/shadcn/ui/drawer.rb +217 -0
- data/lib/shadcn/ui/dropdown_menu.rb +384 -0
- data/lib/shadcn/ui/empty.rb +97 -0
- data/lib/shadcn/ui/field.rb +126 -0
- data/lib/shadcn/ui/hover_card.rb +75 -0
- data/lib/shadcn/ui/input.rb +36 -0
- data/lib/shadcn/ui/input_group.rb +32 -0
- data/lib/shadcn/ui/input_otp.rb +112 -0
- data/lib/shadcn/ui/item.rb +115 -0
- data/lib/shadcn/ui/kbd.rb +45 -0
- data/lib/shadcn/ui/label.rb +28 -0
- data/lib/shadcn/ui/menubar.rb +345 -0
- data/lib/shadcn/ui/native_select.rb +31 -0
- data/lib/shadcn/ui/navigation_menu.rb +238 -0
- data/lib/shadcn/ui/pagination.rb +224 -0
- data/lib/shadcn/ui/popover.rb +147 -0
- data/lib/shadcn/ui/progress.rb +40 -0
- data/lib/shadcn/ui/radio_group.rb +92 -0
- data/lib/shadcn/ui/resizable.rb +108 -0
- data/lib/shadcn/ui/scroll_area.rb +75 -0
- data/lib/shadcn/ui/select.rb +235 -0
- data/lib/shadcn/ui/separator.rb +36 -0
- data/lib/shadcn/ui/sheet.rb +231 -0
- data/lib/shadcn/ui/sidebar.rb +420 -0
- data/lib/shadcn/ui/skeleton.rb +23 -0
- data/lib/shadcn/ui/slider.rb +72 -0
- data/lib/shadcn/ui/sonner.rb +177 -0
- data/lib/shadcn/ui/spinner.rb +58 -0
- data/lib/shadcn/ui/switch.rb +75 -0
- data/lib/shadcn/ui/table.rb +154 -0
- data/lib/shadcn/ui/tabs.rb +154 -0
- data/lib/shadcn/ui/text_field.rb +146 -0
- data/lib/shadcn/ui/textarea.rb +32 -0
- data/lib/shadcn/ui/theme_toggle.rb +74 -0
- data/lib/shadcn/ui/toggle.rb +66 -0
- data/lib/shadcn/ui/toggle_group.rb +75 -0
- data/lib/shadcn/ui/tooltip.rb +78 -0
- data/lib/shadcn/ui/typography.rb +217 -0
- data/lib/shadcn/version.rb +5 -0
- data/lib/shadcn-phlex.rb +6 -0
- data/lib/shadcn.rb +80 -0
- data/package.json +14 -0
- data/skills/shadcn-phlex/SKILL.md +190 -0
- data/skills/shadcn-phlex/evals/evals.json +90 -0
- data/skills/shadcn-phlex/references/component-catalog.md +355 -0
- data/skills/shadcn-phlex/rules/composition.md +235 -0
- data/skills/shadcn-phlex/rules/forms.md +151 -0
- data/skills/shadcn-phlex/rules/helpers.md +54 -0
- data/skills/shadcn-phlex/rules/stimulus.md +61 -0
- data/skills/shadcn-phlex/rules/styling.md +177 -0
- metadata +209 -0
|
@@ -0,0 +1,384 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Shadcn
|
|
4
|
+
module UI
|
|
5
|
+
# Port of shadcn/ui DropdownMenu
|
|
6
|
+
# Requires JS for open/close behavior
|
|
7
|
+
class DropdownMenu < Base
|
|
8
|
+
def initialize(**attrs)
|
|
9
|
+
@attrs = attrs
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def view_template(&block)
|
|
13
|
+
div(**build_attrs, &block)
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
private
|
|
17
|
+
|
|
18
|
+
def build_attrs
|
|
19
|
+
@attrs.merge(
|
|
20
|
+
data_slot: "dropdown-menu",
|
|
21
|
+
data_controller: "shadcn--dropdown-menu",
|
|
22
|
+
data_action: "click@window->shadcn--dropdown-menu#hide keydown.esc@window->shadcn--dropdown-menu#hideOnEscape keydown->shadcn--dropdown-menu#navigate"
|
|
23
|
+
)
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
class DropdownMenuTrigger < Base
|
|
28
|
+
def initialize(**attrs)
|
|
29
|
+
@attrs = attrs
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def view_template(&block)
|
|
33
|
+
div(**build_attrs, &block)
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
private
|
|
37
|
+
|
|
38
|
+
def build_attrs
|
|
39
|
+
@attrs.merge(
|
|
40
|
+
data_slot: "dropdown-menu-trigger",
|
|
41
|
+
data_shadcn__dropdown_menu_target: "trigger",
|
|
42
|
+
data_action: "click->shadcn--dropdown-menu#toggle",
|
|
43
|
+
role: "button",
|
|
44
|
+
style: "display: inline-block"
|
|
45
|
+
)
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
class DropdownMenuContent < Base
|
|
50
|
+
def initialize(**attrs)
|
|
51
|
+
@attrs = attrs
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def view_template(&block)
|
|
55
|
+
div(**build_attrs, &block)
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
private
|
|
59
|
+
|
|
60
|
+
def build_attrs
|
|
61
|
+
classes = cn(
|
|
62
|
+
"z-50 max-h-[var(--radix-dropdown-menu-content-available-height)] min-w-[8rem]",
|
|
63
|
+
"overflow-x-hidden overflow-y-auto rounded-md border bg-popover p-1 text-popover-foreground shadow-md",
|
|
64
|
+
"data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2",
|
|
65
|
+
"data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
|
|
66
|
+
"data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95",
|
|
67
|
+
"data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95",
|
|
68
|
+
@attrs.delete(:class)
|
|
69
|
+
)
|
|
70
|
+
@attrs.merge(
|
|
71
|
+
data_slot: "dropdown-menu-content",
|
|
72
|
+
data_shadcn__dropdown_menu_target: "content",
|
|
73
|
+
role: "menu",
|
|
74
|
+
hidden: true,
|
|
75
|
+
class: classes
|
|
76
|
+
)
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
class DropdownMenuGroup < Base
|
|
81
|
+
def initialize(**attrs)
|
|
82
|
+
@attrs = attrs
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
def view_template(&block)
|
|
86
|
+
div(**build_attrs, &block)
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
private
|
|
90
|
+
|
|
91
|
+
def build_attrs
|
|
92
|
+
@attrs.merge(data_slot: "dropdown-menu-group", role: "group")
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
class DropdownMenuItem < Base
|
|
97
|
+
def initialize(inset: false, variant: :default, **attrs)
|
|
98
|
+
@inset = inset
|
|
99
|
+
@variant = variant
|
|
100
|
+
@attrs = attrs
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
def view_template(&block)
|
|
104
|
+
div(**build_attrs, &block)
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
private
|
|
108
|
+
|
|
109
|
+
def build_attrs
|
|
110
|
+
classes = cn(
|
|
111
|
+
"relative flex cursor-default items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-hidden select-none",
|
|
112
|
+
"focus:bg-accent focus:text-accent-foreground",
|
|
113
|
+
"data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
|
|
114
|
+
"data-[inset]:pl-8",
|
|
115
|
+
"data-[variant=destructive]:text-destructive data-[variant=destructive]:focus:bg-destructive/10 data-[variant=destructive]:focus:text-destructive",
|
|
116
|
+
"dark:data-[variant=destructive]:focus:bg-destructive/20",
|
|
117
|
+
"[&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
|
|
118
|
+
"[&_svg:not([class*='text-'])]:text-muted-foreground",
|
|
119
|
+
@attrs.delete(:class)
|
|
120
|
+
)
|
|
121
|
+
result = @attrs.merge(
|
|
122
|
+
data_slot: "dropdown-menu-item",
|
|
123
|
+
data_shadcn__dropdown_menu_target: "item",
|
|
124
|
+
data_action: "click->shadcn--dropdown-menu#selectItem",
|
|
125
|
+
data_variant: @variant,
|
|
126
|
+
role: "menuitem",
|
|
127
|
+
tabindex: "-1",
|
|
128
|
+
class: classes
|
|
129
|
+
)
|
|
130
|
+
result[:data_inset] = true if @inset
|
|
131
|
+
result
|
|
132
|
+
end
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
class DropdownMenuCheckboxItem < Base
|
|
136
|
+
def initialize(checked: false, **attrs)
|
|
137
|
+
@checked = checked
|
|
138
|
+
@attrs = attrs
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
def view_template(&block)
|
|
142
|
+
div(**build_attrs) do
|
|
143
|
+
span(class: "pointer-events-none absolute left-2 flex size-3.5 items-center justify-center") do
|
|
144
|
+
if @checked
|
|
145
|
+
svg(
|
|
146
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
147
|
+
width: "16", height: "16",
|
|
148
|
+
viewbox: "0 0 24 24",
|
|
149
|
+
fill: "none",
|
|
150
|
+
stroke: "currentColor",
|
|
151
|
+
stroke_width: "2",
|
|
152
|
+
stroke_linecap: "round",
|
|
153
|
+
stroke_linejoin: "round",
|
|
154
|
+
class: "size-4"
|
|
155
|
+
) do |s|
|
|
156
|
+
s.path(d: "M20 6 9 17l-5-5")
|
|
157
|
+
end
|
|
158
|
+
end
|
|
159
|
+
end
|
|
160
|
+
yield if block_given?
|
|
161
|
+
end
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
private
|
|
165
|
+
|
|
166
|
+
def build_attrs
|
|
167
|
+
classes = cn(
|
|
168
|
+
"relative flex cursor-default items-center gap-2 rounded-sm py-1.5 pr-2 pl-8 text-sm",
|
|
169
|
+
"outline-hidden select-none",
|
|
170
|
+
"focus:bg-accent focus:text-accent-foreground",
|
|
171
|
+
"data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
|
|
172
|
+
"[&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
|
|
173
|
+
@attrs.delete(:class)
|
|
174
|
+
)
|
|
175
|
+
@attrs.merge(
|
|
176
|
+
data_slot: "dropdown-menu-checkbox-item",
|
|
177
|
+
data_shadcn__dropdown_menu_target: "item",
|
|
178
|
+
data_action: "click->shadcn--dropdown-menu#selectItem",
|
|
179
|
+
role: "menuitemcheckbox",
|
|
180
|
+
aria_checked: @checked,
|
|
181
|
+
tabindex: "-1",
|
|
182
|
+
class: classes
|
|
183
|
+
)
|
|
184
|
+
end
|
|
185
|
+
end
|
|
186
|
+
|
|
187
|
+
class DropdownMenuRadioGroup < Base
|
|
188
|
+
def initialize(**attrs)
|
|
189
|
+
@attrs = attrs
|
|
190
|
+
end
|
|
191
|
+
|
|
192
|
+
def view_template(&block)
|
|
193
|
+
div(**build_attrs, &block)
|
|
194
|
+
end
|
|
195
|
+
|
|
196
|
+
private
|
|
197
|
+
|
|
198
|
+
def build_attrs
|
|
199
|
+
@attrs.merge(data_slot: "dropdown-menu-radio-group", role: "group")
|
|
200
|
+
end
|
|
201
|
+
end
|
|
202
|
+
|
|
203
|
+
class DropdownMenuRadioItem < Base
|
|
204
|
+
def initialize(checked: false, **attrs)
|
|
205
|
+
@checked = checked
|
|
206
|
+
@attrs = attrs
|
|
207
|
+
end
|
|
208
|
+
|
|
209
|
+
def view_template(&block)
|
|
210
|
+
div(**build_attrs) do
|
|
211
|
+
span(class: "pointer-events-none absolute left-2 flex size-3.5 items-center justify-center") do
|
|
212
|
+
if @checked
|
|
213
|
+
svg(
|
|
214
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
215
|
+
width: "8", height: "8",
|
|
216
|
+
viewbox: "0 0 24 24",
|
|
217
|
+
fill: "currentColor",
|
|
218
|
+
class: "size-2"
|
|
219
|
+
) do |s|
|
|
220
|
+
s.circle(cx: "12", cy: "12", r: "10")
|
|
221
|
+
end
|
|
222
|
+
end
|
|
223
|
+
end
|
|
224
|
+
yield if block_given?
|
|
225
|
+
end
|
|
226
|
+
end
|
|
227
|
+
|
|
228
|
+
private
|
|
229
|
+
|
|
230
|
+
def build_attrs
|
|
231
|
+
classes = cn(
|
|
232
|
+
"relative flex cursor-default items-center gap-2 rounded-sm py-1.5 pr-2 pl-8 text-sm",
|
|
233
|
+
"outline-hidden select-none",
|
|
234
|
+
"focus:bg-accent focus:text-accent-foreground",
|
|
235
|
+
"data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
|
|
236
|
+
"[&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
|
|
237
|
+
@attrs.delete(:class)
|
|
238
|
+
)
|
|
239
|
+
@attrs.merge(
|
|
240
|
+
data_slot: "dropdown-menu-radio-item",
|
|
241
|
+
data_shadcn__dropdown_menu_target: "item",
|
|
242
|
+
data_action: "click->shadcn--dropdown-menu#selectItem",
|
|
243
|
+
role: "menuitemradio",
|
|
244
|
+
aria_checked: @checked,
|
|
245
|
+
tabindex: "-1",
|
|
246
|
+
class: classes
|
|
247
|
+
)
|
|
248
|
+
end
|
|
249
|
+
end
|
|
250
|
+
|
|
251
|
+
class DropdownMenuLabel < Base
|
|
252
|
+
def initialize(inset: false, **attrs)
|
|
253
|
+
@inset = inset
|
|
254
|
+
@attrs = attrs
|
|
255
|
+
end
|
|
256
|
+
|
|
257
|
+
def view_template(&block)
|
|
258
|
+
div(**build_attrs, &block)
|
|
259
|
+
end
|
|
260
|
+
|
|
261
|
+
private
|
|
262
|
+
|
|
263
|
+
def build_attrs
|
|
264
|
+
classes = cn(
|
|
265
|
+
"px-2 py-1.5 text-sm font-medium data-[inset]:pl-8",
|
|
266
|
+
@attrs.delete(:class)
|
|
267
|
+
)
|
|
268
|
+
result = @attrs.merge(data_slot: "dropdown-menu-label", class: classes)
|
|
269
|
+
result[:data_inset] = true if @inset
|
|
270
|
+
result
|
|
271
|
+
end
|
|
272
|
+
end
|
|
273
|
+
|
|
274
|
+
class DropdownMenuSeparator < Base
|
|
275
|
+
def initialize(**attrs)
|
|
276
|
+
@attrs = attrs
|
|
277
|
+
end
|
|
278
|
+
|
|
279
|
+
def view_template
|
|
280
|
+
div(**build_attrs)
|
|
281
|
+
end
|
|
282
|
+
|
|
283
|
+
private
|
|
284
|
+
|
|
285
|
+
def build_attrs
|
|
286
|
+
classes = cn("-mx-1 my-1 h-px bg-border", @attrs.delete(:class))
|
|
287
|
+
@attrs.merge(data_slot: "dropdown-menu-separator", role: "separator", class: classes)
|
|
288
|
+
end
|
|
289
|
+
end
|
|
290
|
+
|
|
291
|
+
class DropdownMenuShortcut < Base
|
|
292
|
+
def initialize(**attrs)
|
|
293
|
+
@attrs = attrs
|
|
294
|
+
end
|
|
295
|
+
|
|
296
|
+
def view_template(&block)
|
|
297
|
+
span(**build_attrs, &block)
|
|
298
|
+
end
|
|
299
|
+
|
|
300
|
+
private
|
|
301
|
+
|
|
302
|
+
def build_attrs
|
|
303
|
+
classes = cn("ml-auto text-xs tracking-widest text-muted-foreground", @attrs.delete(:class))
|
|
304
|
+
@attrs.merge(data_slot: "dropdown-menu-shortcut", class: classes)
|
|
305
|
+
end
|
|
306
|
+
end
|
|
307
|
+
|
|
308
|
+
class DropdownMenuSubTrigger < Base
|
|
309
|
+
def initialize(inset: false, **attrs)
|
|
310
|
+
@inset = inset
|
|
311
|
+
@attrs = attrs
|
|
312
|
+
end
|
|
313
|
+
|
|
314
|
+
def view_template(&block)
|
|
315
|
+
div(**build_attrs) do
|
|
316
|
+
yield if block_given?
|
|
317
|
+
# ChevronRight icon
|
|
318
|
+
svg(
|
|
319
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
320
|
+
width: "16", height: "16",
|
|
321
|
+
viewbox: "0 0 24 24",
|
|
322
|
+
fill: "none",
|
|
323
|
+
stroke: "currentColor",
|
|
324
|
+
stroke_width: "2",
|
|
325
|
+
stroke_linecap: "round",
|
|
326
|
+
stroke_linejoin: "round",
|
|
327
|
+
class: "ml-auto size-4"
|
|
328
|
+
) do |s|
|
|
329
|
+
s.path(d: "m9 18 6-6-6-6")
|
|
330
|
+
end
|
|
331
|
+
end
|
|
332
|
+
end
|
|
333
|
+
|
|
334
|
+
private
|
|
335
|
+
|
|
336
|
+
def build_attrs
|
|
337
|
+
classes = cn(
|
|
338
|
+
"flex cursor-default items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-hidden select-none",
|
|
339
|
+
"focus:bg-accent focus:text-accent-foreground",
|
|
340
|
+
"data-[inset]:pl-8 data-[state=open]:bg-accent data-[state=open]:text-accent-foreground",
|
|
341
|
+
"[&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
|
|
342
|
+
"[&_svg:not([class*='text-'])]:text-muted-foreground",
|
|
343
|
+
@attrs.delete(:class)
|
|
344
|
+
)
|
|
345
|
+
result = @attrs.merge(
|
|
346
|
+
data_slot: "dropdown-menu-sub-trigger",
|
|
347
|
+
data_shadcn__dropdown_menu_target: "subTrigger",
|
|
348
|
+
class: classes
|
|
349
|
+
)
|
|
350
|
+
result[:data_inset] = true if @inset
|
|
351
|
+
result
|
|
352
|
+
end
|
|
353
|
+
end
|
|
354
|
+
|
|
355
|
+
class DropdownMenuSubContent < Base
|
|
356
|
+
def initialize(**attrs)
|
|
357
|
+
@attrs = attrs
|
|
358
|
+
end
|
|
359
|
+
|
|
360
|
+
def view_template(&block)
|
|
361
|
+
div(**build_attrs, &block)
|
|
362
|
+
end
|
|
363
|
+
|
|
364
|
+
private
|
|
365
|
+
|
|
366
|
+
def build_attrs
|
|
367
|
+
classes = cn(
|
|
368
|
+
"z-50 min-w-[8rem] overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-lg",
|
|
369
|
+
"data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2",
|
|
370
|
+
"data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
|
|
371
|
+
"data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95",
|
|
372
|
+
"data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95",
|
|
373
|
+
@attrs.delete(:class)
|
|
374
|
+
)
|
|
375
|
+
@attrs.merge(
|
|
376
|
+
data_slot: "dropdown-menu-sub-content",
|
|
377
|
+
data_shadcn__dropdown_menu_target: "subContent",
|
|
378
|
+
hidden: true,
|
|
379
|
+
class: classes
|
|
380
|
+
)
|
|
381
|
+
end
|
|
382
|
+
end
|
|
383
|
+
end
|
|
384
|
+
end
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Shadcn
|
|
4
|
+
module UI
|
|
5
|
+
# Port of shadcn/ui Empty state
|
|
6
|
+
class Empty < 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
|
+
classes = cn(
|
|
19
|
+
"flex min-h-[200px] flex-col items-center justify-center gap-4 rounded-lg border border-dashed p-8 text-center",
|
|
20
|
+
@attrs.delete(:class)
|
|
21
|
+
)
|
|
22
|
+
@attrs.merge(data_slot: "empty", class: classes)
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
class EmptyMedia < 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
|
+
classes = cn(
|
|
39
|
+
"flex items-center justify-center [&_svg]:size-12 [&_svg]:text-muted-foreground",
|
|
40
|
+
@attrs.delete(:class)
|
|
41
|
+
)
|
|
42
|
+
@attrs.merge(data_slot: "empty-media", class: classes)
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
class EmptyTitle < Base
|
|
47
|
+
def initialize(**attrs)
|
|
48
|
+
@attrs = attrs
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def view_template(&block)
|
|
52
|
+
h3(**build_attrs, &block)
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
private
|
|
56
|
+
|
|
57
|
+
def build_attrs
|
|
58
|
+
classes = cn("text-lg font-semibold", @attrs.delete(:class))
|
|
59
|
+
@attrs.merge(data_slot: "empty-title", class: classes)
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
class EmptyDescription < Base
|
|
64
|
+
def initialize(**attrs)
|
|
65
|
+
@attrs = attrs
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def view_template(&block)
|
|
69
|
+
p(**build_attrs, &block)
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
private
|
|
73
|
+
|
|
74
|
+
def build_attrs
|
|
75
|
+
classes = cn("text-sm text-muted-foreground max-w-sm", @attrs.delete(:class))
|
|
76
|
+
@attrs.merge(data_slot: "empty-description", class: classes)
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
class EmptyActions < Base
|
|
81
|
+
def initialize(**attrs)
|
|
82
|
+
@attrs = attrs
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
def view_template(&block)
|
|
86
|
+
div(**build_attrs, &block)
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
private
|
|
90
|
+
|
|
91
|
+
def build_attrs
|
|
92
|
+
classes = cn("flex items-center gap-2", @attrs.delete(:class))
|
|
93
|
+
@attrs.merge(data_slot: "empty-actions", class: classes)
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
end
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Shadcn
|
|
4
|
+
module UI
|
|
5
|
+
# Port of shadcn/ui Field
|
|
6
|
+
# Form field composition: label, control, description, error message
|
|
7
|
+
class Field < Base
|
|
8
|
+
def initialize(disabled: false, **attrs)
|
|
9
|
+
@disabled = disabled
|
|
10
|
+
@attrs = attrs
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def view_template(&block)
|
|
14
|
+
div(**build_attrs, &block)
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
private
|
|
18
|
+
|
|
19
|
+
def build_attrs
|
|
20
|
+
classes = cn(
|
|
21
|
+
"group/field grid gap-2",
|
|
22
|
+
@attrs.delete(:class)
|
|
23
|
+
)
|
|
24
|
+
@attrs.merge(
|
|
25
|
+
data_slot: "field",
|
|
26
|
+
data_disabled: @disabled || nil,
|
|
27
|
+
class: classes
|
|
28
|
+
)
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
class FieldControl < Base
|
|
33
|
+
def initialize(**attrs)
|
|
34
|
+
@attrs = attrs
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def view_template(&block)
|
|
38
|
+
div(**build_attrs, &block)
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
private
|
|
42
|
+
|
|
43
|
+
def build_attrs
|
|
44
|
+
@attrs.merge(data_slot: "field-control")
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
class FieldDescription < Base
|
|
49
|
+
def initialize(**attrs)
|
|
50
|
+
@attrs = attrs
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def view_template(&block)
|
|
54
|
+
p(**build_attrs, &block)
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
private
|
|
58
|
+
|
|
59
|
+
def build_attrs
|
|
60
|
+
classes = cn("text-muted-foreground text-sm", @attrs.delete(:class))
|
|
61
|
+
@attrs.merge(data_slot: "field-description", class: classes)
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
class FieldError < Base
|
|
66
|
+
def initialize(**attrs)
|
|
67
|
+
@attrs = attrs
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def view_template(&block)
|
|
71
|
+
p(**build_attrs, &block)
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
private
|
|
75
|
+
|
|
76
|
+
def build_attrs
|
|
77
|
+
classes = cn("text-destructive text-sm font-medium", @attrs.delete(:class))
|
|
78
|
+
@attrs.merge(
|
|
79
|
+
data_slot: "field-error",
|
|
80
|
+
role: "alert",
|
|
81
|
+
class: classes
|
|
82
|
+
)
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
class Fieldset < Base
|
|
87
|
+
def initialize(disabled: false, **attrs)
|
|
88
|
+
@disabled = disabled
|
|
89
|
+
@attrs = attrs
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
def view_template(&block)
|
|
93
|
+
fieldset(**build_attrs, &block)
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
private
|
|
97
|
+
|
|
98
|
+
def build_attrs
|
|
99
|
+
classes = cn(
|
|
100
|
+
"grid gap-6 rounded-lg border p-4 disabled:opacity-50",
|
|
101
|
+
@attrs.delete(:class)
|
|
102
|
+
)
|
|
103
|
+
result = @attrs.merge(data_slot: "fieldset", class: classes)
|
|
104
|
+
result[:disabled] = true if @disabled
|
|
105
|
+
result
|
|
106
|
+
end
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
class FieldsetLegend < Base
|
|
110
|
+
def initialize(**attrs)
|
|
111
|
+
@attrs = attrs
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
def view_template(&block)
|
|
115
|
+
legend(**build_attrs, &block)
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
private
|
|
119
|
+
|
|
120
|
+
def build_attrs
|
|
121
|
+
classes = cn("text-sm font-medium leading-none", @attrs.delete(:class))
|
|
122
|
+
@attrs.merge(data_slot: "fieldset-legend", class: classes)
|
|
123
|
+
end
|
|
124
|
+
end
|
|
125
|
+
end
|
|
126
|
+
end
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Shadcn
|
|
4
|
+
module UI
|
|
5
|
+
# Port of shadcn/ui HoverCard
|
|
6
|
+
class HoverCard < 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: "hover-card",
|
|
20
|
+
data_controller: "shadcn--hover-card"
|
|
21
|
+
)
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
class HoverCardTrigger < Base
|
|
26
|
+
def initialize(**attrs)
|
|
27
|
+
@attrs = attrs
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def view_template(&block)
|
|
31
|
+
a(**build_attrs, &block)
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
private
|
|
35
|
+
|
|
36
|
+
def build_attrs
|
|
37
|
+
@attrs.merge(
|
|
38
|
+
data_slot: "hover-card-trigger",
|
|
39
|
+
data_shadcn__hover_card_target: "trigger",
|
|
40
|
+
data_action: "mouseenter->shadcn--hover-card#triggerEnter mouseleave->shadcn--hover-card#triggerLeave"
|
|
41
|
+
)
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
class HoverCardContent < Base
|
|
46
|
+
def initialize(**attrs)
|
|
47
|
+
@attrs = attrs
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def view_template(&block)
|
|
51
|
+
div(**build_attrs, &block)
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
private
|
|
55
|
+
|
|
56
|
+
def build_attrs
|
|
57
|
+
classes = cn(
|
|
58
|
+
"z-50 w-64 rounded-md border bg-popover p-4 text-popover-foreground shadow-md outline-hidden",
|
|
59
|
+
"data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2",
|
|
60
|
+
"data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
|
|
61
|
+
"data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95",
|
|
62
|
+
"data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95",
|
|
63
|
+
@attrs.delete(:class)
|
|
64
|
+
)
|
|
65
|
+
@attrs.merge(
|
|
66
|
+
data_slot: "hover-card-content",
|
|
67
|
+
data_shadcn__hover_card_target: "content",
|
|
68
|
+
data_action: "mouseenter->shadcn--hover-card#contentEnter mouseleave->shadcn--hover-card#contentLeave",
|
|
69
|
+
hidden: true,
|
|
70
|
+
class: classes
|
|
71
|
+
)
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
end
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Shadcn
|
|
4
|
+
module UI
|
|
5
|
+
# Port of shadcn/ui Input
|
|
6
|
+
class Input < Base
|
|
7
|
+
def initialize(type: "text", **attrs)
|
|
8
|
+
@type = type
|
|
9
|
+
@attrs = attrs
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def view_template
|
|
13
|
+
input(**build_attrs)
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
private
|
|
17
|
+
|
|
18
|
+
def build_attrs
|
|
19
|
+
classes = cn(
|
|
20
|
+
"h-9 w-full min-w-0 rounded-md border border-input bg-transparent px-3 py-1 text-base shadow-xs",
|
|
21
|
+
"transition-[color,box-shadow] outline-none",
|
|
22
|
+
"selection:bg-primary selection:text-primary-foreground",
|
|
23
|
+
"file:inline-flex file:h-7 file:border-0 file:bg-transparent file:text-sm file:font-medium file:text-foreground",
|
|
24
|
+
"placeholder:text-muted-foreground",
|
|
25
|
+
"focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50",
|
|
26
|
+
"disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50",
|
|
27
|
+
"md:text-sm",
|
|
28
|
+
"dark:bg-input/30",
|
|
29
|
+
"aria-invalid:border-destructive aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40",
|
|
30
|
+
@attrs.delete(:class)
|
|
31
|
+
)
|
|
32
|
+
@attrs.merge(data_slot: "input", type: @type, class: classes)
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|