How To Animate Buttons In A Speed Dial - javascript

I am building my own SpeedDial which is inspired by the Material UI one, but can't currently figure out how I can make the hidden buttons transition smoothly like they do in the example link above when the SpeedDial is hovered. It's not like they are sliding out, but each button appears one after the other smoothly. If you go to the link above, you will see what I mean. Select the "left" direction, since it is most like my example code.
Here is a quick screenshot of what the SpeedDial looks like, just in case you are wondering.
I have a working CodeSandbox where I have the entire Speed Dial built, but it's lacking the animation I am looking for. My initial thought was to not conditionally render the components, and have them always visible, but use visibility: hidden. However, I don't think doing that solves my problem, and won't allow for any transitions to be set.
For sake of completeness, I will include all of the code, but I highly recommend that you just mess around with the CodeSandbox, as it already works.
App.js
import { useState } from "react";
import SpeedDial from "./components/SpeedDial";
import SpeedDialAction from "./components/SpeedDialAction";
import "./styles.css";
const actions = [
{ label: "share", icon: "share " },
{ label: "print", icon: "print" },
{ label: "save", icon: "floppy-o" },
{ label: "copy", icon: "copy" }
];
export default function App() {
const [activeAction, setActiveAction] = useState("");
return (
<div className="App">
<SpeedDial
direction="left"
style={{
position: "absolute",
bottom: 40,
right: 40
}}
>
{actions.map((action) => (
<SpeedDialAction
key={action.label}
direction="left"
setActiveAction={setActiveAction}
hovered={action.label === activeAction}
label={action.label}
icon={action.icon}
/>
))}
</SpeedDial>
</div>
);
}
Basically, the SpeedDial component accepts children, specifically an array that will create several SpeedDialAction components. When the SpeedDial button is hovered, the SpeedDialAction components become visible next to the main SpeedDial button (the blue button). When the mouse is moved out of the SpeedDial, they disappear.
SpeedDial.js
import React, { useState } from "react";
import Icon from "./Icon";
import "./SpeedDial.css";
export default function SpeedDial({ children, direction, style }) {
const [hovered, setHovered] = useState(false);
return (
<div
className={`speed-dial ${direction}`}
onMouseEnter={() => setHovered(true)}
onMouseLeave={() => {
setHovered(false);
}}
onClick={() => {
setHovered(!hovered);
}}
style={style}
>
<button className="main-btn">
<Icon
name="plus"
style={{
transform: hovered ? "rotate(45deg" : "none",
transition: "transform ease .25s"
}}
/>
</button>
<div className={`action-wrapper ${direction}`}>{hovered && children}</div>
</div>
);
}
SpeedDial.defaultProps = {
direction: "right",
onClick: () => {}
};
Notice how in the action-wrapper that this is where we are conditionally rendering the SpeedDialAction components via the children prop. I am aware that conditionally rendering like this works pretty much like display: none, and won't allow me to animate it. That's why I hope you can help!
SpeedDial.css
.speed-dial {
display: inline-flex;
position: relative;
align-items: center;
width: auto;
}
.speed-dial.top {
flex-direction: column-reverse;
align-items: flex-start;
justify-content: center;
}
.speed-dial.bottom {
flex-direction: column;
align-items: flex-start;
justify-content: center;
}
.speed-dial.left {
flex-direction: row-reverse;
}
.speed-dial > .main-btn {
background: rgb(25, 118, 210);
height: 60px;
width: 60px;
border-radius: 50%;
color: white;
font-size: 18px;
cursor: pointer;
box-shadow: rgb(0 0 0 / 20%) 0px 3px 5px -1px,
rgb(0 0 0 / 14%) 0px 6px 10px 0px, rgb(0 0 0 / 12%) 0px 1px 18px 0px;
}
.speed-dial > .action-wrapper {
display: flex;
}
.speed-dial > .action-wrapper.top {
flex-direction: column;
align-items: flex-start;
transform: translateX(7px);
}
.speed-dial > .action-wrapper.bottom {
flex-direction: column;
align-items: flex-start;
transform: translateX(7px);
}
SpeedDialAction.js
import React from "react";
import Icon from "./Icon";
import "./SpeedDialAction.css";
export default function SpeedDialAction({
icon,
label,
hovered,
setActiveAction,
direction,
onClick
}) {
return (
<div className="speed-dial-action" onClick={onClick}>
{hovered && <div className={`label ${direction}`}>{label}</div>}
<button
onMouseEnter={() => setActiveAction(label)}
onMouseLeave={() => setActiveAction("")}
className={direction}
>
<Icon name={icon} />
</button>
</div>
);
}
Nothing crazy going on here, I just added it for sake of thoroughness.
SpeedDialAction.css
.speed-dial-action {
display: flex;
flex-direction: column;
align-items: center;
position: relative;
}
.speed-dial-action button {
box-shadow: rgb(0 0 0 / 20%) 0px 3px 5px -1px,
rgb(0 0 0 / 14%) 0px 6px 10px 0px, rgb(0 0 0 / 12%) 0px 1px 18px 0px;
background: white;
height: 45px;
width: 45px;
border-radius: 50%;
font-size: 20px;
cursor: pointer;
}
.speed-dial-action button:hover {
background: #d8d8d8;
}
.speed-dial-action button.left {
margin-right: 15px;
}
.speed-dial-action button.right {
margin-left: 15px;
}
.speed-dial-action button.bottom {
margin-top: 15px;
}
.speed-dial-action button.top {
margin-bottom: 15px;
}
.speed-dial-action .label {
background: #565656;
color: white;
padding: 5px 8px;
position: absolute;
border-radius: 4px;
text-transform: capitalize;
}
.speed-dial-action .label.left {
top: -40px;
}
.speed-dial-action .label.right {
top: -40px;
}

