hotwire_combobox 0.1.13 → 0.1.15

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 4f868770ad8adc907437ea9d7c0f2a9550b848a1ab0ca3dddb3592aacd4ce75a
4
- data.tar.gz: 6d1932f507fd650da412d2bacc1d6dc01faed6b8d3f2230fe9df1251b66baaf9
3
+ metadata.gz: aa358f06557f2f70b072650e12b11a270fc3afa36addc8e73820105ea7383da5
4
+ data.tar.gz: e35a86d210a02af308614ebdb983a3af69026824ea327fc0f1e353a9612bf807
5
5
  SHA512:
6
- metadata.gz: 31127e0fa02f231b604b10b25f5ff514b33065814a3283ae4d6c338ab59835ef8696e19496f867bec8ddc564066a1b5af2c6c7fa1a88e88507c1943bb741f6ce
7
- data.tar.gz: 77070143486b82fa1b0d3179e1fb74fe43300988a6a6c2c3654dc883a06d1b4b3a9e069e10d8e71f543ee72ee6a242cba42ef7e595e6ba377a530e73954a1aca
6
+ metadata.gz: 011a0b91f679374998444836f121bdede29a86fc8d4c905dd5f0988e41b834be84044fd9cd9a1cc91e88d3bd7812f16b727d9144764968fe07bb44b6d8e06a66
7
+ data.tar.gz: 7ff6b81587de0d53496fcb008cffa0c9ff4df8d19d1f9e8583b7becba82234252b60a57cf4cc258fb5b633a9e5d90821991a14bcb341732a59454a8b50b996d2
data/README.md CHANGED
@@ -1,115 +1,21 @@
1
- # HotwireCombobox
1
+ <p align="center">
2
+ <img src="docs/assets/images/logo.png" height=150>
3
+ </p>
2
4
 
3
- A combobox implementation for Ruby on Rails apps running on Hotwire.
5
+ # Autocomplete for Rails apps using Hotwire
4
6
 
