New: Project Aphrodite

This commit is contained in:
Qstick
2018-11-23 02:04:42 -05:00
parent 65efa15551
commit 8430cb40ab
1080 changed files with 73015 additions and 0 deletions

View File

@@ -0,0 +1,16 @@
.toolbar {
display: flex;
justify-content: space-between;
flex: 0 0 auto;
padding: 0 20px;
height: $toolbarHeight;
background-color: $toolbarBackgroundColor;
color: $toolbarColor;
line-height: 60px;
}
@media only screen and (max-width: $breakpointSmall) {
.toolbar {
padding: 0 10px;
}
}

View File

@@ -0,0 +1,33 @@
import PropTypes from 'prop-types';
import React, { Component } from 'react';
import styles from './PageToolbar.css';
class PageToolbar extends Component {
//
// Render
render() {
const {
className,
children
} = this.props;
return (
<div className={className}>
{children}
</div>
);
}
}
PageToolbar.propTypes = {
className: PropTypes.string,
children: PropTypes.node.isRequired
};
PageToolbar.defaultProps = {
className: styles.toolbar
};
export default PageToolbar;

View File

@@ -0,0 +1,32 @@
.toolbarButton {
composes: link from 'Components/Link/Link.css';
width: $toolbarButtonWidth;
text-align: center;
&:hover {
color: $toobarButtonHoverColor;
}
&.isDisabled {
color: $disabledColor;
}
}
.isDisabled {
color: $disabledColor;
}
.labelContainer {
display: flex;
align-items: center;
justify-content: center;
min-height: 16px;
}
.label {
padding: 0 3px;
color: $toolbarLabelColor;
font-size: $extraSmallFontSize;
line-height: calc($extraSmallFontSize + 1px);
}

View File

@@ -0,0 +1,57 @@
import PropTypes from 'prop-types';
import React from 'react';
import classNames from 'classnames';
import { icons } from 'Helpers/Props';
import Icon from 'Components/Icon';
import Link from 'Components/Link/Link';
import styles from './PageToolbarButton.css';
function PageToolbarButton(props) {
const {
label,
iconName,
spinningName,
isDisabled,
isSpinning,
...otherProps
} = props;
return (
<Link
className={classNames(
styles.toolbarButton,
isDisabled && styles.isDisabled
)}
isDisabled={isDisabled || isSpinning}
{...otherProps}
>
<Icon
name={isSpinning ? (spinningName || iconName) : iconName}
isSpinning={isSpinning}
size={21}
/>
<div className={styles.labelContainer}>
<div className={styles.label}>
{label}
</div>
</div>
</Link>
);
}
PageToolbarButton.propTypes = {
label: PropTypes.string.isRequired,
iconName: PropTypes.object.isRequired,
spinningName: PropTypes.object,
isSpinning: PropTypes.bool,
isDisabled: PropTypes.bool
};
PageToolbarButton.defaultProps = {
spinningName: icons.SPINNER,
isDisabled: false,
isSpinning: false
};
export default PageToolbarButton;

View File

@@ -0,0 +1,40 @@
.sectionContainer {
display: flex;
flex: 1 1 10%;
overflow: hidden;
}
.section {
display: flex;
align-items: center;
flex-grow: 1;
}
.left {
justify-content: flex-start;
}
.center {
justify-content: center;
}
.right {
justify-content: flex-end;
}
.overflowMenuButton {
composes: menuButton from 'Components/Menu/ToolbarMenuButton.css';
}
.overflowMenuItemIcon {
margin-right: 8px;
}
@media only screen and (max-width: $breakpointSmall) {
.overflowMenuButton {
&::after {
margin-left: 0;
content: '\25BE';
}
}
}

View File

