Refactor/scope and context (#1960)
* wip * Test with Dropdown * wip * wip * Finished removing DropdownRecoilScopeContext * Fix from PR
This commit is contained in:
@ -1,8 +1,6 @@
|
||||
import { useOpenCreateActivityDrawer } from '@/activities/hooks/useOpenCreateActivityDrawer';
|
||||
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 { RecoilScope } from '@/ui/utilities/recoil-scope/components/RecoilScope';
|
||||
import { useRecoilScopedValue } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedValue';
|
||||
import { filtersScopedState } from '@/ui/view-bar/states/filtersScopedState';
|
||||
import { ActivityType } from '~/generated/graphql';
|
||||
@ -26,9 +24,5 @@ export const PageAddTaskButton = () => {
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<RecoilScope CustomRecoilScopeContext={DropdownRecoilScopeContext}>
|
||||
<PageAddButton onClick={handleClick} />
|
||||
</RecoilScope>
|
||||
);
|
||||
return <PageAddButton onClick={handleClick} />;
|
||||
};
|
||||
|
||||
@ -14,7 +14,7 @@ export const PipelineAddButton = () => {
|
||||
const { enqueueSnackBar } = useSnackBar();
|
||||
|
||||
const { closeDropdown, toggleDropdown } = useDropdown({
|
||||
dropdownId: 'add-pipeline-progress',
|
||||
dropdownScopeId: 'add-pipeline-progress',
|
||||
});
|
||||
|
||||
const createCompanyProgress = useCreateCompanyProgress();
|
||||
|
||||
@ -3,8 +3,6 @@ import { useSearchParams } from 'react-router-dom';
|
||||
import { useRecoilCallback, useRecoilState, useRecoilValue } from 'recoil';
|
||||
|
||||
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 { useRecoilScopedValue } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedValue';
|
||||
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 { canPersistBoardColumnsSelector } from '../states/selectors/canPersistBoardColumnsSelector';
|
||||
import { BoardColumnDefinition } from '../types/BoardColumnDefinition';
|
||||
import { BoardOptionsDropdownKey } from '../types/BoardOptionsDropdownKey';
|
||||
import { BoardOptionsHotkeyScope } from '../types/BoardOptionsHotkeyScope';
|
||||
import { BoardScopeIds } from '../types/enums/BoardScopeIds';
|
||||
|
||||
import { BoardOptionsDropdown } from './BoardOptionsDropdown';
|
||||
|
||||
@ -101,27 +99,25 @@ export const BoardHeader = ({ className, onStageAdd }: BoardHeaderProps) => {
|
||||
const canPersistView = canPersistBoardCardFields || canPersistBoardColumns;
|
||||
|
||||
return (
|
||||
<RecoilScope CustomRecoilScopeContext={DropdownRecoilScopeContext}>
|
||||
<ViewBarContext.Provider
|
||||
value={{
|
||||
...viewBarContextProps,
|
||||
canPersistViewFields: canPersistView,
|
||||
onCurrentViewSubmit: handleCurrentViewSubmit,
|
||||
onViewBarReset: handleViewBarReset,
|
||||
onViewSelect: handleViewSelect,
|
||||
}}
|
||||
>
|
||||
<ViewBar
|
||||
className={className}
|
||||
optionsDropdownButton={
|
||||
<BoardOptionsDropdown
|
||||
customHotkeyScope={{ scope: BoardOptionsHotkeyScope.Dropdown }}
|
||||
onStageAdd={onStageAdd}
|
||||
/>
|
||||
}
|
||||
optionsDropdownKey={BoardOptionsDropdownKey}
|
||||
/>
|
||||
</ViewBarContext.Provider>
|
||||
</RecoilScope>
|
||||
<ViewBarContext.Provider
|
||||
value={{
|
||||
...viewBarContextProps,
|
||||
canPersistViewFields: canPersistView,
|
||||
onCurrentViewSubmit: handleCurrentViewSubmit,
|
||||
onViewBarReset: handleViewBarReset,
|
||||
onViewSelect: handleViewSelect,
|
||||
}}
|
||||
>
|
||||
<ViewBar
|
||||
className={className}
|
||||
optionsDropdownButton={
|
||||
<BoardOptionsDropdown
|
||||
customHotkeyScope={{ scope: BoardOptionsHotkeyScope.Dropdown }}
|
||||
onStageAdd={onStageAdd}
|
||||
/>
|
||||
}
|
||||
optionsDropdownScopeId={BoardScopeIds.OptionsDropdown}
|
||||
/>
|
||||
</ViewBarContext.Provider>
|
||||
);
|
||||
};
|
||||
|
||||
@ -3,7 +3,7 @@ import { useResetRecoilState } from 'recoil';
|
||||
import { ViewBarDropdownButton } from '@/ui/view-bar/components/ViewBarDropdownButton';
|
||||
import { viewEditModeState } from '@/ui/view-bar/states/viewEditModeState';
|
||||
|
||||
import { BoardOptionsDropdownKey } from '../types/BoardOptionsDropdownKey';
|
||||
import { BoardScopeIds } from '../types/enums/BoardScopeIds';
|
||||
|
||||
import { BoardOptionsDropdownButton } from './BoardOptionsDropdownButton';
|
||||
import {
|
||||
@ -32,7 +32,7 @@ export const BoardOptionsDropdown = ({
|
||||
/>
|
||||
}
|
||||
dropdownHotkeyScope={customHotkeyScope}
|
||||
dropdownId={BoardOptionsDropdownKey}
|
||||
dropdownId={BoardScopeIds.OptionsDropdown}
|
||||
onClickOutside={resetViewEditMode}
|
||||
/>
|
||||
);
|
||||
|
||||
@ -1,11 +1,11 @@
|
||||
import { StyledHeaderDropdownButton } from '@/ui/dropdown/components/StyledHeaderDropdownButton';
|
||||
import { useDropdown } from '@/ui/dropdown/hooks/useDropdown';
|
||||
|
||||
import { BoardOptionsDropdownKey } from '../types/BoardOptionsDropdownKey';
|
||||
import { BoardScopeIds } from '../types/enums/BoardScopeIds';
|
||||
|
||||
export const BoardOptionsDropdownButton = () => {
|
||||
const { isDropdownOpen, toggleDropdown } = useDropdown({
|
||||
dropdownId: BoardOptionsDropdownKey,
|
||||
dropdownScopeId: BoardScopeIds.OptionsDropdown,
|
||||
});
|
||||
|
||||
const handleClick = () => {
|
||||
|
||||
@ -43,7 +43,6 @@ import { savedBoardCardFieldsFamilyState } from '../states/savedBoardCardFieldsF
|
||||
import { hiddenBoardCardFieldsScopedSelector } from '../states/selectors/hiddenBoardCardFieldsScopedSelector';
|
||||
import { visibleBoardCardFieldsScopedSelector } from '../states/selectors/visibleBoardCardFieldsScopedSelector';
|
||||
import { BoardColumnDefinition } from '../types/BoardColumnDefinition';
|
||||
import { BoardOptionsDropdownKey } from '../types/BoardOptionsDropdownKey';
|
||||
|
||||
export type BoardOptionsDropdownContentProps = {
|
||||
customHotkeyScope: HotkeyScope;
|
||||
@ -144,9 +143,7 @@ export const BoardOptionsDropdownContent = ({
|
||||
|
||||
const { handleFieldVisibilityChange } = useBoardCardFields();
|
||||
|
||||
const { closeDropdown } = useDropdown({
|
||||
dropdownId: BoardOptionsDropdownKey,
|
||||
});
|
||||
const { closeDropdown } = useDropdown();
|
||||
|
||||
useScopedHotkeys(
|
||||
Key.Escape,
|
||||
|
||||
@ -3,8 +3,6 @@ import { userEvent, within } from '@storybook/testing-library';
|
||||
|
||||
import { BoardContext } from '@/companies/states/contexts/BoardContext';
|
||||
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 { ComponentDecorator } from '~/testing/decorators/ComponentDecorator';
|
||||
import { ComponentWithRecoilScopeDecorator } from '~/testing/decorators/ComponentWithRecoilScopeDecorator';
|
||||
@ -26,9 +24,7 @@ const meta: Meta<typeof BoardOptionsDropdown> = {
|
||||
ViewBarRecoilScopeContext: parameters.customRecoilScopeContext,
|
||||
}}
|
||||
>
|
||||
<RecoilScope CustomRecoilScopeContext={DropdownRecoilScopeContext}>
|
||||
<Story />
|
||||
</RecoilScope>
|
||||
<Story />
|
||||
</ViewBarContext.Provider>
|
||||
</BoardContext.Provider>
|
||||
),
|
||||
|
||||
@ -1 +0,0 @@
|
||||
export const BoardOptionsDropdownKey = 'board-options';
|
||||
3
front/src/modules/ui/board/types/enums/BoardScopeIds.ts
Normal file
3
front/src/modules/ui/board/types/enums/BoardScopeIds.ts
Normal file
@ -0,0 +1,3 @@
|
||||
export enum BoardScopeIds {
|
||||
OptionsDropdown = 'board-options',
|
||||
}
|
||||
@ -1,4 +1,5 @@
|
||||
import { DropdownMenu } from '@/ui/dropdown/components/DropdownMenu';
|
||||
import { DropdownScope } from '@/ui/dropdown/scopes/DropdownScope';
|
||||
import { FieldMetadata } from '@/ui/field/types/FieldMetadata';
|
||||
|
||||
import { ColumnDefinition } from '../types/ColumnDefinition';
|
||||
@ -20,19 +21,20 @@ export const ColumnHeadWithDropdown = ({
|
||||
primaryColumnKey,
|
||||
}: ColumnHeadWithDropdownProps) => {
|
||||
return (
|
||||
<DropdownMenu
|
||||
clickableComponent={<ColumnHead column={column} />}
|
||||
dropdownId={column.key + '-header'}
|
||||
dropdownComponents={
|
||||
<DataTableColumnDropdownMenu
|
||||
column={column}
|
||||
isFirstColumn={isFirstColumn}
|
||||
isLastColumn={isLastColumn}
|
||||
primaryColumnKey={primaryColumnKey}
|
||||
/>
|
||||
}
|
||||
dropdownHotkeyScope={{ scope: column.key + '-header' }}
|
||||
dropdownOffset={{ x: 0, y: -8 }}
|
||||
/>
|
||||
<DropdownScope dropdownScopeId={column.key + '-header'}>
|
||||
<DropdownMenu
|
||||
clickableComponent={<ColumnHead column={column} />}
|
||||
dropdownComponents={
|
||||
<DataTableColumnDropdownMenu
|
||||
column={column}
|
||||
isFirstColumn={isFirstColumn}
|
||||
isLastColumn={isLastColumn}
|
||||
primaryColumnKey={primaryColumnKey}
|
||||
/>
|
||||
}
|
||||
dropdownHotkeyScope={{ scope: column.key + '-header' }}
|
||||
dropdownOffset={{ x: 0, y: -8 }}
|
||||
/>
|
||||
</DropdownScope>
|
||||
);
|
||||
};
|
||||
|
||||
@ -26,7 +26,7 @@ export const DataTableColumnDropdownMenu = ({
|
||||
useTableColumns();
|
||||
|
||||
const { closeDropdown } = useDropdown({
|
||||
dropdownId: ColumnHeadDropdownId,
|
||||
dropdownScopeId: ColumnHeadDropdownId,
|
||||
});
|
||||
|
||||
const handleColumnMoveLeft = () => {
|
||||
|
||||
@ -3,10 +3,8 @@ import styled from '@emotion/styled';
|
||||
import { useRecoilCallback, useRecoilState } from 'recoil';
|
||||
|
||||
import { IconButton } from '@/ui/button/components/IconButton';
|
||||
import { DropdownRecoilScopeContext } from '@/ui/dropdown/states/recoil-scope-contexts/DropdownRecoilScopeContext';
|
||||
import { IconPlus } from '@/ui/icon';
|
||||
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 { useTableColumns } from '../hooks/useTableColumns';
|
||||
@ -177,36 +175,33 @@ export const DataTableHeader = () => {
|
||||
>
|
||||
<SelectAllCheckbox />
|
||||
</th>
|
||||
<RecoilScope CustomRecoilScopeContext={DropdownRecoilScopeContext}>
|
||||
{visibleTableColumns.map((column, index) => (
|
||||
<StyledColumnHeaderCell
|
||||
key={column.key}
|
||||
isResizing={resizedFieldKey === column.key}
|
||||
columnWidth={Math.max(
|
||||
tableColumnsByKey[column.key].size +
|
||||
(resizedFieldKey === column.key ? resizeFieldOffset : 0),
|
||||
COLUMN_MIN_WIDTH,
|
||||
)}
|
||||
>
|
||||
<StyledColumnHeadContainer>
|
||||
<ColumnHeadWithDropdown
|
||||
column={column}
|
||||
isFirstColumn={index === 1}
|
||||
isLastColumn={index === visibleTableColumns.length - 1}
|
||||
primaryColumnKey={primaryColumn.key}
|
||||
/>
|
||||
</StyledColumnHeadContainer>
|
||||
|
||||
<StyledResizeHandler
|
||||
className="cursor-col-resize"
|
||||
role="separator"
|
||||
onPointerDown={() => {
|
||||
setResizedFieldKey(column.key);
|
||||
}}
|
||||
{visibleTableColumns.map((column, index) => (
|
||||
<StyledColumnHeaderCell
|
||||
key={column.key}
|
||||
isResizing={resizedFieldKey === column.key}
|
||||
columnWidth={Math.max(
|
||||
tableColumnsByKey[column.key].size +
|
||||
(resizedFieldKey === column.key ? resizeFieldOffset : 0),
|
||||
COLUMN_MIN_WIDTH,
|
||||
)}
|
||||
>
|
||||
<StyledColumnHeadContainer>
|
||||
<ColumnHeadWithDropdown
|
||||
column={column}
|
||||
isFirstColumn={index === 1}
|
||||
isLastColumn={index === visibleTableColumns.length - 1}
|
||||
primaryColumnKey={primaryColumn.key}
|
||||
/>
|
||||
</StyledColumnHeaderCell>
|
||||
))}
|
||||
</RecoilScope>
|
||||
</StyledColumnHeadContainer>
|
||||
<StyledResizeHandler
|
||||
className="cursor-col-resize"
|
||||
role="separator"
|
||||
onPointerDown={() => {
|
||||
setResizedFieldKey(column.key);
|
||||
}}
|
||||
/>
|
||||
</StyledColumnHeaderCell>
|
||||
))}
|
||||
|
||||
<th>
|
||||
{hiddenTableColumns.length > 0 && (
|
||||
|
||||
@ -4,7 +4,7 @@ import { useDropdown } from '@/ui/dropdown/hooks/useDropdown';
|
||||
|
||||
export const TableOptionsDropdownButton = () => {
|
||||
const { isDropdownOpen, toggleDropdown } = useDropdown({
|
||||
dropdownId: TableOptionsDropdownId,
|
||||
dropdownScopeId: TableOptionsDropdownId,
|
||||
});
|
||||
|
||||
return (
|
||||
|
||||
@ -22,7 +22,6 @@ import { currentViewScopedSelector } from '@/ui/view-bar/states/selectors/curren
|
||||
import { viewsByIdScopedSelector } from '@/ui/view-bar/states/selectors/viewsByIdScopedSelector';
|
||||
import { viewEditModeState } from '@/ui/view-bar/states/viewEditModeState';
|
||||
|
||||
import { TableOptionsDropdownId } from '../../constants/TableOptionsDropdownId';
|
||||
import { useTableColumns } from '../../hooks/useTableColumns';
|
||||
import { TableRecoilScopeContext } from '../../states/recoil-scope-contexts/TableRecoilScopeContext';
|
||||
import { savedTableColumnsFamilyState } from '../../states/savedTableColumnsFamilyState';
|
||||
@ -37,9 +36,7 @@ export const TableOptionsDropdownContent = () => {
|
||||
const scopeId = useRecoilScopeId(TableRecoilScopeContext);
|
||||
|
||||
const { onImport } = useContext(ViewBarContext);
|
||||
const { closeDropdown } = useDropdown({
|
||||
dropdownId: TableOptionsDropdownId,
|
||||
});
|
||||
const { closeDropdown } = useDropdown();
|
||||
|
||||
const [currentMenu, setCurrentMenu] = useState<TableOptionsMenu | undefined>(
|
||||
undefined,
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
import { Meta, StoryObj } from '@storybook/react';
|
||||
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 { ViewBarContext } from '@/ui/view-bar/contexts/ViewBarContext';
|
||||
import { ComponentDecorator } from '~/testing/decorators/ComponentDecorator';
|
||||
@ -20,9 +19,7 @@ const meta: Meta<typeof TableOptionsDropdown> = {
|
||||
ViewBarRecoilScopeContext: TableRecoilScopeContext,
|
||||
}}
|
||||
>
|
||||
<RecoilScope CustomRecoilScopeContext={DropdownRecoilScopeContext}>
|
||||
<Story />
|
||||
</RecoilScope>
|
||||
<Story />
|
||||
</ViewBarContext.Provider>
|
||||
</RecoilScope>
|
||||
),
|
||||
|
||||
@ -2,8 +2,6 @@ import { useContext } from 'react';
|
||||
import { useSearchParams } from 'react-router-dom';
|
||||
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 { ViewBar } from '@/ui/view-bar/components/ViewBar';
|
||||
import { ViewBarContext } from '@/ui/view-bar/contexts/ViewBarContext';
|
||||
@ -34,23 +32,21 @@ export const TableHeader = () => {
|
||||
);
|
||||
|
||||
return (
|
||||
<RecoilScope CustomRecoilScopeContext={DropdownRecoilScopeContext}>
|
||||
<ViewBarContext.Provider
|
||||
value={{
|
||||
...viewBarContextProps,
|
||||
onCurrentViewSubmit,
|
||||
onViewSelect: handleViewSelect,
|
||||
}}
|
||||
>
|
||||
<ViewBar
|
||||
optionsDropdownButton={
|
||||
<TableOptionsDropdown
|
||||
customHotkeyScope={{ scope: TableOptionsHotkeyScope.Dropdown }}
|
||||
/>
|
||||
}
|
||||
optionsDropdownKey={TableOptionsDropdownId}
|
||||
/>
|
||||
</ViewBarContext.Provider>
|
||||
</RecoilScope>
|
||||
<ViewBarContext.Provider
|
||||
value={{
|
||||
...viewBarContextProps,
|
||||
onCurrentViewSubmit,
|
||||
onViewSelect: handleViewSelect,
|
||||
}}
|
||||
>
|
||||
<ViewBar
|
||||
optionsDropdownButton={
|
||||
<TableOptionsDropdown
|
||||
customHotkeyScope={{ scope: TableOptionsHotkeyScope.Dropdown }}
|
||||
/>
|
||||
}
|
||||
optionsDropdownScopeId={TableOptionsDropdownId}
|
||||
/>
|
||||
</ViewBarContext.Provider>
|
||||
);
|
||||
};
|
||||
|
||||
@ -16,7 +16,6 @@ import { DropdownToggleEffect } from './DropdownToggleEffect';
|
||||
type DropdownMenuProps = {
|
||||
clickableComponent?: JSX.Element | JSX.Element[];
|
||||
dropdownComponents: JSX.Element | JSX.Element[];
|
||||
dropdownId: string;
|
||||
hotkey?: {
|
||||
key: Keys;
|
||||
scope: string;
|
||||
@ -32,7 +31,6 @@ type DropdownMenuProps = {
|
||||
export const DropdownMenu = ({
|
||||
clickableComponent,
|
||||
dropdownComponents,
|
||||
dropdownId,
|
||||
hotkey,
|
||||
dropdownHotkeyScope,
|
||||
dropdownPlacement = 'bottom-end',
|
||||
@ -43,9 +41,7 @@ export const DropdownMenu = ({
|
||||
}: DropdownMenuProps) => {
|
||||
const containerRef = useRef<HTMLDivElement>(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 (
|
||||
<div ref={containerRef}>
|
||||
{clickableComponent && (
|
||||
@ -101,11 +97,7 @@ export const DropdownMenu = ({
|
||||
{dropdownComponents}
|
||||
</div>
|
||||
)}
|
||||
<DropdownToggleEffect
|
||||
dropdownId={dropdownId}
|
||||
onDropdownClose={onClose}
|
||||
onDropdownOpen={onOpen}
|
||||
/>
|
||||
<DropdownToggleEffect onDropdownClose={onClose} onDropdownOpen={onOpen} />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
@ -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) {
|
||||
|
||||
@ -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,
|
||||
};
|
||||
};
|
||||
|
||||
23
front/src/modules/ui/dropdown/hooks/useDropdownStates.ts
Normal file
23
front/src/modules/ui/dropdown/hooks/useDropdownStates.ts
Normal 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,
|
||||
};
|
||||
};
|
||||
@ -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,
|
||||
]);
|
||||
};
|
||||
|
||||
19
front/src/modules/ui/dropdown/scopes/DropdownScope.tsx
Normal file
19
front/src/modules/ui/dropdown/scopes/DropdownScope.tsx
Normal 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>
|
||||
);
|
||||
};
|
||||
@ -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>();
|
||||
@ -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,
|
||||
});
|
||||
@ -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,
|
||||
});
|
||||
@ -1,9 +0,0 @@
|
||||
import { atomFamily } from 'recoil';
|
||||
|
||||
export const isDropdownButtonOpenScopedFamilyState = atomFamily<
|
||||
boolean,
|
||||
string
|
||||
>({
|
||||
key: 'isDropdownButtonOpenScopedState',
|
||||
default: false,
|
||||
});
|
||||
@ -0,0 +1,6 @@
|
||||
import { createScopedState } from '@/ui/utilities/recoil-scope/utils/createScopedState';
|
||||
|
||||
export const isDropdownOpenScopedState = createScopedState<boolean>({
|
||||
key: 'isDropdownOpenScopedState',
|
||||
defaultValue: false,
|
||||
});
|
||||
@ -1,3 +0,0 @@
|
||||
import { createContext } from 'react';
|
||||
|
||||
export const DropdownRecoilScopeContext = createContext<string | null>(null);
|
||||
@ -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<Country>();
|
||||
|
||||
const { isDropdownOpen, closeDropdown } = useDropdown({
|
||||
dropdownId: 'country-picker',
|
||||
dropdownScopeId: 'country-picker',
|
||||
});
|
||||
|
||||
const handleChange = (countryCode: string) => {
|
||||
@ -122,25 +123,26 @@ export const CountryPickerDropdownButton = ({
|
||||
}, [countries, value]);
|
||||
|
||||
return (
|
||||
<DropdownMenu
|
||||
dropdownId="country-picker"
|
||||
dropdownHotkeyScope={{ scope: CountryPickerHotkeyScope.CountryPicker }}
|
||||
clickableComponent={
|
||||
<StyledDropdownButtonContainer isUnfolded={isDropdownOpen}>
|
||||
<StyledIconContainer>
|
||||
{selectedCountry ? <selectedCountry.Flag /> : <IconWorld />}
|
||||
<IconChevronDown size={theme.icon.size.sm} />
|
||||
</StyledIconContainer>
|
||||
</StyledDropdownButtonContainer>
|
||||
}
|
||||
dropdownComponents={
|
||||
<CountryPickerDropdownSelect
|
||||
countries={countries}
|
||||
selectedCountry={selectedCountry}
|
||||
onChange={handleChange}
|
||||
/>
|
||||
}
|
||||
dropdownOffset={{ x: -60, y: -34 }}
|
||||
/>
|
||||
<DropdownScope dropdownScopeId="country-picker">
|
||||
<DropdownMenu
|
||||
dropdownHotkeyScope={{ scope: CountryPickerHotkeyScope.CountryPicker }}
|
||||
clickableComponent={
|
||||
<StyledDropdownButtonContainer isUnfolded={isDropdownOpen}>
|
||||
<StyledIconContainer>
|
||||
{selectedCountry ? <selectedCountry.Flag /> : <IconWorld />}
|
||||
<IconChevronDown size={theme.icon.size.sm} />
|
||||
</StyledIconContainer>
|
||||
</StyledDropdownButtonContainer>
|
||||
}
|
||||
dropdownComponents={
|
||||
<CountryPickerDropdownSelect
|
||||
countries={countries}
|
||||
selectedCountry={selectedCountry}
|
||||
onChange={handleChange}
|
||||
/>
|
||||
}
|
||||
dropdownOffset={{ x: -60, y: -34 }}
|
||||
/>
|
||||
</DropdownScope>
|
||||
);
|
||||
};
|
||||
|
||||
@ -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 (
|
||||
<StyledContainer ref={wrapperRef}>
|
||||
<RecoilScope CustomRecoilScopeContext={DropdownRecoilScopeContext}>
|
||||
<StyledCustomPhoneInput
|
||||
autoFocus={autoFocus}
|
||||
placeholder="Phone number"
|
||||
value={value}
|
||||
onChange={setInternalValue}
|
||||
international={true}
|
||||
withCountryCallingCode={true}
|
||||
countrySelectComponent={CountryPickerDropdownButton}
|
||||
/>
|
||||
</RecoilScope>
|
||||
<StyledCustomPhoneInput
|
||||
autoFocus={autoFocus}
|
||||
placeholder="Phone number"
|
||||
value={value}
|
||||
onChange={setInternalValue}
|
||||
international={true}
|
||||
withCountryCallingCode={true}
|
||||
countrySelectComponent={CountryPickerDropdownButton}
|
||||
/>
|
||||
</StyledContainer>
|
||||
);
|
||||
};
|
||||
|
||||
@ -22,7 +22,7 @@ export const ShowPageAddButton = ({
|
||||
entity: ActivityTargetableEntity;
|
||||
}) => {
|
||||
const { closeDropdown, toggleDropdown } = useDropdown({
|
||||
dropdownId: 'add-show-page',
|
||||
dropdownScopeId: 'add-show-page',
|
||||
});
|
||||
const openCreateActivity = useOpenCreateActivityDrawer();
|
||||
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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<string | null>) => {
|
||||
const recoilScopeId = useContext(SpecificContext);
|
||||
|
||||
|
||||
@ -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);
|
||||
|
||||
|
||||
@ -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 = <StateType>(
|
||||
recoilState: (familyUniqueId: string) => RecoilState<StateType>,
|
||||
uniqueIdInRecoilScope: string,
|
||||
CustomRecoilScopeContext?: Context<string | null>,
|
||||
export const useRecoilScopedFamilyState = <
|
||||
StateType,
|
||||
FamilyKey extends SerializableParam,
|
||||
>(
|
||||
recoilState: (
|
||||
scopedFamilyKey: ScopedFamilyStateKey<FamilyKey>,
|
||||
) => RecoilState<StateType>,
|
||||
scopeId: string,
|
||||
familyKey: FamilyKey,
|
||||
) => {
|
||||
const recoilScopeId = useContext(
|
||||
CustomRecoilScopeContext ?? RecoilScopeContext,
|
||||
return useRecoilState<StateType>(
|
||||
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));
|
||||
};
|
||||
|
||||
@ -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,
|
||||
}),
|
||||
);
|
||||
};
|
||||
@ -3,6 +3,9 @@ import { RecoilState, RecoilValueReadOnly, useRecoilValue } from 'recoil';
|
||||
|
||||
import { RecoilScopeContext } from '../states/RecoilScopeContext';
|
||||
|
||||
/**
|
||||
* @deprecated use useRecoilScopedStateV2 instead
|
||||
*/
|
||||
export const useRecoilScopedValue = <T>(
|
||||
recoilState: (param: string) => RecoilState<T> | RecoilValueReadOnly<T>,
|
||||
CustomRecoilScopeContext?: Context<string | null>,
|
||||
|
||||
@ -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,
|
||||
}),
|
||||
);
|
||||
};
|
||||
@ -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.');
|
||||
}
|
||||
};
|
||||
@ -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;
|
||||
};
|
||||
@ -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;
|
||||
};
|
||||
@ -0,0 +1,4 @@
|
||||
import { Context } from 'react';
|
||||
|
||||
export type ScopeInternalContext<T extends { scopeId: string }> =
|
||||
Context<T | null>;
|
||||
@ -0,0 +1,6 @@
|
||||
import { SerializableParam } from 'recoil';
|
||||
|
||||
export type ScopedFamilyStateKey<FamilyKey extends SerializableParam> = {
|
||||
scopeId: string;
|
||||
familyKey: FamilyKey;
|
||||
};
|
||||
@ -0,0 +1,3 @@
|
||||
export type ScopedStateKey = {
|
||||
scopeId: string;
|
||||
};
|
||||
@ -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>;
|
||||
};
|
||||
@ -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,
|
||||
});
|
||||
};
|
||||
@ -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,
|
||||
});
|
||||
};
|
||||
@ -6,7 +6,7 @@ import { FilterDropdownId } from '../constants/FilterDropdownId';
|
||||
|
||||
export const AddFilterFromDropdownButton = () => {
|
||||
const { toggleDropdown } = useDropdown({
|
||||
dropdownId: FilterDropdownId,
|
||||
dropdownScopeId: FilterDropdownId,
|
||||
});
|
||||
|
||||
const handleClick = () => {
|
||||
|
||||
@ -13,7 +13,7 @@ export const MultipleFiltersButton = () => {
|
||||
const { ViewBarRecoilScopeContext } = useViewBarContext();
|
||||
|
||||
const { isDropdownOpen, toggleDropdown } = useDropdown({
|
||||
dropdownId: FilterDropdownId,
|
||||
dropdownScopeId: FilterDropdownId,
|
||||
});
|
||||
|
||||
const [, setIsFilterDropdownOperandSelectUnfolded] = useRecoilScopedState(
|
||||
|
||||
@ -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 (
|
||||
<StyledDropdownButtonContainer data-select-disable>
|
||||
<StyledHeaderDropdownButton
|
||||
isUnfolded={isFilterDropdownUnfolded}
|
||||
onClick={() => handleIsUnfoldedChange(!isFilterDropdownUnfolded)}
|
||||
>
|
||||
{filters[0] ? (
|
||||
<GenericEntityFilterChip
|
||||
filter={filters[0]}
|
||||
Icon={
|
||||
filters[0].operand === FilterOperand.IsNotNull
|
||||
? availableFilter.SelectAllIcon
|
||||
: undefined
|
||||
}
|
||||
/>
|
||||
) : (
|
||||
'Filter'
|
||||
)}
|
||||
<IconChevronDown size={theme.icon.size.md} />
|
||||
</StyledHeaderDropdownButton>
|
||||
{isFilterDropdownUnfolded && (
|
||||
<DropdownMenuContainer onClose={() => handleIsUnfoldedChange(false)}>
|
||||
<FilterDropdownEntitySearchInput />
|
||||
<FilterDropdownEntitySelect />
|
||||
</DropdownMenuContainer>
|
||||
)}
|
||||
</StyledDropdownButtonContainer>
|
||||
<DropdownScope dropdownScopeId="single-entity-filter-dropdown">
|
||||
<DropdownMenu
|
||||
dropdownHotkeyScope={hotkeyScope}
|
||||
dropdownOffset={{ x: 0, y: -28 }}
|
||||
clickableComponent={
|
||||
<StyledHeaderDropdownButton>
|
||||
{filters[0] ? (
|
||||
<GenericEntityFilterChip
|
||||
filter={filters[0]}
|
||||
Icon={
|
||||
filters[0].operand === FilterOperand.IsNotNull
|
||||
? availableFilter.SelectAllIcon
|
||||
: undefined
|
||||
}
|
||||
/>
|
||||
) : (
|
||||
'Filter'
|
||||
)}
|
||||
<IconChevronDown size={theme.icon.size.md} />
|
||||
</StyledHeaderDropdownButton>
|
||||
}
|
||||
dropdownComponents={
|
||||
<DropdownMenuContainer>
|
||||
<FilterDropdownEntitySearchInput />
|
||||
<FilterDropdownEntitySelect />
|
||||
</DropdownMenuContainer>
|
||||
}
|
||||
/>
|
||||
</DropdownScope>
|
||||
);
|
||||
};
|
||||
|
||||
@ -55,7 +55,7 @@ export const SortDropdownButton = ({
|
||||
const isSortSelected = sorts.length > 0;
|
||||
|
||||
const { toggleDropdown } = useDropdown({
|
||||
dropdownId: SortDropdownId,
|
||||
dropdownScopeId: SortDropdownId,
|
||||
});
|
||||
|
||||
const handleButtonClick = () => {
|
||||
|
||||
@ -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 (
|
||||
|
||||
@ -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 (
|
||||
<DropdownMenu
|
||||
clickableComponent={buttonComponent}
|
||||
dropdownComponents={dropdownComponents}
|
||||
dropdownId={dropdownId}
|
||||
hotkey={hotkey}
|
||||
dropdownHotkeyScope={dropdownHotkeyScope}
|
||||
dropdownOffset={{ x: 0, y: 8 }}
|
||||
dropdownPlacement={dropdownPlacement}
|
||||
onClickOutside={onClickOutside}
|
||||
onClose={onClose}
|
||||
onOpen={onOpen}
|
||||
/>
|
||||
<DropdownScope dropdownScopeId={dropdownId}>
|
||||
<DropdownMenu
|
||||
clickableComponent={buttonComponent}
|
||||
dropdownComponents={dropdownComponents}
|
||||
hotkey={hotkey}
|
||||
dropdownHotkeyScope={dropdownHotkeyScope}
|
||||
dropdownOffset={{ x: 0, y: 8 }}
|
||||
dropdownPlacement={dropdownPlacement}
|
||||
onClickOutside={onClickOutside}
|
||||
onClose={onClose}
|
||||
onOpen={onOpen}
|
||||
/>
|
||||
</DropdownScope>
|
||||
);
|
||||
};
|
||||
|
||||
@ -105,7 +105,7 @@ export const ViewsDropdownButton = ({
|
||||
);
|
||||
|
||||
const { isDropdownOpen, closeDropdown } = useDropdown({
|
||||
dropdownId: ViewsDropdownId,
|
||||
dropdownScopeId: ViewsDropdownId,
|
||||
});
|
||||
|
||||
const setViewEditMode = useSetRecoilState(viewEditModeState);
|
||||
|
||||
@ -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 = () => {
|
||||
<SpreadsheetImportProvider>
|
||||
<PageContainer>
|
||||
<PageHeader title="Companies" Icon={IconBuildingSkyscraper}>
|
||||
<RecoilScope CustomRecoilScopeContext={DropdownRecoilScopeContext}>
|
||||
<PageHotkeysEffect onAddButtonClick={handleAddButtonClick} />
|
||||
<PageAddButton onClick={handleAddButtonClick} />
|
||||
</RecoilScope>
|
||||
<PageHotkeysEffect onAddButtonClick={handleAddButtonClick} />
|
||||
<PageAddButton onClick={handleAddButtonClick} />
|
||||
</PageHeader>
|
||||
<PageBody>
|
||||
<RecoilScope
|
||||
|
||||
@ -6,7 +6,6 @@ import { CompanyTeam } from '@/companies/components/CompanyTeam';
|
||||
import { useCompanyQuery } from '@/companies/hooks/useCompanyQuery';
|
||||
import { useFavorites } from '@/favorites/hooks/useFavorites';
|
||||
import { AppPath } from '@/types/AppPath';
|
||||
import { DropdownRecoilScopeContext } from '@/ui/dropdown/states/recoil-scope-contexts/DropdownRecoilScopeContext';
|
||||
import { FieldContext } from '@/ui/field/contexts/FieldContext';
|
||||
import { IconBuildingSkyscraper } from '@/ui/icon';
|
||||
import { InlineCell } from '@/ui/inline-cell/components/InlineCell';
|
||||
@ -62,19 +61,17 @@ export const CompanyShow = () => {
|
||||
hasBackButton
|
||||
Icon={IconBuildingSkyscraper}
|
||||
>
|
||||
<RecoilScope CustomRecoilScopeContext={DropdownRecoilScopeContext}>
|
||||
<PageFavoriteButton
|
||||
isFavorite={isFavorite}
|
||||
onClick={handleFavoriteButtonClick}
|
||||
/>
|
||||
<ShowPageAddButton
|
||||
key="add"
|
||||
entity={{
|
||||
id: company.id,
|
||||
type: ActivityTargetableEntityType.Company,
|
||||
}}
|
||||
/>
|
||||
</RecoilScope>
|
||||
<PageFavoriteButton
|
||||
isFavorite={isFavorite}
|
||||
onClick={handleFavoriteButtonClick}
|
||||
/>
|
||||
<ShowPageAddButton
|
||||
key="add"
|
||||
entity={{
|
||||
id: company.id,
|
||||
type: ActivityTargetableEntityType.Company,
|
||||
}}
|
||||
/>
|
||||
</PageHeader>
|
||||
<PageBody>
|
||||
<RecoilScope CustomRecoilScopeContext={ShowPageRecoilScopeContext}>
|
||||
|
||||
@ -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 = () => {
|
||||
<PageContainer>
|
||||
<RecoilScope>
|
||||
<PageHeader title="Opportunities" Icon={IconTargetArrow}>
|
||||
<RecoilScope CustomRecoilScopeContext={DropdownRecoilScopeContext}>
|
||||
<PipelineAddButton />
|
||||
</RecoilScope>
|
||||
<PipelineAddButton />
|
||||
</PageHeader>
|
||||
<PageBody>
|
||||
<BoardOptionsContext.Provider value={opportunitiesBoardOptions}>
|
||||
|
||||
@ -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 = () => {
|
||||
<SpreadsheetImportProvider>
|
||||
<PageContainer>
|
||||
<PageHeader title="People" Icon={IconUser}>
|
||||
<RecoilScope CustomRecoilScopeContext={DropdownRecoilScopeContext}>
|
||||
<PageHotkeysEffect onAddButtonClick={handleAddButtonClick} />
|
||||
<PageAddButton onClick={handleAddButtonClick} />
|
||||
</RecoilScope>
|
||||
<PageHotkeysEffect onAddButtonClick={handleAddButtonClick} />
|
||||
<PageAddButton onClick={handleAddButtonClick} />
|
||||
</PageHeader>
|
||||
<PageBody>
|
||||
<RecoilScope
|
||||
|
||||
@ -7,7 +7,6 @@ import { useFavorites } from '@/favorites/hooks/useFavorites';
|
||||
import { GET_PERSON } from '@/people/graphql/queries/getPerson';
|
||||
import { usePersonQuery } from '@/people/hooks/usePersonQuery';
|
||||
import { AppPath } from '@/types/AppPath';
|
||||
import { DropdownRecoilScopeContext } from '@/ui/dropdown/states/recoil-scope-contexts/DropdownRecoilScopeContext';
|
||||
import { FieldContext } from '@/ui/field/contexts/FieldContext';
|
||||
import { IconUser } from '@/ui/icon';
|
||||
import { InlineCell } from '@/ui/inline-cell/components/InlineCell';
|
||||
@ -77,27 +76,25 @@ export const PersonShow = () => {
|
||||
<PageContainer>
|
||||
<PageTitle title={person.displayName || 'No Name'} />
|
||||
<PageHeader title={person.firstName ?? ''} Icon={IconUser} hasBackButton>
|
||||
<RecoilScope CustomRecoilScopeContext={DropdownRecoilScopeContext}>
|
||||
<PageFavoriteButton
|
||||
isFavorite={isFavorite}
|
||||
onClick={handleFavoriteButtonClick}
|
||||
/>
|
||||
<ShowPageAddButton
|
||||
key="add"
|
||||
entity={{
|
||||
id: person.id,
|
||||
type: ActivityTargetableEntityType.Person,
|
||||
relatedEntities: person.company?.id
|
||||
? [
|
||||
{
|
||||
id: person.company?.id,
|
||||
type: ActivityTargetableEntityType.Company,
|
||||
},
|
||||
]
|
||||
: undefined,
|
||||
}}
|
||||
/>
|
||||
</RecoilScope>
|
||||
<PageFavoriteButton
|
||||
isFavorite={isFavorite}
|
||||
onClick={handleFavoriteButtonClick}
|
||||
/>
|
||||
<ShowPageAddButton
|
||||
key="add"
|
||||
entity={{
|
||||
id: person.id,
|
||||
type: ActivityTargetableEntityType.Person,
|
||||
relatedEntities: person.company?.id
|
||||
? [
|
||||
{
|
||||
id: person.company?.id,
|
||||
type: ActivityTargetableEntityType.Company,
|
||||
},
|
||||
]
|
||||
: undefined,
|
||||
}}
|
||||
/>
|
||||
</PageHeader>
|
||||
<PageBody>
|
||||
<RecoilScope CustomRecoilScopeContext={ShowPageRecoilScopeContext}>
|
||||
|
||||
@ -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 (
|
||||
<PageContainer>
|
||||
<RecoilScope CustomRecoilScopeContext={DropdownRecoilScopeContext}>
|
||||
<RecoilScope CustomRecoilScopeContext={TasksRecoilScopeContext}>
|
||||
<TasksEffect />
|
||||
<PageHeader title="Tasks" Icon={IconCheckbox}>
|
||||
<PageAddTaskButton />
|
||||
</PageHeader>
|
||||
<PageBody>
|
||||
{/* TODO: we should refactor filters as a standalone module ? */}
|
||||
<ViewBarContext.Provider
|
||||
value={{
|
||||
ViewBarRecoilScopeContext: TasksRecoilScopeContext,
|
||||
}}
|
||||
>
|
||||
<StyledTasksContainer>
|
||||
<TopBar
|
||||
leftComponent={
|
||||
<StyledTabListContainer>
|
||||
<TabList
|
||||
context={TasksRecoilScopeContext}
|
||||
tabs={TASK_TABS}
|
||||
/>
|
||||
</StyledTabListContainer>
|
||||
}
|
||||
rightComponent={
|
||||
<FilterDropdownButton
|
||||
key="tasks-filter-dropdown-button"
|
||||
hotkeyScope={{
|
||||
scope: RelationPickerHotkeyScope.RelationPicker,
|
||||
}}
|
||||
<RecoilScope CustomRecoilScopeContext={TasksRecoilScopeContext}>
|
||||
<TasksEffect />
|
||||
<PageHeader title="Tasks" Icon={IconCheckbox}>
|
||||
<PageAddTaskButton />
|
||||
</PageHeader>
|
||||
<PageBody>
|
||||
{/* TODO: we should refactor filters as a standalone module ? */}
|
||||
<ViewBarContext.Provider
|
||||
value={{
|
||||
ViewBarRecoilScopeContext: TasksRecoilScopeContext,
|
||||
}}
|
||||
>
|
||||
<StyledTasksContainer>
|
||||
<TopBar
|
||||
leftComponent={
|
||||
<StyledTabListContainer>
|
||||
<TabList
|
||||
context={TasksRecoilScopeContext}
|
||||
tabs={TASK_TABS}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
<TaskGroups />
|
||||
</StyledTasksContainer>
|
||||
</ViewBarContext.Provider>
|
||||
</PageBody>
|
||||
</RecoilScope>
|
||||
</StyledTabListContainer>
|
||||
}
|
||||
rightComponent={
|
||||
<FilterDropdownButton
|
||||
key="tasks-filter-dropdown-button"
|
||||
hotkeyScope={{
|
||||
scope: RelationPickerHotkeyScope.RelationPicker,
|
||||
}}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
<TaskGroups />
|
||||
</StyledTasksContainer>
|
||||
</ViewBarContext.Provider>
|
||||
</PageBody>
|
||||
</RecoilScope>
|
||||
</PageContainer>
|
||||
);
|
||||
|
||||
3
front/src/types/ScopeContextProps.ts
Normal file
3
front/src/types/ScopeContextProps.ts
Normal file
@ -0,0 +1,3 @@
|
||||
export type ScopeContextProps<T = unknown> = {
|
||||
scopeId: string;
|
||||
} & T;
|
||||
Reference in New Issue
Block a user