Refactor/scope and context (#1960)

* wip

* Test with Dropdown

* wip

* wip

* Finished removing DropdownRecoilScopeContext

* Fix from PR
This commit is contained in:
Lucas Bordeau
2023-10-11 15:35:47 +02:00
committed by GitHub
parent a342af74d1
commit 22451a68b3
61 changed files with 531 additions and 426 deletions

View File

@ -1,8 +1,6 @@
import { useOpenCreateActivityDrawer } from '@/activities/hooks/useOpenCreateActivityDrawer'; import { useOpenCreateActivityDrawer } from '@/activities/hooks/useOpenCreateActivityDrawer';
import { TasksRecoilScopeContext } from '@/activities/states/recoil-scope-contexts/TasksRecoilScopeContext'; import { TasksRecoilScopeContext } from '@/activities/states/recoil-scope-contexts/TasksRecoilScopeContext';
import { DropdownRecoilScopeContext } from '@/ui/dropdown/states/recoil-scope-contexts/DropdownRecoilScopeContext';
import { PageAddButton } from '@/ui/layout/components/PageAddButton'; import { PageAddButton } from '@/ui/layout/components/PageAddButton';
import { RecoilScope } from '@/ui/utilities/recoil-scope/components/RecoilScope';
import { useRecoilScopedValue } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedValue'; import { useRecoilScopedValue } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedValue';
import { filtersScopedState } from '@/ui/view-bar/states/filtersScopedState'; import { filtersScopedState } from '@/ui/view-bar/states/filtersScopedState';
import { ActivityType } from '~/generated/graphql'; import { ActivityType } from '~/generated/graphql';
@ -26,9 +24,5 @@ export const PageAddTaskButton = () => {
}); });
}; };
return ( return <PageAddButton onClick={handleClick} />;
<RecoilScope CustomRecoilScopeContext={DropdownRecoilScopeContext}>
<PageAddButton onClick={handleClick} />
</RecoilScope>
);
}; };

View File

@ -14,7 +14,7 @@ export const PipelineAddButton = () => {
const { enqueueSnackBar } = useSnackBar(); const { enqueueSnackBar } = useSnackBar();
const { closeDropdown, toggleDropdown } = useDropdown({ const { closeDropdown, toggleDropdown } = useDropdown({
dropdownId: 'add-pipeline-progress', dropdownScopeId: 'add-pipeline-progress',
}); });
const createCompanyProgress = useCreateCompanyProgress(); const createCompanyProgress = useCreateCompanyProgress();

View File

@ -3,8 +3,6 @@ import { useSearchParams } from 'react-router-dom';
import { useRecoilCallback, useRecoilState, useRecoilValue } from 'recoil'; import { useRecoilCallback, useRecoilState, useRecoilValue } from 'recoil';
import { BoardContext } from '@/companies/states/contexts/BoardContext'; import { BoardContext } from '@/companies/states/contexts/BoardContext';
import { DropdownRecoilScopeContext } from '@/ui/dropdown/states/recoil-scope-contexts/DropdownRecoilScopeContext';
import { RecoilScope } from '@/ui/utilities/recoil-scope/components/RecoilScope';
import { useRecoilScopedState } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedState'; import { useRecoilScopedState } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedState';
import { useRecoilScopedValue } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedValue'; import { useRecoilScopedValue } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedValue';
import { useRecoilScopeId } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopeId'; import { useRecoilScopeId } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopeId';
@ -19,8 +17,8 @@ import { savedBoardColumnsState } from '../states/savedBoardColumnsState';
import { canPersistBoardCardFieldsScopedFamilySelector } from '../states/selectors/canPersistBoardCardFieldsScopedFamilySelector'; import { canPersistBoardCardFieldsScopedFamilySelector } from '../states/selectors/canPersistBoardCardFieldsScopedFamilySelector';
import { canPersistBoardColumnsSelector } from '../states/selectors/canPersistBoardColumnsSelector'; import { canPersistBoardColumnsSelector } from '../states/selectors/canPersistBoardColumnsSelector';
import { BoardColumnDefinition } from '../types/BoardColumnDefinition'; import { BoardColumnDefinition } from '../types/BoardColumnDefinition';
import { BoardOptionsDropdownKey } from '../types/BoardOptionsDropdownKey';
import { BoardOptionsHotkeyScope } from '../types/BoardOptionsHotkeyScope'; import { BoardOptionsHotkeyScope } from '../types/BoardOptionsHotkeyScope';
import { BoardScopeIds } from '../types/enums/BoardScopeIds';
import { BoardOptionsDropdown } from './BoardOptionsDropdown'; import { BoardOptionsDropdown } from './BoardOptionsDropdown';
@ -101,27 +99,25 @@ export const BoardHeader = ({ className, onStageAdd }: BoardHeaderProps) => {
const canPersistView = canPersistBoardCardFields || canPersistBoardColumns; const canPersistView = canPersistBoardCardFields || canPersistBoardColumns;
return ( return (
<RecoilScope CustomRecoilScopeContext={DropdownRecoilScopeContext}> <ViewBarContext.Provider
<ViewBarContext.Provider value={{
value={{ ...viewBarContextProps,
...viewBarContextProps, canPersistViewFields: canPersistView,
canPersistViewFields: canPersistView, onCurrentViewSubmit: handleCurrentViewSubmit,
onCurrentViewSubmit: handleCurrentViewSubmit, onViewBarReset: handleViewBarReset,
onViewBarReset: handleViewBarReset, onViewSelect: handleViewSelect,
onViewSelect: handleViewSelect, }}
}} >
> <ViewBar
<ViewBar className={className}
className={className} optionsDropdownButton={
optionsDropdownButton={ <BoardOptionsDropdown
<BoardOptionsDropdown customHotkeyScope={{ scope: BoardOptionsHotkeyScope.Dropdown }}
customHotkeyScope={{ scope: BoardOptionsHotkeyScope.Dropdown }} onStageAdd={onStageAdd}
onStageAdd={onStageAdd} />
/> }
} optionsDropdownScopeId={BoardScopeIds.OptionsDropdown}
optionsDropdownKey={BoardOptionsDropdownKey} />
/> </ViewBarContext.Provider>
</ViewBarContext.Provider>
</RecoilScope>
); );
}; };

View File

@ -3,7 +3,7 @@ import { useResetRecoilState } from 'recoil';
import { ViewBarDropdownButton } from '@/ui/view-bar/components/ViewBarDropdownButton'; import { ViewBarDropdownButton } from '@/ui/view-bar/components/ViewBarDropdownButton';
import { viewEditModeState } from '@/ui/view-bar/states/viewEditModeState'; import { viewEditModeState } from '@/ui/view-bar/states/viewEditModeState';
import { BoardOptionsDropdownKey } from '../types/BoardOptionsDropdownKey'; import { BoardScopeIds } from '../types/enums/BoardScopeIds';
import { BoardOptionsDropdownButton } from './BoardOptionsDropdownButton'; import { BoardOptionsDropdownButton } from './BoardOptionsDropdownButton';
import { import {
@ -32,7 +32,7 @@ export const BoardOptionsDropdown = ({
/> />
} }
dropdownHotkeyScope={customHotkeyScope} dropdownHotkeyScope={customHotkeyScope}
dropdownId={BoardOptionsDropdownKey} dropdownId={BoardScopeIds.OptionsDropdown}
onClickOutside={resetViewEditMode} onClickOutside={resetViewEditMode}
/> />
); );

View File

@ -1,11 +1,11 @@
import { StyledHeaderDropdownButton } from '@/ui/dropdown/components/StyledHeaderDropdownButton'; import { StyledHeaderDropdownButton } from '@/ui/dropdown/components/StyledHeaderDropdownButton';
import { useDropdown } from '@/ui/dropdown/hooks/useDropdown'; import { useDropdown } from '@/ui/dropdown/hooks/useDropdown';
import { BoardOptionsDropdownKey } from '../types/BoardOptionsDropdownKey'; import { BoardScopeIds } from '../types/enums/BoardScopeIds';
export const BoardOptionsDropdownButton = () => { export const BoardOptionsDropdownButton = () => {
const { isDropdownOpen, toggleDropdown } = useDropdown({ const { isDropdownOpen, toggleDropdown } = useDropdown({
dropdownId: BoardOptionsDropdownKey, dropdownScopeId: BoardScopeIds.OptionsDropdown,
}); });
const handleClick = () => { const handleClick = () => {

View File

@ -43,7 +43,6 @@ import { savedBoardCardFieldsFamilyState } from '../states/savedBoardCardFieldsF
import { hiddenBoardCardFieldsScopedSelector } from '../states/selectors/hiddenBoardCardFieldsScopedSelector'; import { hiddenBoardCardFieldsScopedSelector } from '../states/selectors/hiddenBoardCardFieldsScopedSelector';
import { visibleBoardCardFieldsScopedSelector } from '../states/selectors/visibleBoardCardFieldsScopedSelector'; import { visibleBoardCardFieldsScopedSelector } from '../states/selectors/visibleBoardCardFieldsScopedSelector';
import { BoardColumnDefinition } from '../types/BoardColumnDefinition'; import { BoardColumnDefinition } from '../types/BoardColumnDefinition';
import { BoardOptionsDropdownKey } from '../types/BoardOptionsDropdownKey';
export type BoardOptionsDropdownContentProps = { export type BoardOptionsDropdownContentProps = {
customHotkeyScope: HotkeyScope; customHotkeyScope: HotkeyScope;
@ -144,9 +143,7 @@ export const BoardOptionsDropdownContent = ({
const { handleFieldVisibilityChange } = useBoardCardFields(); const { handleFieldVisibilityChange } = useBoardCardFields();
const { closeDropdown } = useDropdown({ const { closeDropdown } = useDropdown();
dropdownId: BoardOptionsDropdownKey,
});
useScopedHotkeys( useScopedHotkeys(
Key.Escape, Key.Escape,

View File

@ -3,8 +3,6 @@ import { userEvent, within } from '@storybook/testing-library';
import { BoardContext } from '@/companies/states/contexts/BoardContext'; import { BoardContext } from '@/companies/states/contexts/BoardContext';
import { CompanyBoardRecoilScopeContext } from '@/companies/states/recoil-scope-contexts/CompanyBoardRecoilScopeContext'; import { CompanyBoardRecoilScopeContext } from '@/companies/states/recoil-scope-contexts/CompanyBoardRecoilScopeContext';
import { DropdownRecoilScopeContext } from '@/ui/dropdown/states/recoil-scope-contexts/DropdownRecoilScopeContext';
import { RecoilScope } from '@/ui/utilities/recoil-scope/components/RecoilScope';
import { ViewBarContext } from '@/ui/view-bar/contexts/ViewBarContext'; import { ViewBarContext } from '@/ui/view-bar/contexts/ViewBarContext';
import { ComponentDecorator } from '~/testing/decorators/ComponentDecorator'; import { ComponentDecorator } from '~/testing/decorators/ComponentDecorator';
import { ComponentWithRecoilScopeDecorator } from '~/testing/decorators/ComponentWithRecoilScopeDecorator'; import { ComponentWithRecoilScopeDecorator } from '~/testing/decorators/ComponentWithRecoilScopeDecorator';
@ -26,9 +24,7 @@ const meta: Meta<typeof BoardOptionsDropdown> = {
ViewBarRecoilScopeContext: parameters.customRecoilScopeContext, ViewBarRecoilScopeContext: parameters.customRecoilScopeContext,
}} }}
> >
<RecoilScope CustomRecoilScopeContext={DropdownRecoilScopeContext}> <Story />
<Story />
</RecoilScope>
</ViewBarContext.Provider> </ViewBarContext.Provider>
</BoardContext.Provider> </BoardContext.Provider>
), ),

View File

@ -1 +0,0 @@
export const BoardOptionsDropdownKey = 'board-options';

View File

@ -0,0 +1,3 @@
export enum BoardScopeIds {
OptionsDropdown = 'board-options',
}

View File

@ -1,4 +1,5 @@
import { DropdownMenu } from '@/ui/dropdown/components/DropdownMenu'; import { DropdownMenu } from '@/ui/dropdown/components/DropdownMenu';
import { DropdownScope } from '@/ui/dropdown/scopes/DropdownScope';
import { FieldMetadata } from '@/ui/field/types/FieldMetadata'; import { FieldMetadata } from '@/ui/field/types/FieldMetadata';
import { ColumnDefinition } from '../types/ColumnDefinition'; import { ColumnDefinition } from '../types/ColumnDefinition';
@ -20,19 +21,20 @@ export const ColumnHeadWithDropdown = ({
primaryColumnKey, primaryColumnKey,
}: ColumnHeadWithDropdownProps) => { }: ColumnHeadWithDropdownProps) => {
return ( return (
<DropdownMenu <DropdownScope dropdownScopeId={column.key + '-header'}>
clickableComponent={<ColumnHead column={column} />} <DropdownMenu
dropdownId={column.key + '-header'} clickableComponent={<ColumnHead column={column} />}
dropdownComponents={ dropdownComponents={
<DataTableColumnDropdownMenu <DataTableColumnDropdownMenu
column={column} column={column}
isFirstColumn={isFirstColumn} isFirstColumn={isFirstColumn}
isLastColumn={isLastColumn} isLastColumn={isLastColumn}
primaryColumnKey={primaryColumnKey} primaryColumnKey={primaryColumnKey}
/> />
} }
dropdownHotkeyScope={{ scope: column.key + '-header' }} dropdownHotkeyScope={{ scope: column.key + '-header' }}
dropdownOffset={{ x: 0, y: -8 }} dropdownOffset={{ x: 0, y: -8 }}
/> />
</DropdownScope>
); );
}; };

View File

@ -26,7 +26,7 @@ export const DataTableColumnDropdownMenu = ({
useTableColumns(); useTableColumns();
const { closeDropdown } = useDropdown({ const { closeDropdown } = useDropdown({
dropdownId: ColumnHeadDropdownId, dropdownScopeId: ColumnHeadDropdownId,
}); });
const handleColumnMoveLeft = () => { const handleColumnMoveLeft = () => {

View File

@ -3,10 +3,8 @@ import styled from '@emotion/styled';
import { useRecoilCallback, useRecoilState } from 'recoil'; import { useRecoilCallback, useRecoilState } from 'recoil';
import { IconButton } from '@/ui/button/components/IconButton'; import { IconButton } from '@/ui/button/components/IconButton';
import { DropdownRecoilScopeContext } from '@/ui/dropdown/states/recoil-scope-contexts/DropdownRecoilScopeContext';
import { IconPlus } from '@/ui/icon'; import { IconPlus } from '@/ui/icon';
import { useTrackPointer } from '@/ui/utilities/pointer-event/hooks/useTrackPointer'; import { useTrackPointer } from '@/ui/utilities/pointer-event/hooks/useTrackPointer';
import { RecoilScope } from '@/ui/utilities/recoil-scope/components/RecoilScope';
import { useRecoilScopedValue } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedValue'; import { useRecoilScopedValue } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedValue';
import { useTableColumns } from '../hooks/useTableColumns'; import { useTableColumns } from '../hooks/useTableColumns';
@ -177,36 +175,33 @@ export const DataTableHeader = () => {
> >
<SelectAllCheckbox /> <SelectAllCheckbox />
</th> </th>
<RecoilScope CustomRecoilScopeContext={DropdownRecoilScopeContext}> {visibleTableColumns.map((column, index) => (
{visibleTableColumns.map((column, index) => ( <StyledColumnHeaderCell
<StyledColumnHeaderCell key={column.key}
key={column.key} isResizing={resizedFieldKey === column.key}
isResizing={resizedFieldKey === column.key} columnWidth={Math.max(
columnWidth={Math.max( tableColumnsByKey[column.key].size +
tableColumnsByKey[column.key].size + (resizedFieldKey === column.key ? resizeFieldOffset : 0),
(resizedFieldKey === column.key ? resizeFieldOffset : 0), COLUMN_MIN_WIDTH,
COLUMN_MIN_WIDTH, )}
)} >
> <StyledColumnHeadContainer>
<StyledColumnHeadContainer> <ColumnHeadWithDropdown
<ColumnHeadWithDropdown column={column}
column={column} isFirstColumn={index === 1}
isFirstColumn={index === 1} isLastColumn={index === visibleTableColumns.length - 1}
isLastColumn={index === visibleTableColumns.length - 1} primaryColumnKey={primaryColumn.key}
primaryColumnKey={primaryColumn.key}
/>
</StyledColumnHeadContainer>
<StyledResizeHandler
className="cursor-col-resize"
role="separator"
onPointerDown={() => {
setResizedFieldKey(column.key);
}}
/> />
</StyledColumnHeaderCell> </StyledColumnHeadContainer>
))} <StyledResizeHandler
</RecoilScope> className="cursor-col-resize"
role="separator"
onPointerDown={() => {
setResizedFieldKey(column.key);
}}
/>
</StyledColumnHeaderCell>
))}
<th> <th>
{hiddenTableColumns.length > 0 && ( {hiddenTableColumns.length > 0 && (

View File

@ -4,7 +4,7 @@ import { useDropdown } from '@/ui/dropdown/hooks/useDropdown';
export const TableOptionsDropdownButton = () => { export const TableOptionsDropdownButton = () => {
const { isDropdownOpen, toggleDropdown } = useDropdown({ const { isDropdownOpen, toggleDropdown } = useDropdown({
dropdownId: TableOptionsDropdownId, dropdownScopeId: TableOptionsDropdownId,
}); });
return ( return (

View File

@ -22,7 +22,6 @@ import { currentViewScopedSelector } from '@/ui/view-bar/states/selectors/curren
import { viewsByIdScopedSelector } from '@/ui/view-bar/states/selectors/viewsByIdScopedSelector'; import { viewsByIdScopedSelector } from '@/ui/view-bar/states/selectors/viewsByIdScopedSelector';
import { viewEditModeState } from '@/ui/view-bar/states/viewEditModeState'; import { viewEditModeState } from '@/ui/view-bar/states/viewEditModeState';
import { TableOptionsDropdownId } from '../../constants/TableOptionsDropdownId';
import { useTableColumns } from '../../hooks/useTableColumns'; import { useTableColumns } from '../../hooks/useTableColumns';
import { TableRecoilScopeContext } from '../../states/recoil-scope-contexts/TableRecoilScopeContext'; import { TableRecoilScopeContext } from '../../states/recoil-scope-contexts/TableRecoilScopeContext';
import { savedTableColumnsFamilyState } from '../../states/savedTableColumnsFamilyState'; import { savedTableColumnsFamilyState } from '../../states/savedTableColumnsFamilyState';
@ -37,9 +36,7 @@ export const TableOptionsDropdownContent = () => {
const scopeId = useRecoilScopeId(TableRecoilScopeContext); const scopeId = useRecoilScopeId(TableRecoilScopeContext);
const { onImport } = useContext(ViewBarContext); const { onImport } = useContext(ViewBarContext);
const { closeDropdown } = useDropdown({ const { closeDropdown } = useDropdown();
dropdownId: TableOptionsDropdownId,
});
const [currentMenu, setCurrentMenu] = useState<TableOptionsMenu | undefined>( const [currentMenu, setCurrentMenu] = useState<TableOptionsMenu | undefined>(
undefined, undefined,

View File

@ -1,7 +1,6 @@
import { Meta, StoryObj } from '@storybook/react'; import { Meta, StoryObj } from '@storybook/react';
import { userEvent, within } from '@storybook/testing-library'; import { userEvent, within } from '@storybook/testing-library';
import { DropdownRecoilScopeContext } from '@/ui/dropdown/states/recoil-scope-contexts/DropdownRecoilScopeContext';
import { RecoilScope } from '@/ui/utilities/recoil-scope/components/RecoilScope'; import { RecoilScope } from '@/ui/utilities/recoil-scope/components/RecoilScope';
import { ViewBarContext } from '@/ui/view-bar/contexts/ViewBarContext'; import { ViewBarContext } from '@/ui/view-bar/contexts/ViewBarContext';
import { ComponentDecorator } from '~/testing/decorators/ComponentDecorator'; import { ComponentDecorator } from '~/testing/decorators/ComponentDecorator';
@ -20,9 +19,7 @@ const meta: Meta<typeof TableOptionsDropdown> = {
ViewBarRecoilScopeContext: TableRecoilScopeContext, ViewBarRecoilScopeContext: TableRecoilScopeContext,
}} }}
> >
<RecoilScope CustomRecoilScopeContext={DropdownRecoilScopeContext}> <Story />
<Story />
</RecoilScope>
</ViewBarContext.Provider> </ViewBarContext.Provider>
</RecoilScope> </RecoilScope>
), ),

View File

@ -2,8 +2,6 @@ import { useContext } from 'react';
import { useSearchParams } from 'react-router-dom'; import { useSearchParams } from 'react-router-dom';
import { useRecoilCallback } from 'recoil'; import { useRecoilCallback } from 'recoil';
import { DropdownRecoilScopeContext } from '@/ui/dropdown/states/recoil-scope-contexts/DropdownRecoilScopeContext';
import { RecoilScope } from '@/ui/utilities/recoil-scope/components/RecoilScope';
import { useRecoilScopeId } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopeId'; import { useRecoilScopeId } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopeId';
import { ViewBar } from '@/ui/view-bar/components/ViewBar'; import { ViewBar } from '@/ui/view-bar/components/ViewBar';
import { ViewBarContext } from '@/ui/view-bar/contexts/ViewBarContext'; import { ViewBarContext } from '@/ui/view-bar/contexts/ViewBarContext';
@ -34,23 +32,21 @@ export const TableHeader = () => {
); );
return ( return (
<RecoilScope CustomRecoilScopeContext={DropdownRecoilScopeContext}> <ViewBarContext.Provider
<ViewBarContext.Provider value={{
value={{ ...viewBarContextProps,
...viewBarContextProps, onCurrentViewSubmit,
onCurrentViewSubmit, onViewSelect: handleViewSelect,
onViewSelect: handleViewSelect, }}
}} >
> <ViewBar
<ViewBar optionsDropdownButton={
optionsDropdownButton={ <TableOptionsDropdown
<TableOptionsDropdown customHotkeyScope={{ scope: TableOptionsHotkeyScope.Dropdown }}
customHotkeyScope={{ scope: TableOptionsHotkeyScope.Dropdown }} />
/> }
} optionsDropdownScopeId={TableOptionsDropdownId}
optionsDropdownKey={TableOptionsDropdownId} />
/> </ViewBarContext.Provider>
</ViewBarContext.Provider>
</RecoilScope>
); );
}; };

View File

@ -16,7 +16,6 @@ import { DropdownToggleEffect } from './DropdownToggleEffect';
type DropdownMenuProps = { type DropdownMenuProps = {
clickableComponent?: JSX.Element | JSX.Element[]; clickableComponent?: JSX.Element | JSX.Element[];
dropdownComponents: JSX.Element | JSX.Element[]; dropdownComponents: JSX.Element | JSX.Element[];
dropdownId: string;
hotkey?: { hotkey?: {
key: Keys; key: Keys;
scope: string; scope: string;
@ -32,7 +31,6 @@ type DropdownMenuProps = {
export const DropdownMenu = ({ export const DropdownMenu = ({
clickableComponent, clickableComponent,
dropdownComponents, dropdownComponents,
dropdownId,
hotkey, hotkey,
dropdownHotkeyScope, dropdownHotkeyScope,
dropdownPlacement = 'bottom-end', dropdownPlacement = 'bottom-end',
@ -43,9 +41,7 @@ export const DropdownMenu = ({
}: DropdownMenuProps) => { }: DropdownMenuProps) => {
const containerRef = useRef<HTMLDivElement>(null); const containerRef = useRef<HTMLDivElement>(null);
const { isDropdownOpen, toggleDropdown, closeDropdown } = useDropdown({ const { isDropdownOpen, toggleDropdown, closeDropdown } = useDropdown();
dropdownId,
});
const { refs, floatingStyles } = useFloating({ const { refs, floatingStyles } = useFloating({
placement: dropdownPlacement, placement: dropdownPlacement,
@ -71,8 +67,7 @@ export const DropdownMenu = ({
}); });
useInternalHotkeyScopeManagement({ useInternalHotkeyScopeManagement({
dropdownId, dropdownHotkeyScopeFromParent: dropdownHotkeyScope,
dropdownHotkeyScope,
}); });
useScopedHotkeys( useScopedHotkeys(
@ -83,6 +78,7 @@ export const DropdownMenu = ({
dropdownHotkeyScope.scope, dropdownHotkeyScope.scope,
[closeDropdown], [closeDropdown],
); );
return ( return (
<div ref={containerRef}> <div ref={containerRef}>
{clickableComponent && ( {clickableComponent && (
@ -101,11 +97,7 @@ export const DropdownMenu = ({
{dropdownComponents} {dropdownComponents}
</div> </div>
)} )}
<DropdownToggleEffect <DropdownToggleEffect onDropdownClose={onClose} onDropdownOpen={onOpen} />
dropdownId={dropdownId}
onDropdownClose={onClose}
onDropdownOpen={onOpen}
/>
</div> </div>
); );
}; };

View File

@ -3,15 +3,13 @@ import { useEffect } from 'react';
import { useDropdown } from '@/ui/dropdown/hooks/useDropdown'; import { useDropdown } from '@/ui/dropdown/hooks/useDropdown';
export const DropdownToggleEffect = ({ export const DropdownToggleEffect = ({
dropdownId,
onDropdownClose, onDropdownClose,
onDropdownOpen, onDropdownOpen,
}: { }: {
dropdownId: string;
onDropdownClose?: () => void; onDropdownClose?: () => void;
onDropdownOpen?: () => void; onDropdownOpen?: () => void;
}) => { }) => {
const { isDropdownOpen } = useDropdown({ dropdownId }); const { isDropdownOpen } = useDropdown();
useEffect(() => { useEffect(() => {
if (isDropdownOpen) { if (isDropdownOpen) {

View File

@ -1,47 +1,52 @@
import { usePreviousHotkeyScope } from '@/ui/utilities/hotkey/hooks/usePreviousHotkeyScope'; 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 { DropdownScopeInternalContext } from '../scopes/scope-internal-context/DropdownScopeInternalContext';
import { isDropdownButtonOpenScopedFamilyState } from '../states/isDropdownButtonOpenScopedFamilyState';
import { DropdownRecoilScopeContext } from '../states/recoil-scope-contexts/DropdownRecoilScopeContext';
export const useDropdown = ({ dropdownId }: { dropdownId: string }) => { import { useDropdownStates } from './useDropdownStates';
type UseDropdownProps = {
dropdownScopeId?: string;
};
export const useDropdown = (props?: UseDropdownProps) => {
const { const {
setHotkeyScopeAndMemorizePreviousScope, setHotkeyScopeAndMemorizePreviousScope,
goBackToPreviousHotkeyScope, goBackToPreviousHotkeyScope,
} = usePreviousHotkeyScope(); } = usePreviousHotkeyScope();
const [isDropdownButtonOpen, setIsDropdownButtonOpen] = const scopeId = useAvailableScopeIdOrThrow(
useRecoilScopedFamilyState( DropdownScopeInternalContext,
isDropdownButtonOpenScopedFamilyState, props?.dropdownScopeId,
dropdownId,
DropdownRecoilScopeContext,
);
const [dropdownButtonHotkeyScope] = useRecoilScopedFamilyState(
dropdownButtonHotkeyScopeScopedFamilyState,
dropdownId,
DropdownRecoilScopeContext,
); );
const {
dropdownHotkeyScope,
setDropdownHotkeyScope,
isDropdownOpen,
setIsDropdownOpen,
} = useDropdownStates({
scopeId,
});
const closeDropdownButton = () => { const closeDropdownButton = () => {
goBackToPreviousHotkeyScope(); goBackToPreviousHotkeyScope();
setIsDropdownButtonOpen(false); setIsDropdownOpen(false);
}; };
const openDropdownButton = () => { const openDropdownButton = () => {
setIsDropdownButtonOpen(true); setIsDropdownOpen(true);
if (dropdownButtonHotkeyScope) { if (dropdownHotkeyScope) {
setHotkeyScopeAndMemorizePreviousScope( setHotkeyScopeAndMemorizePreviousScope(
dropdownButtonHotkeyScope.scope, dropdownHotkeyScope.scope,
dropdownButtonHotkeyScope.customScopes, dropdownHotkeyScope.customScopes,
); );
} }
}; };
const toggleDropdownButton = () => { const toggleDropdownButton = () => {
if (isDropdownButtonOpen) { if (isDropdownOpen) {
closeDropdownButton(); closeDropdownButton();
} else { } else {
openDropdownButton(); openDropdownButton();
@ -49,9 +54,12 @@ export const useDropdown = ({ dropdownId }: { dropdownId: string }) => {
}; };
return { return {
isDropdownOpen: isDropdownButtonOpen, isDropdownOpen: isDropdownOpen,
closeDropdown: closeDropdownButton, closeDropdown: closeDropdownButton,
toggleDropdown: toggleDropdownButton, toggleDropdown: toggleDropdownButton,
openDropdown: openDropdownButton, openDropdown: openDropdownButton,
scopeId,
dropdownHotkeyScope,
setDropdownHotkeyScope,
}; };
}; };

View File

@ -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,
};
};

View File

@ -1,33 +1,24 @@
import { useEffect } from 'react'; import { useEffect } from 'react';
import { HotkeyScope } from '@/ui/utilities/hotkey/types/HotkeyScope'; import { HotkeyScope } from '@/ui/utilities/hotkey/types/HotkeyScope';
import { useRecoilScopedFamilyState } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedFamilyState';
import { isDeeplyEqual } from '~/utils/isDeeplyEqual'; import { isDeeplyEqual } from '~/utils/isDeeplyEqual';
import { dropdownButtonHotkeyScopeScopedFamilyState } from '../states/dropdownButtonHotkeyScopeScopedFamilyState'; import { useDropdown } from './useDropdown';
import { DropdownRecoilScopeContext } from '../states/recoil-scope-contexts/DropdownRecoilScopeContext';
export const useInternalHotkeyScopeManagement = ({ export const useInternalHotkeyScopeManagement = ({
dropdownId, dropdownHotkeyScopeFromParent,
dropdownHotkeyScope,
}: { }: {
dropdownId: string; dropdownHotkeyScopeFromParent?: HotkeyScope;
dropdownHotkeyScope?: HotkeyScope;
}) => { }) => {
const [dropdownButtonHotkeyScope, setDropdownButtonHotkeyScope] = const { dropdownHotkeyScope, setDropdownHotkeyScope } = useDropdown();
useRecoilScopedFamilyState(
dropdownButtonHotkeyScopeScopedFamilyState,
dropdownId,
DropdownRecoilScopeContext,
);
useEffect(() => { useEffect(() => {
if (!isDeeplyEqual(dropdownButtonHotkeyScope, dropdownHotkeyScope)) { if (!isDeeplyEqual(dropdownHotkeyScopeFromParent, dropdownHotkeyScope)) {
setDropdownButtonHotkeyScope(dropdownHotkeyScope); setDropdownHotkeyScope(dropdownHotkeyScopeFromParent);
} }
}, [ }, [
dropdownHotkeyScope, dropdownHotkeyScope,
dropdownButtonHotkeyScope, dropdownHotkeyScopeFromParent,
setDropdownButtonHotkeyScope, setDropdownHotkeyScope,
]); ]);
}; };

View File

@ -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 (
<DropdownScopeInternalContext.Provider value={{ scopeId: dropdownScopeId }}>
{children}
</DropdownScopeInternalContext.Provider>
);
};

View File

@ -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<DropdownScopeInternalContextProps>();

View File

@ -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,
});

View File

@ -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,
});

View File

@ -1,9 +0,0 @@
import { atomFamily } from 'recoil';
export const isDropdownButtonOpenScopedFamilyState = atomFamily<
boolean,
string
>({
key: 'isDropdownButtonOpenScopedState',
default: false,
});

View File

@ -0,0 +1,6 @@
import { createScopedState } from '@/ui/utilities/recoil-scope/utils/createScopedState';
export const isDropdownOpenScopedState = createScopedState<boolean>({
key: 'isDropdownOpenScopedState',
defaultValue: false,
});

View File

@ -1,3 +0,0 @@
import { createContext } from 'react';
export const DropdownRecoilScopeContext = createContext<string | null>(null);

View File

@ -8,6 +8,7 @@ import { CountryCallingCode } from 'libphonenumber-js';
import { DropdownMenu } from '@/ui/dropdown/components/DropdownMenu'; import { DropdownMenu } from '@/ui/dropdown/components/DropdownMenu';
import { useDropdown } from '@/ui/dropdown/hooks/useDropdown'; import { useDropdown } from '@/ui/dropdown/hooks/useDropdown';
import { DropdownScope } from '@/ui/dropdown/scopes/DropdownScope';
import { IconChevronDown } from '@/ui/icon'; import { IconChevronDown } from '@/ui/icon';
import { IconWorld } from '../constants/icons'; import { IconWorld } from '../constants/icons';
@ -77,7 +78,7 @@ export const CountryPickerDropdownButton = ({
const [selectedCountry, setSelectedCountry] = useState<Country>(); const [selectedCountry, setSelectedCountry] = useState<Country>();
const { isDropdownOpen, closeDropdown } = useDropdown({ const { isDropdownOpen, closeDropdown } = useDropdown({
dropdownId: 'country-picker', dropdownScopeId: 'country-picker',
}); });
const handleChange = (countryCode: string) => { const handleChange = (countryCode: string) => {
@ -122,25 +123,26 @@ export const CountryPickerDropdownButton = ({
}, [countries, value]); }, [countries, value]);
return ( return (
<DropdownMenu <DropdownScope dropdownScopeId="country-picker">
dropdownId="country-picker" <DropdownMenu
dropdownHotkeyScope={{ scope: CountryPickerHotkeyScope.CountryPicker }} dropdownHotkeyScope={{ scope: CountryPickerHotkeyScope.CountryPicker }}
clickableComponent={ clickableComponent={
<StyledDropdownButtonContainer isUnfolded={isDropdownOpen}> <StyledDropdownButtonContainer isUnfolded={isDropdownOpen}>
<StyledIconContainer> <StyledIconContainer>
{selectedCountry ? <selectedCountry.Flag /> : <IconWorld />} {selectedCountry ? <selectedCountry.Flag /> : <IconWorld />}
<IconChevronDown size={theme.icon.size.sm} /> <IconChevronDown size={theme.icon.size.sm} />
</StyledIconContainer> </StyledIconContainer>
</StyledDropdownButtonContainer> </StyledDropdownButtonContainer>
} }
dropdownComponents={ dropdownComponents={
<CountryPickerDropdownSelect <CountryPickerDropdownSelect
countries={countries} countries={countries}
selectedCountry={selectedCountry} selectedCountry={selectedCountry}
onChange={handleChange} onChange={handleChange}
/> />
} }
dropdownOffset={{ x: -60, y: -34 }} dropdownOffset={{ x: -60, y: -34 }}
/> />
</DropdownScope>
); );
}; };

View File

@ -2,9 +2,6 @@ import { useEffect, useRef, useState } from 'react';
import ReactPhoneNumberInput from 'react-phone-number-input'; import ReactPhoneNumberInput from 'react-phone-number-input';
import styled from '@emotion/styled'; 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 { useRegisterInputEvents } from '../hooks/useRegisterInputEvents';
import { CountryPickerDropdownButton } from './CountryPickerDropdownButton'; import { CountryPickerDropdownButton } from './CountryPickerDropdownButton';
@ -86,17 +83,15 @@ export const PhoneInput = ({
return ( return (
<StyledContainer ref={wrapperRef}> <StyledContainer ref={wrapperRef}>
<RecoilScope CustomRecoilScopeContext={DropdownRecoilScopeContext}> <StyledCustomPhoneInput
<StyledCustomPhoneInput autoFocus={autoFocus}
autoFocus={autoFocus} placeholder="Phone number"
placeholder="Phone number" value={value}
value={value} onChange={setInternalValue}
onChange={setInternalValue} international={true}
international={true} withCountryCallingCode={true}
withCountryCallingCode={true} countrySelectComponent={CountryPickerDropdownButton}
countrySelectComponent={CountryPickerDropdownButton} />
/>
</RecoilScope>
</StyledContainer> </StyledContainer>
); );
}; };

View File

@ -22,7 +22,7 @@ export const ShowPageAddButton = ({
entity: ActivityTargetableEntity; entity: ActivityTargetableEntity;
}) => { }) => {
const { closeDropdown, toggleDropdown } = useDropdown({ const { closeDropdown, toggleDropdown } = useDropdown({
dropdownId: 'add-show-page', dropdownScopeId: 'add-show-page',
}); });
const openCreateActivity = useOpenCreateActivityDrawer(); const openCreateActivity = useOpenCreateActivityDrawer();

View File

@ -5,6 +5,10 @@ import { RecoilScopeContext as RecoilScopeContextType } from '@/types/RecoilScop
import { RecoilScopeContext } from '../states/RecoilScopeContext'; import { RecoilScopeContext } from '../states/RecoilScopeContext';
/**
*
* @deprecated Use a custom scope context instead, see example with DropdownScope
*/
export const RecoilScope = ({ export const RecoilScope = ({
children, children,
scopeId, scopeId,

View File

@ -1,5 +1,9 @@
import { Context, useContext } from 'react'; 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<string | null>) => { export const useContextScopeId = (SpecificContext: Context<string | null>) => {
const recoilScopeId = useContext(SpecificContext); const recoilScopeId = useContext(SpecificContext);

View File

@ -2,6 +2,9 @@ import { useContext } from 'react';
import { RecoilScopeContext } from '@/types/RecoilScopeContext'; 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) => { export const useRecoilScopeId = (RecoilScopeContext: RecoilScopeContext) => {
const recoilScopeId = useContext(RecoilScopeContext); const recoilScopeId = useContext(RecoilScopeContext);

View File

@ -1,25 +1,21 @@
import { Context, useContext } from 'react'; import { RecoilState, SerializableParam, useRecoilState } from 'recoil';
import { RecoilState, useRecoilState } from 'recoil';
import { RecoilScopeContext } from '../states/RecoilScopeContext'; import { ScopedFamilyStateKey } from '../scopes-internal/types/ScopedFamilyStateKey';
export const useRecoilScopedFamilyState = <StateType>( export const useRecoilScopedFamilyState = <
recoilState: (familyUniqueId: string) => RecoilState<StateType>, StateType,
uniqueIdInRecoilScope: string, FamilyKey extends SerializableParam,
CustomRecoilScopeContext?: Context<string | null>, >(
recoilState: (
scopedFamilyKey: ScopedFamilyStateKey<FamilyKey>,
) => RecoilState<StateType>,
scopeId: string,
familyKey: FamilyKey,
) => { ) => {
const recoilScopeId = useContext( return useRecoilState<StateType>(
CustomRecoilScopeContext ?? RecoilScopeContext, 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<StateType>(recoilState(familyUniqueId));
}; };

View File

@ -0,0 +1,14 @@
import { RecoilState, useRecoilState } from 'recoil';
import { ScopedStateKey } from '../scopes-internal/types/ScopedStateKey';
export const useRecoilScopedStateV2 = <StateType>(
recoilState: (scopedKey: ScopedStateKey) => RecoilState<StateType>,
scopeId: string,
) => {
return useRecoilState<StateType>(
recoilState({
scopeId,
}),
);
};

View File

@ -3,6 +3,9 @@ import { RecoilState, RecoilValueReadOnly, useRecoilValue } from 'recoil';
import { RecoilScopeContext } from '../states/RecoilScopeContext'; import { RecoilScopeContext } from '../states/RecoilScopeContext';
/**
* @deprecated use useRecoilScopedStateV2 instead
*/
export const useRecoilScopedValue = <T>( export const useRecoilScopedValue = <T>(
recoilState: (param: string) => RecoilState<T> | RecoilValueReadOnly<T>, recoilState: (param: string) => RecoilState<T> | RecoilValueReadOnly<T>,
CustomRecoilScopeContext?: Context<string | null>, CustomRecoilScopeContext?: Context<string | null>,

View File

@ -0,0 +1,14 @@
import { RecoilState, useRecoilValue } from 'recoil';
import { ScopedStateKey } from '../scopes-internal/types/ScopedStateKey';
export const useRecoilScopedValueV2 = <StateType>(
recoilState: (scopedKey: ScopedStateKey) => RecoilState<StateType>,
scopeId: string,
) => {
return useRecoilValue<StateType>(
recoilState({
scopeId,
}),
);
};

View File

@ -0,0 +1,20 @@
import { ScopeInternalContext } from '../types/ScopeInternalContext';
import { useScopeInternalContext } from './useScopeInternalContext';
export const useAvailableScopeIdOrThrow = <T extends { scopeId: string }>(
Context: ScopeInternalContext<T>,
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.');
}
};

View File

@ -0,0 +1,11 @@
import { useContext } from 'react';
import { ScopeInternalContext } from '../types/ScopeInternalContext';
export const useScopeInternalContext = <T extends { scopeId: string }>(
Context: ScopeInternalContext<T>,
) => {
const context = useContext(Context);
return context;
};

View File

@ -0,0 +1,19 @@
import { useContext } from 'react';
import { isDefined } from '~/utils/isDefined';
import { ScopeInternalContext } from '../types/ScopeInternalContext';
export const useScopeInternalContextOrThrow = <T extends { scopeId: string }>(
Context: ScopeInternalContext<T>,
) => {
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;
};

View File

@ -0,0 +1,4 @@
import { Context } from 'react';
export type ScopeInternalContext<T extends { scopeId: string }> =
Context<T | null>;

View File

@ -0,0 +1,6 @@
import { SerializableParam } from 'recoil';
export type ScopedFamilyStateKey<FamilyKey extends SerializableParam> = {
scopeId: string;
familyKey: FamilyKey;
};

View File

@ -0,0 +1,3 @@
export type ScopedStateKey = {
scopeId: string;
};

View File

@ -0,0 +1,13 @@
import { Context, createContext } from 'react';
import { ScopedStateKey } from '../types/ScopedStateKey';
type ScopeInternalContext<T extends ScopedStateKey> = Context<T | null>;
export const createScopeInternalContext = <T extends ScopedStateKey>(
initialValue?: T,
) => {
return createContext<T | null>(
initialValue ?? null,
) as ScopeInternalContext<T>;
};

View File

@ -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<ValueType, ScopedFamilyStateKey<FamilyKey>>({
key,
default: defaultValue,
});
};

View File

@ -0,0 +1,14 @@
import { atomFamily } from 'recoil';
export const createScopedState = <ValueType>({
key,
defaultValue,
}: {
key: string;
defaultValue: ValueType;
}) => {
return atomFamily<ValueType, { scopeId: string }>({
key,
default: defaultValue,
});
};

View File

@ -6,7 +6,7 @@ import { FilterDropdownId } from '../constants/FilterDropdownId';
export const AddFilterFromDropdownButton = () => { export const AddFilterFromDropdownButton = () => {
const { toggleDropdown } = useDropdown({ const { toggleDropdown } = useDropdown({
dropdownId: FilterDropdownId, dropdownScopeId: FilterDropdownId,
}); });
const handleClick = () => { const handleClick = () => {

View File

@ -13,7 +13,7 @@ export const MultipleFiltersButton = () => {
const { ViewBarRecoilScopeContext } = useViewBarContext(); const { ViewBarRecoilScopeContext } = useViewBarContext();
const { isDropdownOpen, toggleDropdown } = useDropdown({ const { isDropdownOpen, toggleDropdown } = useDropdown({
dropdownId: FilterDropdownId, dropdownScopeId: FilterDropdownId,
}); });
const [, setIsFilterDropdownOperandSelectUnfolded] = useRecoilScopedState( const [, setIsFilterDropdownOperandSelectUnfolded] = useRecoilScopedState(

View File

@ -1,22 +1,19 @@
import React from 'react'; import React from 'react';
import { useTheme } from '@emotion/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 { DropdownMenuContainer } from '@/ui/dropdown/components/DropdownMenuContainer';
import { DropdownRecoilScopeContext } from '@/ui/dropdown/states/recoil-scope-contexts/DropdownRecoilScopeContext'; import { StyledHeaderDropdownButton } from '@/ui/dropdown/components/StyledHeaderDropdownButton';
import { IconChevronDown } from '@/ui/icon'; import { DropdownScope } from '@/ui/dropdown/scopes/DropdownScope';
import { useSetHotkeyScope } from '@/ui/utilities/hotkey/hooks/useSetHotkeyScope'; import { IconChevronDown } from '@/ui/icon/index';
import { HotkeyScope } from '@/ui/utilities/hotkey/types/HotkeyScope'; import { HotkeyScope } from '@/ui/utilities/hotkey/types/HotkeyScope';
import { useRecoilScopedState } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedState'; import { useRecoilScopedState } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedState';
import { filterDefinitionUsedInDropdownScopedState } from '@/ui/view-bar/states/filterDefinitionUsedInDropdownScopedState'; 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 { selectedOperandInDropdownScopedState } from '@/ui/view-bar/states/selectedOperandInDropdownScopedState';
import { StyledHeaderDropdownButton } from '../../dropdown/components/StyledHeaderDropdownButton';
import { useViewBarContext } from '../hooks/useViewBarContext'; import { useViewBarContext } from '../hooks/useViewBarContext';
import { availableFiltersScopedState } from '../states/availableFiltersScopedState'; import { availableFiltersScopedState } from '../states/availableFiltersScopedState';
import { filtersScopedState } from '../states/filtersScopedState'; import { filtersScopedState } from '../states/filtersScopedState';
import { isFilterDropdownUnfoldedScopedState } from '../states/isFilterDropdownUnfoldedScopedState';
import { FilterOperand } from '../types/FilterOperand'; import { FilterOperand } from '../types/FilterOperand';
import { getOperandsForFilterType } from '../utils/getOperandsForFilterType'; import { getOperandsForFilterType } from '../utils/getOperandsForFilterType';
@ -24,19 +21,11 @@ import { FilterDropdownEntitySearchInput } from './FilterDropdownEntitySearchInp
import { FilterDropdownEntitySelect } from './FilterDropdownEntitySelect'; import { FilterDropdownEntitySelect } from './FilterDropdownEntitySelect';
import { GenericEntityFilterChip } from './GenericEntityFilterChip'; import { GenericEntityFilterChip } from './GenericEntityFilterChip';
const StyledDropdownButtonContainer = styled.div`
display: flex;
flex-direction: column;
position: relative;
`;
export const SingleEntityFilterDropdownButton = ({ export const SingleEntityFilterDropdownButton = ({
hotkeyScope, hotkeyScope,
}: { }: {
hotkeyScope: HotkeyScope; hotkeyScope: HotkeyScope;
}) => { }) => {
const theme = useTheme();
const { ViewBarRecoilScopeContext } = useViewBarContext(); const { ViewBarRecoilScopeContext } = useViewBarContext();
const [availableFilters] = useRecoilScopedState( const [availableFilters] = useRecoilScopedState(
@ -45,12 +34,6 @@ export const SingleEntityFilterDropdownButton = ({
); );
const availableFilter = availableFilters[0]; const availableFilter = availableFilters[0];
const [isFilterDropdownUnfolded, setIsFilterDropdownUnfolded] =
useRecoilScopedState(
isFilterDropdownUnfoldedScopedState,
DropdownRecoilScopeContext,
);
const [filters] = useRecoilScopedState( const [filters] = useRecoilScopedState(
filtersScopedState, filtersScopedState,
ViewBarRecoilScopeContext, ViewBarRecoilScopeContext,
@ -61,11 +44,6 @@ export const SingleEntityFilterDropdownButton = ({
ViewBarRecoilScopeContext, ViewBarRecoilScopeContext,
); );
const [, setFilterDropdownSearchInput] = useRecoilScopedState(
filterDropdownSearchInputScopedState,
ViewBarRecoilScopeContext,
);
const [, setSelectedOperandInDropdown] = useRecoilScopedState( const [, setSelectedOperandInDropdown] = useRecoilScopedState(
selectedOperandInDropdownScopedState, selectedOperandInDropdownScopedState,
ViewBarRecoilScopeContext, ViewBarRecoilScopeContext,
@ -81,45 +59,37 @@ export const SingleEntityFilterDropdownButton = ({
setSelectedOperandInDropdown, setSelectedOperandInDropdown,
]); ]);
const setHotkeyScope = useSetHotkeyScope(); const theme = useTheme();
const handleIsUnfoldedChange = (newIsUnfolded: boolean) => {
if (newIsUnfolded) {
setHotkeyScope(hotkeyScope.scope, hotkeyScope.customScopes);
setIsFilterDropdownUnfolded(true);
} else {
setHotkeyScope(hotkeyScope.scope, hotkeyScope.customScopes);
setIsFilterDropdownUnfolded(false);
setFilterDropdownSearchInput('');
}
};
return ( return (
<StyledDropdownButtonContainer data-select-disable> <DropdownScope dropdownScopeId="single-entity-filter-dropdown">
<StyledHeaderDropdownButton <DropdownMenu
isUnfolded={isFilterDropdownUnfolded} dropdownHotkeyScope={hotkeyScope}
onClick={() => handleIsUnfoldedChange(!isFilterDropdownUnfolded)} dropdownOffset={{ x: 0, y: -28 }}
> clickableComponent={
{filters[0] ? ( <StyledHeaderDropdownButton>
<GenericEntityFilterChip {filters[0] ? (
filter={filters[0]} <GenericEntityFilterChip
Icon={ filter={filters[0]}
filters[0].operand === FilterOperand.IsNotNull Icon={
? availableFilter.SelectAllIcon filters[0].operand === FilterOperand.IsNotNull
: undefined ? availableFilter.SelectAllIcon
} : undefined
/> }
) : ( />
'Filter' ) : (
)} 'Filter'
<IconChevronDown size={theme.icon.size.md} /> )}
</StyledHeaderDropdownButton> <IconChevronDown size={theme.icon.size.md} />
{isFilterDropdownUnfolded && ( </StyledHeaderDropdownButton>
<DropdownMenuContainer onClose={() => handleIsUnfoldedChange(false)}> }
<FilterDropdownEntitySearchInput /> dropdownComponents={
<FilterDropdownEntitySelect /> <DropdownMenuContainer>
</DropdownMenuContainer> <FilterDropdownEntitySearchInput />
)} <FilterDropdownEntitySelect />
</StyledDropdownButtonContainer> </DropdownMenuContainer>
}
/>
</DropdownScope>
); );
}; };

View File

@ -55,7 +55,7 @@ export const SortDropdownButton = ({
const isSortSelected = sorts.length > 0; const isSortSelected = sorts.length > 0;
const { toggleDropdown } = useDropdown({ const { toggleDropdown } = useDropdown({
dropdownId: SortDropdownId, dropdownScopeId: SortDropdownId,
}); });
const handleButtonClick = () => { const handleButtonClick = () => {

View File

@ -15,16 +15,16 @@ import { ViewsDropdownButton } from './ViewsDropdownButton';
export type ViewBarProps = { export type ViewBarProps = {
className?: string; className?: string;
optionsDropdownButton: ReactNode; optionsDropdownButton: ReactNode;
optionsDropdownKey: string; optionsDropdownScopeId: string;
}; };
export const ViewBar = ({ export const ViewBar = ({
className, className,
optionsDropdownButton, optionsDropdownButton,
optionsDropdownKey, optionsDropdownScopeId,
}: ViewBarProps) => { }: ViewBarProps) => {
const { openDropdown: openOptionsDropdownButton } = useDropdown({ const { openDropdown: openOptionsDropdownButton } = useDropdown({
dropdownId: optionsDropdownKey, dropdownScopeId: optionsDropdownScopeId,
}); });
return ( return (

View File

@ -2,6 +2,7 @@ import { Keys } from 'react-hotkeys-hook';
import { Placement } from '@floating-ui/react'; import { Placement } from '@floating-ui/react';
import { DropdownMenu } from '@/ui/dropdown/components/DropdownMenu'; import { DropdownMenu } from '@/ui/dropdown/components/DropdownMenu';
import { DropdownScope } from '@/ui/dropdown/scopes/DropdownScope';
import { HotkeyScope } from '@/ui/utilities/hotkey/types/HotkeyScope'; import { HotkeyScope } from '@/ui/utilities/hotkey/types/HotkeyScope';
type ViewBarDropdownButtonProps = { type ViewBarDropdownButtonProps = {
@ -31,17 +32,18 @@ export const ViewBarDropdownButton = ({
onOpen, onOpen,
}: ViewBarDropdownButtonProps) => { }: ViewBarDropdownButtonProps) => {
return ( return (
<DropdownMenu <DropdownScope dropdownScopeId={dropdownId}>
clickableComponent={buttonComponent} <DropdownMenu
dropdownComponents={dropdownComponents} clickableComponent={buttonComponent}
dropdownId={dropdownId} dropdownComponents={dropdownComponents}
hotkey={hotkey} hotkey={hotkey}
dropdownHotkeyScope={dropdownHotkeyScope} dropdownHotkeyScope={dropdownHotkeyScope}
dropdownOffset={{ x: 0, y: 8 }} dropdownOffset={{ x: 0, y: 8 }}
dropdownPlacement={dropdownPlacement} dropdownPlacement={dropdownPlacement}
onClickOutside={onClickOutside} onClickOutside={onClickOutside}
onClose={onClose} onClose={onClose}
onOpen={onOpen} onOpen={onOpen}
/> />
</DropdownScope>
); );
}; };

View File

@ -105,7 +105,7 @@ export const ViewsDropdownButton = ({
); );
const { isDropdownOpen, closeDropdown } = useDropdown({ const { isDropdownOpen, closeDropdown } = useDropdown({
dropdownId: ViewsDropdownId, dropdownScopeId: ViewsDropdownId,
}); });
const setViewEditMode = useSetRecoilState(viewEditModeState); const setViewEditMode = useSetRecoilState(viewEditModeState);

View File

@ -11,7 +11,6 @@ import { DataTableContextMenu } from '@/ui/data-table/context-menu/components/Da
import { useUpsertDataTableItem } from '@/ui/data-table/hooks/useUpsertDataTableItem'; import { useUpsertDataTableItem } from '@/ui/data-table/hooks/useUpsertDataTableItem';
import { useUpsertTableRowId } from '@/ui/data-table/hooks/useUpsertTableRowId'; import { useUpsertTableRowId } from '@/ui/data-table/hooks/useUpsertTableRowId';
import { TableRecoilScopeContext } from '@/ui/data-table/states/recoil-scope-contexts/TableRecoilScopeContext'; 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 { IconBuildingSkyscraper } from '@/ui/icon';
import { PageAddButton } from '@/ui/layout/components/PageAddButton'; import { PageAddButton } from '@/ui/layout/components/PageAddButton';
import { PageBody } from '@/ui/layout/components/PageBody'; import { PageBody } from '@/ui/layout/components/PageBody';
@ -58,10 +57,8 @@ export const Companies = () => {
<SpreadsheetImportProvider> <SpreadsheetImportProvider>
<PageContainer> <PageContainer>
<PageHeader title="Companies" Icon={IconBuildingSkyscraper}> <PageHeader title="Companies" Icon={IconBuildingSkyscraper}>
<RecoilScope CustomRecoilScopeContext={DropdownRecoilScopeContext}> <PageHotkeysEffect onAddButtonClick={handleAddButtonClick} />
<PageHotkeysEffect onAddButtonClick={handleAddButtonClick} /> <PageAddButton onClick={handleAddButtonClick} />
<PageAddButton onClick={handleAddButtonClick} />
</RecoilScope>
</PageHeader> </PageHeader>
<PageBody> <PageBody>
<RecoilScope <RecoilScope

View File

@ -6,7 +6,6 @@ import { CompanyTeam } from '@/companies/components/CompanyTeam';
import { useCompanyQuery } from '@/companies/hooks/useCompanyQuery'; import { useCompanyQuery } from '@/companies/hooks/useCompanyQuery';
import { useFavorites } from '@/favorites/hooks/useFavorites'; import { useFavorites } from '@/favorites/hooks/useFavorites';
import { AppPath } from '@/types/AppPath'; import { AppPath } from '@/types/AppPath';
import { DropdownRecoilScopeContext } from '@/ui/dropdown/states/recoil-scope-contexts/DropdownRecoilScopeContext';
import { FieldContext } from '@/ui/field/contexts/FieldContext'; import { FieldContext } from '@/ui/field/contexts/FieldContext';
import { IconBuildingSkyscraper } from '@/ui/icon'; import { IconBuildingSkyscraper } from '@/ui/icon';
import { InlineCell } from '@/ui/inline-cell/components/InlineCell'; import { InlineCell } from '@/ui/inline-cell/components/InlineCell';
@ -62,19 +61,17 @@ export const CompanyShow = () => {
hasBackButton hasBackButton
Icon={IconBuildingSkyscraper} Icon={IconBuildingSkyscraper}
> >
<RecoilScope CustomRecoilScopeContext={DropdownRecoilScopeContext}> <PageFavoriteButton
<PageFavoriteButton isFavorite={isFavorite}
isFavorite={isFavorite} onClick={handleFavoriteButtonClick}
onClick={handleFavoriteButtonClick} />
/> <ShowPageAddButton
<ShowPageAddButton key="add"
key="add" entity={{
entity={{ id: company.id,
id: company.id, type: ActivityTargetableEntityType.Company,
type: ActivityTargetableEntityType.Company, }}
}} />
/>
</RecoilScope>
</PageHeader> </PageHeader>
<PageBody> <PageBody>
<RecoilScope CustomRecoilScopeContext={ShowPageRecoilScopeContext}> <RecoilScope CustomRecoilScopeContext={ShowPageRecoilScopeContext}>

View File

@ -3,7 +3,6 @@ import { CompanyBoardRecoilScopeContext } from '@/companies/states/recoil-scope-
import { PipelineAddButton } from '@/pipeline/components/PipelineAddButton'; import { PipelineAddButton } from '@/pipeline/components/PipelineAddButton';
import { usePipelineStages } from '@/pipeline/hooks/usePipelineStages'; import { usePipelineStages } from '@/pipeline/hooks/usePipelineStages';
import { BoardOptionsContext } from '@/ui/board/contexts/BoardOptionsContext'; import { BoardOptionsContext } from '@/ui/board/contexts/BoardOptionsContext';
import { DropdownRecoilScopeContext } from '@/ui/dropdown/states/recoil-scope-contexts/DropdownRecoilScopeContext';
import { IconTargetArrow } from '@/ui/icon'; import { IconTargetArrow } from '@/ui/icon';
import { PageBody } from '@/ui/layout/components/PageBody'; import { PageBody } from '@/ui/layout/components/PageBody';
import { PageContainer } from '@/ui/layout/components/PageContainer'; import { PageContainer } from '@/ui/layout/components/PageContainer';
@ -44,9 +43,7 @@ export const Opportunities = () => {
<PageContainer> <PageContainer>
<RecoilScope> <RecoilScope>
<PageHeader title="Opportunities" Icon={IconTargetArrow}> <PageHeader title="Opportunities" Icon={IconTargetArrow}>
<RecoilScope CustomRecoilScopeContext={DropdownRecoilScopeContext}> <PipelineAddButton />
<PipelineAddButton />
</RecoilScope>
</PageHeader> </PageHeader>
<PageBody> <PageBody>
<BoardOptionsContext.Provider value={opportunitiesBoardOptions}> <BoardOptionsContext.Provider value={opportunitiesBoardOptions}>

View File

@ -9,7 +9,6 @@ import { DataTableContextMenu } from '@/ui/data-table/context-menu/components/Da
import { useUpsertDataTableItem } from '@/ui/data-table/hooks/useUpsertDataTableItem'; import { useUpsertDataTableItem } from '@/ui/data-table/hooks/useUpsertDataTableItem';
import { useUpsertTableRowId } from '@/ui/data-table/hooks/useUpsertTableRowId'; import { useUpsertTableRowId } from '@/ui/data-table/hooks/useUpsertTableRowId';
import { TableRecoilScopeContext } from '@/ui/data-table/states/recoil-scope-contexts/TableRecoilScopeContext'; 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 { IconUser } from '@/ui/icon';
import { PageAddButton } from '@/ui/layout/components/PageAddButton'; import { PageAddButton } from '@/ui/layout/components/PageAddButton';
import { PageBody } from '@/ui/layout/components/PageBody'; import { PageBody } from '@/ui/layout/components/PageBody';
@ -54,10 +53,8 @@ export const People = () => {
<SpreadsheetImportProvider> <SpreadsheetImportProvider>
<PageContainer> <PageContainer>
<PageHeader title="People" Icon={IconUser}> <PageHeader title="People" Icon={IconUser}>
<RecoilScope CustomRecoilScopeContext={DropdownRecoilScopeContext}> <PageHotkeysEffect onAddButtonClick={handleAddButtonClick} />
<PageHotkeysEffect onAddButtonClick={handleAddButtonClick} /> <PageAddButton onClick={handleAddButtonClick} />
<PageAddButton onClick={handleAddButtonClick} />
</RecoilScope>
</PageHeader> </PageHeader>
<PageBody> <PageBody>
<RecoilScope <RecoilScope

View File

@ -7,7 +7,6 @@ import { useFavorites } from '@/favorites/hooks/useFavorites';
import { GET_PERSON } from '@/people/graphql/queries/getPerson'; import { GET_PERSON } from '@/people/graphql/queries/getPerson';
import { usePersonQuery } from '@/people/hooks/usePersonQuery'; import { usePersonQuery } from '@/people/hooks/usePersonQuery';
import { AppPath } from '@/types/AppPath'; import { AppPath } from '@/types/AppPath';
import { DropdownRecoilScopeContext } from '@/ui/dropdown/states/recoil-scope-contexts/DropdownRecoilScopeContext';
import { FieldContext } from '@/ui/field/contexts/FieldContext'; import { FieldContext } from '@/ui/field/contexts/FieldContext';
import { IconUser } from '@/ui/icon'; import { IconUser } from '@/ui/icon';
import { InlineCell } from '@/ui/inline-cell/components/InlineCell'; import { InlineCell } from '@/ui/inline-cell/components/InlineCell';
@ -77,27 +76,25 @@ export const PersonShow = () => {
<PageContainer> <PageContainer>
<PageTitle title={person.displayName || 'No Name'} /> <PageTitle title={person.displayName || 'No Name'} />
<PageHeader title={person.firstName ?? ''} Icon={IconUser} hasBackButton> <PageHeader title={person.firstName ?? ''} Icon={IconUser} hasBackButton>
<RecoilScope CustomRecoilScopeContext={DropdownRecoilScopeContext}> <PageFavoriteButton
<PageFavoriteButton isFavorite={isFavorite}
isFavorite={isFavorite} onClick={handleFavoriteButtonClick}
onClick={handleFavoriteButtonClick} />
/> <ShowPageAddButton
<ShowPageAddButton key="add"
key="add" entity={{
entity={{ id: person.id,
id: person.id, type: ActivityTargetableEntityType.Person,
type: ActivityTargetableEntityType.Person, relatedEntities: person.company?.id
relatedEntities: person.company?.id ? [
? [ {
{ id: person.company?.id,
id: person.company?.id, type: ActivityTargetableEntityType.Company,
type: ActivityTargetableEntityType.Company, },
}, ]
] : undefined,
: undefined, }}
}} />
/>
</RecoilScope>
</PageHeader> </PageHeader>
<PageBody> <PageBody>
<RecoilScope CustomRecoilScopeContext={ShowPageRecoilScopeContext}> <RecoilScope CustomRecoilScopeContext={ShowPageRecoilScopeContext}>

View File

@ -3,7 +3,6 @@ import styled from '@emotion/styled';
import { TasksRecoilScopeContext } from '@/activities/states/recoil-scope-contexts/TasksRecoilScopeContext'; import { TasksRecoilScopeContext } from '@/activities/states/recoil-scope-contexts/TasksRecoilScopeContext';
import { PageAddTaskButton } from '@/activities/tasks/components/PageAddTaskButton'; import { PageAddTaskButton } from '@/activities/tasks/components/PageAddTaskButton';
import { TaskGroups } from '@/activities/tasks/components/TaskGroups'; 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 { IconArchive, IconCheck, IconCheckbox } from '@/ui/icon/index';
import { RelationPickerHotkeyScope } from '@/ui/input/relation-picker/types/RelationPickerHotkeyScope'; import { RelationPickerHotkeyScope } from '@/ui/input/relation-picker/types/RelationPickerHotkeyScope';
import { PageBody } from '@/ui/layout/components/PageBody'; import { PageBody } from '@/ui/layout/components/PageBody';
@ -48,43 +47,41 @@ export const Tasks = () => {
return ( return (
<PageContainer> <PageContainer>
<RecoilScope CustomRecoilScopeContext={DropdownRecoilScopeContext}> <RecoilScope CustomRecoilScopeContext={TasksRecoilScopeContext}>
<RecoilScope CustomRecoilScopeContext={TasksRecoilScopeContext}> <TasksEffect />
<TasksEffect /> <PageHeader title="Tasks" Icon={IconCheckbox}>
<PageHeader title="Tasks" Icon={IconCheckbox}> <PageAddTaskButton />
<PageAddTaskButton /> </PageHeader>
</PageHeader> <PageBody>
<PageBody> {/* TODO: we should refactor filters as a standalone module ? */}
{/* TODO: we should refactor filters as a standalone module ? */} <ViewBarContext.Provider
<ViewBarContext.Provider value={{
value={{ ViewBarRecoilScopeContext: TasksRecoilScopeContext,
ViewBarRecoilScopeContext: TasksRecoilScopeContext, }}
}} >
> <StyledTasksContainer>
<StyledTasksContainer> <TopBar
<TopBar leftComponent={
leftComponent={ <StyledTabListContainer>
<StyledTabListContainer> <TabList
<TabList context={TasksRecoilScopeContext}
context={TasksRecoilScopeContext} tabs={TASK_TABS}
tabs={TASK_TABS}
/>
</StyledTabListContainer>
}
rightComponent={
<FilterDropdownButton
key="tasks-filter-dropdown-button"
hotkeyScope={{
scope: RelationPickerHotkeyScope.RelationPicker,
}}
/> />
} </StyledTabListContainer>
/> }
<TaskGroups /> rightComponent={
</StyledTasksContainer> <FilterDropdownButton
</ViewBarContext.Provider> key="tasks-filter-dropdown-button"
</PageBody> hotkeyScope={{
</RecoilScope> scope: RelationPickerHotkeyScope.RelationPicker,
}}
/>
}
/>
<TaskGroups />
</StyledTasksContainer>
</ViewBarContext.Provider>
</PageBody>
</RecoilScope> </RecoilScope>
</PageContainer> </PageContainer>
); );

View File

@ -0,0 +1,3 @@
export type ScopeContextProps<T = unknown> = {
scopeId: string;
} & T;