advanced_select 0.1.5 → 0.1.7
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 +18 -4
- data/app/javascript/advanced_select/advanced_select_controller.js +147 -25
- data/lib/advanced_select/helper.rb +13 -3
- data/lib/advanced_select/version.rb +1 -1
- data/lib/generators/advanced_select/install/templates/advanced_select_controller.js +147 -27
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: e0e19ef001d7d6c5ca336850d173c8b0f18397d8b03de9d77ce0f52506cf7b62
|
|
4
|
+
data.tar.gz: 374e46c6892ca911d29ddeba525ac3f9695a2c2f166028223f0c577b5f56c3fb
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 30d707af2c9f59b38535c99bebe961f29097fa934451d3e539201f1519951f0ed306fde9afca729c64e8fff2c78ac7cda75f45b72288fc68404df3b335d42fd2
|
|
7
|
+
data.tar.gz: e4099f22327d3b4274bb419c80616684eed98db8d76997fbf33d57ffe96df10ace487b2df0f53fdd87a1033c387c1fbff1e1b6cc056c46a61c74e92bf54a634a
|
data/README.md
CHANGED
|
@@ -77,7 +77,7 @@ AdvancedSelect does not provide query objects, model concerns, authorization log
|
|
|
77
77
|
Add the gem to the host Rails app:
|
|
78
78
|
|
|
79
79
|
```ruby
|
|
80
|
-
gem "advanced_select", "~> 0.1.
|
|
80
|
+
gem "advanced_select", "~> 0.1.7"
|
|
81
81
|
```
|
|
82
82
|
|
|
83
83
|
Run the installer:
|
|
@@ -484,24 +484,38 @@ For richer layouts — a table, badges, compatibility columns, etc. — pass `to
|
|
|
484
484
|
) %>
|
|
485
485
|
```
|
|
486
486
|
|
|
487
|
+
The partial is rendered on the server for the initial selection, and the controller keeps it in sync client-side as the selection changes — no Turbo Stream round-trip. To opt in, mark the row container and provide a one-row `<template>` the controller clones per selected option:
|
|
488
|
+
|
|
489
|
+
- `data-advanced-select-tooltip-list` on the element that holds the rows (the same hook the built-in list uses).
|
|
490
|
+
- `<template data-advanced-select-tooltip-template>` containing the markup for a single selected row.
|
|
491
|
+
- `data-advanced-select-tooltip-field="…"` on the elements inside the template that should be filled in. Available fields include `id`, `value`, `label`, `display_label`, extra option keys, and nested paths such as `test.name`; values are written with `textContent` (text only, no markup).
|
|
492
|
+
|
|
487
493
|
```erb
|
|
488
494
|
<%# app/views/alternatives/_tooltip.html.erb %>
|
|
489
495
|
<table>
|
|
490
496
|
<thead>
|
|
491
|
-
<tr><th>Code & Name</th><th>Type</th></tr>
|
|
497
|
+
<tr><th>Code & Name</th><th>Type</th><th>Test</th></tr>
|
|
492
498
|
</thead>
|
|
493
|
-
<tbody>
|
|
499
|
+
<tbody data-advanced-select-tooltip-list>
|
|
494
500
|
<% selected_options.each do |option| %>
|
|
495
501
|
<tr>
|
|
496
502
|
<td><%= option.fetch(:display_label) %></td>
|
|
497
503
|
<td><%= option.fetch(:value) %></td>
|
|
504
|
+
<td><%= option.dig(:test, :name) %></td>
|
|
498
505
|
</tr>
|
|
499
506
|
<% end %>
|
|
500
507
|
</tbody>
|
|
508
|
+
<template data-advanced-select-tooltip-template>
|
|
509
|
+
<tr>
|
|
510
|
+
<td data-advanced-select-tooltip-field="display_label"></td>
|
|
511
|
+
<td data-advanced-select-tooltip-field="value"></td>
|
|
512
|
+
<td data-advanced-select-tooltip-field="test.name"></td>
|
|
513
|
+
</tr>
|
|
514
|
+
</template>
|
|
501
515
|
</table>
|
|
502
516
|
```
|
|
503
517
|
|
|
504
|
-
|
|
518
|
+
The initial server-rendered rows stay until the first change, then the controller rebuilds them from the template — so just like `tooltip: true`, the custom tooltip updates client-side automatically. A custom partial without a `<template>` still renders once and stays static, which is fine for display-only content.
|
|
505
519
|
|
|
506
520
|
### Add Mode
|
|
507
521
|
|
|
@@ -39,11 +39,7 @@ export default class extends Controller {
|
|
|
39
39
|
this.optionActiveClasses = this.classList(this.element.dataset.advancedSelectOptionActiveClass || "ui-advanced-select-option-active")
|
|
40
40
|
this.addOptionActiveClasses = this.classList(this.element.dataset.advancedSelectAddOptionActiveClass || "")
|
|
41
41
|
this.optionSelectedClasses = this.classList(this.element.dataset.advancedSelectOptionSelectedClass || "")
|
|
42
|
-
this.selectedValue = this.selectedValue.map((option) => (
|
|
43
|
-
...option,
|
|
44
|
-
id: option.id.toString(),
|
|
45
|
-
displayLabel: option.displayLabel || option.label
|
|
46
|
-
}))
|
|
42
|
+
this.selectedValue = this.selectedValue.map((option) => this.normalizeSelectedOption(option))
|
|
47
43
|
this.close = this.close.bind(this)
|
|
48
44
|
this.renderOptionsState()
|
|
49
45
|
this.setupEagerDependentFields()
|
|
@@ -109,7 +105,7 @@ export default class extends Controller {
|
|
|
109
105
|
|
|
110
106
|
choose(event) {
|
|
111
107
|
event.preventDefault()
|
|
112
|
-
this.
|
|
108
|
+
this.selectOptionFromElement(event.currentTarget)
|
|
113
109
|
}
|
|
114
110
|
|
|
115
111
|
add(event) {
|
|
@@ -281,17 +277,28 @@ export default class extends Controller {
|
|
|
281
277
|
}
|
|
282
278
|
|
|
283
279
|
selectOption(value, label, submitValue = value, { displayLabel = label, refreshOptions = this.multipleValue } = {}) {
|
|
284
|
-
|
|
285
|
-
|
|
280
|
+
this.selectOptionData(
|
|
281
|
+
this.normalizeSelectedOption({ id: value, value: submitValue, label, displayLabel }),
|
|
282
|
+
{ refreshOptions }
|
|
283
|
+
)
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
selectOptionFromElement(element, { refreshOptions = this.multipleValue } = {}) {
|
|
287
|
+
this.selectOptionData(this.optionData(element), { refreshOptions })
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
selectOptionData(option, { refreshOptions = this.multipleValue } = {}) {
|
|
291
|
+
const selectedOption = this.normalizeSelectedOption(option)
|
|
292
|
+
const value = selectedOption.id
|
|
286
293
|
|
|
287
294
|
if (this.multipleValue) {
|
|
288
295
|
if (this.selectedValue.some((option) => option.id === value)) {
|
|
289
296
|
this.selectedValue = this.selectedValue.filter((option) => option.id !== value)
|
|
290
297
|
} else {
|
|
291
|
-
this.selectedValue = [
|
|
298
|
+
this.selectedValue = [selectedOption, ...this.selectedValue]
|
|
292
299
|
}
|
|
293
300
|
} else {
|
|
294
|
-
this.selectedValue = [
|
|
301
|
+
this.selectedValue = [selectedOption]
|
|
295
302
|
}
|
|
296
303
|
|
|
297
304
|
this.renderSelection()
|
|
@@ -304,6 +311,51 @@ export default class extends Controller {
|
|
|
304
311
|
}
|
|
305
312
|
}
|
|
306
313
|
|
|
314
|
+
optionData(element) {
|
|
315
|
+
const data = this.parseOptionData(element.dataset.advancedSelectOptionParam)
|
|
316
|
+
const value = element.dataset.advancedSelectValueParam
|
|
317
|
+
const submitValue = element.dataset.advancedSelectSubmitValueParam || value
|
|
318
|
+
const label = element.dataset.advancedSelectLabelParam
|
|
319
|
+
const displayLabel = element.dataset.advancedSelectDisplayLabelParam || label
|
|
320
|
+
|
|
321
|
+
return {
|
|
322
|
+
...data,
|
|
323
|
+
id: value,
|
|
324
|
+
value: submitValue,
|
|
325
|
+
label,
|
|
326
|
+
displayLabel
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
parseOptionData(json) {
|
|
331
|
+
if (!json) {
|
|
332
|
+
return {}
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
try {
|
|
336
|
+
const data = JSON.parse(json)
|
|
337
|
+
return data && typeof data === "object" && !Array.isArray(data) ? data : {}
|
|
338
|
+
} catch (_error) {
|
|
339
|
+
return {}
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
normalizeSelectedOption(option) {
|
|
344
|
+
const id = option.id.toString()
|
|
345
|
+
const value = (option.value || id).toString()
|
|
346
|
+
const label = (option.label || option.displayLabel || option.display_label || value).toString()
|
|
347
|
+
const displayLabel = (option.displayLabel || option.display_label || label).toString()
|
|
348
|
+
|
|
349
|
+
return {
|
|
350
|
+
...option,
|
|
351
|
+
id,
|
|
352
|
+
value,
|
|
353
|
+
label,
|
|
354
|
+
displayLabel,
|
|
355
|
+
display_label: displayLabel
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
|
|
307
359
|
renderSelection() {
|
|
308
360
|
this.hiddenFieldsTarget.replaceChildren(...this.hiddenFieldElements)
|
|
309
361
|
this.summaryTarget.replaceChildren(...this.selectionElements)
|
|
@@ -383,13 +435,7 @@ export default class extends Controller {
|
|
|
383
435
|
return
|
|
384
436
|
}
|
|
385
437
|
|
|
386
|
-
|
|
387
|
-
this.selectOption(
|
|
388
|
-
option.dataset.advancedSelectValueParam,
|
|
389
|
-
option.dataset.advancedSelectLabelParam,
|
|
390
|
-
option.dataset.advancedSelectSubmitValueParam || option.dataset.advancedSelectValueParam,
|
|
391
|
-
{ displayLabel: option.dataset.advancedSelectDisplayLabelParam, refreshOptions: false }
|
|
392
|
-
)
|
|
438
|
+
this.selectOptionFromElement(options[0], { refreshOptions: false })
|
|
393
439
|
}
|
|
394
440
|
|
|
395
441
|
chooseActiveOption() {
|
|
@@ -401,12 +447,7 @@ export default class extends Controller {
|
|
|
401
447
|
this.activeOption.dataset.advancedSelectDisplayLabelParam
|
|
402
448
|
)
|
|
403
449
|
} else {
|
|
404
|
-
this.
|
|
405
|
-
this.activeOption.dataset.advancedSelectValueParam,
|
|
406
|
-
this.activeOption.dataset.advancedSelectLabelParam,
|
|
407
|
-
this.activeOption.dataset.advancedSelectSubmitValueParam || this.activeOption.dataset.advancedSelectValueParam,
|
|
408
|
-
{ displayLabel: this.activeOption.dataset.advancedSelectDisplayLabelParam }
|
|
409
|
-
)
|
|
450
|
+
this.selectOptionFromElement(this.activeOption)
|
|
410
451
|
}
|
|
411
452
|
}
|
|
412
453
|
|
|
@@ -526,10 +567,19 @@ export default class extends Controller {
|
|
|
526
567
|
}
|
|
527
568
|
|
|
528
569
|
renderTooltip() {
|
|
529
|
-
if (!this.hasTooltipTarget
|
|
570
|
+
if (!this.hasTooltipTarget) {
|
|
530
571
|
return
|
|
531
572
|
}
|
|
532
573
|
|
|
574
|
+
if (this.tooltipTarget.hasAttribute("data-advanced-select-tooltip-custom")) {
|
|
575
|
+
this.renderCustomTooltip()
|
|
576
|
+
return
|
|
577
|
+
}
|
|
578
|
+
|
|
579
|
+
this.renderBuiltInTooltip()
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
renderBuiltInTooltip() {
|
|
533
583
|
const list = this.tooltipTarget.querySelector("[data-advanced-select-tooltip-list]")
|
|
534
584
|
if (!list) {
|
|
535
585
|
return
|
|
@@ -544,6 +594,78 @@ export default class extends Controller {
|
|
|
544
594
|
}
|
|
545
595
|
}
|
|
546
596
|
|
|
597
|
+
renderCustomTooltip() {
|
|
598
|
+
const list = this.tooltipTarget.querySelector("[data-advanced-select-tooltip-list]")
|
|
599
|
+
const template = this.customTooltipTemplate()
|
|
600
|
+
|
|
601
|
+
if (!list || !template) {
|
|
602
|
+
if (this.selectedValue.length === 0) {
|
|
603
|
+
this.hideTooltip(true)
|
|
604
|
+
}
|
|
605
|
+
return
|
|
606
|
+
}
|
|
607
|
+
|
|
608
|
+
list.replaceChildren(
|
|
609
|
+
...this.selectedValue.map((option) => this.customTooltipElement(template, option))
|
|
610
|
+
)
|
|
611
|
+
|
|
612
|
+
if (this.selectedValue.length === 0) {
|
|
613
|
+
this.hideTooltip(true)
|
|
614
|
+
}
|
|
615
|
+
}
|
|
616
|
+
|
|
617
|
+
customTooltipTemplate() {
|
|
618
|
+
if (this.customTooltipTemplateContent) {
|
|
619
|
+
return this.customTooltipTemplateContent
|
|
620
|
+
}
|
|
621
|
+
|
|
622
|
+
const template = this.tooltipTarget.querySelector("template[data-advanced-select-tooltip-template]")
|
|
623
|
+
if (!template) {
|
|
624
|
+
return null
|
|
625
|
+
}
|
|
626
|
+
|
|
627
|
+
this.customTooltipTemplateContent = template.content.cloneNode(true)
|
|
628
|
+
template.remove()
|
|
629
|
+
return this.customTooltipTemplateContent
|
|
630
|
+
}
|
|
631
|
+
|
|
632
|
+
customTooltipElement(template, option) {
|
|
633
|
+
const fragment = template.cloneNode(true)
|
|
634
|
+
fragment.querySelectorAll("[data-advanced-select-tooltip-field]").forEach((element) => {
|
|
635
|
+
element.textContent = this.tooltipFieldValue(option, element.dataset.advancedSelectTooltipField)
|
|
636
|
+
})
|
|
637
|
+
|
|
638
|
+
return fragment
|
|
639
|
+
}
|
|
640
|
+
|
|
641
|
+
tooltipFieldValue(option, field) {
|
|
642
|
+
if (field === "display_label" || field === "displayLabel") {
|
|
643
|
+
return this.displayLabel(option)
|
|
644
|
+
}
|
|
645
|
+
|
|
646
|
+
const value = this.optionFieldValue(option, field)
|
|
647
|
+
return value === undefined || value === null ? "" : value.toString()
|
|
648
|
+
}
|
|
649
|
+
|
|
650
|
+
optionFieldValue(option, field) {
|
|
651
|
+
const directValue = option[field] ?? option[this.camelize(field)]
|
|
652
|
+
if (directValue !== undefined) {
|
|
653
|
+
return directValue
|
|
654
|
+
}
|
|
655
|
+
|
|
656
|
+
return field.split(".").reduce((value, segment) => {
|
|
657
|
+
if (value === undefined || value === null) {
|
|
658
|
+
return undefined
|
|
659
|
+
}
|
|
660
|
+
|
|
661
|
+
return value[segment] ?? value[this.camelize(segment)]
|
|
662
|
+
}, option)
|
|
663
|
+
}
|
|
664
|
+
|
|
665
|
+
camelize(value) {
|
|
666
|
+
return value.replace(/_([a-z])/g, (_match, character) => character.toUpperCase())
|
|
667
|
+
}
|
|
668
|
+
|
|
547
669
|
textElement(tagName, className, text) {
|
|
548
670
|
const element = document.createElement(tagName)
|
|
549
671
|
element.className = className
|
|
@@ -579,4 +701,4 @@ export default class extends Controller {
|
|
|
579
701
|
get expanded() {
|
|
580
702
|
return this.triggerTarget.getAttribute("aria-expanded") === "true"
|
|
581
703
|
}
|
|
582
|
-
}
|
|
704
|
+
}
|
|
@@ -50,12 +50,12 @@ module AdvancedSelect
|
|
|
50
50
|
|
|
51
51
|
def advanced_select_selected_options(selected)
|
|
52
52
|
advanced_select_array(selected).map do |option|
|
|
53
|
-
|
|
53
|
+
option.to_h.merge(
|
|
54
54
|
id: option.fetch(:id).to_s,
|
|
55
55
|
value: advanced_select_option_value(option),
|
|
56
56
|
label: advanced_select_option_label(option),
|
|
57
57
|
display_label: advanced_select_option_display_label(option)
|
|
58
|
-
|
|
58
|
+
)
|
|
59
59
|
end
|
|
60
60
|
end
|
|
61
61
|
|
|
@@ -79,7 +79,8 @@ module AdvancedSelect
|
|
|
79
79
|
advanced_select_value_param: option.fetch(:id),
|
|
80
80
|
advanced_select_submit_value_param: advanced_select_option_value(option),
|
|
81
81
|
advanced_select_label_param: advanced_select_option_label(option),
|
|
82
|
-
advanced_select_display_label_param: advanced_select_option_display_label(option)
|
|
82
|
+
advanced_select_display_label_param: advanced_select_option_display_label(option),
|
|
83
|
+
advanced_select_option_param: advanced_select_option_payload(option)
|
|
83
84
|
}
|
|
84
85
|
) do
|
|
85
86
|
safe_join([
|
|
@@ -170,6 +171,15 @@ module AdvancedSelect
|
|
|
170
171
|
option[:description].to_s
|
|
171
172
|
end
|
|
172
173
|
|
|
174
|
+
def advanced_select_option_payload(option)
|
|
175
|
+
option.to_h.merge(
|
|
176
|
+
id: option.fetch(:id).to_s,
|
|
177
|
+
value: advanced_select_option_value(option),
|
|
178
|
+
label: advanced_select_option_label(option),
|
|
179
|
+
display_label: advanced_select_option_display_label(option)
|
|
180
|
+
).to_json
|
|
181
|
+
end
|
|
182
|
+
|
|
173
183
|
def advanced_select_display_label(label)
|
|
174
184
|
label.to_s.split(" > ").last
|
|
175
185
|
end
|
|
@@ -39,11 +39,7 @@ export default class extends Controller {
|
|
|
39
39
|
this.optionActiveClasses = this.classList(this.element.dataset.advancedSelectOptionActiveClass || "ui-advanced-select-option-active")
|
|
40
40
|
this.addOptionActiveClasses = this.classList(this.element.dataset.advancedSelectAddOptionActiveClass || "")
|
|
41
41
|
this.optionSelectedClasses = this.classList(this.element.dataset.advancedSelectOptionSelectedClass || "")
|
|
42
|
-
this.selectedValue = this.selectedValue.map((option) => (
|
|
43
|
-
...option,
|
|
44
|
-
id: option.id.toString(),
|
|
45
|
-
displayLabel: option.displayLabel || option.label
|
|
46
|
-
}))
|
|
42
|
+
this.selectedValue = this.selectedValue.map((option) => this.normalizeSelectedOption(option))
|
|
47
43
|
this.close = this.close.bind(this)
|
|
48
44
|
this.renderOptionsState()
|
|
49
45
|
this.setupEagerDependentFields()
|
|
@@ -109,7 +105,7 @@ export default class extends Controller {
|
|
|
109
105
|
|
|
110
106
|
choose(event) {
|
|
111
107
|
event.preventDefault()
|
|
112
|
-
this.
|
|
108
|
+
this.selectOptionFromElement(event.currentTarget)
|
|
113
109
|
}
|
|
114
110
|
|
|
115
111
|
add(event) {
|
|
@@ -281,17 +277,28 @@ export default class extends Controller {
|
|
|
281
277
|
}
|
|
282
278
|
|
|
283
279
|
selectOption(value, label, submitValue = value, { displayLabel = label, refreshOptions = this.multipleValue } = {}) {
|
|
284
|
-
|
|
285
|
-
|
|
280
|
+
this.selectOptionData(
|
|
281
|
+
this.normalizeSelectedOption({ id: value, value: submitValue, label, displayLabel }),
|
|
282
|
+
{ refreshOptions }
|
|
283
|
+
)
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
selectOptionFromElement(element, { refreshOptions = this.multipleValue } = {}) {
|
|
287
|
+
this.selectOptionData(this.optionData(element), { refreshOptions })
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
selectOptionData(option, { refreshOptions = this.multipleValue } = {}) {
|
|
291
|
+
const selectedOption = this.normalizeSelectedOption(option)
|
|
292
|
+
const value = selectedOption.id
|
|
286
293
|
|
|
287
294
|
if (this.multipleValue) {
|
|
288
295
|
if (this.selectedValue.some((option) => option.id === value)) {
|
|
289
296
|
this.selectedValue = this.selectedValue.filter((option) => option.id !== value)
|
|
290
297
|
} else {
|
|
291
|
-
this.selectedValue = [
|
|
298
|
+
this.selectedValue = [selectedOption, ...this.selectedValue]
|
|
292
299
|
}
|
|
293
300
|
} else {
|
|
294
|
-
this.selectedValue = [
|
|
301
|
+
this.selectedValue = [selectedOption]
|
|
295
302
|
}
|
|
296
303
|
|
|
297
304
|
this.renderSelection()
|
|
@@ -304,6 +311,51 @@ export default class extends Controller {
|
|
|
304
311
|
}
|
|
305
312
|
}
|
|
306
313
|
|
|
314
|
+
optionData(element) {
|
|
315
|
+
const data = this.parseOptionData(element.dataset.advancedSelectOptionParam)
|
|
316
|
+
const value = element.dataset.advancedSelectValueParam
|
|
317
|
+
const submitValue = element.dataset.advancedSelectSubmitValueParam || value
|
|
318
|
+
const label = element.dataset.advancedSelectLabelParam
|
|
319
|
+
const displayLabel = element.dataset.advancedSelectDisplayLabelParam || label
|
|
320
|
+
|
|
321
|
+
return {
|
|
322
|
+
...data,
|
|
323
|
+
id: value,
|
|
324
|
+
value: submitValue,
|
|
325
|
+
label,
|
|
326
|
+
displayLabel
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
parseOptionData(json) {
|
|
331
|
+
if (!json) {
|
|
332
|
+
return {}
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
try {
|
|
336
|
+
const data = JSON.parse(json)
|
|
337
|
+
return data && typeof data === "object" && !Array.isArray(data) ? data : {}
|
|
338
|
+
} catch (_error) {
|
|
339
|
+
return {}
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
normalizeSelectedOption(option) {
|
|
344
|
+
const id = option.id.toString()
|
|
345
|
+
const value = (option.value || id).toString()
|
|
346
|
+
const label = (option.label || option.displayLabel || option.display_label || value).toString()
|
|
347
|
+
const displayLabel = (option.displayLabel || option.display_label || label).toString()
|
|
348
|
+
|
|
349
|
+
return {
|
|
350
|
+
...option,
|
|
351
|
+
id,
|
|
352
|
+
value,
|
|
353
|
+
label,
|
|
354
|
+
displayLabel,
|
|
355
|
+
display_label: displayLabel
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
|
|
307
359
|
renderSelection() {
|
|
308
360
|
this.hiddenFieldsTarget.replaceChildren(...this.hiddenFieldElements)
|
|
309
361
|
this.summaryTarget.replaceChildren(...this.selectionElements)
|
|
@@ -354,8 +406,6 @@ export default class extends Controller {
|
|
|
354
406
|
return
|
|
355
407
|
}
|
|
356
408
|
|
|
357
|
-
// Delegated on document so it keeps working after a parent advanced select
|
|
358
|
-
// rewrites its hidden input on each selection.
|
|
359
409
|
this.dependentChangeListener = (event) => {
|
|
360
410
|
if (event.target instanceof Element && this.dependentSelectors.some((selector) => event.target.matches(selector))) {
|
|
361
411
|
this.reloadDependentOptions()
|
|
@@ -385,13 +435,7 @@ export default class extends Controller {
|
|
|
385
435
|
return
|
|
386
436
|
}
|
|
387
437
|
|
|
388
|
-
|
|
389
|
-
this.selectOption(
|
|
390
|
-
option.dataset.advancedSelectValueParam,
|
|
391
|
-
option.dataset.advancedSelectLabelParam,
|
|
392
|
-
option.dataset.advancedSelectSubmitValueParam || option.dataset.advancedSelectValueParam,
|
|
393
|
-
{ displayLabel: option.dataset.advancedSelectDisplayLabelParam, refreshOptions: false }
|
|
394
|
-
)
|
|
438
|
+
this.selectOptionFromElement(options[0], { refreshOptions: false })
|
|
395
439
|
}
|
|
396
440
|
|
|
397
441
|
chooseActiveOption() {
|
|
@@ -403,12 +447,7 @@ export default class extends Controller {
|
|
|
403
447
|
this.activeOption.dataset.advancedSelectDisplayLabelParam
|
|
404
448
|
)
|
|
405
449
|
} else {
|
|
406
|
-
this.
|
|
407
|
-
this.activeOption.dataset.advancedSelectValueParam,
|
|
408
|
-
this.activeOption.dataset.advancedSelectLabelParam,
|
|
409
|
-
this.activeOption.dataset.advancedSelectSubmitValueParam || this.activeOption.dataset.advancedSelectValueParam,
|
|
410
|
-
{ displayLabel: this.activeOption.dataset.advancedSelectDisplayLabelParam }
|
|
411
|
-
)
|
|
450
|
+
this.selectOptionFromElement(this.activeOption)
|
|
412
451
|
}
|
|
413
452
|
}
|
|
414
453
|
|
|
@@ -528,10 +567,19 @@ export default class extends Controller {
|
|
|
528
567
|
}
|
|
529
568
|
|
|
530
569
|
renderTooltip() {
|
|
531
|
-
if (!this.hasTooltipTarget
|
|
570
|
+
if (!this.hasTooltipTarget) {
|
|
532
571
|
return
|
|
533
572
|
}
|
|
534
573
|
|
|
574
|
+
if (this.tooltipTarget.hasAttribute("data-advanced-select-tooltip-custom")) {
|
|
575
|
+
this.renderCustomTooltip()
|
|
576
|
+
return
|
|
577
|
+
}
|
|
578
|
+
|
|
579
|
+
this.renderBuiltInTooltip()
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
renderBuiltInTooltip() {
|
|
535
583
|
const list = this.tooltipTarget.querySelector("[data-advanced-select-tooltip-list]")
|
|
536
584
|
if (!list) {
|
|
537
585
|
return
|
|
@@ -546,6 +594,78 @@ export default class extends Controller {
|
|
|
546
594
|
}
|
|
547
595
|
}
|
|
548
596
|
|
|
597
|
+
renderCustomTooltip() {
|
|
598
|
+
const list = this.tooltipTarget.querySelector("[data-advanced-select-tooltip-list]")
|
|
599
|
+
const template = this.customTooltipTemplate()
|
|
600
|
+
|
|
601
|
+
if (!list || !template) {
|
|
602
|
+
if (this.selectedValue.length === 0) {
|
|
603
|
+
this.hideTooltip(true)
|
|
604
|
+
}
|
|
605
|
+
return
|
|
606
|
+
}
|
|
607
|
+
|
|
608
|
+
list.replaceChildren(
|
|
609
|
+
...this.selectedValue.map((option) => this.customTooltipElement(template, option))
|
|
610
|
+
)
|
|
611
|
+
|
|
612
|
+
if (this.selectedValue.length === 0) {
|
|
613
|
+
this.hideTooltip(true)
|
|
614
|
+
}
|
|
615
|
+
}
|
|
616
|
+
|
|
617
|
+
customTooltipTemplate() {
|
|
618
|
+
if (this.customTooltipTemplateContent) {
|
|
619
|
+
return this.customTooltipTemplateContent
|
|
620
|
+
}
|
|
621
|
+
|
|
622
|
+
const template = this.tooltipTarget.querySelector("template[data-advanced-select-tooltip-template]")
|
|
623
|
+
if (!template) {
|
|
624
|
+
return null
|
|
625
|
+
}
|
|
626
|
+
|
|
627
|
+
this.customTooltipTemplateContent = template.content.cloneNode(true)
|
|
628
|
+
template.remove()
|
|
629
|
+
return this.customTooltipTemplateContent
|
|
630
|
+
}
|
|
631
|
+
|
|
632
|
+
customTooltipElement(template, option) {
|
|
633
|
+
const fragment = template.cloneNode(true)
|
|
634
|
+
fragment.querySelectorAll("[data-advanced-select-tooltip-field]").forEach((element) => {
|
|
635
|
+
element.textContent = this.tooltipFieldValue(option, element.dataset.advancedSelectTooltipField)
|
|
636
|
+
})
|
|
637
|
+
|
|
638
|
+
return fragment
|
|
639
|
+
}
|
|
640
|
+
|
|
641
|
+
tooltipFieldValue(option, field) {
|
|
642
|
+
if (field === "display_label" || field === "displayLabel") {
|
|
643
|
+
return this.displayLabel(option)
|
|
644
|
+
}
|
|
645
|
+
|
|
646
|
+
const value = this.optionFieldValue(option, field)
|
|
647
|
+
return value === undefined || value === null ? "" : value.toString()
|
|
648
|
+
}
|
|
649
|
+
|
|
650
|
+
optionFieldValue(option, field) {
|
|
651
|
+
const directValue = option[field] ?? option[this.camelize(field)]
|
|
652
|
+
if (directValue !== undefined) {
|
|
653
|
+
return directValue
|
|
654
|
+
}
|
|
655
|
+
|
|
656
|
+
return field.split(".").reduce((value, segment) => {
|
|
657
|
+
if (value === undefined || value === null) {
|
|
658
|
+
return undefined
|
|
659
|
+
}
|
|
660
|
+
|
|
661
|
+
return value[segment] ?? value[this.camelize(segment)]
|
|
662
|
+
}, option)
|
|
663
|
+
}
|
|
664
|
+
|
|
665
|
+
camelize(value) {
|
|
666
|
+
return value.replace(/_([a-z])/g, (_match, character) => character.toUpperCase())
|
|
667
|
+
}
|
|
668
|
+
|
|
549
669
|
textElement(tagName, className, text) {
|
|
550
670
|
const element = document.createElement(tagName)
|
|
551
671
|
element.className = className
|
|
@@ -581,4 +701,4 @@ export default class extends Controller {
|
|
|
581
701
|
get expanded() {
|
|
582
702
|
return this.triggerTarget.getAttribute("aria-expanded") === "true"
|
|
583
703
|
}
|
|
584
|
-
}
|
|
704
|
+
}
|