hotwire_combobox 0.3.1 → 0.4.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 (51) hide show
  1. checksums.yaml +4 -4
  2. data/MIT-LICENSE +1 -1
  3. data/README.md +19 -15
  4. data/app/assets/config/hw_combobox_manifest.js +1 -1
  5. data/app/assets/javascripts/controllers/hw_combobox_controller.js +34 -18
  6. data/app/assets/javascripts/hotwire_combobox.esm.js +147 -72
  7. data/app/assets/javascripts/hw_combobox/helpers.js +9 -0
  8. data/app/assets/javascripts/hw_combobox/models/combobox/autocomplete.js +14 -4
  9. data/app/assets/javascripts/hw_combobox/models/combobox/callbacks.js +46 -0
  10. data/app/assets/javascripts/hw_combobox/models/combobox/dialog.js +1 -4
  11. data/app/assets/javascripts/hw_combobox/models/combobox/filtering.js +19 -8
  12. data/app/assets/javascripts/hw_combobox/models/combobox/form_field.js +1 -2
  13. data/app/assets/javascripts/hw_combobox/models/combobox/multiselect.js +6 -3
  14. data/app/assets/javascripts/hw_combobox/models/combobox/navigation.js +2 -2
  15. data/app/assets/javascripts/hw_combobox/models/combobox/selection.js +4 -12
  16. data/app/assets/javascripts/hw_combobox/models/combobox/toggle.js +13 -18
  17. data/app/assets/javascripts/hw_combobox/models/combobox.js +1 -0
  18. data/app/assets/stylesheets/hotwire_combobox.css +13 -3
  19. data/app/presenters/hotwire_combobox/component/announced.rb +5 -0
  20. data/app/presenters/hotwire_combobox/component/associations.rb +18 -0
  21. data/app/presenters/hotwire_combobox/component/async.rb +6 -0
  22. data/app/presenters/hotwire_combobox/component/customizable.rb +8 -20
  23. data/app/presenters/hotwire_combobox/component/freetext.rb +26 -0
  24. data/app/presenters/hotwire_combobox/component/markup/dialog.rb +57 -0
  25. data/app/presenters/hotwire_combobox/component/markup/fieldset.rb +46 -0
  26. data/app/presenters/hotwire_combobox/component/markup/form.rb +6 -0
  27. data/app/presenters/hotwire_combobox/component/markup/handle.rb +7 -0
  28. data/app/presenters/hotwire_combobox/component/markup/hidden_field.rb +28 -0
  29. data/app/presenters/hotwire_combobox/component/markup/input.rb +44 -0
  30. data/app/presenters/hotwire_combobox/component/markup/label.rb +5 -0
  31. data/app/presenters/hotwire_combobox/component/markup/listbox.rb +14 -0
  32. data/app/presenters/hotwire_combobox/component/markup/wrapper.rb +7 -0
  33. data/app/presenters/hotwire_combobox/component/multiselect.rb +6 -0
  34. data/app/presenters/hotwire_combobox/component/paginated.rb +18 -0
  35. data/app/presenters/hotwire_combobox/component.rb +32 -398
  36. data/app/presenters/hotwire_combobox/listbox/group.rb +3 -15
  37. data/app/presenters/hotwire_combobox/listbox/item.rb +5 -12
  38. data/app/presenters/hotwire_combobox/listbox/option.rb +3 -19
  39. data/app/views/hotwire_combobox/_component.html.erb +28 -6
  40. data/app/views/hotwire_combobox/_pagination.html.erb +3 -3
  41. data/config/hw_importmap.rb +1 -1
  42. data/lib/hotwire_combobox/helper.rb +24 -65
  43. data/lib/hotwire_combobox/platform.rb +15 -0
  44. data/lib/hotwire_combobox/version.rb +1 -1
  45. data/lib/hotwire_combobox.rb +1 -0
  46. metadata +34 -11
  47. data/app/assets/javascripts/hotwire_combobox.umd.js +0 -1769
  48. data/app/views/hotwire_combobox/combobox/_dialog.html.erb +0 -9
  49. data/app/views/hotwire_combobox/combobox/_hidden_field.html.erb +0 -4
  50. data/app/views/hotwire_combobox/combobox/_input.html.erb +0 -2
  51. data/app/views/hotwire_combobox/combobox/_paginated_listbox.html.erb +0 -9
