How to show loading indicator on top of the form? - javascript

I am trying to display loading indicator on top of the form (to overlay it) but having a hard time to do so...
I want to show loading indicator when trash / minus or plus is clicked.
I am using React.js + MobX. I do have a boolean flag, but what is difficult to achieve to actually place the loading indicator on top of the form (without corrupting the styling...)
Source code of cart:
import React, { useEffect, useContext } from "react";
import { Container, Row, Col } from "react-bootstrap";
import PromotionalCode from "./PromotionalCode";
import { observer } from "mobx-react-lite";
import { RootStoreContext } from "../../app/stores/rootStore";
import CartBody from "./CartBody";
import CartSummary from "./CartSummary";
import BasicSpinner from "../../app/common/spinners/BasicSpinner";
import Section from "../../app/common/Page/Section";
import TextContainer from "../../app/common/Page/TextContainer";
const Cart = () => {
const rootStore = useContext(RootStoreContext);
const {
cart,
total,
discount,
subtotal,
loadingCart,
loadCartOnSignIn,
loadGuestCart,
isChangingQuantity,
} = rootStore.cartStore;
const { isLoggedIn } = rootStore.userStore;
useEffect(() => {
if (isLoggedIn) {
loadCartOnSignIn();
} else {
loadGuestCart();
}
}, [loadCartOnSignIn, loadGuestCart]);
if (loadingCart)
return (
<div className="mt-5">
<BasicSpinner />
</div>
);
return (
<Section className="checkout p-0">
<TextContainer style={{ padding: "2rem 2rem 0 2rem" }}>
<h2 className="font-weight-bold">Cart</h2>
<p>The competition tickets currently added in the cart.</p>
</TextContainer>
<Container className="cart pt-0">
{loadingCart ? (
<BasicSpinner />
) : (
<div style={isChangingQuantity ? { position: "relative" } : {}}>
{cart?.cartItems && cart.cartItems.length !== 0 ? (
<>
<div className="cart__contents">
<div className="cart__contents__heading-container">
<h2>Items in your bag</h2>
</div>
<div className="cart__contents__table-wrapper">
<table className=" table cart__contents__table-wrapper__table">
<thead>
<tr>
<th scope="col">Description</th>
<th scope="col"></th>
<th scope="col"></th>
<th scope="col">Quantity</th>
<th scope="col">Price</th>
</tr>
</thead>
<CartBody />
</table>
</div>
</div>
<Container className="mt-5 ">
<Row className="justify-content-between cart__discount-and-total">
<Col md={5} className="cart__discount-and-total__discount">
{/* <PromotionalCode /> */}
</Col>
<Col className="cart__discount-and-total__total" md={6}>
<CartSummary
total={total}
subtotal={subtotal}
discount={discount.toString()}
/>
</Col>
</Row>
</Container>
</>
) : (
<div>There is no items in the cart yet.</div>
)}
</div>
)}
</Container>
</Section>
);
};
export default observer(Cart);
Desired result is something like this:
Is it even possible?

You can use absolute positioning for overlay and spinner.
Just render them inside your form, on the bottom.
.form {
// use relative on your form
position: relative;
}
.overlay {
position: absolute;
// this will stretch overlay to fill width and height of form
top: 0;
right: 0;
bottom: 0;
left: 0;
background-color: rgba(0, 0, 0, 0.1); // some transparent grey color
}
.spinner {
position: absolute;
// this will place spinner in center of form
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}

Related

I am trying to align movieCards side by side. But they are aligning in a column