Related

The way to optimize react and Typescript codes

In this case, I used React + TypeScript and ant-design. The following code works perfectly but I want the codes to be summarized as much as possible. This is about starting a site that has 3 pages. For example, how can I write this part (const { id, title, description, background } = splash;) so that I don't need to define (splashs[index].background , splashs[index].title, splashs[index].description) all the time.
Thank you in advance for your cooperation.
.splash {
height: 100vh;
position: relative;
overflow: hidden;
}
.bg {
background-color: var(--cjp);
}
.BgGradiant {
background: linear-gradient(107.78deg, rgba(80, 21, 100, 0) 1.87%, rgba(80, 21, 100, 0.05) 18.6%, rgba(80, 21, 100, 0.51) 25.79%, #1C3396 99.02%, #1C3396 51.08%);
}
.context{
width: 80%;
}
.content {
text-align: center;
}
.content h1,
.content p {
color: var(--cwh);
}
.backgroundImage>img {
position: absolute;
width: 100%;
height: 100%;
top: 0;
left: 0;
z-index: -1;
-o-object-fit: cover;
object-fit: cover;
}
.logo {
width: 100%;
text-align: center;
}
.btns {
display: flex !important;
align-items: center;
justify-content: space-between;
-webkit-margin-start: auto;
margin-inline-start: auto;
-webkit-margin-end: auto;
margin-inline-end: auto;
-webkit-margin-before: 2rem;
margin-block-start: 7rem;
}
.btns :global(.ant-btn){
background-color: var(--cwh);
border-radius: var(--borderRadius12);
position: relative;
padding: 4px 10px !important;
}
.btns :global(.ant-btn)::after{
content: "";
position: absolute;
width: 125%;
height: 125%;
top: 50%;
left: 50%;
border: 1px solid var(--chb);
border-radius: var(--borderRadius14);
transform: translate(-50%, -50%);
}
.btns :global(.ant-btn > span){
margin-left: 0 !important;
}
.btns :global(.ant-btn > span > svg){
fill: var(--cal);
}
.btnSkip {
background-color: unset;
outline: none;
border: none;
color: var(--cca);
}
.btnLogin{
-webkit-margin-before: 2rem;
margin-block-start: 7rem;
}
.btnLogin :global(.ant-btn){
border-radius: var(--borderRadius10);
background-color: var(--cwh);
color: var(--cjp);
}
.btnLogin :global(.ant-btn > span){
font-family: "Display-Bold";
}
.dots {
position: absolute;
bottom: 17%;
display: flex !important;
align-items: center;
justify-content: center;
left: 50%;
transform: translateX(-50%);
}
.dot {
width: 10px;
height: 10px;
border-radius: 50%;
}
.dotActive {
background-color: var(--cwh);
}
.dotDeActive {
background-color: var(--cca);
}
.dot:not(:last-child) {
-webkit-margin-end: 0.5rem;
margin-inline-end: 0.5rem;
}
.contentInner{
height: 100vh;
display: flex;
flex-direction: column;
align-items: center;
-webkit-padding-before: 2rem;
padding-block-start: 2rem;
-webkit-padding-after: 3rem;
padding-block-end: 3rem;
}
.contentInner1{
justify-content: space-between;
}
.contentInner2{
justify-content: flex-end;
}
import React, { useState } from 'react';
import { useNavigate } from "react-router-dom";
import { Row, Col, Button } from 'antd';
import { ArrowRightOutlined } from '#ant-design/icons';
import Container from '../../Components/UI/Container/Container'
import classes from './Splash.module.css';
import { backgroundSplash1, backgroundSplash2, logoImage } from '../../Assets/index';
const Splash = () => {
let navigate = useNavigate();
const [index, setIndex] = useState<number>(0);
const {
splash,
bg,
BgGradiant,
context,
content,
backgroundImage,
logo,
btns,
btnLogin,
btnSkip,
dots,
dot,
dotActive,
dotDeActive,
contentInner,
contentInner1,
contentInner2,
} = classes
const splashs = [
{
id: 0,
title: 'Page 1 : title 1',
desctiption: '1- Lorem ipsum 1 ',
background: logoImage,
},
{
id: 1,
title: 'Page 2 : title 2',
desctiption: '2- Lorem ipsum 2 ',
background: backgroundSplash1,
},
{
id: 2,
title: 'Page 3 : title3',
desctiption: '3- Lorem ipsum 3',
background: backgroundSplash2,
}
];
const nextBnt = () => {
setIndex(index + 1);
if (index === splashs.length - 1) {
return navigate("/login");
}
}
const skipBtn = () => {
console.log('skip ');
return navigate("/login");
}
const loginBtn = () => {
return navigate("/login");
}
return (
<>
<Row>
<Col xs={24}>
<section
className={`${index === 0 ? bg : BgGradiant} ${splash}`}>
{
splashs.map((splash) => {
const { id, title, desctiption, background } = splash;
console.log(title, "title");
return (
<>
{
index !== 0 && (
<div className={backgroundImage}>
<img src={splashs[index].background} />
</div>
)
}
<Container key={id} className={backgroundImage}>
<div className={`${index === 0 ? contentInner1 : contentInner2} ${contentInner}`}>
{
index === 0 && (
<div className={logo}>
<img src={logoImage} alt="logoImage" />
</div>
)
}
<div className={context}>
<div className={content}>
<h1>{splashs[index].title}</h1>
<p>{splashs[index].desctiption}</p>
</div>
{/* BTNS */}
{
index === splashs.length - 1 ? (
<div className={btnLogin}>
<Button block onClick={loginBtn}>Login</Button>
</div>
) : (
<div className={btns}>
<button className={btnSkip} onClick={skipBtn}>skip</button>
<Button onClick={nextBnt}> <ArrowRightOutlined /></Button>
</div>
)
}
</div>
</div>
</Container>
</>
)
})
}
<div className={dots}>
{
Array.from({ length: 3 }).map((item, idx) => {
return (
<div key={idx} className={`${dot} ${index === idx ? dotActive : dotDeActive}`}></div>
)
})
}
</div>
</section>
</Col>
</Row>
</>
)
}
export default Splash;
Just an advice, your question isn't really well formulated so it's hard to understand what are you trying to accomplish. Try to keep the questions clear and remove any redundant code so the community can better understand it.
If I'm assuming correctly that you don't want to use splash[index] then you should change splash[index].title to title, same for the other props.
Since you already destructured the splash object with const { id, title, description, background } = splash; all these will be available.
Another thing here is, .map method returns the item in the array so I don't see the point in you using the index inside the loop to access the item from the array.

Building a page layout using the FLIP technique

I'm a beginner in programming, and I'm trying to build a page layout using the FLIP technique in React, but I'm stuck, so I'd like to ask for your help. For more information about the FLIP technique, please refer to the reference URL.
Quickly, what I would like to build is a page layout that consists of one main content and multiple sub-contents, as shown in the image below, and that can be repositioned with the main content by clicking on the sub-content.
I found a layout similar to the one I want to create by surfing the net, and I've included it below as a reference URL.
・What I tried.
I thought I could achieve this using hooks, CSS, and the react-flip-toolkit -library-. In fact, the code to swap the position of the two elements was easy to implement.
https://codesandbox.io/s/flip-l4o04?file=/src/index.js
However, I don't know how to write the code to control the behavior when the number of contents is increased.
The react-flip-toolkit uses the flipkey as a key, so if I just add more containers, onclick will fire all the containers in the flipper tag and it won't work.
https://codesandbox.io/s/flip-test-f3pqb?file=/src/index.js
Next, I rewrote some of the code.
const AnimatedSquare = () => {
const [active, setActive] = useState(false);
const toggleActive = () => setActive(prevState => !prevState);
const [active1, setActive1] = useState(false);
const toggleActive1 = () => setActive1(prevState => !prevState);
return (
<div>
<Flipper flipKey={active,active1}>
<div className="box">
<Flipped flipId="square">
<div
className={active ? "square" : "square-active"}
onClick={toggleActive}/>
</Flipped>
<Flipped flipId="square1">
<div
className={active1 ? "square-active-1" : "square1"}
onClick={toggleActive1}/>
</Flipped>
Only the part of the code that was rewritten now behaves independently, but this is not good enough because it cannot be repositioned with the main content.
・Question
What code should I write to make each container behave independently and swap its position with the main content as shown in the reference URL? I would appreciate your help.
~Reference URL~
・About the FLIP technique
・What I want to build like this
I think this is what you want
I just divide active state on squares and accumulate active square classes
If someday the link stops opening, I attach the code below
import React, { useCallback, useState } from "react";
import ReactDOM from "react-dom";
import { Flipper, Flipped } from "react-flip-toolkit";
import "./styles.css";
const AnimatedSquare = () => {
const [active, setActive] = useState({
square1: true,
square2: false,
square3: false,
square4: false,
square5: false
});
const handleActive = useCallback(
(id) => () =>
setActive({
square1: false,
square2: false,
square3: false,
square4: false,
square5: false,
[id]: true
}),
[]
);
return (
<div>
<Flipper flipKey={true}>
<div className="box">
<Flipped flipId="square1">
<div
className={`square1${active.square1 ? " active" : ""}`}
onClick={handleActive("square1")}
></div>
</Flipped>
<Flipped flipId="square2">
<div
className={`square2${active.square2 ? " active" : ""}`}
onClick={handleActive("square2")}
/>
</Flipped>
</div>
<Flipped flipId="square3">
<div
className={`square3${active.square3 ? " active" : ""}`}
onClick={handleActive("square3")}
/>
</Flipped>
<Flipped flipId="square4">
<div
className={`square4${active.square4 ? " active" : ""}`}
onClick={handleActive("square4")}
/>
</Flipped>
<Flipped flipId="square5">
<div
className={`square5${active.square5 ? " active" : ""}`}
onClick={handleActive("square5")}
/>
</Flipped>
</Flipper>
</div>
);
};
ReactDOM.render(<AnimatedSquare />, document.querySelector("#root"));
* {
box-sizing: border-box;
}
body {
justify-content: left;
align-items: left;
min-height: 100vh;
}
.box {
position: relative;
}
.square1 {
transition: all 1s;
margin-top: 10px;
margin-left: 5px;
margin-right: 5px;
width: 17rem;
height: 12rem;
cursor: pointer;
background-image: linear-gradient(45deg, rgb(0, 255, 0), rgb(71, 182, 108));
}
.square2 {
transition: all 1s;
margin-top: 10px;
margin-left: 5px;
margin-right: 5px;
width: 17rem;
height: 12rem;
cursor: pointer;
background-image: linear-gradient(45deg, rgb(255, 0, 255), rgb(182, 71, 158));
}
.square3 {
transition: all 1s;
margin-top: 10px;
margin-left: 5px;
margin-right: 5px;
width: 17rem;
height: 12rem;
cursor: pointer;
background-image: linear-gradient(
45deg,
rgb(113, 206, 234),
rgb(60, 107, 170)
);
}
.square4 {
transition: all 1s;
margin-top: 10px;
margin-left: 5px;
margin-right: 5px;
width: 17rem;
height: 12rem;
cursor: pointer;
background-image: linear-gradient(45deg, rgb(253, 72, 0), rgb(170, 110, 60));
}
.square5 {
transition: all 1s;
margin-top: 10px;
margin-left: 5px;
margin-right: 5px;
width: 17rem;
height: 12rem;
cursor: pointer;
background-image: linear-gradient(45deg, rgb(255, 0, 0), rgb(182, 71, 71));
}
.active {
position: fixed;
top: 0;
transform: translateX(300px);
width: 75rem;
height: 50rem;
cursor: pointer;
}

Styled Component only pass some value to custom component

Hi I'm trying to configure the colour of a button base on it's button type which will be put in a modal. In this case, either "Success" or "Danger":
import React from "react";
import styled from "styled-components";
const ButtonStyled = styled.button`
background-color: ${(props) =>
props.btnType === "Success" ? "green" : "red"};
border: none;
color: white;
outline: none;
cursor: pointer;
font: Regular 400;
padding: 0.5rem 0;
margin: 0.5rem 1rem;
font-weight: bold;
margin-left: 0;
padding-left: 0;
`;
const Button = (props) => {
console.log(props.btnType);
return (
<ButtonStyled btnType={props.btnType} onClick={props.clicked}>
{props.children}
</ButtonStyled>
);
};
export default Button;
Here's where I called the button:
import React from "react";
import Button from "../../UI/Button/Button";
const OrderSummary = (props) => {
return (
<div>
<Button btnType="Danger" clicked={props.cancelPurchase}>
Cancel
</Button>
<Button btnType="Success" clicked={props.continuePurchase}>
Continue
</Button>
</div>
);
};
export default OrderSummary;
Here is where the Order Summary being called:
import React, { useState } from "react";
import Modal from "../../components/UI/Modal/Modal";
import OrderSummary from "../../components/Burger/OrderSummary/OrderSummary";
const INGREDIENT_PRICES = {
salad: 0.5,
cheese: 0.4,
meat: 1.3,
bacon: 0.7,
};
const BurgerBuilder = () => {
const [ingredients, setIngredients] = useState({
salad: 0,
bacon: 0,
cheese: 0,
meat: 0,
});
const [totalPrice, setTotalPrice] = useState(4);
const [purchasing, setPurchasing] = useState(false);
const purchaseCancelHandler = () => {
setPurchasing(false);
};
const purchaseContinueHandler = () => {
alert("continue");
};
return (
<div>
<Modal show={purchasing} modalClosed={purchaseCancelHandler}>
<OrderSummary
continuePurchase={purchaseContinueHandler}
cancelPurchase={purchaseCancelHandler}
ingredients={ingredients}
price={totalPrice.toFixed(2)}
/>
</Modal>
</div>
);
};
export default BurgerBuilder;
Down here is the modal where the Button being applied
import React from "react";
import styled from "styled-components";
import Backdrop from "../Backdrop/Backdrop";
const ModalWrapper = styled.div`
box-shadow: 0 5px 16px rgba(0, 0, 0, 0.2);
background: #fff;
justify-content: center;
align-items: center;
color: #000;
display: grid;
grid-template-columns: 1fr 1fr;
position: fixed;
z-index: 500;
border-radius: 10px;
transition: all 0.3s ease-out;
padding: 16px;
left: 15%;
top: 30%;
#media (min-width: 600px) {
width: 500px;
left: calc(50% - 250px);
}
`;
const ModalContent = styled.div`
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
line-height: 1.8;
color: #141414;
p {
margin-bottom: 1rem;
}
button {
padding: 10px 24px;
background: #141414;
// color: #fff;
border: none;
}
`;
const Modal = (props) => {
return (
<div>
<Backdrop show={props.show} clicked={props.modalClosed} />
<ModalWrapper
style={{
transform: props.show ? "translateY(0)" : "translateY(-100vh)",
opacity: props.show ? "1" : "0",
}}
>
<ModalContent>{props.children}</ModalContent>
</ModalWrapper>
</div>
// <ModalStyled>{props.children}</ModalStyled>
);
};
export default Modal;
As I did some testing, the values in the styled component Modal Content which state the button in that content will be that. Hence it affected the value of the styled component Button Styled. However, if I removed the value of button in Modal Content, the value of the color in ButtonStyled won't be accepted. Does anyone happen to know why?
The button style defined in the ModalContent is overriding the button style you've defined in your Button component.
button {
padding: 10px 24px;
background: #141414; // <-- overrides button background color
border: none;
}
With CSS the most specific selector will be the one that sets the style and I suspect the ModalContent styled component's computed style results with a more specific selector for button elements than the one from ButtonStyled.
What you can do is bump the selector specificity of the ButtonStyled background color.
Pseudoelements, pseudoselectors, and nesting, the last section.
Finally, the ampersand can be used to increase the specificity of
rules on the component; this can be useful if you are dealing with a
mixed styled-components and vanilla CSS environment where there might
be conflicting styles
const ButtonStyled = styled.button`
&& {
background-color: ${(props) =>
props.btnType === "Success" ? "green" : "red"};
}
border: none;
color: white;
outline: none;
cursor: pointer;
font: Regular 400;
padding: 0.5rem 0;
margin: 0.5rem 1rem;
font-weight: bold;
margin-left: 0;
padding-left: 0;
`;

ReactJS: Changing button hover property depending on the text inside the button

I am working on a React application and I am using Redux to store the state. I have the following code:
request.component.jsx:
import React, { Component } from 'react';
import { connect } from 'react-redux';
import Loading from '../loading/loading.component';
import { changeRequestStatus } from '../../redux/requests/requests.actions';
import { RESOLVED, AWAITING_WAIT_STAFF } from '../../redux/requests/requests.status-types'
import './request.styles.scss';
class Request extends Component {
state = { isLoading: false }
render() {
const { _id, table_no, timestamp, description, status } = this.props.request;
const { user, changeRequestStatus } = this.props;
return (
<>
{this.state.isLoading ? <Loading /> : null}
<div className="request-box">
<div className="request-details">
<div>
<h1 style={{ color: status === AWAITING_WAIT_STAFF ? "#28bfa6" : "#f5a953" }}>Table {table_no}, {new Date(timestamp).toLocaleString()}</h1>
<h2>{description}</h2>
</div>
<div className="status-button">
<button
className="request-button"
onClick={async () => {
this.setState({ isLoading: true })
await changeRequestStatus(_id, status === AWAITING_WAIT_STAFF ? user.username : RESOLVED)
this.setState({ isLoading: false })
}} style={{ background: status === AWAITING_WAIT_STAFF ? "linear-gradient(to right, rgba(141,227,227,1) 0%, rgba(114,240,218,1) 100%)" : "linear-gradient(to right, rgba(255,213,94,1) 0%, rgba(246,170,123,1) 100%)" }}>
{status}
</button>
</div>
</div>
</div>
</>
)
}
}
const mapStateToProps = (state) => {
return {
requests: state.requests.requests,
user: state.user.currentUser
}
}
export default connect(mapStateToProps, { changeRequestStatus })(Request);
request.styles.scss:
.request-box {
border: 1px solid #c3c9c8;
height: 200px;
max-width: 100%;
border-radius: 5px;
position: relative;
background-color: white;
font-family: Helvetica;
box-shadow: 0 10px 6px -6px #ededed;
margin: 10px;
}
.request-details {
display: flex;
flex-wrap: wrap;
align-items: center;
justify-content: space-between;
padding: 0 30px;
height: 100%;
h1 {
font-size: 30px;
color: #28bfa6;
text-align: left;
}
h2 {
font-size: 22px;
text-align: left;
}
}
.status-button {
padding-bottom: 25px;
width: 100%;
#media (min-width: 1000px) {
width: auto;
padding-right: 20px;
padding-left: 100px;
}
}
.request-button {
height: 50px;
font-size: 19px;
font-weight: 600;
border: none;
border-radius: 5px;
padding: 10px 25px;
background-size: 150% auto;
background: linear-gradient(to right, rgba(141,227,227,1) 0%, rgba(114,240,218,1) 100%);
cursor: pointer;
&:hover {
background: #2de1c2;
}
}
In my Request component, I am changing the background property of my request-button div depending on the value of the status variable.
However, I would like to change the request-buttton:hover property depending on the value of status variable in my Request component.
I am not sure what the correct syntax would be to achieve this. Any insights are appreciated.
Create different CSS classes for each button color/status. Then use a ternary operator to apply the CSS class to the button when status changes. Check the status via Redux and then apply a CSS className like this.

Style adjustments in a reactJS dropdown Menu

I'm currently building a ReactJS App, and I needed to implement a dropdown menu.
I currently have the menu built, but I need help for two elements :
The style of the menu
The position of the menu
1 - Style of the menu
The menu looks like that :
But when I hoover the menu, there is a gap between the hovering of a link and the link itselfs.
For example :
Here I hoover 'Rename', but styling come under it...
2 - Position of the menu
Simple question : How can I choose myself the position of the menu in my page ?
Neither relative to the parent element nor in a fixed page position ?
Here are my source codes :
VerticalDots.js
import React from "react";
import "./VerticalDots.css";
export default class VerticalDots extends React.Component {
state = {
status: false,
elements: [
"Rename",
"Duplicate",
"Archive",
"Delete Permanently"
]
}
buttonClick = (e, curstat) => {
e.stopPropagation();
this.setState({ status: curstat });
};
displayElements(){
if(this.state.status){
return(
<div className="show-options">
{this.state.elements.map((value, key) => {
return (
<div className="data-row">{value}</div>
);
})}
</div>
);
}
}
render() {
return (
<div className="dropdown-root">
<div className="text-box">
<div className="button" onClick={e => this.buttonClick(e, !this.state.status)}>
<img src={require("../imgs/3dots-vertical.png")} alt="NotFound"/>
</div>
{this.displayElements()}
</div>
</div>
);
}
}
VerticalDots.css
.text-box {
width: 100px;
height: 40px;
position: relative;
text-align: left;
}
.button {
text-align: right;
font-size: 13px;
}
.show-options {
height: 110px;
width: 150px;
border: 1px solid #7A7A7A;
border-radius: 4px;
position: relative;
background: #EBEBEB;
cursor: pointer;
overflow-y: scroll;
overflow-x: hidden;
z-index: 1;
}
.data-row {
height: 20px;
text-align: left;
/* margin: 0px 10px 0px 10px; */
color: #25073C;
border-radius: 4px;
}
.data-row:hover {
background-color: #1464F6;
color: white;
}
.drop-text {
margin-top: 10px;
margin-left: 5px;
position: absolute;
}
.column9:hover{
background-color: red;
}
https://codesandbox.io/s/cool-engelbart-tuz4y
I couldn't recreate your first issue, can you please clarify further on what's happening on your end? It might be that you have other styles included that are causing that issue.
Regarding issue number 2, normal css rules apply to .show-options which is a child of .text-box. I am not sure how you want to position it, but one suggestion would be to use flex, something like:
.text-box {
height: auto;
display: flex;
flex-direction: column
}
I solved the problem, a hidden line-height was linked 3 branches above.
Thank you for your time !

Categories

Resources