mirror of
https://github.com/ragestudio/comty.git
synced 2025-06-10 19:14:16 +00:00
implement QuickNav
This commit is contained in:
parent
42a03c5f87
commit
f71213da8d
@ -83,6 +83,75 @@ const AccountButton = (props) => {
|
|||||||
</div>
|
</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) => {
|
export default (props) => {
|
||||||
return <WithPlayerContext>
|
return <WithPlayerContext>
|
||||||
<BottomBar
|
<BottomBar
|
||||||
@ -99,6 +168,7 @@ export class BottomBar extends React.Component {
|
|||||||
show: true,
|
show: true,
|
||||||
visible: true,
|
visible: true,
|
||||||
render: null,
|
render: null,
|
||||||
|
quickNavVisible: false
|
||||||
}
|
}
|
||||||
|
|
||||||
busEvents = {
|
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() {
|
render() {
|
||||||
if (this.state.render) {
|
if (this.state.render) {
|
||||||
return <div className="bottomBar">
|
return <div className="bottomBar">
|
||||||
@ -173,63 +332,75 @@ export class BottomBar extends React.Component {
|
|||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
return <Motion style={{ y: spring(this.state.show ? 0 : 300) }}>
|
return <>
|
||||||
{({ y }) => <div
|
<QuickNavMenu
|
||||||
className="bottomBar"
|
visible={this.state.quickNavVisible}
|
||||||
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>
|
|
||||||
|
|
||||||
{
|
<Motion style={{ y: spring(this.state.show ? 0 : 300) }}>
|
||||||
this.context.currentManifest && <div
|
{({ y }) => <div
|
||||||
className="item"
|
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
|
<div className="icon">
|
||||||
manifest={this.context.currentManifest}
|
{createIconRender("PlusCircle")}
|
||||||
playback={this.context.playbackStatus}
|
</div>
|
||||||
colorAnalysis={this.context.coverColorAnalysis}
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
}
|
|
||||||
|
|
||||||
<div
|
{
|
||||||
key="navigator"
|
this.context.currentManifest && <div
|
||||||
id="navigator"
|
className="item"
|
||||||
className="item"
|
>
|
||||||
onClick={() => app.setLocation("/")}
|
<PlayerButton
|
||||||
>
|
manifest={this.context.currentManifest}
|
||||||
<div className="icon">
|
playback={this.context.playbackStatus}
|
||||||
{createIconRender("Home")}
|
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>
|
||||||
|
|
||||||
|
<div
|
||||||
|
key="searcher"
|
||||||
|
id="searcher"
|
||||||
|
className="item"
|
||||||
|
onClick={app.controls.openSearcher}
|
||||||
|
>
|
||||||
|
<div className="icon">
|
||||||
|
{createIconRender("Search")}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<AccountButton />
|
||||||
</div>
|
</div>
|
||||||
|
</div>}
|
||||||
<div
|
</Motion>
|
||||||
key="searcher"
|
</>
|
||||||
id="searcher"
|
|
||||||
className="item"
|
|
||||||
onClick={app.controls.openSearcher}
|
|
||||||
>
|
|
||||||
<div className="icon">
|
|
||||||
{createIconRender("Search")}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<AccountButton />
|
|
||||||
</div>
|
|
||||||
</div>}
|
|
||||||
</Motion>
|
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,6 +1,114 @@
|
|||||||
@import "theme/vars.less";
|
@import "theme/vars.less";
|
||||||
@import "theme/animations.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 {
|
.player_btn {
|
||||||
color: var(--color);
|
color: var(--color);
|
||||||
|
|
||||||
@ -23,6 +131,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.bottomBar {
|
.bottomBar {
|
||||||
|
position: absolute;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
|
|
||||||
@ -31,6 +140,8 @@
|
|||||||
|
|
||||||
position: relative;
|
position: relative;
|
||||||
|
|
||||||
|
z-index: 550;
|
||||||
|
|
||||||
left: 0;
|
left: 0;
|
||||||
bottom: 0;
|
bottom: 0;
|
||||||
|
|
||||||
@ -46,7 +157,7 @@
|
|||||||
background-color: var(--background-color-accent);
|
background-color: var(--background-color-accent);
|
||||||
border-radius: 12px 12px 0 0;
|
border-radius: 12px 12px 0 0;
|
||||||
|
|
||||||
box-shadow: @card-shadow;
|
box-shadow: @card-shadow-top;
|
||||||
|
|
||||||
.items {
|
.items {
|
||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user