shadcn-phlex 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (113) hide show
  1. checksums.yaml +7 -0
  2. data/README.md +195 -0
  3. data/app.css +20 -0
  4. data/css/shadcn-source.css +3 -0
  5. data/css/shadcn-tailwind.css +160 -0
  6. data/css/themes/mauve.css +62 -0
  7. data/css/themes/mist.css +62 -0
  8. data/css/themes/neutral.css +74 -0
  9. data/css/themes/olive.css +62 -0
  10. data/css/themes/stone.css +62 -0
  11. data/css/themes/taupe.css +62 -0
  12. data/css/themes/zinc.css +62 -0
  13. data/js/controllers/accordion_controller.js +135 -0
  14. data/js/controllers/checkbox_controller.js +52 -0
  15. data/js/controllers/collapsible_controller.js +85 -0
  16. data/js/controllers/combobox_controller.js +168 -0
  17. data/js/controllers/command_controller.js +171 -0
  18. data/js/controllers/context_menu_controller.js +132 -0
  19. data/js/controllers/dark_mode_controller.js +106 -0
  20. data/js/controllers/dialog_controller.js +205 -0
  21. data/js/controllers/drawer_controller.js +161 -0
  22. data/js/controllers/dropdown_menu_controller.js +189 -0
  23. data/js/controllers/hover_card_controller.js +85 -0
  24. data/js/controllers/index.js +89 -0
  25. data/js/controllers/menubar_controller.js +171 -0
  26. data/js/controllers/navigation_menu_controller.js +160 -0
  27. data/js/controllers/popover_controller.js +151 -0
  28. data/js/controllers/radio_group_controller.js +78 -0
  29. data/js/controllers/scroll_area_controller.js +117 -0
  30. data/js/controllers/select_controller.js +198 -0
  31. data/js/controllers/sheet_controller.js +130 -0
  32. data/js/controllers/slider_controller.js +142 -0
  33. data/js/controllers/switch_controller.js +40 -0
  34. data/js/controllers/tabs_controller.js +96 -0
  35. data/js/controllers/toast_controller.js +206 -0
  36. data/js/controllers/toggle_controller.js +30 -0
  37. data/js/controllers/toggle_group_controller.js +73 -0
  38. data/js/controllers/tooltip_controller.js +146 -0
  39. data/lib/generators/shadcn_phlex/component_generator.rb +79 -0
  40. data/lib/generators/shadcn_phlex/install_generator.rb +217 -0
  41. data/lib/shadcn/base.rb +27 -0
  42. data/lib/shadcn/engine.rb +24 -0
  43. data/lib/shadcn/kit.rb +1158 -0
  44. data/lib/shadcn/themes/accent_colors.rb +106 -0
  45. data/lib/shadcn/themes/base_colors.rb +313 -0
  46. data/lib/shadcn/ui/accordion.rb +135 -0
  47. data/lib/shadcn/ui/alert.rb +79 -0
  48. data/lib/shadcn/ui/alert_dialog.rb +220 -0
  49. data/lib/shadcn/ui/aspect_ratio.rb +35 -0
  50. data/lib/shadcn/ui/avatar.rb +134 -0
  51. data/lib/shadcn/ui/badge.rb +48 -0
  52. data/lib/shadcn/ui/breadcrumb.rb +180 -0
  53. data/lib/shadcn/ui/button.rb +63 -0
  54. data/lib/shadcn/ui/button_group.rb +58 -0
  55. data/lib/shadcn/ui/card.rb +133 -0
  56. data/lib/shadcn/ui/checkbox.rb +72 -0
  57. data/lib/shadcn/ui/collapsible.rb +76 -0
  58. data/lib/shadcn/ui/combobox.rb +229 -0
  59. data/lib/shadcn/ui/command.rb +256 -0
  60. data/lib/shadcn/ui/context_menu.rb +319 -0
  61. data/lib/shadcn/ui/dialog.rb +226 -0
  62. data/lib/shadcn/ui/direction.rb +23 -0
  63. data/lib/shadcn/ui/drawer.rb +217 -0
  64. data/lib/shadcn/ui/dropdown_menu.rb +384 -0
  65. data/lib/shadcn/ui/empty.rb +97 -0
  66. data/lib/shadcn/ui/field.rb +126 -0
  67. data/lib/shadcn/ui/hover_card.rb +75 -0
  68. data/lib/shadcn/ui/input.rb +36 -0
  69. data/lib/shadcn/ui/input_group.rb +32 -0
  70. data/lib/shadcn/ui/input_otp.rb +112 -0
  71. data/lib/shadcn/ui/item.rb +115 -0
  72. data/lib/shadcn/ui/kbd.rb +45 -0
  73. data/lib/shadcn/ui/label.rb +28 -0
  74. data/lib/shadcn/ui/menubar.rb +345 -0
  75. data/lib/shadcn/ui/native_select.rb +31 -0
  76. data/lib/shadcn/ui/navigation_menu.rb +238 -0
  77. data/lib/shadcn/ui/pagination.rb +224 -0
  78. data/lib/shadcn/ui/popover.rb +147 -0
  79. data/lib/shadcn/ui/progress.rb +40 -0
  80. data/lib/shadcn/ui/radio_group.rb +92 -0
  81. data/lib/shadcn/ui/resizable.rb +108 -0
  82. data/lib/shadcn/ui/scroll_area.rb +75 -0
  83. data/lib/shadcn/ui/select.rb +235 -0
  84. data/lib/shadcn/ui/separator.rb +36 -0
  85. data/lib/shadcn/ui/sheet.rb +231 -0
  86. data/lib/shadcn/ui/sidebar.rb +420 -0
  87. data/lib/shadcn/ui/skeleton.rb +23 -0
  88. data/lib/shadcn/ui/slider.rb +72 -0
  89. data/lib/shadcn/ui/sonner.rb +177 -0
  90. data/lib/shadcn/ui/spinner.rb +58 -0
  91. data/lib/shadcn/ui/switch.rb +75 -0
  92. data/lib/shadcn/ui/table.rb +154 -0
  93. data/lib/shadcn/ui/tabs.rb +154 -0
  94. data/lib/shadcn/ui/text_field.rb +146 -0
  95. data/lib/shadcn/ui/textarea.rb +32 -0
  96. data/lib/shadcn/ui/theme_toggle.rb +74 -0
  97. data/lib/shadcn/ui/toggle.rb +66 -0
  98. data/lib/shadcn/ui/toggle_group.rb +75 -0
  99. data/lib/shadcn/ui/tooltip.rb +78 -0
  100. data/lib/shadcn/ui/typography.rb +217 -0
  101. data/lib/shadcn/version.rb +5 -0
  102. data/lib/shadcn-phlex.rb +6 -0
  103. data/lib/shadcn.rb +80 -0
  104. data/package.json +14 -0
  105. data/skills/shadcn-phlex/SKILL.md +190 -0
  106. data/skills/shadcn-phlex/evals/evals.json +90 -0
  107. data/skills/shadcn-phlex/references/component-catalog.md +355 -0
  108. data/skills/shadcn-phlex/rules/composition.md +235 -0
  109. data/skills/shadcn-phlex/rules/forms.md +151 -0
  110. data/skills/shadcn-phlex/rules/helpers.md +54 -0
  111. data/skills/shadcn-phlex/rules/stimulus.md +61 -0
  112. data/skills/shadcn-phlex/rules/styling.md +177 -0
  113. metadata +209 -0