@@ -0,0 +1,28 @@
1
+ module HotwireCombobox::Component::Markup::HiddenField
2
+ def hidden_field_attrs
3
+ customize :hidden_field, base: {
4
+ id: hidden_field_id, name: hidden_field_name, value: hidden_field_value,
5
+ data: { hw_combobox_target: "hiddenField" } }
6
+ end
7
+
8
+ private
9
+ def hidden_field_id
10
+ "#{canonical_id}-hw-hidden-field"
11
+ end
12
+
13
+ def hidden_field_name
14
+ form&.field_name(name) || name
15
+ end
16
+
17
+ def hidden_field_value
18
+ return value if value
19
+
20
+ if form_object&.try(:defined_enums)&.try(:[], name)
21
+ form_object.public_send "#{name}_before_type_cast"
22
+ else
23
+ form_object&.try(name).then do |value|
24
+ value.respond_to?(:map) ? value.join(",") : value
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,44 @@
1
+ module HotwireCombobox::Component::Markup::Input
2
+ def input_attrs
3
+ customize :input, base: {
4
+ id: input_id, role: :combobox, type: input_type,
5
+ class: "hw-combobox__input", autocomplete: :off,
6
+ data: input_data, aria: input_aria
7
+ }.merge(combobox_attrs.except(:data, :aria))
8
+ end
9
+
10
+ private
11
+ def input_id
12
+ canonical_id
13
+ end
14
+
15
+ def input_type
16
+ combobox_attrs[:type].to_s.presence_in(%w[ text search ]) || "text"
17
+ end
18
+
19
+ def input_data
20
+ data = combobox_attrs.fetch(:data, {}).dup
21
+ action = %w[
22
+ click->hw-combobox#toggle
23
+ keydown->hw-combobox#prepareToFilter
24
+ input->hw-combobox#filterAndSelect
25
+ keydown->hw-combobox#navigate
26
+ click@window->hw-combobox#closeOnClickOutside
27
+ focusin@window->hw-combobox#closeOnFocusOutside
28
+ turbo:before-stream-render@document->hw-combobox#rerouteListboxStreamToDialog
29
+ turbo:before-cache@document->hw-combobox#hideChipsForCache
30
+ turbo:morph-element->hw-combobox#idempotentConnect
31
+ ].append(data.delete(:action)).compact.join(" ")
32
+
33
+ data.merge action: action, hw_combobox_target: "combobox", async_id: canonical_id
34
+ end
35
+
36
+ def input_aria
37
+ combobox_attrs.fetch(:aria, {}).merge \
38
+ controls: listbox_id,
39
+ owns: listbox_id,
40
+ haspopup: "listbox",
41
+ autocomplete: autocomplete,
42
+ activedescendant: ""
43
+ end
44
+ end
@@ -0,0 +1,5 @@
1
+ module HotwireCombobox::Component::Markup::Label
2
+ def label_attrs
3
+ customize :label, base: { class: "hw-combobox__label", for: input_id, hidden: label.blank? }
4
+ end
5
+ end
@@ -0,0 +1,14 @@
1
+ module HotwireCombobox::Component::Markup::Listbox
2
+ def listbox_attrs
3
+ customize :listbox, base: {
4
+ id: listbox_id, role: :listbox, hidden: "",
5
+ class: "hw-combobox__listbox",
6
+ data: { hw_combobox_target: "listbox" },
7
+ aria: { multiselectable: multiselect? } }
8
+ end
9
+
10
+ private
11
+ def listbox_id
12
+ "#{canonical_id}-hw-listbox"
13
+ end
14
+ end
@@ -0,0 +1,7 @@
1
+ module HotwireCombobox::Component::Markup::Wrapper
2
+ def main_wrapper_attrs
3
+ customize :main_wrapper, base: {
4
+ class: "hw-combobox__main__wrapper",
5
+ data: { action: ("click->hw-combobox#open:self" if multiselect?), hw_combobox_target: "mainWrapper" } }
6
+ end
7
+ end
@@ -0,0 +1,6 @@
1
+ module HotwireCombobox::Component::Multiselect
2
+ private
3
+ def multiselect?
4
+ multiselect_chip_src.present?
5
+ end
6
+ end
@@ -0,0 +1,18 @@
1
+ module HotwireCombobox::Component::Paginated
2
+ def paginated?
3
+ async_src.present?
4
+ end
5
+
6
+ def pagination_attrs
7
+ { for_id: canonical_id, src: async_src, loading: preload_next_page? ? :eager : :lazy }
8
+ end
9
+
10
+ private
11
+ def preload_next_page?
12
+ view.hw_first_page? && preload?
13
+ end
14
+
15
+ def preload?
16
+ preload.present?
17
+ end
18
+ end
@@ -1,36 +1,38 @@
1
1
  require "securerandom"
