Improve ui appearance
This commit is contained in:
parent
8fa7247835
commit
f6fb6f72f4
|
@ -16,6 +16,7 @@
|
|||
"react-redux": "^8.0.1",
|
||||
"react-scripts": "5.0.1",
|
||||
"react-use-websocket": "^4.3.1",
|
||||
"sass": "^1.58.3",
|
||||
"typescript": "^4.6.0",
|
||||
"web-vitals": "^2.1.0"
|
||||
},
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
import React from 'react';
|
||||
import logo from './logo.svg';
|
||||
import { RadioToggler } from './features/radio/Radio';
|
||||
import './App.css';
|
||||
|
||||
|
@ -7,7 +6,6 @@ function App() {
|
|||
return (
|
||||
<div className="App">
|
||||
<header className="App-header">
|
||||
<img src={logo} className="App-logo" alt="logo" />
|
||||
<RadioToggler />
|
||||
</header>
|
||||
</div>
|
||||
|
|
|
@ -1,79 +0,0 @@
|
|||
.row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.row > button {
|
||||
margin-left: 4px;
|
||||
margin-right: 8px;
|
||||
}
|
||||
|
||||
.row:not(:last-child) {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.value {
|
||||
font-size: 78px;
|
||||
padding-left: 16px;
|
||||
padding-right: 16px;
|
||||
margin-top: 2px;
|
||||
font-family: 'Courier New', Courier, monospace;
|
||||
}
|
||||
|
||||
.button {
|
||||
appearance: none;
|
||||
background: none;
|
||||
font-size: 32px;
|
||||
padding-left: 12px;
|
||||
padding-right: 12px;
|
||||
outline: none;
|
||||
border: 2px solid transparent;
|
||||
color: rgb(112, 76, 182);
|
||||
padding-bottom: 4px;
|
||||
cursor: pointer;
|
||||
background-color: rgba(112, 76, 182, 0.1);
|
||||
border-radius: 2px;
|
||||
transition: all 0.15s;
|
||||
}
|
||||
|
||||
.textbox {
|
||||
font-size: 32px;
|
||||
padding: 2px;
|
||||
width: 64px;
|
||||
text-align: center;
|
||||
margin-right: 4px;
|
||||
}
|
||||
|
||||
.button:hover,
|
||||
.button:focus {
|
||||
border: 2px solid rgba(112, 76, 182, 0.4);
|
||||
}
|
||||
|
||||
.button:active {
|
||||
background-color: rgba(112, 76, 182, 0.2);
|
||||
}
|
||||
|
||||
.asyncButton {
|
||||
composes: button;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.asyncButton:after {
|
||||
content: '';
|
||||
background-color: rgba(112, 76, 182, 0.15);
|
||||
display: block;
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
left: 0;
|
||||
top: 0;
|
||||
opacity: 0;
|
||||
transition: width 1s linear, opacity 0.5s ease 1s;
|
||||
}
|
||||
|
||||
.asyncButton:active:after {
|
||||
width: 0%;
|
||||
opacity: 1;
|
||||
transition: 0s;
|
||||
}
|
116
ui/src/features/radio/Radio.module.scss
Normal file
116
ui/src/features/radio/Radio.module.scss
Normal file
|
@ -0,0 +1,116 @@
|
|||
@-webkit-keyframes rotor{
|
||||
from{ -webkit-transform: rotate(0deg); }
|
||||
to{ -webkit-transform: rotate(360deg); }
|
||||
}
|
||||
@-moz-keyframes rotor{
|
||||
from{ -moz-transform: rotate(0deg); }
|
||||
to{ -moz-transform: rotate(360deg); }
|
||||
}
|
||||
@-o-keyframes rotor{
|
||||
from{ -o-transform: rotate(0deg); }
|
||||
to{ -o-transform: rotate(360deg); }
|
||||
}
|
||||
@keyframes rotor{
|
||||
from{ transform: rotate(0deg); }
|
||||
to{ transform: rotate(360deg); }
|
||||
}
|
||||
|
||||
$circle-size: 350px;
|
||||
$inner-size: 310px;
|
||||
$border-radius: 160px;
|
||||
$margin: 20px;
|
||||
$shadow-size: 20px;
|
||||
$rotor-color-active: #6eeb34;
|
||||
$rotor-color-stopped: #ff0000;
|
||||
$rotor-color-loading: #fcc203;
|
||||
$disconnected-color: #8a8987;
|
||||
$hover-color: #fff7b0;
|
||||
|
||||
@mixin wedge($color) {
|
||||
background-image: linear-gradient(45deg,
|
||||
white 0%,
|
||||
white 30%,
|
||||
$color 30%,
|
||||
$color 70%,
|
||||
white 70%,
|
||||
white 100%);
|
||||
}
|
||||
|
||||
@mixin rotor-animation {
|
||||
-webkit-animation: rotor 1.5s linear 0s infinite normal;
|
||||
-mox-animation: rotor 1.5s linear 0s infinite normal;
|
||||
-o-animation: rotor 1.5s linear 0s infinite normal;
|
||||
animation: rotor 1.5s linear 0s infinite normal;
|
||||
}
|
||||
|
||||
@mixin circle-shape {
|
||||
width: $circle-size;
|
||||
height: $circle-size;
|
||||
border-radius: $border-radius;
|
||||
/*** outer circle position: under */
|
||||
z-index: 1;
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
.buttonWrap {
|
||||
width: $circle-size;
|
||||
height: $circle-size;
|
||||
margin: $margin 0;
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
|
||||
/* Inner circle */
|
||||
.clicker {
|
||||
width: $inner-size;
|
||||
height: $inner-size; /* 20px smaller b/c of margin below */
|
||||
margin: $margin;
|
||||
background-color: #fff;
|
||||
border-radius: $border-radius;
|
||||
|
||||
/* Overlays this circle on the .circle */
|
||||
z-index: 2;
|
||||
position: absolute;
|
||||
|
||||
/* centers the text: adjust to desired size */
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-content: center;
|
||||
flex-direction: column;
|
||||
text-align: center;
|
||||
font-size: 60px;
|
||||
user-select: none;
|
||||
|
||||
/* shadow */
|
||||
-webkit-box-shadow: 0px 0px $shadow-size 0px rgba(0, 0, 0, 0.5);
|
||||
-moz-box-shadow: 0px 0px $shadow-size 0px rgba(0, 0, 0, 0.5);
|
||||
-o-box-shadow: 0px 0px $shadow-size 0px rgba(0, 0, 0, 0.5);
|
||||
box-shadow: 0px 0px $shadow-size 0px rgba(0, 0, 0, 0.5);
|
||||
|
||||
&:hover {
|
||||
background-color: $hover-color;
|
||||
}
|
||||
}
|
||||
|
||||
/** outer circle **/
|
||||
.circleDisconnected {
|
||||
@include circle-shape;
|
||||
background-color: $disconnected-color;
|
||||
}
|
||||
|
||||
.circleStarted {
|
||||
@include circle-shape;
|
||||
@include wedge($rotor-color-active);
|
||||
@include rotor-animation;
|
||||
}
|
||||
|
||||
.circleLoading {
|
||||
@include circle-shape;
|
||||
@include wedge($rotor-color-loading);
|
||||
@include rotor-animation;
|
||||
}
|
||||
|
||||
.circleStopped {
|
||||
@include circle-shape;
|
||||
background-color: $rotor-color-stopped;
|
||||
}
|
||||
}
|
|
@ -4,12 +4,7 @@ import useWebSocket from 'react-use-websocket';
|
|||
import { useAppSelector, useAppDispatch } from '../../app/hooks';
|
||||
import { RadioState, ConnectionState } from '../../app/types';
|
||||
import {
|
||||
setRadioStarted,
|
||||
setRadioStopped,
|
||||
toggleRadioState,
|
||||
setConnected,
|
||||
setDisconnected,
|
||||
toggleConnection,
|
||||
selectLoading,
|
||||
selectRadio,
|
||||
selectConnection,
|
||||
|
@ -19,6 +14,7 @@ import {
|
|||
getOnWSClose,
|
||||
getOnWSMessage,
|
||||
} from './radioWS';
|
||||
import styles from './Radio.module.scss';
|
||||
|
||||
const getWSUrl = async () => {
|
||||
const wsUrl = process.env.WS_ADDR ? process.env.WS_ADDR : "ws://localhost/liveness";
|
||||
|
@ -27,7 +23,7 @@ const getWSUrl = async () => {
|
|||
};
|
||||
|
||||
export function RadioToggler() {
|
||||
const loadingState = useAppSelector(selectLoading);
|
||||
const loading = useAppSelector(selectLoading);
|
||||
const radioState = useAppSelector(selectRadio);
|
||||
const connectionState = useAppSelector(selectConnection);
|
||||
|
||||
|
@ -42,24 +38,39 @@ export function RadioToggler() {
|
|||
const onWSMessage = getOnWSMessage(dispatch);
|
||||
useEffect(() => {
|
||||
if (lastJsonMessage != null) {
|
||||
onWSMessage(lastJsonMessage);
|
||||
if (!loading) {
|
||||
onWSMessage(lastJsonMessage);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if (loadingState) {
|
||||
return <div>Loading...</div>;
|
||||
let buttonStatus = "";
|
||||
let circleStyle = styles.circleDisconnected;
|
||||
if (loading) {
|
||||
buttonStatus = "Loading";
|
||||
circleStyle = styles.circleLoading;
|
||||
} else if (connectionState) {
|
||||
switch (radioState) {
|
||||
case RadioState.STARTED:
|
||||
buttonStatus = "ON AIR";
|
||||
circleStyle = styles.circleStarted;
|
||||
break;
|
||||
case RadioState.STOPPED:
|
||||
buttonStatus = "OFFLINE";
|
||||
circleStyle = styles.circleStopped;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div>
|
||||
<button
|
||||
aria-label="toggle-radio-state"
|
||||
<div className={styles.buttonWrap} aria-label="toggle-radio-state">
|
||||
<div
|
||||
className={styles.clicker}
|
||||
onClick={() => dispatch(toggleRadioState())}
|
||||
>TOGGLE</button>
|
||||
<div>Radio: {radioState}</div>
|
||||
<div>Connection: {connectionState}</div>
|
||||
>{buttonStatus}</div>
|
||||
<div className={circleStyle}></div>
|
||||
</div>
|
||||
</div>
|
||||
</div >
|
||||
);
|
||||
}
|
||||
|
|
18
ui/yarn.lock
18
ui/yarn.lock
|
@ -3053,7 +3053,7 @@ check-types@^11.1.1:
|
|||
resolved "https://registry.yarnpkg.com/check-types/-/check-types-11.2.2.tgz#7afc0b6a860d686885062f2dba888ba5710335b4"
|
||||
integrity sha512-HBiYvXvn9Z70Z88XKjz3AEKd4HJhBXsa3j7xFnITAzoS8+q6eIGi8qDB8FKPBAjtuxjI/zFpwuiCb8oDtKOYrA==
|
||||
|
||||
chokidar@^3.4.2, chokidar@^3.5.3:
|
||||
"chokidar@>=3.0.0 <4.0.0", chokidar@^3.4.2, chokidar@^3.5.3:
|
||||
version "3.5.3"
|
||||
resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.5.3.tgz#1cf37c8707b932bd1af1ae22c0432e2acd1903bd"
|
||||
integrity sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==
|
||||
|
@ -4997,6 +4997,11 @@ immer@^9.0.16, immer@^9.0.7:
|
|||
resolved "https://registry.yarnpkg.com/immer/-/immer-9.0.19.tgz#67fb97310555690b5f9cd8380d38fc0aabb6b38b"
|
||||
integrity sha512-eY+Y0qcsB4TZKwgQzLaE/lqYMlKhv5J9dyd2RhhtGhNo2njPXDqU9XPfcNfa3MIDsdtZt5KlkIsirlo4dHsWdQ==
|
||||
|
||||
immutable@^4.0.0:
|
||||
version "4.2.4"
|
||||
resolved "https://registry.yarnpkg.com/immutable/-/immutable-4.2.4.tgz#83260d50889526b4b531a5e293709a77f7c55a2a"
|
||||
integrity sha512-WDxL3Hheb1JkRN3sQkyujNlL/xRjAo3rJtaU5xeufUauG66JdMr32bLj4gF+vWl84DIA3Zxw7tiAjneYzRRw+w==
|
||||
|
||||
import-fresh@^3.0.0, import-fresh@^3.1.0, import-fresh@^3.2.1:
|
||||
version "3.3.0"
|
||||
resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.3.0.tgz#37162c25fcb9ebaa2e6e53d5b4d88ce17d9e0c2b"
|
||||
|
@ -7899,6 +7904,15 @@ sass-loader@^12.3.0:
|
|||
klona "^2.0.4"
|
||||
neo-async "^2.6.2"
|
||||
|
||||
sass@^1.58.3:
|
||||
version "1.58.3"
|
||||
resolved "https://registry.yarnpkg.com/sass/-/sass-1.58.3.tgz#2348cc052061ba4f00243a208b09c40e031f270d"
|
||||
integrity sha512-Q7RaEtYf6BflYrQ+buPudKR26/lH+10EmO9bBqbmPh/KeLqv8bjpTNqxe71ocONqXq+jYiCbpPUmQMS+JJPk4A==
|
||||
dependencies:
|
||||
chokidar ">=3.0.0 <4.0.0"
|
||||
immutable "^4.0.0"
|
||||
source-map-js ">=0.6.2 <2.0.0"
|
||||
|
||||
sax@~1.2.4:
|
||||
version "1.2.4"
|
||||
resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9"
|
||||
|
@ -8105,7 +8119,7 @@ source-list-map@^2.0.0, source-list-map@^2.0.1:
|
|||
resolved "https://registry.yarnpkg.com/source-list-map/-/source-list-map-2.0.1.tgz#3993bd873bfc48479cca9ea3a547835c7c154b34"
|
||||
integrity sha512-qnQ7gVMxGNxsiL4lEuJwe/To8UnK7fAnmbGEEH8RpLouuKbeEm0lhbQVFIrNSuB+G7tVrAlVsZgETT5nljf+Iw==
|
||||
|
||||
source-map-js@^1.0.1, source-map-js@^1.0.2:
|
||||
"source-map-js@>=0.6.2 <2.0.0", source-map-js@^1.0.1, source-map-js@^1.0.2:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.0.2.tgz#adbc361d9c62df380125e7f161f71c826f1e490c"
|
||||
integrity sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==
|
||||
|
|
Loading…
Reference in New Issue
Block a user