Skip to content

Instantly share code, notes, and snippets.

@Elirena
Created June 20, 2018 10:20
Show Gist options
  • Save Elirena/650d3ae53937704c68e09e12d590ea6f to your computer and use it in GitHub Desktop.
Save Elirena/650d3ae53937704c68e09e12d590ea6f to your computer and use it in GitHub Desktop.
Users UI/UX New Concept
.page
.users
script#user-template(type="text/x-handlebars-template")
.user
.photo
img(src="{{photo}}")
.info
.name {{name}}
.nickName {{userName}}
dl
dt Born
dd {{born}}
dt DOB
dd {{dob}}
dt E-mail
dd {{email}}
.funcs
.left
.icon.status.fa.fa-edit(title="Status")
.icon.delete.fa.fa-trash(title="Delete")
.right
.icon.clone.fa.fa-copy(title="Clone")
.icon.send.fa.fa-envelope(title="Send message")
script#new-user-template(type="text/x-handlebars-template")
.user.new
.photo
.placeHolder
.info
.name Add new user
.editorBox
.propertyEditor
.popup
.header
.title Send a message
.icon
i.fa.fa-send(title="Send")
.content
textarea(placeholder="Your message")
.instructions
ol(type="1")
li Select a user
li Try to change Name or Nickname property
li Press "Send message to user" button and watch the nice popup window
li Select an other user too
li Try the multiple property edit (e.g. change status)
li Watch the "Add new user" button hover effect
###
Instructions:
-------------
1. Select a user
2. Try to change Name or Nickname property
3. Press "Send message to user" button and watch the nice popup window
4. Select an other user too
5. Try the multiple property edit (e.g. change status)
6. Watch the "Add new user" button hover effect
###
###
Pictures:
http://rapsag.deviantart.com/art/Walter-White-396773756
http://ignis-vitae.deviantart.com/art/Jesse-Pinkman-Breaking-Bad-343381256
http://jkim34.deviantart.com/art/Saul-Goodman-369909575
###
users = [
id: 1
name: "Walter White"
userName: "Heisenberg"
realName: "Brian Cranston"
email: "[email protected]"
born: "California, USA"
dob: "1956-03-07"
photo: "https://s3-us-west-2.amazonaws.com/s.cdpn.io/102308/walter_white_by_rapsag.d6k88l8_(1).jpg"
status: true
created: "2015-03-30 22:49:32"
skills: ["Chemist", "Teacher", "Clever"]
settings:
themeColor: "#fab000"
motto: "Say my name!"
,
id: 2
name: "Jesse Pinkman"
userName: "jessy"
realName: "Aaron Paul"
email: "[email protected]"
born: "Idaho, USA"
dob: "1979-08-27"
photo: "https://s3-us-west-2.amazonaws.com/s.cdpn.io/102308/jesse_pinkman___breaking_bad_by_ignis_vitae.d5ofuo8.jpg"
status: true
created: "2015-03-30 23:50:11"
skills: ["Student", "Dealer", "Unmindful"]
settings:
themeColor: "#000"
motto: "You're my free pass...!"
,
id: 3
name: "Saul Goodman"
userName: "saul"
realName: "James Morgan McGill"
email: "[email protected]"
born: "Illinois, USA"
dob: "1962-10-22"
photo: "https://s3-us-west-2.amazonaws.com/s.cdpn.io/102308/saul_goodman_by_jkim34.d648g1z.jpg"
status: false
created: "2015-03-31 13:42:50"
skills: ["Lawyer", "Clever", "Corrupt"]
settings:
themeColor: "Red"
motto: "Better call Saul!"
]
showUsers = ->
container = $(".users")
$.each users, (i, user) ->
tpl = Handlebars.compile($("#user-template").html())
userDiv = $(tpl(user)).data("object", user).appendTo container
if not user.status
userDiv.addClass "inactive"
# New user
tpl = Handlebars.compile($("#new-user-template").html())
$(tpl({})).appendTo container
$ ->
showUsers()
# Show users (slideIn)
tl = new TimelineLite()
tl.staggerTo ".user", 1, {opacity: 0.6, y: 0}, 0.2
# Event handler for user functions
$(".user .icon.status").on "click", (e)->
toastr.info "Change user status"
userDiv = $(e.target).closest(".user")
userDiv.toggleClass("inactive")
userDiv.data("object").status = not userDiv.data("object").status
e.stopPropagation()
$(".user .icon.delete").on "click", (e)->
toastr.info "Delete user"
e.stopPropagation()
$(".user .icon.clone").on "click", (e)->
toastr.info "Clone user"
e.stopPropagation()
$(".user .icon.send").on "click", (e)->
$(".popup").addClass "show"
e.stopPropagation()
$(".popup .icon i").on "click", ->
$(".popup").removeClass("show")
toastr.info "Message sent"
getUserDivs = -> $(".user.selected")
# Event handler for selection
$(".user:not(.new)").on "click", ->
$this = $(this)
$this.toggleClass "selected"
selectedCount = $(".user.selected").length
if selectedCount > 0
# $(".page").addClass "editing"
# Get selected objects
selectedObjs = []
$(".user.selected").each (i, user) ->
selectedObjs.push $(user).data("object");
# Show editor
pjs = new PJS ".propertyEditor", propertiesSchema, selectedObjs
pjs.on "changed", (editor, value, objects) ->
divs = getUserDivs objects
switch (editor.fieldName)
when "name" then $(divs).find(".info .name").text value
when "userName" then $(divs).find(".info .nickName").text value
when "born" then $(divs).find("dl dd:eq(0)").text value
when "dob" then $(divs).find("dl dd:eq(1)").text value
when "email" then $(divs).find("dl dd:eq(2)").text value
when "status"
if value then $(divs).removeClass("inactive") else $(divs).addClass("inactive")
pjs.on "function-sendMessage", (editor, objects) -> $(".popup").addClass "show"
# Show editor
TweenLite.to ".editorBox", 0.8, { right: 0, ease:Power4.easeOut}
TweenLite.to ".users", 1.9, { "margin-right": "350px", ease:Power2.easeOut}
else
# $(".page").removeClass "editing"
# Hide editor
TweenLite.to ".editorBox", 0.8, { right: "-350px", ease:Power4.easeOut}
TweenLite.to ".users", 1.8, { "margin-right": "0px", ease:Power2.easeOut}
# Event handler for new user
$(".user.new").on "click", ->
toastr.success "Add a new user"
# --- PROPERTIES SCHEMA ---
propertiesSchema =
editors: [
field: "name"
title: "Name"
type: "text"
required: true
multiEdit: true
featured: true
,
field: "userName"
title: "Nick name"
type: "text"
required: true
multiEdit: false
featured: true
,
field: "realName"
title: "Real full name"
toolTip: "This is the user's real born name"
type: "text"
required: false
multiEdit: false
,
field: "email"
title: "E-mail"
type: "email"
required: false
multiEdit: false
field: "password"
title: "Password"
type: "password"
placeHolder: "Password"
required: true
multiEdit: true
featured: true
toolTip: "Enter the user's password"
hint: "Minimum 6 characters"
validate: (value, objs) -> if value.length < 6 then return "Password is too short! Minimum 6 characters!"
,
field: "phone"
title: "Phone"
type: "text"
required: false
multiEdit: true
pattern: "^[0-9]{3}-[0-9]{4}$"
hint: "Format: 000-0000"
,
field: "born"
title: "Born"
type: "text"
required: false
multiEdit: true
,
field: "dob"
title: "Date of birth"
type: "date"
format: "YYYY-MM-DD"
required: false
multiEdit: true
validate: (value, objs) ->
if value is "1963-03-07"
return "Invalid date!"
,
field: "settings.isActor"
title: "Is an actor?"
type: "boolean"
required: true
default: false
multiEdit: true
,
field: "status"
title: "Status"
type: "boolean"
required: true
default: false
multiEdit: true
,
field: "created"
title: "Created at"
type: "timestamp"
format: "YYYY-MM-DD HH:mm:ss"
required: false
readonly: true
,
field: "skills"
title: "Skills"
type: "checklist"
multiEdit: true
listBox: false
required: true
rows: 6
values: [
"Chemist"
"Teacher"
"Student"
"Lawyer"
"Dealer"
"Clever"
"Unmindful"
"Corrupt"
]
,
field: "settings.motto"
title: "Motto"
type: "textarea"
required: false
multiEdit: true
placeHolder: "What do you think?"
rows: 3
,
field: "settings.nativeLang"
title: "Native language"
type: "select"
required: true
multiEdit: true
values: [
{ id: "en", name: "English" }
{ id: "de", name: "Deutsch" }
{ id: "it", name: "Italiano" }
{ id: "es", name: "Español" }
{ id: "fr", name: "Français" }
]
,
field: "sendMessage"
title: "Send message to user"
styleClass: "fa fa-envelope"
type: "button"
multiEdit: true
schemaFunction: true
,
field: "clone"
title: "Clone user"
styleClass: "fa fa-copy"
type: "button"
schemaFunction: true
multiEdit: false
onclick: (objs)->
toastr.info "Clone selected #{objs.length} users"
,
field: "delete"
title: "Delete user"
styleClass: "fa fa-trash"
type: "button"
schemaFunction: true
multiEdit: true
onclick: (objs)->
toastr.info "Delete selected #{objs.length} users"
]
<script src="//cdnjs.cloudflare.com/ajax/libs/jquery/2.1.3/jquery.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/toastr.js/latest/js/toastr.min.js"></script>
<script src="https://s3-us-west-2.amazonaws.com/s.cdpn.io/102308/propertiesJS.js?_=123"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/handlebars.js/3.0.1/handlebars.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/latest/TweenMax.min.js"></script>
@import "compass";
@import url(https://fonts.googleapis.com/css?family=Source+Sans+Pro:300,400,600,700,900|Dosis:300,400,600,700,800|Droid+Sans:400,700|Lato:300,400,700,900|PT+Sans:400,700|Ubuntu:300,400,500,700|Open+Sans:400,300,600,700|Roboto:400,300,500,700,900|Roboto+Condensed:400,300,700|Open+Sans+Condensed:300,700|Play:400,700|Maven+Pro:400,500,700,900&subset=latin,latin-ext);
// --- VARIABLES ---
// Colors calculated with: https://codepen.io/icebob/pen/OPrXJq
$base:#565656;
$lighter1: lighten(adjust-hue($base, 5), 10%);
$lighter2: lighten(adjust-hue($base, 10), 20%);
$darker1: darken(adjust-hue($base, -5), 10%);
$darker2: darken(adjust-hue($base, -10), 20%);
$textColor: lighten(adjust-hue($base, 10), 50%);
$selectedColor: #fab000;
$itemWidth: 280px;
$itemHeight: 320px;
$imgSize: 200px;
$fontFamily: "Open Sans", "Helvetica Neue", Tahoma, sans-serif;
$fontFamilyAlt: "Open Sans Condensed", "Lato", "Segoe UI Light", Arial;
// --- GLOBAL STYLES ---
* {
@include box-sizing(border-box);
-webkit-font-smoothing: antialiased;
}
html {
background-color: $base;
font-family: $fontFamily;
font-size: 16px;
color: $textColor;
}
body {
overflow: hidden;
}
// --- GLOBAL MIXINS ---
@mixin clearfix() {
&:before,
&:after {
content: "";
display: table;
}
&:after {
clear: both;
}
}
@mixin keyframes($name) {
@-webkit-keyframes #{$name} {
@content;
}
@-moz-keyframes #{$name} {
@content;
}
@-ms-keyframes #{$name} {
@content;
}
@keyframes #{$name} {
@content;
}
}
%textShadow {
@include text-shadow(1px 1px 3px rgba(Black, 0.75));
}
// --- BODY STYLES ---
body {
padding: 30px;
}
// --- MESSAGES STYLES ---
.messages {
position: absolute;
top: 8px; left: 0; right: 0;
height: 20px;
text-align: center;
opacity: 0;
@include transform(translate3d(0, -100%, 0));
@include transition(all .5s ease);
.selectedCount {
display: inline-block;
margin: auto;
background-color: $lighter1;
@include box-shadow(0 0 10px rgba(Black, 0.4));
color: darker2;
text-align: center;
padding: 6px 20px;
@include border-radius(16px);
@extend %textShadow;
}
&.visible {
opacity: 1;
@include transform(none);
}
}
// --- USERS STYLES ---
.users {
margin: 20px;
@include user-select(none);
@include display-flex();
@include flex-direction(row);
@include justify-content(space-around);
@include flex-wrap(wrap);
.user {
position: relative;
width: $itemWidth;
height: $itemHeight;
margin-bottom: 30px;
margin-right: 0px;
cursor: pointer;
// Initial state
opacity: 0.0;
@include transform(translateY(-40px));
.photo {
position: relative;
text-align: center;
z-index: 10;
img {
width: $imgSize;
height: $imgSize;
@include border-radius(100%);
@include box-shadow(0 0 10px rgba(Black, 0.75));
overflow: hidden;
@include transition(transform, .5s ease);
@include perspective(1000);
@include transform-style(preserve-3d);
//@include backface-visibility(hidden);
} // img
} // .photo
.info {
text-align: center;
margin-top: 10px;
font-family: $fontFamilyAlt;
font-size: 1.3rem;
z-index: 8;
@extend %textShadow;
.name {
text-align: center;
} // .name
.nickName {
margin-top: 4px;
text-align: center;
font-size: 0.9rem;
&:before {
content: "("
}
&:after {
content: ")"
}
} // .nickName
} // .info
dl {
z-index: 8;
margin-top: 10px;
font-size: 0.8rem;
@extend %textShadow;
dt {
text-align: right;
font-weight: 700;
float: left;
width: 30%;
padding: 2px 0px;
&:after {
content: ":";
margin-right: 10px;
}
} // dt
dd {
text-align: left;
float: right;
width: 70%;
padding: 2px 0px;
} // dd
dt, dd {
opacity: 0;
@include transform(translate3d(0, -100%, 0));
@include transition(all .5s ease);
} // dt, dd
} // dl
.funcs {
position: absolute;
left: $itemWidth/2; top: $imgSize/2;
z-index: 8;
.icon {
position: absolute;
opacity: 0.8;
font-size: 1.3rem;
@include transition(transform 0.5s ease, font-size 0.2s ease);
&:hover {
&:after {
position: absolute;
display: block;
content: attr(title);
padding: .3em 1em;
background: rgba(Black, 0.5);
border-radius: 0.5em;
color: $textColor;
@extend %textShadow;
z-index: 20;
white-space: nowrap;
font-size: 0.8rem;
font-family: "Open Sans";
right: 1.8em;
bottom: 0;
} // after
} // hover
} // .icon
.left .icon {
padding-left: 20px;
}
.right .icon {
padding-right: 20px;
&:hover:after {
right: initial;
left: 2.2em;
}
}
} // .funcs
// --- USER HOVER/SELECTED STYLES ---
&:hover, &.selected {
opacity: 1.0 !important;
img {
@include box-shadow(0 0 20px rgba(Black, 1));
}
.funcs {
z-index: 11;
.icon:hover {
opacity: 1.0;
//font-size: 1.5rem;
color: $selectedColor;
}
.left {
:nth-child(1) {
@include transform(translate(-110px, 80px));
}
:nth-child(2) {
@include transform(translate(-130px, 50px));
}
:nth-child(3) {
@include transform(translate(-150px, 20px));
}
:nth-child(4) {
@include transform(translate(-155px, -10px));
}
:nth-child(5) {
@include transform(translate(-150px, -40px));
}
:nth-child(6) {
@include transform(translate(-135px, -70px));
}
} // .left
.right {
:nth-child(1) {
@include transform(translate(70px, 80px));
}
:nth-child(2) {
@include transform(translate(100px, 50px));
}
:nth-child(3) {
@include transform(translate(110px, 20px));
}
:nth-child(4) {
@include transform(translate(115px, -10px));
}
:nth-child(5) {
@include transform(translate(110px, -40px));
}
:nth-child(6) {
@include transform(translate(95px, -70px));
}
} // .right
} // .funcs
dt, dd {
//@include animation-duration(1s);
//@include animation-fill-mode(both);
//@include animation-name(fadeInDown);
opacity: 1.0;
transform: none;
} // dt, dd
} // hover/selected
&.selected {
.name {
color: $selectedColor;
font-weight: 700;
}
.photo {
img {
@include transform(rotateY(180deg));
}
&:after {
position: absolute;
content: "";
left: ($itemWidth - $imgSize) / 2 - 3;
top: -3px;
width: $imgSize;
height: $imgSize;
border: 3px dashed rgba($selectedColor, 0.5);
@include border-radius(100%);
@include animation(dash 20s infinite linear);
}
} // .photo
} // .selected
&.inactive {
.photo {
@include filter(grayscale(100%));
}
.info {
font-style: italic;
}
&:after {
content: "INACTIVE";
padding: 4px 10px;
position: absolute;
left: 0px;
right: 0px;
top: 0px;
bottom: 120px;
width: 160px;
height: 20px;
line-height: 20px;
margin: auto;
z-index: 20;
font-family: "Source Sans Pro", "Arial Black", Arial, sans-serif;
font-weight: 700;
color: #FFDDDD;
font-size: 2.0rem;
@include text-shadow(0px 0px 1px rgba(Red, 1.0));
text-align: center;
@include border-radius(8px);
border: 3px solid #880000;
@include transform(rotateZ(-20deg));
background-color: rgba(Red, 0.3);
}
}
} // .user
// --- NEW USER STYLES ---
.user.new {
.photo {
text-align: center;
.placeHolder {
margin: auto;
width: $imgSize;
height: $imgSize;
@include border-radius(100%);
border: 8px dashed $lighter1;
@include box-shadow(none);
@include transition(transform 0.5s ease);
}
&:before {
position: absolute;
left: 30px; top: 0;
text-align: center;
vertical-align: middle;
content: "\f234";
font-family: FontAwesome;
font-size: 5.0rem;
width: $imgSize;
height: $imgSize;
line-height: $imgSize;
color: $lighter1;
}
} // .photo
&:hover {
.photo .placeHolder {
@include transform(rotate(90deg));
}
} // hover
} // .user.new
} // .users
$editorWidth: 350px;
.editorBox {
position: fixed;
top: 0px; bottom: 0px;
right: -$editorWidth;
width: $editorWidth;
@include perspective(500);
//background-color: #1d1f20;
//@include box-shadow(-2px 0px 10px rgba(black, 0.8));
.propertyEditor {
background-color: #323232;
font-size: 0.9rem;
}
} // .editorBox
.page {
overflow: hidden;
width: 100%;
}
/*
.page.editing {
.users {
margin-right: $editorWidth;
} // .users
.editorBox {
right: 0px;
} // .editorBox
} // .page.editing
*/
$popupHeight: 300px;
.popup {
//display: none;
position: absolute;
left: 0;right: 0;
top: 0; bottom: 0;
margin: auto;
width: 50%;
height: $popupHeight;
overflow: hidden;
z-index: 20;
font-family: "Montserrat", "Open Sans";
padding: 10px;
background-color: rgba($darker2, 0.9);
border: 1px solid rgba(0,0,0,0.4);
@include border-radius(10px);
@include box-shadow(0 0 20px rgba(Black, 0.8));
@include text-shadow(1px 1px 8px rgba(Black, 0.6));
@include transform-style(preserve-3D);
@include transform(perspective(1000px) translateY($popupHeight) rotateX(-74deg));
@include backface-visibility(hidden);
opacity: 0.2;
@include transition(all .5s ease);
&.show {
display: block;
opacity: 1;
@include transform(none);
}
.header {
padding: 10px 10px;
.title {
font-size: 1.6rem;
font-family: "Open Sans Condensed";
float: left;
}
.icon {
float: right;
font-size: 1.3rem;
i {
cursor: pointer;
@include transform(none);
@include transition(all .3s ease);
&:hover {
@include transform(scale(1.2));
}
}
}
@include clearfix();
}
.content {
padding: 10px;
position: relative;
textarea {
width: 100%;
height: $popupHeight - 100px;
margin: 0;
border: 0;
padding: 5px;
background: rgba(White, 0.1);
border: 1px solid rgba(0,0,0,0.5);
@include border-radius(5px);
font-family: "Open Sans";
@include text-shadow(1px 1px 3px rgba(Black, 0.4));
color: White;
@include input-placeholder {
color: rgba(White, 0.4);
}
} // textare
} // .content
} // .popup
.instructions {
display: none;
position: absolute;
left: 0;
bottom: 0;
padding: 10px;
font-size: 1.1rem;
font-family: $fontFamilyAlt;
color: $textColor;
@include text-shadow(1px 1px 3px rgba(0,0,0,0.7));
@include transition(color .3s linear);
ol {
list-style-type: decimal;
padding-left: 20px;
//@include column-count(2);
li {
padding: 2px 0px;
}
}
} // .instructions
body:hover .instructions {
opacity: 0.2;
}
@include keyframes(dash) {
0% {
@include transform(rotate(0));
}
100% {
@include transform(rotate(360deg));
}
}
<link href="//maxcdn.bootstrapcdn.com/font-awesome/4.3.0/css/font-awesome.min.css" rel="stylesheet" />
<link href="https://s3-us-west-2.amazonaws.com/s.cdpn.io/102308/propertiesJS.css" rel="stylesheet" />
<link href="https://cdnjs.cloudflare.com/ajax/libs/toastr.js/2.1.1/toastr.min.css" rel="stylesheet" />
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment