hotwire_combobox 0.3.1 → 0.4.0

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.
Files changed (51) hide show
  1. checksums.yaml +4 -4
  2. data/MIT-LICENSE +1 -1
  3. data/README.md +19 -15
  4. data/app/assets/config/hw_combobox_manifest.js +1 -1
  5. data/app/assets/javascripts/controllers/hw_combobox_controller.js +34 -18
  6. data/app/assets/javascripts/hotwire_combobox.esm.js +147 -72
  7. data/app/assets/javascripts/hw_combobox/helpers.js +9 -0
  8. data/app/assets/javascripts/hw_combobox/models/combobox/autocomplete.js +14 -4
  9. data/app/assets/javascripts/hw_combobox/models/combobox/callbacks.js +46 -0
  10. data/app/assets/javascripts/hw_combobox/models/combobox/dialog.js +1 -4
  11. data/app/assets/javascripts/hw_combobox/models/combobox/filtering.js +19 -8
  12. data/app/assets/javascripts/hw_combobox/models/combobox/form_field.js +1 -2
  13. data/app/assets/javascripts/hw_combobox/models/combobox/multiselect.js +6 -3
  14. data/app/assets/javascripts/hw_combobox/models/combobox/navigation.js +2 -2
  15. data/app/assets/javascripts/hw_combobox/models/combobox/selection.js +4 -12
  16. data/app/assets/javascripts/hw_combobox/models/combobox/toggle.js +13 -18
  17. data/app/assets/javascripts/hw_combobox/models/combobox.js +1 -0
  18. data/app/assets/stylesheets/hotwire_combobox.css +13 -3
  19. data/app/presenters/hotwire_combobox/component/announced.rb +5 -0
  20. data/app/presenters/hotwire_combobox/component/associations.rb +18 -0
  21. data/app/presenters/hotwire_combobox/component/async.rb +6 -0
  22. data/app/presenters/hotwire_combobox/component/customizable.rb +8 -20
  23. data/app/presenters/hotwire_combobox/component/freetext.rb +26 -0
  24. data/app/presenters/hotwire_combobox/component/markup/dialog.rb +57 -0
  25. data/app/presenters/hotwire_combobox/component/markup/fieldset.rb +46 -0
  26. data/app/presenters/hotwire_combobox/component/markup/form.rb +6 -0
  27. data/app/presenters/hotwire_combobox/component/markup/handle.rb +7 -0
  28. data/app/presenters/hotwire_combobox/component/markup/hidden_field.rb +28 -0
  29. data/app/presenters/hotwire_combobox/component/markup/input.rb +44 -0
  30. data/app/presenters/hotwire_combobox/component/markup/label.rb +5 -0
  31. data/app/presenters/hotwire_combobox/component/markup/listbox.rb +14 -0
  32. data/app/presenters/hotwire_combobox/component/markup/wrapper.rb +7 -0
  33. data/app/presenters/hotwire_combobox/component/multiselect.rb +6 -0
  34. data/app/presenters/hotwire_combobox/component/paginated.rb +18 -0
  35. data/app/presenters/hotwire_combobox/component.rb +32 -398
  36. data/app/presenters/hotwire_combobox/listbox/group.rb +3 -15
  37. data/app/presenters/hotwire_combobox/listbox/item.rb +5 -12
  38. data/app/presenters/hotwire_combobox/listbox/option.rb +3 -19
  39. data/app/views/hotwire_combobox/_component.html.erb +28 -6
  40. data/app/views/hotwire_combobox/_pagination.html.erb +3 -3
  41. data/config/hw_importmap.rb +1 -1
  42. data/lib/hotwire_combobox/helper.rb +24 -65
  43. data/lib/hotwire_combobox/platform.rb +15 -0
  44. data/lib/hotwire_combobox/version.rb +1 -1
  45. data/lib/hotwire_combobox.rb +1 -0
  46. metadata +34 -11
  47. data/app/assets/javascripts/hotwire_combobox.umd.js +0 -1769
  48. data/app/views/hotwire_combobox/combobox/_dialog.html.erb +0 -9
  49. data/app/views/hotwire_combobox/combobox/_hidden_field.html.erb +0 -4
  50. data/app/views/hotwire_combobox/combobox/_input.html.erb +0 -2
  51. data/app/views/hotwire_combobox/combobox/_paginated_listbox.html.erb +0 -9
