shadcn_phlexcomponents 0.1.11 → 0.1.14

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 (63) hide show
  1. checksums.yaml +4 -4
  2. data/app/javascript/controllers/accordion_controller.ts +65 -62
  3. data/app/javascript/controllers/alert_dialog_controller.ts +12 -0
  4. data/app/javascript/controllers/avatar_controller.ts +7 -2
  5. data/app/javascript/controllers/checkbox_controller.ts +11 -4
  6. data/app/javascript/controllers/collapsible_controller.ts +12 -5
  7. data/app/javascript/controllers/combobox_controller.ts +270 -39
  8. data/app/javascript/controllers/command_controller.ts +223 -51
  9. data/app/javascript/controllers/date_picker_controller.ts +185 -125
  10. data/app/javascript/controllers/date_range_picker_controller.ts +89 -79
  11. data/app/javascript/controllers/dialog_controller.ts +59 -57
  12. data/app/javascript/controllers/dropdown_menu_controller.ts +212 -36
  13. data/app/javascript/controllers/dropdown_menu_sub_controller.ts +31 -29
  14. data/app/javascript/controllers/form_field_controller.ts +6 -1
  15. data/app/javascript/controllers/hover_card_controller.ts +36 -26
  16. data/app/javascript/controllers/loading_button_controller.ts +6 -1
  17. data/app/javascript/controllers/popover_controller.ts +42 -65
  18. data/app/javascript/controllers/progress_controller.ts +9 -3
  19. data/app/javascript/controllers/radio_group_controller.ts +16 -9
  20. data/app/javascript/controllers/select_controller.ts +206 -65
  21. data/app/javascript/controllers/slider_controller.ts +23 -16
  22. data/app/javascript/controllers/switch_controller.ts +11 -4
  23. data/app/javascript/controllers/tabs_controller.ts +26 -18
  24. data/app/javascript/controllers/theme_switcher_controller.ts +6 -1
  25. data/app/javascript/controllers/toast_container_controller.ts +6 -1
  26. data/app/javascript/controllers/toast_controller.ts +7 -1
  27. data/app/javascript/controllers/toggle_controller.ts +28 -0
  28. data/app/javascript/controllers/toggle_group_controller.ts +28 -0
  29. data/app/javascript/controllers/tooltip_controller.ts +43 -31
  30. data/app/javascript/shadcn_phlexcomponents.ts +29 -25
  31. data/app/javascript/utils/command.ts +544 -0
  32. data/app/javascript/utils/floating_ui.ts +196 -0
  33. data/app/javascript/utils/index.ts +417 -0
  34. data/app/stylesheets/date_picker.css +118 -0
  35. data/lib/shadcn_phlexcomponents/alias.rb +3 -0
  36. data/lib/shadcn_phlexcomponents/components/accordion.rb +2 -1
  37. data/lib/shadcn_phlexcomponents/components/alert_dialog.rb +18 -15
  38. data/lib/shadcn_phlexcomponents/components/base.rb +14 -0
  39. data/lib/shadcn_phlexcomponents/components/collapsible.rb +1 -2
  40. data/lib/shadcn_phlexcomponents/components/combobox.rb +87 -57
  41. data/lib/shadcn_phlexcomponents/components/command.rb +77 -47
  42. data/lib/shadcn_phlexcomponents/components/date_picker.rb +25 -81
  43. data/lib/shadcn_phlexcomponents/components/date_range_picker.rb +21 -4
  44. data/lib/shadcn_phlexcomponents/components/dialog.rb +14 -12
  45. data/lib/shadcn_phlexcomponents/components/dropdown_menu.rb +5 -4
  46. data/lib/shadcn_phlexcomponents/components/dropdown_menu_sub.rb +2 -1
  47. data/lib/shadcn_phlexcomponents/components/form/form_combobox.rb +64 -0
  48. data/lib/shadcn_phlexcomponents/components/form.rb +14 -0
  49. data/lib/shadcn_phlexcomponents/components/hover_card.rb +3 -2
  50. data/lib/shadcn_phlexcomponents/components/popover.rb +3 -3
  51. data/lib/shadcn_phlexcomponents/components/select.rb +10 -25
  52. data/lib/shadcn_phlexcomponents/components/sheet.rb +15 -11
  53. data/lib/shadcn_phlexcomponents/components/table.rb +1 -1
  54. data/lib/shadcn_phlexcomponents/components/tabs.rb +1 -1
  55. data/lib/shadcn_phlexcomponents/components/toast_container.rb +1 -1
  56. data/lib/shadcn_phlexcomponents/components/toggle.rb +54 -0
  57. data/lib/shadcn_phlexcomponents/components/tooltip.rb +3 -2
  58. data/lib/shadcn_phlexcomponents/engine.rb +1 -5
  59. data/lib/shadcn_phlexcomponents/version.rb +1 -1
  60. metadata +9 -4
  61. data/app/javascript/controllers/command_root_controller.ts +0 -355
  62. data/app/javascript/controllers/dropdown_menu_root_controller.ts +0 -234
  63. data/app/javascript/utils.ts +0 -437
