Removed last old DropdownButton (#1573)
* Removed last old DropdownButton * Update front/src/modules/ui/view-bar/components/SingleEntityFilterDropdownButton.tsx Co-authored-by: Thaïs <guigon.thais@gmail.com> * Fix CI --------- Co-authored-by: Charles Bochet <charlesBochet@users.noreply.github.com> Co-authored-by: Thaïs <guigon.thais@gmail.com> Co-authored-by: Charles Bochet <charles@twenty.com>
This commit is contained in:
5
.github/workflows/ci-front.yaml
vendored
5
.github/workflows/ci-front.yaml
vendored
@ -11,13 +11,8 @@ jobs:
|
|||||||
REACT_APP_SERVER_BASE_URL: http://localhost:3000
|
REACT_APP_SERVER_BASE_URL: http://localhost:3000
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
if: github.event_name == 'push'
|
|
||||||
with:
|
with:
|
||||||
ref: ${{ github.head_ref || github.ref_name }}
|
ref: ${{ github.head_ref || github.ref_name }}
|
||||||
- uses: actions/checkout@v3
|
|
||||||
if: github.event_name == 'pull_request_target'
|
|
||||||
with:
|
|
||||||
ref: "refs/pull/${{ github.event.number }}/merge"
|
|
||||||
- name: Setup Node.js
|
- name: Setup Node.js
|
||||||
uses: actions/setup-node@v3
|
uses: actions/setup-node@v3
|
||||||
with:
|
with:
|
||||||
|
|||||||
@ -1,16 +1,13 @@
|
|||||||
import { useEffect, useRef } from 'react';
|
import { useRef } from 'react';
|
||||||
import { Keys } from 'react-hotkeys-hook';
|
import { Keys } from 'react-hotkeys-hook';
|
||||||
import { flip, offset, Placement, useFloating } from '@floating-ui/react';
|
import { flip, offset, Placement, useFloating } from '@floating-ui/react';
|
||||||
|
|
||||||
import { HotkeyScope } from '@/ui/utilities/hotkey/types/HotkeyScope';
|
import { HotkeyScope } from '@/ui/utilities/hotkey/types/HotkeyScope';
|
||||||
import { useListenClickOutside } from '@/ui/utilities/pointer-event/hooks/useListenClickOutside';
|
import { useListenClickOutside } from '@/ui/utilities/pointer-event/hooks/useListenClickOutside';
|
||||||
import { useRecoilScopedFamilyState } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedFamilyState';
|
|
||||||
import { isDeeplyEqual } from '~/utils/isDeeplyEqual';
|
|
||||||
|
|
||||||
import { HotkeyEffect } from '../../utilities/hotkey/components/HotkeyEffect';
|
import { HotkeyEffect } from '../../utilities/hotkey/components/HotkeyEffect';
|
||||||
import { useDropdownButton } from '../hooks/useDropdownButton';
|
import { useDropdownButton } from '../hooks/useDropdownButton';
|
||||||
import { dropdownButtonCustomHotkeyScopeScopedFamilyState } from '../states/dropdownButtonCustomHotkeyScopeScopedFamilyState';
|
import { useInternalHotkeyScopeManagement } from '../hooks/useInternalHotkeyScopeManagement';
|
||||||
import { DropdownRecoilScopeContext } from '../states/recoil-scope-contexts/DropdownRecoilScopeContext';
|
|
||||||
|
|
||||||
type OwnProps = {
|
type OwnProps = {
|
||||||
buttonComponents: JSX.Element | JSX.Element[];
|
buttonComponents: JSX.Element | JSX.Element[];
|
||||||
@ -57,22 +54,10 @@ export function DropdownButton({
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const [dropdownButtonCustomHotkeyScope, setDropdownButtonCustomHotkeyScope] =
|
useInternalHotkeyScopeManagement({
|
||||||
useRecoilScopedFamilyState(
|
dropdownId,
|
||||||
dropdownButtonCustomHotkeyScopeScopedFamilyState,
|
|
||||||
dropdownId,
|
|
||||||
DropdownRecoilScopeContext,
|
|
||||||
);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (!isDeeplyEqual(dropdownButtonCustomHotkeyScope, dropdownHotkeyScope)) {
|
|
||||||
setDropdownButtonCustomHotkeyScope(dropdownHotkeyScope);
|
|
||||||
}
|
|
||||||
}, [
|
|
||||||
dropdownHotkeyScope,
|
dropdownHotkeyScope,
|
||||||
dropdownButtonCustomHotkeyScope,
|
});
|
||||||
setDropdownButtonCustomHotkeyScope,
|
|
||||||
]);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div ref={containerRef}>
|
<div ref={containerRef}>
|
||||||
|
|||||||
@ -0,0 +1,27 @@
|
|||||||
|
import styled from '@emotion/styled';
|
||||||
|
|
||||||
|
type StyledDropdownButtonProps = {
|
||||||
|
isUnfolded: boolean;
|
||||||
|
isActive?: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const StyledDropdownButtonContainer = styled.div<StyledDropdownButtonProps>`
|
||||||
|
align-items: center;
|
||||||
|
background: ${({ theme }) => theme.background.primary};
|
||||||
|
border-radius: ${({ theme }) => theme.border.radius.sm};
|
||||||
|
color: ${({ isActive, theme, color }) =>
|
||||||
|
color ?? (isActive ? theme.color.blue : 'none')};
|
||||||
|
cursor: pointer;
|
||||||
|
display: flex;
|
||||||
|
filter: ${(props) => (props.isUnfolded ? 'brightness(0.95)' : 'none')};
|
||||||
|
|
||||||
|
padding: ${({ theme }) => theme.spacing(1)};
|
||||||
|
padding-left: ${({ theme }) => theme.spacing(2)};
|
||||||
|
|
||||||
|
padding-right: ${({ theme }) => theme.spacing(2)};
|
||||||
|
user-select: none;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
filter: brightness(0.95);
|
||||||
|
}
|
||||||
|
`;
|
||||||
@ -1,7 +1,7 @@
|
|||||||
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 { useRecoilScopedFamilyState } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedFamilyState';
|
||||||
|
|
||||||
import { dropdownButtonCustomHotkeyScopeScopedFamilyState } from '../states/dropdownButtonCustomHotkeyScopeScopedFamilyState';
|
import { dropdownButtonHotkeyScopeScopedFamilyState } from '../states/dropdownButtonHotkeyScopeScopedFamilyState';
|
||||||
import { isDropdownButtonOpenScopedFamilyState } from '../states/isDropdownButtonOpenScopedFamilyState';
|
import { isDropdownButtonOpenScopedFamilyState } from '../states/isDropdownButtonOpenScopedFamilyState';
|
||||||
import { DropdownRecoilScopeContext } from '../states/recoil-scope-contexts/DropdownRecoilScopeContext';
|
import { DropdownRecoilScopeContext } from '../states/recoil-scope-contexts/DropdownRecoilScopeContext';
|
||||||
|
|
||||||
@ -18,8 +18,8 @@ export function useDropdownButton({ dropdownId }: { dropdownId: string }) {
|
|||||||
DropdownRecoilScopeContext,
|
DropdownRecoilScopeContext,
|
||||||
);
|
);
|
||||||
|
|
||||||
const [dropdownButtonCustomHotkeyScope] = useRecoilScopedFamilyState(
|
const [dropdownButtonHotkeyScope] = useRecoilScopedFamilyState(
|
||||||
dropdownButtonCustomHotkeyScopeScopedFamilyState,
|
dropdownButtonHotkeyScopeScopedFamilyState,
|
||||||
dropdownId,
|
dropdownId,
|
||||||
DropdownRecoilScopeContext,
|
DropdownRecoilScopeContext,
|
||||||
);
|
);
|
||||||
@ -32,10 +32,10 @@ export function useDropdownButton({ dropdownId }: { dropdownId: string }) {
|
|||||||
function openDropdownButton() {
|
function openDropdownButton() {
|
||||||
setIsDropdownButtonOpen(true);
|
setIsDropdownButtonOpen(true);
|
||||||
|
|
||||||
if (dropdownButtonCustomHotkeyScope) {
|
if (dropdownButtonHotkeyScope) {
|
||||||
setHotkeyScopeAndMemorizePreviousScope(
|
setHotkeyScopeAndMemorizePreviousScope(
|
||||||
dropdownButtonCustomHotkeyScope.scope,
|
dropdownButtonHotkeyScope.scope,
|
||||||
dropdownButtonCustomHotkeyScope.customScopes,
|
dropdownButtonHotkeyScope.customScopes,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -0,0 +1,33 @@
|
|||||||
|
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';
|
||||||
|
|
||||||
|
export const useInternalHotkeyScopeManagement = ({
|
||||||
|
dropdownId,
|
||||||
|
dropdownHotkeyScope,
|
||||||
|
}: {
|
||||||
|
dropdownId: string;
|
||||||
|
dropdownHotkeyScope?: HotkeyScope;
|
||||||
|
}) => {
|
||||||
|
const [dropdownButtonHotkeyScope, setDropdownButtonHotkeyScope] =
|
||||||
|
useRecoilScopedFamilyState(
|
||||||
|
dropdownButtonHotkeyScopeScopedFamilyState,
|
||||||
|
dropdownId,
|
||||||
|
DropdownRecoilScopeContext,
|
||||||
|
);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!isDeeplyEqual(dropdownButtonHotkeyScope, dropdownHotkeyScope)) {
|
||||||
|
setDropdownButtonHotkeyScope(dropdownHotkeyScope);
|
||||||
|
}
|
||||||
|
}, [
|
||||||
|
dropdownHotkeyScope,
|
||||||
|
dropdownButtonHotkeyScope,
|
||||||
|
setDropdownButtonHotkeyScope,
|
||||||
|
]);
|
||||||
|
};
|
||||||
@ -2,10 +2,10 @@ import { atomFamily } from 'recoil';
|
|||||||
|
|
||||||
import { HotkeyScope } from '@/ui/utilities/hotkey/types/HotkeyScope';
|
import { HotkeyScope } from '@/ui/utilities/hotkey/types/HotkeyScope';
|
||||||
|
|
||||||
export const dropdownButtonCustomHotkeyScopeScopedFamilyState = atomFamily<
|
export const dropdownButtonHotkeyScopeScopedFamilyState = atomFamily<
|
||||||
HotkeyScope | null | undefined,
|
HotkeyScope | null | undefined,
|
||||||
string
|
string
|
||||||
>({
|
>({
|
||||||
key: 'dropdownButtonCustomHotkeyScopeScopedState',
|
key: 'dropdownButtonHotkeyScopeScopedFamilyState',
|
||||||
default: null,
|
default: null,
|
||||||
});
|
});
|
||||||
@ -4,8 +4,8 @@ import { RecoilState, useRecoilState } from 'recoil';
|
|||||||
import { RecoilScopeContext } from '../states/RecoilScopeContext';
|
import { RecoilScopeContext } from '../states/RecoilScopeContext';
|
||||||
|
|
||||||
export function useRecoilScopedFamilyState<StateType>(
|
export function useRecoilScopedFamilyState<StateType>(
|
||||||
recoilState: (param: string) => RecoilState<StateType>,
|
recoilState: (familyUniqueId: string) => RecoilState<StateType>,
|
||||||
stateKey: string,
|
uniqueIdInRecoilScope: string,
|
||||||
SpecificContext?: Context<string | null>,
|
SpecificContext?: Context<string | null>,
|
||||||
) {
|
) {
|
||||||
const recoilScopeId = useContext(SpecificContext ?? RecoilScopeContext);
|
const recoilScopeId = useContext(SpecificContext ?? RecoilScopeContext);
|
||||||
@ -13,9 +13,11 @@ export function useRecoilScopedFamilyState<StateType>(
|
|||||||
if (!recoilScopeId)
|
if (!recoilScopeId)
|
||||||
throw new Error(
|
throw new Error(
|
||||||
`Using a scoped atom without a RecoilScope : ${
|
`Using a scoped atom without a RecoilScope : ${
|
||||||
recoilState(stateKey).key
|
recoilState('').key
|
||||||
}, verify that you are using a RecoilScope with a specific context if you intended to do so.`,
|
}, verify that you are using a RecoilScope with a specific context if you intended to do so.`,
|
||||||
);
|
);
|
||||||
|
|
||||||
return useRecoilState<StateType>(recoilState(recoilScopeId + stateKey));
|
const familyUniqueId = recoilScopeId + uniqueIdInRecoilScope;
|
||||||
|
|
||||||
|
return useRecoilState<StateType>(recoilState(familyUniqueId));
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,126 +0,0 @@
|
|||||||
import { ReactNode } from 'react';
|
|
||||||
import { useTheme } from '@emotion/react';
|
|
||||||
import styled from '@emotion/styled';
|
|
||||||
import { Key } from 'ts-key-enum';
|
|
||||||
|
|
||||||
import { IconComponent } from '@/ui/icon/types/IconComponent';
|
|
||||||
import { useScopedHotkeys } from '@/ui/utilities/hotkey/hooks/useScopedHotkeys';
|
|
||||||
|
|
||||||
import { DropdownMenuContainer } from './DropdownMenuContainer';
|
|
||||||
|
|
||||||
type DropdownButtonProps = {
|
|
||||||
anchor?: 'left' | 'right';
|
|
||||||
label: ReactNode;
|
|
||||||
isActive: boolean;
|
|
||||||
children?: ReactNode;
|
|
||||||
isUnfolded?: boolean;
|
|
||||||
Icon?: IconComponent;
|
|
||||||
onIsUnfoldedChange?: (newIsUnfolded: boolean) => void;
|
|
||||||
resetState?: () => void;
|
|
||||||
hotkeyScope: string;
|
|
||||||
color?: string;
|
|
||||||
menuWidth?: `${string}px` | 'auto' | number;
|
|
||||||
};
|
|
||||||
|
|
||||||
const StyledDropdownButtonContainer = styled.div`
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
`;
|
|
||||||
|
|
||||||
const StyledDropdownButtonIcon = styled.div`
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
margin-right: ${({ theme }) => theme.spacing(1)};
|
|
||||||
`;
|
|
||||||
|
|
||||||
type StyledDropdownButtonProps = {
|
|
||||||
isUnfolded: boolean;
|
|
||||||
isActive: boolean;
|
|
||||||
};
|
|
||||||
|
|
||||||
const StyledDropdownButton = styled.div<StyledDropdownButtonProps>`
|
|
||||||
align-items: center;
|
|
||||||
background: ${({ theme }) => theme.background.primary};
|
|
||||||
border-radius: ${({ theme }) => theme.border.radius.sm};
|
|
||||||
color: ${({ isActive, theme, color }) =>
|
|
||||||
color ?? (isActive ? theme.color.blue : 'none')};
|
|
||||||
cursor: pointer;
|
|
||||||
display: flex;
|
|
||||||
filter: ${(props) => (props.isUnfolded ? 'brightness(0.95)' : 'none')};
|
|
||||||
|
|
||||||
padding: ${({ theme }) => theme.spacing(1)};
|
|
||||||
padding-left: ${({ theme }) => theme.spacing(2)};
|
|
||||||
|
|
||||||
padding-right: ${({ theme }) => theme.spacing(2)};
|
|
||||||
user-select: none;
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
filter: brightness(0.95);
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @deprecated use ui/dropdown/components/DropdownButton.tsx instead
|
|
||||||
*/
|
|
||||||
function DropdownButton({
|
|
||||||
anchor,
|
|
||||||
label,
|
|
||||||
isActive,
|
|
||||||
children,
|
|
||||||
isUnfolded = false,
|
|
||||||
onIsUnfoldedChange,
|
|
||||||
Icon,
|
|
||||||
hotkeyScope,
|
|
||||||
color,
|
|
||||||
menuWidth,
|
|
||||||
}: DropdownButtonProps) {
|
|
||||||
useScopedHotkeys(
|
|
||||||
[Key.Enter, Key.Escape],
|
|
||||||
() => {
|
|
||||||
onIsUnfoldedChange?.(false);
|
|
||||||
},
|
|
||||||
hotkeyScope,
|
|
||||||
[onIsUnfoldedChange],
|
|
||||||
);
|
|
||||||
|
|
||||||
const onButtonClick = () => {
|
|
||||||
onIsUnfoldedChange?.(!isUnfolded);
|
|
||||||
};
|
|
||||||
|
|
||||||
const onOutsideClick = () => {
|
|
||||||
onIsUnfoldedChange?.(false);
|
|
||||||
};
|
|
||||||
|
|
||||||
const theme = useTheme();
|
|
||||||
|
|
||||||
return (
|
|
||||||
<StyledDropdownButtonContainer>
|
|
||||||
<StyledDropdownButton
|
|
||||||
isUnfolded={isUnfolded}
|
|
||||||
onClick={onButtonClick}
|
|
||||||
isActive={isActive}
|
|
||||||
aria-selected={isActive}
|
|
||||||
color={color}
|
|
||||||
>
|
|
||||||
{Icon && (
|
|
||||||
<StyledDropdownButtonIcon>
|
|
||||||
{<Icon size={theme.icon.size.md} />}
|
|
||||||
</StyledDropdownButtonIcon>
|
|
||||||
)}
|
|
||||||
{label}
|
|
||||||
</StyledDropdownButton>
|
|
||||||
{isUnfolded && (
|
|
||||||
<DropdownMenuContainer
|
|
||||||
width={menuWidth}
|
|
||||||
anchor={anchor}
|
|
||||||
onClose={onOutsideClick}
|
|
||||||
>
|
|
||||||
{children}
|
|
||||||
</DropdownMenuContainer>
|
|
||||||
)}
|
|
||||||
</StyledDropdownButtonContainer>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default DropdownButton;
|
|
||||||
@ -1,15 +1,54 @@
|
|||||||
|
import { Context } from 'react';
|
||||||
|
|
||||||
import { StyledHeaderDropdownButton } from '@/ui/dropdown/components/StyledHeaderDropdownButton';
|
import { StyledHeaderDropdownButton } from '@/ui/dropdown/components/StyledHeaderDropdownButton';
|
||||||
import { useDropdownButton } from '@/ui/dropdown/hooks/useDropdownButton';
|
import { useDropdownButton } from '@/ui/dropdown/hooks/useDropdownButton';
|
||||||
|
import { useRecoilScopedState } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedState';
|
||||||
|
|
||||||
import { FilterDropdownId } from '../constants/FilterDropdownId';
|
import { FilterDropdownId } from '../constants/FilterDropdownId';
|
||||||
|
import { filterDefinitionUsedInDropdownScopedState } from '../states/filterDefinitionUsedInDropdownScopedState';
|
||||||
|
import { filterDropdownSearchInputScopedState } from '../states/filterDropdownSearchInputScopedState';
|
||||||
|
import { isFilterDropdownOperandSelectUnfoldedScopedState } from '../states/isFilterDropdownOperandSelectUnfoldedScopedState';
|
||||||
|
import { selectedOperandInDropdownScopedState } from '../states/selectedOperandInDropdownScopedState';
|
||||||
|
|
||||||
export function MultipleFiltersButton() {
|
type OwnProps = {
|
||||||
|
context: Context<string | null>;
|
||||||
|
};
|
||||||
|
|
||||||
|
export function MultipleFiltersButton({ context }: OwnProps) {
|
||||||
const { isDropdownButtonOpen, toggleDropdownButton } = useDropdownButton({
|
const { isDropdownButtonOpen, toggleDropdownButton } = useDropdownButton({
|
||||||
dropdownId: FilterDropdownId,
|
dropdownId: FilterDropdownId,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const [, setIsFilterDropdownOperandSelectUnfolded] = useRecoilScopedState(
|
||||||
|
isFilterDropdownOperandSelectUnfoldedScopedState,
|
||||||
|
context,
|
||||||
|
);
|
||||||
|
|
||||||
|
const [, setFilterDefinitionUsedInDropdown] = useRecoilScopedState(
|
||||||
|
filterDefinitionUsedInDropdownScopedState,
|
||||||
|
context,
|
||||||
|
);
|
||||||
|
|
||||||
|
const [, setFilterDropdownSearchInput] = useRecoilScopedState(
|
||||||
|
filterDropdownSearchInputScopedState,
|
||||||
|
context,
|
||||||
|
);
|
||||||
|
|
||||||
|
const [, setSelectedOperandInDropdown] = useRecoilScopedState(
|
||||||
|
selectedOperandInDropdownScopedState,
|
||||||
|
context,
|
||||||
|
);
|
||||||
|
|
||||||
|
const resetState = () => {
|
||||||
|
setIsFilterDropdownOperandSelectUnfolded(false);
|
||||||
|
setFilterDefinitionUsedInDropdown(null);
|
||||||
|
setSelectedOperandInDropdown(null);
|
||||||
|
setFilterDropdownSearchInput('');
|
||||||
|
};
|
||||||
|
|
||||||
function handleClick() {
|
function handleClick() {
|
||||||
toggleDropdownButton();
|
toggleDropdownButton();
|
||||||
|
resetState();
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@ -1,15 +1,9 @@
|
|||||||
import { Context, useCallback, useEffect } from 'react';
|
import { Context } from 'react';
|
||||||
|
|
||||||
import { DropdownButton } from '@/ui/dropdown/components/DropdownButton';
|
import { DropdownButton } from '@/ui/dropdown/components/DropdownButton';
|
||||||
import { useDropdownButton } from '@/ui/dropdown/hooks/useDropdownButton';
|
|
||||||
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 { FilterDropdownId } from '../constants/FilterDropdownId';
|
import { FilterDropdownId } from '../constants/FilterDropdownId';
|
||||||
import { filterDefinitionUsedInDropdownScopedState } from '../states/filterDefinitionUsedInDropdownScopedState';
|
|
||||||
import { filterDropdownSearchInputScopedState } from '../states/filterDropdownSearchInputScopedState';
|
|
||||||
import { isFilterDropdownOperandSelectUnfoldedScopedState } from '../states/isFilterDropdownOperandSelectUnfoldedScopedState';
|
|
||||||
import { selectedOperandInDropdownScopedState } from '../states/selectedOperandInDropdownScopedState';
|
|
||||||
|
|
||||||
import { MultipleFiltersButton } from './MultipleFiltersButton';
|
import { MultipleFiltersButton } from './MultipleFiltersButton';
|
||||||
import { MultipleFiltersDropdownContent } from './MultipleFiltersDropdownContent';
|
import { MultipleFiltersDropdownContent } from './MultipleFiltersDropdownContent';
|
||||||
@ -23,52 +17,10 @@ export function MultipleFiltersDropdownButton({
|
|||||||
context,
|
context,
|
||||||
hotkeyScope,
|
hotkeyScope,
|
||||||
}: MultipleFiltersDropdownButtonProps) {
|
}: MultipleFiltersDropdownButtonProps) {
|
||||||
const [, setIsFilterDropdownOperandSelectUnfolded] = useRecoilScopedState(
|
|
||||||
isFilterDropdownOperandSelectUnfoldedScopedState,
|
|
||||||
context,
|
|
||||||
);
|
|
||||||
|
|
||||||
const [, setFilterDefinitionUsedInDropdown] = useRecoilScopedState(
|
|
||||||
filterDefinitionUsedInDropdownScopedState,
|
|
||||||
context,
|
|
||||||
);
|
|
||||||
|
|
||||||
const [, setFilterDropdownSearchInput] = useRecoilScopedState(
|
|
||||||
filterDropdownSearchInputScopedState,
|
|
||||||
context,
|
|
||||||
);
|
|
||||||
|
|
||||||
const [, setSelectedOperandInDropdown] = useRecoilScopedState(
|
|
||||||
selectedOperandInDropdownScopedState,
|
|
||||||
context,
|
|
||||||
);
|
|
||||||
|
|
||||||
const { isDropdownButtonOpen } = useDropdownButton({
|
|
||||||
dropdownId: FilterDropdownId,
|
|
||||||
});
|
|
||||||
|
|
||||||
const resetState = useCallback(() => {
|
|
||||||
setIsFilterDropdownOperandSelectUnfolded(false);
|
|
||||||
setFilterDefinitionUsedInDropdown(null);
|
|
||||||
setSelectedOperandInDropdown(null);
|
|
||||||
setFilterDropdownSearchInput('');
|
|
||||||
}, [
|
|
||||||
setFilterDefinitionUsedInDropdown,
|
|
||||||
setSelectedOperandInDropdown,
|
|
||||||
setFilterDropdownSearchInput,
|
|
||||||
setIsFilterDropdownOperandSelectUnfolded,
|
|
||||||
]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (!isDropdownButtonOpen) {
|
|
||||||
resetState();
|
|
||||||
}
|
|
||||||
}, [isDropdownButtonOpen, resetState]);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<DropdownButton
|
<DropdownButton
|
||||||
dropdownId={FilterDropdownId}
|
dropdownId={FilterDropdownId}
|
||||||
buttonComponents={<MultipleFiltersButton />}
|
buttonComponents={<MultipleFiltersButton context={context} />}
|
||||||
dropdownComponents={<MultipleFiltersDropdownContent context={context} />}
|
dropdownComponents={<MultipleFiltersDropdownContent context={context} />}
|
||||||
dropdownHotkeyScope={hotkeyScope}
|
dropdownHotkeyScope={hotkeyScope}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@ -3,6 +3,7 @@ import React from 'react';
|
|||||||
import { useTheme } from '@emotion/react';
|
import { useTheme } from '@emotion/react';
|
||||||
import styled from '@emotion/styled';
|
import styled from '@emotion/styled';
|
||||||
|
|
||||||
|
import { DropdownMenuContainer } from '@/ui/dropdown/components/DropdownMenuContainer';
|
||||||
import { DropdownRecoilScopeContext } from '@/ui/dropdown/states/recoil-scope-contexts/DropdownRecoilScopeContext';
|
import { DropdownRecoilScopeContext } from '@/ui/dropdown/states/recoil-scope-contexts/DropdownRecoilScopeContext';
|
||||||
import { IconChevronDown } from '@/ui/icon';
|
import { IconChevronDown } from '@/ui/icon';
|
||||||
import { useSetHotkeyScope } from '@/ui/utilities/hotkey/hooks/useSetHotkeyScope';
|
import { useSetHotkeyScope } from '@/ui/utilities/hotkey/hooks/useSetHotkeyScope';
|
||||||
@ -18,7 +19,6 @@ import { filtersScopedState } from '../states/filtersScopedState';
|
|||||||
import { isFilterDropdownUnfoldedScopedState } from '../states/isFilterDropdownUnfoldedScopedState';
|
import { isFilterDropdownUnfoldedScopedState } from '../states/isFilterDropdownUnfoldedScopedState';
|
||||||
import { getOperandsForFilterType } from '../utils/getOperandsForFilterType';
|
import { getOperandsForFilterType } from '../utils/getOperandsForFilterType';
|
||||||
|
|
||||||
import { DropdownMenuContainer } from './DropdownMenuContainer';
|
|
||||||
import { FilterDropdownEntitySearchInput } from './FilterDropdownEntitySearchInput';
|
import { FilterDropdownEntitySearchInput } from './FilterDropdownEntitySearchInput';
|
||||||
import { FilterDropdownEntitySelect } from './FilterDropdownEntitySelect';
|
import { FilterDropdownEntitySelect } from './FilterDropdownEntitySelect';
|
||||||
import { GenericEntityFilterChip } from './GenericEntityFilterChip';
|
import { GenericEntityFilterChip } from './GenericEntityFilterChip';
|
||||||
|
|||||||
@ -5,13 +5,13 @@ import { Key } from 'ts-key-enum';
|
|||||||
|
|
||||||
import { Button } from '@/ui/button/components/Button';
|
import { Button } from '@/ui/button/components/Button';
|
||||||
import { ButtonGroup } from '@/ui/button/components/ButtonGroup';
|
import { ButtonGroup } from '@/ui/button/components/ButtonGroup';
|
||||||
|
import { DropdownMenuContainer } from '@/ui/dropdown/components/DropdownMenuContainer';
|
||||||
import { StyledDropdownMenuItemsContainer } from '@/ui/dropdown/components/StyledDropdownMenuItemsContainer';
|
import { StyledDropdownMenuItemsContainer } from '@/ui/dropdown/components/StyledDropdownMenuItemsContainer';
|
||||||
import { IconChevronDown, IconPlus } from '@/ui/icon';
|
import { IconChevronDown, IconPlus } from '@/ui/icon';
|
||||||
import { MenuItem } from '@/ui/menu-item/components/MenuItem';
|
import { MenuItem } from '@/ui/menu-item/components/MenuItem';
|
||||||
import { useScopedHotkeys } from '@/ui/utilities/hotkey/hooks/useScopedHotkeys';
|
import { useScopedHotkeys } from '@/ui/utilities/hotkey/hooks/useScopedHotkeys';
|
||||||
import { useContextScopeId } from '@/ui/utilities/recoil-scope/hooks/useContextScopeId';
|
import { useContextScopeId } from '@/ui/utilities/recoil-scope/hooks/useContextScopeId';
|
||||||
import { useRecoilScopedValue } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedValue';
|
import { useRecoilScopedValue } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedValue';
|
||||||
import { DropdownMenuContainer } from '@/ui/view-bar/components/DropdownMenuContainer';
|
|
||||||
import { currentViewIdScopedState } from '@/ui/view-bar/states/currentViewIdScopedState';
|
import { currentViewIdScopedState } from '@/ui/view-bar/states/currentViewIdScopedState';
|
||||||
import { filtersScopedState } from '@/ui/view-bar/states/filtersScopedState';
|
import { filtersScopedState } from '@/ui/view-bar/states/filtersScopedState';
|
||||||
import { savedFiltersFamilyState } from '@/ui/view-bar/states/savedFiltersFamilyState';
|
import { savedFiltersFamilyState } from '@/ui/view-bar/states/savedFiltersFamilyState';
|
||||||
|
|||||||
@ -35,7 +35,7 @@ export const ViewBar = ({
|
|||||||
leftComponent={
|
leftComponent={
|
||||||
<ViewsDropdownButton
|
<ViewsDropdownButton
|
||||||
onViewEditModeChange={openOptionsDropdownButton}
|
onViewEditModeChange={openOptionsDropdownButton}
|
||||||
hotkeyScope={ViewsHotkeyScope.ListDropdown}
|
hotkeyScope={{ scope: ViewsHotkeyScope.ListDropdown }}
|
||||||
scopeContext={scopeContext}
|
scopeContext={scopeContext}
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,17 +1,14 @@
|
|||||||
import {
|
import { type Context, type MouseEvent, useContext } from 'react';
|
||||||
type Context,
|
|
||||||
type MouseEvent,
|
|
||||||
useCallback,
|
|
||||||
useContext,
|
|
||||||
useEffect,
|
|
||||||
useState,
|
|
||||||
} from 'react';
|
|
||||||
import { useTheme } from '@emotion/react';
|
import { useTheme } from '@emotion/react';
|
||||||
import styled from '@emotion/styled';
|
import styled from '@emotion/styled';
|
||||||
import { useRecoilCallback, useSetRecoilState } from 'recoil';
|
import { useRecoilCallback, useSetRecoilState } from 'recoil';
|
||||||
|
|
||||||
|
import { DropdownButton } from '@/ui/dropdown/components/DropdownButton';
|
||||||
|
import { StyledDropdownButtonContainer } from '@/ui/dropdown/components/StyledDropdownButtonContainer';
|
||||||
|
import { StyledDropdownMenu } from '@/ui/dropdown/components/StyledDropdownMenu';
|
||||||
import { StyledDropdownMenuItemsContainer } from '@/ui/dropdown/components/StyledDropdownMenuItemsContainer';
|
import { StyledDropdownMenuItemsContainer } from '@/ui/dropdown/components/StyledDropdownMenuItemsContainer';
|
||||||
import { StyledDropdownMenuSeparator } from '@/ui/dropdown/components/StyledDropdownMenuSeparator';
|
import { StyledDropdownMenuSeparator } from '@/ui/dropdown/components/StyledDropdownMenuSeparator';
|
||||||
|
import { useDropdownButton } from '@/ui/dropdown/hooks/useDropdownButton';
|
||||||
import {
|
import {
|
||||||
IconChevronDown,
|
IconChevronDown,
|
||||||
IconList,
|
IconList,
|
||||||
@ -21,10 +18,10 @@ import {
|
|||||||
} from '@/ui/icon';
|
} from '@/ui/icon';
|
||||||
import { MenuItem } from '@/ui/menu-item/components/MenuItem';
|
import { MenuItem } from '@/ui/menu-item/components/MenuItem';
|
||||||
import { MOBILE_VIEWPORT } from '@/ui/theme/constants/theme';
|
import { MOBILE_VIEWPORT } from '@/ui/theme/constants/theme';
|
||||||
import { usePreviousHotkeyScope } from '@/ui/utilities/hotkey/hooks/usePreviousHotkeyScope';
|
import { HotkeyScope } from '@/ui/utilities/hotkey/types/HotkeyScope';
|
||||||
import { useContextScopeId } from '@/ui/utilities/recoil-scope/hooks/useContextScopeId';
|
import { useContextScopeId } from '@/ui/utilities/recoil-scope/hooks/useContextScopeId';
|
||||||
|
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 DropdownButton from '@/ui/view-bar/components/DropdownButton';
|
|
||||||
import { currentViewIdScopedState } from '@/ui/view-bar/states/currentViewIdScopedState';
|
import { currentViewIdScopedState } from '@/ui/view-bar/states/currentViewIdScopedState';
|
||||||
import { filtersScopedState } from '@/ui/view-bar/states/filtersScopedState';
|
import { filtersScopedState } from '@/ui/view-bar/states/filtersScopedState';
|
||||||
import { savedFiltersFamilyState } from '@/ui/view-bar/states/savedFiltersFamilyState';
|
import { savedFiltersFamilyState } from '@/ui/view-bar/states/savedFiltersFamilyState';
|
||||||
@ -33,9 +30,9 @@ import { currentViewScopedSelector } from '@/ui/view-bar/states/selectors/curren
|
|||||||
import { sortsScopedState } from '@/ui/view-bar/states/sortsScopedState';
|
import { sortsScopedState } from '@/ui/view-bar/states/sortsScopedState';
|
||||||
import { viewEditModeState } from '@/ui/view-bar/states/viewEditModeState';
|
import { viewEditModeState } from '@/ui/view-bar/states/viewEditModeState';
|
||||||
import { viewsScopedState } from '@/ui/view-bar/states/viewsScopedState';
|
import { viewsScopedState } from '@/ui/view-bar/states/viewsScopedState';
|
||||||
import { ViewsHotkeyScope } from '@/ui/view-bar/types/ViewsHotkeyScope';
|
|
||||||
import { assertNotNull } from '~/utils/assert';
|
import { assertNotNull } from '~/utils/assert';
|
||||||
|
|
||||||
|
import { ViewsDropdownId } from '../constants/ViewsDropdownId';
|
||||||
import { ViewBarContext } from '../contexts/ViewBarContext';
|
import { ViewBarContext } from '../contexts/ViewBarContext';
|
||||||
import { useRemoveView } from '../hooks/useRemoveView';
|
import { useRemoveView } from '../hooks/useRemoveView';
|
||||||
|
|
||||||
@ -73,7 +70,7 @@ const StyledViewName = styled.span`
|
|||||||
`;
|
`;
|
||||||
|
|
||||||
export type ViewsDropdownButtonProps = {
|
export type ViewsDropdownButtonProps = {
|
||||||
hotkeyScope: ViewsHotkeyScope;
|
hotkeyScope: HotkeyScope;
|
||||||
onViewEditModeChange?: () => void;
|
onViewEditModeChange?: () => void;
|
||||||
scopeContext: Context<string | null>;
|
scopeContext: Context<string | null>;
|
||||||
};
|
};
|
||||||
@ -88,24 +85,24 @@ export const ViewsDropdownButton = ({
|
|||||||
const { defaultViewName, onViewSelect } = useContext(ViewBarContext);
|
const { defaultViewName, onViewSelect } = useContext(ViewBarContext);
|
||||||
const recoilScopeId = useContextScopeId(scopeContext);
|
const recoilScopeId = useContextScopeId(scopeContext);
|
||||||
|
|
||||||
const [isUnfolded, setIsUnfolded] = useState(false);
|
|
||||||
|
|
||||||
const currentView = useRecoilScopedValue(
|
const currentView = useRecoilScopedValue(
|
||||||
currentViewScopedSelector,
|
currentViewScopedSelector,
|
||||||
scopeContext,
|
scopeContext,
|
||||||
);
|
);
|
||||||
const views = useRecoilScopedValue(viewsScopedState, scopeContext);
|
const [views] = useRecoilScopedState(viewsScopedState, scopeContext);
|
||||||
const setViewEditMode = useSetRecoilState(viewEditModeState);
|
|
||||||
|
|
||||||
const {
|
const { isDropdownButtonOpen, closeDropdownButton, toggleDropdownButton } =
|
||||||
goBackToPreviousHotkeyScope,
|
useDropdownButton({
|
||||||
setHotkeyScopeAndMemorizePreviousScope,
|
dropdownId: ViewsDropdownId,
|
||||||
} = usePreviousHotkeyScope();
|
});
|
||||||
|
|
||||||
|
const setViewEditMode = useSetRecoilState(viewEditModeState);
|
||||||
|
|
||||||
const handleViewSelect = useRecoilCallback(
|
const handleViewSelect = useRecoilCallback(
|
||||||
({ set, snapshot }) =>
|
({ set, snapshot }) =>
|
||||||
async (viewId: string) => {
|
async (viewId: string) => {
|
||||||
await onViewSelect?.(viewId);
|
await onViewSelect?.(viewId);
|
||||||
|
|
||||||
const savedFilters = await snapshot.getPromise(
|
const savedFilters = await snapshot.getPromise(
|
||||||
savedFiltersFamilyState(viewId),
|
savedFiltersFamilyState(viewId),
|
||||||
);
|
);
|
||||||
@ -116,26 +113,26 @@ export const ViewsDropdownButton = ({
|
|||||||
set(filtersScopedState(recoilScopeId), savedFilters);
|
set(filtersScopedState(recoilScopeId), savedFilters);
|
||||||
set(sortsScopedState(recoilScopeId), savedSorts);
|
set(sortsScopedState(recoilScopeId), savedSorts);
|
||||||
set(currentViewIdScopedState(recoilScopeId), viewId);
|
set(currentViewIdScopedState(recoilScopeId), viewId);
|
||||||
setIsUnfolded(false);
|
closeDropdownButton();
|
||||||
},
|
},
|
||||||
[onViewSelect, recoilScopeId],
|
[onViewSelect, recoilScopeId, closeDropdownButton],
|
||||||
);
|
);
|
||||||
|
|
||||||
const handleAddViewButtonClick = useCallback(() => {
|
const handleAddViewButtonClick = () => {
|
||||||
setViewEditMode({ mode: 'create', viewId: undefined });
|
setViewEditMode({ mode: 'create', viewId: undefined });
|
||||||
onViewEditModeChange?.();
|
onViewEditModeChange?.();
|
||||||
setIsUnfolded(false);
|
closeDropdownButton();
|
||||||
}, [setViewEditMode, onViewEditModeChange]);
|
};
|
||||||
|
|
||||||
const handleEditViewButtonClick = useCallback(
|
const handleEditViewButtonClick = (
|
||||||
(event: MouseEvent<HTMLButtonElement>, viewId: string) => {
|
event: MouseEvent<HTMLButtonElement>,
|
||||||
event.stopPropagation();
|
viewId: string,
|
||||||
setViewEditMode({ mode: 'edit', viewId });
|
) => {
|
||||||
onViewEditModeChange?.();
|
event.stopPropagation();
|
||||||
setIsUnfolded(false);
|
setViewEditMode({ mode: 'edit', viewId });
|
||||||
},
|
onViewEditModeChange?.();
|
||||||
[setViewEditMode, onViewEditModeChange],
|
closeDropdownButton();
|
||||||
);
|
};
|
||||||
|
|
||||||
const { removeView } = useRemoveView({ scopeContext });
|
const { removeView } = useRemoveView({ scopeContext });
|
||||||
|
|
||||||
@ -146,24 +143,22 @@ export const ViewsDropdownButton = ({
|
|||||||
event.stopPropagation();
|
event.stopPropagation();
|
||||||
|
|
||||||
await removeView(viewId);
|
await removeView(viewId);
|
||||||
setIsUnfolded(false);
|
closeDropdownButton();
|
||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
const handleViewButtonClick = () => {
|
||||||
isUnfolded
|
toggleDropdownButton();
|
||||||
? setHotkeyScopeAndMemorizePreviousScope(hotkeyScope)
|
};
|
||||||
: goBackToPreviousHotkeyScope();
|
|
||||||
}, [
|
|
||||||
hotkeyScope,
|
|
||||||
goBackToPreviousHotkeyScope,
|
|
||||||
isUnfolded,
|
|
||||||
setHotkeyScopeAndMemorizePreviousScope,
|
|
||||||
]);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<DropdownButton
|
<DropdownButton
|
||||||
label={
|
dropdownId={ViewsDropdownId}
|
||||||
<>
|
dropdownHotkeyScope={hotkeyScope}
|
||||||
|
buttonComponents={
|
||||||
|
<StyledDropdownButtonContainer
|
||||||
|
isUnfolded={isDropdownButtonOpen}
|
||||||
|
onClick={handleViewButtonClick}
|
||||||
|
>
|
||||||
<StyledViewIcon size={theme.icon.size.md} />
|
<StyledViewIcon size={theme.icon.size.md} />
|
||||||
<StyledViewName>
|
<StyledViewName>
|
||||||
{currentView?.name || defaultViewName}
|
{currentView?.name || defaultViewName}
|
||||||
@ -171,47 +166,44 @@ export const ViewsDropdownButton = ({
|
|||||||
<StyledDropdownLabelAdornments>
|
<StyledDropdownLabelAdornments>
|
||||||
· {views.length} <IconChevronDown size={theme.icon.size.sm} />
|
· {views.length} <IconChevronDown size={theme.icon.size.sm} />
|
||||||
</StyledDropdownLabelAdornments>
|
</StyledDropdownLabelAdornments>
|
||||||
</>
|
</StyledDropdownButtonContainer>
|
||||||
}
|
}
|
||||||
isActive={false}
|
dropdownComponents={
|
||||||
isUnfolded={isUnfolded}
|
<StyledDropdownMenu>
|
||||||
onIsUnfoldedChange={setIsUnfolded}
|
<StyledDropdownMenuItemsContainer>
|
||||||
anchor="left"
|
{views.map((view) => (
|
||||||
hotkeyScope={hotkeyScope}
|
<MenuItem
|
||||||
menuWidth="auto"
|
key={view.id}
|
||||||
>
|
iconButtons={[
|
||||||
<StyledDropdownMenuItemsContainer>
|
{
|
||||||
{views.map((view) => (
|
Icon: IconPencil,
|
||||||
<MenuItem
|
|
||||||
key={view.id}
|
|
||||||
iconButtons={[
|
|
||||||
{
|
|
||||||
Icon: IconPencil,
|
|
||||||
onClick: (event: MouseEvent<HTMLButtonElement>) =>
|
|
||||||
handleEditViewButtonClick(event, view.id),
|
|
||||||
},
|
|
||||||
views.length > 1
|
|
||||||
? {
|
|
||||||
Icon: IconTrash,
|
|
||||||
onClick: (event: MouseEvent<HTMLButtonElement>) =>
|
onClick: (event: MouseEvent<HTMLButtonElement>) =>
|
||||||
handleDeleteViewButtonClick(event, view.id),
|
handleEditViewButtonClick(event, view.id),
|
||||||
}
|
},
|
||||||
: null,
|
views.length > 1
|
||||||
].filter(assertNotNull)}
|
? {
|
||||||
onClick={() => handleViewSelect(view.id)}
|
Icon: IconTrash,
|
||||||
LeftIcon={IconList}
|
onClick: (event: MouseEvent<HTMLButtonElement>) =>
|
||||||
text={view.name}
|
handleDeleteViewButtonClick(event, view.id),
|
||||||
/>
|
}
|
||||||
))}
|
: null,
|
||||||
</StyledDropdownMenuItemsContainer>
|
].filter(assertNotNull)}
|
||||||
<StyledDropdownMenuSeparator />
|
onClick={() => handleViewSelect(view.id)}
|
||||||
<StyledBoldDropdownMenuItemsContainer>
|
LeftIcon={IconList}
|
||||||
<MenuItem
|
text={view.name}
|
||||||
onClick={handleAddViewButtonClick}
|
/>
|
||||||
LeftIcon={IconPlus}
|
))}
|
||||||
text="Add view"
|
</StyledDropdownMenuItemsContainer>
|
||||||
/>
|
<StyledDropdownMenuSeparator />
|
||||||
</StyledBoldDropdownMenuItemsContainer>
|
<StyledBoldDropdownMenuItemsContainer>
|
||||||
</DropdownButton>
|
<MenuItem
|
||||||
|
onClick={handleAddViewButtonClick}
|
||||||
|
LeftIcon={IconPlus}
|
||||||
|
text="Add view"
|
||||||
|
/>
|
||||||
|
</StyledBoldDropdownMenuItemsContainer>
|
||||||
|
</StyledDropdownMenu>
|
||||||
|
}
|
||||||
|
/>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@ -0,0 +1 @@
|
|||||||
|
export const ViewsDropdownId = 'views';
|
||||||
Reference in New Issue
Block a user