How to add animation on container changing its width realtime - reactjs

I am making a form that expands when the user enters any character/key and gets back to its original width when there is no character left, for the plus point, I want to add animation to that expandable form so it gives a smooth look.
Here is the code and I want this action as follows

Not in React but I made an example in Vanilla JS hope it helps. I used CSS variables for changing width and making it smooth.
Instead of removing svg icons completely try scaling it down to 0 or changing it opacity to 0
const btns = Array.from(document.querySelectorAll('.btn'))
const input = document.querySelector('input')
btns.shift() // for leaving first button
input.addEventListener('input', (event) => {
btns.forEach((btn) => {
if (event.target.value.trim().length > 0) {
btn.classList.add('scale-0')
btn.parentElement.classList.add('width-50')
} else {
btn.classList.remove('scale-0')
if (btn.parentElement.classList.contains('width-50')) {
btn.parentElement.classList.remove('width-50')
}
}
})
})
#import 'https://cdn.jsdelivr.net/gh/KunalTanwar/normalize/css/normalize.inter.min.css';
body {
display: grid;
place-items: center;
}
.row {
--column-gap: 1rem; /* 8px */
width: 100%;
display: flex;
max-width: 525px;
column-gap: var(--column-gap);
}
.row input {
flex-grow: 1;
border-radius: 100vmax;
padding: 0.75rem 1.25rem;
border: 1px solid #c1c1c1;
}
.container {
--column-gap: 0.5rem; /* 16px */
--btn-size: 3.125rem; /* 50px */
flex-shrink: 0;
display: inherit;
column-gap: var(--column-gap);
width: calc(var(--column-gap) * 3 + var(--btn-size) * 4);
transition: width 250ms ease;
}
.container.width-50 {
width: var(--btn-size);
}
.container .btn {
width: var(--btn-size);
border-radius: 50%;
aspect-ratio: 1/1;
border: 1px solid #c1c1c1;
transition: transform 250ms ease;
}
.container .btn.scale-0 {
transform: scale(0);
}
<div class="row">
<div class="container">
<button class="btn">1</button>
<button class="btn">2</button>
<button class="btn">3</button>
<button class="btn">4</button>
</div>
<input type="text" />
</div>

Related

Structuring Sidebar in React

Relatively new to React, and am wanting to recreate the design below
enter image description here
I have the base formatting down, but as you will notice, there are lines separating the logo blocks, from the login and signup blocks, with the signup and login buttons pushed to the bottom.
Below is my current code
CSS:
width: 100vw;
height: 100vh;
}
body {
margin: 0;
padding: 0;
}
.Sidebar {
height: 100%;
width: 20%;
background-color: white;
border-right: 1px solid #F0F4FB;
padding-left: 15px;
box-sizing: border-box;
}
.SidebarList {
height: auto;
width: 100%;
padding-left: 15px;
font-size: 18px;
border: 2px #FD954E;
box-sizing: border-box
}
.SidebarList .row {
width: 100%;
height: 50px;
background-color: white;
list-style-type: none;
margin: 0%;
padding-bottom: 15px;
display: flex;
color: #A7ACB6;
justify-content: center;
align-items: center;
font-family: Arial, Helvetica, sans-serif;
}
.SidebarList .row:hover {
cursor: pointer;
background-color: #E7E7E7 ;
}
.SidebarList #active {
background-color: white;
color: #FD954E
}
.SidebarList .Login {
background-color: white;
color: #FD954E;
width: 279px;
height: 39px;
right: 1596px;
top: 958px;
border: 1px solid #FD954E;
box-sizing: border-box;
border-radius: 19.5px;
}
.SidebarList .SignUp {
width: 279px;
height: 39px;
right: 1596px;
top: 1011px;
background: #FD954E;
border-radius: 19.5px;
border: none;
}
.row #icon {
flex: 30%;
display: grid;
place-items: center;
transform: scale(1.2)
}
.row #title {
flex: 70%;
}
.Logo {
padding-left: 25px;
padding-top: 25px;
padding-bottom: 30px;
border-bottom: 1px solid #F0F4FB;
width: 55%;
}
Sidebar.js
import React from "react";
import "../App.css";
import { SidebarData } from './SidebarData'
import Logo from './Logo.svg'
function Sidebar() {
return (
<div className="Sidebar">
<div>
<img src = {Logo} alt='Logo’ className=‘Logo’ />
</div>
<ul className="SidebarList">
{SidebarData.map((val, key) => {
return (
<li
key={key}
className="row"
id={window.location.pathname == val.link ? "active" : ""}
onClick={() => {
window.location.pathname = val.link;
}}
>
<div id="icon">{val.icon}</div> <div id="title">{val.title}</div>
</li>
);
})}
</ul>
<div className= "SidebarList">
<button className="Login">
Login
</button>
</div>
<div className= "SidebarList">
<button className="SignUp">
Sign Up
</button>
</div>
</div>
);
}
export default Sidebar;
How should I structure my code in order to acheive my desired result? Ex: with the logo at the top with the seperator, the list of navigation elements, and then the login and signup buttons at the bottom with the seperator?
Currently, my sidebar looks as follows, with the seperator not full width between the logo and navigation elements, and the buttons extending beyond the sidebar.
enter image description here
It would be easier to simplify the problem with just HTML and CSS as that's much easier to troubleshoot. Part of your problem is that you are defining the width of the sidebar as a percentage of the screen width but elements within the sidebar are defined with a width in pixels. When the browser window is too small, your buttons will appear outside the full width of the sidebar. You could either code all your values as percentages or in pixels. Alternatively, you could use a mix and just set a min-width for the sidebar so that you don't end up with elements out of place.
The reason that your line break is not the full width of your sidebar is because you are defining it with the border-bottom property of the logo. Your logo is not 100% the width of the sidebar so your line break will only be the width of the logo. A better solution would be to define a div that is set to width: 100%. This way, you will have more control.
Here is a simplified solution to your sidebar problem using pixels to define the widths.
HTML:
<div class="sidebar">
<div class="header">
<div class="logo">Logo</div>
</div>
<div class="line-break" />
<div class="content">
<ul class="nav">
<li>Home</li>
<li>Blog</li>
</ul>
</div>
<div class="line-break" />
<div class="footer">
<button class="login">Login</button>
<button class="sign-up">Sign up</button>
</div>
</div>
CSS:
html, body {
height: 100%;
margin: 0;
padding: 0;
}
.sidebar {
width: 300px;
height: 100%;
border-right: 1px solid grey;
}
.line-break {
height: 1px;
width: 100%;
background-color: grey;
}
.header .logo {
height: 40px;
width: 200px;
background-color: grey;
margin: 20px;
}
ul.nav {
list-style-type: none;
padding: 20px 0 0 40px;
}
ul.nav li {
margin-bottom: 14px;
}
ul.nav li:last-child {
margin-bottom: 0;
}
.footer {
display: flex;
flex-direction: column;
align-items: center;
padding: 20px 0;
}
.footer button {
padding: 6px 0;
width: 80%;
margin-bottom: 14px;
}
.footer button:last-child {
margin-bottom: 0;
}
And here is a link to a CodePen
where you can see this in action.

Unable to limit the maxlength and minlength for input type number

JSX:
const input = (props) => {
return (
<div>
<span className="input-icon">{props.icon}</span>
<input
type={props.type || "number"}
className={props.inputClassName || "main-input"}
{...props.attributes}
/>
</div>
);
};
CSS:
.main-input {
width: 100%;
height: 2rem;
padding: 4px 5px 4px 0;
border: none;
caret-color: hsl(183, 100%, 15%);
outline: none;
color:hsl(183, 100%, 15%);
font-weight: bolder;
font-family: inherit;
font-size: 1.1rem;
}
.main-input:focus {
border-bottom: hsl(184, 14%, 56%) solid 1.5px;
}
.main-input::-webkit-outer-spin-button,
.main-input::-webkit-inner-spin-button {
-webkit-appearance: none;
}
.main-input[type="number"] {
-moz-appearance: textfield;
text-align: right;
}
.input-icon {
position: absolute;
padding: 5px 0 0 10px;
font-weight: bolder;
}
.card-label{
font-size: 10px;
color: hsl(186, 14%, 43%);
}
I have changed the number type input and removed the counter using the appearance CSS property. But now I'm trying to limit(maxlength) to 5 and minlength to 1 and also minus(As shown in the image) values are not allowed. Please comment if anyone knows a solution or please comment with any suggestions.
add onChange callback to input, and prevent adding anything inside input which is not number.

Circular progress bar with react

In my react app I am using a circular progress bar to show time left. The progress bar is using bootstrap 3. The code is working fine when the value is hard coded but when I give a percentage value from a state variable the progress bar is not showing. I copied this code from the net and it uses bootstrap 3. I want to use it to show reverse timer. So the percentage is 100 initially and it decreases every second. The circle shows the initial value at first but as soon as the value of percent changes the circle loses the color and shows basic gray color.
the code in render is
constructor(props){
super(props);
this.state = {
hours: 2,
min: 0,
secs: 0,
fin: false,
percent: 100
}
}
componentDidMount(){
this.myInterval = setInterval(() => {
var {hours,min,secs,fin,percent} = this.state;
if(secs > 0){
secs--;
} else {
secs = 59;
if(min > 0){
min--;
percent = Math.floor(percent - 0.8);
} else {
min = 59;
percent = Math.floor(percent - 0.8);
hours--;
if(hours < 0){
fin = true;
hours = min = secs = 0;
}
}
}
this.setState(prevState => ({
hours, min , secs , fin, percent
}))
}, 1000);
}
<div className="container">
<div className="row">
<div className="col-sm-3 col-md-2">
<div className="progress" data-percentage={this.state.percent}>
<span className="progress-left">
<span className="progress-bar"></span>
</span>
<span className="progress-right">
<span className="progress-bar"></span>
</span>
<div className="progress-value">
<div>
{this.state.hours}:{this.state.min}<br/>
<span>Time Left</span>
</div>
</div>
</div>
</div></div></div>
SASS
$borderWidth: 7px;
$animationTime: 1.5s;
$border-color-default: #eee;
$border-color-fill: #ffb43e;
$size: 150px;
//Create how many steps
$howManySteps: 10; //this needs to be even.
//for fun try using 20 and changine in the HTML the data-percentage to 15 or 85
.progress {
width: $size;
height: $size;
line-height: $size;
background: none;
margin: 0 auto;
box-shadow: none;
position: relative;
&:after {
content: "";
width: 100%;
height: 100%;
border-radius: 50%;
border: $borderWidth solid $border-color-default;
position: absolute;
top: 0;
left: 0;
}
> span {
width: 50%;
height: 100%;
overflow: hidden;
position: absolute;
top: 0;
z-index: 1;
}
.progress-left {
left: 0;
}
.progress-bar {
width: 100%;
height: 100%;
background: none;
border-width: $borderWidth;
border-style: solid;
position: absolute;
top: 0;
border-color: $border-color-fill;
}
.progress-left .progress-bar {
left: 100%;
border-top-right-radius: ($size/2);;
border-bottom-right-radius: ($size/2);;
border-left: 0;
-webkit-transform-origin: center left;
transform-origin: center left;
//animation: loading-2 1.5s linear forwards 1.8s;
}
.progress-right {
right: 0;
.progress-bar {
left: -100%;
border-top-left-radius: ($size/2);;
border-bottom-left-radius: ($size/2);;
border-right: 0;
-webkit-transform-origin: center right;
transform-origin: center right;
//animation: loading-1 1.8s linear forwards;
}
}
.progress-value {
display: flex;
border-radius: 50%;
font-size: 28px;
text-align: center;
line-height: 20px;
align-items: center;
justify-content: center;
height: 100%;
//font-family: $work-sans;
font-weight: 300;
div {
margin-top: 10px;
}
span {
font-size: 12px;
text-transform: uppercase;
}
}
}
/* This for loop creates the necessary css animation names
Due to the split circle of progress-left and progress right, we must use the animations on each side.
*/
#for $i from 1 through $howManySteps {
$stepName: ($i*(100 / $howManySteps));
//animation only the left side if below 50%
#if $i <= ($howManySteps/2) {
.progress[data-percentage="#{$stepName}"] {
.progress-right .progress-bar {
animation: loading-#{$i} $animationTime linear forwards;
}
.progress-left .progress-bar {animation: 0;}
}
}
//animation only the right side if above 50%
#if $i > ($howManySteps/2) {
.progress[data-percentage="#{$stepName}"] {
.progress-right .progress-bar {
animation: loading-#{($howManySteps/2)} $animationTime linear forwards; //set the animation to longest animation
}
.progress-left .progress-bar {
animation: loading-#{$i - ($howManySteps/2)} $animationTime linear forwards $animationTime;
}
}
}
}
//animation
#for $i from 1 through ($howManySteps/2) {
$degrees: (180/($howManySteps/2));
$degrees: ($degrees*$i);
#keyframes loading-#{$i}{
0%{
-webkit-transform: rotate(0deg);
transform: rotate(0deg);
}
100%{
-webkit-transform: rotate($degrees);
transform: rotate(#{$degrees}deg);
}
}
}
//additional styling
.progress {
margin-bottom: 1em;
}

