diff --git a/packages/twenty-front/src/modules/action-menu/hooks/useShouldActionBeRegisteredParams.ts b/packages/twenty-front/src/modules/action-menu/hooks/useShouldActionBeRegisteredParams.ts index 62d3b1a9ad53f..57d37941c51e9 100644 --- a/packages/twenty-front/src/modules/action-menu/hooks/useShouldActionBeRegisteredParams.ts +++ b/packages/twenty-front/src/modules/action-menu/hooks/useShouldActionBeRegisteredParams.ts @@ -1,123 +1,124 @@ -import { forceRegisteredActionsByKeyState } from '@/action-menu/actions/states/forceRegisteredActionsMapComponentState'; -import { type ShouldBeRegisteredFunctionParams } from '@/action-menu/actions/types/ShouldBeRegisteredFunctionParams'; -import { getActionViewType } from '@/action-menu/actions/utils/getActionViewType'; -import { ActionMenuContext } from '@/action-menu/contexts/ActionMenuContext'; -import { objectPermissionsFamilySelector } from '@/auth/states/objectPermissionsFamilySelector'; -import { contextStoreCurrentViewTypeComponentState } from '@/context-store/states/contextStoreCurrentViewTypeComponentState'; -import { contextStoreNumberOfSelectedRecordsComponentState } from '@/context-store/states/contextStoreNumberOfSelectedRecordsComponentState'; -import { contextStoreTargetedRecordsRuleComponentState } from '@/context-store/states/contextStoreTargetedRecordsRuleComponentState'; -import { ContextStoreViewType } from '@/context-store/types/ContextStoreViewType'; -import { useFavorites } from '@/favorites/hooks/useFavorites'; -import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular'; -import { type ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem'; -import { useObjectPermissionsForObject } from '@/object-record/hooks/useObjectPermissionsForObject'; -import { hasAnySoftDeleteFilterOnViewComponentSelector } from '@/object-record/record-filter/states/hasAnySoftDeleteFilterOnView'; -import { recordStoreFamilyState } from '@/object-record/record-store/states/recordStoreFamilyState'; -import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue'; -import { useContext } from 'react'; -import { useRecoilCallback, useRecoilValue } from 'recoil'; - -export const useShouldActionBeRegisteredParams = ({ - objectMetadataItem, -}: { - objectMetadataItem?: ObjectMetadataItem; -}): ShouldBeRegisteredFunctionParams => { - const { sortedFavorites: favorites } = useFavorites(); - - const contextStoreTargetedRecordsRule = useRecoilComponentValue( - contextStoreTargetedRecordsRuleComponentState, - ); - - const recordId = - contextStoreTargetedRecordsRule.mode === 'selection' - ? contextStoreTargetedRecordsRule.selectedRecordIds[0] - : undefined; - - const foundFavorite = favorites?.find( - (favorite) => favorite.recordId === recordId, - ); - - const isFavorite = !!foundFavorite; - - const selectedRecord = - useRecoilValue(recordStoreFamilyState(recordId ?? '')) || undefined; - - const objectPermissions = useObjectPermissionsForObject( - objectMetadataItem?.id ?? '', - ); - - const isNoteOrTask = - objectMetadataItem?.nameSingular === CoreObjectNameSingular.Note || - objectMetadataItem?.nameSingular === CoreObjectNameSingular.Task; - - const { isInRightDrawer } = useContext(ActionMenuContext); - - const hasAnySoftDeleteFilterOnView = useRecoilComponentValue( - hasAnySoftDeleteFilterOnViewComponentSelector, - ); - - const isShowPage = - useRecoilComponentValue(contextStoreCurrentViewTypeComponentState) === - ContextStoreViewType.ShowPage; - - const numberOfSelectedRecords = useRecoilComponentValue( - contextStoreNumberOfSelectedRecordsComponentState, - ); - - const contextStoreCurrentViewType = useRecoilComponentValue( - contextStoreCurrentViewTypeComponentState, - ); - - const viewType = getActionViewType( - contextStoreCurrentViewType, - contextStoreTargetedRecordsRule, - ); - - const getObjectReadPermission = useRecoilCallback( - ({ snapshot }) => - (objectMetadataNameSingular: string) => { - return snapshot - .getLoadable( - objectPermissionsFamilySelector({ - objectNameSingular: objectMetadataNameSingular, - }), - ) - .getValue().canRead; - }, - [], - ); - - const getObjectWritePermission = useRecoilCallback( - ({ snapshot }) => - (objectMetadataNameSingular: string) => { - return snapshot - .getLoadable( - objectPermissionsFamilySelector({ - objectNameSingular: objectMetadataNameSingular, - }), - ) - .getValue().canUpdate; - }, - [], - ); - - const forceRegisteredActionsByKey = useRecoilValue( - forceRegisteredActionsByKeyState, - ); - - return { - objectMetadataItem, - isFavorite, - objectPermissions, - isNoteOrTask, - isInRightDrawer, - hasAnySoftDeleteFilterOnView, - isShowPage, - selectedRecord, - numberOfSelectedRecords, - viewType: viewType ?? undefined, - getTargetObjectReadPermission: getObjectReadPermission, - getTargetObjectWritePermission: getObjectWritePermission, - forceRegisteredActionsByKey, - }; -}; +import { forceRegisteredActionsByKeyState } from '@/action-menu/actions/states/forceRegisteredActionsMapComponentState'; +import { type ShouldBeRegisteredFunctionParams } from '@/action-menu/actions/types/ShouldBeRegisteredFunctionParams'; +import { getActionViewType } from '@/action-menu/actions/utils/getActionViewType'; +import { ActionMenuContext } from '@/action-menu/contexts/ActionMenuContext'; +import { objectPermissionsFamilySelector } from '@/auth/states/objectPermissionsFamilySelector'; +import { contextStoreCurrentViewTypeComponentState } from '@/context-store/states/contextStoreCurrentViewTypeComponentState'; +import { contextStoreNumberOfSelectedRecordsComponentState } from '@/context-store/states/contextStoreNumberOfSelectedRecordsComponentState'; +import { contextStoreTargetedRecordsRuleComponentState } from '@/context-store/states/contextStoreTargetedRecordsRuleComponentState'; +import { ContextStoreViewType } from '@/context-store/types/ContextStoreViewType'; +import { useFavorites } from '@/favorites/hooks/useFavorites'; +import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular'; +import { type ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem'; +import { useObjectPermissionsForObject } from '@/object-record/hooks/useObjectPermissionsForObject'; +import { hasAnySoftDeleteFilterOnViewComponentSelector } from '@/object-record/record-filter/states/hasAnySoftDeleteFilterOnView'; +import { recordStoreFamilyState } from '@/object-record/record-store/states/recordStoreFamilyState'; +import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue'; +import { useContext } from 'react'; +import { useRecoilCallback, useRecoilValue } from 'recoil'; +import { isDefined } from 'twenty-shared/utils'; + +export const useShouldActionBeRegisteredParams = ({ + objectMetadataItem, +}: { + objectMetadataItem?: ObjectMetadataItem; +}): ShouldBeRegisteredFunctionParams => { + const { sortedFavorites: favorites } = useFavorites(); + + const contextStoreTargetedRecordsRule = useRecoilComponentValue( + contextStoreTargetedRecordsRuleComponentState, + ); + + const recordId = + contextStoreTargetedRecordsRule.mode === 'selection' + ? contextStoreTargetedRecordsRule.selectedRecordIds[0] + : undefined; + + const foundFavorite = favorites?.find( + (favorite) => favorite.recordId === recordId, + ); + + const isFavorite = !!foundFavorite; + + const selectedRecord = + useRecoilValue(recordStoreFamilyState(recordId ?? '')) || undefined; + + const objectPermissions = useObjectPermissionsForObject( + objectMetadataItem?.id ?? '', + ); + + const isNoteOrTask = + objectMetadataItem?.nameSingular === CoreObjectNameSingular.Note || + objectMetadataItem?.nameSingular === CoreObjectNameSingular.Task; + + const { isInRightDrawer } = useContext(ActionMenuContext); + + const hasAnySoftDeleteFilterOnView = useRecoilComponentValue( + hasAnySoftDeleteFilterOnViewComponentSelector, + ); + + const isShowPage = + useRecoilComponentValue(contextStoreCurrentViewTypeComponentState) === + ContextStoreViewType.ShowPage; + + const numberOfSelectedRecords = useRecoilComponentValue( + contextStoreNumberOfSelectedRecordsComponentState, + ); + + const contextStoreCurrentViewType = useRecoilComponentValue( + contextStoreCurrentViewTypeComponentState, + ); + + const viewType = getActionViewType( + contextStoreCurrentViewType, + contextStoreTargetedRecordsRule, + ); + + const getObjectReadPermission = useRecoilCallback( + ({ snapshot }) => + (objectMetadataNameSingular: string) => { + return snapshot + .getLoadable( + objectPermissionsFamilySelector({ + objectNameSingular: objectMetadataNameSingular, + }), + ) + .getValue().canRead; + }, + [], + ); + + const getObjectWritePermission = useRecoilCallback( + ({ snapshot }) => + (objectMetadataNameSingular: string) => { + return snapshot + .getLoadable( + objectPermissionsFamilySelector({ + objectNameSingular: objectMetadataNameSingular, + }), + ) + .getValue().canUpdate; + }, + [], + ); + + const forceRegisteredActionsByKey = useRecoilValue( + forceRegisteredActionsByKeyState, + ); + + return { + objectMetadataItem, + isFavorite, + objectPermissions, + isNoteOrTask, + isInRightDrawer, + hasAnySoftDeleteFilterOnView, + isShowPage, + selectedRecord, + numberOfSelectedRecords, + viewType: viewType ?? undefined, + getTargetObjectReadPermission: getObjectReadPermission, + getTargetObjectWritePermission: getObjectWritePermission, + forceRegisteredActionsByKey, + }; +}; diff --git a/packages/twenty-front/src/modules/object-record/record-table/record-table-cell/components/RecordTableCellEditMode.tsx b/packages/twenty-front/src/modules/object-record/record-table/record-table-cell/components/RecordTableCellEditMode.tsx index 4c0752676a475..5ddf0e28fc02d 100644 --- a/packages/twenty-front/src/modules/object-record/record-table/record-table-cell/components/RecordTableCellEditMode.tsx +++ b/packages/twenty-front/src/modules/object-record/record-table/record-table-cell/components/RecordTableCellEditMode.tsx @@ -1,122 +1,131 @@ -import { useIsFieldInputOnly } from '@/object-record/record-field/ui/hooks/useIsFieldInputOnly'; -import { RecordFieldComponentInstanceContext } from '@/object-record/record-field/ui/states/contexts/RecordFieldComponentInstanceContext'; -import { recordFieldInputIsFieldInErrorComponentState } from '@/object-record/record-field/ui/states/recordFieldInputIsFieldInErrorComponentState'; -import { recordFieldInputLayoutDirectionComponentState } from '@/object-record/record-field/ui/states/recordFieldInputLayoutDirectionComponentState'; -import { recordFieldInputLayoutDirectionLoadingComponentState } from '@/object-record/record-field/ui/states/recordFieldInputLayoutDirectionLoadingComponentState'; -import { RecordTableCellContext } from '@/object-record/record-table/contexts/RecordTableCellContext'; -import { useFocusRecordTableCell } from '@/object-record/record-table/record-table-cell/hooks/useFocusRecordTableCell'; -import { OverlayContainer } from '@/ui/layout/overlay/components/OverlayContainer'; -import { useAvailableComponentInstanceIdOrThrow } from '@/ui/utilities/state/component-state/hooks/useAvailableComponentInstanceIdOrThrow'; -import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue'; -import { useSetRecoilComponentState } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentState'; -import styled from '@emotion/styled'; -import { - autoUpdate, - flip, - offset, - useFloating, - type MiddlewareState, -} from '@floating-ui/react'; -import { useContext, type ReactElement } from 'react'; - -const StyledEditableCellEditModeContainer = styled.div<{ - isFieldInputOnly: boolean; -}>` - align-items: center; - display: flex; - height: 100%; - position: absolute; - width: calc(100% + 2px); -`; - -const StyledInputModeOnlyContainer = styled.div` - align-items: center; - display: flex; - height: 100%; - overflow: hidden; - padding-left: 8px; - width: 100%; -`; - -export type RecordTableCellEditModeProps = { - children: ReactElement; -}; - -export const RecordTableCellEditMode = ({ - children, -}: RecordTableCellEditModeProps) => { - const isFieldInError = useRecoilComponentValue( - recordFieldInputIsFieldInErrorComponentState, - ); - - const recordFieldComponentInstanceId = useAvailableComponentInstanceIdOrThrow( - RecordFieldComponentInstanceContext, - ); - const setFieldInputLayoutDirection = useSetRecoilComponentState( - recordFieldInputLayoutDirectionComponentState, - recordFieldComponentInstanceId, - ); - - const setFieldInputLayoutDirectionLoading = useSetRecoilComponentState( - recordFieldInputLayoutDirectionLoadingComponentState, - recordFieldComponentInstanceId, - ); - - const setFieldInputLayoutDirectionMiddleware = { - name: 'middleware', - fn: async (state: MiddlewareState) => { - setFieldInputLayoutDirection( - state.placement.startsWith('bottom') ? 'downward' : 'upward', - ); - setFieldInputLayoutDirectionLoading(false); - return {}; - }, - }; - - const { refs, floatingStyles } = useFloating({ - placement: 'bottom-start', - middleware: [ - flip(), - offset({ - mainAxis: -33, - crossAxis: -3, - }), - setFieldInputLayoutDirectionMiddleware, - ], - - whileElementsMounted: autoUpdate, - }); - - const isFieldInputOnly = useIsFieldInputOnly(); - - const { cellPosition } = useContext(RecordTableCellContext); - - const { focusRecordTableCell } = useFocusRecordTableCell(); - - return ( - - {isFieldInputOnly ? ( - { - focusRecordTableCell(cellPosition); - }} - > - {children} - - ) : ( - - {children} - - )} - - ); -}; +import { useIsFieldInputOnly } from '@/object-record/record-field/ui/hooks/useIsFieldInputOnly'; +import { RecordFieldComponentInstanceContext } from '@/object-record/record-field/ui/states/contexts/RecordFieldComponentInstanceContext'; +import { recordFieldInputIsFieldInErrorComponentState } from '@/object-record/record-field/ui/states/recordFieldInputIsFieldInErrorComponentState'; +import { recordFieldInputLayoutDirectionComponentState } from '@/object-record/record-field/ui/states/recordFieldInputLayoutDirectionComponentState'; +import { recordFieldInputLayoutDirectionLoadingComponentState } from '@/object-record/record-field/ui/states/recordFieldInputLayoutDirectionLoadingComponentState'; +import { RecordTableCellContext } from '@/object-record/record-table/contexts/RecordTableCellContext'; +import { useFocusRecordTableCell } from '@/object-record/record-table/record-table-cell/hooks/useFocusRecordTableCell'; +import { getRecordTableColumnFieldWidthCSSVariableName } from '@/object-record/record-table/utils/getRecordTableColumnFieldWidthCSSVariableName'; +import { OverlayContainer } from '@/ui/layout/overlay/components/OverlayContainer'; +import { useAvailableComponentInstanceIdOrThrow } from '@/ui/utilities/state/component-state/hooks/useAvailableComponentInstanceIdOrThrow'; +import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue'; +import { useSetRecoilComponentState } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentState'; +import styled from '@emotion/styled'; +import { + autoUpdate, + flip, + offset, + useFloating, + type MiddlewareState, +} from '@floating-ui/react'; +import { useContext, type ReactElement } from 'react'; + +const StyledEditableCellEditModeContainer = styled.div<{ + isFieldInputOnly: boolean; +}>` + align-items: center; + display: flex; + height: 100%; + position: absolute; + width: calc(100% + 2px); +`; + +const StyledInputModeOnlyContainer = styled.div` + align-items: center; + display: flex; + height: 100%; + overflow: hidden; + padding-left: 8px; + width: 100%; +`; + +const StyledOverlayContainer = styled(OverlayContainer)<{ + columnIndex: number; +}>` + width: var(${({ columnIndex }) => + getRecordTableColumnFieldWidthCSSVariableName(columnIndex)}); +`; + +export type RecordTableCellEditModeProps = { + children: ReactElement; +}; + +export const RecordTableCellEditMode = ({ + children, +}: RecordTableCellEditModeProps) => { + const isFieldInError = useRecoilComponentValue( + recordFieldInputIsFieldInErrorComponentState, + ); + + const recordFieldComponentInstanceId = useAvailableComponentInstanceIdOrThrow( + RecordFieldComponentInstanceContext, + ); + const setFieldInputLayoutDirection = useSetRecoilComponentState( + recordFieldInputLayoutDirectionComponentState, + recordFieldComponentInstanceId, + ); + + const setFieldInputLayoutDirectionLoading = useSetRecoilComponentState( + recordFieldInputLayoutDirectionLoadingComponentState, + recordFieldComponentInstanceId, + ); + + const setFieldInputLayoutDirectionMiddleware = { + name: 'middleware', + fn: async (state: MiddlewareState) => { + setFieldInputLayoutDirection( + state.placement.startsWith('bottom') ? 'downward' : 'upward', + ); + setFieldInputLayoutDirectionLoading(false); + return {}; + }, + }; + + const { refs, floatingStyles } = useFloating({ + placement: 'bottom-start', + middleware: [ + flip(), + offset({ + mainAxis: -33, + crossAxis: -3, + }), + setFieldInputLayoutDirectionMiddleware, + ], + + whileElementsMounted: autoUpdate, + }); + + const isFieldInputOnly = useIsFieldInputOnly(); + + const { cellPosition } = useContext(RecordTableCellContext); + + const { focusRecordTableCell } = useFocusRecordTableCell(); + + return ( + + {isFieldInputOnly ? ( + { + focusRecordTableCell(cellPosition); + }} + > + {children} + + ) : ( + + {children} + + )} + + ); +};