Improve ui appearance
This commit is contained in:
parent
8fa7247835
commit
f6fb6f72f4
|
@ -16,6 +16,7 @@
|
||||||
"react-redux": "^8.0.1",
|
"react-redux": "^8.0.1",
|
||||||
"react-scripts": "5.0.1",
|
"react-scripts": "5.0.1",
|
||||||
"react-use-websocket": "^4.3.1",
|
"react-use-websocket": "^4.3.1",
|
||||||
|
"sass": "^1.58.3",
|
||||||
"typescript": "^4.6.0",
|
"typescript": "^4.6.0",
|
||||||
"web-vitals": "^2.1.0"
|
"web-vitals": "^2.1.0"
|
||||||
},
|
},
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import logo from './logo.svg';
|
|
||||||
import { RadioToggler } from './features/radio/Radio';
|
import { RadioToggler } from './features/radio/Radio';
|
||||||
import './App.css';
|
import './App.css';
|
||||||
|
|
||||||
|
@ -7,7 +6,6 @@ function App() {
|
||||||
return (
|
return (
|
||||||
<div className="App">
|
<div className="App">
|
||||||
<header className="App-header">
|
<header className="App-header">
|
||||||
<img src={logo} className="App-logo" alt="logo" />
|
|
||||||
<RadioToggler />
|
<RadioToggler />
|
||||||
</header>
|
</header>
|
||||||
</div>
|
</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 { useAppSelector, useAppDispatch } from '../../app/hooks';
|
||||||
import { RadioState, ConnectionState } from '../../app/types';
|
import { RadioState, ConnectionState } from '../../app/types';
|
||||||
import {
|
import {
|
||||||
setRadioStarted,
|
|
||||||
setRadioStopped,
|
|
||||||
toggleRadioState,
|
toggleRadioState,
|
||||||
setConnected,
|
|
||||||
setDisconnected,
|
|
||||||
toggleConnection,
|
|
||||||
selectLoading,
|
selectLoading,
|
||||||
selectRadio,
|
selectRadio,
|
||||||
selectConnection,
|
selectConnection,
|
||||||
|
@ -19,6 +14,7 @@ import {
|
||||||
getOnWSClose,
|
getOnWSClose,
|
||||||
getOnWSMessage,
|
getOnWSMessage,
|
||||||
} from './radioWS';
|
} from './radioWS';
|
||||||
|
import styles from './Radio.module.scss';
|
||||||
|
|
||||||
const getWSUrl = async () => {
|
const getWSUrl = async () => {
|
||||||
const wsUrl = process.env.WS_ADDR ? process.env.WS_ADDR : "ws://localhost/liveness";
|
const wsUrl = process.env.WS_ADDR ? process.env.WS_ADDR : "ws://localhost/liveness";
|
||||||
|
@ -27,7 +23,7 @@ const getWSUrl = async () => {
|
||||||
};
|
};
|
||||||
|
|
||||||
export function RadioToggler() {
|
export function RadioToggler() {
|
||||||
const loadingState = useAppSelector(selectLoading);
|
const loading = useAppSelector(selectLoading);
|
||||||
const radioState = useAppSelector(selectRadio);
|
const radioState = useAppSelector(selectRadio);
|
||||||
const connectionState = useAppSelector(selectConnection);
|
const connectionState = useAppSelector(selectConnection);
|
||||||
|
|
||||||
|
@ -42,24 +38,39 @@ export function RadioToggler() {
|
||||||
const onWSMessage = getOnWSMessage(dispatch);
|
const onWSMessage = getOnWSMessage(dispatch);
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (lastJsonMessage != null) {
|
if (lastJsonMessage != null) {
|
||||||
|
if (!loading) {
|
||||||
onWSMessage(lastJsonMessage);
|
onWSMessage(lastJsonMessage);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
if (loadingState) {
|
let buttonStatus = "";
|
||||||
return <div>Loading...</div>;
|
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 (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<div>
|
<div className={styles.buttonWrap} aria-label="toggle-radio-state">
|
||||||
<button
|
<div
|
||||||
aria-label="toggle-radio-state"
|
className={styles.clicker}
|
||||||
onClick={() => dispatch(toggleRadioState())}
|
onClick={() => dispatch(toggleRadioState())}
|
||||||
>TOGGLE</button>
|
>{buttonStatus}</div>
|
||||||
<div>Radio: {radioState}</div>
|
<div className={circleStyle}></div>
|
||||||
<div>Connection: {connectionState}</div>
|
|
||||||
</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"
|
resolved "https://registry.yarnpkg.com/check-types/-/check-types-11.2.2.tgz#7afc0b6a860d686885062f2dba888ba5710335b4"
|
||||||
integrity sha512-HBiYvXvn9Z70Z88XKjz3AEKd4HJhBXsa3j7xFnITAzoS8+q6eIGi8qDB8FKPBAjtuxjI/zFpwuiCb8oDtKOYrA==
|
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"
|
version "3.5.3"
|
||||||
resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.5.3.tgz#1cf37c8707b932bd1af1ae22c0432e2acd1903bd"
|
resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.5.3.tgz#1cf37c8707b932bd1af1ae22c0432e2acd1903bd"
|
||||||
integrity sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==
|
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"
|
resolved "https://registry.yarnpkg.com/immer/-/immer-9.0.19.tgz#67fb97310555690b5f9cd8380d38fc0aabb6b38b"
|
||||||
integrity sha512-eY+Y0qcsB4TZKwgQzLaE/lqYMlKhv5J9dyd2RhhtGhNo2njPXDqU9XPfcNfa3MIDsdtZt5KlkIsirlo4dHsWdQ==
|
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:
|
import-fresh@^3.0.0, import-fresh@^3.1.0, import-fresh@^3.2.1:
|
||||||
version "3.3.0"
|
version "3.3.0"
|
||||||
resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.3.0.tgz#37162c25fcb9ebaa2e6e53d5b4d88ce17d9e0c2b"
|
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"
|
klona "^2.0.4"
|
||||||
neo-async "^2.6.2"
|
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:
|
sax@~1.2.4:
|
||||||
version "1.2.4"
|
version "1.2.4"
|
||||||
resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9"
|
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"
|
resolved "https://registry.yarnpkg.com/source-list-map/-/source-list-map-2.0.1.tgz#3993bd873bfc48479cca9ea3a547835c7c154b34"
|
||||||
integrity sha512-qnQ7gVMxGNxsiL4lEuJwe/To8UnK7fAnmbGEEH8RpLouuKbeEm0lhbQVFIrNSuB+G7tVrAlVsZgETT5nljf+Iw==
|
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"
|
version "1.0.2"
|
||||||
resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.0.2.tgz#adbc361d9c62df380125e7f161f71c826f1e490c"
|
resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.0.2.tgz#adbc361d9c62df380125e7f161f71c826f1e490c"
|
||||||
integrity sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==
|
integrity sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==
|
||||||
|
|
Loading…
Reference in New Issue
Block a user