React.js Popup modal for shopping cart - javascript

I am currently working on a shopping cart and I am trying to figure out how to have the modal appear once I click on the shopping cart icon. I have looked at the documentation for the semantic-ui for modals but it is vague as to how to get the modal to appear when clicking on something. I am using the semantic-ui class="ui modal" for the modal.
I was thinking of putting an onClick on the icon but was still confused as to how to go from there. Currently, I have the icon in another component and the shopping cart in another separate component. I want the items to appear inside of the pop-up modal which should be the shopping cart.
import React from 'react'
import { Icon } from 'semantic-ui-react';
const ShoppingCartIcon = () => {
return(
<Icon.Group size='big' className="shopping_cart_icon">
<Icon link name='shopping cart'/>
<Icon corner='top right'/>
</Icon.Group>
)
}
export default ShoppingCartIcon;
import React from 'react'
import Shirt from './Shirt';
class ShoppingCart extends React.Component {
render() {
const listShirts = this.props.shirts.map(shirt => {
return <Shirt key={shirt.id} {...shirt}/>
})
return(
<div className="ui modal">
<div className="content">
{listShirts}
</div>
</div>
)
}
}
export default ShoppingCart;
Currently, I do not have the functionality for adding items to the cart working yet. I just want to focus on getting the modal to show up once I click on the shopping cart icon

as far as I see, you are not using neither redux nor context api. you are passing props with props drilling.
so this is how you should organize your code step by step.
we render cartIcon component in the header.js. here is a classic header
Header.js
import CartDropdown from "../cart-dropdown/cart-dropdown.component";
class Header extends React.Component {
constructor(props) {
super(props);
state = { hidden: true, cartItems:[]};
}
toggleHidden() {
this.setState(() => ({ hidden: !this.state.hidden }));
}
render() {
return (
<div className="header">
<Link className="logo-container" to="/">
<Logo className="logo" />
</Link>
<div className="options">
<Link className="option" to="/shop">
SHOP
</Link>
<Link to="/contact" className="option">
CONTACT
</Link>
{/* <Link></Link> */}
<CartIcon />
</div>
{hidden ? null : (
<CartDropdown
toggle={this.toggleHidden}
cartItems={this.state.cartItems}
></CartDropdown>
)}
</div>
);
}
}
you said you have not set the addItem functionality yet. as you add items to the cartItems array you will display them in the cart.
now we need to set up the cartDropdown component.
const CartDropdown = ({ cartItems, toggleHidden }) => (
<div className="cart-dropdown">
<div className="cart-items">
{cartItems.length ? (
cartItems.map(item => <CartItem key={item.id} item={item} />)
) : (
<span className="empty-message"> Your cart is empty </span>
)}
</div>
<CustomButton
onClick={() => {
toggleHidden();
}}
>
GO TO CHECKOUT
</CustomButton>
</div>
);
here we need to add css for cartDropdown. I do not how you are dealing with your css. prop-types or scss but here is the css code so you can apply to your project.
css for cartDropdown component
.cart-dropdown {
position: absolute;
width: 240px;
height: 340px;
display: flex;
flex-direction: column;
padding: 20px;
border: 1px solid black;
background-color: white;
top: 80px;
right: 0;
z-index: 5;
.cart-items {
height: 240px;
display: flex;
flex-direction: column;
overflow: scroll;
}
.empty-message {
font-size: 18px;
margin: 50px auto;
}
button {
margin-top: auto;
}
}

Related

I want to change display of a div when clicked -React Styled Component

