mirror of
https://github.com/ragestudio/comty.git
synced 2025-06-10 02:54:15 +00:00
implement QuickNav
This commit is contained in:
parent
42a03c5f87
commit
f71213da8d
@ -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>
|
||||
</>
|
||||
}
|
||||
}
|
@ -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;
|
||||
|
Loading…
x
Reference in New Issue
Block a user