This is my movie list component. I tried different attributes in the div section to get the cards in a row. But all the cards are aligned in a column.should i use either flex or grid.If yes how can i use them. Even i referred to many resources for this.But didn't worked for me. What should I do to get the cards as I wish . Please assist me.
import React, { useState } from 'react'
import { useSelector } from 'react-redux'
import MovieCard from './MovieCard'
const MoviesList = (props) => {
const [search, setSearch] = useState('')
const [filterBy, setFilterBy] = useState([])
const [orderBy, setOrderBy] = useState('')
const movies = useSelector((state) => {
console.log(state.movies)
return state.movies
})
const handleChange = (e) => {
const inputValue = e.target.value
setSearch(inputValue)
const filteredValue = movies.filter((movie) => {
return movie.Title.toLowerCase().includes(inputValue)
})
setFilterBy(filteredValue)
console.log(filteredValue)
}
const handleSelectChange = (e) => {
setOrderBy(e.target.value)
}
const show = (movies) => {
switch (orderBy) {
case 'a-z': return [...movies.sort((a, b) => a.Title.localeCompare(b.Title))]
case 'z-a': return [...movies.sort((a, b) => b.Title.localeCompare(a.Title))]
case '1-100': return [...movies.sort((a, b) => a.imdbRating - b.imdbRating)]
case '100-1': return [...movies.sort((a, b) => b.imdbRating - a.imdbRating)]
default: return [...movies]
}
}
return (
<div className='container'>
<div className='d-flex mb-3 '>
<h1 style={{ textAlign: 'center', border: 'solid lightgreen', backgroundColor: 'white' }} >My Movie List</h1>
<form style={{ float: 'right', marginRight: '35px' }}>
<input type="text" placeholder='search by name' value={search} onChange={handleChange} />
<select value={orderBy} onChange={handleSelectChange} >
<option value="">orderBy</option>
<option value="a-z">a-z</option>
<option value="z-a">z-a</option>
<option value="1-100">1-100</option>
<option value="100-1">100-1</option>
</select>
</form>
</div>
<div className=" row pt-2 justify-content-around " style={{ textAlign: 'center', width: '100%' }}>
{
filterBy.length > 0 ? (
filterBy.map((movie) => {
return <MovieCard key={movie.imdbID} Poster={movie.Poster} Title={movie.Title} imdbRating={movie.imdbRating} imdbID={movie.imdbID} />
})
) : (
show(movies).map((movie) => {
return <MovieCard key={movie.imdbID} {...movie} />
})
)
}
</div>
</div >
)
}
export default MoviesList
And this is Movie card component
import React from 'react'
import { useDispatch } from 'react-redux'
import { removeMovie } from '../actions/moviesAction'
const MovieCard = (props) => {
console.log(props)
const { Title, Poster, imdbRating, imdbID } = props
const dispatch = useDispatch()
return (
<div className="card mt-2 p-2 bd-highlight border shadow rounded" style={{ width: '16rem' }}>
<img className="card-img-left pt-2" src={Poster} alt={Title} style={{ width: '200px', height: '200px' }} />
<div className="card-body">
<h4 className="card-title">Name : {Title}</h4>
<h5 className="card-title">Ranking : #{imdbRating}</h5>
<ion-icon name="trash" style={{ color: 'red' }} onClick={() => {
dispatch(removeMovie(imdbID))
}}>
</ion-icon>
</div>
</div>
)}
export default MovieCard
Please help me to resolve this. Thankyou
Try providing display: flex, to the div rendering MovieCard, like this:
<div className=" row pt-2 justify-content-around "
style={{ textAlign: 'center', width: '100%', display: 'flex' }}>
{
filterBy.length > 0 ? (
filterBy.map((movie) => {
return <MovieCard key={movie.imdbID} Poster={movie.Poster} Title={movie.Title} imdbRating={movie.imdbRating} imdbID={movie.imdbID} />
})
) : (
show(movies).map((movie) => {
return <MovieCard key={movie.imdbID} {...movie} />
})
)
}
</div>
You can just use flex here.
add the below code to your parent div (div containing all the cards).
If you have a css file add a class to that div and add the following styles.
display: flex;
flex-wrap: wrap;
justify-content: center;
or if you want to use inline styling do the following:
<div className=" row pt-2 justify-content-around " style={{ textAlign: 'center', width: '100%', display: "flex", flexWrap: "wrap", justifyContent: "center" }}>
{filterBy.length > 0 ? (
filterBy.map((movie) => {
return <MovieCard key={movie.imdbID} Poster={movie.Poster} Title={movie.Title} imdbRating={movie.imdbRating} imdbID={movie.imdbID} />
})
) : (
show(movies).map((movie) => {
return <MovieCard key={movie.imdbID} {...movie} />
}
)
)}
</div>
Try using a grid method something like the code below will allow you control the number of rows and columns you want as well as size them.
**
<h1>Example { grid-template-rows: 40% 50% 50px;}</h1>
<p><strong style="color: red">The shorthand property, grid-template, can replace both </strong>grid-template-rows and grid-template-columns.</p>
<hr>
<p style="font-family: Courier">When using grid-template, the values before the slash will determine the size of each row. The values after the slash determine the size of each column. </p><hr>
<p style="font-family: Courier">In this example there are 3 rows and 3 columns added to the grid.</p><h1>Example {grid-template: 40% 50% 50px/100px 50% 200px;}</h1><br>
<p><strong style="color: red">By using the (fr) unit, we can define the size of columns and rows as a fraction of the grid’s length and width.</strong><br>Using (fr) makes it easier to prevent grid items from overflowing the boundaries of the grid.</p>
<h1>Example {grid-template: 1fr 1fr 1fr / 3fr 50% 1fr;}<br> width: 400px;<br>
**
If you're using Bootstrap then you don't need to use flex nor grid directly. This is the Bootstrap's way to achieve the desired layout:
<div className="row">
{movies.map(item => (
<div className="col">
// then here yo can put your card for the movie
</div>
))}
</div>
This is the Bootstrap doc about it

select only one card reactjs