@@ -23,32 +23,16 @@ class HotwireCombobox::Listbox::Option
23
23
  attr_reader :option
24
24
 
25
25
  def options
26
- {
27
- id: id,
28
- role: :option,
26
+ { id: id, role: :option, tabindex: "-1",
29
27
  class: [ "hw-combobox__option", { "hw-combobox__option--blank": blank? } ],
30
- data: data,
31
- aria: aria
32
- }
28
+ data: { action: "click->hw-combobox#selectOnClick", filterable_as: filterable_as, autocompletable_as: autocompletable_as, value: value },
29
+ aria: { selected: false } }
33
30
  end
34
31
 
35
32
  def id
36
33
  @id ||= option.try(:id) || SecureRandom.uuid
37
34
  end
38
35
 
39
- def data
40
- {
41
- action: "click->hw-combobox#selectOnClick",
42
- filterable_as: filterable_as,
43
- autocompletable_as: autocompletable_as,
44
- value: value
45
- }
46
- end
47
-
48
- def aria
49
- { selected: false }
50
- end
51
-
52
36
  def filterable_as
53
37
  option.try(:filterable_as) || option.try(:display)
54
38
  end
@@ -1,11 +1,13 @@
1
+ <% hidden_field_attrs = component.hidden_field_attrs %>
2
+
1
3
  <%= tag.fieldset **component.fieldset_attrs do %>
2
4
  <%= tag.label component.label, **component.label_attrs %>
3
5
 
4
- <%= render "hotwire_combobox/combobox/hidden_field", component: component %>
6
+ <%= hidden_field_tag hidden_field_attrs.delete(:name), hidden_field_attrs.delete(:value), **hidden_field_attrs %>
5
7
 
6
8
  <%= tag.div **component.main_wrapper_attrs do %>
7
- <%# Essential styles defined here, removing these would break the combobox %>
8
9
  <%= tag.style nonce: content_security_policy_nonce do %>
10
+ <%# Essential styles defined here, removing these would break the combobox %>
9
11
  .hw-combobox__announcer {
10
12
  position: absolute;
11
13
  width: 1px;
@@ -18,12 +20,32 @@
18
20
  }
19
21
 
20
22
  .hw_combobox__pagination__wrapper {
21
- height: 5px;
23
+ height: 1px;
22
24
  }
23
25
  <% end %>
26
+
24
27
  <%= tag.div **component.announcer_attrs %>
25
- <%= render "hotwire_combobox/combobox/input", component: component %>
26
- <%= render "hotwire_combobox/combobox/paginated_listbox", component: component %>
27
- <%= render "hotwire_combobox/combobox/dialog", component: component %>
28
+ <%= tag.input **component.input_attrs %>
29
+ <%= tag.span **component.handle_attrs %>
30
+
31
+ <%= tag.ul **component.listbox_attrs do |ul| %>
32
+ <% component.options.each do |option| %>
33
+ <%= render option %>
34
+ <% end %>
35
+
36
+ <% if component.paginated? %>
37
+ <%= render "hotwire_combobox/pagination", **component.pagination_attrs %>
38
+ <% end %>
39
+ <% end %>
40
+
41
+ <%= tag.div **component.dialog_focus_trap_attrs %>
42
+
43
+ <%= tag.div **component.dialog_wrapper_attrs do %>
44
+ <%= tag.dialog **component.dialog_attrs do %>
45
+ <%= tag.label component.dialog_label, **component.dialog_label_attrs %>
46
+ <%= tag.input **component.dialog_input_attrs %>
47
+ <%= tag.ul **component.dialog_listbox_attrs %>
48
+ <% end %>
49
+ <% end %>
28
50
  <% end %>
