Have both JSX and normal className together - javascript

I have a css style class that needs to be in JSX form as it depends on the state, but I also need to use style class the ordinary way for styling. How do I combine these together?
I've tried
<span className={statusColor} className="uppercase">
Which only takes into account the last class one
<span className={statusColor uppercase}>
Which obviously looks for a variable called uppercase and errors out
<span className="uppercase {statusColor}">
Neither of them work
Below is a dummied-down version of my code. What is the correct way to do this?
const Component = React.createClass({
return {
completed: false
}
},
render () {
let statusColor;
this.state.completed === true ? statusColor = "green" : statusColor = "red";
return (
<div className="status-banner">
<span className="kata-text-status">Status: <span className={statusColor} className="uppercase">{this.state.completed.toString()}</span></span>
</div>
)
}
})

Try this:
<span className={`uppercase ${statusColor}`}>
This will use an ES6 template string (`uppercase ${statusColor}`) and interpolate statusColor with ${...}.

<span className={'uppercase' + ' ' + statusColor}>

You can do it in several ways.
Firstly you can use + to concat more than one string variables and generate dynamic strings
<span className={"uppercase " + statusColor}>
Or you can use npm modules classnames.

The brackets {} translate to normal JS. So you can just use + to concatenate.
<span className={"uppercase " + statusColor}>

Related

How to add multiple classNames to nextjs elements

I have an unordered list element that looks like this:
<ul className={styles["projects-pd-subdetails-list"]}>
{detail.subdetails.map((sub) => (
<li
className={styles["projects-pd-text projects-pd-subdetail"]}
>
{sub}
</li>
))}
</ul>
With a normal React element, I would be able to apply the multiple classes for the li element like this:
<li className="projects-pd-text projects-pd-subdetail">{sub}</li>
However, having a space like I do in nextjs means the styles are just getting ignored. How can I fix this problem and properly account for two classNames for my li element here?
You can use multiple className like this
<li className={`${styles.projects-pd-text} ${styles.projects-pd-subdetail}`}>
{sub}
</li>
But there is a problem. It may throws an error(I guess, not sure). You may use camelCase in your css className.
<li className={`${styles.projectsPdText} ${styles.projectsPdSubdetail}`}>
{sub}
</li>
or, if you don't want to camelCase
<li className={`${styles["projects-pd-text"]} ${styles["projects-pd-subdetail"]}`}>
{sub}
</li>
Let me know if it works.
A simple array join should suffice.
["class1", "class2", "class3"].join(" ")
result: "class1 class2 class3"
<li className={[styles.projects_pd_text, styles.projects_pd_subdetail].join(" ")}>
{sub}
</li>
Or save it as a variable for multiple uses
const listClasses = [styles.projects_pd_text, styles.projects_pd_subdetail].join(" ")
As stated in my original comment I have not worked with Next.js.
It appears as though styles is a map of some kind i.e.:
const styles = {
"projects-pd-subdetails-list": "Class Name A",
"projects-pd-text": "Class Name B",
"projects-pd-subdetail": "Class Name C"
}
This means that by using a line similar to styles["projects-pd-text projects-pd-subdetail"] you are attempting to retrieve the value for the key "projects-pd-text projects-pd-subdetail" which does not exist.
I would suggest retrieving the values individually from the map and then joining them together with your choice of string concatenation.
className={styles["projects-pd-subdetail"] + " " + styles["projects-pd-text"]}
// OR
className={`${styles["projects-pd-subdetail"]} ${styles["projects-pd-text"]}`}
clsx is generally used to conditionally apply a given className.
https://www.npmjs.com/package/clsx
Because it might be tedious to always write styles.className for every class you need to add to an element, you can create a utility function that makes things look neater.
For example, in my case, I created this function:
export const classes = (styles: any, classes: string) => {
const list = classes.split(' ');
classes = '';
for (const className of list) {
classes += `${styles[className] }`
}
return classes;
}
in a util file.
And on elements, I can do this
<div className={classes( styles, 'hero-container text-success text-bold italicize another-class yet-another-class ')}>
Lorem ipsum dolor
</div>
A PLUS:
One other issue around classnames I encountered getting started with NextJS on VSCode was getting emmet tag generation to list the classnames the NextJS way when I type css class selector.
"Complete NextJS Classname Emmet": {
"scope": "javascript,typescript,typescriptreact,javascriptreact",
"prefix": ["."],
"body": [
"<div className={classes( styles, '$1')}>",
"\t$2",
"</div>"
],
"description": "Lean autocomplete emmet in nextjs css modules classname"
}
I added this to my VSCode > Preferences > User Snippets > typescriptreact.json
These made working with classnames in NextJS easier for me - especially on VSCode.
It worked for me.
<div className={styles.side +" "+ styles.side2}>side2</div>
Thanks to CodenNerd
If you console log your css or scss module class (ex. ImportedStyleModule.someClassName) you'll see it's just a string that has an auto generated UID concatenated.
Ergo, it's just a string so you can use a number of ways to join them like so:
//ts
const mergeStyles = (styleArray: string[]) => (styleArray.map((style: string) => `${style}`).join(" "));
//js
const mergeStyles = (styleArray) => (styleArray.map((style) => `${style}`).join(" "));
//example
return (
<span
onClick={() => setIsClicked(!isClicked)}
className={`${!!isClicked
? mergeStyles([NavbarStyle.navButton, NavbarStyle.navButtonOpen])
: NavbarStyle.navButton}`}
>
<Image src='/image-logo.svg' alt='logo' width='80px' height='40px' />
</span>
);
<div className={`${GloStyles.flexRow} ${MyStyles.gap_10rem}`}> Hello </div>
Explanation
className accept 'String' only
use 'String template' solved,
it is just like a normally react app className, nothing fancy
enter image description here
enter image description here
a Function would be much cleaner
const convertToNextClassName = (className) => className.split(' ').map(c => styles[c]).join(' ')
then in the jsx you can directly call it
<div className={convertToNextClassName("firstClass secondClass")}></div>
for conditional i suggest using classnames from npm
The array join() is the cleanest solution, as #rain mentioned here.
You can also use global css class names.
<span className={[styles.grid, "disabled"].join(" ")}>content</span>
This is how it works for me in my style component
<ul>
<li className={router.pathname == "/" && styles.active}><Link href="/">Home</Link></li>
<li className={router.pathname == "/about" && styles.active}><Link href="/about">About</Link></li>
<li className={router.pathname == "/contact" && styles.active}><Link href="/contact">Contact</Link></li>
<li><Link href="/404">404</Link></li>
</ul>

