feat: Optional full-width toggle for document display (#2869)
* Migration, model, presenter * Working implementation * fix: Account for table of contents * Checkbox -> Toggle * Checkbox -> Toggle
This commit is contained in:
@@ -1,62 +0,0 @@
|
||||
import * as React from "react";
|
||||
import { VisuallyHidden } from "reakit/VisuallyHidden";
|
||||
import styled from "styled-components";
|
||||
import HelpText from "~/components/HelpText";
|
||||
|
||||
export type Props = {
|
||||
checked?: boolean;
|
||||
label?: React.ReactNode;
|
||||
labelHidden?: boolean;
|
||||
className?: string;
|
||||
name?: string;
|
||||
disabled?: boolean;
|
||||
onChange: (event: React.ChangeEvent<HTMLInputElement>) => unknown;
|
||||
note?: React.ReactNode;
|
||||
small?: boolean;
|
||||
};
|
||||
|
||||
const LabelText = styled.span<{ small?: boolean }>`
|
||||
font-weight: 500;
|
||||
margin-left: ${(props) => (props.small ? "6px" : "10px")};
|
||||
${(props) => (props.small ? `color: ${props.theme.textSecondary}` : "")};
|
||||
`;
|
||||
|
||||
const Wrapper = styled.div<{ small?: boolean }>`
|
||||
padding-bottom: 8px;
|
||||
${(props) => (props.small ? "font-size: 14px" : "")};
|
||||
width: 100%;
|
||||
`;
|
||||
|
||||
const Label = styled.label`
|
||||
display: flex;
|
||||
align-items: center;
|
||||
user-select: none;
|
||||
`;
|
||||
|
||||
export default function Checkbox({
|
||||
label,
|
||||
labelHidden,
|
||||
note,
|
||||
className,
|
||||
small,
|
||||
...rest
|
||||
}: Props) {
|
||||
const wrappedLabel = <LabelText small={small}>{label}</LabelText>;
|
||||
|
||||
return (
|
||||
<>
|
||||
<Wrapper small={small} className={className}>
|
||||
<Label>
|
||||
<input type="checkbox" {...rest} />
|
||||
{label &&
|
||||
(labelHidden ? (
|
||||
<VisuallyHidden>{wrappedLabel}</VisuallyHidden>
|
||||
) : (
|
||||
wrappedLabel
|
||||
))}
|
||||
</Label>
|
||||
{note && <HelpText small>{note}</HelpText>}
|
||||
</Wrapper>
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -7,7 +7,7 @@ import Arrow from "~/components/Arrow";
|
||||
type Props = {
|
||||
direction: "left" | "right";
|
||||
style?: React.CSSProperties;
|
||||
onClick?: () => any;
|
||||
onClick?: React.MouseEventHandler<HTMLButtonElement>;
|
||||
};
|
||||
|
||||
const Toggle = React.forwardRef<HTMLButtonElement, Props>(
|
||||
|
||||
104
app/components/Toggle.tsx
Normal file
104
app/components/Toggle.tsx
Normal file
@@ -0,0 +1,104 @@
|
||||
import * as React from "react";
|
||||
import { VisuallyHidden } from "reakit/VisuallyHidden";
|
||||
import styled from "styled-components";
|
||||
import HelpText from "~/components/HelpText";
|
||||
|
||||
export type Props = {
|
||||
checked?: boolean;
|
||||
label?: React.ReactNode;
|
||||
labelHidden?: boolean;
|
||||
className?: string;
|
||||
name?: string;
|
||||
disabled?: boolean;
|
||||
onChange: (event: React.ChangeEvent<HTMLInputElement>) => unknown;
|
||||
note?: React.ReactNode;
|
||||
};
|
||||
|
||||
const LabelText = styled.span`
|
||||
font-weight: 500;
|
||||
margin-left: 10px;
|
||||
`;
|
||||
|
||||
const Wrapper = styled.div`
|
||||
padding-bottom: 8px;
|
||||
width: 100%;
|
||||
`;
|
||||
|
||||
const Label = styled.label`
|
||||
display: flex;
|
||||
align-items: center;
|
||||
user-select: none;
|
||||
`;
|
||||
|
||||
const SlideToggle = styled.label`
|
||||
cursor: pointer;
|
||||
text-indent: -9999px;
|
||||
width: 26px;
|
||||
height: 14px;
|
||||
background: ${(props) => props.theme.slate};
|
||||
display: block;
|
||||
border-radius: 10px;
|
||||
position: relative;
|
||||
|
||||
&:after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
top: 2px;
|
||||
left: 2px;
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
background: ${(props) => props.theme.white};
|
||||
border-radius: 5px;
|
||||
transition: width 100ms ease-in-out;
|
||||
}
|
||||
|
||||
&:active:after {
|
||||
width: 12px;
|
||||
}
|
||||
`;
|
||||
|
||||
const HiddenInput = styled.input`
|
||||
height: 0;
|
||||
width: 0;
|
||||
visibility: hidden;
|
||||
|
||||
&:checked + ${SlideToggle} {
|
||||
background: ${(props) => props.theme.primary};
|
||||
}
|
||||
|
||||
&:checked + ${SlideToggle}:after {
|
||||
left: calc(100% - 2px);
|
||||
transform: translateX(-100%);
|
||||
}
|
||||
`;
|
||||
|
||||
let inputId = 0;
|
||||
|
||||
export default function Toggle({
|
||||
label,
|
||||
labelHidden,
|
||||
note,
|
||||
className,
|
||||
...rest
|
||||
}: Props) {
|
||||
const wrappedLabel = <LabelText>{label}</LabelText>;
|
||||
const [id] = React.useState(`checkbox-input-${inputId++}`);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Wrapper className={className}>
|
||||
<Label>
|
||||
<HiddenInput type="checkbox" id={id} {...rest} />
|
||||
<SlideToggle htmlFor={id} />
|
||||
{label &&
|
||||
(labelHidden ? (
|
||||
<VisuallyHidden>{wrappedLabel}</VisuallyHidden>
|
||||
) : (
|
||||
wrappedLabel
|
||||
))}
|
||||
</Label>
|
||||
{note && <HelpText small>{note}</HelpText>}
|
||||
</Wrapper>
|
||||
</>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user