4

I want a progressbar, which is a multibar. Looks like this:

[----------][----50%    ][        ]
[---20%    ][           ][        ]
[----------][-----------][---80%  ]

It has three distinctive part, RED-GREEN-RED:

[ RED RED  ][GREEN GREEN][ RED RED]

It is realtime, the value comes from a remote json api, if the value is in the green zone, everything is fine, otherwise action required from colleagues (if its under the desired zone, or above it).

So far I implemented the html+css, works fine in Firefox and Chrome. Here is the demo project on jsfiddle:

Now I need to "reactify" the code, to be alive:)

The problem is, I can not inline style the following CSS rule:

    progress[value]::-webkit-progress-value { /* Chrome */
  background-image:
    -webkit-linear-gradient(
      135deg,
      transparent 33%,
      rgba(0, 0, 0, 0.1) 33%,
      rgba(0, 0, 0, 0.1) 66%,
      transparent 66%
    ),
    -webkit-linear-gradient(
      top,
      rgba(255, 255, 255, 0.25),
      rgba(0, 0, 0, 0.25)
    ),
    -webkit-linear-gradient(
      left,
      rgba(255,0,0,0.8),
      rgba(255,0,0,0.8) 37.5%,
      rgba(0,255,0,0.8) 37.5%,
      rgba(0,255,0,0.8) 87.5%,
      rgba(255,0,0,0.8) 87.5%
    );
}

The reactified Progressbar.js is rather simple:

const Progressbar = ({percent, min, max}) => {

  let styleProgress = {  backgroundImage:
    `linear-gradient(
      90deg,
      rgba(255,0,0,0.1),
      rgba(255,0,0,0.1) ${min}%,
      rgba(0,255,0,0.1) ${min}%,
      rgba(0,255,0,0.1) ${max}%,
      rgba(255,0,0,0.1) ${max}%)`
  };

  return(
      <div className="wrapper-progressbar">
        <progress style={styleProgress} max="100" value={percent}>
        </progress>
      </div>
  );
};

I can set the faint backround (so min and max is always visible), but not the actual progressbar.

Does anyone has any idea, how to make dynamic css selectors with React?

ps: Only Chrome and Firefox support is required, as it will end up as a TV in a control room (Kiosk mode). So no IE support is needed at all. The html5 <progress> element would be the right solution imho.

Update: Here is a demo react project: https://jsfiddle.net/ave0x0nb/3/

The progressbar is either single colored (red 0-30%), has two colors (red 0-30%, green 30-70%), or tricolored (red 0-30%, green 30-70%, red 70-100%) depending the actual value. So if the value is 50% it is two colored (0-30% red and 30-50% green).

The problem right now (and hence the question), that it is always tricolored (the above react demo project), and do not respect the background progressbar's limits. See the 20%,50%,80% jsfiddle demo projects for reference.

Update2: I added an animation as a reference. progressbar

