React-Window with dynamic list dimensions - javascript

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>
);

Related

React Native Elements ListItem.Accordion items cause SectionList to scroll (much) further than desired

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

React/MUI Popover positioning incorrectly with anchorPosition

I'm using a React/MUI Popover inside a react-window List element and am unable to get the Popover to position correctly -- it always winds up in the top left corner of the window (the component is unable to perform a getBoundingClientRctd() on the anchor element [anchorEl in the docs]).
So to get around that problem temporarily, I decided to use the anchorPosition parameter which allows to set an absolute position -- in my case, just the middle of the window. That's not working either.
I've reviewed the values in Chrome DevTools and everything seems to be OK (i.e., I do get an anchorEl when I'm using that; I get valid positionLeft/Top values; etc...
Probably something really simple and hoping someone can point out what I did wrong.
Edited: Key elements of the solution
Row component must be defined outside of the containing component.
the <List> component has an itemData attribute which is used to pass custom data to Row.
Edited to add react-window List renderer.
Here's the basic setup:
Popover renderer
renderPopover(template, itemId) {
const { anchorEl, anchorId } = this.state;
const { classes } = this.props;
const open = Boolean(anchorEl) && anchorId === itemId;
const bgColor = '#babaef';
const { innerHeight, innerWidth } = window;
const positionLeft = innerWidth / 2;
const positionTop = innerHeight / 2;
console.log(`renderPopover: ${positionLeft} / ${positionTop}`);
<Popover
id="simple-popper"
open={open}
style={{ color: 'Black' }}
anchorEl={anchorEl}
onClose={event => this.handlePopoverClose(event)}
anchorPosition={{ left: {positionLeft}, top: {positionTop} }}
anchorReference="anchorPosition"
>
<Typography style={{ backgroundColor: bgColor }} className={classes.typography}>
{this.renderScheduleElements(template, itemId)}
</Typography>
</Popover>
);
}
Button element renderer
renderScheduleComponent(template, itemId) {
const { anchorEl, anchorId } = this.state;
const open = Boolean(anchorEl) && anchorId === itemId;
const { classes } = this.props;
const id = open ? 'simple-popper' : undefined;
return (
<Grid key={itemId} item>
<Paper className={classes.paper}>
<div style={{ padding: '4px' }}>
<Button
NO_ref={itemId}
NODE_ref={(node) => this.buttonRef = node}
id={itemId}
name={itemId}
aria-owns={id}
aria-haspopup="true"
variant="outlined"
color="primary"
style={{
fontWeight: 'bold',
padding: '8px',
margin: 'auto',
display: 'block',
width: '100%',
}}
onClick={event => this.handlePopoverClick(event, itemId)}
>
{template.templateName}
</Button>
{(this.renderPopover).call(this, template, itemId)}
</div>
</Paper>
</Grid>
);
}
Click event handler
handlePopoverClick(event, id) {
event.preventDefault();
console.log(`handlePopoverClick : ${event.currentTarget.name}`);
this.setState({
anchorEl: event.currentTarget,
anchorId: id,
});
}
react-window List renderer
renderScheduleColumn(columnData) {
const { classes } = this.props;
const { scheduleDate, scheduleTemplates } = columnData;
this.scheduleTemplates = scheduleTemplates;
const Row = ({ index, style }) => {
return (
<div className={index % 2 ? "ListItemOdd" : "ListItemEven"} style={style}>
{this.renderScheduleComponent(scheduleTemplates[index], `${scheduleDate}:${index}`)}
</div>
);
}
const { columnHeight, columnWidth } = this.state;
return (
<Grid id={scheduleDate} key={scheduleDate} item>
<Paper className={classes.paper}>
<div style={{ width: '100%', textAlign: 'center' }}>
<Typography variant="h6" style={{ padding: '24px', color: 'white', backgroundColor: '#3f51b5' }}>
{scheduleDate}
</Typography>
</div>
<List
className="List"
height={columnHeight}
itemCount={scheduleTemplates.length}
itemSize={50}
width={columnWidth}
>
{Row}
</List>
</Paper>
</Grid>
);
}
It looks like a similar problem as here: React Material-UI menu anchor broken by react-window list.
You should move the definition of your Row function out of renderScheduleColumn so that it is a consistent type. This will require moving/reworking renderScheduleComponent as well. You can use the itemData property on the List to pass information to the Row.

React Virtualized table with CellMeasurer is not calculating the height properly

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>

Ternary operator on autoHeight for VirtualizedTable

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

autoHeight with WindowScroller doesn't render full List

when i use autoHeight parameter on WindowScroller's child (List),
it doesn't render whole list of items (only few of them), but without autoHeight everything renders, but the rest of page is not scrolling.
Thank you, there is my code and screenshot:
const bars = this.props.bars
const rowRenderer = ({ index, key, style }) => {
const bar = bars.get(index)
return (
<div key={key} style={style}>
<BarListItem
key={key}
onClick={this.props.onBarClick}
{...this.props}
{...bar}
/>
</div>
)
}
return <WindowScroller>
{({ height, isScrolling, onChildScroll, scrollTop }) => (
<AutoSizer disableHeight>
{({ width }) => (
<List
autoHeight
onScroll={onChildScroll}
ref="List"
height={height}
rowCount={bars.size}
rowRenderer={rowRenderer}
rowHeight={height/5.5}
width={width}
isScrolling={isScrolling}
// onChildScroll={onChildScroll}
scrollTop={scrollTop}
/>)}
</AutoSizer>
)}
</WindowScroller>
screenshot

Categories