hotwire_combobox 0.1.41 → 0.1.42

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: 1fc70c854bf6cdceda08dd727b854a63b7deb0703d9a8dafcab0bd74ca3e73d0
4
- data.tar.gz: ec4ad558a47dcdb94e68d360671a89f3d9147a7a2a172a741f06f59dfbfd096a
3
+ metadata.gz: 1fc3750ecb7bd58bd28e13d0cc2d543ae4c614535c927c9483e504b643171a8d
4
+ data.tar.gz: dc6482de8a4344a6d801797d497f6adec8afcc0357e821e1dff8188ecf4c2f68
5
5
  SHA512:
6
- metadata.gz: 1cb4ab65ec2312a055400870a1c2e91a11e598c389daf96f1f3f079627fac208fbaad73bad1a957db1a5eff33eaaba7c7d2c7eccc7aa389bdcdf9c51e26832b6
7
- data.tar.gz: fbb2db40e5b529084c5c65ab644c3af9c0f5721741f2692dcc85e76362f82ebdddf94f979d02b80328b8b39828468e591d6d61faeec3ecff7f262c327bb78a6b
6
+ metadata.gz: 8884e9e8295b73d37d15762f08c1a69452a7117f99af09dba4369aedea9af2116bc9683127f6f29535813fa5182b21969eb35e4d077edee1f94ce1a6808aadfe
7
+ data.tar.gz: b58dd5870a0508912c910b387f7970ffc4a08a16e40612b448e7c35ae244d0f8e02f9dd9356e5cdd8ae7f0c45c1bd65513ece94fcbeba38818c39d39c9ecd870
data/README.md CHANGED
@@ -80,6 +80,9 @@ import HwComboboxController from "@josefarias/hotwire_combobox"
80
80
  application.register("hw-combobox", HwComboboxController)
81
81
  ```
82
82
 
83
+ > [!WARNING]
84
+ > Keep in mind you need to update both the npm package and the gem every time there's a new version of HotwireCombobox. You should always run the same version number on both sides.
85
+
83
86
  ### Configuring CSS
84
87
 
85
88
  This library comes with optional default styles. Follow the instructions below to include them in your app.
@@ -109,6 +112,19 @@ Require the styles in `app/assets/stylesheets/application.css`:
109
112
  Visit [the docs site](https://hotwirecombobox.com/) for a demo and detailed documentation.
110
113
  If the site is down, you can run the docs locally by cloning [the docs repo](https://github.com/josefarias/hotwire_combobox_docs).
111
114
 
115
+ ## Notes about accessibility
116
+
117
+ This gem follows the [APG combobox pattern guidelines](https://www.w3.org/WAI/ARIA/apg/patterns/combobox/) with some exceptions we feel increase the usefulness of the component without much detriment to the overall accessible experience.
118
+
119
+ These are the exceptions:
120
+
121
+ 1. Users cannot manipulate the combobox while it's closed. As long as the combobox is focused, the listbox is shown.
122
+ 2. The escape key closes the listbox and blurs the combobox. It does not clear the combobox.
123
+ 3. The listbox has wrap-around selection. That is, pressing `Up Arrow` when the user is on the first option will select the last option. And pressing `Down Arrow` when on the last option will select the first option. In paginated comboboxes, the first and last options refer to the currently available options. More options may be loaded after navigating to the last currently available option.
124
+ 4. It is possible to have an unlabled combobox, as that responsibility is delegated to the implementing user.
125
+
126
+ It should be noted none of the maintainers use assistive technologies in their daily lives. If you do, and you feel these exceptions are detrimental to your ability to use the component, or if you find an undocumented exception, please [open a GitHub issue](https://github.com/josefarias/hotwire_combobox/issues). We'll get it sorted.
127
+
112
128
  ## Contributing
113
129
 
114
130
  Please read [CONTRIBUTING.md](./CONTRIBUTING.md).
@@ -237,16 +237,21 @@ Combobox.Dialog = Base => class extends Base {
237
237
 
238
238
  Combobox.Events = Base => class extends Base {
239
239
  _dispatchSelectionEvent({ isNew }) {
240
- const detail = {
240
+ dispatch("hw-combobox:selection", { target: this.element, detail: { ...this._eventableDetails, isNew } });
241
+ }
242
+
243
+ _dispatchClosedEvent() {
244
+ dispatch("hw-combobox:closed", { target: this.element, detail: this._eventableDetails });
245
+ }
246
+
247
+ get _eventableDetails() {
248
+ return {
241
249
  value: this.hiddenFieldTarget.value,
242
250
  display: this._fullQuery,
243
251
  query: this._typedQuery,
244
252
  fieldName: this.hiddenFieldTarget.name,
245
- isValid: this._valueIsValid,
246
- isNew: isNew
247
- };
248
-
249
- dispatch("hw-combobox:selection", { target: this.element, detail });
253
+ isValid: this._valueIsValid
254
+ }
250
255
  }
251
256
  };
252
257
 
@@ -1099,6 +1104,7 @@ Combobox.Toggle = Base => class extends Base {
1099
1104
  if (this._isOpen) {
1100
1105
  this._lockInSelection();
1101
1106
  this.expandedValue = false;
1107
+ this._dispatchClosedEvent();
1102
1108
  }
1103
1109
  }
1104
1110
 
@@ -241,16 +241,21 @@
241
241
 
242
242
  Combobox.Events = Base => class extends Base {
243
243
  _dispatchSelectionEvent({ isNew }) {
244
- const detail = {
244
+ dispatch("hw-combobox:selection", { target: this.element, detail: { ...this._eventableDetails, isNew } });
245
+ }
246
+
247
+ _dispatchClosedEvent() {
248
+ dispatch("hw-combobox:closed", { target: this.element, detail: this._eventableDetails });
249
+ }
250
+
251
+ get _eventableDetails() {
252
+ return {
245
253
  value: this.hiddenFieldTarget.value,
246
254
  display: this._fullQuery,
247
255
  query: this._typedQuery,
248
256
  fieldName: this.hiddenFieldTarget.name,
249
- isValid: this._valueIsValid,
250
- isNew: isNew
251
- };
252
-
253
- dispatch("hw-combobox:selection", { target: this.element, detail });
257
+ isValid: this._valueIsValid
258
+ }
254
259
  }
255
260
  };
256
261
 
@@ -1103,6 +1108,7 @@
1103
1108
  if (this._isOpen) {
1104
1109
  this._lockInSelection();
1105
1110
  this.expandedValue = false;
1111
+ this._dispatchClosedEvent();
1106
1112
  }
1107
1113
  }
1108
1114
 
@@ -3,15 +3,20 @@ import { dispatch } from "hw_combobox/helpers"
3
3
 
4
4
  Combobox.Events = Base => class extends Base {
5
5
  _dispatchSelectionEvent({ isNew }) {
6
- const detail = {
6
+ dispatch("hw-combobox:selection", { target: this.element, detail: { ...this._eventableDetails, isNew } })
7
+ }
8
+
9
+ _dispatchClosedEvent() {
10
+ dispatch("hw-combobox:closed", { target: this.element, detail: this._eventableDetails })
11
+ }
12
+
13
+ get _eventableDetails() {
14
+ return {
7
15
  value: this.hiddenFieldTarget.value,
8
16
  display: this._fullQuery,
9
17
  query: this._typedQuery,
10
18
  fieldName: this.hiddenFieldTarget.name,
11
- isValid: this._valueIsValid,
12
- isNew: isNew
19
+ isValid: this._valueIsValid
13
20
  }
14
-
15
- dispatch("hw-combobox:selection", { target: this.element, detail })
16
21
  }
17
22
  }
@@ -10,6 +10,7 @@ Combobox.Toggle = Base => class extends Base {
10
10
  if (this._isOpen) {
11
11
  this._lockInSelection()
12
12
  this.expandedValue = false
13
+ this._dispatchClosedEvent()
13
14
  }
14
15
  }
15
16
 
@@ -9,8 +9,8 @@ module HotwireCombobox
9
9
 
10
10
  unless HotwireCombobox.bypass_convenience_methods?
11
11
  module FormBuilderExtensions
12
- def combobox(*args, **kwargs)
13
- @template.hw_combobox_tag(*args, **kwargs.merge(form: self))
12
+ def combobox(*args, **kwargs, &block)
13
+ @template.hw_combobox_tag(*args, **kwargs.merge(form: self), &block)
14
14
  end
15
15
  end
16
16
 
@@ -26,11 +26,9 @@ module HotwireCombobox
26
26
  if options.first.is_a? HotwireCombobox::Listbox::Option
27
27
  options
28
28
  else
29
- render_in_proc = hw_render_in_proc(render_in) if render_in.present?
30
-
31
- hw_parse_combobox_options(options, render_in: render_in_proc, **methods.merge(display: display)).tap do |options|
32
- options.unshift(hw_blank_option(include_blank)) if include_blank.present?
33
- end
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
34
32
  end
35
33
  end
36
34
  hw_alias :hw_combobox_options
@@ -85,7 +83,7 @@ module HotwireCombobox
85
83
  if include_blank.is_a? Hash
86
84
  text = include_blank.delete(:text)
87
85
 
88
- [ text, hw_render_in_proc(include_blank).(text) ]
86
+ [ text, hw_call_render_in_proc(hw_render_in_proc(include_blank), text, display: text, value: "") ]
89
87
  else
90
88
  [ include_blank, include_blank ]
91
89
  end
@@ -102,7 +100,9 @@ module HotwireCombobox
102
100
 
103
101
  private
104
102
  def hw_render_in_proc(render_in)
105
- ->(object) { render(**render_in.reverse_merge(object: object)) }
103
+ if render_in.present?
104
+ ->(object, locals) { render(**render_in.reverse_merge(object: object, locals: locals)) }
105
+ end
106
106
  end
107
107
 
108
108
  def hw_extract_options_and_src(options_or_src, render_in, include_blank)
@@ -113,40 +113,50 @@ module HotwireCombobox
113
113
  end
114
114
  end
115
115
 
116
- def hw_parse_combobox_options(options, render_in: nil, **methods)
116
+ def hw_parse_combobox_options(options, render_in_proc: nil, **methods)
117
117
  options.map do |option|
118
118
  HotwireCombobox::Listbox::Option.new \
119
- **hw_option_attrs_for(option, render_in: render_in, **methods)
119
+ **hw_option_attrs_for(option, render_in_proc: render_in_proc, **methods)
120
120
  end
121
121
  end
122
122
 
123
- def hw_option_attrs_for(option, render_in: nil, **methods)
123
+ def hw_option_attrs_for(option, render_in_proc: nil, **methods)
124
124
  case option
125
125
  when Hash
126
- option
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
127
129
  when String
128
130
  {}.tap do |attrs|
129
131
  attrs[:display] = option
130
132
  attrs[:value] = option
131
- attrs[:content] = render_in.(option) if render_in
133
+ attrs[:content] = hw_call_render_in_proc(render_in_proc, attrs[:display], attrs) if render_in_proc
132
134
  end
133
135
  when Array
134
136
  {}.tap do |attrs|
135
137
  attrs[:display] = option.first
136
138
  attrs[:value] = option.last
137
- attrs[:content] = render_in.(option.first) if render_in
139
+ attrs[:content] = hw_call_render_in_proc(render_in_proc, attrs[:display], attrs) if render_in_proc
138
140
  end
139
141
  else
140
142
  {}.tap do |attrs|
141
- attrs[:value] = hw_call_method_or_proc(option, methods[:value] || :id)
142
-
143
143
  attrs[:id] = hw_call_method_or_proc(option, methods[:id]) if methods[:id]
144
144
  attrs[:display] = hw_call_method_or_proc(option, methods[:display]) if methods[:display]
145
- attrs[:content] = hw_call_method_or_proc(option, render_in || methods[:content]) if render_in || methods[:content]
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
146
152
  end
147
153
  end
148
154
  end
149
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
+
150
160
  def hw_call_method_or_proc(object, method_or_proc)
151
161
  if method_or_proc.is_a? Proc
152
162
  method_or_proc.call object
@@ -1,3 +1,3 @@
1
1
  module HotwireCombobox
2
- VERSION = "0.1.41"
2
+ VERSION = "0.1.42"
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.41
4
+ version: 0.1.42
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-12 00:00:00.000000000 Z
11
+ date: 2024-03-14 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails