CSS styling overhaul
FIXED Issue with rendering time - minutes now in fixed 2-digit format ADD Sidebar Topbar components to help with UI/UX ADD Avatar and Menu PNG files for style TODO Improve styling ET AL removed redundant code
This commit is contained in:
parent
7e5f5bdf77
commit
4117c11c35
16 changed files with 261 additions and 66 deletions
|
@ -1,6 +1,6 @@
|
|||
body {
|
||||
background-color: black;
|
||||
color: #00FF33;
|
||||
/* background-color: black;
|
||||
color: #00FF33; */
|
||||
margin: 1%;
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
|
18
src/App.tsx
18
src/App.tsx
|
@ -7,6 +7,8 @@ import strings from "./Intl/strings.json";
|
|||
import { LangContext, LoginContext, LoginType } from "./context";
|
||||
import { contentTypes, domain, endpoints, port } from "./consts";
|
||||
import { Login } from "./Login/Login";
|
||||
import { Sidebar } from "./Sidebar/Sidebar";
|
||||
import { Topbar } from "./Topbar/Topbar";
|
||||
// what we "in the business" call type gymnastics
|
||||
const Wrapper = (): React.ReactElement => {
|
||||
const [lang, setLang] = useState<LangType>("en_US");
|
||||
|
@ -16,11 +18,19 @@ const Wrapper = (): React.ReactElement => {
|
|||
? `IRC logged in as ${login.username}`
|
||||
: "IRC Chat";
|
||||
}, [login]);
|
||||
const [sidebarEnabled, setSidebarEnabled] = useState(false);
|
||||
return (
|
||||
<LangContext.Provider value={lang}>
|
||||
<h1>{strings[lang].homepage.title}</h1>
|
||||
<p>{strings[lang].homepage.description}</p>
|
||||
<LoginContext.Provider value={login}>
|
||||
<Topbar
|
||||
setSidebarEnable={(enabled: boolean) =>
|
||||
setSidebarEnabled(enabled)
|
||||
}
|
||||
></Topbar>
|
||||
<Sidebar
|
||||
isEnabled={sidebarEnabled}
|
||||
setEnable={(enabled: boolean) => setSidebarEnabled(enabled)}
|
||||
></Sidebar>
|
||||
{/* callbacks for altering the Lang/Login contexts */}
|
||||
<Login
|
||||
setLogin={(value) => {
|
||||
|
@ -97,8 +107,8 @@ const App = ({
|
|||
})
|
||||
.then((responseBody: { success: boolean }) => {
|
||||
if (responseBody.success) {
|
||||
// TODO Put new username response true handler method stub
|
||||
} else {
|
||||
// TODO Put new username response true handler method stub
|
||||
} else {
|
||||
console.error(
|
||||
"Server POST message failed."
|
||||
);
|
||||
|
|
|
@ -12,6 +12,14 @@
|
|||
}
|
||||
|
||||
.chat {
|
||||
/* float: left; */
|
||||
/* min-height: 80vh; */
|
||||
position: relative;
|
||||
box-shadow:
|
||||
0 2.8px 2.2px rgba(0, 0, 0, 0.034),
|
||||
0 6.7px 5.3px rgba(0, 0, 0, 0.048),
|
||||
0 12.5px 10px rgba(0, 0, 0, 0.06),
|
||||
0 22.3px 17.9px rgba(0, 0, 0, 0.072),
|
||||
0 41.8px 33.4px rgba(0, 0, 0, 0.086),
|
||||
0 100px 80px rgba(0, 0, 0, 0.12)
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"variableNames": ["userName", "content"],
|
||||
"acceptedLangs": ["en_US", "zh_TW", "el_GR"],
|
||||
"acceptedLangs": ["en_US", "zh_TW", "el_GR", "ar_SA"],
|
||||
"en_US": {
|
||||
"homepage": {
|
||||
"userNamePrompt": "Your username: ",
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
.uname-error-text {
|
||||
color: red;
|
||||
}
|
||||
.login {}
|
|
@ -5,64 +5,61 @@ import strings from "../Intl/strings.json";
|
|||
import "./MessageDisplay.css";
|
||||
import { queryByRole } from "@testing-library/react";
|
||||
export const MessageDisplay = ({
|
||||
type,
|
||||
fromUserId,
|
||||
toUserId,
|
||||
content,
|
||||
timeMillis,
|
||||
type,
|
||||
fromUserId,
|
||||
toUserId,
|
||||
content,
|
||||
timeMillis,
|
||||
}: Message): React.ReactElement<Message> => {
|
||||
const dateTime: Date = new Date(timeMillis);
|
||||
const lang = useContext(LangContext);
|
||||
const msgPage = strings[lang].chat;
|
||||
/* FIXED funny error
|
||||
* DESCRIPTION
|
||||
* The line below was
|
||||
* return (<p>[{dateTime.toLocaleString(Intl.DateTimeFormat().resolvedOptions().timeZone)}]...</p>)
|
||||
* The line incorrectly generated a value of "UTC" as the parameter to toLocaleString()
|
||||
* While "UTC" is an accepted string value, in EEST, aka. "Europe/Athens" timezone string is not an acceptable parameter.
|
||||
* This caused the return statement to fail, and the message fails to render, despite it being correctly committed to the db.
|
||||
* Funny clown moment 🤡
|
||||
*/
|
||||
const timeString = `${
|
||||
dateTime.getHours() > 12
|
||||
? dateTime.getHours() - 12
|
||||
: dateTime.getHours()
|
||||
}:${dateTime.getMinutes()} ${dateTime.getHours() > 12 ? "PM" : "AM"}`;
|
||||
switch (type) {
|
||||
case MessageType.HELLO as MessageType:
|
||||
return (
|
||||
<p className="msg">
|
||||
[{timeString}]{" "}
|
||||
{msgPage.joinMessage.replace(
|
||||
"$userName",
|
||||
fromUserId
|
||||
)}
|
||||
</p>
|
||||
);
|
||||
case MessageType.MESSAGE as MessageType:
|
||||
return (
|
||||
<p className="msg">
|
||||
[{timeString}]{" "}
|
||||
{msgPage.serverMessage
|
||||
.replace(
|
||||
"$userName",
|
||||
fromUserId
|
||||
)
|
||||
.replace("$content", content)}
|
||||
</p>
|
||||
);
|
||||
case MessageType.DATA as MessageType:
|
||||
return <></>;
|
||||
case MessageType.CHNAME as MessageType:
|
||||
return <></>;
|
||||
default:
|
||||
console.error("Illegal MessageType reported!");
|
||||
return (
|
||||
<p className="msg-err">
|
||||
[{timeString}] **THIS MESSAGE CANNOT BE
|
||||
CORRECTLY SHOWN BECAUSE THE CLIENT
|
||||
ENCOUNTERED AN ERROR**
|
||||
</p>
|
||||
);
|
||||
}
|
||||
const dateTime: Date = new Date(timeMillis);
|
||||
const lang = useContext(LangContext);
|
||||
const msgPage = strings[lang].chat;
|
||||
/* FIXED funny error
|
||||
* DESCRIPTION
|
||||
* The line below was
|
||||
* return (<p>[{dateTime.toLocaleString(Intl.DateTimeFormat().resolvedOptions().timeZone)}]...</p>)
|
||||
* The line incorrectly generated a value of "UTC" as the parameter to toLocaleString()
|
||||
* While "UTC" is an accepted string value, in EEST, aka. "Europe/Athens" timezone string is not an acceptable parameter.
|
||||
* This caused the return statement to fail, and the message fails to render, despite it being correctly committed to the db.
|
||||
* Funny clown moment 🤡
|
||||
*/
|
||||
const timeString = `${
|
||||
dateTime.getHours() > 12
|
||||
? dateTime.getHours() - 12
|
||||
: dateTime.getHours()
|
||||
}:${
|
||||
dateTime.getMinutes() >= 10
|
||||
? dateTime.getMinutes()
|
||||
: `0${dateTime.getMinutes().toString()}`
|
||||
} ${dateTime.getHours() > 12 ? "PM" : "AM"}`;
|
||||
switch (type) {
|
||||
case MessageType.HELLO as MessageType:
|
||||
return (
|
||||
<p className="msg">
|
||||
[{timeString}]{" "}
|
||||
{msgPage.joinMessage.replace("$userName", fromUserId)}
|
||||
</p>
|
||||
);
|
||||
case MessageType.MESSAGE as MessageType:
|
||||
return (
|
||||
<p className="msg">
|
||||
[{timeString}]{" "}
|
||||
{msgPage.serverMessage
|
||||
.replace("$userName", fromUserId)
|
||||
.replace("$content", content)}
|
||||
</p>
|
||||
);
|
||||
case MessageType.DATA as MessageType:
|
||||
return <></>;
|
||||
case MessageType.CHNAME as MessageType:
|
||||
return <></>;
|
||||
default:
|
||||
console.error("Illegal MessageType reported!");
|
||||
return (
|
||||
<p className="msg-err">
|
||||
[{timeString}] **THIS MESSAGE CANNOT BE CORRECTLY SHOWN
|
||||
BECAUSE THE CLIENT ENCOUNTERED AN ERROR**
|
||||
</p>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
|
5
src/Sidebar/Components/Avatar.css
Normal file
5
src/Sidebar/Components/Avatar.css
Normal file
|
@ -0,0 +1,5 @@
|
|||
.avatar {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
justify-content:center;
|
||||
}
|
9
src/Sidebar/Components/Avatar.tsx
Normal file
9
src/Sidebar/Components/Avatar.tsx
Normal file
|
@ -0,0 +1,9 @@
|
|||
import "./Avatar.css";
|
||||
import placeholderImage from "./placeholder.jpg";
|
||||
export const Avatar = () => {
|
||||
return (
|
||||
<div className="avatar">
|
||||
<img src={placeholderImage} width="75px"></img>
|
||||
</div>
|
||||
);
|
||||
};
|
50
src/Sidebar/Components/SidebarMenu.tsx
Normal file
50
src/Sidebar/Components/SidebarMenu.tsx
Normal file
|
@ -0,0 +1,50 @@
|
|||
import "../Sidebar.css";
|
||||
import { Avatar } from "./Avatar";
|
||||
export const SidebarMenuItem = ({
|
||||
text,
|
||||
href,
|
||||
handler,
|
||||
}: {
|
||||
text: string;
|
||||
href?: string;
|
||||
handler?: () => void;
|
||||
}) => {
|
||||
return (
|
||||
<div
|
||||
className="sidebar-menu-item"
|
||||
onClick={
|
||||
handler
|
||||
? () => {
|
||||
handler();
|
||||
}
|
||||
: () => {}
|
||||
}
|
||||
>
|
||||
<li>
|
||||
<span>
|
||||
<i>{href ? <a href={href}>{text}</a> : text}</i>
|
||||
</span>
|
||||
</li>
|
||||
<hr></hr>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
export const SidebarMenu = ({
|
||||
exitHandler,
|
||||
}: {
|
||||
exitHandler: (enabled: boolean) => void;
|
||||
}) => {
|
||||
return (
|
||||
<div className="sidebar-menu">
|
||||
<Avatar></Avatar>
|
||||
<SidebarMenuItem text="My Account"></SidebarMenuItem>
|
||||
<SidebarMenuItem text="Personalisation"></SidebarMenuItem>
|
||||
<SidebarMenuItem text="Language"></SidebarMenuItem>
|
||||
<SidebarMenuItem text="Server Configuration"></SidebarMenuItem>
|
||||
<SidebarMenuItem
|
||||
text="Return to homepage"
|
||||
handler={() => exitHandler(false)}
|
||||
></SidebarMenuItem>
|
||||
</div>
|
||||
);
|
||||
};
|
BIN
src/Sidebar/Components/placeholder.jpg
Normal file
BIN
src/Sidebar/Components/placeholder.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 17 KiB |
47
src/Sidebar/Sidebar.css
Normal file
47
src/Sidebar/Sidebar.css
Normal file
|
@ -0,0 +1,47 @@
|
|||
|
||||
.sidebar {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
background-color: rgba(0, 0, 0, 0.5);
|
||||
animation: fadeIn, 5s;
|
||||
z-index: 10;
|
||||
}
|
||||
.sidebar-menu-item {
|
||||
margin: 5px;
|
||||
transition: all 0.5s;
|
||||
}
|
||||
|
||||
.sidebar-menu-item:hover {
|
||||
background-color: rgba(0, 0, 0, 0.2);
|
||||
box-shadow: 5px 5px rgba(0, 0, 0, 0.5);
|
||||
}
|
||||
.sidebar-content {
|
||||
background-color: white;
|
||||
width: max(15%, 200px);
|
||||
height: 100%;
|
||||
box-shadow: 10px 10px rgba(0, 0, 0, 0.5);
|
||||
}
|
||||
@keyframes fadeIn {
|
||||
0% {background-color: transparent;}
|
||||
100% {background-color: rgba(0, 0, 0, 0.5);;}
|
||||
}
|
||||
@-moz-keyframes fadeIn {
|
||||
0% {background-color: transparent;}
|
||||
100% {background-color: rgba(0, 0, 0, 0.5);;}
|
||||
}
|
||||
@-webkit-keyframes fadeIn {
|
||||
0% {background-color: transparent;}
|
||||
100% {background-color: rgba(0, 0, 0, 0.5);;}
|
||||
}
|
||||
|
||||
@-o-keyframes fadeIn {
|
||||
0% {background-color: transparent;}
|
||||
100% {background-color: rgba(0, 0, 0, 0.5);;}
|
||||
}
|
||||
@-ms-keyframes fadeIn {
|
||||
0% {background-color: transparent;}
|
||||
100% {background-color: rgba(0, 0, 0, 0.5);;}
|
||||
}
|
24
src/Sidebar/Sidebar.tsx
Normal file
24
src/Sidebar/Sidebar.tsx
Normal file
|
@ -0,0 +1,24 @@
|
|||
import { useState } from "react";
|
||||
import "./Sidebar.css";
|
||||
import { SidebarMenu } from "./Components/SidebarMenu";
|
||||
export const Sidebar = ({
|
||||
isEnabled,
|
||||
setEnable,
|
||||
}: {
|
||||
isEnabled: boolean;
|
||||
setEnable: (enabled: boolean) => void;
|
||||
}) => {
|
||||
return isEnabled ? (
|
||||
<div className="sidebar">
|
||||
<div className="sidebar-content">
|
||||
<SidebarMenu
|
||||
exitHandler={(value) => {
|
||||
setEnable(value);
|
||||
}}
|
||||
></SidebarMenu>
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<></>
|
||||
);
|
||||
};
|
10
src/Topbar/Topbar.css
Normal file
10
src/Topbar/Topbar.css
Normal file
|
@ -0,0 +1,10 @@
|
|||
.topbar {
|
||||
width: 100vw;
|
||||
height: 10%;
|
||||
z-index: 5;
|
||||
display: flex;
|
||||
justify-content: baseline;
|
||||
}
|
||||
.topbar-span .children {
|
||||
margin-left: 10px;
|
||||
}
|
33
src/Topbar/Topbar.tsx
Normal file
33
src/Topbar/Topbar.tsx
Normal file
|
@ -0,0 +1,33 @@
|
|||
import "./Topbar.css";
|
||||
import strings from "../Intl/strings.json";
|
||||
import { useContext } from "react";
|
||||
import { LangContext } from "../context";
|
||||
import menu from "./menu.png";
|
||||
export const Topbar = ({
|
||||
setSidebarEnable,
|
||||
}: {
|
||||
setSidebarEnable: (enabled: boolean) => void;
|
||||
}) => {
|
||||
const lang = useContext(LangContext);
|
||||
return (
|
||||
<div className="topbar">
|
||||
<img
|
||||
onClick={() => {
|
||||
setSidebarEnable(true);
|
||||
}}
|
||||
src={menu}
|
||||
width="100px"
|
||||
height="100px"
|
||||
alt="Open Selection Menu"
|
||||
></img>
|
||||
<span className="topbar-span">
|
||||
<h1 className="topbar-span children">
|
||||
{strings[lang].homepage.title}
|
||||
</h1>
|
||||
{/* <p className="topbar-span children">
|
||||
{strings[lang].homepage.description}
|
||||
</p> */}
|
||||
</span>
|
||||
</div>
|
||||
);
|
||||
};
|
BIN
src/Topbar/menu.png
Normal file
BIN
src/Topbar/menu.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 10 KiB |
|
@ -42,5 +42,6 @@ export type Message = {
|
|||
content: string;
|
||||
timeMillis: number;
|
||||
};
|
||||
// Type gymnastics to provide dynamic ESLint support
|
||||
export const acceptedLangs = ["en_US", "zh_TW", "el_GR", "ar_SA"] as const;
|
||||
export type LangType = (typeof acceptedLangs)[number];
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue