UNPKG

lucid-ui

Version:

A UI component library from AppNexus.

205 lines (204 loc) 8.04 kB
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 };