fix: I should be able to use "enter" key to create profile (#4978)
## Context Fixes #4808 TL;DR Introducing pure stateless modal component ("UI modal") for our auth modal not to have default hotkeyScope overriding our create-profile hotkeyScope + we dont want the shortcut to be available for all the modal content, only for the input that should not be using a hotkeyscope, so we are using onKeyDown for the specific issue on create profile. Explanation create-profile hotkey scope is set by PageChangeEffect; CreateProfile component adds enter key shortcut; but this scope is overwritten by the default scope by the Modal component that expects a hotkeyScope to reset to (and defaults to the default hotkeyScope if none indicated). In the auth flow we were using that Modal component to give a modal look to the flow but it is not a modal per say, it's a set of pages contained within a modal look. By creating this UI component we are escaping that hotkeyScope overriding that does not make sense in our context. ## How was it tested Locally Storybook
This commit is contained in:
@ -1,9 +1,9 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import styled from '@emotion/styled';
|
import styled from '@emotion/styled';
|
||||||
|
|
||||||
import { Modal as UIModal } from '@/ui/layout/modal/components/Modal';
|
import { ModalLayout } from '@/ui/layout/modal/components/ModalLayout';
|
||||||
|
|
||||||
const StyledContent = styled(UIModal.Content)`
|
const StyledContent = styled(ModalLayout.Content)`
|
||||||
align-items: center;
|
align-items: center;
|
||||||
width: calc(400px - ${({ theme }) => theme.spacing(10 * 2)});
|
width: calc(400px - ${({ theme }) => theme.spacing(10 * 2)});
|
||||||
`;
|
`;
|
||||||
@ -11,7 +11,7 @@ const StyledContent = styled(UIModal.Content)`
|
|||||||
type AuthModalProps = { children: React.ReactNode };
|
type AuthModalProps = { children: React.ReactNode };
|
||||||
|
|
||||||
export const AuthModal = ({ children }: AuthModalProps) => (
|
export const AuthModal = ({ children }: AuthModalProps) => (
|
||||||
<UIModal isOpen={true} padding={'none'}>
|
<ModalLayout padding={'none'}>
|
||||||
<StyledContent>{children}</StyledContent>
|
<StyledContent>{children}</StyledContent>
|
||||||
</UIModal>
|
</ModalLayout>
|
||||||
);
|
);
|
||||||
|
|||||||
@ -164,9 +164,9 @@ export const SignInUpForm = () => {
|
|||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
error={showErrors ? error?.message : undefined}
|
error={showErrors ? error?.message : undefined}
|
||||||
onKeyDown={handleKeyDown}
|
|
||||||
fullWidth
|
fullWidth
|
||||||
disableHotkeys
|
disableHotkeys
|
||||||
|
onKeyDown={handleKeyDown}
|
||||||
/>
|
/>
|
||||||
</StyledInputContainer>
|
</StyledInputContainer>
|
||||||
)}
|
)}
|
||||||
@ -198,10 +198,10 @@ export const SignInUpForm = () => {
|
|||||||
placeholder="Password"
|
placeholder="Password"
|
||||||
onBlur={onBlur}
|
onBlur={onBlur}
|
||||||
onChange={onChange}
|
onChange={onChange}
|
||||||
onKeyDown={handleKeyDown}
|
|
||||||
error={showErrors ? error?.message : undefined}
|
error={showErrors ? error?.message : undefined}
|
||||||
fullWidth
|
fullWidth
|
||||||
disableHotkeys
|
disableHotkeys
|
||||||
|
onKeyDown={handleKeyDown}
|
||||||
/>
|
/>
|
||||||
</StyledInputContainer>
|
</StyledInputContainer>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@ -5,9 +5,7 @@ import { useParams } from 'react-router-dom';
|
|||||||
import { useNavigateAfterSignInUp } from '@/auth/sign-in-up/hooks/useNavigateAfterSignInUp.ts';
|
import { useNavigateAfterSignInUp } from '@/auth/sign-in-up/hooks/useNavigateAfterSignInUp.ts';
|
||||||
import { Form } from '@/auth/sign-in-up/hooks/useSignInUpForm.ts';
|
import { Form } from '@/auth/sign-in-up/hooks/useSignInUpForm.ts';
|
||||||
import { AppPath } from '@/types/AppPath';
|
import { AppPath } from '@/types/AppPath';
|
||||||
import { PageHotkeyScope } from '@/types/PageHotkeyScope';
|
|
||||||
import { useSnackBar } from '@/ui/feedback/snack-bar-manager/hooks/useSnackBar';
|
import { useSnackBar } from '@/ui/feedback/snack-bar-manager/hooks/useSnackBar';
|
||||||
import { useScopedHotkeys } from '@/ui/utilities/hotkey/hooks/useScopedHotkeys';
|
|
||||||
import { useIsMatchingLocation } from '~/hooks/useIsMatchingLocation';
|
import { useIsMatchingLocation } from '~/hooks/useIsMatchingLocation';
|
||||||
|
|
||||||
import { useAuth } from '../../hooks/useAuth';
|
import { useAuth } from '../../hooks/useAuth';
|
||||||
@ -118,31 +116,6 @@ export const useSignInUp = (form: UseFormReturn<Form>) => {
|
|||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
|
||||||
useScopedHotkeys(
|
|
||||||
'enter',
|
|
||||||
() => {
|
|
||||||
if (signInUpStep === SignInUpStep.Init) {
|
|
||||||
continueWithEmail();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (signInUpStep === SignInUpStep.Email) {
|
|
||||||
continueWithCredentials();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (signInUpStep === SignInUpStep.Password) {
|
|
||||||
form.handleSubmit(submitCredentials)();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
PageHotkeyScope.SignInUp,
|
|
||||||
[
|
|
||||||
continueWithEmail,
|
|
||||||
signInUpStep,
|
|
||||||
continueWithCredentials,
|
|
||||||
form,
|
|
||||||
submitCredentials,
|
|
||||||
],
|
|
||||||
);
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
isInviteMode,
|
isInviteMode,
|
||||||
signInUpStep,
|
signInUpStep,
|
||||||
|
|||||||
@ -1,8 +1,10 @@
|
|||||||
import React, { useEffect, useRef } from 'react';
|
import React, { useEffect, useRef } from 'react';
|
||||||
import styled from '@emotion/styled';
|
|
||||||
import { motion } from 'framer-motion';
|
|
||||||
import { Key } from 'ts-key-enum';
|
import { Key } from 'ts-key-enum';
|
||||||
|
|
||||||
|
import {
|
||||||
|
ModalLayout,
|
||||||
|
ModalLayoutProps,
|
||||||
|
} from '@/ui/layout/modal/components/ModalLayout';
|
||||||
import { usePreviousHotkeyScope } from '@/ui/utilities/hotkey/hooks/usePreviousHotkeyScope';
|
import { usePreviousHotkeyScope } from '@/ui/utilities/hotkey/hooks/usePreviousHotkeyScope';
|
||||||
import { useScopedHotkeys } from '@/ui/utilities/hotkey/hooks/useScopedHotkeys';
|
import { useScopedHotkeys } from '@/ui/utilities/hotkey/hooks/useScopedHotkeys';
|
||||||
import {
|
import {
|
||||||
@ -12,135 +14,11 @@ import {
|
|||||||
|
|
||||||
import { ModalHotkeyScope } from './types/ModalHotkeyScope';
|
import { ModalHotkeyScope } from './types/ModalHotkeyScope';
|
||||||
|
|
||||||
const StyledModalDiv = styled(motion.div)<{
|
type ModalProps = ModalLayoutProps & {
|
||||||
size?: ModalSize;
|
|
||||||
padding?: ModalPadding;
|
|
||||||
}>`
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
background: ${({ theme }) => theme.background.primary};
|
|
||||||
color: ${({ theme }) => theme.font.color.primary};
|
|
||||||
border-radius: ${({ theme }) => theme.border.radius.md};
|
|
||||||
overflow: hidden;
|
|
||||||
max-height: 90vh;
|
|
||||||
z-index: 10000; // should be higher than Backdrop's z-index
|
|
||||||
|
|
||||||
width: ${({ size, theme }) => {
|
|
||||||
switch (size) {
|
|
||||||
case 'small':
|
|
||||||
return theme.modal.size.sm;
|
|
||||||
case 'medium':
|
|
||||||
return theme.modal.size.md;
|
|
||||||
case 'large':
|
|
||||||
return theme.modal.size.lg;
|
|
||||||
default:
|
|
||||||
return 'auto';
|
|
||||||
}
|
|
||||||
}};
|
|
||||||
|
|
||||||
padding: ${({ padding, theme }) => {
|
|
||||||
switch (padding) {
|
|
||||||
case 'none':
|
|
||||||
return theme.spacing(0);
|
|
||||||
case 'small':
|
|
||||||
return theme.spacing(2);
|
|
||||||
case 'medium':
|
|
||||||
return theme.spacing(4);
|
|
||||||
case 'large':
|
|
||||||
return theme.spacing(6);
|
|
||||||
default:
|
|
||||||
return 'auto';
|
|
||||||
}
|
|
||||||
}};
|
|
||||||
`;
|
|
||||||
|
|
||||||
const StyledHeader = styled.div`
|
|
||||||
align-items: center;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
height: 60px;
|
|
||||||
overflow: hidden;
|
|
||||||
padding: ${({ theme }) => theme.spacing(5)};
|
|
||||||
`;
|
|
||||||
|
|
||||||
const StyledContent = styled.div`
|
|
||||||
display: flex;
|
|
||||||
flex: 1;
|
|
||||||
flex: 1 1 0%;
|
|
||||||
flex-direction: column;
|
|
||||||
overflow-y: auto;
|
|
||||||
padding: ${({ theme }) => theme.spacing(10)};
|
|
||||||
`;
|
|
||||||
|
|
||||||
const StyledFooter = styled.div`
|
|
||||||
align-items: center;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
height: 60px;
|
|
||||||
overflow: hidden;
|
|
||||||
padding: ${({ theme }) => theme.spacing(5)};
|
|
||||||
`;
|
|
||||||
|
|
||||||
const StyledBackDrop = styled(motion.div)`
|
|
||||||
align-items: center;
|
|
||||||
background: ${({ theme }) => theme.background.overlay};
|
|
||||||
display: flex;
|
|
||||||
height: 100%;
|
|
||||||
justify-content: center;
|
|
||||||
left: 0;
|
|
||||||
position: fixed;
|
|
||||||
top: 0;
|
|
||||||
width: 100%;
|
|
||||||
z-index: 9999;
|
|
||||||
`;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Modal components
|
|
||||||
*/
|
|
||||||
type ModalHeaderProps = React.PropsWithChildren & {
|
|
||||||
className?: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
const ModalHeader = ({ children, className }: ModalHeaderProps) => (
|
|
||||||
<StyledHeader className={className}>{children}</StyledHeader>
|
|
||||||
);
|
|
||||||
|
|
||||||
type ModalContentProps = React.PropsWithChildren & {
|
|
||||||
className?: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
const ModalContent = ({ children, className }: ModalContentProps) => (
|
|
||||||
<StyledContent className={className}>{children}</StyledContent>
|
|
||||||
);
|
|
||||||
|
|
||||||
type ModalFooterProps = React.PropsWithChildren & {
|
|
||||||
className?: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
const ModalFooter = ({ children, className }: ModalFooterProps) => (
|
|
||||||
<StyledFooter className={className}>{children}</StyledFooter>
|
|
||||||
);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Modal
|
|
||||||
*/
|
|
||||||
export type ModalSize = 'small' | 'medium' | 'large';
|
|
||||||
export type ModalPadding = 'none' | 'small' | 'medium' | 'large';
|
|
||||||
|
|
||||||
type ModalProps = React.PropsWithChildren & {
|
|
||||||
isOpen?: boolean;
|
isOpen?: boolean;
|
||||||
onClose?: () => void;
|
|
||||||
hotkeyScope?: ModalHotkeyScope;
|
hotkeyScope?: ModalHotkeyScope;
|
||||||
|
onClose?: () => void;
|
||||||
onEnter?: () => void;
|
onEnter?: () => void;
|
||||||
size?: ModalSize;
|
|
||||||
padding?: ModalPadding;
|
|
||||||
className?: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
const modalVariants = {
|
|
||||||
hidden: { opacity: 0 },
|
|
||||||
visible: { opacity: 1 },
|
|
||||||
exit: { opacity: 0 },
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export const Modal = ({
|
export const Modal = ({
|
||||||
@ -153,14 +31,6 @@ export const Modal = ({
|
|||||||
padding = 'medium',
|
padding = 'medium',
|
||||||
className,
|
className,
|
||||||
}: ModalProps) => {
|
}: ModalProps) => {
|
||||||
const modalRef = useRef<HTMLDivElement>(null);
|
|
||||||
|
|
||||||
useListenClickOutside({
|
|
||||||
refs: [modalRef],
|
|
||||||
callback: () => onClose?.(),
|
|
||||||
mode: ClickOutsideMode.comparePixels,
|
|
||||||
});
|
|
||||||
|
|
||||||
const {
|
const {
|
||||||
goBackToPreviousHotkeyScope,
|
goBackToPreviousHotkeyScope,
|
||||||
setHotkeyScopeAndMemorizePreviousScope,
|
setHotkeyScopeAndMemorizePreviousScope,
|
||||||
@ -196,30 +66,28 @@ export const Modal = ({
|
|||||||
setHotkeyScopeAndMemorizePreviousScope,
|
setHotkeyScopeAndMemorizePreviousScope,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
const modalRef = useRef<HTMLDivElement>(null);
|
||||||
|
|
||||||
|
useListenClickOutside({
|
||||||
|
refs: [modalRef],
|
||||||
|
callback: () => onClose?.(),
|
||||||
|
mode: ClickOutsideMode.comparePixels,
|
||||||
|
});
|
||||||
|
|
||||||
return isOpen ? (
|
return isOpen ? (
|
||||||
<StyledBackDrop>
|
<ModalLayout
|
||||||
<StyledModalDiv
|
className={className}
|
||||||
// framer-motion seems to have typing problems with refs
|
modalRef={modalRef}
|
||||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
size={size}
|
||||||
// @ts-ignore
|
padding={padding}
|
||||||
ref={modalRef}
|
>
|
||||||
size={size}
|
{children}
|
||||||
padding={padding}
|
</ModalLayout>
|
||||||
initial="hidden"
|
|
||||||
animate="visible"
|
|
||||||
exit="exit"
|
|
||||||
layout
|
|
||||||
variants={modalVariants}
|
|
||||||
className={className}
|
|
||||||
>
|
|
||||||
{children}
|
|
||||||
</StyledModalDiv>
|
|
||||||
</StyledBackDrop>
|
|
||||||
) : (
|
) : (
|
||||||
<></>
|
<></>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
Modal.Header = ModalHeader;
|
Modal.Header = ModalLayout.Header;
|
||||||
Modal.Content = ModalContent;
|
Modal.Content = ModalLayout.Content;
|
||||||
Modal.Footer = ModalFooter;
|
Modal.Footer = ModalLayout.Footer;
|
||||||
|
|||||||
@ -0,0 +1,168 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import styled from '@emotion/styled';
|
||||||
|
import { motion } from 'framer-motion';
|
||||||
|
|
||||||
|
const StyledModalDiv = styled(motion.div)<{
|
||||||
|
size?: ModalSize;
|
||||||
|
padding?: ModalPadding;
|
||||||
|
}>`
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
background: ${({ theme }) => theme.background.primary};
|
||||||
|
color: ${({ theme }) => theme.font.color.primary};
|
||||||
|
border-radius: ${({ theme }) => theme.border.radius.md};
|
||||||
|
overflow: hidden;
|
||||||
|
max-height: 90vh;
|
||||||
|
z-index: 10000; // should be higher than Backdrop's z-index
|
||||||
|
|
||||||
|
width: ${({ size, theme }) => {
|
||||||
|
switch (size) {
|
||||||
|
case 'small':
|
||||||
|
return theme.modal.size.sm;
|
||||||
|
case 'medium':
|
||||||
|
return theme.modal.size.md;
|
||||||
|
case 'large':
|
||||||
|
return theme.modal.size.lg;
|
||||||
|
default:
|
||||||
|
return 'auto';
|
||||||
|
}
|
||||||
|
}};
|
||||||
|
|
||||||
|
padding: ${({ padding, theme }) => {
|
||||||
|
switch (padding) {
|
||||||
|
case 'none':
|
||||||
|
return theme.spacing(0);
|
||||||
|
case 'small':
|
||||||
|
return theme.spacing(2);
|
||||||
|
case 'medium':
|
||||||
|
return theme.spacing(4);
|
||||||
|
case 'large':
|
||||||
|
return theme.spacing(6);
|
||||||
|
default:
|
||||||
|
return 'auto';
|
||||||
|
}
|
||||||
|
}};
|
||||||
|
`;
|
||||||
|
|
||||||
|
const StyledHeader = styled.div`
|
||||||
|
align-items: center;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
height: 60px;
|
||||||
|
overflow: hidden;
|
||||||
|
padding: ${({ theme }) => theme.spacing(5)};
|
||||||
|
`;
|
||||||
|
|
||||||
|
const StyledContent = styled.div`
|
||||||
|
display: flex;
|
||||||
|
flex: 1;
|
||||||
|
flex: 1 1 0%;
|
||||||
|
flex-direction: column;
|
||||||
|
overflow-y: auto;
|
||||||
|
padding: ${({ theme }) => theme.spacing(10)};
|
||||||
|
`;
|
||||||
|
|
||||||
|
const StyledFooter = styled.div`
|
||||||
|
align-items: center;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
height: 60px;
|
||||||
|
overflow: hidden;
|
||||||
|
padding: ${({ theme }) => theme.spacing(5)};
|
||||||
|
`;
|
||||||
|
|
||||||
|
const StyledBackDrop = styled(motion.div)`
|
||||||
|
align-items: center;
|
||||||
|
background: ${({ theme }) => theme.background.overlay};
|
||||||
|
display: flex;
|
||||||
|
height: 100%;
|
||||||
|
justify-content: center;
|
||||||
|
left: 0;
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
width: 100%;
|
||||||
|
z-index: 9999;
|
||||||
|
`;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Modal components
|
||||||
|
*/
|
||||||
|
type ModalLayoutHeaderProps = React.PropsWithChildren & {
|
||||||
|
className?: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
const ModalLayoutHeader = ({ children, className }: ModalLayoutHeaderProps) => (
|
||||||
|
<StyledHeader className={className}>{children}</StyledHeader>
|
||||||
|
);
|
||||||
|
|
||||||
|
type ModalLayoutContentProps = React.PropsWithChildren & {
|
||||||
|
className?: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
const ModalLayoutContent = ({
|
||||||
|
children,
|
||||||
|
className,
|
||||||
|
}: ModalLayoutContentProps) => (
|
||||||
|
<StyledContent className={className}>{children}</StyledContent>
|
||||||
|
);
|
||||||
|
|
||||||
|
type ModalLayoutFooterProps = React.PropsWithChildren & {
|
||||||
|
className?: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
const ModalLayoutFooter = ({ children, className }: ModalLayoutFooterProps) => (
|
||||||
|
<StyledFooter className={className}>{children}</StyledFooter>
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Modal
|
||||||
|
*/
|
||||||
|
export type ModalSize = 'small' | 'medium' | 'large';
|
||||||
|
export type ModalPadding = 'none' | 'small' | 'medium' | 'large';
|
||||||
|
|
||||||
|
export type ModalLayoutProps = React.PropsWithChildren & {
|
||||||
|
size?: ModalSize;
|
||||||
|
padding?: ModalPadding;
|
||||||
|
className?: string;
|
||||||
|
modalRef?: React.RefObject<HTMLElement>;
|
||||||
|
};
|
||||||
|
|
||||||
|
const modalVariants = {
|
||||||
|
hidden: { opacity: 0 },
|
||||||
|
visible: { opacity: 1 },
|
||||||
|
exit: { opacity: 0 },
|
||||||
|
};
|
||||||
|
|
||||||
|
// This component should be used over Modal when seeking a modal feel without modal state (hotkeyScope etc)
|
||||||
|
export const ModalLayout = ({
|
||||||
|
children,
|
||||||
|
size = 'medium',
|
||||||
|
padding = 'medium',
|
||||||
|
modalRef,
|
||||||
|
className,
|
||||||
|
}: ModalLayoutProps) => {
|
||||||
|
return (
|
||||||
|
<StyledBackDrop>
|
||||||
|
<StyledModalDiv
|
||||||
|
// framer-motion seems to have typing problems with refs
|
||||||
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||||
|
// @ts-ignore
|
||||||
|
ref={modalRef}
|
||||||
|
size={size}
|
||||||
|
padding={padding}
|
||||||
|
initial="hidden"
|
||||||
|
animate="visible"
|
||||||
|
exit="exit"
|
||||||
|
layout
|
||||||
|
variants={modalVariants}
|
||||||
|
className={className}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</StyledModalDiv>
|
||||||
|
</StyledBackDrop>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
ModalLayout.Header = ModalLayoutHeader;
|
||||||
|
ModalLayout.Content = ModalLayoutContent;
|
||||||
|
ModalLayout.Footer = ModalLayoutFooter;
|
||||||
@ -0,0 +1,36 @@
|
|||||||
|
import { Meta, StoryObj } from '@storybook/react';
|
||||||
|
import { ComponentDecorator } from 'twenty-ui';
|
||||||
|
|
||||||
|
import { ModalLayout } from '@/ui/layout/modal/components/ModalLayout';
|
||||||
|
|
||||||
|
const meta: Meta<typeof ModalLayout> = {
|
||||||
|
title: 'UI/Layout/Modal/ModalLayout',
|
||||||
|
component: ModalLayout,
|
||||||
|
};
|
||||||
|
|
||||||
|
export default meta;
|
||||||
|
type Story = StoryObj<typeof ModalLayout>;
|
||||||
|
|
||||||
|
export const Default: Story = {
|
||||||
|
args: {
|
||||||
|
size: 'medium',
|
||||||
|
padding: 'medium',
|
||||||
|
children: (
|
||||||
|
<>
|
||||||
|
<ModalLayout.Header>Stay in touch</ModalLayout.Header>
|
||||||
|
<ModalLayout.Content>
|
||||||
|
This is a dummy newletter form so don't bother trying to test it. Not
|
||||||
|
that I expect you to, anyways. :)
|
||||||
|
</ModalLayout.Content>
|
||||||
|
<ModalLayout.Footer>
|
||||||
|
By using Twenty, you're opting for the finest CRM experience you'll
|
||||||
|
ever encounter.
|
||||||
|
</ModalLayout.Footer>
|
||||||
|
</>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
decorators: [ComponentDecorator],
|
||||||
|
argTypes: {
|
||||||
|
children: { control: false },
|
||||||
|
},
|
||||||
|
};
|
||||||
@ -3,7 +3,6 @@ import { Controller, SubmitHandler, useForm } from 'react-hook-form';
|
|||||||
import styled from '@emotion/styled';
|
import styled from '@emotion/styled';
|
||||||
import { zodResolver } from '@hookform/resolvers/zod';
|
import { zodResolver } from '@hookform/resolvers/zod';
|
||||||
import { useRecoilState } from 'recoil';
|
import { useRecoilState } from 'recoil';
|
||||||
import { Key } from 'ts-key-enum';
|
|
||||||
import { z } from 'zod';
|
import { z } from 'zod';
|
||||||
|
|
||||||
import { SubTitle } from '@/auth/components/SubTitle';
|
import { SubTitle } from '@/auth/components/SubTitle';
|
||||||
@ -14,12 +13,10 @@ import { OnboardingStatus } from '@/auth/utils/getOnboardingStatus';
|
|||||||
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
|
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
|
||||||
import { useUpdateOneRecord } from '@/object-record/hooks/useUpdateOneRecord';
|
import { useUpdateOneRecord } from '@/object-record/hooks/useUpdateOneRecord';
|
||||||
import { ProfilePictureUploader } from '@/settings/profile/components/ProfilePictureUploader';
|
import { ProfilePictureUploader } from '@/settings/profile/components/ProfilePictureUploader';
|
||||||
import { PageHotkeyScope } from '@/types/PageHotkeyScope';
|
|
||||||
import { H2Title } from '@/ui/display/typography/components/H2Title';
|
import { H2Title } from '@/ui/display/typography/components/H2Title';
|
||||||
import { useSnackBar } from '@/ui/feedback/snack-bar-manager/hooks/useSnackBar';
|
import { useSnackBar } from '@/ui/feedback/snack-bar-manager/hooks/useSnackBar';
|
||||||
import { MainButton } from '@/ui/input/button/components/MainButton';
|
import { MainButton } from '@/ui/input/button/components/MainButton';
|
||||||
import { TextInput } from '@/ui/input/components/TextInput';
|
import { TextInput } from '@/ui/input/components/TextInput';
|
||||||
import { useScopedHotkeys } from '@/ui/utilities/hotkey/hooks/useScopedHotkeys';
|
|
||||||
import { WorkspaceMember } from '@/workspace-member/types/WorkspaceMember';
|
import { WorkspaceMember } from '@/workspace-member/types/WorkspaceMember';
|
||||||
|
|
||||||
const StyledContentContainer = styled.div`
|
const StyledContentContainer = styled.div`
|
||||||
@ -126,19 +123,17 @@ export const CreateProfile = () => {
|
|||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
|
||||||
useScopedHotkeys(
|
|
||||||
Key.Enter,
|
|
||||||
() => {
|
|
||||||
onSubmit(getValues());
|
|
||||||
},
|
|
||||||
PageHotkeyScope.CreateProfile,
|
|
||||||
[onSubmit],
|
|
||||||
);
|
|
||||||
|
|
||||||
if (onboardingStatus !== OnboardingStatus.OngoingProfileCreation) {
|
if (onboardingStatus !== OnboardingStatus.OngoingProfileCreation) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const onNameInputKeyDown = (event: React.KeyboardEvent<HTMLInputElement>) => {
|
||||||
|
if (event.key === 'Enter') {
|
||||||
|
event.preventDefault();
|
||||||
|
onSubmit(getValues());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Title withMarginTop={false}>Create profile</Title>
|
<Title withMarginTop={false}>Create profile</Title>
|
||||||
@ -171,6 +166,7 @@ export const CreateProfile = () => {
|
|||||||
placeholder="Tim"
|
placeholder="Tim"
|
||||||
error={error?.message}
|
error={error?.message}
|
||||||
fullWidth
|
fullWidth
|
||||||
|
onKeyDown={onNameInputKeyDown}
|
||||||
disableHotkeys
|
disableHotkeys
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
@ -190,6 +186,7 @@ export const CreateProfile = () => {
|
|||||||
placeholder="Cook"
|
placeholder="Cook"
|
||||||
error={error?.message}
|
error={error?.message}
|
||||||
fullWidth
|
fullWidth
|
||||||
|
onKeyDown={onNameInputKeyDown}
|
||||||
disableHotkeys
|
disableHotkeys
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|||||||
Reference in New Issue
Block a user