React-virtualized WindowScroller not rendering new rows if we have max-height - reactjs

React virtualized WindowScroller is not rendering new rows if we have max-height to parent element. Below is the codesandbox link where the issue is reproduced.
https://codesandbox.io/s/react-virtualized-windowscroller-not-working-for-max-height-ribjod
import React from "react";
import { render } from "react-dom";
import { WindowScroller, List, AutoSizer } from "react-virtualized";
import "react-virtualized/styles.css";
const list = Array.from({ length: 100 }).map(
(_, index) => `list item ${index + 1}`
);
const rowRenderer = ({ index, style }) => (
<div key={index} style={style}>
{list[index]}
</div>
);
const App = () => {
return (
<div style={{ background: "yellow" }}>
<div style={{ background: "red" }}>Some content</div>
<div
class="windowScrollWrapper"
style={{ "maxHeight": "200px", overflow: "auto" }}
>
<WindowScroller className="windowScroller">
{({ height, onChildScroll, scrollTop }) => (
<AutoSizer className="AutoSizer" disableHeight={true}>
{({ width }) => {
return (
<List
autoHeight
height={height}
rowCount={list.length}
rowHeight={32}
rowRenderer={rowRenderer}
onScroll={onChildScroll}
scrollTop={scrollTop}
width={width}
/>
);
}}
</AutoSizer>
)}
</WindowScroller>
</div>
<div style={{}}>Some other content</div>
</div>
);
};
render(<App />, document.getElementById("root"));

Related

Why is react-beautiful-dnd draggable items all over the place on drag?

I am using react-beautiful-dnd and MUI with Next.js.
Very simple example code however doesn't seem to be working.
Instead it looks like this:
What is wrong with my code here? Or, is it to do with some styling of MUI?
Here's the list component with DragDropContext container:
<DragDropContext
onDragEnd={(result, provided) => {
if (!result.destination) {
return;
}
if (result.destination.index === result.source.index) {
return;
}
const reorderedList = reorder(
quiz!.questions,
result.source.index,
result.destination.index
) as Question[];
setQuiz(prev => ({
...prev!,
questions: reorderedList.map((q: any, i) => ({ ...q, position: i })),
}));
console.log('drag');
}}
>
<Droppable type='q' droppableId={'questions'}>
{droppableProvided => (
<div style={{ display: 'flex', flexDirection: 'column', margin: 0 }} ref={droppableProvided.innerRef} {...droppableProvided.droppableProps}>
{droppableProvided.placeholder}
{orderBy(quiz!.questions, 'position').map((question, index) => (
<Draggable key={question.secondaryId} draggableId={question.secondaryId} index={index}>
{(provided, snapshot) => (
<QuestionCard question={question as any} provided={provided} snapshot={snapshot} />
)}
</Draggable>
))}
</div>
)}
</Droppable>
</DragDropContext>;
And, here's the draggable child:
const QuestionCard: React.FC<{
provided: DraggableProvided,
snapshot: DraggableStateSnapshot,
question: Question;
}> = ({ question, provided, snapshot }) => {
const container = useRef(null);
const component = (
<Card
sx={{ mb: 1, position: 'relative', height: 'auto' }}
ref={provided.innerRef}
{...provided.draggableProps}
{...provided.dragHandleProps}
>
<CardHeader title={question.name} />
{/* <Divider /> */}
</Card>
);
if ((provided.draggableProps.style as any)?.position === 'fixed') {
return (
<Portal container={container.current}>
{component}
</Portal>
);
}
return component;
};

how to change only current component style on hover