Add flags to Alpine.js UI - dynamic class name

I'm trying to add:
https://github.com/lipis/flag-icon-css
to my app so that when someone clicks on a mapbox country I can show the flag
Is there a way that you would recommend how to do this?
I tried the following but I get TypeError: (void 0) is undefined
<span class="flag-icon" x-bind:class="{ [`flag-icon-${$store.ui.clicked.flag}`]: true }">
<h3 class="pt-3 mb-3 ml-5 text-lg " x-text="$store.ui.clicked.name">Country / City name</h3>
</span>
Spruce.store('ui', {
clicked: {
name: 'Welcome!',
u: 'US',
flag: (this.region_ident ?? this.u).toLowerCase().slice(0, 2),
},
})
edit: inside my on map click function I have this:
Spruce.store('ui').clicked = {
...Spruce.store('ui').clicked,
city: e.features[0].properties,
name,
}
Maybe I could try:
$el.addClass(`flag-icon-${$store.ui.clicked.flag}`)
But I am not sure where to put that.
This works but I think an Alpine.js solution would be more succinct.
document.getElementById('flag-show-bottom').removeAttribute('class')
document
.getElementById('flag-show-bottom')
.classList.add(
'flag-icon',
`flag-icon-${e.features[0].properties.u.toLowerCase().slice(0, 2)}`,
)
I finally figured it out. x-bind: takes an expression. You can use it on anything. it is really versatile. You don't have to use :class={ className: bool } format.
You can just use strings:
<img src="../images/blank.gif"
x-bind:class="`flag ${ $store.ui.clicked.country
? 'flag-' + $store.ui.clicked.country.region_ident.toLowerCase().slice(0, 2)
: $store.ui.clicked.city
? 'flag-' + $store.ui.clicked.city.u.toLowerCase().slice(0, 2)
: '' }`" />
(I switched to using a different flag library but the logic is the same)

How to add multiple condition in a single class using Angular.js

I need one help. I need to add multiple condition with AND operator in ng-class using Angular.js. I am explaining my code below.
<div class="mainaccordion_title-width" ng-click="manageCollapseExpand(place, false)">
<i ng-class="{'fa fa-minus-circle': place.expanded, 'fa fa-plus-circle': !place.expanded }"></i>
{{place.root_des}}
</div>
Here I am adding one condition but I need to add another condition (i.e-place.cstatus==1 and place.cstatus==0) for both class. Please help.
Use && instead of and.
(i.e-place.cstatus==1 && place.cstatus==0)
As commented before, you can also create a function that takes arguments and return classnames.
Benefit of this approach is, it will keep your view clean and the logic will stay in JS. This will also make it more configurable as you can write a code and share it across multiple elements.
function myCtrl($scope) {
$scope.place = {
expanded: false,
cstatus: 1,
root_des: 'test'
}
$scope.manageCollapseExpand = function(place, seleccted) {
place.expanded = !place.expanded
}
// assuming place should be an array
$scope.getClasses = function(index) {
var className = 'fa ';
if ($scope.place.expanded && $scope.place.cstatus === 1) {
className += 'fa-minus-circle '
} else {
className += 'fa-plus-circle ';
}
return className
}
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app ng-controller='myCtrl'>
<div class="mainaccordion_title-width" ng-click="manageCollapseExpand(place, false)">
<i ng-class="getClasses(0)"></i>
{{place.root_des}}
<p>Classes: {{getClasses(0)}}</p>
</div>
</div>
Simply you can conbind condition using &&
<div class="mainaccordion_title-width" ng-click="manageCollapseExpand(place, false)">
<i ng-class="{'fa fa-minus-circle': place.expanded && place.cstatus==1, 'fa fa-plus-circle': !place.expanded && place.cstatus==0 }"></i>
{{place.root_des}}
</div>

How to render Html tags passed as string property in React.js

Is it possible to pass HTML tags as properties and then render in React.js
For example ES2015 syntax:
I add the property as a string with HTML tags:
renderItems(posts) {
var groupDate = this.props.item.date.toUpperCase();
var listCol = '<h4>' + groupDate.toString() + '</h4>';
return (<ViewList list={posts} postCol1={listCol}/>);
}
And in my ViewList component I have:
<div className="col-md-9">{this.props.postCol1}</div>
This is rendered as :
<h4>JANUARY 28TH, 2016</h4>
How do I get react to render the date excluding the HTML tags?
Different views uses this component and tags may be different explaining why I would like to include the tag.
This actually worked:
renderItems(posts) {
var groupDate = this.props.item.date.toUpperCase();
return (<ViewList list={posts} postCol1={<h4>{groupDate.toString()}</h4>}/>);
}
Take the <h4> tags out of the listCol string. Your ViewList component should render them:
<div className="col-md-9">
<h4>
{this.props.postCol1}
</h4>
</div>

React keeps escaping the amp character (&) in attributes

I have a component with a data-icon attribute. The value of this attribute should be, for instance, &#xf00f so that css can render it via content: attr( data-icon );.
However, whatever I try: React keeps escaping to &. Even when I provide the proper unicode character \u0026#xf00f.
Is there some way to stop React from messing with that value? Besides dangerously setting inner html, as I do not want to add another wrapper.
Component
define( [ 'react', 'util' ], function( React, Util )
{
return React.createClass(
{
render: function()
{
//var amp = '\u0026',
var amp = String.fromCharCode( 38 ),
// Util.icons[x] returns a String, such as "f00f"
code = amp + '#x' + Util.icons[this.props.name] + ';';
return (
<i data-icon={code}>
{this.props.children ? <span>{this.props.children}</span> : null}
</i>
);
}
} );
} );
Usage
<Widget.Icon name="add" />
Output
<i data-icon="&#xf0fb;" data-reactid=".170lse36465.7.0"></i>
Well, I just realized, that for my particular use case I can simply get away with:
<i data-icon={String.fromCharCode( "f00f" )} />
https://github.com/facebook/react/issues/3769

Categories

Resources