29
51
  <% end %>
@@ -1,7 +1,7 @@
1
- <%# locals: (for_id:, src:) -%>
1
+ <%# locals: (for_id:, src:, loading: :lazy) -%>
2
2
 
3
3
  <%= tag.li id: hw_pagination_frame_wrapper_id(for_id), class: "hw_combobox__pagination__wrapper",
4
- data: { hw_combobox_target: "endOfOptionsStream", input_type: params[:input_type] },
4
+ data: { hw_combobox_target: "endOfOptionsStream", input_type: params[:input_type], callback_id: params[:callback_id] },
5
5
  aria: { hidden: true } do %>
6
- <%= turbo_frame_tag hw_pagination_frame_id(for_id), src: src, loading: :lazy %>
6
+ <%= turbo_frame_tag hw_pagination_frame_id(for_id), src: src, loading: loading %>
7
7
  <% end %>
@@ -1 +1 @@
1
- pin_all_from File.expand_path("../app/assets/javascripts", __dir__)
1
+ pin "controllers/hw_combobox_controller", to: "hotwire_combobox.esm.js"
@@ -16,88 +16,47 @@ module HotwireCombobox
16
16
 
17
17
  def hw_combobox_tag(name, options_or_src = [], render_in: {}, include_blank: nil, **kwargs, &block)
18
18
  options, src = hw_extract_options_and_src options_or_src, render_in, include_blank
19
- component = HotwireCombobox::Component.new self, name, options: options, async_src: src, **kwargs
19
+ component = HotwireCombobox::Component.new self, name, options: options, async_src: src, request: request, **kwargs
20
20
  render component, &block
21
21
  end
22
22
 
23
- def hw_combobox_options(
24
- options,
25
- render_in: {},
26
- include_blank: nil,
27
- display: :to_combobox_display,
28
- **custom_methods)
29
- HotwireCombobox::Listbox::Item.collection_for \
30
- self,
31
- options,
32
- render_in: render_in,
33
- include_blank: include_blank,
34
- **custom_methods.merge(display: display)
23
+ def hw_combobox_options(options, render_in: {}, include_blank: nil, display: :to_combobox_display, **custom_methods)
24
+ HotwireCombobox::Listbox::Item.collection_for self, options, render_in: render_in, include_blank: include_blank, **custom_methods.merge(display: display)
35
25
  end
36
26
 
37
- def hw_paginated_combobox_options(
38
- options,
39
- for_id: params[:for_id],
40
- src: hw_fullpath_for_pagination,
41
- next_page: nil,
42
- render_in: {},
43
- include_blank: {},
44
- **custom_methods)
45
- include_blank = params[:page].to_i > 0 ? nil : include_blank
27
+ def hw_paginated_combobox_options(options, for_id: params[:for_id], src: hw_fullpath_for_pagination, next_page: nil, render_in: {}, include_blank: {}, **custom_methods)
28
+ include_blank = hw_first_page? ? include_blank : nil
46
29
  options = hw_combobox_options options, render_in: render_in, include_blank: include_blank, **custom_methods
47
30
  this_page = render "hotwire_combobox/paginated_options", for_id: for_id, options: options
48
31
  next_page = render "hotwire_combobox/next_page", for_id: for_id, src: hw_combobox_next_page_uri(src, next_page, for_id)
49
32
 
50
33
  safe_join [ this_page, next_page ]
51
34
  end
35
+ # setup up backward compatibility with old `#hw_paginated_combobox_options`
52
36
  alias_method :hw_async_combobox_options, :hw_paginated_combobox_options
53
37
 
