diff --git a/docs/app/Components/ComponentDoc/ComponentControls/ComponentControls.js b/docs/app/Components/ComponentDoc/ComponentControls/ComponentControls.js new file mode 100644 index 0000000000..66b72c5b5b --- /dev/null +++ b/docs/app/Components/ComponentDoc/ComponentControls/ComponentControls.js @@ -0,0 +1,64 @@ +import PropTypes from 'prop-types' +import React from 'react' +import { Menu, Transition } from 'semantic-ui-react' + +import { updateForKeys } from 'docs/app/HOC' +import ComponentControlsCopyLink from './ComponentControlsCopyLink' +import ComponentControlsEditCode from './ComponentControlsEditCode' +import ComponentControlsMaximize from './ComponentControlsMaximize' +import ComponentControlsShowHtml from './ComponentControlsShowHtml' + +const ComponentControls = (props) => { + const { + anchorName, showHTML, showCode, + onCopyLink, onShowHTML, onShowCode, + visible, + } = props + + return ( + + {/* Heads up! Don't remove this `div`, visible Transition applies `display: block`, + while Menu should have `display: inline-flex` + */} +
+ + + + + + +
+
+ ) +} + +ComponentControls.propTypes = { + anchorName: PropTypes.string, + onCopyLink: PropTypes.func, + onShowCode: PropTypes.func, + onShowHTML: PropTypes.func, + showCode: PropTypes.bool, + showHTML: PropTypes.bool, + visible: PropTypes.bool, +} + +export default updateForKeys(['showCode', 'showHTML', 'visible'])(ComponentControls) diff --git a/docs/app/Components/ComponentDoc/ComponentControls/ComponentControlsCopyLink.js b/docs/app/Components/ComponentDoc/ComponentControls/ComponentControlsCopyLink.js new file mode 100644 index 0000000000..be3bfce14a --- /dev/null +++ b/docs/app/Components/ComponentDoc/ComponentControls/ComponentControlsCopyLink.js @@ -0,0 +1,56 @@ +import PropTypes from 'prop-types' +import React, { Component } from 'react' +import { Icon, Menu } from 'semantic-ui-react' + +import ComponentControlsToolTip from './ComponentControlsToolTip' + +export default class ComponentControlsCopyLink extends Component { + state = {} + + static propTypes = { + anchorName: PropTypes.string, + onClick: PropTypes.func, + } + + shouldComponentUpdate(nextProps, nextState) { + return this.state.active !== nextState.active + } + + componentDidMount() { + this.mounted = true + } + + componentWillUnmount() { + this.mounted = false + } + + handleClick = (e) => { + const { onClick } = this.props + + e.preventDefault() + onClick() + + this.setState({ active: true }) + setTimeout(this.resetActive, 3000) + } + + resetActive = () => this.mounted && this.setState({ active: false }) + + render() { + const { anchorName } = this.props + const { active } = this.state + + return ( + + + + + + ) + } +} diff --git a/docs/app/Components/ComponentDoc/ComponentControls/ComponentControlsEditCode.js b/docs/app/Components/ComponentDoc/ComponentControls/ComponentControlsEditCode.js new file mode 100644 index 0000000000..cbb2c6589c --- /dev/null +++ b/docs/app/Components/ComponentDoc/ComponentControls/ComponentControlsEditCode.js @@ -0,0 +1,26 @@ +import PropTypes from 'prop-types' +import React from 'react' +import { Icon, Menu } from 'semantic-ui-react' + +import { updateForKeys } from 'docs/app/HOC' +import ComponentControlsToolTip from './ComponentControlsToolTip' + +const ComponentControlsEditCode = ({ active, onClick }) => ( + + + + + +) + +ComponentControlsEditCode.propTypes = { + active: PropTypes.bool, + onClick: PropTypes.func, +} + +export default updateForKeys(['active'])(ComponentControlsEditCode) diff --git a/docs/app/Components/ComponentDoc/ComponentControls/ComponentControlsMaximize.js b/docs/app/Components/ComponentDoc/ComponentControls/ComponentControlsMaximize.js new file mode 100644 index 0000000000..f70c108612 --- /dev/null +++ b/docs/app/Components/ComponentDoc/ComponentControls/ComponentControlsMaximize.js @@ -0,0 +1,25 @@ +import PropTypes from 'prop-types' +import React from 'react' +import { Icon, Menu } from 'semantic-ui-react' + +import { neverUpdate } from 'docs/app/HOC' +import ComponentControlsToolTip from './ComponentControlsToolTip' + +const ComponentControlsMaximize = ({ anchorName }) => ( + + + + + +) + +ComponentControlsMaximize.propTypes = { + anchorName: PropTypes.string, +} + +export default neverUpdate(ComponentControlsMaximize) diff --git a/docs/app/Components/ComponentDoc/ComponentControls/ComponentControlsShowHtml.js b/docs/app/Components/ComponentDoc/ComponentControls/ComponentControlsShowHtml.js new file mode 100644 index 0000000000..34eae91545 --- /dev/null +++ b/docs/app/Components/ComponentDoc/ComponentControls/ComponentControlsShowHtml.js @@ -0,0 +1,26 @@ +import PropTypes from 'prop-types' +import React from 'react' +import { Icon, Menu } from 'semantic-ui-react' + +import { updateForKeys } from 'docs/app/HOC' +import ComponentControlsToolTip from './ComponentControlsToolTip' + +const ComponentControlsShowHtml = ({ active, onClick }) => ( + + + + + +) + +ComponentControlsShowHtml.propTypes = { + active: PropTypes.bool, + onClick: PropTypes.func, +} + +export default updateForKeys(['active'])(ComponentControlsShowHtml) diff --git a/docs/app/Components/ComponentDoc/ComponentControls/ComponentControlsToolTip.js b/docs/app/Components/ComponentDoc/ComponentControls/ComponentControlsToolTip.js new file mode 100644 index 0000000000..bbaf350ac6 --- /dev/null +++ b/docs/app/Components/ComponentDoc/ComponentControls/ComponentControlsToolTip.js @@ -0,0 +1,28 @@ +import PropTypes from 'prop-types' +import React from 'react' +import { Popup } from 'semantic-ui-react' + +const toolTipStyle = { + padding: '0.5em', + textAlign: 'center', + width: 100, +} + +const ComponentControlsToolTip = ({ children, content }) => ( + +) + +ComponentControlsToolTip.propTypes = { + children: PropTypes.node, + content: PropTypes.node, +} + +export default ComponentControlsToolTip diff --git a/docs/app/Components/ComponentDoc/ComponentControls/index.js b/docs/app/Components/ComponentDoc/ComponentControls/index.js new file mode 100644 index 0000000000..8e0a5c9949 --- /dev/null +++ b/docs/app/Components/ComponentDoc/ComponentControls/index.js @@ -0,0 +1 @@ +export default from './ComponentControls' diff --git a/docs/app/Components/ComponentDoc/ComponentExample.js b/docs/app/Components/ComponentDoc/ComponentExample/ComponentExample.js similarity index 81% rename from docs/app/Components/ComponentDoc/ComponentExample.js rename to docs/app/Components/ComponentDoc/ComponentExample/ComponentExample.js index 98352c16f3..a2529856b7 100644 --- a/docs/app/Components/ComponentDoc/ComponentExample.js +++ b/docs/app/Components/ComponentDoc/ComponentExample/ComponentExample.js @@ -8,8 +8,9 @@ import { html } from 'js-beautify' import copyToClipboard from 'copy-to-clipboard' import { exampleContext, repoURL, scrollToAnchor } from 'docs/app/utils' -import { Divider, Grid, Icon, Header, Menu, Popup } from 'src' +import { Divider, Grid, Header, Menu } from 'src' import Editor from 'docs/app/Components/Editor/Editor' +import ComponentControls from '../ComponentControls' const babelConfig = { presets: ['es2015', 'react', 'stage-1'], @@ -40,23 +41,6 @@ const errorStyle = { background: '#fff2f2', } -const toolTipStyle = { width: 100, textAlign: 'center', padding: '0.5em' } -const ToolTip = ({ children, content }) => ( - -) -ToolTip.propTypes = { - children: PropTypes.node, - content: PropTypes.node, -} - /** * Renders a `component` and the raw `code` that produced it. * Allows toggling the the raw `code` code block. @@ -102,16 +86,15 @@ class ComponentExample extends Component { history.replace(location.pathname) } - handleDirectLinkClick = (e) => { - e.preventDefault() + handleDirectLinkClick = () => { this.setHashAndScroll() - copyToClipboard(location.href) - this.setState({ copiedDirectLink: true }) - - setTimeout(() => this.setState({ copiedDirectLink: false }), 1000) } + handleMouseEnter = () => this.setState({ controlsVisible: true }) + + handleMouseLeave = () => this.setState({ controlsVisible: false }) + handleShowCodeClick = (e) => { e.preventDefault() @@ -150,7 +133,7 @@ class ComponentExample extends Component { getOriginalSourceCode = () => { const { examplePath } = this.props - if (!this.sourceCode) this.sourceCode = require(`!raw-loader!../../Examples/${examplePath}`) + if (!this.sourceCode) this.sourceCode = require(`!raw-loader!../../../Examples/${examplePath}`) return this.sourceCode } @@ -392,7 +375,7 @@ class ComponentExample extends Component { render() { const { children, description, title } = this.props - const { copiedDirectLink, exampleElement, showCode, showHTML } = this.state + const { controlsVisible, exampleElement, showCode, showHTML } = this.state const exampleStyle = {} if (showCode || showHTML || location.hash === `#${this.anchorName}`) { @@ -400,43 +383,46 @@ class ComponentExample extends Component { } return ( - - - {title &&
} - {description &&

{description}

} - - - - - - - - - - - - - - - - - - - - - - - - {children && ( - - {children} + + + + {title &&
} + {description &&

{description}

} - )} - - {exampleElement} - - {this.renderJSX()} - {this.renderHTML()} + + + + + + + {children && ( + + {children} + + )} + + + + + {exampleElement} + + {this.renderJSX()} + {this.renderHTML()} + ) } diff --git a/docs/app/Components/ComponentDoc/ComponentExample/index.js b/docs/app/Components/ComponentDoc/ComponentExample/index.js new file mode 100644 index 0000000000..5d0308ea7a --- /dev/null +++ b/docs/app/Components/ComponentDoc/ComponentExample/index.js @@ -0,0 +1 @@ +export default from './ComponentExample' diff --git a/docs/app/Components/ComponentDoc/ComponentProps.js b/docs/app/Components/ComponentDoc/ComponentProps/ComponentProps.js similarity index 74% rename from docs/app/Components/ComponentDoc/ComponentProps.js rename to docs/app/Components/ComponentDoc/ComponentProps/ComponentProps.js index 2354960af0..6a24944b87 100644 --- a/docs/app/Components/ComponentDoc/ComponentProps.js +++ b/docs/app/Components/ComponentDoc/ComponentProps/ComponentProps.js @@ -3,27 +3,8 @@ import PropTypes from 'prop-types' import React, { Component } from 'react' import { Icon, Popup, Table } from 'src' - -const extraDescriptionStyle = { - color: '#666', -} -const extraDescriptionContentStyle = { - marginLeft: '0.5em', -} - -const Extra = ({ title, children, inline, ...rest }) => ( -
- {title} -
- {children} -
-
-) -Extra.propTypes = { - children: PropTypes.node, - inline: PropTypes.bool, - title: PropTypes.node, -} +import ComponentPropsEnum from './ComponentPropsEnum' +import ComponentPropsExtra from './ComponentPropsExtra' const getTagType = tag => (tag.type.type === 'AllLiteral' ? 'any' : tag.type.name) @@ -106,40 +87,22 @@ export default class ComponentProps extends Component { }) return ( - {item.name}({paramSignature}){returns ? `: ${getTagType(returns)}` : ''}}> + {item.name}({paramSignature}){returns ? `: ${getTagType(returns)}` : ''}}> {tagDescriptionRows} - + ) } renderEnums = ({ name, type, value }) => { - if (type !== '{enum}' || !value) return - const { showEnumsFor } = this.state - const truncateAt = 10 - const valueElements = _.map(value, val => {val} ) - - // show all if there are few - if (value.length < truncateAt) return {valueElements} - - // add button to show more when there are many values and it is not toggled - if (!showEnumsFor[name]) { - return ( - - - Show all {value.length} - -
{valueElements.slice(0, truncateAt - 1)}...
-
- ) - } - // add "show more" button when there are many + if (type !== '{enum}' || !value) return return ( - - Show less -
{valueElements}
-
+ ) } diff --git a/docs/app/Components/ComponentDoc/ComponentProps/ComponentPropsEnum.js b/docs/app/Components/ComponentDoc/ComponentProps/ComponentPropsEnum.js new file mode 100644 index 0000000000..e7c87f4dac --- /dev/null +++ b/docs/app/Components/ComponentDoc/ComponentProps/ComponentPropsEnum.js @@ -0,0 +1,43 @@ +import _ from 'lodash' +import PropTypes from 'prop-types' +import React from 'react' + +import { updateForKeys } from 'docs/app/HOC' +import ComponentPropsExtra from './ComponentPropsExtra' +import ComponentPropsToggle from './ComponentPropsEnumToggle' +import ComponentPropsValue from './ComponentPropsEnumValue' + +const ComponentPropsEnum = ({ limit, showAll, toggle, values }) => { + const exceeds = values.length > limit + const sliced = showAll ? values : _.slice(values, 0, limit) + + return ( + + {exceeds && ( + + )} + +
+ {_.map(sliced, value => {value})} + {exceeds && !showAll && '...'} +
+
+ ) +} + +ComponentPropsEnum.defaultProps = { + limit: 10, +} + +ComponentPropsEnum.propTypes = { + limit: PropTypes.number, + showAll: PropTypes.bool, + toggle: PropTypes.func, + values: PropTypes.array, +} + +export default updateForKeys(['showAll'])(ComponentPropsEnum) diff --git a/docs/app/Components/ComponentDoc/ComponentProps/ComponentPropsEnumToggle.js b/docs/app/Components/ComponentDoc/ComponentProps/ComponentPropsEnumToggle.js new file mode 100644 index 0000000000..1186444ac1 --- /dev/null +++ b/docs/app/Components/ComponentDoc/ComponentProps/ComponentPropsEnumToggle.js @@ -0,0 +1,22 @@ +import PropTypes from 'prop-types' +import React from 'react' + +import { updateForKeys } from 'docs/app/HOC' + +const toggleStyle = { + cursor: 'pointer', +} + +const ComponentPropsEnumToggle = ({ showAll, toggle, total }) => ( + + {showAll ? 'Show less' : `Show all ${total}`} + +) + +ComponentPropsEnumToggle.propTypes = { + showAll: PropTypes.bool, + toggle: PropTypes.func, + total: PropTypes.number, +} + +export default updateForKeys(['showAll'])(ComponentPropsEnumToggle) diff --git a/docs/app/Components/ComponentDoc/ComponentProps/ComponentPropsEnumValue.js b/docs/app/Components/ComponentDoc/ComponentProps/ComponentPropsEnumValue.js new file mode 100644 index 0000000000..e85a479122 --- /dev/null +++ b/docs/app/Components/ComponentDoc/ComponentProps/ComponentPropsEnumValue.js @@ -0,0 +1,21 @@ +import PropTypes from 'prop-types' +import React from 'react' + +import { neverUpdate } from 'docs/app/HOC' + +const spanStyle = { + display: 'inline-block', + paddingRight: '0.2em', +} + +const ComponentPropsEnumValue = ({ children }) => ( + + {children} + +) + +ComponentPropsEnumValue.propTypes = { + children: PropTypes.node, +} + +export default neverUpdate(ComponentPropsEnumValue) diff --git a/docs/app/Components/ComponentDoc/ComponentProps/ComponentPropsExtra.js b/docs/app/Components/ComponentDoc/ComponentProps/ComponentPropsExtra.js new file mode 100644 index 0000000000..8df08ecdaf --- /dev/null +++ b/docs/app/Components/ComponentDoc/ComponentProps/ComponentPropsExtra.js @@ -0,0 +1,34 @@ +import PropTypes from 'prop-types' +import React from 'react' + +const descriptionStyle = { + color: '#666', +} +const contentStyle = { + marginLeft: '0.5em', +} +const contentBlockStyle = { + ...contentStyle, + display: 'block', +} +const contentInlineStyle = { + ...contentStyle, + display: 'inline', +} + +const ComponentPropsExtra = ({ children, inline, title, ...rest }) => ( +
+ {title} +
+ {children} +
+
+) + +ComponentPropsExtra.propTypes = { + children: PropTypes.node, + inline: PropTypes.bool, + title: PropTypes.node, +} + +export default ComponentPropsExtra diff --git a/docs/app/Components/ComponentDoc/ComponentProps/index.js b/docs/app/Components/ComponentDoc/ComponentProps/index.js new file mode 100644 index 0000000000..7bf82a824a --- /dev/null +++ b/docs/app/Components/ComponentDoc/ComponentProps/index.js @@ -0,0 +1 @@ +export default from './ComponentProps' diff --git a/docs/app/HOC/index.js b/docs/app/HOC/index.js new file mode 100644 index 0000000000..f65e11d07d --- /dev/null +++ b/docs/app/HOC/index.js @@ -0,0 +1,2 @@ +export neverUpdate from './neverUpdate' +export updateForKeys from './updateForKeys' diff --git a/docs/app/HOC/neverUpdate.js b/docs/app/HOC/neverUpdate.js new file mode 100644 index 0000000000..7c65a6d297 --- /dev/null +++ b/docs/app/HOC/neverUpdate.js @@ -0,0 +1,13 @@ +import React, { Component } from 'react' + +const neverUpdate = ChildComponent => class extends Component { + shouldComponentUpdate() { + return false + } + + render() { + return + } +} + +export default neverUpdate diff --git a/docs/app/HOC/updateForKeys.js b/docs/app/HOC/updateForKeys.js new file mode 100644 index 0000000000..6338583d43 --- /dev/null +++ b/docs/app/HOC/updateForKeys.js @@ -0,0 +1,16 @@ +import _ from 'lodash' +import React, { Component } from 'react' + +import { shallowEqual } from 'src/lib' + +const updateForKeys = propKeys => ChildComponent => class extends Component { + shouldComponentUpdate(nextProps) { + return !shallowEqual(_.pick(this.props, propKeys), _.pick(nextProps, propKeys)) + } + + render() { + return + } +} + +export default updateForKeys diff --git a/docs/app/index.ejs b/docs/app/index.ejs index 73573aa8b9..5a472dceac 100644 --- a/docs/app/index.ejs +++ b/docs/app/index.ejs @@ -163,26 +163,6 @@ .docs-icon-set-column:hover > .icon { transform: scale(1.5); } - - /* Examples */ - - .docs-example-menu { - transition: opacity 200ms; - position: absolute; - top: 1rem; - right: 1rem; - opacity: 0; - } - - .docs-example:hover .docs-example-menu { - opacity: 1; - } - - .docs-example { - margin-bottom: 2em !important; - padding-bottom: 1em !important; - transition: box-shadow 300ms; - } diff --git a/src/lib/index.js b/src/lib/index.js index ade9ce13b6..e3ab09dae3 100644 --- a/src/lib/index.js +++ b/src/lib/index.js @@ -40,3 +40,4 @@ export { default as keyboardKey } from './keyboardKey' export { numberToWordMap, numberToWord } from './numberToWord' export normalizeTransitionDuration from './normalizeTransitionDuration' export { default as objectDiff } from './objectDiff' +export shallowEqual from './shallowEqual' diff --git a/src/lib/shallowEqual.js b/src/lib/shallowEqual.js new file mode 100644 index 0000000000..9525c8fae8 --- /dev/null +++ b/src/lib/shallowEqual.js @@ -0,0 +1,3 @@ +import shallowEqual from 'fbjs/lib/shallowEqual' + +export default shallowEqual