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,32 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Shadcn
|
|
4
|
+
module UI
|
|
5
|
+
# Port of shadcn/ui InputGroup
|
|
6
|
+
class InputGroup < 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 h-9 w-full items-center rounded-md border border-input bg-transparent shadow-xs",
|
|
20
|
+
"transition-[color,box-shadow]",
|
|
21
|
+
"focus-within:border-ring focus-within:ring-[3px] focus-within:ring-ring/50",
|
|
22
|
+
"aria-invalid:border-destructive aria-invalid:ring-destructive/20",
|
|
23
|
+
"dark:bg-input/30 dark:aria-invalid:ring-destructive/40",
|
|
24
|
+
"has-[:disabled]:cursor-not-allowed has-[:disabled]:opacity-50",
|
|
25
|
+
"[&>input]:border-0 [&>input]:bg-transparent [&>input]:shadow-none [&>input]:ring-0 [&>input]:focus-visible:ring-0 [&>input]:focus-visible:border-0",
|
|
26
|
+
@attrs.delete(:class)
|
|
27
|
+
)
|
|
28
|
+
@attrs.merge(data_slot: "input-group", class: classes)
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Shadcn
|
|
4
|
+
module UI
|
|
5
|
+
# Port of shadcn/ui InputOTP
|
|
6
|
+
# One-time password input with individual digit boxes
|
|
7
|
+
class InputOTP < Base
|
|
8
|
+
def initialize(max_length: 6, **attrs)
|
|
9
|
+
@max_length = max_length
|
|
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
|
+
"flex items-center gap-2 has-[:disabled]:opacity-50",
|
|
22
|
+
@attrs.delete(:class)
|
|
23
|
+
)
|
|
24
|
+
@attrs.merge(
|
|
25
|
+
data_slot: "input-otp",
|
|
26
|
+
data_input_otp_container: true,
|
|
27
|
+
class: classes
|
|
28
|
+
)
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
class InputOTPGroup < 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
|
+
classes = cn("flex items-center", @attrs.delete(:class))
|
|
45
|
+
@attrs.merge(data_slot: "input-otp-group", class: classes)
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
class InputOTPSlot < Base
|
|
50
|
+
def initialize(index: 0, active: false, **attrs)
|
|
51
|
+
@index = index
|
|
52
|
+
@active = active
|
|
53
|
+
@attrs = attrs
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def view_template(&block)
|
|
57
|
+
div(**build_attrs) do
|
|
58
|
+
if block_given?
|
|
59
|
+
yield
|
|
60
|
+
else
|
|
61
|
+
# Caret when active
|
|
62
|
+
if @active
|
|
63
|
+
div(class: "pointer-events-none absolute inset-0 flex items-center justify-center") do
|
|
64
|
+
div(class: "h-4 w-px animate-caret-blink bg-foreground duration-1000")
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
private
|
|
72
|
+
|
|
73
|
+
def build_attrs
|
|
74
|
+
classes = cn(
|
|
75
|
+
"relative flex h-9 w-9 items-center justify-center border-y border-r border-input text-sm shadow-xs transition-all",
|
|
76
|
+
"first:rounded-l-md first:border-l last:rounded-r-md",
|
|
77
|
+
@active ? "z-10 ring-2 ring-ring" : "",
|
|
78
|
+
@attrs.delete(:class)
|
|
79
|
+
)
|
|
80
|
+
@attrs.merge(
|
|
81
|
+
data_slot: "input-otp-slot",
|
|
82
|
+
data_index: @index,
|
|
83
|
+
data_active: @active,
|
|
84
|
+
class: classes
|
|
85
|
+
)
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
class InputOTPSeparator < Base
|
|
90
|
+
def initialize(**attrs)
|
|
91
|
+
@attrs = attrs
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
def view_template
|
|
95
|
+
div(**build_attrs) do
|
|
96
|
+
# Dot separator
|
|
97
|
+
svg(xmlns: "http://www.w3.org/2000/svg", width: "16", height: "16",
|
|
98
|
+
viewbox: "0 0 24 24", fill: "none", stroke: "currentColor",
|
|
99
|
+
stroke_width: "2", class: "size-4") do |s|
|
|
100
|
+
s.circle(cx: "12.1", cy: "12.1", r: "1")
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
private
|
|
106
|
+
|
|
107
|
+
def build_attrs
|
|
108
|
+
@attrs.merge(data_slot: "input-otp-separator", role: "separator")
|
|
109
|
+
end
|
|
110
|
+
end
|
|
111
|
+
end
|
|
112
|
+
end
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Shadcn
|
|
4
|
+
module UI
|
|
5
|
+
# Port of shadcn/ui Item
|
|
6
|
+
# Versatile content display component (media, content, actions)
|
|
7
|
+
class Item < 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
|
+
classes = cn(
|
|
20
|
+
"flex items-start gap-3 rounded-lg p-3",
|
|
21
|
+
@attrs.delete(:class)
|
|
22
|
+
)
|
|
23
|
+
@attrs.merge(data_slot: "item", class: classes)
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
class ItemMedia < 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
|
+
classes = cn(
|
|
40
|
+
"flex shrink-0 items-center justify-center [&_svg]:size-5 [&_svg]:text-muted-foreground",
|
|
41
|
+
@attrs.delete(:class)
|
|
42
|
+
)
|
|
43
|
+
@attrs.merge(data_slot: "item-media", class: classes)
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
class ItemContent < Base
|
|
48
|
+
def initialize(**attrs)
|
|
49
|
+
@attrs = attrs
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def view_template(&block)
|
|
53
|
+
div(**build_attrs, &block)
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
private
|
|
57
|
+
|
|
58
|
+
def build_attrs
|
|
59
|
+
classes = cn("flex min-w-0 flex-1 flex-col gap-1", @attrs.delete(:class))
|
|
60
|
+
@attrs.merge(data_slot: "item-content", class: classes)
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
class ItemTitle < Base
|
|
65
|
+
def initialize(**attrs)
|
|
66
|
+
@attrs = attrs
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def view_template(&block)
|
|
70
|
+
div(**build_attrs, &block)
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
private
|
|
74
|
+
|
|
75
|
+
def build_attrs
|
|
76
|
+
classes = cn("text-sm font-medium leading-none", @attrs.delete(:class))
|
|
77
|
+
@attrs.merge(data_slot: "item-title", class: classes)
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
class ItemDescription < Base
|
|
82
|
+
def initialize(**attrs)
|
|
83
|
+
@attrs = attrs
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def view_template(&block)
|
|
87
|
+
p(**build_attrs, &block)
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
private
|
|
91
|
+
|
|
92
|
+
def build_attrs
|
|
93
|
+
classes = cn("text-sm text-muted-foreground", @attrs.delete(:class))
|
|
94
|
+
@attrs.merge(data_slot: "item-description", class: classes)
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
class ItemActions < Base
|
|
99
|
+
def initialize(**attrs)
|
|
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("flex shrink-0 items-center gap-1", @attrs.delete(:class))
|
|
111
|
+
@attrs.merge(data_slot: "item-actions", class: classes)
|
|
112
|
+
end
|
|
113
|
+
end
|
|
114
|
+
end
|
|
115
|
+
end
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Shadcn
|
|
4
|
+
module UI
|
|
5
|
+
# Port of shadcn/ui Kbd
|
|
6
|
+
class Kbd < Base
|
|
7
|
+
def initialize(**attrs)
|
|
8
|
+
@attrs = attrs
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def view_template(&block)
|
|
12
|
+
kbd(**build_attrs, &block)
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
private
|
|
16
|
+
|
|
17
|
+
def build_attrs
|
|
18
|
+
classes = cn(
|
|
19
|
+
"pointer-events-none inline-flex h-5 w-fit min-w-5 items-center justify-center gap-1",
|
|
20
|
+
"rounded-sm bg-muted px-1 font-sans text-xs font-medium text-muted-foreground select-none",
|
|
21
|
+
"[&_svg:not([class*='size-'])]:size-3",
|
|
22
|
+
@attrs.delete(:class)
|
|
23
|
+
)
|
|
24
|
+
@attrs.merge(data_slot: "kbd", class: classes)
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
class KbdGroup < Base
|
|
29
|
+
def initialize(**attrs)
|
|
30
|
+
@attrs = attrs
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def view_template(&block)
|
|
34
|
+
kbd(**build_attrs, &block)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
private
|
|
38
|
+
|
|
39
|
+
def build_attrs
|
|
40
|
+
classes = cn("inline-flex items-center gap-1", @attrs.delete(:class))
|
|
41
|
+
@attrs.merge(data_slot: "kbd-group", class: classes)
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Shadcn
|
|
4
|
+
module UI
|
|
5
|
+
# Port of shadcn/ui Label
|
|
6
|
+
class Label < Base
|
|
7
|
+
def initialize(**attrs)
|
|
8
|
+
@attrs = attrs
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def view_template(&block)
|
|
12
|
+
label(**build_attrs, &block)
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
private
|
|
16
|
+
|
|
17
|
+
def build_attrs
|
|
18
|
+
classes = cn(
|
|
19
|
+
"flex items-center gap-2 text-sm leading-none font-medium select-none",
|
|
20
|
+
"group-data-[disabled=true]:pointer-events-none group-data-[disabled=true]:opacity-50",
|
|
21
|
+
"peer-disabled:cursor-not-allowed peer-disabled:opacity-50",
|
|
22
|
+
@attrs.delete(:class)
|
|
23
|
+
)
|
|
24
|
+
@attrs.merge(data_slot: "label", class: classes)
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
@@ -0,0 +1,345 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Shadcn
|
|
4
|
+
module UI
|
|
5
|
+
# Port of shadcn/ui Menubar
|
|
6
|
+
class Menubar < 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 h-9 items-center gap-1 rounded-md border bg-background p-1 shadow-xs",
|
|
20
|
+
@attrs.delete(:class)
|
|
21
|
+
)
|
|
22
|
+
@attrs.merge(
|
|
23
|
+
data_slot: "menubar",
|
|
24
|
+
data_controller: "shadcn--menubar",
|
|
25
|
+
role: "menubar",
|
|
26
|
+
class: classes
|
|
27
|
+
)
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
class MenubarMenu < Base
|
|
32
|
+
def initialize(**attrs)
|
|
33
|
+
@attrs = attrs
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def view_template(&block)
|
|
37
|
+
div(**build_attrs, &block)
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
private
|
|
41
|
+
|
|
42
|
+
def build_attrs
|
|
43
|
+
@attrs.merge(data_slot: "menubar-menu")
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
class MenubarTrigger < Base
|
|
48
|
+
def initialize(**attrs)
|
|
49
|
+
@attrs = attrs
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def view_template(&block)
|
|
53
|
+
button(**build_attrs, &block)
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
private
|
|
57
|
+
|
|
58
|
+
def build_attrs
|
|
59
|
+
classes = cn(
|
|
60
|
+
"flex items-center rounded-sm px-2 py-1 text-sm font-medium outline-hidden select-none cursor-default",
|
|
61
|
+
"focus:bg-accent focus:text-accent-foreground",
|
|
62
|
+
"data-[state=open]:bg-accent data-[state=open]:text-accent-foreground",
|
|
63
|
+
@attrs.delete(:class)
|
|
64
|
+
)
|
|
65
|
+
@attrs.merge(
|
|
66
|
+
data_slot: "menubar-trigger",
|
|
67
|
+
data_shadcn__menubar_target: "trigger",
|
|
68
|
+
data_action: "click->shadcn--menubar#toggleMenu mouseenter->shadcn--menubar#enterTrigger",
|
|
69
|
+
type: "button",
|
|
70
|
+
class: classes
|
|
71
|
+
)
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
class MenubarContent < Base
|
|
76
|
+
def initialize(**attrs)
|
|
77
|
+
@attrs = attrs
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def view_template(&block)
|
|
81
|
+
div(**build_attrs, &block)
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
private
|
|
85
|
+
|
|
86
|
+
def build_attrs
|
|
87
|
+
classes = cn(
|
|
88
|
+
"z-50 min-w-[12rem] overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-md",
|
|
89
|
+
"data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95",
|
|
90
|
+
"data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95",
|
|
91
|
+
"data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2",
|
|
92
|
+
"data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
|
|
93
|
+
@attrs.delete(:class)
|
|
94
|
+
)
|
|
95
|
+
@attrs.merge(
|
|
96
|
+
data_slot: "menubar-content",
|
|
97
|
+
data_shadcn__menubar_target: "content",
|
|
98
|
+
role: "menu",
|
|
99
|
+
hidden: true,
|
|
100
|
+
class: classes
|
|
101
|
+
)
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
class MenubarItem < Base
|
|
106
|
+
def initialize(inset: false, variant: :default, **attrs)
|
|
107
|
+
@inset = inset
|
|
108
|
+
@variant = variant
|
|
109
|
+
@attrs = attrs
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
def view_template(&block)
|
|
113
|
+
div(**build_attrs, &block)
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
private
|
|
117
|
+
|
|
118
|
+
def build_attrs
|
|
119
|
+
classes = cn(
|
|
120
|
+
"relative flex cursor-default items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-hidden select-none",
|
|
121
|
+
"focus:bg-accent focus:text-accent-foreground",
|
|
122
|
+
"data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
|
|
123
|
+
"data-[inset]:pl-8",
|
|
124
|
+
"data-[variant=destructive]:text-destructive data-[variant=destructive]:focus:bg-destructive/10",
|
|
125
|
+
"[&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
|
|
126
|
+
"[&_svg:not([class*='text-'])]:text-muted-foreground",
|
|
127
|
+
@attrs.delete(:class)
|
|
128
|
+
)
|
|
129
|
+
result = @attrs.merge(
|
|
130
|
+
data_slot: "menubar-item",
|
|
131
|
+
data_shadcn__menubar_target: "item",
|
|
132
|
+
data_action: "click->shadcn--menubar#selectItem",
|
|
133
|
+
data_variant: @variant,
|
|
134
|
+
role: "menuitem",
|
|
135
|
+
tabindex: "-1",
|
|
136
|
+
class: classes
|
|
137
|
+
)
|
|
138
|
+
result[:data_inset] = true if @inset
|
|
139
|
+
result
|
|
140
|
+
end
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
class MenubarCheckboxItem < Base
|
|
144
|
+
def initialize(checked: false, **attrs)
|
|
145
|
+
@checked = checked
|
|
146
|
+
@attrs = attrs
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
def view_template(&block)
|
|
150
|
+
div(**build_attrs) do
|
|
151
|
+
span(class: "pointer-events-none absolute left-2 flex size-3.5 items-center justify-center") do
|
|
152
|
+
if @checked
|
|
153
|
+
svg(
|
|
154
|
+
xmlns: "http://www.w3.org/2000/svg", width: "16", height: "16",
|
|
155
|
+
viewbox: "0 0 24 24", fill: "none", stroke: "currentColor",
|
|
156
|
+
stroke_width: "2", class: "size-4"
|
|
157
|
+
) { |s| s.path(d: "M20 6 9 17l-5-5") }
|
|
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 focus:bg-accent focus:text-accent-foreground",
|
|
170
|
+
"data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
|
|
171
|
+
@attrs.delete(:class)
|
|
172
|
+
)
|
|
173
|
+
@attrs.merge(
|
|
174
|
+
data_slot: "menubar-checkbox-item",
|
|
175
|
+
role: "menuitemcheckbox",
|
|
176
|
+
aria_checked: @checked,
|
|
177
|
+
class: classes
|
|
178
|
+
)
|
|
179
|
+
end
|
|
180
|
+
end
|
|
181
|
+
|
|
182
|
+
class MenubarRadioGroup < Base
|
|
183
|
+
def initialize(**attrs)
|
|
184
|
+
@attrs = attrs
|
|
185
|
+
end
|
|
186
|
+
|
|
187
|
+
def view_template(&block)
|
|
188
|
+
div(**build_attrs, &block)
|
|
189
|
+
end
|
|
190
|
+
|
|
191
|
+
private
|
|
192
|
+
|
|
193
|
+
def build_attrs
|
|
194
|
+
@attrs.merge(data_slot: "menubar-radio-group", role: "group")
|
|
195
|
+
end
|
|
196
|
+
end
|
|
197
|
+
|
|
198
|
+
class MenubarRadioItem < Base
|
|
199
|
+
def initialize(checked: false, **attrs)
|
|
200
|
+
@checked = checked
|
|
201
|
+
@attrs = attrs
|
|
202
|
+
end
|
|
203
|
+
|
|
204
|
+
def view_template(&block)
|
|
205
|
+
div(**build_attrs) do
|
|
206
|
+
span(class: "pointer-events-none absolute left-2 flex size-3.5 items-center justify-center") do
|
|
207
|
+
if @checked
|
|
208
|
+
svg(
|
|
209
|
+
xmlns: "http://www.w3.org/2000/svg", width: "8", height: "8",
|
|
210
|
+
viewbox: "0 0 24 24", fill: "currentColor", class: "size-2"
|
|
211
|
+
) { |s| s.circle(cx: "12", cy: "12", r: "10") }
|
|
212
|
+
end
|
|
213
|
+
end
|
|
214
|
+
yield if block_given?
|
|
215
|
+
end
|
|
216
|
+
end
|
|
217
|
+
|
|
218
|
+
private
|
|
219
|
+
|
|
220
|
+
def build_attrs
|
|
221
|
+
classes = cn(
|
|
222
|
+
"relative flex cursor-default items-center gap-2 rounded-sm py-1.5 pr-2 pl-8 text-sm",
|
|
223
|
+
"outline-hidden select-none focus:bg-accent focus:text-accent-foreground",
|
|
224
|
+
"data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
|
|
225
|
+
@attrs.delete(:class)
|
|
226
|
+
)
|
|
227
|
+
@attrs.merge(
|
|
228
|
+
data_slot: "menubar-radio-item",
|
|
229
|
+
role: "menuitemradio",
|
|
230
|
+
aria_checked: @checked,
|
|
231
|
+
class: classes
|
|
232
|
+
)
|
|
233
|
+
end
|
|
234
|
+
end
|
|
235
|
+
|
|
236
|
+
class MenubarLabel < Base
|
|
237
|
+
def initialize(inset: false, **attrs)
|
|
238
|
+
@inset = inset
|
|
239
|
+
@attrs = attrs
|
|
240
|
+
end
|
|
241
|
+
|
|
242
|
+
def view_template(&block)
|
|
243
|
+
div(**build_attrs, &block)
|
|
244
|
+
end
|
|
245
|
+
|
|
246
|
+
private
|
|
247
|
+
|
|
248
|
+
def build_attrs
|
|
249
|
+
classes = cn("px-2 py-1.5 text-sm font-medium data-[inset]:pl-8", @attrs.delete(:class))
|
|
250
|
+
result = @attrs.merge(data_slot: "menubar-label", class: classes)
|
|
251
|
+
result[:data_inset] = true if @inset
|
|
252
|
+
result
|
|
253
|
+
end
|
|
254
|
+
end
|
|
255
|
+
|
|
256
|
+
class MenubarSeparator < Base
|
|
257
|
+
def initialize(**attrs)
|
|
258
|
+
@attrs = attrs
|
|
259
|
+
end
|
|
260
|
+
|
|
261
|
+
def view_template
|
|
262
|
+
div(**build_attrs)
|
|
263
|
+
end
|
|
264
|
+
|
|
265
|
+
private
|
|
266
|
+
|
|
267
|
+
def build_attrs
|
|
268
|
+
classes = cn("-mx-1 my-1 h-px bg-muted", @attrs.delete(:class))
|
|
269
|
+
@attrs.merge(data_slot: "menubar-separator", role: "separator", class: classes)
|
|
270
|
+
end
|
|
271
|
+
end
|
|
272
|
+
|
|
273
|
+
class MenubarShortcut < Base
|
|
274
|
+
def initialize(**attrs)
|
|
275
|
+
@attrs = attrs
|
|
276
|
+
end
|
|
277
|
+
|
|
278
|
+
def view_template(&block)
|
|
279
|
+
span(**build_attrs, &block)
|
|
280
|
+
end
|
|
281
|
+
|
|
282
|
+
private
|
|
283
|
+
|
|
284
|
+
def build_attrs
|
|
285
|
+
classes = cn("ml-auto text-xs tracking-widest text-muted-foreground", @attrs.delete(:class))
|
|
286
|
+
@attrs.merge(data_slot: "menubar-shortcut", class: classes)
|
|
287
|
+
end
|
|
288
|
+
end
|
|
289
|
+
|
|
290
|
+
class MenubarSubTrigger < Base
|
|
291
|
+
def initialize(inset: false, **attrs)
|
|
292
|
+
@inset = inset
|
|
293
|
+
@attrs = attrs
|
|
294
|
+
end
|
|
295
|
+
|
|
296
|
+
def view_template(&block)
|
|
297
|
+
div(**build_attrs) do
|
|
298
|
+
yield if block_given?
|
|
299
|
+
svg(
|
|
300
|
+
xmlns: "http://www.w3.org/2000/svg", width: "16", height: "16",
|
|
301
|
+
viewbox: "0 0 24 24", fill: "none", stroke: "currentColor",
|
|
302
|
+
stroke_width: "2", class: "ml-auto size-4"
|
|
303
|
+
) { |s| s.path(d: "m9 18 6-6-6-6") }
|
|
304
|
+
end
|
|
305
|
+
end
|
|
306
|
+
|
|
307
|
+
private
|
|
308
|
+
|
|
309
|
+
def build_attrs
|
|
310
|
+
classes = cn(
|
|
311
|
+
"flex cursor-default items-center rounded-sm px-2 py-1.5 text-sm outline-hidden select-none",
|
|
312
|
+
"focus:bg-accent focus:text-accent-foreground",
|
|
313
|
+
"data-[inset]:pl-8 data-[state=open]:bg-accent data-[state=open]:text-accent-foreground",
|
|
314
|
+
"[&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
|
|
315
|
+
@attrs.delete(:class)
|
|
316
|
+
)
|
|
317
|
+
result = @attrs.merge(data_slot: "menubar-sub-trigger", class: classes)
|
|
318
|
+
result[:data_inset] = true if @inset
|
|
319
|
+
result
|
|
320
|
+
end
|
|
321
|
+
end
|
|
322
|
+
|
|
323
|
+
class MenubarSubContent < Base
|
|
324
|
+
def initialize(**attrs)
|
|
325
|
+
@attrs = attrs
|
|
326
|
+
end
|
|
327
|
+
|
|
328
|
+
def view_template(&block)
|
|
329
|
+
div(**build_attrs, &block)
|
|
330
|
+
end
|
|
331
|
+
|
|
332
|
+
private
|
|
333
|
+
|
|
334
|
+
def build_attrs
|
|
335
|
+
classes = cn(
|
|
336
|
+
"z-50 min-w-[8rem] overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-lg",
|
|
337
|
+
"data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95",
|
|
338
|
+
"data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95",
|
|
339
|
+
@attrs.delete(:class)
|
|
340
|
+
)
|
|
341
|
+
@attrs.merge(data_slot: "menubar-sub-content", class: classes)
|
|
342
|
+
end
|
|
343
|
+
end
|
|
344
|
+
end
|
|
345
|
+
end
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Shadcn
|
|
4
|
+
module UI
|
|
5
|
+
# Port of shadcn/ui NativeSelect
|
|
6
|
+
class NativeSelect < Base
|
|
7
|
+
def initialize(**attrs)
|
|
8
|
+
@attrs = attrs
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def view_template(&block)
|
|
12
|
+
select(**build_attrs, &block)
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
private
|
|
16
|
+
|
|
17
|
+
def build_attrs
|
|
18
|
+
classes = cn(
|
|
19
|
+
"border-input flex h-9 w-full items-center rounded-md border bg-transparent px-3 py-1 text-base shadow-xs",
|
|
20
|
+
"transition-[color,box-shadow] outline-none",
|
|
21
|
+
"focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50",
|
|
22
|
+
"disabled:cursor-not-allowed disabled:opacity-50 md:text-sm",
|
|
23
|
+
"aria-invalid:border-destructive aria-invalid:ring-destructive/20",
|
|
24
|
+
"dark:bg-input/30",
|
|
25
|
+
@attrs.delete(:class)
|
|
26
|
+
)
|
|
27
|
+
@attrs.merge(data_slot: "native-select", class: classes)
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|