Button Loading react-hooks-nextjs - html

I am doing a loading spinner button, When I press the button, the loading icon appears but does not disappear at the marked time, and continues with the load. Here is the code:
componentOne:
import React from 'react'
function Loader(){
return(
<>
<div className="loaderIcon"></div>
<style jsx>
{`
.loaderIcon{
border: 10px solid #f3f3f3;
border-radius: 50%;
border-top: 10px solid #3498bd;
width: 60px;
height: 60px;
animation: spin 2s linear infinite;
}
#keyframes spin{
0% { transform: rotate(0deg);}
100% { transform rotate(360deg);}
}
`}
</style>
</>
)
}
export default Loader;
componentTwo:
import React, {useState} from 'react';
import Loader from '../components/loader'
function ButtonLoading(){
const [loading, setLoading] = useState(false)
function loadData(){
setLoading ({loading: false})
setTimeout(()=>{
setLoading({loading: true});
}, 1000);
}
return(
<>
<div>
<button className="btnLoading" onClick={loadData} disabled={loading}>
{loading &&(<Loader/>)}
{loading && <span className="oneSpan">Loading page</span>}
{!loading && <span className="twoSpan">Load page</span>}
</button>
</div>
<style jsx>
{`
.btnLoading{
background-color: green;
}
.oneSpan{
color: red;
font-size: 20px;
}
.twoSpan{
color: black;
font-size: 20px;
}
`}
</style>
</>
)
}
export default ButtonLoading;

I think you got the loading backwards. I believe the function should look like `
function loadData(){
setLoading ({loading: true})
setTimeout(()=>{
setLoading({loading: false});
}, 1000);
}`
SetTimeout runs the function AFTER the timer is up

Related

Animate dialog overflows with visible scrolls

I have created a dialog that slides out on closing if on mobile view.
Otherwise it just scales backwards.
This component is based on the awesome work from here:
https://web.dev/building-a-dialog-component/#styling-with-open-props
With some tweeks of my own to adapt it to react and my own needs.
This will add scrollbar on html body for a breef second.
This is super annoying and only fix that works is to hide scrollbar on the html-tag.
html {
box-sizing: border-box;
margin: 0px; padding: 0px
&::-webkit-scrollbar {
display: none;
}
scrollbar-width: none;
}
I do not like this at all, are there any better solutions for this?
import { useEffect, useState, useRef } from "react";
import styled, { keyframes } from "styled-components";
import PropTypes from "prop-types";
import { shadow } from "shared/theme";
import { spacingIndex } from "shared/spacing";
const scaleDown = keyframes`
to { transform: scale(.75) }
`;
const slideOutDown = keyframes`
to { transform: translateY(100%) }
`;
const slideInUp = keyframes`
from { transform: translateY(100%) }
`;
const Container = styled.dialog`
display: ${({ open }) => (open ? "flex" : "none")};
justify-content: center;
align-items: center;
border-radius: 8px;
border: none;
max-inline-size: min(90vw, 60ch);
max-block-size: min(80vh, 100%);
overflow: hidden;
margin: auto;
padding: ${spacingIndex * 2}px;
inset: 0;
box-shadow: ${shadow.overlay};
transition: opacity 0.5s cubic-bezier(0.25, 0, 0.3, 1);
animation: ${scaleDown} 0.5s cubic-bezier(0.25, 0, 0.3, 1) forwards;
animation-timing-function: cubic-bezier(0.5, -0.5, 0.1, 1.5);
&:is([open]) {
animation: ${slideInUp} 0.5s cubic-bezier(0.25, 0, 0.3, 1) forwards;
}
&:not([open]) {
pointer-events: none;
opacity: 0;
}
::backdrop {
backdrop-filter: blur(4px);
transition: backdrop-filter 0.5s ease;
}
#media (max-width: 768px) {
animation: ${slideOutDown} 0.5s cubic-bezier(0.25, 0, 0.3, 1) forwards;
margin-block-end: 16px;
}
`;
const Dialog = ({ isOpened, onClose, children }) => {
const ref = useRef(null);
const [open, setOpen] = useState(false);
const isClickOutsideOfDialog = (dialogEl, event) => {
if (!dialogEl) return false;
const rect = dialogEl.getBoundingClientRect();
return (
event.clientY < rect.top ||
event.clientY > rect.bottom ||
event.clientX < rect.left ||
event.clientX > rect.right
);
};
const onDialogContainerClick = (event) => {
if (isClickOutsideOfDialog(ref.current, event)) onClose();
};
const handleAnimationEnd = (e) => {
if (slideInUp.name !== e.animationName) setOpen(false);
};
useEffect(() => {
ref.current?.addEventListener("animationend", handleAnimationEnd);
return () => ref.current?.removeEventListener("animationend", handleAnimationEnd);
}, []);
useEffect(() => {
if (!isOpened) ref.current?.close();
if (isOpened) {
setOpen(true);
ref.current?.showModal();
}
}, [isOpened]);
return (
<Container
{...{
ref,
open,
onClick: onDialogContainerClick,
}}>
{children}
</Container>
);
};
Dialog.propTypes = {
isOpened: PropTypes.bool,
onClose: PropTypes.func,
};
export default Dialog;
Edit: Guess I was tired and stupid..Just added position:fixed; because I then move the dialog out of the document flow.

Div disappears when window resize

I am trying to make a small animation of two circles for the web, and I want it to be responsive so when I make the window "phone size" everything will still be there.
Each circle is a div, and both of them are inside a div with the class animation container. Everything changes sizes and is shown on the screen except the circles.
I have tried resizing them to see if they were too big, but it hasn't worked. I don't know what to do, help, please. We are using react and some bootstrap, but I think it has to do more with the CSS.
JSX Code
import React, { useEffect, useState } from 'react'
import { Container, Form } from 'react-bootstrap'
import useAuth from './useAuth'
import TrackSearchResult from './TrackSearchResult'
import SpotifyWebApi from 'spotify-web-api-node'
import Player from './Player'
import Navbar from './Navbar'
import './Dashboard.css'
const spotifyApi = new SpotifyWebApi({
clientId: '0d9e02c2d8504b05a8ae26f33b338212',
});
export default function Dashboard({code}) {
const accessToken = useAuth(code)
const [search,setSearch] = useState('')
const [searchResults,setSearchResults] = useState([])
const [playingTrack, setPlayingTrack] = useState()
function chooseTrack(track) {
setPlayingTrack(track)
//we clear the search when we choose a track
setSearch('')
}
useEffect(()=>{
if (!accessToken) return
spotifyApi.setAccessToken(accessToken)
},[accessToken])
useEffect(()=>{
if (!search) return setSearchResults([])
if (!accessToken) return
let cancel = false //so we just search on request at a time
spotifyApi.searchTracks(search)
.then(res => {
if (cancel) return
setSearchResults(res.body.tracks.items.map(track => {
//loop through all images and get the smallest image
const smallestAlbumImage = track.album.images.reduce((smallest, image) => {
if (image.height < smallest.height) {
return image
} else {
return smallest
}
}, track.album.images[0])
return {
artist: track.artists[0].name,
title: track.name,
uri: track.uri,
albumUrl: smallestAlbumImage.url
}
}))
})
return () => cancel = true
},[search, accessToken])
return (
<Container className="d-flex flex-column pt-2 pb-0 px-0" style={{height:"100vh"}}>
{/* <Form.Control
type="search"
placeholder="Search Songs/Artists"
value={search}
onChange={e => setSearch(e.target.value)} //to get the value of search
/> */}
<div className="animation-container align-items-center">
<div
id="shape-1"
class="
rounded-circle
position-absolute
opacity-70
scale-up-center
d-none d-md-block
"
></div>
<div
id="shape-2"
class="
rounded-circle
position-absolute
opacity-70
scale-up-center
fade-delay-2000
d-none d-md-block
"
></div>
</div>
<div className="song-container">
<div className="song">song1</div>
<div className="song">song2</div>
<div className="song">song3</div>
<div className="song">song4</div>
</div>
<div className="flex-grow-1 my-2" style={{overflowY: "auto"}}>
{/* VERY IMPORTANT
Here we use track arrow function with parentesis and not curly brackets
maybe because it returns a component */}
{searchResults.map(track =>(
<TrackSearchResult track={track} key={track.uri} chooseTrack={chooseTrack}/>
))}
</div>
<div><Player accessToken={accessToken} trackUri={playingTrack?.uri}/></div>
<Navbar/>
</Container>
)
}
CSS:
.animation-container {
height: 50vh;
max-width: 20vh;
align-items: center;
justify-content: center;
display: flex;
margin: 0 auto;
}
#shape-1,
#shape-2,
{
min-width: 25vh;
height: 25vh;
mix-blend-mode: multiply;
filter: blur(24px);
}
#shape-1 {
background-color: hsl(225, 100%, 43%);
left: 99vh;
}
#shape-2 {
background-color: hsl(0, 92%, 64%);
right: 99vh;
}
.scale-up-center {
animation: scale-up-center 7s cubic-bezier(0.39, 0.575, 0.565, 1) infinite both;
}
#keyframes scale-up-center {
0% {
transform: translate(0px, 0px) scale(1);
}
33% {
transform: translate(30px, -50px) scale(1.1);
}
66% {
transform: translate(-20px, 20px) scale(0.9);
}
100% {
transform: translate(0px, 0px) scale(1);
}
}
.fade-delay-2000 {
animation-delay: 2s;
}
.fade-delay-4000 {
animation-delay: 4s;
}
.song-container {
padding: 3em;
display: grid;
grid-gap: 2em;
height: auto;
grid-template-columns: repeat(2, 1fr);
grid-template-rows: repeat(2, 1fr);
}
.song {
padding: 2em;
border-radius: .3em;
box-shadow: 10px 10px 30px rgba(0, 0, 0, 0.1);
height: 10vh;
width: auto;
}

