This commit is contained in:
Janis
2023-11-12 18:03:20 +01:00
parent a4f64c3781
commit 5f0ca2c209
4 changed files with 80 additions and 61 deletions

View File

@@ -1,9 +1,10 @@
import React, { ChangeEvent, useRef, useState } from "react"; import React, { ChangeEvent, useRef, useState, useEffect } from "react";
import "@styles/Generator.scss"; import "@styles/Generator.scss";
import Viewer from "./Viewer"; import Viewer from "./Viewer";
export default function Generator() { export default function Generator() {
const [outputUrl, setOutputUrl] = useState(new URL(window.location.href).toString()); // State variables
const [outputUrl, setOutputUrl] = useState(() => new URL(window.location.href).toString());
const copiedTooltipRef = useRef<HTMLDivElement>(null); const copiedTooltipRef = useRef<HTMLDivElement>(null);
const [remoteAppPort, setRemoteAppPort] = useState(5899); const [remoteAppPort, setRemoteAppPort] = useState(5899);
@@ -11,10 +12,12 @@ export default function Generator() {
const [hideNonTalking, setHideNonTalking] = useState(false); const [hideNonTalking, setHideNonTalking] = useState(false);
const [clientLimit, setClientLimit] = useState(0); const [clientLimit, setClientLimit] = useState(0);
React.useEffect(() => { // Effect to generate URL when dependencies change
useEffect(() => {
generateUrl(); generateUrl();
}, [remoteAppPort, showChannelName, hideNonTalking, clientLimit]); }, [remoteAppPort, showChannelName, hideNonTalking, clientLimit]);
// Function to generate the output URL
function generateUrl() { function generateUrl() {
const url = new URL(window.location.href.replace("/generate", "")); const url = new URL(window.location.href.replace("/generate", ""));
url.searchParams.set("remoteAppPort", remoteAppPort.toString()); url.searchParams.set("remoteAppPort", remoteAppPort.toString());
@@ -25,6 +28,7 @@ export default function Generator() {
setOutputUrl(url.toString()); setOutputUrl(url.toString());
} }
// Function to copy URL to clipboard
function copy() { function copy() {
navigator.clipboard.writeText(outputUrl); navigator.clipboard.writeText(outputUrl);
@@ -43,16 +47,21 @@ export default function Generator() {
return ( return (
<div className="generator"> <div className="generator">
{/* Header */}
<div className="headline"> <div className="headline">
<h1>TS5-OBS-Overlay Generator</h1> <h1>TS5-OBS-Overlay Generator</h1>
<h4>by DerTyp7</h4> <h4>by DerTyp7</h4>
</div> </div>
{/* Instructions */}
<div className="instructions"> <div className="instructions">
<p>1. Customize your settings</p> <p>1. Customize your settings</p>
<p>2. Copy the generated URL</p> <p>2. Copy the generated URL</p>
<p>3. Paste the URL into the BrowserSource URL field in OBS</p> <p>3. Paste the URL into the BrowserSource URL field in OBS</p>
<a href="#">Click here for detailed instructions</a> <a href="#">Click here for detailed instructions</a>
</div> </div>
{/* Output Section */}
<div className="output"> <div className="output">
<p className="url"> <p className="url">
<code>{outputUrl}</code> <code>{outputUrl}</code>
@@ -64,61 +73,46 @@ export default function Generator() {
Copied! Copied!
</div> </div>
</div> </div>
{/* Generator Content */}
<div className="generatorContent"> <div className="generatorContent">
{/* Configurations */}
<div className="configurations"> <div className="configurations">
<h2>Configurations</h2> <h2>Configurations</h2>
<div className="options"> <div className="options">
{/* Option Sections */}
<section> <section>
<div {/* Show Channel Name Option */}
className="option" <div className="option" onClick={() => setShowChannelName(!showChannelName)}>
onClick={() => {
setShowChannelName(!showChannelName);
}}
>
<input type="checkbox" checked={showChannelName} /> <input type="checkbox" checked={showChannelName} />
<label>Show channelname</label> <label>Show channel name</label>
</div> </div>
<div {/* Hide Non-Talking Clients Option */}
className="option" <div className="option" onClick={() => setHideNonTalking(!hideNonTalking)}>
onClick={() => {
setHideNonTalking(!hideNonTalking);
}}
>
<input type="checkbox" checked={hideNonTalking} /> <input type="checkbox" checked={hideNonTalking} />
<label>Hide non talking clients</label> <label>Hide non talking clients</label>
</div> </div>
</section> </section>
<section> <section>
{/* Client Limit Option */}
<div className="option"> <div className="option">
<input <input type="number" value={clientLimit} min={0} onChange={(e: ChangeEvent<HTMLInputElement>) => setClientLimit(parseInt(e.target.value))} />
type="number"
value={20}
min={0}
onChange={(e: ChangeEvent<HTMLInputElement>) => {
setClientLimit(parseInt(e.target.value));
}}
/>
<label>Client Limit</label> <label>Client Limit</label>
</div> </div>
{/* RemoteApp-Port Option */}
<div className="option"> <div className="option">
<input <input type="number" value={remoteAppPort} min={0} onChange={(e: ChangeEvent<HTMLInputElement>) => setRemoteAppPort(parseInt(e.target.value))} />
type="number"
value={5899}
min={0}
onChange={(e: ChangeEvent<HTMLInputElement>) => {
setRemoteAppPort(parseInt(e.target.value));
}}
/>
<label>RemoteApp-Port</label> <label>RemoteApp-Port</label>
</div> </div>
</section> </section>
</div> </div>
</div> </div>
{/* Preview */}
<div className="preview"> <div className="preview">
<Viewer remoteAppPort={remoteAppPort} showChannelName={showChannelName} hideNonTalking={hideNonTalking} clientLimit={clientLimit} /> <Viewer remoteAppPort={remoteAppPort} showChannelName={showChannelName} hideNonTalking={hideNonTalking} clientLimit={clientLimit} />
</div> </div>

View File

@@ -3,6 +3,7 @@ $breakpoint-1: 1200px;
$breakpoint-2: 790px; $breakpoint-2: 790px;
$breakpoint-3: 600px; $breakpoint-3: 600px;
// Tooltip animation keyframes
@keyframes tooltipAnimation { @keyframes tooltipAnimation {
0% { 0% {
opacity: 0; opacity: 0;
@@ -16,7 +17,7 @@ $breakpoint-3: 600px;
.generator { .generator {
background-color: #232528; background-color: #232528;
color: white; color: #fff;
width: 100%; width: 100%;
display: flex; display: flex;
height: 100%; height: 100%;
@@ -39,25 +40,17 @@ $breakpoint-3: 600px;
gap: 10px 50px; gap: 10px 50px;
flex-wrap: wrap; flex-wrap: wrap;
padding-bottom: 10px; padding-bottom: 10px;
p, p,
a { a {
font-size: 0.8rem; font-size: 0.8rem;
} }
p { p {
color: #b4b4b4; color: #b4b4b4;
} }
a {
color: #3abe78;
font-weight: bold;
text-decoration: none;
transition: all 100ms ease-in-out;
&:hover {
color: #31f399;
}
}
} }
.output { .output {
display: flex; display: flex;
flex-direction: row; flex-direction: row;
@@ -142,6 +135,7 @@ $breakpoint-3: 600px;
justify-content: left; justify-content: left;
column-gap: 10px; column-gap: 10px;
width: 100%; width: 100%;
input { input {
-webkit-appearance: none; -webkit-appearance: none;
-moz-appearance: none; -moz-appearance: none;
@@ -154,7 +148,7 @@ $breakpoint-3: 600px;
outline: none; outline: none;
transition: all 200ms ease-in-out; transition: all 200ms ease-in-out;
position: relative; position: relative;
color: white; color: #fff;
text-align: center; text-align: center;
&::-webkit-outer-spin-button, &::-webkit-outer-spin-button,
@@ -163,7 +157,7 @@ $breakpoint-3: 600px;
margin: 0; margin: 0;
} }
// make it a cross when checked // Cross when checked styles
&:checked { &:checked {
&:after { &:after {
content: ""; content: "";
@@ -193,7 +187,10 @@ $breakpoint-3: 600px;
input[type="number"] { input[type="number"] {
width: 50px; width: 50px;
height: 25px;
cursor: text; cursor: text;
-moz-appearance: textfield;
appearance: textfield;
} }
label { label {
@@ -202,9 +199,13 @@ $breakpoint-3: 600px;
} }
} }
} }
// Preview styles
.preview { .preview {
flex: 1; flex: 1;
border: 2px solid #31f39973; border: 2px solid #31f39973;
// Viewer styles (see src/styles/Viewer.scss)
.viewer { .viewer {
background-image: url("/images/viewer_example_background.png"); background-image: url("/images/viewer_example_background.png");
background-repeat: no-repeat; background-repeat: no-repeat;
@@ -214,6 +215,7 @@ $breakpoint-3: 600px;
} }
} }
// Responsive styles
@media screen and (max-width: $breakpoint-1) { @media screen and (max-width: $breakpoint-1) {
.generatorContent { .generatorContent {
flex-direction: column; flex-direction: column;
@@ -243,13 +245,13 @@ $breakpoint-3: 600px;
.url { .url {
width: 200px; width: 200px;
} }
}
.generatorContent { .generatorContent {
.configurations { .configurations {
.options { .options {
flex-direction: column; flex-direction: column;
gap: 30px; gap: 30px;
}
} }
} }
} }

View File

@@ -1,14 +1,14 @@
//* Viewer styles
// this file contains styles for the viewer component
// styles for the viewer component should not be modified somewhere else
.viewer { .viewer {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
gap: 0 0; padding: 0.5rem;
font-size: 3rem;
color: white;
padding: 10px;
h1, h1,
p { p {
margin: 0;
background-color: #2f313680; background-color: #2f313680;
padding: 0.25rem 0.5rem; padding: 0.25rem 0.5rem;
border-radius: 0.25rem; border-radius: 0.25rem;
@@ -18,6 +18,7 @@
max-width: 20ch; max-width: 20ch;
user-select: none; user-select: none;
} }
.channelNameContainer { .channelNameContainer {
display: flex; display: flex;
align-items: center; align-items: center;
@@ -31,12 +32,14 @@
align-items: center; align-items: center;
margin: 0.5rem 0; margin: 0.5rem 0;
// icon styles
svg { svg {
width: 2.1rem; width: 2.1rem;
aspect-ratio: 1/1; aspect-ratio: 1/1;
margin-right: 0.5rem; margin-right: 0.5rem;
} }
// client name styles
p { p {
font-size: 1.4rem; font-size: 1.4rem;
} }

View File

@@ -1,3 +1,4 @@
// Reset styles for all elements
* { * {
font-family: Arial, Helvetica, sans-serif; font-family: Arial, Helvetica, sans-serif;
font-weight: bold; font-weight: bold;
@@ -7,19 +8,23 @@
box-sizing: border-box; box-sizing: border-box;
} }
// Set up basic styles for the entire page
body, body,
html { html {
min-height: 100vh; min-height: 100vh;
min-width: 100%; min-width: 100%;
display: flex; display: flex;
overflow-x: hidden; overflow-x: hidden;
color: #fff;
} }
// Ensure the root element takes up the full viewport
#root { #root {
height: 100%; height: 100%;
min-width: 100%; min-width: 100%;
} }
// Headline styles
h1 { h1 {
font-size: 1.8rem; font-size: 1.8rem;
} }
@@ -36,6 +41,7 @@ h4 {
font-size: 1.1rem; font-size: 1.1rem;
} }
// Common styles for heading elements
h1, h1,
h2, h2,
h3, h3,
@@ -45,6 +51,19 @@ h6 {
font-weight: bold; font-weight: bold;
} }
// Text styles
a {
color: #3abe78;
font-weight: bold;
text-decoration: none;
transition: all 100ms ease-in-out;
&:hover {
color: #31f399;
}
}
// Button styles
button { button {
background-color: #202024; background-color: #202024;
color: #fff; color: #fff;
@@ -54,13 +73,14 @@ button {
padding: 10px 20px; padding: 10px 20px;
cursor: pointer; cursor: pointer;
transition: all 300ms ease-in-out; transition: all 300ms ease-in-out;
&:hover { &:hover {
background-color: #42d486; background-color: #42d486;
color: #202024; color: #202024;
} }
} }
// custom dark themed scrollbar // Custom dark-themed scrollbar
::-webkit-scrollbar { ::-webkit-scrollbar {
width: 5px; width: 5px;
height: 5px; height: 5px;
@@ -74,8 +94,8 @@ button {
::-webkit-scrollbar-thumb { ::-webkit-scrollbar-thumb {
background: #31f39973; background: #31f39973;
border-radius: 10px; border-radius: 10px;
}
::-webkit-scrollbar-thumb:hover { &:hover {
background: #48ee95; background: #48ee95;
}
} }