1- import { DatagridPlugin } from "../../types" ;
2- import { Datagrid } from "../.." ;
1+ import { DatagridPlugin } from "../../types" ;
2+ import { Datagrid } from "../.." ;
33
44export const CheckboxAttribute = "data-check" ;
55
66export 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