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,92 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Shadcn
|
|
4
|
+
module UI
|
|
5
|
+
# Port of shadcn/ui RadioGroup
|
|
6
|
+
class RadioGroup < Base
|
|
7
|
+
def initialize(name: nil, value: nil, **attrs)
|
|
8
|
+
@name = name
|
|
9
|
+
@value = value
|
|
10
|
+
@attrs = attrs
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def view_template(&block)
|
|
14
|
+
div(**build_attrs) do
|
|
15
|
+
if @name
|
|
16
|
+
input(type: "hidden", name: @name, value: @value.to_s, data_shadcn__radio_group_target: "input")
|
|
17
|
+
end
|
|
18
|
+
yield if block_given?
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
private
|
|
23
|
+
|
|
24
|
+
def build_attrs
|
|
25
|
+
classes = cn("grid gap-3", @attrs.delete(:class))
|
|
26
|
+
attrs = @attrs.merge(
|
|
27
|
+
data_slot: "radio-group",
|
|
28
|
+
data_controller: "shadcn--radio-group",
|
|
29
|
+
role: "radiogroup",
|
|
30
|
+
class: classes
|
|
31
|
+
)
|
|
32
|
+
attrs[:data_shadcn__radio_group_value_value] = @value.to_s if @value
|
|
33
|
+
attrs
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
class RadioGroupItem < Base
|
|
38
|
+
def initialize(value:, checked: false, **attrs)
|
|
39
|
+
@value = value
|
|
40
|
+
@checked = checked
|
|
41
|
+
@attrs = attrs
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def view_template
|
|
45
|
+
button(**build_attrs) do
|
|
46
|
+
span(
|
|
47
|
+
data_shadcn__radio_group_target: "indicator",
|
|
48
|
+
data_value: @value,
|
|
49
|
+
hidden: !@checked,
|
|
50
|
+
class: "relative flex items-center justify-center"
|
|
51
|
+
) do
|
|
52
|
+
svg(
|
|
53
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
54
|
+
width: "8", height: "8",
|
|
55
|
+
viewbox: "0 0 24 24",
|
|
56
|
+
fill: "currentColor",
|
|
57
|
+
class: "size-2.5 fill-primary"
|
|
58
|
+
) do |s|
|
|
59
|
+
s.circle(cx: "12", cy: "12", r: "10")
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
private
|
|
66
|
+
|
|
67
|
+
def build_attrs
|
|
68
|
+
classes = cn(
|
|
69
|
+
"aspect-square size-4 shrink-0 rounded-full border border-input shadow-xs",
|
|
70
|
+
"transition-shadow outline-none",
|
|
71
|
+
"focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50",
|
|
72
|
+
"disabled:cursor-not-allowed disabled:opacity-50",
|
|
73
|
+
"aria-invalid:border-destructive aria-invalid:ring-destructive/20",
|
|
74
|
+
"data-[state=checked]:border-primary",
|
|
75
|
+
"dark:bg-input/30 dark:aria-invalid:ring-destructive/40",
|
|
76
|
+
@attrs.delete(:class)
|
|
77
|
+
)
|
|
78
|
+
@attrs.merge(
|
|
79
|
+
data_slot: "radio-group-item",
|
|
80
|
+
data_shadcn__radio_group_target: "item",
|
|
81
|
+
data_action: "click->shadcn--radio-group#select keydown->shadcn--radio-group#keydown",
|
|
82
|
+
data_state: @checked ? "checked" : "unchecked",
|
|
83
|
+
data_value: @value,
|
|
84
|
+
role: "radio",
|
|
85
|
+
type: "button",
|
|
86
|
+
aria_checked: @checked,
|
|
87
|
+
class: classes
|
|
88
|
+
)
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
end
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Shadcn
|
|
4
|
+
module UI
|
|
5
|
+
# Port of shadcn/ui Resizable
|
|
6
|
+
# Panel groups with drag handles
|
|
7
|
+
class ResizablePanelGroup < Base
|
|
8
|
+
def initialize(direction: :horizontal, **attrs)
|
|
9
|
+
@direction = direction
|
|
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 h-full w-full",
|
|
22
|
+
@direction == :vertical ? "flex-col" : "flex-row",
|
|
23
|
+
"data-[panel-group-direction=vertical]:flex-col",
|
|
24
|
+
@attrs.delete(:class)
|
|
25
|
+
)
|
|
26
|
+
@attrs.merge(
|
|
27
|
+
data_slot: "resizable-panel-group",
|
|
28
|
+
data_panel_group: true,
|
|
29
|
+
data_panel_group_direction: @direction,
|
|
30
|
+
class: classes
|
|
31
|
+
)
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
class ResizablePanel < Base
|
|
36
|
+
def initialize(default_size: nil, min_size: nil, max_size: nil, **attrs)
|
|
37
|
+
@default_size = default_size
|
|
38
|
+
@min_size = min_size
|
|
39
|
+
@max_size = max_size
|
|
40
|
+
@attrs = attrs
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def view_template(&block)
|
|
44
|
+
div(**build_attrs, &block)
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
private
|
|
48
|
+
|
|
49
|
+
def build_attrs
|
|
50
|
+
style_parts = []
|
|
51
|
+
style_parts << "flex: #{@default_size} 1 0%" if @default_size
|
|
52
|
+
style_parts << "min-width: #{@min_size}%" if @min_size
|
|
53
|
+
style_parts << "max-width: #{@max_size}%" if @max_size
|
|
54
|
+
|
|
55
|
+
classes = cn("flex-1 overflow-auto", @attrs.delete(:class))
|
|
56
|
+
result = @attrs.merge(data_slot: "resizable-panel", data_panel: true, class: classes)
|
|
57
|
+
result[:style] = style_parts.join("; ") unless style_parts.empty?
|
|
58
|
+
result
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
class ResizableHandle < Base
|
|
63
|
+
def initialize(with_handle: false, **attrs)
|
|
64
|
+
@with_handle = with_handle
|
|
65
|
+
@attrs = attrs
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def view_template(&block)
|
|
69
|
+
div(**build_attrs) do
|
|
70
|
+
if @with_handle
|
|
71
|
+
div(class: "z-10 flex h-4 w-3 items-center justify-center rounded-sm border bg-border") do
|
|
72
|
+
svg(xmlns: "http://www.w3.org/2000/svg", width: "10", height: "10",
|
|
73
|
+
viewbox: "0 0 24 24", fill: "none", stroke: "currentColor",
|
|
74
|
+
stroke_width: "2", class: "size-2.5") do |s|
|
|
75
|
+
s.path(d: "M9 5v14")
|
|
76
|
+
s.path(d: "M15 5v14")
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
yield if block_given?
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
private
|
|
85
|
+
|
|
86
|
+
def build_attrs
|
|
87
|
+
classes = cn(
|
|
88
|
+
"relative flex w-px items-center justify-center bg-border",
|
|
89
|
+
"after:absolute after:inset-y-0 after:left-1/2 after:w-1 after:-translate-x-1/2",
|
|
90
|
+
"focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring focus-visible:ring-offset-1",
|
|
91
|
+
"data-[panel-group-direction=vertical]:h-px data-[panel-group-direction=vertical]:w-full",
|
|
92
|
+
"data-[panel-group-direction=vertical]:after:left-0 data-[panel-group-direction=vertical]:after:h-1",
|
|
93
|
+
"data-[panel-group-direction=vertical]:after:w-full data-[panel-group-direction=vertical]:after:-translate-y-1/2",
|
|
94
|
+
"data-[panel-group-direction=vertical]:after:translate-x-0",
|
|
95
|
+
"[&[data-panel-group-direction=vertical]>div]:rotate-90",
|
|
96
|
+
@attrs.delete(:class)
|
|
97
|
+
)
|
|
98
|
+
@attrs.merge(
|
|
99
|
+
data_slot: "resizable-handle",
|
|
100
|
+
data_panel_resize_handle: true,
|
|
101
|
+
role: "separator",
|
|
102
|
+
tabindex: "0",
|
|
103
|
+
class: classes
|
|
104
|
+
)
|
|
105
|
+
end
|
|
106
|
+
end
|
|
107
|
+
end
|
|
108
|
+
end
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Shadcn
|
|
4
|
+
module UI
|
|
5
|
+
# Port of shadcn/ui ScrollArea
|
|
6
|
+
class ScrollArea < Base
|
|
7
|
+
def initialize(**attrs)
|
|
8
|
+
@attrs = attrs
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def view_template(&block)
|
|
12
|
+
div(**build_attrs) do
|
|
13
|
+
div(
|
|
14
|
+
data_slot: "scroll-area-viewport",
|
|
15
|
+
data_shadcn__scroll_area_target: "viewport",
|
|
16
|
+
class: "size-full overflow-auto rounded-[inherit]",
|
|
17
|
+
&block
|
|
18
|
+
)
|
|
19
|
+
render ScrollBar.new
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
private
|
|
24
|
+
|
|
25
|
+
def build_attrs
|
|
26
|
+
classes = cn("relative overflow-hidden", @attrs.delete(:class))
|
|
27
|
+
@attrs.merge(
|
|
28
|
+
data_slot: "scroll-area",
|
|
29
|
+
data_controller: "shadcn--scroll-area",
|
|
30
|
+
class: classes
|
|
31
|
+
)
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
class ScrollBar < Base
|
|
36
|
+
def initialize(orientation: :vertical, **attrs)
|
|
37
|
+
@orientation = orientation
|
|
38
|
+
@attrs = attrs
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def view_template
|
|
42
|
+
div(**build_attrs) do
|
|
43
|
+
div(
|
|
44
|
+
data_slot: "scroll-area-thumb",
|
|
45
|
+
data_shadcn__scroll_area_target: "thumb",
|
|
46
|
+
data_action: "pointerdown->shadcn--scroll-area#startDrag",
|
|
47
|
+
class: "relative flex-1 rounded-full bg-border"
|
|
48
|
+
)
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
private
|
|
53
|
+
|
|
54
|
+
def build_attrs
|
|
55
|
+
orientation_classes = if @orientation == :vertical
|
|
56
|
+
"h-full w-2.5 border-l border-l-transparent"
|
|
57
|
+
else
|
|
58
|
+
"h-2.5 flex-col border-t border-t-transparent"
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
classes = cn(
|
|
62
|
+
"flex touch-none p-px transition-colors select-none",
|
|
63
|
+
orientation_classes,
|
|
64
|
+
@attrs.delete(:class)
|
|
65
|
+
)
|
|
66
|
+
@attrs.merge(
|
|
67
|
+
data_slot: "scroll-area-scrollbar",
|
|
68
|
+
data_shadcn__scroll_area_target: "scrollbar",
|
|
69
|
+
data_orientation: @orientation,
|
|
70
|
+
class: classes
|
|
71
|
+
)
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
end
|
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Shadcn
|
|
4
|
+
module UI
|
|
5
|
+
# Port of shadcn/ui Select
|
|
6
|
+
class Select < Base
|
|
7
|
+
def initialize(name: nil, **attrs)
|
|
8
|
+
@name = name
|
|
9
|
+
@attrs = attrs
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def view_template(&block)
|
|
13
|
+
div(**build_attrs) do
|
|
14
|
+
if @name
|
|
15
|
+
input(type: "hidden", name: @name, value: "", data_shadcn__select_target: "input")
|
|
16
|
+
end
|
|
17
|
+
yield if block_given?
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
private
|
|
22
|
+
|
|
23
|
+
def build_attrs
|
|
24
|
+
@attrs.merge(
|
|
25
|
+
data_slot: "select",
|
|
26
|
+
data_controller: "shadcn--select",
|
|
27
|
+
data_action: "click@window->shadcn--select#hide keydown.esc@window->shadcn--select#hideOnEscape keydown->shadcn--select#navigate"
|
|
28
|
+
)
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
class SelectTrigger < Base
|
|
33
|
+
def initialize(size: :default, **attrs)
|
|
34
|
+
@size = size
|
|
35
|
+
@attrs = attrs
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def view_template(&block)
|
|
39
|
+
button(**build_attrs) do
|
|
40
|
+
yield if block_given?
|
|
41
|
+
# ChevronDown icon
|
|
42
|
+
svg(
|
|
43
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
44
|
+
width: "16", height: "16",
|
|
45
|
+
viewbox: "0 0 24 24",
|
|
46
|
+
fill: "none",
|
|
47
|
+
stroke: "currentColor",
|
|
48
|
+
stroke_width: "2",
|
|
49
|
+
stroke_linecap: "round",
|
|
50
|
+
stroke_linejoin: "round",
|
|
51
|
+
class: "size-4 opacity-50 shrink-0"
|
|
52
|
+
) do |s|
|
|
53
|
+
s.path(d: "m6 9 6 6 6-6")
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
private
|
|
59
|
+
|
|
60
|
+
def build_attrs
|
|
61
|
+
classes = cn(
|
|
62
|
+
"flex w-fit items-center justify-between gap-2 rounded-md border border-input bg-transparent px-3 py-2 text-sm whitespace-nowrap shadow-xs",
|
|
63
|
+
"transition-[color,box-shadow] outline-none",
|
|
64
|
+
"focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50",
|
|
65
|
+
"disabled:cursor-not-allowed disabled:opacity-50",
|
|
66
|
+
"aria-invalid:border-destructive aria-invalid:ring-destructive/20",
|
|
67
|
+
"data-[placeholder]:text-muted-foreground data-[size=default]:h-9 data-[size=sm]:h-8",
|
|
68
|
+
"*:data-[slot=select-value]:line-clamp-1 *:data-[slot=select-value]:flex *:data-[slot=select-value]:items-center *:data-[slot=select-value]:gap-2",
|
|
69
|
+
"dark:bg-input/30 dark:hover:bg-input/50 dark:aria-invalid:ring-destructive/40",
|
|
70
|
+
"[&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 [&_svg:not([class*='text-'])]:text-muted-foreground",
|
|
71
|
+
@attrs.delete(:class)
|
|
72
|
+
)
|
|
73
|
+
@attrs.merge(
|
|
74
|
+
data_slot: "select-trigger",
|
|
75
|
+
data_shadcn__select_target: "trigger",
|
|
76
|
+
data_action: "click->shadcn--select#toggle",
|
|
77
|
+
data_size: @size,
|
|
78
|
+
type: "button",
|
|
79
|
+
role: "combobox",
|
|
80
|
+
class: classes
|
|
81
|
+
)
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
class SelectValue < Base
|
|
86
|
+
def initialize(placeholder: nil, **attrs)
|
|
87
|
+
@placeholder = placeholder
|
|
88
|
+
@attrs = attrs
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
def view_template(&block)
|
|
92
|
+
span(**build_attrs) do
|
|
93
|
+
if block_given?
|
|
94
|
+
yield
|
|
95
|
+
elsif @placeholder
|
|
96
|
+
plain @placeholder
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
private
|
|
102
|
+
|
|
103
|
+
def build_attrs
|
|
104
|
+
@attrs.merge(
|
|
105
|
+
data_slot: "select-value",
|
|
106
|
+
data_shadcn__select_target: "value"
|
|
107
|
+
)
|
|
108
|
+
end
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
class SelectContent < Base
|
|
112
|
+
def initialize(position: :popper, **attrs)
|
|
113
|
+
@position = position
|
|
114
|
+
@attrs = attrs
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
def view_template(&block)
|
|
118
|
+
div(**build_attrs, &block)
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
private
|
|
122
|
+
|
|
123
|
+
def build_attrs
|
|
124
|
+
popper_classes = @position == :popper ?
|
|
125
|
+
"max-h-[min(var(--radix-select-content-available-height),24rem)]" : ""
|
|
126
|
+
|
|
127
|
+
classes = cn(
|
|
128
|
+
"relative z-50 max-h-(--radix-select-content-available-height) min-w-[8rem] overflow-x-hidden overflow-y-auto rounded-md border bg-popover text-popover-foreground shadow-md origin-(--radix-select-content-transform-origin)",
|
|
129
|
+
"data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2",
|
|
130
|
+
"data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
|
|
131
|
+
"data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95",
|
|
132
|
+
"data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95",
|
|
133
|
+
popper_classes,
|
|
134
|
+
@attrs.delete(:class)
|
|
135
|
+
)
|
|
136
|
+
@attrs.merge(
|
|
137
|
+
data_slot: "select-content",
|
|
138
|
+
data_shadcn__select_target: "content",
|
|
139
|
+
data_position: @position,
|
|
140
|
+
role: "listbox",
|
|
141
|
+
hidden: true,
|
|
142
|
+
class: classes
|
|
143
|
+
)
|
|
144
|
+
end
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
class SelectItem < Base
|
|
148
|
+
def initialize(value:, **attrs)
|
|
149
|
+
@value = value
|
|
150
|
+
@attrs = attrs
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
def view_template(&block)
|
|
154
|
+
div(**build_attrs) do
|
|
155
|
+
yield if block_given?
|
|
156
|
+
span(class: "absolute right-2 flex size-3.5 items-center justify-center") do
|
|
157
|
+
# Check icon rendered when selected via data-state
|
|
158
|
+
end
|
|
159
|
+
end
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
private
|
|
163
|
+
|
|
164
|
+
def build_attrs
|
|
165
|
+
classes = cn(
|
|
166
|
+
"relative flex w-full cursor-default items-center gap-2 rounded-sm py-1.5 pr-8 pl-2 text-sm",
|
|
167
|
+
"outline-hidden select-none",
|
|
168
|
+
"focus:bg-accent focus:text-accent-foreground",
|
|
169
|
+
"data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
|
|
170
|
+
"[&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 [&_svg:not([class*='text-'])]:text-muted-foreground",
|
|
171
|
+
@attrs.delete(:class)
|
|
172
|
+
)
|
|
173
|
+
@attrs.merge(
|
|
174
|
+
data_slot: "select-item",
|
|
175
|
+
data_shadcn__select_target: "item",
|
|
176
|
+
data_action: "click->shadcn--select#selectItem",
|
|
177
|
+
data_value: @value,
|
|
178
|
+
role: "option",
|
|
179
|
+
tabindex: "-1",
|
|
180
|
+
class: classes
|
|
181
|
+
)
|
|
182
|
+
end
|
|
183
|
+
end
|
|
184
|
+
|
|
185
|
+
class SelectGroup < Base
|
|
186
|
+
def initialize(**attrs)
|
|
187
|
+
@attrs = attrs
|
|
188
|
+
end
|
|
189
|
+
|
|
190
|
+
def view_template(&block)
|
|
191
|
+
div(**build_attrs, &block)
|
|
192
|
+
end
|
|
193
|
+
|
|
194
|
+
private
|
|
195
|
+
|
|
196
|
+
def build_attrs
|
|
197
|
+
@attrs.merge(data_slot: "select-group", role: "group")
|
|
198
|
+
end
|
|
199
|
+
end
|
|
200
|
+
|
|
201
|
+
class SelectLabel < Base
|
|
202
|
+
def initialize(**attrs)
|
|
203
|
+
@attrs = attrs
|
|
204
|
+
end
|
|
205
|
+
|
|
206
|
+
def view_template(&block)
|
|
207
|
+
div(**build_attrs, &block)
|
|
208
|
+
end
|
|
209
|
+
|
|
210
|
+
private
|
|
211
|
+
|
|
212
|
+
def build_attrs
|
|
213
|
+
classes = cn("px-2 py-1.5 text-xs text-muted-foreground font-medium", @attrs.delete(:class))
|
|
214
|
+
@attrs.merge(data_slot: "select-label", class: classes)
|
|
215
|
+
end
|
|
216
|
+
end
|
|
217
|
+
|
|
218
|
+
class SelectSeparator < Base
|
|
219
|
+
def initialize(**attrs)
|
|
220
|
+
@attrs = attrs
|
|
221
|
+
end
|
|
222
|
+
|
|
223
|
+
def view_template
|
|
224
|
+
div(**build_attrs)
|
|
225
|
+
end
|
|
226
|
+
|
|
227
|
+
private
|
|
228
|
+
|
|
229
|
+
def build_attrs
|
|
230
|
+
classes = cn("pointer-events-none -mx-1 my-1 h-px bg-muted", @attrs.delete(:class))
|
|
231
|
+
@attrs.merge(data_slot: "select-separator", class: classes)
|
|
232
|
+
end
|
|
233
|
+
end
|
|
234
|
+
end
|
|
235
|
+
end
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Shadcn
|
|
4
|
+
module UI
|
|
5
|
+
# Port of shadcn/ui Separator
|
|
6
|
+
class Separator < Base
|
|
7
|
+
def initialize(orientation: :horizontal, decorative: true, **attrs)
|
|
8
|
+
@orientation = orientation
|
|
9
|
+
@decorative = decorative
|
|
10
|
+
@attrs = attrs
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def view_template
|
|
14
|
+
div(**build_attrs)
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
private
|
|
18
|
+
|
|
19
|
+
def build_attrs
|
|
20
|
+
classes = cn(
|
|
21
|
+
"shrink-0 bg-border data-[orientation=horizontal]:h-px data-[orientation=horizontal]:w-full data-[orientation=vertical]:h-full data-[orientation=vertical]:w-px",
|
|
22
|
+
@attrs.delete(:class)
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
result = @attrs.merge(
|
|
26
|
+
data_slot: "separator",
|
|
27
|
+
data_orientation: @orientation,
|
|
28
|
+
role: @decorative ? "none" : "separator",
|
|
29
|
+
class: classes
|
|
30
|
+
)
|
|
31
|
+
result[:aria_orientation] = @orientation.to_s unless @decorative
|
|
32
|
+
result
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|