hotwire_combobox 0.1.43 → 0.2.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 (30) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +1 -0
  3. data/app/assets/javascripts/controllers/hw_combobox_controller.js +25 -3
  4. data/app/assets/javascripts/hotwire_combobox.esm.js +438 -104
  5. data/app/assets/javascripts/hotwire_combobox.umd.js +438 -104
  6. data/app/assets/javascripts/hw_combobox/helpers.js +16 -0
  7. data/app/assets/javascripts/hw_combobox/models/combobox/announcements.js +7 -0
  8. data/app/assets/javascripts/hw_combobox/models/combobox/dialog.js +1 -1
  9. data/app/assets/javascripts/hw_combobox/models/combobox/events.js +21 -11
  10. data/app/assets/javascripts/hw_combobox/models/combobox/filtering.js +20 -12
  11. data/app/assets/javascripts/hw_combobox/models/combobox/form_field.js +74 -0
  12. data/app/assets/javascripts/hw_combobox/models/combobox/multiselect.js +160 -0
  13. data/app/assets/javascripts/hw_combobox/models/combobox/navigation.js +15 -6
  14. data/app/assets/javascripts/hw_combobox/models/combobox/options.js +19 -7
  15. data/app/assets/javascripts/hw_combobox/models/combobox/selection.js +50 -49
  16. data/app/assets/javascripts/hw_combobox/models/combobox/toggle.js +33 -16
  17. data/app/assets/javascripts/hw_combobox/models/combobox/validity.js +1 -1
  18. data/app/assets/javascripts/hw_combobox/models/combobox.js +3 -0
  19. data/app/assets/stylesheets/hotwire_combobox.css +84 -18
  20. data/app/presenters/hotwire_combobox/component/customizable.rb +9 -1
  21. data/app/presenters/hotwire_combobox/component.rb +93 -26
  22. data/app/presenters/hotwire_combobox/listbox/group.rb +45 -0
  23. data/app/presenters/hotwire_combobox/listbox/item.rb +104 -0
  24. data/app/presenters/hotwire_combobox/listbox/option.rb +9 -4
  25. data/app/views/hotwire_combobox/_component.html.erb +1 -0
  26. data/app/views/hotwire_combobox/_selection_chip.turbo_stream.erb +8 -0
  27. data/app/views/hotwire_combobox/layouts/_selection_chip.turbo_stream.erb +7 -0
  28. data/lib/hotwire_combobox/helper.rb +111 -86
  29. data/lib/hotwire_combobox/version.rb +1 -1
  30. metadata +9 -2
@@ -17,6 +17,10 @@ class HotwireCombobox::Listbox::Option
17
17
  option.try(:autocompletable_as) || option.try(:display)
18
18
  end
19
19
 
20
+ def content
21
+ option.try(:content) || option.try(:display)
22
+ end
23
+
20
24
  private
21
25
  Data = Struct.new :id, :value, :display, :content, :blank, :filterable_as, :autocompletable_as, keyword_init: true
22
26
 
@@ -27,7 +31,8 @@ class HotwireCombobox::Listbox::Option
27
31
  id: id,
28
32
  role: :option,
29
33
  class: [ "hw-combobox__option", { "hw-combobox__option--blank": blank? } ],
30
- data: data
34
+ data: data,
35
+ aria: aria
31
36
  }
32
37
  end
33
38
 
@@ -37,15 +42,15 @@ class HotwireCombobox::Listbox::Option
37
42
 
38
43
  def data
39
44
  {
40
- action: "click->hw-combobox#selectOptionOnClick",
45
+ action: "click->hw-combobox#selectOnClick",
41
46
  filterable_as: filterable_as,
42
47
  autocompletable_as: autocompletable_as,
43
48
  value: value
44
49
  }
45
50
  end
46
51
 
47
- def content
48
- option.try(:content) || option.try(:display)
52
+ def aria
53
+ { selected: false }
49
54
  end
50
55
 
51
56
  def filterable_as
@@ -4,6 +4,7 @@
4
4
  <%= render "hotwire_combobox/combobox/hidden_field", component: component %>
5
5
 
6
6
  <%= tag.div **component.main_wrapper_attrs do %>
7
+ <%= tag.div **component.announcer_attrs %>
7
8
  <%= render "hotwire_combobox/combobox/input", component: component %>
8
9
  <%= render "hotwire_combobox/combobox/paginated_listbox", component: component %>
9
10
  <%= render "hotwire_combobox/combobox/dialog", component: component %>