54
38
  def hw_within_combobox_selection_chip(for_id: params[:for_id], &block)
55
39
  render layout: "hotwire_combobox/layouts/selection_chip", locals: { for_id: for_id }, &block
56
40
  end
57
41
 
58
- def hw_combobox_selection_chip(
59
- display:,
60
- value:,
61
- for_id: params[:for_id],
62
- remover_attrs: hw_combobox_chip_remover_attrs(display: display, value: value))
63
- render "hotwire_combobox/selection_chip",
64
- display: display,
65
- value: value,
66
- for_id: for_id,
67
- remover_attrs: remover_attrs
42
+ def hw_combobox_selection_chip(display:, value:, for_id: params[:for_id], remover_attrs: nil)
43
+ remover_attrs ||= hw_combobox_chip_remover_attrs(display: display, value: value)
44
+ render "hotwire_combobox/selection_chip", display: display, value: value, for_id: for_id, remover_attrs: remover_attrs
68
45
  end
69
46
 
70
- def hw_combobox_selection_chips_for(
71
- objects,
72
- display: :to_combobox_display,
73
- value: :id,
74
- for_id: params[:for_id])
47
+ def hw_combobox_selection_chips_for(objects, display: :to_combobox_display, value: :id, for_id: params[:for_id])
75
48
  objects.map do |object|
76
- hw_combobox_selection_chip \
77
- display: hw_call_method(object, display),
78
- value: hw_call_method(object, value),
79
- for_id: for_id
49
+ hw_combobox_selection_chip display: hw_call_method(object, display), value: hw_call_method(object, value), for_id: for_id
80
50
  end.then { |chips| safe_join chips }
81
51
  end
82
52
 
83
53
  def hw_dismissing_combobox_selection_chip(display:, value:, for_id: params[:for_id])
84
- hw_combobox_selection_chip \
85
- display: display,
86
- value: value,
87
- for_id: for_id,
88
- remover_attrs: hw_combobox_dismissing_chip_remover_attrs(display, value)
54
+ hw_combobox_selection_chip display: display, value: value, for_id: for_id, remover_attrs: hw_combobox_dismissing_chip_remover_attrs(display, value)
89
55
  end
90
56
 
91
- def hw_dismissing_combobox_selection_chips_for(
92
- objects,
93
- display: :to_combobox_display,
94
- value: :id,
95
- for_id: params[:for_id])
57
+ def hw_dismissing_combobox_selection_chips_for(objects, display: :to_combobox_display, value: :id, for_id: params[:for_id])
96
58
  objects.map do |object|
97
- hw_dismissing_combobox_selection_chip \
98
- display: hw_call_method(object, display),
99
- value: hw_call_method(object, value),
100
- for_id: for_id
59
+ hw_dismissing_combobox_selection_chip display: hw_call_method(object, display), value: hw_call_method(object, value), for_id: for_id
101
60
  end.then { |chips| safe_join chips }
102
61
  end
103
62
 
@@ -147,13 +106,17 @@ module HotwireCombobox
147
106
  end
148
107
 
149
108
  def hw_combobox_page_stream_action
150
- params[:page].to_i > 0 ? :append : :update
109
+ hw_first_page? ? :update : :append
110
+ end
111
+
112
+ def hw_first_page?
113
+ params[:page].to_i.zero?
151
114
  end
152
115
 
153
116
  def hw_uri_with_params(url_or_path, **params)
154
117
  URI.parse(url_or_path).tap do |url_or_path|
155
- query = URI.decode_www_form(url_or_path.query || "").to_h.merge(params.compact_blank.stringify_keys)
156
- url_or_path.query = URI.encode_www_form query
118
+ query = Rack::Utils.parse_nested_query(url_or_path.query || "").merge(params.compact_blank.stringify_keys)
119
+ url_or_path.query = Rack::Utils.build_nested_query query
157
120
  end.to_s
