I am stuck on how to make a Virtualized Table using the Autosizer to shrink to the smallest possible height until a certain maximum threshold.
Is there a way to convert my autoHeight to use a conditional value?
autoheight={height<500}
Here is my render function:
render(): ?React$Element<any> {
const { columns, selectedItems, rulesList } = this.state;
const { shouldRender, currentRule, sort } = this.props;
if (!shouldRender) return <div />;
return (
<Grid columns={1} id="rulesTabContent">
<Grid.Row style={NO_PADDING_BOTTOM}>
<Grid.Column>
<VirtualizedTable
id="rulesTable"
autoHeight={true}
height={500}
rowDef={rowDef => this.rowDef(rowDef, currentRule.id)}
onRowDoubleClick={this.checkExpanded}
columns={columns}
sortVals={sort}
setSort={this.updateSort}
sortFn={this.sortFunc}
getRowHeight={rowHeight}
noRowsRenderer={this.noRowsRenderer}
rowCount={rulesList.length}
setSelectedItems={this.setSelectedItems}
selectedItems={selectedItems}
list={rulesList}
/>
</Grid.Column>
</Grid.Row>
</Grid>
);
}
Here is the render function of VirtualizedTable, which is used above:
import {
AutoSizer,
Table,
Column,
SortIndicator,
SortDirection
} from "react-virtualized";
.
.
.
render() {
const {
list,
sortVals,
onRowClick,
onRowsRendered,
noRowsRenderer,
getRowHeight,
columns,
onRefresh,
...restOfProps
} = this.props;
return (
<AutoSizer disableHeight>
{({ width }) => (
<Table
headerClassName="virtualized-header"
headerHeight={HEADER_HEIGHT}
rowHeight={getRowHeight ? this._rowHeight : DEFAULT_ROW_HEIGHT}
overscanRowCount={OVERSCAN}
width={width}
sort={this._sort}
sortBy={sortVals.by}
sortDirection={sortVals.direction}
rowCount={list.size}
noRowsRenderer={this._noRows}
rowGetter={this._getRow}
rowClassName={this._getRowClassName}
rowRenderer={this._renderRow}
onRowsRendered={this._onRowsRendered}
onRowClick={this._onRowClick}
{...restOfProps}
>
{Array.isArray(columns)
? columns
: Object.keys(columns).map(key => columns[key])}
</Table>
)}
</AutoSizer>
);
}
}
It's hard to debug without a demo, but maybe something like this?
return (
<AutoSizer>
{({ width, height }) => (
<Table
height={height}
...
-
<VirtualizedTable
id="rulesTable"
autoHeight={height < 500}
height={Math.min(height, 500)}
A little more info, are you looking for this?
style={{height: 'auto', max-height: '500px', overflow: 'scroll'})
this.state = {
autoHeight: {overflowX: 'hidden'},
}
{rulesList.length < 50 ? this.setState({autoHeight: {overFlowX: 'hidden'}}) : this.setState({autoHeight: overFlowX: 'scroll'}) }
From the Devs...
style={{
overflowX: 'hidden',
overflowY: 'hidden'
}}
So you would need to add a style that looks something like this.
style={this.state.autoHeight}
Heres a closed issue on the topic
https://github.com/bvaughn/react-virtualized/issues/488
Related
I'm trying to use arrays in Grommet DataTable. My data looks like this :
{
customer: [
'BANANA',
'Banana',
'banana',
'republic of banana'
],
somethingelse: ['ABC','123','DEF']
}
In a regular Grommet Table , I'm able to use every cell by defining the first value from the array as title - for example customer[0] - and create an expandable arrow to show the rest of the data in 'customer' :
But I don't get how to do this on a cell basis for a Grommet DataTable ?
Here is the way I'm using it in the regular Grommet Table :
<TableCell scope="row" pad={{ left: '2px', righ: '3px' }}>
<TextInput name="tags" size="xsmall" />
</TableCell>
</TableRow>
{searchResults.length > 0 &&
searchResults.map((searchResult, index) => (
<TableRow key={index}>
<TableCell>
<Box direction="row">
<Text size="xsmall">{searchResult.customer[0]}</Text>
{searchResult.customer.length > 1 && (
<Button
plain
hoverIndicator={false}
icon={
isExpanded[index] ? (
<FormDown size="18px" />
) : (
<FormNext size="18px" />
)
}
onClick={() => toggleOpen(index)}
/>
)}
</Box>
<Box>
{isExpanded[index] && listElements(searchResult.customer)}
</Box>
</TableCell>
Here is my Form , using DataTable :
return (
<Form value={formData} onSubmit={onSubmit} onChange={onChange}>
...
<DataTable
fill
border={{ body: 'bottom' }}
paginate
columns={columns}
data={searchResults}
select={select}
onClickRow={(e) => console.log(e.datum)}
onSelect={() => {}}
step={8}
rowDetails={(row) => { // I'm able to use rowDetails to expand and display some data , but how can I use this to 1. Use the [0] element of the array as title and 2. apply to all cells in the row/table.
for (const cell in row) {
// if (cell.length > 1) {
// return listElements(cell);
// }
console.log(cell);
}
}}
...
/>
...
</Form>
);
I was able to achieve that by using the render function and passing a CellElement to it, in which I have created my rules :
const columns = [
{
property: 'customer',
header: <FormField label="Customer" name="customer" size="xsmall" />,
render: (datum) => <CellElement val={datum.customer} />,
},
CellElement.js
import { Box, Text, Button } from 'grommet';
import { FormNext, FormDown } from 'grommet-icons';
import React, { useState } from 'react';
const CellElement = ({ val }) => {
const title = Array.isArray(val) ? val[0] : val;
const [isExpanded, setIsExpanded] = useState({});
const toggleOpen = (category) => {
setIsExpanded({
...isExpanded,
[category]: !isExpanded[category],
});
};
const listElements = (arr) => {
return arr.slice(1).map((el, index) => (
<Text key={index} size="xsmall">
{el}
</Text>
));
};
return (
<Box>
<Box direction="row">
<Text size="xsmall">{title}</Text>
{Array.isArray(val) && val.length > 1 && (
<Button
plain
hoverIndicator={false}
icon={
isExpanded[title] ? (
<FormDown size="18px" />
) : (
<FormNext size="18px" />
)
}
onClick={() => toggleOpen(title)}
/>
)}
</Box>
<Box>{isExpanded[title] && listElements(val)}</Box>
</Box>
);
};
export default CellElement;
I'm trying to add react-window to my stack, however all examples require the width and height of the list to be statically defined like this:
import { FixedSizeList as List } from 'react-window';
const Column = ({ index, style }) => (
<div style={style}>Column {index}</div>
);
const Example = () => (
<List
height={75}
itemCount={1000}
itemSize={100}
layout="horizontal"
width={300}
>
{Column}
</List>
);
In my current code, the width and height are defined as follows:
<div
style={{
overflowY: 'scroll',
height:
width >= 600 &&
(isFirefox() ? '100%' : '-webkit-fill-available'),
width: isFirefox()
? '-moz-available'
: '-webkit-fill-available',
}}
>
{items.map(item => <MyItem ... />}
</div>
As you can see, my width and height are defined as 100% or webkit-fill-available.
How can I adapt this to make it work with react-window or react-virtualized?
I would suggest using the same logic as className in react-window, so we are going to write it like :
const Example = () => (
<List
height={75}
itemCount={1000}
itemSize={100}
layout="horizontal"
width={300}
className={isFirefox() ? 'FirefoxClass' : 'anotherStyle'}
>
{Column}
</List>
);
I'm using a react-native-elements ListItem.Accordion because everything else in my React Native SectionList uses ListItems (and the documentation seems un-opinionated about this), and I'm very happy with the rendering. Unfortunately, when scrolling down it scrolls beyond the last displayed item until it gets to where the end would be if all the accordion items were fully expanded.
Obviously, when all the accordion items are expanded it stops scrolling when it gets to the end.
How do I get the SectionList to only scroll to what's visible when the accordion items are not expanded?
const renderSectionHeader = ({ section: { title, data } }) => {
return { data.length > 0 ? (
<Text>{title}</Text>
) : null };
}
const renderSeparator = () => {
return (
<View style={{
height: 1,
backgroundColor: "#CED0CE",
marginLeft: "5%",
width: "90%",
}} />
);
};
const renderItem = ({ item }) => {
return (
<ListItem.Accordion
content={
<>
<MaterialIcons.Button
name="shopping-basket"
style={{paddingLeft:20}}
onPress={() => { alert(item.greeting) }} />
<ListItem.Content style={{marginLeft:15}}>
<ListItem.Title>{item.title}</ListItem.Title>
<ListItem.Subtitle>{item.subtitle}</ListItem.Subtitle>
</ListItem.Content>
</>
}
isExpanded={isExpanded}
onPress={() => {
toggleAccordionItem(item.key);
}}
>
<ListItem item={item}>
<MaterialIcons.Button
name="airport-shuttle"
onPress={() => { alert(item.direction) }}>
<Text>Show Direction</Text>
</MaterialIcons.Button>
</ListItem>
</ListItem.Accordion>
);
};
return (
<SafeAreaView>
<SectionList
refreshing={!isRefreshing}
sections={sections}
renderItem={renderItem}
renderSectionHeader={renderSectionHeader}
ItemSeparatorComponent={renderSeparator}
keyExtractor={(item) => item.key}
/>
</SafeAreaView>
)
Just started using React Native last week, but my workaround for a similar situation was to use the ListItem inside the accordion like this
{isExpanded && <ListItem item={item} />}
But it turns the animation of the accordion clunky. Don't know which is the correct way to address the issue, if someone has experiencied this issue before and know what is the proper way to solve i would appreciate
I have a strange issue going on. The react-virtualized table with CellMeasurer is not calculating the height properly.
My render function looks something like this :-
<Table
deferredMeasurementCache={this._cache}
className={mainClasses}
width={500}
height={500}
headerHeight={headerHeight}
rowHeight={this._cache.rowHeight}
rowCount={dataList.length}
sort={this.sort}
sortBy={sortBy}
sortDirection={sortDirection}
rowGetter={({ index }) => dataList[index]}
rowRenderer={this.rowRenderer}
overscanRowCount={overscanRowCount}
onRowClick={onRowClick}
>
{
columnsDef.map((item, index) => {
if (index === 0) {
return (
<Column
key={item.dataKey}
dataKey={item.dataKey}
width={25}
cellRenderer={this.checkboxCellRenderer}
disableSort={!this.isSortEnabled(item)}
headerRenderer={this.checkBoxHeaderRenderer}
/>
);
}
return (
<Column
key={item.dataKey}
dataKey={item.dataKey}
width={item.width ? item.width : 0}
label={item.label}
cellRenderer={this.dynamicCellRenderer}
disableSort={!this.isSortEnabled(item)}
headerRenderer={this.headerRenderer}
flexGrow={item.flexGrow}
/>
);
})
}
</Table>
My dynamic cell renderer function is as follows :-
dynamicCellRenderer = (data) => {
return (
<CellMeasurer
cache={this._cache}
columnIndex={0}
key={data.cellData}
parent={data.parent}
rowIndex={data.rowIndex}
>
<div
style={{
whiteSpace: 'normal'
}}
>
{data.cellData}
</div>
</CellMeasurer>
);
}
I have cache defined in constructor :-
constructor(props) {
super(props);
this._cache = new CellMeasurerCache({ minHeight: props.rowHeight, fixedWidth: true });
}
Can anyone please help me with where am I going wrong. Am I missing something with implementation of CellMeasurer? Any help is appreciated. Thanks in advance.
I had the same issue.
I think that the problem is because each cell's height is calculated before it's content is loaded.
I used the parameter measure of the CellMeasurer and call it when the content is loaded
<CellMeasurer
cache={cache}
columnIndex={0}
key={key}
parent={parent}
rowIndex={index}
width={width}
>
{({measure}) => (
<Content onLoad={measure} />
)}
</CellMeasurer>
I am trying to update the list when my redux store changes but for some odd reason it isn't. I have to manually refresh the page to see my changes. Here's the snippet of my List component and rowRenderer.
<InfiniteLoader
isRowLoaded={this._isRowLoaded}
loadMoreRows={this._loadMoreRows}
rowCount={visibleRequest.length}
>
{({ onRowsRendered, registerChild }) => (
<AutoSizer>
{({ height, width }) => (
<List
ref={registerChild}
className="List"
height={height}
rowHeight={listRowHeight}
onRowsRendered={onRowsRendered}
rowCount={rowCount}
rowRenderer={this._rowRenderer}
width={width}
/>
)}
</AutoSizer>
)}
</InfiniteLoader>
_rowRenderer = ({ index, key, style }) => {
const { loadedRowsMap, selected } = this.state;
const row = this.getDatum(index);
let content;
if (loadedRowsMap[index] === STATUS_LOADED) {
content = row;
} else {
content = (
<div className="placeholder" style={{ width: _.random(100, 200) }} />
);
}
return (
<PendingChat
key={key}
content={content}
style={style}
row={row}
{...this.props}
/>
);
};
Yeah, I ran into the same problem. Its because the references to your objects don't change when you do
const row = this.getDatum(index);
let content;
if (loadedRowsMap[index] === STATUS_LOADED) {
content = row;
}
Take a look at immutability.