import React, { Component } from 'react'
class ProgressBar extends Component {
render() {
let progressContainer = document.querySelector('.progress-container');
let valueContainer = document.querySelector('.progress-value');
const speed = 20;
let progressValue = 0;
let progressEndValue = 70;
function updateElements() {
valueContainer = document.querySelector('.progress-value');
progressContainer = document.querySelector('.progress-container');
}
const createProgress = setInterval(() => {
progressValue++;
updateElements();
valueContainer.innerText = `${progressValue} %`
progressContainer.style.background = `conic-gradient(
rgb(239 68 68) ${progressValue * 3.6}deg,
black 1deg,
rgb(241 245 249) 1deg,
)`
if (progressValue == progressEndValue) {
clearInterval(createProgress);
}
}, speed)
return (
<div className='progress progress-container w-full h-full rounded-full flex justify-center items-center'>
<div className="progress w-3/4 h-3/4 rounded-full bg-slate-100 flex justify-center items-center">
<h1 className='progress-value' >0 %</h1>
</div>
</div>
)
}
}
export default ProgressBar;
So here is my code, I am basically trying to create a dynamic animated circular progress bar here.
I use updateElements function to prevent the uncaught error of null, the progress value is changing between 0 and 70 percent successfully in the DOM. but the conic-gradient background does not applying in the DOM from the function. but if I set it statically in the CSS file with the same code. it works.
Someone help me please I am struggling since yesterday!!
import React, { Component } from 'react'
class ProgressBar extends Component {
state={
progressValue:0,
speed:20,
progressEndValue:70
}
render() {
let progressContainer = document.querySelector('.progress-container');
let valueContainer = document.querySelector('.progress-value');
function helperFunctions() {
valueContainer = document.querySelector('.progress-value');
progressContainer = document.querySelector('.progress-container');
}
const createProgress = setInterval(() => {
if (this.state.progressValue <= this.state.progressEndValue) {
this.setState({progressValue:this.state.progressValue+1});
helperFunctions();
valueContainer.innerText = `${this.state.progressValue} %`
progressContainer.style.background = `conic-gradient(rgb(239 68 68) ${this.state.progressValue * 3.6}deg,black 1deg,rgb(241 245 249) 1deg)`
} else {
clearInterval(createProgress);
}
}, this.state.speed)
return (
<div className='progress progress-container w-full h-full rounded-full flex justify-center items-center'>
<div className="progress w-3/4 h-3/4 rounded-full bg-slate-100 flex justify-center items-center">
<h1 className='progress-value' >0 %</h1>
</div>
</div>
)
}
}
export default ProgressBar;
Now it works fine :)
suggestion:
If your were using functional component, it could be done much easier and you could use useRef intead of document.querySelector as it is recomanded is React document
The main problem was the last , in conic-gradient
Related
The page for my web3-based event application works fine until I go into my "past events" and click "attendees." When I do that, I get Syntax error: Unexpected token.
The issue is specifically on line 86 of my code, for (let i = 0; < event.confirmedAttendees.length; i++) {. In VS Code, I have 4 errors, all of which point to this line. The errors are "parsing error: unexpected token", 2 "identifier expected" errors, and 1 "expression expected."
I've checked the code against the GitHub repo I forked the entire project file from, edited it, and now it all matches.
I'm new to coding with no previous JS experience. I've been scratching my head on this one all day. I have no idea what the issue is.
Here is the entire code from my [id].js file. The issue is with line 86.
import { useState, useEffect } from "react";
import Head from "next/head";
import Link from "next/link";
import { gql } from "#apollo/client";
import client from "../../../apollo-client";
import { ConnectButton } from "#rainbow-me/rainbowkit";
import { useAccount } from "wagmi";
import connectContract from "../../../utils/connectContract";
import formatTimestamp from "../../../utils/formatTimestamp";
import DashboardNav from "../../../components/DashboardNav";
import Alert from "../../../components/Alert";
function PastEvent({ event }) {
const { data: account } = useAccount();
console.log("event", event);
const [success, setSuccess] = useState(null);
const [message, setMessage] = useState(null);
const [loading, setLoading] = useState(null);
const [mounted, setMounted] = useState(false);
const confirmAttendee = async (attendee) => {
try {
const rsvpContract = connectContract();
if (rsvpContract) {
const txn = await rsvpContract.confirmAttendee(event.id, attendee);
setLoading(true);
console.log("Minting...", txn.hash);
await txn.wait();
console.log("Minted -- ", txn.hash);
setSuccess(true);
setLoading(false);
setMessage("Attendance has been confirmed.");
} else {
console.log("Ethereum object doesn't exist!");
}
} catch (error) {
setSuccess(false);
// setMessage(
// `Error: ${process.env.NEXT_PUBLIC_TESTNET_EXPLORER_URL}tx/${txn.hash}`
// );
setMessage("Error!");
setLoading(false);
console.log(error);
}
};
const confirmAllAttendees = async () => {
console.log("confirmAllAttendees");
try {
const rsvpContract = connectContract();
if (rsvpContract) {
console.log("contract exists");
const txn = await rsvpContract.confirmAllAttendees(event.id, {
gasLimit: 300000,
});
console.log("await txn");
setLoading(true);
console.log("Mining...", txn.hash);
await txn.wait();
console.log("Mined -- ", txn.hash);
setSuccess(true);
setLoading(false);
setMessage("All attendees confirmed successfully.");
} else {
console.log("Ethereum object doesn't exist!");
}
} catch (error) {
setSuccess(false);
// setMessage(
// `Error: ${process.env.NEXT_PUBLIC_TESTNET_EXPLORER_URL}tx/${txn.hash}`
// );
setMessage("Error!");
setLoading(false);
console.log(error);
}
};
function checkIfConfirmed(event, address) {
// console.log(event.confirmedAttendees);
for (let i = 0; < event.confirmedAttendees.length; i++) {
let confirmedAddress = event.confirmedAttendees[i].attendee.id;
if (confirmedAddress.toLowercase() == address.toLowercase()) {
return true;
}
}
return false;
}
useEffect(() => {
setMounted(true);
}, []);
return (
mounted && (
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<Head>
<title>My Dashboard | web3rsvp</title>
<meta name="description" content="Manage your events and RSVPs" />
</Head>
<div className="flex flex-wrap py-8">
<DashboardNav page={"events"} />
<div className="sm:w-10/12 sm:pl-8">
{loading && (
<Alert
alertType={"loading"}
alertBody={"Please wait"}
triggerAlert={true}
color={"white"}
/>
)}
{success && (
<Alert
alertType={"success"}
alertBody={message}
triggerAlert={true}
color={"palegreen"}
/>
)}
{success === false && (
<Alert
alertType={"failed"}
alertBody={message}
triggerAlert={true}
color={"palevioletred"}
/>
)}
{account ? (
account.address.toLowerCase() ===
event.eventOwner.toLowerCase() ? (
<section>
<Link href="/my-events/past">
<a className="text-indigo-800 text-sm hover:underline">
← Back
</a>
</Link>
<h6 className="text-sm mt-4 mb-2">
{formatTimestamp(event.eventTimestamp)}
</h6>
<h1 className="text-2xl tracking-tight font-extrabold text-gray-900 sm:text-3xl md:text-4xl mb-8">
{event.name}
</h1>
<div className="-my-2 -mx-4 overflow-x-auto sm:-mx-6 lg:-mx-8">
<div className="inline-block min-w-full py-2 align-middle md:px-6 lg:px-8">
<div className="overflow-hidden shadow ring-1 ring-black ring-opacity-5 md:rounded-lg">
<table className="min-w-full divide-y divide-gray-300">
<thead className="bg-gray-50">
<tr>
<th
scope="col"
className="py-3.5 pl-4 pr-3 text-left text-sm font-semibold text-gray-900 sm:pl-6"
>
Attendee
</th>
<th
scope="col"
className="text-right py-3.5 pl-3 pr-4 sm:pr-6"
>
<button
type="button"
className="items-center px-4 py-2 border border-transparent text-sm font-medium rounded-full text-indigo-700 bg-indigo-100 hover:bg-indigo-200 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500"
onClick={confirmAllAttendees}
>
Confirm all
</button>
</th>
</tr>
</thead>
<tbody className="divide-y divide-gray-200 bg-white">
{event.rsvps.map((rsvp) => (
<tr key={rsvp.attendee.id}>
<td className="whitespace-nowrap py-4 pl-4 pr-3 text-sm font-medium text-gray-900 sm:pl-6">
{rsvp.attendee.id}
</td>
<td className="relative whitespace-nowrap py-4 pl-3 pr-4 text-right text-sm font-medium sm:pr-6">
{checkIfConfirmed(event, rsvp.attendee.id) ? (
<p>Confirmed</p>
) : (
<button
type="button"
className="text-indigo-600 hover:text-indigo-900"
onClick={() =>
confirmAttendee(rsvp.attendee.id)
}
>
Confirm attendee
</button>
)}
</td>
</tr>
))}
</tbody>
</table>
</div>
</div>
</div>
</section>
) : (
<p>You do not have permission to manage this event.</p>
)
) : (
<ConnectButton />
)}
</div>
</div>
</div>
)
);
}
export default PastEvent;
export async function getServerSideProps(context) {
const { id } = context.params;
const { data } = await client.query({
query: gql`
query Event($id: String!) {
event(id: $id) {
id
eventID
name
eventOwner
eventTimestamp
maxCapacity
totalRSVPs
totalConfirmedAttendees
rsvps {
id
attendee {
id
}
}
confirmedAttendees {
attendee {
id
}
}
}
}
`,
variables: {
id: id,
},
});
return {
props: {
event: data.event,
},
};
}
It looks like you missed the i in the for loop check in this function:
function checkIfConfirmed(event, address) {
// console.log(event.confirmedAttendees);
for (let i = 0; < event.confirmedAttendees.length; i++) {
let confirmedAddress = event.confirmedAttendees[i].attendee.id;
if (confirmedAddress.toLowercase() == address.toLowercase()) {
return true;
}
}
return false;
}
Take a look at the second line in your function:
// currently:
for (let i = 0; < event.confirmedAttendees.length; i++)
// missing i in the check, should be:
for (let i = 0; i < event.confirmedAttendees.length; i++)
Let me know if anything seems unclear regarding for loops!
So I'm trying to make a dynamic reusable switch selector component exactly like that on https://www.themoviedb.org/ to select between a number of options such as ["a", "b", "c"].
I've got most of the logic down, it might be a bit messy now, but my problem really is that I can't seem to figure out at which width or distance to move the coloured div to accurately place it right on top of the label/option title.
This is what I've got so far, the text colour changes correctly when selected, and the transaction is also smooth, but the position is wrong.
type SwitchProps = {
optionTitles: string[];
};
type Selector = {
isToggled: boolean;
optionTitle: string;
width: number | undefined;
};
const Switch: FC<SwitchProps> = (props) => {
const [selectors, setSelectors] = useState<Selector[]>([]);
const [currentToggled, setCurrentToggled] = useState<Selector & { index: number }>({
index: 0,
isToggled: true,
optionTitle: props.optionTitles[0],
width: 110,
});
const elementsRef = useRef<RefObject<HTMLDivElement>[]>(props.optionTitles.map(() => createRef()));
useLayoutEffect(() => {
if (selectors.length >= props.optionTitles.length) {
return;
}
props.optionTitles.map((optionName, index) => {
setSelectors((prevState) => [
...prevState,
{
isToggled: index === 0 ? true : false,
optionTitle: optionName,
width: 110,
},
]);
});
}, []);
const handlerToggleClick = (sectorIndex: number, toggleState: boolean) => {
let data = selectors;
data.forEach((selector, index) => {
selector.isToggled = false;
selector.width = elementsRef.current[sectorIndex].current?.offsetWidth;
});
data[sectorIndex].isToggled = true;
setCurrentToggled({ index: sectorIndex, ...data[sectorIndex] });
setSelectors(data);
};
return (
<div className="relative z-[1] h-8 border border-solid border-tmdbDarkBlue rounded-[30px] font-medium flex items-center">
{selectors.map((sector, index) => (
<div
key={index}
ref={elementsRef.current[index]}
className={`py-1 px-5 h-8 text-sm font-semibold flex items-center ${
sector.isToggled && "switch-active-text"
}`}
>
<span
className="cursor-pointer flex items-center"
onClick={() => handlerToggleClick(index, !sector.isToggled)}
>
{sector.optionTitle}
</span>
</div>
))}
<div
className={`absolute z-[-1] h-8 w-20 bg-tmdbDarkBlue rounded-[30px] transition-all duration-150 ease-in`}
style={{
width: currentToggled.width,
left: currentToggled.index === 0 ? 0 : (currentToggled.width as number) * 1.8,
}}
></div>
</div>
);
};
export default Switch;
If there are other ways to improve my code, please do let me know. I'm trying to get better at things, which is why I'm working on this clone sorta project.
I am working on my Next.js website. Using Tailwind CSS I have managed to change the color dynamically before returning a component with static names & colors.
Now I am fetching data from the API and the title + color needs to be dynamically created with the values from the API.
the title prop equals the hardcoded project (1,2,3) name. The color should be color prop.
Is there an intelligent way to create an object literal with the dynamic data?
Hardcoded values work just perfectly fine.
import { motion } from 'framer-motion';
import Image from 'next/image';
import Link from 'next/link';
import { projectsArrayProps } from '#typings/propTypes';
const Projects = ({ allProjects }) => {
console.log(allProjects);
return (
<div className="card--grid grid grid-cols-3 lg:grid-cols-4 gap-4 auto-rows-[100px] sm:auto-rows-[120px] md:auto-rows-[200px]">
{allProjects.map(
({ id, logo, title, color }: projectsArrayProps, index) => {
// const projectColor: { [key: string]: string } = {
// project1: 'bg-[#fbc340]/[0.07]',
// project2: 'bg-[#70d1db]/[0.07]',
// project3: 'bg-[#ea5b52]/[0.07]',
// project4: 'bg-[#ff1e00]/[0.07]',
// project5: 'bg-[#ff99f3]/[0.07]',
// };
const projectColor: { [key: string]: string } = {};
return (
<motion.div
key={id}
className={`rounded-md bg-[${projectColor['title']}]`}
initial="hidden"
whileInView="visible"
viewport={{ once: true }}
transition={{
duration: 0.1,
stiffness: 200,
delay: index * 0.085,
type: 'spring'
}}
variants={{
hidden: { opacity: 0, scale: 0.6 },
visible: { opacity: 1, scale: 1 }
}}
>
<Link href="">
<a
target="_blank"
className="flex flex-col items-center justify-center w-full h-full"
>
<div className="flex flex-col items-center justify-center relative w-full h-full max-w-[64px] md:max-w-[100px] lg:max-w-[120px]">
<img
className="object-contain"
src={logo.url}
alt={logo.alt}
/>
</div>
</a>
</Link>
</motion.div>
);
}
)}
</div>
);
};
export default Projects;
Tailwind extracts classes at build time, so you cannot create dynamic classes at runtime. You could safelist some classes to make sure they are created even when they are not present in the code at build time (https://tailwindcss.com/docs/content-configuration#safelisting-classes). Unfortunately this only helps you if you know all possible colors that can be returned from the API.
I'm working on creating a swiping effect ( Drag to show modal). When ever I swipe the element the element overflow the screen , I use getBoundingClientRect() to check for collision but it's not working correctly for some reasons.
This is the code Below
Note: ClassNames are Tailwinds
<template>
<div id="page" class="fixed top-0 left-0 bottom-0 right-0">
<div class="relative h-full overflow-y-hidden">
<div
ref="dragable"
class="absolute left-0 right-0 h-full -bottom-[45%] bg-purple-200 p-2 border-1 rounded-t-xl"
#touchstart="onTouchStart"
#touchmove="onTouchMove"
#touchend="onTouchEnd">
{{ touchTimer }}
</div>
</div>
</div>
</template>
<script>
export default {
data(){
return {
initialY: 0,
currentY: 0,
pageY: 0,
collide: false,
touchTimer: 0,
touchInterval: null
};
},
computed: {
dragable(){
return this.$refs.dragable;
}
},
methods: {
onTouchStart(event){
this.initialY = event.touches[0].clientY;
event.currentTarget.style.transition = "unset";
},
onTouchMove(event){
this.currentY = event.touches[0].clientY - this.initialY;
const view = event.currentTarget;
const { top } = view.getBoundingClientRect();
if(this.currentY && !this.collide)
view.style.transform = `translate(0, ${this.currentY}px)`;
this.collide = top < 0;
},
onTouchEnd(event){
const view = event.currentTarget;
if(!this.collide)
view.style.transform = "translate(0,0)";
}
},
mounted(){
document.body.ondblclick = function (){
document.body.requestFullscreen();
}
}
};
</script>
<style>
body{ -webkit-overscroll-behavior: contain; }
</style>
I want to download only the papayawhip square box without the transparent image behind it.
I am using React Konva. I have the following code:
import * as React from "react"
import { Stage, Layer, Rect } from "react-konva"
import type { Stage as StageType } from "konva/types/Stage"
import { observer } from "mobx-react"
import "./styles.css"
import { useStore } from "./context"
const downloadURI = (uri: string | undefined, name: string | undefined) => {
const link = document.createElement("a")
link.download = name as string
link.href = uri as string
document.body.appendChild(link)
link.click()
document.body.removeChild(link)
}
function App() {
const [fillColor, setFillColor] = React.useState("")
const [downloadClicked, setDownloadClicked] = React.useState(false)
const stageRef = React.useRef<StageType>(null)
const transparentBackground = new window.Image()
transparentBackground.src =
"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAMAAAC6V+0/AAAABlBMVEUAAADY2NjnFMi2AAAAAXRSTlMAQObYZgAAABVJREFUGNNjYIQDBgQY0oLDxBsIQQCltADJNa/7sQAAAABJRU5ErkJggg=="
const store = useStore()
const { win, canvas, browser, pad } = store
return (
<div className="flex">
<div id="sidebar">
<h1 className="text">
Center <span>papayawhip</span> Canvas
</h1>
<input
type="text"
placeholder="Enter Fill Color"
value={fillColor}
onChange={(e) => setFillColor(e.target.value)}
/>
<button
className="inline-flex items-center px-4 py-2 text-sm font-medium text-white bg-indigo-600 border border-transparent rounded-md shadow-sm hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500"
onClick={() => {
setDownloadClicked(true)
const options = { mimeType: `image/png`, quality: 1, pixelRatio: 1 }
const img = stageRef.current?.getStage().toDataURL(options)
downloadURI(img, "download.png")
setDownloadClicked(false)
}}
>
Download Image
</button>
</div>
<Stage
ref={stageRef}
width={canvas.width}
height={canvas.height}
id="konva"
>
<Layer>
{fillColor === "" && downloadClicked && (
<Rect
width={browser.width + 200}
height={browser.height + 200}
x={pad / 2}
y={(win.height - browser.height) / 2}
fillPatternImage={transparentBackground}
fill={fillColor}
/>
)}
<Rect
width={browser.width}
height={browser.height}
x={pad / 2}
y={(win.height - browser.height) / 2}
fill="papayawhip"
/>
</Layer>
</Stage>
</div>
)
}
export default observer(App)
Codesandbox → https://codesandbox.io/s/add-padding-to-centered-canvas-with-sidebar-gqhhl?file=/src/App.tsx
How do I download only the papayawhip rectangle part?
In principle,
hide any shapes you don't want included in the image,
Get the image
unhide what you hid in 1.
Note: do not draw the layer/stage between 1 and 3 or you will see an unwanted flicker.
You can pass x, y, width and height attributes to stage.toDataURL(options) function to capture only a part of the screen:
const options = {
mimeType: `image/png`,
quality: 1,
pixelRatio: 1,
width: browser.width,
height: browser.height,
x: pad / 2,
y: (win.height - browser.height) / 2
};
const img = stageRef.current?.getStage().toDataURL(options);
https://codesandbox.io/s/react-konva-export-part-of-the-stage-hf5sn
I solved it with the help of Anton. Vanquished Wombat's approach was the same as well.
const backgroundSelector = React.useRef(null)
/* gave a `name` to `transparentBackground` rectangle */
<Rect
name="transparentBackground"
/>
/* then searched for it & hid it */
stageRef.current?.findOne(".transparentBackground").hide()
/* download the image */
downloadURI(img, "download.png")
/* show it again */
stageRef.current?.findOne(".transparentBackground").show()
Updated Codesandbox → https://codesandbox.io/s/add-padding-to-centered-canvas-with-sidebar-gqhhl?file=/src/App.tsx