diff --git a/src/renderer/components/Application.css b/src/renderer/components/Application.css index e69de29..737009e 100644 --- a/src/renderer/components/Application.css +++ b/src/renderer/components/Application.css @@ -0,0 +1,4 @@ +.appWrapper { + height: calc(100vh - 22px); + background: linear-gradient(to top, rgba(0, 0, 0, 0.75) 0%, rgba(0, 0, 0, 0) 100%); +} \ No newline at end of file diff --git a/src/renderer/components/Application.tsx b/src/renderer/components/Application.tsx index 517de9c..8bbf092 100644 --- a/src/renderer/components/Application.tsx +++ b/src/renderer/components/Application.tsx @@ -8,37 +8,45 @@ import Landing from './landing/Landing'; import Login from './login/Login'; import Settings from './settings/Settings'; +import './Application.css' + type ApplicationProps = { currentView: View } class Application extends React.Component { - render() { + getViewElement(): JSX.Element { switch(this.props.currentView) { case View.WELCOME: return <> - case View.LANDING: return <> - case View.LOGIN: return <> - - + case View.SETTINGS: return <> - } - + } + + render() { + return ( + <> + +
+ {this.getViewElement()} +
+ + ) } } diff --git a/src/renderer/components/frame/Frame.css b/src/renderer/components/frame/Frame.css index 62563e5..f041949 100644 --- a/src/renderer/components/frame/Frame.css +++ b/src/renderer/components/frame/Frame.css @@ -5,7 +5,7 @@ display: flex; flex-direction: column; transition: background-color 1s ease; - /*background-color: rgba(0, 0, 0, 0.5);*/ + background-color: rgba(0, 0, 0, 0.5); -webkit-user-select: none; } @@ -48,6 +48,7 @@ /* Frame logo (windows only). */ #frameTitleDock { padding: 0px 10px; + display: flex; } #frameTitleText { font-size: 14px; diff --git a/src/renderer/components/login/Login.css b/src/renderer/components/login/Login.css new file mode 100644 index 0000000..bbe80ef --- /dev/null +++ b/src/renderer/components/login/Login.css @@ -0,0 +1,423 @@ +/******************************************************************************* + * * + * Login View (login.ejs) * + * * + ******************************************************************************/ + +/* Styles for dimmer login span. */ +.loginSpanDim { + font-size: 12px; + color: #848484; + font-weight: bold; +} + +/* Main login container. */ +#loginContainer { + position: relative; + display: flex; + justify-content: center; + align-items: center; + height: 100%; + width: 100%; + transition: filter 0.25s ease; + background: rgba(0, 0, 0, 0.50); +} + +/* Login cancel button styles. */ +#loginCancelContainer { + position: absolute; + top: 5%; + right: 5%; +} + +/* Login cancel button styles. */ +#loginCancelButton { + background: none; + border: none; + outline: none; + cursor: pointer; + transition: 0.25s ease; +} +#loginCancelButton:hover #loginCancelIcon, +#loginCancelButton:hover #loginCancelText, +#loginCancelButton:focus #loginCancelIcon, +#loginCancelButton:focus #loginCancelText { + text-shadow: 0px 0px 20px white; +} +#loginCancelButton:hover #loginCancelIcon, +#loginCancelButton:focus #loginCancelIcon { + box-shadow: 0px 0px 20px white; +} +#loginCancelButton:active #loginCancelIcon, +#loginCancelButton:active #loginCancelText { + text-shadow: 0px 0px 20px rgba(255, 255, 255, 0.75); + color: rgba(255, 255, 255, 0.75); + border-color: rgba(255, 255, 255, 0.75); +} +#loginCancelButton:active #loginCancelIcon { + box-shadow: 0px 0px 20px rgba(255, 255, 255, 0.75); +} +#loginCancelButton:disabled { + pointer-events: none; +} +#loginCancelButton:disabled #loginCancelIcon, +#loginCancelButton:disabled #loginCancelText { + color: rgba(255, 255, 255, 0.75); + border-color: rgba(255, 255, 255, 0.75); +} + +/* The X in a circle icon for the cancel button. */ +#loginCancelIcon { + border-radius: 50%; + border: 1px solid white; + box-sizing: border-box; + height: 30px; + width: 30px; + font-size: 19px; + line-height: 30px; + margin: 0 auto; + margin-bottom: 5px; + transition: 0.25s ease; +} +/* Text for the login cancel button. */ +#loginCancelText { + font-size: 15px; + transition: 0.25s ease; +} + +/* Login content wrapper. */ +#loginContent { + display: flex; + justify-content: center; + align-items: center; + height: 100%; + padding: 0px 25px; +} + +/* Login form. */ +#loginForm { + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; +} + +/* Login form anchor styles. */ +#loginForm a { + font-size: 12px; + color: #848484; + font-weight: bold; + text-decoration: none; + transition: 0.25s ease; +} +#loginForm a:hover, +#loginForm a:focus { + color: #a2a2a2; + outline: none; +} +#loginForm a:active { + color: #8b8b8b; +} + +/* Logo on login form. */ +#loginImageSeal { + border-radius: 50%; + border: 2px solid #cad7e1; + background: rgba(1, 2, 1, 0.5); + height: 125px; + width: 125px; + box-shadow: 0px 0px 10px 0px rgb(0, 0, 0); + margin-bottom: 20px; +} + +/* Header on login view. */ +#loginSubheader { + font-family: 'Avenir Medium'; + margin-bottom: 25px; + font-size: 12px; + letter-spacing: 1px; + font-weight: bold; +} + +/* Add spacing between password field and options bar. */ +#labelPassword { + margin-bottom: 13px; +} + +/* Container which contains the forgot and remember options. */ +#loginOptions { + display: flex; + justify-content: space-between; + width: 100%; +} + +/* Remember option text. */ +#loginRememberText { + padding-right: 10px; + transition: 0.25s ease; +} + +/* Login button styles. */ +#loginButton { + background: none; + font-weight: bold; + letter-spacing: 2px; + border: none; + padding: 15px 5px; + margin: 10px 0px; + cursor: pointer; + position: relative; + right: -20px; + transition: 0.5s ease; +} +#loginButton:disabled { + color: rgba(255, 255, 255, 0.75); + pointer-events: none; +} +#loginButton[loading] { + color: #fff; +} +#loginButton:hover, +#loginButton:focus { + text-shadow: 0px 0px 20px #fff; + outline: none; +} +#loginButton:active { + color: #c7c7c7; + text-shadow: 0px 0px 20px #c7c7c7; +} +#loginSVG { + -webkit-transform: translate3d(0, 0, 0); + overflow: visible; + transform: rotate(90deg); + margin-left: 20px; + transition: 0.25s ease; + width: 20px; + height: 20px; +} +#loginButton:hover #loginSVG, +#loginButton:focus #loginSVG { + -webkit-filter: drop-shadow(0px 0px 2px #fff); +} +#loginButton:active #loginSVG .arrowLine { + stroke: #c7c7c7; +} +#loginButton:active #loginSVG { + -webkit-filter: drop-shadow(0px 0px 2px #c7c7c7); +} +#loginButton:disabled #loginSVG .arrowLine { + stroke: rgba(255, 255, 255, 0.75); +} + +#loginButtonContent { + display: flex; + align-items: center; +} + +#loginButton .circle-loader, +#loginButton[loading] #loginSVG { + display: none; +} +#loginButton[loading] .circle-loader, +#loginButton #loginSVG { + display: initial; +} + + +.circle-loader { + margin-left: 20px; + border: 2px solid rgba(255, 255, 255, 0.5); + border-left-color: #ffffff; + animation-name: loader-spin; + animation-duration: 1s; + animation-iteration-count: infinite; + animation-timing-function: linear; + position: relative; + display: inline-block; + vertical-align: top; + border-radius: 50%; + width: 16px; + height: 16px; +} +.load-complete { + animation: none; + border-color: #ffffff; + transition: border 500ms ease-out; +} +.checkmark { + display: none; +} +.checkmark.draw:after { + animation-duration: 800ms; + animation-timing-function: ease; + animation-name: checkmark; + transform: scaleX(-1) rotate(135deg); +} +.checkmark:after { + opacity: 1; + height: 8px; + width: 4px; + transform-origin: left top; + border-right: 2px solid #ffffff; + border-top: 2px solid #ffffff; + content: ''; + left: 2px; + top: 8px; + position: absolute; +} +@keyframes loader-spin { + 0% { + transform: rotate(0deg); + } + 100% { + transform: rotate(360deg); + } +} +@keyframes checkmark { + 0% { + height: 0; + width: 0; + opacity: 1; + } + 20% { + height: 0; + width: 4px; + opacity: 1; + } + 40% { + height: 8px; + width: 4px; + opacity: 1; + } + 100% { + height: 8px; + width: 4px; + opacity: 1; + } +} + + + +/*.spinningCircle { + margin-left: 20px; + height: 16px; + width: 16px; + border-radius: 50%; + border: 2px solid rgba(255,255,255,0); + border-top-color: #ffffff; + border-right-color: #ffffff; + border-left-color: rgba(255, 255, 255, 0.50); + border-bottom-color: rgba(255, 255, 255, 0.50); + animation: single2 4s infinite linear; +} + +@keyframes single2 { + 0% { + transform: rotate(0deg); + } + 100% { + transform: rotate(720deg); + } +}*/ + +/* Disclaimer container. */ +#loginDisclaimer { + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; +} + +/* Add spacing between register anchor and disclaimer. */ +#loginRegisterSpan { + margin-bottom: 5px; +} + +/* Disclaimer text styles. */ +.loginDisclaimerText { + font-size: 7px; + color: #848484; + font-weight: bold; + text-align: center; +} + +/* * * +* Login View | Custom Checkbox +* * */ + +/* Checkbox container. */ +#checkmarkContainer { + display: flex; + justify-content: flex-end; + align-items: center; + position: relative; + cursor: pointer; + font-size: 22px; + -webkit-user-select: none; +} + +/* Hide the default checkbox. */ +#checkmarkContainer input { + opacity: 0; + cursor: pointer; + position: absolute; +} + +/* Create a custom checkbox. */ +.loginCheckmark { + position: relative; + height: 10px; + width: 10px; + border: 1px solid #848484; + border-radius: 1px; + background: none; + transition: 0.25s ease; +} +/* On hover and focus, add a grey border color. */ +#checkmarkContainer:hover input ~ *, +#checkmarkContainer input:focus ~ * { + color: #a2a2a2; + border-color: #a2a2a2; +} +/* On keydown, darken the checkbox a bit. */ +#checkmarkContainer input:active ~ *:not(#loginRememberText) { + color: #8d8d8d; + border-color: #8d8d8d; +} +#checkmarkContainer[disabled] { + pointer-events: none; +} +/* For checked -> #checkmarkContainer input:checked ~ * */ +/* Create the checkmark/indicator (hidden when not checked). */ +.loginCheckmark:after { + content: ""; + display: none; +} +/* Show the checkmark when checked. */ +#checkmarkContainer input:checked ~ .loginCheckmark:after { + display: block; +} +/* Style the checkmark/indicator. */ +#checkmarkContainer .loginCheckmark:after { + position: absolute; + left: 3.5px; + top: 0.5px; + width: 2px; + height: 6px; + border: solid #a2a2a2; + border-width: 0 2px 2px 0; + transform: rotate(45deg); +} + +/* +#login_filter { + height: calc(100% - 22px); + width: 100%; + z-index: 9000; + position: absolute; + filter: blur(8px) contrast(0.9) brightness(1.0); + background: url('./../images/backgrounds/0.jpg') no-repeat center center fixed; + transform: scale(1.2); + background-size: cover; +} +*/ diff --git a/src/renderer/components/login/Login.tsx b/src/renderer/components/login/Login.tsx index 92ffe1e..9054096 100644 --- a/src/renderer/components/login/Login.tsx +++ b/src/renderer/components/login/Login.tsx @@ -1,11 +1,81 @@ import * as React from 'react' +import LoginField from './login-field/LoginField' -export default class Login extends React.Component { +import './Login.css' + +type LoginProperties = { + cancelable: boolean +} + +export default class Login extends React.Component { + + getCancelButton(): JSX.Element { + if(this.props.cancelable) { + return ( + <> +
+ +
+ + ) + } else { + return (<>) + } + } render() { - return <> - LOGIN TBD - + return ( + <> +
+ {this.getCancelButton()} +
+
+ + MINECRAFT LOGIN + + + + +
+ + forgot password? + + +
+ +
+ + Need an Account? + +

Your password is sent directly to mojang and never stored.

+

Helios Launcher is not affiliated with Mojang AB.

+
+ +
+
+ + ) } } \ No newline at end of file diff --git a/src/renderer/components/login/login-field/LoginField.css b/src/renderer/components/login/login-field/LoginField.css new file mode 100644 index 0000000..dd2192e --- /dev/null +++ b/src/renderer/components/login/login-field/LoginField.css @@ -0,0 +1,85 @@ +/* Container to organize login field elements. */ +.loginFieldContainer { + position: relative; + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; +} + +/* SVG icons on the login view. */ +.loginSVG { + fill: #fff; + height: 20px; + width: 20px; +} + +/* Span which displays errors related to login field content. */ +.loginErrorSpan { + font-family: 'Avenir Medium'; + font-weight: bold; + font-size: 8px; + color: #ff1b0c; + width: 100%; + text-align: right; + position: absolute; + top: 7px; + opacity: 0; + transition: 0.25s ease; +} + +.shake { + animation: shake 0.82s cubic-bezier(.36,.07,.19,.97) both; +} + +@keyframes shake { + 10%, 90% { + transform: translate3d(-1px, 0, 0); + } + + 20%, 80% { + transform: translate3d(2px, 0, 0); + } + + 30%, 50%, 70% { + transform: translate3d(-4px, 0, 0); + } + + 40%, 60% { + transform: translate3d(4px, 0, 0); + } +} + +/* Login text input styles. */ +.loginField { + font-family: 'Avenir Book'; + background: none; + border-width: 1.5px 0px 0px 0px; + border-style: solid; + width: 250px; + margin-bottom: 20px; + border-color: #fff; + color: rgba(255, 255, 255, 0.75); + font-weight: bold; + text-align: center; + box-sizing: border-box; + padding: 7.5px; + font-size: 10px; + letter-spacing: 1px; +} +.loginField:focus { + outline: none; +} +.loginField:disabled { + color: rgba(255, 255, 255, 0.50); +} +.loginField::-webkit-input-placeholder { + color: rgba(255, 255, 255, 0.75); + font-size: 10px; + letter-spacing: 1px; + text-align: center; + font-weight: bold; +} +.loginField:focus::-webkit-input-placeholder { + color: transparent; +} \ No newline at end of file diff --git a/src/renderer/components/login/login-field/LoginField.tsx b/src/renderer/components/login/login-field/LoginField.tsx new file mode 100644 index 0000000..0a7709d --- /dev/null +++ b/src/renderer/components/login/login-field/LoginField.tsx @@ -0,0 +1,68 @@ +import * as React from 'react' + +import './LoginField.css' + +type LoginFieldProps = { + password: boolean, + +} + +export default class LoginField extends React.Component { + + getFieldSvg(): JSX.Element { + if(this.props.password) { + + return ( + <> + + + + + + + ) + + } else { + + return ( + <> + + + + + + + ) + + } + } + + getDefaultErrorMessage(): string { + if(this.props.password) { + return '* Required' + } else { + return '* Invalid Value' + } + } + + getPlaceholder(): string { + if(this.props.password) { + return 'PASSWORD' + } else { + return 'EMAIL OR USERNAME' + } + } + + render() { + return ( + <> +
+ {this.getFieldSvg()} + {this.getDefaultErrorMessage()} + +
+ + ) + } + +} \ No newline at end of file diff --git a/src/renderer/index.css b/src/renderer/index.css index 01d3095..e522b30 100644 --- a/src/renderer/index.css +++ b/src/renderer/index.css @@ -25,6 +25,20 @@ * * ******************************************************************************/ +/* TODO: Temp for development */ +body { + background-image: url('../../assets/images/backgrounds/3.jpg'); + background-size: cover; +} + +/* Set height to 100% */ +html { + min-height: 100%; +} +body { + height: 100vh; +} + /* Reset body, html, and div presets. */ body, html, div { margin: 0; diff --git a/src/renderer/redux/reducers/viewReducer.ts b/src/renderer/redux/reducers/viewReducer.ts index e29764e..32793e2 100644 --- a/src/renderer/redux/reducers/viewReducer.ts +++ b/src/renderer/redux/reducers/viewReducer.ts @@ -2,7 +2,7 @@ import { Reducer } from 'redux' import { View } from '../../meta/Views' import { ChangeViewAction, ViewActionType } from '../actions/viewActions' -const defaultView = View.WELCOME +const defaultView = View.LOGIN const ViewReducer: Reducer = (state = defaultView, action) => { switch(action.type) {