2
2
 
3
3
  class HotwireCombobox::Component
4
- include Customizable
5
- include ActiveModel::Validations
4
+ include Announced, Associations, Async, Customizable, Freetext, Multiselect, Paginated
6
5
 
7
- attr_reader :options, :label
6
+ # Markup modules depend on Customizable
7
+ include Markup::Dialog, Markup::Fieldset, Markup::Form, Markup::Handle,
8
+ Markup::HiddenField, Markup::Input, Markup::Label, Markup::Listbox, Markup::Wrapper
8
9
 
9
- validate :name_when_new_on_multiselect_must_match_original_name
10
+ attr_reader :options, :label
10
11
 
11
- def initialize \
12
- view, name,
13
- association_name: nil,
14
- async_src: nil,
15
- autocomplete: :both,
16
- data: {},
17
- dialog_label: nil,
18
- form: nil,
19
- free_text: false,
20
- id: nil,
21
- input: {},
22
- label: nil,
23
- mobile_at: "640px",
24
- multiselect_chip_src: nil,
25
- name_when_new: nil,
26
- open: false,
27
- options: [],
28
- value: nil,
29
- **rest
30
- @view, @autocomplete, @id, @name, @value, @form, @async_src, @label, @free_text,
31
- @name_when_new, @open, @data, @mobile_at, @multiselect_chip_src, @options, @dialog_label =
32
- view, autocomplete, id, name.to_s, value, form, async_src, label, free_text,
33
- name_when_new, open, data, mobile_at, multiselect_chip_src, options, dialog_label
12
+ def initialize(
13
+ view, name,
14
+ association_name: nil,
15
+ async_src: nil,
16
+ autocomplete: :both,
17
+ data: {},
18
+ dialog_label: nil,
19
+ form: nil,
20
+ free_text: false,
21
+ id: nil,
22
+ input: {},
23
+ label: nil,
24
+ mobile_at: "640px",
25
+ multiselect_chip_src: nil,
26
+ name_when_new: nil,
27
+ open: false,
28
+ options: [],
29
+ preload: false,
30
+ request: nil,
31
+ value: nil, **rest)
32
+ @view, @autocomplete, @id, @name, @value, @form, @async_src, @label, @free_text, @request,
33
+ @preload, @name_when_new, @open, @data, @mobile_at, @multiselect_chip_src, @options, @dialog_label =
34
+ view, autocomplete, id, name.to_s, value, form, async_src, label, free_text, request,
35
+ preload, name_when_new, open, data, mobile_at, multiselect_chip_src, options, dialog_label
34
36
 
35
37
  @combobox_attrs = input.reverse_merge(rest).deep_symbolize_keys
36
38
  @association_name = association_name || infer_association_name
@@ -40,382 +42,14 @@ class HotwireCombobox::Component
40
42
 
41
43
  def render_in(view_context, &block)
42
44
  block.call(self) if block_given?