I am creating a div using styled component. I want to change the visibility of the div on button clicked,
const Category = () => {
const [showCategory, setShowCategory] = useState(false)
useEffect(() => {
setShowCategory(false)
}, [])
return (
<button onClick={() => { setShowCategory(true)}}>
New Category
</button>
<AdminInputStyle>
<form>
<form-group>
<label>Add Category</label>
<input type='text' />
</form-group>
<button>Submit</button>
</form>
</AdminInputStyle>
)
}
Here's the styled component
const AdminInputStyle = styled.div`
display: ${(d) => (d.showCategory ? 'show' : 'hidden')};
`
You can try something like this too, show when you need to show the add category when you press add category
return (
<>
<button
onClick={() => {
setShowCategory(true);
}}
>
New Category
</button>
{showCategory && (
<AdminInputStyle>
<form>
<form-group>
<label>Add Category</label>
<input type="text" />
</form-group>
<button>Submit</button>
</form>
</AdminInputStyle>
)}
</>
);
I have an example, but in the case we will use a Button. Clicking it will alter the visibility.
You must pass a property to the styled component if you want it to be visible based on that prop. In your example, you don't pass a prop to the styled component in this scenario, which is why the component cannot detect if it should be visible or not.
You will need to / can use the css function from the styled-components library. This can help you return styles based on the properties your styled-component will have. In this example, our property that we pass to the button will be called visible.
import React from 'react';
import PropTypes from 'prop-types';
import styled, { css } from 'styled-components/macro';
const StyledButton = styled.button`
border-radius: 3px;
color: white;
background-color: green;
cursor: pointer;
width: 100px;
height: 50px;
${({ visible }) => {
return css`
visibility: ${visible ? 'visible' : 'hidden'};
`;
}}
`;
export default function Button({ children, visible, onClick }) {
return (
<StyledButton visible={visible} onClick={onClick}>
{children}
</StyledButton>
);
}
Button.propTypes = {
children: PropTypes.node,
visible: PropTypes.bool,
onClick: PropTypes.func,
};
You can see that passing the visible prop will enable the button to alter its' styles based on whether that property is true or false. We utilize a function within the component that returns the css function and this will control the visibility css property.
Here is how we utilize the button and pass props to it from another component; in this example just the App.js file:
import React, { useState } from 'react';
import './App.css';
import Button from './components/Button';
function App() {
const [visible, setVisible] = useState(true);
function handleClick() {
setVisible(!visible);
}
return (
<div className="App">
<Button visible={visible} onClick={handleClick}>
Click
</Button>
</div>
);
}
export default App;
FYI: For the css; you don't want display: hidden;. hidden is an invalid value for the display prop. You'd want display: none; if you don't want the element to be in the DOM. visibility: hidden; will add the element to the DOM, but it won't be visible. You can use whichever works best for your case 👍🏿

React hooks - set state of Parent from child via function

guys I am just learning React and I decide to do try to build Hero Database and I encounter a problem.
When I click on any of Heroes, Bio state and Display state updates with data about the hero and modal window pop up with more information about him. But I don't know how to close it. I have Modal as a separate component and when I am trying calling the function (updateDisplay) from child component. which should set State of Display to False it just doesn't work :/
Heroes.js
import React, { useState } from "react";
import Hero from "./Hero";
import styled from "styled-components";
function Heroes(props) {
const [Bio, setBio] = useState([]);
const [Display, setDisplay] = useState(false);
const SingleHeroCont = styled.div`
display: flex;
flex-direction: column;
margin-top: 10px;
padding: 10px;
align-items: center;
`;
const content = props.data.map((Hero) => (
<SingleHeroCont
onClick={() => {
setBio(Hero);
setDisplay(true);
}}
key={Hero.id}
>
<h1>{Hero.name}</h1>
<img src={Hero.images.sm} alt="Sorry guys"></img>
</SingleHeroCont>
));
const updateDisplay = () => {
setDisplay(false);
};
return (
<div className="heroes_cont">
<Hero updateDisplay={updateDisplay} Display={Display} BioData={Bio} />
{content}
</div>
);
}
export default Heroes;
Hero.js
import React from "react";
import styled from "styled-components";
function Hero({ Display, updateDisplay, BioData }) {
const HeroAbsolute = styled.div`
display: ${(props) => (props.showBio ? "block" : "none")};
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
background-color: rgba(0, 255, 255, 0.5);
`;
return (
<HeroAbsolute showBio={Display}>
<div className="hero_bio_cont">
<h1>{BioData.name}</h1>
<button onClick={() => updateDisplay}>Close</button>
<div className="hero_bio_appearance">
<div>
<img></img>
<div>
<h2>{BioData.powerstats?.power}</h2>
<h2></h2>
<h2></h2>
</div>
</div>
</div>
</div>
</HeroAbsolute>
);
}
export default Hero;
<button onClick={() => updateDisplay}>Close</button>
As I see, the above code is not working to call updateDisplay function.
Correct usage would be
<button onClick={() => updateDisplay()}>Close</button>
Or
<button onClick={updateDisplay}>Close</button>
Please refer to the arrow function tutorial for the second case.

How to create virtual scrolling for images using React js

Whats my requirement: i have some images in my external folder and i need to import to component and display it and also have to use Virtual Scroll here i have to display 1 row in div and in that 1 row have to show 5-6 images
What i did : i consumed images using context from external folder and showed images in 1 rows in div and 5-6 images but i am facing issue unable to set it to Virtual scrolling
as i checked react-virtualized & react-window plugin but i am not sure how my data is used in that format
After using the react-tiny-virtual-list images are getting stacked
below is my code
class App extends React.Component{
state={
Response:[],
}
importAll(r) {
return r.keys().map(r);
}
componentWillMount() {
let data = this.importAll(require.context('./imageFolder/', false, /\.(png|jpe?g|svg)$/));
this.setState({ Response:data})
}
render(){
return(
<div className="container" id="imagecontainer">
<div className="viewport">
{this.state.Response.map((image, index) => <img key={index} src={image} alt="info"></img> )} }
</div>
</div>
)
}
.container {
padding: 0% 6%;
height: 400px;
}
.viewport {
height: -webkit-fill-available;
width: 100%;
border: 1px solid black;
overflow: scroll;
}
img {
height: 250px;
width: 150px;
padding: 35px;
}
After implementing React-tiny-list
<div id="container">
<div id="viewport">
<VirtualList
height='400px'
width='100%'
itemCount={this.state.items.length}
itemSize={20} // Also supports variable heights (array or function getter)
style={{padding:'20px'}}
renderItem={({index, style}) =>
<div key={index} style={style}>
<img key={index} src={this.state.items[index]} alt="info"></img>
</div>
}
/>
</div>
</div>
you can also use the https://github.com/bvaughn/react-virtualized plugin in this if you want to display this as table you can choose list or you can choose grid also .For you requirement i recommend using Masonry from 'react-virtualized';
below is the sample for displaying
import React from 'react';
import {
CellMeasurer,
CellMeasurerCache,
createMasonryCellPositioner,
Masonry
} from 'react-virtualized';
import 'react-virtualized/styles.css';
var images = [];
const columnWidth = 250;
const defaultHeight = 260;
const defaultWidth = columnWidth;
const cache = new CellMeasurerCache({
defaultHeight,
defaultWidth,
fixedWidth: true
})
// Our masonry layout will use 3 columns with a 10px gutter between
const cellPositioner = createMasonryCellPositioner({
cellMeasurerCache: cache,
columnCount: 4,
columnWidth,
spacer: 27
})
function cellRenderer ({ index, key, parent, style }) {
const datum = images[index]
const height = columnWidth || defaultHeight ;
return (
<CellMeasurer
cache={cache}
index={index}
key={key}
parent={parent}
>
<div style={style}>
<img
src={datum}
style={{
height: height,
width: columnWidth,
display: "block"
}}
alt="info"
/>
</div>
</CellMeasurer>
)
}
class Grid extends React.Component{
importAll(r) {
return r.keys().map(r);
}
componentWillMount() {
images = this.importAll(require.context('../imageFolder/', false, /\.(png|jpe?g|svg)$/));
}
render(){
return(
<div id="container">
<div id="viewport">
<Masonry
cellCount={images.length}
cellMeasurerCache={cache}
cellPositioner={cellPositioner}
cellRenderer={cellRenderer}
height={400}
width={1320}
/>
</div>
</div>
)
}
}
export default Grid;
I hope this will resolve your issue
If you're having trouble implementing the virtual scroll, note that the order of the imports is important when doing this, so pay heed to this - it could be contributing to your issue. (An aside: There is an npm plugin for implementing a virtual list.)
An overview of the import order for virtual scroll is:
import * as React from 'react';
import Paper from '#material-ui/core/Paper';
import {
Grid,
VirtualTable,
TableHeaderRow,
} [from material ui];
import {
your-components
} from 'your-path';
(above is non-specific, just a rough guide to the order)
You could also use a ScrollView if you are unable to implement a "Virtual scroll".
The following style will give you a horizontal scroll (as opposed to the default vertical), to enable you to display your images in a horizontally-scrollable row
<ScrollView horizontal={true}>
Hope this helps

