@@ -2513,7 +2513,6 @@ class IrisGrid extends Component<IrisGridProps, IrisGridState> {
25132513
25142514 if (
25152515 column == null ||
2516- column < 0 ||
25172516 columnCount <= column ||
25182517 ! model . isFilterable ( modelColumn )
25192518 ) {
@@ -2523,22 +2522,19 @@ class IrisGrid extends Component<IrisGridProps, IrisGridState> {
25232522
25242523 const { metricCalculator, metrics } = this . state ;
25252524 assertNotNull ( metrics ) ;
2526- const { left, rightVisible, lastLeft } = metrics ;
2527- if ( column < left ) {
2528- this . grid ?. setViewState ( { left : column } , true ) ;
2529- } else if ( rightVisible < column ) {
2530- const metricState = this . getMetricState ( ) ;
2531- assertNotNull ( metricState ) ;
2532- const newLeft = metricCalculator . getLastLeft (
2533- metricState ,
2534- column ,
2535- metricCalculator . getVisibleWidth ( metricState )
2536- ) ;
2537- this . grid ?. setViewState (
2538- { left : Math . min ( newLeft , lastLeft ) , leftOffset : 0 } ,
2539- true
2540- ) ;
2525+ const metricState = this . getMetricState ( ) ;
2526+ assertNotNull ( metricState ) ;
2527+
2528+ const scrollColumn = metricCalculator . getScrollLeftForColumn (
2529+ column ,
2530+ metricState ,
2531+ metrics
2532+ ) ;
2533+
2534+ if ( scrollColumn != null ) {
2535+ this . grid ?. setViewState ( { left : scrollColumn , leftOffset : 0 } , true ) ;
25412536 }
2537+
25422538 this . lastFocusedFilterBarColumn = column ;
25432539 this . setState ( { focusedFilterBarColumn : column , isFilterBarShown : true } ) ;
25442540 }
@@ -4515,6 +4511,99 @@ class IrisGrid extends Component<IrisGridProps, IrisGridState> {
45154511 this . seekRow ( gotoValue , isBackwards ) ;
45164512 }
45174513
4514+ /**
4515+ * Render the input field for the focused filter
4516+ * @param metrics Grid metrics
4517+ * @param metricCalculator Metric calculator
4518+ * @param focusedFilterBarColumn Column index for the focused filter
4519+ * @param quickFilters Quick filters map
4520+ * @param advancedFilters Advanced filters map
4521+ * @returns The filter input field element or null if not applicable
4522+ */
4523+ getFilterBarInput (
4524+ metrics : GridMetrics | undefined ,
4525+ metricCalculator : IrisGridMetricCalculator ,
4526+ focusedFilterBarColumn : VisibleIndex | null ,
4527+ quickFilters : ReadonlyQuickFilterMap ,
4528+ advancedFilters : ReadonlyAdvancedFilterMap
4529+ ) : ReactElement | null {
4530+ if ( metrics == null || focusedFilterBarColumn == null ) {
4531+ return null ;
4532+ }
4533+
4534+ const metricState = this . getMetricState ( ) ;
4535+ if ( metricState == null ) {
4536+ return null ;
4537+ }
4538+
4539+ const filterBoxCoordinates = metricCalculator . getFilterInputCoordinates (
4540+ focusedFilterBarColumn ,
4541+ metricState ,
4542+ metrics
4543+ ) ;
4544+ if ( filterBoxCoordinates == null ) {
4545+ return null ;
4546+ }
4547+
4548+ const debounceMs = Math . min (
4549+ Math . max ( IrisGrid . minDebounce , Math . round ( metrics . rowCount / 200 ) ) ,
4550+ IrisGrid . maxDebounce
4551+ ) ;
4552+ const {
4553+ x,
4554+ y,
4555+ width : fieldWidth ,
4556+ height : fieldHeight ,
4557+ } = filterBoxCoordinates ;
4558+ const { width } = metrics ;
4559+ const style = {
4560+ top : y ,
4561+ left : x ,
4562+ minWidth : Math . min ( fieldWidth , width - x ) , // Don't cause overflow
4563+ height : fieldHeight ,
4564+ } ;
4565+ let value = '' ;
4566+ let isValid = true ;
4567+ const modelColumn = this . getModelColumn ( focusedFilterBarColumn ) ;
4568+ assertNotNull ( modelColumn ) ;
4569+ const quickFilter = quickFilters . get ( modelColumn ) ;
4570+ const advancedFilter = advancedFilters . get ( modelColumn ) ;
4571+ if ( quickFilter != null ) {
4572+ value = quickFilter . text ;
4573+ isValid = quickFilter . filter != null ;
4574+ }
4575+ const isBarFiltered = quickFilters . size !== 0 || advancedFilters . size !== 0 ;
4576+ const showAdvancedFilterButton =
4577+ metricCalculator . getAdvancedFilterButtonCoordinates (
4578+ focusedFilterBarColumn ,
4579+ metricState ,
4580+ metrics
4581+ ) != null ;
4582+ return (
4583+ < FilterInputField
4584+ ref = { this . filterInputRef }
4585+ style = { style }
4586+ className = { classNames ( {
4587+ error : ! isValid ,
4588+ active : value !== '' || advancedFilter != null ,
4589+ 'iris-grid-has-filter' : isBarFiltered ,
4590+ } ) }
4591+ showAdvancedFilterButton = { showAdvancedFilterButton }
4592+ isAdvancedFilterSet = { advancedFilter != null }
4593+ onAdvancedFiltersTriggered = { ( ) => {
4594+ this . setState ( { shownAdvancedFilter : focusedFilterBarColumn } ) ;
4595+ } }
4596+ key = { focusedFilterBarColumn }
4597+ onChange = { this . handleFilterBarChange }
4598+ onDone = { this . handleFilterBarDone }
4599+ onTab = { this . handleFilterBarTab }
4600+ onContextMenu = { this . grid ?. handleContextMenu }
4601+ debounceMs = { debounceMs }
4602+ value = { value }
4603+ />
4604+ ) ;
4605+ }
4606+
45184607 render ( ) : ReactElement | null {
45194608 const {
45204609 children,
@@ -4649,66 +4738,15 @@ class IrisGrid extends Component<IrisGridProps, IrisGridState> {
46494738 metrics != null && metrics . width > 0 && metrics . height > 0 ;
46504739 const isRollup = ( rollupConfig ?. columns ?. length ?? 0 ) > 0 ;
46514740
4652- let focusField = null ;
4653-
4654- const debounceMs = metrics
4655- ? Math . min (
4656- Math . max ( IrisGrid . minDebounce , Math . round ( metrics . rowCount / 200 ) ) ,
4657- IrisGrid . maxDebounce
4741+ const focusField = isFilterBarShown
4742+ ? this . getFilterBarInput (
4743+ metrics ,
4744+ metricCalculator ,
4745+ focusedFilterBarColumn ,
4746+ quickFilters ,
4747+ advancedFilters
46584748 )
4659- : IrisGrid . maxDebounce ;
4660-
4661- if ( isFilterBarShown && focusedFilterBarColumn != null && metrics != null ) {
4662- const { gridX, gridY, allColumnXs, allColumnWidths, width } = metrics ;
4663- const columnX = allColumnXs . get ( focusedFilterBarColumn ) ;
4664- const columnWidth = allColumnWidths . get ( focusedFilterBarColumn ) ;
4665- if ( columnX != null && columnWidth != null ) {
4666- const x = gridX + columnX ;
4667- const y = gridY - ( theme . filterBarHeight ?? 0 ) ;
4668- const fieldWidth = columnWidth + 1 ; // cover right border
4669- const fieldHeight = ( theme . filterBarHeight ?? 0 ) - 1 ; // remove bottom border
4670- const style = {
4671- top : y ,
4672- left : x ,
4673- minWidth : Math . min ( fieldWidth , width - x ) , // Don't cause overflow
4674- height : fieldHeight ,
4675- } ;
4676- let value = '' ;
4677- let isValid = true ;
4678- const modelColumn = this . getModelColumn ( focusedFilterBarColumn ) ;
4679- assertNotNull ( modelColumn ) ;
4680- const quickFilter = quickFilters . get ( modelColumn ) ;
4681- const advancedFilter = advancedFilters . get ( modelColumn ) ;
4682- if ( quickFilter != null ) {
4683- value = quickFilter . text ;
4684- isValid = quickFilter . filter != null ;
4685- }
4686- const isBarFiltered =
4687- quickFilters . size !== 0 || advancedFilters . size !== 0 ;
4688- focusField = (
4689- < FilterInputField
4690- ref = { this . filterInputRef }
4691- style = { style }
4692- className = { classNames ( {
4693- error : ! isValid ,
4694- active : value !== '' || advancedFilter != null ,
4695- 'iris-grid-has-filter' : isBarFiltered ,
4696- } ) }
4697- isAdvancedFilterSet = { advancedFilter != null }
4698- onAdvancedFiltersTriggered = { ( ) => {
4699- this . setState ( { shownAdvancedFilter : focusedFilterBarColumn } ) ;
4700- } }
4701- key = { focusedFilterBarColumn }
4702- onChange = { this . handleFilterBarChange }
4703- onDone = { this . handleFilterBarDone }
4704- onTab = { this . handleFilterBarTab }
4705- onContextMenu = { this . grid ?. handleContextMenu }
4706- debounceMs = { debounceMs }
4707- value = { value }
4708- />
4709- ) ;
4710- }
4711- }
4749+ : null ;
47124750
47134751 let loadingElement = null ;
47144752 if ( loadingText != null ) {
@@ -4753,26 +4791,27 @@ class IrisGrid extends Component<IrisGridProps, IrisGridState> {
47534791
47544792 const filterBar = [ ] ;
47554793 if ( metrics && isFilterBarShown ) {
4756- const { gridX, gridY, visibleColumns, allColumnXs, allColumnWidths } =
4757- metrics ;
4758- const { filterBarHeight } = theme ;
4794+ const metricState = this . getMetricState ( ) ;
4795+
4796+ // Advanced Filter buttons
4797+ const { visibleColumns } = metrics ;
47594798
47604799 for ( let i = 0 ; i < visibleColumns . length ; i += 1 ) {
47614800 const columnIndex = visibleColumns [ i ] ;
4762-
4763- const columnX = allColumnXs . get ( columnIndex ) ;
4764- const columnWidth = allColumnWidths . get ( columnIndex ) ;
47654801 const modelColumn = this . getModelColumn ( columnIndex ) ;
4802+
47664803 if ( modelColumn != null ) {
47674804 const isFilterable = model . isFilterable ( modelColumn ) ;
4768- if (
4769- isFilterable &&
4770- columnX != null &&
4771- columnWidth != null &&
4772- columnWidth > 0
4773- ) {
4774- const x = gridX + columnX + columnWidth - 24 ;
4775- const y = gridY - ( filterBarHeight ?? 0 ) + 2 ; // 2 acts as top margin for the button
4805+ const buttonCoordinates =
4806+ isFilterable && metricState
4807+ ? metricCalculator . getAdvancedFilterButtonCoordinates (
4808+ columnIndex ,
4809+ metricState ,
4810+ metrics
4811+ )
4812+ : null ;
4813+ if ( buttonCoordinates != null ) {
4814+ const { x, y } = buttonCoordinates ;
47764815 const style : CSSProperties = {
47774816 position : 'absolute' ,
47784817 top : y ,
0 commit comments