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,220 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Shadcn
|
|
4
|
+
module UI
|
|
5
|
+
# Port of shadcn/ui AlertDialog
|
|
6
|
+
class AlertDialog < 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: "alert-dialog",
|
|
20
|
+
data_controller: "shadcn--dialog",
|
|
21
|
+
data_shadcn__dialog_close_on_overlay_value: false
|
|
22
|
+
)
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
class AlertDialogTrigger < 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: "alert-dialog-trigger",
|
|
40
|
+
data_shadcn__dialog_target: "trigger",
|
|
41
|
+
data_action: "click->shadcn--dialog#show",
|
|
42
|
+
role: "button",
|
|
43
|
+
style: "display: inline-block"
|
|
44
|
+
)
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
class AlertDialogOverlay < Base
|
|
49
|
+
def initialize(**attrs)
|
|
50
|
+
@attrs = attrs
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def view_template
|
|
54
|
+
div(**build_attrs)
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
private
|
|
58
|
+
|
|
59
|
+
def build_attrs
|
|
60
|
+
classes = cn(
|
|
61
|
+
"fixed inset-0 z-50 bg-black/50",
|
|
62
|
+
"data-[state=closed]:animate-out data-[state=closed]:fade-out-0",
|
|
63
|
+
"data-[state=open]:animate-in data-[state=open]:fade-in-0",
|
|
64
|
+
@attrs.delete(:class)
|
|
65
|
+
)
|
|
66
|
+
@attrs.merge(
|
|
67
|
+
data_slot: "alert-dialog-overlay",
|
|
68
|
+
data_shadcn__dialog_target: "overlay",
|
|
69
|
+
hidden: true,
|
|
70
|
+
class: classes
|
|
71
|
+
)
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
class AlertDialogContent < Base
|
|
76
|
+
def initialize(size: :default, **attrs)
|
|
77
|
+
@size = size
|
|
78
|
+
@attrs = attrs
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
def view_template(&block)
|
|
82
|
+
render AlertDialogOverlay.new
|
|
83
|
+
|
|
84
|
+
div(**build_attrs, &block)
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
private
|
|
88
|
+
|
|
89
|
+
def build_attrs
|
|
90
|
+
classes = cn(
|
|
91
|
+
"group/alert-dialog-content fixed top-[50%] left-[50%] z-50 grid w-full max-w-[calc(100%-2rem)]",
|
|
92
|
+
"translate-x-[-50%] translate-y-[-50%] gap-4 rounded-lg border bg-background p-6 shadow-lg duration-200",
|
|
93
|
+
"data-[size=sm]:max-w-xs",
|
|
94
|
+
"data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95",
|
|
95
|
+
"data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95",
|
|
96
|
+
"data-[size=default]:sm:max-w-lg",
|
|
97
|
+
@attrs.delete(:class)
|
|
98
|
+
)
|
|
99
|
+
@attrs.merge(
|
|
100
|
+
data_slot: "alert-dialog-content",
|
|
101
|
+
data_shadcn__dialog_target: "content",
|
|
102
|
+
data_size: @size,
|
|
103
|
+
role: "alertdialog",
|
|
104
|
+
hidden: true,
|
|
105
|
+
class: classes
|
|
106
|
+
)
|
|
107
|
+
end
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
class AlertDialogHeader < Base
|
|
111
|
+
def initialize(**attrs)
|
|
112
|
+
@attrs = attrs
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
def view_template(&block)
|
|
116
|
+
div(**build_attrs, &block)
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
private
|
|
120
|
+
|
|
121
|
+
def build_attrs
|
|
122
|
+
classes = cn(
|
|
123
|
+
"grid grid-rows-[auto_1fr] place-items-center gap-1.5 text-center",
|
|
124
|
+
"sm:group-data-[size=default]/alert-dialog-content:place-items-start",
|
|
125
|
+
"sm:group-data-[size=default]/alert-dialog-content:text-left",
|
|
126
|
+
@attrs.delete(:class)
|
|
127
|
+
)
|
|
128
|
+
@attrs.merge(data_slot: "alert-dialog-header", class: classes)
|
|
129
|
+
end
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
class AlertDialogFooter < Base
|
|
133
|
+
def initialize(**attrs)
|
|
134
|
+
@attrs = attrs
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
def view_template(&block)
|
|
138
|
+
div(**build_attrs, &block)
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
private
|
|
142
|
+
|
|
143
|
+
def build_attrs
|
|
144
|
+
classes = cn(
|
|
145
|
+
"flex flex-col-reverse gap-2",
|
|
146
|
+
"group-data-[size=sm]/alert-dialog-content:grid group-data-[size=sm]/alert-dialog-content:grid-cols-2",
|
|
147
|
+
"sm:flex-row sm:justify-end",
|
|
148
|
+
@attrs.delete(:class)
|
|
149
|
+
)
|
|
150
|
+
@attrs.merge(data_slot: "alert-dialog-footer", class: classes)
|
|
151
|
+
end
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
class AlertDialogTitle < Base
|
|
155
|
+
def initialize(**attrs)
|
|
156
|
+
@attrs = attrs
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
def view_template(&block)
|
|
160
|
+
h2(**build_attrs, &block)
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
private
|
|
164
|
+
|
|
165
|
+
def build_attrs
|
|
166
|
+
classes = cn("text-lg font-semibold", @attrs.delete(:class))
|
|
167
|
+
@attrs.merge(
|
|
168
|
+
data_slot: "alert-dialog-title",
|
|
169
|
+
data_shadcn__dialog_target: "title",
|
|
170
|
+
class: classes
|
|
171
|
+
)
|
|
172
|
+
end
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
class AlertDialogDescription < Base
|
|
176
|
+
def initialize(**attrs)
|
|
177
|
+
@attrs = attrs
|
|
178
|
+
end
|
|
179
|
+
|
|
180
|
+
def view_template(&block)
|
|
181
|
+
p(**build_attrs, &block)
|
|
182
|
+
end
|
|
183
|
+
|
|
184
|
+
private
|
|
185
|
+
|
|
186
|
+
def build_attrs
|
|
187
|
+
classes = cn("text-sm text-muted-foreground", @attrs.delete(:class))
|
|
188
|
+
@attrs.merge(
|
|
189
|
+
data_slot: "alert-dialog-description",
|
|
190
|
+
data_shadcn__dialog_target: "description",
|
|
191
|
+
class: classes
|
|
192
|
+
)
|
|
193
|
+
end
|
|
194
|
+
end
|
|
195
|
+
|
|
196
|
+
class AlertDialogAction < Base
|
|
197
|
+
def initialize(variant: :default, size: :default, **attrs)
|
|
198
|
+
@variant = variant
|
|
199
|
+
@size = size
|
|
200
|
+
@attrs = attrs
|
|
201
|
+
end
|
|
202
|
+
|
|
203
|
+
def view_template(&block)
|
|
204
|
+
render Button.new(variant: @variant, size: @size, data_slot: "alert-dialog-action", **@attrs, &block)
|
|
205
|
+
end
|
|
206
|
+
end
|
|
207
|
+
|
|
208
|
+
class AlertDialogCancel < Base
|
|
209
|
+
def initialize(variant: :outline, size: :default, **attrs)
|
|
210
|
+
@variant = variant
|
|
211
|
+
@size = size
|
|
212
|
+
@attrs = attrs
|
|
213
|
+
end
|
|
214
|
+
|
|
215
|
+
def view_template(&block)
|
|
216
|
+
render Button.new(variant: @variant, size: @size, data_slot: "alert-dialog-cancel", data_action: "click->shadcn--dialog#hide", **@attrs, &block)
|
|
217
|
+
end
|
|
218
|
+
end
|
|
219
|
+
end
|
|
220
|
+
end
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Shadcn
|
|
4
|
+
module UI
|
|
5
|
+
# Port of shadcn/ui AspectRatio
|
|
6
|
+
# Uses the padding-bottom trick with absolute-positioned children
|
|
7
|
+
class AspectRatio < Base
|
|
8
|
+
def initialize(ratio: 1.0, **attrs)
|
|
9
|
+
@ratio = ratio
|
|
10
|
+
@attrs = attrs
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def view_template(&block)
|
|
14
|
+
div(**build_attrs) do
|
|
15
|
+
div(style: "position: absolute; inset: 0;", &block)
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
private
|
|
20
|
+
|
|
21
|
+
def build_attrs
|
|
22
|
+
classes = cn("relative w-full", @attrs.delete(:class))
|
|
23
|
+
style = "position: relative; padding-bottom: #{(1.0 / @ratio * 100).round(4)}%"
|
|
24
|
+
existing_style = @attrs.delete(:style)
|
|
25
|
+
style = "#{existing_style}; #{style}" if existing_style
|
|
26
|
+
|
|
27
|
+
@attrs.merge(
|
|
28
|
+
data_slot: "aspect-ratio",
|
|
29
|
+
class: classes,
|
|
30
|
+
style: style
|
|
31
|
+
)
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Shadcn
|
|
4
|
+
module UI
|
|
5
|
+
# Port of shadcn/ui Avatar
|
|
6
|
+
# Sizes: default, sm, lg
|
|
7
|
+
class Avatar < Base
|
|
8
|
+
def initialize(size: :default, **attrs)
|
|
9
|
+
@size = size
|
|
10
|
+
@attrs = attrs
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def view_template(&block)
|
|
14
|
+
span(**build_attrs, &block)
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
private
|
|
18
|
+
|
|
19
|
+
def build_attrs
|
|
20
|
+
classes = cn(
|
|
21
|
+
"group/avatar relative flex size-8 shrink-0 overflow-hidden rounded-full select-none",
|
|
22
|
+
"data-[size=lg]:size-10 data-[size=sm]:size-6",
|
|
23
|
+
@attrs.delete(:class)
|
|
24
|
+
)
|
|
25
|
+
@attrs.merge(data_slot: "avatar", data_size: @size, class: classes)
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
class AvatarImage < Base
|
|
30
|
+
def initialize(src:, alt: "", **attrs)
|
|
31
|
+
@src = src
|
|
32
|
+
@alt = alt
|
|
33
|
+
@attrs = attrs
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def view_template
|
|
37
|
+
img(**build_attrs)
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
private
|
|
41
|
+
|
|
42
|
+
def build_attrs
|
|
43
|
+
classes = cn("aspect-square size-full", @attrs.delete(:class))
|
|
44
|
+
@attrs.merge(data_slot: "avatar-image", src: @src, alt: @alt, class: classes)
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
class AvatarFallback < Base
|
|
49
|
+
def initialize(**attrs)
|
|
50
|
+
@attrs = attrs
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def view_template(&block)
|
|
54
|
+
span(**build_attrs, &block)
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
private
|
|
58
|
+
|
|
59
|
+
def build_attrs
|
|
60
|
+
classes = cn(
|
|
61
|
+
"flex size-full items-center justify-center rounded-full bg-muted text-sm text-muted-foreground",
|
|
62
|
+
"group-data-[size=sm]/avatar:text-xs",
|
|
63
|
+
@attrs.delete(:class)
|
|
64
|
+
)
|
|
65
|
+
@attrs.merge(data_slot: "avatar-fallback", class: classes)
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
class AvatarBadge < Base
|
|
70
|
+
def initialize(**attrs)
|
|
71
|
+
@attrs = attrs
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def view_template(&block)
|
|
75
|
+
span(**build_attrs, &block)
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
private
|
|
79
|
+
|
|
80
|
+
def build_attrs
|
|
81
|
+
classes = cn(
|
|
82
|
+
"absolute right-0 bottom-0 z-10 inline-flex items-center justify-center rounded-full",
|
|
83
|
+
"bg-primary text-primary-foreground ring-2 ring-background select-none",
|
|
84
|
+
"group-data-[size=sm]/avatar:size-2 group-data-[size=sm]/avatar:[&>svg]:hidden",
|
|
85
|
+
"group-data-[size=default]/avatar:size-2.5 group-data-[size=default]/avatar:[&>svg]:size-2",
|
|
86
|
+
"group-data-[size=lg]/avatar:size-3 group-data-[size=lg]/avatar:[&>svg]:size-2",
|
|
87
|
+
@attrs.delete(:class)
|
|
88
|
+
)
|
|
89
|
+
@attrs.merge(data_slot: "avatar-badge", class: classes)
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
class AvatarGroup < Base
|
|
94
|
+
def initialize(**attrs)
|
|
95
|
+
@attrs = attrs
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
def view_template(&block)
|
|
99
|
+
div(**build_attrs, &block)
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
private
|
|
103
|
+
|
|
104
|
+
def build_attrs
|
|
105
|
+
classes = cn(
|
|
106
|
+
"group/avatar-group flex -space-x-2 *:data-[slot=avatar]:ring-2 *:data-[slot=avatar]:ring-background",
|
|
107
|
+
@attrs.delete(:class)
|
|
108
|
+
)
|
|
109
|
+
@attrs.merge(data_slot: "avatar-group", class: classes)
|
|
110
|
+
end
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
class AvatarGroupCount < Base
|
|
114
|
+
def initialize(**attrs)
|
|
115
|
+
@attrs = attrs
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
def view_template(&block)
|
|
119
|
+
div(**build_attrs, &block)
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
private
|
|
123
|
+
|
|
124
|
+
def build_attrs
|
|
125
|
+
classes = cn(
|
|
126
|
+
"relative flex size-8 shrink-0 items-center justify-center rounded-full",
|
|
127
|
+
"bg-muted text-sm text-muted-foreground ring-2 ring-background",
|
|
128
|
+
@attrs.delete(:class)
|
|
129
|
+
)
|
|
130
|
+
@attrs.merge(data_slot: "avatar-group-count", class: classes)
|
|
131
|
+
end
|
|
132
|
+
end
|
|
133
|
+
end
|
|
134
|
+
end
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Shadcn
|
|
4
|
+
module UI
|
|
5
|
+
# Port of shadcn/ui Badge
|
|
6
|
+
# Variants: default, secondary, destructive, outline, ghost, link
|
|
7
|
+
class Badge < Base
|
|
8
|
+
VARIANTS = ClassVariants.build(
|
|
9
|
+
base: [
|
|
10
|
+
"inline-flex w-fit shrink-0 items-center justify-center gap-1 overflow-hidden rounded-full border border-transparent px-2 py-0.5 text-xs font-medium whitespace-nowrap",
|
|
11
|
+
"transition-[color,box-shadow]",
|
|
12
|
+
"focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50",
|
|
13
|
+
"aria-invalid:border-destructive aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40",
|
|
14
|
+
"[&>svg]:pointer-events-none [&>svg]:size-3"
|
|
15
|
+
].join(" "),
|
|
16
|
+
variants: {
|
|
17
|
+
variant: {
|
|
18
|
+
default: "bg-primary text-primary-foreground [a&]:hover:bg-primary/90",
|
|
19
|
+
secondary: "bg-secondary text-secondary-foreground [a&]:hover:bg-secondary/90",
|
|
20
|
+
destructive: "bg-destructive text-white [a&]:hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:bg-destructive/60 dark:focus-visible:ring-destructive/40",
|
|
21
|
+
outline: "border-border text-foreground [a&]:hover:bg-accent [a&]:hover:text-accent-foreground",
|
|
22
|
+
ghost: "[a&]:hover:bg-accent [a&]:hover:text-accent-foreground",
|
|
23
|
+
link: "text-primary underline-offset-4 [a&]:hover:underline"
|
|
24
|
+
}
|
|
25
|
+
},
|
|
26
|
+
defaults: {
|
|
27
|
+
variant: :default
|
|
28
|
+
}
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
def initialize(variant: :default, **attrs)
|
|
32
|
+
@variant = variant
|
|
33
|
+
@attrs = attrs
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def view_template(&block)
|
|
37
|
+
span(**build_attrs, &block)
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
private
|
|
41
|
+
|
|
42
|
+
def build_attrs
|
|
43
|
+
classes = cn(VARIANTS.render(variant: @variant), @attrs.delete(:class))
|
|
44
|
+
@attrs.merge(data_slot: "badge", class: classes)
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Shadcn
|
|
4
|
+
module UI
|
|
5
|
+
# Port of shadcn/ui Breadcrumb
|
|
6
|
+
class Breadcrumb < 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
|
+
@attrs.merge(data_slot: "breadcrumb", aria_label: "breadcrumb")
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
class BreadcrumbList < Base
|
|
23
|
+
def initialize(**attrs)
|
|
24
|
+
@attrs = attrs
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def view_template(&block)
|
|
28
|
+
ol(**build_attrs, &block)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
private
|
|
32
|
+
|
|
33
|
+
def build_attrs
|
|
34
|
+
classes = cn(
|
|
35
|
+
"flex flex-wrap items-center gap-1.5 text-sm break-words text-muted-foreground sm:gap-2.5",
|
|
36
|
+
@attrs.delete(:class)
|
|
37
|
+
)
|
|
38
|
+
@attrs.merge(data_slot: "breadcrumb-list", class: classes)
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
class BreadcrumbItem < Base
|
|
43
|
+
def initialize(**attrs)
|
|
44
|
+
@attrs = attrs
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def view_template(&block)
|
|
48
|
+
li(**build_attrs, &block)
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
private
|
|
52
|
+
|
|
53
|
+
def build_attrs
|
|
54
|
+
classes = cn("inline-flex items-center gap-1.5", @attrs.delete(:class))
|
|
55
|
+
@attrs.merge(data_slot: "breadcrumb-item", class: classes)
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
class BreadcrumbLink < Base
|
|
60
|
+
def initialize(href: "#", **attrs)
|
|
61
|
+
@href = href
|
|
62
|
+
@attrs = attrs
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def view_template(&block)
|
|
66
|
+
a(**build_attrs, &block)
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
private
|
|
70
|
+
|
|
71
|
+
def build_attrs
|
|
72
|
+
classes = cn("transition-colors hover:text-foreground", @attrs.delete(:class))
|
|
73
|
+
@attrs.merge(data_slot: "breadcrumb-link", href: @href, class: classes)
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
class BreadcrumbPage < Base
|
|
78
|
+
def initialize(**attrs)
|
|
79
|
+
@attrs = attrs
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def view_template(&block)
|
|
83
|
+
span(**build_attrs, &block)
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
private
|
|
87
|
+
|
|
88
|
+
def build_attrs
|
|
89
|
+
classes = cn("font-normal text-foreground", @attrs.delete(:class))
|
|
90
|
+
@attrs.merge(
|
|
91
|
+
data_slot: "breadcrumb-page",
|
|
92
|
+
role: "link",
|
|
93
|
+
aria_disabled: "true",
|
|
94
|
+
aria_current: "page",
|
|
95
|
+
class: classes
|
|
96
|
+
)
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
class BreadcrumbSeparator < Base
|
|
101
|
+
def initialize(**attrs)
|
|
102
|
+
@attrs = attrs
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
def view_template(&block)
|
|
106
|
+
li(**build_attrs) do
|
|
107
|
+
if block_given?
|
|
108
|
+
yield
|
|
109
|
+
else
|
|
110
|
+
# Default ChevronRight icon
|
|
111
|
+
svg(
|
|
112
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
113
|
+
width: "14", height: "14",
|
|
114
|
+
viewbox: "0 0 24 24",
|
|
115
|
+
fill: "none",
|
|
116
|
+
stroke: "currentColor",
|
|
117
|
+
stroke_width: "2",
|
|
118
|
+
stroke_linecap: "round",
|
|
119
|
+
stroke_linejoin: "round",
|
|
120
|
+
class: "size-3.5"
|
|
121
|
+
) do |s|
|
|
122
|
+
s.path(d: "m9 18 6-6-6-6")
|
|
123
|
+
end
|
|
124
|
+
end
|
|
125
|
+
end
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
private
|
|
129
|
+
|
|
130
|
+
def build_attrs
|
|
131
|
+
classes = cn("[&>svg]:size-3.5", @attrs.delete(:class))
|
|
132
|
+
@attrs.merge(
|
|
133
|
+
data_slot: "breadcrumb-separator",
|
|
134
|
+
role: "presentation",
|
|
135
|
+
aria_hidden: "true",
|
|
136
|
+
class: classes
|
|
137
|
+
)
|
|
138
|
+
end
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
class BreadcrumbEllipsis < Base
|
|
142
|
+
def initialize(**attrs)
|
|
143
|
+
@attrs = attrs
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
def view_template
|
|
147
|
+
span(**build_attrs) do
|
|
148
|
+
svg(
|
|
149
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
150
|
+
width: "16", height: "16",
|
|
151
|
+
viewbox: "0 0 24 24",
|
|
152
|
+
fill: "none",
|
|
153
|
+
stroke: "currentColor",
|
|
154
|
+
stroke_width: "2",
|
|
155
|
+
stroke_linecap: "round",
|
|
156
|
+
stroke_linejoin: "round",
|
|
157
|
+
class: "size-4"
|
|
158
|
+
) do |s|
|
|
159
|
+
s.circle(cx: "12", cy: "12", r: "1")
|
|
160
|
+
s.circle(cx: "19", cy: "12", r: "1")
|
|
161
|
+
s.circle(cx: "5", cy: "12", r: "1")
|
|
162
|
+
end
|
|
163
|
+
span(class: "sr-only") { "More" }
|
|
164
|
+
end
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
private
|
|
168
|
+
|
|
169
|
+
def build_attrs
|
|
170
|
+
classes = cn("flex size-9 items-center justify-center", @attrs.delete(:class))
|
|
171
|
+
@attrs.merge(
|
|
172
|
+
data_slot: "breadcrumb-ellipsis",
|
|
173
|
+
role: "presentation",
|
|
174
|
+
aria_hidden: "true",
|
|
175
|
+
class: classes
|
|
176
|
+
)
|
|
177
|
+
end
|
|
178
|
+
end
|
|
179
|
+
end
|
|
180
|
+
end
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Shadcn
|
|
4
|
+
module UI
|
|
5
|
+
# Port of shadcn/ui Button
|
|
6
|
+
# Variants: default, destructive, outline, secondary, ghost, link
|
|
7
|
+
# Sizes: default, xs, sm, lg, icon, icon_xs, icon_sm, icon_lg
|
|
8
|
+
class Button < Base
|
|
9
|
+
VARIANTS = ClassVariants.build(
|
|
10
|
+
base: [
|
|
11
|
+
"inline-flex shrink-0 items-center justify-center gap-2 rounded-md text-sm font-medium whitespace-nowrap",
|
|
12
|
+
"transition-all outline-none",
|
|
13
|
+
"focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50",
|
|
14
|
+
"disabled:pointer-events-none disabled:opacity-50",
|
|
15
|
+
"aria-invalid:border-destructive aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40",
|
|
16
|
+
"[&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4"
|
|
17
|
+
].join(" "),
|
|
18
|
+
variants: {
|
|
19
|
+
variant: {
|
|
20
|
+
default: "bg-primary text-primary-foreground hover:bg-primary/90",
|
|
21
|
+
destructive: "bg-destructive text-white hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:bg-destructive/60 dark:focus-visible:ring-destructive/40",
|
|
22
|
+
outline: "border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:border-input dark:bg-input/30 dark:hover:bg-input/50",
|
|
23
|
+
secondary: "bg-secondary text-secondary-foreground hover:bg-secondary/80",
|
|
24
|
+
ghost: "hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50",
|
|
25
|
+
link: "text-primary underline-offset-4 hover:underline"
|
|
26
|
+
},
|
|
27
|
+
size: {
|
|
28
|
+
default: "h-9 px-4 py-2 has-[>svg]:px-3",
|
|
29
|
+
xs: "h-6 gap-1 rounded-md px-2 text-xs has-[>svg]:px-1.5 [&_svg:not([class*='size-'])]:size-3",
|
|
30
|
+
sm: "h-8 gap-1.5 rounded-md px-3 has-[>svg]:px-2.5",
|
|
31
|
+
lg: "h-10 rounded-md px-6 has-[>svg]:px-4",
|
|
32
|
+
icon: "size-9",
|
|
33
|
+
icon_xs: "size-6 rounded-md [&_svg:not([class*='size-'])]:size-3",
|
|
34
|
+
icon_sm: "size-8 rounded-md",
|
|
35
|
+
icon_lg: "size-10 rounded-md"
|
|
36
|
+
}
|
|
37
|
+
},
|
|
38
|
+
defaults: {
|
|
39
|
+
variant: :default,
|
|
40
|
+
size: :default
|
|
41
|
+
}
|
|
42
|
+
)
|
|
43
|
+
|
|
44
|
+
def initialize(variant: :default, size: :default, tag: :button, **attrs)
|
|
45
|
+
@variant = variant
|
|
46
|
+
@size = size
|
|
47
|
+
@tag = tag
|
|
48
|
+
@attrs = attrs
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def view_template(&block)
|
|
52
|
+
send(@tag, **build_attrs, &block)
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
private
|
|
56
|
+
|
|
57
|
+
def build_attrs
|
|
58
|
+
classes = cn(VARIANTS.render(variant: @variant, size: @size), @attrs.delete(:class))
|
|
59
|
+
@attrs.merge(data_slot: "button", class: classes)
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
end
|