How to add CSS animations to a ReactJs component

I'm working on a react component that would render a loading animations.
Here is the js file
import React from 'react'
import './css/CircleSpinner.css'
const CircleSpiner = () => {
return (
<div className="lds-circle" >
</div>
)
}
export default CircleSpiner
The CSS is on a separate file and the component doesn't render the animations as expected. Any help would be appreciated.
Here's the code sandbox for the above.
You need content or draw the circle:
.lds-circle > div {
display: inline-block;
width: 64px;
height: 64px;
margin: 8px;
border-radius: 50%;
border: 1px solid #000;
color: black;
animation: lds-circle 2.4s cubic-bezier(0, 0.2, 0.8, 1) infinite;
}
You are missing border: 1px solid #000; or content so you can view the circle.
Also define the CircleSpiner and use it on your app.js:
CircleSpiner.js
import React from 'react'
const CircleSpiner = () => {
return (
<div>
</div>
)
}
App.js
export default CircleSpiner
import "./styles.css";
import CircleSpiner from "./CircleSpiner"
export default function App() {
return (
<div className="App">
<div className="lds-circle">
<CircleSpiner></CircleSpiner>
</div>
</div>
);
}
Your CSS does not correspond to your html. Specifically, this rule here:
.lds-circle > div {
display: inline-block;
width: 64px;
height: 64px;
margin: 8px;
border-radius: 50%;
color: black;
animation: lds-circle 2.4s cubic-bezier(0, 0.2, 0.8, 1) infinite;
}
The .lds-circle > div selector means "any div which is a direct descendent of .lds-circle". You don't have any descendents of your class so it targets nothing. The second issue is that you are providing a color property which styles foreground elements like text, but you (presumably) want a background-color property to see the background of the div element.
So, putting those two together:
.lds-circle > div {
display: inline-block;
width: 64px;
height: 64px;
margin: 8px;
border-radius: 50%;
background-color: black;
animation: lds-circle 2.4s cubic-bezier(0, 0.2, 0.8, 1) infinite;
}
#keyframes lds-circle {
0%,
100% {
animation-timing-function: cubic-bezier(0.5, 0, 1, 0.5);
}
0% {
transform: rotateY(0deg);
}
50% {
transform: rotateY(1800deg);
animation-timing-function: cubic-bezier(0, 0.5, 0.5, 1);
}
100% {
transform: rotateY(3600deg);
}
}
<div class="lds-circle" >
<div></div>
</div>

Rolling ball when page loads for HTML in flask

