Render single component on hover - javascript

I currently have two avatars that are rendered on a page. On hover I want a card to pop up that displays player information.
Currently when hovering on the first avatar both player cards pop up and i only want the card of the avatar that is hovered over.
The second issue is that on hover of the second avatar nothing happens.
I'd like the card of the avatar that is hovered to only pop up.
function Profiles() {
const { hovered, ref } = useHover();
return (
<Container style={{ display: "flex" }}>
<div ref={ref}>
{hovered ? (
<PlayerOne />
) : (
<Avatar
radius="xl"
size="xl"
src={tobias}
style={{ border: "black solid .1rem" }}
sx={(theme) => ({
backgroundColor: theme.colors.yellow[1],
"&:hover": {
backgroundColor: theme.colors.yellow[3],
},
})}
/>
)}
</div>
<div>
{hovered ? (
<PlayerTwo />
) : (
<Avatar
radius="xl"
size="xl"
src={vij}
style={{ border: "black solid .1rem" }}
sx={(theme) => ({
backgroundColor: theme.colors.yellow[1],
"&:hover": {
backgroundColor: theme.colors.yellow[3],
},
})}
/>
)}
</div>
</Container>
);
}
export default Profiles;

this makes sense but just use good old CSS. oh and if using React its className of course, not class
<div class="parent">
<div class="child1">
<PlayerOne />
</div>
<div class="child2">
<Avatar
radius="xl"
size="xl"
src={vij}
style={{ border: "black solid .1rem" }}
sx={(theme) => ({
backgroundColor: theme.colors.yellow[1],
"&:hover": {
backgroundColor: theme.colors.yellow[3],
},
})}
/>
</div>
</div>
<div class="parent">
<div class="child1">
<PlayerTwo />
</div>
<div class="child2">
<Avatar
radius="xl"
size="xl"
src={tobias}
style={{ border: "black solid .1rem" }}
sx={(theme) => ({
backgroundColor: theme.colors.yellow[1],
"&:hover": {
backgroundColor: theme.colors.yellow[3],
},
})}
/>
</div>
</div>
in your CSS
.child1 { display: none; }
.parent:hover > .child1 { display: none; }
.parent:hover > .child2 { display: block; }

Related

Update scroll so that cursor remains with the dragged item when new items are added

I have a list of items in a scrollable div (overflow-y: scroll). When I start dragging (in the real website I am using react-beautiful-dnd) some of the items will expand to show subitems since they are dropdown. This causes the position of the items to shift down and so the item that I was dragging moves downwards but my cursor remains in the same position.
Here's the link to the problem: https://codesandbox.io/s/gracious-einstein-vvsbtp?file=/src/App.js
import { useState } from "react";
export default function App() {
const [show, setShow] = useState(false);
const handleDrag = () => {
setShow(true);
};
const DisplayHello = () => {
return (
<>
{new Array(5).fill(0, 0, 5).map((ele, index) => {
return (
<p style={{ margin: "5px" }} key={index}>
Hello
</p>
);
})}
</>
);
};
return (
<div className="App" style={{ height: "400px" }}>
<div
style={{
display: "flex",
flexDirection: "column",
height: "100%",
border: "1px solid red",
width: "200px",
overflow: "scroll"
}}
>
<DisplayHello />
{show && <DisplayHello />}
<div
style={{ backgroundColor: "dodgerblue" }}
draggable="true"
onDrag={handleDrag}
>
Drag Me
</div>
{show && <DisplayHello />}
<DisplayHello />
</div>
</div>
);
}
What I want is that even if the items expand, the cursor should remain on the draggable item. Is this even possible?

How to create a custom button in react-pdf-viewer?

