hotwire_combobox 0.1.4 → 0.1.6

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: d8eaa849ad87e3edaaced39ab2cddddd2de2c4b57c112024c13706055df674eb
4
- data.tar.gz: 9668cf95d8b4b6e994acd852ba6fbac8db3c491959ce40ed520479074d2d326a
3
+ metadata.gz: 06d497472d7966424ceea6dc655bf8baf2bfa7b2232ab587bb1ca6221f1b8e13
4
+ data.tar.gz: 994345e54975287e4958e917d8ff687851e6628604249c5ec59e2bcf9a858fb2
5
5
  SHA512:
6
- metadata.gz: f4f143a0aeafc3fcc7b6f76447ff464342cbffe40f59d55502cc619bcda27bcaba066cf4927a87cf1e551d563c84f0b1ef0bba90ab5d596c9d0e2b3bcfb9a37c
7
- data.tar.gz: f0adeb1d14897243d9b87daa9a67f779765a1c59a9862113928f9b521959a824fafa955d9fe7580e0acef0c841c821ea377c4507208f6dc698adb1f53976a1d2
6
+ metadata.gz: b3bd3a8d9266f43a542641f762ae059d2f8cc2a5c3d98f14b8a853bc5173fd5b382482fa89c0ff103df7e3e24fbf06a6aad5fccffe4d01bd3194959114610742
7
+ data.tar.gz: aa29ec37fa9fd51d640622686a91f6e2bf31c6c9df6a1618d88d540dcea3c1f6e5033379bf0397726904a34a19927bb1b5e9b016d797e7e861d993229e79d171
data/README.md CHANGED
@@ -8,7 +8,7 @@ How to use my plugin.
8
8
  Add this line to your application's Gemfile:
9
9
 
10
10
  ```ruby
11
- gem "hotwire_combobox", require: "combobox"
11
+ gem "hotwire_combobox"
12
12
  ```
13
13
 
14
14
  And then execute:
@@ -44,8 +44,10 @@ $ bin/rails s
44
44
 
45
45
  ### Releasing
46
46
 
47
- 1. Bump the version in `lib/hotwire_combobox/version.rb`
48
- 2. Run `bundle exec rake release`
47
+ 1. Bump the version in `lib/hotwire_combobox/version.rb` (e.g. `VERSION = "0.1.0"`)
48
+ 2. Bump the version in `Gemfile.lock` (e.g. `hotwire_combobox (0.1.0)`)
49
+ 3. Commit the change (e.g. `git commit -am "Bump to 0.1.0"`)
50
+ 4. Run `bundle exec rake release`
49
51
 
50
52
  ## License
51
53
  The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
@@ -1,7 +1,7 @@
1
1
  import { Controller } from "@hotwired/stimulus"
2
2
 
3
3
  export default class extends Controller {
4
- static classes = [ "selected" ]
4
+ static classes = [ "selected", "invalid" ]
5
5
  static targets = [ "combobox", "listbox", "valueField" ]
6
6
  static values = { expanded: Boolean, filterableAttribute: String, autocompletableAttribute: String }
7
7
 
@@ -16,12 +16,13 @@ export default class extends Controller {
16
16
  }
17
17
 
18
18
  close() {
19
+ this.commitSelection()
19
20
  this.expandedValue = false
20
21
  }
21
22
 
22
23
  selectOption(event) {
23
24
  this.select(event.currentTarget)
24
- this.commitSelection()
25
+ this.close()
25
26
  }
26
27
 
27
28
  filter(event) {
@@ -76,14 +77,13 @@ export default class extends Controller {
76
77
  cancel(event)
77
78
  },
78
79
  Enter(event) {
79
- this.commitSelection()
80
+ this.close()
80
81
  cancel(event)
81
82
  }
82
83
  }
83
84
 
84
85
  commitSelection() {
85
86
  this.select(this.selectedOptionElement, { force: true })
86
- this.close()
87
87
  }
88
88
 
89
89
  expandedValueChanged() {
@@ -109,8 +109,17 @@ export default class extends Controller {
109
109
 
110
110
  if (option) {
111
111
  if (this.hasSelectedClass) option.classList.add(this.selectedClass)
112
+ if (this.hasInvalidClass) this.comboboxTarget.classList.remove(this.invalidClass)
113
+
112
114
  this.maybeAutocompleteWith(option, { force })
113
115
  this.executeSelect(option, { selected: true })
116
+ } else {
117
+ if (this.valueIsInvalid) {
118
+ if (this.hasInvalidClass) this.comboboxTarget.classList.add(this.invalidClass)
119
+
120
+ this.comboboxTarget.setAttribute("aria-invalid", true)
121
+ this.comboboxTarget.setAttribute("aria-errormessage", `Please select a valid option for ${this.comboboxTarget.name}`)
122
+ }
114
123
  }
115
124
  }
116
125
 
@@ -176,6 +185,11 @@ export default class extends Controller {
176
185
  get isOpen() {
177
186
  return this.expandedValue
178
187
  }
188
+
189
+ get valueIsInvalid() {
190
+ const isRequiredAndEmpty = this.comboboxTarget.required && !this.valueFieldTarget.value
191
+ return isRequiredAndEmpty
192
+ }
179
193
  }
180
194
 
181
195
  function applyFilter(query, { matching }) {
@@ -0,0 +1,75 @@
1
+ module HotwireCombobox
2
+ module Helper
3
+ def hw_combobox_tag(name, value = nil, form: nil, options: [], data: {}, input: {}, **attrs)
4
+ value_field_attrs = {}.tap do |h|
5
+ h[:id] = default_hw_combobox_value_field_id attrs, form, name
6
+ h[:name] = default_hw_combobox_value_field_name form, name
7
+ h[:data] = default_hw_combobox_value_field_data
8
+ h[:value] = form&.object&.public_send(name) || value
9
+ end
10
+
11
+ attrs[:type] ||= :text
12
+ attrs[:role] = :combobox
13
+ attrs[:id] = hw_combobox_id value_field_attrs[:id]
14
+ attrs[:data] = default_hw_combobox_data input.fetch(:data, {})
15
+ attrs[:aria] = default_hw_combobox_aria value_field_attrs, input.fetch(:aria, {})
16
+
17
+ render "hotwire_combobox/combobox", options: options,
18
+ attrs: attrs, value_field_attrs: value_field_attrs,
19
+ listbox_id: hw_combobox_listbox_id(value_field_attrs[:id]),
20
+ parent_data: default_hw_combobox_parent_data(attrs, data)
21
+ end
22
+
23
+ def value_for_hw_listbox_option(option)
24
+ option.try(:value) || option.id
25
+ end
26
+
27
+ private
28
+ def default_hw_combobox_value_field_id(attrs, form, name)
29
+ attrs.delete(:id) || form&.field_id(name)
30
+ end
31
+
32
+ def default_hw_combobox_value_field_name(form, name)
33
+ form&.field_name(name) || name
34
+ end
35
+
36
+ def default_hw_combobox_value_field_data
37
+ { "hw-combobox-target": "valueField" }
38
+ end
39
+
40
+ def default_hw_combobox_data(data)
41
+ data.reverse_merge! \
42
+ "action": "
43
+ focus->hw-combobox#open
44
+ input->hw-combobox#filter
45
+ keydown->hw-combobox#navigate
46
+ click@window->hw-combobox#closeOnClickOutside
47
+ focusin@window->hw-combobox#closeOnFocusOutside".squish,
48
+ "hw-combobox-target": "combobox"
49
+ end
50
+
51
+ def default_hw_combobox_aria(attrs, aria)
52
+ aria.reverse_merge! \
53
+ "controls": hw_combobox_listbox_id(attrs[:id]),
54
+ "owns": hw_combobox_listbox_id(attrs[:id]),
55
+ "haspopup": "listbox",
56
+ "autocomplete": "both"
57
+ end
58
+
59
+ def default_hw_combobox_parent_data(attrs, data)
60
+ data.reverse_merge! \
61
+ "controller": token_list("hw-combobox", data.delete(:controller)),
62
+ "hw-combobox-expanded-value": attrs.delete(:open),
63
+ "hw-combobox-filterable-attribute-value": "data-filterable-as",
64
+ "hw-combobox-autocompletable-attribute-value": "data-autocompletable-as"
65
+ end
66
+
67
+ def hw_combobox_id(id)
68
+ "#{id}-hw-combobox"
69
+ end
70
+
71
+ def hw_combobox_listbox_id(id)
72
+ "#{id}-hw-listbox"
73
+ end
74
+ end
75
+ end
@@ -1,19 +1,19 @@
1
- <%= tag.fieldset class: "hotwire-combobox", data: parent_data do %>
1
+ <%= tag.fieldset class: "hw-combobox", data: parent_data do %>
2
2
  <%= hidden_field_tag value_field_attrs.delete(:name),
3
3
  value_field_attrs.delete(:value), **value_field_attrs %>
4
4
 
5
5
  <%= tag.input **attrs %>
6
6
 
7
7
  <%= tag.ul id: listbox_id, hidden: "", role: :listbox,
8
- data: { "combobox-target": "listbox" } do |ul| %>
8
+ data: { "hw-combobox-target": "listbox" } do |ul| %>
9
9
  <% options.each do |option| %>
10
10
  <%= tag.li option.content, id: option.try(:id),
11
11
  role: :option, style: "cursor: pointer;",
12
12
  data: {
13
- "action": "click->combobox#selectOption",
13
+ "action": "click->hw-combobox#selectOption",
14
14
  "filterable-as": option.try(:filterable_as),
15
15
  "autocompletable-as": option.try(:autocompletable_as),
16
- "value": value_for_listbox_option(option) } %>
16
+ "value": value_for_hw_listbox_option(option) } %>
17
17
  <% end %>
18
18
  <% end %>
19
19
  <% end %>
@@ -0,0 +1,15 @@
1
+ module HotwireCombobox
2
+ class Engine < ::Rails::Engine
3
+ isolate_namespace HotwireCombobox
4
+
5
+ initializer "hotwire_combobox.view_helpers" do
6
+ ActiveSupport.on_load :action_controller do
7
+ helper HotwireCombobox::Helper
8
+ end
9
+ end
10
+
11
+ initializer "hotwire_combobox.importmap", before: "importmap" do |app|
12
+ app.config.importmap.paths << Engine.root.join("config/importmap.rb")
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,3 @@
1
+ module HotwireCombobox
2
+ VERSION = "0.1.6"
3
+ end
@@ -0,0 +1,6 @@
1
+ require "hotwire_combobox/version"
2
+ require "hotwire_combobox/engine"
3
+
4
+ module HotwireCombobox
5
+ Option = Struct.new(:id, :value, :content, :filterable_as, :autocompletable_as)
6
+ 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.4
4
+ version: 0.1.6
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jose Farias
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-09-14 00:00:00.000000000 Z
11
+ date: 2023-12-10 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails
@@ -52,7 +52,7 @@ dependencies:
52
52
  - - ">="
53
53
  - !ruby/object:Gem::Version
54
54
  version: '1.2'
55
- description: A combobox implementation for Ruby on Rails.
55
+ description: A combobox implementation for Ruby on Rails apps running on Hotwire.
56
56
  email:
57
57
  - jose@farias.mx
58
58
  executables: []
@@ -62,16 +62,16 @@ files:
62
62
  - MIT-LICENSE
63
63
  - README.md
64
64
  - Rakefile
65
- - app/assets/javascripts/application.js
66
65
  - app/assets/javascripts/controllers/application.js
67
- - app/assets/javascripts/controllers/combobox_controller.js
66
+ - app/assets/javascripts/controllers/hw_combobox_controller.js
68
67
  - app/assets/javascripts/controllers/index.js
69
- - app/helpers/combobox/helper.rb
70
- - app/views/combobox/_combobox.html.erb
68
+ - app/assets/javascripts/hotwire_combobox_application.js
69
+ - app/helpers/hotwire_combobox/helper.rb
70
+ - app/views/hotwire_combobox/_combobox.html.erb
71
71
  - config/importmap.rb
72
- - lib/combobox.rb
73
- - lib/combobox/engine.rb
74
- - lib/combobox/version.rb
72
+ - lib/hotwire_combobox.rb
73
+ - lib/hotwire_combobox/engine.rb
74
+ - lib/hotwire_combobox/version.rb
75
75
  homepage: https://github.com/josefarias/hotwire_combobox
76
76
  licenses:
77
77
  - MIT
@@ -97,5 +97,5 @@ requirements: []
97
97
  rubygems_version: 3.4.18
98
98
  signing_key:
99
99
  specification_version: 4
100
- summary: A combobox implementation for Ruby on Rails
100
+ summary: A combobox implementation for Ruby on Rails apps running on Hotwire
101
101
  test_files: []
@@ -1,75 +0,0 @@
1
- module Combobox
2
- module Helper
3
- def combobox_tag(name, value = nil, form: nil, options: [], data: {}, input: {}, **attrs)
4
- value_field_attrs = {}.tap do |h|
5
- h[:id] = default_combobox_value_field_id attrs, form, name
6
- h[:name] = default_combobox_value_field_name form, name
7
- h[:data] = default_combobox_value_field_data
8
- h[:value] = form&.object&.public_send(name) || value
9
- end
10
-
11
- attrs[:type] ||= :text
12
- attrs[:role] = :combobox
13
- attrs[:id] = combobox_id(value_field_attrs[:id])
14
- attrs[:data] = default_combobox_data input.fetch(:data, {})
15
- attrs[:aria] = default_combobox_aria value_field_attrs, input.fetch(:aria, {})
16
-
17
- render "combobox/combobox", options: options,
18
- attrs: attrs, value_field_attrs: value_field_attrs,
19
- listbox_id: combobox_listbox_id(value_field_attrs[:id]),
20
- parent_data: default_combobox_parent_data(attrs, data)
21
- end
22
-
23
- def value_for_listbox_option(option)
24
- option.try(:value) || option.id
25
- end
26
-
27
- private
28
- def default_combobox_value_field_id(attrs, form, name)
29
- attrs.delete(:id) || form&.field_id(name)
30
- end
31
-
32
- def default_combobox_value_field_name(form, name)
33
- form&.field_name(name) || name
34
- end
35
-
36
- def default_combobox_value_field_data
37
- { "combobox-target": "valueField" }
38
- end
39
-
40
- def default_combobox_data(data)
41
- data.reverse_merge! \
42
- "action": "
43
- focus->combobox#open
44
- input->combobox#filter
45
- keydown->combobox#navigate
46
- click@window->combobox#closeOnClickOutside
47
- focusin@window->combobox#closeOnFocusOutside".squish,
48
- "combobox-target": "combobox"
49
- end
50
-
51
- def default_combobox_aria(attrs, aria)
52
- aria.reverse_merge! \
53
- "controls": combobox_listbox_id(attrs[:id]),
54
- "owns": combobox_listbox_id(attrs[:id]),
55
- "haspopup": "listbox",
56
- "autocomplete": "both"
57
- end
58
-
59
- def default_combobox_parent_data(attrs, data)
60
- data.reverse_merge! \
61
- "controller": token_list(:combobox, data.delete(:controller)),
62
- "combobox-expanded-value": attrs.delete(:open),
63
- "combobox-filterable-attribute-value": "data-filterable-as",
64
- "combobox-autocompletable-attribute-value": "data-autocompletable-as"
65
- end
66
-
67
- def combobox_id(id)
68
- "#{id}-combobox"
69
- end
70
-
71
- def combobox_listbox_id(id)
72
- "#{id}-listbox"
73
- end
74
- end
75
- end
@@ -1,15 +0,0 @@
1
- module Combobox
2
- class Engine < ::Rails::Engine
3
- isolate_namespace Combobox
4
-
5
- initializer "combobox.view_helpers" do
6
- ActiveSupport.on_load :action_controller do
7
- helper Combobox::Helper
8
- end
9
- end
10
-
11
- initializer "combobox.importmap", before: "importmap" do |app|
12
- app.config.importmap.paths << Engine.root.join("config/importmap.rb")
13
- end
14
- end
15
- end
@@ -1,3 +0,0 @@
1
- module Combobox
2
- VERSION = "0.1.4"
3
- end
data/lib/combobox.rb DELETED
@@ -1,6 +0,0 @@
1
- require "combobox/version"
2
- require "combobox/engine"
3
-
4
- module Combobox
5
- Option = Struct.new(:id, :value, :content, :filterable_as, :autocompletable_as)
6
- end