@@ -1,3 +1,48 @@
1
+ [data-vc='calendar'] {
2
+ @apply relative flex flex-col outline-none;
3
+ }
4
+
5
+ [data-vc='controls'] {
6
+ @apply absolute z-20 -left-1 -right-1 -top-1.5 flex justify-between items-center pt-1.5 px-1 pointer-events-none box-content;
7
+ }
8
+
9
+ [data-vc='grid'] {
10
+ @apply grid gap-4 grid-cols-1 md:grid-cols-2;
11
+ }
12
+
13
+ [data-vc='column'] {
14
+ @apply flex flex-col;
15
+ }
16
+
17
+ [data-vc='header'] {
18
+ @apply relative flex items-center mb-4;
19
+ }
20
+
21
+ [data-vc-header='content'] {
22
+ @apply grid grid-flow-col gap-x-1 auto-cols-max items-center justify-center px-4 whitespace-pre-wrap grow;
23
+ }
24
+
25
+ [data-vc='month'],
26
+ [data-vc='year'] {
27
+ @apply inline-flex items-center justify-center whitespace-nowrap font-medium transition-all;
28
+ @apply disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4;
29
+ @apply shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px];
30
+ @apply aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive border;
31
+ @apply shadow-xs hover:bg-accent hover:text-accent-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50;
32
+ @apply rounded-md gap-1.5 px-3 has-[>svg]:px-2.5 text-xs h-7 bg-transparent;
33
+ }
34
+
35
+ [data-vc-arrow='prev'],
36
+ [data-vc-arrow='next'] {
37
+ @apply inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium;
38
+ @apply transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none;
39
+ @apply [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring;
40
+ @apply focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20;
41
+ @apply dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive border shadow-xs hover:bg-accent;
42
+ @apply hover:text-accent-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50 pointer-events-auto;
43
+ @apply size-7 bg-transparent p-0 opacity-50 hover:opacity-100;
44
+ }
45
+
1
46
  [data-vc-arrow='next']:before {
2
47
  @apply bg-current size-4 mask-size-[auto_16px] mask-center mask-no-repeat absolute content-[''];
3
48
  mask-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-chevron-right-icon lucide-chevron-right"><path d="m9 18 6-6-6-6"/></svg>');
@@ -8,6 +53,79 @@
8
53
  mask-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-chevron-left-icon lucide-chevron-left"><path d="m15 18-6-6 6-6"/></svg>');
9
54
  }
10
55
 
56
+ [data-vc='wrapper'] {
57
+ @apply flex items-center content-center h-full;
58
+ }
59
+
60
+ [data-vc='content'] {
61
+ @apply flex flex-col grow h-full;
62
+ }
63
+
64
+ [data-vc='months'] {
65
+ @apply grid gap-2 grid-cols-4 items-center grow;
66
+ }
67
+
68
+ [data-vc='years'] {
69
+ @apply grid gap-2 grid-cols-5 items-center grow;
70
+ }
71
+
72
+ [data-vc-months-month],
73
+ [data-vc-years-year] {
74
+ @apply inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-all;
75
+ @apply disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4;
76
+ @apply shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px];
77
+ @apply aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive;
78
+ @apply hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50 h-9 px-4 py-2 has-[>svg]:px-3;
79
+ @apply aria-[selected=true]:text-primary-foreground aria-[selected=true]:bg-primary;
80
+ @apply aria-[selected=true]:hover:text-primary-foreground aria-[selected=true]:hover:bg-primary;
81
+ }
82
+
83
+ [data-vc='week'] {
84
+ @apply grid mb-2 grid-cols-[repeat(7,_1fr)] justify-items-center items-center text-center;
85
+ }
86
+
87
+ [data-vc-week-day] {
88
+ @apply text-muted-foreground rounded-md w-8 font-normal text-[0.8rem];
89
+ }
90
+
91
+ [data-vc='dates'] {
92
+ @apply grid gap-y-2 grid-cols-[repeat(7,_1fr)] justify-items-center items-center;
93
+ }
94
+
95
+ [data-vc-date-btn] {
96
+ @apply inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm transition-all;
97
+ @apply disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4;
98
+ @apply shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px];
99
+ @apply aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive;
100
+ @apply hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50 has-[>svg]:px-3 size-8 p-0 font-normal;
101
+ @apply aria-[disabled]:text-muted-foreground aria-[disabled]:opacity-50 aria-[disabled]:pointer-events-none;
102
+ }
103
+
104
+ [data-vc-date-selected] [data-vc-date-btn] {
105
+ @apply bg-primary text-primary-foreground hover:bg-primary hover:text-primary-foreground focus:bg-primary focus:text-primary-foreground;
106
+ }
107
+
108
+ [data-vc-date-today] [data-vc-date-btn] {
109
+ @apply bg-accent text-accent-foreground;
110
+ }
111
+
112
+ [data-vc-date-month='prev'] [data-vc-date-btn],
113
+ [data-vc-date-month='next'] [data-vc-date-btn] {
114
+ @apply text-muted-foreground;
115
+ }
116
+
117
+ [data-vc-date-selected='middle'][data-vc-date-selected] [data-vc-date-btn] {
118
+ @apply bg-accent text-accent-foreground;
119
+ }
120
+
121
+ [data-vc-date-selected='first'][data-vc-date-selected] [data-vc-date-btn] {
122
+ @apply bg-primary text-primary-foreground;
123
+ }
124
+
125
+ [data-vc-date-hover] [data-vc-date-btn] {
126
+ @apply bg-accent text-accent-foreground;
127
+ }
128
+
11
129
  [data-vc='grid'][data-vc-grid='hidden'] [data-vc='column'] {
12
130
  pointer-events: none;
13
131
  opacity: 0.3;
@@ -14,7 +14,9 @@ CheckboxGroup = ShadcnPhlexcomponents::CheckboxGroup
14
14
  Checkbox = ShadcnPhlexcomponents::Checkbox
15
15
  Collapsible = ShadcnPhlexcomponents::Collapsible
16
16
  Command = ShadcnPhlexcomponents::Command
17
+ CommandItem = ShadcnPhlexcomponents::CommandItem
17
18
  Combobox = ShadcnPhlexcomponents::Combobox
19
+ ComboboxItem = ShadcnPhlexcomponents::ComboboxItem
18
20
  DatePicker = ShadcnPhlexcomponents::DatePicker
19
21
  DateRangePicker = ShadcnPhlexcomponents::DateRangePicker
20
22
  Dialog = ShadcnPhlexcomponents::Dialog
@@ -42,4 +44,5 @@ Textarea = ShadcnPhlexcomponents::Textarea
42
44
  ThemeSwitcher = ShadcnPhlexcomponents::ThemeSwitcher
43
45
  Toast = ShadcnPhlexcomponents::Toast
44
46
  ToastContainer = ShadcnPhlexcomponents::ToastContainer
47
+ Toggle = ShadcnPhlexcomponents::Toggle
45
48
  Tooltip = ShadcnPhlexcomponents::Tooltip
@@ -81,6 +81,7 @@ module ShadcnPhlexcomponents
81
81
  },
82
82
  data: {
83
83
  state: "closed",
84
+ accordion_target: "trigger",
84
85
  action: <<~HEREDOC,
85
86
  click->accordion#toggle
86
87
  keydown.up->accordion#focusTrigger:prevent
@@ -119,7 +120,7 @@ module ShadcnPhlexcomponents
119
120
  },
120
121
  data: {
121
122
  state: "closed",
122
- shadcn_phlexcomponents: "accordion-content-container",
123
+ accordion_target: "content"
123
124
  },
124
125
  ) do
125
126
  div(**@attributes, &)
@@ -2,7 +2,7 @@
2
2
 
3
3
  module ShadcnPhlexcomponents
4
4
  class AlertDialog < Base
5
- class_variants(base: "inline-block max-w-fit")
5
+ class_variants(base: "inline-flex max-w-fit")
6
6
 
7
7
  def initialize(open: false, **attributes)
8
8
  @open = open
@@ -49,14 +49,18 @@ module ShadcnPhlexcomponents
49
49
  def default_attributes
50
50
  {
51
51
  data: {
52
- controller: "dialog",
53
- dialog_is_open_value: @open.to_s,
54
- },
55
- }
52
+ controller: "alert-dialog",
53
+ alert_dialog_is_open_value: @open.to_s
54
+ }
55
+ }
56
56
  end
