Skip to content

Commit

Permalink
feat(ngrid): allow minimum height based on row count
Browse files Browse the repository at this point in the history
BREAKING CHANGES:

`PblNgridComponent.fallbackMinHeight` is now deprecated and will be removed in the next major version (4.0.0), use `PblNgridComponent.minDataViewHeight` instead

The UI layout has changed when virtual scroll is disabled (`vScrollNone`).
Previously, the layout was inconsistent, creating different behviour when vScroll was disabled. The fixed metadata rows were set within the container and the data viewport had no scrollbar. This is now changed and it will behave exactly the same in both modes.
  • Loading branch information
shlomiassaf committed Dec 3, 2020
1 parent b488d91 commit 296fe5d
Show file tree
Hide file tree
Showing 25 changed files with 223 additions and 130 deletions.
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<div *ngIf="section === 1" fxLayoutAlign="start stretch">
<div fxFlex="0 1 332px" class="col-quickthrough-table1">
<pbl-ngrid showFooter [columns]="columns" vScrollNone
<pbl-ngrid showFooter [columns]="columns" vScrollNone [minDataViewHeight]="-999"
[dataSource]="[ { id: 1, name: 'John', age: 25 }, { id: 2, name: 'Tom', age: 44 } ]"></pbl-ngrid>
</div>
<div fxLayoutAlign="center center">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
</ul>
</div>
<div fxFlex="50%">
<pbl-ngrid vScrollNone [showHeader]="showHeader" [showFooter]="showFooter"
<pbl-ngrid vScrollNone [showHeader]="showHeader" [showFooter]="showFooter" minDataViewHeight="-2"
[columns]="columns" [dataSource]="ds"></pbl-ngrid>
</div>
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ I> If you're unfamiliar with meta-row or data/table rows read the [quick-through

And now a live example:

<div pbl-example-view="pbl-columns-simple-model-example" containerClass="table-height-300 mat-elevation-z7"></div>
<div pbl-example-view="pbl-columns-simple-model-example" containerClass="mat-elevation-z7"></div>

We got 2 rows for 1 instance. The first row represents headers for the data columns and the 2nd row the data itself.

Expand Down
Original file line number Diff line number Diff line change
@@ -1 +1 @@
<pbl-ngrid style="height: 110px" [dataSource]="dsSimpleModel" [columns]="columnsSimpleModel"></pbl-ngrid>
<pbl-ngrid [dataSource]="dsSimpleModel" [columns]="columnsSimpleModel" [minDataViewHeight]="-1"></pbl-ngrid>
Original file line number Diff line number Diff line change
@@ -1 +1 @@
<pbl-ngrid [dataSource]="ds" [columns]="columns" fallbackMinHeight="150"></pbl-ngrid>
<pbl-ngrid [dataSource]="ds" [columns]="columns" minDataViewHeight="-5"></pbl-ngrid>
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ Let's take this made up requirement for a table:

And: All columns must be at least 40px wide.

<div pbl-example-view="pbl-columns-factory-example"></div>
<div pbl-example-view="pbl-columns-factory-example" containerClass="mat-elevation-z7"></div>

