improve layout for mobile & use of top-bar

This commit is contained in:
SrGooglo 2023-06-26 23:08:44 +00:00
parent 11f978cca9
commit c52834c0c8
25 changed files with 761 additions and 414 deletions

View File

@ -17,6 +17,7 @@
"backgroundPosition": "center",
"backgroundRepeat": "no-repeat",
"backgroundAttachment": "fixed",
"top-bar-height": "52px",
"compact-mode": false
},
"defaultVariant": "dark",

View File

@ -1,7 +1,7 @@
export default [
{
path: "/login",
useLayout: "none",
useLayout: "minimal",
public: true
},
{
@ -46,12 +46,12 @@ export default [
},
{
path: "/landing/*",
useLayout: "blank",
useLayout: "minimal",
public: true
},
{
path: "/nfc/*",
useLayout: "blank",
useLayout: "minimal",
public: true
}
]

View File

@ -1,3 +1,4 @@
export { default as TopBar } from "./topBar"
export { default as BottomBar } from "./bottomBar"
export { default as Drawer } from "./drawer"
export { default as Sidebar } from "./sidebar"

View File

@ -0,0 +1,157 @@
import React from "react"
import classnames from "classnames"
import { Motion, spring } from "react-motion"
import "./index.less"
const useLayoutInterface = (namespace, ctx) => {
React.useEffect(() => {
if (app.layout["namespace"] === "object") {
throw new Error(`Layout namespace [${namespace}] already exists`)
}
app.layout[namespace] = ctx
}, [])
return app.layout[namespace]
}
const useDefaultVisibility = (defaultValue) => {
const [visible, setVisible] = React.useState(defaultValue ?? false)
React.useEffect(() => {
setTimeout(() => {
setVisible(true)
}, 10)
}, [])
return [visible, setVisible]
}
export const UseTopBar = (props) => {
app.layout.top_bar.render(
<React.Fragment>
{props.children}
</React.Fragment>,
props.options)
React.useEffect(() => {
return () => {
app.layout.top_bar.renderDefault()
}
}, [])
return null
}
export default (props) => {
const [visible, setVisible] = useDefaultVisibility()
const [shouldUseTopBarSpacer, setShouldUseTopBarSpacer] = React.useState(true)
const [render, setRender] = React.useState(null)
useLayoutInterface("top_bar", {
toggleVisibility: (to) => {
setVisible((prev) => {
if (typeof to === undefined) {
to = !prev
}
return to
})
},
render: (component, options) => {
handleUpdateRender(component, options)
},
renderDefault: () => {
setRender(null)
},
shouldUseTopBarSpacer: (to) => {
app.layout.toogleTopBarSpacer(to)
setShouldUseTopBarSpacer(to)
}
})
const handleUpdateRender = (...args) => {
if (document.startViewTransition) {
return document.startViewTransition(() => {
updateRender(...args)
})
}
return updateRender(...args)
}
const updateRender = (component, options = {}) => {
setRender({
component,
options
})
}
React.useEffect(() => {
if (!shouldUseTopBarSpacer) {
app.layout.tooglePagePanelSpacer(true)
} else {
app.layout.tooglePagePanelSpacer(false)
}
}, [shouldUseTopBarSpacer])
React.useEffect(() => {
if (shouldUseTopBarSpacer) {
if (visible) {
app.layout.toogleTopBarSpacer(true)
} else {
app.layout.toogleTopBarSpacer(false)
}
} else {
if (visible) {
app.layout.tooglePagePanelSpacer(true)
} else {
app.layout.tooglePagePanelSpacer(false)
}
app.layout.toogleTopBarSpacer(false)
}
}, [visible])
React.useEffect(() => {
if (render) {
setVisible(true)
} else {
setVisible(false)
}
}, [render])
const heightValue = visible ? Number(app.cores.style.defaultVar("top-bar-height").replace("px", "")) : 0
console.log(render)
return <Motion style={{
y: spring(visible ? 0 : 300,),
height: spring(heightValue,),
}}>
{({ y, height }) => {
return <>
<div
className="top-bar_wrapper"
style={{
WebkitTransform: `translateY(-${y}px)`,
transform: `translateY(-${y}px)`,
height: `${height}px`,
}}
>
<div
className={classnames(
"top-bar",
render?.options?.className,
)}
>
{
render?.component && React.cloneElement(render?.component, render?.options?.props ?? {})
}
</div>
</div>
</>
}}
</Motion>
}

View File

@ -0,0 +1,52 @@
@import "theme/vars.less";
#top-bar_spacer {
position: relative;
display: block;
width: 100%;
height: calc(@top_bar_height + @top_bar_padding * 2);
min-height: calc(@top_bar_height + @top_bar_padding * 2);
//height: 0;
//min-height: 0;
}
.top-bar_wrapper {
position: absolute;
top: 0;
left: 0;
z-index: 310;
padding: 10px;
width: 100%;
}
.top-bar {
position: relative;
display: flex;
flex-direction: row;
justify-content: center;
align-items: center;
background-color: var(--background-color-accent);
border-radius: 12px;
width: 100%;
height: @top_bar_height;
padding: 10px;
box-shadow: @card-shadow-top;
transition: all 150ms ease-in-out;
color: var(--text-color);
}