57
57
 
58
58
  def view_template(&)
59
- div(**@attributes, &)
59
+ div(**@attributes) do
60
+ overlay("alert-dialog")
61
+
62
+ yield
63
+ end
60
64
  end
61
65
  end
62
66
 
@@ -75,10 +79,10 @@ module ShadcnPhlexcomponents
75
79
  expanded: "false",
76
80
  controls: "#{@aria_id}-content",
77
81
  },
78
- data: {
79
- action: "click->dialog#open",
80
- dialog_target: "trigger",
82
+ data: {
81
83
  as_child: @as_child.to_s,
84
+ alert_dialog_target: "trigger",
85
+ action: "click->alert-dialog#open"
82
86
  },
83
87
  }
84
88
  end
@@ -106,7 +110,7 @@ module ShadcnPhlexcomponents
106
110
  data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95
107
111
  data-[state=open]:zoom-in-95 fixed top-[50%] left-[50%] z-50 grid w-full max-w-[calc(100%-2rem)]
108
112
  translate-x-[-50%] translate-y-[-50%] gap-4 rounded-lg border p-6 shadow-lg duration-200 sm:max-w-lg
109
- pointer-events-auto
113
+ pointer-events-auto outline-none
110
114
  HEREDOC
111
115
  )
112
116
 
@@ -126,14 +130,13 @@ module ShadcnPhlexcomponents
126
130
  },
127
131
  data: {
128
132
  state: "closed",
129
- dialog_target: "content",
133
+ alert_dialog_target: "content"
130
134
  },
131
135
  }
132
136
  end
133
137
 
134
138
  def view_template(&)
135
- @class = @attributes.delete(:class)
136
- div(class: "#{@class} hidden", **@attributes, &)
139
+ div(style: { display: "none" }, **@attributes, &)
137
140
  end
138
141
  end
139
142
 
@@ -201,7 +204,7 @@ module ShadcnPhlexcomponents
201
204
  def default_attributes
202
205
  {
203
206
  data: {
204
- action: "click->dialog#close",
207
+ action: "click->alert-dialog#close",
205
208
  },
206
209
  }
207
210
  end
@@ -222,7 +225,7 @@ module ShadcnPhlexcomponents
222
225
  def default_attributes
223
226
  {
224
227
  data: {
225
- action: "click->dialog#close",
228
+ action: "click->alert-dialog#close",
226
229
  },
227
230
  }
228
231
  end
@@ -141,5 +141,19 @@ module ShadcnPhlexcomponents
141
141
  disabled
142
142
  end
143
143
  end
144
+
145
+ def overlay(component)
146
+ div(
147
+ style: { display: "none" },
148
+ class: "data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 fixed inset-0 z-50 bg-black/50 pointer-events-auto",
149
+ aria: {
150
+ hidden: true,
151
+ },
152
+ data: {
153
+ state: "closed",
154
+ "#{component}-target" => "overlay"
155
+ }
156
+ )
157
+ end
144
158
  end
