summaryrefslogtreecommitdiff
path: root/WebInterface/NodeJSServer/src
diff options
context:
space:
mode:
Diffstat (limited to 'WebInterface/NodeJSServer/src')
-rw-r--r--WebInterface/NodeJSServer/src/about.js4
-rw-r--r--WebInterface/NodeJSServer/src/index.js8
-rw-r--r--WebInterface/NodeJSServer/src/modules/hash.js17
-rw-r--r--WebInterface/NodeJSServer/src/modules/server-client.js8
-rw-r--r--WebInterface/NodeJSServer/src/modules/ui/login-modal.js67
-rw-r--r--WebInterface/NodeJSServer/src/modules/ui/modal.js17
-rw-r--r--WebInterface/NodeJSServer/src/modules/ui/server-creator.js7
-rw-r--r--WebInterface/NodeJSServer/src/modules/ui/server-listing.js11
-rw-r--r--WebInterface/NodeJSServer/src/play.js0
-rw-r--r--WebInterface/NodeJSServer/src/style/about.scss5
-rw-r--r--WebInterface/NodeJSServer/src/style/index.scss435
-rw-r--r--WebInterface/NodeJSServer/src/style/partials/_base.scss20
-rw-r--r--WebInterface/NodeJSServer/src/style/partials/_btn.scss75
-rw-r--r--WebInterface/NodeJSServer/src/style/partials/backdrop/_base.scss58
-rw-r--r--WebInterface/NodeJSServer/src/style/partials/backdrop/_menu.scss43
-rw-r--r--WebInterface/NodeJSServer/src/style/partials/front-layer/_base.scss23
-rw-r--r--WebInterface/NodeJSServer/src/style/partials/front-layer/_copyright.scss10
-rw-r--r--WebInterface/NodeJSServer/src/style/partials/front-layer/_notifications.scss66
-rw-r--r--WebInterface/NodeJSServer/src/style/partials/front-layer/_server-listing.scss121
-rw-r--r--WebInterface/NodeJSServer/src/style/partials/modal/_base.scss35
-rw-r--r--WebInterface/NodeJSServer/src/style/partials/modal/_login.scss34
21 files changed, 620 insertions, 444 deletions
diff --git a/WebInterface/NodeJSServer/src/about.js b/WebInterface/NodeJSServer/src/about.js
new file mode 100644
index 0000000..1561a2f
--- /dev/null
+++ b/WebInterface/NodeJSServer/src/about.js
@@ -0,0 +1,4 @@
+import Backdrop from './modules/ui/backdrop.js';
+
+let backdrop = new Backdrop('menu', 'front-layer', 'show-menu');
+backdrop.register();
diff --git a/WebInterface/NodeJSServer/src/index.js b/WebInterface/NodeJSServer/src/index.js
index 01a9ba1..a37ae5f 100644
--- a/WebInterface/NodeJSServer/src/index.js
+++ b/WebInterface/NodeJSServer/src/index.js
@@ -1,7 +1,7 @@
import Backdrop from './modules/ui/backdrop.js';
import BannerController from './modules/ui/notification-banner.js';
import ServerClient from './modules/server-client.js'
-import Modal from './modules/ui/modal.js'; // TODO: DEBUGGING
+import LoginModal from './modules/ui/login-modal.js'; // TODO: JUST FOR DEBUGGING
let backdrop = new Backdrop('menu', 'front-layer', 'show-menu');
backdrop.register();
@@ -10,8 +10,10 @@ let notifications = new BannerController('notifications',
'banner-info', 'dismiss-banner');
notifications.register();
-let client = new ServerClient('http://89.183.117.197:5000/chatHub', 'server-list', true);
+let client = new ServerClient('http://89.183.8.51:5000/chatHub', 'server-list', true);
document.getElementById('refresh-button')
.addEventListener('click', client.loadServers.bind(client));
-new Modal('Test Titel');
+new LoginModal('The Crew', client);
+
+window.client = client; //TODO: REMOVE, JUST FOR DEBUGGING
diff --git a/WebInterface/NodeJSServer/src/modules/hash.js b/WebInterface/NodeJSServer/src/modules/hash.js
new file mode 100644
index 0000000..826c8ee
--- /dev/null
+++ b/WebInterface/NodeJSServer/src/modules/hash.js
@@ -0,0 +1,17 @@
+String.prototype.getHash = async function() {
+ let data = new ArrayBuffer(this.length * 2);
+ let bufferView = new Uint16Array(data);
+ for (let i = 0; i < this.length; i++) {
+ bufferView[i] = this.charCodeAt(i);
+ }
+
+ let encrypted = await crypto.subtle.digest('SHA-256', bufferView);
+ let byteArray = new Uint8Array(encrypted);
+ let base64String = '';
+
+ for (let byte of byteArray) {
+ base64String += String.fromCharCode(byte);
+ }
+
+ return btoa(base64String);
+}
diff --git a/WebInterface/NodeJSServer/src/modules/server-client.js b/WebInterface/NodeJSServer/src/modules/server-client.js
index e550bb7..0a257ba 100644
--- a/WebInterface/NodeJSServer/src/modules/server-client.js
+++ b/WebInterface/NodeJSServer/src/modules/server-client.js
@@ -1,6 +1,5 @@
import * as signalR from '@aspnet/signalr';
import ServerListing from './ui/server-listing.js';
-import ServerCreator from './ui/server-creator.js';
export default class ServerClient {
constructor(url, serverListingId, debug = false) {
@@ -21,6 +20,8 @@ export default class ServerClient {
this.refreshing = false;
this.serverListing = new ServerListing(serverListingId);
+
+ this.messageHandling(); //TODO: REMOVE, JUST FOR DEBUGGING
}
loadServers() {
@@ -43,7 +44,11 @@ export default class ServerClient {
}
createServer(){
+ // TODO: Create
+ }
+ sendLogin(group, password, username){
+ this.connection.invoke('Login', group, username, password);
}
messageHandling(){
@@ -52,6 +57,7 @@ export default class ServerClient {
.replace(/</g, "&lt;")
.replace(/>/g, "&gt;");
let encodedMsg = user + " sagt: " + msg;
+ console.log(encodedMsg); //TODO: REMOVE, JUST FOR DEBUGGING
});
}
}
diff --git a/WebInterface/NodeJSServer/src/modules/ui/login-modal.js b/WebInterface/NodeJSServer/src/modules/ui/login-modal.js
new file mode 100644
index 0000000..cb1f2ac
--- /dev/null
+++ b/WebInterface/NodeJSServer/src/modules/ui/login-modal.js
@@ -0,0 +1,67 @@
+import Modal from './modal.js';
+import '../hash.js';
+
+export default class LoginModal extends Modal {
+ constructor(serverName, serverClient) {
+ super(serverName);
+ this.serverName = serverName;
+ this.serverClient = serverClient;
+
+ let passBox = document.createElement('div');
+ let nameBox = document.createElement('div');
+ let sendBox = document.createElement('div');
+
+ let passwordLabel = document.createElement('label');
+ let passwordInput = document.createElement('input');
+ passwordLabel.setAttribute('for', 'password-input');
+ passwordLabel.textContent = 'Passwort:';
+ passwordLabel.title = 'Das Passwort des Spiels'
+ passwordInput.id = 'password-input';
+ passwordInput.type = 'password';
+ passwordInput.placeholder = 'Passwort';
+
+ let nameLabel = document.createElement('label');
+ let nameInput = document.createElement('input');
+ nameLabel.setAttribute('for', 'name-input');
+ nameLabel.textContent = 'Benutzername:';
+ nameLabel.title = 'Dein Anzeigename'
+ nameInput.id = 'name-input';
+ nameInput.type = 'text';
+ nameInput.autocomplete = 'on';
+ nameInput.placeholder = 'Name';
+
+ let sendButton = document.createElement('button');
+ sendButton.className = 'btn';
+ sendButton.textContent = 'Login';
+ sendButton.id = 'login-button';
+
+
+ passBox.appendChild(passwordLabel);
+ passBox.appendChild(passwordInput);
+ nameBox.appendChild(nameLabel);
+ nameBox.appendChild(nameInput);
+ sendBox.appendChild(sendButton);
+
+ this.body.appendChild(passBox);
+ this.body.appendChild(nameBox);
+ this.body.appendChild(sendBox);
+
+ this.nameInput = nameInput;
+ this.passwordInput = passwordInput;
+ this.loginButton = sendButton;
+
+ this.registerLoginBtn();
+ }
+
+ registerLoginBtn() {
+ this.loginButton.addEventListener('click', () => {
+ console.log('button pressed')
+ let userName = this.nameInput.value;
+ this.passwordInput.value.getHash()
+ .then((result) => {
+ this.serverClient.sendLogin(this.serverName, result, userName);
+ this.close();
+ });
+ });
+ }
+}
diff --git a/WebInterface/NodeJSServer/src/modules/ui/modal.js b/WebInterface/NodeJSServer/src/modules/ui/modal.js
index e1aa8a2..55e38bd 100644
--- a/WebInterface/NodeJSServer/src/modules/ui/modal.js
+++ b/WebInterface/NodeJSServer/src/modules/ui/modal.js
@@ -13,6 +13,7 @@ export default class Modal {
title.textContent = titleString;
modal.appendChild(title);
+ modal.appendChild(body);
modalBackground.appendChild(modal);
document.body.appendChild(modalBackground);
@@ -25,11 +26,19 @@ export default class Modal {
}
registerEvents() {
+ this.modal.addEventListener('click', (e) => {
+ e.stopPropagation();
+ });
+
this.bg.addEventListener('click', () => {
- this.bg.classList.add('hidden');
- this.bg.addEventListener('transitionend', () => {
- document.body.removeChild(this.bg);
- });
+ this.close();
+ });
+ }
+
+ close() {
+ this.bg.classList.add('hidden');
+ this.bg.addEventListener('transitionend', () => {
+ document.body.removeChild(this.bg);
});
}
diff --git a/WebInterface/NodeJSServer/src/modules/ui/server-creator.js b/WebInterface/NodeJSServer/src/modules/ui/server-creator.js
deleted file mode 100644
index 4203dfe..0000000
--- a/WebInterface/NodeJSServer/src/modules/ui/server-creator.js
+++ /dev/null
@@ -1,7 +0,0 @@
-import Modal from './modal.js';
-
-export default class ServerCreator extends Modal {
- constructor() {
- super('Neues Spiel')
- }
-}
diff --git a/WebInterface/NodeJSServer/src/modules/ui/server-listing.js b/WebInterface/NodeJSServer/src/modules/ui/server-listing.js
index be66573..2c501fd 100644
--- a/WebInterface/NodeJSServer/src/modules/ui/server-listing.js
+++ b/WebInterface/NodeJSServer/src/modules/ui/server-listing.js
@@ -15,12 +15,14 @@ export default class ServerListing {
let serverDiv = document.createElement('div');
let nameSpan = document.createElement('span');
+ let rightAlignDiv = document.createElement('div');
let onlineDot = document.createElement('div');
let playerCountSpan = document.createElement('span');
let playerCountStaticSpan = document.createElement('span');
let joinButton = document.createElement('button');
serverDiv.className = 'server';
nameSpan.className = 'server-name';
+ rightAlignDiv.className = 'right-aligned-items';
onlineDot.className = 'player-count-dot';
playerCountSpan.className = 'player-count';
playerCountStaticSpan.className = 'player-count-static';
@@ -31,11 +33,12 @@ export default class ServerListing {
playerCountStaticSpan.textContent = 'Spieler online';
joinButton.textContent = 'Beitreten';
+ rightAlignDiv.appendChild(onlineDot);
+ rightAlignDiv.appendChild(playerCountSpan);
+ rightAlignDiv.appendChild(playerCountStaticSpan);
+ rightAlignDiv.appendChild(joinButton);
serverDiv.appendChild(nameSpan);
- serverDiv.appendChild(onlineDot);
- serverDiv.appendChild(playerCountSpan);
- serverDiv.appendChild(playerCountStaticSpan);
- serverDiv.appendChild(joinButton);
+ serverDiv.appendChild(rightAlignDiv)
this.serverListing.appendChild(serverDiv);
}
}
diff --git a/WebInterface/NodeJSServer/src/play.js b/WebInterface/NodeJSServer/src/play.js
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/WebInterface/NodeJSServer/src/play.js
diff --git a/WebInterface/NodeJSServer/src/style/about.scss b/WebInterface/NodeJSServer/src/style/about.scss
new file mode 100644
index 0000000..9d8ad50
--- /dev/null
+++ b/WebInterface/NodeJSServer/src/style/about.scss
@@ -0,0 +1,5 @@
+@import 'partials/base';
+@import 'partials/backdrop/base';
+@import 'partials/backdrop/menu';
+@import 'partials/front-layer/base';
+@import 'partials/front-layer/copyright';
diff --git a/WebInterface/NodeJSServer/src/style/index.scss b/WebInterface/NodeJSServer/src/style/index.scss
index b5ca607..04c896a 100644
--- a/WebInterface/NodeJSServer/src/style/index.scss
+++ b/WebInterface/NodeJSServer/src/style/index.scss
@@ -1,425 +1,10 @@
-@import 'partials/_colors.scss';
-
-html,body {
- height: 100vh;
- margin: 0;
- padding: 0;
- font-family: 'Roboto', sans-serif;
- font-display: swap;
- overflow: hidden;
- background-color: $primary;
- color: $primary-text;
- user-select: none;
-}
-
-body {
- display: flex;
- flex-direction: column;
- background-color: $secondary;
- position: relative;
-}
-
-.btn {
- border: none;
- border-radius: 4px;
- padding: 8px;
- margin: 0;
- font-size: 1rem;
- font-family: 'Roboto Condensed', sans-serif;
- font-weight: bold;
- display: inline-block;
- background-color: $secondary;
- color: $secondary-text;
- text-transform: uppercase;
- box-shadow: 0 1px 3px rgba(0,0,0,0.12), 0 1px 2px rgba(0,0,0,0.24);
- cursor: pointer;
- letter-spacing: 0.25rem;
-
- background-position: center;
- transition: background 800ms ease, box-shadow 100ms ease, color 200ms ease;
-
- &:hover {
- background: $secondary-dark radial-gradient(circle, transparent 1%, $secondary-dark 1%) center/15000%;
- box-shadow: 0 3px 6px rgba(0,0,0,0.16), 0 3px 6px rgba(0,0,0,0.23);
- }
-
- &:active {
- background-color: $secondary-light;
- background-size: 100%;
- box-shadow: 0 1px 3px rgba(0,0,0,0.12);
- transition: background 0s, box-shadow 0s;
- }
-
- &:disabled {
- color: $secondary-text-disabled;
- background: $secondary-disabled;
- box-shadow: none;
- transition: background 200ms ease, box-shadow 200ms ease, color 200ms ease;
- }
-}
-
-.text-btn {
- padding: 8px;
- margin: 0;
- font-size: 1rem;
- font-family: 'Roboto Condensed', sans-serif;
- font-weight: bold;
- display: inline-block;
- color: $secondary-text;
- background-color: rgba(0, 0, 0, 0);
- text-transform: uppercase;
- box-shadow: none;
-
- background-position: center;
- transition: background 800ms ease, box-shadow 100ms ease, color 200ms ease;
-
- &:hover {
- background: $secondary-dark-transparent radial-gradient(circle, transparent 1%, $secondary-dark-transparent 1%) center/15000%;
- box-shadow: 0 3px 6px rgba(0,0,0,0.16), 0 3px 6px rgba(0,0,0,0.23);
- }
-
- &:active {
- background-color: $secondary-light;
- background-size: 100%;
- box-shadow: none;
- transition: background 0s, box-shadow 0s;
- }
-
- &:disabled {
- background: rgba(0,0,0,0);
- color: $secondary-text-disabled;
- box-shadow: none;
- transition: background 200ms ease, box-shadow 200ms ease, color 200ms ease;
- }
-}
-
-.backdrop {
- background-color: $secondary;
- color: $secondary-text;
- font-size: 1rem;
-
- .header-bar {
- display: flex;
- align-items: center;
-
- @media (max-height: 550px) {
- margin-top: 0.125rem;
- }
-
- .menu-icon {
- background-image: url("./ressources/menu.png");
- background-position: center;
- background-repeat: no-repeat;
- width: 36px;
- height: 36px;
- padding: 4px;
- margin: 1rem;
- display: inline-block;
- border: none;
- border-radius: 32px;
- transition: background-color 100ms ease;
-
- @media (max-height: 550px) {
- padding: 0;
- margin: 0.125rem;
- }
-
- &:hover {
- background-color: $secondary-dark;
- }
-
- &.open {
- background-image: url("./ressources/menu_close.png");
- }
- }
-
- .header {
- margin: 0;
- padding: 16px;
- text-align: center;
- flex-grow: 1;
-
- @media (min-width: 450px) {
- margin-right: 56px;
- }
-
- @media (max-height: 550px) {
- padding: 0;
- }
- }
- }
-
- .menu-actions {
- box-sizing: border-box;
- transition: max-height 200ms ease, color 200ms ease, background-color 100ms ease;;
- position: relative;
- max-height: 16rem;
- margin: 0 1rem;
-
- .menu-option {
- color: $primary-text;
- text-decoration: none;
- box-sizing: border-box;
- display: inline-block;
- font-size: 1.5rem;
- width: 100%;
- text-align: center;
- padding: 1rem;
- border: none;
- border-radius: 8px;
- cursor: pointer;
-
- &:hover {
- background-color: $secondary-half-dark;
- }
-
- &.active {
- background-color: $secondary-dark;
- }
- }
-
- &.hidden {
- max-height: 0;
- color: rgba(0,0,0,0);
-
- .menu-option {
- color: rgba(0,0,0,0);
- background-color: rgba(0,0,0,0) !important;
- }
- }
- }
-}
-
-.container {
- @keyframes start {
- from {top: 100vh;}
- to {top: 0;}
- }
-
- position: relative;
- border: none;
- border-radius: 16px 16px 0 0;
- min-height: 0;
- height: 100%;
-
- box-sizing: border-box;
- background-color: white;
- margin-top: 8px;
- animation-name: start;
- animation-duration: 1s;
- animation-timing-function: ease;
- color: black;
-
- display: flex;
- flex-direction: column;
-
- .banner {
- z-index: 1;
- background-color: white;
- border: none;
- border-radius: 16px 16px 0 0;
- display: flex;
- margin: 0;
- margin-bottom: 1rem;
- padding-top: 1rem;
- max-height: 10rem;
- flex-direction: row;
- flex-wrap: wrap;
- transform-origin: top;
- transition: max-height 200ms ease, transform 200ms ease, visibility 200ms step-start;
- min-height: 3.5rem;
- justify-items: center;
- justify-content: center;
-
- @media (max-height: 550px) {
- position: absolute;
- width: 100%;
- box-shadow: 0 14px 28px rgba(0,0,0,0.25), 0 10px 10px rgba(0,0,0,0.22);
- }
-
- &.hidden {
- transform: scaleY(0);
- visibility: hidden;
- max-height: 0;
- transition: max-height 200ms ease, transform 200ms ease, visibility 200ms step-end;
- min-height: 0;
- margin-bottom: 0;
- }
-
- .banner-text {
- align-self: left;
- margin: 1rem;
- flex-grow: 100;
- }
-
- .btn-container{
- display: flex;
- flex-grow: 1;
- text-align: right;
-
-
- .banner-button {
- color: $secondary-dark;
- padding: 8px 16px;
- margin: 0;
- margin-right: 1rem;
- letter-spacing: 0.125rem;
- }
- }
-
- hr {
- width: 100%;
-
- @media (max-height: 550px) {
- margin-bottom: 0;
- }
- }
- }
-
- .server-listing {
- box-sizing: border-box;
- background-color: $primary;
- color: $primary-text;
- min-height: 0;
- display: flex;
- flex-direction: column;
- border-style: none;
- border-radius: 8px;
- margin: 1rem;
- margin-bottom: 2rem;
- padding: 0.5rem;
- padding-top: 0.25rem;
- box-shadow: 0 8px 10px 1px rgba(0, 0, 0, .14), 0 3px 14px 2px rgba(0, 0, 0, .12), 0 5px 5px -3px rgba(0, 0, 0, .2);
- h1 {
- text-align: center;
- @media (max-height: 550px) {
- display: none;
- }
- }
- hr {
- width: 100%;
- @media (max-height: 550px) {
- display: none;
- }
- }
-
- @media (max-height: 450px) {
- padding-bottom: 0.125rem
- }
-
- .server-entries {
- overflow-y: auto;
- min-height: 0;
-
- .server {
- font-size: 1.25rem;
- display: flex;
- flex-direction: row;
-
- @media (max-width: 1000px) {
- flex-wrap: wrap;
- }
-
- align-items: center;
- background-color: $primary-light;
- padding: 0.5rem;
- margin-bottom: 0.25rem;
- border-style: none;
- border-radius: 8px;
-
- .server-name {
- font-weight: bold;
- letter-spacing: 0.125rem;
- white-space: nowrap;
- overflow: hidden;
- margin: 0.5rem 0;
- }
-
- .player-count {
- font-family: 'Roboto Condensed', sans-serif;
- }
-
- .player-count-static {
- @media (max-width: 1000px) {
- display: none;
- }
-
- white-space: nowrap;
- margin-left: 0.25rem;
- letter-spacing: 0;
- font-family: 'Roboto Condensed', sans-serif;
- }
-
- .player-count-dot {
- background-color: $online-green;
- border-radius: 50%;
- min-width: 1rem;
- min-height: 1rem;
- width: 1rem;
- height: 1rem;
- margin-left: auto;
- margin-right: 0.25rem;
- }
-
- .join-btn {
- margin-left: 0.5rem;
- min-width: 12rem;
- }
- }
- }
-
- .button-container {
- display: flex;
- flex-direction: row-reverse;
- margin-top: 1rem;
- margin-right: 0.25rem;
- min-height: 2.5rem;
-
- @media (max-height: 450px) {
- margin-top: 0.125rem;
- min-height: 2rem;
- }
- }
- }
-
- .copyright-container {
- box-sizing: border-box;
- position: absolute;
- width: 100%;
- margin: 4px;
- bottom: 0;
- text-align: center;
- }
-}
-
-.modal-container {
- position:absolute;
- top: 0;
- width: 100vw;
- height: 100vh;
- background-color: #000000AA;
- display: flex;
- opacity: 1;
- transition: opacity 200ms ease;
-
- &.hidden {
- opacity: 0;
- }
-
- .modal {
- background-color: #546e7a;
- margin: auto;
- padding: 1rem;
- border-style: none;
- border-radius: 8px;
- box-shadow: 0 19px 38px rgba(0,0,0,0.30), 0 15px 12px rgba(0,0,0,0.22);
- }
-
- .modal-title {
- margin: 1rem;
- }
-
- .modal-body {
- margin: 0.25rem;
- }
-}
+@import 'partials/base';
+@import 'partials/btn';
+@import 'partials/backdrop/base';
+@import 'partials/backdrop/menu';
+@import 'partials/front-layer/base';
+@import 'partials/front-layer/notifications';
+@import 'partials/front-layer/server-listing';
+@import 'partials/front-layer/copyright';
+@import 'partials/modal/base';
+@import 'partials/modal/login';
diff --git a/WebInterface/NodeJSServer/src/style/partials/_base.scss b/WebInterface/NodeJSServer/src/style/partials/_base.scss
new file mode 100644
index 0000000..5f300fd
--- /dev/null
+++ b/WebInterface/NodeJSServer/src/style/partials/_base.scss
@@ -0,0 +1,20 @@
+@import 'colors';
+
+html,body {
+ height: 100vh;
+ margin: 0;
+ padding: 0;
+ font-family: 'Roboto', sans-serif;
+ font-display: swap;
+ overflow: hidden;
+ background-color: $primary;
+ color: $primary-text;
+ user-select: none;
+}
+
+body {
+ display: flex;
+ flex-direction: column;
+ background-color: $secondary;
+ position: relative;
+}
diff --git a/WebInterface/NodeJSServer/src/style/partials/_btn.scss b/WebInterface/NodeJSServer/src/style/partials/_btn.scss
new file mode 100644
index 0000000..cf5ee33
--- /dev/null
+++ b/WebInterface/NodeJSServer/src/style/partials/_btn.scss
@@ -0,0 +1,75 @@
+@import 'colors';
+
+.btn {
+ border: none;
+ border-radius: 4px;
+ padding: 8px;
+ margin: 0;
+ font-size: 1rem;
+ font-family: 'Roboto Condensed', sans-serif;
+ font-weight: bold;
+ display: inline-block;
+ background-color: $secondary;
+ color: $secondary-text;
+ text-transform: uppercase;
+ box-shadow: 0 1px 3px rgba(0,0,0,0.12), 0 1px 2px rgba(0,0,0,0.24);
+ cursor: pointer;
+ letter-spacing: 0.25rem;
+
+ background-position: center;
+ transition: background 800ms ease, box-shadow 100ms ease, color 200ms ease;
+
+ &:hover {
+ background: $secondary-dark radial-gradient(circle, transparent 1%, $secondary-dark 1%) center/15000%;
+ box-shadow: 0 3px 6px rgba(0,0,0,0.16), 0 3px 6px rgba(0,0,0,0.23);
+ }
+
+ &:active {
+ background-color: $secondary-light;
+ background-size: 100%;
+ box-shadow: 0 1px 3px rgba(0,0,0,0.12);
+ transition: background 0s, box-shadow 0s;
+ }
+
+ &:disabled {
+ color: $secondary-text-disabled;
+ background: $secondary-disabled;
+ box-shadow: none;
+ transition: background 200ms ease, box-shadow 200ms ease, color 200ms ease;
+ }
+}
+
+.text-btn {
+ padding: 8px;
+ margin: 0;
+ font-size: 1rem;
+ font-family: 'Roboto Condensed', sans-serif;
+ font-weight: bold;
+ display: inline-block;
+ color: $secondary-text;
+ background-color: rgba(0, 0, 0, 0);
+ text-transform: uppercase;
+ box-shadow: none;
+
+ background-position: center;
+ transition: background 800ms ease, box-shadow 100ms ease, color 200ms ease;
+
+ &:hover {
+ background: $secondary-dark-transparent radial-gradient(circle, transparent 1%, $secondary-dark-transparent 1%) center/15000%;
+ box-shadow: 0 3px 6px rgba(0,0,0,0.16), 0 3px 6px rgba(0,0,0,0.23);
+ }
+
+ &:active {
+ background-color: $secondary-light;
+ background-size: 100%;
+ box-shadow: none;
+ transition: background 0s, box-shadow 0s;
+ }
+
+ &:disabled {
+ background: rgba(0,0,0,0);
+ color: $secondary-text-disabled;
+ box-shadow: none;
+ transition: background 200ms ease, box-shadow 200ms ease, color 200ms ease;
+ }
+}
diff --git a/WebInterface/NodeJSServer/src/style/partials/backdrop/_base.scss b/WebInterface/NodeJSServer/src/style/partials/backdrop/_base.scss
new file mode 100644
index 0000000..1b7a924
--- /dev/null
+++ b/WebInterface/NodeJSServer/src/style/partials/backdrop/_base.scss
@@ -0,0 +1,58 @@
+@import '../colors';
+
+.backdrop {
+ background-color: $secondary;
+ color: $secondary-text;
+ font-size: 1rem;
+
+ .header-bar {
+ display: flex;
+ align-items: center;
+
+ @media (max-height: 550px) {
+ margin-top: 0.125rem;
+ }
+
+ .menu-icon {
+ background-image: url("../ressources/menu.png");
+ background-position: center;
+ background-repeat: no-repeat;
+ width: 36px;
+ height: 36px;
+ padding: 4px;
+ margin: 1rem;
+ display: inline-block;
+ border: none;
+ border-radius: 32px;
+ transition: background-color 100ms ease;
+
+ @media (max-height: 550px) {
+ padding: 0;
+ margin: 0.125rem;
+ }
+
+ &:hover {
+ background-color: $secondary-dark;
+ }
+
+ &.open {
+ background-image: url("../ressources/menu_close.png");
+ }
+ }
+
+ .header {
+ margin: 0;
+ padding: 16px;
+ text-align: center;
+ flex-grow: 1;
+
+ @media (min-width: 450px) {
+ margin-right: 56px;
+ }
+
+ @media (max-height: 550px) {
+ padding: 0;
+ }
+ }
+ }
+}
diff --git a/WebInterface/NodeJSServer/src/style/partials/backdrop/_menu.scss b/WebInterface/NodeJSServer/src/style/partials/backdrop/_menu.scss
new file mode 100644
index 0000000..26833d5
--- /dev/null
+++ b/WebInterface/NodeJSServer/src/style/partials/backdrop/_menu.scss
@@ -0,0 +1,43 @@
+@import '../colors';
+
+.backdrop {
+ .menu-actions {
+ box-sizing: border-box;
+ transition: max-height 200ms ease, color 200ms ease, background-color 100ms ease;;
+ position: relative;
+ max-height: 16rem;
+ margin: 0 1rem;
+
+ .menu-option {
+ color: $primary-text;
+ text-decoration: none;
+ box-sizing: border-box;
+ display: inline-block;
+ font-size: 1.5rem;
+ width: 100%;
+ text-align: center;
+ padding: 1rem;
+ border: none;
+ border-radius: 8px;
+ cursor: pointer;
+
+ &:hover {
+ background-color: $secondary-half-dark;
+ }
+
+ &.active {
+ background-color: $secondary-dark;
+ }
+ }
+
+ &.hidden {
+ max-height: 0;
+ color: rgba(0,0,0,0);
+
+ .menu-option {
+ color: rgba(0,0,0,0);
+ background-color: rgba(0,0,0,0) !important;
+ }
+ }
+ }
+}
diff --git a/WebInterface/NodeJSServer/src/style/partials/front-layer/_base.scss b/WebInterface/NodeJSServer/src/style/partials/front-layer/_base.scss
new file mode 100644
index 0000000..6a2aa9a
--- /dev/null
+++ b/WebInterface/NodeJSServer/src/style/partials/front-layer/_base.scss
@@ -0,0 +1,23 @@
+.front-layer {
+ @keyframes start {
+ from {top: 100vh;}
+ to {top: 0;}
+ }
+
+ position: relative;
+ border: none;
+ border-radius: 16px 16px 0 0;
+ min-height: 0;
+ height: 100%;
+
+ box-sizing: border-box;
+ background-color: white;
+ margin-top: 8px;
+ animation-name: start;
+ animation-duration: 1s;
+ animation-timing-function: ease;
+ color: black;
+
+ display: flex;
+ flex-direction: column;
+}
diff --git a/WebInterface/NodeJSServer/src/style/partials/front-layer/_copyright.scss b/WebInterface/NodeJSServer/src/style/partials/front-layer/_copyright.scss
new file mode 100644
index 0000000..31ac614
--- /dev/null
+++ b/WebInterface/NodeJSServer/src/style/partials/front-layer/_copyright.scss
@@ -0,0 +1,10 @@
+.front-layer{
+ .copyright-container {
+ box-sizing: border-box;
+ position: absolute;
+ width: 100%;
+ margin: 4px;
+ bottom: 0;
+ text-align: center;
+ }
+}
diff --git a/WebInterface/NodeJSServer/src/style/partials/front-layer/_notifications.scss b/WebInterface/NodeJSServer/src/style/partials/front-layer/_notifications.scss
new file mode 100644
index 0000000..4dd7361
--- /dev/null
+++ b/WebInterface/NodeJSServer/src/style/partials/front-layer/_notifications.scss
@@ -0,0 +1,66 @@
+@import '../colors';
+
+.front-layer {
+ .banner {
+ z-index: 1;
+ background-color: white;
+ border: none;
+ border-radius: 16px 16px 0 0;
+ display: flex;
+ margin: 0;
+ margin-bottom: 1rem;
+ padding-top: 1rem;
+ max-height: 10rem;
+ flex-direction: row;
+ flex-wrap: wrap;
+ transform-origin: top;
+ transition: max-height 200ms ease, transform 200ms ease, visibility 200ms step-start;
+ min-height: 3.5rem;
+ justify-items: center;
+ justify-content: center;
+
+ @media (max-height: 550px) {
+ position: absolute;
+ width: 100%;
+ box-shadow: 0 14px 28px rgba(0,0,0,0.25), 0 10px 10px rgba(0,0,0,0.22);
+ }
+
+ &.hidden {
+ transform: scaleY(0);
+ visibility: hidden;
+ max-height: 0;
+ transition: max-height 200ms ease, transform 200ms ease, visibility 200ms step-end;
+ min-height: 0;
+ margin-bottom: 0;
+ }
+
+ .banner-text {
+ align-self: left;
+ margin: 1rem;
+ flex-grow: 100;
+ }
+
+ .btn-container{
+ display: flex;
+ flex-grow: 1;
+ text-align: right;
+
+
+ .banner-button {
+ color: $secondary-dark;
+ padding: 8px 16px;
+ margin: 0;
+ margin-right: 1rem;
+ letter-spacing: 0.125rem;
+ }
+ }
+
+ hr {
+ width: 100%;
+
+ @media (max-height: 550px) {
+ margin-bottom: 0;
+ }
+ }
+ }
+}
diff --git a/WebInterface/NodeJSServer/src/style/partials/front-layer/_server-listing.scss b/WebInterface/NodeJSServer/src/style/partials/front-layer/_server-listing.scss
new file mode 100644
index 0000000..7e67178
--- /dev/null
+++ b/WebInterface/NodeJSServer/src/style/partials/front-layer/_server-listing.scss
@@ -0,0 +1,121 @@
+@import '../colors';
+
+.front-layer {
+ .server-listing {
+ box-sizing: border-box;
+ background-color: $primary;
+ color: $primary-text;
+ min-height: 0;
+ display: flex;
+ flex-direction: column;
+ border-style: none;
+ border-radius: 8px;
+ margin: 1rem;
+ margin-bottom: 2rem;
+ padding: 0.5rem;
+ padding-top: 0.25rem;
+ box-shadow: 0 8px 10px 1px rgba(0, 0, 0, .14), 0 3px 14px 2px rgba(0, 0, 0, .12), 0 5px 5px -3px rgba(0, 0, 0, .2);
+ h1 {
+ text-align: center;
+ @media (max-height: 550px) {
+ display: none;
+ }
+ }
+ hr {
+ width: 100%;
+ @media (max-height: 550px) {
+ display: none;
+ }
+ }
+
+ @media (max-height: 450px) {
+ padding-bottom: 0.125rem
+ }
+
+ .server-entries {
+ overflow-y: auto;
+ min-height: 0;
+
+ .server {
+ font-size: 1.25rem;
+ display: flex;
+ flex-direction: row;
+
+ @media (max-width: 1000px) {
+ flex-wrap: wrap;
+ }
+
+ align-items: center;
+ background-color: $primary-light;
+ padding: 0.5rem;
+ margin-bottom: 0.25rem;
+ border-style: none;
+ border-radius: 8px;
+
+ .server-name {
+ font-weight: bold;
+ letter-spacing: 0.125rem;
+ white-space: nowrap;
+ overflow: hidden;
+ margin: 0.5rem 0;
+ }
+
+ .right-aligned-items {
+ margin-left: auto;
+ white-space: nowrap;
+ display: flex;
+ align-items: center;
+
+ .player-count-dot {
+ background-color: $online-green;
+ border-radius: 50%;
+ min-width: 1rem;
+ min-height: 1rem;
+ width: 1rem;
+ height: 1rem;
+ margin-right: 0.25rem;
+ }
+
+ .player-count {
+ font-family: 'Roboto Condensed', sans-serif;
+ }
+
+ .player-count-static {
+ @media (max-width: 1000px) {
+ display: none;
+ }
+
+ white-space: nowrap;
+ margin-left: 0.25rem;
+ letter-spacing: 0;
+ font-family: 'Roboto Condensed', sans-serif;
+ }
+
+ .join-btn {
+ margin-left: 0.5rem;
+ min-width: 12rem;
+ }
+ }
+ }
+ }
+
+ .button-container {
+ display: flex;
+ flex-direction: row-reverse;
+ margin-top: 1rem;
+ margin-right: 0.25rem;
+ min-height: 2.5rem;
+
+ @media (max-height: 450px) {
+ margin-top: 0.125rem;
+ min-height: 2rem;
+ }
+
+ .btn {
+ @media (max-width: 450px) {
+ letter-spacing: 0;
+ }
+ }
+ }
+ }
+}
diff --git a/WebInterface/NodeJSServer/src/style/partials/modal/_base.scss b/WebInterface/NodeJSServer/src/style/partials/modal/_base.scss
new file mode 100644
index 0000000..006c241
--- /dev/null
+++ b/WebInterface/NodeJSServer/src/style/partials/modal/_base.scss
@@ -0,0 +1,35 @@
+@import '../colors';
+
+.modal-container {
+ position:absolute;
+ top: 0;
+ width: 100vw;
+ height: 100vh;
+ background-color: #000000AA;
+ display: flex;
+ opacity: 1;
+ transition: opacity 200ms ease;
+
+ &.hidden {
+ opacity: 0;
+ }
+
+ .modal {
+ background-color: $primary;
+ margin: auto;
+ padding: 1rem;
+ border-style: none;
+ border-radius: 8px;
+ box-shadow: 0 19px 38px rgba(0,0,0,0.30), 0 15px 12px rgba(0,0,0,0.22);
+ }
+
+ .modal-title {
+ max-width: 85vw;
+ margin: 1rem;
+ text-align: center;
+ }
+
+ .modal-body {
+ margin: 0.25rem;
+ }
+}
diff --git a/WebInterface/NodeJSServer/src/style/partials/modal/_login.scss b/WebInterface/NodeJSServer/src/style/partials/modal/_login.scss
new file mode 100644
index 0000000..eed978f
--- /dev/null
+++ b/WebInterface/NodeJSServer/src/style/partials/modal/_login.scss
@@ -0,0 +1,34 @@
+.modal {
+ .modal-title {
+ white-space: nowrap;
+ overflow: hidden;
+ }
+
+ .modal-body {
+ display: grid;
+ grid-template-columns: 10em 1fr;
+ grid-template-rows: 1fr 1fr 1fr;
+ grid-row-gap: 0.5rem;
+
+ div {
+ display: contents;
+ font-size: 1.25rem;
+
+ label {
+ flex-basis: 30%;
+ margin-right: 2rem;
+
+ grid-column: 1 2;
+ }
+
+ input {
+ flex-basis: calc(70% - 2rem);
+ grid-column: 2 3;
+ }
+
+ button {
+ grid-column: 1 / 3;
+ }
+ }
+ }
+}