get index value from react loop and pass it to css - reactjs

I created react component that is taking an array and stacking all the progress bars into one.
const ProgressBar = (props, {...customProps}) => {
const GroupProgressBar = [];
props.groups.map((group, i) => {
const widthValue = (group.value / group.max) * 100;
GroupProgressBar.push
(<div style={{width: `${widthValue}%`}}
key = {i}
className={`well-background--${group.concept}
animation
terra-ProgressGroup--progress
terra-ProgressBar--${props.heightSize}`}>
</div>);
})
return <div className='terra-ProgressGroup'> {GroupProgressBar} </div>
}
CSS (classes below i want to convert to single class):
....
.well-background--concept1 {
animation-delay: 1s;
z-index: -1;
}
.well-background--concept2 {
animation-delay: 2s;
z-index: -2;
}
.well-background--concept3 {
animation-delay: 3s;
z-index: -3;
}
I tried to convert these classes into one but have no luck
:root {
--concept-code: key;
--concept-code2: key;
}
.animation {
animation-delay: var(--concept-code)s;
z-index: var(--concept-code2);
}
Basically i dont want keep adding those similar classes so i am trying to create a single class and pass those numbers from react component. probably using key = {i}
How can i achieve that?

Why even bother with CSS for this case? The point of CSS is that the styles cascade and have hierarchy. If the style applies only to a specific React component, then it's best to render the appropriate styles directly.
const ProgressBar = (props, {...customProps}) => {
const GroupProgressBar = [];
props.groups.map((group, i) => {
const widthValue = (group.value / group.max) * 100;
GroupProgressBar.push
(<div
style={{
width: `${widthValue}%`,
animationDelay: /* based on the concept number */,
zIndex: /* based on the concept number */
}}
key = {i}
className={`well-background--${group.concept}
animation
terra-ProgressGroup--progress
terra-ProgressBar--${props.heightSize}`}>
</div>);
})
return <div className='terra-ProgressGroup'> {GroupProgressBar} </div>
}
Note that I'm not sure how you can generate the appropriate number for your style because I don't know your data structure. But that would be trivial enough. The important part is just rendering the style in the component instead of CSS.
There's not really a great way to dynamically inject CSS class style definitions into the page with React. And even so, that would be vastly over-engineering a solution for this problem. Better to just create lots of .well-background class definitions in the CSS manually, as many as you think you'll need (10? 20? How many will there really be?).

Related

Shorter way to use SCSS #exported classNames in React (Next.js + css-modules)

