UX from Session to Console

old
Javi Jimenez Villar 2015-05-30 16:19:37 +02:00
parent 19a04a6297
commit 0fd99c3ba4
15 changed files with 192 additions and 90 deletions

BIN
dist/assets/img/avatar.jpg vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

View File

@ -1,45 +1,63 @@
"use strict"
SPArouter = require "spa-router"
# -- Models
Session = require "./models/session"
# -- components
Button = require "./components/button"
Router = require "./components/router"
# -- forms
FormSession = require "./forms/form.session"
# -- Screens
ScreenSession = require "./screens/session"
ScreenConsole = require "./screens/console"
# -- Modules
C = require "./modules/constants"
App = React.createClass
# -- States & Properties
getInitialState: ->
session : null
context : "campaigns"
# -- Lifecycle
componentWillMount: ->
SPArouter.listen
"/session/:id" : (id) =>
@setState session: false, context: id
"/:context/:area/:element" : (context, area, element) =>
@setState session: true, context: context
"/session/:context" : (context) =>
@setState session: false, context: context
"/:context/:area" : (context, area) =>
@setState session: true, context: context
"/:context" : (context) =>
@setState session: true, context: context
@setState session: true, context: context, area: area
# -- Events
onSessionSuccess: (data) ->
@setState session: true
SPArouter.path "console"
header_style = @refs.header.getDOMNode().classList
header_style.add "disabled"
header_style.remove "expanded"
setTimeout =>
SPArouter.path "campaigns/list"
, C.ANIMATION.DURATION
setTimeout =>
header_style.remove "disabled"
, (C.ANIMATION.DURATION * 2)
onConsoleScroll: (value) -> @setState scrolling: value
onLogout: -> SPArouter.path "session/login"
# -- Render
render: ->
<app>
{
if @state.session
<ScreenConsole context={@state.context}/>
else
<ScreenSession context={@state.context} onSuccess={@onSessionSuccess} />
}
if @state.session
avatar = "http://soyjavi.com/assets/img/soyjavi.hat.jpg"
else
avatar = "./assets/img/avatar.jpg"
<app className={"scrolling" if @state.scrolling}>
<header ref="header"
data-flex="horizontal center grow"
data-flex-justify="start"
className={"expanded" unless @state.session}>
<img src={avatar} data-flex-grow="min" onClick={@onLogout}/>
{
if @state.session
<Router context={@state.context} area={@state.area} />
else
<FormSession context={@state.context} onSuccess={@onSessionSuccess}/>
}
</header>
{ <ScreenConsole context={@state.context} onScroll={@onConsoleScroll}/> if @state.session }
</app>
React.render <App />, document.body

View File

@ -15,10 +15,10 @@ module.exports = React.createClass
# -- Events
onClick: (event) ->
event.preventDefault()
console.log ">"
@props.onClick.call @, event
# -- Render
render: ->
<button onClick={@onClick} className={@props.style}>
<button onClick={@onClick} className={@props.style} disabled={@props.disabled}>
<abbr>{@props.caption}</abbr>
</button>

View File

@ -23,11 +23,7 @@ module.exports = React.createClass
{
for route, index in @props.routes
method = if route.back is true then @onBack
console.log route.className
<a href={"#" + route.route}
key={index}
className={route.className}
onClick={method}>
<a href={"#" + route.route} key={index} className={route.className} onClick={method}>
{route.label}
</a>
}

View File

@ -0,0 +1,44 @@
###
@todo
###
Button = require "./button"
Navigation = require "./navigation"
C = require "../modules/constants"
module.exports = React.createClass
# -- States & Properties
propTypes:
routes : React.PropTypes.array.required
getDefaultProps: ->
routes : [
label: "Campaigns", route: "/campaigns/list"
,
label: "Creatives", route: "/creatives/list"
,
label: "Users", route: "/users/list"
,
label: "Deals", route: "/deals/list"
,
label: "Analytics", route: "/analytics"
]
# -- Render
render: ->
for route in @props.routes
route.className = if route.label.toLowerCase() is @props.context then "active" else ""
for route in subroutes = C.SUBROUTES[@props.context.toUpperCase()]
route.className = if route.label.toLowerCase() is @props.area then "active" else ""
<div>
<div data-flex-grow="max">
<Navigation routes={@props.routes} role="text"/>
<h1>Console</h1>
<Navigation routes={subroutes} role="text"/>
</div>
<nav data-role="circle">
<Button caption="+" style="circle primary"/>
<Button caption="?" style="circle secondary"/>
</nav>
</div>

View File

@ -2,6 +2,9 @@
@todo
###
# -- components
Button = require "../components/button"
module.exports = React.createClass
# -- States & Properties
@ -11,7 +14,7 @@ module.exports = React.createClass
onSuccess : React.PropTypes.function
getInitialState: ->
disabled: false
disabled : true
# -- Events
onKeyUp: (event) ->
@ -32,23 +35,19 @@ module.exports = React.createClass
# -- Render
render: ->
<article data-screen="session" className={@state.active} data-flex="vertical center">
label = if @props.context is "login" then "Sign In" else "Sign Up"
<form data-flex="vertical" className="session">
<h1>Welcome...</h1>
<form data-flex="vertical center">
<input ref="mail" type="text" placeholder="mail" onKeyUp={@onKeyUp} className="transparent"/>
<input ref="password" type="password" placeholder="password" onKeyUp={@onKeyUp} className="transparent"/>
<button ref="button" onClick={@onSign} disabled={@state.disabled} className="radius white">
<abbr>{ if @props.context is "login" then "Sign In" else "Sign Up"}</abbr>
</button>
</form>
<input ref="mail" type="text" placeholder="mail" onKeyUp={@onKeyUp} className="transparent"/>
<input ref="password" type="password" placeholder="password" onKeyUp={@onKeyUp} className="transparent"/>
<Button ref="button" caption={label} onClick={@onSign} disabled={@state.disabled} style="primary" />
{
if @props.context is "login"
<a href="#/session/signup">Dont have an account? <strong>Sign Up</strong></a>
else
<a href="#/session/login">You have an account, <strong>Sign In</strong></a>
}
<small>Copyright 2015</small>
</article>
</form>
# -- Private methods
_getFormValues: ->

View File

@ -12,10 +12,15 @@ module.exports =
,
label: "reports", route: "/campaigns/reports"
]
CREATIVES: [
label: "list", route: "/creatives/list"
]
USERS: []
DEALS: []
ANALYTICS: []
HEIGHT:
LI : 80
ANIMATION:
DURATION : 450

View File

@ -2,48 +2,13 @@
@todo
###
SPArouter = require "spa-router"
Header = require "../components/header"
List = require "../components/list"
ListItem = require "../components/list_item"
C = require "../modules/constants"
module.exports = React.createClass
# -- States & Properties
propTypes:
routes : React.PropTypes.array
getDefaultProps: ->
routes : [
label: "Campaigns", route: "/campaigns"
,
label: "Creatives", route: "/creatives"
,
label: "Users", route: "/users"
,
label: "Deals", route: "/deals"
,
label: "Analytics", route: "/analytics"
]
subroutes : []
getInitialState: ->
scrolling : false
onScroll: (value) ->
@setState scrolling: value
render: ->
context = @props.context.toUpperCase()
mock = (id: i, title: "Title #{i}" for i in [1..128])
for route in @props.routes
route.className = if route.label.toLowerCase() is @props.context then "active" else ""
<article data-screen="console" className={"scrolling" if @state.scrolling}>
<Header title="Console"
routes={@props.routes}
subroutes={C.SUBROUTES[context]} />
<List dataSource={mock} itemFactory={ListItem} onScroll={@onScroll} />
<article data-screen="console">
<List dataSource={mock} itemFactory={ListItem} onScroll={@props.onScroll} />
</article>

View File

@ -26,7 +26,6 @@ OFFSET = (UNIT * 1.35)
HEADER_HEIGHT = 33vh
BUTTON_HEIGHT = (2.5 * SPACE)
BUTTON_HEIGHT = 14vw
BUTTON_CIRCLE_HEIGHT = (2.75 * SPACE)
BORDER_RADIUS = (SPACE / 2)
H1_SHADOW = (SPACE / 6) (SPACE / 6) rgba(0,0,0,0.2)

