Skip to content

Commit fbc03cb

Browse files
committed
add menu variant of Checkbox
1 parent 8ac8863 commit fbc03cb

File tree

2 files changed

+121
-11
lines changed

2 files changed

+121
-11
lines changed

src/components/Checkbox/Checkbox.js

Lines changed: 76 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import { createDisabledTextStyles, createHatchedBackground } from '../common';
77
import { padding, fontSizes } from '../common/system';
88
import useControlledOrUncontrolled from '../common/hooks/useControlledOrUncontrolled';
99
import Cutout from '../Cutout/Cutout';
10+
import { StyledListItem } from '../ListItem/ListItem';
1011

1112
const checkboxSize = 20;
1213

@@ -22,6 +23,11 @@ const StyledLabel = styled.label`
2223
user-select: none;
2324
font-size: ${fontSizes.md};
2425
${props => props.isDisabled && createDisabledTextStyles()}
26+
27+
${StyledListItem} & {
28+
margin: 0;
29+
height: 100%;
30+
}
2531
`;
2632

2733
const StyledInput = styled.input`
@@ -63,6 +69,21 @@ const StyledFlatCheckbox = styled.div`
6369
background: ${({ theme, isDisabled }) =>
6470
isDisabled ? theme.flatLight : theme.canvas};
6571
`;
72+
73+
const StyledMenuCheckbox = styled.div`
74+
position: relative;
75+
box-sizing: border-box;
76+
display: inline-block;
77+
background: ${({ theme, isDisabled }) =>
78+
isDisabled ? theme.flatLight : theme.canvas};
79+
${sharedCheckboxStyles}
80+
width: ${checkboxSize - 4}px;
81+
height: ${checkboxSize - 4}px;
82+
background: none;
83+
border: none;
84+
outline: none;
85+
`;
86+
6687
const CheckmarkIcon = styled.span.attrs(() => ({
6788
'data-testid': 'checkmarkIcon'
6889
}))`
@@ -84,15 +105,46 @@ const CheckmarkIcon = styled.span.attrs(() => ({
84105
isDisabled ? theme.checkmarkDisabled : theme.checkmark};
85106
border-width: 0 3px 3px 0;
86107
transform: translate(-50%, -50%) rotate(45deg);
108+
109+
${({ variant, theme, isDisabled }) =>
110+
variant === 'menu'
111+
? css`
112+
border-color: ${isDisabled ? theme.textDisabled : theme.text};
113+
filter: drop-shadow(
114+
1px 1px 0px
115+
${isDisabled ? theme.textDisabledShadow : 'transparent'}
116+
);
117+
`
118+
: css`
119+
border-color: ${isDisabled
120+
? theme.checkmarkDisabled
121+
: theme.checkmark};
122+
`}
123+
${StyledListItem}:hover & {
124+
${({ theme, isDisabled, variant }) =>
125+
!isDisabled &&
126+
variant === 'menu' &&
127+
css`
128+
border-color: ${theme.textInvert};
129+
`};
87130
}
88131
`;
89132
const IndeterminateIcon = styled.span.attrs(() => ({
90133
'data-testid': 'indeterminateIcon'
91134
}))`
92135
display: inline-block;
93136
position: relative;
94-
width: 100%;
95-
height: 100%;
137+
138+
${({ variant }) =>
139+
variant === 'menu'
140+
? css`
141+
height: calc(100% - 4px);
142+
width: calc(100% - 4px);
143+
`
144+
: css`
145+
width: 100%;
146+
height: 100%;
147+
`}
96148
&:after {
97149
content: '';
98150
display: block;
@@ -104,10 +156,20 @@ const IndeterminateIcon = styled.span.attrs(() => ({
104156
createHatchedBackground({
105157
mainColor: isDisabled ? theme.checkmarkDisabled : theme.checkmark
106158
})}
107-
background-position: -1px -1px, 1px 1px;
108-
outline: 1px solid
109-
${({ theme, isDisabled }) => (isDisabled ? theme.material : theme.canvas)};
110-
outline-offset: -1px;
159+
background-position: 0px 0px, 2px 2px;
160+
161+
${({ variant, isDisabled, theme }) =>
162+
variant === 'menu' &&
163+
css`
164+
${StyledListItem}:hover & {
165+
${createHatchedBackground({
166+
mainColor: theme.textInvert
167+
})}
168+
}
169+
filter: drop-shadow(
170+
1px 1px 0px ${isDisabled ? theme.textDisabledShadow : 'transparent'}
171+
);
172+
`};
111173
}
112174
`;
113175
const LabelText = styled.span`
@@ -139,8 +201,12 @@ const Checkbox = React.forwardRef(function Checkbox(props, ref) {
139201
setState(newState);
140202
if (onChange) onChange(e);
141203
};
142-
const CheckboxComponent =
143-
variant === 'flat' ? StyledFlatCheckbox : StyledCheckbox;
204+
205+
const CheckboxComponent = {
206+
flat: StyledFlatCheckbox,
207+
default: StyledCheckbox,
208+
menu: StyledMenuCheckbox
209+
}[variant];
144210

145211
let Icon = null;
146212
if (indeterminate) {
@@ -156,7 +222,7 @@ const Checkbox = React.forwardRef(function Checkbox(props, ref) {
156222
isDisabled={disabled}
157223
role='presentation'
158224
>
159-
{Icon && <Icon isDisabled={disabled} />}
225+
{Icon && <Icon isDisabled={disabled} variant={variant} />}
160226
</CheckboxComponent>
161227
{label && <LabelText>{label}</LabelText>}
162228
<StyledInput
@@ -200,7 +266,7 @@ Checkbox.propTypes = {
200266
label: propTypes.oneOfType([propTypes.string, propTypes.number]),
201267
checked: propTypes.bool,
202268
disabled: propTypes.bool,
203-
variant: propTypes.oneOf(['default', 'flat']),
269+
variant: propTypes.oneOf(['default', 'flat', 'menu']),
204270
style: propTypes.shape([propTypes.string, propTypes.number]),
205271
defaultChecked: propTypes.bool,
206272
indeterminate: propTypes.bool,

src/components/Checkbox/Checkbox.stories.js

Lines changed: 45 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,15 @@ import React from 'react';
22
import { storiesOf } from '@storybook/react';
33
import styled from 'styled-components';
44

5-
import { Checkbox, Fieldset, Button, Cutout } from '..';
5+
import {
6+
Checkbox,
7+
Fieldset,
8+
Button,
9+
Cutout,
10+
List,
11+
ListItem,
12+
Divider
13+
} from '..';
614

715
const StyledCutout = styled(Cutout)`
816
background: ${({ theme }) => theme.canvas};
@@ -64,6 +72,42 @@ storiesOf('Checkbox', module)
6472
</div>
6573
</StyledCutout>
6674
))
75+
)
76+
.add('menu', () =>
77+
React.createElement(() => (
78+
<List>
79+
<ListItem size='md'>
80+
<Checkbox
81+
name='useGradient'
82+
variant='menu'
83+
value='useGradient'
84+
label='Use gradient'
85+
defaultChecked
86+
/>
87+
</ListItem>
88+
<ListItem size='md'>
89+
<Checkbox
90+
name='thickBrush'
91+
variant='menu'
92+
defaultChecked={false}
93+
value='thickBrush'
94+
label='Thick brush'
95+
indeterminate
96+
/>
97+
</ListItem>
98+
<Divider />
99+
<ListItem size='md' disabled>
100+
<Checkbox
101+
name='autoSave'
102+
variant='menu'
103+
value='autoSave'
104+
checked
105+
label='Auto-save'
106+
disabled
107+
/>
108+
</ListItem>
109+
</List>
110+
))
67111
);
68112

69113
class ControlledCheckboxGroupExample extends React.Component {

0 commit comments

Comments
 (0)