I have a set of semantic color classes in SCSS, which should be applied to components based on their props. I'm using React + Next.js + css-modules.
What I Want:
The current code I wrote below works correctly, but I want a simpler approach... declaring a bunch of classNames for every component to #extend something else is overkill! I want to write the extension directly in jsx part. Is there a better (more dynamic) way to do this? maybe inline extends?
Not a valid code, but I'm looking for something like this:
export default function Component({ status }) {
return (
<div style={#extend %{status}}>
...
</div>
)
}
Code
This is the semantics file. I import it inside other scss files to extend the classes:
/* _semantics.scss */
%warning {
background: orange;
color: red;
}
%error {
background: red;
color: black;
}
...
Example Component
/* component.module.scss */
#use "semantics" as *;
.warning {
#extend %warning;
}
.error {
#extend %error;
}
.success {
#extend %success;
}
// component.jsx
import css from "./component.module.scss"
export default function Component({ status }) {
return (
<div className={css[status]}>
...
</div>
)
}
// index.jsx
<Component status="warning">...</Component>
Notes
I am looking for an alternative way, so:
Using a package is fine
Using #include (mixins) instead of #extend is fine
Using .semantic-class instead of %semantic-class is fine
you can use global css to achieve this purpose

AgGrid: How to blink cell in different color depending on change

I'm using ag-grid-react like this:
import { AgGridReact } from "ag-grid-react";
import "ag-grid-community/dist/styles/ag-grid.css";
import "ag-grid-community/dist/styles/ag-theme-balham.css";
and declares it like this in render(some details removed for clarity):
return <div className="MarketGrid ag-theme-balham" >
<AgGridReact domLayout='autoHeight'
enableCellChangeFlash={true}
rowSelection={'single'}
onSelectionChanged={this.onSelectionChanged.bind(this)}
onGridReady={this.onGridReady.bind(this)} />
</div>;
the "enableCellChangeFlash" property works fine, and I know how to change the color of it, but what if I want to change the color depending on whether the new value is higher or lower than the previous?
My update code looks somewhat like this:
let row = this.gridApi.getRowNode(this.props.productIds.indexOf(id));
for (var f in data) {
row.setDataValue(f, data[f]);
}
}
I guess I should set the cell style somewhere, but I'm not sure where.
UPDATE: According to this page https://www.ag-grid.com/javascript-grid-data-update/#flashing I should do this:
"If you want to override how the flash presents itself (eg change the background color, or remove the animation) then override the relevant CSS classes."
Anyone knows how to do this?
I almost got this now. The answer I found out is based on CSS. Set the classname for blinking/stop-blinking like:
return (<div key={Math.random()}
className={some logic to set classname here, like "blink-red" or "blink-blue"}
onAnimationEnd={() => this.setState({ fade: false })}
> {this.state.value} </div>);
and use onAnimationEnd to set some state variable that will affect that logic.
Then add the fading logic like this to CSS:
.blink-red {
animation: redtotransparent 3s ease 5s;
}
#keyframes redtotransparent {
from {
background-color: red;
color: white;
}
to {
background-color: transparent;
color: black;
}
}
It's still not perfect, but almost there. Changing the key makes React think the DOM has changed. When I come up with anything better I will add it here.
Click on the Flashing Cell option in the dropdown at the top and then you can choose a different color for an up change and a down change. you can also set how long it will flash for.

How to start animation when props change (via redux) in React Native?

I have an overlayed view in my React Native app which I need to animate on and off screen when the user pushes a button. I know how to position the view and animate it but I can't work out how to trigger the animation.
My redux store has a very simple state with an isOpen flag saying whether the panel is open or closed. I map the state to the panel component's props and when the isOpen prop changes I want to trigger the open or close animation. Obviously if the user presses the toggle button mid animation the currently running animation needs to be cancelled.
This should be simple but I can't find any examples. Any help would be much appreciated.
React Native
To begin an animation on a change of props you can simply start your animation in componentDidUpdate. Here's an example:
componentDidUpdate(prevProps) {
if (this.props.isOpen !== prevProps.isOpen) {
this.state.animation.start();
}
}
Assuming your animation is defined in the component's state.
React (Browser):
[Not relevant to this question but potentially useful.]
A simple way to do this is using CSS transitions. What you can do is give the panel component a CSS class for closed (or open but I find using the closed/collapsed as a style easier because then the default is open).
Then in your panel's render:
render() {
const { isOpen } = this.props;
return <div className={ 'panel' + (isOpen ? '' : ' closed') }></div>
}
And in your CSS:
.panel {
/* some other styles */
transition: .5s ease-in;
}
.closed {
height: 0;
}
This way CSS can handle the animation logic and your concern of clicking the open/close button before the current animation has finished is addressed.
Here are the CSS transition docs: https://developer.mozilla.org/en-US/docs/Web/CSS/transition
Edit: A disadvantage of this method is that height must be explicitly set when the panel is open.
Here's a little example snippet:
function togglePanel() {
const panel = document.querySelector('div.panel');
if (panel.classList.contains('closed')) {
panel.classList.remove('closed');
} else {
panel.classList.add('closed');
}
}
.panel {
background-color: #00c0de;
height: 4rem;
overflow: hidden;
transition: .5s ease-in;
}
.closed {
height: 0;
}
<button onclick='togglePanel()'>Toggle Panel</button>
<div class='panel closed'>
<span>Hello, I'm the panel</span>
</div>

