init
This commit is contained in:
133
ui/desktop/components/AppMenu.js
Normal file
133
ui/desktop/components/AppMenu.js
Normal file
@@ -0,0 +1,133 @@
|
||||
css(`
|
||||
app-menu {
|
||||
color: var(--tan);
|
||||
transform: translateX(-50%);
|
||||
transition: transform .3s;
|
||||
display: flex; gap: 2em; position: fixed; left: 50vw; bottom: 2em;
|
||||
}
|
||||
|
||||
app-menu.minimized {
|
||||
color: var(--accent);
|
||||
transform: translate(-50%, 65%);
|
||||
border: 0.2px solid var(--accent);
|
||||
padding-top: 0.5em;
|
||||
padding-left: 2em;
|
||||
padding-right: 2em;
|
||||
padding-bottom: 4em;
|
||||
bottom: 1em;
|
||||
border-radius: 12px;
|
||||
}
|
||||
|
||||
app-menu p {
|
||||
cursor: default;
|
||||
transition: transform .3s, text-decoration .3s;
|
||||
padding: 0.5em;
|
||||
border-radius: 5px;
|
||||
text-underline-offset: 5px;
|
||||
}
|
||||
app-menu p:hover {
|
||||
text-decoration: underline;
|
||||
transform: translateY(-5%)
|
||||
}
|
||||
app-menu p.touched {
|
||||
text-decoration: underline;
|
||||
transform: translateY(0%)
|
||||
}
|
||||
app-menu p.selected {
|
||||
text-decoration: underline;
|
||||
transform: translateY(-10%)
|
||||
}
|
||||
|
||||
#divider.minimized {
|
||||
display: none;
|
||||
}
|
||||
`)
|
||||
|
||||
register(
|
||||
|
||||
class AppMenu extends Shadow {
|
||||
selected;
|
||||
|
||||
constructor(selected) {
|
||||
super()
|
||||
this.selected = selected
|
||||
}
|
||||
|
||||
render() {
|
||||
VStack(() => {
|
||||
HStack(() => {
|
||||
p("Forum")
|
||||
p("Messages")
|
||||
p("Market")
|
||||
p("Jobs")
|
||||
})
|
||||
.justifyContent("center")
|
||||
.gap(1.5, em)
|
||||
.paddingRight(2, em)
|
||||
|
||||
img("/_/images/divider.svg", "40vw")
|
||||
.attr({
|
||||
"id": "divider",
|
||||
})
|
||||
})
|
||||
.gap(0.5, em)
|
||||
.onNavigate(() => {
|
||||
if(window.location.pathname === "/") {
|
||||
this.styleMaximized()
|
||||
$("app-window").close()
|
||||
} else {
|
||||
this.styleMinimized()
|
||||
$("app-window").open(this.selected)
|
||||
}
|
||||
})
|
||||
.onAppear(() => {
|
||||
Array.from(this.querySelectorAll("p")).forEach((el) => {
|
||||
el.addEventListener("mousedown", (e) => {
|
||||
el.classList.add("touched")
|
||||
})
|
||||
})
|
||||
window.addEventListener("mouseup", (e) => {
|
||||
let target = e.target
|
||||
if(!target.matches("app-menu p")) {
|
||||
return
|
||||
}
|
||||
|
||||
target.classList.remove("touched")
|
||||
|
||||
if(target.classList.contains("selected")) {
|
||||
this.selected = ""
|
||||
window.navigateTo("/")
|
||||
} else {
|
||||
this.selected = target.innerText
|
||||
window.navigateTo("/app/" + target.innerText.toLowerCase())
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
if(this.selected) {
|
||||
this.styleMinimized()
|
||||
}
|
||||
}
|
||||
|
||||
styleMaximized() {
|
||||
$$("app-menu p").forEach((el) => {
|
||||
el.classList.remove("selected")
|
||||
})
|
||||
this.classList.remove("minimized")
|
||||
$("#divider").style.display = ""
|
||||
}
|
||||
|
||||
styleMinimized() {
|
||||
$$("app-menu p").forEach((el) => {
|
||||
if(el.innerText !== this.selected) {
|
||||
el.classList.remove("selected")
|
||||
} else {
|
||||
el.classList.add("selected")
|
||||
}
|
||||
})
|
||||
this.classList.add("minimized")
|
||||
$("#divider").style.display = "none"
|
||||
}
|
||||
}
|
||||
|
||||
, "app-menu")
|
||||
54
ui/desktop/components/AppWindow.js
Normal file
54
ui/desktop/components/AppWindow.js
Normal file
@@ -0,0 +1,54 @@
|
||||
import "../apps/Forum/Forum.js"
|
||||
import "../apps/Tasks/Tasks.js"
|
||||
import "../apps/Messages/Messages.js"
|
||||
import "../apps/Market/Market.js"
|
||||
import "../apps/Jobs/Jobs.js"
|
||||
|
||||
class AppWindow extends Shadow {
|
||||
app;
|
||||
|
||||
constructor(app) {
|
||||
super()
|
||||
this.app = app
|
||||
}
|
||||
|
||||
render() {
|
||||
ZStack(() => {
|
||||
switch(this.app) {
|
||||
case "Forum":
|
||||
Forum()
|
||||
break;
|
||||
case "Messages":
|
||||
Messages()
|
||||
break;
|
||||
case "Market":
|
||||
Market()
|
||||
break;
|
||||
case "Jobs":
|
||||
Jobs()
|
||||
break;
|
||||
}
|
||||
})
|
||||
.position("fixed")
|
||||
.display(this.app ? 'block' : 'none')
|
||||
.width(100, "vw")
|
||||
.height(100, "vh")
|
||||
.background("#591d10")
|
||||
.x(0)
|
||||
.y(0)
|
||||
// .backgroundImage("/_/images/fabric.png")
|
||||
// .backgroundSize("33vw auto")
|
||||
}
|
||||
|
||||
open(app) {
|
||||
this.app = app
|
||||
this.rerender()
|
||||
}
|
||||
|
||||
close() {
|
||||
this.style.display = "none"
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
register(AppWindow, "app-window")
|
||||
91
ui/desktop/components/Home.js
Normal file
91
ui/desktop/components/Home.js
Normal file
@@ -0,0 +1,91 @@
|
||||
import "./AppWindow.js"
|
||||
import "./AppMenu.js"
|
||||
import "./ProfileButton.js"
|
||||
import "./InputBox.js"
|
||||
import "./Sidebar.js"
|
||||
|
||||
class Home extends Shadow {
|
||||
|
||||
render() {
|
||||
ZStack(() => {
|
||||
img("/_/icons/logo.svg", "2.5em")
|
||||
.position("fixed")
|
||||
.left(3, em)
|
||||
.top(3, vh)
|
||||
.zIndex(3)
|
||||
.onClick(() => {
|
||||
window.navigateTo("/")
|
||||
})
|
||||
|
||||
div()
|
||||
.width(100, vw)
|
||||
.height(100, vh)
|
||||
.margin(0)
|
||||
.backgroundImage("/_/images/the_return.webp")
|
||||
.backgroundSize("cover")
|
||||
.backgroundPosition("48% 65%")
|
||||
.backgroundRepeat("no-repeat")
|
||||
|
||||
switch(window.location.pathname) {
|
||||
case "/":
|
||||
AppWindow()
|
||||
AppMenu()
|
||||
break
|
||||
case "/app/jobs":
|
||||
AppWindow("Jobs")
|
||||
AppMenu("Jobs")
|
||||
break;
|
||||
case "/app/messages":
|
||||
AppWindow("Messages")
|
||||
AppMenu("Messages")
|
||||
break;
|
||||
case "/app/market":
|
||||
AppWindow("Market")
|
||||
AppMenu("Market")
|
||||
break;
|
||||
case "/app/forum":
|
||||
AppWindow("Forum")
|
||||
AppMenu("Forum")
|
||||
break;
|
||||
default:
|
||||
throw new Error("Unknown route!")
|
||||
}
|
||||
|
||||
|
||||
HStack(() => {
|
||||
ProfileButton()
|
||||
.zIndex(1)
|
||||
.cursor("default")
|
||||
|
||||
a("/signout", "Sign Out")
|
||||
.background("transparent")
|
||||
.border(window.location.pathname === "/" ? "1px solid var(--tan)" : "0.5px solid #bb7c36")
|
||||
.color(window.location.pathname === "/" ? "var(--tan)" : "var(--accent)")
|
||||
.borderRadius(5, px)
|
||||
.onHover(function (hovering) {
|
||||
if(hovering) {
|
||||
this.style.background = "var(--green)"
|
||||
} else {
|
||||
this.style.background = ""
|
||||
}
|
||||
})
|
||||
.onNavigate(function () {
|
||||
if(window.location.pathname === "/") {
|
||||
this.style.border = "1px solid var(--tan)"
|
||||
this.style.color = "var(--tan)"
|
||||
} else {
|
||||
this.style.border = "0.5px solid #bb7c36"
|
||||
this.style.color = "var(--accent)"
|
||||
}
|
||||
})
|
||||
})
|
||||
.gap(1, em)
|
||||
.xRight(2, em).y(2.3, em)
|
||||
.position("fixed")
|
||||
.alignVertical("center")
|
||||
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
register(Home)
|
||||
54
ui/desktop/components/InputBox.js
Normal file
54
ui/desktop/components/InputBox.js
Normal file
@@ -0,0 +1,54 @@
|
||||
css(`
|
||||
input-box {
|
||||
display: block;
|
||||
width: 60vw;
|
||||
position: fixed;
|
||||
left: 20vw;
|
||||
bottom: 2vw;
|
||||
}
|
||||
|
||||
.input {
|
||||
width: 100%;
|
||||
background-color: var(--accent2);
|
||||
opacity: 0.5;
|
||||
border-radius: 12px;
|
||||
border: none;
|
||||
resize: none;
|
||||
color: var(--orange);
|
||||
padding: 1em;
|
||||
height: 1em;
|
||||
outline: none;
|
||||
transition: opacity .1s, scale .1s
|
||||
}
|
||||
|
||||
.input:focus {
|
||||
opacity: 80%;
|
||||
scale: 1.02
|
||||
}
|
||||
`)
|
||||
|
||||
export default class InputBox extends HTMLElement {
|
||||
hovered = false
|
||||
|
||||
connectedCallback() {
|
||||
this.render()
|
||||
this.addListeners()
|
||||
}
|
||||
|
||||
render() {
|
||||
this.innerHTML = /* html */`
|
||||
<textarea class="input"></textarea>
|
||||
`
|
||||
}
|
||||
|
||||
addListeners() {
|
||||
this.$("textarea").addEventListener("keydown", (e) => {
|
||||
if(e.key === "Enter") {
|
||||
e.preventDefault()
|
||||
e.target.blur()
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
customElements.define("input-box", InputBox)
|
||||
25
ui/desktop/components/LoadingCircle.js
Normal file
25
ui/desktop/components/LoadingCircle.js
Normal file
@@ -0,0 +1,25 @@
|
||||
class LoadingCircle extends Shadow {
|
||||
render() {
|
||||
div()
|
||||
.borderRadius(100, pct)
|
||||
.width(2, em).height(2, em)
|
||||
.x(45, pct).y(50, pct)
|
||||
.center()
|
||||
.backgroundColor("var(--accent")
|
||||
.transition("transform 1.75s ease-in-out")
|
||||
.onAppear(function () {
|
||||
let growing = true;
|
||||
|
||||
setInterval(() => {
|
||||
if (growing) {
|
||||
this.style.transform = "scale(1.5)";
|
||||
} else {
|
||||
this.style.transform = "scale(0.7)";
|
||||
}
|
||||
growing = !growing;
|
||||
}, 750);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
register(LoadingCircle)
|
||||
43
ui/desktop/components/ProfileButton.js
Normal file
43
ui/desktop/components/ProfileButton.js
Normal file
@@ -0,0 +1,43 @@
|
||||
import "./ProfileMenu.js"
|
||||
|
||||
class ProfileButton extends Shadow {
|
||||
|
||||
async render() {
|
||||
ZStack(async () => {
|
||||
img("/_/icons/profile.svg", "1.5em", "1.5em")
|
||||
.backgroundColor("var(--accent)")
|
||||
.padding(0.2, em)
|
||||
.borderRadius(5, px)
|
||||
|
||||
ProfileMenu()
|
||||
|
||||
})
|
||||
.display("block")
|
||||
.onAppear(() => {
|
||||
window.addEventListener("mousedown", (e) => { // bad - adding every time it renders
|
||||
if(!e.target.closest("profilebutton-")) {
|
||||
this.$("profile-menu").style.display = "none"
|
||||
}
|
||||
})
|
||||
})
|
||||
.onHover((hovering, e) => {
|
||||
console.log(hovering)
|
||||
console.log(e.target)
|
||||
if(hovering && !e.target.closest("profile-menu")) {
|
||||
this.$("img").backgroundColor("var(--accent)")
|
||||
this.$("img").style.outline = "1px solid black"
|
||||
} else if(!e.target.closest("profile-menu")) {
|
||||
this.$("img").backgroundColor("")
|
||||
this.$("img").style.outline = ""
|
||||
}
|
||||
})
|
||||
.onClick((done) => {
|
||||
console.log(done)
|
||||
if(done) {
|
||||
this.$("profile-menu").style.display = ""
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
register(ProfileButton)
|
||||
68
ui/desktop/components/ProfileMenu.js
Normal file
68
ui/desktop/components/ProfileMenu.js
Normal file
@@ -0,0 +1,68 @@
|
||||
class ProfileMenu extends Shadow {
|
||||
|
||||
render() {
|
||||
VStack(() => {
|
||||
h2("Profile")
|
||||
|
||||
HStack(() => {
|
||||
p("Email: ")
|
||||
.fontWeight("bold")
|
||||
|
||||
p(window.profile?.email)
|
||||
})
|
||||
.gap(1, em)
|
||||
|
||||
HStack(() => {
|
||||
p("Name: ")
|
||||
.fontWeight("bold")
|
||||
|
||||
p(window.profile?.name)
|
||||
})
|
||||
.gap(1, em)
|
||||
|
||||
p("X")
|
||||
.onClick(() => {
|
||||
this.style.display = "none"
|
||||
})
|
||||
.xRight(2, em).y(1, em)
|
||||
})
|
||||
.paddingLeft(1, em)
|
||||
.color("var(--accent)")
|
||||
.position("fixed")
|
||||
.border("1px solid var(--accent)")
|
||||
.x(50, vw).y(47, vh)
|
||||
.width(70, vw)
|
||||
.height(70, vh)
|
||||
.backgroundColor("black")
|
||||
.center()
|
||||
.display("none")
|
||||
.onAppear(async () => {
|
||||
if(!window.profile) {
|
||||
window.profile = await this.fetchProfile()
|
||||
this.rerender()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
async fetchProfile() {
|
||||
try {
|
||||
const res = await fetch("/profile", {
|
||||
method: "GET",
|
||||
credentials: "include",
|
||||
headers: {
|
||||
"Content-Type": "application/json"
|
||||
}
|
||||
});
|
||||
|
||||
if (!res.ok) throw new Error("Failed to fetch profile");
|
||||
|
||||
const profile = await res.json();
|
||||
console.log(profile);
|
||||
return profile
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
register(ProfileMenu, "profile-menu")
|
||||
39
ui/desktop/components/Sidebar.js
Normal file
39
ui/desktop/components/Sidebar.js
Normal file
@@ -0,0 +1,39 @@
|
||||
css(`
|
||||
side-bar {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
height: 100vh;
|
||||
width: 16vw;
|
||||
border-right: 0.5px solid var(--accent2);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding-top: 13vh;
|
||||
}
|
||||
|
||||
side-bar button {
|
||||
color: var(--darkbrown);
|
||||
margin: 1.5em;
|
||||
background-color: color-mix(in srgb, var(--accent2) 35%, var(--orange) 65%);
|
||||
border: 1px solid var(--orange);
|
||||
border-radius: 12px;
|
||||
padding: 0.5em;
|
||||
font-weight: bold;
|
||||
}
|
||||
`)
|
||||
|
||||
|
||||
class Sidebar extends HTMLElement {
|
||||
connectedCallback() {
|
||||
this.render()
|
||||
}
|
||||
|
||||
render() {
|
||||
this.innerHTML = /* html */ `
|
||||
<span id="title" style="position: absolute; left: 50%; transform: translateX(-50%) " class="link" onclick='window.location.href="/"'>hyperia</span>
|
||||
<button>Main</button>
|
||||
`
|
||||
}
|
||||
}
|
||||
|
||||
customElements.define("side-bar", Sidebar)
|
||||
Reference in New Issue
Block a user