@@ -0,0 +1,58 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Shadcn
4
+ module UI
5
+ # Port of shadcn/ui ButtonGroup
6
+ class ButtonGroup < Base
7
+ def initialize(orientation: :horizontal, **attrs)
8
+ @orientation = orientation
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
+ "inline-flex items-center gap-0 -space-x-px shadow-xs",
21
+ "data-[orientation=vertical]:flex-col data-[orientation=vertical]:space-x-0 data-[orientation=vertical]:-space-y-px",
22
+ "[&_[data-slot=button]]:rounded-none [&_[data-slot=button]]:shadow-none",
23
+ "[&_[data-slot=button]:first-child]:rounded-s-md [&_[data-slot=button]:last-child]:rounded-e-md",
24
+ "data-[orientation=vertical]:[&_[data-slot=button]:first-child]:rounded-se-none data-[orientation=vertical]:[&_[data-slot=button]:first-child]:rounded-ss-md",
25
+ "data-[orientation=vertical]:[&_[data-slot=button]:last-child]:rounded-es-md data-[orientation=vertical]:[&_[data-slot=button]:last-child]:rounded-ee-none",
26
+ @attrs.delete(:class)
27
+ )
28
+ @attrs.merge(
29
+ data_slot: "button-group",
30
+ data_orientation: @orientation,
31
+ role: "group",
32
+ class: classes
33
+ )
34
+ end
35
+ end
36
+
37
+ class ButtonGroupText < Base
38
+ def initialize(**attrs)
39
+ @attrs = attrs
40
+ end
41
+
42
+ def view_template(&block)
43
+ span(**build_attrs, &block)
44
+ end
45
+
46
+ private
47
+
48
+ def build_attrs
49
+ classes = cn(
50
+ "inline-flex items-center justify-center border px-3 text-sm font-medium",
51
+ "border-input bg-background text-foreground",
52
+ @attrs.delete(:class)
53
+ )
54
+ @attrs.merge(data_slot: "button-group-text", class: classes)
55
+ end
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,133 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Shadcn
4
+ module UI
5
+ # Port of shadcn/ui Card
6
+ # Sub-components: Card, CardHeader, CardTitle, CardDescription, CardAction, CardContent, CardFooter
7
+ class Card < 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
+ "bg-card text-card-foreground flex flex-col gap-6 rounded-xl border py-6 shadow-sm",
21
+ @attrs.delete(:class)
22
+ )
23
+ @attrs.merge(data_slot: "card", class: classes)
24
+ end
25
+ end
26
+
27
+ class CardHeader < 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
+ "@container/card-header grid auto-rows-min grid-rows-[auto_auto] items-start gap-1.5 px-6",
41
+ "has-data-[slot=card-action]:grid-cols-[1fr_auto] [.border-b]:pb-6",
42
+ @attrs.delete(:class)
43
+ )
44
+ @attrs.merge(data_slot: "card-header", class: classes)
45
+ end
46
+ end
47
+
48
+ class CardTitle < Base
49
+ def initialize(**attrs)
50
+ @attrs = attrs
51
+ end
52
+
53
+ def view_template(&block)
54
+ div(**build_attrs, &block)
55
+ end
56
+
57
+ private
58
+
59
+ def build_attrs
60
+ classes = cn("leading-none font-semibold", @attrs.delete(:class))
61
+ @attrs.merge(data_slot: "card-title", class: classes)
62
+ end
63
+ end
64
+
65
+ class CardDescription < Base
66
+ def initialize(**attrs)
67
+ @attrs = attrs
68
+ end
69
+
70
+ def view_template(&block)
71
+ div(**build_attrs, &block)
72
+ end
73
+
74
+ private
75
+
76
+ def build_attrs
77
+ classes = cn("text-muted-foreground text-sm", @attrs.delete(:class))
78
+ @attrs.merge(data_slot: "card-description", class: classes)
79
+ end
80
+ end
81
+
82
+ class CardAction < Base
83
+ def initialize(**attrs)
84
+ @attrs = attrs
85
+ end
86
+
87
+ def view_template(&block)
88
+ div(**build_attrs, &block)
89
+ end
90
+
91
+ private
92
+
93
+ def build_attrs
94
+ classes = cn("col-start-2 row-span-2 row-start-1 self-start justify-self-end", @attrs.delete(:class))
95
+ @attrs.merge(data_slot: "card-action", class: classes)
96
+ end
97
+ end
98
+
99
+ class CardContent < Base
100
+ def initialize(**attrs)
101
+ @attrs = attrs
102
+ end
103
+
104
+ def view_template(&block)
105
+ div(**build_attrs, &block)
106
+ end
107
+
108
+ private
109
+
110
+ def build_attrs
111
+ classes = cn("px-6", @attrs.delete(:class))
112
+ @attrs.merge(data_slot: "card-content", class: classes)
113
+ end
114
+ end
115
+
116
+ class CardFooter < Base
117
+ def initialize(**attrs)
118
+ @attrs = attrs
119
+ end
120
+
121
+ def view_template(&block)
122
+ div(**build_attrs, &block)
123
+ end
124
+
125
+ private
126
+
127
+ def build_attrs
128
+ classes = cn("flex items-center px-6 [.border-t]:pt-6", @attrs.delete(:class))
129
+ @attrs.merge(data_slot: "card-footer", class: classes)
130
+ end
131
+ end
132
+ end
133
+ end
@@ -0,0 +1,72 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Shadcn
4
+ module UI
5
+ # Port of shadcn/ui Checkbox
6
+ class Checkbox < Base
7
+ def initialize(checked: false, name: nil, **attrs)
8
+ @checked = checked
9
+ @name = name
10
+ @attrs = attrs
11
+ end
12
+
13
+ def view_template(&block)
14
+ label(data_controller: "shadcn--checkbox", data_shadcn__checkbox_checked_value: @checked.to_s, class: "inline-flex items-center gap-2 cursor-pointer") do
15
+ if @name
16
+ input(type: "hidden", name: @name, value: @checked ? "1" : "0", data_shadcn__checkbox_target: "input")
17
+ end
18
+ button(**build_attrs) do
19
+ span(
20
+ data_slot: "checkbox-indicator",
21
+ data_shadcn__checkbox_target: "indicator",
22
+ hidden: !@checked,
23
+ class: "grid place-content-center text-current transition-none"
24
+ ) do
25
+ svg(
26
+ xmlns: "http://www.w3.org/2000/svg",
27
+ width: "14", height: "14",
28
+ viewbox: "0 0 24 24",
29
+ fill: "none",
30
+ stroke: "currentColor",
31
+ stroke_width: "2",
32
+ stroke_linecap: "round",
33
+ stroke_linejoin: "round",
34
+ class: "size-3.5"
35
+ ) do |s|
36
+ s.path(d: "M20 6 9 17l-5-5")
37
+ end
38
+ end
39
+ end
40
+ if block
41
+ span(data_slot: "checkbox-label", class: "text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70", &block)
42
+ end
43
+ end
44
+ end
45
+
46
+ private
47
+
48
+ def build_attrs
49
+ classes = cn(
50
+ "peer size-4 shrink-0 rounded-[4px] border border-input shadow-xs",
51
+ "transition-shadow outline-none",
52
+ "focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50",
53
+ "disabled:cursor-not-allowed disabled:opacity-50",
54
+ "aria-invalid:border-destructive aria-invalid:ring-destructive/20",
55
+ "data-[state=checked]:border-primary data-[state=checked]:bg-primary data-[state=checked]:text-primary-foreground",
56
+ "dark:bg-input/30 dark:aria-invalid:ring-destructive/40 dark:data-[state=checked]:bg-primary",
57
+ @attrs.delete(:class)
58
+ )
59
+ @attrs.merge(
60
+ data_slot: "checkbox",
61
+ data_shadcn__checkbox_target: "button",
62
+ data_action: "click->shadcn--checkbox#toggle",
63
+ data_state: @checked ? "checked" : "unchecked",
64
+ role: "checkbox",
65
+ type: "button",
66
+ aria_checked: @checked,
67
+ class: classes
68
+ )
69
+ end
70
+ end
71
+ end
72
+ end
@@ -0,0 +1,76 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Shadcn
4
+ module UI
5
+ # Port of shadcn/ui Collapsible
6
+ # Uses data-state="open"/"closed" for JS interactivity
7
+ class Collapsible < Base
8
+ def initialize(open: false, **attrs)
9
+ @open = open
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
+ @attrs.merge(
21
+ data_slot: "collapsible",
22
+ data_state: @open ? "open" : "closed",
23
+ data_controller: "shadcn--collapsible",
24
+ data_shadcn__collapsible_open_value: @open
25
+ )
26
+ end
27
+ end
28
+
29
+ class CollapsibleTrigger < Base
30
+ def initialize(**attrs)
31
+ @attrs = attrs
32
+ end
33
+
34
+ def view_template(&block)
35
+ div(**build_attrs, &block)
36
+ end
37
+
38
+ private
39
+
40
+ def build_attrs
41
+ @attrs.merge(
42
+ data_slot: "collapsible-trigger",
43
+ data_shadcn__collapsible_target: "trigger",
44
+ data_action: "click->shadcn--collapsible#toggle",
45
+ role: "button",
46
+ style: "display: inline-block"
47
+ )
48
+ end
49
+ end
50
+
51
+ class CollapsibleContent < Base
52
+ def initialize(**attrs)
53
+ @attrs = attrs
54
+ end
55
+
56
+ def view_template(&block)
57
+ div(**build_attrs, &block)
58
+ end
59
+
60
+ private
61
+
62
+ def build_attrs
63
+ classes = cn(
64
+ "overflow-hidden",
65
+ "data-[state=closed]:animate-collapsible-up data-[state=open]:animate-collapsible-down",
66
+ @attrs.delete(:class)
67
+ )
68
+ @attrs.merge(
69
+ data_slot: "collapsible-content",
70
+ data_shadcn__collapsible_target: "content",
71
+ class: classes
72
+ )
73
+ end
74
+ end
75
+ end
76
+ end
@@ -0,0 +1,229 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Shadcn
4
+ module UI
5
+ # Port of shadcn/ui Combobox
6
+ # Searchable select with filtering
7
+ # Wired to shadcn--combobox Stimulus controller
8
+ class Combobox < Base
9
+ def initialize(name: nil, **attrs)
10
+ @name = name
11
+ @attrs = attrs
12
+ end
13
+
14
+ def view_template(&block)
15
+ div(**build_attrs) do
16
+ if @name
17
+ input(type: "hidden", name: @name, value: "", data_shadcn__combobox_target: "hiddenInput")
18
+ end
19
+ yield if block_given?
20
+ end
21
+ end
22
+
23
+ private
24
+
25
+ def build_attrs
26
+ classes = cn("relative", @attrs.delete(:class))
27
+ @attrs.merge(
28
+ data_slot: "combobox",
29
+ data_controller: "shadcn--combobox",
30
+ data_action: "click@window->shadcn--combobox#hide keydown.esc@window->shadcn--combobox#hideOnEscape keydown->shadcn--combobox#keydown",
31
+ class: classes
32
+ )
33
+ end
34
+ end
35
+
36
+ class ComboboxTrigger < Base
37
+ def initialize(**attrs)
38
+ @attrs = attrs
39
+ end
40
+
41
+ def view_template(&block)
42
+ button(**build_attrs) do
43
+ yield if block_given?
44
+ # ChevronsUpDown icon
45
+ svg(xmlns: "http://www.w3.org/2000/svg", width: "16", height: "16",
46
+ viewbox: "0 0 24 24", fill: "none", stroke: "currentColor",
47
+ stroke_width: "2", class: "ml-2 size-4 shrink-0 opacity-50") do |s|
48
+ s.path(d: "m7 15 5 5 5-5")
49
+ s.path(d: "m7 9 5-5 5 5")
50
+ end
51
+ end
52
+ end
53
+
54
+ private
55
+
56
+ def build_attrs
57
+ classes = cn(
58
+ "flex h-9 w-full items-center justify-between gap-2 rounded-md border border-input bg-transparent px-3",
59
+ "text-sm shadow-xs transition-[color,box-shadow] outline-none whitespace-nowrap",
60
+ "focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50",
61
+ "disabled:cursor-not-allowed disabled:opacity-50",
62
+ "dark:bg-input/30",
63
+ "[&_svg]:pointer-events-none [&_svg]:shrink-0",
64
+ @attrs.delete(:class)
65
+ )
66
+ @attrs.merge(
67
+ data_slot: "combobox-trigger",
68
+ data_action: "click->shadcn--combobox#toggle",
69
+ type: "button",
70
+ role: "combobox",
71
+ class: classes
72
+ )
73
+ end
74
+ end
75
+
76
+ class ComboboxValue < Base
77
+ def initialize(placeholder: "Select...", **attrs)
78
+ @placeholder = placeholder
79
+ @attrs = attrs
80
+ end
81
+
82
+ def view_template(&block)
83
+ span(**build_attrs) do
84
+ if block_given?
85
+ yield
86
+ else
87
+ plain @placeholder
88
+ end
89
+ end
90
+ end
91
+
92
+ private
93
+
94
+ def build_attrs
95
+ @attrs.merge(
96
+ data_slot: "combobox-value",
97
+ data_shadcn__combobox_target: "value"
98
+ )
99
+ end
100
+ end
101
+
102
+ class ComboboxContent < Base
103
+ def initialize(**attrs)
104
+ @attrs = attrs
105
+ end
106
+
107
+ def view_template(&block)
108
+ div(**build_attrs, &block)
109
+ end
110
+
111
+ private
112
+
113
+ def build_attrs
114
+ classes = cn(
115
+ "z-50 w-full min-w-[8rem] overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-md",
116
+ "data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95",
117
+ "data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95",
118
+ @attrs.delete(:class)
119
+ )
120
+ @attrs.merge(
121
+ data_slot: "combobox-content",
122
+ data_shadcn__combobox_target: "content",
123
+ role: "listbox",
124
+ hidden: true,
125
+ class: classes
126
+ )
127
+ end
128
+ end
129
+
130
+ class ComboboxInput < Base
131
+ def initialize(placeholder: "Search...", **attrs)
132
+ @placeholder = placeholder
133
+ @attrs = attrs
134
+ end
135
+
136
+ def view_template
137
+ div(class: "flex items-center border-b px-3") do
138
+ svg(xmlns: "http://www.w3.org/2000/svg", width: "16", height: "16",
139
+ viewbox: "0 0 24 24", fill: "none", stroke: "currentColor",
140
+ stroke_width: "2", class: "mr-2 size-4 shrink-0 opacity-50") do |s|
141
+ s.circle(cx: "11", cy: "11", r: "8")
142
+ s.path(d: "m21 21-4.3-4.3")
143
+ end
144
+ input(**build_attrs)
145
+ end
146
+ end
147
+
148
+ private
149
+
150
+ def build_attrs
151
+ classes = cn(
152
+ "flex h-10 w-full rounded-md bg-transparent py-3 text-sm outline-none",
153
+ "placeholder:text-muted-foreground disabled:cursor-not-allowed disabled:opacity-50",
154
+ @attrs.delete(:class)
155
+ )
156
+ @attrs.merge(
157
+ data_slot: "combobox-input",
158
+ data_shadcn__combobox_target: "input",
159
+ data_action: "input->shadcn--combobox#filter keydown->shadcn--combobox#keydown",
160
+ type: "text",
161
+ placeholder: @placeholder,
162
+ class: classes
163
+ )
164
+ end
165
+ end
166
+
167
+ class ComboboxEmpty < Base
168
+ def initialize(**attrs)
169
+ @attrs = attrs
170
+ end
171
+
172
+ def view_template(&block)
173
+ div(**build_attrs) do
174
+ if block_given?
175
+ yield
176
+ else
177
+ plain "No results found."
178
+ end
179
+ end
180
+ end
181
+
182
+ private
183
+
184
+ def build_attrs
185
+ classes = cn("py-6 text-center text-sm text-muted-foreground", @attrs.delete(:class))
186
+ @attrs.merge(
187
+ data_slot: "combobox-empty",
188
+ data_shadcn__combobox_target: "empty",
189
+ hidden: true,
190
+ class: classes
191
+ )
192
+ end
193
+ end
194
+
195
+ class ComboboxItem < Base
196
+ def initialize(value:, **attrs)
197
+ @value = value
198
+ @attrs = attrs
199
+ end
200
+
201
+ def view_template(&block)
202
+ div(**build_attrs, &block)
203
+ end
204
+
205
+ private
206
+
207
+ def build_attrs
208
+ classes = cn(
209
+ "relative flex cursor-default items-center gap-2 rounded-sm px-2 py-1.5 text-sm",
210
+ "outline-hidden select-none",
211
+ "data-[state=checked]:font-medium",
212
+ "focus:bg-accent focus:text-accent-foreground",
213
+ "hover:bg-accent hover:text-accent-foreground",
214
+ "data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
215
+ @attrs.delete(:class)
216
+ )
217
+ @attrs.merge(
218
+ data_slot: "combobox-item",
219
+ data_shadcn__combobox_target: "item",
220
+ data_action: "click->shadcn--combobox#selectItem",
221
+ data_value: @value,
222
+ role: "option",
223
+ tabindex: "-1",
224
+ class: classes
225
+ )
226
+ end
227
+ end
228
+ end
229
+ end