arcol
  • 1,510
  • 1
  • 15
  • 20
  • Have you tried using a function in your className attribute? Or, in one of the lifecycle functions, assign a property to the response of a function. For example, `componentWillUpdate() { this.setState({ color: this.getColor([nextProps]) }}` [https://stackoverflow.com/questions/34011322/call-function-inside-of-classname-react-js](https://stackoverflow.com/questions/34011322/call-function-inside-of-classname-react-js) – arodjabel Nov 20 '17 at 20:46
  • The progressbar component receives the value via props. It works fine (it is fired in ComponentDidMount, and updates react redux state). Also there is a numerical display (for the actual value), which works fine. (a simple react component with a single huge `` element. – arcol Nov 20 '17 at 20:48
  • https://stackoverflow.com/questions/41503150/adding-style-attributes-to-a-css-class-dynamically-in-react-app – arodjabel Nov 20 '17 at 20:51
  • It looks like to me that the value attribute should just be a props or state value. for example value={this.props.value} – arodjabel Nov 20 '17 at 20:53
  • [https://jsfiddle.net/jxnf88tj/](https://jsfiddle.net/jxnf88tj/) – arodjabel Nov 20 '17 at 21:43
  • added a react project and clarified the question. Sorry if it was confusing. – arcol Nov 20 '17 at 21:58

2 Answers2

0

function updateValue(){
 const progress = document.querySelector('progress');  
  progress.value = Math.round(Math.random()*100) + 1;
  console.log(progress.value);
}

const interval = setInterval(updateValue, 500);

setTimeout(function () {
 clearInterval(interval);
}, 4000);
.wrapper-progressbar {
  flex: 1;
}

progress {
  width: 100%;
  background-image:
    linear-gradient(
      90deg,
      rgba(255,0,0,0.1),
      rgba(255,0,0,0.1) 30%,
      rgba(0,255,0,0.1) 30%,
      rgba(0,255,0,0.1) 70%, 
      rgba(255,0,0,0.1) 70%);
  border: 0;
  height: 2em;
  border-radius: 9px;
  box-shadow: 0 2px 3px rgba(0, 0, 0, 0.25) inset;
}

progress[value]::-webkit-progress-bar {
  width: 100%;
  background: transparent;
  background-image:
    -webkit-linear-gradient(
      left,
      rgba(255,0,0,0.1),
      rgba(255,0,0,0.1) 30%,
      rgba(0,255,0,0.1) 30%,
      rgba(0,255,0,0.1) 70%,
      rgba(255,0,0,0.1) 70%);
  border: 0;
  height: 2em;
  border-radius: 9px;
  box-shadow: 0 2px 3px rgba(0, 0, 0, 0.25) inset;
}

progress[value] {
  background-color: transparent;
  border-radius: 5px;
  box-shadow: 0 2px 3px rgba(0, 0, 0, 0.25) inset;
  position: relative;
  /* Reset the default appearance */
  -webkit-appearance: none;
  -moz-appearance: none;
  appearance: none;
  /* Get rid of default border in Firefox. */
  /*border: none;*/
}

progress[value]::-moz-progress-bar { /* Firefox */
  border-radius: 2px;
  background-size: 65px, 100%, 100%;
  background-color: transparent;
  background-image:
    -moz-linear-gradient(
      135deg,
      transparent 33%,
      rgba(0, 0, 0, 0.1) 33%,
      rgba(0, 0, 0, 0.1) 66%,
      transparent 66%
    ),
    -moz-linear-gradient(
      top,
      rgba(255, 255, 255, 0.25),
      rgba(0, 0, 0, 0.25)
    ),
    -moz-linear-gradient(
      left,
      rgba(255,0,0,0.8),
      rgba(255,0,0,0.8) 100%,
      rgba(0,255,0,0.8) 100%,
      rgba(0,255,0,0.8) 100%,
      rgba(255,0,0,0.8) 100%
    );
}

progress[value]::-webkit-progress-value { /* Chrome */
  border-radius: 2px;
  background-size: 65px, 100%, 100%;
  background-color: transparent;
  background-image:
    -webkit-linear-gradient(
      135deg,
      transparent 33%,
      rgba(0, 0, 0, 0.1) 33%,
      rgba(0, 0, 0, 0.1) 66%,
      transparent 66%
    ),
    -webkit-linear-gradient(
      top,
      rgba(255, 255, 255, 0.25),
      rgba(0, 0, 0, 0.25)
    ),
    -webkit-linear-gradient(
      left,
      rgba(255,0,0,0.8),
      rgba(255,0,0,0.8) 100%,
      rgba(0,255,0,0.8) 100%,
      rgba(0,255,0,0.8) 100%,
      rgba(255,0,0,0.8) 100%
    );
}
    <div className="wrapper-progressbar">
      <progress style={styleProgress} max="100" value="90"></progress>
    </div>
arodjabel
  • 420
  • 5
  • 14
  • You missed the whole point. The progressbar is tricolored. Yours is all red. Look at the 80% case for example: https://jsfiddle.net/4cLa8kra/ – arcol Nov 20 '17 at 21:52
0

Use classnames npm package. You can send the progress in your component (say 20%, 50%, 80%) and accordingly apply classnames in your component as :

progressBarClass = classNames('defaultClass', {
        'green': props.progress < PROGRESS_VALUE.green,
        'yellow': PROGRESS_VALUE.green < props.progress < PROGRESS_VALUE.yellow,
        'red': props.progress > PROGRESS_VALUE.red
      }),

in render:

render(){
   return (
       <div classNames={progressBarClass}>{'progress-bar'}</div>   
   );
}

in css file:

.green {
  //...green styles
}

.yellow {
  //...yellow styles
}


.red {
  //...red styles
}

I haven't written the exact code but you can conditionally apply classes like this. Instead of doing inline-css because it makes your code unreadable and I personally don't prefer inline-styles.

Ajay Gaur
  • 5,140
  • 6
  • 38
  • 60
  • Your progressbar is either green or yellow or red. While I want to have a progressbar with three colors (above 70%), two colors (above 30%), and single color below 30%. I will make an animated gif these evening, to make it extra clear. This "display" will replace an old physical display, with a series of LEDs, which looked like this: `[G][G][G][R][R][R][G][G][G]`, so this progressbar must imitate a series of led. – arcol Nov 21 '17 at 12:49
  • I haven't given you the exact solution but a gist how it should be done than putting inline styles to it. I guess you can write the solution yourself if you get to know how to do it and as stackoverflow community, that should be enough – Ajay Gaur Nov 21 '17 at 13:29
  • With your idea, I should create exactly 100 css rules, and apply them proportional to the percent. It is kind of bruteforce, no? Otherwise you may misunderstood the question. – arcol Nov 21 '17 at 18:14
  • Bruteforce? There will be 3 styles and they'll be segregated and more understandable. Instead messing your whole CSS file, you can just make 3 classes (or more), take out some common styles and you will able to write maintainable code. – Ajay Gaur Nov 21 '17 at 18:20
  • I uploaded an animation (original post). I can not imagine how to replicate that animation with your css classes, unless I create one class for each percent value: class_000, class_001 ... class_099, class_100. Am I missing something with your solution? – arcol Nov 21 '17 at 18:45