horizontal scrolling
Summary: Adding a property `horizontallyScrollable` the `ManagedTable` that enables horizontal scrolling, if that is required by the table's contents. By default this behaviour is disabled to not break existing tables. Reviewed By: jknoxville Differential Revision: D15166144 fbshipit-source-id: 7a9b76facdbe717deb3d097e0b8883d4b0732d51
This commit is contained in:
committed by
Facebook Github Bot
parent
da44a02cad
commit
fca7bc93ee
@@ -116,6 +116,10 @@ export type ManagedTableProps = {|
|
|||||||
* Callback when sorting changes
|
* Callback when sorting changes
|
||||||
*/
|
*/
|
||||||
onSort?: (order: TableRowSortOrder) => void,
|
onSort?: (order: TableRowSortOrder) => void,
|
||||||
|
/**
|
||||||
|
* Table scroll horizontally, if needed
|
||||||
|
*/
|
||||||
|
horizontallyScrollable?: boolean,
|
||||||
|};
|
|};
|
||||||
|
|
||||||
type ManagedTableState = {|
|
type ManagedTableState = {|
|
||||||
@@ -126,9 +130,10 @@ type ManagedTableState = {|
|
|||||||
shouldScrollToBottom: boolean,
|
shouldScrollToBottom: boolean,
|
||||||
|};
|
|};
|
||||||
|
|
||||||
const Container = styled(FlexColumn)({
|
const Container = styled(FlexColumn)(props => ({
|
||||||
|
overflow: props.overflow ? 'scroll' : 'visible',
|
||||||
flexGrow: 1,
|
flexGrow: 1,
|
||||||
});
|
}));
|
||||||
|
|
||||||
class ManagedTable extends React.Component<
|
class ManagedTable extends React.Component<
|
||||||
ManagedTableProps,
|
ManagedTableProps,
|
||||||
@@ -318,8 +323,13 @@ class ManagedTable extends React.Component<
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
onColumnResize = (columnSizes: TableColumnSizes) => {
|
onColumnResize = (id: string, width: number | string) => {
|
||||||
this.setState({columnSizes});
|
this.setState(({columnSizes}) => ({
|
||||||
|
columnSizes: {
|
||||||
|
...columnSizes,
|
||||||
|
[id]: width,
|
||||||
|
},
|
||||||
|
}));
|
||||||
};
|
};
|
||||||
|
|
||||||
scrollToBottom() {
|
scrollToBottom() {
|
||||||
@@ -499,7 +509,13 @@ class ManagedTable extends React.Component<
|
|||||||
);
|
);
|
||||||
|
|
||||||
getRow = ({index, style}) => {
|
getRow = ({index, style}) => {
|
||||||
const {onAddFilter, multiline, zebra, rows} = this.props;
|
const {
|
||||||
|
onAddFilter,
|
||||||
|
multiline,
|
||||||
|
zebra,
|
||||||
|
rows,
|
||||||
|
horizontallyScrollable,
|
||||||
|
} = this.props;
|
||||||
const {columnOrder, columnSizes, highlightedRows} = this.state;
|
const {columnOrder, columnSizes, highlightedRows} = this.state;
|
||||||
const columnKeys = columnOrder
|
const columnKeys = columnOrder
|
||||||
.map(k => (k.visible ? k.key : null))
|
.map(k => (k.visible ? k.key : null))
|
||||||
@@ -520,16 +536,37 @@ class ManagedTable extends React.Component<
|
|||||||
style={style}
|
style={style}
|
||||||
onAddFilter={onAddFilter}
|
onAddFilter={onAddFilter}
|
||||||
zebra={zebra}
|
zebra={zebra}
|
||||||
|
horizontallyScrollable={horizontallyScrollable}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const {columns, rows, rowLineHeight, hideHeader} = this.props;
|
const {
|
||||||
|
columns,
|
||||||
|
rows,
|
||||||
|
rowLineHeight,
|
||||||
|
hideHeader,
|
||||||
|
horizontallyScrollable,
|
||||||
|
} = this.props;
|
||||||
const {columnOrder, columnSizes} = this.state;
|
const {columnOrder, columnSizes} = this.state;
|
||||||
|
|
||||||
|
let computedWidth = 0;
|
||||||
|
if (horizontallyScrollable) {
|
||||||
|
for (const col in columnSizes) {
|
||||||
|
const width = columnSizes[col];
|
||||||
|
if (isNaN(width)) {
|
||||||
|
// non-numeric columns with, can't caluclate
|
||||||
|
computedWidth = 0;
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
computedWidth += parseInt(width, 10);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Container>
|
<Container overflow={horizontallyScrollable}>
|
||||||
{hideHeader !== true && (
|
{hideHeader !== true && (
|
||||||
<TableHead
|
<TableHead
|
||||||
columnOrder={columnOrder}
|
columnOrder={columnOrder}
|
||||||
@@ -539,6 +576,7 @@ class ManagedTable extends React.Component<
|
|||||||
sortOrder={this.state.sortOrder}
|
sortOrder={this.state.sortOrder}
|
||||||
columnSizes={columnSizes}
|
columnSizes={columnSizes}
|
||||||
onSort={this.onSort}
|
onSort={this.onSort}
|
||||||
|
horizontallyScrollable={horizontallyScrollable}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
<Container>
|
<Container>
|
||||||
@@ -560,7 +598,7 @@ class ManagedTable extends React.Component<
|
|||||||
DEFAULT_ROW_HEIGHT
|
DEFAULT_ROW_HEIGHT
|
||||||
}
|
}
|
||||||
ref={this.tableRef}
|
ref={this.tableRef}
|
||||||
width={width}
|
width={Math.max(width, computedWidth)}
|
||||||
estimatedItemSize={rowLineHeight || DEFAULT_ROW_HEIGHT}
|
estimatedItemSize={rowLineHeight || DEFAULT_ROW_HEIGHT}
|
||||||
overscanCount={5}
|
overscanCount={5}
|
||||||
innerRef={this.scrollRef}
|
innerRef={this.scrollRef}
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ const TableHeaderColumnContainer = styled('div')({
|
|||||||
padding: '0 8px',
|
padding: '0 8px',
|
||||||
});
|
});
|
||||||
|
|
||||||
const TableHeadContainer = styled(FlexRow)({
|
const TableHeadContainer = styled(FlexRow)(props => ({
|
||||||
borderBottom: `1px solid ${colors.sectionHeaderBorder}`,
|
borderBottom: `1px solid ${colors.sectionHeaderBorder}`,
|
||||||
color: colors.light50,
|
color: colors.light50,
|
||||||
flexShrink: 0,
|
flexShrink: 0,
|
||||||
@@ -53,7 +53,8 @@ const TableHeadContainer = styled(FlexRow)({
|
|||||||
textAlign: 'left',
|
textAlign: 'left',
|
||||||
top: 0,
|
top: 0,
|
||||||
zIndex: 2,
|
zIndex: 2,
|
||||||
});
|
minWidth: props.horizontallyScrollable ? 'min-content' : 0,
|
||||||
|
}));
|
||||||
|
|
||||||
const TableHeadColumnContainer = styled('div')(props => ({
|
const TableHeadColumnContainer = styled('div')(props => ({
|
||||||
position: 'relative',
|
position: 'relative',
|
||||||
@@ -97,9 +98,17 @@ class TableHeadColumn extends PureComponent<{
|
|||||||
onColumnResize: ?TableOnColumnResize,
|
onColumnResize: ?TableOnColumnResize,
|
||||||
children?: React$Node,
|
children?: React$Node,
|
||||||
title?: string,
|
title?: string,
|
||||||
|
horizontallyScrollable?: boolean,
|
||||||
}> {
|
}> {
|
||||||
ref: HTMLElement;
|
ref: HTMLElement;
|
||||||
|
|
||||||
|
componentDidMount() {
|
||||||
|
if (this.props.horizontallyScrollable) {
|
||||||
|
// measure initial width
|
||||||
|
this.onResize(this.ref.offsetWidth);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
onClick = () => {
|
onClick = () => {
|
||||||
const {id, onSort, sortOrder} = this.props;
|
const {id, onSort, sortOrder} = this.props;
|
||||||
|
|
||||||
@@ -117,7 +126,7 @@ class TableHeadColumn extends PureComponent<{
|
|||||||
};
|
};
|
||||||
|
|
||||||
onResize = (newWidth: number) => {
|
onResize = (newWidth: number) => {
|
||||||
const {id, columnSizes, onColumnResize, width} = this.props;
|
const {id, onColumnResize, width} = this.props;
|
||||||
if (!onColumnResize) {
|
if (!onColumnResize) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -143,10 +152,7 @@ class TableHeadColumn extends PureComponent<{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
onColumnResize({
|
onColumnResize(id, normalizedWidth);
|
||||||
...columnSizes,
|
|
||||||
[id]: normalizedWidth,
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
setRef = (ref: HTMLElement) => {
|
setRef = (ref: HTMLElement) => {
|
||||||
@@ -191,6 +197,7 @@ export default class TableHead extends PureComponent<{
|
|||||||
onSort: ?TableOnSort,
|
onSort: ?TableOnSort,
|
||||||
columnSizes: TableColumnSizes,
|
columnSizes: TableColumnSizes,
|
||||||
onColumnResize: ?TableOnColumnResize,
|
onColumnResize: ?TableOnColumnResize,
|
||||||
|
horizontallyScrollable?: boolean,
|
||||||
}> {
|
}> {
|
||||||
buildContextMenu = (): MenuTemplate => {
|
buildContextMenu = (): MenuTemplate => {
|
||||||
const visibles = this.props.columnOrder
|
const visibles = this.props.columnOrder
|
||||||
@@ -237,6 +244,7 @@ export default class TableHead extends PureComponent<{
|
|||||||
onColumnResize,
|
onColumnResize,
|
||||||
onSort,
|
onSort,
|
||||||
sortOrder,
|
sortOrder,
|
||||||
|
horizontallyScrollable,
|
||||||
} = this.props;
|
} = this.props;
|
||||||
const elems = [];
|
const elems = [];
|
||||||
|
|
||||||
@@ -284,7 +292,8 @@ export default class TableHead extends PureComponent<{
|
|||||||
onSort={onSort}
|
onSort={onSort}
|
||||||
columnSizes={columnSizes}
|
columnSizes={columnSizes}
|
||||||
onColumnResize={onColumnResize}
|
onColumnResize={onColumnResize}
|
||||||
title={key}>
|
title={key}
|
||||||
|
horizontallyScrollable={horizontallyScrollable}>
|
||||||
{col.value}
|
{col.value}
|
||||||
{arrow}
|
{arrow}
|
||||||
</TableHeadColumn>
|
</TableHeadColumn>
|
||||||
@@ -296,10 +305,11 @@ export default class TableHead extends PureComponent<{
|
|||||||
|
|
||||||
lastResizable = isResizable;
|
lastResizable = isResizable;
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ContextMenu buildItems={this.buildContextMenu}>
|
<ContextMenu buildItems={this.buildContextMenu}>
|
||||||
<TableHeadContainer>{elems}</TableHeadContainer>
|
<TableHeadContainer horizontallyScrollable={horizontallyScrollable}>
|
||||||
|
{elems}
|
||||||
|
</TableHeadContainer>
|
||||||
</ContextMenu>
|
</ContextMenu>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ export type TableHighlightedRows = Array<string>;
|
|||||||
|
|
||||||
export type TableColumnKeys = Array<string>;
|
export type TableColumnKeys = Array<string>;
|
||||||
|
|
||||||
export type TableOnColumnResize = (sizes: TableColumnSizes) => void;
|
export type TableOnColumnResize = (id: string, size: number | string) => void;
|
||||||
export type TableOnColumnOrder = (order: TableColumnOrder) => void;
|
export type TableOnColumnOrder = (order: TableColumnOrder) => void;
|
||||||
export type TableOnSort = (order: TableRowSortOrder) => void;
|
export type TableOnSort = (order: TableRowSortOrder) => void;
|
||||||
export type TableOnHighlight = (
|
export type TableOnHighlight = (
|
||||||
|
|||||||
Reference in New Issue
Block a user