unique default state/class to react toggle - javascript

I have the below, except currently it's setting the minimize class as default. Toggling works fine, I just would like a unique default class to be show at first before any click has happened. After a click, it would then go between maximize and minimize classes as it does now.
If I could keep the below functionality, and just prevent maximize or minimize classes from being added automatically, this should solve the issue, however anytime I try to remove it breaks.
class ToggleIt extends Component {
constructor(props) {
super(props)
this.state = {
expandedTray: false,
}
this.toggleExpandedTray = this.toggleExpandedTray.bind(this);
}
toggleExpandedTray() {
this.setState({ expandedTray: !this.state.expandedTray, });
}
render() {
return (
<div
className={`something--isi-tray ${this.state.expandedTray ? 'expanded-tray' : 'minimize-tray'}`}
onClick={this.toggleExpandedTray}
>
<div className="something--isi-tray__content">
<h3>{this.props.mainHeadingTitle}</h3>
<div
data-rte-editelement
dangerouslySetInnerHTML={{
__html: DOMPurify.sanitize(this.props.isiContent)
}}
/>
</div>
</div>
)
}
}

Toggling works fine, I just would like a unique default class to be
show at first before any click has happened.
If I correctly understood you could set null as initial value in state, and then
this.state.expandedTray === null ? "some-class" : this.state.expandedTray ? 'expanded-tray' : 'minimize-tray'

Related

React : Setting scrollTop on Div doesn't work in a specific case

I have been stuck at this since today morning and I still can't figure out where did I go wrong. I wanted to highlight text inside a textarea input type. I know this isn't possible, but I found a clever solution to cheat the viewer into thinking that. Here is the link to the article and Here is the link to the codeio pen for the project.
I have been trying to recreate the same using react and normal javascript but the scrollTop property for div with className highlight just doesn't work. If someone could help me debug what I'm doing wrong, that would be wonderful!
class CodeTextArea extends React.Component {
constructor(props){
super(props);
this.state = {scrollTop: 0,
scrollLeft: 0
};
this.setScroll = this.setScroll.bind(this);
}
setScroll(top,left){
this.setState({scrollTop: top, scrollLeft: left});
}
render(){
return(
<div class="container">
<div class="backdrop">
<Highlight scrollTop={this.state.scrollTop} scrollLeft={this.state.scrollLeft}/>
</div>
<Textarea setScrollTop={this.setScroll}/>
</div>
);
}
}
class Highlight extends React.Component {
constructor(props){
super(props);
this.divRef = React.createRef();
}
componentDidUpdate(prevProps){
if (this.props.scrollTop !== prevProps.scrollTop) {
/*console.log(this.divRef.current);
console.log(this.props.scrollTop);
console.log(this.props.scrollLeft);*/
this.divRef.current.scrollTop = this.props.scrollTop;
}
}
render(){
return (
<div class="highlights" ref={this.divRef}><mark>This</mark> demo shows how to highlight bits of text within a <mark>textarea</mark>. Alright, that's a lie. You can't actually render markup inside a textarea. However, you can fake it by carefully positioning a div behind the textarea and adding your highlight markup there. JavaScript takes care of syncing the content and scroll position from the textarea to the div, so everything lines up nicely. Hit the toggle button to peek behind the curtain. And feel free to edit this text. All capitalized words will be highlighted.
</div>
);
}
}
class TogglePerspective extends React.Component {
constructor(props){
super(props);
this.clickHandler = this.clickHandler.bind(this);
this.buttonRef = React.createRef();
}
clickHandler(){
}
render(){
return (
<button onClick={this.clickHandler} ref={this.buttonRef}>Toggle Perspective</button>
);
}
}
class Textarea extends React.Component {
constructor(props){
super(props);
this.handleScroll = this.handleScroll.bind(this);
this.handleChange = this.handleChange.bind(this);
this.applyHighlights = this.applyHighlights.bind(this);
this.textareaRef = React.createRef();
this.state = {value: 'This demo shows how to highlight bits of text within a textarea. Alright, that\'s a lie. You can\'t actually render markup inside a textarea. However, you can fake it by carefully positioning a div behind the textarea and adding your highlight markup there. JavaScript takes care of syncing the content and scroll position from the textarea to the div, so everything lines up nicely. Hit the toggle button to peek behind the curtain. And feel free to edit this text. All capitalized words will be highlighted.'};
}
applyHighlights(text){
return text
.replace(/\n$/g, '\n\n')
.replace(/[A-Z].*?\b/g, '<mark>$&</mark>');
}
handleScroll(){
let scrollTop = this.textareaRef.current.scrollTop;
let scrollLeft = this.textareaRef.current.scrollLeft;
this.props.setScrollTop(scrollTop,scrollLeft);
}
handleChange(event){
let textareaValue = event.targrt.value;
this.setState({value: textareaValue});
let highlightedText = this.applyHighlights(textareaValue);
}
render(){
return (
<textarea ref={this.textareaRef} value={this.state.value} onChange={this.handleChange} onScroll={this.handleScroll}></textarea>
);
}
}
class Editor extends React.Component {
render(){
return (
<div>
<CodeTextArea />
<TogglePerspective />
</div>
);
}
}
ReactDOM.render(
<Editor />,
document.getElementById('root')
);
Here is the codeIo pen to my recreation. Just please tell me why the
highlight class div scrollTop attribute is not working.
I don't usually post long code here unless I'm truly frustrated so any help is appreciated.
Looks like the scrollTop property is being set on div.highlights when it should be set on div.backdrop.
Move div.backdrop into the Highlight Component and put the ref on that element:
<div class="backdrop" ref={this.divRef}>
<div class="highlights">
<mark>This</mark> demo shows how to highlight bits of text within a <mark>textarea</mark>.
Alright, that's a lie. You can't actually render markup inside a textarea. However, you can
fake it by carefully positioning a div behind the textarea and adding your highlight markup there.
JavaScript takes care of syncing the content and scroll position from the textarea to the div, so everything lines up nicely.
Hit the toggle button to peek behind the curtain. And feel free to edit this text. All capitalized words will be highlighted.
</div>

Toggle prop passed on one of many targets in ReactJS

Just starting off with ReactJS and have a project where I am showing an accordion of issues and including a details area that is hidden on the start.
There is a button in the accordion bar that should pass a prop to the child element to hide or show them. I have refs on the button and on the details child compoment and added a function to call the function and pass the ref of the details area. I am just not sure how to dynamically change the class hidden on one of many areas and not all of them.
Not sure if putting a class on each element and then learning how to toggle the particular child's class is better or changing the prop to the child.
I can get to the change function but am drawing a blank from there and all the googling shows how to do one element with a grand change of state but I need individual elements.
Here is what I have so far.
Parent
...
<AccordionItem key={item.id} className={iconClass} title={`${item.area}`} expanded={item === 1}>
{
item.issues.map(issue => {
let trim = (issue.issue.length>21) ? `${issue.issue.substring(0,22)}...`: issue.issue;
return (
<div className="issue-bar container-fluid">
<div className="row issue-bar-row">
<span className="issue-title"><img src={CriticalRed} alt="Critical"/> {trim}</span>
<span className="btns">
<button className="btn btn-details" onClick={() => this.showDetail(`details-${issue.id}`)}>Details</button>
</span>
</div>
<IssuesDetails ref={`details-${issue.id}`} issue={issue} shouldHide={true} />
</div>
)
})
}
<div>
</div>
</AccordionItem>
...
Child
export default class IssuesDetails extends Component{
render(){
let issueDetails = classNames( 'issue-details', { hidden: this.props.shouldHide } )
return(
<div className={issueDetails}>
<div className="issues-details-title">
<h3>{this.props.issue.issue}</h3>
</div>
<div className="issues-details-details">
{this.props.issue.details}
</div>
<div className="issues-details-gallery">
<ImageGallery source={this.props.issue.photos} showPlayButton={false} useBrowserFullscreen={false} />
</div>
<button className="btn btn-success">Resolve</button>
</div>
)
}
}
Thanks for any help you provide or places you can send me!
If i'm understanding correctly, you need to be able to swap out shouldHide={true} in certain circumstances. To do this, you'll want your parent component to have a state object which indicates whether they should be hidden or not.
Exactly what this state object looks like depends on what sort of data you're working with. If the issues is a single array, then perhaps the state could be an array of booleans indicating whether each issue is expanded or not. I suspect you may have a more nested data structure, but i can't tell exactly since some of the code was omitted.
So assuming you have an array, it might look like this (i've omitted some things from the render method for brevity):
class Parent extends React.Component {
constructor(props) {
super(props);
this.state = {
hidden: (new Array(props.issues.length)).fill(false),
};
}
showDetail(index) {
let newHidden = this.state.hidden.slice();
newHidden[index] = true;
this.setState({
hidden: newHidden
});
}
render() {
return (
<AccordionItem>
{this.props.issues.map((issue, index) => {
<div>
<button onClick={() => this.showDetail(index))}/>
<IssuesDetails issue={issue} shouldHide={this.state.hidden[index]}/>
</div>
})}
</AccordionItem>
);
}
}
Take a look at these:
https://codepen.io/JanickFischr/pen/xWEZOG
style={{display: this.props.display}}
I think it will help with your problem. If you need more information, please just ask.

