Migrate dropdown to jotai (#18063)

Here we go again
This commit is contained in:
Charles Bochet
2026-02-19 00:58:26 +01:00
committed by GitHub
parent 66deb8be63
commit 36c2b0e23b
54 changed files with 599 additions and 370 deletions
@@ -10,7 +10,7 @@ import { Dropdown } from '@/ui/layout/dropdown/components/Dropdown';
import { StyledHeaderDropdownButton } from '@/ui/layout/dropdown/components/StyledHeaderDropdownButton';
import { DROPDOWN_OFFSET_Y } from '@/ui/layout/dropdown/constants/DropdownOffsetY';
import { isDropdownOpenComponentState } from '@/ui/layout/dropdown/states/isDropdownOpenComponentState';
import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue';
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilComponentValueV2';
import { type ViewType } from '@/views/types/ViewType';
import { Trans } from '@lingui/react/macro';
@@ -28,7 +28,7 @@ export const ObjectOptionsDropdown = ({
const { currentContentId, handleContentChange, handleResetContent } =
useDropdownContextCurrentContentId<ObjectOptionsContentId>();
const isDropdownOpen = useRecoilComponentValue(
const isDropdownOpen = useRecoilComponentValueV2(
isDropdownOpenComponentState,
OBJECT_OPTIONS_DROPDOWN_ID,
);
@@ -27,6 +27,7 @@ import { SelectableListItem } from '@/ui/layout/selectable-list/components/Selec
import { selectedItemIdComponentState } from '@/ui/layout/selectable-list/states/selectedItemIdComponentState';
import { useRecoilComponentState } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentState';
import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue';
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilComponentValueV2';
import { useSetRecoilComponentState } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentState';
import { Trans, useLingui } from '@lingui/react/macro';
import { useRecoilValue } from 'recoil';
@@ -138,7 +139,7 @@ export const ObjectSortDropdownButton = () => {
setIsRecordSortDirectionMenuUnfolded(false);
};
const isDropdownOpen = useRecoilComponentValue(
const isDropdownOpen = useRecoilComponentValueV2(
isDropdownOpenComponentState,
OBJECT_SORT_DROPDOWN_ID,
);
@@ -1,6 +1,6 @@
import { StyledHeaderDropdownButton } from '@/ui/layout/dropdown/components/StyledHeaderDropdownButton';
import { isDropdownOpenComponentState } from '@/ui/layout/dropdown/states/isDropdownOpenComponentState';
import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue';
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilComponentValueV2';
import styled from '@emotion/styled';
import { type Nullable } from 'twenty-shared/types';
import { Tag } from 'twenty-ui/components';
@@ -23,7 +23,7 @@ export const RecordBoardColumnHeaderAggregateDropdownButton = ({
value?: Nullable<string | number>;
tooltip?: Nullable<string>;
}) => {
const isDropdownOpen = useRecoilComponentValue(
const isDropdownOpen = useRecoilComponentValueV2(
isDropdownOpenComponentState,
dropdownId,
);
@@ -12,7 +12,7 @@ import { type FieldMorphRelationMetadata } from '@/object-record/record-field/ui
import { getRecordFieldCardRelationPickerDropdownId } from '@/object-record/record-show/utils/getRecordFieldCardRelationPickerDropdownId';
import { isDropdownOpenComponentState } from '@/ui/layout/dropdown/states/isDropdownOpenComponentState';
import { useIsMobile } from '@/ui/utilities/responsive/hooks/useIsMobile';
import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue';
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilComponentValueV2';
import { RecordDetailMorphRelationSectionDropdown } from '@/object-record/record-field-list/record-detail-section/relation/components/RecordDetailMorphRelationSectionDropdown';
import { RecordDetailRelationRecordsList } from '@/object-record/record-field-list/record-detail-section/relation/components/RecordDetailRelationRecordsList';
@@ -49,7 +49,7 @@ export const RecordDetailMorphRelationSection = ({
instanceId: scopeInstanceId,
});
const isDropdownOpen = useRecoilComponentValue(
const isDropdownOpen = useRecoilComponentValueV2(
isDropdownOpenComponentState,
dropdownId,
);
@@ -16,7 +16,7 @@ import { getRecordFieldCardRelationPickerDropdownId } from '@/object-record/reco
import { Dropdown } from '@/ui/layout/dropdown/components/Dropdown';
import { useCloseDropdown } from '@/ui/layout/dropdown/hooks/useCloseDropdown';
import { dropdownPlacementComponentState } from '@/ui/layout/dropdown/states/dropdownPlacementComponentState';
import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue';
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilComponentValueV2';
import { useSetRecoilComponentState } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentState';
import { FieldMetadataType } from 'twenty-shared/types';
import { isDefined } from 'twenty-shared/utils';
@@ -65,7 +65,7 @@ export const RecordDetailMorphRelationSectionDropdownManyToOne = ({
const { closeDropdown } = useCloseDropdown();
const dropdownPlacement = useRecoilComponentValue(
const dropdownPlacement = useRecoilComponentValueV2(
dropdownPlacementComponentState,
dropdownId,
);
@@ -18,7 +18,7 @@ import { Dropdown } from '@/ui/layout/dropdown/components/Dropdown';
import { GenericDropdownContentWidth } from '@/ui/layout/dropdown/constants/GenericDropdownContentWidth';
import { useCloseDropdown } from '@/ui/layout/dropdown/hooks/useCloseDropdown';
import { dropdownPlacementComponentState } from '@/ui/layout/dropdown/states/dropdownPlacementComponentState';
import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue';
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilComponentValueV2';
import { useSetRecoilComponentState } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentState';
import { FieldMetadataType } from 'twenty-shared/types';
import { CustomError, isDefined } from 'twenty-shared/utils';
@@ -68,7 +68,7 @@ export const RecordDetailMorphRelationSectionDropdownOneToMany = ({
const { closeDropdown } = useCloseDropdown();
const dropdownPlacement = useRecoilComponentValue(
const dropdownPlacement = useRecoilComponentValueV2(
dropdownPlacementComponentState,
dropdownId,
);
@@ -28,7 +28,7 @@ import { useCloseDropdown } from '@/ui/layout/dropdown/hooks/useCloseDropdown';
import { isDropdownOpenComponentState } from '@/ui/layout/dropdown/states/isDropdownOpenComponentState';
import { ConfirmationModal } from '@/ui/layout/modal/components/ConfirmationModal';
import { useModal } from '@/ui/layout/modal/hooks/useModal';
import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue';
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilComponentValueV2';
import { useSetRecoilComponentState } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentState';
import { t } from '@lingui/core/macro';
import { Trans } from '@lingui/react/macro';
@@ -152,7 +152,7 @@ export const RecordDetailRelationRecordsListItem = ({
const dropdownInstanceId = `record-field-card-menu:${scopeInstanceId}:${relationFieldMetadataId}:${relationRecord.id}`;
const { closeDropdown } = useCloseDropdown();
const isDropdownOpen = useRecoilComponentValue(
const isDropdownOpen = useRecoilComponentValueV2(
isDropdownOpenComponentState,
dropdownInstanceId,
);
@@ -21,7 +21,7 @@ import { AggregateOperations } from '@/object-record/record-table/constants/Aggr
import { type ObjectRecord } from '@/object-record/types/ObjectRecord';
import { isDropdownOpenComponentState } from '@/ui/layout/dropdown/states/isDropdownOpenComponentState';
import { useIsMobile } from '@/ui/utilities/responsive/hooks/useIsMobile';
import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue';
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilComponentValueV2';
import { coreIndexViewIdFromObjectMetadataItemFamilySelector } from '@/views/states/selectors/coreIndexViewIdFromObjectMetadataItemFamilySelector';
import { useLingui } from '@lingui/react/macro';
import {
@@ -100,7 +100,7 @@ export const RecordDetailRelationSection = ({
instanceId: scopeInstanceId,
});
const isDropdownOpen = useRecoilComponentValue(
const isDropdownOpen = useRecoilComponentValueV2(
isDropdownOpenComponentState,
dropdownId,
);
@@ -21,7 +21,7 @@ import { type ObjectRecord } from '@/object-record/types/ObjectRecord';
import { Dropdown } from '@/ui/layout/dropdown/components/Dropdown';
import { useCloseDropdown } from '@/ui/layout/dropdown/hooks/useCloseDropdown';
import { dropdownPlacementComponentState } from '@/ui/layout/dropdown/states/dropdownPlacementComponentState';
import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue';
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilComponentValueV2';
import { useSetRecoilComponentState } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentState';
import { CustomError, isDefined } from 'twenty-shared/utils';
import { IconPlus } from 'twenty-ui/display';
@@ -85,7 +85,7 @@ export const RecordDetailRelationSectionDropdownToMany = ({
const { closeDropdown } = useCloseDropdown();
const dropdownPlacement = useRecoilComponentValue(
const dropdownPlacement = useRecoilComponentValueV2(
dropdownPlacementComponentState,
dropdownId,
);
@@ -18,7 +18,7 @@ import { type ObjectRecord } from '@/object-record/types/ObjectRecord';
import { Dropdown } from '@/ui/layout/dropdown/components/Dropdown';
import { useCloseDropdown } from '@/ui/layout/dropdown/hooks/useCloseDropdown';
import { dropdownPlacementComponentState } from '@/ui/layout/dropdown/states/dropdownPlacementComponentState';
import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue';
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilComponentValueV2';
import { useSetRecoilComponentState } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentState';
import { useObjectMetadataItems } from '@/object-metadata/hooks/useObjectMetadataItems';
@@ -96,7 +96,7 @@ export const RecordDetailRelationSectionDropdownToOne = ({
const { closeDropdown } = useCloseDropdown();
const dropdownPlacement = useRecoilComponentValue(
const dropdownPlacement = useRecoilComponentValueV2(
dropdownPlacementComponentState,
dropdownId,
);
@@ -20,7 +20,7 @@ import { isDropdownOpenComponentState } from '@/ui/layout/dropdown/states/isDrop
import { usePushFocusItemToFocusStack } from '@/ui/utilities/focus/hooks/usePushFocusItemToFocusStack';
import { useRemoveFocusItemFromFocusStackById } from '@/ui/utilities/focus/hooks/useRemoveFocusItemFromFocusStackById';
import { FocusComponentType } from '@/ui/utilities/focus/types/FocusComponentType';
import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue';
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilComponentValueV2';
import { isStandaloneVariableString } from '@/workflow/utils/isStandaloneVariableString';
import { useTheme } from '@emotion/react';
import styled from '@emotion/styled';
@@ -128,7 +128,7 @@ export const FormArrayFieldInput = ({
const containerRef = useRef<HTMLDivElement>(null);
const dropdownId = `dropdown-${instanceId}`;
const isDropdownOpen = useRecoilComponentValue(
const isDropdownOpen = useRecoilComponentValueV2(
isDropdownOpenComponentState,
dropdownId,
);
@@ -4,7 +4,7 @@ import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/Drop
import { useCloseDropdown } from '@/ui/layout/dropdown/hooks/useCloseDropdown';
import { isDropdownOpenComponentState } from '@/ui/layout/dropdown/states/isDropdownOpenComponentState';
import { MenuItemWithOptionDropdown } from '@/ui/navigation/menu-item/components/MenuItemWithOptionDropdown';
import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue';
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilComponentValueV2';
import React, { useState } from 'react';
import {
IconBookmark,
@@ -44,7 +44,7 @@ export const MultiItemFieldMenuItem = <T,>({
}: MultiItemFieldMenuItemProps<T>) => {
const [isHovered, setIsHovered] = useState(false);
const { closeDropdown } = useCloseDropdown();
const isDropdownOpen = useRecoilComponentValue(
const isDropdownOpen = useRecoilComponentValueV2(
isDropdownOpenComponentState,
dropdownId,
);
@@ -19,7 +19,7 @@ import { usePersistFieldFromFieldInputContext } from '@/object-record/record-fie
import { getDropdownFocusIdForRecordField } from '@/object-record/utils/getDropdownFocusIdForRecordField';
import { useGoBackToPreviousDropdownFocusId } from '@/ui/layout/dropdown/hooks/useGoBackToPreviousDropdownFocusId';
import { activeDropdownFocusIdState } from '@/ui/layout/dropdown/states/activeDropdownFocusIdState';
import { useRecoilCallback } from 'recoil';
import { jotaiStore } from '@/ui/utilities/state/jotai/jotaiStore';
import { useIcons } from 'twenty-ui/display';
import { RecordInlineCellContainer } from './RecordInlineCellContainer';
import {
@@ -133,37 +133,36 @@ export const RecordInlineCell = ({
closeInlineCell();
};
const handleClickOutside = useRecoilCallback(
({ snapshot }) =>
({
event,
newValue,
skipPersist,
}: Parameters<FieldInputClickOutsideEvent>[0]) => {
const currentDropdownFocusId = snapshot
.getLoadable(activeDropdownFocusIdState)
.getValue();
const handleClickOutside = useCallback(
({
event,
newValue,
skipPersist,
}: Parameters<FieldInputClickOutsideEvent>[0]) => {
const currentDropdownFocusId = jotaiStore.get(
activeDropdownFocusIdState.atom,
);
const expectedDropdownFocusId = getDropdownFocusIdForRecordField({
recordId,
fieldMetadataId: fieldDefinition.fieldMetadataId,
componentType: 'inline-cell',
instanceId: scopeInstanceId,
});
const expectedDropdownFocusId = getDropdownFocusIdForRecordField({
recordId,
fieldMetadataId: fieldDefinition.fieldMetadataId,
componentType: 'inline-cell',
instanceId: scopeInstanceId,
});
if (currentDropdownFocusId !== expectedDropdownFocusId) {
return;
}
if (currentDropdownFocusId !== expectedDropdownFocusId) {
return;
}
event?.preventDefault();
event?.stopImmediatePropagation();
event?.preventDefault();
event?.stopImmediatePropagation();
if (skipPersist !== true) {
persistFieldFromFieldInputContext(newValue);
}
if (skipPersist !== true) {
persistFieldFromFieldInputContext(newValue);
}
closeInlineCell();
},
closeInlineCell();
},
[
recordId,
fieldDefinition.fieldMetadataId,
@@ -6,6 +6,7 @@ import { RecordTableColumnAggregateFooterValue } from '@/object-record/record-ta
import { hasAggregateOperationForViewFieldFamilySelector } from '@/object-record/record-table/record-table-footer/states/hasAggregateOperationForViewFieldFamilySelector';
import { isDropdownOpenComponentState } from '@/ui/layout/dropdown/states/isDropdownOpenComponentState';
import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue';
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilComponentValueV2';
import { useTheme } from '@emotion/react';
import styled from '@emotion/styled';
import { useContext, useState } from 'react';
@@ -57,7 +58,7 @@ export const RecordTableColumnAggregateFooterValueCell = ({
}) => {
const [isHovered, setIsHovered] = useState(false);
const isDropdownOpen = useRecoilComponentValue(
const isDropdownOpen = useRecoilComponentValueV2(
isDropdownOpenComponentState,
dropdownId,
);