View File

@ -29,26 +29,24 @@ const NavMenu = (props) => {
const NavMenuMobile = (props) => {
return <div className="__mobile__navmenu_wrapper">
<div className="card">
{
props.items.map((item) => {
return <antd.Button
key={item.key}
className={classnames(
"item",
item.key === props.activeKey && "active",
)}
onClick={() => props.onClickItem(item.key)}
type="ghost"
disabled={item.disabled}
>
<div className="icon">
{item.icon}
</div>
</antd.Button>
})
}
</div>
{
props.items.map((item) => {
return <antd.Button
key={item.key}
className={classnames(
"card_item",
item.key === props.activeKey && "active",
)}
onClick={() => props.onClickItem(item.key)}
type="ghost"
disabled={item.disabled}
>
<div className="icon">
{item.icon}
</div>
</antd.Button>
})
}
</div>
}

View File

@ -5,60 +5,58 @@
}
.__mobile__navmenu_wrapper {
box-sizing: border-box;
display: flex;
flex-direction: row;
position: relative;
top: 0;
left: 0;
justify-content: space-evenly;
width: 100%;
}
.card {
width: 100%;
height: @app_topBar_height;
.card {
width: 100%;
height: @app_topBar_height;
display: flex;
flex-direction: row;
display: flex;
flex-direction: row;
justify-content: space-evenly;
justify-content: space-evenly;
box-shadow: @card-shadow;
box-shadow: @card-shadow;
gap: 10px;
padding: 5px;
border-radius: 12px;
gap: 10px;
padding: 5px;
border-radius: 12px;
isolation: unset;
overflow: visible;
isolation: unset;
overflow: visible;
}
.item {
display: flex;
flex-direction: column;
.card_item {
display: flex;
flex-direction: column;
align-items: center;
align-items: center;
height: fit-content;
height: fit-content;
gap: 4px;
gap: 4px;
padding: 8px 10px;
padding: 8px 10px;
&.active {
color: var(--colorPrimary);
}
&.active {
color: var(--colorPrimary);
}
.icon {
margin: 0;
line-height: 1rem;
font-size: 1.5rem;
}
.icon {
margin: 0;
line-height: 1rem;
font-size: 1.5rem;
}
.label {
height: fit-content;
line-height: 1rem;
font-size: 0.8rem;
}
}
.label {
height: fit-content;
line-height: 1rem;
font-size: 0.8rem;
}
}

View File

@ -4,6 +4,7 @@ import * as antd from "antd"
import { createIconRender } from "components/Icons"
import { UseTopBar } from "components/Layout/topBar"
import NavMenu from "./components/NavMenu"
import "./index.less"
@ -29,6 +30,14 @@ export class PagePanelWithNavMenu extends React.Component {
primaryPanelRef = React.createRef()
componentDidMount() {
app.layout.top_bar.shouldUseTopBarSpacer(false)
}
componentWillUnmount() {
app.layout.top_bar.shouldUseTopBarSpacer(true)
}
renderActiveTab() {
if (!Array.isArray(this.props.tabs)) {
console.error("PagePanelWithNavMenu: tabs must be an array")
@ -158,14 +167,27 @@ export class PagePanelWithNavMenu extends React.Component {
},
]
if (app.isMobile) {
delete panels[0]
}
if (this.props.extraPanel) {
panels.push(this.props.extraPanel)
}
return <PagePanels
primaryPanelClassName={this.props.primaryPanelClassName}
panels={panels}
/>
return <>
{
app.isMobile && app.layout.top_bar.render(<NavMenu
activeKey={this.state.activeTab}
items={this.getItems(this.props.tabs)}
onClickItem={(key) => this.handleTabChange(key)}
/>)
}
<PagePanels
primaryPanelClassName={this.props.primaryPanelClassName}
panels={panels}
/>
</>
}
}

View File

@ -1,3 +1,57 @@
@import "theme/vars.less";
#root {
&.mobile {
&.page-panel-spacer {
.pagePanels {
.panel {
&.center {
padding-top: calc(@top_bar_height + @top_bar_padding * 2);
}
}
}
}
.pagePanels {
display: flex;
flex-direction: column;
align-items: center;
justify-content: flex-start;
overflow: unset;
overflow-x: visible;
overflow-y: overlay;
width: 100%;
height: max-content;
padding: 0;
-webkit-mask-image: linear-gradient(180deg, transparent 0%, rgba(0, 0, 0, 1) 5%, rgba(0, 0, 0, 1) 97%, transparent 100%);
mask-image: linear-gradient(180deg, transparent 0%, rgba(0, 0, 0, 1) 5%, rgba(0, 0, 0, 1) 97%, transparent 100%);
.panel {
width: 100%;
height: unset;
&.center {
z-index: 300;
overflow: unset;
overflow-x: unset;
overflow-y: unset;
height: unset;
transition: all 150ms ease-in-out;
}
}
}
}
}
.pagePanels {
display: grid;

View File

@ -36,6 +36,46 @@ const typeToComponent = {
"playlist": (args) => <PlaylistTimelineEntry {...args} />,
}
const PostList = (props) => {
return <LoadMore
ref={props.listRef}
className="post-list"
loadingComponent={LoadingComponent}
noResultComponent={NoResultComponent}
hasMore={props.hasMore}
fetching={props.loading}
onBottom={props.onLoadMore}
>
{
!props.realtimeUpdates && !app.isMobile && <div className="resume_btn_wrapper">
<antd.Button
type="primary"
shape="round"
onClick={props.onResumeRealtimeUpdates}
loading={props.resumingLoading}
icon={<Icons.SyncOutlined />}
>
Resume
</antd.Button>
</div>
}
{
props.list.map((data) => {
return React.createElement(typeToComponent[data.type ?? "post"] ?? PostCard, {
key: data._id,
data: data,
events: {
onClickLike: props.onLikePost,
onClickSave: props.onSavePost,
onClickDelete: props.onDeletePost,
onClickEdit: props.onEditPost,
}
})
})
}
</LoadMore>
}
export class PostsListsComponent extends React.Component {
state = {
openPost: null,
@ -366,44 +406,34 @@ export class PostsListsComponent extends React.Component {
</div>
}
const PostListProps = {
listRef: this.listRef,
list: this.state.list,
onLikePost: this.onLikePost,
onSavePost: this.onSavePost,
onDeletePost: this.onDeletePost,
onEditPost: this.onEditPost,
onLoadMore: this.onLoadMore,
hasMore: this.state.hasMore,
loading: this.state.loading,
realtimeUpdates: this.state.realtimeUpdates,
resumingLoading: this.state.resumingLoading,
onResumeRealtimeUpdates: this.onResumeRealtimeUpdates,
}
if (app.isMobile) {
return <PostList
{...PostListProps}
/>
}
return <div className="post-list_wrapper">
<LoadMore
ref={this.listRef}
className="post-list"
loadingComponent={LoadingComponent}
noResultComponent={NoResultComponent}
hasMore={this.state.hasMore}
fetching={this.state.loading}
onBottom={this.onLoadMore}
>
{
!this.state.realtimeUpdates && !app.isMobile && <div className="resume_btn_wrapper">
<antd.Button
type="primary"
shape="round"
onClick={this.onResumeRealtimeUpdates}
loading={this.state.resumingLoading}
icon={<Icons.SyncOutlined />}
>
Resume
</antd.Button>
</div>
}
{
this.state.list.map((data) => {
return React.createElement(typeToComponent[data.type ?? "post"] ?? PostCard, {
key: data._id,
data: data,
events: {
onClickLike: this.onLikePost,
onClickSave: this.onSavePost,
onClickDelete: this.onDeletePost,
onClickEdit: this.onEditPost,
}
})
})
}
</LoadMore>
<PostList
{...PostListProps}
/>
</div>
}
}

View File

@ -1,3 +1,37 @@
@import "theme/vars.less";
#root {
&.mobile {
.post-list {
overflow: unset;
overflow-x: hidden;
overflow-y: overlay;
height: 100%;
//padding-top: 5px;
border-radius: 0;
.playlistTimelineEntry {
width: 100%;
.playlistTimelineEntry_content {
.playlistTimelineEntry_thumbnail {
img {
width: 20vw;
height: 20vw;
}
}
}
}
.postCard {
width: 100%;
}
}
}
}
.post-list_wrapper {
display: flex;
flex-direction: column;
@ -20,7 +54,7 @@
//will-change: transform;
overflow: hidden;
overflow-y: overlay;
width: 100%;
height: 100%;

View File

@ -7,7 +7,7 @@ export default class Layout extends React.PureComponent {
progressBar = progressBar.configure({ parent: "html", showSpinner: false })
state = {
layoutType: app.isMobile ? "mobile" : "default",
layoutType: "default",
renderError: null,
}
@ -51,14 +51,18 @@ export default class Layout extends React.PureComponent {
}
componentDidMount() {
// if (window.app.cores.settings.get("forceMobileMode") || app.isMobile) {
// app.layout.set("mobile")
// }
// register events
Object.keys(this.events).forEach((event) => {
window.app.eventBus.on(event, this.events[event])
})
if (app.isMobile) {
this.layoutInterface.toogleMobileStyle(true)
}
if (app.cores.settings.get("reduceAnimations")) {
this.layoutInterface.toogleRootContainerClassname("reduce-animations", true)
}
}
componentWillUnmount() {
@ -75,25 +79,22 @@ export default class Layout extends React.PureComponent {
makePageTransition(path, options = {}) {
this.progressBar.start()
if (app.cores.settings.get("reduceAnimations") || options.state?.noTransition) {
const content_layout = document.getElementById("content_layout")
if (!content_layout) {
console.warn("content_layout not found, no animation will be played")
this.progressBar.done()
return false
}
const transitionLayer = document.getElementById("transitionLayer")
if (!transitionLayer) {
console.warn("transitionLayer not found, no animation will be played")
return false
}
transitionLayer.classList.add("fade-transverse-leave")
content_layout.classList.add("fade-transverse-leave")
setTimeout(() => {
this.progressBar.done()
transitionLayer.classList.remove("fade-transverse-leave")
content_layout.classList.remove("fade-transverse-leave")
}, options.state?.transitionDelay ?? 250)
}
@ -110,25 +111,53 @@ export default class Layout extends React.PureComponent {
})
},
toogleCenteredContent: (to) => {
return this.layoutInterface.toogleRootContainerClassname("centered-content", to)
},
toogleMobileStyle: (to) => {
return this.layoutInterface.toogleRootContainerClassname("mobile", to)
},
toogleReducedAnimations: (to) => {
return this.layoutInterface.toogleRootContainerClassname("reduce-animations", to)
},
toogleTopBarSpacer: (to) => {
return this.layoutInterface.toogleRootContainerClassname("top-bar-spacer", to)
},
tooglePagePanelSpacer: (to) => {
return this.layoutInterface.toogleRootContainerClassname("page-panel-spacer", to)
},
toogleRootContainerClassname: (classname, to) => {
const root = document.getElementById("root")
if (app.isMobile) {
console.warn("Skipping centered content on mobile")
return false
}
if (!root) {
console.error("root not found")
return false
}
to = typeof to === "boolean" ? to : !root.classList.contains("centered-content")
to = typeof to === "boolean" ? to : !root.classList.contains(classname)
if (root.classList.contains(classname) === to) {
// ignore
return false
}
if (to === true) {
root.classList.add("centered_content")
root.classList.add(classname)
} else {
root.classList.remove("centered_content")
root.classList.remove(classname)
}
},
scrollTo: (to) => {
const content_layout = document.getElementById("content_layout")
if (!content_layout) {
console.error("content_layout not found")
return false
}
content_layout.scrollTo({
...to,
behavior: "smooth",
})
}
}
@ -148,7 +177,7 @@ export default class Layout extends React.PureComponent {
return JSON.stringify(this.state.renderError)
}
const Layout = Layouts[app.isMobile ? "mobile" : layoutType]
const Layout = Layouts[layoutType]
if (!Layout) {
return app.eventBus.emit("runtime.crash", new Error(`Layout type [${layoutType}] not found`))

View File

@ -2,4 +2,4 @@ import React from "react"
export default (props) => {
return props.children
}
}

View File

@ -2,13 +2,13 @@ import React from "react"
import classnames from "classnames"
import { Layout } from "antd"
import { Sidebar, Drawer, Sidedrawer, Modal } from "components/Layout"
import { Sidebar, Drawer, Sidedrawer, Modal, BottomBar, TopBar } from "components/Layout"
import BackgroundDecorator from "components/BackgroundDecorator"
import { createWithDom as FloatingStack } from "../components/floatingStack"
export default (props) => {
const DesktopLayout = (props) => {
React.useEffect(() => {
const floatingStack = FloatingStack()
@ -20,30 +20,55 @@ export default (props) => {
return <>
<BackgroundDecorator />
<Layout className="app_layout" style={{ height: "100%" }}>
<Layout id="app_layout" className="app_layout">
<Modal />
<Drawer />
<Sidebar />
<Sidedrawer />
<Layout.Content
id="content_layout"
className={classnames(
"content_layout",
...props.contentClassnames ?? [],
"content_layout",
{
["floating-sidebar"]: window.app?.cores.settings.get("sidebar.floating")
}
},
"fade-transverse-active",
)}
>
<div
id="transitionLayer"
className={classnames(
"page_layout",
"fade-transverse-active",
)}
>
{React.cloneElement(props.children, props)}
</div>
{
React.cloneElement(props.children, props)
}
</Layout.Content>
</Layout>
</>
}
}
const MobileLayout = (props) => {
return <Layout id="app_layout" className="app_layout">
<Modal />
<TopBar />
<Layout.Content
id="content_layout"
className={classnames(
...props.layoutPageModesClassnames ?? [],
"content_layout",
"fade-transverse-active",
)}
>
{
React.cloneElement(props.children, props)
}
</Layout.Content>
<BottomBar />
<Sidedrawer />
<Drawer />
</Layout>
}
export default (props) => {
return window.app.isMobile ? <MobileLayout {...props} /> : <DesktopLayout {...props} />
}

View File

@ -1,11 +1,9 @@
import Default from "./default"
import Mobile from "./mobile"
import None from "./none"
import Minimal from "./minimal"
import Blank from "./blank"
export default {
default: Default,
mobile: Mobile,
none: None,
minimal: Minimal,
blank: Blank,
}

View File

@ -5,7 +5,7 @@ import classnames from "classnames"
import { Drawer, Sidedrawer } from "components/Layout"
export default (props) => {
return <antd.Layout className={classnames("app_layout", { ["mobile"]: app.isMobile })} style={{ height: "100%" }}>
return <antd.Layout className={classnames("app_layout")} style={{ height: "100%" }}>
<Drawer />
<Sidedrawer />
<div id="transitionLayer" className="fade-transverse-active">

View File

@ -1,21 +0,0 @@
import React from "react"
import classnames from "classnames"
import * as antd from "antd"
import { BottomBar, Drawer, Sidedrawer, Modal } from "components/Layout"
export default (props) => {
return <antd.Layout id="app_layout" className={classnames("app_layout", ["mobile"])}>
<Modal />
<antd.Layout.Content className={classnames("content_layout", ...props.layoutPageModesClassnames ?? [])}>
<div id="transitionLayer" className={classnames("content_wrapper", "fade-transverse-active")}>
{React.cloneElement(props.children, props)}
</div>
</antd.Layout.Content>
<BottomBar />
<Sidedrawer />
<Drawer />
</antd.Layout>
}

View File

@ -1,5 +1,60 @@
@import "theme/vars.less";
@borderRadius: 12px;
#root {
&.mobile {
.accountProfile {
display: flex;
flex-direction: column;
.panels {
display: flex;
flex-direction: column;
.tabMenuWrapper {
position: fixed;
top: 0px;
left: 0px;
z-index: 300;
width: 100%;
height: @top_bar_height;
padding: @top_bar_padding;
box-shadow: @card-shadow-top;
display: flex;
flex-direction: row;
align-items: center;
opacity: 0;
.ant-menu {
display: flex;
flex-direction: row;
.ant-menu-item {
width: fit-content;
}
}
}
}
&.topHidden {
.tabMenuWrapper {
opacity: 1;
}
}
}
}
}
.accountProfile {
display: flex;

View File

@ -1,6 +1,31 @@
@panel-width: 500px;
@chatbox-header-height: 50px;
#root {
&.mobile {
.livestream {
flex-direction: column;
height: 100%;
}
.livestream_panel {
position: absolute;
bottom: 0;
height: 20%;
}
.livestream_player {
height: 100%;
width: 100%;
.plyr {
width: 100%;
height: 100%;
}
}
}
}
.plyr__controls {
width: 100%;
display: inline-flex;

View File

@ -1,3 +1,38 @@
#root {
&.mobile {
.playlist_view {
display: flex;
flex-direction: column;
width: 100%;
.play_info_wrapper {
position: relative;
.play_info {
width: 100%;
.play_info_cover {
width: 20vh;
height: 20vh;
img {
width: 100%;
height: 100%;
object-fit: contain;
}
}
}
}
.list {
padding: 30px 10px;
}
}
}
}
.playlist_view {
display: grid;
@ -60,7 +95,7 @@
width: 100%;
height: 100%;
object-fit: cover;
object-fit: contain;
}
}

View File

@ -5,6 +5,7 @@ import { Translation } from "react-i18next"
import useUrlQueryActiveKey from "hooks/useUrlQueryActiveKey"
import { Icons, createIconRender } from "components/Icons"
import { UseTopBar } from "components/Layout/topBar"
import {
composedSettingsByGroups as settingsGroups,
@ -20,38 +21,31 @@ const SettingsHeader = ({
activeKey,
back = () => { }
} = {}) => {
if (activeKey) {
const currentTab = composedTabs[activeKey]
const currentTab = composedTabs[activeKey]
return <div className="__mobile__settings_header nav">
<antd.Button
return <UseTopBar
options={{
className: "settings_nav"
}}
>
{
activeKey && <antd.Button
icon={<Icons.MdChevronLeft />}
onClick={back}
size="large"
type="ghost"
/>
}
<h1>
{
createIconRender(currentTab?.icon)
}
<Translation>
{(t) => t(currentTab?.label ?? activeKey)}
</Translation>
</h1>
</div>
}
return <div className="__mobile__settings_header">
<h1>
{
createIconRender("Settings")
createIconRender(currentTab?.icon ?? "Settings")
}
<Translation>
{(t) => t("Settings")}
{(t) => t(currentTab?.label ?? activeKey ?? "Settings")}
</Translation>
</h1>
</div>
</UseTopBar>
}
export default (props) => {
@ -80,6 +74,11 @@ export default (props) => {
const changeTab = (key) => {
lastKey = key
setActiveKey(key)
// scroll to top
app.layout.scrollTo({
top: 0,
})
}
return <div className="__mobile__settings">

View File

@ -1,4 +1,32 @@
@top_nav_height: 52px;
.settings_nav {
display: flex;
flex-direction: row;
align-items: center;
justify-content: flex-start;
width: 100%;
gap: 20px;
h1 {
margin: 0;
view-transition-name: main-header-text;
width: fit-content;
}
svg {
color: var(--colorPrimary);
}
.ant-btn {
font-size: 2rem;
svg {
color: var(--text-color);
}
}
}
.__mobile__settings {
display: flex;
@ -7,47 +35,6 @@
gap: 30px;
.__mobile__settings_header {
position: sticky;
top: 0;
left: 0;
z-index: 200;
width: 100%;
height: @top_nav_height;
display: flex;
flex-direction: row;
align-items: center;
gap: 20px;
padding: 5px 20px;
border-radius: 12px;
border-bottom: 1px solid var(--border-color);
background-color: var(--background-color-accent);
h1 {
margin: 0;
view-transition-name: main-header-text;
width: fit-content;
}
svg {
color: var(--colorPrimary);
}
.ant-btn {
font-size: 2rem;
svg {
color: var(--text-color);
}
}
}
.settings_list {
view-transition-name: settings-list;

View File

@ -128,25 +128,19 @@ html {
font-size: calc(16px * var(--fontScale));
&.electron {
.page_layout {
padding-top: 35px;
}
.ant-layout-sider {
padding-top: 0px;
}
}
&.centered_content {
.content_layout {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
.app_layout {
.content_layout {
display: flex;
flex-direction: column;
.page_layout {
width: 60%;
height: calc(100vh - var(--layoutPadding) * 2);
justify-content: center;
align-items: center;
}
}
}
@ -192,27 +186,19 @@ html {
z-index: 200;
transition: all 200ms ease-in-out;
}
&.mobile {
//padding-top: 20px;
::-webkit-scrollbar {
display: none !important;
width: 0;
height: 0;
z-index: 0;
}
}
}
.content_layout {
position: relative;
-webkit-overflow-scrolling: touch;
box-sizing: border-box;
display: flex;
flex-direction: column;
align-items: center;
justify-content: flex-start;
height: 100%;
width: 100%;
@ -232,11 +218,6 @@ html {
&.floating-sidebar {
margin-left: @app_sidebar_width;
}
.page_layout {
width: 100%;
height: 100%;
}
}
.root_background {

View File

@ -1,183 +1,66 @@
@import "theme/vars.less";
@top_bar_height: 52px;
// #root,body,html {
// height: 100%!important;
// max-height: 100%!important;
// }
.app_layout {
#root {
&.mobile {
display: flex;
flex-direction: column;
&.centered-content {
.app_layout {
.content_layout {
display: flex;
flex-direction: column;
height: 100% !important;
max-height: 100% !important;
justify-content: flex-start;
align-items: center;
padding: 0;
.content_wrapper {
height: 100%;
width: 100%;
max-height: 100%;
overflow-y: scroll;
padding: 10px;
}
}
}
.content_layout {
&.top-bar-spacer {
.app_layout {
.content_layout {
padding-top: calc(@top_bar_height + @top_bar_padding * 2);
}
}
}
.app_layout {
display: flex;
flex-direction: column;
overflow: hidden;
align-items: center;
justify-content: center;
width: 100%;
height: 100%;
max-height: 100%;
padding: 7px 0;
padding-bottom: 0px;
box-sizing: border-box;
}
.pagePanels {
display: flex;
flex-direction: column;
align-items: center;
justify-content: flex-start;
overflow-x: visible;
width: 100%;
height: 100%;
padding: 0;
.panel {
width: 100%;
&.left {
position: absolute;
height: @top_bar_height;
padding: 10px;
z-index: 310;
top: 0;
left: 0;
overflow: visible;
overflow-x: visible;
}
&.center {
z-index: 300;
overflow-y: visible;
overflow-x: hidden;
margin-top: calc(@top_bar_height + 10px);
margin-bottom: 10px;
border-radius: 12px;
height: 100%;
//-webkit-mask-image: linear-gradient(0deg, rgba(0, 0, 0, 0) 0%, rgb(0, 0, 0) 10px, rgb(0, 0, 0) calc(100% - 10px), rgba(0, 0, 0, 0) 100%);
//mask-image: linear-gradient(0deg, rgba(0, 0, 0, 0) 0%, rgb(0, 0, 0) 10px, rgb(0, 0, 0) calc(100% - 10px), rgba(0, 0, 0, 0) 100%);
}
::-webkit-scrollbar {
display: none !important;
width: 0;
height: 0;
z-index: 0;
}
.card {
width: 100%;
height: 100%;
transition: all 150ms ease-in-out;
.content_layout {
display: flex;
flex-direction: row;
flex-direction: column;
padding: 5px;
align-items: center;
justify-content: flex-start;
justify-content: space-evenly;
margin: 0;
padding: 0;
padding-bottom: 0px;
box-shadow: @card-shadow;
overflow: unset;
overflow-x: hidden;
overflow-y: overlay;
border-radius: 12px;
isolation: unset;
overflow: visible;
}
}
.playlist_view {
display: flex;
flex-direction: column;
.play_info_wrapper {
position: relative;
.play_info {
width: 100%;
.play_info_cover {
width: 100%;
height: 100%;
}
}
}
.list {
padding: 30px 10px;
}
}
.post-list_wrapper {
.post-list {
padding-top: 10px;
border-radius: 0;
.playlistTimelineEntry {
width: 100%;
.playlistTimelineEntry_content {
.playlistTimelineEntry_thumbnail {
img {
width: 20vw;
height: 20vw;
}
}
}
}
.postCard {
>div {
width: 100%;
}
}
}
.livestream {
flex-direction: column;
height: 100%;
}
.livestream_panel {
position: absolute;
bottom: 0;
height: 20%;
}
.livestream_player {
height: 100%;
width: 100%;
.plyr {
width: 100%;
height: 100%;
}
}
}
}

View File

@ -18,4 +18,8 @@
@transition-ease-inout: all 150ms ease-in-out;
@card-shadow: 0 0 0 1px rgba(63, 63, 68, 0.05), 0 1px 3px 0 var(--shadow-color);
@card-shadow-top: 0 -4px 3px 0 rgba(63, 63, 68, 0.05), 0 0 0 2px var(--shadow-color);
@card-shadow-top: 0 -4px 3px 0 rgba(63, 63, 68, 0.05), 0 0 0 2px var(--shadow-color);
// Mobile
@top_bar_height: 52px;
@top_bar_padding: 10px;