mirror of
https://github.com/naturalcrit/naturalcrit.git
synced 2025-10-27 07:29:54 +00:00
lint
This commit is contained in:
parent
0141433131
commit
6ff861afb7
@ -1,129 +1,135 @@
|
||||
const React = require('react');
|
||||
const _ = require('lodash');
|
||||
const _ = require('lodash');
|
||||
const createClass = require('create-react-class');
|
||||
|
||||
const BadgeTemplate = require('./badgeTemplate.js');
|
||||
|
||||
const replaceAll = (text, target, str)=>text.replace(new RegExp(target, 'g'), str);
|
||||
const replaceAll = (text, target, str) => text.replace(new RegExp(target, 'g'), str);
|
||||
|
||||
const BadgeRender = createClass({
|
||||
getDefaultProps: function() {
|
||||
getDefaultProps: function () {
|
||||
return {
|
||||
title: '',
|
||||
text: '',
|
||||
rawSVG : '',
|
||||
color : '#333'
|
||||
rawSVG: '',
|
||||
color: '#333',
|
||||
};
|
||||
},
|
||||
componentWillReceiveProps: function(nextProps) {
|
||||
componentWillReceiveProps: function (nextProps) {
|
||||
this.drawBadge(nextProps);
|
||||
},
|
||||
|
||||
shouldComponentUpdate: function(nextProps, nextState) {
|
||||
return false
|
||||
shouldComponentUpdate: function (nextProps, nextState) {
|
||||
return false;
|
||||
},
|
||||
|
||||
componentDidMount: function() {
|
||||
componentDidMount: function () {
|
||||
this.ctx = this.refs.canvas.getContext('2d');
|
||||
this.drawBadge(this.props);
|
||||
},
|
||||
|
||||
handleDownload : function(){
|
||||
|
||||
handleDownload: function () {
|
||||
const target = document.createElement('a');
|
||||
const name = (this.props.title ? _.snakeCase(this.props.title) : 'badge')
|
||||
const name = this.props.title ? _.snakeCase(this.props.title) : 'badge';
|
||||
target.download = `${name}.png`;
|
||||
target.href = this.refs.canvas.toDataURL("image/png").replace(/^data:image\/[^;]/, 'data:application/octet-stream');
|
||||
target.href = this.refs.canvas
|
||||
.toDataURL('image/png')
|
||||
.replace(/^data:image\/[^;]/, 'data:application/octet-stream');
|
||||
target.click();
|
||||
},
|
||||
|
||||
clearCanvas : function(){
|
||||
clearCanvas: function () {
|
||||
this.ctx.clearRect(0, 0, this.refs.canvas.width, this.refs.canvas.height);
|
||||
},
|
||||
|
||||
readyFrame : function(color){
|
||||
return new Promise((resolve, reject)=>{
|
||||
readyFrame: function (color) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const frame = new Image();
|
||||
frame.src = `data:image/svg+xml;base64,${btoa(BadgeTemplate(color))}`;
|
||||
frame.onload = ()=>resolve(frame);
|
||||
frame.onload = () => resolve(frame);
|
||||
});
|
||||
},
|
||||
readyIconSVG : function(props){
|
||||
return new Promise((resolve, reject)=>{
|
||||
if(!props.rawSVG) return resolve();
|
||||
readyIconSVG: function (props) {
|
||||
return new Promise((resolve, reject) => {
|
||||
if (!props.rawSVG) return resolve();
|
||||
const icon = new Image();
|
||||
let svg = props.rawSVG || '';
|
||||
//console.log(svg);
|
||||
if(svg.indexOf('style=') === -1){
|
||||
svg = _.reduce(['path', 'rect', 'polygon', 'circle', 'polyline', 'ellipse'], (acc, type)=>{
|
||||
return replaceAll(acc, `<${type}`, `<${type} style="fill:${props.color}"`);
|
||||
}, svg);
|
||||
if (svg.indexOf('style=') === -1) {
|
||||
svg = _.reduce(
|
||||
['path', 'rect', 'polygon', 'circle', 'polyline', 'ellipse'],
|
||||
(acc, type) => {
|
||||
return replaceAll(acc, `<${type}`, `<${type} style="fill:${props.color}"`);
|
||||
},
|
||||
svg
|
||||
);
|
||||
}
|
||||
svg = svg.replace(/<text.*<\/text>/, '');
|
||||
icon.onload = ()=>resolve(icon);
|
||||
icon.onload = () => resolve(icon);
|
||||
icon.src = `data:image/svg+xml;base64,${btoa(svg)}`;
|
||||
});
|
||||
},
|
||||
drawSVG : function(props){
|
||||
return Promise.all([this.readyFrame(props.color), this.readyIconSVG(props)])
|
||||
.then(([frame, icon])=>{
|
||||
this.clearCanvas();
|
||||
if(frame) this.ctx.drawImage(frame, 0, 0);
|
||||
if(icon){
|
||||
const scale = 1.1
|
||||
const newWidth = icon.width * scale;
|
||||
const newHeight = icon.height * scale;
|
||||
this.ctx.drawImage(icon,
|
||||
150 - newWidth / 2, 120 - newWidth / 2,
|
||||
newWidth, newHeight);
|
||||
}
|
||||
})
|
||||
drawSVG: function (props) {
|
||||
return Promise.all([this.readyFrame(props.color), this.readyIconSVG(props)]).then(([frame, icon]) => {
|
||||
this.clearCanvas();
|
||||
if (frame) this.ctx.drawImage(frame, 0, 0);
|
||||
if (icon) {
|
||||
const scale = 1.1;
|
||||
const newWidth = icon.width * scale;
|
||||
const newHeight = icon.height * scale;
|
||||
this.ctx.drawImage(icon, 150 - newWidth / 2, 120 - newWidth / 2, newWidth, newHeight);
|
||||
}
|
||||
});
|
||||
},
|
||||
drawTitle : function(title){
|
||||
this.ctx.textAlign='center';
|
||||
this.ctx.textBaseline="middle";
|
||||
this.ctx.fillStyle = "#ffffff";
|
||||
drawTitle: function (title) {
|
||||
this.ctx.textAlign = 'center';
|
||||
this.ctx.textBaseline = 'middle';
|
||||
this.ctx.fillStyle = '#ffffff';
|
||||
|
||||
const trySize = (font)=>{
|
||||
this.ctx.font=`${font}px Calluna`;
|
||||
const trySize = (font) => {
|
||||
this.ctx.font = `${font}px Calluna`;
|
||||
const length = this.ctx.measureText(title).width;
|
||||
if(length >= 230) return trySize(font-1);
|
||||
if (length >= 230) return trySize(font - 1);
|
||||
return font;
|
||||
};
|
||||
const finalSize = trySize(35);
|
||||
this.ctx.fillText(title,150,220);
|
||||
this.ctx.fillText(title, 150, 220);
|
||||
},
|
||||
drawText : function(text){
|
||||
this.ctx.textAlign='left';
|
||||
this.ctx.font='bold 18px Calluna';
|
||||
this.ctx.fillStyle = "#000";
|
||||
drawText: function (text) {
|
||||
this.ctx.textAlign = 'left';
|
||||
this.ctx.font = 'bold 18px Calluna';
|
||||
this.ctx.fillStyle = '#000';
|
||||
|
||||
const lines = _.reduce(text.split(' '), (acc, word)=>{
|
||||
const currLine = _.last(acc);
|
||||
const length = this.ctx.measureText(`${currLine.join(' ')} ${word}`).width;
|
||||
if(length >= this.refs.canvas.width - 30){
|
||||
acc.push([word]);
|
||||
}else{
|
||||
currLine.push(word);
|
||||
}
|
||||
return acc;
|
||||
}, [[]]);
|
||||
_.each(lines, (line, index)=>{
|
||||
this.ctx.fillText(line.join(' '),15,315 + index*20);
|
||||
const lines = _.reduce(
|
||||
text.split(' '),
|
||||
(acc, word) => {
|
||||
const currLine = _.last(acc);
|
||||
const length = this.ctx.measureText(`${currLine.join(' ')} ${word}`).width;
|
||||
if (length >= this.refs.canvas.width - 30) {
|
||||
acc.push([word]);
|
||||
} else {
|
||||
currLine.push(word);
|
||||
}
|
||||
return acc;
|
||||
},
|
||||
[[]]
|
||||
);
|
||||
_.each(lines, (line, index) => {
|
||||
this.ctx.fillText(line.join(' '), 15, 315 + index * 20);
|
||||
});
|
||||
},
|
||||
drawAttribution : function(svg){
|
||||
drawAttribution: function (svg) {
|
||||
const canvas = this.refs.canvas;
|
||||
this.ctx.textAlign='left';
|
||||
this.ctx.font='9px Open Sans';
|
||||
this.ctx.fillStyle = "#bbb";
|
||||
this.ctx.textAlign = 'left';
|
||||
this.ctx.font = '9px Open Sans';
|
||||
this.ctx.fillStyle = '#bbb';
|
||||
let maxDepth = 95;
|
||||
|
||||
const check = svg.match(/<text.*<\/text>/);
|
||||
if(check && check.length){
|
||||
if (check && check.length) {
|
||||
const a = check[0].indexOf('Created by ') + 11;
|
||||
const b = check[0].indexOf('</text>');
|
||||
const author = check[0].substr(a, b-a);
|
||||
const author = check[0].substr(a, b - a);
|
||||
|
||||
const width = this.ctx.measureText(`Icon by ${author}`).width;
|
||||
maxDepth = _.max([maxDepth, width + 3]);
|
||||
@ -132,28 +138,29 @@ const BadgeRender = createClass({
|
||||
this.ctx.fillText(`Made with NaturalCrit`, canvas.width - maxDepth, canvas.height - 7);
|
||||
},
|
||||
|
||||
drawBadge : function(props){
|
||||
let height = (props.text ? 400 : 320);
|
||||
if(this.refs.canvas.height != height) this.refs.canvas.height = height;
|
||||
this.drawSVG(props)
|
||||
.then(()=>{
|
||||
this.drawTitle(props.title);
|
||||
this.drawText(props.text);
|
||||
this.drawAttribution(props.rawSVG);
|
||||
})
|
||||
drawBadge: function (props) {
|
||||
let height = props.text ? 400 : 320;
|
||||
if (this.refs.canvas.height != height) this.refs.canvas.height = height;
|
||||
this.drawSVG(props).then(() => {
|
||||
this.drawTitle(props.title);
|
||||
this.drawText(props.text);
|
||||
this.drawAttribution(props.rawSVG);
|
||||
});
|
||||
},
|
||||
|
||||
render: function(){
|
||||
return <div className='badgeRender'>
|
||||
<canvas ref='canvas' width={300} height={320}/>
|
||||
<div>
|
||||
<button onClick={this.handleDownload}>
|
||||
<i className='fa fa-download' />
|
||||
Download
|
||||
</button>
|
||||
render: function () {
|
||||
return (
|
||||
<div className="badgeRender">
|
||||
<canvas ref="canvas" width={300} height={320} />
|
||||
<div>
|
||||
<button onClick={this.handleDownload}>
|
||||
<i className="fa fa-download" />
|
||||
Download
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
module.exports = BadgeRender;
|
||||
|
||||
@ -1,33 +1,37 @@
|
||||
|
||||
@font-face {
|
||||
font-family : Calluna;
|
||||
font-style : normal;
|
||||
font-weight : normal;
|
||||
src : url('/assets/badges/badgeRender/calluna.woff2') format('woff2'),
|
||||
url('/assets/badges/badgeRender/calluna.woff') format('woff');
|
||||
font-family: Calluna;
|
||||
font-style: normal;
|
||||
font-weight: normal;
|
||||
src: url('/assets/badges/badgeRender/calluna.woff2') format('woff2'),
|
||||
url('/assets/badges/badgeRender/calluna.woff') format('woff');
|
||||
}
|
||||
.badgeRender{
|
||||
text-align : center;
|
||||
canvas{
|
||||
border : 1px solid #999;
|
||||
|
||||
.badgeRender {
|
||||
text-align: center;
|
||||
|
||||
canvas {
|
||||
border: 1px solid #999;
|
||||
}
|
||||
button{
|
||||
|
||||
button {
|
||||
.animate(background-color);
|
||||
margin : 10px auto;
|
||||
padding : 5px 20px;
|
||||
cursor : pointer;
|
||||
background-color : @green;
|
||||
font-size : 0.8em;
|
||||
color : white;
|
||||
border : none;
|
||||
highlight : none;
|
||||
i{
|
||||
vertical-align : middle;
|
||||
margin-right : 8px;
|
||||
font-size : 2em;
|
||||
margin: 10px auto;
|
||||
padding: 5px 20px;
|
||||
cursor: pointer;
|
||||
background-color: @green;
|
||||
font-size: 0.8em;
|
||||
color: white;
|
||||
border: none;
|
||||
highlight: none;
|
||||
|
||||
i {
|
||||
vertical-align: middle;
|
||||
margin-right: 8px;
|
||||
font-size: 2em;
|
||||
}
|
||||
&:hover{
|
||||
background-color : darken(@green, 10%);
|
||||
|
||||
&:hover {
|
||||
background-color: darken(@green, 10%);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,14 +1,26 @@
|
||||
module.exports = (color = '#666')=>{
|
||||
|
||||
|
||||
module.exports = (color = '#666') => {
|
||||
function shadeColor2(color, percent) {
|
||||
var f=parseInt(color.slice(1),16),t=percent<0?0:255,p=percent<0?percent*-1:percent,R=f>>16,G=f>>8&0x00FF,B=f&0x0000FF;
|
||||
return "#"+(0x1000000+(Math.round((t-R)*p)+R)*0x10000+(Math.round((t-G)*p)+G)*0x100+(Math.round((t-B)*p)+B)).toString(16).slice(1);
|
||||
var f = parseInt(color.slice(1), 16),
|
||||
t = percent < 0 ? 0 : 255,
|
||||
p = percent < 0 ? percent * -1 : percent,
|
||||
R = f >> 16,
|
||||
G = (f >> 8) & 0x00ff,
|
||||
B = f & 0x0000ff;
|
||||
return (
|
||||
'#' +
|
||||
(
|
||||
0x1000000 +
|
||||
(Math.round((t - R) * p) + R) * 0x10000 +
|
||||
(Math.round((t - G) * p) + G) * 0x100 +
|
||||
(Math.round((t - B) * p) + B)
|
||||
)
|
||||
.toString(16)
|
||||
.slice(1)
|
||||
);
|
||||
}
|
||||
|
||||
const light = shadeColor2(color, 0.4);
|
||||
const dark = shadeColor2(color, -0.3);
|
||||
|
||||
|
||||
return `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 344.83 341.02"><defs><style>.cls-1{fill:${color};}.cls-2{fill:${light};}.cls-3{fill:${dark};}</style></defs><title>Asset 1</title><g id="Layer_2" data-name="Layer 2"><g id="Layer_1-2" data-name="Layer 1"><polygon class="cls-1" points="344.83 291.26 297.49 291.26 297.49 239.3 344.83 239.3 324.5 264.36 344.83 291.26"/><polygon class="cls-1" points="0 239.3 47.34 239.3 47.34 291.26 0 291.26 20.34 266.2 0 239.3"/><path class="cls-1" d="M172.42,341,93.87,283.33A139.56,139.56,0,0,1,37.13,171.25V32.78L60.05,29.9A429.26,429.26,0,0,0,163.15,3.64L172.43,0l8,3.16A429.34,429.34,0,0,0,283.73,29.73l24,3.06V171.25A139.56,139.56,0,0,1,251,283.33l-6.32-8.61,6.32,8.61ZM58.49,51.62V171.25a118.13,118.13,0,0,0,48,94.87l65.9,48.41,65.9-48.41a118.13,118.13,0,0,0,48-94.87V51.6L281,50.92A450.67,450.67,0,0,1,172.57,23L172.4,23l-1.46.57A450.65,450.65,0,0,1,62.71,51.09Z"/><path class="cls-1" d="M172.42,341,93.87,283.33A139.56,139.56,0,0,1,37.13,171.25V32.78L60.05,29.9A429.26,429.26,0,0,0,163.15,3.64L172.43,0l8,3.16A429.34,429.34,0,0,0,283.73,29.73l24,3.06V171.25A139.56,139.56,0,0,1,251,283.33l-6.32-8.61,6.32,8.61ZM58.49,51.62V171.25a118.13,118.13,0,0,0,48,94.87l65.9,48.41,65.9-48.41a118.13,118.13,0,0,0,48-94.87V51.6L281,50.92A450.67,450.67,0,0,1,172.57,23L172.4,23l-1.46.57A450.65,450.65,0,0,1,62.71,51.09Z"/><path class="cls-1" d="M172.42,302.22l-60-44.09a108.18,108.18,0,0,1-44-86.88V60.37L73,59.7A461.13,461.13,0,0,0,170.5,34.33l1.9-.72,1.9.72a461.12,461.12,0,0,0,97.56,25.35l4.57.67V171.25a108.18,108.18,0,0,1-44,86.88ZM79.08,69.58V171.25a97.46,97.46,0,0,0,39.62,78.27L172.42,289l53.71-39.45a97.46,97.46,0,0,0,39.63-78.27V69.55A471.85,471.85,0,0,1,172.4,45,472,472,0,0,1,79.08,69.58Z"/><rect class="cls-2" x="27.78" y="223.53" width="289.27" height="51.96"/><polygon class="cls-3" points="317.05 275.49 297.49 275.49 297.49 291.26 317.05 275.49"/><polygon class="cls-3" points="27.78 275.49 47.34 275.49 47.34 291.26 27.78 275.49"/></g></g></svg>`;
|
||||
}
|
||||
};
|
||||
|
||||
File diff suppressed because one or more lines are too long
@ -1,52 +1,58 @@
|
||||
|
||||
@import "shared/naturalcrit/styles/core.less";
|
||||
.badges{
|
||||
width : 800px;
|
||||
margin : auto;
|
||||
h1{
|
||||
margin-bottom : 10px;
|
||||
padding-top : 30px;
|
||||
font-family : 'Calluna';
|
||||
font-size : 2.5em;
|
||||
font-weight : 800;
|
||||
text-align : center;
|
||||
|
||||
.badges {
|
||||
width: 800px;
|
||||
margin: auto;
|
||||
|
||||
h1 {
|
||||
margin-bottom: 10px;
|
||||
padding-top: 30px;
|
||||
font-family: 'Calluna';
|
||||
font-size: 2.5em;
|
||||
font-weight: 800;
|
||||
text-align: center;
|
||||
}
|
||||
&>p{
|
||||
margin-bottom : 20px;
|
||||
font-size : 0.8em;
|
||||
text-align : center;
|
||||
|
||||
&>p {
|
||||
margin-bottom: 20px;
|
||||
font-size: 0.8em;
|
||||
text-align: center;
|
||||
//font-family: 'Calluna';
|
||||
}
|
||||
.content{
|
||||
display : flex;
|
||||
padding-top : 50px;
|
||||
justify-content : space-around;
|
||||
.badgeRender, .controls{
|
||||
padding : 0px 50px;
|
||||
|
||||
.content {
|
||||
display: flex;
|
||||
padding-top: 50px;
|
||||
justify-content: space-around;
|
||||
|
||||
.badgeRender,
|
||||
.controls {
|
||||
padding: 0px 50px;
|
||||
}
|
||||
}
|
||||
.credit{
|
||||
|
||||
.credit {
|
||||
display: block;
|
||||
margin-top : 50px;
|
||||
opacity : 0.8;
|
||||
font-family : 'Calluna';
|
||||
font-size : 0.8em;
|
||||
text-align : center;
|
||||
margin-top: 50px;
|
||||
opacity: 0.8;
|
||||
font-family: 'Calluna';
|
||||
font-size: 0.8em;
|
||||
text-align: center;
|
||||
text-decoration: none;
|
||||
color : inherit;
|
||||
color: inherit;
|
||||
cursor: pointer;
|
||||
line-height: 1.2em;
|
||||
|
||||
&:hover{
|
||||
&:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.logo {
|
||||
position:fixed;
|
||||
top:10px;
|
||||
left:10px;
|
||||
position: fixed;
|
||||
top: 10px;
|
||||
left: 10px;
|
||||
font-size: 2em;
|
||||
color: black;
|
||||
text-decoration: none;
|
||||
@ -65,5 +71,4 @@
|
||||
font-family: 'CodeBold';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,86 +1,95 @@
|
||||
const React = require('react');
|
||||
const _ = require('lodash');
|
||||
const _ = require('lodash');
|
||||
const createClass = require('create-react-class');
|
||||
const cx = require('classnames');
|
||||
const Color = require('react-color');
|
||||
|
||||
|
||||
const Controls = createClass({
|
||||
getDefaultProps: function() {
|
||||
getDefaultProps: function () {
|
||||
return {
|
||||
data : {
|
||||
data: {
|
||||
title: '',
|
||||
text: '',
|
||||
color : '#2b4486',
|
||||
rawSVG : ''
|
||||
color: '#2b4486',
|
||||
rawSVG: '',
|
||||
},
|
||||
onChange : ()=>{}
|
||||
onChange: () => {},
|
||||
};
|
||||
},
|
||||
getInitialState: function() {
|
||||
getInitialState: function () {
|
||||
return {
|
||||
hover: false
|
||||
hover: false,
|
||||
};
|
||||
},
|
||||
handleDrop: function(e){
|
||||
handleDrop: function (e) {
|
||||
e.preventDefault();
|
||||
const files = e.target.files || e.dataTransfer.files;
|
||||
const reader = new FileReader();
|
||||
reader.onload = (e)=>{
|
||||
reader.onload = (e) => {
|
||||
this.handleChange('rawSVG', e.target.result);
|
||||
}
|
||||
};
|
||||
reader.readAsText(files[0]);
|
||||
this.setState({hover : false});
|
||||
this.setState({ hover: false });
|
||||
},
|
||||
handleHover : function(e, val){
|
||||
handleHover: function (e, val) {
|
||||
e.preventDefault();
|
||||
this.setState({hover : val});
|
||||
this.setState({ hover: val });
|
||||
},
|
||||
handleChange : function(path, val){
|
||||
handleChange: function (path, val) {
|
||||
this.props.onChange(_.set(this.props.data, path, val));
|
||||
},
|
||||
render: function(){
|
||||
return <div className='controls'>
|
||||
<div className='field'>
|
||||
<label>Title</label>
|
||||
<input type='text'
|
||||
className='value'
|
||||
value={this.props.data.title}
|
||||
onChange={(e)=>this.handleChange('title', e.target.value)} />
|
||||
</div>
|
||||
<div className='field'>
|
||||
<label>Text</label>
|
||||
<textarea type='text'
|
||||
className='value'
|
||||
rows={3}
|
||||
value={this.props.data.text}
|
||||
onChange={(e)=>this.handleChange('text', e.target.value)} />
|
||||
</div>
|
||||
<div className='field'>
|
||||
<label>Color</label>
|
||||
<Color.SliderPicker
|
||||
className='value'
|
||||
disableAlpha={true}
|
||||
color={this.props.data.color}
|
||||
onChange={(colorObj)=>this.handleChange('color', colorObj.hex)}
|
||||
/>
|
||||
</div>
|
||||
<div className='field svg'>
|
||||
<label>SVG</label>
|
||||
<div className='value'>
|
||||
<div className={cx('dropZone', {hover : this.state.hover})}
|
||||
onDragOver={(e)=>this.handleHover(e, true)}
|
||||
onDragLeave={(e)=>this.handleHover(e, false)}
|
||||
onDrop={this.handleDrop}>
|
||||
<i className='fa fa-arrow-down' />
|
||||
<p>Drop SVG here</p>
|
||||
render: function () {
|
||||
return (
|
||||
<div className="controls">
|
||||
<div className="field">
|
||||
<label>Title</label>
|
||||
<input
|
||||
type="text"
|
||||
className="value"
|
||||
value={this.props.data.title}
|
||||
onChange={(e) => this.handleChange('title', e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
<div className="field">
|
||||
<label>Text</label>
|
||||
<textarea
|
||||
type="text"
|
||||
className="value"
|
||||
rows={3}
|
||||
value={this.props.data.text}
|
||||
onChange={(e) => this.handleChange('text', e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
<div className="field">
|
||||
<label>Color</label>
|
||||
<Color.SliderPicker
|
||||
className="value"
|
||||
disableAlpha={true}
|
||||
color={this.props.data.color}
|
||||
onChange={(colorObj) => this.handleChange('color', colorObj.hex)}
|
||||
/>
|
||||
</div>
|
||||
<div className="field svg">
|
||||
<label>SVG</label>
|
||||
<div className="value">
|
||||
<div
|
||||
className={cx('dropZone', { hover: this.state.hover })}
|
||||
onDragOver={(e) => this.handleHover(e, true)}
|
||||
onDragLeave={(e) => this.handleHover(e, false)}
|
||||
onDrop={this.handleDrop}>
|
||||
<i className="fa fa-arrow-down" />
|
||||
<p>Drop SVG here</p>
|
||||
</div>
|
||||
<input type="file" onChange={this.handleDrop} />
|
||||
<p>
|
||||
Download an icon from <a href="https://thenounproject.com/">The Noun Project</a>, then drag
|
||||
and drop it here.
|
||||
</p>
|
||||
</div>
|
||||
<input type='file' onChange={this.handleDrop} />
|
||||
<p>Download an icon from <a href='https://thenounproject.com/'>The Noun Project</a>, then drag and drop it here.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
module.exports = Controls;
|
||||
|
||||
@ -1,47 +1,58 @@
|
||||
.controls {
|
||||
width: 500px;
|
||||
|
||||
.controls{
|
||||
width : 500px;
|
||||
.field{
|
||||
display : flex;
|
||||
margin-bottom : 10px;
|
||||
label{
|
||||
vertical-align : top;
|
||||
width : 70px;
|
||||
font-size : 0.8em;
|
||||
font-weight : 800;
|
||||
.field {
|
||||
display: flex;
|
||||
margin-bottom: 10px;
|
||||
|
||||
label {
|
||||
vertical-align: top;
|
||||
width: 70px;
|
||||
font-size: 0.8em;
|
||||
font-weight: 800;
|
||||
}
|
||||
.value, .slider-picker{
|
||||
flex-grow : 1;
|
||||
|
||||
.value,
|
||||
.slider-picker {
|
||||
flex-grow: 1;
|
||||
}
|
||||
textarea.value, input.value{
|
||||
padding : 4px;
|
||||
font-family : 'Open Sans';
|
||||
font-size : 1em;
|
||||
rows : 3;
|
||||
|
||||
textarea.value,
|
||||
input.value {
|
||||
padding: 4px;
|
||||
font-family: 'Open Sans';
|
||||
font-size: 1em;
|
||||
rows: 3;
|
||||
}
|
||||
&.svg{
|
||||
|
||||
&.svg {
|
||||
margin-left: 20px;
|
||||
.dropZone{
|
||||
|
||||
.dropZone {
|
||||
margin-left: -1px;
|
||||
padding : 20px 10px;
|
||||
font-size : 0.8em;
|
||||
font-weight : 800;
|
||||
text-align : center;
|
||||
border : 1px #999 dashed;
|
||||
border-width : 8px;
|
||||
i{
|
||||
font-size : 3em;
|
||||
color : #999
|
||||
padding: 20px 10px;
|
||||
font-size: 0.8em;
|
||||
font-weight: 800;
|
||||
text-align: center;
|
||||
border: 1px #999 dashed;
|
||||
border-width: 8px;
|
||||
|
||||
i {
|
||||
font-size: 3em;
|
||||
color: #999
|
||||
}
|
||||
&.hover{
|
||||
background-color : #ddd;
|
||||
|
||||
&.hover {
|
||||
background-color: #ddd;
|
||||
}
|
||||
}
|
||||
.value>p{
|
||||
margin-top : 10px;
|
||||
font-size : 0.7em;
|
||||
|
||||
.value>p {
|
||||
margin-top: 10px;
|
||||
font-size: 0.7em;
|
||||
}
|
||||
input{
|
||||
|
||||
input {
|
||||
margin-top: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,63 +1,65 @@
|
||||
const request = require('superagent');
|
||||
|
||||
const AccountActions = {
|
||||
|
||||
login : (user, pass) => {
|
||||
login: (user, pass) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
request.post('/login')
|
||||
.send({ user , pass })
|
||||
request
|
||||
.post('/login')
|
||||
.send({ user, pass })
|
||||
.end((err, res) => {
|
||||
if(err) return reject(res.body);
|
||||
if (err) return reject(res.body);
|
||||
AccountActions.createSession(res.body);
|
||||
return resolve(res.body);
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
signup : (user, pass) => {
|
||||
signup: (user, pass) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
request.post('/signup')
|
||||
.send({ user , pass })
|
||||
request
|
||||
.post('/signup')
|
||||
.send({ user, pass })
|
||||
.end((err, res) => {
|
||||
if(err) return reject(res.body);
|
||||
if (err) return reject(res.body);
|
||||
AccountActions.createSession(res.body);
|
||||
return resolve(res.body);
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
linkGoogle : (username, pass, user) => {
|
||||
linkGoogle: (username, pass, user) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
request.post('/link')
|
||||
.send({ username , pass, user })
|
||||
request
|
||||
.post('/link')
|
||||
.send({ username, pass, user })
|
||||
.end((err, res) => {
|
||||
if(err) return reject(res.body);
|
||||
if (err) return reject(res.body);
|
||||
AccountActions.createSession(res.body);
|
||||
return resolve(res.body);
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
checkUsername : (username) => {
|
||||
checkUsername: (username) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
request.get(`/user_exists/${username}`)
|
||||
request
|
||||
.get(`/user_exists/${username}`)
|
||||
.send()
|
||||
.end((err, res) => {
|
||||
if(err) return reject(res.body);
|
||||
if (err) return reject(res.body);
|
||||
return resolve(res.body);
|
||||
});
|
||||
});
|
||||
},
|
||||
createSession: (token) => {
|
||||
const domain = window.domain === '.local.naturalcrit.com' ? 'localhost' : window.domain;
|
||||
document.cookie = `nc_session=${token}; max-age=${60*60*24*365}; path=/; samesite=lax; domain=${domain};`;
|
||||
document.cookie = `nc_session=${token}; max-age=${60 * 60 * 24 * 365}; path=/; samesite=lax; domain=${domain};`;
|
||||
},
|
||||
|
||||
|
||||
removeSession : () => {
|
||||
removeSession: () => {
|
||||
const domain = window.domain === '.local.naturalcrit.com' ? 'localhost' : window.domain;
|
||||
document.cookie = `nc_session=, expires=Thu, 01 Jan 1970 00:00:01 GMT, samesite=lax, domain=${domain}`;
|
||||
}
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
module.exports = AccountActions;
|
||||
module.exports = AccountActions;
|
||||
|
||||
@ -12,7 +12,6 @@ const AccountPage = (props) => {
|
||||
<p>
|
||||
<b>Username:</b> {props.user.username}
|
||||
<br />
|
||||
|
||||
</p>
|
||||
<br />
|
||||
<button
|
||||
|
||||
@ -1,257 +1,283 @@
|
||||
const React = require('react');
|
||||
const _ = require('lodash');
|
||||
const cx = require('classnames');
|
||||
const _ = require('lodash');
|
||||
const cx = require('classnames');
|
||||
|
||||
const NaturalCritIcon = require('naturalcrit/svg/naturalcrit.svg.jsx');
|
||||
const AccountActions = require('../account.actions.js');
|
||||
//TODO: Almost identidal to "loginPage". Should possibly be merged to reduce redundancy
|
||||
|
||||
const LoginPage = React.createClass({
|
||||
getDefaultProps: function() {
|
||||
getDefaultProps: function () {
|
||||
return {
|
||||
redirect : '',
|
||||
user : null
|
||||
redirect: '',
|
||||
user: null,
|
||||
};
|
||||
},
|
||||
getInitialState: function() {
|
||||
getInitialState: function () {
|
||||
return {
|
||||
view : 'login', //or 'signup'
|
||||
visible : false,
|
||||
view: 'login', //or 'signup'
|
||||
visible: false,
|
||||
|
||||
username : '',
|
||||
password : '',
|
||||
username: '',
|
||||
password: '',
|
||||
|
||||
processing : false,
|
||||
checkingUsername : false,
|
||||
redirecting : false,
|
||||
processing: false,
|
||||
checkingUsername: false,
|
||||
redirecting: false,
|
||||
|
||||
usernameExists : false,
|
||||
usernameExists: false,
|
||||
|
||||
errors : null,
|
||||
success : false,
|
||||
errors: null,
|
||||
success: false,
|
||||
};
|
||||
},
|
||||
componentDidMount: function() {
|
||||
window.document.onkeypress = (e)=>{
|
||||
if(e.code == 'Enter') this.handleClick();
|
||||
}
|
||||
componentDidMount: function () {
|
||||
window.document.onkeypress = (e) => {
|
||||
if (e.code == 'Enter') this.handleClick();
|
||||
};
|
||||
},
|
||||
|
||||
handleUserChange : function(e){
|
||||
handleUserChange: function (e) {
|
||||
this.setState(
|
||||
{
|
||||
usernameExists: true,
|
||||
checkingUsername: true,
|
||||
username: e.target.value,
|
||||
},
|
||||
() => {
|
||||
if (this.state.view === 'signup') this.checkUsername();
|
||||
}
|
||||
);
|
||||
},
|
||||
handlePassChange: function (e) {
|
||||
this.setState({ password: e.target.value });
|
||||
},
|
||||
handleClick: function () {
|
||||
if (!this.isValid()) return;
|
||||
if (this.state.view === 'login') this.login();
|
||||
if (this.state.view === 'signup') this.signup();
|
||||
},
|
||||
|
||||
redirect: function () {
|
||||
if (!this.props.redirect) return window.location.reload();
|
||||
this.setState(
|
||||
{
|
||||
redirecting: true,
|
||||
},
|
||||
() => {
|
||||
window.location = this.props.redirect;
|
||||
}
|
||||
);
|
||||
},
|
||||
|
||||
login: function () {
|
||||
this.setState({
|
||||
usernameExists : true,
|
||||
checkingUsername : true,
|
||||
username : e.target.value
|
||||
}, ()=>{ if(this.state.view==='signup') this.checkUsername(); });
|
||||
},
|
||||
handlePassChange : function(e){
|
||||
this.setState({ password : e.target.value });
|
||||
},
|
||||
handleClick : function(){
|
||||
if(!this.isValid()) return;
|
||||
if(this.state.view === 'login') this.login();
|
||||
if(this.state.view === 'signup') this.signup();
|
||||
},
|
||||
|
||||
redirect : function(){
|
||||
if(!this.props.redirect) return window.location.reload();
|
||||
this.setState({
|
||||
redirecting : true
|
||||
}, () => {window.location = this.props.redirect;});
|
||||
},
|
||||
|
||||
|
||||
login : function(){
|
||||
this.setState({
|
||||
processing : true,
|
||||
errors : null
|
||||
processing: true,
|
||||
errors: null,
|
||||
});
|
||||
AccountActions.login(this.state.username, this.state.password)
|
||||
.then(result => {
|
||||
AccountActions.linkGoogle(this.state.username, this.state.password, this.props.user);
|
||||
})
|
||||
.then((token) => {
|
||||
window.location = '/success';
|
||||
})
|
||||
.catch((err) => {
|
||||
console.log(err);
|
||||
this.setState({
|
||||
processing : false,
|
||||
errors : err
|
||||
.then((result) => {
|
||||
AccountActions.linkGoogle(this.state.username, this.state.password, this.props.user);
|
||||
})
|
||||
.then((token) => {
|
||||
window.location = '/success';
|
||||
})
|
||||
.catch((err) => {
|
||||
console.log(err);
|
||||
this.setState({
|
||||
processing: false,
|
||||
errors: err,
|
||||
});
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
logout : function(e){
|
||||
logout: function (e) {
|
||||
e.preventDefault();
|
||||
AccountActions.removeSession();
|
||||
window.location.reload();
|
||||
return false;
|
||||
},
|
||||
|
||||
signup : function(){
|
||||
signup: function () {
|
||||
this.setState({
|
||||
processing : true,
|
||||
errors : null
|
||||
processing: true,
|
||||
errors: null,
|
||||
});
|
||||
AccountActions.signup(this.state.username, this.state.password)
|
||||
.then(result => {
|
||||
AccountActions.linkGoogle(this.state.username, this.state.password, this.props.user);
|
||||
})
|
||||
.then((token) => {
|
||||
window.location = '/success';
|
||||
})
|
||||
.catch((err) => {
|
||||
console.log(err);
|
||||
this.setState({
|
||||
processing : false,
|
||||
errors : err
|
||||
.then((result) => {
|
||||
AccountActions.linkGoogle(this.state.username, this.state.password, this.props.user);
|
||||
})
|
||||
.then((token) => {
|
||||
window.location = '/success';
|
||||
})
|
||||
.catch((err) => {
|
||||
console.log(err);
|
||||
this.setState({
|
||||
processing: false,
|
||||
errors: err,
|
||||
});
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
checkUsername : function(){
|
||||
if(this.state.username === '') return;
|
||||
checkUsername: function () {
|
||||
if (this.state.username === '') return;
|
||||
this.setState({
|
||||
checkingUsername : true
|
||||
checkingUsername: true,
|
||||
});
|
||||
this.debounceCheckUsername();
|
||||
},
|
||||
|
||||
debounceCheckUsername : _.debounce(function(){
|
||||
AccountActions.checkUsername(this.state.username)
|
||||
.then((doesExist) => {
|
||||
this.setState({
|
||||
usernameExists : !!doesExist,
|
||||
checkingUsername : false
|
||||
});
|
||||
})
|
||||
debounceCheckUsername: _.debounce(function () {
|
||||
AccountActions.checkUsername(this.state.username).then((doesExist) => {
|
||||
this.setState({
|
||||
usernameExists: !!doesExist,
|
||||
checkingUsername: false,
|
||||
});
|
||||
});
|
||||
}, 1000),
|
||||
|
||||
handleChangeView : function(newView){
|
||||
this.setState({
|
||||
view : newView,
|
||||
errors : null
|
||||
},this.checkUsername);
|
||||
handleChangeView: function (newView) {
|
||||
this.setState(
|
||||
{
|
||||
view: newView,
|
||||
errors: null,
|
||||
},
|
||||
this.checkUsername
|
||||
);
|
||||
},
|
||||
|
||||
isValid : function(){
|
||||
if(this.state.processing) return false;
|
||||
isValid: function () {
|
||||
if (this.state.processing) return false;
|
||||
|
||||
if(this.state.view === 'login'){
|
||||
if (this.state.view === 'login') {
|
||||
return this.state.username && this.state.password;
|
||||
}else if(this.state.view === 'signup'){
|
||||
} else if (this.state.view === 'signup') {
|
||||
return this.state.username && this.state.password && !this.state.usernameExists;
|
||||
}
|
||||
},
|
||||
|
||||
renderErrors : function(){
|
||||
if(!this.state.errors) return;
|
||||
if(this.state.errors.msg) return <div className='errors'>{this.state.errors.msg}</div>;
|
||||
return <div className='errors'>Something went wrong</div>;
|
||||
renderErrors: function () {
|
||||
if (!this.state.errors) return;
|
||||
if (this.state.errors.msg) return <div className="errors">{this.state.errors.msg}</div>;
|
||||
return <div className="errors">Something went wrong</div>;
|
||||
},
|
||||
|
||||
renderUsernameValidation : function(){
|
||||
if(this.state.view === 'login') return;
|
||||
renderUsernameValidation: function () {
|
||||
if (this.state.view === 'login') return;
|
||||
|
||||
let icon = null;
|
||||
|
||||
if(this.state.checkingUsername){
|
||||
icon = <i className='fa fa-spinner fa-spin' />;
|
||||
}else if(!this.state.username || this.state.usernameExists){
|
||||
icon = <i className='fa fa-times red' />;
|
||||
}else if(!this.state.usernameExists){
|
||||
icon = <i className='fa fa-check green' />
|
||||
if (this.state.checkingUsername) {
|
||||
icon = <i className="fa fa-spinner fa-spin" />;
|
||||
} else if (!this.state.username || this.state.usernameExists) {
|
||||
icon = <i className="fa fa-times red" />;
|
||||
} else if (!this.state.usernameExists) {
|
||||
icon = <i className="fa fa-check green" />;
|
||||
}
|
||||
|
||||
return <div className='control'>{icon}</div>
|
||||
return <div className="control">{icon}</div>;
|
||||
},
|
||||
|
||||
renderButton : function(){
|
||||
renderButton: function () {
|
||||
let className = '';
|
||||
let text = '';
|
||||
let icon = '';
|
||||
|
||||
if(this.state.processing){
|
||||
if (this.state.processing) {
|
||||
className = 'processing';
|
||||
text = 'processing';
|
||||
icon = 'fa-spinner fa-spin';
|
||||
}else if(this.state.view === 'login'){
|
||||
} else if (this.state.view === 'login') {
|
||||
className = 'login';
|
||||
text = 'login';
|
||||
icon = 'fa-sign-in';
|
||||
}else if(this.state.view === 'signup'){
|
||||
} else if (this.state.view === 'signup') {
|
||||
className = 'signup';
|
||||
text = 'signup';
|
||||
icon = 'fa-user-plus';
|
||||
}
|
||||
|
||||
return <button
|
||||
className={cx('action', className)}
|
||||
disabled={!this.isValid()}
|
||||
onClick={this.handleClick}>
|
||||
<i className={`fa ${icon}`} />
|
||||
{text}
|
||||
</button>
|
||||
return (
|
||||
<button className={cx('action', className)} disabled={!this.isValid()} onClick={this.handleClick}>
|
||||
<i className={`fa ${icon}`} />
|
||||
{text}
|
||||
</button>
|
||||
);
|
||||
},
|
||||
|
||||
render : function(){
|
||||
return <div className='loginPage'>
|
||||
<div className='logo'>
|
||||
<NaturalCritIcon />
|
||||
<span className='name'>
|
||||
Natural
|
||||
<span className='crit'>Crit</span>
|
||||
</span>
|
||||
render: function () {
|
||||
return (
|
||||
<div className="loginPage">
|
||||
<div className="logo">
|
||||
<NaturalCritIcon />
|
||||
<span className="name">
|
||||
Natural
|
||||
<span className="crit">Crit</span>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<p>
|
||||
To finish linking your Google account to the Homebrewery, please create a user ID
|
||||
<br />
|
||||
for the Homebrewery below (or sign in to an existing Homebrewery account).
|
||||
<br />
|
||||
<br />
|
||||
You will only need to complete this step once. After your Google account is linked,
|
||||
<br />
|
||||
you will be able to access the Homebrewery with your Google account.
|
||||
</p>
|
||||
|
||||
<div className="content">
|
||||
<div className="switchView">
|
||||
<div
|
||||
className={cx('login', { 'selected': this.state.view === 'login' })}
|
||||
onClick={this.handleChangeView.bind(null, 'login')}>
|
||||
<i className="fa fa-sign-in" /> Login
|
||||
</div>
|
||||
|
||||
<div
|
||||
className={cx('signup', { 'selected': this.state.view === 'signup' })}
|
||||
onClick={this.handleChangeView.bind(null, 'signup')}>
|
||||
<i className="fa fa-user-plus" /> Signup
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="field user">
|
||||
<label>username</label>
|
||||
<input type="text" onChange={this.handleUserChange} value={this.state.username} />
|
||||
{this.renderUsernameValidation()}
|
||||
{this.state.usernameExists && !this.state.checkingUsername && this.state.view === 'signup' ? (
|
||||
<div className="userExists">User with that name already exists</div>
|
||||
) : null}
|
||||
</div>
|
||||
|
||||
<div className="field password">
|
||||
<label>password</label>
|
||||
<input
|
||||
type={cx({ text: this.state.visible, password: !this.state.visible })}
|
||||
onChange={this.handlePassChange}
|
||||
value={this.state.password}
|
||||
/>
|
||||
|
||||
<div
|
||||
className="control"
|
||||
onClick={() => {
|
||||
this.setState({ visible: !this.state.visible });
|
||||
}}>
|
||||
<i
|
||||
className={cx('fa', {
|
||||
'fa-eye': !this.state.visible,
|
||||
'fa-eye-slash': this.state.visible,
|
||||
})}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
{this.renderErrors()}
|
||||
{this.renderButton()}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<p>To finish linking your Google account to the Homebrewery, please create a user ID<br />
|
||||
for the Homebrewery below (or sign in to an existing Homebrewery account).<br />
|
||||
<br />
|
||||
You will only need to complete this step once. After your Google account is linked,<br />
|
||||
you will be able to access the Homebrewery with your Google account.</p>
|
||||
|
||||
<div className='content'>
|
||||
<div className='switchView'>
|
||||
<div className={cx('login', {'selected' : this.state.view === 'login'})}
|
||||
onClick={this.handleChangeView.bind(null, 'login')}>
|
||||
<i className='fa fa-sign-in' /> Login
|
||||
</div>
|
||||
|
||||
<div className={cx('signup', {'selected' : this.state.view === 'signup'})}
|
||||
onClick={this.handleChangeView.bind(null, 'signup')}>
|
||||
<i className='fa fa-user-plus' /> Signup
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
<div className='field user'>
|
||||
<label>username</label>
|
||||
<input
|
||||
type='text'
|
||||
onChange={this.handleUserChange}
|
||||
value={this.state.username} />
|
||||
{this.renderUsernameValidation()}
|
||||
{this.state.usernameExists && !this.state.checkingUsername && this.state.view==='signup' ? <div className='userExists'>User with that name already exists</div> : null}
|
||||
</div>
|
||||
|
||||
<div className='field password'>
|
||||
<label>password</label>
|
||||
<input
|
||||
type={cx({text : this.state.visible, password : !this.state.visible})}
|
||||
onChange={this.handlePassChange}
|
||||
value={this.state.password} />
|
||||
|
||||
<div className='control' onClick={()=>{this.setState({visible : !this.state.visible})}}>
|
||||
<i className={cx('fa', {'fa-eye' : !this.state.visible, 'fa-eye-slash' : this.state.visible})} />
|
||||
</div>
|
||||
</div>
|
||||
{this.renderErrors()}
|
||||
{this.renderButton()}
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
module.exports = LoginPage;
|
||||
|
||||
@ -1,104 +1,129 @@
|
||||
|
||||
.loginPage{
|
||||
text-align : center;
|
||||
.loginPage {
|
||||
text-align: center;
|
||||
padding-top: 30px;
|
||||
.content{
|
||||
|
||||
width : 400px;
|
||||
margin : 0 auto;
|
||||
padding : 20px;
|
||||
.content {
|
||||
|
||||
width: 400px;
|
||||
margin: 0 auto;
|
||||
padding: 20px;
|
||||
padding-top: 30px;
|
||||
.switchView{
|
||||
width : 100%;
|
||||
&>div{
|
||||
|
||||
.switchView {
|
||||
width: 100%;
|
||||
|
||||
&>div {
|
||||
.animate(background-color);
|
||||
display : inline-block;
|
||||
width : 50%;
|
||||
padding : 10px;
|
||||
cursor : pointer;
|
||||
background-color : transparent;
|
||||
font-size : 0.8em;
|
||||
font-weight : 800;
|
||||
text-transform : uppercase;
|
||||
i{
|
||||
vertical-align : middle;
|
||||
font-size : 2em;
|
||||
display: inline-block;
|
||||
width: 50%;
|
||||
padding: 10px;
|
||||
cursor: pointer;
|
||||
background-color: transparent;
|
||||
font-size: 0.8em;
|
||||
font-weight: 800;
|
||||
text-transform: uppercase;
|
||||
|
||||
i {
|
||||
vertical-align: middle;
|
||||
font-size: 2em;
|
||||
}
|
||||
}
|
||||
.login:hover, .login.selected{
|
||||
background-color : fade(@blue, 20%);
|
||||
|
||||
.login:hover,
|
||||
.login.selected {
|
||||
background-color: fade(@blue, 20%);
|
||||
}
|
||||
.signup:hover, .signup.selected{
|
||||
background-color : fade(@green, 20%);
|
||||
|
||||
.signup:hover,
|
||||
.signup.selected {
|
||||
background-color: fade(@green, 20%);
|
||||
}
|
||||
}
|
||||
.field{
|
||||
position : relative;
|
||||
margin : 20px 0px;
|
||||
text-align : left;
|
||||
label{
|
||||
display : block;
|
||||
width : 100%;
|
||||
font-size : 0.6em;
|
||||
font-weight : 800;
|
||||
line-height : 1.1em;
|
||||
text-transform : uppercase;
|
||||
|
||||
.field {
|
||||
position: relative;
|
||||
margin: 20px 0px;
|
||||
text-align: left;
|
||||
|
||||
label {
|
||||
display: block;
|
||||
width: 100%;
|
||||
font-size: 0.6em;
|
||||
font-weight: 800;
|
||||
line-height: 1.1em;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
input{
|
||||
width : 100%;
|
||||
padding : 10px;
|
||||
font-size : 1.2em;
|
||||
color : #333;
|
||||
|
||||
input {
|
||||
width: 100%;
|
||||
padding: 10px;
|
||||
font-size: 1.2em;
|
||||
color: #333;
|
||||
}
|
||||
.control{
|
||||
position : absolute;
|
||||
display : block;
|
||||
right : -40px;
|
||||
bottom : 0px;
|
||||
height : 46px;
|
||||
width : 40px;
|
||||
text-align : center;
|
||||
i{
|
||||
margin-top : 15px;
|
||||
|
||||
.control {
|
||||
position: absolute;
|
||||
display: block;
|
||||
right: -40px;
|
||||
bottom: 0px;
|
||||
height: 46px;
|
||||
width: 40px;
|
||||
text-align: center;
|
||||
|
||||
i {
|
||||
margin-top: 15px;
|
||||
}
|
||||
}
|
||||
&.password .control{
|
||||
cursor : pointer;
|
||||
|
||||
&.password .control {
|
||||
cursor: pointer;
|
||||
}
|
||||
.userExists{
|
||||
|
||||
.userExists {
|
||||
font-size: 0.6em;
|
||||
position: absolute;
|
||||
right : 0px;
|
||||
color : @red;
|
||||
right: 0px;
|
||||
color: @red;
|
||||
}
|
||||
}
|
||||
button.action{
|
||||
|
||||
button.action {
|
||||
.animate(opacity);
|
||||
padding : 10px 20px;
|
||||
cursor : pointer;
|
||||
opacity : 0.8;
|
||||
font-size : 0.8em;
|
||||
font-weight : 800;
|
||||
color : black;
|
||||
text-transform : uppercase;
|
||||
border : none;
|
||||
outline : none;
|
||||
&:hover{
|
||||
opacity : 1;
|
||||
padding: 10px 20px;
|
||||
cursor: pointer;
|
||||
opacity: 0.8;
|
||||
font-size: 0.8em;
|
||||
font-weight: 800;
|
||||
color: black;
|
||||
text-transform: uppercase;
|
||||
border: none;
|
||||
outline: none;
|
||||
|
||||
&:hover {
|
||||
opacity: 1;
|
||||
}
|
||||
&:disabled{
|
||||
cursor : not-allowed;
|
||||
opacity : 0.3;
|
||||
background-color : #ddd;
|
||||
|
||||
&:disabled {
|
||||
cursor: not-allowed;
|
||||
opacity: 0.3;
|
||||
background-color: #ddd;
|
||||
}
|
||||
&.login{ background-color: @blue; }
|
||||
&.signup{ background-color: @green; }
|
||||
i{
|
||||
margin-right : 10px;
|
||||
font-size : 1.5em;
|
||||
|
||||
&.login {
|
||||
background-color: @blue;
|
||||
}
|
||||
|
||||
&.signup {
|
||||
background-color: @green;
|
||||
}
|
||||
|
||||
i {
|
||||
margin-right: 10px;
|
||||
font-size: 1.5em;
|
||||
}
|
||||
}
|
||||
button.google{
|
||||
|
||||
button.google {
|
||||
cursor: pointer;
|
||||
width: 191px;
|
||||
height: 46px;
|
||||
@ -106,23 +131,27 @@
|
||||
outline: none;
|
||||
background-image: url('../assets/naturalcrit/styles/btn_google_signin_light_normal_web.png');
|
||||
background-size: contain;
|
||||
&:hover{
|
||||
background-image: url('../assets/naturalcrit/styles/btn_google_signin_light_hover_web.png');
|
||||
|
||||
&:hover {
|
||||
background-image: url('../assets/naturalcrit/styles/btn_google_signin_light_hover_web.png');
|
||||
}
|
||||
&:focus{
|
||||
background-image: url('../assets/naturalcrit/styles/btn_google_signin_light_pressed_web.png');
|
||||
|
||||
&:focus {
|
||||
background-image: url('../assets/naturalcrit/styles/btn_google_signin_light_pressed_web.png');
|
||||
}
|
||||
}
|
||||
.errors{
|
||||
margin-bottom : 20px;
|
||||
color : @red;
|
||||
|
||||
.errors {
|
||||
margin-bottom: 20px;
|
||||
color: @red;
|
||||
}
|
||||
}
|
||||
.divider{
|
||||
|
||||
.divider {
|
||||
font-size: 1em;
|
||||
font-weight: 800;
|
||||
color: black;
|
||||
text-transform: uppercase;
|
||||
padding: 12px 20px 10px;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,6 +1,6 @@
|
||||
const React = require('react');
|
||||
const _ = require('lodash');
|
||||
const cx = require('classnames');
|
||||
const _ = require('lodash');
|
||||
const cx = require('classnames');
|
||||
|
||||
const NaturalCritIcon = require('naturalcrit/components/naturalcritLogo.jsx');
|
||||
|
||||
@ -8,75 +8,75 @@ const HomebrewIcon = require('naturalcrit/svg/homebrew.svg.jsx');
|
||||
const TPKIcon = require('naturalcrit/svg/tpk.svg.jsx');
|
||||
const BadgeIcon = require('naturalcrit/svg/badge.svg.jsx');
|
||||
|
||||
|
||||
const HomePage = React.createClass({
|
||||
getDefaultProps: function() {
|
||||
return {
|
||||
tools : [
|
||||
{
|
||||
id : 'homebrew',
|
||||
path : 'https://homebrewery.naturalcrit.com',
|
||||
name : 'The Homebrewery',
|
||||
icon : <HomebrewIcon />,
|
||||
desc : 'Make authentic-looking D&D homebrews using Markdown',
|
||||
getDefaultProps: function () {
|
||||
return {
|
||||
tools: [
|
||||
{
|
||||
id: 'homebrew',
|
||||
path: 'https://homebrewery.naturalcrit.com',
|
||||
name: 'The Homebrewery',
|
||||
icon: <HomebrewIcon />,
|
||||
desc: 'Make authentic-looking D&D homebrews using Markdown',
|
||||
|
||||
show : true,
|
||||
beta : false
|
||||
},
|
||||
{
|
||||
id : 'badges',
|
||||
path : '/badges',
|
||||
name : 'Achievement Badges',
|
||||
icon : <BadgeIcon />,
|
||||
desc : 'Create simple badges to award your players',
|
||||
show: true,
|
||||
beta: false,
|
||||
},
|
||||
{
|
||||
id: 'badges',
|
||||
path: '/badges',
|
||||
name: 'Achievement Badges',
|
||||
icon: <BadgeIcon />,
|
||||
desc: 'Create simple badges to award your players',
|
||||
|
||||
show : true,
|
||||
beta : false
|
||||
},
|
||||
{
|
||||
id : 'tpk',
|
||||
path : 'http://tpk.naturalcrit.com',
|
||||
name : 'Total Player Knoller',
|
||||
icon : <TPKIcon />,
|
||||
desc : 'Effortless custom character sheets',
|
||||
show: true,
|
||||
beta: false,
|
||||
},
|
||||
{
|
||||
id: 'tpk',
|
||||
path: 'http://tpk.naturalcrit.com',
|
||||
name: 'Total Player Knoller',
|
||||
icon: <TPKIcon />,
|
||||
desc: 'Effortless custom character sheets',
|
||||
|
||||
show : false,
|
||||
beta : true
|
||||
},
|
||||
show: false,
|
||||
beta: true,
|
||||
},
|
||||
],
|
||||
};
|
||||
},
|
||||
|
||||
]
|
||||
};
|
||||
},
|
||||
renderTool: function (tool) {
|
||||
if (!tool.show) return null;
|
||||
|
||||
renderTool : function(tool){
|
||||
if(!tool.show) return null;
|
||||
|
||||
return <a href={tool.path} className={cx('tool', tool.id, {beta : tool.beta})} key={tool.id}>
|
||||
<div className='content'>
|
||||
return (
|
||||
<a href={tool.path} className={cx('tool', tool.id, { beta: tool.beta })} key={tool.id}>
|
||||
<div className="content">
|
||||
{tool.icon}
|
||||
<h2>{tool.name}</h2>
|
||||
<p>{tool.desc}</p>
|
||||
</div>
|
||||
</a>;
|
||||
},
|
||||
</a>
|
||||
);
|
||||
},
|
||||
|
||||
renderTools : function(){
|
||||
return _.map(this.props.tools, (tool)=>{
|
||||
return this.renderTool(tool);
|
||||
});
|
||||
},
|
||||
renderTools: function () {
|
||||
return _.map(this.props.tools, (tool) => {
|
||||
return this.renderTool(tool);
|
||||
});
|
||||
},
|
||||
|
||||
render : function(){
|
||||
return <div className='homePage'>
|
||||
<div className='top'>
|
||||
render: function () {
|
||||
return (
|
||||
<div className="homePage">
|
||||
<div className="top">
|
||||
<NaturalCritIcon />
|
||||
<p>Top-tier tools for the discerning DM</p>
|
||||
</div>
|
||||
<div className='tools'>
|
||||
{this.renderTools()}
|
||||
</div>
|
||||
<div className="tools">{this.renderTools()}</div>
|
||||
</div>
|
||||
}
|
||||
});
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
module.exports = HomePage;
|
||||
|
||||
@ -1,122 +1,175 @@
|
||||
.homePage {
|
||||
height: 100vh;
|
||||
background-color: white;
|
||||
|
||||
.homePage{
|
||||
height : 100vh;
|
||||
background-color : white;
|
||||
.top{
|
||||
.top {
|
||||
.fadeInTop(1s);
|
||||
.delay(0.5);
|
||||
margin-bottom : 100px;
|
||||
padding-top : 100px;
|
||||
text-align : center;
|
||||
margin-bottom: 100px;
|
||||
padding-top: 100px;
|
||||
text-align: center;
|
||||
|
||||
p{
|
||||
margin-top : 10px;
|
||||
font-size : 1.3em;
|
||||
font-style : italic;
|
||||
color : @grey;
|
||||
p {
|
||||
margin-top: 10px;
|
||||
font-size: 1.3em;
|
||||
font-style: italic;
|
||||
color: @grey;
|
||||
}
|
||||
}
|
||||
.tools{
|
||||
width : 100%;
|
||||
text-align : center;
|
||||
.tool{
|
||||
|
||||
.tools {
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
|
||||
.tool {
|
||||
.sequentialDelay(0.5s, 1s);
|
||||
.fadeInDown(1s);
|
||||
.keep();
|
||||
display : inline-block;
|
||||
vertical-align : top;
|
||||
cursor : pointer;
|
||||
opacity : 0;
|
||||
color : black;
|
||||
text-align : center;
|
||||
text-decoration : none;
|
||||
&+.tool{
|
||||
border-left : 1px solid #666;
|
||||
display: inline-block;
|
||||
vertical-align: top;
|
||||
cursor: pointer;
|
||||
opacity: 0;
|
||||
color: black;
|
||||
text-align: center;
|
||||
text-decoration: none;
|
||||
|
||||
&+.tool {
|
||||
border-left: 1px solid #666;
|
||||
}
|
||||
.badges svg{
|
||||
|
||||
.badges svg {
|
||||
width: 110px;
|
||||
transform: translateY(-8px);
|
||||
}
|
||||
.content{
|
||||
|
||||
.content {
|
||||
.addSketch(360px);
|
||||
.animateAll(0.5s);
|
||||
position : relative;
|
||||
width : 320px;
|
||||
padding : 35px;
|
||||
&:hover{
|
||||
svg, h2{
|
||||
position: relative;
|
||||
width: 320px;
|
||||
padding: 35px;
|
||||
|
||||
&:hover {
|
||||
|
||||
svg,
|
||||
h2 {
|
||||
.transform(scale(1.3));
|
||||
}
|
||||
}
|
||||
h2{
|
||||
|
||||
h2 {
|
||||
.animateAll(0.5s);
|
||||
font-family : 'CodeBold';
|
||||
font-size : 2em;
|
||||
font-family: 'CodeBold';
|
||||
font-size: 2em;
|
||||
}
|
||||
p{
|
||||
max-width : 300px;
|
||||
margin : 20px auto;
|
||||
line-height : 1.5em;
|
||||
|
||||
p {
|
||||
max-width: 300px;
|
||||
margin: 20px auto;
|
||||
line-height: 1.5em;
|
||||
}
|
||||
svg{
|
||||
|
||||
svg {
|
||||
.animateAll(0.5s);
|
||||
height : 9em;
|
||||
margin-bottom : 0.8em;
|
||||
height: 9em;
|
||||
margin-bottom: 0.8em;
|
||||
}
|
||||
}
|
||||
.content:hover{
|
||||
background-color : fade(@teal, 20%);
|
||||
|
||||
.content:hover {
|
||||
background-color: fade(@teal, 20%);
|
||||
}
|
||||
|
||||
//Beta styles
|
||||
&.beta{
|
||||
overflow : hidden;
|
||||
cursor : initial;
|
||||
.content{
|
||||
&:after{
|
||||
&.beta {
|
||||
overflow: hidden;
|
||||
cursor: initial;
|
||||
|
||||
.content {
|
||||
&:after {
|
||||
.animateAll();
|
||||
.transform(rotate(45deg));
|
||||
content : "beta!";
|
||||
position : absolute;
|
||||
display : block;
|
||||
top : -12px;
|
||||
right : -46px;
|
||||
width : 36%;
|
||||
padding-top : 27px;
|
||||
padding-bottom : 15px;
|
||||
content: "beta!";
|
||||
position: absolute;
|
||||
display: block;
|
||||
top: -12px;
|
||||
right: -46px;
|
||||
width: 36%;
|
||||
padding-top: 27px;
|
||||
padding-bottom: 15px;
|
||||
//opacity : 0;
|
||||
background-color : fade(@grey, 50%);
|
||||
font-size : 1em;
|
||||
font-weight : 800;
|
||||
text-align : center;
|
||||
text-transform : uppercase;
|
||||
background-color: fade(@grey, 50%);
|
||||
font-size: 1em;
|
||||
font-weight: 800;
|
||||
text-align: center;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.addSketch(@length, @color : black){
|
||||
path, line, polyline, circle, rect, polygon {
|
||||
|
||||
.addSketch(@length, @color : black) {
|
||||
|
||||
path,
|
||||
line,
|
||||
polyline,
|
||||
circle,
|
||||
rect,
|
||||
polygon {
|
||||
.sketch(@length, @color, 4s);
|
||||
stroke-dasharray : @length;
|
||||
stroke-dashoffset : 0px;
|
||||
stroke : @color;
|
||||
stroke-width : 0.5px;
|
||||
fill : @color;
|
||||
stroke-dasharray: @length;
|
||||
stroke-dashoffset: 0px;
|
||||
stroke: @color;
|
||||
stroke-width: 0.5px;
|
||||
fill: @color;
|
||||
//.animateAll(3s);
|
||||
}
|
||||
}
|
||||
.sketch(@length, @color : black, @duration : 3s, @easing : @defaultEasing){
|
||||
|
||||
.sketch(@length, @color : black, @duration : 3s, @easing : @defaultEasing) {
|
||||
.createAnimation(sketch, @duration, @easing);
|
||||
.sketchKeyFrames(){
|
||||
0% { stroke-dashoffset : @length; fill: transparent;}
|
||||
50% { stroke-dashoffset : @length; fill: transparent;}
|
||||
80% { stroke-dashoffset : 0px; fill: transparent;}
|
||||
100% { stroke-dashoffset : 0px; fill:@color;}
|
||||
|
||||
.sketchKeyFrames() {
|
||||
0% {
|
||||
stroke-dashoffset: @length;
|
||||
fill: transparent;
|
||||
}
|
||||
|
||||
50% {
|
||||
stroke-dashoffset: @length;
|
||||
fill: transparent;
|
||||
}
|
||||
|
||||
80% {
|
||||
stroke-dashoffset: 0px;
|
||||
fill: transparent;
|
||||
}
|
||||
|
||||
100% {
|
||||
stroke-dashoffset: 0px;
|
||||
fill: @color;
|
||||
}
|
||||
}
|
||||
@-webkit-keyframes sketch {.sketchKeyFrames();}
|
||||
@-moz-keyframes sketch {.sketchKeyFrames();}
|
||||
@-ms-keyframes sketch {.sketchKeyFrames();}
|
||||
@-o-keyframes sketch {.sketchKeyFrames();}
|
||||
@keyframes sketch {.sketchKeyFrames();}
|
||||
}
|
||||
|
||||
@-webkit-keyframes sketch {
|
||||
.sketchKeyFrames();
|
||||
}
|
||||
|
||||
@-moz-keyframes sketch {
|
||||
.sketchKeyFrames();
|
||||
}
|
||||
|
||||
@-ms-keyframes sketch {
|
||||
.sketchKeyFrames();
|
||||
}
|
||||
|
||||
@-o-keyframes sketch {
|
||||
.sketchKeyFrames();
|
||||
}
|
||||
|
||||
@keyframes sketch {
|
||||
.sketchKeyFrames();
|
||||
}
|
||||
}
|
||||
@ -1,187 +1,208 @@
|
||||
const React = require('react');
|
||||
const _ = require('lodash');
|
||||
const cx = require('classnames');
|
||||
const _ = require('lodash');
|
||||
const cx = require('classnames');
|
||||
|
||||
const NaturalCritIcon = require('naturalcrit/components/naturalcritLogo.jsx');
|
||||
const AccountActions = require('../account.actions.js');
|
||||
|
||||
const RedirectLocation = 'NC-REDIRECT-URL';
|
||||
|
||||
|
||||
const LoginPage = React.createClass({
|
||||
getDefaultProps: function() {
|
||||
getDefaultProps: function () {
|
||||
return {
|
||||
redirect : '',
|
||||
user : null
|
||||
redirect: '',
|
||||
user: null,
|
||||
};
|
||||
},
|
||||
getInitialState: function() {
|
||||
getInitialState: function () {
|
||||
return {
|
||||
view : 'login', //or 'signup'
|
||||
visible : false,
|
||||
view: 'login', //or 'signup'
|
||||
visible: false,
|
||||
|
||||
username : '',
|
||||
password : '',
|
||||
username: '',
|
||||
password: '',
|
||||
|
||||
processing : false,
|
||||
checkingUsername : false,
|
||||
redirecting : false,
|
||||
processing: false,
|
||||
checkingUsername: false,
|
||||
redirecting: false,
|
||||
|
||||
usernameExists : false,
|
||||
usernameExists: false,
|
||||
|
||||
errors : null,
|
||||
success : false,
|
||||
errors: null,
|
||||
success: false,
|
||||
};
|
||||
},
|
||||
componentDidMount: function() {
|
||||
window.document.onkeypress = (e)=>{
|
||||
if(e.code == 'Enter') this.handleClick();
|
||||
}
|
||||
componentDidMount: function () {
|
||||
window.document.onkeypress = (e) => {
|
||||
if (e.code == 'Enter') this.handleClick();
|
||||
};
|
||||
|
||||
this.handleRedirectURL();
|
||||
},
|
||||
|
||||
handleRedirectURL : function() {
|
||||
if(!this.props.redirect) {
|
||||
handleRedirectURL: function () {
|
||||
if (!this.props.redirect) {
|
||||
return window.sessionStorage.removeItem(RedirectLocation);
|
||||
};
|
||||
}
|
||||
return window.sessionStorage.setItem(RedirectLocation, this.props.redirect);
|
||||
},
|
||||
|
||||
handleUserChange : function(e){
|
||||
this.setState({username : e.target.value});
|
||||
if(this.props.user && this.props.user.username) return;
|
||||
this.setState({
|
||||
usernameExists : true,
|
||||
checkingUsername : true,
|
||||
}, ()=>{ if(this.state.view==='signup') this.checkUsername(); });
|
||||
handleUserChange: function (e) {
|
||||
this.setState({ username: e.target.value });
|
||||
if (this.props.user && this.props.user.username) return;
|
||||
this.setState(
|
||||
{
|
||||
usernameExists: true,
|
||||
checkingUsername: true,
|
||||
},
|
||||
() => {
|
||||
if (this.state.view === 'signup') this.checkUsername();
|
||||
}
|
||||
);
|
||||
},
|
||||
handlePassChange : function(e){
|
||||
this.setState({ password : e.target.value });
|
||||
handlePassChange: function (e) {
|
||||
this.setState({ password: e.target.value });
|
||||
},
|
||||
handleClick : function(){
|
||||
if(!this.isValid()) return;
|
||||
if(this.state.view === 'login') this.login();
|
||||
if(this.state.view === 'signup') this.signup();
|
||||
handleClick: function () {
|
||||
if (!this.isValid()) return;
|
||||
if (this.state.view === 'login') this.login();
|
||||
if (this.state.view === 'signup') this.signup();
|
||||
},
|
||||
|
||||
redirect : function(){
|
||||
if(!this.props.redirect) return window.location = '/';
|
||||
this.setState({
|
||||
redirecting : true
|
||||
}, () => {window.location = this.props.redirect;});
|
||||
redirect: function () {
|
||||
if (!this.props.redirect) return (window.location = '/');
|
||||
this.setState(
|
||||
{
|
||||
redirecting: true,
|
||||
},
|
||||
() => {
|
||||
window.location = this.props.redirect;
|
||||
}
|
||||
);
|
||||
},
|
||||
|
||||
login : function(){
|
||||
login: function () {
|
||||
this.setState({
|
||||
processing : true,
|
||||
errors : null
|
||||
processing: true,
|
||||
errors: null,
|
||||
});
|
||||
AccountActions.login(this.state.username, this.state.password)
|
||||
.then((token) => {
|
||||
this.setState({
|
||||
processing : false,
|
||||
errors : null,
|
||||
success : true
|
||||
}, this.redirect);
|
||||
this.setState(
|
||||
{
|
||||
processing: false,
|
||||
errors: null,
|
||||
success: true,
|
||||
},
|
||||
this.redirect
|
||||
);
|
||||
})
|
||||
.catch((err) => {
|
||||
console.log(err);
|
||||
this.setState({
|
||||
processing : false,
|
||||
errors : err
|
||||
processing: false,
|
||||
errors: err,
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
logout : function(e){
|
||||
|
||||
logout: function (e) {
|
||||
e.preventDefault();
|
||||
AccountActions.removeSession();
|
||||
window.location.reload();
|
||||
return false;
|
||||
},
|
||||
|
||||
signup : function(){
|
||||
signup: function () {
|
||||
this.setState({
|
||||
processing : true,
|
||||
errors : null
|
||||
processing: true,
|
||||
errors: null,
|
||||
});
|
||||
const regex = /^(?!.*@).{3,}$/;
|
||||
|
||||
if(!regex.test(this.state.username)) {
|
||||
if (!regex.test(this.state.username)) {
|
||||
this.setState({
|
||||
processing : false,
|
||||
errors : {username : 'Username must be at least 3 characters long.'}
|
||||
processing: false,
|
||||
errors: { username: 'Username must be at least 3 characters long.' },
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
AccountActions.signup(this.state.username, this.state.password)
|
||||
.then((token) => {
|
||||
this.setState({
|
||||
processing : false,
|
||||
errors : null,
|
||||
success : true
|
||||
}, this.redirect);
|
||||
this.setState(
|
||||
{
|
||||
processing: false,
|
||||
errors: null,
|
||||
success: true,
|
||||
},
|
||||
this.redirect
|
||||
);
|
||||
})
|
||||
.catch((err) => {
|
||||
console.log(err);
|
||||
this.setState({
|
||||
processing : false,
|
||||
errors : err
|
||||
processing: false,
|
||||
errors: err,
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
checkUsername : function(){
|
||||
if(this.state.username === '') return;
|
||||
checkUsername: function () {
|
||||
if (this.state.username === '') return;
|
||||
this.setState({
|
||||
checkingUsername : true
|
||||
checkingUsername: true,
|
||||
});
|
||||
this.debounceCheckUsername();
|
||||
},
|
||||
|
||||
debounceCheckUsername : _.debounce(function(){
|
||||
AccountActions.checkUsername(this.state.username)
|
||||
.then((doesExist) => {
|
||||
this.setState({
|
||||
usernameExists : !!doesExist,
|
||||
checkingUsername : false
|
||||
});
|
||||
})
|
||||
debounceCheckUsername: _.debounce(function () {
|
||||
AccountActions.checkUsername(this.state.username).then((doesExist) => {
|
||||
this.setState({
|
||||
usernameExists: !!doesExist,
|
||||
checkingUsername: false,
|
||||
});
|
||||
});
|
||||
}, 1000),
|
||||
|
||||
handleChangeView : function(newView){
|
||||
this.setState({
|
||||
view : newView,
|
||||
errors : null
|
||||
},this.checkUsername);
|
||||
handleChangeView: function (newView) {
|
||||
this.setState(
|
||||
{
|
||||
view: newView,
|
||||
errors: null,
|
||||
},
|
||||
this.checkUsername
|
||||
);
|
||||
},
|
||||
|
||||
isValid : function(){
|
||||
if(this.state.processing) return false;
|
||||
isValid: function () {
|
||||
if (this.state.processing) return false;
|
||||
|
||||
if(this.state.view === 'login'){
|
||||
if (this.state.view === 'login') {
|
||||
return this.state.username && this.state.password;
|
||||
}else if(this.state.view === 'signup'){
|
||||
} else if (this.state.view === 'signup') {
|
||||
return this.state.username && this.state.password && !this.state.usernameExists;
|
||||
}
|
||||
},
|
||||
|
||||
linkGoogle : function(){
|
||||
if(this.props.user) {
|
||||
if(!confirm(`You are currently logged in as ${this.props.user.username}. ` +
|
||||
`Do you want to link this user to a Google account? ` +
|
||||
`This will allow you to access the Homebrewery with your ` +
|
||||
`Google account and back up your files to Google Drive.`))
|
||||
linkGoogle: function () {
|
||||
if (this.props.user) {
|
||||
if (
|
||||
!confirm(
|
||||
`You are currently logged in as ${this.props.user.username}. ` +
|
||||
`Do you want to link this user to a Google account? ` +
|
||||
`This will allow you to access the Homebrewery with your ` +
|
||||
`Google account and back up your files to Google Drive.`
|
||||
)
|
||||
)
|
||||
return;
|
||||
}
|
||||
|
||||
this.setState({
|
||||
processing : true,
|
||||
errors : null
|
||||
processing: true,
|
||||
errors: null,
|
||||
});
|
||||
window.location.href='/auth/google';
|
||||
window.location.href = '/auth/google';
|
||||
},
|
||||
|
||||
// loginGoogle : function(){
|
||||
@ -193,7 +214,6 @@ const LoginPage = React.createClass({
|
||||
// AccountActions.loginGoogle();
|
||||
// },
|
||||
|
||||
|
||||
// console.log("about to start login");
|
||||
// AccountActions.login(this.state.username, this.state.password)
|
||||
// .then((token) => {
|
||||
@ -212,124 +232,150 @@ const LoginPage = React.createClass({
|
||||
// });
|
||||
// },
|
||||
|
||||
renderErrors : function(){
|
||||
if(!this.state.errors) return;
|
||||
if(this.state.errors.msg) return <div className='errors'>{this.state.errors.msg}</div>;
|
||||
return <div className='errors'>Something went wrong</div>;
|
||||
renderErrors: function () {
|
||||
if (!this.state.errors) return;
|
||||
if (this.state.errors.msg) return <div className="errors">{this.state.errors.msg}</div>;
|
||||
return <div className="errors">Something went wrong</div>;
|
||||
},
|
||||
|
||||
renderUsernameValidation : function(){
|
||||
if(this.state.view === 'login') return;
|
||||
renderUsernameValidation: function () {
|
||||
if (this.state.view === 'login') return;
|
||||
|
||||
let icon = null;
|
||||
|
||||
if(this.state.checkingUsername){
|
||||
icon = <i className='fa fa-spinner fa-spin' />;
|
||||
}else if(!this.state.username || this.state.usernameExists){
|
||||
icon = <i className='fa fa-times red' />;
|
||||
}else if(!this.state.usernameExists){
|
||||
icon = <i className='fa fa-check green' />
|
||||
if (this.state.checkingUsername) {
|
||||
icon = <i className="fa fa-spinner fa-spin" />;
|
||||
} else if (!this.state.username || this.state.usernameExists) {
|
||||
icon = <i className="fa fa-times red" />;
|
||||
} else if (!this.state.usernameExists) {
|
||||
icon = <i className="fa fa-check green" />;
|
||||
}
|
||||
|
||||
return <div className='control'>{icon}</div>
|
||||
return <div className="control">{icon}</div>;
|
||||
},
|
||||
|
||||
renderButton : function(){
|
||||
renderButton: function () {
|
||||
let className = '';
|
||||
let text = '';
|
||||
let icon = '';
|
||||
|
||||
if(this.state.processing){
|
||||
if (this.state.processing) {
|
||||
className = 'processing';
|
||||
text = 'processing';
|
||||
icon = 'fa-spinner fa-spin';
|
||||
}else if(this.state.view === 'login'){
|
||||
} else if (this.state.view === 'login') {
|
||||
className = 'login';
|
||||
text = 'login';
|
||||
icon = 'fa-sign-in';
|
||||
}else if(this.state.view === 'signup'){
|
||||
} else if (this.state.view === 'signup') {
|
||||
className = 'signup';
|
||||
text = 'signup';
|
||||
icon = 'fa-user-plus';
|
||||
}
|
||||
|
||||
return <button
|
||||
className={cx('action', className)}
|
||||
disabled={!this.isValid() || this.props.user && this.props.user.username}
|
||||
onClick={this.handleClick}>
|
||||
<i className={`fa ${icon}`} />
|
||||
{text}
|
||||
</button>
|
||||
return (
|
||||
<button
|
||||
className={cx('action', className)}
|
||||
disabled={!this.isValid() || (this.props.user && this.props.user.username)}
|
||||
onClick={this.handleClick}>
|
||||
<i className={`fa ${icon}`} />
|
||||
{text}
|
||||
</button>
|
||||
);
|
||||
},
|
||||
|
||||
renderLoggedIn : function(){
|
||||
if(!this.props.user) return;
|
||||
let loggedInGoogle = "";
|
||||
if (!this.props.user.googleId) {
|
||||
return <small>
|
||||
You are logged in as {this.props.user.username}. <a href='' onClick={this.logout}>logout.</a>
|
||||
</small>
|
||||
}
|
||||
else {
|
||||
return <small>
|
||||
You are logged in via Google as {this.props.user.username}. <a href='' onClick={this.logout}>logout.</a>
|
||||
</small>
|
||||
renderLoggedIn: function () {
|
||||
if (!this.props.user) return;
|
||||
let loggedInGoogle = '';
|
||||
if (!this.props.user.googleId) {
|
||||
return (
|
||||
<small>
|
||||
You are logged in as {this.props.user.username}.{' '}
|
||||
<a href="" onClick={this.logout}>
|
||||
logout.
|
||||
</a>
|
||||
</small>
|
||||
);
|
||||
} else {
|
||||
return (
|
||||
<small>
|
||||
You are logged in via Google as {this.props.user.username}.{' '}
|
||||
<a href="" onClick={this.logout}>
|
||||
logout.
|
||||
</a>
|
||||
</small>
|
||||
);
|
||||
}
|
||||
},
|
||||
|
||||
render : function(){
|
||||
return <div className='loginPage'>
|
||||
<NaturalCritIcon />
|
||||
|
||||
<div className='content'>
|
||||
<div className='switchView'>
|
||||
<div className={cx('login', {'selected' : this.state.view === 'login'})}
|
||||
onClick={this.handleChangeView.bind(null, 'login')}>
|
||||
<i className='fa fa-sign-in' /> Login
|
||||
render: function () {
|
||||
return (
|
||||
<div className="loginPage">
|
||||
<NaturalCritIcon />
|
||||
|
||||
<div className="content">
|
||||
<div className="switchView">
|
||||
<div
|
||||
className={cx('login', { 'selected': this.state.view === 'login' })}
|
||||
onClick={this.handleChangeView.bind(null, 'login')}>
|
||||
<i className="fa fa-sign-in" /> Login
|
||||
</div>
|
||||
|
||||
<div
|
||||
className={cx('signup', { 'selected': this.state.view === 'signup' })}
|
||||
onClick={this.handleChangeView.bind(null, 'signup')}>
|
||||
<i className="fa fa-user-plus" /> Signup
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className={cx('signup', {'selected' : this.state.view === 'signup'})}
|
||||
onClick={this.handleChangeView.bind(null, 'signup')}>
|
||||
<i className='fa fa-user-plus' /> Signup
|
||||
<div className="field user">
|
||||
<label>username</label>
|
||||
<input
|
||||
type="text"
|
||||
title={this.state.view === 'signup' ? 'Min 3 characters, and cannot contain ?!¿@ .' : ''}
|
||||
onChange={this.handleUserChange}
|
||||
value={this.state.username}
|
||||
/>
|
||||
{this.renderUsernameValidation()}
|
||||
{this.state.usernameExists && !this.state.checkingUsername && this.state.view === 'signup' ? (
|
||||
<div className="userExists">User with that name already exists</div>
|
||||
) : null}
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div className="field password">
|
||||
<label>password</label>
|
||||
<input
|
||||
type={cx({ text: this.state.visible, password: !this.state.visible })}
|
||||
onChange={this.handlePassChange}
|
||||
value={this.state.password}
|
||||
/>
|
||||
|
||||
|
||||
<div className='field user'>
|
||||
<label>username</label>
|
||||
<input
|
||||
type='text'
|
||||
title={this.state.view==='signup' ? 'Min 3 characters, and cannot contain ?!¿@ .': ''}
|
||||
onChange={this.handleUserChange}
|
||||
value={this.state.username} />
|
||||
{this.renderUsernameValidation()}
|
||||
{this.state.usernameExists && !this.state.checkingUsername && this.state.view==='signup' ? <div className='userExists'>User with that name already exists</div> : null}
|
||||
</div>
|
||||
|
||||
<div className='field password'>
|
||||
<label>password</label>
|
||||
<input
|
||||
type={cx({text : this.state.visible, password : !this.state.visible})}
|
||||
onChange={this.handlePassChange}
|
||||
value={this.state.password} />
|
||||
|
||||
<div className='control' onClick={()=>{this.setState({visible : !this.state.visible})}}>
|
||||
<i className={cx('fa', {'fa-eye' : !this.state.visible, 'fa-eye-slash' : this.state.visible})} />
|
||||
<div
|
||||
className="control"
|
||||
onClick={() => {
|
||||
this.setState({ visible: !this.state.visible });
|
||||
}}>
|
||||
<i
|
||||
className={cx('fa', {
|
||||
'fa-eye': !this.state.visible,
|
||||
'fa-eye-slash': this.state.visible,
|
||||
})}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
{this.renderErrors()}
|
||||
{this.renderButton()}
|
||||
<div className="divider">⎯⎯ OR ⎯⎯</div>
|
||||
<button className="google" onClick={this.linkGoogle}></button>
|
||||
</div>
|
||||
{this.renderErrors()}
|
||||
{this.renderButton()}
|
||||
<div className='divider'>⎯⎯ OR ⎯⎯</div>
|
||||
<button className='google' onClick={this.linkGoogle}></button>
|
||||
<br />
|
||||
<br />
|
||||
<br />
|
||||
<br />
|
||||
{this.renderLoggedIn()}
|
||||
</div>
|
||||
<br />
|
||||
<br />
|
||||
<br />
|
||||
<br />
|
||||
{this.renderLoggedIn()}
|
||||
</div>
|
||||
}
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
module.exports = LoginPage;
|
||||
|
||||
@ -1,134 +1,162 @@
|
||||
|
||||
.loginPage{
|
||||
text-align : center;
|
||||
.loginPage {
|
||||
text-align: center;
|
||||
padding-top: 30px;
|
||||
.content{
|
||||
|
||||
width : 400px;
|
||||
margin : 0 auto;
|
||||
padding : 20px;
|
||||
.content {
|
||||
|
||||
width: 400px;
|
||||
margin: 0 auto;
|
||||
padding: 20px;
|
||||
padding-top: 50px;
|
||||
.switchView{
|
||||
width : 100%;
|
||||
&>div{
|
||||
|
||||
.switchView {
|
||||
width: 100%;
|
||||
|
||||
&>div {
|
||||
.animate(background-color);
|
||||
display : inline-block;
|
||||
width : 50%;
|
||||
padding : 10px;
|
||||
cursor : pointer;
|
||||
background-color : transparent;
|
||||
font-size : 0.8em;
|
||||
font-weight : 800;
|
||||
text-transform : uppercase;
|
||||
i{
|
||||
vertical-align : middle;
|
||||
font-size : 2em;
|
||||
display: inline-block;
|
||||
width: 50%;
|
||||
padding: 10px;
|
||||
cursor: pointer;
|
||||
background-color: transparent;
|
||||
font-size: 0.8em;
|
||||
font-weight: 800;
|
||||
text-transform: uppercase;
|
||||
|
||||
i {
|
||||
vertical-align: middle;
|
||||
font-size: 2em;
|
||||
}
|
||||
}
|
||||
.login:hover, .login.selected{
|
||||
background-color : fade(@blue, 20%);
|
||||
|
||||
.login:hover,
|
||||
.login.selected {
|
||||
background-color: fade(@blue, 20%);
|
||||
}
|
||||
.signup:hover, .signup.selected{
|
||||
background-color : fade(@green, 20%);
|
||||
|
||||
.signup:hover,
|
||||
.signup.selected {
|
||||
background-color: fade(@green, 20%);
|
||||
}
|
||||
}
|
||||
.field{
|
||||
position : relative;
|
||||
margin : 20px 0px;
|
||||
text-align : left;
|
||||
label{
|
||||
display : block;
|
||||
width : 100%;
|
||||
font-size : 0.6em;
|
||||
font-weight : 800;
|
||||
line-height : 1.1em;
|
||||
text-transform : uppercase;
|
||||
|
||||
.field {
|
||||
position: relative;
|
||||
margin: 20px 0px;
|
||||
text-align: left;
|
||||
|
||||
label {
|
||||
display: block;
|
||||
width: 100%;
|
||||
font-size: 0.6em;
|
||||
font-weight: 800;
|
||||
line-height: 1.1em;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
input{
|
||||
width : 100%;
|
||||
padding : 10px;
|
||||
font-size : 1.2em;
|
||||
color : #333;
|
||||
|
||||
input {
|
||||
width: 100%;
|
||||
padding: 10px;
|
||||
font-size: 1.2em;
|
||||
color: #333;
|
||||
}
|
||||
.control{
|
||||
position : absolute;
|
||||
display : block;
|
||||
right : -40px;
|
||||
bottom : 0px;
|
||||
height : 46px;
|
||||
width : 40px;
|
||||
text-align : center;
|
||||
i{
|
||||
margin-top : 15px;
|
||||
|
||||
.control {
|
||||
position: absolute;
|
||||
display: block;
|
||||
right: -40px;
|
||||
bottom: 0px;
|
||||
height: 46px;
|
||||
width: 40px;
|
||||
text-align: center;
|
||||
|
||||
i {
|
||||
margin-top: 15px;
|
||||
}
|
||||
}
|
||||
&.password .control{
|
||||
cursor : pointer;
|
||||
|
||||
&.password .control {
|
||||
cursor: pointer;
|
||||
}
|
||||
.userExists{
|
||||
|
||||
.userExists {
|
||||
font-size: 0.6em;
|
||||
position: absolute;
|
||||
right : 0px;
|
||||
color : @red;
|
||||
right: 0px;
|
||||
color: @red;
|
||||
}
|
||||
}
|
||||
button.action{
|
||||
|
||||
button.action {
|
||||
.animate(opacity);
|
||||
padding : 10px 20px;
|
||||
cursor : pointer;
|
||||
opacity : 0.8;
|
||||
font-size : 0.8em;
|
||||
font-weight : 800;
|
||||
color : black;
|
||||
text-transform : uppercase;
|
||||
border : none;
|
||||
outline : none;
|
||||
&:hover{
|
||||
opacity : 1;
|
||||
padding: 10px 20px;
|
||||
cursor: pointer;
|
||||
opacity: 0.8;
|
||||
font-size: 0.8em;
|
||||
font-weight: 800;
|
||||
color: black;
|
||||
text-transform: uppercase;
|
||||
border: none;
|
||||
outline: none;
|
||||
|
||||
&:hover {
|
||||
opacity: 1;
|
||||
}
|
||||
&:disabled{
|
||||
cursor : not-allowed;
|
||||
opacity : 0.3;
|
||||
background-color : #ddd;
|
||||
|
||||
&:disabled {
|
||||
cursor: not-allowed;
|
||||
opacity: 0.3;
|
||||
background-color: #ddd;
|
||||
}
|
||||
&.login{ background-color: @blue; }
|
||||
&.signup{ background-color: @green; }
|
||||
i{
|
||||
margin-right : 10px;
|
||||
font-size : 1.5em;
|
||||
|
||||
&.login {
|
||||
background-color: @blue;
|
||||
}
|
||||
|
||||
&.signup {
|
||||
background-color: @green;
|
||||
}
|
||||
|
||||
i {
|
||||
margin-right: 10px;
|
||||
font-size: 1.5em;
|
||||
}
|
||||
}
|
||||
button.google{
|
||||
|
||||
button.google {
|
||||
cursor: pointer;
|
||||
width: 191px;
|
||||
height: 46px;
|
||||
border: none;
|
||||
outline: none;
|
||||
background-color:unset;
|
||||
background-color: unset;
|
||||
background-image: url('../assets/naturalcrit/styles/btn_google_signin_light_normal_web.png');
|
||||
background-size: contain;
|
||||
&:hover{
|
||||
background-image: url('../assets/naturalcrit/styles/btn_google_signin_light_hover_web.png');
|
||||
|
||||
&:hover {
|
||||
background-image: url('../assets/naturalcrit/styles/btn_google_signin_light_hover_web.png');
|
||||
}
|
||||
&:focus{
|
||||
background-image: url('../assets/naturalcrit/styles/btn_google_signin_light_pressed_web.png');
|
||||
|
||||
&:focus {
|
||||
background-image: url('../assets/naturalcrit/styles/btn_google_signin_light_pressed_web.png');
|
||||
}
|
||||
}
|
||||
.errors{
|
||||
margin-bottom : 20px;
|
||||
color : @red;
|
||||
|
||||
.errors {
|
||||
margin-bottom: 20px;
|
||||
color: @red;
|
||||
}
|
||||
}
|
||||
|
||||
.divider{
|
||||
.divider {
|
||||
font-size: 1em;
|
||||
font-weight: 800;
|
||||
color: black;
|
||||
text-transform: uppercase;
|
||||
padding: 12px 20px 10px;
|
||||
}
|
||||
+.accountButton.login {
|
||||
display:none;
|
||||
}
|
||||
}
|
||||
|
||||
+.accountButton.login {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
@ -6,69 +6,70 @@ var NaturalCritIcon = require('naturalcrit/svg/naturalcrit.svg.jsx');
|
||||
var HomebrewIcon = require('naturalcrit/svg/homebrew.svg.jsx');
|
||||
|
||||
var Main = React.createClass({
|
||||
getDefaultProps: function() {
|
||||
getDefaultProps: function () {
|
||||
return {
|
||||
tools : [
|
||||
tools: [
|
||||
{
|
||||
id : 'homebrew',
|
||||
path : 'https://homebrewery.naturalcrit.com',
|
||||
name : 'The Homebrewery',
|
||||
icon : <HomebrewIcon />,
|
||||
desc : 'Make authentic-looking 5e homebrews using Markdown',
|
||||
id: 'homebrew',
|
||||
path: 'https://homebrewery.naturalcrit.com',
|
||||
name: 'The Homebrewery',
|
||||
icon: <HomebrewIcon />,
|
||||
desc: 'Make authentic-looking 5e homebrews using Markdown',
|
||||
|
||||
show : true,
|
||||
beta : false
|
||||
show: true,
|
||||
beta: false,
|
||||
},
|
||||
{
|
||||
id : 'homebrew2',
|
||||
path : '/homebrew',
|
||||
name : 'The Homebrewery',
|
||||
icon : <HomebrewIcon />,
|
||||
desc : 'Make authentic-looking 5e homebrews using Markdown',
|
||||
id: 'homebrew2',
|
||||
path: '/homebrew',
|
||||
name: 'The Homebrewery',
|
||||
icon: <HomebrewIcon />,
|
||||
desc: 'Make authentic-looking 5e homebrews using Markdown',
|
||||
|
||||
show : false,
|
||||
beta : true
|
||||
show: false,
|
||||
beta: true,
|
||||
},
|
||||
|
||||
]
|
||||
],
|
||||
};
|
||||
},
|
||||
|
||||
renderTool : function(tool){
|
||||
if(!tool.show) return null;
|
||||
renderTool: function (tool) {
|
||||
if (!tool.show) return null;
|
||||
|
||||
return <a href={tool.path} className={cx('tool', tool.id, {beta : tool.beta})} key={tool.id}>
|
||||
<div className='content'>
|
||||
{tool.icon}
|
||||
<h2>{tool.name}</h2>
|
||||
<p>{tool.desc}</p>
|
||||
</div>
|
||||
</a>;
|
||||
return (
|
||||
<a href={tool.path} className={cx('tool', tool.id, { beta: tool.beta })} key={tool.id}>
|
||||
<div className="content">
|
||||
{tool.icon}
|
||||
<h2>{tool.name}</h2>
|
||||
<p>{tool.desc}</p>
|
||||
</div>
|
||||
</a>
|
||||
);
|
||||
},
|
||||
|
||||
renderTools : function(){
|
||||
return _.map(this.props.tools, (tool)=>{
|
||||
renderTools: function () {
|
||||
return _.map(this.props.tools, (tool) => {
|
||||
return this.renderTool(tool);
|
||||
});
|
||||
},
|
||||
|
||||
render : function(){
|
||||
return <div className='main'>
|
||||
<div className='top'>
|
||||
<div className='logo'>
|
||||
<NaturalCritIcon />
|
||||
<span className='name'>
|
||||
Natural
|
||||
<span className='crit'>Crit</span>
|
||||
</span>
|
||||
render: function () {
|
||||
return (
|
||||
<div className="main">
|
||||
<div className="top">
|
||||
<div className="logo">
|
||||
<NaturalCritIcon />
|
||||
<span className="name">
|
||||
Natural
|
||||
<span className="crit">Crit</span>
|
||||
</span>
|
||||
</div>
|
||||
<p>Top-tier tools for the discerning DM</p>
|
||||
</div>
|
||||
<p>Top-tier tools for the discerning DM</p>
|
||||
<div className="tools">{this.renderTools()}</div>
|
||||
</div>
|
||||
<div className='tools'>
|
||||
{this.renderTools()}
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
module.exports = Main;
|
||||
|
||||
@ -1,136 +1,200 @@
|
||||
@import 'naturalcrit/styles/core.less';
|
||||
.main{
|
||||
height : 100vh;
|
||||
background-color : white;
|
||||
.top{
|
||||
|
||||
.main {
|
||||
height: 100vh;
|
||||
background-color: white;
|
||||
|
||||
.top {
|
||||
.fadeInTop(1s);
|
||||
.delay(0.5);
|
||||
margin-bottom : 100px;
|
||||
padding-top : 100px;
|
||||
text-align : center;
|
||||
.logo{
|
||||
font-size : 4em;
|
||||
color : black;
|
||||
svg{
|
||||
height : .9em;
|
||||
margin-right : .2em;
|
||||
cursor : pointer;
|
||||
fill : black;
|
||||
margin-bottom: 100px;
|
||||
padding-top: 100px;
|
||||
text-align: center;
|
||||
|
||||
.logo {
|
||||
font-size: 4em;
|
||||
color: black;
|
||||
|
||||
svg {
|
||||
height: .9em;
|
||||
margin-right: .2em;
|
||||
cursor: pointer;
|
||||
fill: black;
|
||||
}
|
||||
.name{
|
||||
font-family : 'CodeLight';
|
||||
.crit{
|
||||
font-family : 'CodeBold';
|
||||
|
||||
.name {
|
||||
font-family: 'CodeLight';
|
||||
|
||||
.crit {
|
||||
font-family: 'CodeBold';
|
||||
}
|
||||
}
|
||||
}
|
||||
p{
|
||||
margin-top : 10px;
|
||||
font-size : 1.3em;
|
||||
font-style : italic;
|
||||
color : @grey;
|
||||
|
||||
p {
|
||||
margin-top: 10px;
|
||||
font-size: 1.3em;
|
||||
font-style: italic;
|
||||
color: @grey;
|
||||
}
|
||||
}
|
||||
.tools{
|
||||
width : 100%;
|
||||
text-align : center;
|
||||
.tool{
|
||||
|
||||
.tools {
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
|
||||
.tool {
|
||||
.sequentialDelay(0.5s, 1s);
|
||||
.fadeInDown(1s);
|
||||
.keep();
|
||||
display : inline-block;
|
||||
cursor : pointer;
|
||||
opacity : 0;
|
||||
color : black;
|
||||
text-align : center;
|
||||
text-decoration : none;
|
||||
&+.tool{
|
||||
border-left : 1px solid #666;
|
||||
display: inline-block;
|
||||
cursor: pointer;
|
||||
opacity: 0;
|
||||
color: black;
|
||||
text-align: center;
|
||||
text-decoration: none;
|
||||
|
||||
&+.tool {
|
||||
border-left: 1px solid #666;
|
||||
}
|
||||
.content{
|
||||
|
||||
.content {
|
||||
.addSketch(360px);
|
||||
.animateAll(0.5s);
|
||||
position : relative;
|
||||
width : 320px;
|
||||
padding : 35px;
|
||||
&:hover{
|
||||
svg, h2{
|
||||
position: relative;
|
||||
width: 320px;
|
||||
padding: 35px;
|
||||
|
||||
&:hover {
|
||||
|
||||
svg,
|
||||
h2 {
|
||||
.transform(scale(1.3));
|
||||
}
|
||||
}
|
||||
h2{
|
||||
|
||||
h2 {
|
||||
.animateAll(0.5s);
|
||||
font-family : 'CodeBold';
|
||||
font-size : 2em;
|
||||
font-family: 'CodeBold';
|
||||
font-size: 2em;
|
||||
}
|
||||
p{
|
||||
max-width : 300px;
|
||||
margin : 20px auto;
|
||||
line-height : 1.5em;
|
||||
|
||||
p {
|
||||
max-width: 300px;
|
||||
margin: 20px auto;
|
||||
line-height: 1.5em;
|
||||
}
|
||||
svg{
|
||||
|
||||
svg {
|
||||
.animateAll(0.5s);
|
||||
height : 10em;
|
||||
height: 10em;
|
||||
}
|
||||
}
|
||||
.content:hover{
|
||||
background-color : fade(@teal, 20%);
|
||||
|
||||
.content:hover {
|
||||
background-color: fade(@teal, 20%);
|
||||
}
|
||||
|
||||
//Beta styles
|
||||
&.beta{
|
||||
cursor : initial;
|
||||
.content{
|
||||
&:hover{
|
||||
svg, h2{
|
||||
&.beta {
|
||||
cursor: initial;
|
||||
|
||||
.content {
|
||||
&:hover {
|
||||
|
||||
svg,
|
||||
h2 {
|
||||
.transform(scale(1.0));
|
||||
}
|
||||
}
|
||||
svg, h2{
|
||||
opacity : 0.3;
|
||||
|
||||
svg,
|
||||
h2 {
|
||||
opacity: 0.3;
|
||||
}
|
||||
&:after{
|
||||
|
||||
&:after {
|
||||
.animateAll();
|
||||
content : "beta!";
|
||||
position : absolute;
|
||||
display : block;
|
||||
top : 120px;
|
||||
left : 0px;
|
||||
width : 100%;
|
||||
padding : 10px 0px;
|
||||
content: "beta!";
|
||||
position: absolute;
|
||||
display: block;
|
||||
top: 120px;
|
||||
left: 0px;
|
||||
width: 100%;
|
||||
padding: 10px 0px;
|
||||
//opacity : 0;
|
||||
background-color : fade(@grey, 50%);
|
||||
font-size : 2em;
|
||||
font-weight : 800;
|
||||
text-align : center;
|
||||
text-transform : uppercase;
|
||||
background-color: fade(@grey, 50%);
|
||||
font-size: 2em;
|
||||
font-weight: 800;
|
||||
text-align: center;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.addSketch(@length, @color : black){
|
||||
path, line, polyline, circle, rect, polygon {
|
||||
|
||||
.addSketch(@length, @color : black) {
|
||||
|
||||
path,
|
||||
line,
|
||||
polyline,
|
||||
circle,
|
||||
rect,
|
||||
polygon {
|
||||
.sketch(@length, @color, 4s);
|
||||
stroke-dasharray : @length;
|
||||
stroke-dashoffset : 0px;
|
||||
stroke : @color;
|
||||
stroke-width : 0.5px;
|
||||
fill : @color;
|
||||
stroke-dasharray: @length;
|
||||
stroke-dashoffset: 0px;
|
||||
stroke: @color;
|
||||
stroke-width: 0.5px;
|
||||
fill: @color;
|
||||
//.animateAll(3s);
|
||||
}
|
||||
}
|
||||
.sketch(@length, @color : black, @duration : 3s, @easing : @defaultEasing){
|
||||
|
||||
.sketch(@length, @color : black, @duration : 3s, @easing : @defaultEasing) {
|
||||
.createAnimation(sketch, @duration, @easing);
|
||||
.sketchKeyFrames(){
|
||||
0% { stroke-dashoffset : @length; fill: transparent;}
|
||||
50% { stroke-dashoffset : @length; fill: transparent;}
|
||||
80% { stroke-dashoffset : 0px; fill: transparent;}
|
||||
100% { stroke-dashoffset : 0px; fill:@color;}
|
||||
|
||||
.sketchKeyFrames() {
|
||||
0% {
|
||||
stroke-dashoffset: @length;
|
||||
fill: transparent;
|
||||
}
|
||||
|
||||
50% {
|
||||
stroke-dashoffset: @length;
|
||||
fill: transparent;
|
||||
}
|
||||
|
||||
80% {
|
||||
stroke-dashoffset: 0px;
|
||||
fill: transparent;
|
||||
}
|
||||
|
||||
100% {
|
||||
stroke-dashoffset: 0px;
|
||||
fill: @color;
|
||||
}
|
||||
}
|
||||
|
||||
@-webkit-keyframes sketch {
|
||||
.sketchKeyFrames();
|
||||
}
|
||||
|
||||
@-moz-keyframes sketch {
|
||||
.sketchKeyFrames();
|
||||
}
|
||||
|
||||
@-ms-keyframes sketch {
|
||||
.sketchKeyFrames();
|
||||
}
|
||||
|
||||
@-o-keyframes sketch {
|
||||
.sketchKeyFrames();
|
||||
}
|
||||
|
||||
@keyframes sketch {
|
||||
.sketchKeyFrames();
|
||||
}
|
||||
@-webkit-keyframes sketch {.sketchKeyFrames();}
|
||||
@-moz-keyframes sketch {.sketchKeyFrames();}
|
||||
@-ms-keyframes sketch {.sketchKeyFrames();}
|
||||
@-o-keyframes sketch {.sketchKeyFrames();}
|
||||
@keyframes sketch {.sketchKeyFrames();}
|
||||
}
|
||||
@ -11,62 +11,55 @@ const GoogleRedirect = require('./googleRedirect/googleRedirect.jsx');
|
||||
|
||||
let Router;
|
||||
const Naturalcrit = React.createClass({
|
||||
getDefaultProps: function() {
|
||||
getDefaultProps: function () {
|
||||
return {
|
||||
user : null,
|
||||
url : '',
|
||||
domain : '',
|
||||
authToken : ''
|
||||
user: null,
|
||||
url: '',
|
||||
domain: '',
|
||||
authToken: '',
|
||||
};
|
||||
},
|
||||
|
||||
componentWillMount: function() {
|
||||
componentWillMount: function () {
|
||||
global.domain = this.props.domain;
|
||||
|
||||
Router = CreateRouter({
|
||||
'/account' : (args, query) => {
|
||||
return <AccountPage
|
||||
user={this.props.user} />
|
||||
'/account': (args, query) => {
|
||||
return <AccountPage user={this.props.user} />;
|
||||
},
|
||||
'/login' : (args, query) => {
|
||||
return <LoginPage
|
||||
redirect={query.redirect}
|
||||
user={this.props.user} />
|
||||
'/login': (args, query) => {
|
||||
return <LoginPage redirect={query.redirect} user={this.props.user} />;
|
||||
},
|
||||
'/success' : (args, query) => {
|
||||
return <SuccessPage
|
||||
user={this.props.user} />
|
||||
'/success': (args, query) => {
|
||||
return <SuccessPage user={this.props.user} />;
|
||||
},
|
||||
'/auth/google/redirect' : (args, query) => {
|
||||
return <GoogleRedirect
|
||||
user={this.props.user} />
|
||||
'/auth/google/redirect': (args, query) => {
|
||||
return <GoogleRedirect user={this.props.user} />;
|
||||
},
|
||||
'*': () => {
|
||||
return <HomePage configTools={this.props.tools} user={this.props.user} />;
|
||||
},
|
||||
'*' : () => {
|
||||
return <HomePage
|
||||
configTools={this.props.tools}
|
||||
user={this.props.user} />
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
renderAccount : function(){
|
||||
renderAccount: function () {
|
||||
let accountLink = '';
|
||||
if(this.props.user && this.props.user.username) {
|
||||
accountLink=<a href='/account'>{this.props.user.username}</a>
|
||||
if (this.props.user && this.props.user.username) {
|
||||
accountLink = <a href="/account">{this.props.user.username}</a>;
|
||||
} else {
|
||||
accountLink=<a href='/login'>Log in</a>
|
||||
};
|
||||
accountLink = <a href="/login">Log in</a>;
|
||||
}
|
||||
return accountLink;
|
||||
},
|
||||
|
||||
render : function(){
|
||||
return <div className='naturalcrit'>
|
||||
<Router initialUrl={this.props.url}/>
|
||||
<div className={`accountButton ${this.props.user ? '': 'login'}`}>
|
||||
{this.renderAccount()}
|
||||
render: function () {
|
||||
return (
|
||||
<div className="naturalcrit">
|
||||
<Router initialUrl={this.props.url} />
|
||||
<div className={`accountButton ${this.props.user ? '' : 'login'}`}>{this.renderAccount()}</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
module.exports = Naturalcrit;
|
||||
|
||||
@ -6,7 +6,7 @@
|
||||
}
|
||||
|
||||
a {
|
||||
cursor:pointer;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
b {
|
||||
@ -14,8 +14,8 @@ b {
|
||||
}
|
||||
|
||||
small {
|
||||
font-size : 12px;
|
||||
font-style : italic;
|
||||
font-size: 12px;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.logo {
|
||||
@ -47,7 +47,7 @@ small {
|
||||
}
|
||||
|
||||
button {
|
||||
border:none;
|
||||
border: none;
|
||||
font-family: 'CodeBold';
|
||||
letter-spacing: 1px;
|
||||
padding: 12px 20px;
|
||||
|
||||
@ -1,14 +1,9 @@
|
||||
var React = require('react');
|
||||
var _ = require('lodash');
|
||||
var cx = require('classnames');
|
||||
|
||||
var SignupPage = React.createClass({
|
||||
|
||||
render : function(){
|
||||
return <div className='signupPage'>
|
||||
SignupPage Component Ready.
|
||||
</div>
|
||||
}
|
||||
render: function () {
|
||||
return <div className="signupPage">SignupPage Component Ready.</div>;
|
||||
},
|
||||
});
|
||||
|
||||
module.exports = SignupPage;
|
||||
|
||||
@ -1,3 +0,0 @@
|
||||
.signupPage{
|
||||
|
||||
}
|
||||
@ -5,48 +5,51 @@ const NaturalCritIcon = require('naturalcrit/components/naturalcritLogo.jsx');
|
||||
const RedirectLocation = 'NC-REDIRECT-URL';
|
||||
|
||||
const SuccessPage = React.createClass({
|
||||
getDefaultProps: function() {
|
||||
getDefaultProps: function () {
|
||||
return {
|
||||
redirect : '',
|
||||
user : null
|
||||
redirect: '',
|
||||
user: null,
|
||||
};
|
||||
},
|
||||
getInitialState: function() {
|
||||
getInitialState: function () {
|
||||
return {
|
||||
view : 'login', //or 'signup'
|
||||
visible : false,
|
||||
view: 'login', //or 'signup'
|
||||
visible: false,
|
||||
|
||||
username : '',
|
||||
password : '',
|
||||
username: '',
|
||||
password: '',
|
||||
|
||||
processing : false,
|
||||
checkingUsername : false,
|
||||
redirecting : false,
|
||||
processing: false,
|
||||
checkingUsername: false,
|
||||
redirecting: false,
|
||||
|
||||
usernameExists : false,
|
||||
usernameExists: false,
|
||||
|
||||
errors : null,
|
||||
success : false,
|
||||
errors: null,
|
||||
success: false,
|
||||
};
|
||||
},
|
||||
componentDidMount: function() {
|
||||
componentDidMount: function () {
|
||||
const redirectURL = window.sessionStorage.getItem(RedirectLocation) || '/';
|
||||
window.sessionStorage.removeItem(RedirectLocation);
|
||||
setTimeout(function(){window.location=redirectURL;}, 1500);
|
||||
},
|
||||
render : function(){
|
||||
return <div className='loginPage'>
|
||||
<NaturalCritIcon />
|
||||
setTimeout(function () {
|
||||
window.location = redirectURL;
|
||||
}, 1500);
|
||||
},
|
||||
render: function () {
|
||||
return (
|
||||
<div className="loginPage">
|
||||
<NaturalCritIcon />
|
||||
|
||||
<div className='content'>
|
||||
<p>Successfully logged in!</p>
|
||||
<br />
|
||||
<br />
|
||||
<p>Redirecting...</p>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
}
|
||||
<div className="content">
|
||||
<p>Successfully logged in!</p>
|
||||
<br />
|
||||
<br />
|
||||
<p>Redirecting...</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
module.exports = SuccessPage;
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
module.exports = function(vitreum){
|
||||
module.exports = function (vitreum) {
|
||||
return `
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
@ -21,7 +21,4 @@ module.exports = function(vitreum){
|
||||
${vitreum.js}
|
||||
</html>
|
||||
`;
|
||||
}
|
||||
|
||||
|
||||
|
||||
};
|
||||
|
||||
@ -5,16 +5,20 @@
|
||||
@import 'naturalcrit/styles/tooltip.less';
|
||||
|
||||
@font-face {
|
||||
font-family : CodeLight;
|
||||
src : url('/assets/naturalcrit/styles/CODE Light.otf');
|
||||
font-family: CodeLight;
|
||||
src: url('/assets/naturalcrit/styles/CODE Light.otf');
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family : CodeBold;
|
||||
src : url('/assets/naturalcrit/styles/CODE Bold.otf');
|
||||
font-family: CodeBold;
|
||||
src: url('/assets/naturalcrit/styles/CODE Bold.otf');
|
||||
}
|
||||
html,body, #reactContainer{
|
||||
|
||||
html,
|
||||
body,
|
||||
#reactContainer {
|
||||
min-height: 100vh;
|
||||
height: 100vh;
|
||||
margin: 0;
|
||||
font-family : 'Open Sans', sans-serif;
|
||||
font-family: 'Open Sans', sans-serif;
|
||||
}
|
||||
@ -1,86 +1,100 @@
|
||||
|
||||
@containerWidth : 1000px;
|
||||
|
||||
html, body{
|
||||
position : relative;
|
||||
height : 100%;
|
||||
min-height : 100%;
|
||||
background-color : #eee;
|
||||
font-family : 'Lato', sans-serif;
|
||||
color : @copyGrey;
|
||||
html,
|
||||
body {
|
||||
position: relative;
|
||||
height: 100%;
|
||||
min-height: 100%;
|
||||
background-color: #eee;
|
||||
font-family: 'Lato', sans-serif;
|
||||
color: @copyGrey;
|
||||
}
|
||||
.container{
|
||||
position : relative;
|
||||
max-width : @containerWidth;
|
||||
margin : 0 auto;
|
||||
padding-right : 20px;
|
||||
padding-left : 20px;
|
||||
|
||||
.container {
|
||||
position: relative;
|
||||
max-width: @containerWidth;
|
||||
margin: 0 auto;
|
||||
padding-right: 20px;
|
||||
padding-left: 20px;
|
||||
}
|
||||
h1{
|
||||
margin-top : 10px;
|
||||
margin-bottom : 15px;
|
||||
font-size : 2em;
|
||||
|
||||
h1 {
|
||||
margin-top: 10px;
|
||||
margin-bottom: 15px;
|
||||
font-size: 2em;
|
||||
}
|
||||
h2{
|
||||
margin-top : 10px;
|
||||
margin-bottom : 15px;
|
||||
font-size : 1.5em;
|
||||
font-weight : 900;
|
||||
|
||||
h2 {
|
||||
margin-top: 10px;
|
||||
margin-bottom: 15px;
|
||||
font-size: 1.5em;
|
||||
font-weight: 900;
|
||||
}
|
||||
h3{
|
||||
margin-top : 5px;
|
||||
margin-bottom : 7px;
|
||||
font-size : 1em;
|
||||
font-weight : 900;
|
||||
|
||||
h3 {
|
||||
margin-top: 5px;
|
||||
margin-bottom: 7px;
|
||||
font-size: 1em;
|
||||
font-weight: 900;
|
||||
}
|
||||
p{
|
||||
margin-bottom : 1em;
|
||||
font-size : 16px;
|
||||
color : @copyGrey;
|
||||
line-height : 1.5em;
|
||||
|
||||
p {
|
||||
margin-bottom: 1em;
|
||||
font-size: 16px;
|
||||
color: @copyGrey;
|
||||
line-height: 1.5em;
|
||||
}
|
||||
code{
|
||||
background-color : #F8F8F8;
|
||||
font-family : 'Courier', mono;
|
||||
color : black;
|
||||
white-space : pre;
|
||||
|
||||
code {
|
||||
background-color: #F8F8F8;
|
||||
font-family: 'Courier', mono;
|
||||
color: black;
|
||||
white-space: pre;
|
||||
}
|
||||
a{
|
||||
color : inherit;
|
||||
|
||||
a {
|
||||
color: inherit;
|
||||
}
|
||||
strong{
|
||||
font-weight : bold;
|
||||
|
||||
strong {
|
||||
font-weight: bold;
|
||||
}
|
||||
button{
|
||||
|
||||
button {
|
||||
.button();
|
||||
}
|
||||
.button(@backgroundColor : @green){
|
||||
|
||||
.button(@backgroundColor : @green) {
|
||||
.animate(background-color);
|
||||
display : inline-block;
|
||||
padding : 0.6em 1.2em;
|
||||
cursor : pointer;
|
||||
background-color : @backgroundColor;
|
||||
font-family : "Lato", Helvetica, Arial, sans-serif;
|
||||
font-size : 15px;
|
||||
color : white;
|
||||
text-decoration : none;
|
||||
border : none;
|
||||
outline : none;
|
||||
&:hover{
|
||||
background-color : darken(@backgroundColor, 5%);
|
||||
display: inline-block;
|
||||
padding: 0.6em 1.2em;
|
||||
cursor: pointer;
|
||||
background-color: @backgroundColor;
|
||||
font-family: "Lato", Helvetica, Arial, sans-serif;
|
||||
font-size: 15px;
|
||||
color: white;
|
||||
text-decoration: none;
|
||||
border: none;
|
||||
outline: none;
|
||||
|
||||
&:hover {
|
||||
background-color: darken(@backgroundColor, 5%);
|
||||
}
|
||||
&:active{
|
||||
background-color : darken(@backgroundColor, 10%);
|
||||
|
||||
&:active {
|
||||
background-color: darken(@backgroundColor, 10%);
|
||||
}
|
||||
&:disabled{
|
||||
background-color : @silver !important;
|
||||
|
||||
&:disabled {
|
||||
background-color: @silver !important;
|
||||
}
|
||||
}
|
||||
.iconButton(@backgroundColor : @green){
|
||||
padding : 0.6em;
|
||||
cursor : pointer;
|
||||
background-color : @backgroundColor;
|
||||
font-size : 14px;
|
||||
color : white;
|
||||
text-align : center;
|
||||
|
||||
.iconButton(@backgroundColor : @green) {
|
||||
padding: 0.6em;
|
||||
cursor: pointer;
|
||||
background-color: @backgroundColor;
|
||||
font-size: 14px;
|
||||
color: white;
|
||||
text-align: center;
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user