How to interpolate styles in React?

I want to color-code words by language in my React app. I have a styles file, which contains things like:
english: {
// style here
}
french: {
// style here
}
spanish: {
// style here
}
// etc.
Each of my words (entrys) is an object that contains a language key/value pair, i.e. {language: french}.
I could create a long case/switch statement, but I'm looking for a shorter way. Here's my case/switch statement:
var color;
switch (entry.language) {
case 'english':
color = styles.english;
break;
case 'french':
color = styles.french;
break;
// etc.
<div style={color}> {entry.word} </div>
That seems unnecessarily repetitive, especially if I have a lot of languages. Instead, I want to be able to just interpolate entry.language into my div, something like:
<div style={styles.{entry.language}} {entry.word} </div>
That doesn't work. Is there a way to do it?
What you want is to access an object. The bracket notation is what you need
<div style={styles[entry.language]} >{entry.word} </div>
I stumble on this page with a similar question but ended up finding the answer
from https://medium.com/capital-one-developers/cut-the-sass-how-to-use-inline-javascript-styles-for-everything-f5ac5b77ae57
import React from 'react';
import { StyleSheet, css } from 'aphrodite';
import { brandRed } from './colors';
const styles = StyleSheet.create({
foo: {
border: `1px solid ${brandRed}`,
},
});
const UserMenu = () => (
<div className={css(style.foo)}>
This box has a nice border.
</div>
);

React native drag and drop

I'm trying to implement drag and drop in a React Native application.
Has anyone out there used this ? Somehow I'm not able to drag the text. There are not enough steps to use the npm module.
After debugging for some time I found that onLayout & onLongPress are undefined, can anyone help me on this ?
Assuming you're new to React Native (as am I, kind of), and you don't have an example of your code, there might be a few things going on. (and there is a high chance you already know all of these things).
Did you follow this example? Here they set onLayout and onLongPress as the props of the DraggableThing component. Next they call the component with functions (onDragItemLayout, startDragHandler) bound to these properties. These functions are supplied by importing the createDropZone, did you import everything?
If you did, importing third-party components is kind of a hassle, because most of the times you're required to do some things in xCode. Not sure if that's also the case right now, but it might be something to look into.
Also my advice would be to take a look at the module supplied in the answer above.
This is a simple React drag and drop.
class Square extends React.Component {
constructor() {
super();
this.state = {
posx: 10,
posy: 10,
};
this.setDrag = this.setDrag.bind(this);
this.startDrag = this.startDrag.bind(this);
this.stopDrag = this.stopDrag.bind(this);
}
componentDidMount() {
this.sq.addEventListener('mousedown', this.setDrag);
}
startDrag(e) {
this.setState({
posx: parseInt(e.clientX - this.startPosX, 10),
posy: parseInt(e.clientY - this.startPosY, 10),
});
this.startPosX = e.clientX - this.state.posx;
this.startPosY = e.clientY - this.state.posy;
}
stopDrag() {
document.documentElement.removeEventListener('mousemove', this.startDrag);
document.documentElement.removeEventListener('mouseup', this.stopDrag);
this.sq.addEventListener('mousedown', this.setDrag);
}
setDrag(e) {
this.sq.removeEventListener('mousedown', this.setDrag);
this.startPosX = e.clientX - this.state.posx;
this.startPosY = e.clientY - this.state.posy;
document.documentElement.addEventListener('mousemove', this.startDrag);
document.documentElement.addEventListener('mouseup', this.stopDrag);
}
render() {
return (
<div
className='square'
style={{
left: this.state.posx,
top: this.state.posy,
}}
ref={(sq) => { this.sq = sq; }}
>
{this.state.posx}
</div>
);
}
}
ReactDOM.render(
<Square />,
document.getElementById('app')
);
.square {
position: absolute;
width: 100px;
height: 100px;
background: #900;
border: 1px solid #333;
}
<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>
<div id="app"></div>

Resources