Change style based on useState variable in React - html

I want to animate a search icon on click in React. I am using useRef hook to get the element and I pass some custom style to the component.
const [searchBar, setSearchBar ] = useState(false);
const searchIcon = useRef();
const searchIconStyle = {
transition: 'rotate .3s ease', // smooth transition
rotate: searchBar? "360deg" : "0",
}
function handleSearchClick(e) {
setSearchBar(prevState => !prevState);
}
So, the code from above is working first time when I click, but it doesn't afterwards. The search icon is a FontAwesome component
{searchBar && <input type="search" placeholder="Search product..."/>}
<FontAwesomeIcon icon={faMagnifyingGlass} className="search-icon"
onClick={handleSearchClick} ref={searchIcon} style={searchIconStyle}/>
How can I animate the icon on each click (depending on the change of searchBar variable?)

you're not setting the rotate property properly.
just change :
rotate: searchBar? "360deg" : "0",
to :
rotate: searchBar? "360deg" : "0deg",
this is a demo in codesandbox ( I used a button instead of FontAwesomeIcon cause you didn't tell which library you are using)

I like use classnames. Example for you:
className={classNames(
{ 'basicStyle': true},
{ 'withoutAnimation': !searchBar },
{ 'withAnimation': searchBar },
)}

Related

Autocomplete disable styling? (Picture attached)

I am using material-ui + react. I am building a simple form and everything is working fine except I have a small UI that Im not sure how to fix. Whenever I use the browser auto-complete, the input field remains highlighted. I've been digging through the CSS in the chrome dev tools and cannot find what is setting this.
This happens in all textfields right now.
Still happens when I remove custom styling from the component.
Hard to put into words so here are some screenshots:
Before:
After:
It then remains highlighted after I click away and select something else. However, if I type in a password this highlighting does not take place.
Component:
const useStyles = makeStyles(theme => ({
border: {
'& .MuiOutlinedInput-root.Mui-focused .MuiOutlinedInput-notchedOutline': {
borderColor: theme.palette.secondary.main
},
width: '75%',
alignSelf: 'center'
},
})
const LoginForm = () => {
const classes = useStyles();
<TextField
name='password'
type='password'
value={formik.values.password}
className={classes.border}
variant='outlined'
label='Password'
onChange={formik.handleChange}
onBlur={formik.handleBlur}
error={formik.touched.password && Boolean(formik.errors.password)}
helperText={formik.touched.password ? formik.errors.password : ''}
/>
}
Adding this to my theme.js seemed to fix the issue for me:
const theme = {
overrides: {
MuiOutlinedInput: {
input: {
'&:-webkit-autofill': {
WebkitBoxShadow: '0 0 0 100px #303030 inset',
WebkitTextFillColor: '#fff',
},
},
},
}
}

Angular/Typescript Text with routerLink

Updated Question for more Clarity:
Need to display some texts and links as innerHTML(data from service/DB) in the Angular HTML and when user clicks, it should go to Typescript and programmatically navigates by router.navigate
Also, How to add DomSanitizer from #ViewChild/ElementRef
Added all example in below code
Here is the updated stackblitz code
As shown in screenshot from angular.io some texts and some links
Sorry, I didn't realize you answered my comment. Angular routing is not secondary, if you don't use Angular modules you'll end up with just an HTML/CSS/Typescript application. you need at least the RouterModule for Angular to be able to use routing and hence, do what it's supposed to with the DOM.
First:
You are not importing RouterModule
solution:
imports: [
BrowserModule,
FormsModule,
RouterModule.forRoot([]) // this one
]
Second:
You can't bind Angular events through innerHTML property
fix:
Make use of #ViewChild directive to change your innerHTML property and manually bind to the click event, so change in your app.component.html from
<div id="box" [innerHTML]="shouldbedivcontent" ></div>
to
<div #box id="box"></div>
Now, in your app.component.ts, add a property to hold a reference to that "box" element so you can later make some changes to the dom with it:
#ViewChild('box') container: ElementRef;
Implement AfterViewInit, that hook is where you will be able to actually handle your container, if you try using it for example in OnInit you'd get undefined because that component's html is not in the dom yet.
export class AppComponent implements AfterViewInit {
and
ngAfterViewInit() {
this.container.nativeElement.innerHTML = this.shouldbedivcontent;
this.container.nativeElement.addEventListener('click',
() => this.goto('bar')
);
}
change shouldbedivcontent property from:
'1) this is a click
<a (click)="goto("bar")">Click</a><br>
2)this is with routerlink
<a routerLink="" (click)="goto("bar")">Click</a><br>
3)This only works with href
bar and test'
to
'1) this is a click
<a id="link_1">Click</a><br>
2)this is with routerlink
<a [routerLink]="" (click)="goto(\'bar\')">Click</a><br>
3)This only works with href
bar and test'
And even so you'd still not get the default anchor style unless you apply some styling yourself.
Third
You are not HTML sanitizing, which could be dangerous. read more here
MY SUGGESTION:
Seems like a lot to do for you and a lot to read for someone else working alongside you for something you could easily do like in the example below!
Move your html to your app.component.html:
<div id="box">
1) this is a click
<a (click)="goto('bar')">Click</a><br>
2)this is with routerlink
<a routerLink="" (click)="goto('bar')">Click</a><br>
3)This only works with href
bar and test
</div>
<p>Below is actual content</p>
You'll notice that everything works now, except the anchor without routerLink or href, because that's not a link.
EDIT:
Looking at the new stackblitz, i suggest a change of approach, binding to innerHTML is ok when working with plain text or even some simple html but not a great choice to bind events or routing logic.
Angular's Renderer2 provides with a bunch of methods to dyncamically add elements to the DOM. With that on the table, you just need a little effort to take that simple html you get from your backend and turn it into something like (paste this property in your code to test it along the rest of the code provided below):
public jsonHTML = [
{
tagName: '',
text: 'some text with click ',
attributes: {
}
},
{
tagName: 'a',
text: 'bar',
attributes: {
value: 'bar' // goto parameter
}
},
{
tagName: '',
text: ' some more text with click ',
attributes: {
}
},
{
tagName: 'a',
text: 'foo',
attributes: {
value: 'foo' // goto parameter
}
}
]
Once you have it, it's way easier to create all of those elements dynamically:
this is for the code in your Q1:
Inject Renderer2 with private r2: Renderer2
And replace the Q1 related code in AfterViewInit hook to:
const parent = this.r2.createElement('div'); // container div to our stuff
this.jsonHTML.forEach((element) => {
const attributes = Object.keys(element.attributes);
const el = element.tagName && this.r2.createElement(element.tagName);
const text = this.r2.createText(element.text);
if (!el) { // when there's no tag to create we just create text directly into the div.
this.r2.appendChild(
parent,
text
);
} else { // otherwise we create it inside <a></a>
this.r2.appendChild(
el,
text
);
this.r2.appendChild(
parent,
el
);
}
if (attributes.length > 0) {
attributes.forEach((name) => {
if (el) {
this.r2.setAttribute(el, name, element.attributes[name]); // just the value attribute for now
if (name === 'value') {
this.r2.listen(el, 'click', () => {
this.goto(element.attributes[name]); // event binding with property "value" as parameter to navigate to
})
}
} else {
throw new Error('no html tag specified as element...');
}
})
}
})
this.r2.appendChild(this.container.nativeElement, parent); // div added to the DOM
No html sanitizer needed and no need to use routerLink either just inject Router and navigate to the route you want! Make improvements to the code t make it fit your needs, it should be at least a good starting point
Good Luck!
You have a css problem.
looks like a link
<a [routerLink]="something"></a> looks like a link, because if you inspect the HTML it actually gets an href property added because of routerLink
<a (click)="goTo()"></a> does NOT look like a link, because there is no href
Chrome and Safari default user agents css will not style <a> without an href (haven't confirmed Firefox but I'm sure its likely). Same thing for frameworks like bootstrap.
Updated stackblitz with CSS moved to global, not app.css
https://stackblitz.com/edit/angular-ivy-kkgmkc?embed=1&file=src/styles.css
This will style all links as the default blue, or -webkit-link if that browser supports it. It should be in your global.css file if you want it to work through the whole app.
a {
color: rgb(0, 0, 238);
color: -webkit-link;
cursor: pointer;
text-decoration: underline;
}
this works perfectly for me :D
#Directive({
selector: "[linkify]",
})
// * Apply Angular Routing behavior, PreventDefault behavior
export class CustomLinkDirective {
#Input()
appStyle: boolean = true;
constructor(
private router: Router,
private ref: ElementRef,
#Inject(PLATFORM_ID) private platformId: Object
) {}
#HostListener("click", ["$event"])
onClick(e: any) {
e.preventDefault();
const href = e.target.getAttribute("href");
href && this.router.navigate([href]);
}
ngAfterViewInit() {
if (isPlatformBrowser(this.platformId)) {
this.ref.nativeElement.querySelectorAll("a").forEach((a: HTMLElement) => {
const href = a.getAttribute("href");
href &&
this.appStyle &&
a.classList.add("text-indigo-600", "hover:text-indigo-500");
});
}
}
}
HOW I USE IT
<p linkify
class="mt-3 text-lg text-gray-500 include-link"
[innerHtml]="apiSectionText"
></p>
result

How to map a json list only after an onclick event in react?

Is there a way to use map() in order to feel objects into a datalist element, only after user clicks on that input?
for instance, I want the list of languages below to start filling data only if the user clicks on its input:
import React, { Component } from 'react';
import languages from './languages.json';
const langLi = languages.languages.map(languages => // i want this to happen only after clicking the input
<option>
{languages.lang}
</option>
)
class Form extends Component {
render () {
return (
<form>
<input type='text' list='langData'/>
<datalist id='langData'>{langLi}</datalist> //
</form>
)
}
}
When I inserted an onClick trigger to the input and wrote a function that fills the datalist's innerHTML, nothing happened. Is there a way to solve this?
Thanks!
Example of languages.json:
{
"languages":[
{"lang": "Chinese",
"country": "China",
"rank": 1},
{"lang": "Spanish",
"country": "Spain",
"rank": 2},
{"lang": "English",
"country": "United Kingdom",
"rank": 3}
]
}
You need to manage state on your React Component and insert elements via call function when event of your input is calling.
import React, { Component } from 'react';
import languages from './languages.json';
class Form extends Component {
construct(props) {
super(props);
this.state = {
languages: []
}
}
addLanguagesHandler = () => {
const myLanguages = this.state.languages;
myLanguages.push(); // <= Push your languages here
// Map new languages to state
this.setState({
languages: myLanguages
})
}
render () {
return (
<form>
<input type='text' list='langData' onFocus={() => this.addLanguagesHandler()} />
<datalist id='langData'>
{this.state.languages.map((language, index) => (
{/* Your logic for UI */}
<option key={index}>{language}</option>
))}
</datalist>
</form>
)
}
}
Yes, of course you have, and more than one
But one important thing to note: you are inside react, therefore you need to use react and not manipulate Dom directly.
You can fill state. I. e. this. setState({languages: fill()}) inside onclick
You can use a reducer
You can run this. languages = fillMyList(); and then this.forceUpdate(). this will fill the list with languages and then will rerender react shadow Dom (which in turn will update the real dom

Semantic Ui Variables and Overrides

Hey everyone, I am using the React Semantic Ui Library and I have a question about theming.
I am using a menu collection and I want to custom 1 thing:
in semantic-UI/site/collections/menu.variables I have my variable #secondaryPointingActiveBorderColor: #black;
and I want to change his color if, but if I do that in this file that will be global for all menu and I want to edit the color just for one specific menu so I moved on my menu.overrides file but how can I select my variable with CSS?
In my project I am using the Semantic Ui Component like this :
<Tab className={styles.tab} menu={{ secondary: true, pointing: true }} panes={panes} />
In the picture, you can see the React Component and his HTML equivalent? I tried with
.tab.ui.pointing.secondary.menu{
border-color:red;
}
The selector was
.ui.secondary.pointing.menu .active.item {
border-color: red;
}
The class to target the required element in your case can be achieved by:
.ui.secondary.pointing.menu .active.item {
border-color: red;
}
But this till affect all the menu active items, so hence we need to have our own specific selecter.
So I'll be adding a class named as stack-overflow (change the name acc to your use case)
import React from "react";
import { Tab } from "semantic-ui-react";
import "./style.css";
const panes = [
{
menuItem: "Tab 1",
render: () => <Tab.Pane attached={false}>Tab 1 Content</Tab.Pane>
},
{
menuItem: "Tab 2",
render: () => <Tab.Pane attached={false}>Tab 2 Content</Tab.Pane>
},
{
menuItem: "Tab 3",
render: () => <Tab.Pane attached={false}>Tab 3 Content</Tab.Pane>
}
];
const TabExampleSecondaryPointing = () => (
<Tab
className="stack-overflow"
menu={{ secondary: true, pointing: true }}
panes={panes}
/>
);
export default TabExampleSecondaryPointing;
// style.css
.stack-overflow .ui.secondary.pointing.menu .active.item {
border-color: red;
}
Here is a sandbox for demonstration: Sandbox link

How can I access a hover state in reactjs?

I have a sidenav with a bunch of basketball teams. So I would like to display something different for each team when one of them is being hovered over. Also, I am using Reactjs so if I could have a variable that I could pass to another component that would be awesome.
React components expose all the standard Javascript mouse events in their top-level interface. Of course, you can still use :hover in your CSS, and that may be adequate for some of your needs, but for the more advanced behaviors triggered by a hover you'll need to use the Javascript. So to manage hover interactions, you'll want to use onMouseEnter and onMouseLeave. You then attach them to handlers in your component like so:
<ReactComponent
onMouseEnter={() => this.someHandler}
onMouseLeave={() => this.someOtherHandler}
/>
You'll then use some combination of state/props to pass changed state or properties down to your child React components.
ReactJs defines the following synthetic events for mouse events:
onClick onContextMenu onDoubleClick onDrag onDragEnd onDragEnter onDragExit
onDragLeave onDragOver onDragStart onDrop onMouseDown onMouseEnter onMouseLeave
onMouseMove onMouseOut onMouseOver onMouseUp
As you can see there is no hover event, because browsers do not define a hover event natively.
You will want to add handlers for onMouseEnter and onMouseLeave for hover behavior.
ReactJS Docs - Events
For having hover effect you can simply try this code
import React from "react";
import "./styles.css";
export default function App() {
function MouseOver(event) {
event.target.style.background = 'red';
}
function MouseOut(event){
event.target.style.background="";
}
return (
<div className="App">
<button onMouseOver={MouseOver} onMouseOut={MouseOut}>Hover over me!</button>
</div>
);
}
Or if you want to handle this situation using useState() hook then you can try this piece of code
import React from "react";
import "./styles.css";
export default function App() {
let [over,setOver]=React.useState(false);
let buttonstyle={
backgroundColor:''
}
if(over){
buttonstyle.backgroundColor="green";
}
else{
buttonstyle.backgroundColor='';
}
return (
<div className="App">
<button style={buttonstyle}
onMouseOver={()=>setOver(true)}
onMouseOut={()=>setOver(false)}
>Hover over me!</button>
</div>
);
}
Both of the above code will work for hover effect but first procedure is easier to write and understand
I know the accepted answer is great but for anyone who is looking for a hover like feel you can use setTimeout on mouseover and save the handle in a map (of let's say list ids to setTimeout Handle). On mouseover clear the handle from setTimeout and delete it from the map
onMouseOver={() => this.onMouseOver(someId)}
onMouseOut={() => this.onMouseOut(someId)
And implement the map as follows:
onMouseOver(listId: string) {
this.setState({
... // whatever
});
const handle = setTimeout(() => {
scrollPreviewToComponentId(listId);
}, 1000); // Replace 1000ms with any time you feel is good enough for your hover action
this.hoverHandleMap[listId] = handle;
}
onMouseOut(listId: string) {
this.setState({
... // whatever
});
const handle = this.hoverHandleMap[listId];
clearTimeout(handle);
delete this.hoverHandleMap[listId];
}
And the map is like so,
hoverHandleMap: { [listId: string]: NodeJS.Timeout } = {};
I prefer onMouseOver and onMouseOut because it also applies to all the children in the HTMLElement. If this is not required you may use onMouseEnter and onMouseLeave respectively.
This won't work for OP because they wanted a variable but for those who just want a UI hover effect it's usually easier to stick with CSS.
Below example will reveal a delete button when an item is hovered over:
<div className="revealer">
<div>
{itemName}
</div>
<div className="hidden">
<Btn label="Delete"/>
</div>
</div>
.hidden {
display: none;
}
.revealer:hover .hidden {
display: block;
}
Parent div has revealer class. When it's hovered over, it'll reveal the hidden div. Hidden div must be nested inside revealer div.
You can implement your own component logics using those events which stolli and BentOnCoding suggested above, or use the module named react-hover
if I could have a variable that I could pass to another component that would be awesome.
then you can simply wrap another component
<ReactHover options={optionsCursorTrueWithMargin}>
<Trigger type="trigger">
<TriggerComponent />
</Trigger>
<Hover type="hover">
<HoverComponent />
</Hover>
</ReactHover>
or your plain HTML:
<ReactHover options={optionsCursorTrueWithMargin}>
<Trigger type="trigger">
<h1 style={{ background: '#abbcf1', width: '200px' }}> Hover on me </h1>
</Trigger>
<Hover type="hover">
<h1> I am hover HTML </h1>
</Hover>
</ReactHover>
demo code here: demo
Using useState,
import React, { useState } from "react";
function App() {
const [ishover,sethover]=useState(false);
function MouseOver() {
sethover(true);
}
function MouseOut() {
sethover(false);
}
return (
<div>
<button
style={{backgroundColor: ishover?"black":null}}
onMouseOver={MouseOver}
onMouseOut={MouseOut}
onClick={handleClick}
>
Submit
</button>
</div>
);
}
export default App;
You can try to implement below code. Hover functionality can be acheived with Tooltip.
Please refer below code and link for clarity
https://mui.com/material-ui/react-tooltip/
import * as React from 'react';
import DeleteIcon from '#mui/icons-material/Delete';
import IconButton from '#mui/material/IconButton';
import Tooltip from '#mui/material/Tooltip';
export default function BasicTooltip() {
return (
<Tooltip title="Delete">
<IconButton>
<DeleteIcon />
</IconButton>
</Tooltip>
);
}