@@ -0,0 +1,8 @@
1
+ <%# locals: (display:, value:, for_id:, remover_attrs:) -%>
2
+
3
+ <%= within_combobox_selection_chip(for_id: for_id) do %>
4
+ <%= tag.div class: "hw-combobox__chip" do %>
5
+ <%= tag.span display %>
6
+ <%= tag.span **remover_attrs %>
7
+ <% end %>
8
+ <% end %>
@@ -0,0 +1,7 @@
1
+ <%# locals: (for_id:) -%>
2
+
3
+ <%= turbo_stream.before for_id do %>
4
+ <%= tag.div data: { hw_combobox_chip: "" } do %>
5
+ <%= yield %>
6
+ <% end %>
7
+ <% end %>
@@ -13,38 +13,129 @@ module HotwireCombobox
13
13
  def hw_combobox_style_tag(*args, **kwargs)
14
14
  stylesheet_link_tag HotwireCombobox.stylesheet_path, *args, **kwargs
15
15
  end
16
- hw_alias :hw_combobox_style_tag
17
16
 
18
17
  def hw_combobox_tag(name, options_or_src = [], render_in: {}, include_blank: nil, **kwargs, &block)
19
- options, src = hw_extract_options_and_src(options_or_src, render_in, include_blank)
18
+ options, src = hw_extract_options_and_src options_or_src, render_in, include_blank
20
19
  component = HotwireCombobox::Component.new self, name, options: options, async_src: src, **kwargs
21
20
  render component, &block
22
21
  end
23
- hw_alias :hw_combobox_tag
24
22
 
25
- def hw_combobox_options(options, render_in: {}, include_blank: nil, display: :to_combobox_display, **methods)
23
+ def hw_combobox_options(
24
+ options,
25
+ render_in: {},
26
+ include_blank: nil,
27
+ display: :to_combobox_display,
28
+ **custom_methods)
26
29
  if options.first.is_a? HotwireCombobox::Listbox::Option
27
30
  options
28
31
  else
29
- opts = hw_parse_combobox_options options, render_in_proc: hw_render_in_proc(render_in), **methods.merge(display: display)
30
- opts.unshift(hw_blank_option(include_blank)) if include_blank.present?
31
- opts
32
+ HotwireCombobox::Listbox::Item.collection_for \
33
+ self,
34
+ options,
35
+ render_in: render_in,
36
+ include_blank: include_blank,
37
+ **custom_methods.merge(display: display)
32
38
  end
33
39
  end
34
- hw_alias :hw_combobox_options
35
40
 
36
- def hw_paginated_combobox_options(options, for_id: params[:for_id], src: request.path, next_page: nil, render_in: {}, include_blank: {}, **methods)
37
- include_blank = params[:page] ? nil : include_blank
38
- options = hw_combobox_options options, render_in: render_in, include_blank: include_blank, **methods
41
+ def hw_paginated_combobox_options(
42
+ options,
43
+ for_id: params[:for_id],
44
+ src: request.path,
45
+ next_page: nil,
46
+ render_in: {},
47
+ include_blank: {},
48
+ **custom_methods)
49
+ include_blank = params[:page].to_i > 0 ? nil : include_blank
50
+ options = hw_combobox_options options, render_in: render_in, include_blank: include_blank, **custom_methods
39
51
  this_page = render "hotwire_combobox/paginated_options", for_id: for_id, options: options
40
52
  next_page = render "hotwire_combobox/next_page", for_id: for_id, src: src, next_page: next_page
41
53
 
42
54
  safe_join [ this_page, next_page ]
43
55
  end
44
- hw_alias :hw_paginated_combobox_options
45
-
46
56
  alias_method :hw_async_combobox_options, :hw_paginated_combobox_options
57
+
58
+ def hw_within_combobox_selection_chip(for_id: params[:for_id], &block)
59
+ render layout: "hotwire_combobox/layouts/selection_chip", locals: { for_id: for_id }, &block
60
+ end
61
+
62
+ def hw_combobox_selection_chip(
63
+ display:,
64
+ value:,
65
+ for_id: params[:for_id],
66
+ remover_attrs: hw_combobox_chip_remover_attrs(display: display, value: value))
67
+ render "hotwire_combobox/selection_chip",
68
+ display: display,
69
+ value: value,
70
+ for_id: for_id,
71
+ remover_attrs: remover_attrs
72
+ end
73
+
74
+ def hw_combobox_selection_chips_for(
75
+ objects,
76
+ display: :to_combobox_display,
77
+ value: :id,
78
+ for_id: params[:for_id])
79
+ objects.map do |object|
80
+ hw_combobox_selection_chip \
81
+ display: hw_call_method(object, display),
82
+ value: hw_call_method(object, value),
83
+ for_id: for_id
84
+ end.then { |chips| safe_join chips }
85
+ end
86
+
87
+ def hw_dismissing_combobox_selection_chip(display:, value:, for_id: params[:for_id])
88
+ hw_combobox_selection_chip \
89
+ display: display,
90
+ value: value,
91
+ for_id: for_id,
92
+ remover_attrs: hw_combobox_dismissing_chip_remover_attrs(display, value)
93
+ end
94
+
95
+ def hw_dismissing_combobox_selection_chips_for(
96
+ objects,
97
+ display: :to_combobox_display,
98
+ value: :id,
99
+ for_id: params[:for_id])
100
+ objects.map do |object|
101
+ hw_dismissing_combobox_selection_chip \
102
+ display: hw_call_method(object, display),
103
+ value: hw_call_method(object, value),
104
+ for_id: for_id
105
+ end.then { |chips| safe_join chips }
106
+ end
107
+
108
+ def hw_combobox_chip_remover_attrs(display:, value:, **kwargs)
109
+ {
110
+ tabindex: "0",
111
+ class: token_list("hw-combobox__chip__remover", kwargs[:class]),
112
+ aria: { label: "Remove #{display}" },
113
+ data: {
114
+ action: "click->hw-combobox#removeChip:stop keydown->hw-combobox#navigateChip",
115
+ hw_combobox_target: "chipDismisser",
116
+ hw_combobox_value_param: value
117
+ }
118
+ }
119
+ end
120
+
121
+ def hw_combobox_dismissing_chip_remover_attrs(display, value)
122
+ hw_combobox_chip_remover_attrs(display: display, value: value).tap do |attrs|
123
+ attrs[:data][:hw_combobox_target] = token_list(attrs[:data][:hw_combobox_target], "closer")
124
+ end
125
+ end
126
+
127
+ hw_alias :hw_combobox_style_tag
128
+ hw_alias :hw_combobox_tag
129
+ hw_alias :hw_combobox_options
130
+ hw_alias :hw_paginated_combobox_options
47
131
  hw_alias :hw_async_combobox_options
132
+ hw_alias :hw_within_combobox_selection_chip
133
+ hw_alias :hw_combobox_selection_chip
134
+ hw_alias :hw_combobox_selection_chips_for
135
+ hw_alias :hw_dismissing_combobox_selection_chip
136
+ hw_alias :hw_dismissing_combobox_selection_chips_for
137
+ hw_alias :hw_combobox_chip_remover_attrs
138
+ hw_alias :hw_combobox_dismissing_chip_remover_attrs
48
139
 
49
140
  # private library use only
50
141
  def hw_listbox_id(id)
@@ -70,23 +161,7 @@ module HotwireCombobox
70
161
  end
71
162
 
72
163
  def hw_combobox_page_stream_action
73
- params[:page] ? :append : :update
74
- end
75
-
76
- def hw_blank_option(include_blank)
77
- display, content = hw_extract_blank_display_and_content include_blank
78
-
79
- HotwireCombobox::Listbox::Option.new display: display, content: content, value: "", blank: true
80
- end
81
-
82
- def hw_extract_blank_display_and_content(include_blank)
83
- if include_blank.is_a? Hash
84
- text = include_blank.delete(:text)
85
-
86
- [ text, hw_call_render_in_proc(hw_render_in_proc(include_blank), text, display: text, value: "") ]
87
- else
88
- [ include_blank, include_blank ]
89
- end
164
+ params[:page].to_i > 0 ? :append : :update
90
165
  end
91
166
 
92
167
  def hw_uri_with_params(url_or_path, **params)
@@ -98,13 +173,15 @@ module HotwireCombobox
98
173
  url_or_path
99
174
  end
100
175
 
101
- private
102
- def hw_render_in_proc(render_in)
103
- if render_in.present?
104
- ->(object, locals) { render(**render_in.reverse_merge(object: object, locals: locals)) }
176
+ def hw_call_method_or_proc(object, method_or_proc)
177
+ if method_or_proc.is_a? Proc
178
+ method_or_proc.call object
179
+ else
180
+ hw_call_method object, method_or_proc
105
181
  end
106
182
  end
107
183
 
184
+ private
108
185
  def hw_extract_options_and_src(options_or_src, render_in, include_blank)
109
186
  if options_or_src.is_a? String
110
187
  [ [], options_or_src ]
@@ -113,58 +190,6 @@ module HotwireCombobox
113
190
  end
114
191
  end
115
192
 
