mirror of
https://github.com/ragestudio/comty.git
synced 2025-06-09 18:44:16 +00:00
refactor & restyle sidebar
This commit is contained in:
parent
8787dd16f4
commit
e28efd1698
@ -6,14 +6,12 @@
|
|||||||
"notifications_vibrate": true,
|
"notifications_vibrate": true,
|
||||||
"notifications_sound_volume": 50,
|
"notifications_sound_volume": 50,
|
||||||
"longPressDelay": 600,
|
"longPressDelay": 600,
|
||||||
"autoCollapseDelay": 500,
|
"sidebar.collapsable": true,
|
||||||
"autoCollapseDelayEnabled": true,
|
"sidebar.collapse_delay_time": 500,
|
||||||
"haptic_feedback": true,
|
"haptic_feedback": true,
|
||||||
"collapseOnLooseFocus": true,
|
|
||||||
"style.auto_darkMode": true,
|
"style.auto_darkMode": true,
|
||||||
"feed_max_fetch": 20,
|
"feed_max_fetch": 20,
|
||||||
"style.compactMode": false,
|
"style.compactMode": false,
|
||||||
"sidebar.floating": false,
|
|
||||||
"player.compressor": false,
|
"player.compressor": false,
|
||||||
"language": "en"
|
"language": "en"
|
||||||
}
|
}
|
@ -12,17 +12,6 @@ export default {
|
|||||||
group: "app",
|
group: "app",
|
||||||
order: 1,
|
order: 1,
|
||||||
settings: [
|
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",
|
id: "style.reduceAnimations",
|
||||||
group: "animations",
|
group: "animations",
|
||||||
|
@ -148,24 +148,23 @@ export default {
|
|||||||
mobile: false,
|
mobile: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "collapseOnLooseFocus",
|
id: "sidebar.collapsable",
|
||||||
storaged: true,
|
|
||||||
group: "sidebar",
|
group: "sidebar",
|
||||||
component: "Switch",
|
component: "Switch",
|
||||||
icon: "Columns",
|
icon: "Columns",
|
||||||
title: "Auto Collapse",
|
title: "Auto Collapse",
|
||||||
description: "Collapse the sidebar when loose focus",
|
description: "Allow to collapse the sidebar when loose focus.",
|
||||||
emitEvent: "settingChanged.sidebar_collapse",
|
emitEvent: "settingChanged.sidebar_collapse",
|
||||||
|
storaged: true,
|
||||||
mobile: false,
|
mobile: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "autoCollapseDelay",
|
id: "sidebar.collapse_delay_time",
|
||||||
storaged: true,
|
|
||||||
group: "sidebar",
|
group: "sidebar",
|
||||||
component: "Slider",
|
component: "Slider",
|
||||||
icon: "MdTimer",
|
icon: "MdTimer",
|
||||||
dependsOn: {
|
dependsOn: {
|
||||||
"collapseOnLooseFocus": true
|
"sidebar.collapsable": true
|
||||||
},
|
},
|
||||||
title: "Auto Collapse timeout",
|
title: "Auto Collapse timeout",
|
||||||
description: "Set the delay before the sidebar is collapsed",
|
description: "Set the delay before the sidebar is collapsed",
|
||||||
@ -181,6 +180,7 @@ export default {
|
|||||||
2000: "2s",
|
2000: "2s",
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
storaged: true,
|
||||||
mobile: false,
|
mobile: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -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>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
@ -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;
|
|
||||||
}
|
|
@ -1 +0,0 @@
|
|||||||
export { default as SidebarEditor } from './editor';
|
|
@ -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>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,4 +0,0 @@
|
|||||||
.sidebar_selector_item{
|
|
||||||
height: fit-content;
|
|
||||||
padding: 0;
|
|
||||||
}
|
|
@ -1,9 +1,10 @@
|
|||||||
import React from "react"
|
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 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 { Icons, createIconRender } from "components/Icons"
|
||||||
|
|
||||||
import sidebarItems from "schemas/sidebar"
|
import sidebarItems from "schemas/sidebar"
|
||||||
@ -34,259 +35,136 @@ const onClickHandlers = {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const getSidebarComponents = () => {
|
const generateTopItems = () => {
|
||||||
const items = {}
|
return sidebarItems.map((item) => {
|
||||||
|
return {
|
||||||
sidebarItems.forEach((item, index) => {
|
key: item.id,
|
||||||
items[item.id] = {
|
icon: createIconRender(item.icon),
|
||||||
...item,
|
label: <Translation>
|
||||||
index,
|
{t => t(item.title ?? item.id)}
|
||||||
content: (
|
</Translation>,
|
||||||
<>
|
disabled: item.disabled,
|
||||||
{createIconRender(item.icon)} {item.title}
|
children: item.children,
|
||||||
</>
|
|
||||||
),
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
return items
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const generateItems = () => {
|
const ActionMenuItems = [
|
||||||
const components = getSidebarComponents()
|
{
|
||||||
|
key: "account",
|
||||||
const itemsMap = []
|
label: <>
|
||||||
const pathResolvers = {}
|
<Icons.User />
|
||||||
|
<Translation>
|
||||||
Object.keys(components).forEach((key, index) => {
|
{t => t("Account")}
|
||||||
const component = components[key]
|
</Translation>
|
||||||
|
</>,
|
||||||
if (typeof component.path !== "undefined") {
|
},
|
||||||
pathResolvers[component.id] = component.path
|
{
|
||||||
}
|
type: "divider"
|
||||||
|
},
|
||||||
itemsMap.push(component)
|
{
|
||||||
})
|
key: "switch_account",
|
||||||
|
label: <>
|
||||||
return {
|
<Icons.MdSwitchAccount />
|
||||||
itemsMap,
|
<Translation>
|
||||||
pathResolvers,
|
{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 {
|
export default class Sidebar extends React.Component {
|
||||||
constructor(props) {
|
state = {
|
||||||
super(props)
|
visible: false,
|
||||||
|
expanded: false,
|
||||||
|
|
||||||
this.controller = window.app["SidebarController"] = {
|
topItems: generateTopItems(),
|
||||||
toggleVisibility: this.toggleVisibility,
|
bottomItems: [],
|
||||||
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)
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
sidebarRef = React.createRef()
|
sidebarRef = React.createRef()
|
||||||
|
|
||||||
collapseDebounce = null
|
collapseDebounce = null
|
||||||
|
|
||||||
componentDidMount = async () => {
|
interface = window.app.layout.sidebar = {
|
||||||
await this.loadItems()
|
toggleVisibility: this.toggleVisibility,
|
||||||
|
toggleCollapse: this.toggleExpanded,
|
||||||
|
isVisible: () => this.state.visible,
|
||||||
|
isExpanded: () => this.state.expanded,
|
||||||
|
updateBottomItemProps: (id, newProps) => {
|
||||||
|
let updatedValue = this.state.bottomItems
|
||||||
|
|
||||||
setTimeout(() => {
|
updatedValue = updatedValue.map((item) => {
|
||||||
this.controller.toggleVisibility(true)
|
if (item.id === id) {
|
||||||
}, 100)
|
item.props = {
|
||||||
}
|
...item.props,
|
||||||
|
...newProps,
|
||||||
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)
|
|
||||||
}
|
}
|
||||||
{
|
|
||||||
options.title && <h1>
|
|
||||||
<Translation>
|
|
||||||
{t => t(options.title)}
|
|
||||||
</Translation>
|
|
||||||
</h1>
|
|
||||||
}
|
|
||||||
</div>,
|
|
||||||
customRender: React.createElement(render, {
|
|
||||||
...options.props ?? {},
|
|
||||||
close: this.closeRender,
|
|
||||||
})
|
})
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
closeRender = () => {
|
this.setState({
|
||||||
this.setState({
|
bottomItems: updatedValue
|
||||||
customRenderTitle: null,
|
})
|
||||||
customRender: null,
|
},
|
||||||
})
|
attachBottomItem: (id, children, options) => {
|
||||||
}
|
if (!id) {
|
||||||
|
throw new Error("ID is required")
|
||||||
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>
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return <Menu.Item
|
if (!children) {
|
||||||
key={item.id}
|
throw new Error("Children is required")
|
||||||
icon={handleRenderIcon(item.icon)}
|
}
|
||||||
disabled={item.disabled ?? false}
|
|
||||||
{...item.props}
|
if (this.state.bottomItems.find((item) => item.id === id)) {
|
||||||
>
|
throw new Error("Item already exists")
|
||||||
<Translation>
|
}
|
||||||
{t => t(item.title ?? item.id)}
|
|
||||||
</Translation>
|
let updatedValue = this.state.bottomItems
|
||||||
</Menu.Item>
|
|
||||||
})
|
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) => {
|
handleClick = (e) => {
|
||||||
@ -309,13 +187,9 @@ export default class Sidebar extends React.Component {
|
|||||||
|
|
||||||
window.app.cores.sound.useUIAudio("sidebar.switch_tab")
|
window.app.cores.sound.useUIAudio("sidebar.switch_tab")
|
||||||
|
|
||||||
if (typeof this.state.pathResolvers === "object") {
|
const item = sidebarItems.find((item) => item.id === e.key)
|
||||||
if (typeof this.state.pathResolvers[e.key] !== "undefined") {
|
|
||||||
return window.app.location.push(`/${this.state.pathResolvers[e.key]}`, 150)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return window.app.location.push(`/${e.key}`, 150)
|
return window.app.location.push(`/${item.path ?? e.key}`, 150)
|
||||||
}
|
}
|
||||||
|
|
||||||
toggleExpanded = (to, force) => {
|
toggleExpanded = (to, force) => {
|
||||||
@ -337,7 +211,7 @@ export default class Sidebar extends React.Component {
|
|||||||
if (!to) {
|
if (!to) {
|
||||||
this.collapseDebounce = setTimeout(() => {
|
this.collapseDebounce = setTimeout(() => {
|
||||||
this.setState({ expanded: to })
|
this.setState({ expanded: to })
|
||||||
}, window.app.cores.settings.get("autoCollapseDelay") ?? 500)
|
}, window.app.cores.settings.get("sidebar.collapse_delay_time") ?? 500)
|
||||||
} else {
|
} else {
|
||||||
this.setState({ expanded: to })
|
this.setState({ expanded: to })
|
||||||
}
|
}
|
||||||
@ -349,14 +223,16 @@ export default class Sidebar extends React.Component {
|
|||||||
this.setState({ visible: to ?? !this.state.visible })
|
this.setState({ visible: to ?? !this.state.visible })
|
||||||
}
|
}
|
||||||
|
|
||||||
toggleElevation = (to) => {
|
|
||||||
this.setState({ elevated: to ?? !this.state.elevated })
|
|
||||||
}
|
|
||||||
|
|
||||||
onMouseEnter = () => {
|
onMouseEnter = () => {
|
||||||
if (!this.state.visible) return
|
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)
|
this.toggleExpanded(true)
|
||||||
}
|
}
|
||||||
@ -364,7 +240,7 @@ export default class Sidebar extends React.Component {
|
|||||||
handleMouseLeave = () => {
|
handleMouseLeave = () => {
|
||||||
if (!this.state.visible) return
|
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)
|
this.toggleExpanded(false)
|
||||||
}
|
}
|
||||||
@ -378,42 +254,6 @@ export default class Sidebar extends React.Component {
|
|||||||
this.setState({ dropdownOpen: to })
|
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) => {
|
onClickDropdownItem = (item) => {
|
||||||
const handler = onClickHandlers[item.key]
|
const handler = onClickHandlers[item.key]
|
||||||
|
|
||||||
@ -423,123 +263,128 @@ export default class Sidebar extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
if (!this.state.menus) return null
|
|
||||||
|
|
||||||
const defaultSelectedKey = window.location.pathname.replace("/", "")
|
const defaultSelectedKey = window.location.pathname.replace("/", "")
|
||||||
|
|
||||||
return <div
|
return <Motion style={{
|
||||||
onMouseEnter={this.onMouseEnter}
|
x: spring(!this.state.visible ? 100 : 0),
|
||||||
onMouseLeave={this.handleMouseLeave}
|
}}>
|
||||||
className={
|
{({ x }) => {
|
||||||
classnames(
|
return <div
|
||||||
"app_sidebar",
|
className="app_sidebar_wrapper"
|
||||||
{
|
style={{
|
||||||
["customRender"]: this.state.customRender,
|
transform: `translateX(-${x}%)`,
|
||||||
["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}
|
|
||||||
>
|
>
|
||||||
{this.state.customRender}
|
<div
|
||||||
</CustomRender>
|
onMouseEnter={this.onMouseEnter}
|
||||||
}
|
onMouseLeave={this.handleMouseLeave}
|
||||||
|
className={classnames(
|
||||||
|
"app_sidebar",
|
||||||
|
{
|
||||||
|
["expanded"]: this.state.visible && this.state.expanded,
|
||||||
|
["hidden"]: !this.state.visible,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
ref={this.sidebarRef}
|
||||||
|
>
|
||||||
|
|
||||||
{
|
<div className="app_sidebar_header">
|
||||||
!this.state.customRender && <>
|
<div className="app_sidebar_header_logo">
|
||||||
<div className="app_sidebar_header">
|
<img src={config.logo?.alt} />
|
||||||
<div className="app_sidebar_header_logo">
|
</div>
|
||||||
<img src={config.logo?.alt} />
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
|
|
||||||
<div key="menu" className="app_sidebar_menu_wrapper">
|
<div key="menu" className="app_sidebar_menu_wrapper">
|
||||||
<Menu
|
<Menu
|
||||||
selectable={true}
|
mode="inline"
|
||||||
mode="inline"
|
onClick={this.handleClick}
|
||||||
onClick={this.handleClick}
|
defaultSelectedKeys={[defaultSelectedKey]}
|
||||||
defaultSelectedKeys={[defaultSelectedKey]}
|
items={this.state.topItems}
|
||||||
|
selectable
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div
|
||||||
|
key="bottom"
|
||||||
|
className={classnames(
|
||||||
|
"app_sidebar_menu_wrapper",
|
||||||
|
"bottom"
|
||||||
|
)}
|
||||||
>
|
>
|
||||||
{this.renderMenuItems(this.state.menus)}
|
<Menu
|
||||||
</Menu>
|
selectable={false}
|
||||||
</div>
|
mode="inline"
|
||||||
|
onClick={this.handleClick}
|
||||||
<div key="bottom" className={classnames("app_sidebar_menu_wrapper", "bottom")}>
|
>
|
||||||
<Menu selectable={false} mode="inline" onClick={this.handleClick}>
|
{
|
||||||
{
|
this.state.bottomItems.map((item) => {
|
||||||
this.state.bottomItems.map((item) => {
|
if (item.noContainer) {
|
||||||
if (item.noContainer) {
|
return React.createElement(item.children, item.childrenProps)
|
||||||
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.Item>
|
|
||||||
})
|
return <Menu.Item
|
||||||
}
|
key={item.id}
|
||||||
<Menu.Item key="search" icon={<Icons.Search />} >
|
className="extra_bottom_item"
|
||||||
<Translation>
|
icon={createIconRender(item.icon)}
|
||||||
{(t) => t("Search")}
|
disabled={item.disabled ?? false}
|
||||||
</Translation>
|
{...item.containerProps}
|
||||||
</Menu.Item>
|
>
|
||||||
<Menu.Item key="notifications" icon={<Icons.Bell />}>
|
{
|
||||||
<Translation>
|
React.createElement(item.children, item.childrenProps)
|
||||||
{t => t("Notifications")}
|
}
|
||||||
</Translation>
|
</Menu.Item>
|
||||||
</Menu.Item>
|
})
|
||||||
<Menu.Item key="settings" icon={<Icons.Settings />}>
|
}
|
||||||
<Translation>
|
<Menu.Item key="search" icon={<Icons.Search />} >
|
||||||
{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 />}>
|
|
||||||
<Translation>
|
<Translation>
|
||||||
{t => t("Login")}
|
{(t) => t("Search")}
|
||||||
</Translation>
|
</Translation>
|
||||||
</Menu.Item>
|
</Menu.Item>
|
||||||
}
|
<Menu.Item key="notifications" icon={<Icons.Bell />}>
|
||||||
</Menu>
|
<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>
|
||||||
}
|
}}
|
||||||
</div>
|
</Motion>
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,20 +1,32 @@
|
|||||||
@import "theme/vars.less";
|
@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 {
|
.app_sidebar {
|
||||||
position: relative;
|
position: relative;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
|
||||||
left: 0;
|
|
||||||
top: 0;
|
|
||||||
|
|
||||||
z-index: 1000;
|
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
|
||||||
width: @app_sidebar_width;
|
width: @sidebar_width;
|
||||||
min-width: @app_sidebar_width;
|
min-width: @sidebar_width;
|
||||||
|
|
||||||
height: 100dvh;
|
height: 100%;
|
||||||
|
|
||||||
padding: 10px 0;
|
padding: 10px 0;
|
||||||
|
|
||||||
@ -22,16 +34,14 @@
|
|||||||
|
|
||||||
background-color: var(--sidebar-background-color);
|
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);
|
border: 1px solid var(--sidebar-background-color);
|
||||||
|
|
||||||
&.floating {
|
|
||||||
position: absolute;
|
|
||||||
box-shadow: 0 0 5px 4px rgba(0, 0, 0, 0.1) !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.expanded {
|
&.expanded {
|
||||||
width: @app_sidebar_width_expanded;
|
width: @sidebar_width_expanded;
|
||||||
|
|
||||||
.app_sidebar_menu_wrapper {
|
.app_sidebar_menu_wrapper {
|
||||||
.ant-menu {
|
.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 {
|
&.hidden {
|
||||||
flex: 0 !important;
|
width: 0;
|
||||||
min-width: 0 !important;
|
min-width: 0;
|
||||||
background-color: transparent !important;
|
padding: 0;
|
||||||
width: 0 !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.elevated {
|
|
||||||
box-shadow: 0 0 5px 4px rgba(0, 0, 0, 0.1) !important;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.app_sidebar_header {
|
.app_sidebar_header {
|
||||||
@ -154,7 +140,7 @@
|
|||||||
|
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
|
||||||
padding: 0 0 0 calc(@app_sidebar_width / 2);
|
padding: 0 0 0 calc(@sidebar_width / 2);
|
||||||
margin: 5px 0;
|
margin: 5px 0;
|
||||||
|
|
||||||
transition: all 150ms ease-in-out;
|
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
@ -467,11 +467,11 @@ export default class Player extends Core {
|
|||||||
}
|
}
|
||||||
case "minimized": {
|
case "minimized": {
|
||||||
if (change.object.minimized) {
|
if (change.object.minimized) {
|
||||||
app.SidebarController.attachBottomItem("player", BackgroundMediaPlayer, {
|
app.layout.sidebar.attachBottomItem("player", BackgroundMediaPlayer, {
|
||||||
noContainer: true
|
noContainer: true
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
app.SidebarController.removeBottomItem("player")
|
app.layout.sidebar.removeBottomItem("player")
|
||||||
}
|
}
|
||||||
|
|
||||||
app.eventBus.emit("player.minimized.update", change.object.minimized)
|
app.eventBus.emit("player.minimized.update", change.object.minimized)
|
||||||
|
@ -313,8 +313,8 @@ export default class StreamViewer extends React.Component {
|
|||||||
to = !this.state.cinemaMode
|
to = !this.state.cinemaMode
|
||||||
}
|
}
|
||||||
|
|
||||||
if (app.SidebarController) {
|
if (app.layout.sidebar) {
|
||||||
app.SidebarController.toggleVisibility(!to)
|
app.layout.sidebar.toggleVisibility(!to)
|
||||||
}
|
}
|
||||||
|
|
||||||
this.setState({ cinemaMode: to })
|
this.setState({ cinemaMode: to })
|
||||||
|
@ -601,8 +601,8 @@ export default class SyncLyrics extends React.Component {
|
|||||||
colorAnalysis,
|
colorAnalysis,
|
||||||
})
|
})
|
||||||
|
|
||||||
if (app.SidebarController) {
|
if (app.layout.sidebar) {
|
||||||
app.SidebarController.toggleVisibility(false)
|
app.layout.sidebar.toggleVisibility(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (app.layout.floatingStack) {
|
if (app.layout.floatingStack) {
|
||||||
@ -646,8 +646,8 @@ export default class SyncLyrics extends React.Component {
|
|||||||
|
|
||||||
delete window._hacks
|
delete window._hacks
|
||||||
|
|
||||||
if (app.SidebarController) {
|
if (app.layout.sidebar) {
|
||||||
app.SidebarController.toggleVisibility(true)
|
app.layout.sidebar.toggleVisibility(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (app.layout.floatingStack) {
|
if (app.layout.floatingStack) {
|
||||||
|
@ -213,10 +213,6 @@ html {
|
|||||||
padding: var(--layoutPadding);
|
padding: var(--layoutPadding);
|
||||||
|
|
||||||
background-color: transparent;
|
background-color: transparent;
|
||||||
|
|
||||||
&.floating-sidebar {
|
|
||||||
margin-left: @app_sidebar_width;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.root_background {
|
.root_background {
|
||||||
|
@ -1,14 +1,8 @@
|
|||||||
@app_sidebar_width: 80px;
|
@sidebar_width: 80px;
|
||||||
@app_sidebar_width_expanded: 230px;
|
@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-in: all 0.3s ease-out;
|
||||||
@transition-ease-out: all 0.3s ease-out;
|
@transition-ease-out: all 0.3s ease-out;
|
||||||
@transition-ease-inout: all 150ms ease-in-out;
|
@transition-ease-inout: all 150ms ease-in-out;
|
||||||
@ -20,5 +14,5 @@
|
|||||||
@top_bar_height: 52px;
|
@top_bar_height: 52px;
|
||||||
@top_bar_padding: 10px;
|
@top_bar_padding: 10px;
|
||||||
|
|
||||||
@app_bottomBar_iconSize: 45px;
|
@bottomBar_iconSize: 45px;
|
||||||
@app_topBar_height: 52px;
|
@topBar_height: 52px;
|
Loading…
x
Reference in New Issue
Block a user