advanced_select 0.1.0 → 0.1.2

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 54b72a840fc9224ed932cb7cef5f070b9d1886be46a88898879d7a1d4bd9681b
4
- data.tar.gz: 6799024650b16bc9110353edf37ae1507c1a07b5b8482109a992cd436b47f5a0
3
+ metadata.gz: 8acafe10e284da26e61bec53737160926b3b6f43e51aa4d2a690e9a9b5a47367
4
+ data.tar.gz: c2465e1a792dc86ece9747b18fe2dcb3e077d706aed3e178515fe1376308067b
5
5
  SHA512:
6
- metadata.gz: 4084582d35f08891e34dbaebdd46ec565ac7f169a4752c9bc8dc58390f81f5cdad0163b3bff45d3e987f15a9e65c7d3029be20f0feadc4ed3ab7d0aa2a336c60
7
- data.tar.gz: b797dc1f36f445dfcb4b18b3b0fd59e810670bc09efaf4db4640f80f4ea465f972fab662ecd22f1edc8106288cd22281733f7a7927fa7f802f08995b8430e862
6
+ metadata.gz: d9a0cefc246959a5f8f5910e91e648f5654c55eee8bbb63ae78232c5abd9b7ab0b307eb04744f70016d5e23ab54cc3b7c9a6973c18b5d2ea3b885255a54e8c5a
7
+ data.tar.gz: 4e4143bb1da3fb3e50f5aa6fda9cce40f3631c6f7d0ba5b2185a5f06ca4cb9f0b16938932aca365206cf0391ac5b8151335ae1e32e1e5c9abd4a26447e383617
data/README.md CHANGED
@@ -75,7 +75,7 @@ AdvancedSelect does not provide query objects, model concerns, authorization log
75
75
  Add the gem to the host Rails app:
76
76
 
77
77
  ```ruby
78
- gem "advanced_select", git: "https://github.com/MehmetCelik4/advanced_select.git"
78
+ gem "advanced_select", "~> 0.1.0"
79
79
  ```
80
80
 
81
81
  Run the installer:
@@ -408,6 +408,25 @@ For remote multiple options, pass `multiple: true` to the options render too:
408
408
  ) %>
409
409
  ```
410
410
 
411
+ When users select an option from a long multiple-select list, AdvancedSelect moves that visible option to the top of the dropdown. This applies to both local options and remote Turbo Stream-refreshed option lists, so newly selected rows stay easy to review and deselect.
412
+
413
+ For multiple selects, AdvancedSelect emits a blank hidden field (`<input type="hidden" name="..." value="">`) before the selected values, matching Rails' built-in `f.select multiple: true` and `f.collection_check_boxes` behavior. Without it, clearing every selection would leave the parameter out of the submission entirely, so Rails would skip updating the association instead of clearing it.
414
+
415
+ Pass `include_hidden: false` to opt out — for example, when your controller relies on the parameter being absent for partial updates:
416
+
417
+ ```erb
418
+ <%= advanced_select_tag(
419
+ "record[item_ids][]",
420
+ id: "record_item_ids",
421
+ selected: selected_options,
422
+ options: options,
423
+ placeholder: t(".items_placeholder"),
424
+ multiple: true,
425
+ include_hidden: false,
426
+ searchable: false
427
+ ) %>
428
+ ```
429
+
411
430
  ### Add Mode
412
431
 
413
432
  Set `add_mode: true` when users may submit a new typed value:
@@ -610,6 +629,7 @@ advanced_select_tag(
610
629
  searchable: true,
611
630
  add_mode: false,
612
631
  dependent_fields: {},
632
+ include_hidden: true,
613
633
  option_content_partial: nil,
614
634
  classes: {},
615
635
  append_classes: {}
@@ -8,6 +8,7 @@ export default class extends Controller {
8
8
  delay: { type: Number, default: 200 },
9
9
  dependentFields: Object,
10
10
  errorText: String,
11
+ includeHidden: { type: Boolean, default: true },
11
12
  inputId: String,
12
13
  loadingText: String,
13
14
  multiple: Boolean,
@@ -37,6 +38,7 @@ export default class extends Controller {
37
38
  displayLabel: option.displayLabel || option.label
38
39
  }))
39
40
  this.close = this.close.bind(this)
41
+ this.renderOptionsState()
40
42
  }
41
43
 
42
44
  disconnect() {
@@ -214,7 +216,7 @@ export default class extends Controller {
214
216
  if (this.selectedValue.some((option) => option.id === value)) {
215
217
  this.selectedValue = this.selectedValue.filter((option) => option.id !== value)
216
218
  } else {
217
- this.selectedValue = this.selectedValue.concat({ id: value, value: submitValue, label, displayLabel })
219
+ this.selectedValue = [{ id: value, value: submitValue, label, displayLabel }, ...this.selectedValue]
218
220
  }
219
221
  } else {
220
222
  this.selectedValue = [{ id: value, value: submitValue, label, displayLabel }]
@@ -240,6 +242,7 @@ export default class extends Controller {
240
242
 
241
243
  renderOptionsState() {
242
244
  const selectedIds = new Set(this.selectedValue.map((option) => option.id))
245
+ const container = this.currentOptionsTarget
243
246
 
244
247
  this.optionElements.forEach((option) => {
245
248
  const selected = selectedIds.has(option.dataset.advancedSelectValueParam)
@@ -251,6 +254,13 @@ export default class extends Controller {
251
254
  check.textContent = selected ? "\u2713" : ""
252
255
  }
253
256
  })
257
+
258
+ for (let i = this.selectedValue.length - 1; i >= 0; i--) {
259
+ const option = container.querySelector(
260
+ `[data-advanced-select-option][data-advanced-select-value-param="${this.selectedValue[i].id}"]`
261
+ )
262
+ if (option) container.prepend(option)
263
+ }
254
264
  }
255
265
 
256
266
  chooseActiveOption() {
@@ -303,7 +313,13 @@ export default class extends Controller {
303
313
  }
304
314
 
305
315
  get hiddenFieldElements() {
306
- const options = this.multipleValue ? this.selectedValue : [this.selectedValue[0]]
316
+ let options
317
+
318
+ if (this.multipleValue) {
319
+ options = this.includeHiddenValue ? [null, ...this.selectedValue] : this.selectedValue
320
+ } else {
321
+ options = [this.selectedValue[0]]
322
+ }
307
323
 
308
324
  return options.map((option) => {
309
325
  const input = document.createElement("input")
@@ -376,4 +392,4 @@ export default class extends Controller {
376
392
  get expanded() {
377
393
  return this.triggerTarget.getAttribute("aria-expanded") === "true"
378
394
  }
379
- }
395
+ }
@@ -4,13 +4,15 @@
4
4
  aria-busy="false"
5
5
  <% if local_assigns[:multiple] %>aria-multiselectable="true"<% end %>
6
6
  role="listbox">
7
+ <% selected_option_ids = advanced_select_selected_option_ids(selected_options) %>
8
+
7
9
  <% advanced_select_option_groups(options).each do |group| %>
8
10
  <% if group[:label].present? %>
9
11
  <div class="<%= advanced_select_class(class_map, :group_label) %>"><%= group.fetch(:label) %></div>
10
12
  <% end %>
11
13
 
12
14
  <% group.fetch(:options).each do |option| %>
13
- <%= render partial: "advanced_select/option", locals: { option: option, selected_options: selected_options, option_content_partial: option_content_partial, class_map: class_map } %>
15
+ <%= advanced_select_option_tag(option, selected_options, option_content_partial, class_map, selected_option_ids: selected_option_ids) %>
14
16
  <% end %>
15
17
  <% end %>
16
18
 
@@ -11,6 +11,7 @@
11
11
  data-advanced-select-multiple-value="<%= multiple %>"
12
12
  data-advanced-select-searchable-value="<%= searchable %>"
13
13
  data-advanced-select-add-mode-value="<%= add_mode %>"
14
+ data-advanced-select-include-hidden-value="<%= include_hidden %>"
14
15
  data-advanced-select-placeholder-class="<%= advanced_select_class(class_map, :placeholder) %>"
15
16
  data-advanced-select-value-class="<%= advanced_select_class(class_map, :value) %>"
16
17
  data-advanced-select-token-class="<%= advanced_select_class(class_map, :token) %>"
@@ -22,6 +23,9 @@
22
23
  data-advanced-select-selected-value="<%= advanced_select_selected_value(selected_options) %>">
23
24
  <div data-advanced-select-target="hiddenFields">
24
25
  <% if multiple %>
26
+ <% if include_hidden %>
27
+ <%= hidden_field_tag name, "", id: nil %>
28
+ <% end %>
25
29
  <% selected_options.each do |option| %>
26
30
  <%= hidden_field_tag name, option.fetch(:value, option.fetch(:id)) %>
27
31
  <% end %>
@@ -1,6 +1,6 @@
1
1
  module AdvancedSelect
2
2
  module Helper
3
- def advanced_select_tag(name, id:, selected:, options:, placeholder:, options_url: nil, multiple: false, searchable: true, add_mode: false, dependent_fields: {}, option_content_partial: nil, classes: {}, append_classes: {})
3
+ def advanced_select_tag(name, id:, selected:, options:, placeholder:, options_url: nil, multiple: false, searchable: true, add_mode: false, dependent_fields: {}, include_hidden: true, option_content_partial: nil, classes: {}, append_classes: {})
4
4
  selected_options = advanced_select_selected_options(selected)
5
5
  class_map = advanced_select_class_map(classes, append_classes)
6
6
 
@@ -15,6 +15,7 @@ module AdvancedSelect
15
15
  searchable: searchable && options_url.present?,
16
16
  add_mode: add_mode,
17
17
  dependent_fields: dependent_fields,
18
+ include_hidden: include_hidden,
18
19
  target_id: "#{id}_options",
19
20
  option_content_partial: option_content_partial,
20
21
  class_map: class_map
@@ -59,6 +60,56 @@ module AdvancedSelect
59
60
  end.to_json
60
61
  end
61
62
 
63
+ def advanced_select_option_tag(option, selected_options, option_content_partial, class_map, selected_option_ids: nil)
64
+ selected = advanced_select_option_selected?(option, selected_options, selected_option_ids)
65
+
66
+ tag.button(
67
+ type: "button",
68
+ class: advanced_select_class(class_map, :option, (:option_selected if selected)),
69
+ role: "option",
70
+ aria: { selected: selected },
71
+ data: {
72
+ advanced_select_option: "",
73
+ action: "mouseenter->advanced-select#activateOption mousedown->advanced-select#choose",
74
+ advanced_select_value_param: option.fetch(:id),
75
+ advanced_select_submit_value_param: advanced_select_option_value(option),
76
+ advanced_select_label_param: advanced_select_option_label(option),
77
+ advanced_select_display_label_param: advanced_select_option_display_label(option)
78
+ }
79
+ ) do
80
+ safe_join([
81
+ tag.span(
82
+ (selected ? "\u2713" : ""),
83
+ class: advanced_select_class(class_map, :option_check),
84
+ data: { advanced_select_option_check: "" }
85
+ ),
86
+ advanced_select_option_content_tag(option, option_content_partial, class_map)
87
+ ])
88
+ end
89
+ end
90
+
91
+ def advanced_select_option_content_tag(option, option_content_partial, class_map)
92
+ if option_content_partial.present?
93
+ render partial: option_content_partial, locals: { option: option }
94
+ else
95
+ advanced_select_default_option_content_tag(option, class_map)
96
+ end
97
+ end
98
+
99
+ def advanced_select_default_option_content_tag(option, class_map)
100
+ description = advanced_select_option_description(option)
101
+ content = [tag.span(advanced_select_option_label(option))]
102
+
103
+ if description.present?
104
+ content << tag.span(
105
+ description,
106
+ class: advanced_select_class(class_map, :option_description)
107
+ )
108
+ end
109
+
110
+ tag.span(safe_join(content), class: advanced_select_class(class_map, :option_content))
111
+ end
112
+
62
113
  def advanced_select_options_for_render(options, selected_options, searchable)
63
114
  searchable ? selected_options.presence || options : options
64
115
  end
@@ -82,7 +133,15 @@ module AdvancedSelect
82
133
  end
83
134
  end
84
135
 
85
- def advanced_select_option_selected?(option, selected_options)
136
+ def advanced_select_selected_option_ids(selected_options)
137
+ selected_options.each_with_object({}) do |selected_option, selected_option_ids|
138
+ selected_option_ids[selected_option.fetch(:id).to_s] = true
139
+ end
140
+ end
141
+
142
+ def advanced_select_option_selected?(option, selected_options, selected_option_ids = nil)
143
+ return selected_option_ids.key?(option.fetch(:id).to_s) if selected_option_ids
144
+
86
145
  selected_options.any? { |selected_option| selected_option.fetch(:id).to_s == option.fetch(:id).to_s }
87
146
  end
88
147
 
@@ -1,3 +1,3 @@
1
1
  module AdvancedSelect
2
- VERSION = "0.1.0"
2
+ VERSION = "0.1.2"
3
3
  end
metadata CHANGED
@@ -1,10 +1,12 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: advanced_select
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.1.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Mehmet Celik
8
+ - Tankut Ozbeyendir
9
+ - Emre ULUSOY
8
10
  bindir: bin
9
11
  cert_chain: []
10
12
  date: 1980-01-01 00:00:00.000000000 Z
@@ -84,6 +86,8 @@ description: AdvancedSelect provides helper-rendered Rails partials, Stimulus dr
84
86
  leaving data loading, authorization, and endpoints to the host app.
85
87
  email:
86
88
  - mehmetcelik4@gmail.com
89
+ - tankutozbeyendir@gmail.com
90
+ - ulsyemr@gmail.com
87
91
  executables: []
88
92
  extensions: []
89
93
  extra_rdoc_files: []