I have a requirement where I need to highlight the component on hover
I have two styles defined baseStyle and highlight
<div style={{ background: '#fff' }}>
<div
onMouseEnter={(e) => setToolStyle({ ...baseStyle, ...highlight })}
onMouseLeave={e => setToolStyle(baseStyle)}
style={toolStyle}>
<FontAwesomeIcon icon={faMousePointer} style={{ fontSize: "20px" }} />
</div>
<div
onMouseEnter={(e) => setToolStyle({ ...baseStyle, ...highlight })}
onMouseLeave={e => setToolStyle(baseStyle)}
style={toolStyle}>
<FontAwesomeIcon icon={faMousePointer} style={{ fontSize: "20px" }} />
</div>
</div>
Expected Output:
on hovering any of the component only that component must be highlighted(i.e just need to add highlight css class to it).
but right now all the component is getting highlighted because of toolStyle state. can anyone help me on this by giving some logic.
Here is an example
you can create a small component with the highlight effect
and use it component any number of times and from anywhere
(As I said in comments )
https://codesandbox.io/s/laughing-mclaren-4i8en?file=/src/highlight.js
child.js
import { useState } from "react";
const baseStyle = { color: "black", fontSize: 14 };
const highlight = { color: "red" };
export default function Highlight({ text }) {
const [toolStyle, setToolStyle] = useState(baseStyle);
return (
<div
onMouseEnter={(e) => setToolStyle({ ...baseStyle, ...highlight })}
onMouseLeave={(e) => setToolStyle(baseStyle)}
style={toolStyle}
>
{text}
</div>
);
}
parent.js
import Highlight from "./highlight";
import "./styles.css";
export default function App() {
return (
<div>
<Highlight text="text 1" />
<Highlight text="text 2" />
<Highlight text="text 3" />
</div>
);
}
This is the best way to implement this with code reusability and
less of code duplication
You can do something like this:
export default function App() {
let [hovered, setHovered] = React.useState({ div1: false, div2: false });
return (
<div>
<div
className={hovered.div1 ? 'hovered' : ''}
onMouseEnter={(e) => setHovered((ps) => ({ ...ps, div1: true }))}
onMouseLeave={(e) => setHovered((ps) => ({ ...ps, div1: false }))}
>
Hello
</div>
<div
className={hovered.div2 ? 'hovered' : ''}
onMouseEnter={(e) => setHovered((ps) => ({ ...ps, div2: true }))}
onMouseLeave={(e) => setHovered((ps) => ({ ...ps, div2: false }))}
>
Hello
</div>
</div>
);
}
You can move your styles to CSS file and avoid extra complexity:
App.js
import { FontAwesomeIcon } from "#fortawesome/react-fontawesome";
import { faCoffee } from "#fortawesome/free-solid-svg-icons";
import "./App.css";
function App() {
return (
<div className="wrapper">
<div>
<FontAwesomeIcon icon={faCoffee} />
</div>
<div>
<FontAwesomeIcon icon={faCoffee} />
</div>
</div>
);
}
export default App;
App.css
.wrapper {
background: #fff;
}
.wrapper > div {
color: red;
font-size: 20px;
}
.wrapper > div:hover {
color: green;
cursor: pointer;
}

How to get ref of google-maps-react map and panto to latlng

