Skip to content

Commit

Permalink
feat: add size and icons for toggle component
Browse files Browse the repository at this point in the history
  • Loading branch information
jeromeraffin committed Jan 18, 2024
1 parent cff07b7 commit 9d9f311
Show file tree
Hide file tree
Showing 6 changed files with 179 additions and 17 deletions.
2 changes: 1 addition & 1 deletion docs/components/Header/styles.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ export const Header = styled.header(
padding: 0 md;
top: 0;
width: 100%;
z-index: 2;
z-index: 3;
@media (min-width: md) {
padding: 0 lg;
Expand Down
40 changes: 39 additions & 1 deletion docs/pages/components/toggle.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ function() {
return (
<Stack>
<Field label="Toggle">
<Toggle checked={toggle} onClick={() => setToggle(!toggle)} />
<Toggle size="sm" checked={toggle} onClick={() => setToggle(!toggle)} />
</Field>
<Field label="Toggle">
<Toggle checked={toggleChecked} onClick={() => setToggleChecked(!toggleChecked)} />
Expand Down Expand Up @@ -79,6 +79,44 @@ function() {
}
```

## Icons

Pass a `checkedIcon` or `uncheckedIcon` through to decorate your `Toggle` and give more context to the toggle action. We recommend to use an icon with `sm` or `md` size.

```jsx
function () {
const [toggle, setToggle] = React.useState(false)

return (
<Stack>
<Toggle size="md" uncheckedIcon={<ShowIcon />} checkedIcon={<HideIcon />} aria-label="Toggle component" checked={toggle} onClick={() => setToggle(!toggle)} />
</Stack>
)
}
```

## Sizes

Use size property with option:

- `xs` (24px - default)
- `sm` (32px)
- `md` (40px)

```jsx
function () {
const [toggle, setToggle] = React.useState(false)

return (
<Stack>
<Toggle aria-label="Toggle component" checked={toggle} onClick={() => setToggle(!toggle)} />
<Toggle size="sm" aria-label="Toggle component" checked={toggle} onClick={() => setToggle(!toggle)} />
<Toggle size="md" aria-label="Toggle component" checked={toggle} onClick={() => setToggle(!toggle)} />
</Stack>
)
}
```

## Variants

Use `hint`, `warning`, `error`, `info` or `success` properties on Field component to add a variant status on your checkbox. The label, hint or border color are set by magic 🪄
Expand Down
2 changes: 2 additions & 0 deletions packages/Checkbox/src/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ export const Checkbox = forwardRef<'input', CheckboxProps>(
disabled,
name,
onChange,
size,
...rest
},
ref
Expand All @@ -44,6 +45,7 @@ export const Checkbox = forwardRef<'input', CheckboxProps>(
name={name}
onChange={handleChange}
ref={ref}
size={size}
{...rest}
/>
)
Expand Down
80 changes: 72 additions & 8 deletions packages/Core/src/theme/toggles.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,15 @@ import { CSSObject } from '@xstyled/styled-components'
import { WuiTheme } from './types'

// To allow for line-height of text in label
const LINE_HEIGHT_ADJUSTMENTS = '0.15rem'
const XS_LINE_HEIGHT_ADJUSTMENTS = '0.40rem'
const SM_LINE_HEIGHT_ADJUSTMENTS = '0.10rem'

type State = 'default' | 'checked' | 'disabled'
type State = 'default' | 'checked' | 'disabled' | 'sizes'

export type ThemeToggles = {
item: Record<State, CSSObject>
after: Record<State, CSSObject>
icon: Record<'sizes' | 'position', CSSObject>
}

export const getToggles = (theme: WuiTheme): ThemeToggles => {
Expand All @@ -18,20 +20,34 @@ export const getToggles = (theme: WuiTheme): ThemeToggles => {
return {
item: {
default: {
width: toRem(24),
height: toRem(16),
borderRadius: toRem(16),
backgroundColor: colors['light-900'],
borderColor: colors.border,
borderWidth: borderWidths.sm,
borderStyle: 'solid',
marginTop: LINE_HEIGHT_ADJUSTMENTS,
borderRadius: toRem(16),

'&:focus': {
borderColor: colors['primary-200'],
...focus(colors['primary-200']),
},
},
sizes: {
xs: {
width: toRem(28),
height: toRem(16),
marginTop: XS_LINE_HEIGHT_ADJUSTMENTS,
},
sm: {
width: toRem(36),
height: toRem(20),
marginTop: SM_LINE_HEIGHT_ADJUSTMENTS,
},
md: {
width: toRem(44),
height: toRem(24),
marginTop: '0 !important',
},
},
checked: {
backgroundColor: colors['primary-500'],
borderColor: colors['primary-500'],
Expand All @@ -43,8 +59,6 @@ export const getToggles = (theme: WuiTheme): ThemeToggles => {
},
after: {
default: {
width: toRem(12),
height: toRem(12),
backgroundColor: colors['light-900'],
borderColor: colors['dark-400'],
borderWidth: borderWidths.sm,
Expand All @@ -59,6 +73,56 @@ export const getToggles = (theme: WuiTheme): ThemeToggles => {
borderColor: 'transparent',
backgroundColor: colors['nude-600'],
},
sizes: {
xs: {
width: toRem(12),
height: toRem(12),
},
sm: {
width: toRem(16),
height: toRem(16),
},
md: {
width: toRem(20),
height: toRem(20),
},
},
},
icon: {
position: {
xs: {
top: '5px',
left: '2px',
right: '2px',
},
sm: {
top: '1px',
left: '4px',
right: '4px',
},
md: {
top: '4px',
left: '4px',
right: '4px',
},
},
sizes: {
xs: {
width: toRem(10),
height: toRem(10),
fontSize: toRem(10),
},
sm: {
width: toRem(12),
height: toRem(12),
fontSize: toRem(12),
},
md: {
width: toRem(16),
height: toRem(16),
fontSize: toRem(16),
},
},
},
}
}
29 changes: 25 additions & 4 deletions packages/Toggle/src/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,32 @@ import { CreateWuiProps, forwardRef } from '@welcome-ui/system'

import * as S from './styles'

export type ToggleOptions = Omit<CheckboxProps, 'Component'>
type Size = 'xs' | 'sm' | 'md'

export type ToggleOptions = Omit<
CheckboxProps,
'Component' | 'iconPlacement' | 'indeterminate' | 'hasIcon' | 'transparent' | 'isClearable'
> & {
checkedIcon?: JSX.Element
uncheckedIcon?: JSX.Element
size?: Size
}
export type ToggleProps = CreateWuiProps<'input', ToggleOptions>

export const Toggle = forwardRef<'input', ToggleProps>((props, ref) => (
<Checkbox {...props} Component={S.Toggle} ref={ref} />
))
export const Toggle = forwardRef<'input', ToggleProps>(
({ checked, checkedIcon, onClick, size = 'xs', uncheckedIcon, ...rest }, ref) => {
const hasIcon = checkedIcon && uncheckedIcon
return (
<S.Wrapper onClick={onClick}>
{hasIcon && (
<S.IconWrapper checked={checked} size={size}>
{checked ? checkedIcon : uncheckedIcon}
</S.IconWrapper>
)}
<Checkbox {...rest} Component={S.Toggle} checked={checked} ref={ref} size={size} />
</S.Wrapper>
)
}
)

Toggle.displayName = 'Toggle'
43 changes: 40 additions & 3 deletions packages/Toggle/src/styles.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,27 +4,31 @@ import { shouldForwardProp } from '@welcome-ui/system'

import { ToggleOptions } from './index'

type Size = 'xs' | 'sm' | 'md'

export const Toggle = styled(Ariakit.Checkbox).withConfig({ shouldForwardProp })<ToggleOptions>(
({ order = '-1' }) => css`
({ order = '-1', size }) => css`
${th('toggles.item.default')};
${th(`toggles.item.sizes.${size}`)};
position: relative;
display: block;
appearance: none;
background: transparent;
outline: none !important; /* important for firefox */
cursor: pointer;
transition: medium;
order: ${order};
&::after {
${th('toggles.after.default')};
${th(`toggles.after.sizes.${size}`)};
content: '';
top: 0;
bottom: 0;
left: 2;
position: absolute;
margin: auto;
transition: medium;
z-index: 2;
}
&:disabled {
Expand All @@ -39,7 +43,7 @@ export const Toggle = styled(Ariakit.Checkbox).withConfig({ shouldForwardProp })
&:checked {
&::after {
/* border + left padding + right padding */
transform: translateX(calc(${th('toggles.item.default.width')} - 100% - 0.3rem));
transform: translateX(calc(${th(`toggles.item.sizes.${size}.width`)} - 100% - 0.3rem));
}
&:not(:disabled) {
Expand All @@ -54,3 +58,36 @@ export const Toggle = styled(Ariakit.Checkbox).withConfig({ shouldForwardProp })
${system};
`
)

export const Wrapper = styled.box<{ onClick: React.MouseEventHandler<HTMLInputElement> }>`
position: relative;
display: inline-block;
cursor: pointer;
`

export const IconWrapper = styled.box.withConfig({ shouldForwardProp })<{
checked: boolean
size: Size
}>(
({ checked, size }) => css`
position: absolute;
z-index: 1;
top: ${th(`toggles.icon.position.${size}.top`)};
> svg,
i {
${th(`toggles.icon.sizes.${size}`)}
}
${checked &&
css`
left: ${th(`toggles.icon.position.${size}.left`)};
color: dark-900;
`}
${!checked &&
css`
right: ${th(`toggles.icon.position.${size}.right`)};
`}
`
)

0 comments on commit 9d9f311

Please sign in to comment.