Skip to content

Commit 9bf48c1

Browse files
author
FalkWolsky
committed
Admin Panel Responsive
1 parent 9d1d607 commit 9bf48c1

File tree

6 files changed

+182
-160
lines changed

6 files changed

+182
-160
lines changed

client/packages/lowcoder/src/components/layout/Layout.tsx

Lines changed: 96 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,15 @@ import { Route, Switch } from "react-router-dom";
22
import { default as AntdLayout } from "antd/es/layout";
33
import { AppHeader } from "pages/common/header";
44
import * as React from "react";
5-
import { ReactElement } from "react";
5+
import { ReactElement, useState, useEffect } from "react";
66
import { HelpDropdown } from "pages/common/help";
77
import MainContent from "components/layout/MainContent";
88
import SideBar from "components/layout/SideBar";
99
import { CNMainContent, CNSidebar } from "constants/styleSelectors";
1010
import { SideBarSection, SideBarSectionProps } from "./SideBarSection";
1111
import styled from "styled-components";
12+
import { MenuOutlined } from "@ant-design/icons";
13+
import { Drawer, Button } from "antd";
1214

1315
type LayoutProps = {
1416
sections: SideBarSectionProps[];
@@ -29,9 +31,53 @@ const SideBarV2 = styled(SideBar)`
2931
}
3032
`;
3133

34+
const MobileMenuButton = styled(Button)`
35+
display: none;
36+
position: fixed;
37+
top: 75px;
38+
right: 22px;
39+
z-index: 1000;
40+
41+
@media screen and (max-width: 720px) {
42+
display: block;
43+
}
44+
`;
45+
46+
const DrawerContentWrapper = styled.div`
47+
height: 100%;
48+
display: flex;
49+
flex-direction: column;
50+
`;
51+
3252
export function Layout(props: LayoutProps) {
53+
54+
const [drawerVisible, setDrawerVisible] = useState(false);
55+
const [isMobile, setIsMobile] = useState(false);
56+
57+
const toggleDrawer = () => {
58+
setDrawerVisible(!drawerVisible);
59+
};
60+
61+
const handleMenuClick = () => {
62+
setDrawerVisible(false); // Close the drawer
63+
};
64+
65+
useEffect(() => {
66+
const handleResize = () => setIsMobile(window.innerWidth <= 720);
67+
handleResize(); // Check on initial render
68+
window.addEventListener("resize", handleResize);
69+
return () => window.removeEventListener("resize", handleResize);
70+
}, []);
71+
72+
const mobileSections = props.sections.map((section) => ({
73+
...section,
74+
items: section.items.filter((item) => item.mobileVisible !== false), // Filter mobile-visible items
75+
}));
76+
77+
const desktopSections = props.sections;
78+
3379
const routes: ReactElement[] = [];
34-
props.sections.forEach((section) => {
80+
desktopSections.forEach((section) => {
3581
section.items.forEach((item) => {
3682
routes.push(
3783
<Route
@@ -48,18 +94,57 @@ export function Layout(props: LayoutProps) {
4894
<AntdLayout style={{ height: "100%" }}>
4995
<AppHeader />
5096
<HelpDropdown />
97+
98+
{/* Mobile Hamburger Button */}
99+
{isMobile && (
100+
<MobileMenuButton
101+
type="primary"
102+
shape="circle"
103+
icon={<MenuOutlined />}
104+
onClick={toggleDrawer}
105+
/>
106+
)}
107+
108+
{/* Drawer for Mobile Sidebar */}
109+
<Drawer
110+
width={"240px"}
111+
placement="right"
112+
closable={true}
113+
onClose={toggleDrawer}
114+
visible={drawerVisible}
115+
bodyStyle={{ padding: "0px" }}
116+
destroyOnClose // Ensure drawer content is removed when closed
117+
>
118+
<DrawerContentWrapper>
119+
<SideBarV2 className={CNSidebar}>
120+
{mobileSections
121+
.filter((section) => section.items.length > 0)
122+
.map((section, index) => (
123+
<SideBarSection
124+
key={index}
125+
{...section}
126+
onItemClick={handleMenuClick} // Pass handler to close the drawer
127+
/>
128+
))}
129+
</SideBarV2>
130+
</DrawerContentWrapper>
131+
</Drawer>
132+
133+
{/* Desktop Layout */}
51134
<AntdLayout>
52-
<SideBarV2 className={CNSidebar}>
53-
{props.sections
54-
.filter((section) => section.items.length > 0)
55-
.map((section, index) => (
56-
<SideBarSection key={index} {...section} />
57-
))}
58-
</SideBarV2>
135+
{!isMobile && (
136+
<SideBarV2 className={`${CNSidebar} desktop-only`}>
137+
{desktopSections
138+
.filter((section) => section.items.length > 0)
139+
.map((section, index) => (
140+
<SideBarSection key={index} {...section} />
141+
))}
142+
</SideBarV2>
143+
)}
59144
<MainContent className={CNMainContent}>
60-
<Switch>{routes} </Switch>
145+
<Switch>{routes}</Switch>
61146
</MainContent>
62147
</AntdLayout>
63148
</AntdLayout>
64149
);
65-
}
150+
}

client/packages/lowcoder/src/components/layout/SideBarSection.tsx

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,12 @@ export const SideBarSection = (props: SideBarSectionProps) => {
2424
const user = useSelector<AppState, User>(getUser);
2525
const applications = useSelector<AppState, ApplicationMeta[]>(normalAppListSelector);
2626
const currentPath = useLocation().pathname;
27-
const isShow = props.items.map(item => item.visible ? item.visible({ user: user, applications: applications }) : true).includes(true);
27+
const isShow = props.items
28+
.map((item) => (item.visible ? item.visible({ user: user, applications: applications }) : true))
29+
.includes(true);
30+
2831
return (
29-
<Wrapper className={ isShow ? CNSidebarSection : ''} style={props.style}>
32+
<Wrapper className={isShow ? CNSidebarSection : ""} style={props.style}>
3033
{props.title}
3134
{props.items
3235
.filter((item) =>
@@ -42,10 +45,15 @@ export const SideBarSection = (props: SideBarSectionProps) => {
4245
? item.onSelected(item.routePath, currentPath)
4346
: defaultOnSelectedFn(item.routePath, currentPath)
4447
}
45-
onClick={
46-
item.onClick ??
47-
(() => currentPath !== item.routePath && history.push(item.routePath))
48-
}
48+
onClick={() => {
49+
// Trigger item's onClick if defined
50+
item.onClick
51+
? item.onClick("")
52+
: currentPath !== item.routePath && history.push(item.routePath);
53+
54+
// Trigger parent onItemClick to close the drawer
55+
props.onItemClick?.();
56+
}}
4957
/>
5058
);
5159
})}
@@ -54,15 +62,17 @@ export const SideBarSection = (props: SideBarSectionProps) => {
5462
};
5563

5664
export type SideBarItemType = Omit<SideBarItemProps, "selected"> & {
57-
onSelected?: (routePath: string, currentPath: string) => boolean; // customize select logic from url path
65+
onSelected?: (routePath: string, currentPath: string) => boolean; // Customize select logic from URL path
5866
routePath: string;
5967
routePathExact?: boolean;
6068
visible?: (params: { user: User; applications: ApplicationMeta[] }) => boolean;
6169
routeComp: React.ComponentType<any>;
70+
mobileVisible?: boolean;
6271
};
6372

6473
export interface SideBarSectionProps {
6574
title?: ReactNode;
6675
items: SideBarItemType[];
6776
style?: CSSProperties;
77+
onItemClick?: () => void; // New prop for handling item click
6878
}

client/packages/lowcoder/src/pages/ApplicationV2/HomeLayout.tsx

Lines changed: 52 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ const HeaderWrapper = styled.div`
5151
padding: 0 36px;
5252
align-items: center;
5353
flex-shrink: 0;
54-
@media screen and (max-width: 500px) {
54+
@media screen and (max-width: 720px) {
5555
padding: 0 24px;
5656
}
5757
`;
@@ -61,11 +61,13 @@ const OperationWrapper = styled.div`
6161
align-items: center;
6262
justify-content: space-between;
6363
width: 100%;
64-
height: 32px;
64+
height: auto;
6565
padding: 0 36px;
6666
margin: 8px 0 20px 0;
67-
@media screen and (max-width: 500px) {
68-
padding: 0 24px;
67+
@media screen and (max-width: 850px) {
68+
flex-direction: column;
69+
align-items: flex-start;
70+
gap: 16px;
6971
}
7072
`;
7173

@@ -90,17 +92,9 @@ const Breadcrumb = styled(AntdBreadcrumb)`
9092
flex-direction: column;
9193
justify-content: center;
9294
}
93-
`;
9495
95-
const OperationRightWrapper = styled.div`
96-
display: flex;
97-
align-items: center;
98-
flex-shrink: 0;
99-
margin-left: auto;
100-
@media screen and (max-width: 500px) {
101-
> Button {
102-
display: none;
103-
}
96+
@media screen and (max-width: 720px) {
97+
display:none
10498
}
10599
`;
106100

@@ -209,8 +203,8 @@ const PaginationLayout = styled.div`
209203

210204
const LayoutSwitcher = styled.div`
211205
position: absolute;
212-
right: 36px;
213-
top: 6px;
206+
left: 20px;
207+
top: 40px;
214208
cursor: pointer;
215209
width: 32px;
216210
height: 16px;
@@ -223,10 +217,6 @@ const LayoutSwitcher = styled.div`
223217
&:hover {
224218
background-color: #f5f5f6;
225219
}
226-
227-
@media screen and (max-width: 500px) {
228-
display: none;
229-
}
230220
`;
231221

232222
const HomeView = styled.div`
@@ -249,28 +239,22 @@ const StyleHomeCover = styled.div`
249239
border-radius:10px 10px 0 0;
250240
`;
251241

252-
const StyleHomeContent = styled.div`
253-
position: relative;
254-
margin-top:-50px;
255-
display: flex;
256-
align-items: end;
257-
gap: 20px;
242+
const SearchWrapper = styled.div`
243+
width: auto;
258244
259-
.subtitle {
260-
color: #8b8fa3;
261-
}
245+
display: flex;
246+
align-items: center;
247+
flex-shrink: 0;
248+
margin-left: auto;
262249
263-
.button-end {
264-
margin-left: auto;
265-
}
266-
267-
svg {
268-
margin-right: 5px;
269-
vertical-align: middle;
250+
@media screen and (max-width: 980px) {
251+
width: 100%;
252+
> Button {
253+
display: none;
270254
}
255+
}
271256
`;
272257

273-
274258
function showNewUserGuide(user: User) {
275259
return (
276260
user.orgDev &&
@@ -515,7 +499,7 @@ export function HomeLayout(props: HomeLayoutProps) {
515499

516500
{showNewUserGuide(user) && <HomepageTourV2 />}
517501

518-
<HomeView>
502+
<HomeView>
519503
<StyleHomeCover>
520504
<h1 style={{color: "#ffffff", marginTop : "12px"}}>
521505
{mode === "marketplace" && trans("home.appMarketplace")}
@@ -550,20 +534,20 @@ export function HomeLayout(props: HomeLayoutProps) {
550534
suffixIcon={<ArrowSolidIcon />} />
551535
)}
552536
{(mode === "view" || mode === "folder") &&
553-
<FilterDropdown
554-
style={{ minWidth: "220px" }}
555-
variant="borderless"
556-
value={categoryFilter}
557-
onChange={(value: any) => {
558-
setCategoryFilter(value as ApplicationCategoriesEnum)
559-
setCategoryFilterPagination(value as ApplicationCategoriesEnum);
560-
}
561-
537+
<FilterDropdown
538+
style={{ minWidth: "220px" }}
539+
variant="borderless"
540+
value={categoryFilter}
541+
onChange={(value: any) => {
542+
setCategoryFilter(value as ApplicationCategoriesEnum)
543+
setCategoryFilterPagination(value as ApplicationCategoriesEnum);
562544
}
563-
options={categoryOptions}
564-
// getPopupContainer={(node) => node}
565-
suffixIcon={<ArrowSolidIcon />}
566-
/>}
545+
546+
}
547+
options={categoryOptions}
548+
// getPopupContainer={(node) => node}
549+
suffixIcon={<ArrowSolidIcon />}
550+
/>}
567551
{mode === "marketplace" && (
568552
<FilterDropdown
569553
style={{ minWidth: "220px" }}
@@ -575,17 +559,28 @@ export function HomeLayout(props: HomeLayoutProps) {
575559
suffixIcon={<ArrowSolidIcon />} />
576560
)}
577561

578-
<OperationRightWrapper>
562+
<LayoutSwitcher onClick={() => setLayout(layout === "list" ? "card" : "list")}>
563+
{layout === "list" ? <HomeCardIcon/> : <HomeListIcon/>}
564+
</LayoutSwitcher>
565+
566+
<SearchWrapper>
579567
<Search
580568
placeholder={trans("search")}
581569
value={searchValue || ""}
582570
onChange={(e) => setSearchValue(e.target.value)}
583571
style={{ width: "192px", height: "32px", margin: "0" }}
584572
/>
585-
{mode !== "trash" && mode !== "marketplace" && user.orgDev && (
586-
<CreateDropdown defaultVisible={showNewUserGuide(user)} mode={mode} setModify={setIsCreated} modify={isCreated!} />
587-
)}
588-
</OperationRightWrapper>
573+
574+
575+
{mode !== "trash" && mode !== "marketplace" && user.orgDev && (
576+
<CreateDropdown
577+
defaultVisible={showNewUserGuide(user)}
578+
mode={mode}
579+
setModify={setIsCreated}
580+
modify={isCreated!} />
581+
)}
582+
</SearchWrapper>
583+
589584
</OperationWrapper>
590585

591586
<Divider />
@@ -602,10 +597,7 @@ export function HomeLayout(props: HomeLayoutProps) {
602597
<TrashTableView resources={resList} setModify={setModify} modify={modify!}/>
603598
) : (
604599
<>
605-
<LayoutSwitcher onClick={() => setLayout(layout === "list" ? "card" : "list")}>
606-
{layout === "list" ? <HomeCardIcon style={{marginRight: "-11px"}}/> : <HomeListIcon style={{marginTop: "-30px"}}/>}
607-
</LayoutSwitcher>
608-
600+
609601
{mode === "marketplace" && (
610602
<>
611603
{layout === "list" ? (

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy