<Button /> supports icons directly to style hover states
This commit is contained in:
parent
56865047fd
commit
7cf3a0bea0
7 changed files with 52 additions and 80 deletions
|
@ -4,6 +4,7 @@ import styled from 'styled-components';
|
||||||
import { Link } from 'react-router';
|
import { Link } from 'react-router';
|
||||||
|
|
||||||
import { remSize, prop } from '../theme';
|
import { remSize, prop } from '../theme';
|
||||||
|
import Icon, { ValidIconNameType } from './Icon';
|
||||||
|
|
||||||
// The '&&&' will increase the specificity of the
|
// The '&&&' will increase the specificity of the
|
||||||
// component's CSS so that it overrides the more
|
// component's CSS so that it overrides the more
|
||||||
|
@ -28,6 +29,10 @@ const StyledButton = styled.button`
|
||||||
&:hover:not(:disabled) {
|
&:hover:not(:disabled) {
|
||||||
color: ${prop('buttonHoverColor')};
|
color: ${prop('buttonHoverColor')};
|
||||||
background-color: ${prop('buttonHoverColorBackground')};
|
background-color: ${prop('buttonHoverColorBackground')};
|
||||||
|
|
||||||
|
svg * {
|
||||||
|
fill: ${prop('buttonHoverColor')};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&:disabled {
|
&:disabled {
|
||||||
|
@ -46,27 +51,36 @@ const StyledButton = styled.button`
|
||||||
* A Button performs an primary action
|
* A Button performs an primary action
|
||||||
*/
|
*/
|
||||||
const Button = ({
|
const Button = ({
|
||||||
children, href, label, to, type, ...props
|
children, href, iconAfterName, iconBeforeName, label, to, type, ...props
|
||||||
}) => {
|
}) => {
|
||||||
|
const iconAfter = iconAfterName && <Icon name={iconAfterName} />;
|
||||||
|
const iconBefore = iconBeforeName && <Icon name={iconBeforeName} />;
|
||||||
|
|
||||||
|
const content = <>{iconBefore}<span>{children}</span>{iconAfter}</>;
|
||||||
|
|
||||||
if (href) {
|
if (href) {
|
||||||
return <StyledButton as="a" aria-label={label} href={href} {...props}>{children}</StyledButton>;
|
return <StyledButton as="a" aria-label={label} href={href} {...props}>{content}</StyledButton>;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (to) {
|
if (to) {
|
||||||
return <StyledButton as={Link} aria-label={label} to={to} {...props}>{children}</StyledButton>;
|
return <StyledButton as={Link} aria-label={label} to={to} {...props}>{content}</StyledButton>;
|
||||||
}
|
}
|
||||||
|
|
||||||
return <StyledButton aria-label={label} type={type} {...props}>{children}</StyledButton>;
|
return <StyledButton aria-label={label} type={type} {...props}>{content}</StyledButton>;
|
||||||
};
|
};
|
||||||
|
|
||||||
Button.defaultProps = {
|
Button.defaultProps = {
|
||||||
disabled: false,
|
disabled: false,
|
||||||
|
iconAfterName: null,
|
||||||
|
iconBeforeName: null,
|
||||||
href: null,
|
href: null,
|
||||||
label: null,
|
label: null,
|
||||||
to: null,
|
to: null,
|
||||||
type: 'button',
|
type: 'button',
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Button.iconNames = Icon.names;
|
||||||
|
|
||||||
Button.propTypes = {
|
Button.propTypes = {
|
||||||
/**
|
/**
|
||||||
* The visible part of the button, telling the user what
|
* The visible part of the button, telling the user what
|
||||||
|
@ -77,6 +91,15 @@ Button.propTypes = {
|
||||||
If the button can be activated or not
|
If the button can be activated or not
|
||||||
*/
|
*/
|
||||||
disabled: PropTypes.bool,
|
disabled: PropTypes.bool,
|
||||||
|
/**
|
||||||
|
* Name of icon to place before child content
|
||||||
|
*/
|
||||||
|
iconAfterName: ValidIconNameType,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Name of icon to place after child content
|
||||||
|
*/
|
||||||
|
iconBeforeName: ValidIconNameType,
|
||||||
/**
|
/**
|
||||||
* Specifying an href will use an <a> to link to the URL
|
* Specifying an href will use an <a> to link to the URL
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -35,12 +35,10 @@ export const ReactRouterLink = () => (
|
||||||
<Button to="./somewhere" label="submit">Actually a Link</Button>
|
<Button to="./somewhere" label="submit">Actually a Link</Button>
|
||||||
);
|
);
|
||||||
|
|
||||||
export const InternalElementMargin = () => (
|
export const ButtonWithIconBefore = () => (
|
||||||
<Button>
|
<Button iconBeforeName={Button.iconNames.github}>Create</Button>
|
||||||
<span>Internal</span>
|
);
|
||||||
<span>elements</span>
|
|
||||||
<span>have</span>
|
export const ButtonWithIconAfter = () => (
|
||||||
<span>right</span>
|
<Button iconAfterName={Button.iconNames.github}>Create</Button>
|
||||||
<span>margins</span>
|
|
||||||
</Button>
|
|
||||||
);
|
);
|
||||||
|
|
|
@ -22,6 +22,8 @@ const icons = {
|
||||||
*/
|
*/
|
||||||
const names = lodash.mapValues(icons, (value, key) => key);
|
const names = lodash.mapValues(icons, (value, key) => key);
|
||||||
|
|
||||||
|
export const ValidIconNameType = PropTypes.oneOf(Object.keys(names));
|
||||||
|
|
||||||
|
|
||||||
function Icon({ name, ...props }) {
|
function Icon({ name, ...props }) {
|
||||||
return (
|
return (
|
||||||
|
@ -29,10 +31,11 @@ function Icon({ name, ...props }) {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
Icon.names = names;
|
Icon.names = names;
|
||||||
|
|
||||||
Icon.propTypes = {
|
Icon.propTypes = {
|
||||||
name: PropTypes.oneOf(Object.keys(names)).isRequired
|
name: ValidIconNameType.isRequired
|
||||||
};
|
};
|
||||||
|
|
||||||
export default Icon;
|
export default Icon;
|
||||||
|
|
|
@ -1,47 +1,9 @@
|
||||||
<?xml version="1.0" encoding="iso-8859-1"?>
|
<?xml version="1.0" encoding="iso-8859-1"?>
|
||||||
<!-- Generator: Adobe Illustrator 19.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
<svg width="32px" height="32px" viewBox="0 0 32 32" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
<g>
|
||||||
viewBox="0 0 512 512" style="enable-background:new 0 0 512 512;" xml:space="preserve">
|
<path d="M7.091875,19.338 L5.978,23.49625 L1.9068125,23.582375 C0.690125,21.3256875 0,18.74375 0,16 C0,13.3468125 0.64525,10.8448125 1.789,8.64175 L1.789875,8.64175 L5.414375,9.30625 L7.002125,12.909 C6.6698125,13.8778125 6.4886875,14.9178125 6.4886875,16 C6.4888125,17.1745 6.7015625,18.2998125 7.091875,19.338 Z" fill="#FBBB00"></path>
|
||||||
<path style="fill:#FBBB00;" d="M113.47,309.408L95.648,375.94l-65.139,1.378C11.042,341.211,0,299.9,0,256
|
<path d="M31.7204375,13.011 C31.9041875,13.978875 32,14.9784375 32,16 C32,17.1455 31.8795625,18.262875 31.650125,19.3406875 C30.87125,23.008375 28.8360625,26.211 26.01675,28.477375 L26.015875,28.4765 L21.450625,28.2435625 L20.8045,24.210125 C22.67525,23.113 24.13725,21.3960625 24.907375,19.3406875 L16.35175,19.3406875 L16.35175,13.011 L25.0321875,13.011 L31.7204375,13.011 Z" fill="#518EF8"></path>
|
||||||
c0-42.451,10.324-82.483,28.624-117.732h0.014l57.992,10.632l25.404,57.644c-5.317,15.501-8.215,32.141-8.215,49.456
|
<path d="M26.0158125,28.4765 L26.0166875,28.477375 C23.27475,30.6813125 19.791625,32 16,32 C9.9068125,32 4.60925,28.5943125 1.9068125,23.5824375 L7.091875,19.3380625 C8.4430625,22.9441875 11.92175,25.51125 16,25.51125 C17.7529375,25.51125 19.3951875,25.037375 20.804375,24.210125 L26.0158125,28.4765 Z" fill="#28B446"></path>
|
||||||
C103.821,274.792,107.225,292.797,113.47,309.408z"/>
|
<path d="M26.21275,3.6835 L21.0294375,7.927 C19.571,7.015375 17.847,6.48875 16,6.48875 C11.8294375,6.48875 8.2856875,9.1735625 7.0021875,12.909 L1.789875,8.64175 L1.789,8.64175 C4.451875,3.5076875 9.81625,0 16,0 C19.8821875,0 23.44175,1.382875 26.21275,3.6835 Z" fill="#F14336"></path>
|
||||||
<path style="fill:#518EF8;" d="M507.527,208.176C510.467,223.662,512,239.655,512,256c0,18.328-1.927,36.206-5.598,53.451
|
</g>
|
||||||
c-12.462,58.683-45.025,109.925-90.134,146.187l-0.014-0.014l-73.044-3.727l-10.338-64.535
|
|
||||||
c29.932-17.554,53.324-45.025,65.646-77.911h-136.89V208.176h138.887L507.527,208.176L507.527,208.176z"/>
|
|
||||||
<path style="fill:#28B446;" d="M416.253,455.624l0.014,0.014C372.396,490.901,316.666,512,256,512
|
|
||||||
c-97.491,0-182.252-54.491-225.491-134.681l82.961-67.91c21.619,57.698,77.278,98.771,142.53,98.771
|
|
||||||
c28.047,0,54.323-7.582,76.87-20.818L416.253,455.624z"/>
|
|
||||||
<path style="fill:#F14336;" d="M419.404,58.936l-82.933,67.896c-23.335-14.586-50.919-23.012-80.471-23.012
|
|
||||||
c-66.729,0-123.429,42.957-143.965,102.724l-83.397-68.276h-0.014C71.23,56.123,157.06,0,256,0
|
|
||||||
C318.115,0,375.068,22.126,419.404,58.936z"/>
|
|
||||||
<g>
|
|
||||||
</g>
|
|
||||||
<g>
|
|
||||||
</g>
|
|
||||||
<g>
|
|
||||||
</g>
|
|
||||||
<g>
|
|
||||||
</g>
|
|
||||||
<g>
|
|
||||||
</g>
|
|
||||||
<g>
|
|
||||||
</g>
|
|
||||||
<g>
|
|
||||||
</g>
|
|
||||||
<g>
|
|
||||||
</g>
|
|
||||||
<g>
|
|
||||||
</g>
|
|
||||||
<g>
|
|
||||||
</g>
|
|
||||||
<g>
|
|
||||||
</g>
|
|
||||||
<g>
|
|
||||||
</g>
|
|
||||||
<g>
|
|
||||||
</g>
|
|
||||||
<g>
|
|
||||||
</g>
|
|
||||||
<g>
|
|
||||||
</g>
|
|
||||||
</svg>
|
</svg>
|
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 1.5 KiB |
|
@ -1,9 +1,7 @@
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import InlineSVG from 'react-inlinesvg';
|
|
||||||
|
|
||||||
import Button from '../../../common/Button';
|
import Button from '../../../common/Button';
|
||||||
import Icon from '../../../common/Icon';
|
|
||||||
import CopyableInput from '../../IDE/components/CopyableInput';
|
import CopyableInput from '../../IDE/components/CopyableInput';
|
||||||
|
|
||||||
import APIKeyList from './APIKeyList';
|
import APIKeyList from './APIKeyList';
|
||||||
|
@ -82,13 +80,12 @@ class APIKeyForm extends React.Component {
|
||||||
value={this.state.keyLabel}
|
value={this.state.keyLabel}
|
||||||
/>
|
/>
|
||||||
<Button
|
<Button
|
||||||
|
iconBeforeName={Button.iconNames.plus}
|
||||||
disabled={this.state.keyLabel === ''}
|
disabled={this.state.keyLabel === ''}
|
||||||
type="submit"
|
type="submit"
|
||||||
label="Create new key"
|
label="Create new key"
|
||||||
>
|
>
|
||||||
<Icon name={Icon.names.plus} />
|
Create
|
||||||
<span>Create</span>
|
|
||||||
</Button>
|
</Button>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
|
|
|
@ -9,7 +9,6 @@ import { bindActionCreators } from 'redux';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
|
|
||||||
import Button from '../../../common/Button';
|
import Button from '../../../common/Button';
|
||||||
import Icon from '../../../common/Icon';
|
|
||||||
import * as ProjectActions from '../../IDE/actions/project';
|
import * as ProjectActions from '../../IDE/actions/project';
|
||||||
import * as ProjectsActions from '../../IDE/actions/projects';
|
import * as ProjectsActions from '../../IDE/actions/projects';
|
||||||
import * as CollectionsActions from '../../IDE/actions/collections';
|
import * as CollectionsActions from '../../IDE/actions/collections';
|
||||||
|
@ -54,9 +53,9 @@ const ShareURL = ({ value }) => {
|
||||||
return (
|
return (
|
||||||
<div className="collection-share" ref={node}>
|
<div className="collection-share" ref={node}>
|
||||||
<Button
|
<Button
|
||||||
|
iconAfterName={Button.iconNames.sortArrowDown}
|
||||||
onClick={() => setShowURL(!showURL)}
|
onClick={() => setShowURL(!showURL)}
|
||||||
><span>Share</span>
|
>Share
|
||||||
<Icon name={Icon.names.sortArrowDown} />
|
|
||||||
</Button>
|
</Button>
|
||||||
{ showURL &&
|
{ showURL &&
|
||||||
<div className="collection__share-dropdown">
|
<div className="collection__share-dropdown">
|
||||||
|
|
|
@ -2,10 +2,9 @@ import PropTypes from 'prop-types';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import styled from 'styled-components';
|
import styled from 'styled-components';
|
||||||
|
|
||||||
import { remSize, prop } from '../../../theme';
|
import { remSize } from '../../../theme';
|
||||||
|
|
||||||
import Button from '../../../common/Button';
|
import Button from '../../../common/Button';
|
||||||
import Icon from '../../../common/Icon';
|
|
||||||
|
|
||||||
const authUrls = {
|
const authUrls = {
|
||||||
github: '/auth-github',
|
github: '/auth-github',
|
||||||
|
@ -24,24 +23,15 @@ const services = {
|
||||||
|
|
||||||
const StyledButton = styled(Button)`
|
const StyledButton = styled(Button)`
|
||||||
width: ${remSize(300)};
|
width: ${remSize(300)};
|
||||||
|
|
||||||
> * + * {
|
|
||||||
margin-left: ${remSize(10)};
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
|
|
||||||
const StyledIcon = styled(Icon)`
|
|
||||||
width: ${remSize(32)};
|
|
||||||
height: ${remSize(32)};
|
|
||||||
`;
|
`;
|
||||||
|
|
||||||
function SocialAuthButton({ service }) {
|
function SocialAuthButton({ service }) {
|
||||||
return (
|
return (
|
||||||
<StyledButton
|
<StyledButton
|
||||||
|
iconBeforeName={Button.iconNames[service]}
|
||||||
href={authUrls[service]}
|
href={authUrls[service]}
|
||||||
>
|
>
|
||||||
<StyledIcon name={Icon.names[service]} />
|
{labels[service]}
|
||||||
<span>{labels[service]}</span>
|
|
||||||
</StyledButton>
|
</StyledButton>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue