Skip to content

Commit 74fe236

Browse files
author
PeAd
committed
All the changes were not invoking a change event. THis means that the parry checkboxes can never be kep in sync with the native checkboxes.
This necessitated adding a check thet I am not changing the current element. On reload checkbox callbacks were not initialised. Meaning the checkboxes could not do a second action after the first was invoked as it reloads the page and prevents checking. All callbacks should only ever invoke the onChange on the native input as the happy inputs may not even exist. This prevents inifinite looping. The counter should be initialised to 0/10 so the select count does not just appear out of nowhere. The counter used to show the Total including the check all button if any button but the check all button was clicked. Actually initialise the group action state at all times. Any javascript could run before this callback and this would ignore the checked boxes. The multirow selection did ot work due to 1) no change events 2) the last checkbox being change at the start of the function rather than the end. 3) we were taking the lastCheckbox and lastCheckbox??? 4) rowindex is global not only in this part of the table.
1 parent e936663 commit 74fe236

File tree

2 files changed

+80
-45
lines changed

2 files changed

+80
-45
lines changed

assets/integrations/happy.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -171,7 +171,11 @@ export class Happy {
171171

172172
const checked = happyInput.classList.contains("active");
173173
checked ? happyInput.classList.remove("active") : happyInput.classList.add("active");
174-
input.checked = !checked;
174+
if(input.checked !== !checked){
175+
const evt = new Event('change',{bubbles: false, cancelable: true,composed: false});
176+
input.checked = !checked;
177+
input.dispatchEvent(evt);
178+
}
175179
}
176180

177181
checkCheckboxStateOnChange({target}: Event) {

assets/plugins/features/checkboxes.ts

Lines changed: 75 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,49 @@
1-
import { DatagridPlugin } from "../../types";
2-
import { Datagrid } from "../..";
1+
import {DatagridPlugin} from "../../types";
2+
import {Datagrid} from "../..";
33

44
export const CheckboxAttribute = "data-check";
55

66
export class CheckboxPlugin implements DatagridPlugin {
7+
8+
private wasInit: Array<Datagrid> = [];
9+
10+
loadCheckboxCount(datagrid: Datagrid) {
11+
const counter = document.querySelector<HTMLElement>(".datagrid-selected-rows-count");
12+
const total = Array.from(datagrid.el.querySelectorAll<HTMLInputElement>(`input[data-check='${datagrid.name}']`)).filter(c => !c.hasAttribute("data-check-all"));
13+
const checked = total.filter(e => (e.checked));
14+
if (counter) {
15+
counter.innerText = `${checked.length}/${total.length}`;
16+
}
17+
document.querySelectorAll<HTMLInputElement | HTMLButtonElement>(
18+
".row-group-actions *[type='submit']"
19+
).forEach(button => {
20+
button.disabled = checked.length === 0;
21+
});
22+
const select = datagrid.el.querySelector<HTMLSelectElement>("select[name='group_action[group_action]']");
23+
if (select) {
24+
select.disabled = checked.length === 0;
25+
}
26+
}
27+
728
onDatagridInit(datagrid: Datagrid): boolean {
8-
let lastCheckbox = null;
29+
if (!this.wasInit.includes(datagrid)) {
30+
datagrid.ajax.addEventListener('complete', () => {
31+
this.onDatagridInit(datagrid)
32+
});
33+
this.wasInit.push(datagrid);
34+
this.loadCheckboxCount(datagrid);
35+
}
36+
this.loadCheckboxCount(datagrid);
937

10-
datagrid.el.addEventListener("click", e => {
11-
if (!(e.target instanceof HTMLElement)) return;
38+
let lastCheckbox: null | HTMLElement = null;
1239

40+
datagrid.el.addEventListener("click", e => {
41+
if (!(e.target instanceof HTMLElement)) {
42+
return;
43+
}
1344
if (e.target.classList.contains("col-checkbox")) {
14-
lastCheckbox = e.target;
1545
if (e.shiftKey && lastCheckbox) {
16-
const currentCheckboxRow = lastCheckbox.closest("tr");
46+
const currentCheckboxRow = e.target.closest("tr");
1747
if (!currentCheckboxRow) return;
1848

1949
const lastCheckboxRow = lastCheckbox.closest("tr");
@@ -23,68 +53,69 @@ export class CheckboxPlugin implements DatagridPlugin {
2353
if (!lastCheckboxTbody) return;
2454

2555
const checkboxesRows = Array.from(lastCheckboxTbody.querySelectorAll<HTMLElement>("tr"));
26-
const [start, end] = [lastCheckboxRow.rowIndex, currentCheckboxRow.rowIndex].sort();
56+
const headerRows = Array.from(lastCheckboxTbody.closest('table')?.querySelectorAll<HTMLElement>("thead tr") ?? []).length;
57+
58+
const [start, end] = [lastCheckboxRow.rowIndex -headerRows, currentCheckboxRow.rowIndex -headerRows].sort();
2759
const rows = checkboxesRows.slice(start, end + 1);
2860

2961
rows.forEach(row => {
3062
const input = row.querySelector<HTMLInputElement>('.col-checkbox input[type="checkbox"]');
3163
if (input) {
32-
input.checked = true;
64+
if (!input.checked) {
65+
input.checked = true;
66+
input.dispatchEvent(new Event('change', {bubbles: true}))
67+
}
68+
3369
}
3470
});
3571
}
72+
lastCheckbox = e.target;
3673
}
3774
});
3875

76+
3977
let checkboxes = datagrid.el.querySelectorAll<HTMLInputElement>(`input[data-check='${datagrid.name}']`);
40-
const select = datagrid.el.querySelector<HTMLSelectElement>("select[name='group_action[group_action]']");
41-
const actionButtons = document.querySelectorAll<HTMLInputElement | HTMLButtonElement>(
42-
".row-group-actions *[type='submit']"
43-
);
44-
const counter = document.querySelector<HTMLElement>(".datagrid-selected-rows-count");
4578

4679
// Handling a checkbox click + select all checkbox
80+
let notUserInvoked = false;
4781
checkboxes.forEach(checkEl => {
4882
checkEl.addEventListener("change", () => {
4983
// Select all
5084
const isSelectAll = checkEl.hasAttribute("data-check-all");
5185
if (isSelectAll) {
52-
if (datagrid.name !== checkEl.getAttribute("data-check-all")) return;
53-
54-
checkboxes.forEach(checkbox => (checkbox.checked = checkEl.checked));
55-
56-
// todo: refactor not to repeat this code twice
57-
actionButtons.forEach(button => (button.disabled = !checkEl.checked));
58-
59-
if (select) {
60-
select.disabled = !checkEl.checked;
61-
}
62-
63-
if (counter) {
64-
const total = Array.from(checkboxes).filter(c => !c.hasAttribute("data-check-all")).length;
65-
counter.innerText = `${checkEl.checked ? total : 0}/${total}`;
86+
if (notUserInvoked) {
87+
return;
6688
}
89+
if (datagrid.name !== checkEl.getAttribute("data-check-all")) return;
90+
const targetCheck = checkEl.checked;//this is vital as it gets swithced around due to not all being checked just yet.
91+
checkboxes.forEach(checkbox => {
92+
if (checkbox !== checkEl && checkbox.checked !== targetCheck) {
93+
checkbox.checked = targetCheck;
94+
//this will end up calling this callback a lot. But it needs to eb done as otherwise the happy checkboxes fail horribly.
95+
//Bubbles is needed as the happy callback catches on document
96+
notUserInvoked = true;//prevent nesting
97+
checkbox.dispatchEvent(new Event('change', {bubbles: true}));
98+
notUserInvoked = false;
99+
}
100+
});
67101
return;
68-
} else {
69-
const selectAll = datagrid.el.querySelector<HTMLInputElement>(`input[data-check='${datagrid.name}'][data-check-all]`);
70-
if (selectAll) {
71-
selectAll.checked = Array.from(checkboxes).filter(c => !c.hasAttribute("data-check-all")).every(c => c.checked);
72-
}
73102
}
74103

75-
const checkedBoxes = Array.from(checkboxes).filter(checkbox => checkbox.checked && !checkEl.hasAttribute("data-check-all"));
76-
const hasChecked = checkedBoxes.length >= 1;
77-
78-
actionButtons.forEach(button => (button.disabled = !hasChecked));
79-
80-
if (select) {
81-
select.disabled = !hasChecked;
82-
}
83-
84-
if (counter) {
85-
counter.innerText = `${checkedBoxes.length}/${checkboxes.length}`;
104+
const selectAll = datagrid.el.querySelector<HTMLInputElement>(`input[data-check='${datagrid.name}'][data-check-all]`);
105+
if (selectAll) {
106+
const allChecked = Array.from(checkboxes).filter(c => !c.hasAttribute("data-check-all")).every(c => c.checked);
107+
if (allChecked != selectAll.checked) {
108+
notUserInvoked = true;
109+
selectAll.checked = allChecked;
110+
selectAll.dispatchEvent(new Event('change', {bubbles: true}));
111+
notUserInvoked = false;
112+
}
86113
}
87114
});
115+
116+
checkEl.addEventListener("change", () => {
117+
this.loadCheckboxCount(datagrid);
118+
})
88119
});
89120

90121
return true;

0 commit comments

Comments
 (0)