React-Virtualized keep floating Table header inside WindowScroller - react-virtualized

The documentation for WindowScroller in React-Virtualized (https://github.com/bvaughn/react-virtualized/blob/master/docs/WindowScroller.md) suggests that it is compatible with the Table and the below code does sort of work
<WindowScroller>
{({ height, isScrolling, registerChild, scrollTop }) => (
<AutoSizer disableHeight>
{({ width }) => (
<div ref={registerChild}>
<Table
autoHeight
isScrolling={isScrolling}
scrollTop={scrollTop}
height={height}
width={Math.max(width, 700)}
headerHeight={20}
rowHeight={30}
rowCount={warps.length}
rowGetter={({ index }) => warps[index]}
>
<Column
dataKey="one"
label="One"
width={100}
/>
<Column
dataKey="two"
label="Two"
width={50}
/>
</Table>
</div>
)}
</AutoSizer>
but the table header is now no longer floating above the table as was the case without WindowScroller. Is there any way that I can get this behavior back? I guess I could manually render the header outside the table, but that will be quite a hassle because I want to use flexGrow on the columns so I will not know their widths and would probably need to create a clone table with no rows for that?

Related

Iterate through array and display React component one at a time

I have the following page:
const GalleryPage: NextPage = () => {
const router = useRouter();
const id = router.query.id as string;
return (
<Gallery id={id} imageUrl="someRandomImageUrl.png" />
);
};
And within the Gallery Component, I have
...
return (
<div>
<Form
onSubmit={handleSubmit}
name="judging"
>
<input type="hidden" name="form-name" value="judging" />
<Container text style={{ marginTop: '2em' }} textAlign="center">
<Image src={galleryProps?.imageUrl} size="medium" verticalAlign="middle" />
<Table singleLine>
<Table.Header>
<TableHeader />
</Table.Header>
<Table.Body>
<TableRow
title="Composition"
name="composition"
item={composition}
setItem={setComposition}
/>
<TableRow
title="Creativity - Originality"
name="creativity"
item={creativity}
setItem={setCreativity}
/>
<TableRow
title="Neatness - Finishing details"
name="neatness"
item={neatness}
setItem={setNeatness}
/>
</Table.Body>
</Table>
</Container>
<Container text style={{ marginTop: '1em' }} textAlign="center">
<p>
<Button type="submit">Submit</Button>
</p>
</Container>
</Form>
</div>
);
Where TableRow and TableHeader are custom components.
What I can't seem to make happen
I have an array of image URLs:
const imageUrls = ['firstImageUrl.png', 'secondImageUrl.jpeg', 'thirdImageUrl.png']
And for each item in the imageUrls, I want to display the Gallery component. And only once the form in the Gallery is submitted, do I want the next instance to display with the following image.
ie:
firstcase: <Gallery id={id} imageUrl="firstImageUrl.png" /> once form submitted, second case
secondcase: <Gallery id={id} imageUrl="secondImageUrl.jpeg" /> once form submitted, third case ...
You can create a state called galleryIndex that starts of as 0 and then to gallery you pass a function prop called onSumbit and inside there you increase the state galleryIndex by 1 and then inside the Gallery page you set imageUrl on Gallery component to be the element from the array imageUrls at the index galleryIndex

How to make Material-UI GridListTitleBar and Image a Link in ReactJS

I am using Material UI GridList to display a list of Events in React. Everything is working fine except that I am not able to make Title or Image a Link. Does anyone know how to make Title and Image a Link?
Here is my Component.
<div className={classes.root}>
<GridListTile key="Subheader" style={{ height: "auto" }}>
<ListSubheader component="div">This is List of Events</ListSubheader>
</GridListTile>
<GridList
cellHeight={330}
cols={matches ? 1 : 3}
className={classes.gridList}
spacing={12}
>
{tileData.length > 0 &&
tileData.map((tile, index) => {
return (
<GridListTile
key={Math.floor(Math.random() * new Date().getTime())}
>
<img src={tile.eventImage} alt={tile.title} />
<GridListTileBar
title={tile.title}
subtitle={<span>by: {tile.description}</span>}
actionIcon={<IconButton title={tile.title} />}
/>
</GridListTile>
);
})}
</GridList>
</div>
);
Update
This is an update to the answer that was given below. The first image is now smaller than the rest of the images after update the code with the solution given below.
Here is the new code that I am trying:
<div className={classes.root}>
<GridListTile key="Subheader" style={{ height: "auto" }}>
<ListSubheader component="div">This is List of Events</ListSubheader>
</GridListTile>
<GridList
cellHeight={330}
cols={matches ? 1 : 3}
className={classes.gridList}
spacing={12}
>
{tileData.length > 0 &&
tileData.map((tile, index) => {
return (
<GridListTile
key={Math.floor(Math.random() * new Date().getTime())}
>
<a href={"events/" + tile._id + "/eventcomments"}>
<img
src={tile.eventImage}
alt={tile.title}
className="MuiGridListTile-imgFullHeight"
/>
<GridListTileBar title={tile.title} />
</a>
</GridListTile>
);
})}
</GridList>
</div>
Try wrapping the image with an anchor tag like this:
// ...
<GridListTile key={Math.floor(Math.random() * new Date().getTime())}>
<a href="https://www.google.com/">
<img src={tile.eventImage} alt={tile.title} className="MuiGridListTile-imgFullHeight" />
</a>
<GridListTileBar
title={tile.title}
subtitle={<span>by: {tile.description}</span>}
actionIcon={<IconButton title={tile.title} />}
/>
</GridListTile>
// ...
It is important, after wrapping the image inside an anchor tag to add the class MuiGridListTile-imgFullHeight to the image to keep the same styling of the grid. Normally this class is added automatically, but if you wrap it inside an anchor tag it isn't. So you need to add it manually.
Update
The image shows expected behavior, because your first image is not wide enough to cover the whole tile and you only added the class to scale the img to full height. There is also a class to scale to full width: MuiGridListTile-imgFullWidth, but you can't use both of these classes together for your use case as the styles conflict (see the style definition here: https://github.com/mui-org/material-ui/blob/master/packages/material-ui/src/GridListTile/GridListTile.js).
You can try to set the width to 100% in the image style prop:
<img
// ...
style={{ width: "100%" }}
className="MuiGridListTile-imgFullHeight"
/>
I eventually managed to get it to work without breaking or stretching my images, and here is what I did.
<div className={classes.root}>
<GridListTile key="Subheader" style={{ height: "auto" }}>
<ListSubheader component="div">This is List of Events</ListSubheader>
</GridListTile>
<GridList
cellHeight={330}
cols={matches ? 1 : 3}
className={classes.gridList}
spacing={12}
>
{tileData.length > 0 &&
tileData.map((tile, index) => {
return (
<GridListTile
component={Link}
to={"/events/" + tile._id + "/eventcomments"}
key={Math.floor(Math.random() * new Date().getTime())}
>
<img src={tile.eventImage} alt={tile.title} />
<GridListTileBar title={tile.title} />
</GridListTile>
);
})}
</GridList>
</div>
const Gallery = () => {
const classes = useStyles();
return (
<Fragment>
<div style={{background: "black"}}>
<Toolbar/>
<Grid container spacing={0}>
<Grid item xs={12}>
<Paper className={classes.paper}>GALERIA</Paper>
<div className={classes.root}>
<GridList cellHeight={280} className={classes.gridList} cols={1}>
{tileData.map((tile) => (
<GridListTile style={{ width: "100%" }} key={tile.img} cols={tile.cols || 1}>
<img src={tile.img} alt={tile.title} />
</GridListTile>
))}
</GridList>
</div>
</Grid>
</Grid>
</div>
</Fragment>
)
}
export default Gallery;
You can change the type of underlying component rendered by the GridListTime Material-UI component. Just add the component="a" to have it render as an anchor tag. Then you may specify an href prop to pass in your route. See below:
<GridList cellHeight={160} className={classes.gridList} cols={3}>
{tileData.map((tile) => (
<GridListTile key={tile.img} cols={tile.cols || 1} component="a" href="/">
<img src={tile.img} alt={tile.title} />
</GridListTile>
))}
</GridList>

How do I have more complex layouts in `react-admin` "Show" and "Edit" and "Create" screens?

I'm trying to build on react-admin. The base structure is this:
<Show {...props} >
<SimpleShowLayout>
<TextField source="id" />
<TextField source="name" />
</SimpleShowLayout>
</Show>
I'm looking to do something like this:
<Show {...props} >
<div className="row">
<div className="col-sm-6">
<TextField source="id" />
</div>
<div className="col-sm-6">
<TextField source="name" />
</div>
</div>
</Show>
We need to update our documentation about this. We recently decoupled components with logic, that we name XXXController (ListController, CreateController, etc) in the ra-core package and UI components (List, Create, etc) in the ra-ui-materialui package which the controllers.
Think about the react-admin package as a distribution of react-admin with material-ui UI. When heavy customization is needed, you can use the controllers directly.
For now, you'll have to explore the source code to fully understand how to use them.
Here's an example:
import { ShowController, ShowView, SimpleShowLayout, TextField } from 'react-admin';
const UserShow = props => (
<ShowController {...props}>
{controllerProps =>
// You're on your own here
}
</ShowController>
);

Bootstrap-like col-row-grid functionality on React Native?

It will be better if I demonstrate with images.
This is what I am trying to achieve. Assume landscape mode tablet size. Let's say I have X amount of elements in array. I want to map it across the row until it has 3 items in a row, then goes down. With bootstrap, I can do something like col-md-4 three times.
Currently I am using native-base. It has an interesting grid systems, but not exactly what I wanted.
This is what I have right now:
<Grid>
<Row>
{ recipeStore.categories.map((category, index) => {
return (
<Col key={index} style={{height: 300}}>
<Card>
<CardItem>
<Body>
<Text>{ category.name }</Text>
</Body>
</CardItem>
</Card>
</Col>
)
})}
</Row>
</Grid>
How can I get the array iteration to fill out 3 columns then goes to the next row?
You can use flexWrap: 'wrap' on the parent contain and then use flexBasis on the children.
import React, { Component } from 'react';
import { View, StyleSheet } from 'react-native';
const ABox = () => <View style={styles.box} />;
export default class App extends Component {
render() {
return (
<View style={styles.container}>
<ABox />
<ABox />
<ABox />
<ABox />
<ABox />
<ABox />
<ABox />
<ABox />
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
flexWrap: 'wrap',
flexDirection: 'row',
paddingTop: 20,
},
box: {
flexBasis: 75,
borderWidth: 1,
borderColor: 'black',
height: 40,
margin: 10,
}
});
Snack: https://snack.expo.io/HkUFUp7ub
The best way to do this is using the FlatList it is good in performance and easy to use. And it is recommended for making a list (see this). Additionally you don't need to add any extra package to your project.
You can easily use the FlatList as below:
_keyExtractor = (item, index) => index;
_renderItem = ({item: category}) => (
<Card>
<CardItem>
<Body>
<Text>{ category.name }</Text>
</Body>
</CardItem>
</Card>
);
render() {
return (
<FlatList
numColumns={3}
data={recipeStore.categories}
keyExtractor={this._keyExtractor}
renderItem={this._renderItem}
/>
);
}
Note: you should use the numColumns peroperty to define the number of items in each row.
You can see the documentation of FlatList in here.

How is possible to combine InfiniteLoader with WindowScroller?

How can I create an infinite scroll list but in a window scroller? (the same as Facebook timeline - Mock up)?
Below is the code that I have tried, but it does not work as expected. It only displays the first items and after that it does not display anything more.
<div className={styles.WindowScrollerWrapper}>
<InfiniteLoader
isRowLoaded={this._isRowLoaded}
loadMoreRows={this._loadMoreRows}
rowCount={list.size}
threshold={10}>
{({ onRowsRendered, registerChild }) => (
<WindowScroller>
{({ height, isScrolling, scrollTop }) => (
<AutoSizer disableHeight>
{({ width }) => (
<List
ref={registerChild}
className={styles.List}
autoHeight
height={height}
width={width}
onRowsRendered={onRowsRendered}
rowCount={list.size}
rowHeight={30}
rowRenderer={this._rowRenderer}
scrollToIndex={randomScrollToIndex}
/>
)}
</AutoSizer>
)}
</WindowScroller>
)}
</InfiniteLoader>
</div>
Many thanks in advance.
Update
Here is the URL to demo: https://plnkr.co/edit/akyEpZ0cXhfs2jtZgQmN
Based on your Plnkr, here's a corrected Plnkr that shows how it should work. (You were forgetting to pass the scrollTop param from WindowScroller to the child List.)
Here you go:
<InfiniteLoader
isRowLoaded={this._isRowLoaded}
loadMoreRows={this._loadMoreRows}
rowCount={list.size}
threshold={10}>
{({ onRowsRendered, registerChild }) => (
<WindowScroller>
{({ height, isScrolling, scrollTop }) => (
<AutoSizer disableHeight>
{({ width }) => (
<List
ref={registerChild}
className="List"
autoHeight
height={height}
width={width}
onRowsRendered={onRowsRendered}
rowCount={list.size}
rowHeight={30}
rowRenderer={this._rowRenderer}
scrollTop={scrollTop} />
)}
</AutoSizer>
)}
</WindowScroller>
)}
</InfiniteLoader>

Resources