11// TODO: fix the rules disabled below
22/* eslint-disable @stylistic/max-len */
3- /* eslint-disable @stylistic/no-mixed-operators */
43/* eslint-disable @typescript-eslint/explicit-function-return-type */
54/* eslint-disable @typescript-eslint/explicit-module-boundary-types */
65/* eslint-disable @typescript-eslint/init-declarations */
109/* eslint-disable @typescript-eslint/no-shadow */
1110/* eslint-disable @typescript-eslint/no-unsafe-return */
1211/* eslint-disable consistent-return */
13- /* eslint-disable max-depth */
1412/* eslint-disable no-param-reassign */
1513/* eslint-disable no-plusplus */
1614import type { DataSource , Store } from '@js/common/data' ;
1715import $ from '@js/core/renderer' ;
1816import type { Callback } from '@js/core/utils/callbacks' ;
19- import { deferRender , equalByValue } from '@js/core/utils/common' ;
17+ import { deferRender } from '@js/core/utils/common' ;
2018import type { DeferredObj } from '@js/core/utils/deferred' ;
2119import { Deferred , when } from '@js/core/utils/deferred' ;
2220import { each } from '@js/core/utils/iterator' ;
@@ -62,8 +60,13 @@ import type {
6260 PagingOptionName ,
6361 PagingResult ,
6462 ProcessedItem ,
63+ UpdateChange ,
64+ UpdateRowChange ,
6565} from './types' ;
6666import { resolvePaginate , syncPaging } from './utils/paging' ;
67+ import {
68+ getChangedRowIndices , getRowOperation , isSameGroupRowState , pushChangedRow , resetChangedRows ,
69+ } from './utils/row_changes' ;
6770import { generateRowValues } from './utils/row_values' ;
6871
6972export class DataController extends DataHelperMixin ( modules . Controller ) {
@@ -808,7 +811,7 @@ export class DataController extends DataHelperMixin(modules.Controller) {
808811 }
809812
810813 if ( change . changeType === 'update' ) {
811- this . _applyChangeUpdate ( change ) ;
814+ this . applyChangeUpdate ( change ) ;
812815 } else if ( change . changeType === 'refresh' ) {
813816 if ( this . items ( ) . length && change . repaintChangesOnly ) {
814817 this . _applyChangesOnly ( change ) ;
@@ -818,168 +821,195 @@ export class DataController extends DataHelperMixin(modules.Controller) {
818821 }
819822 }
820823
821- private _applyChangeFull ( change ) {
822- this . _items = change . items . slice ( 0 ) ;
824+ private _applyChangeFull ( change : DataChange ) : void {
825+ this . _items = ( change . items ?? [ ] ) . slice ( 0 ) ;
823826 }
824827
825- private _getRowIndices ( change ) {
826- const rowIndices = change . rowIndices . slice ( 0 ) ;
827- const rowIndexDelta = this . getRowIndexDelta ( ) ;
828+ private updateRow (
829+ newItem : ProcessedItem ,
830+ rowIndex : number ,
831+ visibleRowIndex : number ,
832+ isPartialUpdate : boolean ,
833+ ) : UpdateRowChange {
834+ const oldItem = this . _items [ rowIndex ] ;
828835
829- rowIndices . sort ( ( a , b ) => a - b ) ;
836+ this . _items [ rowIndex ] = newItem ;
830837
831- for ( let i = 0 ; i < rowIndices . length ; i ++ ) {
832- let correctedRowIndex = rowIndices [ i ] ;
838+ if ( oldItem . visible !== newItem . visible ) {
839+ return {
840+ changeType : 'update' ,
841+ rowIndex : visibleRowIndex ,
842+ item : { visible : newItem . visible } as ProcessedItem ,
843+ } ;
844+ }
833845
834- if ( change . allowInvisibleRowIndices ) {
835- correctedRowIndex += rowIndexDelta ;
836- }
846+ return {
847+ changeType : 'update' ,
848+ rowIndex : visibleRowIndex ,
849+ item : newItem ,
850+ columnIndices : isPartialUpdate
851+ ? this . _partialUpdateRow ( oldItem , newItem , visibleRowIndex )
852+ : undefined ,
853+ } ;
854+ }
837855
838- if ( correctedRowIndex < 0 ) {
839- rowIndices . splice ( i , 1 ) ;
840- i -- ;
841- }
856+ private applyRowOperation (
857+ newItems : ProcessedItem [ ] ,
858+ rowIndex : number ,
859+ rowIndexDelta : number ,
860+ isPartialUpdate : boolean ,
861+ ) : UpdateRowChange | undefined {
862+ const visibleRowIndex = rowIndex - rowIndexDelta ;
863+ const item = newItems [ rowIndex ] ;
864+
865+ if ( item ) {
866+ item . rowIndex = rowIndex ;
842867 }
843868
844- return rowIndices ;
869+ switch ( getRowOperation ( this . _items , newItems , rowIndex ) ) {
870+ case 'update' :
871+ return this . updateRow ( item , rowIndex , visibleRowIndex , isPartialUpdate ) ;
872+ case 'insert' :
873+ this . _items . splice ( rowIndex , 0 , item ) ;
874+ return { changeType : 'insert' , rowIndex : visibleRowIndex , item } ;
875+ case 'remove' :
876+ this . _items . splice ( rowIndex , 1 ) ;
877+ return { changeType : 'remove' , rowIndex : visibleRowIndex , item } ;
878+ case 'replace' :
879+ this . _items [ rowIndex ] = item ;
880+ return { changeType : 'update' , rowIndex : visibleRowIndex , item } ;
881+ default :
882+ return undefined ;
883+ }
845884 }
846885
847886 /**
848887 * @extended : editing
849888 */
850- protected _applyChangeUpdate ( change ) : void {
851- const { items } = change ;
852- const rowIndices = this . _getRowIndices ( change ) ;
889+ protected applyChangeUpdate ( change : UpdateChange ) : void {
890+ const newItems = change . items ?? [ ] ;
853891 const rowIndexDelta = this . getRowIndexDelta ( ) ;
854- const repaintChangesOnly = this . option ( 'repaintChangesOnly' ) ;
855- let prevIndex = - 1 ;
892+ const isPartialUpdate = Boolean ( this . option ( 'repaintChangesOnly' ) ) && ! change . isFullUpdate ;
893+ const rowIndices = getChangedRowIndices (
894+ change . rowIndices ,
895+ rowIndexDelta ,
896+ change . allowInvisibleRowIndices ,
897+ ) ;
898+ const changedRows = resetChangedRows ( change ) ;
899+ let prevRowIndex = - 1 ;
856900 let rowIndexCorrection = 0 ;
857- let changeType ;
858901
859- change . items = [ ] ;
860- change . rowIndices = [ ] ;
861- change . columnIndices = [ ] ;
862- change . changeTypes = [ ] ;
902+ rowIndices . forEach ( ( changedRowIndex : number ) => {
903+ const rowIndex = changedRowIndex + rowIndexCorrection + rowIndexDelta ;
863904
864- const equalItems = function ( item1 , item2 , strict ?) {
865- let result = item1 && item2 && equalByValue ( item1 . key , item2 . key ) ;
866- if ( result && strict ) {
867- result = item1 . rowType === item2 . rowType && ( item2 . rowType !== 'detail' || item1 . isEditing === item2 . isEditing ) ;
905+ if ( prevRowIndex === rowIndex ) {
906+ return ;
868907 }
869- return result ;
870- } ;
871-
872- each ( rowIndices , ( index , rowIndex ) => {
873- let columnIndices ;
874908
875- rowIndex += rowIndexCorrection + rowIndexDelta ;
909+ prevRowIndex = rowIndex ;
876910
877- if ( prevIndex === rowIndex ) return ;
911+ const changedRow = this . applyRowOperation ( newItems , rowIndex , rowIndexDelta , isPartialUpdate ) ;
878912
879- prevIndex = rowIndex ;
880- const oldItem = this . _items [ rowIndex ] ;
881- const oldNextItem = this . _items [ rowIndex + 1 ] ;
882- const newItem = items [ rowIndex ] ;
883- const newNextItem = items [ rowIndex + 1 ] ;
884-
885- const strict = equalItems ( oldItem , oldNextItem ) || equalItems ( newItem , newNextItem ) ;
886-
887- if ( newItem ) {
888- newItem . rowIndex = rowIndex ;
889- change . items . push ( newItem ) ;
890- }
891-
892- if ( oldItem && newItem && equalItems ( oldItem , newItem , strict ) ) {
893- changeType = 'update' ;
894- this . _items [ rowIndex ] = newItem ;
895- if ( oldItem . visible !== newItem . visible ) {
896- change . items . splice ( - 1 , 1 , { visible : newItem . visible } ) ;
897- } else if ( repaintChangesOnly && ! change . isFullUpdate ) {
898- columnIndices = this . _partialUpdateRow ( oldItem , newItem , rowIndex - rowIndexDelta ) ;
899- }
900- } else if ( newItem && ! oldItem || ( newNextItem && equalItems ( oldItem , newNextItem , strict ) ) ) {
901- changeType = 'insert' ;
902- this . _items . splice ( rowIndex , 0 , newItem ) ;
903- rowIndexCorrection ++ ;
904- } else if ( oldItem && ! newItem || ( oldNextItem && equalItems ( newItem , oldNextItem , strict ) ) ) {
905- changeType = 'remove' ;
906- this . _items . splice ( rowIndex , 1 ) ;
907- rowIndexCorrection -- ;
908- prevIndex = - 1 ;
909- } else if ( newItem ) {
910- changeType = 'update' ;
911- this . _items [ rowIndex ] = newItem ;
912- } else {
913+ if ( ! changedRow ) {
913914 return ;
914915 }
915916
916- change . rowIndices . push ( rowIndex - rowIndexDelta ) ;
917- change . changeTypes . push ( changeType ) ;
918- change . columnIndices . push ( columnIndices ) ;
917+ pushChangedRow ( changedRows , changedRow ) ;
918+
919+ if ( changedRow . changeType === 'insert' ) {
920+ rowIndexCorrection += 1 ;
921+ } else if ( changedRow . changeType === 'remove' ) {
922+ rowIndexCorrection -= 1 ;
923+ prevRowIndex = - 1 ;
924+ }
919925 } ) ;
920926 }
921927
922928 /**
923929 * @extended : editing, validating
924930 */
925- // eslint-disable-next-line @typescript-eslint/no-unused-vars
926- protected _isCellChanged ( oldRow , newRow , visibleRowIndex , columnIndex , isLiveUpdate ) {
927- if ( JSON . stringify ( oldRow . values [ columnIndex ] ) !== JSON . stringify ( newRow . values [ columnIndex ] ) ) {
931+ protected _isCellChanged (
932+ oldRow : ProcessedItem ,
933+ newRow : ProcessedItem ,
934+ visibleRowIndex : number ,
935+ columnIndex : number ,
936+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
937+ isLiveUpdate ?: boolean ,
938+ ) : boolean {
939+ const oldValue = oldRow . values [ columnIndex ] ;
940+ const newValue = newRow . values [ columnIndex ] ;
941+
942+ if ( JSON . stringify ( oldValue ) !== JSON . stringify ( newValue ) ) {
928943 return true ;
929944 }
930945
931- function isCellModified ( row , columnIndex ) {
932- return row . modifiedValues ? row . modifiedValues [ columnIndex ] !== undefined : false ;
933- }
946+ const isCellModified = ( row : ProcessedItem ) : boolean => row . modifiedValues ?. [ columnIndex ] !== undefined ;
934947
935- if ( isCellModified ( oldRow , columnIndex ) !== isCellModified ( newRow , columnIndex ) ) {
936- return true ;
937- }
938-
939- return false ;
948+ return isCellModified ( oldRow ) !== isCellModified ( newRow ) ;
940949 }
941950
942951 /**
943952 * @extended : editing_row_based, editing, editing_form_based
944953 */
945- protected _getChangedColumnIndices ( oldItem , newItem , visibleRowIndex , isLiveUpdate ) {
946- let columnIndices ;
947- if ( oldItem . rowType === newItem . rowType ) {
948- if ( newItem . rowType !== 'group' && newItem . rowType !== 'groupFooter' ) {
949- columnIndices = [ ] ;
950-
951- if ( newItem . rowType !== 'detail' ) {
952- for ( let columnIndex = 0 ; columnIndex < oldItem . values . length ; columnIndex ++ ) {
953- if ( this . _isCellChanged ( oldItem , newItem , visibleRowIndex , columnIndex , isLiveUpdate ) ) {
954- columnIndices . push ( columnIndex ) ;
955- }
956- }
957- }
954+ protected _getChangedColumnIndices (
955+ oldItem : ProcessedItem ,
956+ newItem : ProcessedItem ,
957+ visibleRowIndex : number ,
958+ isLiveUpdate ?: boolean ,
959+ ) : number [ ] | undefined {
960+ if ( oldItem . rowType !== newItem . rowType ) {
961+ return undefined ;
962+ }
963+
964+ if ( newItem . rowType === 'group' ) {
965+ if ( ! oldItem . cells || ! isSameGroupRowState ( oldItem , newItem ) ) {
966+ return undefined ;
958967 }
959968
960- if ( newItem . rowType === 'group' && oldItem . cells ) {
961- const isRowStateEquals = newItem . isExpanded === oldItem . isExpanded
962- && newItem . data . isContinuation === oldItem . data . isContinuation
963- && newItem . data . isContinuationOnNextPage === oldItem . data . isContinuationOnNextPage ;
969+ return oldItem . cells
970+ . map ( ( cell , index ) => ( cell . column ?. type !== 'groupExpand' ? index : - 1 ) )
971+ . filter ( ( index ) => index >= 0 ) ;
972+ }
973+
974+ if ( newItem . rowType === 'groupFooter' ) {
975+ return undefined ;
976+ }
964977
965- if ( isRowStateEquals ) {
966- columnIndices = oldItem . cells . map ( ( cell , index ) => ( cell . column ?. type !== 'groupExpand' ? index : - 1 ) ) . filter ( ( index ) => index >= 0 ) ;
967- }
978+ const columnIndices : number [ ] = [ ] ;
979+
980+ if ( newItem . rowType === 'detail' ) {
981+ return columnIndices ;
982+ }
983+
984+ for ( let columnIndex = 0 ; columnIndex < oldItem . values . length ; columnIndex += 1 ) {
985+ if ( this . _isCellChanged ( oldItem , newItem , visibleRowIndex , columnIndex , isLiveUpdate ) ) {
986+ columnIndices . push ( columnIndex ) ;
968987 }
969988 }
989+
970990 return columnIndices ;
971991 }
972992
973- private _partialUpdateRow ( oldItem , newItem , visibleRowIndex , isLiveUpdate ?) {
974- let changedColumnIndices = this . _getChangedColumnIndices ( oldItem , newItem , visibleRowIndex , isLiveUpdate ) ;
975-
976- if ( changedColumnIndices ?. length && this . option ( 'dataRowTemplate' ) ) {
977- changedColumnIndices = undefined ;
978- }
979-
980- if ( changedColumnIndices ) {
993+ private _partialUpdateRow (
994+ oldItem : ProcessedItem ,
995+ newItem : ProcessedItem ,
996+ visibleRowIndex : number ,
997+ isLiveUpdate ?: boolean ,
998+ ) : number [ ] | undefined {
999+ const changedColumnIndices = this
1000+ . _getChangedColumnIndices (
1001+ oldItem ,
1002+ newItem ,
1003+ visibleRowIndex ,
1004+ isLiveUpdate ,
1005+ ) ;
1006+ const columnIndices = changedColumnIndices ?. length && this . option ( 'dataRowTemplate' )
1007+ ? undefined
1008+ : changedColumnIndices ;
1009+
1010+ if ( columnIndices ) {
9811011 oldItem . cells ?. forEach ( ( cell , columnIndex ) => {
982- const isCellChanged = changedColumnIndices . indexOf ( columnIndex ) >= 0 ;
1012+ const isCellChanged = columnIndices . includes ( columnIndex ) ;
9831013 if ( ! isCellChanged && cell ?. update ) {
9841014 cell . update ( newItem ) ;
9851015 }
@@ -996,24 +1026,24 @@ export class DataController extends DataHelperMixin(modules.Controller) {
9961026 oldItem . update ?.( newItem ) ;
9971027 }
9981028
999- return changedColumnIndices ;
1029+ return columnIndices ;
10001030 }
10011031
1002- protected _isItemEquals ( item1 , item2 ) {
1032+ protected _isItemEquals ( item1 : ProcessedItem , item2 : ProcessedItem ) : boolean {
10031033 if ( JSON . stringify ( item1 . values ) !== JSON . stringify ( item2 . values ) ) {
10041034 return false ;
10051035 }
10061036
1007- const compareFields = [ 'modified' , 'isNewRow' , 'removed' , 'isEditing' ] ;
1037+ const compareFields = [ 'modified' , 'isNewRow' , 'removed' , 'isEditing' ] as const ;
10081038 if ( compareFields . some ( ( field ) => item1 [ field ] !== item2 [ field ] ) ) {
10091039 return false ;
10101040 }
10111041
10121042 if ( item1 . rowType === 'group' || item1 . rowType === 'groupFooter' ) {
1013- const expandedMatch = item1 . isExpanded === item2 . isExpanded ;
1014- const summaryCellsMatch = JSON . stringify ( item1 . summaryCells ) === JSON . stringify ( item2 . summaryCells ) ;
1015- const continuationMatch = item1 . data ?. isContinuation === item2 . data ?. isContinuation && item1 . data ?. isContinuationOnNextPage === item2 . data ?. isContinuationOnNextPage ;
1016- if ( ! expandedMatch || ! summaryCellsMatch || ! continuationMatch ) {
1043+ const summaryCellsMatch = JSON . stringify ( item1 . summaryCells )
1044+ === JSON . stringify ( item2 . summaryCells ) ;
1045+
1046+ if ( ! summaryCellsMatch || ! isSameGroupRowState ( item1 , item2 ) ) {
10171047 return false ;
10181048 }
10191049 }
0 commit comments