@@ -13,7 +13,7 @@ import { SELECT_COUNTRY_DROPDOWN_ID } from '@/ui/input/components/internal/count
|
||||
import { Dropdown } from '@/ui/layout/dropdown/components/Dropdown';
|
||||
import { activeDropdownFocusIdState } from '@/ui/layout/dropdown/states/activeDropdownFocusIdState';
|
||||
import { useListenClickOutside } from '@/ui/utilities/pointer-event/hooks/useListenClickOutside';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
import { useRecoilValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilValueV2';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { MOBILE_VIEWPORT } from 'twenty-ui/theme';
|
||||
import { v4 } from 'uuid';
|
||||
@@ -236,7 +236,7 @@ export const AddressInput = ({
|
||||
onShiftTab: handleShiftTab,
|
||||
});
|
||||
|
||||
const activeDropdownFocusId = useRecoilValue(activeDropdownFocusIdState);
|
||||
const activeDropdownFocusId = useRecoilValueV2(activeDropdownFocusIdState);
|
||||
|
||||
useListenClickOutside({
|
||||
refs: [wrapperRef],
|
||||
|
||||
+2
-2
@@ -10,7 +10,7 @@ import { PhoneCountryPickerDropdownSelect } from './PhoneCountryPickerDropdownSe
|
||||
import { PHONE_COUNTRY_CODE_PICKER_DROPDOWN_ID } from '@/ui/input/components/internal/phone/constants/PhoneCountryCodePickerDropdownId';
|
||||
import { useCloseDropdown } from '@/ui/layout/dropdown/hooks/useCloseDropdown';
|
||||
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 'react-phone-number-input/style.css';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { IconChevronDown, IconWorld } from 'twenty-ui/display';
|
||||
@@ -78,7 +78,7 @@ export const PhoneCountryPickerDropdownButton = ({
|
||||
|
||||
const [selectedCountry, setSelectedCountry] = useState<Country>();
|
||||
|
||||
const isDropdownOpen = useRecoilComponentValue(
|
||||
const isDropdownOpen = useRecoilComponentValueV2(
|
||||
isDropdownOpenComponentState,
|
||||
PHONE_COUNTRY_CODE_PICKER_DROPDOWN_ID,
|
||||
);
|
||||
|
||||
@@ -13,8 +13,8 @@ import { dropdownYPositionComponentState } from '@/ui/layout/dropdown/states/int
|
||||
import { isDropdownOpenComponentState } from '@/ui/layout/dropdown/states/isDropdownOpenComponentState';
|
||||
import { type DropdownOffset } from '@/ui/layout/dropdown/types/DropdownOffset';
|
||||
import { type GlobalHotkeysConfig } from '@/ui/utilities/hotkey/types/GlobalHotkeysConfig';
|
||||
import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue';
|
||||
import { useSetRecoilComponentState } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentState';
|
||||
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilComponentValueV2';
|
||||
import { useSetRecoilComponentStateV2 } from '@/ui/utilities/state/jotai/hooks/useSetRecoilComponentStateV2';
|
||||
import styled from '@emotion/styled';
|
||||
import {
|
||||
type Placement,
|
||||
@@ -24,10 +24,9 @@ import {
|
||||
size,
|
||||
useFloating,
|
||||
} from '@floating-ui/react';
|
||||
import { type MouseEvent, type ReactNode } from 'react';
|
||||
import { type MouseEvent, type ReactNode, useCallback } from 'react';
|
||||
import { flushSync } from 'react-dom';
|
||||
import { type Keys } from 'react-hotkeys-hook';
|
||||
import { useRecoilCallback } from 'recoil';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { useIsMobile } from 'twenty-ui/utilities';
|
||||
|
||||
@@ -89,7 +88,7 @@ export const Dropdown = ({
|
||||
disableClickForClickableComponent = false,
|
||||
middlewareBoundaryPadding = {},
|
||||
}: DropdownProps) => {
|
||||
const isDropdownOpen = useRecoilComponentValue(
|
||||
const isDropdownOpen = useRecoilComponentValueV2(
|
||||
isDropdownOpenComponentState,
|
||||
dropdownId,
|
||||
);
|
||||
@@ -108,17 +107,17 @@ export const Dropdown = ({
|
||||
]
|
||||
: [];
|
||||
|
||||
const setDropdownMaxHeight = useSetRecoilComponentState(
|
||||
const setDropdownMaxHeight = useSetRecoilComponentStateV2(
|
||||
dropdownMaxHeightComponentState,
|
||||
dropdownId,
|
||||
);
|
||||
|
||||
const setDropdownMaxWidth = useSetRecoilComponentState(
|
||||
const setDropdownMaxWidth = useSetRecoilComponentStateV2(
|
||||
dropdownMaxWidthComponentState,
|
||||
dropdownId,
|
||||
);
|
||||
|
||||
const setDropdownYPosition = useSetRecoilComponentState(
|
||||
const setDropdownYPosition = useSetRecoilComponentStateV2(
|
||||
dropdownYPositionComponentState,
|
||||
dropdownId,
|
||||
);
|
||||
@@ -173,8 +172,8 @@ export const Dropdown = ({
|
||||
strategy: dropdownStrategy,
|
||||
});
|
||||
|
||||
const handleClickableComponentClick = useRecoilCallback(
|
||||
() => async (event: MouseEvent) => {
|
||||
const handleClickableComponentClick = useCallback(
|
||||
async (event: MouseEvent) => {
|
||||
if (disableClickForClickableComponent) return;
|
||||
event.stopPropagation();
|
||||
event.preventDefault();
|
||||
|
||||
+4
-2
@@ -1,7 +1,7 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
|
||||
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';
|
||||
|
||||
export const DropdownOnToggleEffect = ({
|
||||
onDropdownClose,
|
||||
@@ -10,7 +10,9 @@ export const DropdownOnToggleEffect = ({
|
||||
onDropdownClose?: () => void;
|
||||
onDropdownOpen?: () => void;
|
||||
}) => {
|
||||
const isDropdownOpen = useRecoilComponentValue(isDropdownOpenComponentState);
|
||||
const isDropdownOpen = useRecoilComponentValueV2(
|
||||
isDropdownOpenComponentState,
|
||||
);
|
||||
|
||||
const [currentIsDropdownOpen, setCurrentIsDropdownOpen] =
|
||||
useState(isDropdownOpen);
|
||||
|
||||
+10
-8
@@ -11,8 +11,9 @@ import { HotkeyEffect } from '@/ui/utilities/hotkey/components/HotkeyEffect';
|
||||
import { useHotkeysOnFocusedElement } from '@/ui/utilities/hotkey/hooks/useHotkeysOnFocusedElement';
|
||||
import { ClickOutsideListenerContext } from '@/ui/utilities/pointer-event/contexts/ClickOutsideListenerContext';
|
||||
import { useListenClickOutside } from '@/ui/utilities/pointer-event/hooks/useListenClickOutside';
|
||||
import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue';
|
||||
import { useSetRecoilComponentState } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentState';
|
||||
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilComponentValueV2';
|
||||
import { useRecoilValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilValueV2';
|
||||
import { useSetRecoilComponentStateV2 } from '@/ui/utilities/state/jotai/hooks/useSetRecoilComponentStateV2';
|
||||
import styled from '@emotion/styled';
|
||||
import {
|
||||
FloatingPortal,
|
||||
@@ -21,7 +22,6 @@ import {
|
||||
} from '@floating-ui/react';
|
||||
import { useContext, useEffect } from 'react';
|
||||
import { type Keys } from 'react-hotkeys-hook';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
import { Key } from 'ts-key-enum';
|
||||
|
||||
export const StyledDropdownContentContainer = styled.div<{
|
||||
@@ -70,23 +70,25 @@ export const DropdownInternalContainer = ({
|
||||
excludedClickOutsideIds,
|
||||
isDropdownInModal = false,
|
||||
}: DropdownInternalContainerProps) => {
|
||||
const isDropdownOpen = useRecoilComponentValue(isDropdownOpenComponentState);
|
||||
const isDropdownOpen = useRecoilComponentValueV2(
|
||||
isDropdownOpenComponentState,
|
||||
);
|
||||
|
||||
const { closeDropdown } = useCloseDropdown();
|
||||
|
||||
const activeDropdownFocusId = useRecoilValue(activeDropdownFocusIdState);
|
||||
const activeDropdownFocusId = useRecoilValueV2(activeDropdownFocusIdState);
|
||||
|
||||
const dropdownMaxHeight = useRecoilComponentValue(
|
||||
const dropdownMaxHeight = useRecoilComponentValueV2(
|
||||
dropdownMaxHeightComponentState,
|
||||
dropdownId,
|
||||
);
|
||||
|
||||
const dropdownMaxWidth = useRecoilComponentValue(
|
||||
const dropdownMaxWidth = useRecoilComponentValueV2(
|
||||
dropdownMaxWidthComponentState,
|
||||
dropdownId,
|
||||
);
|
||||
|
||||
const setDropdownPlacement = useSetRecoilComponentState(
|
||||
const setDropdownPlacement = useSetRecoilComponentStateV2(
|
||||
dropdownPlacementComponentState,
|
||||
dropdownId,
|
||||
);
|
||||
|
||||
+20
-9
@@ -1,4 +1,5 @@
|
||||
import { renderHook } from '@testing-library/react';
|
||||
import { Provider as JotaiProvider } from 'jotai';
|
||||
import { act } from 'react';
|
||||
import { RecoilRoot } from 'recoil';
|
||||
|
||||
@@ -6,27 +7,37 @@ import { DropdownComponentInstanceContext } from '@/ui/layout/dropdown/contexts/
|
||||
import { useCloseAnyOpenDropdown } from '@/ui/layout/dropdown/hooks/useCloseAnyOpenDropdown';
|
||||
import { useOpenDropdown } from '@/ui/layout/dropdown/hooks/useOpenDropdown';
|
||||
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 { jotaiStore } from '@/ui/utilities/state/jotai/jotaiStore';
|
||||
|
||||
const dropdownId = 'test-dropdown-id';
|
||||
|
||||
const Wrapper = ({ children }: { children: React.ReactNode }) => {
|
||||
return (
|
||||
<RecoilRoot>
|
||||
<DropdownComponentInstanceContext.Provider
|
||||
value={{ instanceId: dropdownId }}
|
||||
>
|
||||
{children}
|
||||
</DropdownComponentInstanceContext.Provider>
|
||||
</RecoilRoot>
|
||||
<JotaiProvider store={jotaiStore}>
|
||||
<RecoilRoot>
|
||||
<DropdownComponentInstanceContext.Provider
|
||||
value={{ instanceId: dropdownId }}
|
||||
>
|
||||
{children}
|
||||
</DropdownComponentInstanceContext.Provider>
|
||||
</RecoilRoot>
|
||||
</JotaiProvider>
|
||||
);
|
||||
};
|
||||
|
||||
describe('useCloseAnyOpenDropdown', () => {
|
||||
beforeEach(() => {
|
||||
jotaiStore.set(
|
||||
isDropdownOpenComponentState.atomFamily({ instanceId: dropdownId }),
|
||||
false,
|
||||
);
|
||||
});
|
||||
|
||||
it('should open dropdown and then close it with closeAnyOpenDropdown', async () => {
|
||||
const { result } = renderHook(
|
||||
() => {
|
||||
const isDropdownOpen = useRecoilComponentValue(
|
||||
const isDropdownOpen = useRecoilComponentValueV2(
|
||||
isDropdownOpenComponentState,
|
||||
dropdownId,
|
||||
);
|
||||
|
||||
+32
-15
@@ -1,4 +1,5 @@
|
||||
import { renderHook } from '@testing-library/react';
|
||||
import { Provider as JotaiProvider } from 'jotai';
|
||||
import { act } from 'react';
|
||||
import { RecoilRoot } from 'recoil';
|
||||
|
||||
@@ -6,36 +7,52 @@ import { DropdownComponentInstanceContext } from '@/ui/layout/dropdown/contexts/
|
||||
import { useCloseDropdown } from '@/ui/layout/dropdown/hooks/useCloseDropdown';
|
||||
import { useOpenDropdown } from '@/ui/layout/dropdown/hooks/useOpenDropdown';
|
||||
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 { jotaiStore } from '@/ui/utilities/state/jotai/jotaiStore';
|
||||
|
||||
const dropdownId = 'test-dropdown-id';
|
||||
const outsideDropdownId = 'test-dropdown-id-outside';
|
||||
|
||||
const Wrapper = ({ children }: { children: React.ReactNode }) => {
|
||||
return (
|
||||
<RecoilRoot>
|
||||
<DropdownComponentInstanceContext.Provider
|
||||
value={{ instanceId: dropdownId }}
|
||||
>
|
||||
{children}
|
||||
</DropdownComponentInstanceContext.Provider>
|
||||
<DropdownComponentInstanceContext.Provider
|
||||
value={{ instanceId: outsideDropdownId }}
|
||||
></DropdownComponentInstanceContext.Provider>
|
||||
</RecoilRoot>
|
||||
<JotaiProvider store={jotaiStore}>
|
||||
<RecoilRoot>
|
||||
<DropdownComponentInstanceContext.Provider
|
||||
value={{ instanceId: dropdownId }}
|
||||
>
|
||||
{children}
|
||||
</DropdownComponentInstanceContext.Provider>
|
||||
<DropdownComponentInstanceContext.Provider
|
||||
value={{ instanceId: outsideDropdownId }}
|
||||
></DropdownComponentInstanceContext.Provider>
|
||||
</RecoilRoot>
|
||||
</JotaiProvider>
|
||||
);
|
||||
};
|
||||
|
||||
describe('useCloseDropdown', () => {
|
||||
beforeEach(() => {
|
||||
jotaiStore.set(
|
||||
isDropdownOpenComponentState.atomFamily({ instanceId: dropdownId }),
|
||||
false,
|
||||
);
|
||||
jotaiStore.set(
|
||||
isDropdownOpenComponentState.atomFamily({
|
||||
instanceId: outsideDropdownId,
|
||||
}),
|
||||
false,
|
||||
);
|
||||
});
|
||||
|
||||
it('should close dropdown from inside component instance context', async () => {
|
||||
const { result } = renderHook(
|
||||
() => {
|
||||
const isOutsideDropdownOpen = useRecoilComponentValue(
|
||||
const isOutsideDropdownOpen = useRecoilComponentValueV2(
|
||||
isDropdownOpenComponentState,
|
||||
outsideDropdownId,
|
||||
);
|
||||
|
||||
const isInsideDropdownOpen = useRecoilComponentValue(
|
||||
const isInsideDropdownOpen = useRecoilComponentValueV2(
|
||||
isDropdownOpenComponentState,
|
||||
);
|
||||
|
||||
@@ -72,12 +89,12 @@ describe('useCloseDropdown', () => {
|
||||
it('should close dropdown from outside component instance context', async () => {
|
||||
const { result } = renderHook(
|
||||
() => {
|
||||
const isOutsideDropdownOpen = useRecoilComponentValue(
|
||||
const isOutsideDropdownOpen = useRecoilComponentValueV2(
|
||||
isDropdownOpenComponentState,
|
||||
outsideDropdownId,
|
||||
);
|
||||
|
||||
const isInsideDropdownOpen = useRecoilComponentValue(
|
||||
const isInsideDropdownOpen = useRecoilComponentValueV2(
|
||||
isDropdownOpenComponentState,
|
||||
);
|
||||
|
||||
|
||||
+32
-15
@@ -1,40 +1,57 @@
|
||||
import { renderHook } from '@testing-library/react';
|
||||
import { Provider as JotaiProvider } from 'jotai';
|
||||
import { act } from 'react';
|
||||
import { RecoilRoot } from 'recoil';
|
||||
|
||||
import { DropdownComponentInstanceContext } from '@/ui/layout/dropdown/contexts/DropdownComponentInstanceContext';
|
||||
import { useOpenDropdown } from '@/ui/layout/dropdown/hooks/useOpenDropdown';
|
||||
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 { jotaiStore } from '@/ui/utilities/state/jotai/jotaiStore';
|
||||
|
||||
const dropdownId = 'test-dropdown-id';
|
||||
const outsideDropdownId = 'test-dropdown-id-outside';
|
||||
|
||||
const Wrapper = ({ children }: { children: React.ReactNode }) => {
|
||||
return (
|
||||
<RecoilRoot>
|
||||
<DropdownComponentInstanceContext.Provider
|
||||
value={{ instanceId: dropdownId }}
|
||||
>
|
||||
{children}
|
||||
</DropdownComponentInstanceContext.Provider>
|
||||
<DropdownComponentInstanceContext.Provider
|
||||
value={{ instanceId: outsideDropdownId }}
|
||||
></DropdownComponentInstanceContext.Provider>
|
||||
</RecoilRoot>
|
||||
<JotaiProvider store={jotaiStore}>
|
||||
<RecoilRoot>
|
||||
<DropdownComponentInstanceContext.Provider
|
||||
value={{ instanceId: dropdownId }}
|
||||
>
|
||||
{children}
|
||||
</DropdownComponentInstanceContext.Provider>
|
||||
<DropdownComponentInstanceContext.Provider
|
||||
value={{ instanceId: outsideDropdownId }}
|
||||
></DropdownComponentInstanceContext.Provider>
|
||||
</RecoilRoot>
|
||||
</JotaiProvider>
|
||||
);
|
||||
};
|
||||
|
||||
describe('useOpenDropdown', () => {
|
||||
beforeEach(() => {
|
||||
jotaiStore.set(
|
||||
isDropdownOpenComponentState.atomFamily({ instanceId: dropdownId }),
|
||||
false,
|
||||
);
|
||||
jotaiStore.set(
|
||||
isDropdownOpenComponentState.atomFamily({
|
||||
instanceId: outsideDropdownId,
|
||||
}),
|
||||
false,
|
||||
);
|
||||
});
|
||||
|
||||
it('should open dropdown from inside component instance context', async () => {
|
||||
const { result } = renderHook(
|
||||
() => {
|
||||
const isOutsideDropdownOpen = useRecoilComponentValue(
|
||||
const isOutsideDropdownOpen = useRecoilComponentValueV2(
|
||||
isDropdownOpenComponentState,
|
||||
outsideDropdownId,
|
||||
);
|
||||
|
||||
const isInsideDropdownOpen = useRecoilComponentValue(
|
||||
const isInsideDropdownOpen = useRecoilComponentValueV2(
|
||||
isDropdownOpenComponentState,
|
||||
);
|
||||
|
||||
@@ -61,12 +78,12 @@ describe('useOpenDropdown', () => {
|
||||
it('should open dropdown from outside component instance context', async () => {
|
||||
const { result } = renderHook(
|
||||
() => {
|
||||
const isOutsideDropdownOpen = useRecoilComponentValue(
|
||||
const isOutsideDropdownOpen = useRecoilComponentValueV2(
|
||||
isDropdownOpenComponentState,
|
||||
outsideDropdownId,
|
||||
);
|
||||
|
||||
const isInsideDropdownOpen = useRecoilComponentValue(
|
||||
const isInsideDropdownOpen = useRecoilComponentValueV2(
|
||||
isDropdownOpenComponentState,
|
||||
);
|
||||
|
||||
|
||||
+32
-15
@@ -1,40 +1,57 @@
|
||||
import { renderHook } from '@testing-library/react';
|
||||
import { Provider as JotaiProvider } from 'jotai';
|
||||
import { act } from 'react';
|
||||
import { RecoilRoot } from 'recoil';
|
||||
|
||||
import { DropdownComponentInstanceContext } from '@/ui/layout/dropdown/contexts/DropdownComponentInstanceContext';
|
||||
import { useToggleDropdown } from '@/ui/layout/dropdown/hooks/useToggleDropdown';
|
||||
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 { jotaiStore } from '@/ui/utilities/state/jotai/jotaiStore';
|
||||
|
||||
const dropdownId = 'test-dropdown-id';
|
||||
const outsideDropdownId = 'test-dropdown-id-outside';
|
||||
|
||||
const Wrapper = ({ children }: { children: React.ReactNode }) => {
|
||||
return (
|
||||
<RecoilRoot>
|
||||
<DropdownComponentInstanceContext.Provider
|
||||
value={{ instanceId: dropdownId }}
|
||||
>
|
||||
{children}
|
||||
</DropdownComponentInstanceContext.Provider>
|
||||
<DropdownComponentInstanceContext.Provider
|
||||
value={{ instanceId: outsideDropdownId }}
|
||||
></DropdownComponentInstanceContext.Provider>
|
||||
</RecoilRoot>
|
||||
<JotaiProvider store={jotaiStore}>
|
||||
<RecoilRoot>
|
||||
<DropdownComponentInstanceContext.Provider
|
||||
value={{ instanceId: dropdownId }}
|
||||
>
|
||||
{children}
|
||||
</DropdownComponentInstanceContext.Provider>
|
||||
<DropdownComponentInstanceContext.Provider
|
||||
value={{ instanceId: outsideDropdownId }}
|
||||
></DropdownComponentInstanceContext.Provider>
|
||||
</RecoilRoot>
|
||||
</JotaiProvider>
|
||||
);
|
||||
};
|
||||
|
||||
describe('useToggleDropdown', () => {
|
||||
beforeEach(() => {
|
||||
jotaiStore.set(
|
||||
isDropdownOpenComponentState.atomFamily({ instanceId: dropdownId }),
|
||||
false,
|
||||
);
|
||||
jotaiStore.set(
|
||||
isDropdownOpenComponentState.atomFamily({
|
||||
instanceId: outsideDropdownId,
|
||||
}),
|
||||
false,
|
||||
);
|
||||
});
|
||||
|
||||
it('should toggle dropdown from inside component instance context', async () => {
|
||||
const { result } = renderHook(
|
||||
() => {
|
||||
const isOutsideDropdownOpen = useRecoilComponentValue(
|
||||
const isOutsideDropdownOpen = useRecoilComponentValueV2(
|
||||
isDropdownOpenComponentState,
|
||||
outsideDropdownId,
|
||||
);
|
||||
|
||||
const isInsideDropdownOpen = useRecoilComponentValue(
|
||||
const isInsideDropdownOpen = useRecoilComponentValueV2(
|
||||
isDropdownOpenComponentState,
|
||||
);
|
||||
const { toggleDropdown } = useToggleDropdown();
|
||||
@@ -74,12 +91,12 @@ describe('useToggleDropdown', () => {
|
||||
it('should toggle dropdown from outside component instance context', async () => {
|
||||
const { result } = renderHook(
|
||||
() => {
|
||||
const isOutsideDropdownOpen = useRecoilComponentValue(
|
||||
const isOutsideDropdownOpen = useRecoilComponentValueV2(
|
||||
isDropdownOpenComponentState,
|
||||
outsideDropdownId,
|
||||
);
|
||||
|
||||
const isInsideDropdownOpen = useRecoilComponentValue(
|
||||
const isInsideDropdownOpen = useRecoilComponentValueV2(
|
||||
isDropdownOpenComponentState,
|
||||
);
|
||||
const { toggleDropdown } = useToggleDropdown();
|
||||
|
||||
+31
-34
@@ -1,8 +1,10 @@
|
||||
import { useCallback } from 'react';
|
||||
|
||||
import { useCloseDropdown } from '@/ui/layout/dropdown/hooks/useCloseDropdown';
|
||||
import { activeDropdownFocusIdState } from '@/ui/layout/dropdown/states/activeDropdownFocusIdState';
|
||||
import { previousDropdownFocusIdState } from '@/ui/layout/dropdown/states/previousDropdownFocusIdState';
|
||||
import { useRemoveFocusItemFromFocusStackById } from '@/ui/utilities/focus/hooks/useRemoveFocusItemFromFocusStackById';
|
||||
import { useRecoilCallback } from 'recoil';
|
||||
import { jotaiStore } from '@/ui/utilities/state/jotai/jotaiStore';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
export const useCloseAnyOpenDropdown = () => {
|
||||
@@ -11,46 +13,41 @@ export const useCloseAnyOpenDropdown = () => {
|
||||
const { removeFocusItemFromFocusStackById } =
|
||||
useRemoveFocusItemFromFocusStackById();
|
||||
|
||||
const closeAnyOpenDropdown = useRecoilCallback(
|
||||
({ snapshot, set }) =>
|
||||
() => {
|
||||
const previousDropdownFocusId = snapshot
|
||||
.getLoadable(previousDropdownFocusIdState)
|
||||
.getValue();
|
||||
const closeAnyOpenDropdown = useCallback(() => {
|
||||
const previousDropdownFocusId = jotaiStore.get(
|
||||
previousDropdownFocusIdState.atom,
|
||||
);
|
||||
|
||||
const activeDropdownFocusId = snapshot
|
||||
.getLoadable(activeDropdownFocusIdState)
|
||||
.getValue();
|
||||
const activeDropdownFocusId = jotaiStore.get(
|
||||
activeDropdownFocusIdState.atom,
|
||||
);
|
||||
|
||||
const thereIsNoDropdownOpen =
|
||||
!isDefined(activeDropdownFocusId) &&
|
||||
!isDefined(previousDropdownFocusId);
|
||||
const thereIsNoDropdownOpen =
|
||||
!isDefined(activeDropdownFocusId) && !isDefined(previousDropdownFocusId);
|
||||
|
||||
if (thereIsNoDropdownOpen) {
|
||||
return;
|
||||
}
|
||||
if (thereIsNoDropdownOpen) {
|
||||
return;
|
||||
}
|
||||
|
||||
const thereIsOneNestedDropdownOpen = isDefined(previousDropdownFocusId);
|
||||
const thereIsOneNestedDropdownOpen = isDefined(previousDropdownFocusId);
|
||||
|
||||
if (isDefined(activeDropdownFocusId)) {
|
||||
closeDropdown(activeDropdownFocusId);
|
||||
removeFocusItemFromFocusStackById({
|
||||
focusId: activeDropdownFocusId,
|
||||
});
|
||||
}
|
||||
if (isDefined(activeDropdownFocusId)) {
|
||||
closeDropdown(activeDropdownFocusId);
|
||||
removeFocusItemFromFocusStackById({
|
||||
focusId: activeDropdownFocusId,
|
||||
});
|
||||
}
|
||||
|
||||
if (thereIsOneNestedDropdownOpen) {
|
||||
closeDropdown(previousDropdownFocusId);
|
||||
removeFocusItemFromFocusStackById({
|
||||
focusId: previousDropdownFocusId,
|
||||
});
|
||||
}
|
||||
if (thereIsOneNestedDropdownOpen) {
|
||||
closeDropdown(previousDropdownFocusId);
|
||||
removeFocusItemFromFocusStackById({
|
||||
focusId: previousDropdownFocusId,
|
||||
});
|
||||
}
|
||||
|
||||
set(previousDropdownFocusIdState, null);
|
||||
set(activeDropdownFocusIdState, null);
|
||||
},
|
||||
[closeDropdown, removeFocusItemFromFocusStackById],
|
||||
);
|
||||
jotaiStore.set(previousDropdownFocusIdState.atom, null);
|
||||
jotaiStore.set(activeDropdownFocusIdState.atom, null);
|
||||
}, [closeDropdown, removeFocusItemFromFocusStackById]);
|
||||
|
||||
return { closeAnyOpenDropdown };
|
||||
};
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
import { useCallback } from 'react';
|
||||
|
||||
import { DropdownComponentInstanceContext } from '@/ui/layout/dropdown/contexts/DropdownComponentInstanceContext';
|
||||
import { useGoBackToPreviousDropdownFocusId } from '@/ui/layout/dropdown/hooks/useGoBackToPreviousDropdownFocusId';
|
||||
import { isDropdownOpenComponentState } from '@/ui/layout/dropdown/states/isDropdownOpenComponentState';
|
||||
|
||||
import { useRemoveFocusItemFromFocusStackById } from '@/ui/utilities/focus/hooks/useRemoveFocusItemFromFocusStackById';
|
||||
import { useAvailableComponentInstanceId } from '@/ui/utilities/state/component-state/hooks/useAvailableComponentInstanceId';
|
||||
import { useRecoilCallback } from 'recoil';
|
||||
import { jotaiStore } from '@/ui/utilities/state/jotai/jotaiStore';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
export const useCloseDropdown = () => {
|
||||
@@ -17,40 +19,37 @@ export const useCloseDropdown = () => {
|
||||
const dropdownComponentInstanceIdFromContext =
|
||||
useAvailableComponentInstanceId(DropdownComponentInstanceContext);
|
||||
|
||||
const closeDropdown = useRecoilCallback(
|
||||
({ set, snapshot }) =>
|
||||
(dropdownComponentInstanceIdFromProps?: string) => {
|
||||
const dropdownComponentInstanceId =
|
||||
dropdownComponentInstanceIdFromProps ??
|
||||
dropdownComponentInstanceIdFromContext;
|
||||
const closeDropdown = useCallback(
|
||||
(dropdownComponentInstanceIdFromProps?: string) => {
|
||||
const dropdownComponentInstanceId =
|
||||
dropdownComponentInstanceIdFromProps ??
|
||||
dropdownComponentInstanceIdFromContext;
|
||||
|
||||
if (!isDefined(dropdownComponentInstanceId)) {
|
||||
throw new Error('Dropdown component instance ID is not defined');
|
||||
}
|
||||
if (!isDefined(dropdownComponentInstanceId)) {
|
||||
throw new Error('Dropdown component instance ID is not defined');
|
||||
}
|
||||
|
||||
const isDropdownOpen = snapshot
|
||||
.getLoadable(
|
||||
isDropdownOpenComponentState.atomFamily({
|
||||
instanceId: dropdownComponentInstanceId,
|
||||
}),
|
||||
)
|
||||
.getValue();
|
||||
const isDropdownOpen = jotaiStore.get(
|
||||
isDropdownOpenComponentState.atomFamily({
|
||||
instanceId: dropdownComponentInstanceId,
|
||||
}),
|
||||
);
|
||||
|
||||
if (isDropdownOpen) {
|
||||
removeFocusItemFromFocusStackById({
|
||||
focusId: dropdownComponentInstanceId,
|
||||
});
|
||||
if (isDropdownOpen) {
|
||||
removeFocusItemFromFocusStackById({
|
||||
focusId: dropdownComponentInstanceId,
|
||||
});
|
||||
|
||||
goBackToPreviousDropdownFocusId();
|
||||
goBackToPreviousDropdownFocusId();
|
||||
|
||||
set(
|
||||
isDropdownOpenComponentState.atomFamily({
|
||||
instanceId: dropdownComponentInstanceId,
|
||||
}),
|
||||
false,
|
||||
);
|
||||
}
|
||||
},
|
||||
jotaiStore.set(
|
||||
isDropdownOpenComponentState.atomFamily({
|
||||
instanceId: dropdownComponentInstanceId,
|
||||
}),
|
||||
false,
|
||||
);
|
||||
}
|
||||
},
|
||||
[
|
||||
removeFocusItemFromFocusStackById,
|
||||
goBackToPreviousDropdownFocusId,
|
||||
|
||||
+12
-12
@@ -1,22 +1,22 @@
|
||||
import { useRecoilCallback } from 'recoil';
|
||||
import { useCallback } from 'react';
|
||||
|
||||
import { activeDropdownFocusIdState } from '@/ui/layout/dropdown/states/activeDropdownFocusIdState';
|
||||
import { previousDropdownFocusIdState } from '@/ui/layout/dropdown/states/previousDropdownFocusIdState';
|
||||
import { jotaiStore } from '@/ui/utilities/state/jotai/jotaiStore';
|
||||
|
||||
// TODO: this won't work for more than 1 nested dropdown
|
||||
export const useGoBackToPreviousDropdownFocusId = () => {
|
||||
const goBackToPreviousDropdownFocusId = useRecoilCallback(
|
||||
({ snapshot, set }) =>
|
||||
() => {
|
||||
const previouslyFocusedDropdownId = snapshot
|
||||
.getLoadable(previousDropdownFocusIdState)
|
||||
.getValue();
|
||||
const goBackToPreviousDropdownFocusId = useCallback(() => {
|
||||
const previouslyFocusedDropdownId = jotaiStore.get(
|
||||
previousDropdownFocusIdState.atom,
|
||||
);
|
||||
|
||||
set(activeDropdownFocusIdState, previouslyFocusedDropdownId);
|
||||
set(previousDropdownFocusIdState, null);
|
||||
},
|
||||
[],
|
||||
);
|
||||
jotaiStore.set(
|
||||
activeDropdownFocusIdState.atom,
|
||||
previouslyFocusedDropdownId,
|
||||
);
|
||||
jotaiStore.set(previousDropdownFocusIdState.atom, null);
|
||||
}, []);
|
||||
|
||||
return {
|
||||
goBackToPreviousDropdownFocusId,
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import { useCallback } from 'react';
|
||||
|
||||
import { DropdownComponentInstanceContext } from '@/ui/layout/dropdown/contexts/DropdownComponentInstanceContext';
|
||||
import { useSetActiveDropdownFocusIdAndMemorizePrevious } from '@/ui/layout/dropdown/hooks/useSetFocusedDropdownIdAndMemorizePrevious';
|
||||
|
||||
@@ -6,7 +8,7 @@ import { usePushFocusItemToFocusStack } from '@/ui/utilities/focus/hooks/usePush
|
||||
import { FocusComponentType } from '@/ui/utilities/focus/types/FocusComponentType';
|
||||
import { type GlobalHotkeysConfig } from '@/ui/utilities/hotkey/types/GlobalHotkeysConfig';
|
||||
import { useAvailableComponentInstanceId } from '@/ui/utilities/state/component-state/hooks/useAvailableComponentInstanceId';
|
||||
import { useRecoilCallback } from 'recoil';
|
||||
import { jotaiStore } from '@/ui/utilities/state/jotai/jotaiStore';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
type OpenDropdownArgs = {
|
||||
@@ -22,44 +24,41 @@ export const useOpenDropdown = () => {
|
||||
const dropdownComponentInstanceIdFromContext =
|
||||
useAvailableComponentInstanceId(DropdownComponentInstanceContext);
|
||||
|
||||
const openDropdown = useRecoilCallback(
|
||||
({ set }) =>
|
||||
(args?: OpenDropdownArgs | null | undefined) => {
|
||||
const dropdownComponentInstanceId =
|
||||
args?.dropdownComponentInstanceIdFromProps ??
|
||||
dropdownComponentInstanceIdFromContext;
|
||||
const openDropdown = useCallback(
|
||||
(args?: OpenDropdownArgs | null | undefined) => {
|
||||
const dropdownComponentInstanceId =
|
||||
args?.dropdownComponentInstanceIdFromProps ??
|
||||
dropdownComponentInstanceIdFromContext;
|
||||
|
||||
if (!isDefined(dropdownComponentInstanceId)) {
|
||||
throw new Error('Dropdown component instance ID is not defined');
|
||||
}
|
||||
if (!isDefined(dropdownComponentInstanceId)) {
|
||||
throw new Error('Dropdown component instance ID is not defined');
|
||||
}
|
||||
|
||||
set(
|
||||
isDropdownOpenComponentState.atomFamily({
|
||||
instanceId: dropdownComponentInstanceId,
|
||||
}),
|
||||
true,
|
||||
);
|
||||
jotaiStore.set(
|
||||
isDropdownOpenComponentState.atomFamily({
|
||||
instanceId: dropdownComponentInstanceId,
|
||||
}),
|
||||
true,
|
||||
);
|
||||
|
||||
setActiveDropdownFocusIdAndMemorizePrevious(
|
||||
dropdownComponentInstanceId,
|
||||
);
|
||||
setActiveDropdownFocusIdAndMemorizePrevious(dropdownComponentInstanceId);
|
||||
|
||||
pushFocusItemToFocusStack({
|
||||
focusId: dropdownComponentInstanceId,
|
||||
component: {
|
||||
type: FocusComponentType.DROPDOWN,
|
||||
instanceId: dropdownComponentInstanceId,
|
||||
},
|
||||
globalHotkeysConfig: {
|
||||
enableGlobalHotkeysConflictingWithKeyboard:
|
||||
args?.globalHotkeysConfig
|
||||
?.enableGlobalHotkeysConflictingWithKeyboard ?? false,
|
||||
enableGlobalHotkeysWithModifiers:
|
||||
args?.globalHotkeysConfig?.enableGlobalHotkeysWithModifiers ??
|
||||
false,
|
||||
},
|
||||
});
|
||||
},
|
||||
pushFocusItemToFocusStack({
|
||||
focusId: dropdownComponentInstanceId,
|
||||
component: {
|
||||
type: FocusComponentType.DROPDOWN,
|
||||
instanceId: dropdownComponentInstanceId,
|
||||
},
|
||||
globalHotkeysConfig: {
|
||||
enableGlobalHotkeysConflictingWithKeyboard:
|
||||
args?.globalHotkeysConfig
|
||||
?.enableGlobalHotkeysConflictingWithKeyboard ?? false,
|
||||
enableGlobalHotkeysWithModifiers:
|
||||
args?.globalHotkeysConfig?.enableGlobalHotkeysWithModifiers ??
|
||||
false,
|
||||
},
|
||||
});
|
||||
},
|
||||
[
|
||||
pushFocusItemToFocusStack,
|
||||
setActiveDropdownFocusIdAndMemorizePrevious,
|
||||
|
||||
+13
-13
@@ -1,23 +1,23 @@
|
||||
import { useRecoilCallback } from 'recoil';
|
||||
import { useCallback } from 'react';
|
||||
|
||||
import { activeDropdownFocusIdState } from '@/ui/layout/dropdown/states/activeDropdownFocusIdState';
|
||||
import { previousDropdownFocusIdState } from '@/ui/layout/dropdown/states/previousDropdownFocusIdState';
|
||||
import { jotaiStore } from '@/ui/utilities/state/jotai/jotaiStore';
|
||||
|
||||
export const useSetActiveDropdownFocusIdAndMemorizePrevious = () => {
|
||||
const setActiveDropdownFocusIdAndMemorizePrevious = useRecoilCallback(
|
||||
({ snapshot, set }) =>
|
||||
(dropdownId: string | null) => {
|
||||
const activeDropdownFocusId = snapshot
|
||||
.getLoadable(activeDropdownFocusIdState)
|
||||
.getValue();
|
||||
const setActiveDropdownFocusIdAndMemorizePrevious = useCallback(
|
||||
(dropdownId: string | null) => {
|
||||
const activeDropdownFocusId = jotaiStore.get(
|
||||
activeDropdownFocusIdState.atom,
|
||||
);
|
||||
|
||||
if (activeDropdownFocusId === dropdownId) {
|
||||
return;
|
||||
}
|
||||
if (activeDropdownFocusId === dropdownId) {
|
||||
return;
|
||||
}
|
||||
|
||||
set(previousDropdownFocusIdState, activeDropdownFocusId);
|
||||
set(activeDropdownFocusIdState, dropdownId);
|
||||
},
|
||||
jotaiStore.set(previousDropdownFocusIdState.atom, activeDropdownFocusId);
|
||||
jotaiStore.set(activeDropdownFocusIdState.atom, dropdownId);
|
||||
},
|
||||
[],
|
||||
);
|
||||
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
import { useCallback } from 'react';
|
||||
|
||||
import { DropdownComponentInstanceContext } from '@/ui/layout/dropdown/contexts/DropdownComponentInstanceContext';
|
||||
import { useCloseDropdown } from '@/ui/layout/dropdown/hooks/useCloseDropdown';
|
||||
import { useOpenDropdown } from '@/ui/layout/dropdown/hooks/useOpenDropdown';
|
||||
import { isDropdownOpenComponentState } from '@/ui/layout/dropdown/states/isDropdownOpenComponentState';
|
||||
import { type GlobalHotkeysConfig } from '@/ui/utilities/hotkey/types/GlobalHotkeysConfig';
|
||||
import { useAvailableComponentInstanceId } from '@/ui/utilities/state/component-state/hooks/useAvailableComponentInstanceId';
|
||||
import { useRecoilCallback } from 'recoil';
|
||||
import { jotaiStore } from '@/ui/utilities/state/jotai/jotaiStore';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
type ToggleDropdownArgs = {
|
||||
@@ -19,34 +21,31 @@ export const useToggleDropdown = () => {
|
||||
const { openDropdown } = useOpenDropdown();
|
||||
const { closeDropdown } = useCloseDropdown();
|
||||
|
||||
const toggleDropdown = useRecoilCallback(
|
||||
({ snapshot }) =>
|
||||
(args?: ToggleDropdownArgs | null | undefined) => {
|
||||
const dropdownComponentInstanceId =
|
||||
args?.dropdownComponentInstanceIdFromProps ??
|
||||
dropdownComponentInstanceIdFromContext;
|
||||
const toggleDropdown = useCallback(
|
||||
(args?: ToggleDropdownArgs | null | undefined) => {
|
||||
const dropdownComponentInstanceId =
|
||||
args?.dropdownComponentInstanceIdFromProps ??
|
||||
dropdownComponentInstanceIdFromContext;
|
||||
|
||||
if (!isDefined(dropdownComponentInstanceId)) {
|
||||
throw new Error('Dropdown component instance ID is not defined');
|
||||
}
|
||||
if (!isDefined(dropdownComponentInstanceId)) {
|
||||
throw new Error('Dropdown component instance ID is not defined');
|
||||
}
|
||||
|
||||
const isDropdownOpen = snapshot
|
||||
.getLoadable(
|
||||
isDropdownOpenComponentState.atomFamily({
|
||||
instanceId: dropdownComponentInstanceId,
|
||||
}),
|
||||
)
|
||||
.getValue();
|
||||
const isDropdownOpen = jotaiStore.get(
|
||||
isDropdownOpenComponentState.atomFamily({
|
||||
instanceId: dropdownComponentInstanceId,
|
||||
}),
|
||||
);
|
||||
|
||||
if (isDropdownOpen) {
|
||||
closeDropdown(dropdownComponentInstanceId);
|
||||
} else {
|
||||
openDropdown({
|
||||
dropdownComponentInstanceIdFromProps: dropdownComponentInstanceId,
|
||||
globalHotkeysConfig: args?.globalHotkeysConfig,
|
||||
});
|
||||
}
|
||||
},
|
||||
if (isDropdownOpen) {
|
||||
closeDropdown(dropdownComponentInstanceId);
|
||||
} else {
|
||||
openDropdown({
|
||||
dropdownComponentInstanceIdFromProps: dropdownComponentInstanceId,
|
||||
globalHotkeysConfig: args?.globalHotkeysConfig,
|
||||
});
|
||||
}
|
||||
},
|
||||
[closeDropdown, openDropdown, dropdownComponentInstanceIdFromContext],
|
||||
);
|
||||
|
||||
|
||||
+2
-2
@@ -1,6 +1,6 @@
|
||||
import { createState } from '@/ui/utilities/state/utils/createState';
|
||||
import { createStateV2 } from '@/ui/utilities/state/jotai/utils/createStateV2';
|
||||
|
||||
export const activeDropdownFocusIdState = createState<string | null>({
|
||||
export const activeDropdownFocusIdState = createStateV2<string | null>({
|
||||
key: 'activeDropdownFocusIdState',
|
||||
defaultValue: null,
|
||||
});
|
||||
|
||||
+2
-2
@@ -1,10 +1,10 @@
|
||||
import { DropdownComponentInstanceContext } from '@/ui/layout/dropdown/contexts/DropdownComponentInstanceContext';
|
||||
import { createComponentState } from '@/ui/utilities/state/component-state/utils/createComponentState';
|
||||
import { createComponentStateV2 } from '@/ui/utilities/state/jotai/utils/createComponentStateV2';
|
||||
|
||||
import { type Placement } from '@floating-ui/react';
|
||||
|
||||
export const dropdownPlacementComponentState =
|
||||
createComponentState<Placement | null>({
|
||||
createComponentStateV2<Placement | null>({
|
||||
key: 'dropdownPlacementComponentState',
|
||||
componentInstanceContext: DropdownComponentInstanceContext,
|
||||
defaultValue: null,
|
||||
|
||||
+2
-2
@@ -1,7 +1,7 @@
|
||||
import { DropdownComponentInstanceContext } from '@/ui/layout/dropdown/contexts/DropdownComponentInstanceContext';
|
||||
import { createComponentState } from '@/ui/utilities/state/component-state/utils/createComponentState';
|
||||
import { createComponentStateV2 } from '@/ui/utilities/state/jotai/utils/createComponentStateV2';
|
||||
|
||||
export const dropdownMaxHeightComponentState = createComponentState<
|
||||
export const dropdownMaxHeightComponentState = createComponentStateV2<
|
||||
number | undefined
|
||||
>({
|
||||
key: 'dropdownMaxHeightComponentState',
|
||||
|
||||
+2
-2
@@ -1,7 +1,7 @@
|
||||
import { DropdownComponentInstanceContext } from '@/ui/layout/dropdown/contexts/DropdownComponentInstanceContext';
|
||||
import { createComponentState } from '@/ui/utilities/state/component-state/utils/createComponentState';
|
||||
import { createComponentStateV2 } from '@/ui/utilities/state/jotai/utils/createComponentStateV2';
|
||||
|
||||
export const dropdownMaxWidthComponentState = createComponentState<
|
||||
export const dropdownMaxWidthComponentState = createComponentStateV2<
|
||||
number | undefined
|
||||
>({
|
||||
key: 'dropdownMaxWidthComponentState',
|
||||
|
||||
+2
-2
@@ -1,7 +1,7 @@
|
||||
import { DropdownComponentInstanceContext } from '@/ui/layout/dropdown/contexts/DropdownComponentInstanceContext';
|
||||
import { createComponentState } from '@/ui/utilities/state/component-state/utils/createComponentState';
|
||||
import { createComponentStateV2 } from '@/ui/utilities/state/jotai/utils/createComponentStateV2';
|
||||
|
||||
export const dropdownYPositionComponentState = createComponentState<
|
||||
export const dropdownYPositionComponentState = createComponentStateV2<
|
||||
number | undefined
|
||||
>({
|
||||
key: 'dropdownYPositionComponentState',
|
||||
|
||||
+2
-2
@@ -1,7 +1,7 @@
|
||||
import { DropdownComponentInstanceContext } from '@/ui/layout/dropdown/contexts/DropdownComponentInstanceContext';
|
||||
import { createComponentState } from '@/ui/utilities/state/component-state/utils/createComponentState';
|
||||
import { createComponentStateV2 } from '@/ui/utilities/state/jotai/utils/createComponentStateV2';
|
||||
|
||||
export const isDropdownOpenComponentState = createComponentState<boolean>({
|
||||
export const isDropdownOpenComponentState = createComponentStateV2<boolean>({
|
||||
key: 'isDropdownOpenComponentState',
|
||||
defaultValue: false,
|
||||
componentInstanceContext: DropdownComponentInstanceContext,
|
||||
|
||||
+2
-2
@@ -1,6 +1,6 @@
|
||||
import { createState } from '@/ui/utilities/state/utils/createState';
|
||||
import { createStateV2 } from '@/ui/utilities/state/jotai/utils/createStateV2';
|
||||
|
||||
export const previousDropdownFocusIdState = createState<string | null>({
|
||||
export const previousDropdownFocusIdState = createStateV2<string | null>({
|
||||
key: 'previousDropdownFocusIdState',
|
||||
defaultValue: null,
|
||||
});
|
||||
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
import { useAtom } from 'jotai';
|
||||
|
||||
import { useAvailableComponentInstanceIdOrThrow } from '@/ui/utilities/state/component-state/hooks/useAvailableComponentInstanceIdOrThrow';
|
||||
import { globalComponentInstanceContextMap } from '@/ui/utilities/state/component-state/utils/globalComponentInstanceContextMap';
|
||||
import { type ComponentStateV2 } from '@/ui/utilities/state/jotai/types/ComponentStateV2';
|
||||
|
||||
export const useRecoilComponentStateV2 = <StateType>(
|
||||
componentState: ComponentStateV2<StateType>,
|
||||
instanceIdFromProps?: string,
|
||||
): [
|
||||
StateType,
|
||||
(value: StateType | ((prev: StateType) => StateType)) => void,
|
||||
] => {
|
||||
const componentInstanceContext = globalComponentInstanceContextMap.get(
|
||||
componentState.key,
|
||||
);
|
||||
|
||||
if (!componentInstanceContext) {
|
||||
throw new Error(
|
||||
`Instance context for key "${componentState.key}" is not defined`,
|
||||
);
|
||||
}
|
||||
|
||||
const instanceId = useAvailableComponentInstanceIdOrThrow(
|
||||
componentInstanceContext,
|
||||
instanceIdFromProps,
|
||||
);
|
||||
|
||||
return useAtom(componentState.atomFamily({ instanceId }));
|
||||
};
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
import { useAtomValue } from 'jotai';
|
||||
|
||||
import { useAvailableComponentInstanceIdOrThrow } from '@/ui/utilities/state/component-state/hooks/useAvailableComponentInstanceIdOrThrow';
|
||||
import { globalComponentInstanceContextMap } from '@/ui/utilities/state/component-state/utils/globalComponentInstanceContextMap';
|
||||
import { type ComponentStateV2 } from '@/ui/utilities/state/jotai/types/ComponentStateV2';
|
||||
|
||||
export const useRecoilComponentValueV2 = <StateType>(
|
||||
componentState: ComponentStateV2<StateType>,
|
||||
instanceIdFromProps?: string,
|
||||
): StateType => {
|
||||
const componentInstanceContext = globalComponentInstanceContextMap.get(
|
||||
componentState.key,
|
||||
);
|
||||
|
||||
if (!componentInstanceContext) {
|
||||
throw new Error(
|
||||
`Instance context for key "${componentState.key}" is not defined`,
|
||||
);
|
||||
}
|
||||
|
||||
const instanceId = useAvailableComponentInstanceIdOrThrow(
|
||||
componentInstanceContext,
|
||||
instanceIdFromProps,
|
||||
);
|
||||
|
||||
return useAtomValue(componentState.atomFamily({ instanceId }));
|
||||
};
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
import { useSetAtom } from 'jotai';
|
||||
|
||||
import { useAvailableComponentInstanceIdOrThrow } from '@/ui/utilities/state/component-state/hooks/useAvailableComponentInstanceIdOrThrow';
|
||||
import { globalComponentInstanceContextMap } from '@/ui/utilities/state/component-state/utils/globalComponentInstanceContextMap';
|
||||
import { type ComponentStateV2 } from '@/ui/utilities/state/jotai/types/ComponentStateV2';
|
||||
|
||||
export const useSetRecoilComponentStateV2 = <ValueType>(
|
||||
componentState: ComponentStateV2<ValueType>,
|
||||
instanceIdFromProps?: string,
|
||||
): ((value: ValueType | ((prev: ValueType) => ValueType)) => void) => {
|
||||
const componentInstanceContext = globalComponentInstanceContextMap.get(
|
||||
componentState.key,
|
||||
);
|
||||
|
||||
if (!componentInstanceContext) {
|
||||
throw new Error(
|
||||
`Instance context for key "${componentState.key}" is not defined`,
|
||||
);
|
||||
}
|
||||
|
||||
const instanceId = useAvailableComponentInstanceIdOrThrow(
|
||||
componentInstanceContext,
|
||||
instanceIdFromProps,
|
||||
);
|
||||
|
||||
return useSetAtom(componentState.atomFamily({ instanceId }));
|
||||
};
|
||||
@@ -1,3 +1,8 @@
|
||||
import { createStore } from 'jotai';
|
||||
|
||||
export const jotaiStore = createStore();
|
||||
export let jotaiStore = createStore();
|
||||
|
||||
export const resetJotaiStore = () => {
|
||||
jotaiStore = createStore();
|
||||
return jotaiStore;
|
||||
};
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
import { type WritableAtom } from 'jotai';
|
||||
|
||||
import { type ComponentStateKey } from '@/ui/utilities/state/component-state/types/ComponentStateKey';
|
||||
|
||||
type JotaiWritableAtom<ValueType> = WritableAtom<
|
||||
ValueType,
|
||||
[ValueType | ((prev: ValueType) => ValueType)],
|
||||
void
|
||||
>;
|
||||
|
||||
export type ComponentStateV2<ValueType> = {
|
||||
type: 'ComponentStateV2';
|
||||
key: string;
|
||||
atomFamily: (key: ComponentStateKey) => JotaiWritableAtom<ValueType>;
|
||||
};
|
||||
+50
@@ -0,0 +1,50 @@
|
||||
import { atom } from 'jotai';
|
||||
|
||||
import { type ComponentInstanceStateContext } from '@/ui/utilities/state/component-state/types/ComponentInstanceStateContext';
|
||||
import { type ComponentStateKey } from '@/ui/utilities/state/component-state/types/ComponentStateKey';
|
||||
import { globalComponentInstanceContextMap } from '@/ui/utilities/state/component-state/utils/globalComponentInstanceContextMap';
|
||||
import { type ComponentStateV2 } from '@/ui/utilities/state/jotai/types/ComponentStateV2';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
export const createComponentStateV2 = <ValueType>({
|
||||
key,
|
||||
defaultValue,
|
||||
componentInstanceContext,
|
||||
}: {
|
||||
key: string;
|
||||
defaultValue: ValueType;
|
||||
componentInstanceContext: ComponentInstanceStateContext<any> | null;
|
||||
}): ComponentStateV2<ValueType> => {
|
||||
if (isDefined(componentInstanceContext)) {
|
||||
globalComponentInstanceContextMap.set(key, componentInstanceContext);
|
||||
}
|
||||
|
||||
const atomCache = new Map<
|
||||
string,
|
||||
ReturnType<ComponentStateV2<ValueType>['atomFamily']>
|
||||
>();
|
||||
|
||||
const familyFunction = ({
|
||||
instanceId,
|
||||
}: ComponentStateKey): ReturnType<
|
||||
ComponentStateV2<ValueType>['atomFamily']
|
||||
> => {
|
||||
const existing = atomCache.get(instanceId);
|
||||
|
||||
if (existing !== undefined) {
|
||||
return existing;
|
||||
}
|
||||
|
||||
const baseAtom = atom(defaultValue);
|
||||
baseAtom.debugLabel = `${key}__${instanceId}`;
|
||||
atomCache.set(instanceId, baseAtom);
|
||||
|
||||
return baseAtom;
|
||||
};
|
||||
|
||||
return {
|
||||
type: 'ComponentStateV2',
|
||||
key,
|
||||
atomFamily: familyFunction,
|
||||
};
|
||||
};
|
||||
Reference in New Issue
Block a user