I'm using react-pdf-viewer in a React application and need to customise the button images to suit the style of the app. I can get the viewer to display the correct button style but the button then does not do anything when it is clicked, nor does it display the tooltip when hovered over. I've tried attaching onClick functions to the various elements of the button but they only fire on rendering and not when clicked.
This is the main code for the viewer; I'm first trying to get the download button to work and will then use the solution for the other buttons.
import { Viewer, Worker } from '#react-pdf-viewer/core';
import { defaultLayoutPlugin } from '#react-pdf-viewer/default-layout';
import { zoomPlugin } from '#react-pdf-viewer/zoom';
import { getFilePlugin } from '#react-pdf-viewer/get-file';
import { DownloadButton } from './Components/Buttons';
import '#react-pdf-viewer/core/lib/styles/index.css';
import '#react-pdf-viewer/default-layout/lib/styles/index.css';
import '#react-pdf-viewer/toolbar/lib/styles/index.css';
const App = () => {
const zoomPluginInstance = zoomPlugin();
const { ZoomPopover } = zoomPluginInstance;
const getFilePluginInstance = getFilePlugin();
const { Download } = getFilePluginInstance;
const renderToolbar = (Toolbar) => (
<Toolbar>
{(slots) => {
const { NumberOfPages, CurrentPageInput, ZoomIn, ZoomOut, Print } = slots;
return (
<div
style={{
alignItems: 'center',
display: 'flex',
justifyContent: 'space-between',
marginRight: 40
}}
>
<div style={{display: 'flex', alignItems: 'center'}}>
<div style={{ padding: '0px 5px' }}>
<CurrentPageInput style={{fontSize: 16}} />
</div>
<div style={{ padding: '0px 5px 3px' }}>
/ <NumberOfPages /> pages
</div>
</div>
<div className="zoomControls" style={{display: 'flex', alignItems: 'center'}}>
<div style={{ padding: '0px 5px' }}>
<ZoomOut />
</div>
<div style={{ padding: '0px 5px', backgroundColor: '#fff', borderRadius: 4 }}>
<ZoomPopover />
</div>
<div style={{ padding: '0px 5px' }}>
<ZoomIn />
</div>
</div>
<div style={{display: 'flex', alignItems: 'center'}}>
<div style={{ padding: '0px 5px', marginLeft: 'auto' }}>
<Print />
</div>
<div style={{ padding: '0px 5px' }}>
<Download>
{() => (
<DownloadButton />
)}
</Download>
</div>
</div>
</div>
);
}}
</Toolbar>
);
const defaultLayoutPluginInstance = defaultLayoutPlugin({
renderToolbar,
sidebarTabs: (defaultTabs) => [defaultTabs[0]],
});
return (
<Worker workerUrl="https://unpkg.com/pdfjs-dist#2.9.359/build/pdf.worker.js">
<div className="pdfContainer">
<Viewer
fileUrl={'https://file-examples-com.github.io/uploads/2017/10/file-example_PDF_1MB.pdf'}
plugins={[defaultLayoutPluginInstance, zoomPluginInstance, getFilePluginInstance]}
/>
</div>
</Worker>
);
};
export default App;
and this is the imported download button
export const DownloadButton = () => (
<button className="downloadButton" aria-label="Download">
<img src={process.env.PUBLIC_URL + '/icons/download.svg'} className="btButton" alt="Download" />
</button>
);
As far as the downloading the file, it seems that you've forgotten to use the props of the download button.
What you need to do is add the props and then pass the onClick handler to the DownloadButton like so:
<Download>
{(props) => (
<DownloadButton onCLick={props.onClick} />
)}
</Download>
Since your DownloadButton is a wrapper around a button you also need to define onClick as a prop there and then pass it down to the HTML button element.
/**
* A button used to download the a PDF.
*
* #param {Object} [props]
* #param {*} [props.onClick] The #react-pdf-viewer/get-file click handler.
*/
export const DownloadButton = (props) => {
return (
/** Pass the download button's `onClick` handler to this button. */
<button
onClick={props.onClick}
className="downloadButton"
aria-label="Download"
>
<img
src={process.env.PUBLIC_URL + "/icons/download.svg"}
className="btButton"
alt="Download"
/>
</button>
);
};
As far as the tooltip goes, it seems that you'll have to implement that yourself unless you use the default DownloadButton provided by `#react-pdf-viewer/get-file.

Scroll to the bottom of a scrollable div in react

I've tried numerous solutions to try scroll to the bottom of a div as at the bottom user feedback can be found on whether their registration form successfully submitted.
var objDiv = document.getElementById("form");
objDiv.scrollTop = objDiv.scrollHeight;
and
window.scrollTo(0,document.body.scrollHeight);
and
window.scrollTo(0,document.querySelector("#form").scrollHeight);
With no success
Here's the react code for the form
return (
<div
className={styles.register_container}
id="register-container"
style={{
height: "100%",
overflow: "scroll",
}}
>
<HomeIcon />
<div className={styles.login_button_container}>
<p>Already have an account? </p>
<button
onClick={() => {
router.push("/login");
}}
>
Log in
</button>
</div>
<div
style={{
padding: "60px 0",
height: "100%",
maxWidth: "300px",
display: "flex",
flexDirection: "column",
alignItems: "center",
}}
id="form"
>
<img
src="/assets/Logo/ROA_logowhite.png"
style={{
height: "100px",
maxWidth: "100px",
}}
onClick={() => {
var objDiv = document.getElementById("form");
objDiv.scrollTop = objDiv.scrollHeight;
}}
/>
{page === 2 && (
<div style={{ width: "100%" }}>
<button className="back-button" onClick={backButtonHandler}>
<FontAwesomeIcon icon={faAngleLeft} />
Back
</button>
</div>
)}
{page === 1 && loginDetails}
{page === 2 && personalDetails}
<div style={{ width: "100%", padding: "5px 0" }}>
<button
className="form-button"
onClick={buttonHandler}
style={{ minHeight: "50px" }}
>
{submitButton}
</button>
</div>
{loading && (
<div
style={{
padding: "5px",
width: "100%",
display: "flex",
justifyContent: "center",
alignItems: "center",
}}
>
<ReactLoading
type="spin"
color="#1dd760"
height={50}
width={50}
id="loader"
/>
</div>
)}
{registrationError && (
<p className="form-submit-error">
Registration error, refresh your page and try again
</p>
)}
{true && (
<div>
<p
style={{ marginBottom: "10px" }}
className="form-submit"
id="submit"
>
Redirecting to login ... You should receive a welcome email, if
not please try registering again or visit the{" "}
<a href="/contact" style={{ color: "#1dd760" }}>
contact
</a>{" "}
page.
</p>
</div>
)}
</div>
</div>
);
I can't directly focus on the submit success message as setState is asynchronous in react. Just need a function that allows me to scroll the bottom of the div when the next/submit button is clicked
The proper way to access/modify DOM elements in React function components is to use the useRef hook.
https://reactjs.org/docs/hooks-reference.html#useref
Here is an example how you can solve your problem:
import * as React from "react";
const Component: React.FunctionComponent = () => {
const ref = React.useRef<HTMLDivElement>(null);
const [success, updateSuccess] = React.useState(false);
const buttonHandler = () => {
updateSuccess(true);
if (ref.current) {
const offsetBottom = ref.current.offsetTop + ref.current.offsetHeight;
window.scrollTo({ top: offsetBottom });
}
};
return (
<div>
...
<button onClick={buttonHandler}>Scroll</button>
{success ? (
<div
ref={ref}
style={{ height: "2000px", display: !success && "none" }}
>
Long Success Message
</div>
) : null}
</div>
);
};
export default Component;

Javascript array slicing based on filter

I'm stuck on writing out logic that will show more comments if user clicks show more comments.
How would i go about performing this logic that filters based on the following: initially there are 2 comments that show(out of 7 total comments). I want to break this down as user keeps clicking on show more. Should show 5 more comments, 3 more comments, 1 more comment, until there is no more comments.
I'm not too sure on what im doing.
CommentList.tsx
import React, { Fragment, useState } from "react";
import Grid from "#material-ui/core/Grid";
import List from "#material-ui/core/List";
import Typography from "#material-ui/core/Typography";
import CommentItem from "./../commentItem/CommentItem";
import moment from "moment";
import OurLink from "../../../common/OurLink";
import OurSecondaryButton from "../../../common/OurSecondaryButton";
import OurModal from "../../../common/OurModal";
const ourStyle = {
backgroundColor: "#FAFAFA",
border: "1px solid #f2f2f2",
borderRadius: "4px",
padding: "15px 20px",
margin: "15px",
};
function CommentList(props: any) {
const [showMore, setShowMore] = useState<Number>(2);
const [openModal, setOpenModal] = useState(false);
const [showLessFlag, setShowLessFlag] = useState<Boolean>(false);
const lastIndexOfComments = props.comments.length - 1;
const startIndex = 0;
console.log(lastIndexOfComments);
const showComments = (e) => {
e.preventDefault();
const subtract = props.comments.length - 2 ;
console.log("testing", subtract);
setShowMore(subtract);
// setShowLessFlag(true);
};
const handleClickOpen = () => {
setOpenModal(true);
};
const handleCloseModal = () => {
setOpenModal(false);
};
.....
const showMoreComments = () => {
return props.comments
.slice(startIndex, showMore)
.sort((a, b) => a.id - b.id)
.map((comment, i) => (
<div key={i}>
<List style={{ paddingBottom: "20px" }}>
<img alt="gravatar" style={{ margin: "-10px 15px" }} src={comment.author.gravatar} width="30" height="30" />
<Typography style={{ display: "inline-block", fontWeight: 700, padding: "5px 0px" }} variant="h6" align="left">
{Object.entries(props.currentUser).length === 0 ? (
<Fragment>
<span style={{ cursor: "pointer", fontSize: "12px", fontWeight: isBold(comment) }} onClick={handleClickOpen}>
{comment.author.username}
</span>
{comment.userId === props.userId && <span style={{ fontSize: "12px" }}> (OP)</span>}
{openModal ? <OurModal open={openModal} handleClose={handleCloseModal} /> : null}
</Fragment>
) : (
<Fragment>
<OurLink
style={{ fontSize: "12px", fontWeight: isBold(comment) }}
to={{
pathname: `/profile/${comment.author.username}`,
}}
title={comment.author.username}
/>
{comment.userId === props.userId && <span style={{ fontSize: "12px" }}> (OP)</span>}
</Fragment>
)}
</Typography>
<div style={ourStyle}>
<CommentItem comment={comment} user={props.user} postId={props.postId} {...props} />
<Typography style={{ fontSize: "12px" }} variant="body1" align="left">
{moment(comment.createdAt).calendar()}
</Typography>
</div>
</List>
</div>
));
};
const ourComments = props.comments;
console.log("before comments", ourComments);
console.log("fsfsfsftestingcomments", ourComments.slice(0, showMore));
return (
<Grid>
<Fragment>
<div style={{ margin: "30px 0px" }}>
<OurSecondaryButton onClick={(e) => showComments(e)} component="span" color="secondary">
View More Comments
</OurSecondaryButton>
</div>
</Fragment>
{showLessFlag === true ? (
// will show most recent comments below
showMoreComments()
) : (
<Fragment>
{/* filter based on first comment */}
{props.comments
.filter((item, i) => item)
.sort((a, b) => b.id - a.id)
.slice(startIndex, showMore)
.map((comment, i) => (
<div key={i}>
<List style={{ paddingBottom: "20px" }}>
<img alt="gravatar" style={{ margin: "-10px 15px" }} src={comment.author.gravatar} width="30" height="30" />
<Typography style={{ display: "inline-block", fontWeight: 700, padding: "5px 0px" }} variant="h6" align="left">
{Object.entries(props.currentUser).length === 0 ? (
<Fragment>
<span style={{ fontSize: "12px", cursor: "pointer", fontWeight: isBold(comment) }} onClick={handleClickOpen}>
{comment.author.username}
{comment.userId === props.userId && <span style={{ fontSize: "12px" }}> (OP)</span>}
</span>
{openModal ? <OurModal open={openModal} handleClose={handleCloseModal} /> : null}
</Fragment>
) : (
<Fragment>
<OurLink
style={{ fontSize: "12px", fontWeight: isBold(comment) }}
to={{
pathname: `/profile/${comment.author.username}`,
}}
title={comment.author.username}
/>
{comment.userId === props.userId && <span style={{ fontSize: "12px" }}> (OP)</span>}
</Fragment>
)}
</Typography>
<div style={ourStyle}>
<CommentItem comment={comment} user={props.user} postId={props.postId} {...props} />
<Typography style={{ fontSize: "12px" }} variant="body1" align="left">
{moment(comment.createdAt).calendar()}
</Typography>
</div>
</List>
</div>
))}
</Fragment>
)}
</Grid>
);
}
// prevents un-necesary re renders
export default React.memo(CommentList);
If you have all comments already fetched, you could simply set a limit and only show the comments with an index lower than the limit.
Check this out: https://stackoverflow.com/a/44071932/10955418