React CSSTransitionGroup does not work even with key being set

I have been trying since yesterday to make an animation to my image carousel. As far as I understand, you wrap the content to be animated with the CSSTransitionGroup and make sure it stays in the dom and also specify a unique key to each child of the transition group. I believe I have followed all this yet I see no transition.
One thing worth to mention, While I was trying to get this working I suspected if something could be wrong with the key, so I tried setting the key with a random string. The key would change every-time the state changes, and for some unknown reason I could see the animation. Can someone explain this to me.
I am not sure where I am going wrong, whether the version of transition group or in setting the key to children, No clue !
Below is the code replicating my problem.
var CSSTransitionGroup = React.addons.CSSTransitionGroup
class Images extends React.Component {
constructor(props){
super(props);
this.state = {
showComponent: false,
}
}
componentWillReceiveProps(nextProps){
if (this.props.name === nextProps.showComponentName){
this.setState({
showComponent: true,
})
} else {
this.setState({
showComponent: false,
})
}
}
render() {
if (this.state.showComponent){
return (
<img src={this.props.url} />
)
} else {
return null;
}
}
}
class TransitionExample extends React.Component {
constructor (props) {
super(props);
this.onClick = this.onClick.bind(this);
this.state= {
showComponentName: null,
}
}
onClick(button) {
this.setState({
showComponentName: button.currentTarget.textContent,
})
}
render() {
var imageData = [
"http://lorempixel.com/output/technics-q-c-640-480-9.jpg",
"http://lorempixel.com/output/food-q-c-640-480-8.jpg",
"http://lorempixel.com/output/city-q-c-640-480-9.jpg",
"http://lorempixel.com/output/animals-q-c-640-480-3.jpg"
];
var images = [];
for (var i in imageData) {
i = parseInt(i, 10);
images.push(
<Images url={imageData[i]} showComponentName={this.state.showComponentName} name={imageData[i]} key={imageData[i]} />
);
}
return (
<div>
<div>
<button onClick={this.onClick}>{imageData[0]}</button>
<button onClick={this.onClick}>{imageData[1]}</button>
<button onClick={this.onClick}>{imageData[2]}</button>
<button onClick={this.onClick}>{imageData[3]}</button>
</div>
<div className="transitions">
<CSSTransitionGroup
transitionName="viewphoto"
transitionEnterTimeout={2000}
transitionLeaveTimeout={2000}
transitionAppearTimeout={2000}
transitionAppear={true}
transitionEnter={true}
transitionLeave={true}>
{images}
</CSSTransitionGroup>
</div>
</div>
);
}
}
ReactDOM.render(
<TransitionExample />,
document.getElementById('container')
);
I am also providing the link to the example on jsfiddle
The problem with your code is that images is always an array of elements that don't mount/unmount. The correct approach for this is to change the child. For example, if you substitute the return of the render method of your fiddle with this:
return (
<div>
<div>
<button onClick={this.onClick}>{imageData[0]}</button>
<button onClick={this.onClick}>{imageData[1]}</button>
<button onClick={this.onClick}>{imageData[2]}</button>
<button onClick={this.onClick}>{imageData[3]}</button>
</div>
<div className="transitions">
<CSSTransitionGroup
transitionName="viewphoto"
transitionEnterTimeout={2000}
transitionLeaveTimeout={2000}
transitionAppearTimeout={2000}
transitionAppear={true}
transitionEnter={true}
transitionLeave={true}>
<img src={this.state.showComponentName} key={this.state.showComponentName}/>
</CSSTransitionGroup>
</div>
</div>
);
The animation works! Using a simple img instead of your Images component and giving it the image url (this only works when you have clicked a button, showComponentName should be initialized to show the first image). You could also use a custom component of course, but the point here is that the children elements of CSSTransitionGroup must be changed if you want the animation to trigger because otherwise you are always rendering the same four Images components no matter whether they return the img or not. You might want to check out react-css-transition-replace since it usually works better when it comes to replacing.

