Skip to content

Commit 30919b2

Browse files
authored
[Feature] Combobox shadcn visual revamp (#355)
* chore: ignore .worktrees directory * feat(combobox): revamp visual design to match shadcn/ui * fix(combobox): restore selected, keyboard highlight, and disabled states * feat(combobox): add badge management to Stimulus controller * fix(combobox): address controller quality issues * docs(combobox): update examples for shadcn-style revamp * feat(combobox): add ComboboxInputTrigger, fix badge border, clear button style * feat(combobox): JS controller support for ComboboxInputTrigger + auto-highlight * docs(combobox): 8 examples matching shadcn docs * fix(combobox): remove border and outline from ComboboxInputTrigger input * feat(combobox): shadcn visual revamp with chips, keyboard nav, and accessibility
1 parent 856136f commit 30919b2

17 files changed

Lines changed: 848 additions & 172 deletions

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
.DS_Store
22
node_modules
33
/app/assets/builds/*
4+
.worktrees

lib/ruby_ui/combobox/combobox.rb

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,13 @@ def default_attrs
1919
data: {
2020
controller: "ruby-ui--combobox",
2121
ruby_ui__combobox_term_value: @term,
22-
action: "turbo:morph@window->ruby-ui--combobox#updateTriggerContent"
22+
action: %w[
23+
turbo:morph@window->ruby-ui--combobox#updateTriggerContent
24+
keydown.down->ruby-ui--combobox#keyDownPressed
25+
keydown.up->ruby-ui--combobox#keyUpPressed
26+
keydown.enter->ruby-ui--combobox#keyEnterPressed
27+
keydown.esc->ruby-ui--combobox#closePopover:prevent
28+
]
2329
}
2430
}
2531
end
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
# frozen_string_literal: true
2+
3+
module RubyUI
4+
class ComboboxBadge < Base
5+
def view_template(&)
6+
span(**attrs, &)
7+
end
8+
9+
private
10+
11+
def default_attrs
12+
{
13+
class: "inline-flex items-center gap-1 rounded-md border bg-secondary px-1.5 py-0.5 text-xs font-medium text-secondary-foreground"
14+
}
15+
end
16+
end
17+
end
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
# frozen_string_literal: true
2+
3+
module RubyUI
4+
class ComboboxBadgeTrigger < Base
5+
def initialize(placeholder: "", clear_button: false, **)
6+
@placeholder = placeholder
7+
@clear_button = clear_button
8+
super(**)
9+
end
10+
11+
def view_template(&)
12+
div(**attrs) do
13+
div(data: {ruby_ui__combobox_target: "badgeContainer"}, class: "hidden")
14+
input(
15+
type: "text",
16+
class: "flex-1 min-w-8 bg-transparent border-0 px-0 outline-none focus:ring-0 placeholder:text-muted-foreground text-sm",
17+
autocomplete: "off",
18+
autocorrect: "off",
19+
spellcheck: "false",
20+
placeholder: @placeholder,
21+
data: {
22+
ruby_ui__combobox_target: "badgeInput",
23+
action: "keyup->ruby-ui--combobox#filterItems input->ruby-ui--combobox#filterItems keydown.backspace->ruby-ui--combobox#handleBadgeInputBackspace"
24+
}
25+
)
26+
render ComboboxClearButton.new if @clear_button
27+
end
28+
end
29+
30+
private
31+
32+
# JS-toggled classes (referenced here so Tailwind compiles them): h-auto min-h-9 pt-1.5
33+
def default_attrs
34+
{
35+
class: "flex h-9 w-full flex-wrap items-center gap-1 rounded-md border border-input bg-background px-3 text-sm ring-offset-background focus-within:ring-2 focus-within:ring-ring focus-within:ring-offset-2 cursor-text",
36+
data: {
37+
ruby_ui__combobox_target: "trigger",
38+
action: "click->ruby-ui--combobox#openPopover focusin->ruby-ui--combobox#openPopover"
39+
},
40+
aria: {
41+
haspopup: "listbox",
42+
expanded: "false"
43+
}
44+
}
45+
end
46+
end
47+
end

lib/ruby_ui/combobox/combobox_checkbox.rb

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,7 @@ def view_template
1010

1111
def default_attrs
1212
{
13-
class: [
14-
"peer h-4 w-4 shrink-0 rounded-sm border border-primary ring-offset-background accent-primary",
15-
"disabled:cursor-not-allowed disabled:opacity-50",
16-
"checked:bg-primary checked:text-primary-foreground",
17-
"aria-disabled:cursor-not-allowed aria-disabled:opacity-50 aria-disabled:pointer-events-none",
18-
"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2"
19-
],
13+
class: "peer sr-only",
2014
data: {
2115
ruby_ui__combobox_target: "input",
2216
action: "ruby-ui--combobox#inputChanged"
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
# frozen_string_literal: true
2+
3+
module RubyUI
4+
class ComboboxClearButton < Base
5+
def view_template
6+
button(**attrs) do
7+
svg(
8+
xmlns: "http://www.w3.org/2000/svg",
9+
width: "24",
10+
height: "24",
11+
viewbox: "0 0 24 24",
12+
fill: "none",
13+
stroke: "currentColor",
14+
stroke_width: "2",
15+
stroke_linecap: "round",
16+
stroke_linejoin: "round",
17+
class: "size-3.5"
18+
) do |s|
19+
s.path(d: "M18 6 6 18")
20+
s.path(d: "m6 6 12 12")
21+
end
22+
end
23+
end
24+
25+
private
26+
27+
def default_attrs
28+
{
29+
type: "button",
30+
class: "ml-auto shrink-0 rounded-sm text-muted-foreground hover:text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring hidden",
31+
aria: {label: "Clear selection"},
32+
data: {
33+
ruby_ui__combobox_target: "clearButton",
34+
# JS implementation in combobox_controller.js
35+
action: "ruby-ui--combobox#clearAll"
36+
}
37+
}
38+
end
39+
end
40+
end

0 commit comments

Comments
 (0)