mirror of
https://github.com/twentyhq/twenty.git
synced 2024-12-23 20:13:21 +03:00
Fix noninteractive toggle (#8383)
- Use a label to make the whole card interactive - Disallow the Toggle component to shrink; it used to on mobile devices A focus indicator is missing for the Toggle component. We'll have to add one.
This commit is contained in:
parent
8b5e90aca9
commit
f3e3c186dc
@ -5,7 +5,7 @@ import { SettingsAccountsEventVisibilitySettingsCard } from '@/settings/accounts
|
|||||||
import { SettingsOptionCardContent } from '@/settings/components/SettingsOptionCardContent';
|
import { SettingsOptionCardContent } from '@/settings/components/SettingsOptionCardContent';
|
||||||
import styled from '@emotion/styled';
|
import styled from '@emotion/styled';
|
||||||
import { Section } from '@react-email/components';
|
import { Section } from '@react-email/components';
|
||||||
import { Card, H2Title, Toggle } from 'twenty-ui';
|
import { Card, H2Title } from 'twenty-ui';
|
||||||
import { CalendarChannelVisibility } from '~/generated-metadata/graphql';
|
import { CalendarChannelVisibility } from '~/generated-metadata/graphql';
|
||||||
|
|
||||||
const StyledDetailsContainer = styled.div`
|
const StyledDetailsContainer = styled.div`
|
||||||
@ -21,10 +21,6 @@ type SettingsAccountsCalendarChannelDetailsProps = {
|
|||||||
>;
|
>;
|
||||||
};
|
};
|
||||||
|
|
||||||
const StyledToggle = styled(Toggle)`
|
|
||||||
margin-left: auto;
|
|
||||||
`;
|
|
||||||
|
|
||||||
export const SettingsAccountsCalendarChannelDetails = ({
|
export const SettingsAccountsCalendarChannelDetails = ({
|
||||||
calendarChannel,
|
calendarChannel,
|
||||||
}: SettingsAccountsCalendarChannelDetailsProps) => {
|
}: SettingsAccountsCalendarChannelDetailsProps) => {
|
||||||
@ -71,16 +67,13 @@ export const SettingsAccountsCalendarChannelDetails = ({
|
|||||||
<SettingsOptionCardContent
|
<SettingsOptionCardContent
|
||||||
title="Auto-creation"
|
title="Auto-creation"
|
||||||
description="Automatically create contacts for people."
|
description="Automatically create contacts for people."
|
||||||
onClick={() =>
|
checked={calendarChannel.isContactAutoCreationEnabled}
|
||||||
|
onChange={() => {
|
||||||
handleContactAutoCreationToggle(
|
handleContactAutoCreationToggle(
|
||||||
!calendarChannel.isContactAutoCreationEnabled,
|
!calendarChannel.isContactAutoCreationEnabled,
|
||||||
)
|
);
|
||||||
}
|
}}
|
||||||
>
|
|
||||||
<StyledToggle
|
|
||||||
value={calendarChannel.isContactAutoCreationEnabled}
|
|
||||||
/>
|
/>
|
||||||
</SettingsOptionCardContent>
|
|
||||||
</Card>
|
</Card>
|
||||||
</Section>
|
</Section>
|
||||||
</StyledDetailsContainer>
|
</StyledDetailsContainer>
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import styled from '@emotion/styled';
|
import styled from '@emotion/styled';
|
||||||
import { Card, H2Title, Section, Toggle } from 'twenty-ui';
|
import { Card, H2Title, Section } from 'twenty-ui';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
MessageChannel,
|
MessageChannel,
|
||||||
@ -30,10 +30,6 @@ const StyledDetailsContainer = styled.div`
|
|||||||
gap: ${({ theme }) => theme.spacing(6)};
|
gap: ${({ theme }) => theme.spacing(6)};
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const StyledToggle = styled(Toggle)`
|
|
||||||
margin-left: auto;
|
|
||||||
`;
|
|
||||||
|
|
||||||
export const SettingsAccountsMessageChannelDetails = ({
|
export const SettingsAccountsMessageChannelDetails = ({
|
||||||
messageChannel,
|
messageChannel,
|
||||||
}: SettingsAccountsMessageChannelDetailsProps) => {
|
}: SettingsAccountsMessageChannelDetailsProps) => {
|
||||||
@ -107,25 +103,23 @@ export const SettingsAccountsMessageChannelDetails = ({
|
|||||||
title="Exclude non-professional emails"
|
title="Exclude non-professional emails"
|
||||||
description="Don’t create contacts from/to Gmail, Outlook emails"
|
description="Don’t create contacts from/to Gmail, Outlook emails"
|
||||||
divider
|
divider
|
||||||
onClick={() =>
|
checked={messageChannel.excludeNonProfessionalEmails}
|
||||||
|
onChange={() => {
|
||||||
handleIsNonProfessionalEmailExcludedToggle(
|
handleIsNonProfessionalEmailExcludedToggle(
|
||||||
!messageChannel.excludeNonProfessionalEmails,
|
!messageChannel.excludeNonProfessionalEmails,
|
||||||
)
|
);
|
||||||
}
|
}}
|
||||||
>
|
/>
|
||||||
<StyledToggle value={messageChannel.excludeNonProfessionalEmails} />
|
|
||||||
</SettingsOptionCardContent>
|
|
||||||
<SettingsOptionCardContent
|
<SettingsOptionCardContent
|
||||||
title="Exclude group emails"
|
title="Exclude group emails"
|
||||||
description="Don’t sync emails from team@ support@ noreply@..."
|
description="Don’t sync emails from team@ support@ noreply@..."
|
||||||
onClick={() =>
|
checked={messageChannel.excludeGroupEmails}
|
||||||
|
onChange={() =>
|
||||||
handleIsGroupEmailExcludedToggle(
|
handleIsGroupEmailExcludedToggle(
|
||||||
!messageChannel.excludeGroupEmails,
|
!messageChannel.excludeGroupEmails,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
>
|
/>
|
||||||
<StyledToggle value={messageChannel.excludeGroupEmails} />
|
|
||||||
</SettingsOptionCardContent>
|
|
||||||
</Card>
|
</Card>
|
||||||
</Section>
|
</Section>
|
||||||
</StyledDetailsContainer>
|
</StyledDetailsContainer>
|
||||||
|
@ -1,16 +1,16 @@
|
|||||||
import styled from '@emotion/styled';
|
|
||||||
import { useTheme } from '@emotion/react';
|
import { useTheme } from '@emotion/react';
|
||||||
|
import styled from '@emotion/styled';
|
||||||
|
|
||||||
import { IconComponent, CardContent } from 'twenty-ui';
|
import { useId } from 'react';
|
||||||
import { ReactNode } from 'react';
|
import { CardContent, IconComponent, Toggle } from 'twenty-ui';
|
||||||
|
|
||||||
type SettingsOptionCardContentProps = {
|
type SettingsOptionCardContentProps = {
|
||||||
Icon?: IconComponent;
|
Icon?: IconComponent;
|
||||||
title: string;
|
title: React.ReactNode;
|
||||||
description: string;
|
description: string;
|
||||||
onClick: () => void;
|
|
||||||
children: ReactNode;
|
|
||||||
divider?: boolean;
|
divider?: boolean;
|
||||||
|
checked: boolean;
|
||||||
|
onChange: (checked: boolean) => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
const StyledCardContent = styled(CardContent)`
|
const StyledCardContent = styled(CardContent)`
|
||||||
@ -18,6 +18,7 @@ const StyledCardContent = styled(CardContent)`
|
|||||||
display: flex;
|
display: flex;
|
||||||
gap: ${({ theme }) => theme.spacing(4)};
|
gap: ${({ theme }) => theme.spacing(4)};
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
position: relative;
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
background: ${({ theme }) => theme.background.transparent.lighter};
|
background: ${({ theme }) => theme.background.transparent.lighter};
|
||||||
@ -47,28 +48,48 @@ const StyledIcon = styled.div`
|
|||||||
min-width: ${({ theme }) => theme.icon.size.md};
|
min-width: ${({ theme }) => theme.icon.size.md};
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
const StyledToggle = styled(Toggle)`
|
||||||
|
margin-left: auto;
|
||||||
|
`;
|
||||||
|
|
||||||
|
const StyledCover = styled.span`
|
||||||
|
cursor: pointer;
|
||||||
|
inset: 0;
|
||||||
|
position: absolute;
|
||||||
|
`;
|
||||||
|
|
||||||
export const SettingsOptionCardContent = ({
|
export const SettingsOptionCardContent = ({
|
||||||
Icon,
|
Icon,
|
||||||
title,
|
title,
|
||||||
description,
|
description,
|
||||||
onClick,
|
|
||||||
children,
|
|
||||||
divider,
|
divider,
|
||||||
|
checked,
|
||||||
|
onChange,
|
||||||
}: SettingsOptionCardContentProps) => {
|
}: SettingsOptionCardContentProps) => {
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
|
|
||||||
|
const toggleId = useId();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<StyledCardContent onClick={onClick} divider={divider}>
|
<StyledCardContent divider={divider}>
|
||||||
{Icon && (
|
{Icon && (
|
||||||
<StyledIcon>
|
<StyledIcon>
|
||||||
<Icon size={theme.icon.size.md} stroke={theme.icon.stroke.md} />
|
<Icon size={theme.icon.size.md} stroke={theme.icon.stroke.md} />
|
||||||
</StyledIcon>
|
</StyledIcon>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<StyledTitle>{title}</StyledTitle>
|
<StyledTitle>
|
||||||
|
<label htmlFor={toggleId}>
|
||||||
|
{title}
|
||||||
|
|
||||||
|
<StyledCover />
|
||||||
|
</label>
|
||||||
|
</StyledTitle>
|
||||||
<StyledDescription>{description}</StyledDescription>
|
<StyledDescription>{description}</StyledDescription>
|
||||||
</div>
|
</div>
|
||||||
{children}
|
|
||||||
|
<StyledToggle id={toggleId} value={checked} onChange={onChange} />
|
||||||
</StyledCardContent>
|
</StyledCardContent>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -2,21 +2,21 @@ import { currentWorkspaceState } from '@/auth/states/currentWorkspaceState';
|
|||||||
import { SettingsOptionCardContent } from '@/settings/components/SettingsOptionCardContent';
|
import { SettingsOptionCardContent } from '@/settings/components/SettingsOptionCardContent';
|
||||||
import { SnackBarVariant } from '@/ui/feedback/snack-bar-manager/components/SnackBar';
|
import { SnackBarVariant } from '@/ui/feedback/snack-bar-manager/components/SnackBar';
|
||||||
import { useSnackBar } from '@/ui/feedback/snack-bar-manager/hooks/useSnackBar';
|
import { useSnackBar } from '@/ui/feedback/snack-bar-manager/hooks/useSnackBar';
|
||||||
import styled from '@emotion/styled';
|
|
||||||
import { useRecoilState } from 'recoil';
|
import { useRecoilState } from 'recoil';
|
||||||
import { Card, IconLink, Toggle } from 'twenty-ui';
|
import { Card, IconLink, isDefined } from 'twenty-ui';
|
||||||
import { useUpdateWorkspaceMutation } from '~/generated/graphql';
|
import { useUpdateWorkspaceMutation } from '~/generated/graphql';
|
||||||
|
|
||||||
const StyledToggle = styled(Toggle)`
|
|
||||||
margin-left: auto;
|
|
||||||
`;
|
|
||||||
|
|
||||||
export const SettingsSecurityOptionsList = () => {
|
export const SettingsSecurityOptionsList = () => {
|
||||||
const { enqueueSnackBar } = useSnackBar();
|
const { enqueueSnackBar } = useSnackBar();
|
||||||
|
|
||||||
const [currentWorkspace, setCurrentWorkspace] = useRecoilState(
|
const [currentWorkspace, setCurrentWorkspace] = useRecoilState(
|
||||||
currentWorkspaceState,
|
currentWorkspaceState,
|
||||||
);
|
);
|
||||||
|
if (!isDefined(currentWorkspace)) {
|
||||||
|
throw new Error(
|
||||||
|
'The current workspace must be defined to edit its security options.',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
const [updateWorkspace] = useUpdateWorkspaceMutation();
|
const [updateWorkspace] = useUpdateWorkspaceMutation();
|
||||||
|
|
||||||
@ -49,12 +49,11 @@ export const SettingsSecurityOptionsList = () => {
|
|||||||
Icon={IconLink}
|
Icon={IconLink}
|
||||||
title="Invite by Link"
|
title="Invite by Link"
|
||||||
description="Allow the invitation of new users by sharing an invite link."
|
description="Allow the invitation of new users by sharing an invite link."
|
||||||
onClick={() =>
|
checked={currentWorkspace.isPublicInviteLinkEnabled}
|
||||||
handleChange(!currentWorkspace?.isPublicInviteLinkEnabled)
|
onChange={() =>
|
||||||
|
handleChange(!currentWorkspace.isPublicInviteLinkEnabled)
|
||||||
}
|
}
|
||||||
>
|
/>
|
||||||
<StyledToggle value={currentWorkspace?.isPublicInviteLinkEnabled} />
|
|
||||||
</SettingsOptionCardContent>
|
|
||||||
</Card>
|
</Card>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -12,6 +12,7 @@ type ContainerProps = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const StyledContainer = styled.label<ContainerProps>`
|
const StyledContainer = styled.label<ContainerProps>`
|
||||||
|
flex-shrink: 0;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
background-color: ${({ theme, isOn, color }) =>
|
background-color: ${({ theme, isOn, color }) =>
|
||||||
isOn ? (color ?? theme.color.blue) : theme.background.transparent.medium};
|
isOn ? (color ?? theme.color.blue) : theme.background.transparent.medium};
|
||||||
|
Loading…
Reference in New Issue
Block a user