Change active element in a list using react

I have a list of elements created by using an array in react. On user click how can I make the clicked element active (by adding a CSS class) while making the other elements inactive (by removing the active class)?
My rendering of elements looks like this.
{this.props.people.map(function(person, i){
<div className='media' key={i} onClick={state.handleClick.bind(state,i,state.props)}>
<item className="media-body">{person.name}</item>
</div>
}
When the user clicks on one of these elements an active class will be added to the clicked 'media' element making the clicked element 'media active' while removing the 'active' class from the previously clicked element??
constructor(props) {
super(props);
this.state = { activeIndex: 0 };
}
handleClick(index, props) {
// do something with props
// ...
// now update activeIndex
this.setState({ activeIndex: index });
}
render() {
return (
<div>
{
this.props.people.map(function(person, index) {
const className = this.state.activeIndex === index ? 'media active' : 'media';
return (
<div className={className} key={index} onClick={handleClick.bind(this, index, this.props)}>
<item className="media-body">{person.name}</item>
</div>
);
}, this)
}
</div>
);
}
For the sake of clean code I personally would suggest you creating subcomponents to add functionality to mapped elements.
You could create a small subcomponent which simply returns the element which you want to add functionality to just like this :
...
this.state = {
active: false
}
...
return(
<div className=`media ${this.state.active ? 'active' : ''` onClick={()=>{this.setState({active: true})}}>
<item className="media-body">{this.props.name}</item>
</div>
)
...
And in your map function you simply pass the contents as properties:
{this.props.people.map(function(person, i){
<SubComponent key={i} {...person} />
}
That way you stay with clean code in the "root" component and can add complexity to your subcomponent.
In your handleClick method you could store in the component's state the clicked person (looks like the collection is of people). Then set the className conditionally based on, say, the person's id.
You can set the className using something like:
className={this.state.clickedPersonId === i ? 'media media--clicked' : 'media'}
(NB This is using i, the index of the item in the people array; you may want to use something a little more explicit, like the person's real Id.)

Getting HTML element by Id and switch its CSS through React

I have some files that load into my react components, which have HTML code.
As it is now, the pure HTML code renders just fine, however there is some 'hidden' code that appears whenever you click certain buttons in other parts of the application or on the text above (think of it like panels that expand when you click on it).
The HTML is hidden just using the good old <div id="someId" style="display:none">.
Anyway I am trying to get the correct panel to expand upon clicking their respective buttons.
So in theory, what I need to do is find the element by id, and switch it's display to block whenever needed, and then switch it back when the parent is clicked again.
Unfortunately I have no idea how to do this and so far have gotten nowhere. As it is now, I have access to the component's ids. What I want to know is how in the world can I access that and get to change whatever is rendering?
Create your function:
function element_do(my_element, what_to_do) {
document.getElementById(my_element).style.display = what_to_do;
}
and latter in code you can append wherever you want through javascript onclick or not depends what do you need:
element_do("someId", "none"); // to hide
element_do("someId", "block"); // to show
or create yourself toggle:
function toggle_element(element_id) {
var element = document.getElementById(element_id);
element.style.display = (element.style.display != 'none' ? 'none' : 'block' );
}
// and you can just call it
<button onClick="toggle_element('some_id')">toggle some element</button>
The react way to do it would be with states. Assuming that you know how to use states I'd do something like this:
class ShowHide extends React.Component {
constructor() {
super();
this.state = {myState: true};
this.onClick = this.onClick.bind(this)
}
onClick() {
this.setState({myState: !this.state.myState}) //set the opposite of true/false
}
render() {
const style = {myState ? "display: none" : "display:block"} //if myState is true/false it will set the style
return (<div>
<button onClick={this.onClick}>Click me to hide/show me </button>
<div id="myDiv" style={style}> Here you will hide/show div on click </div>
</div>)
}
}

Categories

Resources