Move Sidebar to flipper-plugin
Summary: This diff deprecates the Sidebar concept, and copies the implementation to Sandy (tried moving first, but since existing plugins use the Sidebar in non-flex (Layout) contexts, that the layout of several plugins, so rather deprecated the old implementation. Instead of exposing `Sidebar` explicitly, one can now put the `resizable` flag on a Layout.Top/Left/Bottom/Right, which makes building layouts even simpler, see demo. The gutter logic was moved to the new implementation, since that was only used by the Sandy chrome anyway. Changelog: Layout.Top / Left / Bottom / Right now support a resizable option Reviewed By: passy Differential Revision: D27233899 fbshipit-source-id: fbbdeb2ebf30d49d0837705a00ea86bb07fc2ba2
This commit is contained in:
committed by
Facebook GitHub Bot
parent
0bf786544a
commit
dd1f2fdeaa
@@ -9,11 +9,10 @@
|
||||
|
||||
import React, {useEffect, useState} from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import Sidebar from '../ui/components/Sidebar';
|
||||
import {toggleRightSidebarAvailable} from '../reducers/application';
|
||||
import {useDispatch, useStore} from '../utils/useStore';
|
||||
import {ContentContainer} from '../sandy-chrome/ContentContainer';
|
||||
import {Layout} from '../ui';
|
||||
import {Layout, _Sidebar} from 'flipper-plugin';
|
||||
|
||||
type OwnProps = {
|
||||
children: any;
|
||||
@@ -66,7 +65,7 @@ export default function DetailSidebar({children, width, minWidth}: OwnProps) {
|
||||
rightSidebarVisible &&
|
||||
domNode &&
|
||||
ReactDOM.createPortal(
|
||||
<Sidebar
|
||||
<_Sidebar
|
||||
minWidth={minWidth}
|
||||
width={width || 300}
|
||||
position="right"
|
||||
@@ -74,7 +73,7 @@ export default function DetailSidebar({children, width, minWidth}: OwnProps) {
|
||||
<ContentContainer>
|
||||
<Layout.ScrollContainer vertical>{children}</Layout.ScrollContainer>
|
||||
</ContentContainer>
|
||||
</Sidebar>,
|
||||
</_Sidebar>,
|
||||
domNode,
|
||||
)) ||
|
||||
null
|
||||
|
||||
@@ -230,6 +230,16 @@ const demos: PreviewProps[] = [
|
||||
'true / number (0)',
|
||||
'Set the spacing between children. If just set, theme.space.small will be used.',
|
||||
],
|
||||
[
|
||||
'resizable',
|
||||
'true / undefined',
|
||||
'If set, this split container will be resizable by the user. It is recommend to set width, maxWidth, minWidth respectively height, maxHeight, minHeight properties as well.',
|
||||
],
|
||||
[
|
||||
'width / height / minWidth / minHeight / maxWidth / maxHeight',
|
||||
'number / undefined',
|
||||
'These dimensions in pixels will be used for clamping if the layout is marked as resizable',
|
||||
],
|
||||
],
|
||||
demos: {
|
||||
'Layout.Top': (
|
||||
@@ -272,19 +282,19 @@ const demos: PreviewProps[] = [
|
||||
</Layout.Left>
|
||||
</Layout.Container>
|
||||
),
|
||||
'Layout.Right + Layout.ScrollContainer': (
|
||||
'Layout.Right resizable + Layout.ScrollContainer': (
|
||||
<Layout.Container style={{height: 150}}>
|
||||
<Layout.Right>
|
||||
<Layout.Right resizable>
|
||||
<Layout.ScrollContainer>{largeChild}</Layout.ScrollContainer>
|
||||
{aFixedWidthBox}
|
||||
{aDynamicBox}
|
||||
</Layout.Right>
|
||||
</Layout.Container>
|
||||
),
|
||||
'Layout.Bottom + Layout.ScrollContainer': (
|
||||
'Layout.Bottom resizable + Layout.ScrollContainer': (
|
||||
<Layout.Container style={{height: 150}}>
|
||||
<Layout.Bottom>
|
||||
<Layout.Bottom resizable height={50} minHeight={20}>
|
||||
<Layout.ScrollContainer>{largeChild}</Layout.ScrollContainer>
|
||||
{aFixedHeightBox}
|
||||
{aDynamicBox}
|
||||
</Layout.Bottom>
|
||||
</Layout.Container>
|
||||
),
|
||||
|
||||
@@ -8,9 +8,8 @@
|
||||
*/
|
||||
|
||||
import React, {useEffect, useState, useCallback} from 'react';
|
||||
import {TrackingScope, useLogger} from 'flipper-plugin';
|
||||
import {TrackingScope, useLogger, _Sidebar, Layout} from 'flipper-plugin';
|
||||
import {Link, styled} from '../ui';
|
||||
import {Layout, Sidebar} from '../ui';
|
||||
import {theme} from 'flipper-plugin';
|
||||
import {ipcRenderer} from 'electron';
|
||||
import {Logger} from '../fb-interfaces/Logger';
|
||||
@@ -148,13 +147,13 @@ export function SandyApp() {
|
||||
toplevelSelection={toplevelSelection}
|
||||
setToplevelSelection={setToplevelSelection}
|
||||
/>
|
||||
<Sidebar width={250} minWidth={220} maxWidth={800} gutter>
|
||||
<_Sidebar width={250} minWidth={220} maxWidth={800} gutter>
|
||||
{leftMenuContent && (
|
||||
<TrackingScope scope={toplevelSelection!}>
|
||||
{leftMenuContent}
|
||||
</TrackingScope>
|
||||
)}
|
||||
</Sidebar>
|
||||
</_Sidebar>
|
||||
</Layout.Horizontal>
|
||||
<MainContainer>
|
||||
{outOfContentsContainer}
|
||||
|
||||
@@ -7,16 +7,13 @@
|
||||
* @format
|
||||
*/
|
||||
|
||||
import {_Interactive, _InteractiveProps} from 'flipper-plugin';
|
||||
import {theme, _Interactive, _InteractiveProps} from 'flipper-plugin';
|
||||
import FlexColumn from './FlexColumn';
|
||||
import {colors} from './colors';
|
||||
import {Component, ReactNode} from 'react';
|
||||
import {Component} from 'react';
|
||||
import styled from '@emotion/styled';
|
||||
import {Property} from 'csstype';
|
||||
import React from 'react';
|
||||
import FlexRow from './FlexRow';
|
||||
import {MoreOutlined} from '@ant-design/icons';
|
||||
import {theme} from 'flipper-plugin';
|
||||
|
||||
const SidebarInteractiveContainer = styled(_Interactive)<_InteractiveProps>({
|
||||
flex: 'none',
|
||||
@@ -25,6 +22,8 @@ SidebarInteractiveContainer.displayName = 'Sidebar:SidebarInteractiveContainer';
|
||||
|
||||
type SidebarPosition = 'left' | 'top' | 'right' | 'bottom';
|
||||
|
||||
const borderStyle = '1px solid ' + theme.dividerColor;
|
||||
|
||||
const SidebarContainer = styled(FlexColumn)<{
|
||||
position: 'right' | 'top' | 'left' | 'bottom';
|
||||
backgroundColor?: Property.BackgroundClip;
|
||||
@@ -36,10 +35,10 @@ const SidebarContainer = styled(FlexColumn)<{
|
||||
: {
|
||||
backgroundColor:
|
||||
props.backgroundColor || colors.macOSTitleBarBackgroundBlur,
|
||||
borderLeft: props.position === 'right' ? '1px solid #b3b3b3' : 'none',
|
||||
borderTop: props.position === 'bottom' ? '1px solid #b3b3b3' : 'none',
|
||||
borderRight: props.position === 'left' ? '1px solid #b3b3b3' : 'none',
|
||||
borderBottom: props.position === 'top' ? '1px solid #b3b3b3' : 'none',
|
||||
borderLeft: props.position === 'right' ? borderStyle : 'none',
|
||||
borderTop: props.position === 'bottom' ? borderStyle : 'none',
|
||||
borderRight: props.position === 'left' ? borderStyle : 'none',
|
||||
borderBottom: props.position === 'top' ? borderStyle : 'none',
|
||||
}),
|
||||
height: '100%',
|
||||
overflowX: 'hidden',
|
||||
@@ -97,10 +96,6 @@ type SidebarProps = {
|
||||
* Class name to customise styling.
|
||||
*/
|
||||
className?: string;
|
||||
/**
|
||||
* use a Sandy themed large gutter
|
||||
*/
|
||||
gutter?: boolean;
|
||||
};
|
||||
|
||||
type SidebarState = {
|
||||
@@ -111,6 +106,7 @@ type SidebarState = {
|
||||
|
||||
/**
|
||||
* A resizable sidebar.
|
||||
* @deprecated use Layout.Top / Right / Bottom / Left from flipper-plugin instead
|
||||
*/
|
||||
export default class Sidebar extends Component<SidebarProps, SidebarState> {
|
||||
constructor(props: SidebarProps, context: Object) {
|
||||
@@ -146,7 +142,7 @@ export default class Sidebar extends Component<SidebarProps, SidebarState> {
|
||||
};
|
||||
|
||||
render() {
|
||||
const {backgroundColor, onResize, position, children, gutter} = this.props;
|
||||
const {backgroundColor, onResize, position, children} = this.props;
|
||||
let height: number | undefined;
|
||||
let minHeight: number | undefined;
|
||||
let maxHeight: number | undefined;
|
||||
@@ -170,7 +166,7 @@ export default class Sidebar extends Component<SidebarProps, SidebarState> {
|
||||
}
|
||||
|
||||
const horizontal = position === 'left' || position === 'right';
|
||||
const gutterWidth = gutter ? theme.space.large : 0;
|
||||
const gutterWidth = 0;
|
||||
|
||||
if (horizontal) {
|
||||
width = width == null ? 200 : width;
|
||||
@@ -198,71 +194,14 @@ export default class Sidebar extends Component<SidebarProps, SidebarState> {
|
||||
minHeight={minHeight}
|
||||
maxHeight={maxHeight}
|
||||
height={
|
||||
!horizontal
|
||||
? onResize
|
||||
? height
|
||||
: this.state.height
|
||||
: gutter /*TODO: should use isSandy check*/
|
||||
? undefined
|
||||
: '100%'
|
||||
!horizontal ? (onResize ? height : this.state.height) : undefined
|
||||
}
|
||||
resizable={resizable}
|
||||
onResize={this.onResize}
|
||||
gutterWidth={gutter ? theme.space.large : undefined}>
|
||||
<SidebarContainer
|
||||
position={position}
|
||||
backgroundColor={backgroundColor}
|
||||
unstyled={gutter}>
|
||||
{gutter ? (
|
||||
<GutterWrapper position={position}>{children}</GutterWrapper>
|
||||
) : (
|
||||
children
|
||||
)}
|
||||
onResize={this.onResize}>
|
||||
<SidebarContainer position={position} backgroundColor={backgroundColor}>
|
||||
{children}
|
||||
</SidebarContainer>
|
||||
</SidebarInteractiveContainer>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const GutterWrapper = ({
|
||||
position,
|
||||
children,
|
||||
}: {
|
||||
position: SidebarPosition;
|
||||
children: ReactNode;
|
||||
}) => {
|
||||
return position === 'right' ? (
|
||||
<FlexRow grow>
|
||||
<VerticalGutter enabled={!!children} />
|
||||
{children}
|
||||
</FlexRow>
|
||||
) : (
|
||||
<FlexRow grow>
|
||||
{children}
|
||||
<VerticalGutter enabled={!!children} />
|
||||
</FlexRow>
|
||||
); // TODO: support top / bottom
|
||||
};
|
||||
|
||||
const VerticalGutterContainer = styled('div')<{enabled: boolean}>(
|
||||
({enabled}) => ({
|
||||
width: theme.space.large,
|
||||
minWidth: theme.space.large,
|
||||
height: '100%',
|
||||
cursor: enabled ? undefined : 'default', // hide cursor from interactive container
|
||||
color: enabled ? theme.textColorPlaceholder : theme.backgroundWash,
|
||||
fontSize: '16px',
|
||||
display: 'flex',
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
background: theme.backgroundWash,
|
||||
':hover': {
|
||||
background: enabled ? theme.dividerColor : undefined,
|
||||
},
|
||||
}),
|
||||
);
|
||||
const VerticalGutter = ({enabled}: {enabled: boolean}) => (
|
||||
<VerticalGutterContainer enabled={enabled}>
|
||||
<MoreOutlined />
|
||||
</VerticalGutterContainer>
|
||||
);
|
||||
|
||||
@@ -55,6 +55,7 @@ export {
|
||||
NuxManagerContext as _NuxManagerContext,
|
||||
createNuxManager as _createNuxManager,
|
||||
} from './ui/NUX';
|
||||
export {Sidebar as _Sidebar} from './ui/Sidebar';
|
||||
|
||||
export {renderReactRoot} from './utils/renderReactRoot';
|
||||
export {
|
||||
|
||||
@@ -141,14 +141,79 @@ type SplitLayoutProps = {
|
||||
gap?: Spacing;
|
||||
children: [React.ReactNode, React.ReactNode];
|
||||
style?: CSSProperties;
|
||||
};
|
||||
} & SplitHorizontalResizableProps &
|
||||
SplitVerticalResizableProps;
|
||||
|
||||
type SplitHorizontalResizableProps =
|
||||
| {
|
||||
resizable: true;
|
||||
/**
|
||||
* Width describes the width of the resizable pane. To set a global width use the style attribute.
|
||||
*/
|
||||
width?: number;
|
||||
minWidth?: number;
|
||||
maxWidth?: number;
|
||||
}
|
||||
| {};
|
||||
|
||||
type SplitVerticalResizableProps =
|
||||
| {
|
||||
resizable: true;
|
||||
/**
|
||||
* Width describes the width of the resizable pane. To set a global width use the style attribute.
|
||||
*/
|
||||
height?: number;
|
||||
minHeight?: number;
|
||||
maxHeight?: number;
|
||||
}
|
||||
| {};
|
||||
|
||||
function renderSplitLayout(
|
||||
props: SplitLayoutProps,
|
||||
direction: 'column' | 'row',
|
||||
grow: 1 | 2,
|
||||
) {
|
||||
const [child1, child2] = props.children;
|
||||
let [child1, child2] = props.children;
|
||||
if ('resizable' in props && props.resizable) {
|
||||
const {
|
||||
width,
|
||||
height,
|
||||
minHeight,
|
||||
minWidth,
|
||||
maxHeight,
|
||||
maxWidth,
|
||||
} = props as any;
|
||||
const sizeProps =
|
||||
direction === 'column'
|
||||
? ({
|
||||
minHeight,
|
||||
height: height ?? 300,
|
||||
maxHeight,
|
||||
} as const)
|
||||
: ({
|
||||
minWidth,
|
||||
width: width ?? 300,
|
||||
maxWidth,
|
||||
} as const);
|
||||
const Sidebar = require('./Sidebar').Sidebar;
|
||||
if (grow === 2) {
|
||||
child1 = (
|
||||
<Sidebar
|
||||
position={direction === 'column' ? 'top' : 'left'}
|
||||
{...sizeProps}>
|
||||
{child1}
|
||||
</Sidebar>
|
||||
);
|
||||
} else {
|
||||
child2 = (
|
||||
<Sidebar
|
||||
position={direction === 'column' ? 'bottom' : 'right'}
|
||||
{...sizeProps}>
|
||||
{child2}
|
||||
</Sidebar>
|
||||
);
|
||||
}
|
||||
}
|
||||
return (
|
||||
<SandySplitContainer {...props} flexDirection={direction} grow={grow}>
|
||||
{child1}
|
||||
@@ -169,16 +234,16 @@ function renderSplitLayout(
|
||||
* Use Layout.Top / Right / Bottom / Left to indicate where the fixed element should live.
|
||||
*/
|
||||
export const Layout = {
|
||||
Top(props: SplitLayoutProps) {
|
||||
Top(props: SplitLayoutProps & SplitVerticalResizableProps) {
|
||||
return renderSplitLayout(props, 'column', 2);
|
||||
},
|
||||
Bottom(props: SplitLayoutProps) {
|
||||
Bottom(props: SplitLayoutProps & SplitVerticalResizableProps) {
|
||||
return renderSplitLayout(props, 'column', 1);
|
||||
},
|
||||
Left(props: SplitLayoutProps) {
|
||||
Left(props: SplitLayoutProps & SplitHorizontalResizableProps) {
|
||||
return renderSplitLayout(props, 'row', 2);
|
||||
},
|
||||
Right(props: SplitLayoutProps) {
|
||||
Right(props: SplitLayoutProps & SplitHorizontalResizableProps) {
|
||||
return renderSplitLayout(props, 'row', 1);
|
||||
},
|
||||
Container,
|
||||
|
||||
275
desktop/flipper-plugin/src/ui/Sidebar.tsx
Normal file
275
desktop/flipper-plugin/src/ui/Sidebar.tsx
Normal file
@@ -0,0 +1,275 @@
|
||||
/**
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @format
|
||||
*/
|
||||
|
||||
import {Layout} from './Layout';
|
||||
import {Component, ReactNode} from 'react';
|
||||
import styled from '@emotion/styled';
|
||||
import {Property} from 'csstype';
|
||||
import React from 'react';
|
||||
import {MoreOutlined} from '@ant-design/icons';
|
||||
import {Interactive, InteractiveProps} from './Interactive';
|
||||
import {theme} from './theme';
|
||||
|
||||
const SidebarInteractiveContainer = styled(Interactive)<InteractiveProps>({
|
||||
display: 'flex',
|
||||
flex: 1,
|
||||
});
|
||||
SidebarInteractiveContainer.displayName = 'Sidebar:SidebarInteractiveContainer';
|
||||
|
||||
type SidebarPosition = 'left' | 'top' | 'right' | 'bottom';
|
||||
|
||||
const borderStyle = `1px solid ${theme.dividerColor}`;
|
||||
|
||||
const SidebarContainer = styled(Layout.Container)<{
|
||||
position: 'right' | 'top' | 'left' | 'bottom';
|
||||
overflow?: boolean;
|
||||
unstyled?: boolean;
|
||||
}>((props) => ({
|
||||
...(props.unstyled
|
||||
? undefined
|
||||
: {
|
||||
borderLeft: props.position === 'right' ? borderStyle : 'none',
|
||||
borderTop: props.position === 'bottom' ? borderStyle : 'none',
|
||||
borderRight: props.position === 'left' ? borderStyle : 'none',
|
||||
borderBottom: props.position === 'top' ? borderStyle : 'none',
|
||||
backgroundColor: theme.backgroundDefault,
|
||||
}),
|
||||
flex: 1,
|
||||
}));
|
||||
SidebarContainer.displayName = 'Sidebar:SidebarContainer';
|
||||
|
||||
type SidebarProps = {
|
||||
/**
|
||||
* Position of the sidebar.
|
||||
*/
|
||||
position: SidebarPosition;
|
||||
|
||||
/**
|
||||
* Default width of the sidebar. Only used for left/right sidebars.
|
||||
*/
|
||||
width?: number;
|
||||
/**
|
||||
* Minimum sidebar width. Only used for left/right sidebars.
|
||||
*/
|
||||
minWidth?: number;
|
||||
/**
|
||||
* Maximum sidebar width. Only used for left/right sidebars.
|
||||
*/
|
||||
maxWidth?: number;
|
||||
|
||||
/**
|
||||
* Default height of the sidebar.
|
||||
*/
|
||||
height?: number;
|
||||
/**
|
||||
* Minimum sidebar height. Only used for top/bottom sidebars.
|
||||
*/
|
||||
minHeight?: number;
|
||||
/**
|
||||
* Maximum sidebar height. Only used for top/bottom sidebars.
|
||||
*/
|
||||
maxHeight?: number;
|
||||
/**
|
||||
* Callback when the sidebar size ahs changed.
|
||||
*/
|
||||
onResize?: (width: number, height: number) => void;
|
||||
/**
|
||||
* Contents of the sidebar.
|
||||
*/
|
||||
children?: React.ReactNode;
|
||||
/**
|
||||
* Class name to customise styling.
|
||||
*/
|
||||
className?: string;
|
||||
/**
|
||||
* use a Sandy themed large gutter
|
||||
*/
|
||||
gutter?: boolean;
|
||||
};
|
||||
|
||||
type SidebarState = {
|
||||
width?: Property.Width<number>;
|
||||
height?: Property.Height<number>;
|
||||
userChange: boolean;
|
||||
};
|
||||
|
||||
/**
|
||||
* A resizable sidebar.
|
||||
*/
|
||||
export class Sidebar extends Component<SidebarProps, SidebarState> {
|
||||
constructor(props: SidebarProps, context: Object) {
|
||||
super(props, context);
|
||||
this.state = {
|
||||
userChange: false,
|
||||
width: props.width,
|
||||
height: props.height,
|
||||
};
|
||||
}
|
||||
|
||||
static defaultProps = {
|
||||
position: 'left',
|
||||
};
|
||||
|
||||
static getDerivedStateFromProps(
|
||||
nextProps: SidebarProps,
|
||||
state: SidebarState,
|
||||
) {
|
||||
if (!state.userChange) {
|
||||
return {width: nextProps.width, height: nextProps.height};
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
onResize = (width: number, height: number) => {
|
||||
const {onResize} = this.props;
|
||||
if (onResize) {
|
||||
onResize(width, height);
|
||||
} else {
|
||||
this.setState({userChange: true, width, height});
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
const {onResize, position, children, gutter} = this.props;
|
||||
let height: number | undefined;
|
||||
let minHeight: number | undefined;
|
||||
let maxHeight: number | undefined;
|
||||
let width: number | undefined;
|
||||
let minWidth: number | undefined;
|
||||
let maxWidth: number | undefined;
|
||||
|
||||
const resizable: {[key: string]: boolean} = {};
|
||||
if (position === 'left') {
|
||||
resizable.right = true;
|
||||
({width, minWidth, maxWidth} = this.props);
|
||||
} else if (position === 'top') {
|
||||
resizable.bottom = true;
|
||||
({height, minHeight, maxHeight} = this.props);
|
||||
} else if (position === 'right') {
|
||||
resizable.left = true;
|
||||
({width, minWidth, maxWidth} = this.props);
|
||||
} else if (position === 'bottom') {
|
||||
resizable.top = true;
|
||||
({height, minHeight, maxHeight} = this.props);
|
||||
}
|
||||
|
||||
const horizontal = position === 'left' || position === 'right';
|
||||
const gutterWidth = gutter ? theme.space.large : 0;
|
||||
|
||||
if (horizontal) {
|
||||
width = width == null ? 200 : width;
|
||||
minWidth = (minWidth == null ? 100 : minWidth) + gutterWidth;
|
||||
maxWidth = maxWidth == null ? 600 : maxWidth;
|
||||
} else {
|
||||
height = height == null ? 200 : height;
|
||||
minHeight = minHeight == null ? 100 : minHeight;
|
||||
maxHeight = maxHeight == null ? 600 : maxHeight;
|
||||
}
|
||||
return (
|
||||
<SidebarInteractiveContainer
|
||||
className={this.props.className}
|
||||
minWidth={minWidth}
|
||||
maxWidth={maxWidth}
|
||||
width={
|
||||
horizontal
|
||||
? !children
|
||||
? gutterWidth
|
||||
: onResize
|
||||
? width
|
||||
: this.state.width
|
||||
: undefined
|
||||
}
|
||||
minHeight={minHeight}
|
||||
maxHeight={maxHeight}
|
||||
height={
|
||||
!horizontal
|
||||
? onResize
|
||||
? height
|
||||
: this.state.height
|
||||
: gutter
|
||||
? undefined
|
||||
: '100%'
|
||||
}
|
||||
resizable={resizable}
|
||||
onResize={this.onResize}
|
||||
gutterWidth={gutter ? theme.space.large : undefined}>
|
||||
<SidebarContainer position={position} unstyled={gutter}>
|
||||
{gutter ? (
|
||||
<GutterWrapper position={position}>{children}</GutterWrapper>
|
||||
) : (
|
||||
children
|
||||
)}
|
||||
</SidebarContainer>
|
||||
</SidebarInteractiveContainer>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const GutterWrapper = ({
|
||||
position,
|
||||
children,
|
||||
}: {
|
||||
position: SidebarPosition;
|
||||
children: ReactNode;
|
||||
}) => {
|
||||
switch (position) {
|
||||
case 'right':
|
||||
return (
|
||||
<Layout.Left>
|
||||
<VerticalGutter enabled={!!children} />
|
||||
{children}
|
||||
</Layout.Left>
|
||||
);
|
||||
case 'left':
|
||||
return (
|
||||
<Layout.Right>
|
||||
{children}
|
||||
<VerticalGutter enabled={!!children} />
|
||||
</Layout.Right>
|
||||
);
|
||||
case 'bottom':
|
||||
// TODO: needs rotated styling
|
||||
return (
|
||||
<Layout.Top>
|
||||
<VerticalGutter enabled={!!children} />
|
||||
{children}
|
||||
</Layout.Top>
|
||||
);
|
||||
case 'top':
|
||||
// TODO: needs rotated styling
|
||||
return (
|
||||
<Layout.Bottom>
|
||||
{children}
|
||||
<VerticalGutter enabled={!!children} />
|
||||
</Layout.Bottom>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
const VerticalGutterContainer = styled('div')<{enabled: boolean}>(
|
||||
({enabled}) => ({
|
||||
width: theme.space.large,
|
||||
minWidth: theme.space.large,
|
||||
cursor: enabled ? undefined : 'default', // hide cursor from interactive container
|
||||
color: enabled ? theme.textColorPlaceholder : theme.backgroundWash,
|
||||
fontSize: '16px',
|
||||
display: 'flex',
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
background: theme.backgroundWash,
|
||||
':hover': {
|
||||
background: enabled ? theme.dividerColor : undefined,
|
||||
},
|
||||
}),
|
||||
);
|
||||
const VerticalGutter = ({enabled}: {enabled: boolean}) => (
|
||||
<VerticalGutterContainer enabled={enabled}>
|
||||
<MoreOutlined />
|
||||
</VerticalGutterContainer>
|
||||
);
|
||||
Reference in New Issue
Block a user