145
159
  end
@@ -84,8 +84,7 @@ module ShadcnPhlexcomponents
84
84
  end
85
85
 
86
86
  def view_template(&)
87
- @class = @attributes.delete(:class)
88
- div(class: "#{@class} hidden", **@attributes, &)
87
+ div(style: { display: "none" }, **@attributes, &)
89
88
  end
90
89
  end
91
90
  end
@@ -9,18 +9,24 @@ module ShadcnPhlexcomponents
9
9
  name: nil,
10
10
  value: nil,
11
11
  placeholder: nil,
12
- dir: "ltr",
13
12
  include_blank: false,
14
13
  disabled: false,
14
+ search_path: nil,
15
+ search_error_text: "Something went wrong, please try again.",
16
+ search_empty_text: "No results found",
17
+ search_placeholder_text: "Search...",
15
18
  **attributes
16
19
  )
17
20
  @id = id
18
21
  @name = name
19
22
  @value = value
20
23
  @placeholder = placeholder
21
- @dir = dir
22
24
  @include_blank = include_blank
23
25
  @disabled = disabled
26
+ @search_path = search_path
27
+ @search_error_text = search_error_text
28
+ @search_empty_text = search_empty_text
29
+ @search_placeholder_text = search_placeholder_text
24
30
  @aria_id = "combobox-#{SecureRandom.hex(5)}"
25
31
  super(**attributes)
26
32
  end
@@ -29,7 +35,6 @@ module ShadcnPhlexcomponents
29
35
  ComboboxTrigger(
30
36
  id: @id,
31
37
  aria_id: @aria_id,
32
- dir: @dir,
33
38
  value: @value,
34
39
  placeholder: @placeholder,
35
40
  disabled: @disabled,
@@ -39,7 +44,13 @@ module ShadcnPhlexcomponents
39
44
 
40
45
  def content(**attributes, &)
41
46
  ComboboxContent(
42
- aria_id: @aria_id, dir: @dir, include_blank: @include_blank, **attributes, &
47
+ include_blank: @include_blank,
48
+ search_error_text: @search_error_text,
49
+ search_empty_text: @search_empty_text,
50
+ search_placeholder_text: @search_placeholder_text,
51
+ aria_id: @aria_id,
52
+ **attributes,
53
+ &
43
54
  )
44
55
  end
45
56
 
@@ -69,13 +80,18 @@ module ShadcnPhlexcomponents
69
80
  ComboboxTrigger(
70
81
  id: @id,
71
82
  aria_id: @aria_id,
72
- dir: @dir,
73
83
  value: @value,
74
84
  placeholder: @placeholder,
75
85
  disabled: @disabled,
76
86
  )
77
87
 
78
- ComboboxContent(aria_id: @aria_id, dir: @dir, include_blank: @include_blank) do
88
+ ComboboxContent(
89
+ aria_id: @aria_id,
90
+ include_blank: @include_blank,
91
+ search_error_text: @search_error_text,
92
+ search_empty_text: @search_empty_text,
93
+ search_placeholder_text: @search_placeholder_text
94
+ ) do
79
95
  collection.each do |item|
80
96
  value = item.public_send(value_method)
81
97
  text = item.public_send(text_method)
@@ -103,6 +119,7 @@ module ShadcnPhlexcomponents
103
119
  data: {
104
120
  aria_id: @aria_id,
105
121
  controller: "combobox",
122
+ search_path: @search_path,
106
123
  combobox_selected_value: @value,
107
124
  },
108
125
  }
@@ -117,25 +134,22 @@ module ShadcnPhlexcomponents
117
134
  aria-invalid:border-destructive dark:bg-input/30 dark:hover:bg-input/50 flex items-center
118
135
  justify-between gap-2 rounded-md border bg-transparent px-3 py-2 text-sm whitespace-nowrap shadow-xs
119
136
  transition-[color,box-shadow] outline-none focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50
