/** * Component layout for {@link Ext.view.Table} * @private * */Ext.define('Ext.view.TableLayout', { extend: 'Ext.layout.component.Auto', alias: 'layout.tableview', type: 'tableview', beginLayout: function(ownerContext) { var me = this, owner = me.owner, ownerGrid = owner.ownerGrid, partner = owner.lockingPartner, partnerContext = ownerContext.lockingPartnerContext, context = ownerContext.context, scrollable = ownerGrid.getScrollable(), partnerVisible; partnerVisible = partner && partner.grid.isVisible() && !(partner.grid.collapsed || partner.grid.floatedFromCollapse); // Flag whether we need to do row height synchronization. // syncRowHeightOnNextLayout is a one time flag used when some code knows it has changed // data height and that the upcoming layout must sync row heights even if the grid // is configured not to for general row rendering. ownerContext.doSyncRowHeights = partnerVisible && (ownerGrid.syncRowHeight || ownerGrid.syncRowHeightOnNextLayout); // The reason for checking .config here is that by setting the overflow on the context, it // overwrites the value in the scrollable. As such, all we're trying to do here is capture // the initial intent to see if the user configured the scroller as x: false. // It's not perfect but will cover 99% of cases. ownerContext.allowScrollX = scrollable && scrollable.config && scrollable.config.x; if (!me.columnFlusherId) { me.columnFlusherId = me.id + '-columns'; me.rowHeightFlusherId = me.id + '-rows'; } me.callParent([ ownerContext ]); // If we are in a twinned grid (locked view) then set up bidirectional links with // the other side's layout context. If the locked or normal side is hidden then // we should treat it as though we were laying out a single grid, so don't setup // the partners. // This is typically if a grid is configured with locking but starts with no locked columns. if (partnerVisible) { if (!partnerContext && partner.componentLayout.isRunning()) { // eslint-disable-next-line max-len (partnerContext = ownerContext.lockingPartnerContext = context.getCmp(partner)).lockingPartnerContext = ownerContext; // Set up opposite side's link if not already aware. if (!partnerContext.lockingPartnerContext) { partnerContext.lockingPartnerContext = ownerContext; } } if (ownerContext.doSyncRowHeights) { if (partnerContext && !partnerContext.rowHeightSynchronizer) { partnerContext.rowHeightSynchronizer = partnerContext.target.syncRowHeightBegin(); } ownerContext.rowHeightSynchronizer = me.owner.syncRowHeightBegin(); } } // Grab a ContextItem for the header container (and make sure the TableLayout can // reach us as well): (ownerContext.headerContext = context.getCmp(me.headerCt)).viewContext = ownerContext; }, beginLayoutCycle: function(ownerContext, firstCycle) { this.callParent([ ownerContext, firstCycle ]); if (ownerContext.syncRowHeights) { ownerContext.target.syncRowHeightClear(ownerContext.rowHeightSynchronizer); ownerContext.syncRowHeights = false; } }, calculate: function(ownerContext) { var me = this, context = ownerContext.context, lockingPartnerContext = ownerContext.lockingPartnerContext, headerContext = ownerContext.headerContext, ownerCtContext = ownerContext.ownerCtContext, state = ownerContext.state, owner = me.owner, bodyDom = owner.body.dom, columnsChanged = headerContext.getProp('columnsChanged'), overflowable, columnFlusher, otherSynchronizer, synchronizer, rowHeightFlusher, bodyHeight, ctSize, overflowY, overflowX, scrollbarHeight; // Shortcut when empty grid - let the base handle it. // EXTJS-14844: Even when no data rows (all.getCount() === 0) there may be // summary rows to size. if (!owner.all.getCount() && (!bodyDom || !owner.body.child('table', true))) { ownerContext.setProp('viewOverflowY', false); me.callParent([ ownerContext ]); return; } // BufferedRenderer#beforeTableLayout reads, so call it in a read phase. if (me.calcCount === 1 && me.owner.bufferedRenderer) { me.owner.bufferedRenderer.beforeTableLayout(ownerContext); } if (columnsChanged === undefined) { // We cannot proceed when we have rows but no columnWidths determined... me.done = false; return; } if (columnsChanged) { if (!(columnFlusher = state.columnFlusher)) { // Since the columns have changed, we need to write the widths to the DOM. // Queue (and possibly replace) a pseudo ContextItem, who's flush method // routes back into this class. context.queueFlush(state.columnFlusher = columnFlusher = { ownerContext: ownerContext, columnsChanged: columnsChanged, layout: me, id: me.columnFlusherId, flush: me.flushColumnWidths }, true); } if (!columnFlusher.flushed) { // We have queued the columns to be written, but they are still pending, so // we cannot proceed. me.done = false; return; } } // They have to turn row height synchronization on, or there may be variable row heights // Either no columns changed, or we have flushed those changes.. which means the // column widths in the DOM are correct. Now we can proceed to syncRowHeights (if // we are locking) or wrap it up by determining our vertical overflow. if (ownerContext.doSyncRowHeights) { if (!(rowHeightFlusher = state.rowHeightFlusher)) { // When we are locking, both sides need to read their row heights in a read // phase (i.e., right now). if (!(synchronizer = state.rowHeights)) { state.rowHeights = synchronizer = ownerContext.rowHeightSynchronizer; me.owner.syncRowHeightMeasure(synchronizer); ownerContext.setProp('rowHeights', synchronizer); } if (!(otherSynchronizer = lockingPartnerContext.getProp('rowHeights'))) { me.done = false; return; } // Queue (and possibly replace) a pseudo ContextItem, who's flush method // routes back into this class. context.queueFlush(state.rowHeightFlusher = rowHeightFlusher = { ownerContext: ownerContext, synchronizer: synchronizer, otherSynchronizer: otherSynchronizer, layout: me, id: me.rowHeightFlusherId, flush: me.flushRowHeights }, true); } if (!rowHeightFlusher.flushed) { me.done = false; return; } } me.callParent([ ownerContext ]); if (!ownerContext.heightModel.shrinkWrap) { if (!ownerCtContext.heightModel.shrinkWrap) { overflowable = true; // We are placed in a fit layout of the gridpanel (our ownerCt), so we need to // consult its containerSize when we are not shrink-wrapping to see if our // content will overflow vertically. ctSize = ownerCtContext.target.layout.getContainerSize(ownerCtContext); if (!ctSize.gotHeight) { me.done = false; return; } bodyHeight = bodyDom.offsetHeight; if (bodyHeight > ctSize.height) { overflowY = true; } } } // Adjust the presence of X scrollability depending upon whether the headers // overflow, and scrollbars take up space. // This has two purposes. // // For lockable assemblies, if there is horizontal overflow in the normal side, // The locked side (which shrinkwraps the columns) must be set to overflow: scroll // in order that it has acquires a matching horizontal scrollbar. // // If no locking, then if there is no horizontal overflow, we set overflow-x: hidden // This avoids "pantom" scrollbars which are only caused by the presence of another // scrollbar. scrollbarHeight = Ext.getScrollbarSize().height; if (me.done && ownerContext.allowScrollX && scrollbarHeight) { // No locking sides, ensure X scrolling is on if there is overflow, // but not if there is no overflow // This eliminates "phantom" scrollbars which are only caused by other scrollbars. // Locking horizontal scrollbars are handled in Ext.grid.locking.Lockable#afterLayout if (!owner.lockingPartner) { if (owner.isAutoTree) { overflowX = true; } else { overflowX = !!ownerContext.headerContext.state.boxPlan.tooNarrow; } ownerContext.setProp('overflowX', overflowX); } // If the overflowY was set to false but then adding a horizontal scrollbar // will overflow the view vertically we need to set overflowY to true if (overflowX && bodyHeight && overflowable) { overflowY = (bodyHeight + scrollbarHeight) > ctSize.height; } } if (me.done || overflowY != null) { ownerContext.setProp('viewOverflowY', !!overflowY); } }, measureContentHeight: function(ownerContext) { var owner = this.owner, bodyDom = owner.body.dom, emptyEl = owner.emptyEl, bodyHeight = 0; if (emptyEl) { bodyHeight += emptyEl.offsetHeight; } if (bodyDom) { bodyHeight += bodyDom.offsetHeight; } // This will have been figured out by now because the columnWidths have been // published... if (ownerContext.headerContext.state.boxPlan.tooNarrow) { bodyHeight += Ext.getScrollbarSize().height; } return bodyHeight; }, flushColumnWidths: function() { // NOTE: The "this" pointer here is the flusher object that was queued. var flusher = this, me = flusher.layout, ownerContext = flusher.ownerContext, columnsChanged = flusher.columnsChanged, owner = ownerContext.target, len = columnsChanged.length, column, i, colWidth, lastBox; if (ownerContext.state.columnFlusher !== flusher) { return; } // Set column width corresponding to each header for (i = 0; i < len; i++) { if (!(column = columnsChanged[i])) { continue; } colWidth = column.props.width; owner.body.select(owner.getColumnSizerSelector(column.target)).setWidth(colWidth); // Allow columns which need to perform layouts on resize queue a layout if (column.target.onCellsResized) { column.target.onCellsResized(colWidth); } // Enable the next go-round of headerCt's ColumnLayout change check to // read true, flushed lastBox widths that are in the Table lastBox = column.lastBox; if (lastBox) { lastBox.width = colWidth; } } flusher.flushed = true; if (!me.pending) { ownerContext.context.queueLayout(me); } }, flushRowHeights: function() { // NOTE: The "this" pointer here is the flusher object that was queued. var flusher = this, me = flusher.layout, ownerContext = flusher.ownerContext; if (ownerContext.state.rowHeightFlusher !== flusher) { return; } ownerContext.target.syncRowHeightFinish(flusher.synchronizer, flusher.otherSynchronizer); flusher.flushed = true; ownerContext.syncRowHeights = true; if (!me.pending) { ownerContext.context.queueLayout(me); } }, finishedLayout: function(ownerContext) { var me = this, ownerGrid = me.owner.ownerGrid, nodeContainer = Ext.fly(me.owner.getNodeContainer()), scroller = this.owner.getScrollable(), buffered; me.callParent([ ownerContext ]); if (nodeContainer) { nodeContainer.setWidth(ownerContext.headerContext.props.contentWidth); } // Inform any buffered renderer about completion of the layout of its view buffered = me.owner.bufferedRenderer; if (buffered) { buffered.afterTableLayout(ownerContext); } if (ownerGrid) { ownerGrid.syncRowHeightOnNextLayout = false; } if (scroller && !scroller.isScrolling) { // BufferedRenderer only sets nextRefreshStartIndex to zero when preserveScrollOnReload // is false. And if variableRowHeight is true, restoring the scroller will be handled // by the bufferedRenderer if (buffered) { if (buffered.nextRefreshStartIndex === 0 || me.owner.hasVariableRowHeight()) { return; } } scroller.restoreState(); } }, getLayoutItems: function() { return this.owner.getRefItems(); }, isValidParent: function() { return true; }});