@@ -88,6 +88,21 @@ class GridContainer extends BaseContainer {
8888 merge : 'deep' ,
8989 value : null
9090 } ,
91+ /**
92+ * @member {Neo.grid.Body|null} bodyEnd=null
93+ * @protected
94+ */
95+ bodyEnd : null ,
96+ /**
97+ * @member {Neo.grid.Body|null} bodyStart=null
98+ * @protected
99+ */
100+ bodyStart : null ,
101+ /**
102+ * @member {Neo.container.Base|null} bodyWrapper=null
103+ * @protected
104+ */
105+ bodyWrapper : null ,
91106 /**
92107 * true uses grid.plugin.CellEditing
93108 * @member {Boolean} cellEditing_=false
@@ -124,6 +139,21 @@ class GridContainer extends BaseContainer {
124139 merge : 'deep' ,
125140 value : null
126141 } ,
142+ /**
143+ * @member {Neo.grid.header.Toolbar|null} headerEnd=null
144+ * @protected
145+ */
146+ headerEnd : null ,
147+ /**
148+ * @member {Neo.grid.header.Toolbar|null} headerStart=null
149+ * @protected
150+ */
151+ headerStart : null ,
152+ /**
153+ * @member {Neo.container.Base|null} headerWrapper=null
154+ * @protected
155+ */
156+ headerWrapper : null ,
127157 /**
128158 * @member {Object} layout={ntype: 'vbox', align: 'stretch'}
129159 * @reactive
@@ -244,7 +274,30 @@ class GridContainer extends BaseContainer {
244274 let me = this ,
245275 { appName, rowHeight, store, windowId} = me ;
246276
247- me . items = [ me . headerToolbar , me . body ] ;
277+ me . headerWrapper = Neo . create ( BaseContainer , {
278+ appName,
279+ cls : [ 'neo-header-wrapper' ] ,
280+ flex : 'none' ,
281+ layout : { ntype : 'hbox' , align : 'stretch' } ,
282+ parentId : me . id ,
283+ theme : me . theme ,
284+ windowId,
285+ items : [ me . headerToolbar ]
286+ } ) ;
287+
288+ me . bodyWrapper = Neo . create ( BaseContainer , {
289+ appName,
290+ cls : [ 'neo-body-wrapper' ] ,
291+ flex : 1 ,
292+ layout : { ntype : 'hbox' , align : 'stretch' } ,
293+ parentId : me . id ,
294+ style : { overflowY : 'auto' } ,
295+ theme : me . theme ,
296+ windowId,
297+ items : [ me . body ]
298+ } ) ;
299+
300+ me . items = [ me . headerWrapper , me . bodyWrapper ] ;
248301
249302 if ( me . footerToolbar ) {
250303 me . items . push ( me . footerToolbar )
@@ -652,7 +705,6 @@ class GridContainer extends BaseContainer {
652705 createColumns ( columns ) {
653706 let me = this ,
654707 { columnDefaults} = me ,
655- headerButtons = [ ] ,
656708 sorters = me . store ?. sorters ,
657709 columnClass , renderer ;
658710
@@ -680,8 +732,6 @@ class GridContainer extends BaseContainer {
680732 scope : me
681733 } ;
682734
683- headerButtons . push ( column ) ;
684-
685735 if ( column . component && ! column . type ) {
686736 column . type = 'component'
687737 }
@@ -697,9 +747,6 @@ class GridContainer extends BaseContainer {
697747 }
698748 }
699749
700- me . headerToolbar . items = headerButtons ;
701- me . headerToolbar . createItems ( ) ;
702-
703750 if ( Neo . typeOf ( me . _columns ) === 'NeoInstance' ) {
704751 me . _columns . clear ( ) ;
705752 me . _columns . add ( columns ) ;
@@ -708,20 +755,119 @@ class GridContainer extends BaseContainer {
708755 me . centerColumns = columns . filter ( c => ! c . locked ) ;
709756 me . lockedEndColumns = columns . filter ( c => c . locked === 'end' ) ;
710757
758+ me . createOrUpdateSubGrids ( ) ;
759+
711760 return me . _columns
712761 }
713762
714763 me . lockedStartColumns = columns . filter ( c => c . locked === 'start' ) ;
715764 me . centerColumns = columns . filter ( c => ! c . locked ) ;
716765 me . lockedEndColumns = columns . filter ( c => c . locked === 'end' ) ;
717766
767+ me . createOrUpdateSubGrids ( ) ;
768+
718769 return Neo . create ( Collection , {
719770 keyProperty : 'dataField' ,
720771 items : columns ,
721772 listeners : { mutate : me . onColumnsMutate , scope : me }
722773 } )
723774 }
724775
776+ /**
777+ * @protected
778+ */
779+ createOrUpdateSubGrids ( ) {
780+ let me = this ;
781+
782+ // --- Center (Default) ---
783+ if ( me . centerColumns . length > 0 ) {
784+ me . headerToolbar . items = me . centerColumns ;
785+ me . headerToolbar . createItems ( ) ;
786+ }
787+
788+ // --- Start (Left) ---
789+ if ( me . lockedStartColumns . length > 0 ) {
790+ if ( ! me . headerStart ) {
791+ me . headerStart = Neo . create ( header . Toolbar , {
792+ ...me . headerToolbar . initialConfig ,
793+ flex : 'none' ,
794+ gridContainer : me ,
795+ items : me . lockedStartColumns ,
796+ layoutLock : 'start' ,
797+ parentId : me . headerWrapper . id ,
798+ theme : me . theme ,
799+ windowId : me . windowId
800+ } ) ;
801+
802+ me . bodyStart = Neo . create ( GridBody , {
803+ ...me . body . initialConfig ,
804+ flex : 'none' ,
805+ gridContainer : me ,
806+ parentId : me . bodyWrapper . id ,
807+ theme : me . theme ,
808+ windowId : me . windowId
809+ } ) ;
810+ } else {
811+ me . headerStart . items = me . lockedStartColumns ;
812+ me . headerStart . createItems ( ) ;
813+ }
814+ } else if ( me . headerStart ) {
815+ me . headerStart . destroy ( ) ;
816+ me . bodyStart . destroy ( ) ;
817+ me . headerStart = me . bodyStart = null ;
818+ }
819+
820+ // --- End (Right) ---
821+ if ( me . lockedEndColumns . length > 0 ) {
822+ if ( ! me . headerEnd ) {
823+ me . headerEnd = Neo . create ( header . Toolbar , {
824+ ...me . headerToolbar . initialConfig ,
825+ flex : 'none' ,
826+ gridContainer : me ,
827+ items : me . lockedEndColumns ,
828+ layoutLock : 'end' ,
829+ parentId : me . headerWrapper . id ,
830+ theme : me . theme ,
831+ windowId : me . windowId
832+ } ) ;
833+
834+ me . bodyEnd = Neo . create ( GridBody , {
835+ ...me . body . initialConfig ,
836+ flex : 'none' ,
837+ gridContainer : me ,
838+ parentId : me . bodyWrapper . id ,
839+ theme : me . theme ,
840+ windowId : me . windowId
841+ } ) ;
842+ } else {
843+ me . headerEnd . items = me . lockedEndColumns ;
844+ me . headerEnd . createItems ( ) ;
845+ }
846+ } else if ( me . headerEnd ) {
847+ me . headerEnd . destroy ( ) ;
848+ me . bodyEnd . destroy ( ) ;
849+ me . headerEnd = me . bodyEnd = null ;
850+ }
851+
852+ // Synchronize SubGrids into DOM via Symmetrical Wrappers
853+ let bodyItems = [ ] ,
854+ headerItems = [ ] ;
855+
856+ if ( me . headerStart ) headerItems . push ( me . headerStart ) ;
857+ if ( me . headerToolbar ) headerItems . push ( me . headerToolbar ) ;
858+ if ( me . headerEnd ) headerItems . push ( me . headerEnd ) ;
859+
860+ if ( me . bodyStart ) bodyItems . push ( me . bodyStart ) ;
861+ if ( me . body ) bodyItems . push ( me . body ) ;
862+ if ( me . bodyEnd ) bodyItems . push ( me . bodyEnd ) ;
863+
864+ me . headerWrapper . items = headerItems ;
865+ me . bodyWrapper . items = bodyItems ;
866+
867+ me . headerWrapper . createItems ( ) ;
868+ me . bodyWrapper . createItems ( ) ;
869+ }
870+
725871 /**
726872 * @param args
727873 */
@@ -748,39 +894,20 @@ class GridContainer extends BaseContainer {
748894 onColumnLockChange ( column ) {
749895 let me = this ,
750896 columnsArray = [ ...me . columns . items ] ,
751- headerToolbar = me . headerToolbar ,
752897 sortedColumns = me . sortColumns ( columnsArray ) ;
753898
754- // 1. Sync the Header Toolbar cleanly via public API
755- // Batched by the framework's core update loop
756- headerToolbar . silentVdomUpdate = true ;
757-
758- sortedColumns . forEach ( ( col , targetIndex ) => {
759- let btn = headerToolbar . getColumn ( col . dataField ) ,
760- currentIndex = headerToolbar . indexOf ( btn ) ;
761-
762- if ( currentIndex !== targetIndex ) {
763- headerToolbar . moveTo ( currentIndex , targetIndex )
764- }
765- } ) ;
766-
767- headerToolbar . silentVdomUpdate = false ;
768- headerToolbar . update ( ) ;
769-
770- // 2. Sync the Collection
771- // clearSilent() and add() is the safest way to reset internal indices while avoiding duplicate mutate events
899+ // Sync the Collection
900+ // clearSilent() and add() is the safest way to reset internal indices
772901 me . columns . clearSilent ( ) ;
773902 me . columns . add ( sortedColumns ) ;
774903
775- me . lockedStartColumns = sortedColumns . filter ( c => c . locked === 'start' ) ;
776- me . centerColumns = sortedColumns . filter ( c => ! c . locked ) ;
777- me . lockedEndColumns = sortedColumns . filter ( c => c . locked === 'end' ) ;
904+ // Trigger Sub-grid Layout Sync
905+ me . onColumnsMutate ( ) ;
778906
779- // 3. Trigger Layout Engine
780- headerToolbar . passSizeToBody ( false ) ;
781-
782- // 4. Force a full row re-render to apply the new column order and styles
783- me . body . createViewData ( ) ;
907+ // Force a full row re-render to apply the new column order and styles
908+ if ( me . body ) me . body . createViewData ( ) ;
909+ if ( me . bodyStart ) me . bodyStart . createViewData ( ) ;
910+ if ( me . bodyEnd ) me . bodyEnd . createViewData ( ) ;
784911
785912 me . scrollManager ?. updateColumnScrollPinningAddon ( )
786913 }
@@ -836,7 +963,14 @@ class GridContainer extends BaseContainer {
836963 * @param {Object } data
837964 */
838965 onColumnsMutate ( data ) {
839- this . updateColCount ( )
966+ let me = this ;
967+
968+ me . lockedStartColumns = me . _columns . items . filter ( c => c . locked === 'start' ) ;
969+ me . centerColumns = me . _columns . items . filter ( c => ! c . locked ) ;
970+ me . lockedEndColumns = me . _columns . items . filter ( c => c . locked === 'end' ) ;
971+
972+ me . createOrUpdateSubGrids ( ) ;
973+ me . updateColCount ( )
840974 }
841975
842976 /**
0 commit comments