hotwire_combobox 0.1.13 → 0.1.15

Sign up to get free protection for your applications and to get access to all the features.
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: []