Skip to content

Instantly share code, notes, and snippets.

@e1blue
Last active July 12, 2018 15:00
Show Gist options
  • Select an option

  • Save e1blue/52c41aab98838dff43a8de0dfa7549fa to your computer and use it in GitHub Desktop.

Select an option

Save e1blue/52c41aab98838dff43a8de0dfa7549fa to your computer and use it in GitHub Desktop.
User List (react + gsap)
<div id="app"></div>
const {SortableContainer, SortableElement, arrayMove} = window.SortableHOC;
const SortableList = SortableContainer(({items}) => {
return (
<ul className="list">
{items.map((value, index) => (
<SortableItem key={`item-${index}`} index={index} value={value} />
))}
</ul>
);
});
const SortableItem = SortableElement(({value}) => {
return (
<li className="todo">
<i className="fa fa-user-circle" />
<div className="info-holder">
<h3 className="name">{value.name}</h3>
<h5 className="role">{value.role}</h5>
<h6 className="exp">{`${value.xp} exp`}</h6>
</div>
</li>
);
});
class AddUser extends React.Component {
constructor(props) {
super(props);
this.inputRef = React.createRef()
}
state = {name: '', value: 1, activeSkill: ''}
componentDidMount() {
const tlAdduser = new TimelineMax()
tlAdduser.staggerTo('.add-user-block', 0.5, {
opacity: 1,
scale: 1,
ease: Bounce.easeOut,
}, 0.2, 0.3)
tlAdduser.to('.add-user-button', 0.6, {
opacity: 1,
})
this.inputRef.current.focus()
}
handleChange = e => {
this.setState({value: event.target.value});
}
renderSkills() {
const skills = ['Front End', 'Back End', 'UX', 'UI', 'Dev Ops', 'Full Stack']
return skills.map((skill, i) => {
let modiferClass = ''
if (this.state.activeSkill === skills[i]) {
modiferClass='active'
}
return (
<div className={`${modiferClass} skill`} onClick={() => {
const skill = skills[i]
this.setState(state => {
return {activeSkill: skill}
})
}}>
{skill}
</div>
)
})
}
render() {
const {name, value, activeSkill} = this.state
const yearString = value.toString() === '1' ? 'year' : 'years'
return (
<div className="add-user">
<div className="add-user-block">
<h6 className='name'>Name</h6>
<input ref={this.inputRef} className='name-input' value={this.state.name} onChange={e => {
const value = e.target.value
this.setState(state => {
return {name: value}
})
}} />
</div>
<div className="add-user-block add-user-block--exp">
<h6 className='expirience-title'>Expirience</h6>
<div className="slider-wrapper">
<span className='exp-value'>{value + ' ' + yearString}</span>
<input
type="range"
min="0"
max="10"
value={this.state.value}
onChange={this.handleChange}
className="slider"
id="myRange"
/>
<div className="range-wrapper">
<span>0</span>
<span>10+</span>
</div>
</div>
</div>
<div className="add-user-block">
<h6 className='name'>Skill</h6>
<div className="skill-wrapper">
{this.renderSkills()}
</div>
</div>
<div className="button-wrapper">
<button disabled={name === '' || activeSkill === ''}onClick={() => {
this.props.addUser({name, value, activeSkill})
}} className="add-user-button"><span className='fa fa-check' /></button>
</div>
</div>
)
}
}
class SortableComponent extends React.Component {
constructor(props) {
super(props);
this.sortableListRef = React.createRef();
}
state = {
items: [{name: 'Trey Anastasio', role: 'Front End Developer', xp: '3 years'}, {name: 'Suzy Greenberg', role: 'Front End Developer', xp: '1 years'}, {name: 'Mike Gordon', role: 'Front End Developer', xp: '6 years'}, {name: 'John Fishman', role: 'UX Designer', xp: '2 years'}, {name: 'Page McConnell', role: 'Front End Developer', xp: '5 years'}, {name: 'Mickey Hart', role: 'Front End Developer', xp: '1 years'}],
};
componentDidUpdate(prevProps) {
if (prevProps.hideItems === false && this.props.hideItems === true ){
this.launchHideAnimation()
}
if (prevProps.hideItems === true && this.props.hideItems === false ){
this.launchShowAnimation()
}
}
launchHideAnimation() {
const tl = new TimelineMax();
const todos = this.sortableListRef.current.container.children
const reversedArray = Array.from(todos).reverse()
tl.staggerTo(reversedArray, 0.3, {
opacity: 0,
ease: Power1.easeOut,
}, 0.05)
tl.to(reversedArray, 0, {
scale: 0,
})
}
launchShowAnimation() {
const todos = this.sortableListRef.current.container.children
TweenMax.staggerTo(todos, 0.75, {
scale: 1,
opacity: 1,
ease: Bounce.easeOut
}, 0.05);
}
addUser = (user) => {
let userList = this.state.items
const newUser = {name: user.name, role: user.activeSkill, xp: `${user.value} years`}
userList.unshift(newUser)
this.setState(state => {
return { items: userList}
}, this.props.toggleItems )
}
onSortEnd = ({oldIndex, newIndex}) => {
this.setState({
items: arrayMove(this.state.items, oldIndex, newIndex),
});
};
renderContent() {
if (this.props.hideItems) {
return <AddUser addUser={this.addUser} />
}
}
render() {
return (
<React.Fragment>
{this.renderContent()}
<div className="sortable-list-wrapper">
<SortableList ref={this.sortableListRef} className="list" lockAxis='y' transitionDuration={500} items={this.state.items} onSortEnd={this.onSortEnd} />
</div>
</React.Fragment>
)
}
}
class AddButton extends React.Component {
render() {
const addButtonModifier = this.props.hideItems ? 'close' : ''
return (
<div className="add-button-container">
<i onClick={this.props.toggleItems} className={`add-button fas fa-plus ${addButtonModifier}`} />
</div>
)
}
}
class Phone extends React.Component {
state = {
hideItems: false,
}
toggleItems = () => {
this.setState(state => {
return {hideItems: !state.hideItems}
})
}
render() {
return (
<div className="phone">
<SortableComponent toggleItems={this.toggleItems} hideItems={this.state.hideItems} />
<AddButton hideItems={this.state.hideItems} toggleItems={this.toggleItems} />
</div>
)
}
}
ReactDOM.render(<Phone/>, document.getElementById('app'));
<script src="https://npmcdn.com/react-sortable-hoc/dist/umd/react-sortable-hoc.min.js"></script>
<script src="https://unpkg.com/react@16/umd/react.production.min.js"></script>
<script src="https://unpkg.com/react-dom@16/umd/react-dom.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/prop-types/15.6.2/prop-types.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/2.0.1/TweenMax.min.js"></script>
$white: #FCFCFC;
$gray: #f1f3f5;
$black: #070708;
$dark-gray: #5B5959;
$orange: #DC852A;
$yellow: #FCE09C;
$green-gradient: linear-gradient(15deg,#14af83,#15b89a);
html, body {
height: 100%;
background: linear-gradient(45deg, darken($gray, 5), lighten(lightgray, 10));
}
h1,h2,h3,h4,h5,h6,h7 {
margin: 0;
}
body {
display: flex;
justify-content: center;
align-items: center;
font-family: Helvetica Neue;
font-family: Roboto, sans-serif;
}
.sortable-list-wrapper {
width: 95%;
height: 94%;
background: lighten(lightgray, 11);
box-shadow: inset 1px 1px 20px rgba(0,0,0,0.1);
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
overflow: auto;
}
.list {
padding: 0;
width: 95%;
max-height: 100%;
margin: 0 auto;
background: lighten(lightgray, 11);
overflow: visible;
overflow-y: scroll;
cursor: ns-resize;
}
.todo {
list-style: none;
background: linear-gradient(15deg, #745fb5, lighten(#9a6dbb, 10));
width: 100%;
margin: 1em auto;
color: $white;
-moz-user-select: -moz-none;
-khtml-user-select: none;
-webkit-user-select: none;
/*
Introduced in IE 10.
See http://ie.microsoft.com/testdrive/HTML5/msUserSelect/
*/
-ms-user-select: none;
user-select: none;
height: 5em;
box-shadow: 1px 2px 6px rgba(0,0,0,0.4);
display: flex;
justify-content: flex-start;
align-items: center;
i {
font-size: 30px;
margin-left: 0.5em;
}
.info-holder {
display: flex;
flex-direction: column;
margin-left: 1em;
}
.name {
color: #333333ba;
font-weight: 400;
}
.role {
font-weight: 400;
font-style: italic;
margin-top: 5px;
}
.exp {
font-weight: 400;
color: lightgray;
}
}
.phone {
background: $white;
opacity: 1;
display: flex;
justify-content: center;
height: 550px;
position: relative;
width: 350px;
box-shadow: 11px 10px 63px 14px rgba(0,0,0,0.18);
}
.add-button-container {
background: white;
position: absolute;
height: 4em;
width: 4em;
border-radius: 50%;
display: flex;
justify-content: center;
align-items: center;
bottom: 30px;
transform: scale(0.8);
right:20px;
box-shadow: 4px 3px 20px rgba(0,0,0,0.2);
z-index: 1000000;
}
.add-button {
position: absolute;
font-size: 50px;
color: #333;
text-shadow: 3px 5px 20px rgba(0,0,0,0.3);
transition: all 0.3s;
&.close {
transform: rotate(45deg);
text-shadow: 0 0 0;
}
}
.add-user {
width: 95%;
max-height: 95%;
background: transparent;
position: absolute;
transform: translate(0%, -50%);
top: 44.5%;
z-index: 100;
.add-user-block {
height: 8em;
margin: 0.5em auto;
width: 95%;
background: white;
opacity: 0;
box-shadow: 2px 1px 10px rgba(0,0,0,0.1);
transform: scale(0);
position: relative;
color: #333;
border-radius: 6px;
position: relative;
display: flex;
justify-content: center;
align-items: center;
flex-direction: column;
.skill-wrapper {
margin: 0 auto;
width: 90%;
margin-top: 2em;
}
.skill {
display: inline-block;
width: 20%;
margin: 0.5em;
opacity: 0.6;
font-size: 13px;
background: #d3d3d3;
color: #333;
text-align: center;
padding: 0.25em 0.75em;
border-radius: 10px;
transition: all 0.2s;
cursor: pointer;
&.active {
background: lightgreen;
color: green;
}
}
.name {
top: 20px;
left: 20px;
font-size: 14px;
color: #333;
position: absolute;
}
.name-input {
border: none;
border-bottom: 3px solid #333;
margin: 1em auto;
width: 80%;
display: block;
&:active, &:focus {
outline: none;
}
}
}
}
.add-user-block--exp {
display: flex;
justify-content: center;
align-items: center;
flex-direction: column;
.expirience-title {
position: absolute;
top: 20px;
left: 20px;
font-size: 14px;
}
.exp-value {
margin-bottom: 0.33em;
color: darken(lightgray, 30);
font-weight: 600;
}
}
.slider {
-webkit-appearance: none;
width: 90%;
height: 10px;
background: #d3d3d3;
outline: none;
border-radius: 10px;
opacity: 0.7;
-webkit-transition: .2s;
transition: opacity .2s;
}
.slider:hover {
opacity: 1;
}
.slider::-webkit-slider-thumb {
-webkit-appearance: none;
appearance: none;
width: 23px;
height: 23px;
border-radius: 50%;
background: #4CAF50;
cursor: pointer;
}
.slider::-moz-range-thumb {
width: 25px;
height: 25px;
background: #4CAF50;
cursor: pointer;
&::before {
content: '';
height: 10px;
width: 10px;
background: red;
position: absolute;
top: 100%;
color: red;
}
}
.range-wrapper {
display: flex;
justify-content: space-between;
width: 86%;
margin-top: 8px;
font-size: 12px;
color: gray;
}
.slider-wrapper {
display: flex;
flex-direction: column;
width: 90%;
align-items: center;
margin-top: 2em;
}
.button-wrapper {
text-align: center;
width: 100%;
.add-user-button {
border: none;
color: white;
opacity: 0;
background: dodgerblue;
padding: 0.25em 1em;
border-radius: 10px;
box-shadow: 1px 2px 10px rgba(0,0,0,0.3);
cursor: pointer;
&:disabled {
background: gray;
cursor: not-allowed;
}
&:active, &:focus{
outline: none;
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment