lucid-ui
Version:
A UI component library from AppNexus.
205 lines (204 loc) • 8.04 kB
JavaScript
import _ from 'lodash';
import React from 'react';
import PropTypes from 'react-peek/prop-types';
import { lucidClassNames } from '../../util/style-helpers';
import { filterTypes, findTypes, omitProps, } from '../../util/component-types';
import { buildModernHybridComponent } from '../../util/state-management';
import * as reducers from './Sidebar.reducers';
import SplitVertical from '../SplitVertical/SplitVertical';
import Button from '../Button/Button';
import ChevronIcon from '../Icon/ChevronIcon/ChevronIcon';
import GripperVerticalIcon from '../Icon/GripperVerticalIcon/GripperVerticalIcon';
const cx = lucidClassNames.bind('&-Sidebar');
const { any, bool, func, node, number, string, object, oneOf, oneOfType, } = PropTypes;
const defaultProps = {
isExpanded: true,
isAnimated: true,
width: 250,
position: 'left',
isResizeDisabled: false,
onResizing: _.noop,
onResize: _.noop,
onToggle: _.noop,
};
const Primary = (_props) => null;
Primary.peek = {
description: `
Main pane content that will have a paired \`Bar\`.
`,
};
Primary.displayName = 'SplitHorizontal.Primary';
Primary.propName = 'Primary';
const Title = (_props) => null;
Title.peek = {
description: `
Sidebar title;
`,
};
Title.propTypes = {
children: node `
Content that will be displayed as the title of the Bar. It's only
shown when the user has the Bar expanded.
`,
};
Title.propTypes = {
children: node `
Sidebar title.
`,
};
Title.displayName = 'Sidebar.Title';
Title.propName = ['Title', 'title'];
const Bar = (_props) => null;
Bar.peek = {
description: `
Content to be placed alongside the Primary pane.
`,
};
Bar.displayName = 'Sidebar.Bar';
Bar.propName = 'Bar';
Bar.propTypes = {
Title: any `
Set the title of the Sidebar. (alias for \`title\` and \`Sidebar.Title\`)
`,
title: any `
Set the title of the Sidebar. (alias for \`Title\` and \`Sidebar.Title\`)
`,
hasGutters: bool `
Adds default padding to the sidebar content.
`,
};
Bar.defaultProps = {
hasGutters: true,
};
class Sidebar extends React.Component {
constructor() {
super(...arguments);
this.handleExpanderClick = (event) => {
const { onToggle } = this.props;
onToggle && onToggle({ props: this.props, event });
};
this.handleResizing = (width, { event }) => {
const { onResizing } = this.props;
onResizing && onResizing(width, { props: this.props, event });
};
this.handleResize = (width, { event }) => {
const { onResize } = this.props;
onResize && onResize(width, { props: this.props, event });
};
}
render() {
const { children, style, className, isExpanded, isAnimated, position, isResizeDisabled, width, ...passThroughs } = this.props;
const primaryProps = _.get(_.first(filterTypes(children, Sidebar.Primary)), 'props', {}); // props from first Primary
const barProps = _.get(_.first(filterTypes(children, Sidebar.Bar)), 'props', {}); // props from first Bar
const titleProps = _.get(findTypes(barProps, Sidebar.Title).concat(findTypes(this.props, Sidebar.Title)), // get titles from Bar and parent Sidebar
'[0].props', // select props from the first title element
{ children: 'Title' } // default props
);
let PrimaryPane, BarPane; // using Left/Right Pane as primary depends on position
if (position !== 'right') {
PrimaryPane = SplitVertical.RightPane;
BarPane = SplitVertical.LeftPane;
}
else {
PrimaryPane = SplitVertical.LeftPane;
BarPane = SplitVertical.RightPane;
}
return (React.createElement(SplitVertical, Object.assign({}, omitProps(passThroughs, undefined, _.keys(Sidebar.propTypes), false), { style: {
minWidth: isExpanded
? _.isNumber(width)
? width + 6
: `calc(${width} + 6px)`
: undefined,
...style,
}, className: cx('&', {
'&-is-resize-disabled': isResizeDisabled,
'&-is-position-right': position === 'right',
'&-is-position-left': position !== 'right',
}, className), isAnimated: isAnimated, isExpanded: isExpanded, collapseShift: 33, onResizing: this.handleResizing, onResize: this.handleResize }),
React.createElement(BarPane, Object.assign({}, omitProps(barProps, undefined, _.keys(Sidebar.Bar.propTypes), false), { className: cx('&-Bar', barProps.className), width: width, style: {
overflow: isExpanded ? 'auto' : 'hidden',
} }),
React.createElement("div", { className: cx('&-Bar-overlay') }),
React.createElement("div", { className: cx('&-Bar-header') },
React.createElement("div", Object.assign({}, titleProps, { className: cx('&-Bar-Title', titleProps.className) })),
React.createElement(Button, { className: cx('&-expander'), kind: 'invisible', onMouseDown: this.handleExpanderClick, hasOnlyIcon: true },
React.createElement(ChevronIcon, { direction: (isExpanded && position === 'right') ||
(!isExpanded && position !== 'right')
? 'right'
: 'left' }))),
React.createElement("div", { className: cx('&-Bar-content', {
'&-Bar-content-has-gutters': barProps.hasGutters,
}) }, barProps.children)),
React.createElement(SplitVertical.Divider, { className: cx('&-Divider') },
React.createElement(GripperVerticalIcon, { className: cx('&-Divider-gripper') })),
React.createElement(PrimaryPane, Object.assign({}, primaryProps, { className: cx('&-Primary', primaryProps.className), isPrimary: true }))));
}
}
Sidebar.displayName = 'Sidebar';
Sidebar.Bar = Bar;
Sidebar.Primary = Primary;
Sidebar.Title = Title;
Sidebar.peek = {
description: `
\`Sidebar\` renders a collapsible, resizeable side bar panel next to
primary content.
`,
categories: ['layout'],
};
Sidebar.reducers = reducers;
Sidebar.propTypes = {
style: object `
Style object that gets applied to the outer element.
`,
className: string `
Appended to the component-specific class names set on the root element.
Value is run through the \`classnames\` library.
`,
children: node `
Direct children must be types {Sidebar.Primary, Sidebar.Bar,
Sidebar.Title}. All content is composed as children of these respective
elements.
`,
width: oneOfType([number, string]) `
Sets the starting width of the Bar.
`,
isExpanded: bool `
Force the Sidebar to be expanded or collapsed.
`,
isAnimated: bool `
Allows animated expand and collapse behavior.
`,
position: oneOf(['left', 'right']) `
Render the Sidebar to the left or right of primary content.
`,
isResizeDisabled: bool `
Disable user resizing of the Sidebar.
`,
title: any `
Set the title of the Sidebar. (alias for \`Title\` and \`Sidebar.Title\`)
`,
Title: any `
Set the title of the Sidebar. (alias for \`title\` and \`Sidebar.Title\`)
`,
Bar: any `
Content to be placed alongside the Primary pane.
`,
Primary: any `
Main pane content that will have a paired \`Bar\`.
`,
onResizing: func `
Called when the user is currently resizing the Sidebar. Signature:
\`(width, { event, props }) => {}\`
`,
onResize: func `
Called when the user resizes the Sidebar. Signature: \`(width, { event,
props }) => {}\`
`,
onToggle: func `
Called when the user expands or collapses the Sidebar. Signature: \`({
event, props }) => {}\`
`,
};
Sidebar.defaultProps = defaultProps;
export default buildModernHybridComponent(Sidebar, { reducers });
export { Sidebar as SidebarDumb };