UX from Session to Console
parent
19a04a6297
commit
0fd99c3ba4
Binary file not shown.
After Width: | Height: | Size: 13 KiB |
|
@ -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
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
}
|
||||
|
|
|
@ -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>
|
|
@ -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: ->
|
|
@ -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
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
|
@ -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
|
||||
|
|
|
@ -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
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue