improve layout for mobile

This commit is contained in:
SrGooglo 2023-06-30 17:12:34 +00:00
parent 82ab99d402
commit 2590d7e3ce
12 changed files with 268 additions and 185 deletions

View File

@ -123,6 +123,9 @@
width: 50px;
height: 50px;
min-width: 50px;
min-height: 50px;
img {
width: 100%;
height: 100%;

View File

@ -28,16 +28,54 @@ const NavMenu = (props) => {
}
const NavMenuMobile = (props) => {
return <div className="__mobile__navmenu_wrapper">
function handleClickItem(item) {
if (item.children && Array.isArray(item.children)) {
return false
}
return props.onClickItem(item.key)
}
return <div
className={classnames(
"__mobile__navmenu_wrapper",
)}
>
{
props.items.map((item) => {
if (!item.disabled && item.children && Array.isArray(item.children)) {
return <antd.Dropdown
trigger={["click"]}
menu={{
items: item.children,
onClick: (item) => {
handleClickItem(item)
}
}}
>
<antd.Button
key={item.key}
className={classnames(
"__mobile__navmenu_item",
item.key === props.activeKey && "active",
)}
type="ghost"
disabled={item.disabled}
>
<div className="icon">
{item.icon}
</div>
</antd.Button>
</antd.Dropdown>
}
return <antd.Button
key={item.key}
className={classnames(
"__mobile__navmenu_item",
item.key === props.activeKey && "active",
)}
onClick={() => props.onClickItem(item.key)}
onClick={() => handleClickItem(item)}
type="ghost"
disabled={item.disabled}
>

View File

@ -23,13 +23,39 @@ export const Panel = (props) => {
export class PagePanelWithNavMenu extends React.Component {
state = {
// if defaultTab is not set, try to get it from query, if not, use the first tab
activeTab: this.props.defaultTab ?? new URLSearchParams(window.location.search).get("type") ?? this.props.tabs[0].key,
renders: [],
}
primaryPanelRef = React.createRef()
interface = {
attachComponent: (id, component, options) => {
const renders = this.state.renders
renders.push({
id: id,
component: component,
options: options,
ref: React.createRef()
})
this.setState({
renders: renders,
})
},
detachComponent: (id) => {
const renders = this.state.renders
const index = renders.findIndex((render) => render.id === id)
renders.splice(index, 1)
}
}
componentDidMount() {
app.layout.page_panels = this.interface
if (app.isMobile) {
app.layout.top_bar.shouldUseTopBarSpacer(false)
} else {
@ -38,6 +64,8 @@ export class PagePanelWithNavMenu extends React.Component {
}
componentWillUnmount() {
delete app.layout.page_panels
if (app.isMobile) {
app.layout.top_bar.shouldUseTopBarSpacer(true)
} else {
@ -163,7 +191,14 @@ export class PagePanelWithNavMenu extends React.Component {
/>
{
Array.isArray(this.props.extraMenuItems) && this.props.extraMenuItems
Array.isArray(this.state.renders) && [
this.state.renders.map((render, index) => {
return React.createElement(render.component, {
...render.options.props,
ref: render.ref
})
})
]
}
</>
},

View File

@ -2,13 +2,10 @@ import React from "react"
import * as antd from "antd"
import { Translation } from "react-i18next"
import WidgetsWrapper from "components/WidgetsWrapper"
import { PagePanelWithNavMenu } from "components/PagePanels"
import { Icons } from "components/Icons"
import { HashtagTrendings, FeaturedEventsAnnouncements, ConnectedFriends } from "components"
import Tabs from "./home/tabs"
export default class Home extends React.Component {

View File

@ -125,6 +125,7 @@ export default (props) => {
})}
icon={<Icons.Plus />}
type="primary"
disabled={app.isMobile}
>
New release
</antd.Button>

View File

@ -114,63 +114,4 @@
}
}
}
}
.music-explorer_search_results {
display: grid;
// if only need 1 column, it will be 1fr
// if need 2 colums, first column will be 1fr, and the second one will be 2fr
grid-template-columns: 1fr 2fr;
// auto generate rows
grid-template-rows: auto;
grid-column-gap: 20px;
grid-row-gap: 20px;
&.one_column {
grid-template-columns: 1fr;
}
&.no_results {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
.music-explorer_search_results_group {
background-color: var(--background-color-accent);
padding: 20px;
border-radius: 8px;
height: fit-content;
gap: 20px;
.explorer_search_results_group_header {
h1 {
margin: 0;
}
}
.music-explorer_search_results_group_list {
display: flex;
flex-direction: column;
gap: 10px;
.playlistItem {
background-color: var(--background-color-primary);
}
.music-track {
background-color: var(--background-color-primary);
}
}
}
}

View File

@ -4,7 +4,7 @@ import classnames from "classnames"
import { Translation } from "react-i18next"
import Searcher from "components/Searcher"
import { ImageViewer, UserPreview } from "components"
import { ImageViewer } from "components"
import { Icons, createIconRender } from "components/Icons"
import { WithPlayerContext } from "contexts/WithPlayerContext"
@ -16,6 +16,18 @@ import MusicTrack from "components/MusicTrack"
import "./index.less"
const MusicNavbar = (props) => {
return <div className="music_navbar">
<Searcher
useUrlQuery
renderResults={false}
model={PlaylistModel.search}
onSearchResult={props.setSearchResults}
onEmpty={() => props.setSearchResults(false)}
/>
</div>
}
const PlaylistsList = (props) => {
const hopNumber = props.hopsPerPage ?? 6
@ -181,52 +193,6 @@ const PlaylistItem = (props) => {
<div className="playlistItem_info_title" onClick={onClick}>
<h1>{playlist.title}</h1>
</div>
{
playlist.publisher && <UserPreview user={playlist.publisher} />
}
</div>
</div>
}
const RecentlyPlayed = (props) => {
return <div className="playlistExplorer_section">
<div className="playlistExplorer_section_header">
<h1>
<Icons.MdReplay />
<Translation>
{(t) => t("Recently Played")}
</Translation>
</h1>
</div>
<div>
<antd.Result
status="warning"
title="Failed to load"
subTitle="We are sorry, but we could not load your playlists. Please try again later."
/>
</div>
</div>
}
const MayLike = (props) => {
return <div className="playlistExplorer_section">
<div className="playlistExplorer_section_header">
<h1>
<Icons.MdRecommend />
<Translation>
{(t) => t("May you like")}
</Translation>
</h1>
</div>
<div>
<antd.Result
status="warning"
title="Failed to load"
subTitle="We are sorry, but we could not load your recomendations. Please try again later."
/>
</div>
</div>
}
@ -328,21 +294,42 @@ const SearchResults = ({
export default (props) => {
const [searchResults, setSearchResults] = React.useState(false)
React.useEffect(() => {
if (app.isMobile) {
app.layout.toggleCenteredContent(true)
}
app.layout.page_panels.attachComponent("music_navbar", MusicNavbar, {
props: {
setSearchResults: setSearchResults
}
})
return () => {
if (app.layout.page_panels) {
app.layout.page_panels.detachComponent("music_navbar")
}
if (app.isMobile) {
app.layout.toggleCenteredContent(false)
}
}
}, [])
return <div
className={classnames(
"musicExplorer",
{
//["search-focused"]: searchFocused,
}
)}
>
<Searcher
useUrlQuery
renderResults={false}
model={PlaylistModel.search}
onSearchResult={setSearchResults}
onEmpty={() => setSearchResults(false)}
/>
{
app.isMobile && <Searcher
useUrlQuery
renderResults={false}
model={PlaylistModel.search}
onSearchResult={setSearchResults}
onEmpty={() => setSearchResults(false)}
/>
}
{
searchResults && <SearchResults
@ -352,8 +339,6 @@ export default (props) => {
{
!searchResults && <div className="feed_main">
<RecentlyPlayed />
<PlaylistsList
headerTitle="From your following artists"
headerIcon={<Icons.MdPerson />}

View File

@ -1,7 +1,23 @@
.music_navbar {
display: flex;
flex-direction: column;
width: 100%;
padding: 20px;
background-color: var(--background-color-accent);
border-radius: 12px;
}
.musicExplorer {
display: flex;
flex-direction: column;
overflow-x: visible;
overflow-y: overlay;
width: 100%;
height: 100%;
@ -24,13 +40,15 @@
gap: 50px;
transition: all 0.2s ease-in-out;
overflow-x: visible;
}
.playlistExplorer_section {
display: flex;
flex-direction: column;
overflow: visible;
overflow-x: visible;
.playlistExplorer_section_header {
display: flex;
@ -58,28 +76,10 @@
}
.playlistExplorer_section_list {
display: grid;
display: flex;
flex-direction: column;
grid-template-columns: repeat(3, 1fr);
grid-template-rows: repeat(2, 1fr);
grid-column-gap: 10px;
grid-row-gap: 10px;
}
// add a media query to change the grid-template-columns when the screen can't fit 3 columns
@media (max-width: 1500px) {
.playlistExplorer_section_list {
grid-template-columns: repeat(2, 1fr);
grid-template-rows: repeat(3, 1fr);
}
}
@media (max-width: 800px) {
.playlistExplorer_section_list {
grid-template-columns: repeat(1, 1fr);
grid-template-rows: repeat(6, 1fr);
}
gap: 10px;
}
}
}
@ -91,22 +91,14 @@
cursor: pointer;
width: 10vw;
height: 10vh;
min-width: 400px;
max-width: 800px;
overflow: visible;
box-sizing: border-box !important;
width: 100%;
height: 140px;
border-radius: 12px;
transition: all 0.2s ease-in-out;
background-color: var(--background-color-accent);
border-radius: 8px;
&.cover-hovering {
.playlistItem_cover {
@ -122,32 +114,16 @@
}
}
.image-wrapper {
width: 10vh;
height: 10vh;
border-radius: 8px;
overflow: hidden;
img {
width: 100%;
height: 100%;
object-fit: cover;
}
}
.playlistItem_cover {
position: relative;
height: 10vh;
height: 140px;
transition: all 0.2s ease-in-out;
img {
width: 100%;
height: 100%;
width: 140px;
height: 140px;
object-fit: cover;
border-radius: 8px;
@ -203,8 +179,6 @@
color: var(--background-color-contrast);
font-family: "Space Grotesk", sans-serif;
overflow: hidden;
&:hover {
text-decoration: underline;
}
@ -214,11 +188,9 @@
overflow: hidden;
text-overflow: ellipsis;
//white-space: nowrap;
margin: 0;
white-space: nowrap;
//calculate the max height of the title using 10vh and 30px for the userPreview and padding
height: 5vh;
margin: 0;
}
}
@ -264,4 +236,93 @@
}
}
}
}
.music-explorer_search_results {
display: grid;
width: 100%;
// if only need 1 column, it will be 1fr
// if need 2 colums, first column will be 1fr, and the second one will be 2fr
grid-template-columns: 1fr 2fr;
// auto generate rows
grid-template-rows: auto;
grid-column-gap: 20px;
grid-row-gap: 20px;
@media screen and (max-width: 1750px) {
display: flex;
flex-direction: column;
align-items: center;
.music-explorer_search_results_group_list {
.playlistItem {
width: 100% !important;
max-width: 100% !important;
}
}
}
&.one_column {
grid-template-columns: 1fr;
}
&.no_results {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
.music-explorer_search_results_group {
background-color: var(--background-color-accent);
padding: 20px;
border-radius: 8px;
height: fit-content;
width: 100%;
gap: 20px;
.explorer_search_results_group_header {
h1 {
margin: 0;
}
}
.music-explorer_search_results_group_list {
display: flex;
flex-direction: column;
gap: 10px;
.playlistItem {
background-color: var(--background-color-primary);
max-width: 300px;
height: 80px;
.playlistItem_cover {
width: 80px;
height: 80px;
img {
height: 80px;
width: 80px;
}
}
}
.music-track {
background-color: var(--background-color-primary);
}
}
}
}

View File

@ -15,6 +15,7 @@ export default () => {
return <PagePanelWithNavMenu
tabs={Tabs}
navMenuHeader={NavMenuHeader}
primaryPanelClassName="full"
useSetQueryType
transition
/>

View File

@ -97,6 +97,12 @@ export default (props) => {
React.useEffect(() => {
loadData()
app.layout.toggleCenteredContent(true)
return () => {
app.layout.toggleCenteredContent(false)
}
}, [])
if (!playlist) {

View File

@ -29,7 +29,7 @@
}
.list {
padding: 30px 10px;
padding: 30px 0;
}
}
}

View File

@ -136,10 +136,25 @@ html {
&.centered-content {
.app_layout {
.content_layout {
width: 50vw !important;
width: 45%;
min-width: 400px;
max-width: 50vw;
max-width: 45%;
margin: auto;
padding-left: 0;
padding-right: 10px;
@media screen and (max-width: 1920px) {
width: 50%;
max-width: 60%;
}
@media screen and (max-width: 1440px) {
width: 80%;
max-width: 80%;
}
}
}
}