I'm trying to create an answer sheet, sometimes referred to as a "Bubble sheet", where the user clicks on "A,B,C, or D" and bubble changes from "light mode to dark mode" as when physically happens penciling in the answer. (Using Tailwind.css)
I'm having a problem with 1) getting only one answer to change state (they all change) and 2) only being able to select one of the answers per question (A,B,C, OR D). Any help would be appreciated as I'm close but stumped before the finish line (so to speak).
I'm passing in props.start from the parent component and repeating component to build answer sheets in increments of 25. You could easily not pass any props and replace "props.start" with 1 to test this out.
import React, { useState } from 'react';
const Bubbles = (props) => {
const [cName, setCname] = useState(
'rounded-full px-2 mx-1 border border-red-600 hover:bg-gray-700 hover:text-white'
);
const handleClick = (event) => {
event.preventDefault();
const answer = event.target.id;
setCname(
'rounded-full px-2 mx-1 border border-red-600 bg-gray-700 text-white'
);
console.log(answer);
};
const NumberedArray = Array.from(Array(25), (_, i) => i + props.start);
return (
<div className='flex flex-wrap flex-col mx-3 overflow-hidden rounded'>
{NumberedArray.map((number, index) => (
<div
className='flex h-10 w-full items-center justify-center bg-gray-100'
key={index}
>
{number}
<div id='A' className={cName} onClick={handleClick}>
A
</div>
<div id='B' className={cName} onClick={handleClick}>
B
</div>
<div id='C' className={cName} onClick={handleClick}>
C
</div>
<div id='D' className={cName} onClick={handleClick}>
D
</div>
</div>
))}
</div>
);
};
export default Bubbles;
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
You have to implement this.
set the default state const [visibleOption,setVisible] = useState('') , const [selctedIndex,setSelctedIndex] = useState('') and In handleClick you have to set the value that you passed and In return apply the ClassName as per Condition wise className={${cName} ${visibleOtion === 'A' && visibleClass}}.
const [visibleOption,setVisible] = useState('')
const [selctedIndex,setSelctedIndex] = useState('')
const visibleClass = 'rounded-full px-2 mx-1 border border-red-600 bg-gray-700 text-white'
const handleClick = (event,value,index) => {
event.preventDefault();
const answer = event.target.id;
setVisible(value)
setSelctedIndex(index.toString())
console.log(answer);
};
//In return written like this
<div id='A' className={`${cName} ${visibleOtion === 'A' && selctedIndex === index.toString() && visibleClass}`} onClick={(e) => handleClick(e, 'A',index)}>
A
</div>
I hope it will work for you.
Break this into 2 separate components. 1) ABCD and 2) Bubbles. In ABCD, map over the answers array. In bubbles, map over the 25 lines of answers. That way you are selecting answers in the ABCD component and don't have the same issues I had.
Related
i use usestate to create saveImages and setSaveImages, this initial as array, i want to each time i call the function, the content is different, so each time must to push to the array that info, but instead of do push, only replace the 1 position of array, doesnt add new position with different info. I don't know if I explain myself
const galery = useSelector((state) => state.search);
const dispatch = useDispatch();
const [saveImages, setSaveImages] = useState([]);
function savePhoto(e) {
e.preventDefault();
const { target } = e;
const content = target.photo.src;
setSaveImages(content)
console.log(content)
localStorage.setItem("img", saveImages);
dispatch(actionAddFavorite(content));
}
return(
<section className="w-full gap-0 sm:columns-2 md:columns-3 xl:columns-4 2xl:columns-5 3xl:columns-6">
{galery.map((item, index) => {
return (
<form onSubmit={savePhoto} key={index} className="relative">
<button className="bg-gray absolute left-5 top-3 shadow-md">
Guardar
</button>
<img
name="photo"
className="object-cover p-2"
src={item.urls.regular}
alt={item.alt_description}
/>
</form>
);
})}
</section>
)
You set your saveImages to contain "content", but what you want is to add "content" to existing saveImages array. Here is how you can do this:
setSaveImages(oldImages => {
return [...oldImages, content];
});
And here you can learn everything you need to know about state in react
I have two almost identical components,
Component A:
const ClaimedLabel = ():React.Element => (
<div tw="">
<p tw="">Some Text here</p>
<div tw="">
<Icon src="./icon.png" />
</div>
</div>)
Component B:
const ClaimedLabelPDF = ():React.Element => (
<View tw="">
<Text tw="">Some Text here</p>
<View tw="">
<Icon src="./icon.png" />
</View>
</View>)
tw - is tailwind style I ignored to improve code redability.
As you can see those two components are identical but diferent tags were used, is it possible to write one single component to reuse it ?
I have one idea to use isPDF flag, put two components into one and return component based on flag value, but in this case I still have duplicates:
const ClaimedLabel = (isPDF: boolean): React.Element => isPDF ? <View>...</View> : <div....</div>
You could just create references to the Types of elements you need to swap.
const ClaimedLabel = (isPDF: boolean): React.Element => {
const RView = isPdf ? View : 'div';
const RText = isPdf ? Text : 'p';
return (
<RView tw="">
<RText tw="">Some Text here</p>
<RView tw="">
<Icon src="./icon.png" />
</RView>
</RView>
);
}
Hey so I'm working on a project where I need to display different components when users click on a button. Can you see if there's anyway I can reactor the code to be cleaner? I'm using react/next.js for the frontend.
Setting up useState to control which component is viewed. Using Boolean array as input
const [views, setViews] = useState([true, false, false])
Displaying the buttons that users will click to select view
<nav className='flex flex-row justify-end'>
<button
type='button'
className={`mainBtn p-2 mr-2` + (views[0] ? ' active' : '')}
onClick={() => setViews([true, false, false])}
>Create New Order</button>
<button
type='button'
className={`mainBtn p-2 mr-2` + (views[1] ? ' active' : '')}
onClick={() => setViews([false, true, false]) }
>View orders</button>
<button
type='button'
className={`mainBtn p-2 mr-2` + (views[2] ? ' active' : '')}
onClick={() => setViews([false, false, true]) }
>Manage account</button>
<button
type='button'
className={`mainBtn p-2 mr-2`}
onClick={() => signOut() }
>Sign Out</button>
</nav>
Using conditional rendering to display the desired components
{views[0] && <DisplayCreateNewOrder id={session.user.customer.locationID}/>}
{views[1] && <DisplayPendingOrders id={session.user.customer.locationID}/>}
{views[2] && <DisplayAccount customer={session.user.customer}/>}
any feedback is much appreciated. Also, the last two code blocks are wrapped in a div element with some tailwinds classes.
Thanks
You can simplify the state as only one view is visible at a time, there is no need to store three boolean variables. Full example - Codesandbox.
Store views in an enum/constant -
const Views = Object.freeze({
Create: "Create",
View: "View",
Manage: "Manage"
});
State can simply be current active view, very simple -
const [view, setView] = useState(Views.View);
Buttons can be refactored into one reusable component -
const LinkButton = ({ text, isActive, onClick }) => {
return (
<button
type="button"
className={`mainBtn p-2 mr-2` + (isActive ? " active" : "")}
onClick={onClick}
>
{text}
</button>
);
};
Then used as
<LinkButton
text="View Orders"
isActive={view === Views.View}
onClick={() => setView(Views.View)}
/>
First, from your code it is evident that only one view can appear at any given time. This means that instead of holding 3 booleans, you can just hold the id/name of the view that is currently active:
const {CreateNewOrder, ViewOrders, ManageAccount} = {'create', 'view', 'account'};
const [activeView, setActiveView] = useState(CreateNewOrder);
Then you can use a function to make the conditional more readable:
const isViewActive = view => activeView === view;
And you would use it like this:
{isViewActive(ManageAccount) && <DisplayAccount id={session.user.customer}/>}
There's probably more consolidation you could do by having all three views accept customer instead of some accepting only the ID.
Is there a possibility to set singe value on React rc-slider Range component? At present to do that I need to drag the second handle in front of the first one or the first one drag after the second one and this behavior is allowing user to drag the handle behind the rail if we are on the edge values
I would like to set this up like on the third image it would show from eg. 39 to 39. Here is my code
import React, { useState } from "react";
import Slider, { Range } from "rc-slider";
import "rc-slider/assets/index.css";
export const RangeSlider = ({
min,
max,
error,
displayUnit,
stereo,
onChange
}) => {
const [minVal, setMinVal] = useState(min);
const [maxVal, setMaxVal] = useState(max);
const props = {
onChange: value => {
if (stereo) {
setMinVal(value[0]);
setMaxVal(value[1]);
} else {
setMinVal(value);
setMaxVal(value);
}
onChange(value);
},
min: min,
max: max,
defaultValue: stereo ? [0, 100] : 0
};
return (
<>
{error && (
<span className="position-outside">
<i className="fas fa-exclamation-circle fa text-danger mt-2"></i>
</span>
)}
<div className="text-primary h6 mb-3">
<strong>
{stereo &&
`Od ${minVal}${
displayUnit ? " " + displayUnit : ""
} do ${maxVal}${displayUnit ? " " + displayUnit : ""}`}
{!stereo &&
`${minVal}${displayUnit ? " " + displayUnit : ""}`}
</strong>
</div>
{stereo ? <Range {...props} /> : <Slider {...props} />}
</>
);
};
I am aware that single range slider is for single values, although the client has requested this feature.
Thanks
Firstly my apologies for making a confusion with the title and a question, the title should be different, I have found the bug I thought it was rc-slider, however it was me translating the second rail to be contained within the track, I have explained everything in detail in this post styling rc-slider track to contain handles within itself therefore I am closing this one. Thanks
I'm Turning this Sign in/up Form into a react application, I'm building it through the use of components. I am new to react so I need some help turning vanilla scripts into react components.
Can someone help me code the vanilla js into components (with sliding transition) ?
Vanilla Javascript
const signUpButton = document.getElementById('signUp');
const signInButton = document.getElementById('signIn');
const container = document.getElementById('container');
signUpButton.addEventListener('click', () => {
container.classList.add("right-panel-active");
});
signInButton.addEventListener('click', () => {
container.classList.remove("right-panel-active");
});
and React Component:
export const SignForms = () => {
const [ containerActive, setContainerActive] = useState(false);
const signUpButton = () => {
setContainerActive(false);
};
const signInButton = () => {
setContainerActive(true);
};
.....
<div className="overlay-container">
<div className="overlay">
<div className="overlay-panel overlay-left">
<div className={containerActive ? " right-panel-active" : ""}></div>
<h1 className="font-effect-3d">Come On!</h1>
<p className="font-effect-emboss">Start Your Journey With Us</p>
<button className="ghost" onClick={signInButton} id="signIn" >Sign In</button>
</div>
<div className="overlay-panel overlay-right">
<h1 className="font-effect-3d">No Account?</h1>
<p className="font-effect-emboss">No Problem! Just Sign Up Here</p>
<button className="ghost " onClick={signUpButton} id="signUp">Sign Up</button>
</div>
</div>
</div>
JsFiddle for Vanilla : https://jsfiddle.net/barrogocarlo/dyg9mqn2/ ||
Git Repo for React app: https://gitlab.com/carlobarrogo/sample-forms
You were pretty much there. Keep up !!
Following is the working code, i was able reproduce on my sandbox.
https://codesandbox.io/s/github/devpandya/SamplSignIns/tree/main/?file=/src/SignForms.js
I found 2 issues:
The App.css in vanila.js was different then in your React Code. just copy it from your Vanila Code
use containerActive state to maniplulate classes in container div.
const signUpButton = () => {
setContainerActive(true);
};
const signInButton = () => {
setContainerActive(false);
};
return (
<div className="signForms">
<div
className={`container ${containerActive && "right-panel-active"}`}
id="container"
>
Thats it.. !!!..
Also there was a extra div added in your overlay, just remove it, no use of it
<div className="overlay-panel overlay-left">
**<div className={containerActive ? " right-panel-active" : ""}></div>**
<h1 className="font-effect-3d">Come On!</h1>