5
- > [!WARNING]
6
- > This gem is pre-release software. It's not ready for production use yet and the API is subject to change.
7
+ [![CI Tests](https://github.com/josefarias/hotwire_combobox/actions/workflows/ci_tests.yml/badge.svg)](https://github.com/josefarias/hotwire_combobox/actions/workflows/ci_tests.yml) [![Gem Version](https://badge.fury.io/rb/hotwire_combobox.svg)](https://badge.fury.io/rb/hotwire_combobox)
7
8
 
8
- ## Installation
9
-
10
- Add this line to your application's Gemfile:
11
-
12
- ```ruby
13
- gem "hotwire_combobox"
14
- ```
15
-
16
- And then execute:
17
- ```bash
18
- $ bundle
19
- ```
20
-
21
- ## Output
22
-
23
- This is the stripped-down output of a combobox generated with HotwireCombobox. Understanding it will be helpful in getting the most out of this library.
24
-
25
- ```html
26
- <fieldset class="hw-combobox">
27
- <input type="hidden" name="provided_name">
28
-
29
- <input type="text" role="combobox">
30
-
31
- <ul role="listbox">
32
- <li role="option">Provided Option 1 Content</li>
33
- <li role="option">Provided Option 2 Content</li>
34
- <!-- ... -->
35
- </ul>
36
- </fieldset>
37
- ```
38
-
39
- The `<ul role="listbox">` element is what gets shown when the combobox is open.
40
-
41
- The library uses stimulus to add interactivity to the combobox and sync the input element's value with the hidden input element.
42
-
43
- The hidden input's value is what ultimately gets sent to the server when submitting a form containing the combobox.
44
-
45
- ## Usage
46
-
47
- ### Options
48
-
49
- Options are what you see when you open the combobox.
50
9
 
51
- The `options` argument takes an array of any objects which respond to:
10
+ > [!IMPORTANT]
11
+ > We need your help to finalize this gem's first major release. Please use it in your apps and report any issues.
52
12
 
53
- | Method | Description |
54
- |--------------------|--------------------------------------------------------------------------------------------------------------------------------------------|
55
- | id | Used as the option element's `id` attribute. Only required if `value` is not provided. |
56
- | value | Used to populate the input element's `value` attribute. Falls back to calling `id` on the object if not provided. |
57
- | content | **Supports HTML** <br> Used as the option element's content. Falls back to calling `display` on the object if not provided. |
58
- | filterable_as | Used to filter down the options when the user types into the input element. Falls back to calling `display` on the object if not provided. |
59
- | autocompletable_as | Used to autocomplete the input element when the user types into it. Falls back to calling `display` on the object if not provided. |
60
- | display | Used as a short-hand for other attributes. See the rest of the list for details. |
61
-
62
- You can use the `combobox_options` helper to create an array of option objects which respond to the above methods:
63
-
64
- ```ruby
65
- combobox_options [
66
- { value: "AL", display: "Alabama" },
67
- { value: "AK", display: "Alaska" },
68
- { value: "AZ", display: "Arizona" },
69
- # ...
70
- ]
71
- ```
72
-
73
- ### Styling
74
-
75
- The combobox is completely unstyled by default. You can use the following CSS selectors to style the combobox:
76
-
77
- * `.hw-combobox` targets the `<fieldset>` element used to wrap the whole component.
78
- * `.hw-combobox [role="combobox"]` targets the input element.
79
- * `.hw-combobox [role="listbox"]` targets the listbox which encloses all option elements.
80
- * `.hw-combobox [role="option"]` targets each option element inside the listbox.
81
-
82
- Additionally, you can pass the following [Stimulus class values](https://stimulus.hotwired.dev/reference/css-classes) to `combobox_tag`:
83
-
84
- * `data-hw-combobox-selected-class`: The class to apply to the selected option while shown inside an open listbox.
85
- * `data-hw-combobox-invalid-class`: The class to apply to the input element when the current value is invalid.
86
-
87
- ### Validity
88
-
89
- Unless `name_when_new` is passed, the hidden input can't have a value that's not in the list of options.
90
-
91
- If a nonexistent value is typed into the combobox, the value of the hidden input will be set empty.
92
-
93
- The only way a value can be marked as invalid is if the field is required and empty after having interacted with the combobox.
94
-
95
- The library will mark the element as invalid but this won't be noticeable in the UI unless you specify styles for the invalid state. See the [Styling](#styling) section for details.
96
-
97
- > [!CAUTION]
98
- > Bad actors can still submit invalid values to the server. You should always validate the input on the server side.
99
-
100
- ### Naming Conflicts
101
-
102
- If your application has naming conflicts with this gem, the following config will turn:
13
+ ## Installation
103
14
 
104
- * `#combobox_tag` into `#hw_combobox_tag`
105
- * `#combobox_options` into `#hw_combobox_options`
15
+ Add this line to your application's Gemfile and run `bundle install`:
106
16
 
107
17
  ```ruby
108
- # config/initializers/hotwire_combobox.rb
109
-
110
- HotwireCombobox.setup do |config|
111
- config.bypass_convenience_methods = true
112
- end
18
+ gem "hotwire_combobox"
113
19
  ```
114
20
 
115
21
  ## Contributing
@@ -30,7 +30,8 @@ export default class HwComboboxController extends Concerns(...concerns) {
30
30
  "dialogListbox",
31
31
  "handle",
32
32
  "hiddenField",
33
- "listbox"
33
+ "listbox",
34
+ "paginationFrame"
34
35
  ]
35
36
 
36
37
  static values = {
@@ -41,6 +42,7 @@ export default class HwComboboxController extends Concerns(...concerns) {
41
42
  filterableAttribute: String,
42
43
  nameWhenNew: String,
43
44
  originalName: String,
45
+ prefilledDisplay: String,
44
46
  smallViewportMaxWidth: String
45
47
  }
46
48
 
@@ -66,4 +68,8 @@ export default class HwComboboxController extends Concerns(...concerns) {
66
68
  this._collapse()
67
69
  }
68
70
  }
71
+
72
+ paginationFrameTargetConnected() {
73
+ this._preselectOption()
74
+ }
69
75
  }
@@ -14,7 +14,7 @@ Combobox.Dialog = Base => class extends Base {
14
14
  }
15
15
 
16
16
  _moveArtifactsToDialog() {
17
- this.dialogComboboxTarget.value = this.actingCombobox.value
17
+ this.dialogComboboxTarget.value = this._actingCombobox.value
18
18
 
19
19
  this._actingCombobox = this.dialogComboboxTarget
20
20
  this._actingListbox = this.dialogListboxTarget
@@ -23,7 +23,7 @@ Combobox.Dialog = Base => class extends Base {
23
23
  }
24
24
 
25
25
  _moveArtifactsInline() {
26
- this.comboboxTarget.value = this.actingCombobox.value
26
+ this.comboboxTarget.value = this._actingCombobox.value
27
27
 
28
28
  this._actingCombobox = this.comboboxTarget
29
29
  this._actingListbox = this.listboxTarget
@@ -32,9 +32,11 @@ Combobox.Dialog = Base => class extends Base {
32
32
  }
33
33
 
34
34
  _resizeDialog = () => {
35
- const fullHeight = window.innerHeight
36
- const viewportHeight = window.visualViewport.height
37
- this.dialogTarget.style.setProperty("--hw-dialog-bottom-padding", `${fullHeight - viewportHeight}px`)
35
+ if (window.visualViewport) {
36
+ const fullHeight = window.innerHeight
37
+ const viewportHeight = window.visualViewport.height
38
+ this.dialogTarget.style.setProperty("--hw-dialog-bottom-padding", `${fullHeight - viewportHeight}px`)
39
+ }
38
40
  }
39
41
 
40
42
  // After closing a dialog, focus returns to the last focused element.
@@ -10,9 +10,9 @@ Combobox.Options = Base => class extends Base {
10
10
  _isValidNewOption(query, { ignoreAutocomplete = false } = {}) {
11
11
  const typedValue = this._actingCombobox.value
12
12
  const autocompletedValue = this._visibleOptionElements[0]?.getAttribute(this.autocompletableAttributeValue)
13
- const insufficentAutocomplete = !autocompletedValue || !startsWith(autocompletedValue, typedValue)
13
+ const insufficientAutocomplete = !autocompletedValue || !startsWith(autocompletedValue, typedValue)
14
14
 
15
- return query.length > 0 && this._allowNew && (ignoreAutocomplete || insufficentAutocomplete)
15
+ return query.length > 0 && this._allowNew && (ignoreAutocomplete || insufficientAutocomplete)
16
16
  }
17
17
 
18
18
  get _allowNew() {
@@ -8,8 +8,8 @@ Combobox.Selection = Base => class extends Base {
8
8
  }
9
9
 
10
10
  _connectSelection() {
11
- if (this.hiddenFieldTarget.value) {
12
- this._selectOptionByValue(this.hiddenFieldTarget.value)
11
+ if (this.hasPrefilledDisplayValue) {
12
+ this._actingCombobox.value = this.prefilledDisplayValue
13
13
  }
14
14
  }
15
15
 
@@ -17,8 +17,6 @@ Combobox.Selection = Base => class extends Base {
17
17
  this._resetOptions()
18
18
 
19
19
  if (option) {
20
- if (this.hasSelectedClass) option.classList.add(this.selectedClass)
21
-
22
20
  this._markValid()
23
21
  this._maybeAutocompleteWith(option, { force })
24
22
  this._commitSelection(option, { selected: true })
@@ -28,21 +26,27 @@ Combobox.Selection = Base => class extends Base {
28
26
  }
29
27
 
30
28
  _commitSelection(option, { selected }) {
31
- option?.setAttribute("aria-selected", selected)
32
- option?.scrollIntoView({ block: "nearest" })
29
+ this._markSelected(option, { selected })
33
30
 
34
31
  if (selected) {
35
- this.hiddenFieldTarget.value = option?.dataset.value
32
+ this.hiddenFieldTarget.value = option.dataset.value
33
+ option.scrollIntoView({ block: "nearest" })
36
34
  } else {
37
35
  this.hiddenFieldTarget.value = null
38
36
  }
39
37
  }
40
38
 
39
+ _markSelected(option, { selected }) {
40
+ if (this.hasSelectedClass) {
41
+ option.classList.toggle(this.selectedClass, selected)
42
+ }
43
+
44
+ option.setAttribute("aria-selected", selected)
45
+ }
46
+
41
47
  _deselect() {
42
48
  const option = this._selectedOptionElement
43
-
44
- if (this.hasSelectedClass) option?.classList.remove(this.selectedClass)
45
- this._commitSelection(option, { selected: false })
49
+ if (option) this._commitSelection(option, { selected: false })
46
50
  }
47
51
 
48
52
  _selectNew(query) {
@@ -56,7 +60,17 @@ Combobox.Selection = Base => class extends Base {
56
60
  this._select(option, { force: true })
57
61
  }
58
62
 
59
- _selectOptionByValue(value) {
60
- this._allOptions.find(option => option.dataset.value === value)?.click()
63
+ _preselectOption() {
64
+ if (this._hasValueButNoSelection && this._allOptions.length < 100) {
65
+ const option = this._allOptions.find(option => {
66
+ return option.dataset.value === this.hiddenFieldTarget.value
67
+ })
68
+
69
+ if (option) this._markSelected(option, { selected: true })
70
+ }
71
+ }
72
+
73
+ get _hasValueButNoSelection() {
74
+ return this.hiddenFieldTarget.value && !this._selectedOptionElement
61
75
  }
62
76
  }
@@ -21,8 +21,11 @@ Combobox.Toggle = Base => class extends Base {
21
21
  }
22
22
  }
23
23
 
24
- closeOnClickOutside({ target }) {
24
+ closeOnClickOutside(event) {
25
+ const target = event.target
26
+
25
27
  if (this.element.contains(target) && !this._isDialogDismisser(target)) return
28
+ if (this._withinElementBounds(event)) return
26
29
 
27
30
  this.close()
28
31
  }
@@ -35,6 +38,17 @@ Combobox.Toggle = Base => class extends Base {
35
38
  this.close()
36
39
  }
37
40
 
41
+ // Some browser extensions like 1Password overlay elements on top of the combobox.
42
+ // Hovering over these elements emits a click event for some reason.
43
+ // These events don't contain any telling information, so we use `_withinElementBounds`
44
+ // as an alternative to check whether the click is legitimate.
45
+ _withinElementBounds(event) {
46
+ const { left, right, top, bottom } = this.element.getBoundingClientRect()
47
+ const { clientX, clientY } = event
48
+
49
+ return clientX >= left && clientX <= right && clientY >= top && clientY <= bottom
50
+ }
51
+
38
52
  _ensureSelection() {
39
53
  if (!this._isValidNewOption(this._actingCombobox.value, { ignoreAutocomplete: true })) {
40
54
  this._select(this._selectedOptionElement, { force: true })
@@ -49,7 +63,9 @@ Combobox.Toggle = Base => class extends Base {
49
63
  return target.closest("dialog") && target.role != "combobox"
50
64
  }
51
65
 
52
- async _expand() {
66
+ _expand() {
67
+ if (this._preselectOnExpansion) this._preselectOption()
68
+
53
69
  if (this._autocompletesList && this._smallViewport) {
54
70
  this._openInDialog()
55
71
  } else {
@@ -101,4 +117,8 @@ Combobox.Toggle = Base => class extends Base {
101
117
  get _isOpen() {
102
118
  return this.expandedValue
103
119
  }
120
+
121
+ get _preselectOnExpansion() {
122
+ return !this._isAsync // async comboboxes preselect based on callbacks
123
+ }
104
124
  }
@@ -15,10 +15,9 @@ module HotwireCombobox
15
15
  end
16
16
  hw_alias :hw_combobox_style_tag
17
17
 
18
- def hw_combobox_tag(*args, async_src: nil, options: [], render_in: {}, **kwargs)
19
- options = hw_combobox_options options, render_in: render_in
20
- src = hw_uri_with_params async_src, format: :turbo_stream
21
- component = HotwireCombobox::Component.new self, *args, options: options, async_src: src, **kwargs
18
+ def hw_combobox_tag(name, options_or_src = [], render_in: {}, **kwargs)
19
+ options, src = hw_extract_options_and_src(options_or_src, render_in)
20
+ component = HotwireCombobox::Component.new self, name, options: options, async_src: src, **kwargs
22
21
 
23
22
  render "hotwire_combobox/combobox", component: component
24
23
  end
@@ -39,20 +38,32 @@ module HotwireCombobox
39
38
  end
40
39
  hw_alias :hw_combobox_options
41
40
 
42
- def hw_paginated_combobox_options(options, for_id:, src:, next_page:, render_in: {}, **methods)
43
- this_page = render("hotwire_combobox/paginated_options", for_id: for_id, options: hw_combobox_options(options, render_in: render_in, **methods), format: :turbo_stream)
44
- next_page = render("hotwire_combobox/next_page", src: src, next_page: next_page, format: :turbo_stream)
41
+ def hw_paginated_combobox_options(options, for_id:, src: request.path, next_page: nil, render_in: {}, **methods)
42
+ this_page = render("hotwire_combobox/paginated_options", for_id: for_id, options: hw_combobox_options(options, render_in: render_in, **methods))
43
+ next_page = render("hotwire_combobox/next_page", for_id: for_id, src: src, next_page: next_page)
45
44
 
46
45
  safe_join [ this_page, next_page ]
47
46
  end
48
47
  hw_alias :hw_paginated_combobox_options
49
48
 
50
- def hw_listbox_options_id(id)
51
- "#{id}-hw-listbox__options"
52
- end
53
- hw_alias :hw_listbox_options_id
49
+ protected # library use only
50
+ def hw_listbox_options_id(id)
51
+ "#{id}-hw-listbox__options"
52
+ end
53
+
54
+ def hw_pagination_frame_id(id)
55
+ "#{id}__hw_combobox_pagination"
56
+ end
54
57
 
55
58
  private
59
+ def hw_extract_options_and_src(options_or_src, render_in)
60
+ if options_or_src.is_a? String
61
+ [ [], hw_uri_with_params(options_or_src, format: :turbo_stream) ]
62
+ else
63
+ [ hw_combobox_options(options_or_src, render_in: render_in), nil ]
64
+ end
65
+ end
66
+
56
67
  def hw_uri_with_params(url_or_path, **params)
57
68
  URI.parse(url_or_path).tap do |url_or_path|
58
69
  query = URI.decode_www_form(url_or_path.query || "").to_h.merge(params)
@@ -1,23 +1,29 @@
1
1
  class HotwireCombobox::Component
2
2
  attr_reader :async_src, :options, :dialog_label
3
3
 
4
- def initialize(
5
- view, name, value = nil,
6
- autocomplete: :both,
7
- id: nil,
8
- form: nil,
9
- name_when_new: nil,
10
- open: false,
11
- small_width: "640px",
12
- async_src: nil,
13
- dialog_label: nil,
14
- options: [], data: {}, input: {}, **rest)
15
- @combobox_attrs = input.reverse_merge(rest).with_indifferent_access
16
-
4
+ def initialize \
5
+ view, name,
6
+ association_name: nil,
7
+ async_src: nil,
8
+ autocomplete: :both,
9
+ data: {},
10
+ dialog_label: nil,
11
+ form: nil,
12
+ id: nil,
13
+ input: {},
14
+ name_when_new: nil,
15
+ open: false,
16
+ options: [],
17
+ small_width: "640px",
18
+ value: nil,
19
+ **rest
17
20
  @view, @autocomplete, @id, @name, @value, @form, @async_src,
18
21
  @name_when_new, @open, @data, @small_width, @options, @dialog_label =
19
22
  view, autocomplete, id, name, value, form, async_src,
20
23
  name_when_new, open, data, small_width, options, dialog_label
24
+
25
+ @combobox_attrs = input.reverse_merge(rest).with_indifferent_access
26
+ @association_name = association_name || infer_association_name
21
27
  end
22
28
 
23
29
  def fieldset_attrs
@@ -125,15 +131,23 @@ class HotwireCombobox::Component
125
131
  end
126
132
 
127
133
  def pagination_attrs
128
- { src: async_src }
134
+ { for_id: hidden_field_id, src: async_src }
129
135
  end
130
136
 
131
137
  private
132
138
  attr_reader :view, :autocomplete, :id, :name, :value, :form,
133
- :name_when_new, :open, :data, :combobox_attrs, :small_width
139
+ :name_when_new, :open, :data, :combobox_attrs, :small_width,
140
+ :association_name
141
+
142
+ def infer_association_name
143
+ if name.to_s.include?("_id")
144
+ name.to_s.sub(/_id\z/, "")
145
+ end
146
+ end
134
147
 
135
148
  def fieldset_data
136
149
  data.reverse_merge \
150
+ pagination_id: hidden_field_id,
137
151
  controller: view.token_list("hw-combobox", data[:controller]),
138
152
  hw_combobox_expanded_value: open,
139
153
  hw_combobox_name_when_new_value: name_when_new,
@@ -141,11 +155,30 @@ class HotwireCombobox::Component
141
155
  hw_combobox_autocomplete_value: autocomplete,
142
156
  hw_combobox_small_viewport_max_width_value: small_width,
143
157
  hw_combobox_async_src_value: async_src,
158
+ hw_combobox_prefilled_display_value: prefilled_display,
144
159
  hw_combobox_filterable_attribute_value: "data-filterable-as",
145
160
  hw_combobox_autocompletable_attribute_value: "data-autocompletable-as",
146
161
  hw_combobox_selected_class: "hw-combobox__option--selected"
147
162
  end
148
163
 
164
+ def prefilled_display
165
+ if async_src && associated_object
166
+ associated_object.to_combobox_display
167
+ elsif value
168
+ options.find { |option| option.value == value }&.content
169
+ end
170
+ end
171
+
172
+ def associated_object
173
+ @associated_object ||= if association_exists?
174
+ form.object.public_send association_name
175
+ end
176
+ end
177
+
178
+ def association_exists?
179
+ form&.object&.class&.reflect_on_association(association_name).present?
180
+ end
181
+
149
182
 
150
183
  def hidden_field_id
151
184
  id || form&.field_id(name)
@@ -180,7 +213,8 @@ class HotwireCombobox::Component
180
213
  keydown->hw-combobox#navigate
181
214
  click@window->hw-combobox#closeOnClickOutside
182
215
  focusin@window->hw-combobox#closeOnFocusOutside".squish,
183
- hw_combobox_target: "combobox"
216
+ hw_combobox_target: "combobox",
217
+ pagination_id: hidden_field_id
184
218
  end
185
219
 
186
220
  def input_aria
@@ -3,8 +3,16 @@ class HotwireCombobox::Listbox::Option
3
3
  @option = option.is_a?(Hash) ? Data.new(**option) : option
4
4
  end
5
5
 
6
- def render_in(view_context)
7
- view_context.tag.li content, **options
6
+ def render_in(view)
7
+ view.tag.li content, **options
8
+ end
9
+
10
+ def value
11
+ option.try(:value) || option.id
12
+ end
13
+
14
+ def content
15
+ option.try(:content) || option.try(:display)
8
16
  end
9
17
 
10
18
  private
@@ -12,10 +20,6 @@ class HotwireCombobox::Listbox::Option
12
20
 
13
21
  attr_reader :option
14
22
 
15
- def content
16
- option.try(:content) || option.try(:display)
17
- end
18
-
19
23
  def options
20
24
  {
21
25
  id: id,
@@ -45,8 +49,4 @@ class HotwireCombobox::Listbox::Option
45
49
  def autocompletable_as
46
50
  option.try(:autocompletable_as) || option.try(:display)
47
51
  end
48
-
49
- def value
50
- option.try(:value) || option.id
51
- end
52
52
  end
@@ -1,5 +1,5 @@
1
- <%# locals: (next_page:, src:) -%>
1
+ <%# locals: (for_id:, next_page:, src:) -%>
2
2
 
3
- <%= turbo_stream.replace :hotwire_combobox_pagination do %>
4
- <%= render "hotwire_combobox/pagination", src: hw_combobox_next_page_uri(src, next_page) %>
3
+ <%= turbo_stream.replace hw_pagination_frame_id(for_id) do %>
4
+ <%= render "hotwire_combobox/pagination", for_id: for_id, src: hw_combobox_next_page_uri(src, next_page) %>
5
5
  <% end %>
@@ -1,3 +1,4 @@
1
- <%# locals: (src:) -%>
1
+ <%# locals: (for_id:, src:) -%>
2
2
 
3
- <%= turbo_frame_tag :hotwire_combobox_pagination, src: src, loading: :lazy %>
3
+ <%= turbo_frame_tag hw_pagination_frame_id(for_id), src: src, loading: :lazy,
4
+ data: { hw_combobox_target: "paginationFrame" } %>
@@ -8,8 +8,8 @@ module HotwireCombobox
8
8
 
9
9
  unless HotwireCombobox.bypass_convenience_methods?
10
10
  module FormBuilderExtensions
11
- def combobox(*args, **kwargs, &block)
12
- @template.hw_combobox_tag *args, **kwargs.merge(form: self), &block
11
+ def combobox(*args, **kwargs)
12
+ @template.hw_combobox_tag *args, **kwargs.merge(form: self)
13
13
  end
14
14
  end
15
15
 
@@ -1,3 +1,3 @@
1
1
  module HotwireCombobox
2
- VERSION = "0.1.13"
2
+ VERSION = "0.1.15"
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: hotwire_combobox
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.13
4
+ version: 0.1.15
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jose Farias
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2024-01-31 00:00:00.000000000 Z
11
+ date: 2024-02-03 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails
@@ -80,7 +80,7 @@ dependencies:
80
80
  - - ">="
81
81
  - !ruby/object:Gem::Version
82
82
  version: 0.0.11
83
- description: A combobox implementation for Ruby on Rails apps running on Hotwire.
83
+ description: A combobox implementation for Ruby on Rails apps using Hotwire.
84
84
  email:
85
85
  - jose@farias.mx
86
86
  executables: []
@@ -149,5 +149,5 @@ requirements: []
149
149
  rubygems_version: 3.4.18
150
150
  signing_key:
151
151
  specification_version: 4
152
- summary: A combobox implementation for Ruby on Rails apps running on Hotwire
152
+ summary: Autocomplete for Rails apps using Hotwire
153
153
  test_files: []