refactor & restyle sidebar

This commit is contained in:
SrGooglo 2023-06-30 10:36:48 +00:00
parent 8787dd16f4
commit e28efd1698
15 changed files with 287 additions and 887 deletions

View File

@ -6,14 +6,12 @@
"notifications_vibrate": true,
"notifications_sound_volume": 50,
"longPressDelay": 600,
"autoCollapseDelay": 500,
"autoCollapseDelayEnabled": true,
"sidebar.collapsable": true,
"sidebar.collapse_delay_time": 500,
"haptic_feedback": true,
"collapseOnLooseFocus": true,
"style.auto_darkMode": true,
"feed_max_fetch": 20,
"style.compactMode": false,
"sidebar.floating": false,
"player.compressor": false,
"language": "en"
}

View File

@ -12,17 +12,6 @@ export default {
group: "app",
order: 1,
settings: [
{
id: "sidebar.floating",
title: "Floating Sidebar",
description: "Make the sidebar float over layout content.",
component: "Switch",
icon: "MdOutlineLastPage",
group: "layout",
emitEvent: "app.softReload",
storaged: true,
mobile: false,
},
{
id: "style.reduceAnimations",
group: "animations",

View File

@ -148,24 +148,23 @@ export default {
mobile: false,
},
{
id: "collapseOnLooseFocus",
storaged: true,
id: "sidebar.collapsable",
group: "sidebar",
component: "Switch",
icon: "Columns",
title: "Auto Collapse",
description: "Collapse the sidebar when loose focus",
description: "Allow to collapse the sidebar when loose focus.",
emitEvent: "settingChanged.sidebar_collapse",
storaged: true,
mobile: false,
},
{
id: "autoCollapseDelay",
storaged: true,
id: "sidebar.collapse_delay_time",
group: "sidebar",
component: "Slider",
icon: "MdTimer",
dependsOn: {
"collapseOnLooseFocus": true
"sidebar.collapsable": true
},
title: "Auto Collapse timeout",
description: "Set the delay before the sidebar is collapsed",
@ -181,6 +180,7 @@ export default {
2000: "2s",
}
},
storaged: true,
mobile: false,
},
{

View File

@ -1,267 +0,0 @@
import React from "react"
import { Button } from "antd"
import { DragDropContext, Droppable, Draggable } from "react-beautiful-dnd"
import { ActionsBar } from "components"
import { Icons, createIconRender } from "components/Icons"
import sidebarItems from "schemas/sidebar"
import { sidebarKeys as defaultSidebarKeys } from "schemas/defaultSettings"
import Selector from "../selector"
import "./index.less"
const allItemsMap = [...sidebarItems].map((item, index) => {
item.key = index.toString()
item.index = index
return item
})
const getAllItems = () => {
let items = {}
allItemsMap.forEach((item) => {
items[item.id] = {
...item,
content: (
<>
{createIconRender(item.icon)} {item.title}
</>
),
}
})
return items
}
const allItems = getAllItems()
export default class SidebarEditor extends React.Component {
state = {
items: [],
lockedIndex: [],
}
componentDidMount() {
this.loadItems()
}
loadItems = () => {
const storagedKeys = window.app.cores.settings.get("sidebarKeys") ?? defaultSidebarKeys
const active = []
const lockedIndex = []
// set current active items
storagedKeys.forEach((key) => {
if (typeof allItems[key] !== "undefined") {
if (allItems[key].locked) {
lockedIndex.push(allItems[key].index)
}
active.push(key)
}
})
this.setState({ items: active, lockedIndex })
}
onSave = () => {
window.app.cores.settings.set("sidebarKeys", this.state.items)
window.app.SidebarController.toggleEdit(false)
}
onDiscard = () => {
window.app.SidebarController.toggleEdit(false)
}
onSetDefaults = () => {
window.app.cores.settings.set("sidebarKeys", defaultSidebarKeys)
this.loadItems()
}
reorder = (list, startIndex, endIndex) => {
const result = Array.from(list)
const [removed] = result.splice(startIndex, 1)
result.splice(endIndex, 0, removed)
return result
}
onDragEnd = (result) => {
if (!result.destination) {
return false
}
if (this.state.lockedIndex.includes(result.destination.index)) {
return false
}
if (allItems[result.draggableId].locked) {
console.warn("Cannot move an locked item")
return false
}
const items = this.reorder(this.state.items, result.source.index, result.destination.index)
this.setState({ items })
}
deleteItem = (key) => {
// check if item is locked
if (allItems[key].locked) {
console.warn("Cannot delete an locked item")
return false
}
this.setState({ items: this.state.items.filter((item) => item !== key) })
}
addItem = () => {
const keys = []
// filter by active keys
allItemsMap.forEach((item) => {
if (!this.state.items.includes(item.id)) {
keys.push(item.id)
}
})
window.app.DrawerController.open("sidebar_item_selector", Selector, {
props: {
width: "65%",
},
componentProps: {
items: keys
},
onDone: (drawer, selectedKeys) => {
drawer.close()
if (Array.isArray(selectedKeys)) {
const update = this.state.items ?? []
selectedKeys.forEach((key) => {
if (update.includes(key)) {
return false
}
update.push(key)
})
this.setState({ items: update })
}
},
})
}
render() {
const grid = 6
const getItemStyle = (isDragging, draggableStyle, component, isDraggingOver) => ({
cursor: component.locked ? "not-allowed" : "grab",
userSelect: "none",
padding: grid * 2,
margin: `0 0 ${grid}px 0`,
borderRadius: "6px",
transition: "150ms all ease-in-out",
width: "100%",
border: isDraggingOver ? "2px dashed #e0e0e0" : "none",
color: component.locked ? "rgba(145,145,145,0.6)" : "#000",
background: component.locked
? "rgba(145, 145, 145, 0.2)"
: isDragging
? "rgba(145, 145, 145, 0.5)"
: "transparent",
...draggableStyle,
})
const getListStyle = (isDraggingOver) => ({
background: "transparent",
transition: "150ms all ease-in-out",
padding: grid,
width: "100%",
})
return (
<div>
<DragDropContext onDragEnd={this.onDragEnd}>
<Droppable droppableId="droppable">
{(droppableProvided, droppableSnapshot) => (
<div
ref={droppableProvided.innerRef}
style={getListStyle(droppableSnapshot.isDraggingOver)}
>
{this.state.items.map((key, index) => {
const itemComponent = allItems[key]
return (
<Draggable
isDragDisabled={itemComponent.locked}
key={key}
draggableId={key}
index={index}
>
{(draggableProvided, draggableSnapshot) => (
<div
ref={draggableProvided.innerRef}
{...draggableProvided.draggableProps}
{...draggableProvided.dragHandleProps}
style={getItemStyle(
draggableSnapshot.isDragging,
draggableProvided.draggableProps.style,
itemComponent,
droppableSnapshot.isDraggingOver,
)}
>
{!allItems[key].locked &&
<Icons.Trash
onClick={() => this.deleteItem(key)}
className="sidebar_editor_deleteBtn"
/>
}
{itemComponent.icon && createIconRender(itemComponent.icon)}
{itemComponent.title ?? itemComponent.id}
</div>
)}
</Draggable>
)
})}
{droppableProvided.placeholder}
</div>
)}
</Droppable>
</DragDropContext>
<ActionsBar
style={{ position: "absolute", bottom: 0, left: 0, width: "100%", borderRadius: "12px 12px 0 0" }}
>
<div>
<Button
style={{ lineHeight: 0 }}
icon={<Icons.Plus style={{ margin: 0, padding: 0 }} />}
onClick={this.addItem}
/>
</div>
<div>
<Button
style={{ lineHeight: 0 }}
icon={<Icons.Check />}
type="primary"
onClick={this.onSave}
>
Done
</Button>
</div>
<div>
<Button onClick={this.onDiscard} icon={<Icons.XCircle />} >Cancel</Button>
</div>
<div>
<Button type="link" onClick={this.onSetDefaults}>Set defaults</Button>
</div>
</ActionsBar>
</div>
)
}
}

View File

@ -1,14 +0,0 @@
.app_sidebar_sider_edit .ant-layout-sider-children{
margin-top: 15px!important;
.app_sidebar_menu_wrapper {
opacity: 0;
height: 0;
overflow: hidden;
}
}
.sidebar_editor_deleteBtn:hover{
color: red;
cursor: pointer;
}

View File

@ -1 +0,0 @@
export { default as SidebarEditor } from './editor';

View File

@ -1,76 +0,0 @@
import React from "react"
import { Icons, createIconRender } from "components/Icons"
import { SelectableList } from "components"
import { List } from "antd"
import sidebarItems from "schemas/sidebar"
import "./index.less"
const getStoragedKeys = () => {
return window.app.cores.settings.get("sidebarKeys") ?? []
}
const getAllItems = () => {
const obj = {}
sidebarItems.forEach((item) => {
obj[item.id] = item
})
return obj
}
const allItems = getAllItems()
export default class SidebarItemSelector extends React.Component {
state = {
items: null,
}
componentDidMount = () => {
const source = (this.props.items ?? getStoragedKeys() ?? []).map((key) => {
return { key }
})
this.setState({ items: source })
}
handleDone = (selectedKeys) => {
if (typeof this.props.onDone === "function") {
this.props.onDone(selectedKeys)
}
}
render() {
return (
<div>
<h1>
<Icons.PlusCircle /> Select items to add
</h1>
{this.state.items && (
<SelectableList
itemLayout="vertical"
size="large"
pagination={{
pageSize: 10,
}}
onDone={this.handleDone}
items={this.state.items ?? []}
itemClassName="sidebar_selector_item"
renderItem={(i) => {
const item = allItems[i.key]
return (
<List.Item key={item.title} className="sidebar_selector_item">
{createIconRender(item.icon)}
{item.title ?? item.id}
</List.Item>
)
}}
/>
)}
</div>
)
}
}

View File

@ -1,4 +0,0 @@
.sidebar_selector_item{
height: fit-content;
padding: 0;
}

View File

@ -1,9 +1,10 @@
import React from "react"
import { Menu, Avatar, Button, Dropdown } from "antd"
import { Translation } from "react-i18next"
import classnames from "classnames"
import config from "config"
import classnames from "classnames"
import { Translation } from "react-i18next"
import { Motion, spring } from "react-motion"
import { Menu, Avatar, Dropdown } from "antd"
import { Icons, createIconRender } from "components/Icons"
import sidebarItems from "schemas/sidebar"
@ -34,259 +35,136 @@ const onClickHandlers = {
}
}
const getSidebarComponents = () => {
const items = {}
sidebarItems.forEach((item, index) => {
items[item.id] = {
...item,
index,
content: (
<>
{createIconRender(item.icon)} {item.title}
</>
),
const generateTopItems = () => {
return sidebarItems.map((item) => {
return {
key: item.id,
icon: createIconRender(item.icon),
label: <Translation>
{t => t(item.title ?? item.id)}
</Translation>,
disabled: item.disabled,
children: item.children,
}
})
return items
}
const generateItems = () => {
const components = getSidebarComponents()
const itemsMap = []
const pathResolvers = {}
Object.keys(components).forEach((key, index) => {
const component = components[key]
if (typeof component.path !== "undefined") {
pathResolvers[component.id] = component.path
}
itemsMap.push(component)
})
return {
itemsMap,
pathResolvers,
const ActionMenuItems = [
{
key: "account",
label: <>
<Icons.User />
<Translation>
{t => t("Account")}
</Translation>
</>,
},
{
type: "divider"
},
{
key: "switch_account",
label: <>
<Icons.MdSwitchAccount />
<Translation>
{t => t("Switch account")}
</Translation>
</>,
},
{
key: "logout",
label: <>
<Icons.LogOut />
<Translation>
{t => t("Logout")}
</Translation>
</>,
danger: true
}
}
const CustomRender = (props) => {
const handleClickOutside = (event) => {
if (props.sidebarRef.current && !props.sidebarRef.current.contains(event.target)) {
if (event.target.closest(".ant-select-item")) {
return
}
props.closeRender()
}
}
React.useEffect(() => {
document.addEventListener("mousedown", handleClickOutside)
return () => {
document.removeEventListener("mousedown", handleClickOutside)
}
}, [])
return <div className="render_content_wrapper">
<div className="render_content_header">
{
props.customRenderTitle ?? null
}
<Button
onClick={props.closeRender}
>
Close
</Button>
</div>
<div className="render_content">
{props.children}
</div>
</div>
}
const handleRenderIcon = (icon) => {
if (typeof icon === "undefined") {
return null
}
return createIconRender(icon)
}
]
export default class Sidebar extends React.Component {
constructor(props) {
super(props)
state = {
visible: false,
expanded: false,
this.controller = window.app["SidebarController"] = {
toggleVisibility: this.toggleVisibility,
toggleElevation: this.toggleElevation,
toggleCollapse: this.toggleExpanded,
isVisible: () => this.state.visible,
isExpanded: () => this.state.expanded,
setCustomRender: this.setRender,
closeCustomRender: this.closeRender,
updateBottomItemProps: (id, newProps) => {
let updatedValue = this.state.bottomItems
updatedValue = updatedValue.map((item) => {
if (item.id === id) {
item.props = {
...item.props,
...newProps,
}
}
})
this.setState({
bottomItems: updatedValue
})
},
attachBottomItem: (id, children, options) => {
if (!id) {
throw new Error("ID is required")
}
if (!children) {
throw new Error("Children is required")
}
if (this.state.bottomItems.find((item) => item.id === id)) {
throw new Error("Item already exists")
}
let updatedValue = this.state.bottomItems
updatedValue.push({
id,
children,
...options
})
this.setState({
bottomItems: updatedValue
})
},
removeBottomItem: (id) => {
let updatedValue = this.state.bottomItems
updatedValue = updatedValue.filter((item) => item.id !== id)
this.setState({
bottomItems: updatedValue
})
}
}
this.state = {
visible: false,
elevated: false,
expanded: false,
dropdownOpen: false,
pathResolvers: null,
menus: null,
customRenderTitle: null,
customRender: null,
bottomItems: [],
}
// handle sidedrawer open/close
window.app.eventBus.on("sidedrawer.hasDrawers", () => {
this.toggleElevation(true)
})
window.app.eventBus.on("sidedrawer.noDrawers", () => {
this.toggleElevation(false)
})
topItems: generateTopItems(),
bottomItems: [],
}
sidebarRef = React.createRef()
collapseDebounce = null
componentDidMount = async () => {
await this.loadItems()
interface = window.app.layout.sidebar = {
toggleVisibility: this.toggleVisibility,
toggleCollapse: this.toggleExpanded,
isVisible: () => this.state.visible,
isExpanded: () => this.state.expanded,
updateBottomItemProps: (id, newProps) => {
let updatedValue = this.state.bottomItems
setTimeout(() => {
this.controller.toggleVisibility(true)
}, 100)
}
setRender = async (render, options = {}) => {
if (!typeof render === "function") {
throw new Error("Render is required to be a function")
}
await this.setState({
customRenderTitle: <div className="render_content_header_title">
{
options.icon && createIconRender(options.icon)
updatedValue = updatedValue.map((item) => {
if (item.id === id) {
item.props = {
...item.props,
...newProps,
}
}
{
options.title && <h1>
<Translation>
{t => t(options.title)}
</Translation>
</h1>
}
</div>,
customRender: React.createElement(render, {
...options.props ?? {},
close: this.closeRender,
})
})
}
closeRender = () => {
this.setState({
customRenderTitle: null,
customRender: null,
})
}
loadItems = async () => {
const generation = generateItems()
// update states
await this.setState({
menus: generation.itemsMap,
pathResolvers: generation.pathResolvers,
})
}
renderMenuItems(items) {
return items.map((item) => {
if (Array.isArray(item.children)) {
return <Menu.SubMenu
key={item.id}
icon={handleRenderIcon(item.icon)}
title={<span>
<Translation>
{t => t(item.title)}
</Translation>
</span>}
{...item.props}
disabled={item.disabled ?? false}
>
{this.renderMenuItems(item.children)}
</Menu.SubMenu>
this.setState({
bottomItems: updatedValue
})
},
attachBottomItem: (id, children, options) => {
if (!id) {
throw new Error("ID is required")
}
return <Menu.Item
key={item.id}
icon={handleRenderIcon(item.icon)}
disabled={item.disabled ?? false}
{...item.props}
>
<Translation>
{t => t(item.title ?? item.id)}
</Translation>
</Menu.Item>
})
if (!children) {
throw new Error("Children is required")
}
if (this.state.bottomItems.find((item) => item.id === id)) {
throw new Error("Item already exists")
}
let updatedValue = this.state.bottomItems
updatedValue.push({
id,
children,
...options
})
this.setState({
bottomItems: updatedValue
})
},
removeBottomItem: (id) => {
let updatedValue = this.state.bottomItems
updatedValue = updatedValue.filter((item) => item.id !== id)
this.setState({
bottomItems: updatedValue
})
},
}
componentDidMount = async () => {
setTimeout(() => {
this.toggleVisibility(true)
if (app.cores.settings.is("sidebar.collapsable", false)) {
this.toggleExpanded(true)
}
}, 10)
}
componentWillUnmount = () => {
delete app.layout.sidebar
}
handleClick = (e) => {
@ -309,13 +187,9 @@ export default class Sidebar extends React.Component {
window.app.cores.sound.useUIAudio("sidebar.switch_tab")
if (typeof this.state.pathResolvers === "object") {
if (typeof this.state.pathResolvers[e.key] !== "undefined") {
return window.app.location.push(`/${this.state.pathResolvers[e.key]}`, 150)
}
}
const item = sidebarItems.find((item) => item.id === e.key)
return window.app.location.push(`/${e.key}`, 150)
return window.app.location.push(`/${item.path ?? e.key}`, 150)
}
toggleExpanded = (to, force) => {
@ -337,7 +211,7 @@ export default class Sidebar extends React.Component {
if (!to) {
this.collapseDebounce = setTimeout(() => {
this.setState({ expanded: to })
}, window.app.cores.settings.get("autoCollapseDelay") ?? 500)
}, window.app.cores.settings.get("sidebar.collapse_delay_time") ?? 500)
} else {
this.setState({ expanded: to })
}
@ -349,14 +223,16 @@ export default class Sidebar extends React.Component {
this.setState({ visible: to ?? !this.state.visible })
}
toggleElevation = (to) => {
this.setState({ elevated: to ?? !this.state.elevated })
}
onMouseEnter = () => {
if (!this.state.visible) return
if (window.app.cores.settings.is("collapseOnLooseFocus", false)) return
if (window.app.cores.settings.is("sidebar.collapsable", false)) {
if (!this.state.expanded) {
this.toggleExpanded(true)
}
return
}
this.toggleExpanded(true)
}
@ -364,7 +240,7 @@ export default class Sidebar extends React.Component {
handleMouseLeave = () => {
if (!this.state.visible) return
if (window.app.cores.settings.is("collapseOnLooseFocus", false)) return
if (window.app.cores.settings.is("sidebar.collapsable", false)) return
this.toggleExpanded(false)
}
@ -378,42 +254,6 @@ export default class Sidebar extends React.Component {
this.setState({ dropdownOpen: to })
}
generateDropdownItems = () => {
return [
{
key: "account",
label: <>
<Icons.User />
<Translation>
{t => t("Account")}
</Translation>
</>,
},
{
type: "divider"
},
{
key: "switch_account",
label: <>
<Icons.MdSwitchAccount />
<Translation>
{t => t("Switch account")}
</Translation>
</>,
},
{
key: "logout",
label: <>
<Icons.LogOut />
<Translation>
{t => t("Logout")}
</Translation>
</>,
danger: true
}
]
}
onClickDropdownItem = (item) => {
const handler = onClickHandlers[item.key]
@ -423,123 +263,128 @@ export default class Sidebar extends React.Component {
}
render() {
if (!this.state.menus) return null
const defaultSelectedKey = window.location.pathname.replace("/", "")
return <div
onMouseEnter={this.onMouseEnter}
onMouseLeave={this.handleMouseLeave}
className={
classnames(
"app_sidebar",
{
["customRender"]: this.state.customRender,
["floating"]: window.app?.cores.settings.get("sidebar.floating"),
["elevated"]: this.state.visible && this.state.elevated,
["expanded"]: this.state.visible && this.state.expanded,
["hidden"]: !this.state.visible,
}
)
}
ref={this.sidebarRef}
>
{
this.state.customRender && <CustomRender
customRenderTitle={this.state.customRenderTitle}
closeRender={this.closeRender}
sidebarRef={this.sidebarRef}
return <Motion style={{
x: spring(!this.state.visible ? 100 : 0),
}}>
{({ x }) => {
return <div
className="app_sidebar_wrapper"
style={{
transform: `translateX(-${x}%)`,
}}
>
{this.state.customRender}
</CustomRender>
}
<div
onMouseEnter={this.onMouseEnter}
onMouseLeave={this.handleMouseLeave}
className={classnames(
"app_sidebar",
{
["expanded"]: this.state.visible && this.state.expanded,
["hidden"]: !this.state.visible,
}
)
}
ref={this.sidebarRef}
>
{
!this.state.customRender && <>
<div className="app_sidebar_header">
<div className="app_sidebar_header_logo">
<img src={config.logo?.alt} />
<div className="app_sidebar_header">
<div className="app_sidebar_header_logo">
<img src={config.logo?.alt} />
</div>
</div>
</div>
<div key="menu" className="app_sidebar_menu_wrapper">
<Menu
selectable={true}
mode="inline"
onClick={this.handleClick}
defaultSelectedKeys={[defaultSelectedKey]}
<div key="menu" className="app_sidebar_menu_wrapper">
<Menu
mode="inline"
onClick={this.handleClick}
defaultSelectedKeys={[defaultSelectedKey]}
items={this.state.topItems}
selectable
/>
</div>
<div
key="bottom"
className={classnames(
"app_sidebar_menu_wrapper",
"bottom"
)}
>
{this.renderMenuItems(this.state.menus)}
</Menu>
</div>
<div key="bottom" className={classnames("app_sidebar_menu_wrapper", "bottom")}>
<Menu selectable={false} mode="inline" onClick={this.handleClick}>
{
this.state.bottomItems.map((item) => {
if (item.noContainer) {
return React.createElement(item.children, item.childrenProps)
}
return <Menu.Item
key={item.id}
className="extra_bottom_item"
icon={handleRenderIcon(item.icon)}
disabled={item.disabled ?? false}
{...item.containerProps}
>
{
React.createElement(item.children, item.childrenProps)
<Menu
selectable={false}
mode="inline"
onClick={this.handleClick}
>
{
this.state.bottomItems.map((item) => {
if (item.noContainer) {
return React.createElement(item.children, item.childrenProps)
}
</Menu.Item>
})
}
<Menu.Item key="search" icon={<Icons.Search />} >
<Translation>
{(t) => t("Search")}
</Translation>
</Menu.Item>
<Menu.Item key="notifications" icon={<Icons.Bell />}>
<Translation>
{t => t("Notifications")}
</Translation>
</Menu.Item>
<Menu.Item key="settings" icon={<Icons.Settings />}>
<Translation>
{t => t("Settings")}
</Translation>
</Menu.Item>
{
app.userData && <Dropdown
menu={{
items: this.generateDropdownItems(),
onClick: this.onClickDropdownItem
}}
autoFocus
placement="top"
trigger={["click"]}
onOpenChange={this.onDropdownOpenChange}
>
<Menu.Item
key="account"
className="user_avatar"
ignoreClick
>
<Avatar shape="square" src={app.userData?.avatar} />
</Menu.Item>
</Dropdown>
}
{
!app.userData && <Menu.Item key="login" icon={<Icons.LogIn />}>
return <Menu.Item
key={item.id}
className="extra_bottom_item"
icon={createIconRender(item.icon)}
disabled={item.disabled ?? false}
{...item.containerProps}
>
{
React.createElement(item.children, item.childrenProps)
}
</Menu.Item>
})
}
<Menu.Item key="search" icon={<Icons.Search />} >
<Translation>
{t => t("Login")}
{(t) => t("Search")}
</Translation>
</Menu.Item>
}
</Menu>
<Menu.Item key="notifications" icon={<Icons.Bell />}>
<Translation>
{t => t("Notifications")}
</Translation>
</Menu.Item>
<Menu.Item key="settings" icon={<Icons.Settings />}>
<Translation>
{t => t("Settings")}
</Translation>
</Menu.Item>
{
app.userData && <Dropdown
menu={{
items: ActionMenuItems,
onClick: this.onClickDropdownItem
}}
autoFocus
placement="top"
trigger={["click"]}
onOpenChange={this.onDropdownOpenChange}
>
<Menu.Item
key="account"
className="user_avatar"
ignoreClick
>
<Avatar shape="square" src={app.userData?.avatar} />
</Menu.Item>
</Dropdown>
}
{
!app.userData && <Menu.Item key="login" icon={<Icons.LogIn />}>
<Translation>
{t => t("Login")}
</Translation>
</Menu.Item>
}
</Menu>
</div>
</div>
</>
}
</div>
</div>
}}
</Motion>
}
}

View File

@ -1,20 +1,32 @@
@import "theme/vars.less";
.app_sidebar_wrapper {
position: relative;
left: 0;
top: 0;
z-index: 1000;
width: fit-content;
height: 100vh;
height: 100dvh;
padding: 10px;
}
.app_sidebar {
position: relative;
display: flex;
flex-direction: column;
left: 0;
top: 0;
z-index: 1000;
overflow: hidden;
width: @app_sidebar_width;
min-width: @app_sidebar_width;
width: @sidebar_width;
min-width: @sidebar_width;
height: 100dvh;
height: 100%;
padding: 10px 0;
@ -22,16 +34,14 @@
background-color: var(--sidebar-background-color);
border-radius: 0 @app_sidebar_borderRadius @app_sidebar_borderRadius 0;
border-radius: @sidebar_borderRadius;
box-shadow: @card-shadow;
//border-radius: 0 @sidebar_borderRadius @sidebar_borderRadius 0;
border: 1px solid var(--sidebar-background-color);
&.floating {
position: absolute;
box-shadow: 0 0 5px 4px rgba(0, 0, 0, 0.1) !important;
}
&.expanded {
width: @app_sidebar_width_expanded;
width: @sidebar_width_expanded;
.app_sidebar_menu_wrapper {
.ant-menu {
@ -50,34 +60,10 @@
}
}
&.customRender {
display: flex;
position: relative;
width: 100%;
min-width: 34vw;
padding: 20px;
max-width: 60vw;
.app_sidebar_menu_wrapper {
animation: disappear 150ms ease-out forwards;
}
.render_content_wrapper {
animation: appear 150ms ease-out forwards;
}
}
&.hidden {
flex: 0 !important;
min-width: 0 !important;
background-color: transparent !important;
width: 0 !important;
}
&.elevated {
box-shadow: 0 0 5px 4px rgba(0, 0, 0, 0.1) !important;
width: 0;
min-width: 0;
padding: 0;
}
.app_sidebar_header {
@ -154,7 +140,7 @@
width: 100%;
padding: 0 0 0 calc(@app_sidebar_width / 2);
padding: 0 0 0 calc(@sidebar_width / 2);
margin: 5px 0;
transition: all 150ms ease-in-out;
@ -191,50 +177,4 @@
}
}
}
.render_content_wrapper {
display: flex;
flex-direction: column;
opacity: 0;
transition: all 150ms ease-in-out;
overflow-y: hidden;
.render_content_header {
display: inline-flex;
flex-direction: row;
align-items: center;
justify-content: space-between;
.render_content_header_title {
display: inline-flex;
flex-direction: row;
align-items: center;
h1,
h2,
h3 {
font-size: 1.8;
margin: 0 !important;
}
svg {
font-size: 1.8rem;
}
}
}
.render_content {
height: 100%;
padding: 20px;
padding-left: 0;
overflow-x: hidden;
overflow-y: scroll;
}
}
}

View File

@ -467,11 +467,11 @@ export default class Player extends Core {
}
case "minimized": {
if (change.object.minimized) {
app.SidebarController.attachBottomItem("player", BackgroundMediaPlayer, {
app.layout.sidebar.attachBottomItem("player", BackgroundMediaPlayer, {
noContainer: true
})
} else {
app.SidebarController.removeBottomItem("player")
app.layout.sidebar.removeBottomItem("player")
}
app.eventBus.emit("player.minimized.update", change.object.minimized)

View File

@ -313,8 +313,8 @@ export default class StreamViewer extends React.Component {
to = !this.state.cinemaMode
}
if (app.SidebarController) {
app.SidebarController.toggleVisibility(!to)
if (app.layout.sidebar) {
app.layout.sidebar.toggleVisibility(!to)
}
this.setState({ cinemaMode: to })

View File

@ -601,8 +601,8 @@ export default class SyncLyrics extends React.Component {
colorAnalysis,
})
if (app.SidebarController) {
app.SidebarController.toggleVisibility(false)
if (app.layout.sidebar) {
app.layout.sidebar.toggleVisibility(false)
}
if (app.layout.floatingStack) {
@ -646,8 +646,8 @@ export default class SyncLyrics extends React.Component {
delete window._hacks
if (app.SidebarController) {
app.SidebarController.toggleVisibility(true)
if (app.layout.sidebar) {
app.layout.sidebar.toggleVisibility(true)
}
if (app.layout.floatingStack) {

View File

@ -213,10 +213,6 @@ html {
padding: var(--layoutPadding);
background-color: transparent;
&.floating-sidebar {
margin-left: @app_sidebar_width;
}
}
.root_background {

View File

@ -1,14 +1,8 @@
@app_sidebar_width: 80px;
@app_sidebar_width_expanded: 230px;
@sidebar_width: 80px;
@sidebar_width_expanded: 230px;
@sidebar_padding: 10px;
@sidebar_borderRadius: 18px;
// borders & radius
@app_sidebar_borderRadius: 18px;
@app_menuItemSize: 100px;
@app_menuItemIconSize: 30px;
@app_menuItemTextSize: 12px;
// TRANSITIONS
@transition-ease-in: all 0.3s ease-out;
@transition-ease-out: all 0.3s ease-out;
@transition-ease-inout: all 150ms ease-in-out;
@ -20,5 +14,5 @@
@top_bar_height: 52px;
@top_bar_padding: 10px;
@app_bottomBar_iconSize: 45px;
@app_topBar_height: 52px;
@bottomBar_iconSize: 45px;
@topBar_height: 52px;