mirror of
https://github.com/ragestudio/comty.git
synced 2025-06-10 19:14:16 +00:00
improve layout for mobile & use of top-bar
This commit is contained in:
parent
11f978cca9
commit
c52834c0c8
@ -17,6 +17,7 @@
|
|||||||
"backgroundPosition": "center",
|
"backgroundPosition": "center",
|
||||||
"backgroundRepeat": "no-repeat",
|
"backgroundRepeat": "no-repeat",
|
||||||
"backgroundAttachment": "fixed",
|
"backgroundAttachment": "fixed",
|
||||||
|
"top-bar-height": "52px",
|
||||||
"compact-mode": false
|
"compact-mode": false
|
||||||
},
|
},
|
||||||
"defaultVariant": "dark",
|
"defaultVariant": "dark",
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
export default [
|
export default [
|
||||||
{
|
{
|
||||||
path: "/login",
|
path: "/login",
|
||||||
useLayout: "none",
|
useLayout: "minimal",
|
||||||
public: true
|
public: true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -46,12 +46,12 @@ export default [
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: "/landing/*",
|
path: "/landing/*",
|
||||||
useLayout: "blank",
|
useLayout: "minimal",
|
||||||
public: true
|
public: true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: "/nfc/*",
|
path: "/nfc/*",
|
||||||
useLayout: "blank",
|
useLayout: "minimal",
|
||||||
public: true
|
public: true
|
||||||
}
|
}
|
||||||
]
|
]
|
@ -1,3 +1,4 @@
|
|||||||
|
export { default as TopBar } from "./topBar"
|
||||||
export { default as BottomBar } from "./bottomBar"
|
export { default as BottomBar } from "./bottomBar"
|
||||||
export { default as Drawer } from "./drawer"
|
export { default as Drawer } from "./drawer"
|
||||||
export { default as Sidebar } from "./sidebar"
|
export { default as Sidebar } from "./sidebar"
|
||||||
|
157
packages/app/src/components/Layout/topBar/index.jsx
Normal file
157
packages/app/src/components/Layout/topBar/index.jsx
Normal 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>
|
||||||
|
}
|
52
packages/app/src/components/Layout/topBar/index.less
Normal file
52
packages/app/src/components/Layout/topBar/index.less
Normal 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);
|
||||||
|
}
|
@ -29,26 +29,24 @@ const NavMenu = (props) => {
|
|||||||
|
|
||||||
const NavMenuMobile = (props) => {
|
const NavMenuMobile = (props) => {
|
||||||
return <div className="__mobile__navmenu_wrapper">
|
return <div className="__mobile__navmenu_wrapper">
|
||||||
<div className="card">
|
{
|
||||||
{
|
props.items.map((item) => {
|
||||||
props.items.map((item) => {
|
return <antd.Button
|
||||||
return <antd.Button
|
key={item.key}
|
||||||
key={item.key}
|
className={classnames(
|
||||||
className={classnames(
|
"card_item",
|
||||||
"item",
|
item.key === props.activeKey && "active",
|
||||||
item.key === props.activeKey && "active",
|
)}
|
||||||
)}
|
onClick={() => props.onClickItem(item.key)}
|
||||||
onClick={() => props.onClickItem(item.key)}
|
type="ghost"
|
||||||
type="ghost"
|
disabled={item.disabled}
|
||||||
disabled={item.disabled}
|
>
|
||||||
>
|
<div className="icon">
|
||||||
<div className="icon">
|
{item.icon}
|
||||||
{item.icon}
|
</div>
|
||||||
</div>
|
</antd.Button>
|
||||||
</antd.Button>
|
})
|
||||||
})
|
}
|
||||||
}
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5,60 +5,58 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.__mobile__navmenu_wrapper {
|
.__mobile__navmenu_wrapper {
|
||||||
box-sizing: border-box;
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
|
||||||
position: relative;
|
justify-content: space-evenly;
|
||||||
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
|
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
.card {
|
.card {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: @app_topBar_height;
|
height: @app_topBar_height;
|
||||||
|
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
|
|
||||||
justify-content: space-evenly;
|
justify-content: space-evenly;
|
||||||
|
|
||||||
box-shadow: @card-shadow;
|
box-shadow: @card-shadow;
|
||||||
|
|
||||||
gap: 10px;
|
gap: 10px;
|
||||||
padding: 5px;
|
padding: 5px;
|
||||||
border-radius: 12px;
|
border-radius: 12px;
|
||||||
|
|
||||||
isolation: unset;
|
isolation: unset;
|
||||||
overflow: visible;
|
overflow: visible;
|
||||||
|
}
|
||||||
|
|
||||||
.item {
|
.card_item {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
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 {
|
&.active {
|
||||||
color: var(--colorPrimary);
|
color: var(--colorPrimary);
|
||||||
}
|
}
|
||||||
|
|
||||||
.icon {
|
.icon {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
line-height: 1rem;
|
line-height: 1rem;
|
||||||
font-size: 1.5rem;
|
font-size: 1.5rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.label {
|
.label {
|
||||||
height: fit-content;
|
height: fit-content;
|
||||||
line-height: 1rem;
|
line-height: 1rem;
|
||||||
font-size: 0.8rem;
|
font-size: 0.8rem;
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -4,6 +4,7 @@ import * as antd from "antd"
|
|||||||
|
|
||||||
import { createIconRender } from "components/Icons"
|
import { createIconRender } from "components/Icons"
|
||||||
|
|
||||||
|
import { UseTopBar } from "components/Layout/topBar"
|
||||||
import NavMenu from "./components/NavMenu"
|
import NavMenu from "./components/NavMenu"
|
||||||
|
|
||||||
import "./index.less"
|
import "./index.less"
|
||||||
@ -29,6 +30,14 @@ export class PagePanelWithNavMenu extends React.Component {
|
|||||||
|
|
||||||
primaryPanelRef = React.createRef()
|
primaryPanelRef = React.createRef()
|
||||||
|
|
||||||
|
componentDidMount() {
|
||||||
|
app.layout.top_bar.shouldUseTopBarSpacer(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
componentWillUnmount() {
|
||||||
|
app.layout.top_bar.shouldUseTopBarSpacer(true)
|
||||||
|
}
|
||||||
|
|
||||||
renderActiveTab() {
|
renderActiveTab() {
|
||||||
if (!Array.isArray(this.props.tabs)) {
|
if (!Array.isArray(this.props.tabs)) {
|
||||||
console.error("PagePanelWithNavMenu: tabs must be an array")
|
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) {
|
if (this.props.extraPanel) {
|
||||||
panels.push(this.props.extraPanel)
|
panels.push(this.props.extraPanel)
|
||||||
}
|
}
|
||||||
|
|
||||||
return <PagePanels
|
return <>
|
||||||
primaryPanelClassName={this.props.primaryPanelClassName}
|
{
|
||||||
panels={panels}
|
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}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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 {
|
.pagePanels {
|
||||||
display: grid;
|
display: grid;
|
||||||
|
|
||||||
|
@ -36,6 +36,46 @@ const typeToComponent = {
|
|||||||
"playlist": (args) => <PlaylistTimelineEntry {...args} />,
|
"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 {
|
export class PostsListsComponent extends React.Component {
|
||||||
state = {
|
state = {
|
||||||
openPost: null,
|
openPost: null,
|
||||||
@ -366,44 +406,34 @@ export class PostsListsComponent extends React.Component {
|
|||||||
</div>
|
</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">
|
return <div className="post-list_wrapper">
|
||||||
<LoadMore
|
<PostList
|
||||||
ref={this.listRef}
|
{...PostListProps}
|
||||||
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>
|
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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 {
|
.post-list_wrapper {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
@ -7,7 +7,7 @@ export default class Layout extends React.PureComponent {
|
|||||||
progressBar = progressBar.configure({ parent: "html", showSpinner: false })
|
progressBar = progressBar.configure({ parent: "html", showSpinner: false })
|
||||||
|
|
||||||
state = {
|
state = {
|
||||||
layoutType: app.isMobile ? "mobile" : "default",
|
layoutType: "default",
|
||||||
renderError: null,
|
renderError: null,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -51,14 +51,18 @@ export default class Layout extends React.PureComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
// if (window.app.cores.settings.get("forceMobileMode") || app.isMobile) {
|
|
||||||
// app.layout.set("mobile")
|
|
||||||
// }
|
|
||||||
|
|
||||||
// register events
|
// register events
|
||||||
Object.keys(this.events).forEach((event) => {
|
Object.keys(this.events).forEach((event) => {
|
||||||
window.app.eventBus.on(event, this.events[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() {
|
componentWillUnmount() {
|
||||||
@ -75,25 +79,22 @@ export default class Layout extends React.PureComponent {
|
|||||||
makePageTransition(path, options = {}) {
|
makePageTransition(path, options = {}) {
|
||||||
this.progressBar.start()
|
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()
|
this.progressBar.done()
|
||||||
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
const transitionLayer = document.getElementById("transitionLayer")
|
content_layout.classList.add("fade-transverse-leave")
|
||||||
|
|
||||||
if (!transitionLayer) {
|
|
||||||
console.warn("transitionLayer not found, no animation will be played")
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
transitionLayer.classList.add("fade-transverse-leave")
|
|
||||||
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
this.progressBar.done()
|
this.progressBar.done()
|
||||||
|
|
||||||
transitionLayer.classList.remove("fade-transverse-leave")
|
content_layout.classList.remove("fade-transverse-leave")
|
||||||
}, options.state?.transitionDelay ?? 250)
|
}, options.state?.transitionDelay ?? 250)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -110,25 +111,53 @@ export default class Layout extends React.PureComponent {
|
|||||||
})
|
})
|
||||||
},
|
},
|
||||||
toogleCenteredContent: (to) => {
|
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")
|
const root = document.getElementById("root")
|
||||||
|
|
||||||
if (app.isMobile) {
|
|
||||||
console.warn("Skipping centered content on mobile")
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!root) {
|
if (!root) {
|
||||||
console.error("root not found")
|
console.error("root not found")
|
||||||
return false
|
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) {
|
if (to === true) {
|
||||||
root.classList.add("centered_content")
|
root.classList.add(classname)
|
||||||
} else {
|
} 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)
|
return JSON.stringify(this.state.renderError)
|
||||||
}
|
}
|
||||||
|
|
||||||
const Layout = Layouts[app.isMobile ? "mobile" : layoutType]
|
const Layout = Layouts[layoutType]
|
||||||
|
|
||||||
if (!Layout) {
|
if (!Layout) {
|
||||||
return app.eventBus.emit("runtime.crash", new Error(`Layout type [${layoutType}] not found`))
|
return app.eventBus.emit("runtime.crash", new Error(`Layout type [${layoutType}] not found`))
|
||||||
|
@ -2,13 +2,13 @@ import React from "react"
|
|||||||
import classnames from "classnames"
|
import classnames from "classnames"
|
||||||
import { Layout } from "antd"
|
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 BackgroundDecorator from "components/BackgroundDecorator"
|
||||||
|
|
||||||
import { createWithDom as FloatingStack } from "../components/floatingStack"
|
import { createWithDom as FloatingStack } from "../components/floatingStack"
|
||||||
|
|
||||||
export default (props) => {
|
const DesktopLayout = (props) => {
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
const floatingStack = FloatingStack()
|
const floatingStack = FloatingStack()
|
||||||
|
|
||||||
@ -20,30 +20,55 @@ export default (props) => {
|
|||||||
return <>
|
return <>
|
||||||
<BackgroundDecorator />
|
<BackgroundDecorator />
|
||||||
|
|
||||||
<Layout className="app_layout" style={{ height: "100%" }}>
|
<Layout id="app_layout" className="app_layout">
|
||||||
<Modal />
|
<Modal />
|
||||||
<Drawer />
|
<Drawer />
|
||||||
<Sidebar />
|
<Sidebar />
|
||||||
<Sidedrawer />
|
<Sidedrawer />
|
||||||
<Layout.Content
|
<Layout.Content
|
||||||
|
id="content_layout"
|
||||||
className={classnames(
|
className={classnames(
|
||||||
"content_layout",
|
|
||||||
...props.contentClassnames ?? [],
|
...props.contentClassnames ?? [],
|
||||||
|
"content_layout",
|
||||||
{
|
{
|
||||||
["floating-sidebar"]: window.app?.cores.settings.get("sidebar.floating")
|
["floating-sidebar"]: window.app?.cores.settings.get("sidebar.floating")
|
||||||
}
|
},
|
||||||
|
"fade-transverse-active",
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<div
|
{
|
||||||
id="transitionLayer"
|
React.cloneElement(props.children, props)
|
||||||
className={classnames(
|
}
|
||||||
"page_layout",
|
|
||||||
"fade-transverse-active",
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
{React.cloneElement(props.children, props)}
|
|
||||||
</div>
|
|
||||||
</Layout.Content>
|
</Layout.Content>
|
||||||
</Layout>
|
</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} />
|
||||||
|
}
|
@ -1,11 +1,9 @@
|
|||||||
import Default from "./default"
|
import Default from "./default"
|
||||||
import Mobile from "./mobile"
|
import Minimal from "./minimal"
|
||||||
import None from "./none"
|
|
||||||
import Blank from "./blank"
|
import Blank from "./blank"
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
default: Default,
|
default: Default,
|
||||||
mobile: Mobile,
|
minimal: Minimal,
|
||||||
none: None,
|
|
||||||
blank: Blank,
|
blank: Blank,
|
||||||
}
|
}
|
@ -5,7 +5,7 @@ import classnames from "classnames"
|
|||||||
import { Drawer, Sidedrawer } from "components/Layout"
|
import { Drawer, Sidedrawer } from "components/Layout"
|
||||||
|
|
||||||
export default (props) => {
|
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 />
|
<Drawer />
|
||||||
<Sidedrawer />
|
<Sidedrawer />
|
||||||
<div id="transitionLayer" className="fade-transverse-active">
|
<div id="transitionLayer" className="fade-transverse-active">
|
@ -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>
|
|
||||||
}
|
|
@ -1,5 +1,60 @@
|
|||||||
|
@import "theme/vars.less";
|
||||||
|
|
||||||
@borderRadius: 12px;
|
@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 {
|
.accountProfile {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|
||||||
|
@ -1,6 +1,31 @@
|
|||||||
@panel-width: 500px;
|
@panel-width: 500px;
|
||||||
@chatbox-header-height: 50px;
|
@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 {
|
.plyr__controls {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
|
@ -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 {
|
.playlist_view {
|
||||||
display: grid;
|
display: grid;
|
||||||
|
|
||||||
@ -60,7 +95,7 @@
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
|
|
||||||
object-fit: cover;
|
object-fit: contain;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5,6 +5,7 @@ import { Translation } from "react-i18next"
|
|||||||
import useUrlQueryActiveKey from "hooks/useUrlQueryActiveKey"
|
import useUrlQueryActiveKey from "hooks/useUrlQueryActiveKey"
|
||||||
|
|
||||||
import { Icons, createIconRender } from "components/Icons"
|
import { Icons, createIconRender } from "components/Icons"
|
||||||
|
import { UseTopBar } from "components/Layout/topBar"
|
||||||
|
|
||||||
import {
|
import {
|
||||||
composedSettingsByGroups as settingsGroups,
|
composedSettingsByGroups as settingsGroups,
|
||||||
@ -20,38 +21,31 @@ const SettingsHeader = ({
|
|||||||
activeKey,
|
activeKey,
|
||||||
back = () => { }
|
back = () => { }
|
||||||
} = {}) => {
|
} = {}) => {
|
||||||
if (activeKey) {
|
const currentTab = composedTabs[activeKey]
|
||||||
const currentTab = composedTabs[activeKey]
|
|
||||||
|
|
||||||
return <div className="__mobile__settings_header nav">
|
return <UseTopBar
|
||||||
<antd.Button
|
options={{
|
||||||
|
className: "settings_nav"
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{
|
||||||
|
activeKey && <antd.Button
|
||||||
icon={<Icons.MdChevronLeft />}
|
icon={<Icons.MdChevronLeft />}
|
||||||
onClick={back}
|
onClick={back}
|
||||||
size="large"
|
size="large"
|
||||||
type="ghost"
|
type="ghost"
|
||||||
/>
|
/>
|
||||||
|
}
|
||||||
|
|
||||||
<h1>
|
|
||||||
{
|
|
||||||
createIconRender(currentTab?.icon)
|
|
||||||
}
|
|
||||||
<Translation>
|
|
||||||
{(t) => t(currentTab?.label ?? activeKey)}
|
|
||||||
</Translation>
|
|
||||||
</h1>
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
|
|
||||||
return <div className="__mobile__settings_header">
|
|
||||||
<h1>
|
<h1>
|
||||||
{
|
{
|
||||||
createIconRender("Settings")
|
createIconRender(currentTab?.icon ?? "Settings")
|
||||||
}
|
}
|
||||||
<Translation>
|
<Translation>
|
||||||
{(t) => t("Settings")}
|
{(t) => t(currentTab?.label ?? activeKey ?? "Settings")}
|
||||||
</Translation>
|
</Translation>
|
||||||
</h1>
|
</h1>
|
||||||
</div>
|
</UseTopBar>
|
||||||
}
|
}
|
||||||
|
|
||||||
export default (props) => {
|
export default (props) => {
|
||||||
@ -80,6 +74,11 @@ export default (props) => {
|
|||||||
const changeTab = (key) => {
|
const changeTab = (key) => {
|
||||||
lastKey = key
|
lastKey = key
|
||||||
setActiveKey(key)
|
setActiveKey(key)
|
||||||
|
|
||||||
|
// scroll to top
|
||||||
|
app.layout.scrollTo({
|
||||||
|
top: 0,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
return <div className="__mobile__settings">
|
return <div className="__mobile__settings">
|
||||||
|
@ -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 {
|
.__mobile__settings {
|
||||||
display: flex;
|
display: flex;
|
||||||
@ -7,47 +35,6 @@
|
|||||||
|
|
||||||
gap: 30px;
|
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 {
|
.settings_list {
|
||||||
view-transition-name: settings-list;
|
view-transition-name: settings-list;
|
||||||
|
|
||||||
|
@ -128,25 +128,19 @@ html {
|
|||||||
font-size: calc(16px * var(--fontScale));
|
font-size: calc(16px * var(--fontScale));
|
||||||
|
|
||||||
&.electron {
|
&.electron {
|
||||||
.page_layout {
|
|
||||||
padding-top: 35px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.ant-layout-sider {
|
.ant-layout-sider {
|
||||||
padding-top: 0px;
|
padding-top: 0px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&.centered_content {
|
&.centered_content {
|
||||||
.content_layout {
|
.app_layout {
|
||||||
display: flex;
|
.content_layout {
|
||||||
flex-direction: column;
|
display: flex;
|
||||||
justify-content: center;
|
flex-direction: column;
|
||||||
align-items: center;
|
|
||||||
|
|
||||||
.page_layout {
|
justify-content: center;
|
||||||
width: 60%;
|
align-items: center;
|
||||||
height: calc(100vh - var(--layoutPadding) * 2);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -192,27 +186,19 @@ html {
|
|||||||
z-index: 200;
|
z-index: 200;
|
||||||
transition: all 200ms ease-in-out;
|
transition: all 200ms ease-in-out;
|
||||||
}
|
}
|
||||||
|
|
||||||
&.mobile {
|
|
||||||
//padding-top: 20px;
|
|
||||||
|
|
||||||
::-webkit-scrollbar {
|
|
||||||
display: none !important;
|
|
||||||
width: 0;
|
|
||||||
height: 0;
|
|
||||||
z-index: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.content_layout {
|
.content_layout {
|
||||||
position: relative;
|
position: relative;
|
||||||
-webkit-overflow-scrolling: touch;
|
-webkit-overflow-scrolling: touch;
|
||||||
|
|
||||||
|
box-sizing: border-box;
|
||||||
|
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
justify-content: flex-start;
|
||||||
|
|
||||||
height: 100%;
|
height: 100%;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
@ -232,11 +218,6 @@ html {
|
|||||||
&.floating-sidebar {
|
&.floating-sidebar {
|
||||||
margin-left: @app_sidebar_width;
|
margin-left: @app_sidebar_width;
|
||||||
}
|
}
|
||||||
|
|
||||||
.page_layout {
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.root_background {
|
.root_background {
|
||||||
|
@ -1,183 +1,66 @@
|
|||||||
@import "theme/vars.less";
|
@import "theme/vars.less";
|
||||||
|
|
||||||
@top_bar_height: 52px;
|
#root {
|
||||||
|
|
||||||
// #root,body,html {
|
|
||||||
// height: 100%!important;
|
|
||||||
// max-height: 100%!important;
|
|
||||||
// }
|
|
||||||
|
|
||||||
.app_layout {
|
|
||||||
&.mobile {
|
&.mobile {
|
||||||
display: flex;
|
&.centered-content {
|
||||||
flex-direction: column;
|
.app_layout {
|
||||||
|
.content_layout {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
|
||||||
height: 100% !important;
|
justify-content: flex-start;
|
||||||
max-height: 100% !important;
|
align-items: center;
|
||||||
|
|
||||||
padding: 0;
|
padding: 10px;
|
||||||
|
}
|
||||||
.content_wrapper {
|
}
|
||||||
height: 100%;
|
|
||||||
width: 100%;
|
|
||||||
max-height: 100%;
|
|
||||||
|
|
||||||
overflow-y: scroll;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.content_layout {
|
&.top-bar-spacer {
|
||||||
|
.app_layout {
|
||||||
|
.content_layout {
|
||||||
|
padding-top: calc(@top_bar_height + @top_bar_padding * 2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.app_layout {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
|
||||||
overflow: hidden;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
|
|
||||||
width: 100%;
|
|
||||||
height: 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%;
|
width: 100%;
|
||||||
height: 100%;
|
|
||||||
|
|
||||||
padding: 0;
|
padding: 0;
|
||||||
|
|
||||||
.panel {
|
::-webkit-scrollbar {
|
||||||
width: 100%;
|
display: none !important;
|
||||||
|
width: 0;
|
||||||
&.left {
|
height: 0;
|
||||||
position: absolute;
|
z-index: 0;
|
||||||
|
|
||||||
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%);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.card {
|
transition: all 150ms ease-in-out;
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
|
|
||||||
|
.content_layout {
|
||||||
display: flex;
|
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;
|
>div {
|
||||||
|
|
||||||
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 {
|
|
||||||
width: 100%;
|
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%;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -19,3 +19,7 @@
|
|||||||
|
|
||||||
@card-shadow: 0 0 0 1px rgba(63, 63, 68, 0.05), 0 1px 3px 0 var(--shadow-color);
|
@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;
|
Loading…
x
Reference in New Issue
Block a user