@@ -0,0 +1,221 @@
import PropTypes from 'prop-types';
import React, { Component } from 'react';
import classNames from 'classnames';
import { forEach } from 'Helpers/elementChildren';
import { align, icons } from 'Helpers/Props';
import dimensions from 'Styles/Variables/dimensions';
import SpinnerIcon from 'Components/SpinnerIcon';
import Measure from 'Components/Measure';
import Menu from 'Components/Menu/Menu';
import MenuContent from 'Components/Menu/MenuContent';
import MenuItem from 'Components/Menu/MenuItem';
import ToolbarMenuButton from 'Components/Menu/ToolbarMenuButton';
import styles from './PageToolbarSection.css';
const BUTTON_WIDTH = parseInt(dimensions.toolbarButtonWidth);
const SEPARATOR_MARGIN = parseInt(dimensions.toolbarSeparatorMargin);
const SEPARATOR_WIDTH = 2 * SEPARATOR_MARGIN + 1;
const SEPARATOR_NAME = 'PageToolbarSeparator';
function calculateOverflowItems(children, isMeasured, width, collapseButtons) {
let buttonCount = 0;
let separatorCount = 0;
const validChildren = [];
forEach(children, (child) => {
const name = child.type.name;
if (name === SEPARATOR_NAME) {
separatorCount++;
} else {
buttonCount++;
}
validChildren.push(child);
});
const buttonsWidth = buttonCount * BUTTON_WIDTH;
const separatorsWidth = separatorCount + SEPARATOR_WIDTH;
const totalWidth = buttonsWidth + separatorsWidth;
// If the width of buttons and separators is less than
// the available width return all valid children.
if (
!isMeasured ||
!collapseButtons ||
totalWidth < width
) {
return {
buttons: validChildren,
buttonCount,
overflowItems: []
};
}
const maxButtons = Math.max(Math.floor((width - separatorsWidth) / BUTTON_WIDTH), 1);
const buttons = [];
const overflowItems = [];
let actualButtons = 0;
// Return all buttons if only one is being pushed to the overflow menu.
if (buttonCount - 1 === maxButtons) {
return {
buttons: validChildren,
buttonCount,
overflowItems: []
};
}
validChildren.forEach((child, index) => {
if (actualButtons < maxButtons) {
if (child.type.name !== SEPARATOR_NAME) {
buttons.push(child);
actualButtons++;
}
} else if (child.type.name !== SEPARATOR_NAME) {
overflowItems.push(child.props);
}
});
return {
buttons,
buttonCount,
overflowItems
};
}
class PageToolbarSection extends Component {
//
// Lifecycle
constructor(props, context) {
super(props, context);
this.state = {
isMeasured: false,
width: 0,
buttons: [],
overflowItems: []
};
}
//
// Listeners
onMeasure = ({ width }) => {
this.setState({
isMeasured: true,
width
});
}
//
// Render
render() {
const {
children,
alignContent,
collapseButtons
} = this.props;
const {
isMeasured,
width
} = this.state;
const {
buttons,
buttonCount,
overflowItems
} = calculateOverflowItems(children, isMeasured, width, collapseButtons);
return (
<Measure
whitelist={['width']}
onMeasure={this.onMeasure}
>
<div
className={styles.sectionContainer}
style={{
flexGrow: buttonCount
}}
>
{
isMeasured ?
<div className={classNames(
styles.section,
styles[alignContent]
)}
>
{
buttons.map((button) => {
return button;
})
}
{
!!overflowItems.length &&
<Menu>
<ToolbarMenuButton
className={styles.overflowMenuButton}
iconName={icons.OVERFLOW}
text="More"
/>
<MenuContent>
{
overflowItems.map((item) => {
const {
iconName,
spinningName,
label,
isDisabled,
isSpinning,
...otherProps
} = item;
return (
<MenuItem
key={label}
isDisabled={isDisabled || isSpinning}
{...otherProps}
>
<SpinnerIcon
className={styles.overflowMenuItemIcon}
name={iconName}
spinningName={spinningName}
isSpinning={isSpinning}
/>
{label}
</MenuItem>
);
})
}
</MenuContent>
</Menu>
}
</div> :
null
}
</div>
</Measure>
);
}
}
PageToolbarSection.propTypes = {
children: PropTypes.node,
alignContent: PropTypes.oneOf([align.LEFT, align.CENTER, align.RIGHT]),
collapseButtons: PropTypes.bool.isRequired
};
PageToolbarSection.defaultProps = {
alignContent: align.LEFT,
collapseButtons: true
};
export default PageToolbarSection;

View File

@@ -0,0 +1,12 @@
.separator {
margin: 10px $toolbarSeparatorMargin;
height: 40px;
border-right: 1px solid #e5e5e5;
opacity: 0.35;
}
@media only screen and (max-width: $breakpointSmall) {
.separator {
margin: 10px 5px;
}
}

View File

@@ -0,0 +1,17 @@
import React, { Component } from 'react';
import styles from './PageToolbarSeparator.css';
class PageToolbarSeparator extends Component {
//
// Render
render() {
return (
<div className={styles.separator} />
);
}
}
export default PageToolbarSeparator;