improve layout components

This commit is contained in:
SrGooglo 2023-02-24 14:32:09 +00:00
parent a159a5b2e4
commit f706b510e9
11 changed files with 256 additions and 263 deletions

View File

@ -38,10 +38,10 @@ const AccountButton = (props) => {
const handleClick = () => { const handleClick = () => {
if (!user) { if (!user) {
return app.eventBus.emit("app.forceLogin") return app.navigation.goAuth()
} }
return app.goToAccount() return app.navigation.goToAccount()
} }
const handleHold = () => { const handleHold = () => {

View File

@ -74,7 +74,7 @@
&.primary { &.primary {
.icon { .icon {
color: var(--background-color-primary); color: var(--background-color-primary);
background-color: var(--primaryColor); background-color: var(--colorPrimary);
} }
} }
} }

58
packages/app/src/components/Layout/header/index.jsx Executable file → Normal file
View File

@ -1,49 +1,27 @@
import React from "react" import React from "react"
import * as antd from "antd"
import { Icons } from "components/Icons"
import { AppSearcher } from "components"
import classnames from "classnames" import classnames from "classnames"
import "./index.less" import "./index.less"
export default class Header extends React.Component { export default (props) => {
controller = window.app["HeaderController"] = { const [visible, setVisible] = React.useState(false)
toggleVisibility: (to) => {
if (window.isMobile) {
to = true
}
this.setState({ visible: to ?? !this.state.visible }) const headerInterface = {
}, toggle: (to) => setVisible((prevValue) => to ?? !prevValue),
isVisible: () => this.state.visible, }
}
state = { React.useEffect(() => {
visible: false, // set by default to create an animation app.layout.header = headerInterface
} }, [])
componentDidMount = async () => { return <div
setTimeout(() => { className={classnames(
this.controller.toggleVisibility(true) "page_header",
}, 100) {
} ["visible"]: visible,
}
render() { )}
return ( >
<antd.Layout.Header className={classnames(`app_header`, { ["hidden"]: !window.isMobile && !this.state.visible })}> {String(window.location.pathname).toTitleCase()}
<div> </div>
<antd.Button
onClick={window.app.openCreateNew}
type="primary"
shape="circle"
icon={<Icons.Plus style={{ margin: 0 }} />}
/>
</div>
{!window.isMobile &&
<div>
<AppSearcher />
</div>}
</antd.Layout.Header>
)
}
} }

45
packages/app/src/components/Layout/header/index.less Executable file → Normal file
View File

@ -1,32 +1,31 @@
@import "theme/index.less"; .page_header {
position: sticky;
.app_header { z-index: 100;
user-select: none;
--webkit-user-select: none;
display: flex; top: 0;
flex-direction: row; left: 0;
align-items: center;
z-index: 100;
height: @app_header_height !important; display: flex;
padding: 10px; flex-direction: row;
align-items: center;
justify-content: flex-start;
transition: all ease-in-out 150ms; overflow: hidden;
background-color: transparent; // hidden values
background: transparent !important; padding: 0;
opacity: 0;
height: 0px;
width: 100%;
border-bottom: 1px var(--border-color) solid; transition: all 150ms ease-in-out;
>div { backdrop-filter: blur(10px);
margin-right: 16px;
}
&.hidden { &.visible {
opacity: 0; opacity: 1;
height: 0 !important; height: 50px;
padding: 0 !important; padding: 20px;
border: 0 !important; }
}
} }

View File

@ -1,6 +1,6 @@
export { default as BottomBar } from "./bottomBar" export { default as BottomBar } from "./bottomBar"
export { default as Header } from "./header"
export { default as Drawer } from "./drawer" export { default as Drawer } from "./drawer"
export { default as Sidebar } from "./sidebar" export { default as Sidebar } from "./sidebar"
export { default as Sidedrawer } from "./sidedrawer" export { default as Sidedrawer } from "./sidedrawer"
export { default as Modal } from "./modal" export { default as Modal } from "./modal"
export { default as Header } from "./header"

View File

@ -4,7 +4,8 @@ import { DragDropContext, Droppable, Draggable } from "react-beautiful-dnd"
import { ActionsBar } from "components" import { ActionsBar } from "components"
import { Icons, createIconRender } from "components/Icons" import { Icons, createIconRender } from "components/Icons"
import sidebarItems from "schemas/routes.json"
import sidebarItems from "schemas/sidebar"
import { sidebarKeys as defaultSidebarKeys } from "schemas/defaultSettings" import { sidebarKeys as defaultSidebarKeys } from "schemas/defaultSettings"
import Selector from "../selector" import Selector from "../selector"
@ -46,7 +47,7 @@ export default class SidebarEditor extends React.Component {
} }
loadItems = () => { loadItems = () => {
const storagedKeys = window.app.settings.get("sidebarKeys") ?? defaultSidebarKeys const storagedKeys = window.app.cores.settings.get("sidebarKeys") ?? defaultSidebarKeys
const active = [] const active = []
const lockedIndex = [] const lockedIndex = []
@ -64,7 +65,7 @@ export default class SidebarEditor extends React.Component {
} }
onSave = () => { onSave = () => {
window.app.settings.set("sidebarKeys", this.state.items) window.app.cores.settings.set("sidebarKeys", this.state.items)
window.app.SidebarController.toggleEdit(false) window.app.SidebarController.toggleEdit(false)
} }
@ -73,7 +74,7 @@ export default class SidebarEditor extends React.Component {
} }
onSetDefaults = () => { onSetDefaults = () => {
window.app.settings.set("sidebarKeys", defaultSidebarKeys) window.app.cores.settings.set("sidebarKeys", defaultSidebarKeys)
this.loadItems() this.loadItems()
} }

View File

@ -3,12 +3,12 @@ import { Icons, createIconRender } from "components/Icons"
import { SelectableList } from "components" import { SelectableList } from "components"
import { List } from "antd" import { List } from "antd"
import sidebarItems from "schemas/routes.json" import sidebarItems from "schemas/sidebar"
import "./index.less" import "./index.less"
const getStoragedKeys = () => { const getStoragedKeys = () => {
return window.app.settings.get("sidebarKeys") ?? [] return window.app.cores.settings.get("sidebarKeys") ?? []
} }
const getAllItems = () => { const getAllItems = () => {

View File

@ -1,29 +1,40 @@
import React from "react" import React from "react"
import { Menu, Avatar, Button } from "antd" import { Menu, Avatar, Button, Dropdown } from "antd"
import { Translation } from "react-i18next" import { Translation } from "react-i18next"
import classnames from "classnames" import classnames from "classnames"
import config from "config" import config from "config"
import { Icons, createIconRender } from "components/Icons" import { Icons, createIconRender } from "components/Icons"
import sidebarItems from "schemas/routes.json" import sidebarItems from "schemas/sidebar"
import "./index.less" import "./index.less"
const onClickHandlers = { const onClickHandlers = {
settings: (event) => { settings: () => {
window.app.openSettings() window.app.navigation.goToSettings()
}, },
notifications: () => {
window.app.controls.openNotifications()
},
search: () => {
window.app.controls.openSearcher()
},
create: () => {
window.app.controls.openCreator()
},
account: () => {
window.app.navigation.goToAccount()
},
login: () => {
window.app.navigation.goAuth()
}
} }
const getSidebarComponents = () => { const getSidebarComponents = () => {
const items = {} const items = {}
sidebarItems.forEach((item, index) => { sidebarItems.forEach((item, index) => {
if (!item.reachable) {
return
}
items[item.id] = { items[item.id] = {
...item, ...item,
index, index,
@ -103,9 +114,9 @@ export default class Sidebar extends React.Component {
this.controller = window.app["SidebarController"] = { this.controller = window.app["SidebarController"] = {
toggleVisibility: this.toggleVisibility, toggleVisibility: this.toggleVisibility,
toggleElevation: this.toggleElevation, toggleElevation: this.toggleElevation,
toggleCollapse: this.toggleCollapse, toggleCollapse: this.toggleExpanded,
isVisible: () => this.state.visible, isVisible: () => this.state.visible,
isCollapsed: () => this.state.collapsed, isExpanded: () => this.state.expanded,
setCustomRender: this.setRender, setCustomRender: this.setRender,
closeCustomRender: this.closeRender, closeCustomRender: this.closeRender,
} }
@ -113,7 +124,7 @@ export default class Sidebar extends React.Component {
this.state = { this.state = {
visible: false, visible: false,
elevated: false, elevated: false,
collapsed: window.app.settings.get("collapseOnLooseFocus") ?? false, expanded: false,
pathResolvers: null, pathResolvers: null,
menus: null, menus: null,
@ -239,10 +250,8 @@ export default class Sidebar extends React.Component {
return window.app.setLocation(`/${e.key}`, 150) return window.app.setLocation(`/${e.key}`, 150)
} }
toggleCollapse = (to) => { toggleExpanded = (to) => {
if (!this.state.editMode) { this.setState({ expanded: to ?? !this.state.expanded })
this.setState({ collapsed: to ?? !this.state.collapsed })
}
} }
toggleVisibility = (to) => { toggleVisibility = (to) => {
@ -256,30 +265,34 @@ export default class Sidebar extends React.Component {
onMouseEnter = () => { onMouseEnter = () => {
if (!this.state.visible) return if (!this.state.visible) return
if (window.app.settings.is("collapseOnLooseFocus", false)) return if (window.app.cores.settings.is("collapseOnLooseFocus", false)) return
clearTimeout(this.collapseDebounce) clearTimeout(this.collapseDebounce)
this.collapseDebounce = null this.collapseDebounce = null
if (this.state.collapsed) { if (!this.state.expanded) {
this.toggleCollapse(false) this.toggleExpanded(true)
} }
} }
handleMouseLeave = () => { handleMouseLeave = () => {
if (!this.state.visible) return if (!this.state.visible) return
if (window.app.settings.is("collapseOnLooseFocus", false)) return if (window.app.cores.settings.is("collapseOnLooseFocus", false)) return
if (!this.state.collapsed) { if (this.state.expanded) {
this.collapseDebounce = setTimeout(() => { this.toggleCollapse(true) }, window.app.settings.get("autoCollapseDelay") ?? 500) this.collapseDebounce = setTimeout(() => {
this.toggleExpanded(false)
}, window.app.cores.settings.get("autoCollapseDelay") ?? 500)
} }
} }
render() { render() {
if (!this.state.menus) return null if (!this.state.menus) return null
const defaultSelectedKey = window.location.pathname.replace("/", "")
return <div return <div
onMouseEnter={this.onMouseEnter} onMouseEnter={this.onMouseEnter}
onMouseLeave={this.handleMouseLeave} onMouseLeave={this.handleMouseLeave}
@ -288,9 +301,9 @@ export default class Sidebar extends React.Component {
"app_sidebar", "app_sidebar",
{ {
["customRender"]: this.state.customRender, ["customRender"]: this.state.customRender,
["floating"]: window.app?.settings.get("sidebar.floating"), ["floating"]: window.app?.cores.settings.get("sidebar.floating"),
["elevated"]: this.state.visible && this.state.elevated, ["elevated"]: this.state.visible && this.state.elevated,
["collapsed"]: this.state.visible && this.state.collapsed, ["expanded"]: this.state.visible && this.state.expanded,
["hidden"]: !this.state.visible, ["hidden"]: !this.state.visible,
} }
) )
@ -310,30 +323,30 @@ export default class Sidebar extends React.Component {
{ {
!this.state.customRender && <> !this.state.customRender && <>
<div className="app_sidebar_header"> <div className="app_sidebar_header">
<div className={classnames("app_sidebar_header_logo", { ["collapsed"]: this.state.collapsed })}> <div className="app_sidebar_header_logo">
<img src={this.state.collapsed ? config.logo?.alt : config.logo?.full} /> <img src={config.logo?.alt} />
</div> </div>
</div> </div>
<div key="menu" className="app_sidebar_menu_wrapper"> <div key="menu" className="app_sidebar_menu_wrapper">
<Menu selectable={true} mode="inline" onClick={this.handleClick}> <Menu
selectable={true}
mode="inline"
onClick={this.handleClick}
defaultSelectedKeys={[defaultSelectedKey]}
>
{this.renderMenuItems(this.state.menus)} {this.renderMenuItems(this.state.menus)}
</Menu> </Menu>
</div> </div>
<div key="bottom" className={classnames("app_sidebar_menu_wrapper", "bottom")}> <div key="bottom" className={classnames("app_sidebar_menu_wrapper", "bottom")}>
<Menu selectable={false} mode="inline" onClick={this.handleClick}> <Menu selectable={false} mode="inline" onClick={this.handleClick}>
<Menu.Item key="search" icon={<Icons.Search />} override_event="app.openSearcher" > <Menu.Item key="search" icon={<Icons.Search />} >
<Translation> <Translation>
{(t) => t("Search")} {(t) => t("Search")}
</Translation> </Translation>
</Menu.Item> </Menu.Item>
<Menu.Item key="create" icon={<Icons.PlusCircle />} override_event="app.openCreator" > <Menu.Item key="notifications" icon={<Icons.Bell />}>
<Translation>
{(t) => t("Create")}
</Translation>
</Menu.Item>
<Menu.Item key="notifications" icon={<Icons.Bell />} override_event="app.openNotifications">
<Translation> <Translation>
{t => t("Notifications")} {t => t("Notifications")}
</Translation> </Translation>
@ -344,9 +357,47 @@ export default class Sidebar extends React.Component {
</Translation> </Translation>
</Menu.Item> </Menu.Item>
{ {
app.userData && <Menu.Item key="account" className="user_avatar"> app.userData && <Dropdown
<Avatar shape="square" src={app.userData?.avatar} /> trigger={["click"]}
</Menu.Item> menu={{
items: [
{
id: "profile",
label: <>
<Icons.User />
<Translation>
{t => t("Profile")}
</Translation>
</>,
onClick: () => {
window.app.navigation.goToAccount()
}
},
{
type: "divider",
},
{
id: "logout",
label: <>
<Icons.LogOut />
<Translation>
{t => t("Logout")}
</Translation>
</>,
}
]
}}
>
<Menu.Item
key="account"
className="user_avatar"
onDoubleClick={() => {
window.app.navigation.goToAccount()
}}
>
<Avatar shape="square" src={app.userData?.avatar} />
</Menu.Item>
</Dropdown>
} }
{ {
!app.userData && <Menu.Item key="login" icon={<Icons.LogIn />}> !app.userData && <Menu.Item key="login" icon={<Icons.LogIn />}>

View File

@ -11,7 +11,7 @@
z-index: 1000; z-index: 1000;
overflow: hidden; overflow: hidden;
width: @app_sidebar_collapsed_width; width: @app_sidebar_width;
min-width: @app_sidebar_width; min-width: @app_sidebar_width;
height: 100vh; height: 100vh;
@ -27,17 +27,18 @@
&.floating { &.floating {
position: absolute; position: absolute;
box-shadow: 0 0 5px 4px rgba(0, 0, 0, 0.1) !important;
} }
&.collapsed { &.expanded {
width: 80px; width: @app_sidebar_width_expanded;
min-width: 80px;
.app_sidebar_menu_wrapper { .app_sidebar_menu_wrapper {
.ant-menu { .ant-menu {
.ant-menu-item:not(.user_avatar) { .ant-menu-item:not(.user_avatar) {
.ant-menu-title-content { .ant-menu-title-content {
animation: disappear 0.3s ease-out forwards; opacity: 1;
width: 100%;
} }
} }
} }
@ -55,11 +56,11 @@
max-width: 60vw; max-width: 60vw;
.app_sidebar_menu_wrapper { .app_sidebar_menu_wrapper {
animation: disappear 0.3s ease-out forwards; animation: disappear 150ms ease-out forwards;
} }
.render_content_wrapper { .render_content_wrapper {
animation: appear 0.3s ease-out forwards; animation: appear 150ms ease-out forwards;
} }
} }
@ -74,6 +75,113 @@
box-shadow: 0 0 5px 4px rgba(0, 0, 0, 0.1) !important; box-shadow: 0 0 5px 4px rgba(0, 0, 0, 0.1) !important;
} }
.app_sidebar_header {
display: flex;
flex-direction: column;
height: 17%;
padding: 10px 0;
.app_sidebar_header_logo {
display: flex;
align-items: center;
justify-content: center;
img {
user-select: none;
--webkit-user-select: none;
width: 80%;
max-height: 40px;
}
}
}
.app_sidebar_menu_wrapper {
height: 65%;
width: 100%;
overflow: overlay;
overflow-x: hidden;
display: flex;
flex-direction: column;
align-items: center;
transition: all 150ms ease-in-out;
padding: 5px;
&.bottom {
position: absolute;
bottom: 0;
left: 0;
height: fit-content;
padding-bottom: 30px;
}
.ant-menu {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
width: 100%;
transition: all 150ms ease-in-out;
.ant-menu-item {
display: inline-flex;
align-items: center;
justify-content: center;
width: 100%;
padding: 0 0 0 calc(@app_sidebar_width / 2);
margin: 5px 0;
transition: all 150ms ease-in-out;
.ant-menu-item-icon {
font-size: 1rem;
margin: 0 !important;
}
.ant-menu-title-content {
text-overflow: clip;
opacity: 0;
width: 0;
}
&.user_avatar {
.ant-menu-title-content {
display: inline-flex;
align-items: flex-start;
justify-content: center;
width: fit-content;
opacity: 1;
}
padding: 0 !important;
}
}
.ant-menu-item-selected {
background-color: var(--background-color-primary) !important;
}
}
}
.render_content_wrapper { .render_content_wrapper {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
@ -119,148 +227,4 @@
overflow-y: scroll; overflow-y: scroll;
} }
} }
.app_sidebar_header {
display: flex;
flex-direction: column;
height: 17%;
padding: 10px 0;
.app_sidebar_header_logo {
display: flex;
align-items: center;
justify-content: center;
img {
user-select: none;
--webkit-user-select: none;
width: 80%;
max-height: 80px;
}
&.collapsed {
img {
max-width: 40px;
}
}
}
}
.app_sidebar_menu_wrapper {
height: 65%;
width: 100%;
overflow: overlay;
overflow-x: hidden;
display: flex;
flex-direction: column;
align-items: center;
transition: all 150ms ease-in-out;
&.bottom {
position: absolute;
bottom: 0;
left: 0;
height: fit-content;
padding-bottom: 30px;
}
.ant-menu {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
width: 100%;
transition: all 150ms ease-in-out;
.ant-menu-item {
display: flex;
align-items: center;
justify-content: center;
width: 90%;
padding: 0 10px !important;
margin: 5px 0 !important;
transition: all 150ms ease-in-out;
.ant-menu-title-content {
flex: 1;
text-overflow: clip;
}
&.user_avatar {
.ant-menu-title-content {
display: inline-flex;
align-items: center;
justify-content: center;
width: fit-content;
}
margin: 0;
padding: 0;
}
svg {
width: fit-content;
margin: 0 !important;
height: 16px;
}
}
}
}
}
@keyframes disappear {
0% {
opacity: 1;
width: 100%;
}
95% {
opacity: 0;
width: 0px;
margin: 0;
}
100% {
opacity: 0;
width: 0px;
margin: 0;
flex: 0;
}
}
@keyframes appear {
0% {
opacity: 0;
width: 0px;
margin: 0;
}
5% {
opacity: 0;
width: 0px;
margin: 0;
}
100% {
opacity: 1;
width: 100%;
margin: 0;
}
} }

View File

@ -227,7 +227,7 @@ export default class SidedrawerController extends React.Component {
className={classnames( className={classnames(
"sidedrawers-wrapper", "sidedrawers-wrapper",
{ {
["floating-sidebar"]: window.app?.settings.get("sidebar.floating") ["floating-sidebar"]: window.app?.cores.settings.get("sidebar.floating")
} }
)} )}
> >

View File

@ -7,7 +7,7 @@
&.floating-sidebar { &.floating-sidebar {
z-index: 950; z-index: 950;
position: absolute; position: absolute;
margin-left: @app_sidebar_collapsed_width; margin-left: @app_sidebar_width;
} }
.sidedrawer { .sidedrawer {