(null);
- const { isDropdownOpen, toggleDropdown, closeDropdown } = useDropdown({
- dropdownId,
- });
+ const { isDropdownOpen, toggleDropdown, closeDropdown } = useDropdown();
const { refs, floatingStyles } = useFloating({
placement: dropdownPlacement,
@@ -71,8 +67,7 @@ export const DropdownMenu = ({
});
useInternalHotkeyScopeManagement({
- dropdownId,
- dropdownHotkeyScope,
+ dropdownHotkeyScopeFromParent: dropdownHotkeyScope,
});
useScopedHotkeys(
@@ -83,6 +78,7 @@ export const DropdownMenu = ({
dropdownHotkeyScope.scope,
[closeDropdown],
);
+
return (
{clickableComponent && (
@@ -101,11 +97,7 @@ export const DropdownMenu = ({
{dropdownComponents}
)}
-
+
);
};
diff --git a/front/src/modules/ui/dropdown/components/DropdownToggleEffect.tsx b/front/src/modules/ui/dropdown/components/DropdownToggleEffect.tsx
index 47099e214..390e939c7 100644
--- a/front/src/modules/ui/dropdown/components/DropdownToggleEffect.tsx
+++ b/front/src/modules/ui/dropdown/components/DropdownToggleEffect.tsx
@@ -3,15 +3,13 @@ import { useEffect } from 'react';
import { useDropdown } from '@/ui/dropdown/hooks/useDropdown';
export const DropdownToggleEffect = ({
- dropdownId,
onDropdownClose,
onDropdownOpen,
}: {
- dropdownId: string;
onDropdownClose?: () => void;
onDropdownOpen?: () => void;
}) => {
- const { isDropdownOpen } = useDropdown({ dropdownId });
+ const { isDropdownOpen } = useDropdown();
useEffect(() => {
if (isDropdownOpen) {
diff --git a/front/src/modules/ui/dropdown/hooks/useDropdown.ts b/front/src/modules/ui/dropdown/hooks/useDropdown.ts
index 32b3def04..1e38288a0 100644
--- a/front/src/modules/ui/dropdown/hooks/useDropdown.ts
+++ b/front/src/modules/ui/dropdown/hooks/useDropdown.ts
@@ -1,47 +1,52 @@
import { usePreviousHotkeyScope } from '@/ui/utilities/hotkey/hooks/usePreviousHotkeyScope';
-import { useRecoilScopedFamilyState } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedFamilyState';
+import { useAvailableScopeIdOrThrow } from '@/ui/utilities/recoil-scope/scopes-internal/hooks/useAvailableScopeId';
-import { dropdownButtonHotkeyScopeScopedFamilyState } from '../states/dropdownButtonHotkeyScopeScopedFamilyState';
-import { isDropdownButtonOpenScopedFamilyState } from '../states/isDropdownButtonOpenScopedFamilyState';
-import { DropdownRecoilScopeContext } from '../states/recoil-scope-contexts/DropdownRecoilScopeContext';
+import { DropdownScopeInternalContext } from '../scopes/scope-internal-context/DropdownScopeInternalContext';
-export const useDropdown = ({ dropdownId }: { dropdownId: string }) => {
+import { useDropdownStates } from './useDropdownStates';
+
+type UseDropdownProps = {
+ dropdownScopeId?: string;
+};
+
+export const useDropdown = (props?: UseDropdownProps) => {
const {
setHotkeyScopeAndMemorizePreviousScope,
goBackToPreviousHotkeyScope,
} = usePreviousHotkeyScope();
- const [isDropdownButtonOpen, setIsDropdownButtonOpen] =
- useRecoilScopedFamilyState(
- isDropdownButtonOpenScopedFamilyState,
- dropdownId,
- DropdownRecoilScopeContext,
- );
-
- const [dropdownButtonHotkeyScope] = useRecoilScopedFamilyState(
- dropdownButtonHotkeyScopeScopedFamilyState,
- dropdownId,
- DropdownRecoilScopeContext,
+ const scopeId = useAvailableScopeIdOrThrow(
+ DropdownScopeInternalContext,
+ props?.dropdownScopeId,
);
+ const {
+ dropdownHotkeyScope,
+ setDropdownHotkeyScope,
+ isDropdownOpen,
+ setIsDropdownOpen,
+ } = useDropdownStates({
+ scopeId,
+ });
+
const closeDropdownButton = () => {
goBackToPreviousHotkeyScope();
- setIsDropdownButtonOpen(false);
+ setIsDropdownOpen(false);
};
const openDropdownButton = () => {
- setIsDropdownButtonOpen(true);
+ setIsDropdownOpen(true);
- if (dropdownButtonHotkeyScope) {
+ if (dropdownHotkeyScope) {
setHotkeyScopeAndMemorizePreviousScope(
- dropdownButtonHotkeyScope.scope,
- dropdownButtonHotkeyScope.customScopes,
+ dropdownHotkeyScope.scope,
+ dropdownHotkeyScope.customScopes,
);
}
};
const toggleDropdownButton = () => {
- if (isDropdownButtonOpen) {
+ if (isDropdownOpen) {
closeDropdownButton();
} else {
openDropdownButton();
@@ -49,9 +54,12 @@ export const useDropdown = ({ dropdownId }: { dropdownId: string }) => {
};
return {
- isDropdownOpen: isDropdownButtonOpen,
+ isDropdownOpen: isDropdownOpen,
closeDropdown: closeDropdownButton,
toggleDropdown: toggleDropdownButton,
openDropdown: openDropdownButton,
+ scopeId,
+ dropdownHotkeyScope,
+ setDropdownHotkeyScope,
};
};
diff --git a/front/src/modules/ui/dropdown/hooks/useDropdownStates.ts b/front/src/modules/ui/dropdown/hooks/useDropdownStates.ts
new file mode 100644
index 000000000..d60ac3c55
--- /dev/null
+++ b/front/src/modules/ui/dropdown/hooks/useDropdownStates.ts
@@ -0,0 +1,23 @@
+import { useRecoilScopedStateV2 } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedStateV2';
+
+import { dropdownHotkeyScopeScopedState } from '../states/dropdownHotkeyScopeScopedState';
+import { isDropdownOpenScopedState } from '../states/isDropdownOpenScopedState';
+
+export const useDropdownStates = ({ scopeId }: { scopeId: string }) => {
+ const [isDropdownOpen, setIsDropdownOpen] = useRecoilScopedStateV2(
+ isDropdownOpenScopedState,
+ scopeId,
+ );
+
+ const [dropdownHotkeyScope, setDropdownHotkeyScope] = useRecoilScopedStateV2(
+ dropdownHotkeyScopeScopedState,
+ scopeId,
+ );
+
+ return {
+ isDropdownOpen,
+ setIsDropdownOpen,
+ dropdownHotkeyScope,
+ setDropdownHotkeyScope,
+ };
+};
diff --git a/front/src/modules/ui/dropdown/hooks/useInternalHotkeyScopeManagement.ts b/front/src/modules/ui/dropdown/hooks/useInternalHotkeyScopeManagement.ts
index 51052c5a6..b63587ded 100644
--- a/front/src/modules/ui/dropdown/hooks/useInternalHotkeyScopeManagement.ts
+++ b/front/src/modules/ui/dropdown/hooks/useInternalHotkeyScopeManagement.ts
@@ -1,33 +1,24 @@
import { useEffect } from 'react';
import { HotkeyScope } from '@/ui/utilities/hotkey/types/HotkeyScope';
-import { useRecoilScopedFamilyState } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedFamilyState';
import { isDeeplyEqual } from '~/utils/isDeeplyEqual';
-import { dropdownButtonHotkeyScopeScopedFamilyState } from '../states/dropdownButtonHotkeyScopeScopedFamilyState';
-import { DropdownRecoilScopeContext } from '../states/recoil-scope-contexts/DropdownRecoilScopeContext';
+import { useDropdown } from './useDropdown';
export const useInternalHotkeyScopeManagement = ({
- dropdownId,
- dropdownHotkeyScope,
+ dropdownHotkeyScopeFromParent,
}: {
- dropdownId: string;
- dropdownHotkeyScope?: HotkeyScope;
+ dropdownHotkeyScopeFromParent?: HotkeyScope;
}) => {
- const [dropdownButtonHotkeyScope, setDropdownButtonHotkeyScope] =
- useRecoilScopedFamilyState(
- dropdownButtonHotkeyScopeScopedFamilyState,
- dropdownId,
- DropdownRecoilScopeContext,
- );
+ const { dropdownHotkeyScope, setDropdownHotkeyScope } = useDropdown();
useEffect(() => {
- if (!isDeeplyEqual(dropdownButtonHotkeyScope, dropdownHotkeyScope)) {
- setDropdownButtonHotkeyScope(dropdownHotkeyScope);
+ if (!isDeeplyEqual(dropdownHotkeyScopeFromParent, dropdownHotkeyScope)) {
+ setDropdownHotkeyScope(dropdownHotkeyScopeFromParent);
}
}, [
dropdownHotkeyScope,
- dropdownButtonHotkeyScope,
- setDropdownButtonHotkeyScope,
+ dropdownHotkeyScopeFromParent,
+ setDropdownHotkeyScope,
]);
};
diff --git a/front/src/modules/ui/dropdown/scopes/DropdownScope.tsx b/front/src/modules/ui/dropdown/scopes/DropdownScope.tsx
new file mode 100644
index 000000000..e6e53702d
--- /dev/null
+++ b/front/src/modules/ui/dropdown/scopes/DropdownScope.tsx
@@ -0,0 +1,19 @@
+import { ReactNode } from 'react';
+
+import { DropdownScopeInternalContext } from './scope-internal-context/DropdownScopeInternalContext';
+
+type DropdownScopeProps = {
+ children: ReactNode;
+ dropdownScopeId: string;
+};
+
+export const DropdownScope = ({
+ children,
+ dropdownScopeId,
+}: DropdownScopeProps) => {
+ return (
+
+ {children}
+
+ );
+};
diff --git a/front/src/modules/ui/dropdown/scopes/scope-internal-context/DropdownScopeInternalContext.ts b/front/src/modules/ui/dropdown/scopes/scope-internal-context/DropdownScopeInternalContext.ts
new file mode 100644
index 000000000..7dbd1ce36
--- /dev/null
+++ b/front/src/modules/ui/dropdown/scopes/scope-internal-context/DropdownScopeInternalContext.ts
@@ -0,0 +1,9 @@
+import { ScopedStateKey } from '@/ui/utilities/recoil-scope/scopes-internal/types/ScopedStateKey';
+import { createScopeInternalContext } from '@/ui/utilities/recoil-scope/scopes-internal/utils/createScopeInternalContext';
+
+type DropdownScopeInternalContextProps = ScopedStateKey & {
+ test?: string;
+};
+
+export const DropdownScopeInternalContext =
+ createScopeInternalContext();
diff --git a/front/src/modules/ui/dropdown/states/dropdownButtonHotkeyScopeScopedFamilyState.ts b/front/src/modules/ui/dropdown/states/dropdownButtonHotkeyScopeScopedFamilyState.ts
deleted file mode 100644
index 67a5952cc..000000000
--- a/front/src/modules/ui/dropdown/states/dropdownButtonHotkeyScopeScopedFamilyState.ts
+++ /dev/null
@@ -1,11 +0,0 @@
-import { atomFamily } from 'recoil';
-
-import { HotkeyScope } from '@/ui/utilities/hotkey/types/HotkeyScope';
-
-export const dropdownButtonHotkeyScopeScopedFamilyState = atomFamily<
- HotkeyScope | null | undefined,
- string
->({
- key: 'dropdownButtonHotkeyScopeScopedFamilyState',
- default: null,
-});
diff --git a/front/src/modules/ui/dropdown/states/dropdownHotkeyScopeScopedState.ts b/front/src/modules/ui/dropdown/states/dropdownHotkeyScopeScopedState.ts
new file mode 100644
index 000000000..fbcac52b3
--- /dev/null
+++ b/front/src/modules/ui/dropdown/states/dropdownHotkeyScopeScopedState.ts
@@ -0,0 +1,9 @@
+import { HotkeyScope } from '@/ui/utilities/hotkey/types/HotkeyScope';
+import { createScopedState } from '@/ui/utilities/recoil-scope/utils/createScopedState';
+
+export const dropdownHotkeyScopeScopedState = createScopedState<
+ HotkeyScope | null | undefined
+>({
+ key: 'dropdownHotkeyScopeScopedState',
+ defaultValue: null,
+});
diff --git a/front/src/modules/ui/dropdown/states/isDropdownButtonOpenScopedFamilyState.ts b/front/src/modules/ui/dropdown/states/isDropdownButtonOpenScopedFamilyState.ts
deleted file mode 100644
index c81d0173f..000000000
--- a/front/src/modules/ui/dropdown/states/isDropdownButtonOpenScopedFamilyState.ts
+++ /dev/null
@@ -1,9 +0,0 @@
-import { atomFamily } from 'recoil';
-
-export const isDropdownButtonOpenScopedFamilyState = atomFamily<
- boolean,
- string
->({
- key: 'isDropdownButtonOpenScopedState',
- default: false,
-});
diff --git a/front/src/modules/ui/dropdown/states/isDropdownOpenScopedState.ts b/front/src/modules/ui/dropdown/states/isDropdownOpenScopedState.ts
new file mode 100644
index 000000000..56d6f905e
--- /dev/null
+++ b/front/src/modules/ui/dropdown/states/isDropdownOpenScopedState.ts
@@ -0,0 +1,6 @@
+import { createScopedState } from '@/ui/utilities/recoil-scope/utils/createScopedState';
+
+export const isDropdownOpenScopedState = createScopedState({
+ key: 'isDropdownOpenScopedState',
+ defaultValue: false,
+});
diff --git a/front/src/modules/ui/dropdown/states/recoil-scope-contexts/DropdownRecoilScopeContext.ts b/front/src/modules/ui/dropdown/states/recoil-scope-contexts/DropdownRecoilScopeContext.ts
deleted file mode 100644
index 139778002..000000000
--- a/front/src/modules/ui/dropdown/states/recoil-scope-contexts/DropdownRecoilScopeContext.ts
+++ /dev/null
@@ -1,3 +0,0 @@
-import { createContext } from 'react';
-
-export const DropdownRecoilScopeContext = createContext(null);
diff --git a/front/src/modules/ui/input/components/CountryPickerDropdownButton.tsx b/front/src/modules/ui/input/components/CountryPickerDropdownButton.tsx
index f75927184..5f80c8d86 100644
--- a/front/src/modules/ui/input/components/CountryPickerDropdownButton.tsx
+++ b/front/src/modules/ui/input/components/CountryPickerDropdownButton.tsx
@@ -8,6 +8,7 @@ import { CountryCallingCode } from 'libphonenumber-js';
import { DropdownMenu } from '@/ui/dropdown/components/DropdownMenu';
import { useDropdown } from '@/ui/dropdown/hooks/useDropdown';
+import { DropdownScope } from '@/ui/dropdown/scopes/DropdownScope';
import { IconChevronDown } from '@/ui/icon';
import { IconWorld } from '../constants/icons';
@@ -77,7 +78,7 @@ export const CountryPickerDropdownButton = ({
const [selectedCountry, setSelectedCountry] = useState();
const { isDropdownOpen, closeDropdown } = useDropdown({
- dropdownId: 'country-picker',
+ dropdownScopeId: 'country-picker',
});
const handleChange = (countryCode: string) => {
@@ -122,25 +123,26 @@ export const CountryPickerDropdownButton = ({
}, [countries, value]);
return (
-
-
- {selectedCountry ? : }
-
-
-
- }
- dropdownComponents={
-
- }
- dropdownOffset={{ x: -60, y: -34 }}
- />
+
+
+
+ {selectedCountry ? : }
+
+
+
+ }
+ dropdownComponents={
+
+ }
+ dropdownOffset={{ x: -60, y: -34 }}
+ />
+
);
};
diff --git a/front/src/modules/ui/input/components/PhoneInput.tsx b/front/src/modules/ui/input/components/PhoneInput.tsx
index a2cf8f833..bd7930508 100644
--- a/front/src/modules/ui/input/components/PhoneInput.tsx
+++ b/front/src/modules/ui/input/components/PhoneInput.tsx
@@ -2,9 +2,6 @@ import { useEffect, useRef, useState } from 'react';
import ReactPhoneNumberInput from 'react-phone-number-input';
import styled from '@emotion/styled';
-import { DropdownRecoilScopeContext } from '@/ui/dropdown/states/recoil-scope-contexts/DropdownRecoilScopeContext';
-import { RecoilScope } from '@/ui/utilities/recoil-scope/components/RecoilScope';
-
import { useRegisterInputEvents } from '../hooks/useRegisterInputEvents';
import { CountryPickerDropdownButton } from './CountryPickerDropdownButton';
@@ -86,17 +83,15 @@ export const PhoneInput = ({
return (
-
-
-
+
);
};
diff --git a/front/src/modules/ui/layout/show-page/components/ShowPageAddButton.tsx b/front/src/modules/ui/layout/show-page/components/ShowPageAddButton.tsx
index 3ab821f94..c5adf075d 100644
--- a/front/src/modules/ui/layout/show-page/components/ShowPageAddButton.tsx
+++ b/front/src/modules/ui/layout/show-page/components/ShowPageAddButton.tsx
@@ -22,7 +22,7 @@ export const ShowPageAddButton = ({
entity: ActivityTargetableEntity;
}) => {
const { closeDropdown, toggleDropdown } = useDropdown({
- dropdownId: 'add-show-page',
+ dropdownScopeId: 'add-show-page',
});
const openCreateActivity = useOpenCreateActivityDrawer();
diff --git a/front/src/modules/ui/utilities/recoil-scope/components/RecoilScope.tsx b/front/src/modules/ui/utilities/recoil-scope/components/RecoilScope.tsx
index 102efc95e..0590d6edb 100644
--- a/front/src/modules/ui/utilities/recoil-scope/components/RecoilScope.tsx
+++ b/front/src/modules/ui/utilities/recoil-scope/components/RecoilScope.tsx
@@ -5,6 +5,10 @@ import { RecoilScopeContext as RecoilScopeContextType } from '@/types/RecoilScop
import { RecoilScopeContext } from '../states/RecoilScopeContext';
+/**
+ *
+ * @deprecated Use a custom scope context instead, see example with DropdownScope
+ */
export const RecoilScope = ({
children,
scopeId,
diff --git a/front/src/modules/ui/utilities/recoil-scope/hooks/useContextScopeId.ts b/front/src/modules/ui/utilities/recoil-scope/hooks/useContextScopeId.ts
index ab3d6b3cc..bf92b9de2 100644
--- a/front/src/modules/ui/utilities/recoil-scope/hooks/useContextScopeId.ts
+++ b/front/src/modules/ui/utilities/recoil-scope/hooks/useContextScopeId.ts
@@ -1,5 +1,9 @@
import { Context, useContext } from 'react';
+/**
+ * @deprecated use a custom scope instead and desctructure the scope id from the scope context
+ * Get the scope context with useScopeInternalContext
+ */
export const useContextScopeId = (SpecificContext: Context) => {
const recoilScopeId = useContext(SpecificContext);
diff --git a/front/src/modules/ui/utilities/recoil-scope/hooks/useRecoilScopeId.ts b/front/src/modules/ui/utilities/recoil-scope/hooks/useRecoilScopeId.ts
index 2f259f394..f14486f1f 100644
--- a/front/src/modules/ui/utilities/recoil-scope/hooks/useRecoilScopeId.ts
+++ b/front/src/modules/ui/utilities/recoil-scope/hooks/useRecoilScopeId.ts
@@ -2,6 +2,9 @@ import { useContext } from 'react';
import { RecoilScopeContext } from '@/types/RecoilScopeContext';
+/**
+ * @deprecated Use a custom scope instead and desctructure the scope id from the scope context
+ */
export const useRecoilScopeId = (RecoilScopeContext: RecoilScopeContext) => {
const recoilScopeId = useContext(RecoilScopeContext);
diff --git a/front/src/modules/ui/utilities/recoil-scope/hooks/useRecoilScopedFamilyState.ts b/front/src/modules/ui/utilities/recoil-scope/hooks/useRecoilScopedFamilyState.ts
index e5dbc7b68..4e135f86b 100644
--- a/front/src/modules/ui/utilities/recoil-scope/hooks/useRecoilScopedFamilyState.ts
+++ b/front/src/modules/ui/utilities/recoil-scope/hooks/useRecoilScopedFamilyState.ts
@@ -1,25 +1,21 @@
-import { Context, useContext } from 'react';
-import { RecoilState, useRecoilState } from 'recoil';
+import { RecoilState, SerializableParam, useRecoilState } from 'recoil';
-import { RecoilScopeContext } from '../states/RecoilScopeContext';
+import { ScopedFamilyStateKey } from '../scopes-internal/types/ScopedFamilyStateKey';
-export const useRecoilScopedFamilyState = (
- recoilState: (familyUniqueId: string) => RecoilState,
- uniqueIdInRecoilScope: string,
- CustomRecoilScopeContext?: Context,
+export const useRecoilScopedFamilyState = <
+ StateType,
+ FamilyKey extends SerializableParam,
+>(
+ recoilState: (
+ scopedFamilyKey: ScopedFamilyStateKey,
+ ) => RecoilState,
+ scopeId: string,
+ familyKey: FamilyKey,
) => {
- const recoilScopeId = useContext(
- CustomRecoilScopeContext ?? RecoilScopeContext,
+ return useRecoilState(
+ recoilState({
+ scopeId,
+ familyKey,
+ }),
);
-
- if (!recoilScopeId)
- throw new Error(
- `Using a scoped atom without a RecoilScope : ${
- recoilState('').key
- }, verify that you are using a RecoilScope with a specific context if you intended to do so.`,
- );
-
- const familyUniqueId = recoilScopeId + uniqueIdInRecoilScope;
-
- return useRecoilState(recoilState(familyUniqueId));
};
diff --git a/front/src/modules/ui/utilities/recoil-scope/hooks/useRecoilScopedStateV2.ts b/front/src/modules/ui/utilities/recoil-scope/hooks/useRecoilScopedStateV2.ts
new file mode 100644
index 000000000..8e81182f6
--- /dev/null
+++ b/front/src/modules/ui/utilities/recoil-scope/hooks/useRecoilScopedStateV2.ts
@@ -0,0 +1,14 @@
+import { RecoilState, useRecoilState } from 'recoil';
+
+import { ScopedStateKey } from '../scopes-internal/types/ScopedStateKey';
+
+export const useRecoilScopedStateV2 = (
+ recoilState: (scopedKey: ScopedStateKey) => RecoilState,
+ scopeId: string,
+) => {
+ return useRecoilState(
+ recoilState({
+ scopeId,
+ }),
+ );
+};
diff --git a/front/src/modules/ui/utilities/recoil-scope/hooks/useRecoilScopedValue.ts b/front/src/modules/ui/utilities/recoil-scope/hooks/useRecoilScopedValue.ts
index 484fd4102..413b55605 100644
--- a/front/src/modules/ui/utilities/recoil-scope/hooks/useRecoilScopedValue.ts
+++ b/front/src/modules/ui/utilities/recoil-scope/hooks/useRecoilScopedValue.ts
@@ -3,6 +3,9 @@ import { RecoilState, RecoilValueReadOnly, useRecoilValue } from 'recoil';
import { RecoilScopeContext } from '../states/RecoilScopeContext';
+/**
+ * @deprecated use useRecoilScopedStateV2 instead
+ */
export const useRecoilScopedValue = (
recoilState: (param: string) => RecoilState | RecoilValueReadOnly,
CustomRecoilScopeContext?: Context,
diff --git a/front/src/modules/ui/utilities/recoil-scope/hooks/useRecoilScopedValueV2.ts b/front/src/modules/ui/utilities/recoil-scope/hooks/useRecoilScopedValueV2.ts
new file mode 100644
index 000000000..b3b2126b7
--- /dev/null
+++ b/front/src/modules/ui/utilities/recoil-scope/hooks/useRecoilScopedValueV2.ts
@@ -0,0 +1,14 @@
+import { RecoilState, useRecoilValue } from 'recoil';
+
+import { ScopedStateKey } from '../scopes-internal/types/ScopedStateKey';
+
+export const useRecoilScopedValueV2 = (
+ recoilState: (scopedKey: ScopedStateKey) => RecoilState,
+ scopeId: string,
+) => {
+ return useRecoilValue(
+ recoilState({
+ scopeId,
+ }),
+ );
+};
diff --git a/front/src/modules/ui/utilities/recoil-scope/scopes-internal/hooks/useAvailableScopeId.ts b/front/src/modules/ui/utilities/recoil-scope/scopes-internal/hooks/useAvailableScopeId.ts
new file mode 100644
index 000000000..6a747291e
--- /dev/null
+++ b/front/src/modules/ui/utilities/recoil-scope/scopes-internal/hooks/useAvailableScopeId.ts
@@ -0,0 +1,20 @@
+import { ScopeInternalContext } from '../types/ScopeInternalContext';
+
+import { useScopeInternalContext } from './useScopeInternalContext';
+
+export const useAvailableScopeIdOrThrow = (
+ Context: ScopeInternalContext,
+ scopeIdFromProps?: string,
+): string => {
+ const scopeInternalContext = useScopeInternalContext(Context);
+
+ const scopeIdFromContext = scopeInternalContext?.scopeId;
+
+ if (scopeIdFromProps) {
+ return scopeIdFromProps;
+ } else if (scopeIdFromContext) {
+ return scopeIdFromContext;
+ } else {
+ throw new Error('Scope id is not provided and cannot be found in context.');
+ }
+};
diff --git a/front/src/modules/ui/utilities/recoil-scope/scopes-internal/hooks/useScopeInternalContext.ts b/front/src/modules/ui/utilities/recoil-scope/scopes-internal/hooks/useScopeInternalContext.ts
new file mode 100644
index 000000000..7b4c4f1c4
--- /dev/null
+++ b/front/src/modules/ui/utilities/recoil-scope/scopes-internal/hooks/useScopeInternalContext.ts
@@ -0,0 +1,11 @@
+import { useContext } from 'react';
+
+import { ScopeInternalContext } from '../types/ScopeInternalContext';
+
+export const useScopeInternalContext = (
+ Context: ScopeInternalContext,
+) => {
+ const context = useContext(Context);
+
+ return context;
+};
diff --git a/front/src/modules/ui/utilities/recoil-scope/scopes-internal/hooks/useScopeInternalContextOrThrow.ts b/front/src/modules/ui/utilities/recoil-scope/scopes-internal/hooks/useScopeInternalContextOrThrow.ts
new file mode 100644
index 000000000..876efdee2
--- /dev/null
+++ b/front/src/modules/ui/utilities/recoil-scope/scopes-internal/hooks/useScopeInternalContextOrThrow.ts
@@ -0,0 +1,19 @@
+import { useContext } from 'react';
+
+import { isDefined } from '~/utils/isDefined';
+
+import { ScopeInternalContext } from '../types/ScopeInternalContext';
+
+export const useScopeInternalContextOrThrow = (
+ Context: ScopeInternalContext,
+) => {
+ const context = useContext(Context);
+
+ if (!isDefined(context)) {
+ throw new Error(
+ `Using a scope context without a ScopeInternalContext.Provider wrapper for context : ${Context.displayName}.`,
+ );
+ }
+
+ return context;
+};
diff --git a/front/src/modules/ui/utilities/recoil-scope/scopes-internal/types/ScopeInternalContext.ts b/front/src/modules/ui/utilities/recoil-scope/scopes-internal/types/ScopeInternalContext.ts
new file mode 100644
index 000000000..bd6ff6ee4
--- /dev/null
+++ b/front/src/modules/ui/utilities/recoil-scope/scopes-internal/types/ScopeInternalContext.ts
@@ -0,0 +1,4 @@
+import { Context } from 'react';
+
+export type ScopeInternalContext =
+ Context;
diff --git a/front/src/modules/ui/utilities/recoil-scope/scopes-internal/types/ScopedFamilyStateKey.ts b/front/src/modules/ui/utilities/recoil-scope/scopes-internal/types/ScopedFamilyStateKey.ts
new file mode 100644
index 000000000..2930aaed4
--- /dev/null
+++ b/front/src/modules/ui/utilities/recoil-scope/scopes-internal/types/ScopedFamilyStateKey.ts
@@ -0,0 +1,6 @@
+import { SerializableParam } from 'recoil';
+
+export type ScopedFamilyStateKey = {
+ scopeId: string;
+ familyKey: FamilyKey;
+};
diff --git a/front/src/modules/ui/utilities/recoil-scope/scopes-internal/types/ScopedStateKey.ts b/front/src/modules/ui/utilities/recoil-scope/scopes-internal/types/ScopedStateKey.ts
new file mode 100644
index 000000000..e0904f87f
--- /dev/null
+++ b/front/src/modules/ui/utilities/recoil-scope/scopes-internal/types/ScopedStateKey.ts
@@ -0,0 +1,3 @@
+export type ScopedStateKey = {
+ scopeId: string;
+};
diff --git a/front/src/modules/ui/utilities/recoil-scope/scopes-internal/utils/createScopeInternalContext.ts b/front/src/modules/ui/utilities/recoil-scope/scopes-internal/utils/createScopeInternalContext.ts
new file mode 100644
index 000000000..e5a3c0ab2
--- /dev/null
+++ b/front/src/modules/ui/utilities/recoil-scope/scopes-internal/utils/createScopeInternalContext.ts
@@ -0,0 +1,13 @@
+import { Context, createContext } from 'react';
+
+import { ScopedStateKey } from '../types/ScopedStateKey';
+
+type ScopeInternalContext = Context;
+
+export const createScopeInternalContext = (
+ initialValue?: T,
+) => {
+ return createContext(
+ initialValue ?? null,
+ ) as ScopeInternalContext;
+};
diff --git a/front/src/modules/ui/utilities/recoil-scope/utils/createScopedFamilyState.ts b/front/src/modules/ui/utilities/recoil-scope/utils/createScopedFamilyState.ts
new file mode 100644
index 000000000..e73a891ef
--- /dev/null
+++ b/front/src/modules/ui/utilities/recoil-scope/utils/createScopedFamilyState.ts
@@ -0,0 +1,19 @@
+import { atomFamily, SerializableParam } from 'recoil';
+
+import { ScopedFamilyStateKey } from '../scopes-internal/types/ScopedFamilyStateKey';
+
+export const createScopedFamilyState = <
+ ValueType,
+ FamilyKey extends SerializableParam,
+>({
+ key,
+ defaultValue,
+}: {
+ key: string;
+ defaultValue: ValueType;
+}) => {
+ return atomFamily>({
+ key,
+ default: defaultValue,
+ });
+};
diff --git a/front/src/modules/ui/utilities/recoil-scope/utils/createScopedState.ts b/front/src/modules/ui/utilities/recoil-scope/utils/createScopedState.ts
new file mode 100644
index 000000000..74d128ac0
--- /dev/null
+++ b/front/src/modules/ui/utilities/recoil-scope/utils/createScopedState.ts
@@ -0,0 +1,14 @@
+import { atomFamily } from 'recoil';
+
+export const createScopedState = ({
+ key,
+ defaultValue,
+}: {
+ key: string;
+ defaultValue: ValueType;
+}) => {
+ return atomFamily({
+ key,
+ default: defaultValue,
+ });
+};
diff --git a/front/src/modules/ui/view-bar/components/AddFilterFromDetailsButton.tsx b/front/src/modules/ui/view-bar/components/AddFilterFromDetailsButton.tsx
index 37f13118c..7249b29cc 100644
--- a/front/src/modules/ui/view-bar/components/AddFilterFromDetailsButton.tsx
+++ b/front/src/modules/ui/view-bar/components/AddFilterFromDetailsButton.tsx
@@ -6,7 +6,7 @@ import { FilterDropdownId } from '../constants/FilterDropdownId';
export const AddFilterFromDropdownButton = () => {
const { toggleDropdown } = useDropdown({
- dropdownId: FilterDropdownId,
+ dropdownScopeId: FilterDropdownId,
});
const handleClick = () => {
diff --git a/front/src/modules/ui/view-bar/components/MultipleFiltersButton.tsx b/front/src/modules/ui/view-bar/components/MultipleFiltersButton.tsx
index b9b5636b2..1ea61b300 100644
--- a/front/src/modules/ui/view-bar/components/MultipleFiltersButton.tsx
+++ b/front/src/modules/ui/view-bar/components/MultipleFiltersButton.tsx
@@ -13,7 +13,7 @@ export const MultipleFiltersButton = () => {
const { ViewBarRecoilScopeContext } = useViewBarContext();
const { isDropdownOpen, toggleDropdown } = useDropdown({
- dropdownId: FilterDropdownId,
+ dropdownScopeId: FilterDropdownId,
});
const [, setIsFilterDropdownOperandSelectUnfolded] = useRecoilScopedState(
diff --git a/front/src/modules/ui/view-bar/components/SingleEntityFilterDropdownButton.tsx b/front/src/modules/ui/view-bar/components/SingleEntityFilterDropdownButton.tsx
index decb80a7f..00a5e6a7a 100644
--- a/front/src/modules/ui/view-bar/components/SingleEntityFilterDropdownButton.tsx
+++ b/front/src/modules/ui/view-bar/components/SingleEntityFilterDropdownButton.tsx
@@ -1,22 +1,19 @@
import React from 'react';
import { useTheme } from '@emotion/react';
-import styled from '@emotion/styled';
+import { DropdownMenu } from '@/ui/dropdown/components/DropdownMenu';
import { DropdownMenuContainer } from '@/ui/dropdown/components/DropdownMenuContainer';
-import { DropdownRecoilScopeContext } from '@/ui/dropdown/states/recoil-scope-contexts/DropdownRecoilScopeContext';
-import { IconChevronDown } from '@/ui/icon';
-import { useSetHotkeyScope } from '@/ui/utilities/hotkey/hooks/useSetHotkeyScope';
+import { StyledHeaderDropdownButton } from '@/ui/dropdown/components/StyledHeaderDropdownButton';
+import { DropdownScope } from '@/ui/dropdown/scopes/DropdownScope';
+import { IconChevronDown } from '@/ui/icon/index';
import { HotkeyScope } from '@/ui/utilities/hotkey/types/HotkeyScope';
import { useRecoilScopedState } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedState';
import { filterDefinitionUsedInDropdownScopedState } from '@/ui/view-bar/states/filterDefinitionUsedInDropdownScopedState';
-import { filterDropdownSearchInputScopedState } from '@/ui/view-bar/states/filterDropdownSearchInputScopedState';
import { selectedOperandInDropdownScopedState } from '@/ui/view-bar/states/selectedOperandInDropdownScopedState';
-import { StyledHeaderDropdownButton } from '../../dropdown/components/StyledHeaderDropdownButton';
import { useViewBarContext } from '../hooks/useViewBarContext';
import { availableFiltersScopedState } from '../states/availableFiltersScopedState';
import { filtersScopedState } from '../states/filtersScopedState';
-import { isFilterDropdownUnfoldedScopedState } from '../states/isFilterDropdownUnfoldedScopedState';
import { FilterOperand } from '../types/FilterOperand';
import { getOperandsForFilterType } from '../utils/getOperandsForFilterType';
@@ -24,19 +21,11 @@ import { FilterDropdownEntitySearchInput } from './FilterDropdownEntitySearchInp
import { FilterDropdownEntitySelect } from './FilterDropdownEntitySelect';
import { GenericEntityFilterChip } from './GenericEntityFilterChip';
-const StyledDropdownButtonContainer = styled.div`
- display: flex;
- flex-direction: column;
- position: relative;
-`;
-
export const SingleEntityFilterDropdownButton = ({
hotkeyScope,
}: {
hotkeyScope: HotkeyScope;
}) => {
- const theme = useTheme();
-
const { ViewBarRecoilScopeContext } = useViewBarContext();
const [availableFilters] = useRecoilScopedState(
@@ -45,12 +34,6 @@ export const SingleEntityFilterDropdownButton = ({
);
const availableFilter = availableFilters[0];
- const [isFilterDropdownUnfolded, setIsFilterDropdownUnfolded] =
- useRecoilScopedState(
- isFilterDropdownUnfoldedScopedState,
- DropdownRecoilScopeContext,
- );
-
const [filters] = useRecoilScopedState(
filtersScopedState,
ViewBarRecoilScopeContext,
@@ -61,11 +44,6 @@ export const SingleEntityFilterDropdownButton = ({
ViewBarRecoilScopeContext,
);
- const [, setFilterDropdownSearchInput] = useRecoilScopedState(
- filterDropdownSearchInputScopedState,
- ViewBarRecoilScopeContext,
- );
-
const [, setSelectedOperandInDropdown] = useRecoilScopedState(
selectedOperandInDropdownScopedState,
ViewBarRecoilScopeContext,
@@ -81,45 +59,37 @@ export const SingleEntityFilterDropdownButton = ({
setSelectedOperandInDropdown,
]);
- const setHotkeyScope = useSetHotkeyScope();
-
- const handleIsUnfoldedChange = (newIsUnfolded: boolean) => {
- if (newIsUnfolded) {
- setHotkeyScope(hotkeyScope.scope, hotkeyScope.customScopes);
- setIsFilterDropdownUnfolded(true);
- } else {
- setHotkeyScope(hotkeyScope.scope, hotkeyScope.customScopes);
- setIsFilterDropdownUnfolded(false);
- setFilterDropdownSearchInput('');
- }
- };
+ const theme = useTheme();
return (
-
- handleIsUnfoldedChange(!isFilterDropdownUnfolded)}
- >
- {filters[0] ? (
-
- ) : (
- 'Filter'
- )}
-
-
- {isFilterDropdownUnfolded && (
- handleIsUnfoldedChange(false)}>
-
-
-
- )}
-
+
+
+ {filters[0] ? (
+
+ ) : (
+ 'Filter'
+ )}
+
+
+ }
+ dropdownComponents={
+
+
+
+
+ }
+ />
+
);
};
diff --git a/front/src/modules/ui/view-bar/components/SortDropdownButton.tsx b/front/src/modules/ui/view-bar/components/SortDropdownButton.tsx
index 0a92b0e16..d50301808 100644
--- a/front/src/modules/ui/view-bar/components/SortDropdownButton.tsx
+++ b/front/src/modules/ui/view-bar/components/SortDropdownButton.tsx
@@ -55,7 +55,7 @@ export const SortDropdownButton = ({
const isSortSelected = sorts.length > 0;
const { toggleDropdown } = useDropdown({
- dropdownId: SortDropdownId,
+ dropdownScopeId: SortDropdownId,
});
const handleButtonClick = () => {
diff --git a/front/src/modules/ui/view-bar/components/ViewBar.tsx b/front/src/modules/ui/view-bar/components/ViewBar.tsx
index 900596d37..4bc5a4094 100644
--- a/front/src/modules/ui/view-bar/components/ViewBar.tsx
+++ b/front/src/modules/ui/view-bar/components/ViewBar.tsx
@@ -15,16 +15,16 @@ import { ViewsDropdownButton } from './ViewsDropdownButton';
export type ViewBarProps = {
className?: string;
optionsDropdownButton: ReactNode;
- optionsDropdownKey: string;
+ optionsDropdownScopeId: string;
};
export const ViewBar = ({
className,
optionsDropdownButton,
- optionsDropdownKey,
+ optionsDropdownScopeId,
}: ViewBarProps) => {
const { openDropdown: openOptionsDropdownButton } = useDropdown({
- dropdownId: optionsDropdownKey,
+ dropdownScopeId: optionsDropdownScopeId,
});
return (
diff --git a/front/src/modules/ui/view-bar/components/ViewBarDropdownButton.tsx b/front/src/modules/ui/view-bar/components/ViewBarDropdownButton.tsx
index 998b7bb3d..9dcc60e68 100644
--- a/front/src/modules/ui/view-bar/components/ViewBarDropdownButton.tsx
+++ b/front/src/modules/ui/view-bar/components/ViewBarDropdownButton.tsx
@@ -2,6 +2,7 @@ import { Keys } from 'react-hotkeys-hook';
import { Placement } from '@floating-ui/react';
import { DropdownMenu } from '@/ui/dropdown/components/DropdownMenu';
+import { DropdownScope } from '@/ui/dropdown/scopes/DropdownScope';
import { HotkeyScope } from '@/ui/utilities/hotkey/types/HotkeyScope';
type ViewBarDropdownButtonProps = {
@@ -31,17 +32,18 @@ export const ViewBarDropdownButton = ({
onOpen,
}: ViewBarDropdownButtonProps) => {
return (
-
+
+
+
);
};
diff --git a/front/src/modules/ui/view-bar/components/ViewsDropdownButton.tsx b/front/src/modules/ui/view-bar/components/ViewsDropdownButton.tsx
index f3cc8839d..b1a1d2510 100644
--- a/front/src/modules/ui/view-bar/components/ViewsDropdownButton.tsx
+++ b/front/src/modules/ui/view-bar/components/ViewsDropdownButton.tsx
@@ -105,7 +105,7 @@ export const ViewsDropdownButton = ({
);
const { isDropdownOpen, closeDropdown } = useDropdown({
- dropdownId: ViewsDropdownId,
+ dropdownScopeId: ViewsDropdownId,
});
const setViewEditMode = useSetRecoilState(viewEditModeState);
diff --git a/front/src/pages/companies/Companies.tsx b/front/src/pages/companies/Companies.tsx
index 52b7b09c1..e35a1f566 100644
--- a/front/src/pages/companies/Companies.tsx
+++ b/front/src/pages/companies/Companies.tsx
@@ -11,7 +11,6 @@ import { DataTableContextMenu } from '@/ui/data-table/context-menu/components/Da
import { useUpsertDataTableItem } from '@/ui/data-table/hooks/useUpsertDataTableItem';
import { useUpsertTableRowId } from '@/ui/data-table/hooks/useUpsertTableRowId';
import { TableRecoilScopeContext } from '@/ui/data-table/states/recoil-scope-contexts/TableRecoilScopeContext';
-import { DropdownRecoilScopeContext } from '@/ui/dropdown/states/recoil-scope-contexts/DropdownRecoilScopeContext';
import { IconBuildingSkyscraper } from '@/ui/icon';
import { PageAddButton } from '@/ui/layout/components/PageAddButton';
import { PageBody } from '@/ui/layout/components/PageBody';
@@ -58,10 +57,8 @@ export const Companies = () => {
-
-
-
-
+
+
{
hasBackButton
Icon={IconBuildingSkyscraper}
>
-
-
-
-
+
+
diff --git a/front/src/pages/opportunities/Opportunities.tsx b/front/src/pages/opportunities/Opportunities.tsx
index 9fbe1be21..b4bd84aba 100644
--- a/front/src/pages/opportunities/Opportunities.tsx
+++ b/front/src/pages/opportunities/Opportunities.tsx
@@ -3,7 +3,6 @@ import { CompanyBoardRecoilScopeContext } from '@/companies/states/recoil-scope-
import { PipelineAddButton } from '@/pipeline/components/PipelineAddButton';
import { usePipelineStages } from '@/pipeline/hooks/usePipelineStages';
import { BoardOptionsContext } from '@/ui/board/contexts/BoardOptionsContext';
-import { DropdownRecoilScopeContext } from '@/ui/dropdown/states/recoil-scope-contexts/DropdownRecoilScopeContext';
import { IconTargetArrow } from '@/ui/icon';
import { PageBody } from '@/ui/layout/components/PageBody';
import { PageContainer } from '@/ui/layout/components/PageContainer';
@@ -44,9 +43,7 @@ export const Opportunities = () => {
-
-
-
+
diff --git a/front/src/pages/people/People.tsx b/front/src/pages/people/People.tsx
index 76c18663e..e0a570ef9 100644
--- a/front/src/pages/people/People.tsx
+++ b/front/src/pages/people/People.tsx
@@ -9,7 +9,6 @@ import { DataTableContextMenu } from '@/ui/data-table/context-menu/components/Da
import { useUpsertDataTableItem } from '@/ui/data-table/hooks/useUpsertDataTableItem';
import { useUpsertTableRowId } from '@/ui/data-table/hooks/useUpsertTableRowId';
import { TableRecoilScopeContext } from '@/ui/data-table/states/recoil-scope-contexts/TableRecoilScopeContext';
-import { DropdownRecoilScopeContext } from '@/ui/dropdown/states/recoil-scope-contexts/DropdownRecoilScopeContext';
import { IconUser } from '@/ui/icon';
import { PageAddButton } from '@/ui/layout/components/PageAddButton';
import { PageBody } from '@/ui/layout/components/PageBody';
@@ -54,10 +53,8 @@ export const People = () => {
-
-
-
-
+
+
{
-
-
-
-
+
+
diff --git a/front/src/pages/tasks/Tasks.tsx b/front/src/pages/tasks/Tasks.tsx
index ca8f80b1f..3e1ace410 100644
--- a/front/src/pages/tasks/Tasks.tsx
+++ b/front/src/pages/tasks/Tasks.tsx
@@ -3,7 +3,6 @@ import styled from '@emotion/styled';
import { TasksRecoilScopeContext } from '@/activities/states/recoil-scope-contexts/TasksRecoilScopeContext';
import { PageAddTaskButton } from '@/activities/tasks/components/PageAddTaskButton';
import { TaskGroups } from '@/activities/tasks/components/TaskGroups';
-import { DropdownRecoilScopeContext } from '@/ui/dropdown/states/recoil-scope-contexts/DropdownRecoilScopeContext';
import { IconArchive, IconCheck, IconCheckbox } from '@/ui/icon/index';
import { RelationPickerHotkeyScope } from '@/ui/input/relation-picker/types/RelationPickerHotkeyScope';
import { PageBody } from '@/ui/layout/components/PageBody';
@@ -48,43 +47,41 @@ export const Tasks = () => {
return (
-
-
-
-
-
-
-
- {/* TODO: we should refactor filters as a standalone module ? */}
-
-
-
-
-
- }
- rightComponent={
-
+
+
+
+
+
+ {/* TODO: we should refactor filters as a standalone module ? */}
+
+
+
+
- }
- />
-
-
-
-
-
+
+ }
+ rightComponent={
+
+ }
+ />
+
+
+
+
);
diff --git a/front/src/types/ScopeContextProps.ts b/front/src/types/ScopeContextProps.ts
new file mode 100644
index 000000000..dcb49f3c9
--- /dev/null
+++ b/front/src/types/ScopeContextProps.ts
@@ -0,0 +1,3 @@
+export type ScopeContextProps = {
+ scopeId: string;
+} & T;