implement QuickNav

This commit is contained in:
SrGooglo 2023-06-13 22:54:38 +00:00
parent 42a03c5f87
commit f71213da8d
2 changed files with 335 additions and 53 deletions

View File

@ -83,6 +83,75 @@ const AccountButton = (props) => {
</div>
}
const QuickNavMenuItems = [
{
id: "music",
icon: "MdAlbum",
label: "Music",
location: "/music"
},
{
id: "tv",
icon: "Tv",
label: "Tv",
location: "/tv"
},
{
id: "groups",
icon: "MdGroups",
label: "Groups",
location: "/groups",
disabled: true,
},
{
id: "marketplace",
icon: "Box",
label: "Marketplace",
location: "/marketplace",
disabled: true
},
]
const QuickNavMenu = ({
visible,
}) => {
return <div
className={classnames(
"quick-nav",
{
["active"]: visible
}
)}
>
{
QuickNavMenuItems.map((item, index) => {
return <div
key={index}
className={classnames(
"quick-nav_item",
{
["disabled"]: item.disabled
}
)}
quicknav-item={item.id}
disabled={item.disabled}
>
{
createIconRender(item.icon)
}
<h1>
{
item.label
}
</h1>
</div>
})
}
</div>
}
export default (props) => {
return <WithPlayerContext>
<BottomBar
@ -99,6 +168,7 @@ export class BottomBar extends React.Component {
show: true,
visible: true,
render: null,
quickNavVisible: false
}
busEvents = {
@ -162,6 +232,95 @@ export class BottomBar extends React.Component {
}
}
handleNavTouchStart = (e) => {
this._navTouchStart = setTimeout(() => {
this.setState({ quickNavVisible: true })
app.cores.haptics.vibrate(80)
// remove the timeout
this._navTouchStart = null
}, 400)
}
handleNavTouchEnd = (event) => {
if (this._lastHovered) {
this._lastHovered.classList.remove("hover")
}
if (this._navTouchStart) {
clearTimeout(this._navTouchStart)
this._navTouchStart = null
return false
}
this.setState({ quickNavVisible: false })
// get cords of the touch
const x = event.changedTouches[0].clientX
const y = event.changedTouches[0].clientY
// get the element at the touch
const element = document.elementFromPoint(x, y)
// get the closest element with the attribute
const closest = element.closest(".quick-nav_item")
if (!closest) {
return false
}
const item = QuickNavMenuItems.find((item) => {
return item.id === closest.getAttribute("quicknav-item")
})
if (!item) {
return false
}
if (item.location) {
app.setLocation(item.location)
app.cores.haptics.vibrate([40, 80])
}
}
handleNavTouchMove = (event) => {
// check if the touch is hovering a quicknav item
const x = event.changedTouches[0].clientX
const y = event.changedTouches[0].clientY
// get the element at the touch
const element = document.elementFromPoint(x, y)
// get the closest element with the attribute
const closest = element.closest("[quicknav-item]")
if (!closest) {
if (this._lastHovered) {
this._lastHovered.classList.remove("hover")
}
this._lastHovered = null
return false
}
if (this._lastHovered !== closest) {
if (this._lastHovered) {
this._lastHovered.classList.remove("hover")
}
this._lastHovered = closest
closest.classList.add("hover")
app.cores.haptics.vibrate(40)
}
}
render() {
if (this.state.render) {
return <div className="bottomBar">
@ -173,63 +332,75 @@ export class BottomBar extends React.Component {
return null
}
return <Motion style={{ y: spring(this.state.show ? 0 : 300) }}>
{({ y }) => <div
className="bottomBar"
style={{
WebkitTransform: `translate3d(0, ${y}px, 0)`,
transform: `translate3d(0, ${y}px, 0)`,
}}
>
<div className="items">
<div
key="creator"
id="creator"
className={classnames("item", "primary")}
onClick={() => app.setLocation("/")}
>
<div className="icon">
{createIconRender("PlusCircle")}
</div>
</div>
return <>
<QuickNavMenu
visible={this.state.quickNavVisible}
/>
{
this.context.currentManifest && <div
className="item"
<Motion style={{ y: spring(this.state.show ? 0 : 300) }}>
{({ y }) => <div
className="bottomBar"
style={{
WebkitTransform: `translate3d(0, ${y}px, 0)`,
transform: `translate3d(0, ${y}px, 0)`,
}}
>
<div className="items">
<div
key="creator"
id="creator"
className={classnames("item", "primary")}
onClick={() => app.setLocation("/")}
>
<PlayerButton
manifest={this.context.currentManifest}
playback={this.context.playbackStatus}
colorAnalysis={this.context.coverColorAnalysis}
/>
<div className="icon">
{createIconRender("PlusCircle")}
</div>
</div>
}
<div
key="navigator"
id="navigator"
className="item"
onClick={() => app.setLocation("/")}
>
<div className="icon">
{createIconRender("Home")}
{
this.context.currentManifest && <div
className="item"
>
<PlayerButton
manifest={this.context.currentManifest}
playback={this.context.playbackStatus}
colorAnalysis={this.context.coverColorAnalysis}
/>
</div>
}
<div
key="navigator"
id="navigator"
className="item"
onClick={() => app.setLocation("/")}
onTouchMove={this.handleNavTouchMove}
onTouchStart={this.handleNavTouchStart}
onTouchEnd={this.handleNavTouchEnd}
onTouchCancel={() => {
this.setState({ quickNavVisible: false })
}}
>
<div className="icon">
{createIconRender("Home")}
</div>
</div>
<div
key="searcher"
id="searcher"
className="item"
onClick={app.controls.openSearcher}
>
<div className="icon">
{createIconRender("Search")}
</div>
</div>
<AccountButton />
</div>
<div
key="searcher"
id="searcher"
className="item"
onClick={app.controls.openSearcher}
>
<div className="icon">
{createIconRender("Search")}
</div>
</div>
<AccountButton />
</div>
</div>}
</Motion>
</div>}
</Motion>
</>
}
}

View File

@ -1,6 +1,114 @@
@import "theme/vars.less";
@import "theme/animations.less";
.quick-nav {
position: absolute;
box-sizing: border-box;
bottom: @app_bottomBar_height;
left: 0;
right: 0;
z-index: 500;
display: flex;
flex-direction: row;
align-items: center;
justify-content: space-evenly;
width: 100%;
height: 0;
opacity: 0;
background-color: var(--background-color-accent);
box-shadow: @card-shadow-top;
transform: translateY(10px);
padding-bottom: 20px;
gap: 20px;
pointer-events: none;
transition: all 150ms ease-in-out;
border-radius: 12px 12px 0 0;
&.active {
pointer-events: all;
height: 100px;
opacity: 1;
}
.quick-nav_item {
position: relative;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
z-index: 500;
width: 20vw;
height: 100%;
//padding: 0 30px;
transition: all 150ms ease-in-out;
color: var(--text-color);
h1 {
margin: 0;
font-size: 0px;
transition: all 150ms ease-in-out;
}
svg {
margin: 0 !important;
transition: all 150ms ease-in-out;
font-size: 2rem;
}
&.disabled {
pointer-events: none;
h1 {
font-size: 0px;
}
svg {
font-size: 2rem;
color: var(--disabled-color);
}
color: var(--disabled-color);
}
&.hover {
h1 {
font-size: 1rem;
}
svg {
font-size: 2.2rem;
color: var(--colorPrimary);
}
color: var(--colorPrimary);
}
}
}
.player_btn {
color: var(--color);
@ -23,6 +131,7 @@
}
.bottomBar {
position: absolute;
display: flex;
flex-direction: row;
@ -31,6 +140,8 @@
position: relative;
z-index: 550;
left: 0;
bottom: 0;
@ -46,7 +157,7 @@
background-color: var(--background-color-accent);
border-radius: 12px 12px 0 0;
box-shadow: @card-shadow;
box-shadow: @card-shadow-top;
.items {
display: inline-flex;