hotwire_combobox 0.1.4 → 0.1.6

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: 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