This commit is contained in:
metacryst
2026-01-04 07:58:23 -06:00
parent b50468eb5a
commit 6a435ac11a
122 changed files with 13995 additions and 19 deletions

View 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")

View 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")

View 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)

View 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)

View 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)

View 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)

View 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")

View 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)