My objective is to pan google-maps-react map to a latlng position, after getting a latlong from react-places-autocomplete when a user selects an address suggestion.
I am facing difficulty in setting ref of map from a child functional component, so that I can call map.panTo(location) in the parent functional component.
Following is my Google-Maps and PlaceAutoComplete child Component:
import React, { useEffect } from 'react';
import { Map, GoogleApiWrapper, Marker } from 'google-maps-react';
import { FormGroup, Label, Input, Spinner, Container, Row, Col } from 'reactstrap';
import PlacesAutocomplete from 'react-places-autocomplete';
const InputAndMap = React.forwardRef((props, ref) => {
return (
<div>
<PlacesAutocomplete
value={props.address}
onChange={props.handleInputChange}
onSelect={props.handleInputSelect}
>
{({ getInputProps, suggestions, getSuggestionItemProps, loading }) => (
<div>
<FormGroup>
<Label for="exampleSearch">Search Address</Label>
<Input
{...getInputProps({
className: 'location-search-input',
})}
type="search"
name="search"
id="exampleSearch"
placeholder="Enter Store Location"
/>
</FormGroup>
<div className="autocomplete-dropdown-container">
{loading && (
<div>
<Spinner size="sm" color="primary" />
Loading...
</div>
)}
{suggestions.map(suggestion => {
const className = suggestion.active ? 'suggestion-item--active' : 'suggestion-item';
const style = suggestion.active
? { backgroundColor: '#007bff', cursor: 'pointer', color: 'white' }
: { backgroundColor: '#ffffff', cursor: 'pointer' };
return (
<div
{...getSuggestionItemProps(suggestion, {
className,
style,
})}
>
<span>{suggestion.description}</span>
</div>
);
})}
</div>
</div>
)}
</PlacesAutocomplete>
<Row className="mb-3" style={{ width: '100%', height: '200px' }}>
<Col>
<Map
id="google-map"
ref={ref} // <<=== setting ref here
style={{ width: '100%', height: '200px' }}
google={props.google}
zoom={8}
initialCenter={{ lat: 47.444, lng: -122.176 }}
onClick={(t, map, e) => props.updateMarker(e.latLng, map)}
>
{props.markerLatLong && <Marker position={props.markerLatLong} />}
</Map>
</Col>
</Row>
</div>
);
});
export default GoogleApiWrapper({
apiKey: process.env.REACT_APP_GOOGLE_API_KEY,
libraries: ['places'],
})(InputAndMap);
This is my parent component, where I want to call the map panto function.
import React, { useState, useEffect } from 'react';
import { Button, Form, Spinner, Container } from 'reactstrap';
import { Redirect } from 'react-router-dom';
import { geocodeByAddress, getLatLng } from 'react-places-autocomplete';
import firebase from 'firebase/app';
import NavBarMenu from '../components/NavBarMenu';
import InputAndMap from '../components/InputAndMap';
import fire from '../config/fire';
function StoreScreen(props) {
const [isLoading, setIsLoading] = useState(false);
const [markerLatLong, setMarkerLatLong] = useState(null);
const [city, setCity] = useState('');
const [address, setAddress] = useState('');
const [redirect, setRedirect] = useState(false);
const ref = React.createRef();
const handleInputChange = address => {
setAddress(address);
};
const handleInputSelect = address => {
setAddress(address);
geocodeByAddress(address)
.then(results => {
processCity(results);
getLatLng(results[0])
.then(latLng => {
console.log('Success', latLng);
console.log(ref);// ==============> this return {current: null}
// ref.current.panTo(latLng);// ==> So I am unable to call this
})
.catch(error => console.error('Error', error));
})
.catch(error => console.error('Error', error));
};
return (
<div>
<NavBarMenu isShopKeeper />
<Container className="h-100">
<Form onSubmit={handleSubmit}>
<h5 className="text-center">Add Store</h5>
<InputAndMap
ref={ref}
markerLatLong={markerLatLong}
updateMarker={updateMarker}
handleInputChange={handleInputChange}
handleInputSelect={handleInputSelect}
address={address}
/>
{isLoading ? (
<div className="row mx-auto justify-content-center align-items-center flex-column">
<Spinner color="secondary" />
</div>
) : (
<Button
disabled={!markerLatLong || !city || !address}
className="mb-4"
color="primary"
size="lg"
block
>
Add Store
</Button>
)}
</Form>
</Container>
</div>
);
}
export default StoreScreen;
I am also attaching the image for better visualizing my problem.
Map.panTo changes the center of the map to the given LatLng in Maps JavaScript API. Since you are using google-maps-react library, you can use react states as value of the center parameter of this library to change the value of the Map's center everytime the state changes. In my example code below, I use the code from the getting started docs of react-places-autocomplete and incorporated it with a simple google-maps-react code.
Here's how I declare the state of the center which currently have a value:
state = {
center: {
lat: 40.854885,
lng: -88.081807
},
address: ""
};
Here's the handleSelect event from the react-places-autocomplete library where it geocodes the selected place from the autocomplete. Then you can see that I set the state of the center to the latLng of the geocoded address.
handleSelect = address => {
geocodeByAddress(address)
.then(results => getLatLng(results[0]))
.then(latLng => this.setState({ center: latLng }))
.catch(error => console.error("Error", error));
};
Here's how I call the Map component of the google-maps-react library where the value of center parameter is the value of the state named center.
<Map
className="map"
google={this.props.google}
onClick={this.onMapClicked}
center={this.state.center}
style={{ height: "100%", position: "relative", width: "100%" }}
zoom={13}
/>
Here's a complete code snippet and the working code on how I incorporated the 2 libraries you are using to change the center of the map everytime you choose an address from autocomplete:
import React, { Component } from "react";
import { Map, GoogleApiWrapper } from "google-maps-react";
import PlacesAutocomplete, {
geocodeByAddress,
getLatLng
} from "react-places-autocomplete";
export class MapContainer extends Component {
state = {
center: {
lat: 40.854885,
lng: -88.081807
},
address: ""
};
handleChange = address => {
this.setState({ address });
};
handleSelect = address => {
geocodeByAddress(address)
.then(results => getLatLng(results[0]))
.then(latLng => this.setState({ center: latLng }))
.catch(error => console.error("Error", error));
};
render() {
if (!this.props.loaded) return <div>Loading...</div>;
return (
<div>
<PlacesAutocomplete
value={this.state.address}
onChange={this.handleChange}
onSelect={this.handleSelect}
>
{({
getInputProps,
suggestions,
getSuggestionItemProps,
loading
}) => (
<div>
<input
{...getInputProps({
placeholder: "Search Places ...",
className: "location-search-input"
})}
/>
<div className="autocomplete-dropdown-container">
{loading && <div>Loading...</div>}
{suggestions.map(suggestion => {
const className = suggestion.active
? "suggestion-item--active"
: "suggestion-item";
// inline style for demonstration purpose
const style = suggestion.active
? { backgroundColor: "#fafafa", cursor: "pointer" }
: { backgroundColor: "#ffffff", cursor: "pointer" };
return (
<div
{...getSuggestionItemProps(suggestion, {
className,
style
})}
>
<span>{suggestion.description}</span>
</div>
);
})}
</div>
</div>
)}
</PlacesAutocomplete>
<Map
className="map"
google={this.props.google}
center={this.state.center}
style={{ height: "100%", position: "relative", width: "100%" }}
zoom={13}
/>
</div>
);
}
}
export default GoogleApiWrapper({
apiKey: "YOUR_API_KEY"
})(MapContainer);

React InfiniteScroll in a scrollable component on the page

I am trying to build an infinite scroll in a div with a fixed height and a scroll attached to it, so my goal is for the window not to move but a component within to have a scroll and the items within to be added infinatly.
this is what i have so far:
import React, { useState } from "react";
import ReactDOM from "react-dom";
import InfiniteScroll from "react-infinite-scroll-component";
const style = {
height: 18,
border: "1px solid green",
margin: 6,
padding: 8
};
const DoseListCardBody = () => {
const [items, setItems] = useState(Array.from({ length: 20 }));
const fetchMoreData = () => {
setItems(items.concat(Array.from({ length: 10 })));
};
return (
<div style={{ height: "100%", overflowY: "scroll" }}>
<InfiniteScroll
dataLength={items.length}
next={fetchMoreData}
hasMore={items.length < 200}
loader={<h4>Loading...</h4>}
>
{items.map((i, index) => (
<div style={style} key={index}>
div - #{index}
</div>
))}
</InfiniteScroll>
</div>
);
};
ReactDOM.render(
<div style={{ height: "35rem", background: "black" }}>
<div style={{ height: "30rem", background: "white" }}>
<DoseListCardBody />
</div>
</div>,
document.getElementById("root")
);
everything works fine if i change
ReactDOM.render(
<div style={{ height: "35rem", background: "black" }}>
<div style={{ height: "30rem", background: "white" }}>
<DoseListCardBody />
</div>
</div>,
document.getElementById("root")
);
to
ReactDOM.render(
<DoseListCardBody />,
document.getElementById("root")
);
I think this is because it is using the scroll of the window not the component.
How do i get InfiniteScroll to use the parent component or a component with a scroll that I specify.
I appologise for the bad terminology, i dont usualy develop web pages.
ok got it!
one must use scrollableTarget as a prop in the InfiniteScroll and specify the ID of the compnent that has the scrollbar.
example:
const DoseListCardBody = () => {
const [items, setItems] = useState(Array.from({ length: 20 }));
const fetchMoreData = () => {
setItems(items.concat(Array.from({ length: 10 })));
};
return (
<div id="scrollableDiv" style={{ height: "100%", overflowY: "scroll" }}>
<InfiniteScroll
dataLength={items.length}
next={fetchMoreData}
hasMore={items.length < 200}
loader={<h4>Loading...</h4>}
scrollableTarget="scrollableDiv"
>
{items.map((i, index) => (
<div style={style} key={index}>
div - #{index}
</div>
))}
</InfiniteScroll>
</div>
);
};
notice the addition of 'id="scrollableDiv"' and 'scrollableTarget="scrollableDiv"'.

How can I use two transitions with material ui?

What I'm trying to do is use Fade and Slide in the same component.
<Slide in={isValid} timeout={timeout} direction="left">
<Fade in={isValid} timeout={timeout}>
<Foo />
</Fade>
</Slide>
But it doesn't work.
When isValid is true, it slides the component without the fade effect and when it's false, the component just blinks and disappears.
How can I make it work? I don't want to use makeStyle.
The Slide and the Fade components both change the style.transition property, so if they act on the same element they clobber portions of the other's work.
The way to get this to work is for them to act on different elements. Introducing a div between the two transitions gets the desired behavior.
import React from "react";
import { makeStyles } from "#material-ui/core/styles";
import Switch from "#material-ui/core/Switch";
import Paper from "#material-ui/core/Paper";
import Fade from "#material-ui/core/Fade";
import Slide from "#material-ui/core/Slide";
import FormControlLabel from "#material-ui/core/FormControlLabel";
const useStyles = makeStyles(theme => ({
root: {
height: 180
},
container: {
display: "flex"
},
paper: {
margin: theme.spacing(1),
backgroundColor: "lightblue"
},
svg: {
width: 100,
height: 100
},
polygon: {
fill: theme.palette.primary.main,
stroke: theme.palette.divider,
strokeWidth: 1
}
}));
export default function SlideAndFade() {
const classes = useStyles();
const [checked, setChecked] = React.useState(false);
const handleChange = () => {
setChecked(prev => !prev);
};
return (
<div className={classes.root}>
<FormControlLabel
control={<Switch checked={checked} onChange={handleChange} />}
label="Show"
/>
<div className={classes.container}>
<Slide in={checked} timeout={1000}>
<div>
<Fade in={checked} timeout={1000}>
<Paper elevation={4} className={classes.paper}>
<svg className={classes.svg}>
<polygon
points="0,100 50,00, 100,100"
className={classes.polygon}
/>
</svg>
</Paper>
</Fade>
</div>
</Slide>
</div>
</div>
);
}
I realized that if you wrap the transition in a div or other element to make it as a container, it will work.
<Slide in={isValid} timeout={timeout} direction="left">
<div> // adding this div will make it work
<Fade in={isValid} timeout={timeout}>
<Foo />
</Fade>
</div>
</Slide>
And then you can just create your own Fade component that wraps a div.
const MyFade = React.forwardRef(
({ children, in: In, timeout, ...otherProps }, ref) => {
return (
<div ref={ref} {...otherProps}>
<Fade in={In} timeout={timeout}>
{children}
</Fade>
</div>
);
}
);
Thanks to #Ryan Cogswe that also helped in this.

Resources