Commit 40e3a609 authored by Randall Leeds's avatar Randall Leeds

rework auth/reg forms and sheet

- Drop deform from angular app -- this was too confusing and strange
  and makes it harder to pull the front-end application out of the
  repository.
- Make the sheet much more attractive by tying it to the toolbar
  in a clearer way and toggling between "Sign in" and the close 'X'.
- Start to normalize the way errors are handled in form submissions
  on the back-end by offloading basic validation messaging to the
  front-end, removing the need to constantly re-render the forms.

Still to do: flash messages (#233)
parent ba8f646e
......@@ -18,7 +18,6 @@ body {
width: 100%;
}
input { width: 100%; }
svg { -webkit-tap-highlight-color: rgba(255, 255, 255, 0); }
......@@ -43,19 +42,24 @@ svg { -webkit-tap-highlight-color: rgba(255, 255, 255, 0); }
.sliding-panels > li {
@include smallshadow(-2px);
@include transition(transform .4s);
@include translateZ(0);
background: url('../images/noise_1.png');
margin-left: 1em;
overflow: scroll;
overflow-y: scroll;
-webkit-overflow-scrolling: touch;
padding: 1em;
position: absolute;
height: 100%;
width: 100%;
left: 0;
right: 0;
&:first-child {
margin-left: 0;
@include box-shadow(none);
left: 0em !important;
}
&:last-child {
left: 1em;
}
&.collapsed {
......@@ -68,17 +72,11 @@ svg { -webkit-tap-highlight-color: rgba(255, 255, 255, 0); }
}
//SIDEBAR LAYOUT////////////////////////////////
#gutter {
background: url('../images/noise_1.png');
height: 100%;
margin-left: $heatmap-width + 17px;
padding-top: 2.5em; // toolbar height + top margin
position: relative;
}
#wrapper {
background: url('../images/noise_1.png');
height: 100%;
margin-left: $heatmap-width + 17px;
padding-top: 2.5em;
position: relative;
}
......@@ -174,51 +172,75 @@ svg { -webkit-tap-highlight-color: rgba(255, 255, 255, 0); }
//SHEET////////////////////////////////
//This comes down from under things.
//This comes down from under the toolbar
.sheet {
@include box-shadow(5px 5px 40px hsla(0, 0%, 0%, .1));
@include single-transition(all, .4s, ease-in-out);
@include transform-origin(50%, 0);
z-index: 4;
background: url('../images/noise_1.png');
padding-top: 1em;
@include transition(
max-height .8s ease-in-out,
opacity .4s ease-in-out
);
max-height: 30em;
opacity: 1;
position: absolute;
width: 100%;
text-align: left;
left: .3em;
right: 0;
top: .3em;
.close {
position: absolute;
right: 1em;
top: 1em;
top: 0;
}
&.collapsed {
@include rotateX(90deg);
max-height: 0;
opacity: 0;
pointer-events: none;
overflow: hidden;
footer {
opacity: 0;
}
}
&.info {
.info {
color: #3a87ad;
background-color: #d9edf7;
border-color: #98BED1;
}
&.good {
.good {
color: #468847;
background-color: #dff0d8;
border-color: #8DC98E;
}
&.bad {
.bad {
color: #b94a48;
background-color: #f2dede;
border-color: #F5A1A0;
}
.nav-tabs {
padding-left: 1.3em;
}
.tab-pane {
@include box-shadow(5px 5px 3px hsla(0, 0%, 0%, .1));
}
footer {
bottom: 1em;
right: 1em;
position: absolute;
@include single-transition(opacity, 1s, ease-in-out, 1s);
float: right;
left: -1em;
position: relative;
text-align: right;
top: -3.5em;
a { @include tertiarytext; }
}
input:not([type="submit"]) { width: 100%; }
}
......@@ -226,27 +248,22 @@ svg { -webkit-tap-highlight-color: rgba(255, 255, 255, 0); }
//TOOL BAR////////////////////////////////
#toolbar {
@include smallshadow;
background: white;
border: 1px solid $grayLighter;
background: $bodyBackground;
border: .1em solid $grayLighter;
border-top-left-radius: 4px;
border-bottom-left-radius: 4px;
height: 2em;
left: 7px;
line-height: 1.4em; // 2em (height) - .6em (padding)
margin-top: .5em;
padding: .3em;
min-height: 2.2em;
position: absolute;
right: -.1em;
text-align: right;
right: 0;
top: .5em;
z-index: 5;
& > div {
.tinyman {
border-left: 1px solid $grayLighter;
display: inline-block;
vertical-align: top;
&:last-child {
border-left: 1px solid $grayLighter;
}
margin: .3em;
}
.tri {
......@@ -255,7 +272,10 @@ svg { -webkit-tap-highlight-color: rgba(255, 255, 255, 0); }
text-shadow:
1px .8px 1.5px #fff,
0 0 0 #000;
float: left;
position: absolute;
z-index: 1;
top: .3em;
left: .3em;
line-height: $baseLineHeight * .9;
font-size: $baseLineHeight * .9;
&:before {
......@@ -273,7 +293,10 @@ svg { -webkit-tap-highlight-color: rgba(255, 255, 255, 0); }
text-shadow:
1px .8px 1.5px #fff,
0 0 0 #000;
float: left;
position: absolute;
z-index: 1;
top: .3em;
left: .3em;
line-height: $baseLineHeight * .9;
font-size: $baseLineHeight * .9;
cursor: ew-resize;
......
......@@ -7,7 +7,6 @@ $sansFontFamily: "Source Sans Pro", $sansFontFamily;
$serifFontFamily: "Merriweather", $serifFontFamily;
//ELEMENT STYLES////////////////////////////////
a {
color: $linkColor;
......@@ -89,15 +88,10 @@ label {
//MCRANDOM////////////////////////////////
.btn {
button, input[type=submit], .btn {
@include sweetbutton;
}
.alert-block {
span.errorMsgLbl { @extend .visuallyhidden; }
span.errorMsg { @extend .visuallyhidden; }
}
//Candidate for cleanup
.btn-link {
@include box-shadow(none);
......@@ -158,10 +152,15 @@ label {
.form-vertical {
select, textarea, input, button {
display: block;
margin-top: .75em;
}
}
input.ng-invalid.ng-dirty {
border-color: lighten($red, 35%);
}
.req {
display: none;
}
......
imports = [
'bootstrap'
'deform'
'h.controllers'
'h.directives'
'h.filters'
......
class App
scope:
sheet:
collapsed: true
tab: 'login'
personas: []
persona: null
token: null
this.$inject = [
'$compile', '$element', '$http', '$location', '$scope',
'annotator', 'deform', 'threading'
'annotator', 'threading'
]
constructor: (
$compile, $element, $http, $location, $scope,
annotator, deform, threading
) ->
annotator, threading
) ->
$scope.reset = => angular.extend $scope, @scope
{plugins, provider} = annotator
heatmap = annotator.plugins.Heatmap
heatmap.element.appendTo $element
......@@ -110,36 +120,10 @@ class App
$scope.$apply -> $location.path('/viewer').search(search).replace()
angular.extend $scope,
auth:
collapsed: true
tab: 'login'
forms: []
$scope.reset = ->
angular.extend $scope,
auth:
collapsed: true
tab: 'login'
username: null
password: null
email: null
code: null
personas: []
persona: null
token: null
$scope.addForm = ($form, name) ->
$scope.forms[name] = $form
$scope.submit = ->
fields = switch $scope.auth.tab
when 'login' then ['username', 'password']
when 'register' then ['username', 'password', 'email']
when 'forgot' then ['email']
when 'activate' then ['password', 'code']
params = ([key, $scope[key]] for key in fields when $scope[key]?)
params.push ['__formid__', $scope.auth.tab]
$scope.submit = (form) ->
params = for name, control of form when control.$modelValue?
[name, control.$modelValue]
params.push ['__formid__', form.$name]
data = (((p.map encodeURIComponent).join '=') for p in params).join '&'
$http.post '', data,
......@@ -149,15 +133,7 @@ class App
.success (data) =>
# Extend the scope with updated model data
angular.extend($scope, data.model) if data.model?
# Compile and link any forms which were re-rendered in this response
for oid of data.form
$form = angular.element data.form[oid]
if oid of $scope.forms
link = ($compile $form)
$scope.forms[oid].replaceWith $form
link $scope
deform.focusFirstInput $form
# TODO: flash data.error server messages
$scope.toggleShow = ->
if annotator.visible
......@@ -173,7 +149,7 @@ class App
annotator.element.find('#persona')
.off('change').on('change', -> $(this).submit())
.off('click')
$scope.auth.collapsed = true
$scope.sheet.collapsed = true
else
$scope.persona = null
$scope.token = null
......@@ -202,17 +178,18 @@ class App
delete plugins.Auth
$scope.$on 'showAuth', (event, show=true) ->
angular.extend $scope.auth,
angular.extend $scope.sheet,
collapsed: !show
tab: 'login'
# Fetch the initial model from the server
$scope.reset()
$http.get 'model',
withCredentials: true
.success (data) =>
angular.extend $scope, data
$scope.$evalAsync 'reset()'
class Annotation
this.$inject = [
......
# Extend deform to allow targeting focusing of input when multiple forms
# are on the same page. See https://github.com/Pylons/deform/pull/128
angular.extend deform,
focusFirstInput: (el) ->
el = el || document.body
input = $(el).find(':input')
.filter('[id ^= deformField]')
.filter('[type != hidden]')
.first()
raw = input?.get(0)
if raw?.type in ['text', 'file', 'password', 'textarea']
if raw.className != "hasDatepicker" then input.focus()
# AngularJS directive that creates data bindings for named controls on forms
# with the 'deform' class and triggers deform callbacks during linking.
deformDirective = ->
compile: (tElement, tAttrs, transclude) ->
# Capture the initial values from the server-side render and set a model
# binding for all the named controls.
initValues = {}
$controls = tElement.find('input,select,textarea').filter('[name]')
.filter ->
# Ignore private members (like __formid__) and hidden fields
# (angular does not data-bind hidden fields)
not (this.name.match '^_' or this.type is hidden)
.each ->
initValues[this.name] =
if this.tagName == 'select'
options = []
selected = null
$(this)
.attr(
'data-ng-options',
"value for value in #{this.name}.values"
)
.find('option').filter('[value!=""]')
.each (i) ->
if this.selected then selected = i
options.push
label: this.label or this.innerText
value: this.value
.remove()
options: options
selected: selected
else
this.value
$(this).attr 'data-ng-model', "#{this.name}"
# Link function
(scope, iElement, iAttrs, controller) ->
if scope.addForm?
name = iAttrs.name or iAttrs.ngModel or iAttrs.id
scope.addForm iElement, name
deform.processCallbacks()
restrict: 'C'
require: 'form'
scope: false
angular.module('deform', []).config [
'$provide', '$compileProvider', '$filterProvider',
($provide, $compileProvider, $filterProvider) ->
# Process any pending callbacks from the initial load
deform.processCallbacks()
# Register the deform service and directive
$provide.value 'deform', deform
$compileProvider.directive 'deform', deformDirective
]
......@@ -106,7 +106,6 @@ tabReveal = ['$parse', ($parse) ->
pane = panes[i]
value = pane.attr.value || pane.attr.title
if value == ngModel.$viewValue
deform.focusFirstInput pane.element
pane.element.css 'display', ''
angular.element(tabs[i]).css 'display', ''
else if value in hiddenPanes
......@@ -123,7 +122,7 @@ thread = ->
scope: true
angular.module('h.directives', ['ngSanitize', 'deform'])
angular.module('h.directives', ['ngSanitize'])
.directive('annotation', annotation)
.directive('recursive', recursive)
.directive('tabReveal', tabReveal)
......
......@@ -133,6 +133,7 @@ class Hypothesis extends Annotator
# Dodge toolbars [DISABLE]
#@provider.getMaxBottom (max) =>
# @element.css('margin-top', "#{max}px")
# @element.find('#toolbar').css("top", "#{max}px")
# @element.find('#gutter').css("margin-top", "#{max}px")
# @plugins.Heatmap.BUCKET_THRESHOLD_PAD += max
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment