diff --git a/front/src/index.tsx b/front/src/index.tsx index 95070ddec..ed254ed2c 100644 --- a/front/src/index.tsx +++ b/front/src/index.tsx @@ -27,15 +27,15 @@ root.render( - - - - + + + + - - - - + + + + diff --git a/front/src/modules/hotkeys/hooks/useGoToHotkeys.ts b/front/src/modules/hotkeys/hooks/useGoToHotkeys.ts index f097ecfb9..a13c04614 100644 --- a/front/src/modules/hotkeys/hooks/useGoToHotkeys.ts +++ b/front/src/modules/hotkeys/hooks/useGoToHotkeys.ts @@ -15,5 +15,11 @@ export function useGoToHotkeys(key: Keys, location: string) { navigate(location); }, InternalHotkeysScope.Goto, + { + enableOnContentEditable: true, + enableOnFormTags: true, + preventDefault: true, + }, + [navigate], ); } diff --git a/front/src/modules/hotkeys/hooks/useSequenceScopedHotkeys.ts b/front/src/modules/hotkeys/hooks/useSequenceScopedHotkeys.ts index b89f8b61f..f8eb838fa 100644 --- a/front/src/modules/hotkeys/hooks/useSequenceScopedHotkeys.ts +++ b/front/src/modules/hotkeys/hooks/useSequenceScopedHotkeys.ts @@ -14,6 +14,7 @@ export function useSequenceHotkeys( enableOnFormTags: true, preventDefault: true, }, + deps: any[] = [], ) { const [pendingHotkey, setPendingHotkey] = useRecoilState(pendingHotkeyState); @@ -23,7 +24,7 @@ export function useSequenceHotkeys( setPendingHotkey(firstKey); }, { ...options, scopes: [scope] }, - [pendingHotkey], + [setPendingHotkey], ); useHotkeys( @@ -36,6 +37,6 @@ export function useSequenceHotkeys( callback(); }, { ...options, scopes: [scope] }, - [pendingHotkey, setPendingHotkey], + [pendingHotkey, setPendingHotkey, ...deps], ); } diff --git a/front/src/modules/hotkeys/hooks/useSetHotkeysScope.ts b/front/src/modules/hotkeys/hooks/useSetHotkeysScope.ts index 9dc9f930e..1ff68b7dc 100644 --- a/front/src/modules/hotkeys/hooks/useSetHotkeysScope.ts +++ b/front/src/modules/hotkeys/hooks/useSetHotkeysScope.ts @@ -6,6 +6,16 @@ import { DEFAULT_HOTKEYS_SCOPE_CUSTOM_SCOPES } from '../constants'; import { currentHotkeysScopeState } from '../states/internal/currentHotkeysScopeState'; import { CustomHotkeysScopes } from '../types/internal/CustomHotkeysScope'; +function isCustomScopesEqual( + customScopesA: CustomHotkeysScopes | undefined, + customScopesB: CustomHotkeysScopes | undefined, +) { + return ( + customScopesA?.commandMenu === customScopesB?.commandMenu && + customScopesA?.goto === customScopesB?.goto + ); +} + export function useSetHotkeysScope() { return useRecoilCallback( ({ snapshot, set }) => @@ -14,16 +24,6 @@ export function useSetHotkeysScope() { currentHotkeysScopeState, ); - function isCustomScopesEqual( - customScopesA: CustomHotkeysScopes | undefined, - customScopesB: CustomHotkeysScopes | undefined, - ) { - return ( - customScopesA?.commandMenu === customScopesB?.commandMenu && - customScopesA?.goto === customScopesB?.goto - ); - } - if (currentHotkeysScope.scope === hotkeysScopeToSet) { if (!isDefined(customScopes)) { if ( diff --git a/front/src/modules/ui/components/editable-cell/EditableCell.tsx b/front/src/modules/ui/components/editable-cell/EditableCell.tsx index 1736323f3..f6d8a1778 100644 --- a/front/src/modules/ui/components/editable-cell/EditableCell.tsx +++ b/front/src/modules/ui/components/editable-cell/EditableCell.tsx @@ -2,12 +2,9 @@ import { ReactElement } from 'react'; import styled from '@emotion/styled'; import { HotkeysScope } from '@/hotkeys/types/internal/HotkeysScope'; -import { InternalHotkeysScope } from '@/hotkeys/types/internal/InternalHotkeysScope'; import { useCurrentCellEditMode } from './hooks/useCurrentCellEditMode'; -import { useEditableCell } from './hooks/useEditableCell'; import { useIsSoftFocusOnCurrentCell } from './hooks/useIsSoftFocusOnCurrentCell'; -import { useSetSoftFocusOnCurrentCell } from './hooks/useSetSoftFocusOnCurrentCell'; import { EditableCellDisplayMode } from './EditableCellDisplayMode'; import { EditableCellEditMode } from './EditableCellEditMode'; import { EditableCellSoftFocusMode } from './EditableCellSoftFocusMode'; @@ -40,33 +37,10 @@ export function EditableCell({ }: OwnProps) { const { isCurrentCellInEditMode } = useCurrentCellEditMode(); - const setSoftFocusOnCurrentCell = useSetSoftFocusOnCurrentCell(); - - const { openEditableCell } = useEditableCell(); - const hasSoftFocus = useIsSoftFocusOnCurrentCell(); - // TODO: we might have silent problematic behavior because of the setTimeout in openEditableCell, investigate - // Maybe we could build a switchEditableCell to handle the case where we go from one cell to another. - // See https://github.com/twentyhq/twenty/issues/446 - function handleOnClick() { - if (isCurrentCellInEditMode) { - return; - } - - if (hasSoftFocus) { - openEditableCell( - editHotkeysScope ?? { - scope: InternalHotkeysScope.CellEditMode, - }, - ); - } else { - setSoftFocusOnCurrentCell(); - } - } - return ( - + {isCurrentCellInEditMode ? ( ` @@ -35,10 +35,14 @@ export const EditableCellNormalModeInnerContainer = styled.div` export function EditableCellDisplayMode({ children, }: React.PropsWithChildren) { - const hasSoftFocus = useIsSoftFocusOnCurrentCell(); + const setSoftFocusOnCurrentCell = useSetSoftFocusOnCurrentCell(); + + function handleOnClick() { + setSoftFocusOnCurrentCell(); + } return ( - + {children} diff --git a/front/src/modules/ui/components/editable-cell/EditableCellSoftFocusMode.tsx b/front/src/modules/ui/components/editable-cell/EditableCellSoftFocusMode.tsx index dad7d19de..240db27db 100644 --- a/front/src/modules/ui/components/editable-cell/EditableCellSoftFocusMode.tsx +++ b/front/src/modules/ui/components/editable-cell/EditableCellSoftFocusMode.tsx @@ -6,25 +6,32 @@ import { InternalHotkeysScope } from '@/hotkeys/types/internal/InternalHotkeysSc import { isNonTextWritingKey } from '@/utils/hotkeys/isNonTextWritingKey'; import { useEditableCell } from './hooks/useEditableCell'; -import { EditableCellDisplayMode } from './EditableCellDisplayMode'; +import { + EditableCellNormalModeInnerContainer, + EditableCellNormalModeOuterContainer, +} from './EditableCellDisplayMode'; export function EditableCellSoftFocusMode({ children, editHotkeysScope, }: React.PropsWithChildren<{ editHotkeysScope?: HotkeysScope }>) { - const { closeEditableCell, openEditableCell } = useEditableCell(); + const { openEditableCell } = useEditableCell(); + + function openEditMode() { + openEditableCell( + editHotkeysScope ?? { + scope: InternalHotkeysScope.CellEditMode, + }, + ); + } useScopedHotkeys( 'enter', () => { - openEditableCell( - editHotkeysScope ?? { - scope: InternalHotkeysScope.CellEditMode, - }, - ); + openEditMode(); }, InternalHotkeysScope.TableSoftFocus, - [closeEditableCell, editHotkeysScope], + [openEditMode], ); useScopedHotkeys( @@ -39,18 +46,27 @@ export function EditableCellSoftFocusMode({ return; } - openEditableCell( - editHotkeysScope ?? { - scope: InternalHotkeysScope.CellEditMode, - }, - ); + openEditMode(); }, InternalHotkeysScope.TableSoftFocus, - [openEditableCell, editHotkeysScope], + [openEditMode], { preventDefault: false, }, ); - return {children}; + function handleClick() { + openEditMode(); + } + + return ( + + + {children} + + + ); } diff --git a/front/src/modules/ui/components/editable-cell/hooks/useSetSoftFocusOnCurrentCell.ts b/front/src/modules/ui/components/editable-cell/hooks/useSetSoftFocusOnCurrentCell.ts index 3f5890966..4d3654799 100644 --- a/front/src/modules/ui/components/editable-cell/hooks/useSetSoftFocusOnCurrentCell.ts +++ b/front/src/modules/ui/components/editable-cell/hooks/useSetSoftFocusOnCurrentCell.ts @@ -1,5 +1,5 @@ -import { useCallback, useMemo } from 'react'; -import { useRecoilState } from 'recoil'; +import { useMemo } from 'react'; +import { useRecoilCallback } from 'recoil'; import { useSetHotkeysScope } from '@/hotkeys/hooks/useSetHotkeysScope'; import { InternalHotkeysScope } from '@/hotkeys/types/internal/InternalHotkeysScope'; @@ -14,6 +14,7 @@ import { CellPosition } from '@/ui/tables/types/CellPosition'; export function useSetSoftFocusOnCurrentCell() { const setSoftFocusPosition = useSetSoftFocusPosition(); + const [currentRowNumber] = useRecoilScopedState( currentRowNumberScopedState, RowContext, @@ -32,18 +33,17 @@ export function useSetSoftFocusOnCurrentCell() { [currentColumnNumber, currentRowNumber], ); - const [, setIsSoftFocusActive] = useRecoilState(isSoftFocusActiveState); - const setHotkeysScope = useSetHotkeysScope(); - return useCallback(() => { - setSoftFocusPosition(currentTablePosition); - setIsSoftFocusActive(true); - setHotkeysScope(InternalHotkeysScope.TableSoftFocus); - }, [ - setSoftFocusPosition, - currentTablePosition, - setIsSoftFocusActive, - setHotkeysScope, - ]); + return useRecoilCallback( + ({ set }) => + () => { + setSoftFocusPosition(currentTablePosition); + + set(isSoftFocusActiveState, true); + + setHotkeysScope(InternalHotkeysScope.TableSoftFocus); + }, + [setHotkeysScope, currentTablePosition, setSoftFocusPosition], + ); } diff --git a/front/src/modules/ui/layout/containers/FlexExpandingContainer.tsx b/front/src/modules/ui/layout/containers/FlexExpandingContainer.tsx new file mode 100644 index 000000000..92c94466e --- /dev/null +++ b/front/src/modules/ui/layout/containers/FlexExpandingContainer.tsx @@ -0,0 +1,7 @@ +import styled from '@emotion/styled'; + +export const FlexExpandingContainer = styled.div` + display: flex; + height: 100%; + width: 100%; +`; diff --git a/front/src/modules/ui/layout/containers/NoTopBarContainer.tsx b/front/src/modules/ui/layout/containers/NoTopBarContainer.tsx index aa9b55ee8..cde2a9ae8 100644 --- a/front/src/modules/ui/layout/containers/NoTopBarContainer.tsx +++ b/front/src/modules/ui/layout/containers/NoTopBarContainer.tsx @@ -1,9 +1,9 @@ import styled from '@emotion/styled'; -import { ContentContainer } from './ContentContainer'; +import { RightDrawerContainer } from './RightDrawerContainer'; type OwnProps = { - children: JSX.Element; + children: JSX.Element | JSX.Element[]; }; const StyledContainer = styled.div` @@ -15,7 +15,7 @@ const StyledContainer = styled.div` export function NoTopBarContainer({ children }: OwnProps) { return ( - {children} + {children} ); } diff --git a/front/src/modules/ui/layout/containers/RightDrawerContainer.tsx b/front/src/modules/ui/layout/containers/RightDrawerContainer.tsx new file mode 100644 index 000000000..2e96d3b1f --- /dev/null +++ b/front/src/modules/ui/layout/containers/RightDrawerContainer.tsx @@ -0,0 +1,43 @@ +import styled from '@emotion/styled'; + +import { Panel } from '../Panel'; +import { RightDrawer } from '../right-drawer/components/RightDrawer'; + +type OwnProps = { + children: JSX.Element | JSX.Element[]; + topMargin?: number; +}; + +const StyledMainContainer = styled.div<{ topMargin: number }>` + background: ${({ theme }) => theme.background.noisy}; + display: flex; + + flex-direction: row; + gap: ${({ theme }) => theme.spacing(2)}; + height: calc(100% - ${(props) => props.topMargin}px); + + padding-bottom: ${({ theme }) => theme.spacing(3)}; + padding-right: ${({ theme }) => theme.spacing(3)}; + width: calc(100% - ${({ theme }) => theme.spacing(3)}); +`; + +type LeftContainerProps = { + isRightDrawerOpen?: boolean; +}; + +const StyledLeftContainer = styled.div` + display: flex; + position: relative; + width: 100%; +`; + +export function RightDrawerContainer({ children, topMargin }: OwnProps) { + return ( + + + {children} + + + + ); +} diff --git a/front/src/modules/ui/layout/containers/VerticalFullWidthContainer.tsx b/front/src/modules/ui/layout/containers/VerticalFullWidthContainer.tsx new file mode 100644 index 000000000..d7b42a9d9 --- /dev/null +++ b/front/src/modules/ui/layout/containers/VerticalFullWidthContainer.tsx @@ -0,0 +1,15 @@ +import styled from '@emotion/styled'; + +const StyledContainer = styled.div` + display: flex; + flex-direction: column; + width: 100%; +`; + +export function VerticalFullWidthContainer({ + children, +}: { + children: JSX.Element[]; +}) { + return {children}; +} diff --git a/front/src/modules/ui/layout/containers/WithTopBarContainer.tsx b/front/src/modules/ui/layout/containers/WithTopBarContainer.tsx index 02f106d72..fc2c3a1c1 100644 --- a/front/src/modules/ui/layout/containers/WithTopBarContainer.tsx +++ b/front/src/modules/ui/layout/containers/WithTopBarContainer.tsx @@ -1,12 +1,13 @@ import { ReactNode } from 'react'; import styled from '@emotion/styled'; +import { TopBarHotkeys } from '../top-bar/TableTopBarHotkeys'; import { TOP_BAR_MIN_HEIGHT, TopBar } from '../top-bar/TopBar'; -import { ContentContainer } from './ContentContainer'; +import { RightDrawerContainer } from './RightDrawerContainer'; type OwnProps = { - children: JSX.Element; + children: JSX.Element | JSX.Element[]; title: string; icon: ReactNode; onAddButtonClick?: () => void; @@ -26,10 +27,11 @@ export function WithTopBarContainer({ }: OwnProps) { return ( + - + {children} - + ); } diff --git a/front/src/modules/ui/layout/top-bar/TableTopBarHotkeys.tsx b/front/src/modules/ui/layout/top-bar/TableTopBarHotkeys.tsx new file mode 100644 index 000000000..e6b87a808 --- /dev/null +++ b/front/src/modules/ui/layout/top-bar/TableTopBarHotkeys.tsx @@ -0,0 +1,17 @@ +import { useScopedHotkeys } from '@/hotkeys/hooks/useScopedHotkeys'; +import { InternalHotkeysScope } from '@/hotkeys/types/internal/InternalHotkeysScope'; + +type OwnProps = { + onAddButtonClick?: () => void; +}; + +export function TopBarHotkeys({ onAddButtonClick }: OwnProps) { + useScopedHotkeys( + 'c', + () => onAddButtonClick?.(), + InternalHotkeysScope.Table, + [onAddButtonClick], + ); + + return <>; +} diff --git a/front/src/modules/ui/layout/top-bar/TopBar.tsx b/front/src/modules/ui/layout/top-bar/TopBar.tsx index d297c91de..3b3fd85db 100644 --- a/front/src/modules/ui/layout/top-bar/TopBar.tsx +++ b/front/src/modules/ui/layout/top-bar/TopBar.tsx @@ -1,8 +1,6 @@ import { ReactNode } from 'react'; import styled from '@emotion/styled'; -import { useScopedHotkeys } from '@/hotkeys/hooks/useScopedHotkeys'; -import { InternalHotkeysScope } from '@/hotkeys/types/internal/InternalHotkeysScope'; import { IconPlus } from '@/ui/icons/index'; import NavCollapseButton from '../navbar/NavCollapseButton'; @@ -51,8 +49,6 @@ type OwnProps = { }; export function TopBar({ title, icon, onAddButtonClick }: OwnProps) { - useScopedHotkeys('c', () => onAddButtonClick?.(), InternalHotkeysScope.Table); - return ( <> diff --git a/front/src/modules/ui/tables/hooks/useLeaveTableFocus.ts b/front/src/modules/ui/tables/hooks/useLeaveTableFocus.ts index 1249b8ba6..fcb3bf65b 100644 --- a/front/src/modules/ui/tables/hooks/useLeaveTableFocus.ts +++ b/front/src/modules/ui/tables/hooks/useLeaveTableFocus.ts @@ -1,4 +1,4 @@ -import { useRecoilValue } from 'recoil'; +import { useRecoilCallback } from 'recoil'; import { useSetHotkeysScope } from '@/hotkeys/hooks/useSetHotkeysScope'; import { currentHotkeysScopeState } from '@/hotkeys/states/internal/currentHotkeysScopeState'; @@ -11,29 +11,39 @@ import { useCloseCurrentCellInEditMode } from './useClearCellInEditMode'; import { useDisableSoftFocus } from './useDisableSoftFocus'; export function useLeaveTableFocus() { - const currentHotkeysScope = useRecoilValue(currentHotkeysScopeState); - const disableSoftFocus = useDisableSoftFocus(); const closeCurrentCellInEditMode = useCloseCurrentCellInEditMode(); const setHotkeysScope = useSetHotkeysScope(); - const isSoftFocusActive = useRecoilValue(isSoftFocusActiveState); - const isSomeInputInEditMode = useRecoilValue(isSomeInputInEditModeState); + return useRecoilCallback( + ({ snapshot }) => + () => { + const isSoftFocusActive = snapshot + .getLoadable(isSoftFocusActiveState) + .valueOrThrow(); - return async function leaveTableFocus() { - // TODO: replace with scope ancestor ? - if (!isSoftFocusActive && !isSomeInputInEditMode) { - return; - } + const isSomeInputInEditMode = snapshot + .getLoadable(isSomeInputInEditModeState) + .valueOrThrow(); - if (currentHotkeysScope?.scope === InternalHotkeysScope.Table) { - return; - } + const currentHotkeysScope = snapshot + .getLoadable(currentHotkeysScopeState) + .valueOrThrow(); - closeCurrentCellInEditMode(); - disableSoftFocus(); + if (!isSoftFocusActive && !isSomeInputInEditMode) { + return; + } - setHotkeysScope(InternalHotkeysScope.Table, { goto: true }); - }; + if (currentHotkeysScope?.scope === InternalHotkeysScope.Table) { + return; + } + + closeCurrentCellInEditMode(); + disableSoftFocus(); + + setHotkeysScope(InternalHotkeysScope.Table, { goto: true }); + }, + [setHotkeysScope, closeCurrentCellInEditMode, disableSoftFocus], + ); } diff --git a/front/src/pages/people/People.tsx b/front/src/pages/people/People.tsx index 708fc9c3e..421e35e9b 100644 --- a/front/src/pages/people/People.tsx +++ b/front/src/pages/people/People.tsx @@ -1,12 +1,12 @@ import { getOperationName } from '@apollo/client/utilities'; import { useTheme } from '@emotion/react'; -import styled from '@emotion/styled'; import { v4 as uuidv4 } from 'uuid'; import { GET_PEOPLE } from '@/people/services'; import { RecoilScope } from '@/recoil-scope/components/RecoilScope'; import { EntityTableActionBar } from '@/ui/components/table/action-bar/EntityTableActionBar'; -import { IconUser } from '@/ui/icons/index'; +import { IconBuildingSkyscraper } from '@/ui/icons/index'; +import { FlexExpandingContainer } from '@/ui/layout/containers/FlexExpandingContainer'; import { WithTopBarContainer } from '@/ui/layout/containers/WithTopBarContainer'; import { TableContext } from '@/ui/tables/states/TableContext'; import { useInsertPersonMutation } from '~/generated/graphql'; @@ -15,12 +15,6 @@ import { TableActionBarButtonCreateCommentThreadPeople } from './table/TableActi import { TableActionBarButtonDeletePeople } from './table/TableActionBarButtonDeletePeople'; import { PeopleTable } from './PeopleTable'; -const StyledPeopleContainer = styled.div` - display: flex; - height: 100%; - width: 100%; -`; - export function People() { const [insertPersonMutation] = useInsertPersonMutation(); @@ -42,20 +36,20 @@ export function People() { const theme = useTheme(); return ( - } - onAddButtonClick={handleAddButtonClick} - > - - + + } + onAddButtonClick={handleAddButtonClick} + > + - + - - + + ); }