116
- def hw_parse_combobox_options(options, render_in_proc: nil, **methods)
117
- options.map do |option|
118
- HotwireCombobox::Listbox::Option.new \
119
- **hw_option_attrs_for(option, render_in_proc: render_in_proc, **methods)
120
- end
121
- end
122
-
123
- def hw_option_attrs_for(option, render_in_proc: nil, **methods)
124
- case option
125
- when Hash
126
- option.tap do |attrs|
127
- attrs[:content] = hw_call_render_in_proc(render_in_proc, attrs[:display], attrs) if render_in_proc
128
- end
129
- when String
130
- {}.tap do |attrs|
131
- attrs[:display] = option
132
- attrs[:value] = option
133
- attrs[:content] = hw_call_render_in_proc(render_in_proc, attrs[:display], attrs) if render_in_proc
134
- end
135
- when Array
136
- {}.tap do |attrs|
137
- attrs[:display] = option.first
138
- attrs[:value] = option.last
139
- attrs[:content] = hw_call_render_in_proc(render_in_proc, attrs[:display], attrs) if render_in_proc
140
- end
141
- else
142
- {}.tap do |attrs|
143
- attrs[:id] = hw_call_method_or_proc(option, methods[:id]) if methods[:id]
144
- attrs[:display] = hw_call_method_or_proc(option, methods[:display]) if methods[:display]
145
- attrs[:value] = hw_call_method_or_proc(option, methods[:value] || :id)
146
-
147
- if render_in_proc
148
- attrs[:content] = hw_call_render_in_proc(render_in_proc, option, attrs)
149
- elsif methods[:content]
150
- attrs[:content] = hw_call_method_or_proc(option, methods[:content])
151
- end
152
- end
153
- end
154
- end
155
-
156
- def hw_call_render_in_proc(render_in_proc, object, attrs)
157
- render_in_proc.(object, combobox_display: attrs[:display], combobox_value: attrs[:value])
158
- end
159
-
160
- def hw_call_method_or_proc(object, method_or_proc)
161
- if method_or_proc.is_a? Proc
162
- method_or_proc.call object
163
- else
164
- hw_call_method object, method_or_proc
165
- end
166
- end
167
-
168
193
  def hw_call_method(object, method)
169
194
  if object.respond_to? method
170
195
  object.public_send method
@@ -1,3 +1,3 @@
1
1
  module HotwireCombobox
2
- VERSION = "0.1.43"
2
+ VERSION = "0.2.0"
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.43
4
+ version: 0.2.0
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-03-15 00:00:00.000000000 Z
11
+ date: 2024-03-22 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails
@@ -69,12 +69,15 @@ files:
69
69
  - app/assets/javascripts/hw_combobox/helpers.js
70
70
  - app/assets/javascripts/hw_combobox/models/combobox.js
71
71
  - app/assets/javascripts/hw_combobox/models/combobox/actors.js
72
+ - app/assets/javascripts/hw_combobox/models/combobox/announcements.js
72
73
  - app/assets/javascripts/hw_combobox/models/combobox/async_loading.js
73
74
  - app/assets/javascripts/hw_combobox/models/combobox/autocomplete.js
74
75
  - app/assets/javascripts/hw_combobox/models/combobox/base.js
75
76
  - app/assets/javascripts/hw_combobox/models/combobox/dialog.js
76
77
  - app/assets/javascripts/hw_combobox/models/combobox/events.js
77
78
  - app/assets/javascripts/hw_combobox/models/combobox/filtering.js
79
+ - app/assets/javascripts/hw_combobox/models/combobox/form_field.js
80
+ - app/assets/javascripts/hw_combobox/models/combobox/multiselect.js
78
81
  - app/assets/javascripts/hw_combobox/models/combobox/navigation.js
79
82
  - app/assets/javascripts/hw_combobox/models/combobox/new_options.js
80
83
  - app/assets/javascripts/hw_combobox/models/combobox/options.js
@@ -86,15 +89,19 @@ files:
86
89
  - app/assets/stylesheets/hotwire_combobox.css
87
90
  - app/presenters/hotwire_combobox/component.rb
88
91
  - app/presenters/hotwire_combobox/component/customizable.rb
92
+ - app/presenters/hotwire_combobox/listbox/group.rb
93
+ - app/presenters/hotwire_combobox/listbox/item.rb
89
94
  - app/presenters/hotwire_combobox/listbox/option.rb
90
95
  - app/views/hotwire_combobox/_component.html.erb
91
96
  - app/views/hotwire_combobox/_next_page.turbo_stream.erb
92
97
  - app/views/hotwire_combobox/_paginated_options.turbo_stream.erb
93
98
  - app/views/hotwire_combobox/_pagination.html.erb
99
+ - app/views/hotwire_combobox/_selection_chip.turbo_stream.erb
94
100
  - app/views/hotwire_combobox/combobox/_dialog.html.erb
95
101
  - app/views/hotwire_combobox/combobox/_hidden_field.html.erb
96
102
  - app/views/hotwire_combobox/combobox/_input.html.erb
97
103
  - app/views/hotwire_combobox/combobox/_paginated_listbox.html.erb
104
+ - app/views/hotwire_combobox/layouts/_selection_chip.turbo_stream.erb
98
105
  - config/hw_importmap.rb
99
106
  - lib/hotwire_combobox.rb
100
107
  - lib/hotwire_combobox/engine.rb