css grid + react component challenge

Working on a css grid challenge with React in codepen.
This is the original codepen: https://codepen.io/tallys/pen/bvwZee/
What I have so far: https://codepen.io/al2613/pen/QmOyKo
.grid-container {
border: 2px dashed goldenrod;
display: inline-grid;
padding: 10px;
grid-template-columns: auto auto;
height: 100vh;
grid-gap: 30px;
}
So I got main content area to span across the grid container. However, I'm stuck as to how I can make the aside always 150px and the grid container align nicely with the div at the top?
I'm new to grid very nice first try. I think this is a little bit hacky, but...it does the job (I guess)
I don't think the CSS on this snippet will work, but nevertheless, here's the Pen...
//Don't edit the JS for the CSS Grid challenge!
class App extends React.Component {
state = {
sidebarActive: false,
}
toggleSidebar() {
this.setState({sidebarActive: !this.state.sidebarActive})
}
render() {
const buttonText = this.state.sidebarActive ? 'Toggle Sidebar Off' : 'Toggle Sidebar On';
const {sidebarActive} = this.state
return (
<div>
<h1 className="heading">CSS Grid when some sections don't render!</h1>
<div className="instructions">
<p>The challenge: Fix the CSS Grid so that the main area takes up all of the available space when the sidebar react component does not render. </p>
<button onClick={this.toggleSidebar.bind(this)}>{buttonText}</button>
</div>
<div className="grid-container">
{sidebarActive && <aside className="sidebar">Sometimes renders!</aside>}
<main className="main">Main content area that should always take up the rest of the space in the container. </main>
</div>
</div>
);
}
}
ReactDOM.render(<App/>, document.getElementById('app'));
// Variables
$brand-color: darkblue;
$brand-section-color: white;
$brand-text-color: #222;
$react-accent: #61dafb;
$react-background: #292c34;
$breakpoint: 768px;
$font-heading: 'Permanent Marker', sans-serif;
$font-default: 'Oswald', sans-serif;
// Styles
body {
font-family: $font-default;
margin: 10vh 10vw;
color: $react-accent;
background: $react-background;
}
.heading {
font-family: $font-heading;
}
.instructions {
padding: 5px 12px 20px 12px;
margin-bottom: 20px;
display: flex;
flex-direction: row;
flex-wrap: wrap;
justify-content: center;
background: lighten(adjust-hue($react-background, 30), 7);
font-size: 1.15rem;
button {
border: none;
background: $react-accent;
font-family: $font-default;
padding: 20px;
border-radius: 0;
cursor: pointer;
transition: .4s ease;
&:hover, &:active, &:focus {
background: adjust-hue($react-accent, 210);
}
}
}
.grid-container {
border: 2px dashed goldenrod;
display: inline-grid;
grid-template-columns: 150px repeat(1, 1fr);
height: 100vh;
width: 100%;
grid-gap: 30px;
}
.sidebar {
background: lighten($react-background, 7);
padding: 10px;
& ~ .main{
grid-column: auto !important;
}
}
.main {
background: darken($react-background, 7);
display: grid;
grid-column: span 2;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<main id="app"></main>
I know it's a really late response, but I remembered this challenge... and I couldn't resist.
My approach is fixing the main to the second column, while setting the sidebar width on the item itself (and a margin-right to fake the grid-gap), allowing for the first column to be declared as minmax(0, auto).
.grid-container {
grid-template-columns: minmax(0, auto) 1fr;
/*grid-gap: 30px;*/
}
.main {
grid-column: 2;
}
.sidebar {
margin-right: 30px;
width: 150px;
}
https://codepen.io/facundocorradini/pen/eKgVzP
That way the first column will have a zero width if the sidebar is not loaded, and get to the 150px sidebar with when it loads.

Left hand side and right hand side listboxes with add one, add all, remove one, remove all buttons in the middle

I have a left-hand side (lhs) list box and right-hand side (rhs) list box I want to be able to select items in the lhs listbox and add one or all of them to the rhs listbox. Then I'd also like a remove one or all from the rhs returning them to the lhs. How would I accomplish this? So far, I can only manage getting the index value of the lhs box to the right but it won't take the actual item name for some reason. This is the code that does that:
private void SelectOne_Click(object sender, RoutedEventArgs e)
{
listBoxFin.ItemsSource = listBoxStart.SelectedIndex.ToString();
}
Hi this is not the final solution but this will help you lot.
Working DEMO.
HTML
<div class="wrapper">
<div class="selectbox alignleft">
<ul id="selection" class="cf">
<li>One <span class="desc">Description</span></li>
<li>...</li>
<li>...</li>
</ul>
</div>
<div class="movebutton alignleft">
<input class="button mover" value=">>" type="button" />
</div>
<div id="moving" class="movebox alignleft">
<ul class="cf">
<li>One <span class="desc">Description</span>
</li>
<li>Two</li>
<li>Three</li>
<li>Four</li>
</ul>
</div>
<div class="alignleft">
<input class="button" id="move-up" type="button" value="Up" />
<input class="button" id="move-down" type="button" value="Down" />
</div>
CSS
.cf:before, .cf:after {
content:"";
display: table;
}
.cf:after {
clear: both;
}
/* For IE 6/7 (trigger hasLayout) */
.cf {
zoom: 1;
}
/* general purpose classes */
.nodisplay {
display: none;
}
.nodisplay_strict {
display: none !important;
}
.alignleft {
float: left;
}
.alignright {
float: right;
}
.button {
cursor: pointer;
background: #eee;
border: 0;
min-width: 116px;
padding: 5px 0;
margin-bottom: 2px;
display: block;
}
body {
padding: 25px;
font-family:Verdana, Geneva, sans-serif;
font-size: 12px;
}
li {
display: block;
cursor: pointer;
padding: 5px;
font-weight: bold;
position: relative;
border-bottom: 1px solid #fff;
}
li.active {
background: #000;
color: #fff;
}
.wrapper {
width: 960px;
margin: 0 auto;
}
.selectbox {
border: 1px solid;
width: 150px;
min-height: 199px;
max-height: 199px;
overflow-y: auto;
position: relative;
}
.movebox {
border: 1px solid;
width: 200px;
min-height: 199px;
max-height: 199px;
overflow-y: auto;
position:relative;
margin-left: 10px;
margin-right: 10px;
}
span.desc {
display: block;
padding-top: 5px;
color: #7a7a7a;
font-weight: normal;
font-style: italic;
}
li.active span.desc {
color: #ccc;
}
.movebox .delete, .movebox .unmoved {
display: inline-block;
position: absolute;
right: 5px;
top: 5px;
z-index: 999;
background:url(icon-close.png) no-repeat 0 0 red;
width: 10px;
height: 10px;
text-indent: -99999px;
}
.movebutton {
margin-left: 10px;
}
.movebutton .mover {
background: url(icon_right.png) no-repeat 0 0 #eee;
height: 48px;
margin: 65px auto 0;
min-width: 0;
text-align: center;
width: 48px;
}
.moved {
background: #d9fffe;
}
#move-up {
background: url("icon_up.png") no-repeat 0 0 #eee;
height: 48px;
margin: 0px auto 0;
min-width: 0;
text-align: center;
width: 48px;
}
#move-down {
background: url("icon_down.png") no-repeat 0 0 #eee;
height: 48px;
margin: 15px auto 0;
min-width: 0;
text-align: center;
width: 48px;
}
JavaScript
// JavaScript Document
$(document).ready(function (e) {
$('.selectbox li, .movebox li').click(function () {
$(this).addClass('active').siblings().removeClass('active');
});
$('.mover').click(function () {
$('.selectbox li.active').addClass('moved').prependTo('.movebox ul').prepend('<span class="delete alignright" onclick="moveToLHS(this);">DELETE</span>');
});
$('.mover').click(function () {
$('.selectbox li.active').addClass('moved');
$('.movebox li.active').removeClass('active');
setTimeout(function () {
$('.movebox li').removeClass('moved');
}, 1500);
});
$('.movebox ul li').each(function () {
$(this).prepend('<span class="unmoved alignright" onclick="deleteFromRHS(this);">DELETE</span>');
});
$("#move-up").click(moveUp);
$("#move-down").click(moveDown);
$("#reset-list").click(resetList);
});
//DELETE
function moveToLHS(t) {
$(t).parent().remove().prependTo('.selectbox ul');
$('.selectbox li').click(function () {
$(this).addClass('active').siblings().removeClass('active');
});
//deleting span
$('.selectbox ul li .delete').each(function () {
$(this).remove();
});
}
function deleteFromRHS(d) {
$(d).parent().remove();
}
// LI Up Down
function moveUp() {
$("#moving li.active").each(function () {
var listItem = $(this);
var listItemPosition = $("#moving li").index(listItem) + 1;
if (listItemPosition == 1) return false;
listItem.insertBefore(listItem.prev());
});
}
function moveDown() {
var itemsCount = $("#moving li").length;
$($("#moving li.active").get().reverse()).each(function () {
var listItem = $(this);
var listItemPosition = $("#moving li").index(listItem) + 1;
if (listItemPosition == itemsCount) return false;
listItem.insertAfter(listItem.next());
});
}
function resetList() {
$("#moving").html(originalItems);
}
Working DEMO
As H.B. noted, there are many ways this could be accomplished. Probably the most widely acclaimed architecture for WPF is MVVM, so I'll try to outline a solution with respect to my understanding of that architecture.
The ViewModel will expose a few different properties: LHSList, LHSSelectedItem, RHSList, RHSSelectedItem (collections are ObservableCollections here) as well as a few commands - MoveLHSSelectedToRHS, MoveLHSToRHS, MoveRHSSelectedToRHS, MoveRHSToLHS.
The lists are simple bindings to the ListViews in the View, and the SelectedItem's of those ListViews are also bound accordingly. The commands simply operate on the lists and the selected items. For example, MoveLHSSelectedToRHS would be a command with an action something like:
public void OnMoveLHSSelectedToRHS()
{
if(LHSSelectedItem==null)
return;
RHSList.Add(LHSSelectedItem);
LHSList.Remove(LHSSelectedItem);
LHSSelectedItem=null;
}
Unfortunately, it looks like you are using code behind at the moment. If you are not familiar with MVVM, I'd suggest looking into Josh Smith's WPF articles - they're a great place to start!

Resources