View File

@ -1,16 +1,20 @@
button, .button
height : BUTTON_HEIGHT
width : auto
font-size : FONT_SIZE_BIG
font-weight : FONT_WEIGHT_BOLD
line-height : BUTTON_HEIGHT
text-align : center
border : none
overflow : hidden
transition-property opacity, box-shadow
transition-property opacity, background-color, box-shadow
transition-duration ANIMATION_DURATION
transition-timing-function ANIMATION_EASE
&:not(.circle)
height : BUTTON_HEIGHT
max-height : BUTTON_HEIGHT
width : auto
&.circle
width : SIZE = BUTTON_CIRCLE_HEIGHT
height : SIZE
@ -22,7 +26,8 @@ button, .button
box-shadow BUTTON_SHADOW, inset 0 0 0 UNIT alpha(WHITE, 15%)
// -- Colors
&.primary
background-color : PRIMARY
&.secondary
background-color : PRIMARY
&:not([disabled])
&.primary
background-color : PRIMARY
&.secondary
background-color : PRIMARY

View File

@ -0,0 +1,15 @@
form
padding : SPACE
// -- Classes
&.session
max-width : 25vw
> *
margin-bottom : (SPACE / 2)
h1
padding : 0
input
font-size : FONT_SIZE_BIG !important
a
text-align : center
font-size : FONT_SIZE_TINY

View File

@ -1,4 +1,5 @@
header
z-index : 2
position : fixed
height : HEADER_HEIGHT
width : 100vw
@ -16,6 +17,7 @@ header
box-shadow 0 0 0 (SIZE / 10) alpha(WHITE, 15%)
h1
padding : (UNIT / 2) 0
text-transform : Capitalize
transition all ANIMATION_DURATION ANIMATION_EASE
nav
&[data-role="text"]
@ -31,7 +33,20 @@ header
> *
margin-left : SPACE
// -- States
> *
transition all ANIMATION_DURATION ANIMATION_EASE
&.expanded
height : 100vh
&.disabled
delayChild index, 2 for index in (0..3)
> *
opacity : 0
transform translateY(-10vh)
// -- Effects
.scrolling &
height : (HEADER_HEIGHT / 2)
box-shadow : HEADER_SHADOW

View File

@ -0,0 +1,38 @@
input[type="email"], input[type="search"], input[type="text"], input[type="tel"],
input[type="url"], input[type="password"], textarea, select
width : 100%
padding : (SPACE / 2)
font-size : FONT_SIZE_NORMAL
border : none
border-bottom : solid (SPACE / 10)
box-shadow : none
box-sizing : border-box
transition all ANIMATION_DURATION ANIMATION_EASE
& :focus
outline : 0
-webkit-appearance : none
-moz-appearance : none
appearance : none
outline : none
-webkit-tap-highlight-color : rgba(255, 255, 255, 0)
-webkit-touch-callout : none
// -- class ------------------------------------------------------------------
&.transparent
font-size : FONT_SIZE_BIG
font-weight : FONT_WEIGHT_BOLD
background-color : transparent
color : WHITE
border-bottom-color : alpha(WHITE, 50%)
&:hover, &:focus, &:active
border-bottom-color : WHITE
&::-webkit-input-placeholder
color : alpha(WHITE, 50%)
&.white
border-bottom-color : alpha(BACKGROUND, 25%)
opacity : 0.75
&:hover, &:focus, &:active
border-bottom-color : THEME
opacity : 1

View File

@ -1,6 +1,8 @@
li
padding : (SPACE / 2) 0
height : 80px
margin-bottom : SPACE
padding : SPACE
background-color : white
box-shadow : HEADER_SHADOW
img
width : SIZE = (2.5 * SPACE)
height : SIZE

View File

@ -2,6 +2,7 @@ nav
&[data-role="text"]
font-size : FONT_SIZE_NORMAL
> *
text-transform : Capitalize
transition opacity ANIMATION_DURATION ANIMATION_EASE
&:not(.active)
font-weight : FONT_WEIGHT_THIN