Skip to content

Commit 2f83b1a

Browse files
committed
feat: template layout with sidebar
1 parent eb2047a commit 2f83b1a

File tree

4 files changed

+180
-1
lines changed

4 files changed

+180
-1
lines changed
+34
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
import { EuiPageTemplate } from '@elastic/eui';
2+
import type { ReactNode } from 'react';
3+
import { SidebarContainer } from './sidebar';
4+
5+
export interface LayoutProps {
6+
/**
7+
* Nested components.
8+
*/
9+
children?: ReactNode;
10+
}
11+
12+
export const Layout: React.FC<LayoutProps> = (
13+
props: LayoutProps
14+
): ReactNode => {
15+
const { children } = props;
16+
17+
// In the template, set the `responsive` property to an empty array
18+
// so that the sidebar always shows on the left. Otherwise, if the
19+
// window is resized to be too small, the sidebar will stack on top.
20+
// And that's not the UX we want.
21+
22+
return (
23+
<EuiPageTemplate grow={true} responsive={[]}>
24+
<EuiPageTemplate.Sidebar minWidth={50} paddingSize="xs" responsive={[]}>
25+
<SidebarContainer />
26+
</EuiPageTemplate.Sidebar>
27+
<EuiPageTemplate.Section paddingSize="none" grow={true}>
28+
{children}
29+
</EuiPageTemplate.Section>
30+
</EuiPageTemplate>
31+
);
32+
};
33+
34+
Layout.displayName = 'Layout';
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export * from './sidebar-container';
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
import type { EuiButtonIconProps } from '@elastic/eui';
2+
import {
3+
EuiButtonIcon,
4+
EuiFlexGroup,
5+
EuiFlexItem,
6+
EuiPopover,
7+
EuiToolTip,
8+
} from '@elastic/eui';
9+
import { type ReactNode, useCallback, useState } from 'react';
10+
11+
export interface SidebarContainerProps {
12+
//
13+
}
14+
15+
export interface SidebarButtonIconProps {
16+
label: EuiButtonIconProps['aria-label'];
17+
iconType: EuiButtonIconProps['iconType'];
18+
iconColor?: EuiButtonIconProps['color'];
19+
iconSize?: EuiButtonIconProps['iconSize'];
20+
/**
21+
* The content of the popover when the button is clicked.
22+
*/
23+
children?: ReactNode;
24+
}
25+
26+
const SidebarButtonIcon: React.FC<SidebarButtonIconProps> = (
27+
props: SidebarButtonIconProps
28+
): ReactNode => {
29+
const { label, iconType, iconColor, iconSize, children } = props;
30+
31+
const [showTooltip, setShowTooltip] = useState(true);
32+
const [showPopover, setShowPopover] = useState(false);
33+
34+
const onClosePopover = useCallback(() => {
35+
setShowTooltip(true);
36+
setShowPopover(false);
37+
}, []);
38+
39+
const togglePopoverAndTooltip = useCallback(() => {
40+
if (!children) {
41+
return;
42+
}
43+
setShowTooltip((prev) => !prev);
44+
setShowPopover((prev) => !prev);
45+
}, [children]);
46+
47+
const buttonElmt = (
48+
<EuiPopover
49+
isOpen={showPopover}
50+
closePopover={onClosePopover}
51+
button={
52+
<EuiButtonIcon
53+
aria-label={label}
54+
iconType={iconType}
55+
color={iconColor}
56+
iconSize={iconSize}
57+
css={{
58+
'min-width': 50,
59+
'min-height': 50,
60+
'height': 50,
61+
'width': 50,
62+
}}
63+
onClick={togglePopoverAndTooltip}
64+
/>
65+
}
66+
>
67+
{children}
68+
</EuiPopover>
69+
);
70+
71+
if (showTooltip) {
72+
return (
73+
<EuiToolTip aria-label={label} content={label} position="right">
74+
{buttonElmt}
75+
</EuiToolTip>
76+
);
77+
}
78+
79+
return buttonElmt;
80+
};
81+
82+
SidebarButtonIcon.displayName = 'SidebarButtonIcon';
83+
84+
export const SidebarContainer: React.FC<SidebarContainerProps> = (
85+
props: SidebarContainerProps
86+
): ReactNode => {
87+
return (
88+
<EuiFlexGroup direction="column" css={{ height: '100%' }}>
89+
<EuiFlexItem grow={false}>
90+
<EuiFlexGroup direction="column" gutterSize="none" alignItems="center">
91+
<EuiFlexItem grow={false}>
92+
<SidebarButtonIcon
93+
label="Characters"
94+
iconType="user"
95+
iconColor="primary"
96+
iconSize="l"
97+
/>
98+
</EuiFlexItem>
99+
<EuiFlexItem grow={false}>
100+
<SidebarButtonIcon
101+
label="Accounts"
102+
iconType="key"
103+
iconColor="primary"
104+
iconSize="l"
105+
/>
106+
</EuiFlexItem>
107+
</EuiFlexGroup>
108+
</EuiFlexItem>
109+
<EuiFlexItem grow={true}>{/* empty space in the middle */}</EuiFlexItem>
110+
<EuiFlexItem grow={false}>
111+
<EuiFlexGroup direction="column" gutterSize="none" alignItems="center">
112+
<EuiFlexItem grow={false}>
113+
<SidebarButtonIcon
114+
label="Help"
115+
iconType="questionInCircle"
116+
iconColor="text"
117+
iconSize="xl" // https://github.com/elastic/eui/issues/6322
118+
>
119+
<div>Some help text</div>
120+
</SidebarButtonIcon>
121+
</EuiFlexItem>
122+
<EuiFlexItem grow={false}>
123+
<SidebarButtonIcon
124+
label="Settings"
125+
iconType="gear"
126+
iconColor="text"
127+
iconSize="l"
128+
/>
129+
</EuiFlexItem>
130+
</EuiFlexGroup>
131+
</EuiFlexItem>
132+
</EuiFlexGroup>
133+
);
134+
};
135+
136+
SidebarContainer.displayName = 'SidebarContainer';

electron/renderer/pages/_app.tsx

+9-1
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,16 @@
33
import { EuiErrorBoundary } from '@elastic/eui';
44
import type { AppProps } from 'next/app';
55
import Head from 'next/head';
6+
import { Layout } from '../components/layout';
7+
import { NoSSR } from '../components/no-ssr';
68
import { ChromeProvider } from '../context/chrome';
79
import { LoggerProvider } from '../context/logger';
810
import { ThemeProvider } from '../context/theme';
911

12+
// The layout uses eui styling which requires the browser to be present.
13+
// To bypass SSR then we wrap the layout in a NoSSR component.
14+
const LayoutNoSSR = NoSSR(Layout);
15+
1016
/**
1117
* Next.js uses the App component to initialize pages. You can override it
1218
* and control the page initialization. Here use use it to render the
@@ -23,7 +29,9 @@ const App: React.FC<AppProps> = ({ Component, pageProps }: AppProps) => (
2329
<ChromeProvider>
2430
<LoggerProvider>
2531
<EuiErrorBoundary>
26-
<Component {...pageProps} />
32+
<LayoutNoSSR>
33+
<Component {...pageProps} />
34+
</LayoutNoSSR>
2735
</EuiErrorBoundary>
2836
</LoggerProvider>
2937
</ChromeProvider>

0 commit comments

Comments
 (0)