Files
protocol/packages/website/ts/components/ui/switch.tsx
2019-03-26 16:20:02 -04:00

189 lines
5.5 KiB
TypeScript

import * as React from 'react';
import styled from 'styled-components';
import { Heading } from 'ts/components/text';
import { colors } from 'ts/style/colors';
interface ToggleProps {
className?: string;
name?: string;
value?: string;
isChecked?: boolean;
isDisabled?: boolean;
onToggle?: (e: React.ChangeEvent) => void;
width?: string;
height?: string;
borderWidth?: string;
borderColor?: string;
backgroundColor?: string;
backgroundColorDisabled?: string;
radius?: string;
radiusBackground?: string;
knobRadius?: string;
knobWidth?: string;
knobHeight?: string;
knobGap?: string;
knobColor?: string;
}
const ToggleContainer = styled.label``;
const DEFAULT_TOGGLE_STYLES = {
WIDTH: '48px',
HEIGHT: '24px',
KNOB_WIDTH: '12px',
KNOB_HEIGHT: '12px',
};
export const ToggleBase = styled.span<ToggleProps>`
position: relative;
box-sizing: border-box;
display: inline-grid;
align-items: center;
width: ${p => p.width || DEFAULT_TOGGLE_STYLES.WIDTH};
height: ${p => p.height || DEFAULT_TOGGLE_STYLES.HEIGHT};
vertical-align: text-top;
margin: 0 4px;
input[type="checkbox"] {
position: absolute;
margin-left: -9999px;
visibility: hidden;
// off state
& + label {
display: inline-grid;
box-sizing: border-box;
align-items: center;
outline: none;
user-select: none;
width: ${p => p.width || DEFAULT_TOGGLE_STYLES.WIDTH};
height: ${p => p.height || DEFAULT_TOGGLE_STYLES.HEIGHT};
background-color: ${p =>
p.knobColor || '#EAEAEA'};
border-radius: ${p => p.radius || '256px'};
cursor: pointer;
transition: background ease-out 0.3s;
&:before {
content: "";
display: block;
position: absolute;
border-radius: ${p =>
p.radiusBackground || '256px'};
width: calc(
${p => p.width || DEFAULT_TOGGLE_STYLES.WIDTH} - 2 *
${p => p.borderWidth || '2px'}
);
height: calc(
${p => p.height || DEFAULT_TOGGLE_STYLES.HEIGHT} - 2 *
${p => p.borderWidth || '2px'}
);
background-color: ${p => p.backgroundColor || colors.white};
left: ${p => p.borderWidth || '2px'};
}
&:after {
display: block;
position: absolute;
content: "";
width: ${p => p.knobWidth || DEFAULT_TOGGLE_STYLES.KNOB_WIDTH};
height: ${p => p.knobHeight || DEFAULT_TOGGLE_STYLES.KNOB_HEIGHT};
border-radius: ${p => p.knobRadius || '100%'};
background-color: ${p =>
p.knobColor || '#EAEAEA'};
transition: all ease-out 0.4s;
margin-left: ${p => p.knobGap || '4px'};
}
}
// on state
&:checked {
& + label {
background-color: ${p => p.borderColor || colors.brandLight};
&:before {
background-color: ${p => p.backgroundColor || colors.white};
}
&:after {
margin-left: calc(
100% - ${p => p.knobWidth || DEFAULT_TOGGLE_STYLES.KNOB_WIDTH} -
${p => p.knobGap || '4px'}
);
transition: all ease-out 0.2s;
background-color: ${p =>
p.knobColor || colors.brandLight};
}
}
&:disabled {
& + label {
background-color: ${p => p.backgroundColorDisabled || '#eee'};
&:after {
box-shadow: none;
}
}
}
}
// disabled
&:disabled {
& + label {
background-color: ${p => p.backgroundColorDisabled || '#eee'};
cursor: default;
&:after {
box-shadow: none;
background-color: ${p => p.backgroundColorDisabled || '#eee'};
}
}
}
}
`;
export const Toggle = React.forwardRef((props: ToggleProps, ref: React.Ref<HTMLInputElement>) => {
const {
className,
name,
isChecked = false,
isDisabled = false,
value = '',
onToggle = () => true,
} = props;
return (
<ToggleBase className={className} {...props}>
<input
ref={ref}
onChange={onToggle}
type={'checkbox'}
defaultChecked={isChecked}
id={name}
name={name}
value={value}
disabled={isDisabled}
/>
<ToggleContainer htmlFor={name} />
</ToggleBase>
);
});
const SwitchWrapper = styled.div`
display: flex;
justify-content: space-between;
padding: 0.75rem 0;
`;
export interface SwitchProps {
label: string;
isChecked?: boolean;
onToggle: (checked: boolean) => void;
}
export class Switch extends React.Component<SwitchProps> {
public switchRef: React.RefObject<HTMLInputElement> = React.createRef();
constructor(props: SwitchProps) {
super(props);
}
public render(): React.ReactNode {
const onToggle = () => { this.props.onToggle(this.switchRef.current.checked); };
return <SwitchWrapper>
<Heading isNoMargin={true} asElement="h3" size={'small'}>{this.props.label}</Heading>
<Toggle ref={this.switchRef} isChecked={this.props.isChecked} onToggle={onToggle} knobGap={'6px'} name={this.props.label}/>
</SwitchWrapper>;
}
}