43
- view_context.render partial: "hotwire_combobox/component", locals: { component: self }
44
- end
45
-
46
-
47
- def fieldset_attrs
48
- apply_customizations_to :fieldset, base: {
49
- class: [ "hw-combobox", { "hw-combobox--multiple": multiselect? } ],
50
- data: fieldset_data
51
- }
52
- end
53
-
54
-
55
- def label_attrs
56
- apply_customizations_to :label, base: {
57
- class: "hw-combobox__label",
58
- for: input_id,
59
- hidden: label.blank?
60
- }
61
- end
62
-
63
-
64
- def hidden_field_attrs
65
- apply_customizations_to :hidden_field, base: {
66
- id: hidden_field_id,
67
- name: hidden_field_name,
68
- data: hidden_field_data,
69
- value: hidden_field_value
70
- }
71
- end
72
-
73
-
74
- def main_wrapper_attrs
75
- apply_customizations_to :main_wrapper, base: {
76
- class: "hw-combobox__main__wrapper",
77
- data: main_wrapper_data
78
- }
79
- end
80
-
81
-
82
- def announcer_attrs
83
- {
84
- class: "hw-combobox__announcer",
85
- aria: announcer_aria,
86
- data: announcer_data
87
- }
88
- end
89
-
90
-
91
- def input_attrs
92
- nested_attrs = %i[ data aria ]
93
-
94
- base = {
95
- id: input_id,
96
- role: :combobox,
97
- class: "hw-combobox__input",
98
- type: input_type,
99
- data: input_data,
100
- aria: input_aria,
101
- autocomplete: :off
102
- }.merge combobox_attrs.except(*nested_attrs)
103
-
104
- apply_customizations_to :input, base: base
105
- end
106
-
107
-
108
- def handle_attrs
109
- apply_customizations_to :handle, base: {
110
- class: "hw-combobox__handle",
111
- data: handle_data
112
- }
113
- end
114
-
115
-
116
- def listbox_attrs
117
- apply_customizations_to :listbox, base: {
118
- id: listbox_id,
119
- role: :listbox,
120
- class: "hw-combobox__listbox",
121
- hidden: "",
122
- data: listbox_data,
123
- aria: listbox_aria
124
- }
125
- end
126
-
127
-
128
- def dialog_wrapper_attrs
129
- apply_customizations_to :dialog_wrapper, base: {
130
- class: "hw-combobox__dialog__wrapper"
131
- }
132
- end
133
-
134
- def dialog_attrs
135
- apply_customizations_to :dialog, base: {
136
- class: "hw-combobox__dialog",
137
- role: :dialog,
138
- data: dialog_data
139
- }
140
- end
141
-
142
- def dialog_label
143
- @dialog_label || label
144
- end
145
-
146
- def dialog_label_attrs
147
- apply_customizations_to :dialog_label, base: {
148
- class: "hw-combobox__dialog__label",
149
- for: dialog_input_id
150
- }
151
- end
152
-
153
- def dialog_input_attrs
154
- apply_customizations_to :dialog_input, base: {
155
- id: dialog_input_id,
156
- role: :combobox,
157
- class: "hw-combobox__dialog__input",
158
- autofocus: "",
159
- type: input_type,
160
- data: dialog_input_data,
161
- aria: dialog_input_aria
162
- }
163
- end
164
-
165
- def dialog_listbox_attrs
166
- apply_customizations_to :dialog_listbox, base: {
167
- id: dialog_listbox_id,
168
- class: "hw-combobox__dialog__listbox",
169
- role: :listbox,
170
- data: dialog_listbox_data,
171
- aria: dialog_listbox_aria
172
- }
173
- end
174
-
175
- def dialog_focus_trap_attrs
176
- {
177
- tabindex: "-1",
178
- data: dialog_focus_trap_data
179
- }
180
- end
181
-
182
-
183
- def paginated?
184
- async_src.present?
185
- end
186
-
187
- def pagination_attrs
188
- { for_id: canonical_id, src: async_src }
45
+ view_context.render partial: "hotwire_combobox/component", locals: { component: self }, formats: [ :html ]
189
46
  end
190
47
 
191
48
  private
192
- attr_reader :view, :autocomplete, :id, :name, :value, :form,
193
- :free_text, :open, :data, :combobox_attrs, :mobile_at,
194
- :association_name, :multiselect_chip_src
195
-
196
- def name_when_new
197
- if free_text && @name_when_new.blank?
198
- hidden_field_name
199
- else
200
- @name_when_new
201
- end
202
- end
203
-
204
- def name_when_new_on_multiselect_must_match_original_name
205
- return unless multiselect? && name_when_new.present?
206
-
207
- unless name_when_new.to_s == hidden_field_name
208
- errors.add :name_when_new, :must_match_original_name,
209
- message: "must match the regular name ('#{hidden_field_name}', in this case) on multiselect comboboxes."
210
- end
211
- end
212
-
213
- def multiselect?
214
- multiselect_chip_src.present?
215
- end
216
-
217
- def infer_association_name
218
- if name.end_with?("_id")
219
- name.sub(/_id\z/, "")
220
- end
221
- end
222
-
223
- def fieldset_data
224
- data.merge \
225
- async_id: canonical_id,
226
- controller: view.token_list("hw-combobox", data[:controller]),
227
- hw_combobox_expanded_value: open,
228
- hw_combobox_name_when_new_value: name_when_new,
229
- hw_combobox_original_name_value: hidden_field_name,
230
- hw_combobox_autocomplete_value: autocomplete,
231
- hw_combobox_small_viewport_max_width_value: mobile_at,
232
- hw_combobox_async_src_value: async_src,
233
- hw_combobox_prefilled_display_value: prefilled_display,
234
- hw_combobox_selection_chip_src_value: multiselect_chip_src,
235
- hw_combobox_filterable_attribute_value: "data-filterable-as",
236
- hw_combobox_autocompletable_attribute_value: "data-autocompletable-as",
237
- hw_combobox_selected_class: "hw-combobox__option--selected",
238
- hw_combobox_invalid_class: "hw-combobox__input--invalid"
239
- end
240
-
241
- def prefilled_display
242
- return if multiselect? || !hidden_field_value
243
-
244
- if async_src && associated_object
245
- associated_object.to_combobox_display
246
- elsif async_src && form_object&.respond_to?(name)
247
- form_object.public_send name
248
- else
249
- options.find_by_value(hidden_field_value)&.autocompletable_as
250
- end
251
- end
252
-
253
- def associated_object
254
- @associated_object ||= if association_exists?
255
- form_object&.public_send association_name
256
- end
257
- end
258
-
259
- def association_exists?
260
- association_name && form_object&.respond_to?(association_name)
261
- end
262
-
263
- def form_object
264
- form&.object
265
- end
266
-
267
- def async_src
268
- view.hw_uri_with_params @async_src, for_id: canonical_id, format: :turbo_stream
269
- end
270
-
49
+ attr_reader :view, :autocomplete, :id, :name, :value, :form, :free_text, :open, :request,
50
+ :data, :combobox_attrs, :mobile_at, :association_name, :multiselect_chip_src, :preload
271
51
 
272
52
  def canonical_id
273
53
  @canonical_id ||= id || form&.field_id(name) || SecureRandom.uuid
274
54
  end
275
-
276
-
277
- def main_wrapper_data
278
- {
279
- action: ("click->hw-combobox#openByFocusing:self" if multiselect?),
280
- hw_combobox_target: "mainWrapper"
281
- }
282
- end
283
-
284
-
285
- def announcer_aria
286
- {
287
- live: :polite,
288
- atomic: true
289
- }
290
- end
291
-
292
- def announcer_data
293
- { hw_combobox_target: "announcer" }
294
- end
295
-
296
-
297
- def hidden_field_id
298
- "#{canonical_id}-hw-hidden-field"
299
- end
300
-
301
- def hidden_field_name
302
- form&.field_name(name) || name
303
- end
304
-
305
- def hidden_field_data
306
- { hw_combobox_target: "hiddenField" }
307
- end
308
-
309
- def hidden_field_value
310
- return value if value
311
-
312
- if form_object&.try(:defined_enums)&.try(:[], name)
313
- form_object.public_send "#{name}_before_type_cast"
314
- else
315
- form_object&.try(name).then do |value|
316
- value.respond_to?(:map) ? value.join(",") : value
317
- end
318
- end
319
- end
320
-
321
-
322
- def input_id
323
- canonical_id
324
- end
325
-
326
- def input_type
327
- combobox_attrs[:type].to_s.presence_in(%w[ text search ]) || "text"
328
- end
329
-
330
- def input_data
331
- combobox_attrs.fetch(:data, {}).merge \
332
- action: "
333
- focus->hw-combobox#open
334
- input->hw-combobox#filterAndSelect
335
- keydown->hw-combobox#navigate
336
- click@window->hw-combobox#closeOnClickOutside
337
- focusin@window->hw-combobox#closeOnFocusOutside
338
- turbo:before-stream-render@document->hw-combobox#rerouteListboxStreamToDialog
339
- turbo:before-cache@document->hw-combobox#hideChipsForCache
340
- turbo:morph-element->hw-combobox#idempotentConnect".squish,
341
- hw_combobox_target: "combobox",
342
- async_id: canonical_id
343
- end
344
-
345
- def input_aria
346
- combobox_attrs.fetch(:aria, {}).merge \
347
- controls: listbox_id,
348
- owns: listbox_id,
349
- haspopup: "listbox",
350
- autocomplete: autocomplete,
351
- activedescendant: ""
352
- end
353
-
354
-
355
- def handle_data
356
- {
357
- action: "click->hw-combobox#clearOrToggleOnHandleClick",
358
- hw_combobox_target: "handle"
359
- }
360
- end
361
-
362
-
363
- def listbox_id
364
- "#{canonical_id}-hw-listbox"
365
- end
366
-
367
- def listbox_data
368
- { hw_combobox_target: "listbox" }
369
- end
370
-
371
- def listbox_aria
372
- { multiselectable: multiselect? }
373
- end
374
-
375
-
376
- def dialog_data
377
- {
378
- action: "keydown->hw-combobox#navigate",
379
- hw_combobox_target: "dialog"
380
- }
381
- end
382
-
383
- def dialog_input_id
384
- "#{canonical_id}-hw-dialog-combobox"
385
- end
386
-
387
- def dialog_input_data
388
- {
389
- action: "
390
- input->hw-combobox#filterAndSelect
391
- keydown->hw-combobox#navigate
392
- click@window->hw-combobox#closeOnClickOutside".squish,
393
- hw_combobox_target: "dialogCombobox"
394
- }
395
- end
396
-
397
- def dialog_input_aria
398
- {
399
- controls: dialog_listbox_id,
400
- owns: dialog_listbox_id,
401
- autocomplete: autocomplete,
402
- activedescendant: ""
403
- }
404
- end
405
-
406
- def dialog_listbox_id
407
- "#{canonical_id}-hw-dialog-listbox"
408
- end
409
-
410
- def dialog_listbox_data
411
- { hw_combobox_target: "dialogListbox" }
412
- end
413
-
414
- def dialog_listbox_aria
415
- { multiselectable: multiselect? }
416
- end
417
-
418
- def dialog_focus_trap_data
419
- { hw_combobox_target: "dialogFocusTrap" }
420
- end
421
55
  end
@@ -9,7 +9,7 @@ class HotwireCombobox::Listbox::Group
9
9
  end
10
10
 
11
11
  def render_in(view)
12
- view.tag.ul **group_attrs do
12
+ view.tag.ul(**group_attrs) do
13
13
  view.concat view.tag.li(name, **label_attrs)
14
14
 
15
15
  options.map do |option|
@@ -26,22 +26,10 @@ class HotwireCombobox::Listbox::Group
26
26
  end
27
27
 
28
28
  def group_attrs
29
- {
30
- class: "hw-combobox__group",
31
- role: :group,
32
- aria: group_aria
33
- }
34
- end
35
-
36
- def group_aria
37
- { labelledby: id }
29
+ { class: "hw-combobox__group", role: :group, aria: { labelledby: id } }
38
30
  end
39
31
 
40
32
  def label_attrs
41
- {
42
- id: id,
43
- class: "hw-combobox__group__label",
44
- role: :presentation
45
- }
33
+ { id: id, class: "hw-combobox__group__label", role: :presentation }
46
34
  end
47
35
  end
@@ -6,11 +6,8 @@ class HotwireCombobox::Listbox::Item
6
6
  end
7
7
 
8
8
  def initialize(view, options, render_in:, include_blank:, **custom_methods)
9
- @view = view
10
- @options = options
11
- @render_in = render_in
12
- @include_blank = include_blank
13
- @custom_methods = custom_methods
9
+ @view, @options, @render_in, @include_blank, @custom_methods =
10
+ view, options, render_in, include_blank, custom_methods
14
11
  end
15
12
 
16
13
  def collection
@@ -37,8 +34,7 @@ class HotwireCombobox::Listbox::Item
37
34
 
38
35
  def create_listbox_group(options)
39
36
  options.map do |group_name, group_options|
40
- HotwireCombobox::Listbox::Group.new group_name,
41
- options: create_listbox_options(group_options)
37
+ HotwireCombobox::Listbox::Group.new group_name, options: create_listbox_options(group_options)
42
38
  end
43
39
  end
44
40
 
@@ -47,7 +43,7 @@ class HotwireCombobox::Listbox::Item
47
43
  options
48
44
  else
49
45
  options.map do |option|
50
- HotwireCombobox::Listbox::Option.new **option_attrs(option)
46
+ HotwireCombobox::Listbox::Option.new(**option_attrs(option))
51
47
  end
52
48
  end
53
49
  end
@@ -86,9 +82,7 @@ class HotwireCombobox::Listbox::Item
86
82
  end
87
83
 
88
84
  def render_content(render_opts: render_in, object:, attrs:)
89
- view.render **render_opts.reverse_merge(
90
- object: object,
91
- locals: { combobox_display: attrs[:display], combobox_value: attrs[:value] })
85
+ view.render(**render_opts.reverse_merge(object: object, locals: { combobox_display: attrs[:display], combobox_value: attrs[:value] }))
92
86
  end
93
87
 
94
88
  def blank_option
@@ -100,7 +94,6 @@ class HotwireCombobox::Listbox::Item
100
94
  case include_blank
101
95
  when Hash
102
96
  text = include_blank.delete(:text)
103
-
104
97
  [ text, render_content(render_opts: include_blank, object: text, attrs: { display: text, value: "" }) ]
105
98
  when String
106
99
  [ include_blank, include_blank ]