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 +4 -4
- data/README.md +5 -3
- data/app/assets/javascripts/controllers/{combobox_controller.js → hw_combobox_controller.js} +18 -4
- data/app/helpers/hotwire_combobox/helper.rb +75 -0
- data/app/views/{combobox → hotwire_combobox}/_combobox.html.erb +4 -4
- data/lib/hotwire_combobox/engine.rb +15 -0
- data/lib/hotwire_combobox/version.rb +3 -0
- data/lib/hotwire_combobox.rb +6 -0
- metadata +11 -11
- data/app/helpers/combobox/helper.rb +0 -75
- data/lib/combobox/engine.rb +0 -15
- data/lib/combobox/version.rb +0 -3
- data/lib/combobox.rb +0 -6
- /data/app/assets/javascripts/{application.js → hotwire_combobox_application.js} +0 -0
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 06d497472d7966424ceea6dc655bf8baf2bfa7b2232ab587bb1ca6221f1b8e13
|
|
4
|
+
data.tar.gz: 994345e54975287e4958e917d8ff687851e6628604249c5ec59e2bcf9a858fb2
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
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"
|
|
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.
|
|
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).
|
data/app/assets/javascripts/controllers/{combobox_controller.js → hw_combobox_controller.js}
RENAMED
|
@@ -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.
|
|
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.
|
|
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: "
|
|
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":
|
|
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
|
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
|
+
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-
|
|
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/
|
|
66
|
+
- app/assets/javascripts/controllers/hw_combobox_controller.js
|
|
68
67
|
- app/assets/javascripts/controllers/index.js
|
|
69
|
-
- app/
|
|
70
|
-
- app/
|
|
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/
|
|
73
|
-
- lib/
|
|
74
|
-
- lib/
|
|
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
|
data/lib/combobox/engine.rb
DELETED
|
@@ -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
|
data/lib/combobox/version.rb
DELETED
data/lib/combobox.rb
DELETED
|
File without changes
|