Summary: _typescript_ Reviewed By: passy Differential Revision: D16830540 fbshipit-source-id: 43741df5844a9f33930af8846cfcb2b28c9c6278
191 lines
5.0 KiB
TypeScript
191 lines
5.0 KiB
TypeScript
/**
|
|
* Copyright 2018-present Facebook.
|
|
* This source code is licensed under the MIT license found in the
|
|
* LICENSE file in the root directory of this source tree.
|
|
* @format
|
|
*/
|
|
|
|
import Interactive from './Interactive';
|
|
import FlexColumn from './FlexColumn';
|
|
import {colors} from './colors';
|
|
import {Component} from 'react';
|
|
import styled from 'react-emotion';
|
|
import {
|
|
BackgroundClipProperty,
|
|
HeightProperty,
|
|
WidthProperty,
|
|
BackgroundColorProperty,
|
|
} from 'csstype';
|
|
import React from 'react';
|
|
|
|
const SidebarInteractiveContainer = styled(Interactive)({
|
|
flex: 'none',
|
|
});
|
|
|
|
type SidebarPosition = 'left' | 'top' | 'right' | 'bottom';
|
|
|
|
const SidebarContainer = styled(FlexColumn)(
|
|
(props: {
|
|
position: 'right' | 'top' | 'left' | 'bottom';
|
|
backgroundColor?: BackgroundClipProperty;
|
|
overflow?: boolean;
|
|
}) => ({
|
|
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',
|
|
height: '100%',
|
|
overflowX: 'hidden',
|
|
overflowY: 'auto',
|
|
textOverflow: props.overflow ? 'ellipsis' : 'auto',
|
|
whiteSpace: props.overflow ? 'nowrap' : 'normal',
|
|
}),
|
|
);
|
|
|
|
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;
|
|
|
|
/**
|
|
* Background color.
|
|
*/
|
|
backgroundColor?: BackgroundColorProperty;
|
|
/**
|
|
* 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;
|
|
};
|
|
|
|
type SidebarState = {
|
|
width?: WidthProperty<number>;
|
|
height?: HeightProperty<number>;
|
|
userChange: boolean;
|
|
};
|
|
|
|
/**
|
|
* A resizable sidebar.
|
|
*/
|
|
export default 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',
|
|
};
|
|
|
|
componentWillReceiveProps(nextProps: SidebarProps) {
|
|
if (!this.state.userChange) {
|
|
this.setState({width: nextProps.width, height: nextProps.height});
|
|
}
|
|
}
|
|
|
|
onResize = (width: number, height: number) => {
|
|
const {onResize} = this.props;
|
|
if (onResize) {
|
|
onResize(width, height);
|
|
} else {
|
|
this.setState({userChange: true, width, height});
|
|
}
|
|
};
|
|
|
|
render() {
|
|
const {backgroundColor, onResize, position, children} = this.props;
|
|
let height: number;
|
|
let minHeight: number;
|
|
let maxHeight: number;
|
|
let width: number;
|
|
let minWidth: number;
|
|
let maxWidth: number;
|
|
|
|
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';
|
|
|
|
if (horizontal) {
|
|
width = width == null ? 200 : width;
|
|
minWidth = minWidth == null ? 100 : minWidth;
|
|
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 ? (onResize ? width : this.state.width) : undefined}
|
|
minHeight={minHeight}
|
|
maxHeight={maxHeight}
|
|
height={
|
|
!horizontal ? (onResize ? height : this.state.height) : undefined
|
|
}
|
|
resizable={resizable}
|
|
onResize={this.onResize}>
|
|
<SidebarContainer position={position} backgroundColor={backgroundColor}>
|
|
{children}
|
|
</SidebarContainer>
|
|
</SidebarInteractiveContainer>
|
|
);
|
|
}
|
|
}
|