Can't reload a progress bar in React after it reached 100% - javascript

i'm building an app where i fetch data from an api and i'm trying to reload those datas automatically after minute along with a progress bar. i made a progress bar that is filling the width withoin a minute. I can't get to work the reloading after i reached the 100% automatically. this is the code for the peogress bar component:
import React, {useState, useEffect} from "react";
import styles from "./ProgressBar.module.css";
const Progress = ({reloading}) => {
const [style, setStyle] = useState({});
setInterval(() => {
const newStyle = {
opacity: 1,
width: `${reloading}%`
}
setStyle(newStyle);
}, 100);
return (
<div className={styles.progress}>
<div className={styles.progressBar} style={style}>
</div>
</div>
)
}
export default Progress;
And this is the css:
* {
box-sizing: border-box;
}
.progress {
background-color: #d8d8d8;
border-radius: 20px;
position: relative;
margin: 15px 0;
height: 30px;
width: 100%
}
.progressBar {
background: linear-gradient(to left,#dd9214, #ecc480);
box-shadow: 0 3px 3px -5px #F2709C, 0 2px 5px #F2709C;
border-radius: 20px;
color: #fff;
display: flex;
align-items: center;
justify-content: center;
height: 100%;
width: 0;
opacity: 0;
transition: 5s;
}
with this code my bar is filling to 100% within a minute, but i can't get how to refresh every time. Shall i use Js methods or i can achieve through css?

Related

On mobile phone images sometimes doesnt load

Problem
In my app I have card with projects and sometimes images of those projects doesn't load on mobile i have to reload browser and to it is loaded correctly I have no idea how.I must also point out that on the computer viewer everything works correctly.
Stack
I`m using Astro.js with Preact.
Here is demonstration video of my problem
https://streamable.com/okquzk
My code
The way i use it. In Astro framework you can create collections with you mdx or md data here i pass route to imgs like this:
Note: in Astro you dont have to write public/something it ok to write
/projects/bhn.webp
---
title: "Black Hat News"
heroImage: /projects/bhn.webp
___
After some step I am using it inside Card.tsx
import "./styles/card.css";
type Props = {
title: string;
heroImage: string;
slug: string;
};
export const Card = ({ title, heroImage, slug }: Props) => (
<a href={slug}>
<div class="card">
<img src={heroImage} alt={title} />
<div class="info">
<h1>{title}</h1>
</div>
</div>
</a>
);
and there is css
/* Set padding top to make a trick with aspect ratio 16:9 */
.card {
padding: 1rem;
width: 100%;
padding-top: 56.25%;
position: relative;
display: flex;
align-items: flex-end;
transition: 0.4s ease-out;
box-shadow: 0px 7px 10px rgba(0, 0, 0, 0.5);
cursor: pointer;
}
.card:before {
content: "";
position: absolute;
top: 0;
left: 0;
display: block;
width: 100%;
height: 100%;
/* background: rgba(0, 0, 0, 0.6); */
z-index: 2;
}
.card img {
width: 100%;
height: 100%;
-o-object-fit: cover;
object-fit: cover;
object-position: center top;
position: absolute;
top: 0;
left: 0;
}
.card .info {
position: relative;
z-index: 3;
color: var(--color-text);
background: var(--color-dark);
padding: 0.4rem;
border-radius: 5px;
}
.card .info h1 {
font-size: var(--font-lg);
}
/* for desktop add nice effects */
#media (min-width: 1024px) {
.card:hover {
transform: translateY(10px);
}
.card:hover:before {
opacity: 1;
background: rgba(0, 0, 0, 0.6);
}
.card:hover .info {
opacity: 1;
transform: translateY(0px);
}
.card:before {
transition: 0.3s;
opacity: 0;
}
.card .info {
opacity: 0;
transform: translateY(30px);
transition: 0.3s;
}
.card .info h1 {
margin: 0px;
}
}
Edit
Added usage
import "./styles/projectsGrid.css";
import { useStore } from "#nanostores/preact";
import { getProjectsByTag } from "#utils/*";
import type { CollectionEntry } from "astro:content";
import { tagValue } from "src/tagStore";
import { Card } from "./Card";
type Props = {
projects: CollectionEntry<"projects">[];
};
export const ProjectsGrid = ({ projects }: Props) => {
const $tagValue = useStore(tagValue);
const filteredProjects = getProjectsByTag(projects, $tagValue);
return (
<div class="projects_wrapper">
{filteredProjects.map(({ data: { title, heroImage }, slug }) => (
<Card title={title} heroImage={heroImage} slug={slug} />
))}
</div>
);
};

Why does a bit of my background gradient show at certain screen sizes?

I can't figure out why I'm getting this little bit of green when the window is an odd number of pixels wide. I think it has something to do with sub-pixel rendering, but I'm just not sure where the green is coming from. It's just the 2nd div too which is weird.
I have some script that is animating the BG of this div. I'm sure this is part of the issue, but I can't figure out why it's only happening to my 2nd div.
I tried to manually set the width of this div, but I was hoping it would be responsive and scale with the window size.
let currentStage = 1
function performAction(selectedStage) {
currentStage = selectedStage
let stages = document.body.getElementsByClassName('stage-flow-item')
let stageLines = document.body.getElementsByClassName('stage-flow-line')
console.log("selectedStage: " + selectedStage)
for (let stage of stages) {
if (stage.id > currentStage) {
stage.classList.remove('completed')
stage.classList.add('active')
} else {
stage.classList.remove('active')
stage.classList.add('completed')
}
}
for (let stageLine of stageLines) {
if (stageLine.id > currentStage) {
stageLine.classList.remove('lineCompleted')
stageLine.classList.add('lineActive')
} else {
stageLine.classList.remove('lineActive')
stageLine.classList.add('lineCompleted')
}
}
}
.stage-flow-container {
display: flex;
justify-content: space-between;
align-items: center;
height: 70px;
padding: 0 30px;
}
.stage-flow-item {
width: 70px;
height: 70px;
min-width: 70px;
border-radius: 50%;
background-color: #ddd;
display: flex;
justify-content: center;
align-items: center;
font-size: 18px;
color: #fff;
cursor: pointer;
}
.stage-flow-item.active {
background-color: #ddd;
}
.stage-flow-item.completed {
background-color: #6ab04c;
}
.stage-flow-line {
width: calc(100vw);
height: 6px;
background-color: #ddd;
/* default color */
background: linear-gradient(to left, #ddd 50%, #6ab04c 50%) right;
position: relative;
background-size: 200%;
transition: .5s ease-out;
}
.stage-flow-line.lineCompleted {
background-position: left;
background-color: #6ab04c;
}
.stage-flow-line.lineActive {
background-position: right;
background-color: #ddd;
}
<div class="stage-flow-container">
<div id=1 class="stage-flow-item" onclick="performAction(1)">1</div>
<div id=1 class="stage-flow-line"></div>
<div id=2 class="stage-flow-item" onclick="performAction(2)">2</div>
<div id=2 class="stage-flow-line"></div>
<div id=3 class="stage-flow-item" onclick="performAction(3)">3</div>
</div>
I'm not sure if this is on the right track, but I'd eliminate the odd 100vw width on the connectors and instead make them flex. I'd then remove the 200% background size multiplier. By setting the gradient points to 100% the problem is gone. I really don't know if this covers your use case, though.
I converted from background gradient to a pseudo-element solution for the color transition. I think it's simpler. You'd probably have to use CSS animations (as opposed to simple transitions) to make it work otherwise. Of course, you could apply the same principle to the stage items as well, implementing a delay to crate a consistent animation across the item and the line.
Note that duplicated ID values are invalid in HTML. They must be unique. I've refactored to use data attributes instead and an event listener instead of inline JavaScript.
const stageEls = document.querySelectorAll('.stage-flow-item')
const lineEls = document.querySelectorAll('.stage-flow-line')
let currentStage = 1
stageEls.forEach(el => {
el.addEventListener('click', () => {
performAction(el.dataset.stage)
})
})
function performAction(selectedStage) {
currentStage = selectedStage
for (let el of stageEls) {
if (el.dataset.stage > currentStage) {
el.classList.remove('completed')
el.classList.add('active')
} else {
el.classList.remove('active')
el.classList.add('completed')
}
}
for (let el of lineEls) {
if (el.dataset.stage > currentStage) {
el.classList.remove('lineCompleted')
el.classList.add('lineActive')
} else {
el.classList.remove('lineActive')
el.classList.add('lineCompleted')
}
}
}
.stage-flow-container {
display: flex;
align-items: center;
height: 70px;
padding: 0 30px;
}
.stage-flow-item {
width: 70px;
height: 70px;
min-width: 70px;
border-radius: 50%;
background-color: #ddd;
display: flex;
justify-content: center;
align-items: center;
font-size: 18px;
color: #fff;
cursor: pointer;
}
.stage-flow-item.active {
background-color: #ddd;
}
.stage-flow-item.completed {
background-color: #6ab04c;
}
.stage-flow-line {
flex: 1;
height: 6px;
background: #ddd;
position: relative;
}
.stage-flow-line::after {
position: absolute;
content: '';
top: 0;
left: 0;
width: 0;
height: 100%;
background: #6ab04c;
transition: all 0.5s ease-out;
}
.stage-flow-line.lineCompleted::after {
width: 100%;
}
<div class="stage-flow-container">
<div data-stage=1 class="stage-flow-item">1</div>
<div data-stage=1 class="stage-flow-line"></div>
<div data-stage=2 class="stage-flow-item">2</div>
<div data-stage=2 class="stage-flow-line"></div>
<div data-stage=3 class="stage-flow-item">3</div>
</div>

SVG styles behaving unexpectedly, not respecting parent containers' height?

I'm trying to create a TextField component that takes an optional left adornment component, which renders a container with an SVG icon within it, where the SVG icon is centered within the parent.
The problem here is that the parent of the SVG element is, for some reason, setting an (arbitrary?) height for itself. Not sure why this is happening, maybe it's got something to do with the SVG props themselves.
If I set the Container height to 3rem, for example, then the adornments' parent containers height does change, but the SVG itself keeps extending outside of the bounds.
Here's how it looks like now:
And here is how I'd like it to look like:
Here's the code:
// TextField
import * as React from "react";
import clsx from "clsx";
import styled from "#emotion/styled";
const Container = styled.div`
--input-color: #99a3ba;
--input-border: #cdd9ed;
--input-border-error: #a30000;
--input-background: #fff;
--input-placeholder: #cbd1dc;
--input-border-focus: #275efe;
--group-color: var(--input-color);
--group-border: var(--input-border);
--group-background: #eef4ff;
--group-color-focus: #fff;
--group-border-focus: var(--input-border-focus);
--group-background-focus: #678efe;
position: relative;
display: flex;
width: 100%;
& > .adornment_container,
.form-field {
white-space: nowrap;
display: block;
&:not(:first-child):not(:last-child) {
border-radius: 0;
}
&:first-child {
border-radius: 0.5rem 0 0 0.5rem;
}
&:last-child {
border-radius: 0 0.5rem 0.5rem 0;
}
&:not(:first-child) {
margin-left: 1px;
}
}
.form-field {
position: relative;
z-index: 1;
flex: 1 1 auto;
width: 1%;
margin-top: 0;
margin-bottom: 0;
}
& > .adornment_container {
width: 5rem;
height: 100%;
text-align: center;
color: var(--group-color);
background: var(--group-background);
border: 1px solid var(--group-border);
transition: background 0.3s ease, border 0.3s ease, color 0.3s ease;
}
&:focus-within {
& > .adornment_container {
color: var(--group-color-focus);
background: var(--group-background-focus);
border-color: var(--group-border-focus);
path {
fill: #ffffff;
}
}
}
`;
const Input = styled.input`
display: block;
width: 100%;
height: 100%;
padding: 0.5rem 1rem;
font-weight: 500;
font-family: inherit;
border-radius: 6px;
-webkit-appearance: none;
color: var(--input-color);
border: 1px solid var(--input-border);
background: var(--input-background);
transition: border 0.3s ease;
&::placeholder {
color: var(--input-placeholder);
}
&:focus {
outline: none;
border-color: var(--input-border-focus);
}
`;
const AdornmentContent = styled.div`
height: 100%;
width: 100%;
`;
type TextFieldProps = {
adornmentContent?: React.ReactNode;
value: string;
disabled?: boolean;
} & Omit<React.HTMLAttributes<HTMLInputElement>, "name">;
const TextField = React.memo(
({
className,
adornmentContent = null,
disabled,
...props
}: TextFieldProps) => {
const adornment = adornmentContent ? (
<AdornmentContent className="adornment_container">
{adornmentContent}
</AdornmentContent>
) : null;
return (
<Container>
{adornment}
<Input className={clsx("form-field", className)} {...props} />
</Container>
);
}
);
export default TextField;
// EmailIcon
import * as React from "react";
const EmailIcon = React.memo(() => {
return (
<svg
aria-hidden="true"
viewBox="0 0 100% 100%"
preserveAspectRatio="xMidYMid meet"
style={{
width: "100%",
height: "100%"
}}
>
<path
d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10h5v-2h-5c-4.34 0-8-3.66-8-8s3.66-8 8-8 8 3.66 8 8v1.43c0 .79-.71 1.57-1.5 1.57s-1.5-.78-1.5-1.57V12c0-2.76-2.24-5-5-5s-5 2.24-5 5 2.24 5 5 5c1.38 0 2.64-.56 3.54-1.47.65.89 1.77 1.47 2.96 1.47 1.97 0 3.5-1.6 3.5-3.57V12c0-5.52-4.48-10-10-10zm0 13c-1.66 0-3-1.34-3-3s1.34-3 3-3 3 1.34 3 3-1.34 3-3 3z"
fill={"#232323"}
/>
</svg>
);
});
export default EmailIcon;
What am I doing wrong here? Here is how I would like it to look like:

Invert two React component text colors when clicking on input tag

I am new to React and did not find any solution to this simple problem.
I want to revert LangText colors when the toggle button is clicked - initially ENG is white and EST is black and every time input is clicked the colors invert.
I have separate files for returning and styling components.
I tried to change the color of a variable (located on top of the style-components file) inside the styled component tag, so with CSS - inside Input pseudo-class &:checked + span. Looked surreal and didn't work in any way.
I would be very thankful for an example of how to change two colors at the same, time in this case. There are always too few examples with components, usually, CSS is used separately, but for me this method is more readable and logical.
import React from 'react'
import { Input, InputWrapper, Slider, ToggleContainer, LangText} from './LangugageToggleElements';
const LanguageToggle = ({onChange}) => {
return(
<ToggleContainer>
<InputWrapper>
<Input type="checkbox" onChange={onChange}/>
<Slider>
<LangText>ENG</LangText>
<LangText>EST</LangText>
</Slider>
</InputWrapper>
</ToggleContainer>
)} ;
export default LanguageToggle
import styled from "styled-components";
let toggleBackground = "#000";
let textColor = "#fff";
export const ToggleContainer = styled.div`
position: absolute;
top: 10px;
right: 20px;
`
export const InputWrapper = styled.label`
position: relative;
display: flex;
justify-content: center;
text-align: center;
`
export const Input = styled.input`
position: absolute;
left: -9999px;
top: -9999px;
&:checked + span {
&:before {
left: 52px;
}
}
`
export const Slider = styled.span`
display: flex;
justify-content: center;
text-align: center;
cursor: pointer;
width: 105px;
border-radius: 100px;
position: relative;
transition: background-color 0.2s;
color: ${textColor};
&:before {
content: "";
position: absolute;
top: 15px;
left: 2px;
width: 50px;
height: 20px;
border-radius: 45px;
transition: 0.2s;
background: ${toggleBackground};
box-shadow: 0 2px 4px 0 rgba(0, 35, 11, 0.2);
}
`;
export const LangText = styled.p`
padding-right: 5px;
padding-left: 5px;
z-index: 1;
letter-spacing: 3px;
`;
I think you can simply use a useState hook to maintain checkbox and the use it for your LangText.
<ToggleContainer>
<InputWrapper>
<Input type="checkbox" onChange={()=>{
onChange();
//use state hook to maitain the checkbox value
}}/>
<Slider>
<LangText className={isChecboxValue === x ? color : invert }>ENG</LangText>
<LangText className={isChecboxValue === y ? invert: color}>EST</LangText>
</Slider>
</InputWrapper>
</ToggleContainer>

Carousel prev and next button logic does not work

I am trying to a make carousel using pure Javascript. I successfully manage to slide the carousel and have created left and right buttons.
I took my slide functions and added them to the button on-click event-listener, but I have problems when I implement the function on my buttons. It does not behave as expected. My code is below, how can I fix this?
const images = document.getElementById('imgs'); //here
const allImages = document.querySelectorAll('#imgs img');
const leftBtn = document.getElementById('left');
const rightBtn = document.getElementById('right');
let index = 0;
function changeSliderPage() {
const dot = [...document.getElementsByClassName('star')];
index++;
if (index > allImages.length - 1) {
index = 0
}
imgs.style.transform = `translateX(${-index * 500}px)`;
dot.forEach((dot, i) => {
if (i === index) {
dot.classList.add('active')
} else {
dot.classList.remove('active')
}
});
};
allImages.forEach(i => {
const elem = document.createElement('div');
elem.classList.add('star');
document.body.appendChild(elem)
});
rightBtn.onclick = () => {
changeSliderPage(index + 1);
}
leftBtn.onclick = () => {
changeSliderPage(index - 1);
}
let x = setInterval(changeSliderPage, 100000);
images.onmouseover = () => {
clearInterval(x)
}
images.onmouseout = () => {
x = setInterval(changeSliderPage, 2000);
}
*{
box-sizing: border-box;
}
body {
margin: 0;
display: flex;
justify-content: center;
align-items: center;
min-height: 100vh;
}
.carousel {
overflow: hidden;
width: 500px;
height: 500px;
box-shadow: 2px 2px 5px rgba(0, 0, 0, .3);
border-radius: 5px;
}
.image-container {
display: flex;
transition: transform 300ms linear;
transform: translateX(0);
}
img {
width:500px;
height: 500px;
object-fit: cover;
}
.star{
cursor: pointer;
height: 15px;
width: 15px;
margin: 0 10px;
border-radius: 50%;
display: inline-block;
transition: background-color 0.6s ease;
background-color: #eeeeee;
}
.star.active{
background-color: red;
}
button{
cursor: pointer;
position: relative;
font-size: 18px;
transition: 0.6s ease;
user-select: none;
height: 50px;
width: 40px;
display: flex;
justify-content: center;
align-items: center;
align-content: center;
top: calc(50% - 25px);
}
button:hover {
background-color: rgba(0,0,0,0.8);
};
button.left {
border-radius: 3px 0 0 3px;
right: 0;
}
button.left {
border-radius: 3px 0 0 3px;
left: 0;
}
<button id="left">❮</button>
<button id="right">❯</button>
<div class="carousel">
<div class="image-container" id="imgs" >
<img src="https://images.unsplash.com/photo-1599736375341-51b0a848f3c7?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=800&q=60" alt="">
<img src="https://images.unsplash.com/photo-1516026672322-bc52d61a55d5?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=800&q=60" alt="">
<img src="https://images.unsplash.com/photo-1573081586928-127ecc7948b0?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=800&q=60" alt="">
<img src="https://images.unsplash.com/flagged/photo-1572850005109-f4ac7529bf9f?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=800&q=60" alt="">
</div>
</div>
Logic that I use with carousels:
for example you have 4 images:
[1][2][3][4]
I have an animation for sliding every image, I add 5th image which is same as image no 1:
[1][2][3][4][1]
Imagine cursor which shows what image is currently displayed, Ill mark cursor as ! !
So at begin:
[!1!][2][3][4][1]
Now the slider moves on...
[1][!2!][3][4][1]
etc...
It moves to last image:
[1][2][3][4][!1!]
And now it has to move under the hood from last image to first image, but without any animation so the whole change is not visible by user:
[!1!][2][3][4][5]
This way you can get inifinite carousel, just need to check in javascript if current image is last one and you want to slide right -> no animation. Same if you are on 1st image and want to slide left.

Categories