diff --git a/packages/twenty-front/src/modules/ui/layout/dropdown/components/DropdownMenuItemsContainer.tsx b/packages/twenty-front/src/modules/ui/layout/dropdown/components/DropdownMenuItemsContainer.tsx index b0f4ac40e..0eeeebfff 100644 --- a/packages/twenty-front/src/modules/ui/layout/dropdown/components/DropdownMenuItemsContainer.tsx +++ b/packages/twenty-front/src/modules/ui/layout/dropdown/components/DropdownMenuItemsContainer.tsx @@ -45,11 +45,17 @@ export const DropdownMenuItemsContainer = ({ }) => { return ( - + {hasMaxHeight ? ( + + + {children} + + + ) : ( {children} - + )} ); }; diff --git a/packages/twenty-front/src/modules/views/view-picker/components/ViewPickerCreateOrEditContent.tsx b/packages/twenty-front/src/modules/views/view-picker/components/ViewPickerCreateOrEditContent.tsx index 8d6ca8bff..127e64400 100644 --- a/packages/twenty-front/src/modules/views/view-picker/components/ViewPickerCreateOrEditContent.tsx +++ b/packages/twenty-front/src/modules/views/view-picker/components/ViewPickerCreateOrEditContent.tsx @@ -1,5 +1,5 @@ import styled from '@emotion/styled'; -import { useRecoilState, useRecoilValue } from 'recoil'; +import { useRecoilState, useRecoilValue, useSetRecoilState } from 'recoil'; import { Key } from 'ts-key-enum'; import { IconChevronLeft, IconLayoutKanban, IconTable, IconX } from 'twenty-ui'; @@ -57,6 +57,7 @@ export const ViewPickerCreateOrEditContent = () => { viewPickerIsPersistingState, viewPickerKanbanFieldMetadataIdState, viewPickerTypeState, + viewPickerIsDirtyState, } = useViewPickerStates(); const [viewPickerInputName, setViewPickerInputName] = useRecoilState( @@ -66,6 +67,7 @@ export const ViewPickerCreateOrEditContent = () => { viewPickerSelectedIconState, ); const viewPickerIsPersisting = useRecoilValue(viewPickerIsPersistingState); + const setViewPickerIsDirty = useSetRecoilState(viewPickerIsDirtyState); const [viewPickerKanbanFieldMetadataId, setViewPickerKanbanFieldMetadataId] = useRecoilState(viewPickerKanbanFieldMetadataIdState); @@ -80,16 +82,13 @@ export const ViewPickerCreateOrEditContent = () => { useScopedHotkeys( Key.Enter, async () => { + if (viewPickerIsPersisting) { + return; + } if (viewPickerMode === 'create') { - if (viewPickerIsPersisting) { - return; - } await handleCreate(); } if (viewPickerMode === 'edit') { - if (viewPickerIsPersisting) { - return; - } await handleUpdate(); } }, @@ -97,6 +96,7 @@ export const ViewPickerCreateOrEditContent = () => { ); const onIconChange = ({ iconKey }: { iconKey: string }) => { + setViewPickerIsDirty(true); setViewPickerSelectedIcon(iconKey); }; @@ -128,7 +128,10 @@ export const ViewPickerCreateOrEditContent = () => { /> setViewPickerInputName(event.target.value)} + onChange={(event) => { + setViewPickerIsDirty(true); + setViewPickerInputName(event.target.value); + }} autoFocus /> @@ -139,7 +142,10 @@ export const ViewPickerCreateOrEditContent = () => { label="View type" fullWidth value={viewPickerType} - onChange={(value) => setViewPickerType(value)} + onChange={(value) => { + setViewPickerIsDirty(true); + setViewPickerType(value); + }} options={[ { value: ViewType.Table, label: 'Table', Icon: IconTable }, { diff --git a/packages/twenty-front/src/modules/views/view-picker/components/ViewPickerCreateOrEditContentEffect.tsx b/packages/twenty-front/src/modules/views/view-picker/components/ViewPickerCreateOrEditContentEffect.tsx index d60ecd153..45c143222 100644 --- a/packages/twenty-front/src/modules/views/view-picker/components/ViewPickerCreateOrEditContentEffect.tsx +++ b/packages/twenty-front/src/modules/views/view-picker/components/ViewPickerCreateOrEditContentEffect.tsx @@ -14,6 +14,7 @@ export const ViewPickerCreateOrEditContentEffect = () => { viewPickerIsPersistingState, viewPickerKanbanFieldMetadataIdState, viewPickerTypeState, + viewPickerIsDirtyState, } = useViewPickerStates(); const setViewPickerSelectedIcon = useSetRecoilState( @@ -30,6 +31,8 @@ export const ViewPickerCreateOrEditContentEffect = () => { viewPickerReferenceViewIdState, ); + const viewPickerIsDirty = useRecoilValue(viewPickerIsDirtyState); + const viewPickerIsPersisting = useRecoilValue(viewPickerIsPersistingState); const { viewsOnCurrentObject } = useGetCurrentView(); @@ -40,7 +43,11 @@ export const ViewPickerCreateOrEditContentEffect = () => { const { availableFieldsForKanban } = useGetAvailableFieldsForKanban(); useEffect(() => { - if (isDefined(referenceView) && !viewPickerIsPersisting) { + if ( + isDefined(referenceView) && + !viewPickerIsPersisting && + !viewPickerIsDirty + ) { setViewPickerSelectedIcon(referenceView.icon); setViewPickerInputName(referenceView.name); setViewPickerKanbanFieldMetadataId(referenceView.kanbanFieldMetadataId); @@ -53,6 +60,7 @@ export const ViewPickerCreateOrEditContentEffect = () => { setViewPickerSelectedIcon, setViewPickerType, viewPickerIsPersisting, + viewPickerIsDirty, ]); useEffect(() => { diff --git a/packages/twenty-front/src/modules/views/view-picker/hooks/useViewPickerPersistView.ts b/packages/twenty-front/src/modules/views/view-picker/hooks/useViewPickerPersistView.ts index 42ef7c2db..ba9c00398 100644 --- a/packages/twenty-front/src/modules/views/view-picker/hooks/useViewPickerPersistView.ts +++ b/packages/twenty-front/src/modules/views/view-picker/hooks/useViewPickerPersistView.ts @@ -15,6 +15,7 @@ export const useViewPickerPersistView = () => { viewPickerReferenceViewIdState, viewPickerKanbanFieldMetadataIdState, viewPickerTypeState, + viewPickerIsDirtyState, } = useViewPickerStates(); const { createView, selectView, removeView, updateView } = useHandleViews(); @@ -35,6 +36,7 @@ export const useViewPickerPersistView = () => { ); const id = v4(); set(viewPickerIsPersistingState, true); + set(viewPickerIsDirtyState, false); await createView({ id, name, @@ -50,6 +52,7 @@ export const useViewPickerPersistView = () => { createView, selectView, viewPickerInputNameState, + viewPickerIsDirtyState, viewPickerIsPersistingState, viewPickerKanbanFieldMetadataIdState, viewPickerSelectedIconState, @@ -62,6 +65,7 @@ export const useViewPickerPersistView = () => { async () => { set(viewPickerIsPersistingState, true); closeAndResetViewPicker(); + set(viewPickerIsDirtyState, false); const viewPickerReferenceViewId = getSnapshotValue( snapshot, viewPickerReferenceViewIdState, @@ -78,6 +82,7 @@ export const useViewPickerPersistView = () => { closeAndResetViewPicker, removeView, selectView, + viewPickerIsDirtyState, viewPickerIsPersistingState, viewPickerReferenceViewIdState, viewsOnCurrentObject, @@ -88,6 +93,7 @@ export const useViewPickerPersistView = () => { ({ set, snapshot }) => async () => { set(viewPickerIsPersistingState, true); + set(viewPickerIsDirtyState, false); closeAndResetViewPicker(); const viewPickerReferenceViewId = getSnapshotValue( @@ -112,12 +118,13 @@ export const useViewPickerPersistView = () => { }, [ viewPickerIsPersistingState, + viewPickerIsDirtyState, + closeAndResetViewPicker, viewPickerReferenceViewIdState, viewPickerInputNameState, viewPickerSelectedIconState, updateView, selectView, - closeAndResetViewPicker, ], ); diff --git a/packages/twenty-front/src/modules/views/view-picker/hooks/useViewPickerStates.ts b/packages/twenty-front/src/modules/views/view-picker/hooks/useViewPickerStates.ts index a95274d41..58bfd987c 100644 --- a/packages/twenty-front/src/modules/views/view-picker/hooks/useViewPickerStates.ts +++ b/packages/twenty-front/src/modules/views/view-picker/hooks/useViewPickerStates.ts @@ -1,6 +1,7 @@ import { useAvailableScopeIdOrThrow } from '@/ui/utilities/recoil-scope/scopes-internal/hooks/useAvailableScopeId'; import { extractComponentState } from '@/ui/utilities/state/component-state/utils/extractComponentState'; import { viewPickerInputNameComponentState } from '@/views/view-picker/states/viewPickerInputNameComponentState'; +import { viewPickerIsDirtyComponentState } from '@/views/view-picker/states/viewPickerIsDirtyComponentState'; import { viewPickerIsPersistingComponentState } from '@/views/view-picker/states/viewPickerIsPersistingComponentState'; import { viewPickerKanbanFieldMetadataIdComponentState } from '@/views/view-picker/states/viewPickerKanbanFieldMetadataIdComponentState'; import { viewPickerModeComponentState } from '@/views/view-picker/states/viewPickerModeComponentState'; @@ -46,5 +47,9 @@ export const useViewPickerStates = (viewComponentId?: string) => { viewPickerTypeComponentState, componentId, ), + viewPickerIsDirtyState: extractComponentState( + viewPickerIsDirtyComponentState, + componentId, + ), }; }; diff --git a/packages/twenty-front/src/modules/views/view-picker/states/viewPickerIsDirtyComponentState.ts b/packages/twenty-front/src/modules/views/view-picker/states/viewPickerIsDirtyComponentState.ts new file mode 100644 index 000000000..a732ae062 --- /dev/null +++ b/packages/twenty-front/src/modules/views/view-picker/states/viewPickerIsDirtyComponentState.ts @@ -0,0 +1,6 @@ +import { createComponentState } from '@/ui/utilities/state/component-state/utils/createComponentState'; + +export const viewPickerIsDirtyComponentState = createComponentState({ + key: 'viewPickerIsDirtyComponentState', + defaultValue: false, +});