Sidebar fully functional
This commit is contained in:
@@ -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;
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
133
src/Home/Home.js
133
src/Home/Home.js
@@ -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)
|
||||||
@@ -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")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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,7 +41,8 @@ class Sidebar extends Shadow {
|
|||||||
.borderRadius(100, pct)
|
.borderRadius(100, pct)
|
||||||
.background("var(--darkaccent)")
|
.background("var(--darkaccent)")
|
||||||
.alignSelf("center")
|
.alignSelf("center")
|
||||||
.onTap(() => {
|
.onClick((done) => {
|
||||||
|
if(done)
|
||||||
this.openProfile()
|
this.openProfile()
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -50,7 +57,8 @@ 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) => {
|
||||||
|
if(done)
|
||||||
this.openProfile()
|
this.openProfile()
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -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")
|
||||||
|
|||||||
@@ -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()
|
||||||
})
|
})
|
||||||
|
|||||||
14
src/index.js
14
src/index.js
@@ -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()
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user