158
121
  rescue URI::InvalidURIError
159
122
  url_or_path
@@ -174,13 +137,9 @@ module HotwireCombobox
174
137
  end
175
138
 
176
139
  def hw_combobox_next_page_uri(uri, next_page, for_id)
177
- return unless next_page
178
-
179
- hw_uri_with_params uri,
180
- page: next_page,
181
- q: params[:q],
182
- for_id: for_id,
183
- format: :turbo_stream
140
+ if next_page
141
+ hw_uri_with_params uri, page: next_page, q: params[:q], for_id: for_id, format: :turbo_stream
142
+ end
184
143
  end
185
144
 
186
145
  def hw_extract_options_and_src(options_or_src, render_in, include_blank)
@@ -0,0 +1,15 @@
1
+ require "platform_agent"
2
+
3
+ class HotwireCombobox::Platform < PlatformAgent
4
+ def ios?
5
+ mobile_webkit? && !android?
6
+ end
7
+
8
+ def android?
9
+ match?(/Android/)
10
+ end
11
+
12
+ def mobile_webkit?
13
+ match?(/AppleWebKit/) && match?(/Mobile/)
14
+ end
15
+ end
@@ -1,3 +1,3 @@
1
1
  module HotwireCombobox
2
- VERSION = "0.3.1"
2
+ VERSION = "0.4.0"
3
3
  end
@@ -1,5 +1,6 @@
1
1
  require "hotwire_combobox/version"
2
2
  require "hotwire_combobox/engine"
3
+ require "hotwire_combobox/platform"
3
4
 
4
5
  module HotwireCombobox
5
6
  mattr_accessor :bypass_convenience_methods
metadata CHANGED
@@ -1,14 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: hotwire_combobox
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.1
4
+ version: 0.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jose Farias
8
- autorequire:
9
8
  bindir: bin
10
9
  cert_chain: []
11
- date: 2024-05-31 00:00:00.000000000 Z
10
+ date: 2025-03-17 00:00:00.000000000 Z
12
11
  dependencies:
13
12
  - !ruby/object:Gem::Dependency
14
13
  name: rails
@@ -52,6 +51,20 @@ dependencies:
52
51
  - - ">="
53
52
  - !ruby/object:Gem::Version
54
53
  version: '1.2'
54
+ - !ruby/object:Gem::Dependency
55
+ name: platform_agent
56
+ requirement: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - ">="
59
+ - !ruby/object:Gem::Version
60
+ version: 1.0.1
61
+ type: :runtime
62
+ prerelease: false
63
+ version_requirements: !ruby/object:Gem::Requirement
64
+ requirements:
65
+ - - ">="
66
+ - !ruby/object:Gem::Version
67
+ version: 1.0.1
55
68
  - !ruby/object:Gem::Dependency
56
69
  name: mocha
57
70
  requirement: !ruby/object:Gem::Requirement
@@ -79,7 +92,6 @@ files:
79
92
  - app/assets/config/hw_combobox_manifest.js
80
93
  - app/assets/javascripts/controllers/hw_combobox_controller.js
81
94
  - app/assets/javascripts/hotwire_combobox.esm.js
82
- - app/assets/javascripts/hotwire_combobox.umd.js
83
95
  - app/assets/javascripts/hw_combobox/helpers.js
84
96
  - app/assets/javascripts/hw_combobox/models/combobox.js
85
97
  - app/assets/javascripts/hw_combobox/models/combobox/actors.js
@@ -87,6 +99,7 @@ files:
87
99
  - app/assets/javascripts/hw_combobox/models/combobox/async_loading.js
88
100
  - app/assets/javascripts/hw_combobox/models/combobox/autocomplete.js
89
101
  - app/assets/javascripts/hw_combobox/models/combobox/base.js
102
+ - app/assets/javascripts/hw_combobox/models/combobox/callbacks.js
90
103
  - app/assets/javascripts/hw_combobox/models/combobox/dialog.js
91
104
  - app/assets/javascripts/hw_combobox/models/combobox/events.js
92
105
  - app/assets/javascripts/hw_combobox/models/combobox/filtering.js
@@ -102,7 +115,22 @@ files:
102
115
  - app/assets/javascripts/hw_combobox/vendor/requestjs.js
103
116
  - app/assets/stylesheets/hotwire_combobox.css
104
117
  - app/presenters/hotwire_combobox/component.rb
118
+ - app/presenters/hotwire_combobox/component/announced.rb
119
+ - app/presenters/hotwire_combobox/component/associations.rb
120
+ - app/presenters/hotwire_combobox/component/async.rb
105
121
  - app/presenters/hotwire_combobox/component/customizable.rb
122
+ - app/presenters/hotwire_combobox/component/freetext.rb
123
+ - app/presenters/hotwire_combobox/component/markup/dialog.rb
124
+ - app/presenters/hotwire_combobox/component/markup/fieldset.rb
125
+ - app/presenters/hotwire_combobox/component/markup/form.rb
126
+ - app/presenters/hotwire_combobox/component/markup/handle.rb
127
+ - app/presenters/hotwire_combobox/component/markup/hidden_field.rb
128
+ - app/presenters/hotwire_combobox/component/markup/input.rb
129
+ - app/presenters/hotwire_combobox/component/markup/label.rb
130
+ - app/presenters/hotwire_combobox/component/markup/listbox.rb
131
+ - app/presenters/hotwire_combobox/component/markup/wrapper.rb
132
+ - app/presenters/hotwire_combobox/component/multiselect.rb
133
+ - app/presenters/hotwire_combobox/component/paginated.rb
106
134
  - app/presenters/hotwire_combobox/listbox/group.rb
107
135
  - app/presenters/hotwire_combobox/listbox/item.rb
108
136
  - app/presenters/hotwire_combobox/listbox/item/collection.rb
@@ -112,15 +140,12 @@ files:
112
140
  - app/views/hotwire_combobox/_paginated_options.turbo_stream.erb
113
141
  - app/views/hotwire_combobox/_pagination.html.erb
114
142
  - app/views/hotwire_combobox/_selection_chip.turbo_stream.erb
115
- - app/views/hotwire_combobox/combobox/_dialog.html.erb
116
- - app/views/hotwire_combobox/combobox/_hidden_field.html.erb
117
- - app/views/hotwire_combobox/combobox/_input.html.erb
118
- - app/views/hotwire_combobox/combobox/_paginated_listbox.html.erb
119
143
  - app/views/hotwire_combobox/layouts/_selection_chip.turbo_stream.erb
120
144
  - config/hw_importmap.rb
121
145
  - lib/hotwire_combobox.rb
122
146
  - lib/hotwire_combobox/engine.rb
123
147
  - lib/hotwire_combobox/helper.rb
148
+ - lib/hotwire_combobox/platform.rb
124
149
  - lib/hotwire_combobox/version.rb
125
150
  homepage: https://hotwirecombobox.com/
126
151
  licenses:
@@ -129,7 +154,6 @@ metadata:
129
154
  homepage_uri: https://hotwirecombobox.com/
130
155
  source_code_uri: https://github.com/josefarias/hotwire_combobox
131
156
  changelog_uri: https://github.com/josefarias/hotwire_combobox/releases
132
- post_install_message:
133
157
  rdoc_options: []
134
158
  require_paths:
135
159
  - lib
@@ -144,8 +168,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
144
168
  - !ruby/object:Gem::Version
145
169
  version: '0'
146
170
  requirements: []
147
- rubygems_version: 3.5.10
148
- signing_key:
171
+ rubygems_version: 3.6.2
149
172
  specification_version: 4
150
173
  summary: Accessible Autocomplete for Rails apps
151
174
  test_files: []