How to split material-ui toolbar into left and right part

How to split material-ui toolbar into left and right part. For example, this is my toolbar
let EnhancedTableToolbar = props => {
const { numSelected, classes ,deletefunc} = props;
return (
<Toolbar
className={classNames(classes.root, {
[classes.highlight]: numSelected > 0,
})}
>
<div className={classes.title}>
{numSelected > 0 ? (
<Typography color="inherit" variant="subtitle1">
{numSelected} selected
</Typography>
) : (
<Typography variant="h6" id="tableTitle">
User List
</Typography>
)}
</div>
<div className={classes.actions}>
{numSelected > 0 ? (
<div >
<div style={{ display: 'inline-block' }}>
<Tooltip title="Delete">
<IconButton aria-label="Delete">
<DeleteIcon onClick={() => { if (window.confirm('Are you sure you wish to delete '+numSelected +' item?')) {deletefunc()} } }>
</DeleteIcon>
</IconButton>
</Tooltip>
</div>
<div style={{ display: 'inline-block' }}>
<Tooltip title="Edit">
<IconButton aria-label="Edit">
<EditIcon>
</EditIcon>
</IconButton>
</Tooltip>
</div>
</div>
) : (
<Tooltip title="Filter list">
<IconButton aria-label="Filter list">
<FilterListIcon />
</IconButton>
</Tooltip>
)}
</div>
</Toolbar>
);
};
I want to show the numSelected in my left side of toolbar and the delete button and edit button at my right side of toolbar. However, my example output show the delete button and edit button just beside the numSelected. Anyone has any solution regarding this issue?
The solution is add
flex: '0 0 auto'
in my actions class and a
<div className={classes.spacer}>
between title class and action class.
This is how I setup spacer, title and action classes.
const toolbarStyles = theme => ({
root: {
paddingRight: theme.spacing.unit,
},
highlight:
theme.palette.type === 'light'
? {
color: theme.palette.secondary.main,
backgroundColor: lighten(theme.palette.secondary.light, 0.85),
}
: {
color: theme.palette.text.primary,
backgroundColor: theme.palette.secondary.dark,
},
spacer: {
flex: '1 1 100%',
},
actions: {
color: theme.palette.text.secondary,
flex: '0 0 auto',
},
title: {
flex: '0 0 auto',
},
});

Categories

Resources