I am creating 3 cards and I need you to be able to select one of them and it has a div inside when I select it and change the css, the example that I have the selected card is the second one and I would like the same to happen with the others but that only one can be selected at the same time
import React from "react";
import "./styles.css";
export default function App() {
return (
<div className="App">
<div className="card"></div>
<div className="card cardSelect">
<div className="select">select</div>
</div>
<div className="card"></div>
</div>
);
}
.card {
width: 5rem;
height: 5rem;
background: red;
margin: 1rem;
}
.cardSelect {
box-shadow: 2px 4px 30px 0px rgba(0, 0, 0, 0.75);
}
You can achieve this using an orchestrator, or a parent component.
I have created you a simpler example at code sandbox
The active card needs to be controlled from a higher instance. In this case it is the App component it self. But you can create a Cards and Card component to encapsulate it. e.g.
<Cards>
<Card />
<Card />
<Card />
</Cards>
Here is the code of the example in the code sandbox:
import React from "react";
import "./styles.css";
const cardStyle = {
width: "5rem",
height: "5rem",
background: "red",
margin: "1rem"
};
const cardSelect = {
boxShadow: "2px 4px 30px 0px rgba(0, 0, 0, 0.75)"
};
export default function App() {
const [selected, setSelected] = React.useState(0);
return (
<div className="App">
<div
style={{ ...cardStyle, ...(selected === 0 && cardSelect) }}
onClick={() => setSelected(0)}
>
{selected === 0 && <div className="select">select</div>}
</div>
<div
style={{ ...cardStyle, ...(selected === 1 && cardSelect) }}
onClick={() => setSelected(1)}
>
{selected === 1 && <div className="select">select</div>}
</div>
<div
style={{ ...cardStyle, ...(selected === 2 && cardSelect) }}
onClick={() => setSelected(2)}
>
{selected === 2 && <div className="select">select</div>}
</div>
</div>
);
}
What you need to do is to
Add a state in the app level selection somewhere, eg. `const [selection, setSelection] = useState('card-0')
Create a component for cards, e.g.
Render that component three times <CardComponent isSelected={selection === 'card-2'} />`
Inside Cardcomponent, add that class only if it is selected. className={"card " + prop.isSelected ? "cardSelect" : ""}

How to animate a sliding cart with react spring with a toggle button

I have almost got this workign but not quite sure what I am doing wrong. It will slide in when I click the toggle button, but it wont slide out when I click it again, it will just rerun the slide in animation.
Any help would be great
I have the following state and toggle function
const [close, setClose] = useState(false)
const toggleCart = () => {
setClose(!close)
}
following component
<CartItems close={close} location={location} />
import React, { useState } from "react"
import tw, { styled } from "twin.macro"
import { useTransition, animated } from "react-spring"
const CartWrapper = styled.div`
.test {
position: fixed;
top: 0px;
z-index: 5000;
right: 0;
height: 100vh;
background: lightgrey;
padding: 25px;
}
`
export function CartItems({ location, close }) {
const transitions = useTransition(close, null, {
enter: { transform: "translate3d(100%,0,0)" },
leave: { transform: "translate3d(0%,0,0)" },
})
return (
<>
<CartWrapper>
{transitions.map(({ props }) => {
return (
<animated.div className="test" style={props}>
<h2>Shopping Cart</h2>
{cart}
<p>Total: {formattedTotalPrice}</p>
<form onSubmit={handleSubmitCheckout}>
{/* include validation with required or other standard HTML validation rules */}
<input
name="name"
placeholder="Name:"
type="text"
onChange={e => setName(e.target.value)}
/>
<input
name="giftMessage"
placeholder="Gift Message:"
type="text"
onChange={e => setGiftMessage(e.target.value)}
/>
<input type="submit" />
</form>
<button onClick={clearCart}>Remove all items</button>
</animated.div>
)
})}
{/* <button onClick={handleSubmit}>Checkout</button> */}
</CartWrapper>
</>
)
}
In your example there is a second item during the transition, one entering, and one leaving. That's why you see always the entering animation.
If you use a boolean instead of array in the useTransition you have to insert a condition in the render method to prevent the second item. Just like the third example in the useTransition doc. https://www.react-spring.io/docs/hooks/use-transition
transitions.map(({ item, props, key }) => {
return (
item && <animated.div className="test" style={props} key={key}>
Now it basically works, but a slight modification in the useTransition is necessary.
const transitions = useTransition(close, null, {
from: { transform: "translate3d(100%,0,0)" },
enter: { transform: "translate3d(0%,0,0)" },
leave: { transform: "translate3d(100%,0,0)" }
});
I have a working example here: https://codesandbox.io/s/toggle-react-spring-transition-ju2jd

React.js Popup modal for shopping cart

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;
}
}

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

Categories

Resources