I'm new to Flask but I'm trying to show a 'rolling ball' while a page loads.
This link: Display a ‘loading’ message while a time consuming function is executed in Flask has been helpful but not giving me the desired results.
from flask import Flask
from flask import request
from flask import render_template
import time
app = Flask(__name__)
def long_load(typeback):
time.sleep(5) #just simulating the waiting period
return "You typed: %s" % typeback
#app.route('/', methods=("POST", "GET"))
def test():
if request.method == 'GET':
return render_template('index.html')
elif request.method == 'POST':
query = request.form['anything']
outcome = long_load(query)
return render_template("post_template.html" , display=outcome)
if __name__ == '__main__':
app.run()
Excerpts from index.html:
<head>
<style>
div#loader {
border: 16px solid #f3f3f3;
border-radius: 50%;
border-top: 16px solid #3498db;
width: 120px;
height: 120px;
-webkit-animation: spin 2s linear infinite; /* Safari */
animation: spin 2s linear infinite ;
}
/* Safari */
#-webkit-keyframes spin {
0% { -webkit-transform: rotate(0deg); }
100% { -webkit-transform: rotate(360deg); }
}
#keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
</style>
<script type="text/javascript">// <![CDATA[
document.onreadystatechange = function() {
if (document.readyState !== "complete") {
document.querySelector("body").style.visibility = "hidden";
document.querySelector("#loader").style.visibility = "visible";
} else {
document.querySelector("#loader").style.display = "none";
document.querySelector("body").style.visibility = "visible";
}
};
// ]]></script>
</head>
<form action="." method="post">>
<body>
<div class="caption">
<table class="center">
<tr>
<td class="NEONpinktxt"> </td>
<td align = "center"> <input type="submit" name="anything_submit" href="#" value="Search Results" id="loader" > </td>
</tr>
<div id="loader"></div>
</table>
</div>
</body>
</form>
When the page loads or refreshes, the rolling ball shows but when the 'Search Results' is clicked on, nothing happens.
Assuming you can get your server to return a proper document fragment
I would change
query = request.form['anything']
to
query = request.form['search']
and do this (Please note I fixed invalid HTML too)
document.onreadystatechange = function() {
var complete = document.readyState === "complete";
document.querySelector("body").style.visibility = complete ? "visible" : "hidden";
document.getElementById("loader").style.display = complete ? "none" : "block";
}
document.getElementById("myForm").addEventListener("submit", function(e) {
document.getElementById("result").style.display = "none";
document.getElementById("loader").style.display = "block";
e.preventDefault(); // stop submit
let token = new FormData();
token.append('search', document.getElementById('search').value);
fetch(this.action, { // form action
method: this.method, // form method
body: token
})
.then(response => response.text())
.then(fromServer => {
document.getElementById('result').innerHTML = fromServer;
document.getElementById("result").style.display = "block";
document.getElementById("loader").style.display = "none";
});
})
div#loader {
border: 16px solid #f3f3f3;
border-radius: 50%;
border-top: 16px solid #3498db;
width: 120px;
height: 120px;
-webkit-animation: spin 2s linear infinite;
/* Safari */
animation: spin 2s linear infinite;
}
/* Safari */
#-webkit-keyframes spin {
0% {
-webkit-transform: rotate(0deg);
}
100% {
-webkit-transform: rotate(360deg);
}
}
#keyframes spin {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}
<form id="myForm" action="getInformattionFromServer" method="post">
<div class="caption">
<table class="center">
<tr>
<td class="NEONpinktxt"><input type="text" id="search" value="" placeholder="Search something" /> </td>
<td align="center"> <input type="submit" value="Search Results"> </td>
</tr>
</table>
<div id="loader"></div>
<div id="result"></div>
</div>
</form>
As a demo of using the loading circle for long running tasks I cobbled together a simple demo that uses Ajax to send a request to some server - the throbber is activated at the beginning of the request and runs until the long running task completes and a reply is received.
The loader is assigned the class throbber so it will play as the page loads - though it is so quick you hardly notice here.
The css was modified slightly so that the animation can be applied separately.
The original HTML was invalid so that has been corrected.
<?php
if( $_SERVER['REQUEST_METHOD']=='POST' ){
ob_clean();
/* emulate long running tasks as result of XHR query */
sleep(mt_rand(2,5));
/* mickey mouse data for demo only */
$_POST['time']=time();
$_POST['ip']=$_SERVER['REMOTE_ADDR'];
$_POST['date']=date(DATE_ATOM);
exit(json_encode($_POST));
}
?>
<!DOCTYPE html>
<html lang='en'>
<head>
<meta charset='utf-8' />
<title>ball loader</title>
<style>
body{visibility:hidden;}
div#loader {
display:inline-block; /* occupies less vertical space! */
width: 120px;
height: 120px;
}
.throbber{
border: 16px solid #f3f3f3;
border-radius: 50%;
border-top: 16px solid #3498db;
visibility:visible;
-webkit-animation: spin 2s linear infinite; /* Safari */
animation: spin 2s linear infinite ;
}
#-webkit-keyframes spin {
0% { -webkit-transform: rotate(0deg); }
100% { -webkit-transform: rotate(360deg); }
}
#keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
</style>
<script>
const ajax=function(url,params,callback){
let xhr=new XMLHttpRequest();
xhr.onload=function(){
if( this.status==200 && this.readyState==4 )callback( this.response )
};
xhr.onerror=function(e){
alert(e)
};
xhr.open( 'POST', url, true );
xhr.setRequestHeader('Content-Type','application/x-www-form-urlencoded');
xhr.setRequestHeader('X-Requested-With','XMLHttpRequest');
xhr.send( params );
};
document.addEventListener('DOMContentLoaded',function( event ){
const submit=document.forms.search.querySelector('input[type="submit"]');
const input=document.forms.search.querySelector('input[ type="text" ][ name="anything" ]');
const throbber=document.getElementById('loader');
const out=document.querySelector('output');
document.body.style.visibility='visible';
throbber.classList.remove('throbber');
submit.addEventListener('click',function(e){
e.preventDefault();
throbber.classList.add('throbber');
ajax( location.href, 'anything='+input.value, function(r){
throbber.classList.remove('throbber');
out.textContent=r
})
});
});
</script>
</head>
<body>
<form name='search' method='post'>
<div class='caption'>
<table class='center'>
<tr>
<td class='NEONpinktxt'>
<input type='text' name='anything' value='banana' />
</td>
<td align='center'>
<input type='submit' name='anything_submit' value='Search Results' />
</td>
</tr>
</table>
<div id='loader' class='throbber'></div>
</div>
</form>
<output></output>
</body>
</html>

Keyframes with Inline Styles ReactJS

I'm trying to set the keyframes of a pulsate animation in ReactJS. I tried just setting the keyframes inside the inline style but that doesn't work.
My code
const NewRelpyheButton = ({style = {}, open, handleOPenSettings}) => {
var bar = {
color: '#000',
padding: '1em 0',
fontSize: '20px',
textAlign: 'center',
cursor: 'pointer',
position: 'fixed',
bottom: '0',
width: '100%',
zIndex: '10',
animation: 'pulse 1.2s ease-in-out',
animationIterationCount: 'infinite',
}
Object.assign(style, {});
let openModal;
if (open) {
openModal = <Modal><NewRelpyhe/></Modal>
}
return (
<div>
{openModal}
<Bar color='purple' style={bar} onClick={handleOpenSettings}>
create a new relphye site
</Bar></div>
)
}
I'm trying to mimic this in css:
.element {
width: 100%;
height: 100%;
animation: pulse 5s infinite;
}
#keyframes pulse {
0% {
background-color: #001F3F;
}
100% {
background-color: #FF4136;
}
}
html,
body {
height: 100%;
}
If you like to keep all your styling tightly coupled to your components, give Styled Components a go. They have a helper for keyframes
e.g.
import styled, { keyframes } from 'styled-components'
const pulse = keyframes`
from {
background-color: #001F3F;
}
to {
background-color: #FF4136;
}
`
const Bar = styled.div`
color: #000;
padding: 1em 0;
font-size: 20px,
text-align: center;
cursor: pointer;
position: fixed;
bottom: '0',
width: 100%;
z-index: 10;
animation: ${pulse} 1.2s ease-in-out;
animation-iteration-count: infinite;
`
Then use like so:
<Bar>I pulse</Bar>
Here's how we will achieve it without adding any dependency.
{/*Your Js File Code */}
import { StrictMode } from "react";
import ReactDOM from "react-dom";
import React from "react";
import "./test.css";
class Anim extends React.Component {
constructor(props) {
super(props);
this.state = {
animationName: ""
};
}
addStylesheetRules(rules) {
var styleEl = document.createElement("style");
document.head.appendChild(styleEl);
var styleSheet = styleEl.sheet;
styleSheet.insertRule(rules, 0);
}
clickHdl() {
let animationName = `animation${Math.round(Math.random() * 100)}`;
let keyframes = `
#-webkit-keyframes ${animationName} {
10% {-webkit-transform:translate(${Math.random() * 300}px, ${
Math.random() * 300
}px)}
90% {-webkit-transform:translate(${Math.random() * 300}px, ${
Math.random() * 300
}px)}
100% {-webkit-transform:translate(${Math.random() * 300}px, ${
Math.random() * 300
}px)}
}`;
this.addStylesheetRules(keyframes);
this.setState({
animationName: animationName
});
}
render() {
let style = {
animationName: this.state.animationName,
animationTimingFunction: "ease-in-out",
animationDuration: "0.6s",
animationDelay: "0.0s",
animationIterationCount: 1,
animationDirection: "normal",
animationFillMode: "forwards"
};
return (
<div>
<button type="button" onClick={this.clickHdl.bind(this)}>
Animation!
</button>
<div className="box" style={style}></div>
</div>
);
}
}
const rootElement = document.getElementById("root");
ReactDOM.render(
<StrictMode>
<Anim />
</StrictMode>,
rootElement
);
{/*Css Code test.css */}
.box {
width: 30px;
height: 30px;
background: red;
border-radius: 50%;
cursor: pointer;
}
Demo: https://codesandbox.io/s/reverent-sun-qjo91?file=/src/index.js