All meta rows are set in the order they we're added.

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ const dataSource = [
];
```

<div pbl-example-view="pbl-datasource-introduction-simple-model-example"></div>
<div pbl-example-view="pbl-datasource-introduction-simple-model-example" containerClass="mat-elevation-z7"></div>

Using a **Promise** adds async functionality and using an **Observable** adds streaming capabilities on top of that.

Expand Down
Original file line number Diff line number Diff line change
@@ -1 +1 @@
<pbl-ngrid style="height: 500px" [dataSource]="ds" [columns]="columns"></pbl-ngrid>
<pbl-ngrid [dataSource]="ds" [columns]="columns" minDataViewHeight="-1"></pbl-ngrid>
Original file line number Diff line number Diff line change
Expand Up @@ -24,5 +24,5 @@ export class DatasourceIntroductionSimpleModelExample {
},
};

ds = timer(1000).pipe(mapTo([ { id: 10, name: 'John Doe', email: 'john.doe@anonymous.com' }]));
ds = [{ id: 10, name: 'John Doe', email: 'john.doe@anonymous.com' }];
}
Original file line number Diff line number Diff line change
@@ -1,11 +1,26 @@
<div fxLayout="row" fxLayoutGap="16px" style="width: 100%; padding: 16px">
<mat-checkbox [checked]="explicitGridHeight" (change)="explicitGridHeight = $event.checked">Explicit grid height</mat-checkbox>
<mat-checkbox [checked]="fallbackMinHeight" (change)="fallbackMinHeight = $event.checked">Set fallbackMinHeight to 150</mat-checkbox>
<mat-checkbox [checked]="vScroll" (change)="vScroll = $event.checked">Virtual Scroll Enabled</mat-checkbox>
<mat-form-field appearance="legacy">
<mat-label>Set Explicit Grid Height</mat-label>
<mat-select [value]="explicitGridHeight" (selectionChange)="explicitGridHeight = $event.value">
<mat-option value="300">300 Pixels</mat-option>
<mat-option value="500">500 Pixels</mat-option>
<mat-option value="750">750 Pixels</mat-option>
<mat-option value="">None</mat-option>
</mat-select>
</mat-form-field>
<mat-form-field appearance="legacy">
<mat-label>Set minDataViewHeight</mat-label>
<mat-select [value]="minDataViewHeight" (selectionChange)="minDataViewHeight = $event.value">
<mat-option value="150">150 Pixels</mat-option>
<mat-option value="-3">3 Rows</mat-option>
<mat-option value="-10">10 Rows</mat-option>
<mat-option value="0">None</mat-option>
</mat-select>
</mat-form-field>

<div fxFlex="*"></div>
<button fxFlex="noshrink" mat-flat-button (click)="redraw()">Redraw</button>
</div>
<div *ngIf="settings" [class.table-height-300]="settings.explicitGridHeight">
<pbl-ngrid *ngIf="settings.vScroll" [dataSource]="ds" [columns]="columns" [fallbackMinHeight]="settings.fallbackMinHeight" vScrollAuto></pbl-ngrid>
<pbl-ngrid *ngIf="!settings.vScroll" [dataSource]="ds" [columns]="columns" [fallbackMinHeight]="settings.fallbackMinHeight" vScrollNone></pbl-ngrid>
<div *ngIf="settings">
<pbl-ngrid [dataSource]="ds" [columns]="columns" [minDataViewHeight]="settings.minDataViewHeight" [style.height]="settings.explicitGridHeight" vScrollAuto></pbl-ngrid>
</div>
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { ChangeDetectionStrategy, Component, ViewEncapsulation, ChangeDetectorRef } from '@angular/core';
import { ChangeDetectionStrategy, Component, ViewEncapsulation, ChangeDetectorRef, OnDestroy } from '@angular/core';
import { createDS, columnFactory } from '@pebula/ngrid';
import { Example } from '@pebula/apps/shared';
import { Person, DemoDataSource } from '@pebula/apps/shared-data';
Expand All @@ -11,7 +11,7 @@ import { Person, DemoDataSource } from '@pebula/apps/shared-data';
changeDetection: ChangeDetectionStrategy.OnPush,
})
@Example('pbl-grid-height-grid-example', { title: 'Grid Height Example' })
export class GridHeightGridExample {
export class GridHeightGridExample implements OnDestroy {

columns = columnFactory()
.default({ minWidth: 40 })
Expand Down Expand Up @@ -51,14 +51,12 @@ export class GridHeightGridExample {
.onTrigger( () => this.datasource.getPeople(0, 500) )
.create();

explicitGridHeight = true;
fallbackMinHeight = true;
vScroll = true;
explicitGridHeight = '';
minDataViewHeight = '-3';

settings: {
explicitGridHeight: boolean;
fallbackMinHeight: number;
vScroll: boolean;
explicitGridHeight: string;
minDataViewHeight: number;
}

constructor(private datasource: DemoDataSource, private cdr: ChangeDetectorRef) {
Expand All @@ -68,6 +66,7 @@ export class GridHeightGridExample {
ngOnDestroy(): void {
this.ds.dispose();
}

redraw(): void {
this.settings = undefined;
setTimeout(() => {
Expand All @@ -78,9 +77,8 @@ export class GridHeightGridExample {

private createSettings(): void {
this.settings = {
explicitGridHeight: this.explicitGridHeight,
fallbackMinHeight: this.fallbackMinHeight ? 150 : 0,
vScroll: this.vScroll,
explicitGridHeight: this.explicitGridHeight ? this.explicitGridHeight + 'px' : null,
minDataViewHeight: Number(this.minDataViewHeight),
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { MatCheckboxModule } from '@angular/material/checkbox';
import { MatButtonModule } from '@angular/material/button';
import { MatSelectModule } from '@angular/material/select';
import { PblNgridModule } from '@pebula/ngrid';

import { BindNgModule } from '@pebula/apps/shared';
Expand All @@ -13,7 +14,7 @@ import { GridHeightGridExample } from './grid-height.component';
imports: [
CommonModule,
ExampleCommonModule,
MatCheckboxModule, MatButtonModule,
MatCheckboxModule, MatButtonModule, MatSelectModule,
PblNgridModule,
],
exports: [ GridHeightGridExample ],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,72 +6,94 @@ ordinal: 2
---
# Height And Scrolling

In the layout introduction we covered the different sections of the table, how meta rows (header, footer, etc...) differ by type (fixed, row, sticky)
and how they interact with each section and with the data rows.
In the layout introduction we covered the different sections of the grid.
How meta rows (header, footer, etc...) differ by type (fixed, row, sticky) and how they interact with each section and with the data rows.

Height and scrolling refer to:
All of the components in the layout are stacked vertically, one after the other.
This vertical stack affect and is affected by the height.

- Total grid height (the height of `<pbl-ngrid>`)
- The height of the data rows section
- The data rows section scroll bar
- The grid's scrollbar (`<pbl-ngrid>`)
In this section we will explain how height affect the vertical stack and how it is affected by the stack itself.
To simplify things we will only refer to 2 categories in the stack:

- **Data Viewport** - The section containing data rows
- **Metadata Viewport** - All other sections (before and after)

When the grid create the layout, it will first let all of the components in the **Metadata Viewport** occupy the height they need.
Once done, the remaining height will be occupied by the **Data Viewport**, assuming there is height left to occupy.

In other words, by default, the data viewport has no size and it will grow based on the available space it has left in the container.

The following effects the height and scrolling:

- CSS style of the grid (`<pbl-ngrid>`) (height)
- Virtual scroll mode
- The layout of meta rows (fixed VS row VS sticky)
- `PblNgridComponent.fallbackMinHeight`
- The layout of meta rows (fixed VS row VS sticky) and size of items in the **Metadata Viewport**
- The value defined in `PblNgridComponent.minDataViewHeight`

All of the above is user defined and different combinations create different height and scrolling layouts.
The height provided, will effect the **Data Viewport** and if no height is set, it will be set by the layout itself.

All of the above is user defined and different combinations will create different height and scrolling layouts.
We will not cover all possible combination, instead we will explain the effect of each definition.

I> To best visualize the effects we will use a column definition set with multiple meta rows (fixed)
I> To best visualize the effects we will use a column definition set with multiple meta rows (fixed), i.e meta rows are **outside** of the data viewport

<div pbl-example-view="pbl-grid-height-grid-example" containerClass="mat-elevation-z7"></div>

## minDataViewHeight

When the container height is fixed and there is no height left for the data viewport then it will get no height (0 height).

To simulate this in the example, set the **Explicit Grid Height** to `300` and the **minDataViewHeight** to `None`.
The items in the **Metadata Viewport** occupy more then 300px, leaving no space for the data rows.

To solve this, we need a way to tell the grid to assign a minimum height to the **Data Viewport**.

We do this by setting the `@Input` property **minDataViewHeight** which accepts a `number` in 2 variations:

<div pbl-example-view="pbl-grid-height-grid-example"></div>
A. Default minimum height in explicit pixels
B. Default minimum height based on an initial amount of rows, multiplied by the row height.

## fallbackMinHeight
For variation **A**, provide a positive value, for **B** provide a negative value.

The `fallbackMinHeight` input defines the absolute minimum height (in pixels) to assign to the data row section container (not the grid).
For example:

When not set, no minimum height is defined to the data row section container which can lead to different outcomes depending on the virtual scroll mode.
A. Minimum data viewport of 100 pixels: `<pbl-ngrid minDataViewHeight="100"></pbl-ngrid>`
B. Minimum data viewport of 2 ros: `<pbl-ngrid minDataViewHeight="-2"></pbl-ngrid>`

### fallbackMinHeight with virtual scroll
**Notes when using the row variation**

When **virtual scroll** is enabled the data row section's height is based on the grid's height.
This means that if there is not explicit height set to the grid the data row's section will have a height of 0.
- The row height is calculated based on an initial row pre-loaded by the grid, this row will get it's height from the CSS theme defined.
- The final amount of rows is the lower value between the row count input and the total rows to render.
i.e. If the input is 1,000,000 rows and there are only 2, the height occupied will reflect 2 rows.

To work around that you can either:
I> Once height is assigned to the **Data Viewport** the user can view all of the data using the scrollbar.

- Set a height to the grid (%, px, flex or any other method).
- Set the **fallbackMinHeight** input property of the grid
## Examples

In the example above you can see that setting the grid's height is not enough (**Explicit grid height**) and you also need to check **Set fallbackMinHeight to 150**.
This happen because the explicit grid height set is **300px** but we have a lot of meta rows which take more then 300px, so data rows get no height.
Let's explore some scenarios and understand the result.

If the total height of all meta rows would have been 100px the data rows section would have been 200px.
### Auto Size of the Data Viewport

If virtual scroll is enabled and **Set fallbackMinHeight to 150** is set to true the total height of the grid will be 150px (data rows) plus the total height of all meta rows.
When the **minDataViewHeight** is set to `None`, items in the **Data Viewport** will occupy space
**only** if there is any left.

### fallbackMinHeight without virtual scroll
In the example above, setting the **Explicit Grid Height** to `300` is not enough, we need a value which is greater then the total
height of the **Metadata Viewport**. If you set it to `500` or above, you will see data rows.

When **virtual scroll** is disabled the data row section will take all the space it needs to render all rows.
The **Data Viewport** will grow as much as it is allowed to, when no **minDataViewHeight** is set

If the grid has an explicit height set the grid will show a single scroll bar that will scroll all rows, including meta rows of type fixed.
### Container Overflow

However, when **virtual scroll** is disabled and **fallbackMinHeight** is set, the behavior of **fallbackMinHeight** is a bit different.
In such case, the data row section is limited to the height set in **fallbackMinHeight**.
If we set the **Explicit Grid Height** to `300` and the **minDataViewHeight** to `10 Rows` we end up with a strange layout, containing 2 vertical scroll bars.

#### Multiple scroll bars
This happen because the calculated minimum height of the **Data Viewport** PLUS the total height of the **Metadata Viewport** is greater then the
fixed height we set (300) causing it to overflow and forcing the container to show the scroll bar.

Multiple bar scroll bars are something you should avoid.
Since there are more rows to render then available height in the **Data Viewport** it also show a scroll bar.

To reproduce them in the example above:
This is something to avoid, the best approach (when possible) is not to limit the size of the grid (or limit with a big enough value) so this won't happen.

- Disable virtual scroll
- Enable explicit grid height & fallbackMinHeight
## Remember, it is minimum

The reason for having 2 scroll bars is the fact that we have a grid height limit of 300px that contains a data row section of 150px plus multiple meta rows that
take more then the remaining 150px.
If we set the **Explicit Grid Height** to `750` and the **minDataViewHeight** to `3 Rows` we end up with more then 3 data rows rendered.

The result is a scroll bar for the entire gris and a scroll bar for the data rows section.
This is because **minDataViewHeight** set's the minimum height, if there is space, it will be occupied.
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,8 @@
cellTooltip
focusMode="cell"
matCheckboxSelection="selection" matCheckboxSelectionColor="primary"
[stickyHeader]="['table']"
[dataSource]="ds"
[columns]="columns"
(cellClick)="$event.context && $event.context.startEdit()"
style="height: 500px"
class=" pbl-ngrid-cell-ellipsis pbl-ngrid-header-cell-ellipsis">
<pbl-demo-action-row filter (refresh)="refresh()" ></pbl-demo-action-row>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -267,7 +267,7 @@ I> Notice that some property can accept an array of keys, some can only accept b

Starting with the **grid** chunk / property, we only specify 3 keys, the grid chunk itself has more keys on it's chunks:

`'showHeader', 'showFooter', 'focusMode', 'usePagination', 'hideColumns', 'fallbackMinHeight'`
`'showHeader', 'showFooter', 'focusMode', 'usePagination', 'hideColumns', 'minDataViewHeight'`

So if we set the filter in **include** the 3 keys are the only state we save for the grid chunk.
If we set the filter to **exclude** we use all other keys **except** the 3.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
<pbl-ngrid blockUi [dataSource]="ds" [columns]="columns" vScrollNone showFooter class="pbl-ngrid-cell-ellipsis enter-and-leave-grid-example "
<pbl-ngrid blockUi [dataSource]="ds" [columns]="columns" vScrollNone showFooter [minDataViewHeight]="-5" class="pbl-ngrid-cell-ellipsis enter-and-leave-grid-example "
(cellEnter)="onEnterLeaveEvents($event, true)" (cellLeave)="onEnterLeaveEvents($event)"
(rowEnter)="onEnterLeaveEvents($event, true)" (rowLeave)="onEnterLeaveEvents($event)"></pbl-ngrid>
Original file line number Diff line number Diff line change
Expand Up @@ -62,4 +62,4 @@ The plugin is used the ContextApi to support mouse & keyboard behavior, you can

<div pbl-example-view="pbl-target-events-example"></div>

<div pbl-example-view="pbl-enter-and-leave-events-example"></div>
<div pbl-example-view="pbl-enter-and-leave-events-example" containerClass="mat-elevation-z7"></div>
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ A table can host multiple header rows including multiple group rows, when settin

<p>This can become hard to track, luckily for us the <a [routerLink]="['../', 'column-factory']">columns factory</a> will strip the complexity for us.</p>

<div pbl-example-view="pbl-multi-header-column-group-example"></div>
<div pbl-example-view="pbl-multi-header-column-group-example" containerClass="mat-elevation-z7"></div>

## Complex Group Hierarchy

Expand Down
Original file line number Diff line number Diff line change
@@ -1 +1 @@
<pbl-ngrid [dataSource]="ds" [columns]="columns"></pbl-ngrid>
<pbl-ngrid [dataSource]="ds" [columns]="columns" [minDataViewHeight]="-5"></pbl-ngrid>
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
<pbl-ngrid rowReorder vScrollNone fallbackMinHeight="150"
<pbl-ngrid rowReorder vScrollNone minDataViewHeight="150"
[dataSource]="ds" [columns]="columns"></pbl-ngrid>
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
usePagination
blockUi
vScrollNone
fallbackMinHeight="300"
minDataViewHeight="300"
[dataSource]="ds"
[columns]="columns">
<pbl-ngrid-paginator *pblNgridPaginatorRef="let table"
Expand Down
10 changes: 10 additions & 0 deletions dev_docs/REFACTOR_REF.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# Refactor Items

## [1] Add support in the RowApi to find rows/cells based on various inputs

- Input of an HTML element to get a row or a cell.
- Input of row ident to get row or row ident + column id to get cell

We then refactor **target-events** to use this API.

Search for this reference in the code to see where refactor is needed
Loading

0 comments on commit 296fe5d

Please sign in to comment.