React.js parent and sibling components: Keep only one nav bar div expanded at a time

I am new to React.js, and I'm learning by converting a website made in vanilla javascript to a React.js application. However, I am having trouble replicating the drop-down menu functionalities on the navigation bar.
Each section is comprised of divs with an onClick that expands its contents vertically to be viewable by changing overflow and height css settings. However, there's a catch: Only one drop-down should be expanded at a time. I was able to do this in the regular JS site, but I would like use parent-child and sibling relationships between components in React to get the same effect in the new rendition.
With an onClick event, one tab of the bar should open and, if any other tabs are expanded, close them.
I tried to use info from the solutions to this question, but it's not in class-component form and deals with inputs rather than divs. In addition, most search results regarding this only concern vanilla js, not React.js.
At first I attempted keeping states in the parent component, but their asynchronous nature made it a struggle to update them at just the right time.
Here I have stripped down the other irrelevant components of the site and provided a replication of my issue.
index.js:
import React from 'react';
import ReactDOM from 'react-dom';
import Tab from './Tab';
import './index.css';
class TabBar extends React.Component {
constructor() {
super();
this.state = {
//currently no states
}
}
render() {
return (
<div className="tab_bar">
<Tab name="Tab 1" />
<Tab name="Tab 2" />
<Tab name="Tab 3" />
</div>
)
}
}
class Main extends React.Component {
render() {
return (
<TabBar />
)
}
}
ReactDOM.render(<Main />, document.getElementById('root'));
Tab.js:
import React from 'react';
class TabHeader extends React.Component {
constructor() {
super();
}
render() {
return (
<div className="tab_header" onClick={this.props.onClick}>
<h3>{this.props.name}</h3>
</div>
)
}
}
class Tab extends React.Component {
constructor(props) {
super(props);
this.state = {
height: "7vh",
overflow: "hidden"
}
this.changeTabHeight = this.changeTabHeight.bind(this);
}
changeTabHeight() {
if (this.state.height === "7vh") {
this.setState({
height: "20vh",
overflow: "unset"
});
} else {
this.setState({
height: "7vh",
overflow: "hidden"
});
}
}
render() {
return (
<div className="tab_container" style={{overflow: this.state.overflow, height: this.state.height}}>
<TabHeader name={this.props.name} onClick={this.changeTabHeight} />
<div className="tab">
<p>This is the tab content.</p>
</div>
</div>
)
}
}
export default Tab;
index.css:
.body, .root {
width: 100%;
height: 100%;
}
.tab_bar {
width: 100%;
height: 10%;
display: flex;
}
.tab_container{
height: 7vh;
margin: 0;
display: inline-block;
background-color: #82d86c;
border-left: 1px solid #99ff7f;
-webkit-transition: height 0.5s;
transition: height 0.5s;
overflow: hidden;
}
.tab_header {
display: flex;
align-items: center;
vertical-align: middle;
height: 7vh;
text-align: center;
background-color: #99ff7f;
}
.tab_header h3 {
margin: 0;
}
.tab {
margin: 0;
padding: 5px;
}
Expected result:
When a tab on the nav bar is clicked, it expands and displays information below it. Clicking it again reverts the tab back to its original height and hides. Clicking another tab while one is currently open will close the old one and expand the new one.
Actual result:
Currently they expand as expected, but they don't have a means of closing each other yet. It needs a structure in the parent to keep track of the drop-downs.
The problem you have currently is that you handle the state directly in Tab component which is a children component inside TabBar.
Below the modular way as you expect.
index.js: We can improve it by creating an array of Tab and iterating through this tab
import React from "react";
import ReactDOM from "react-dom";
import Tab from "./Tab";
import "./index.css";
class TabBar extends React.Component {
constructor() {
super();
this.state = {
selectedTab: null,
};
this.onSelectTab = this.onSelectTab.bind(this);
}
onSelectTab(tabIndex) {
// In case the user clicks again on the tab, unselect it
const selectedTab = this.state.selectedTab === tabIndex ? false : tabIndex;
this.setState({ selectedTab });
}
render() {
return (
<div className="tab_bar">
<Tab
name="Tab 1"
isSelected={this.state.selectedTab === 0}
onClickTab={() => this.onSelectTab(0)}
/>
<Tab
name="Tab 2"
isSelected={this.state.selectedTab === 1}
onClickTab={() => this.onSelectTab(1)}
/>
<Tab
name="Tab 3"
isSelected={this.state.selectedTab === 2}
onClickTab={() => this.onSelectTab(2)}
/>
</div>
);
}
}
class Main extends React.Component {
render() {
return <TabBar />;
}
}
ReactDOM.render(<Main />, document.getElementById("root"));
Tab.js: Keep in mind that it is way better to have functional components, without states.
import React from "react";
class TabHeader extends React.Component {
constructor() {
super();
}
render() {
return (
<div className="tab_header" onClick={this.props.onClickTab}>
<h3>{this.props.name}</h3>
</div>
);
}
}
class Tab extends React.Component {
constructor(props) {
super(props);
}
render() {
// Moved the style in the CSS rather than in function + state
// The !!this.props.isSelected allow to always return false / true response
return (
<div className={ `tab_container is-selected-${!!this.props.isSelected}`}>
<TabHeader name={this.props.name} onClickTab={this.props.onClickTab} />
<div className="tab">
<p>This is the tab content.</p>
</div>
</div>
);
}
}
export default Tab;
index.css: Add these two classes as it is better to use style in css instead of directly inside the component
.is-selected-true {
height: "20vh",
overflow: "unset"
}
.is-selected-false {
height: "7vh",
overflow: "hidden"
}

vertical Menu Ant design height 100%

Good afternoon. I'm new to web development and I'm not able to put the ant design menu (https://ant.design/components/menu/) at 100% of the screen height.
I tried to put <Layout style = {{height:" 100vh "}}> before the but it did not work.
Here's the code I'm using
.
.
.
.
.
import React from 'react'
import { Layout, Menu, Breadcrumb, Icon, Button } from 'antd';
const { Header, Content, Footer, Sider } = Layout;
const SubMenu = Menu.SubMenu;
export class SideMenu extends React.Component {
constructor(props){
super(props)
this.state = {collapsed: false}
}
toggleCollapsed (){
this.setState({
collapsed: !this.state.collapsed
})
}
render() {
return (
<div style={{ width: 256 }}>
<Menu
defaultSelectedKeys={[]}
defaultOpenKeys={[]}
mode="inline"
theme="light"
inlineCollapsed={this.state.collapsed}
>
<Menu.Item key="0">
<div onClick={this.toggleCollapsed.bind(this)}>
<Icon type={this.state.collapsed ? 'menu-unfold' : 'menufold'}/>
</div>
</Menu.Item>
...
<Menu.Item key="5">
<Icon type="rocket" />
<span>Funções</span>
</Menu.Item>
</Menu>
</div>
);
}
}
Thanks for your help.
Try to manage separate style sheet(in my case menu.less) and put it this code
it should be work
.menu-style {
height: 100vh;
width: 200px;
position: fixed;
}
just try it.
You can use ref get the DOM you wanted reference height. Then use the height at some Components.
function refIt(theDOM) {
const height = theDOM.clientHeight;
this.setState({ height });
}
return (<div ref={theDOM => refIt(theDOM)}>
<div height={this.state.height}>
</div>
</div>);

Categories

Resources