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 = () => {
if (!user) {
return app.eventBus.emit("app.forceLogin")
return app.navigation.goAuth()
}
return app.goToAccount()
return app.navigation.goToAccount()
}
const handleHold = () => {

View File

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

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 {
user-select: none;
--webkit-user-select: none;
z-index: 100;
display: flex;
flex-direction: row;
align-items: center;
z-index: 100;
top: 0;
left: 0;
height: @app_header_height !important;
padding: 10px;
display: flex;
flex-direction: row;
align-items: center;
justify-content: flex-start;
transition: all ease-in-out 150ms;
overflow: hidden;
background-color: transparent;
background: transparent !important;
// hidden values
padding: 0;
opacity: 0;
height: 0px;
width: 100%;
border-bottom: 1px var(--border-color) solid;
transition: all 150ms ease-in-out;
>div {
margin-right: 16px;
}
backdrop-filter: blur(10px);
&.hidden {
opacity: 0;
height: 0 !important;
padding: 0 !important;
border: 0 !important;
}
&.visible {
opacity: 1;
height: 50px;
padding: 20px;
}
}

View File

@ -1,6 +1,6 @@
export { default as BottomBar } from "./bottomBar"
export { default as Header } from "./header"
export { default as Drawer } from "./drawer"
export { default as Sidebar } from "./sidebar"
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 { Icons, createIconRender } from "components/Icons"
import sidebarItems from "schemas/routes.json"
import sidebarItems from "schemas/sidebar"
import { sidebarKeys as defaultSidebarKeys } from "schemas/defaultSettings"
import Selector from "../selector"
@ -46,7 +47,7 @@ export default class SidebarEditor extends React.Component {
}
loadItems = () => {
const storagedKeys = window.app.settings.get("sidebarKeys") ?? defaultSidebarKeys
const storagedKeys = window.app.cores.settings.get("sidebarKeys") ?? defaultSidebarKeys
const active = []
const lockedIndex = []
@ -64,7 +65,7 @@ export default class SidebarEditor extends React.Component {
}
onSave = () => {
window.app.settings.set("sidebarKeys", this.state.items)
window.app.cores.settings.set("sidebarKeys", this.state.items)
window.app.SidebarController.toggleEdit(false)
}
@ -73,7 +74,7 @@ export default class SidebarEditor extends React.Component {
}
onSetDefaults = () => {
window.app.settings.set("sidebarKeys", defaultSidebarKeys)
window.app.cores.settings.set("sidebarKeys", defaultSidebarKeys)
this.loadItems()
}

View File

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

View File

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

View File

@ -11,7 +11,7 @@
z-index: 1000;
overflow: hidden;
width: @app_sidebar_collapsed_width;
width: @app_sidebar_width;
min-width: @app_sidebar_width;
height: 100vh;
@ -27,17 +27,18 @@
&.floating {
position: absolute;
box-shadow: 0 0 5px 4px rgba(0, 0, 0, 0.1) !important;
}
&.collapsed {
width: 80px;
min-width: 80px;
&.expanded {
width: @app_sidebar_width_expanded;
.app_sidebar_menu_wrapper {
.ant-menu {
.ant-menu-item:not(.user_avatar) {
.ant-menu-title-content {
animation: disappear 0.3s ease-out forwards;
opacity: 1;
width: 100%;
}
}
}
@ -55,11 +56,11 @@
max-width: 60vw;
.app_sidebar_menu_wrapper {
animation: disappear 0.3s ease-out forwards;
animation: disappear 150ms ease-out forwards;
}
.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;
}
.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 {
display: flex;
flex-direction: column;
@ -119,148 +227,4 @@
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(
"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 {
z-index: 950;
position: absolute;
margin-left: @app_sidebar_collapsed_width;
margin-left: @app_sidebar_width;
}
.sidedrawer {