Remove duplicative Toggle component (#3028)

fix: Tidy some styling and spacing issues in settings
This commit is contained in:
Tom Moor
2022-01-28 20:23:02 -08:00
committed by GitHub
parent 1cd770e38d
commit 76d83b5e82
19 changed files with 112 additions and 197 deletions

View File

@@ -31,7 +31,7 @@ const RealButton = styled.button<{
!props.borderOnHover &&
`
svg {
fill: ${props.iconColor || props.theme.buttonText};
fill: ${props.iconColor || "currentColor"};
}
`}
@@ -69,7 +69,7 @@ const RealButton = styled.button<{
props.borderOnHover
? ""
: `svg {
fill: ${props.iconColor || props.theme.buttonNeutralText};
fill: ${props.iconColor || "currentColor"};
}`
}
@@ -89,7 +89,7 @@ const RealButton = styled.button<{
color: ${props.theme.textTertiary};
svg {
fill: ${props.theme.textTertiary};
fill: currentColor;
}
}
`}
@@ -162,7 +162,7 @@ const Button = <T extends React.ElementType = "button">(
<Inner hasIcon={hasIcon} hasText={hasText} disclosure={disclosure}>
{hasIcon && icon}
{hasText && <Label hasIcon={hasIcon}>{children || value}</Label>}
{disclosure && <ExpandedIcon />}
{disclosure && <ExpandedIcon color="currentColor" />}
</Inner>
</RealButton>
);

View File

@@ -61,7 +61,7 @@ class GroupListItem extends React.Component<Props> {
</>
}
actions={
<Flex align="center">
<Flex align="center" gap={8}>
{showFacepile && (
<Facepile
onClick={this.handleMembersModalOpen}
@@ -69,7 +69,6 @@ class GroupListItem extends React.Component<Props> {
overflow={overflow}
/>
)}
&nbsp;
{renderActions({
openMembersModal: this.handleMembersModalOpen,
})}

View File

@@ -3,7 +3,7 @@ import styled from "styled-components";
const HelpText = styled.p<{ small?: boolean }>`
margin-top: 0;
color: ${(props) => props.theme.textSecondary};
font-size: ${(props) => (props.small ? "13px" : "inherit")};
font-size: ${(props) => (props.small ? "14px" : "inherit")};
`;
export default HelpText;

View File

@@ -88,6 +88,7 @@ export const Outline = styled(Flex)<{
font-weight: normal;
align-items: center;
overflow: hidden;
background: ${(props) => props.theme.background};
`;
export const LabelText = styled.div`

View File

@@ -1,20 +1,35 @@
import * as React from "react";
import styled from "styled-components";
import HelpText from "~/components/HelpText";
import { LabelText } from "~/components/Input";
type Props = {
type Props = React.HTMLAttributes<HTMLInputElement> & {
width?: number;
height?: number;
label?: string;
name?: string;
note?: React.ReactNode;
checked?: boolean;
disabled?: boolean;
onChange: (event: React.ChangeEvent<HTMLInputElement>) => unknown;
id?: string;
};
function Switch({ width = 38, height = 20, label, disabled, ...props }: Props) {
function Switch({
width = 32,
height = 18,
label,
disabled,
className,
note,
...props
}: Props) {
const component = (
<Wrapper width={width} height={height}>
<Input
width={width}
height={height}
className={label ? undefined : className}
>
<HiddenInput
type="checkbox"
width={width}
@@ -23,34 +38,45 @@ function Switch({ width = 38, height = 20, label, disabled, ...props }: Props) {
{...props}
/>
<Slider width={width} height={height} />
</Wrapper>
</Input>
);
if (label) {
return (
<Label disabled={disabled} htmlFor={props.id}>
{component}
<LabelText>{label}</LabelText>
</Label>
<Wrapper>
<Label disabled={disabled} htmlFor={props.id} className={className}>
{component}
<InlineLabelText>{label}</InlineLabelText>
</Label>
{note && <HelpText small>{note}</HelpText>}
</Wrapper>
);
}
return component;
}
const Wrapper = styled.div`
padding-bottom: 8px;
`;
const InlineLabelText = styled(LabelText)`
padding-bottom: 0;
`;
const Label = styled.label<{ disabled?: boolean }>`
display: flex;
align-items: center;
${(props) => (props.disabled ? `opacity: 0.75;` : "")}
`;
const Wrapper = styled.label<{ width: number; height: number }>`
const Input = styled.label<{ width: number; height: number }>`
position: relative;
display: inline-block;
width: ${(props) => props.width}px;
height: ${(props) => props.height}px;
margin-bottom: 4px;
margin-right: 8px;
flex-shrink: 0;
`;
const Slider = styled.span<{ width: number; height: number }>`

View File

@@ -1,104 +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;
};
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>
</>
);
}

View File

@@ -38,7 +38,7 @@ import Separator from "~/components/ContextMenu/Separator";
import Template from "~/components/ContextMenu/Template";
import Flex from "~/components/Flex";
import Modal from "~/components/Modal";
import Toggle from "~/components/Toggle";
import Switch from "~/components/Switch";
import { actionToMenuItem } from "~/actions";
import {
pinDocument,
@@ -453,10 +453,12 @@ function DocumentMenu({
<Separator />
<Style>
<ToggleMenuItem
width={26}
height={14}
label={t("Full width")}
checked={document.fullWidth}
onChange={(ev) => {
document.fullWidth = ev.target.checked;
document.fullWidth = ev.currentTarget.checked;
document.save();
}}
/>
@@ -526,8 +528,8 @@ function DocumentMenu({
);
}
const ToggleMenuItem = styled(Toggle)`
span {
const ToggleMenuItem = styled(Switch)`
* {
font-weight: normal;
color: ${(props) => props.theme.textSecondary};
}

View File

@@ -91,7 +91,7 @@ const CollectionEdit = ({ collectionId, onSubmit }: Props) => {
so often might confuse your team mates.
</Trans>
</HelpText>
<Flex>
<Flex gap={8}>
<Input
type="text"
label={t("Name")}
@@ -101,7 +101,6 @@ const CollectionEdit = ({ collectionId, onSubmit }: Props) => {
autoFocus
flex
/>
&nbsp;
<IconPicker onChange={handleChange} color={color} icon={icon} />
</Flex>
<InputSelect

View File

@@ -122,7 +122,7 @@ class CollectionNew extends React.Component<Props> {
for example.
</Trans>
</HelpText>
<Flex>
<Flex gap={8}>
<Input
type="text"
label={t("Name")}
@@ -132,7 +132,6 @@ class CollectionNew extends React.Component<Props> {
autoFocus
flex
/>
&nbsp;
<IconPicker
onOpen={this.handleIconPickerOpen}
onChange={this.handleChange}
@@ -143,29 +142,20 @@ class CollectionNew extends React.Component<Props> {
<InputSelectPermission
value={this.permission}
onChange={this.handlePermissionChange}
short
note={t(
"This is the default level of access, you can give individual users or groups more access once the collection is created."
)}
/>
<HelpText>
<Trans>
This is the default level of access given to team members, you can
give specific users or groups more access once the collection is
created.
</Trans>
</HelpText>
{teamSharingEnabled && (
<>
<Switch
id="sharing"
label={t("Public document sharing")}
onChange={this.handleSharingChange}
checked={this.sharing}
/>
<HelpText>
<Trans>
When enabled, documents can be shared publicly on the internet.
</Trans>
</HelpText>
</>
<Switch
id="sharing"
label={t("Public document sharing")}
onChange={this.handleSharingChange}
checked={this.sharing}
note={t(
"When enabled any documents within this collection can be shared publicly on the internet."
)}
/>
)}
<Button type="submit" disabled={this.isSaving || !this.name}>

View File

@@ -54,7 +54,6 @@ const CollectionGroupMemberListItem = ({
labelHidden
nude
/>
<Spacer />
<CollectionGroupMemberMenu
onMembers={openMembersModal}
onRemove={onRemove}
@@ -65,14 +64,12 @@ const CollectionGroupMemberListItem = ({
);
};
const Spacer = styled.div`
width: 8px;
`;
const Select = styled(InputSelect)`
margin: 0;
font-size: 14px;
border-color: transparent;
box-shadow: none;
color: ${(props) => props.theme.textSecondary};
select {
margin: 0;

View File

@@ -14,11 +14,11 @@ import MemberMenu from "~/menus/MemberMenu";
type Props = {
user: User;
membership?: Membership | null | undefined;
membership?: Membership | undefined;
canEdit: boolean;
onAdd?: () => any;
onRemove?: () => any;
onUpdate?: (permission: string) => any;
onAdd?: () => void;
onRemove?: () => void;
onUpdate?: (permission: string) => void;
};
const MemberListItem = ({
@@ -62,7 +62,7 @@ const MemberListItem = ({
}
image={<Avatar src={user.avatarUrl} size={32} />}
actions={
<Flex align="center">
<Flex align="center" gap={8}>
{onUpdate && (
<Select
label={t("Permissions")}
@@ -77,7 +77,6 @@ const MemberListItem = ({
)}
{canEdit && (
<>
<Spacer />
{onRemove && <MemberMenu onRemove={onRemove} />}
{onAdd && (
<Button onClick={onAdd} neutral>
@@ -92,14 +91,12 @@ const MemberListItem = ({
);
};
const Spacer = styled.div`
width: 8px;
`;
const Select = styled(InputSelect)`
margin: 0;
font-size: 14px;
border-color: transparent;
box-shadow: none;
color: ${(props) => props.theme.textSecondary};
select {
margin: 0;

View File

@@ -11,7 +11,7 @@ import Time from "~/components/Time";
type Props = {
user: User;
canEdit: boolean;
onAdd: () => any;
onAdd: () => void;
};
const UserListItem = ({ user, onAdd, canEdit }: Props) => {

View File

@@ -203,10 +203,9 @@ function CollectionPermissions({ collection }: Props) {
<InputSelectPermission
onChange={handleChangePermission}
value={collection.permission || ""}
short
nude
/>
<PermissionExplainer>
<PermissionExplainer small>
{!collection.permission && (
<Trans
defaults="The <em>{{ collectionName }}</em> collection is private. Team members have no access to it by default."
@@ -248,20 +247,21 @@ function CollectionPermissions({ collection }: Props) {
onChange={handleSharingChange}
checked={sharing && teamSharingEnabled}
disabled={!teamSharingEnabled}
note={
teamSharingEnabled ? (
<Trans>
When enabled, documents can be shared publicly on the internet.
</Trans>
) : (
<Trans>
Public sharing is currently disabled in the team security
settings.
</Trans>
)
}
/>
<HelpText>
{teamSharingEnabled ? (
<Trans>
When enabled, documents can be shared publicly on the internet.
</Trans>
) : (
<Trans>
Public sharing is currently disabled in the team security settings.
</Trans>
)}
</HelpText>
<Labeled label={t("Additional access")}>
<Actions>
<Actions gap={8}>
<Button
type="button"
onClick={handleAddGroupModalOpen}
@@ -269,7 +269,7 @@ function CollectionPermissions({ collection }: Props) {
neutral
>
{t("Add groups")}
</Button>{" "}
</Button>
<Button
type="button"
onClick={handleAddMemberModalOpen}
@@ -358,7 +358,7 @@ const PermissionExplainer = styled(HelpText)`
margin-bottom: 24px;
`;
const Actions = styled.div`
const Actions = styled(Flex)`
margin-bottom: 12px;
`;

View File

@@ -19,9 +19,11 @@ import useStores from "~/hooks/useStores";
import useToasts from "~/hooks/useToasts";
const MAX_INVITES = 20;
type Props = {
onSubmit: () => void;
};
type InviteRequest = {
email: string;
name: string;
@@ -55,6 +57,7 @@ function Invite({ onSubmit }: Props) {
const { t } = useTranslation();
const predictedDomain = user.email.split("@")[1];
const can = policies.abilities(team.id);
const handleSubmit = React.useCallback(
async (ev: React.SyntheticEvent) => {
ev.preventDefault();
@@ -76,6 +79,7 @@ function Invite({ onSubmit }: Props) {
},
[onSubmit, showToast, invites, t, users]
);
const handleChange = React.useCallback((ev, index) => {
setInvites((prevInvites) => {
const newInvites = [...prevInvites];
@@ -83,6 +87,7 @@ function Invite({ onSubmit }: Props) {
return newInvites;
});
}, []);
const handleAdd = React.useCallback(() => {
if (invites.length >= MAX_INVITES) {
showToast(
@@ -105,6 +110,7 @@ function Invite({ onSubmit }: Props) {
return newInvites;
});
}, [showToast, invites, t]);
const handleRemove = React.useCallback(
(ev: React.SyntheticEvent, index: number) => {
ev.preventDefault();
@@ -116,12 +122,14 @@ function Invite({ onSubmit }: Props) {
},
[]
);
const handleCopy = React.useCallback(() => {
setLinkCopied(true);
showToast(t("Share link copied"), {
type: "success",
});
}, [showToast, t]);
const handleRoleChange = React.useCallback((role: Role, index: number) => {
setInvites((prevInvites) => {
const newInvites = [...prevInvites];
@@ -129,6 +137,7 @@ function Invite({ onSubmit }: Props) {
return newInvites;
});
}, []);
return (
<form onSubmit={handleSubmit}>
{team.guestSignin ? (
@@ -180,9 +189,6 @@ function Invite({ onSubmit }: Props) {
</Button>
</CopyToClipboard>
</Flex>
<p>
<hr />
</p>
</CopyBlock>
)}
{invites.map((invite, index) => (
@@ -253,6 +259,9 @@ function Invite({ onSubmit }: Props) {
const CopyBlock = styled("div")`
margin: 2em 0;
font-size: 14px;
background: ${(props) => props.theme.secondaryBackground};
border-radius: 8px;
padding: 16px 16px 8px;
`;
const Remove = styled("div")`

View File

@@ -6,7 +6,7 @@ import { useTranslation, Trans } from "react-i18next";
import Heading from "~/components/Heading";
import HelpText from "~/components/HelpText";
import Scene from "~/components/Scene";
import Toggle from "~/components/Toggle";
import Switch from "~/components/Switch";
import useCurrentTeam from "~/hooks/useCurrentTeam";
import useStores from "~/hooks/useStores";
import useToasts from "~/hooks/useToasts";
@@ -44,7 +44,7 @@ function Features() {
the experience for all team members.
</Trans>
</HelpText>
<Toggle
<Switch
label={t("Collaborative editing")}
name="collaborativeEditing"
checked={data.collaborativeEditing}

View File

@@ -97,7 +97,6 @@ const Profile = () => {
required
short
/>
<br />
<InputSelect
label={t("Language")}
options={languageOptions}

View File

@@ -8,7 +8,7 @@ import Heading from "~/components/Heading";
import HelpText from "~/components/HelpText";
import InputSelect from "~/components/InputSelect";
import Scene from "~/components/Scene";
import Toggle from "~/components/Toggle";
import Switch from "~/components/Switch";
import env from "~/env";
import useCurrentTeam from "~/hooks/useCurrentTeam";
import useStores from "~/hooks/useStores";
@@ -69,7 +69,7 @@ function Security() {
</Trans>
</HelpText>
<Toggle
<Switch
label={t("Allow email authentication")}
name="guestSignin"
checked={data.guestSignin}
@@ -81,7 +81,7 @@ function Security() {
}
disabled={!env.EMAIL_ENABLED}
/>
<Toggle
<Switch
label={t("Public document sharing")}
name="sharing"
checked={data.sharing}
@@ -90,7 +90,7 @@ function Security() {
"When enabled, documents can be shared publicly on the internet by any team member"
)}
/>
<Toggle
<Switch
label={t("Rich service embeds")}
name="documentEmbeds"
checked={data.documentEmbeds}

View File

@@ -1,6 +1,6 @@
import * as React from "react";
import NotificationSetting from "~/models/NotificationSetting";
import Toggle from "~/components/Toggle";
import Switch from "~/components/Switch";
type Props = {
setting?: NotificationSetting;
@@ -20,7 +20,7 @@ const NotificationListItem = ({
description,
}: Props) => {
return (
<Toggle
<Switch
label={title}
name={event}
checked={!!setting}

View File

@@ -13,7 +13,7 @@ import Flex from "~/components/Flex";
import HelpText from "~/components/HelpText";
import ListItem from "~/components/List/Item";
import Popover from "~/components/Popover";
import Toggle from "~/components/Toggle";
import Switch from "~/components/Switch";
import useToasts from "~/hooks/useToasts";
type Props = {
@@ -82,13 +82,13 @@ function SlackListItem({ integration, collection }: Props) {
<Events>
<h3>{t("Notifications")}</h3>
<HelpText>{t("These events should be posted to Slack")}</HelpText>
<Toggle
<Switch
label={t("Document published")}
name="documents.publish"
checked={integration.events.includes("documents.publish")}
onChange={handleChange}
/>
<Toggle
<Switch
label={t("Document updated")}
name="documents.update"
checked={integration.events.includes("documents.update")}