uchi 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 +7 -0
- data/app/assets/javascripts/controllers/fields/belongs_to_controller.js +130 -0
- data/app/assets/javascripts/controllers/fields/has_many_controller.js +146 -0
- data/app/assets/javascripts/uchi/application.js +3456 -24
- data/app/assets/javascripts/uchi.js +20 -0
- data/app/assets/stylesheets/uchi/application.css +676 -2473
- data/app/assets/tailwind/uchi.css +6 -6
- data/app/components/uchi/field/belongs_to/edit.html.erb +73 -1
- data/app/components/uchi/field/belongs_to/index.html.erb +1 -1
- data/app/components/uchi/field/belongs_to/show.html.erb +3 -5
- data/app/components/uchi/field/belongs_to.rb +53 -30
- data/app/components/uchi/field/boolean/edit.html.erb +1 -1
- data/app/components/uchi/field/date/edit.html.erb +1 -1
- data/app/components/uchi/field/date_time/edit.html.erb +1 -1
- data/app/components/uchi/field/file/edit.html.erb +1 -1
- data/app/components/uchi/field/file/index.html.erb +2 -1
- data/app/components/uchi/field/file/show.html.erb +3 -2
- data/app/components/uchi/field/has_and_belongs_to_many/show.html.erb +6 -4
- data/app/components/uchi/field/has_many/edit.html.erb +86 -1
- data/app/components/uchi/field/has_many/show.html.erb +6 -4
- data/app/components/uchi/field/has_many.rb +59 -11
- data/app/components/uchi/field/id/index.html.erb +1 -1
- data/app/components/uchi/field/id/show.html.erb +1 -1
- data/app/components/uchi/field/image/edit.html.erb +1 -1
- data/app/components/uchi/field/image/index.html.erb +2 -1
- data/app/components/uchi/field/image/show.html.erb +2 -1
- data/app/components/uchi/field/number/edit.html.erb +1 -1
- data/app/components/uchi/field/string/edit.html.erb +1 -1
- data/app/components/uchi/field/text/edit.html.erb +1 -1
- data/app/components/{flowbite → uchi/flowbite}/breadcrumb.rb +5 -5
- data/app/components/{flowbite → uchi/flowbite}/breadcrumb_home.rb +2 -2
- data/app/components/{flowbite → uchi/flowbite}/breadcrumb_item/current.rb +3 -3
- data/app/components/{flowbite → uchi/flowbite}/breadcrumb_item/first.rb +4 -4
- data/app/components/{flowbite → uchi/flowbite}/breadcrumb_item.rb +4 -4
- data/app/components/{flowbite → uchi/flowbite}/breadcrumb_separator.rb +7 -5
- data/app/components/uchi/flowbite/button/outline.rb +37 -0
- data/app/components/uchi/flowbite/button/pill.rb +40 -0
- data/app/components/uchi/flowbite/button.rb +93 -0
- data/app/components/uchi/flowbite/card/card.html.erb +7 -0
- data/app/components/uchi/flowbite/card/title.rb +37 -0
- data/app/components/uchi/flowbite/card.rb +84 -0
- data/app/components/{flowbite → uchi/flowbite}/input/checkbox.rb +7 -7
- data/app/components/{flowbite → uchi/flowbite}/input/date.rb +1 -1
- data/app/components/{flowbite → uchi/flowbite}/input/date_time.rb +1 -1
- data/app/components/{flowbite → uchi/flowbite}/input/email.rb +1 -1
- data/app/components/{flowbite → uchi/flowbite}/input/field.rb +11 -10
- data/app/components/uchi/flowbite/input/file.rb +30 -0
- data/app/components/{flowbite → uchi/flowbite}/input/hint.rb +6 -5
- data/app/components/{flowbite → uchi/flowbite}/input/label.rb +8 -7
- data/app/components/{flowbite → uchi/flowbite}/input/number.rb +1 -1
- data/app/components/{flowbite → uchi/flowbite}/input/password.rb +1 -1
- data/app/components/{flowbite → uchi/flowbite}/input/phone.rb +1 -1
- data/app/components/{flowbite → uchi/flowbite}/input/radio_button.rb +7 -7
- data/app/components/{flowbite → uchi/flowbite}/input/select.rb +16 -7
- data/app/components/uchi/flowbite/input/textarea.rb +42 -0
- data/app/components/{flowbite → uchi/flowbite}/input/url.rb +1 -1
- data/app/components/uchi/flowbite/input/validation_error.rb +39 -0
- data/app/components/uchi/flowbite/input_field/checkbox.html.erb +9 -0
- data/app/components/{flowbite → uchi/flowbite}/input_field/checkbox.rb +10 -6
- data/app/components/{flowbite → uchi/flowbite}/input_field/date.rb +2 -2
- data/app/components/{flowbite → uchi/flowbite}/input_field/date_time.rb +2 -2
- data/app/components/{flowbite → uchi/flowbite}/input_field/email.rb +2 -2
- data/app/components/{flowbite → uchi/flowbite}/input_field/file.rb +2 -2
- data/app/components/uchi/flowbite/input_field/input_field.html.erb +9 -0
- data/app/components/{flowbite → uchi/flowbite}/input_field/number.rb +2 -2
- data/app/components/{flowbite → uchi/flowbite}/input_field/password.rb +2 -2
- data/app/components/{flowbite → uchi/flowbite}/input_field/phone.rb +2 -2
- data/app/components/uchi/flowbite/input_field/radio_button.html.erb +9 -0
- data/app/components/{flowbite → uchi/flowbite}/input_field/radio_button.rb +13 -9
- data/app/components/{flowbite → uchi/flowbite}/input_field/select.rb +7 -3
- data/app/components/{flowbite → uchi/flowbite}/input_field/text.rb +1 -1
- data/app/components/{flowbite → uchi/flowbite}/input_field/textarea.rb +2 -2
- data/app/components/{flowbite → uchi/flowbite}/input_field/url.rb +2 -2
- data/app/components/{flowbite → uchi/flowbite}/input_field.rb +19 -5
- data/app/components/uchi/flowbite/link.rb +41 -0
- data/app/components/{flowbite → uchi/flowbite}/style.rb +1 -1
- data/app/components/{flowbite → uchi/flowbite}/toast/icon.rb +1 -1
- data/app/components/uchi/flowbite/toast/toast.html.erb +40 -0
- data/app/components/{flowbite → uchi/flowbite}/toast.rb +2 -2
- data/app/components/uchi/ui/actions/dropdown/dropdown.html.erb +59 -0
- data/app/components/uchi/ui/actions/dropdown.rb +32 -0
- data/app/components/uchi/ui/breadcrumb/breadcrumb.html.erb +4 -4
- data/app/components/uchi/ui/form/input/collection_checkboxes.html.erb +2 -1
- data/app/components/uchi/ui/form/input/collection_checkboxes.rb +12 -17
- data/app/components/uchi/ui/frame/frame.html.erb +6 -1
- data/app/components/uchi/ui/index/records_table/records_table.html.erb +109 -18
- data/app/components/uchi/ui/index/records_table/search_form/search_form.html.erb +21 -5
- data/app/components/uchi/ui/navigation/navigation.html.erb +9 -0
- data/app/components/uchi/ui/navigation.rb +37 -0
- data/app/components/uchi/ui/no_value/no_value.html.erb +1 -0
- data/app/components/uchi/ui/no_value.rb +8 -0
- data/app/components/uchi/ui/page_header/page_header.html.erb +13 -8
- data/app/components/uchi/ui/pagination/current_link.html.erb +1 -1
- data/app/components/uchi/ui/pagination/gap.html.erb +7 -1
- data/app/components/uchi/ui/pagination/link.html.erb +1 -1
- data/app/components/uchi/ui/pagination/next_link.html.erb +18 -3
- data/app/components/uchi/ui/pagination/pagination.html.erb +3 -1
- data/app/components/uchi/ui/pagination/previous_link.html.erb +18 -3
- data/app/components/uchi/ui/pagination.rb +1 -1
- data/app/components/uchi/ui/show/attribute_fields/attribute_fields.html.erb +4 -3
- data/app/components/uchi/ui/spinner/spinner.html.erb +17 -3
- data/app/controllers/uchi/actions/executions_controller.rb +107 -0
- data/app/controllers/uchi/belongs_to/associated_records_controller.rb +89 -0
- data/app/controllers/uchi/has_many/associated_records_controller.rb +89 -0
- data/app/controllers/uchi/repository_controller.rb +21 -1
- data/app/views/layouts/uchi/_flash_messages.html.erb +1 -1
- data/app/views/layouts/uchi/_javascript.html.erb +1 -0
- data/app/views/layouts/uchi/_stylesheets.html.erb +1 -0
- data/app/views/layouts/uchi/application.html.erb +13 -19
- data/app/views/uchi/belongs_to/associated_records/index.html.erb +13 -0
- data/app/views/uchi/has_many/associated_records/index.html.erb +26 -0
- data/app/views/uchi/navigation/_main.html.erb +92 -0
- data/app/views/uchi/repository/edit.html.erb +2 -2
- data/app/views/uchi/repository/index.html.erb +12 -3
- data/app/views/uchi/repository/new.html.erb +2 -2
- data/app/views/uchi/repository/show.html.erb +13 -2
- data/lib/generators/uchi/controller/controller_generator.rb +0 -4
- data/lib/generators/uchi/controller/templates/controller.rb.tt +0 -5
- data/lib/generators/uchi/install/install_generator.rb +1 -1
- data/lib/generators/uchi/scaffold/scaffold_generator.rb +15 -0
- data/lib/uchi/action.rb +84 -0
- data/lib/uchi/action_response.rb +108 -0
- data/lib/uchi/field/configuration.rb +1 -1
- data/lib/uchi/repository/translate.rb +14 -0
- data/lib/uchi/repository.rb +38 -2
- data/lib/uchi/routes.rb +45 -0
- data/lib/uchi/version.rb +1 -1
- data/lib/uchi.rb +6 -1
- metadata +72 -50
- data/app/components/flowbite/button/outline.rb +0 -22
- data/app/components/flowbite/button/pill.rb +0 -40
- data/app/components/flowbite/button.rb +0 -92
- data/app/components/flowbite/card.rb +0 -45
- data/app/components/flowbite/input/file.rb +0 -30
- data/app/components/flowbite/input/textarea.rb +0 -42
- data/app/components/flowbite/input/validation_error.rb +0 -11
- data/app/components/flowbite/input_field/checkbox.html.erb +0 -14
- data/app/components/flowbite/input_field/input_field.html.erb +0 -8
- data/app/components/flowbite/input_field/radio_button.html.erb +0 -14
- data/app/components/flowbite/link.rb +0 -21
- data/app/components/flowbite/toast/toast.html.erb +0 -11
- /data/app/components/{flowbite → uchi/flowbite}/toast/icon.html.erb +0 -0
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: de2b1d1a3dd3e56ab24bce7e64663414c0dbfe4dfc4ac782ca47fcd967e4b048
|
|
4
|
+
data.tar.gz: b9e0322ebafd58e4369f8b1e0087c8e4ec3bb637226a02b37b890d16be9986ad
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: b2ff7837db46a22eb39adc6cbd07077d9dd288032e6d77468a596c8042e3ab8476bc92ee57a3161ab177523701c0f7e4e4e8f8f49a45fc2691e8eaf0c4c2a0a2
|
|
7
|
+
data.tar.gz: 01accad1e6c453af98f1cccde568c46eb6b6eb9e0474f5454f3b9b59e215df636b00a599909b506f0c878a44d40d2f4b936329d0dab8aea82f422cf0709586ff
|
data/README.md
CHANGED
|
@@ -216,7 +216,14 @@ Rely on defaults whenever possible. If something has already been decided for us
|
|
|
216
216
|
|
|
217
217
|
We don't want to force you to translate everything. If a field doesn't need a translation, don't add one, we'll just fall back to the fields name.
|
|
218
218
|
|
|
219
|
+
### Edits happen on the edit page
|
|
220
|
+
|
|
221
|
+
This includes both attributes and associations as much as feasible.
|
|
222
|
+
|
|
219
223
|
## Credits
|
|
220
224
|
|
|
221
225
|
* Uchi contains parts of [Pagy](https://github.com/ddnexus/pagy), Copyright (c) 2017-2025 Domizio Demichelis
|
|
222
226
|
* Uchi contains parts of [Flowbite Components](https://github.com/substancelab/flowbite-components), Copyright (c) 2025 Substance Lab
|
|
227
|
+
* Uchi uses [combobox-nav](https://github.com/github/combobox-nav), Copyright (c) 2018 GitHub
|
|
228
|
+
* Uchi uses [requestjs-rails](https://github.com/rails/requestjs-rails), Copyright (c) 2021 Marcelo Lauxen
|
|
229
|
+
* Uchi uses [stimulus-use](https://github.com/stimulus-use/stimulus-use), Copyright (c) 2020 Adrien POLY
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import { Controller } from "@hotwired/stimulus"
|
|
2
|
+
import { get } from "@rails/request.js"
|
|
3
|
+
import { useClickOutside, useDebounce } from 'stimulus-use'
|
|
4
|
+
import Combobox from '@github/combobox-nav'
|
|
5
|
+
|
|
6
|
+
export default class extends Controller {
|
|
7
|
+
static debounces = ["handleChange"]
|
|
8
|
+
|
|
9
|
+
static targets = ["id", "dropdown", "input", "label", "list"]
|
|
10
|
+
|
|
11
|
+
static values = {
|
|
12
|
+
backendUrl: String
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
buildCombobox() {
|
|
16
|
+
return new Combobox(this.inputTarget, this.listTarget)
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
clickOutside(event) {
|
|
20
|
+
if (!this.dropdownTarget.hidden) {
|
|
21
|
+
this.closeDropdown()
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
closeDropdown() {
|
|
26
|
+
this.combobox.stop()
|
|
27
|
+
this.dropdownTarget.hidden = true
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
connect() {
|
|
31
|
+
useClickOutside(this, {element: this.dropdownTarget})
|
|
32
|
+
useDebounce(this)
|
|
33
|
+
|
|
34
|
+
this.combobox = this.buildCombobox()
|
|
35
|
+
|
|
36
|
+
this.listTarget.addEventListener('combobox-commit', this.handleComboboxCommit.bind(this))
|
|
37
|
+
this.dropdownTarget.hidden = true
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
disconnect() {
|
|
41
|
+
this.listTarget.removeEventListener('combobox-commit', this.handleComboboxCommit)
|
|
42
|
+
|
|
43
|
+
this.combobox.destroy()
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
fetchOptions(options) {
|
|
47
|
+
get(this.backendUrlValue, {
|
|
48
|
+
query: { query: this.inputTarget.value }
|
|
49
|
+
}).then(({response}) => {
|
|
50
|
+
return response.text()
|
|
51
|
+
}).then((html) => {
|
|
52
|
+
this.listTarget.innerHTML = html
|
|
53
|
+
this.openDropdown()
|
|
54
|
+
this.markSelectedOption()
|
|
55
|
+
if (options?.scrollToSelected) {
|
|
56
|
+
this.scrollToSelectedOption()
|
|
57
|
+
}
|
|
58
|
+
}).catch((error) => {
|
|
59
|
+
console.error("Failed to fetch options:", error)
|
|
60
|
+
this.dropdownTarget.hidden = true
|
|
61
|
+
})
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
handleChange() {
|
|
65
|
+
this.combobox.stop()
|
|
66
|
+
this.fetchOptions()
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
handleComboboxCommit(event) {
|
|
70
|
+
this.setValuesFromElement(event.target)
|
|
71
|
+
this.closeDropdown()
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
handleFocus() {
|
|
75
|
+
this.fetchOptions({ scrollToSelected: true })
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
markSelectedOption() {
|
|
79
|
+
const options = this.listTarget.querySelectorAll('[role="option"]')
|
|
80
|
+
options.forEach((option) => {
|
|
81
|
+
option.removeAttribute('aria-selected')
|
|
82
|
+
const recordId = option.getAttribute('data-id')
|
|
83
|
+
if (recordId === this.idTarget.value) {
|
|
84
|
+
option.setAttribute('aria-selected', 'true')
|
|
85
|
+
}
|
|
86
|
+
})
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
openDropdown() {
|
|
90
|
+
this.combobox.start()
|
|
91
|
+
this.dropdownTarget.hidden = false
|
|
92
|
+
this.inputTarget.focus()
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
scrollToSelectedOption() {
|
|
96
|
+
const selectedOption = this.listTarget.querySelector('[aria-selected="true"]')
|
|
97
|
+
if (selectedOption) {
|
|
98
|
+
selectedOption.scrollIntoView({
|
|
99
|
+
// Aligns the element at the center of the scrollable container,
|
|
100
|
+
// positioning it in the middle of the visible area.
|
|
101
|
+
block: "center",
|
|
102
|
+
inline: "center",
|
|
103
|
+
|
|
104
|
+
// Only the nearest scrollable container is impacted by the scroll.
|
|
105
|
+
container: "nearest"
|
|
106
|
+
})
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
selectOption(event) {
|
|
111
|
+
this.combobox.clearSelection()
|
|
112
|
+
event.target.setAttribute('aria-selected', 'true')
|
|
113
|
+
|
|
114
|
+
this.setValuesFromElement(event.target)
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
setValuesFromElement(element) {
|
|
118
|
+
const recordId = element.getAttribute('data-id')
|
|
119
|
+
this.idTarget.value = recordId
|
|
120
|
+
this.labelTarget.textContent = element.textContent.trim()
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
toggle() {
|
|
124
|
+
if (this.dropdownTarget.hidden) {
|
|
125
|
+
this.openDropdown()
|
|
126
|
+
} else {
|
|
127
|
+
this.closeDropdown()
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
}
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
import { Controller } from "@hotwired/stimulus"
|
|
2
|
+
import { get } from "@rails/request.js"
|
|
3
|
+
import { useClickOutside, useDebounce } from 'stimulus-use'
|
|
4
|
+
|
|
5
|
+
export default class extends Controller {
|
|
6
|
+
static debounces = ["handleChange"]
|
|
7
|
+
|
|
8
|
+
static targets = ["checkbox", "dropdown", "idField", "idsContainer", "input", "label", "list"]
|
|
9
|
+
|
|
10
|
+
static values = {
|
|
11
|
+
backendUrl: String,
|
|
12
|
+
fieldName: String
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
clickOutside(event) {
|
|
16
|
+
if (!this.dropdownTarget.hidden) {
|
|
17
|
+
this.closeDropdown()
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
closeDropdown() {
|
|
22
|
+
this.dropdownTarget.hidden = true
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
connect() {
|
|
26
|
+
useClickOutside(this, {element: this.dropdownTarget})
|
|
27
|
+
useDebounce(this)
|
|
28
|
+
|
|
29
|
+
this.dropdownTarget.hidden = true
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
fetchOptions() {
|
|
33
|
+
get(this.backendUrlValue, {
|
|
34
|
+
query: { query: this.inputTarget.value }
|
|
35
|
+
}).then(({response}) => {
|
|
36
|
+
return response.text()
|
|
37
|
+
}).then((html) => {
|
|
38
|
+
this.listTarget.innerHTML = html
|
|
39
|
+
this.openDropdown()
|
|
40
|
+
this.updateCheckboxStates()
|
|
41
|
+
}).catch((error) => {
|
|
42
|
+
console.error("Failed to fetch options:", error)
|
|
43
|
+
this.dropdownTarget.hidden = true
|
|
44
|
+
})
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
getSelectedIds() {
|
|
48
|
+
return this.idFieldTargets.map(field => String(field.value))
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
handleChange() {
|
|
52
|
+
this.fetchOptions()
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
handleFocus() {
|
|
56
|
+
this.fetchOptions()
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
openDropdown() {
|
|
60
|
+
this.dropdownTarget.hidden = false
|
|
61
|
+
this.inputTarget.focus()
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
handleCheckboxChange(event) {
|
|
65
|
+
const checkbox = event.target
|
|
66
|
+
const listItem = checkbox.closest('li[data-id]')
|
|
67
|
+
const recordId = listItem?.getAttribute('data-id')
|
|
68
|
+
|
|
69
|
+
if (!recordId) return
|
|
70
|
+
|
|
71
|
+
if (checkbox.checked) {
|
|
72
|
+
// Get the title from the label
|
|
73
|
+
const label = listItem?.querySelector('label')
|
|
74
|
+
const title = label ? label.textContent.trim() : ''
|
|
75
|
+
this.addId(recordId, title)
|
|
76
|
+
} else {
|
|
77
|
+
this.removeId(recordId)
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
this.updateLabel()
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
addId(id, title) {
|
|
84
|
+
const selectedIds = this.getSelectedIds()
|
|
85
|
+
if (selectedIds.includes(id)) return
|
|
86
|
+
|
|
87
|
+
// Create a new hidden field for this ID
|
|
88
|
+
const hiddenField = document.createElement('input')
|
|
89
|
+
hiddenField.type = 'hidden'
|
|
90
|
+
hiddenField.name = this.fieldNameValue
|
|
91
|
+
hiddenField.value = id
|
|
92
|
+
hiddenField.setAttribute('data-has-many-target', 'idField')
|
|
93
|
+
hiddenField.setAttribute('data-title', title)
|
|
94
|
+
|
|
95
|
+
this.idsContainerTarget.appendChild(hiddenField)
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
removeId(id) {
|
|
99
|
+
const field = this.idFieldTargets.find(f => f.value === id)
|
|
100
|
+
if (field) {
|
|
101
|
+
field.remove()
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
toggle() {
|
|
106
|
+
if (this.dropdownTarget.hidden) {
|
|
107
|
+
this.openDropdown()
|
|
108
|
+
} else {
|
|
109
|
+
this.closeDropdown()
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
updateCheckboxStates() {
|
|
114
|
+
const selectedIds = this.getSelectedIds()
|
|
115
|
+
|
|
116
|
+
this.checkboxTargets.forEach((checkbox) => {
|
|
117
|
+
const listItem = checkbox.closest('li[data-id]')
|
|
118
|
+
if (!listItem) return
|
|
119
|
+
|
|
120
|
+
const recordId = listItem.getAttribute('data-id')
|
|
121
|
+
const isSelected = selectedIds.includes(recordId)
|
|
122
|
+
|
|
123
|
+
checkbox.checked = isSelected
|
|
124
|
+
|
|
125
|
+
if (isSelected) {
|
|
126
|
+
listItem.setAttribute('aria-selected', 'true')
|
|
127
|
+
} else {
|
|
128
|
+
listItem.removeAttribute('aria-selected')
|
|
129
|
+
}
|
|
130
|
+
})
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
updateLabel() {
|
|
134
|
+
// Get titles from the hidden fields (which persist even when items are filtered)
|
|
135
|
+
const titles = this.idFieldTargets
|
|
136
|
+
.map(field => field.getAttribute('data-title'))
|
|
137
|
+
.filter(title => title) // Remove empty titles
|
|
138
|
+
|
|
139
|
+
if (titles.length === 0) {
|
|
140
|
+
this.labelTarget.innerHTML = '<span class="text-body-subtle">Select items...</span>'
|
|
141
|
+
return
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
this.labelTarget.textContent = titles.join(', ')
|
|
145
|
+
}
|
|
146
|
+
}
|