120
- data-[size=default]:h-9 data-[size=sm]:h-8 *:data-[combobox-target=triggerText]:line-clamp-1#{" "}
121
- *:data-[combobox-target=triggerText]:flex *:data-[combobox-target=triggerText]:items-center *:data-[combobox-target=triggerText]:gap-2
122
- [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4
123
- data-[placeholder]:data-[has-value=false]:text-muted-foreground w-full
137
+ h-9 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4
138
+ data-[placeholder]:data-[has-value=false]:text-muted-foreground w-full disabled:dark:hover:bg-input/30
124
139
  HEREDOC
125
140
  )
126
141
 
127
- def initialize(id: nil, value: nil, placeholder: nil, dir: "ltr", aria_id: nil, **attributes)
142
+ def initialize(id: nil, value: nil, placeholder: nil, aria_id: nil, **attributes)
128
143
  @id = id
129
144
  @value = value
130
145
  @placeholder = placeholder
131
- @dir = dir
132
146
  @aria_id = aria_id
133
147
  super(**attributes)
134
148
  end
135
149
 
136
150
  def view_template
137
151
  button(**@attributes) do
138
- span(class: "pointer-events-none", data: { combobox_target: "triggerText" }) do
152
+ span(class: "pointer-events-none line-clamp-1 flex items-center gap-2", data: { combobox_target: "triggerText" }) do
139
153
  @value || @placeholder
140
154
  end
141
155
 
@@ -147,7 +161,6 @@ module ShadcnPhlexcomponents
147
161
  {
148
162
  type: "button",
149
163
  id: @id,
150
- dir: @dir,
151
164
  role: "combobox",
152
165
  aria: {
153
166
  autocomplete: "none",
@@ -159,8 +172,6 @@ module ShadcnPhlexcomponents
159
172
  has_value: @value.present?.to_s,
160
173
  action: <<~HEREDOC,
161
174
  click->combobox#toggle
162
- keydown.space->combobox#open
163
- keydown.enter->combobox#open
164
175
  keydown.down->combobox#open:prevent
165
176
  HEREDOC
166
177
  combobox_target: "trigger",
@@ -176,27 +187,44 @@ module ShadcnPhlexcomponents
176
187
  data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95
177
188
  data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2
178
189
  data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 relative z-50
179
- max-h-(--radix-popper-available-height) min-w-[8rem] origin-(--radix-popper-transform-origin)
180
- overflow-x-hidden overflow-y-auto rounded-md border shadow-md pointer-events-auto outline-none
190
+ min-w-[8rem] origin-(--radix-popper-transform-origin)
191
+ rounded-md border shadow-md pointer-events-auto outline-none
181
192
  HEREDOC
182
193
  )
183
194
 
184
- def initialize(include_blank: false, dir: "ltr", side: :bottom, align: :center, aria_id: nil, search_placeholder: "Search...", **attributes)
195
+ def initialize(
196
+ include_blank: false,
197
+ side: :bottom,
198
+ align: :center,
199
+ aria_id: nil,
200
+ search_error_text: nil,
201
+ search_empty_text: nil,
202
+ search_placeholder_text: nil,
203
+ **attributes
204
+ )
185
205
  @include_blank = include_blank
186
- @dir = dir
187
206
  @side = side
188
207
  @align = align
189
- @search_placeholder = search_placeholder
208
+ @search_error_text = search_error_text
209
+ @search_empty_text = search_empty_text
210
+ @search_placeholder_text = search_placeholder_text
190
211
  @aria_id = aria_id
191
212
  super(**attributes)
192
213
  end
193
214
 
194
215
  def view_template(&)
195
216
  div(
196
- class: "hidden fixed top-0 left-0 w-max z-50",
217
+ style: { display: "none" },
218
+ class: "fixed top-0 left-0 w-max z-50",
197
219
  data: { combobox_target: "contentContainer" },
198
220
  ) do
199
221
  div(**@attributes) do
222
+ template do
223
+ ComboboxGroup do
224
+ ComboboxLabel { "" }
225
+ end
226
+ end
227
+
200
228
  label(
201
229
  class: "sr-only",
202
230
  id: "#{@aria_id}-search-label",
@@ -224,19 +252,29 @@ module ShadcnPhlexcomponents
224
252
  },
225
253
  data: {
226
254
  combobox_target: "searchInput",
227
- action: "input->combobox#search",
255
+ action: "keydown->combobox#inputKeydown input->combobox#search",
228
256
  },
229
257
  )
230
258
  end
231
259
 
232
- div(class: "p-1 max-h-80 overflow-y-auto", id: "#{@aria_id}-list", data: { combobox_target: "list" }) do
233
- if @include_blank
234
- ComboboxItem(aria_id: @aria_id, value: "", class: "h-8", hide_icon: true) do
235
- @include_blank.is_a?(String) ? @include_blank : ""
260
+ div(class: "p-1 max-h-80 overflow-y-auto", data: { combobox_target: "listContainer"}) do
261
+ ComboboxText(target: "empty") { @search_empty_text }
262
+ ComboboxText(target: "error") { @search_error_text }
263
+ ComboboxText(target: "loading") do
264
+ div(class: "flex justify-center", aria: { label: "Loading" }) do
265
+ icon("loader-circle", class: "animate-spin")
236
266
  end
237
267
  end
238
268
 
239
- div(data: { combobox_target: "results" }, &)
269
+ div(id: "#{@aria_id}-list", data: { combobox_target: "list" }) do
270
+ if @include_blank
271
+ ComboboxItem(aria_id: @aria_id, value: "", class: "h-8") do
272
+ @include_blank.is_a?(String) ? @include_blank : ""
273
+ end
274
+ end
275
+
276
+ yield
277
+ end
240
278
  end
241
279
  end
242
280
  end
@@ -245,7 +283,6 @@ module ShadcnPhlexcomponents
245
283
  def default_attributes
246
284
  {
247
285
  id: "#{@aria_id}-content",
248
- dir: @dir,
249
286
  tabindex: -1,
250
287
  role: "listbox",
251
288
  aria: {
@@ -261,7 +298,7 @@ module ShadcnPhlexcomponents
261
298
  combobox:click:outside->combobox#clickOutside
262
299
  keydown.up->combobox#highlightItem:prevent
263
300
  keydown.down->combobox#highlightItem:prevent
264
- keydown.enter->combobox#select
301
+ keydown.enter->combobox#select:prevent
265
302
  HEREDOC
266
303
  },
267
304
  }
@@ -280,14 +317,6 @@ module ShadcnPhlexcomponents
280
317
  def view_template(&)
281
318
  div(**@attributes, &)
282
319
  end
283
-
284
- def default_attributes
285
- {
286
- data: {
287
- combobox_target: "label",
288
- },
289
- }
290
- end
291
320
  end
292
321
 
293
322
  class ComboboxItem < Base
@@ -365,34 +394,35 @@ module ShadcnPhlexcomponents
365
394
  end
366
395
  end
367
396
 
368
- class ComboboxSeparator < Base
369
- class_variants(base: "bg-border pointer-events-none -mx-1 my-1 h-px")
370
-
371
- def view_template(&)
372
- div(**@attributes, &)
373
- end
374
-
375
- def default_attributes
376
- { aria: { hidden: "true" } }
377
- end
378
- end
379
-
380
- class ComboboxEmpty < Base
397
+ class ComboboxText < Base
381
398
  class_variants(base: "py-6 text-center text-sm hidden")
399
+
400
+ def initialize(target:, **attributes)
401
+ @target = target
402
+ super(**attributes)
403
+ end
382
404
 
383
405
  def default_attributes
384
406
  {
385
407
  role: "presentation",
386
- data: { combobox_target: "empty" },
408
+ data: { combobox_target: @target },
387
409
  }
388
410
  end
389
411
 
390
412
  def view_template(&)
391
- if block_given?
392
- div(**@attributes, &)
393
- else
394
- div(**@attributes) { "No results found" }
395
- end
413
+ div(**@attributes, &)
414
+ end
415
+ end
416
+
417
+ class ComboboxSeparator < Base
418
+ class_variants(base: "bg-border pointer-events-none -mx-1 my-1 h-px")
419
+
420
+ def view_template(&)
421
+ div(**@attributes, &)
422
+ end
423
+
424
+ def default_attributes
425
+ { aria: { hidden: "true" } }
396
426
  end
397
427
  end
398
428
  end