Sidebar fully functional

This commit is contained in:
metacryst
2026-03-21 03:10:50 -05:00
parent 1c6f12c210
commit 21b7b0a252
9 changed files with 129 additions and 84 deletions

View File

@@ -130,7 +130,7 @@ class Login extends Shadow {
if (res.ok) { if (res.ok) {
const { token } = await res.json(); const { token } = await res.json();
await Preferences.set({ key: 'auth_token', value: token }); await Preferences.set({ key: 'auth_token', value: token });
global.onLogin(); global.renderHome();
} else { } else {
const { error } = await res.json(); const { error } = await res.json();
this.errorMessage = error; this.errorMessage = error;

View File

@@ -102,7 +102,7 @@ class Signup extends Shadow {
if (res.ok) { if (res.ok) {
const { token } = await res.json(); const { token } = await res.json();
await Preferences.set({ key: 'auth_token', value: token }); await Preferences.set({ key: 'auth_token', value: token });
global.onLogin(); global.renderHome();
} else { } else {
const { error } = await res.json(); const { error } = await res.json();
this.errorMessage = error; this.errorMessage = error;

View File

@@ -2,31 +2,45 @@ import "../components/Sidebar.js"
import "../components/AppMenu.js" import "../components/AppMenu.js"
import "../components/AppWindowContainer.js" import "../components/AppWindowContainer.js"
css(`
#homeContainer {
-webkit-user-select: none;
}
`)
/*
Sidebar Functionality Checklist:
- Open on Top left network logo touch (WITH transition)
- Follow finger on swipe from left side of the screen
- Open if finger swipw travels far enough to the right (WITH velocity-based transition)
- Re-close if not opened enough of the way (WITH transition)
- Close on touch of home contents (WITH transition)
- Follow finger on swipe beginning near or anywhere on right of divider between sidebar and home contents
- Close if finger swipe travels far enough to the left (WITH velocity-based transition)
- Re-open if not closed enough of the way (WITH transition)
*/
class Home extends Shadow { class Home extends Shadow {
dragStartX = null dragStartX = null
sidebarOpen = false sidebarOpen = false
SIDEBAR_FULL_OPEN = (window.outerWidth * 5) / 6
SIDEBAR_START_THRESHOLD = window.outerWidth / 10
SIDEBAR_CLOSE_DECISION = (window.outerWidth * 2) / 3
SIDEBAR_OPEN_DECISION = (window.outerWidth / 3)
constructor() {
super()
}
render() { render() {
ZStack(() => { ZStack(() => {
Sidebar() Sidebar(this.SIDEBAR_FULL_OPEN)
ZStack(() => { ZStack(() => {
// img("/_/icons/hamburger.svg", "3em")
// .position("absolute")
// .zIndex(2)
// .left(1.5, em)
// .top(1, em)
// .onTouch(function (start) {
// if(start) {
// this.style.scale = "0.8"
// } else if(start === false) {
// this.style.scale = ""
// $("sidebar-").toggle()
// }
// })
VStack(() => { VStack(() => {
AppWindowContainer() AppWindowContainer()
@@ -35,106 +49,105 @@ class Home extends Shadow {
.height(100, pct) .height(100, pct)
.minHeight(0) .minHeight(0)
}) })
.left(0, px)
.backgroundColor("var(--main)") .backgroundColor("var(--main)")
.overflowX("hidden") .overflowX("hidden")
.height(window.visualViewport.height - 20, px) .height(window.visualViewport.height, px)
.position("fixed") .position("fixed")
.top(20, px)
.borderLeft("1px solid var(--accent)") .borderLeft("1px solid var(--accent)")
.onTouch((start, e) => { .onTouch((start, e) => {
// console.log(e)
if(this.sidebarOpen) { if(this.sidebarOpen) {
this.sidebarOpenDrag(start, e) this.sidebarOpenTouch(start, e)
} else { } else {
this.sidebarClosedDrag(start, e) this.sidebarClosedTouch(start, e)
} }
}) })
.attr({ id: "homeContainer" }) .attr({ id: "homeContainer" })
}) })
.userSelect("none")
} }
openSidebar() { openSidebar(duration = 200) {
const home = this.$("#homeContainer"); const home = this.$("#homeContainer");
const oneThird = window.outerWidth / 3; home.style.transition = `left ${duration}ms`;
home.style.transition = "left .2s"; home.style.left = `${this.SIDEBAR_FULL_OPEN}px`;
home.style.left = `${oneThird * 2}px`;
this.sidebarOpen = true; this.sidebarOpen = true;
this.dragStartX = null
setTimeout(() => home.style.transition = "", duration);
} }
closeSidebar() { closeSidebar(duration = 200) {
const home = this.$("#homeContainer"); const home = this.$("#homeContainer");
home.style.transition = "left .2s"; home.style.transition = `left ${duration}ms`;
home.style.left = "0px"; home.style.left = "0px";
this.sidebarOpen = false; this.sidebarOpen = false;
this.dragStartX = null
setTimeout(() => home.style.transition = "", duration);
} }
sidebarOpenDrag(start, e) { sidebarOpenTouch(start, e) {
// console.log("sidebaropendrag")
if(start) { if(start) {
let fiveSixths = (window.outerWidth * 5) / 6
let amount = e.targetTouches[0].clientX let amount = e.targetTouches[0].clientX
if(amount < fiveSixths) { if(amount > (this.SIDEBAR_FULL_OPEN - this.SIDEBAR_START_THRESHOLD)) {
this.$("#homeContainer").dragStartX = e.touches[0].clientX this.dragStartX = e.touches[0].clientX
this.dragStartTime = Date.now(); // ⬅ track start time
document.addEventListener("touchmove", this.moveSidebar) document.addEventListener("touchmove", this.moveSidebar)
} }
} else { } else {
if(!this.$("#homeContainer").dragStartX) return; if(!this.dragStartX) return;
let oneThird = window.outerWidth / 3
let endX = e.changedTouches[0].clientX let endX = e.changedTouches[0].clientX
if(endX < oneThird) { let duration = this.getDuration(this.dragStartX, endX);
this.$("#homeContainer").style.transition = "left .2s" if(Math.abs(this.dragStartX - endX) < 5) { // 2 conditions are separated so this one doesn't close instantly
this.$("#homeContainer").style.left = `0px` this.closeSidebar()
this.sidebarOpen = false } else if(endX < this.SIDEBAR_CLOSE_DECISION) {
this.closeSidebar(duration)
} else { } else {
this.$("#homeContainer").style.transition = "left .2s" this.openSidebar(duration)
this.$("#homeContainer").style.left = `${oneThird * 2}px`
this.sidebarOpen = true
} }
this.$("#homeContainer").style.transition = ""
this.$("#homeContainer").dragStartX = null
document.removeEventListener("touchmove", this.moveSidebar) document.removeEventListener("touchmove", this.moveSidebar)
} }
} }
sidebarClosedDrag(start, e) { sidebarClosedTouch(start, e) {
// console.log(e)
if(start) { if(start) {
let oneTenth = window.outerWidth / 10
let amount = e.targetTouches[0].clientX let amount = e.targetTouches[0].clientX
if(amount < oneTenth) { if(amount < this.SIDEBAR_START_THRESHOLD) {
this.$("#homeContainer").dragStartX = e.touches[0].clientX this.dragStartX = e.touches[0].clientX
this.dragStartTime = Date.now();
document.addEventListener("touchmove", this.moveSidebar) document.addEventListener("touchmove", this.moveSidebar)
} }
} else { } else {
if(!this.$("#homeContainer").dragStartX) return; if(!this.dragStartX) return;
let oneThird = window.outerWidth / 3
let endX = e.changedTouches[0].clientX let endX = e.changedTouches[0].clientX
if(endX > oneThird) { let duration = this.getDuration(this.dragStartX, endX);
this.$("#homeContainer").style.transition = "left .2s" if(endX > this.SIDEBAR_OPEN_DECISION) {
this.$("#homeContainer").style.left = `${oneThird * 2}px` this.openSidebar(duration)
this.sidebarOpen = true
} else { } else {
this.sidebarOpen = false this.closeSidebar(duration)
this.$("#homeContainer").style.left = "0px"
} }
this.$("#homeContainer").style.transition = ""
this.$("#homeContainer").dragStartX = null
document.removeEventListener("touchmove", this.moveSidebar) document.removeEventListener("touchmove", this.moveSidebar)
} }
} }
moveSidebar = (e) => { moveSidebar = (e) => {
let twoThirds = window.outerWidth * .66
let amount = e.targetTouches[0].clientX let amount = e.targetTouches[0].clientX
if(e.targetTouches[0] && amount < twoThirds) { if(e.targetTouches[0] && amount < this.SIDEBAR_FULL_OPEN) {
console.log("dragtivated: ", amount, twoThirds)
this.$("#homeContainer").style.left = `${amount}px` this.$("#homeContainer").style.left = `${amount}px`
} }
} }
getDuration(startX, endX) {
const distance = Math.abs(endX - startX);
const elapsed = Date.now() - this.dragStartTime;
const velocity = distance / elapsed; // px per ms
const duration = Math.round(distance / velocity); // time to cover remaining distance
return Math.min(Math.max(duration, 50), 200); // clamp between 50ms and 200ms
}
} }
register(Home) register(Home)

View File

@@ -26,14 +26,39 @@ class Profile extends Shadow {
render() { render() {
ZStack(() => { ZStack(() => {
p("< Back") div("")
.color("var(--darkred)") .width(3, rem)
.fontSize(1.2, em) .height(3, rem)
.borderRadius(50, pct)
.border("1.5px solid var(--divider)")
.position("absolute") .position("absolute")
.fontFamily("Arial") .fontSize(2, em)
.transform("rotate(180deg)")
.top(3, rem) .top(3, rem)
.left(2, rem) .left(2, rem)
.zIndex(1001) .zIndex(1001)
.display("flex")
.alignItems("center")
.justifyContent("center")
.transition("scale .2s")
.state("touched", function (touched) {
if(touched) {
this.scale("1.5")
this.color("var(--darkaccent)")
this.backgroundColor("var(--divider)")
} else {
this.scale("")
this.color("var(--divider)")
this.backgroundColor("var(--darkaccent)")
}
})
.onTouch(function (start) {
if(start) {
this.attr({touched: "true"})
} else {
this.attr({touched: ""})
}
})
.onClick((done) => { .onClick((done) => {
if(done) if(done)
$("appwindowcontainer-").closeProfile() $("appwindowcontainer-").closeProfile()
@@ -142,7 +167,7 @@ class Profile extends Shadow {
.position("fixed") .position("fixed")
.top(100, vh) .top(100, vh)
.zIndex(5) .zIndex(5)
.transition("top .3s") .transition("top .4s ")
.pointerEvents("none") .pointerEvents("none")
} }

View File

@@ -55,7 +55,7 @@
--sidebar: #240609; --sidebar: #240609;
--divider: #523636; --divider: #523636;
--darktext: #62473E; --darktext: #62473E;
--headertext: #ffd8bbe2; --headertext: #ffd8bb;
--darkred: #6b2c1d; --darkred: #6b2c1d;
--searchbackground: #260F0C; --searchbackground: #260F0C;
--loginButton: var(--darkaccent); --loginButton: var(--darkaccent);

View File

@@ -1,6 +1,12 @@
import util from "../util" import util from "../util"
class Sidebar extends Shadow { class Sidebar extends Shadow {
SIDEBAR_WIDTH
constructor(width) {
super()
this.SIDEBAR_WIDTH = width
}
SidebarItem(text) { SidebarItem(text) {
return p(text) return p(text)
@@ -35,8 +41,9 @@ class Sidebar extends Shadow {
.borderRadius(100, pct) .borderRadius(100, pct)
.background("var(--darkaccent)") .background("var(--darkaccent)")
.alignSelf("center") .alignSelf("center")
.onTap(() => { .onClick((done) => {
this.openProfile() if(done)
this.openProfile()
}) })
h2(global.profile.first_name + " " + global.profile.last_name) h2(global.profile.first_name + " " + global.profile.last_name)
@@ -50,8 +57,9 @@ class Sidebar extends Shadow {
.wordBreak("break-word") .wordBreak("break-word")
.width(100, pct) .width(100, pct)
.borderBottom("2px solid var(--divider)") .borderBottom("2px solid var(--divider)")
.onTap(() => { .onClick((done) => {
this.openProfile() if(done)
this.openProfile()
}) })
this.SidebarItem("Logout") this.SidebarItem("Logout")
@@ -63,7 +71,7 @@ class Sidebar extends Shadow {
.top(-5, vh) .top(-5, vh)
.minWidth(0) .minWidth(0)
.boxSizing("border-box") .boxSizing("border-box")
.width((window.outerWidth / 3) * 2, px) .width(this.SIDEBAR_WIDTH, px)
.borderLeft("1px solid var(--divider)") .borderLeft("1px solid var(--divider)")
.color("var(--text)") .color("var(--text)")
.position("fixed") .position("fixed")

View File

@@ -39,7 +39,7 @@ class TopBar extends Shadow {
.paddingBottom(1.5, em) .paddingBottom(1.5, em)
.verticalAlign("center") .verticalAlign("center")
.gap(0.5, em) .gap(0.5, em)
.marginTop(3, em) .marginTop(4, em)
.onNavigate(() => { .onNavigate(() => {
this.$("p").innerText = global.currentApp() this.$("p").innerText = global.currentApp()
}) })

View File

@@ -140,14 +140,8 @@ let Global = class {
} }
} }
async onLogin() { async renderHome() {
if(!this.profile) { location.reload()
await this.getProfile()
}
await this.Socket.init()
await this.onNavigate()
Home()
// navigateTo("/")
} }
async onLogout() { async onLogout() {
@@ -169,7 +163,9 @@ let Global = class {
} else if(status === 500) { } else if(status === 500) {
ConnectionError() ConnectionError()
} else { } else {
this.onLogin() await this.Socket.init()
await this.onNavigate()
Home()
} }
}) })
} }

View File

@@ -1,6 +1,7 @@
/* /*
Sam Russell Sam Russell
Captured Sun Captured Sun
2.21.26 - Making state() be called on initial definition
2.20.26 - Adding state() 2.20.26 - Adding state()
2.19.26 - Adding dynamicText() 2.19.26 - Adding dynamicText()
2.16.26 - Adding event objects to the onTouch callbacks 2.16.26 - Adding event objects to the onTouch callbacks
@@ -704,6 +705,8 @@ HTMLElement.prototype.state = function(attr, cb) {
}) })
.observe(this, { attributes: true, attributeFilter: [attr] }); .observe(this, { attributes: true, attributeFilter: [attr] });
const value = this.getAttribute(attr);
cb.call(this, value)
return this return this
} }