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
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
if: github.event_name == 'push'
|
||||
with:
|
||||
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
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
|
||||
@ -1,16 +1,13 @@
|
||||
import { useEffect, useRef } from 'react';
|
||||
import { useRef } from 'react';
|
||||
import { Keys } from 'react-hotkeys-hook';
|
||||
import { flip, offset, Placement, useFloating } from '@floating-ui/react';
|
||||
|
||||
import { HotkeyScope } from '@/ui/utilities/hotkey/types/HotkeyScope';
|
||||
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 { useDropdownButton } from '../hooks/useDropdownButton';
|
||||
import { dropdownButtonCustomHotkeyScopeScopedFamilyState } from '../states/dropdownButtonCustomHotkeyScopeScopedFamilyState';
|
||||
import { DropdownRecoilScopeContext } from '../states/recoil-scope-contexts/DropdownRecoilScopeContext';
|
||||
import { useInternalHotkeyScopeManagement } from '../hooks/useInternalHotkeyScopeManagement';
|
||||
|
||||
type OwnProps = {
|
||||
buttonComponents: JSX.Element | JSX.Element[];
|
||||
@ -57,22 +54,10 @@ export function DropdownButton({
|
||||
},
|
||||
});
|
||||
|
||||
const [dropdownButtonCustomHotkeyScope, setDropdownButtonCustomHotkeyScope] =
|
||||
useRecoilScopedFamilyState(
|
||||
dropdownButtonCustomHotkeyScopeScopedFamilyState,
|
||||
dropdownId,
|
||||
DropdownRecoilScopeContext,
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
if (!isDeeplyEqual(dropdownButtonCustomHotkeyScope, dropdownHotkeyScope)) {
|
||||
setDropdownButtonCustomHotkeyScope(dropdownHotkeyScope);
|
||||
}
|
||||
}, [
|
||||
useInternalHotkeyScopeManagement({
|
||||
dropdownId,
|
||||
dropdownHotkeyScope,
|
||||
dropdownButtonCustomHotkeyScope,
|
||||
setDropdownButtonCustomHotkeyScope,
|
||||
]);
|
||||
});
|
||||
|
||||
return (
|
||||
<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 { useRecoilScopedFamilyState } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedFamilyState';
|
||||
|
||||
import { dropdownButtonCustomHotkeyScopeScopedFamilyState } from '../states/dropdownButtonCustomHotkeyScopeScopedFamilyState';
|
||||
import { dropdownButtonHotkeyScopeScopedFamilyState } from '../states/dropdownButtonHotkeyScopeScopedFamilyState';
|
||||
import { isDropdownButtonOpenScopedFamilyState } from '../states/isDropdownButtonOpenScopedFamilyState';
|
||||
import { DropdownRecoilScopeContext } from '../states/recoil-scope-contexts/DropdownRecoilScopeContext';
|
||||
|
||||
@ -18,8 +18,8 @@ export function useDropdownButton({ dropdownId }: { dropdownId: string }) {
|
||||
DropdownRecoilScopeContext,
|
||||
);
|
||||
|
||||
const [dropdownButtonCustomHotkeyScope] = useRecoilScopedFamilyState(
|
||||
dropdownButtonCustomHotkeyScopeScopedFamilyState,
|
||||
const [dropdownButtonHotkeyScope] = useRecoilScopedFamilyState(
|
||||
dropdownButtonHotkeyScopeScopedFamilyState,
|
||||
dropdownId,
|
||||
DropdownRecoilScopeContext,
|
||||
);
|
||||
@ -32,10 +32,10 @@ export function useDropdownButton({ dropdownId }: { dropdownId: string }) {
|
||||
function openDropdownButton() {
|
||||
setIsDropdownButtonOpen(true);
|
||||
|
||||
if (dropdownButtonCustomHotkeyScope) {
|
||||
if (dropdownButtonHotkeyScope) {
|
||||
setHotkeyScopeAndMemorizePreviousScope(
|
||||
dropdownButtonCustomHotkeyScope.scope,
|
||||
dropdownButtonCustomHotkeyScope.customScopes,
|
||||
dropdownButtonHotkeyScope.scope,
|
||||
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';
|
||||
|
||||
export const dropdownButtonCustomHotkeyScopeScopedFamilyState = atomFamily<
|
||||
export const dropdownButtonHotkeyScopeScopedFamilyState = atomFamily<
|
||||
HotkeyScope | null | undefined,
|
||||
string
|
||||
>({
|
||||
key: 'dropdownButtonCustomHotkeyScopeScopedState',
|
||||
key: 'dropdownButtonHotkeyScopeScopedFamilyState',
|
||||
default: null,
|
||||
});
|
||||
@ -4,8 +4,8 @@ import { RecoilState, useRecoilState } from 'recoil';
|
||||
import { RecoilScopeContext } from '../states/RecoilScopeContext';
|
||||
|
||||
export function useRecoilScopedFamilyState<StateType>(
|
||||
recoilState: (param: string) => RecoilState<StateType>,
|
||||
stateKey: string,
|
||||
recoilState: (familyUniqueId: string) => RecoilState<StateType>,
|
||||
uniqueIdInRecoilScope: string,
|
||||
SpecificContext?: Context<string | null>,
|
||||
) {
|
||||
const recoilScopeId = useContext(SpecificContext ?? RecoilScopeContext);
|
||||
@ -13,9 +13,11 @@ export function useRecoilScopedFamilyState<StateType>(
|
||||
if (!recoilScopeId)
|
||||
throw new Error(
|
||||
`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.`,
|
||||
);
|
||||
|
||||
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 { useDropdownButton } from '@/ui/dropdown/hooks/useDropdownButton';
|
||||
import { useRecoilScopedState } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedState';
|
||||
|
||||
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({
|
||||
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() {
|
||||
toggleDropdownButton();
|
||||
resetState();
|
||||
}
|
||||
|
||||
return (
|
||||
|
||||
@ -1,15 +1,9 @@
|
||||
import { Context, useCallback, useEffect } from 'react';
|
||||
import { Context } from 'react';
|
||||
|
||||
import { DropdownButton } from '@/ui/dropdown/components/DropdownButton';
|
||||
import { useDropdownButton } from '@/ui/dropdown/hooks/useDropdownButton';
|
||||
import { HotkeyScope } from '@/ui/utilities/hotkey/types/HotkeyScope';
|
||||
import { useRecoilScopedState } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedState';
|
||||
|
||||
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 { MultipleFiltersDropdownContent } from './MultipleFiltersDropdownContent';
|
||||
@ -23,52 +17,10 @@ export function MultipleFiltersDropdownButton({
|
||||
context,
|
||||
hotkeyScope,
|
||||
}: 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 (
|
||||
<DropdownButton
|
||||
dropdownId={FilterDropdownId}
|
||||
buttonComponents={<MultipleFiltersButton />}
|
||||
buttonComponents={<MultipleFiltersButton context={context} />}
|
||||
dropdownComponents={<MultipleFiltersDropdownContent context={context} />}
|
||||
dropdownHotkeyScope={hotkeyScope}
|
||||
/>
|
||||
|
||||
@ -3,6 +3,7 @@ import React from 'react';
|
||||
import { useTheme } from '@emotion/react';
|
||||
import styled from '@emotion/styled';
|
||||
|
||||
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';
|
||||
@ -18,7 +19,6 @@ import { filtersScopedState } from '../states/filtersScopedState';
|
||||
import { isFilterDropdownUnfoldedScopedState } from '../states/isFilterDropdownUnfoldedScopedState';
|
||||
import { getOperandsForFilterType } from '../utils/getOperandsForFilterType';
|
||||
|
||||
import { DropdownMenuContainer } from './DropdownMenuContainer';
|
||||
import { FilterDropdownEntitySearchInput } from './FilterDropdownEntitySearchInput';
|
||||
import { FilterDropdownEntitySelect } from './FilterDropdownEntitySelect';
|
||||
import { GenericEntityFilterChip } from './GenericEntityFilterChip';
|
||||
|
||||
@ -5,13 +5,13 @@ import { Key } from 'ts-key-enum';
|
||||
|
||||
import { Button } from '@/ui/button/components/Button';
|
||||
import { ButtonGroup } from '@/ui/button/components/ButtonGroup';
|
||||
import { DropdownMenuContainer } from '@/ui/dropdown/components/DropdownMenuContainer';
|
||||
import { StyledDropdownMenuItemsContainer } from '@/ui/dropdown/components/StyledDropdownMenuItemsContainer';
|
||||
import { IconChevronDown, IconPlus } from '@/ui/icon';
|
||||
import { MenuItem } from '@/ui/menu-item/components/MenuItem';
|
||||
import { useScopedHotkeys } from '@/ui/utilities/hotkey/hooks/useScopedHotkeys';
|
||||
import { useContextScopeId } from '@/ui/utilities/recoil-scope/hooks/useContextScopeId';
|
||||
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 { filtersScopedState } from '@/ui/view-bar/states/filtersScopedState';
|
||||
import { savedFiltersFamilyState } from '@/ui/view-bar/states/savedFiltersFamilyState';
|
||||
|
||||
@ -35,7 +35,7 @@ export const ViewBar = ({
|
||||
leftComponent={
|
||||
<ViewsDropdownButton
|
||||
onViewEditModeChange={openOptionsDropdownButton}
|
||||
hotkeyScope={ViewsHotkeyScope.ListDropdown}
|
||||
hotkeyScope={{ scope: ViewsHotkeyScope.ListDropdown }}
|
||||
scopeContext={scopeContext}
|
||||
/>
|
||||
}
|
||||
|
||||
@ -1,17 +1,14 @@
|
||||
import {
|
||||
type Context,
|
||||
type MouseEvent,
|
||||
useCallback,
|
||||
useContext,
|
||||
useEffect,
|
||||
useState,
|
||||
} from 'react';
|
||||
import { type Context, type MouseEvent, useContext } from 'react';
|
||||
import { useTheme } from '@emotion/react';
|
||||
import styled from '@emotion/styled';
|
||||
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 { StyledDropdownMenuSeparator } from '@/ui/dropdown/components/StyledDropdownMenuSeparator';
|
||||
import { useDropdownButton } from '@/ui/dropdown/hooks/useDropdownButton';
|
||||
import {
|
||||
IconChevronDown,
|
||||
IconList,
|
||||
@ -21,10 +18,10 @@ import {
|
||||
} from '@/ui/icon';
|
||||
import { MenuItem } from '@/ui/menu-item/components/MenuItem';
|
||||
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 { useRecoilScopedState } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedState';
|
||||
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 { filtersScopedState } from '@/ui/view-bar/states/filtersScopedState';
|
||||
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 { viewEditModeState } from '@/ui/view-bar/states/viewEditModeState';
|
||||
import { viewsScopedState } from '@/ui/view-bar/states/viewsScopedState';
|
||||
import { ViewsHotkeyScope } from '@/ui/view-bar/types/ViewsHotkeyScope';
|
||||
import { assertNotNull } from '~/utils/assert';
|
||||
|
||||
import { ViewsDropdownId } from '../constants/ViewsDropdownId';
|
||||
import { ViewBarContext } from '../contexts/ViewBarContext';
|
||||
import { useRemoveView } from '../hooks/useRemoveView';
|
||||
|
||||
@ -73,7 +70,7 @@ const StyledViewName = styled.span`
|
||||
`;
|
||||
|
||||
export type ViewsDropdownButtonProps = {
|
||||
hotkeyScope: ViewsHotkeyScope;
|
||||
hotkeyScope: HotkeyScope;
|
||||
onViewEditModeChange?: () => void;
|
||||
scopeContext: Context<string | null>;
|
||||
};
|
||||
@ -88,24 +85,24 @@ export const ViewsDropdownButton = ({
|
||||
const { defaultViewName, onViewSelect } = useContext(ViewBarContext);
|
||||
const recoilScopeId = useContextScopeId(scopeContext);
|
||||
|
||||
const [isUnfolded, setIsUnfolded] = useState(false);
|
||||
|
||||
const currentView = useRecoilScopedValue(
|
||||
currentViewScopedSelector,
|
||||
scopeContext,
|
||||
);
|
||||
const views = useRecoilScopedValue(viewsScopedState, scopeContext);
|
||||
const setViewEditMode = useSetRecoilState(viewEditModeState);
|
||||
const [views] = useRecoilScopedState(viewsScopedState, scopeContext);
|
||||
|
||||
const {
|
||||
goBackToPreviousHotkeyScope,
|
||||
setHotkeyScopeAndMemorizePreviousScope,
|
||||
} = usePreviousHotkeyScope();
|
||||
const { isDropdownButtonOpen, closeDropdownButton, toggleDropdownButton } =
|
||||
useDropdownButton({
|
||||
dropdownId: ViewsDropdownId,
|
||||
});
|
||||
|
||||
const setViewEditMode = useSetRecoilState(viewEditModeState);
|
||||
|
||||
const handleViewSelect = useRecoilCallback(
|
||||
({ set, snapshot }) =>
|
||||
async (viewId: string) => {
|
||||
await onViewSelect?.(viewId);
|
||||
|
||||
const savedFilters = await snapshot.getPromise(
|
||||
savedFiltersFamilyState(viewId),
|
||||
);
|
||||
@ -116,26 +113,26 @@ export const ViewsDropdownButton = ({
|
||||
set(filtersScopedState(recoilScopeId), savedFilters);
|
||||
set(sortsScopedState(recoilScopeId), savedSorts);
|
||||
set(currentViewIdScopedState(recoilScopeId), viewId);
|
||||
setIsUnfolded(false);
|
||||
closeDropdownButton();
|
||||
},
|
||||
[onViewSelect, recoilScopeId],
|
||||
[onViewSelect, recoilScopeId, closeDropdownButton],
|
||||
);
|
||||
|
||||
const handleAddViewButtonClick = useCallback(() => {
|
||||
const handleAddViewButtonClick = () => {
|
||||
setViewEditMode({ mode: 'create', viewId: undefined });
|
||||
onViewEditModeChange?.();
|
||||
setIsUnfolded(false);
|
||||
}, [setViewEditMode, onViewEditModeChange]);
|
||||
closeDropdownButton();
|
||||
};
|
||||
|
||||
const handleEditViewButtonClick = useCallback(
|
||||
(event: MouseEvent<HTMLButtonElement>, viewId: string) => {
|
||||
event.stopPropagation();
|
||||
setViewEditMode({ mode: 'edit', viewId });
|
||||
onViewEditModeChange?.();
|
||||
setIsUnfolded(false);
|
||||
},
|
||||
[setViewEditMode, onViewEditModeChange],
|
||||
);
|
||||
const handleEditViewButtonClick = (
|
||||
event: MouseEvent<HTMLButtonElement>,
|
||||
viewId: string,
|
||||
) => {
|
||||
event.stopPropagation();
|
||||
setViewEditMode({ mode: 'edit', viewId });
|
||||
onViewEditModeChange?.();
|
||||
closeDropdownButton();
|
||||
};
|
||||
|
||||
const { removeView } = useRemoveView({ scopeContext });
|
||||
|
||||
@ -146,24 +143,22 @@ export const ViewsDropdownButton = ({
|
||||
event.stopPropagation();
|
||||
|
||||
await removeView(viewId);
|
||||
setIsUnfolded(false);
|
||||
closeDropdownButton();
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
isUnfolded
|
||||
? setHotkeyScopeAndMemorizePreviousScope(hotkeyScope)
|
||||
: goBackToPreviousHotkeyScope();
|
||||
}, [
|
||||
hotkeyScope,
|
||||
goBackToPreviousHotkeyScope,
|
||||
isUnfolded,
|
||||
setHotkeyScopeAndMemorizePreviousScope,
|
||||
]);
|
||||
const handleViewButtonClick = () => {
|
||||
toggleDropdownButton();
|
||||
};
|
||||
|
||||
return (
|
||||
<DropdownButton
|
||||
label={
|
||||
<>
|
||||
dropdownId={ViewsDropdownId}
|
||||
dropdownHotkeyScope={hotkeyScope}
|
||||
buttonComponents={
|
||||
<StyledDropdownButtonContainer
|
||||
isUnfolded={isDropdownButtonOpen}
|
||||
onClick={handleViewButtonClick}
|
||||
>
|
||||
<StyledViewIcon size={theme.icon.size.md} />
|
||||
<StyledViewName>
|
||||
{currentView?.name || defaultViewName}
|
||||
@ -171,47 +166,44 @@ export const ViewsDropdownButton = ({
|
||||
<StyledDropdownLabelAdornments>
|
||||
· {views.length} <IconChevronDown size={theme.icon.size.sm} />
|
||||
</StyledDropdownLabelAdornments>
|
||||
</>
|
||||
</StyledDropdownButtonContainer>
|
||||
}
|
||||
isActive={false}
|
||||
isUnfolded={isUnfolded}
|
||||
onIsUnfoldedChange={setIsUnfolded}
|
||||
anchor="left"
|
||||
hotkeyScope={hotkeyScope}
|
||||
menuWidth="auto"
|
||||
>
|
||||
<StyledDropdownMenuItemsContainer>
|
||||
{views.map((view) => (
|
||||
<MenuItem
|
||||
key={view.id}
|
||||
iconButtons={[
|
||||
{
|
||||
Icon: IconPencil,
|
||||
onClick: (event: MouseEvent<HTMLButtonElement>) =>
|
||||
handleEditViewButtonClick(event, view.id),
|
||||
},
|
||||
views.length > 1
|
||||
? {
|
||||
Icon: IconTrash,
|
||||
dropdownComponents={
|
||||
<StyledDropdownMenu>
|
||||
<StyledDropdownMenuItemsContainer>
|
||||
{views.map((view) => (
|
||||
<MenuItem
|
||||
key={view.id}
|
||||
iconButtons={[
|
||||
{
|
||||
Icon: IconPencil,
|
||||
onClick: (event: MouseEvent<HTMLButtonElement>) =>
|
||||
handleDeleteViewButtonClick(event, view.id),
|
||||
}
|
||||
: null,
|
||||
].filter(assertNotNull)}
|
||||
onClick={() => handleViewSelect(view.id)}
|
||||
LeftIcon={IconList}
|
||||
text={view.name}
|
||||
/>
|
||||
))}
|
||||
</StyledDropdownMenuItemsContainer>
|
||||
<StyledDropdownMenuSeparator />
|
||||
<StyledBoldDropdownMenuItemsContainer>
|
||||
<MenuItem
|
||||
onClick={handleAddViewButtonClick}
|
||||
LeftIcon={IconPlus}
|
||||
text="Add view"
|
||||
/>
|
||||
</StyledBoldDropdownMenuItemsContainer>
|
||||
</DropdownButton>
|
||||
handleEditViewButtonClick(event, view.id),
|
||||
},
|
||||
views.length > 1
|
||||
? {
|
||||
Icon: IconTrash,
|
||||
onClick: (event: MouseEvent<HTMLButtonElement>) =>
|
||||
handleDeleteViewButtonClick(event, view.id),
|
||||
}
|
||||
: null,
|
||||
].filter(assertNotNull)}
|
||||
onClick={() => handleViewSelect(view.id)}
|
||||
LeftIcon={IconList}
|
||||
text={view.name}
|
||||
/>
|
||||
))}
|
||||
</StyledDropdownMenuItemsContainer>
|
||||
<StyledDropdownMenuSeparator />
|
||||
<StyledBoldDropdownMenuItemsContainer>
|
||||
<MenuItem
|
||||
onClick={handleAddViewButtonClick}
|
||||
LeftIcon={IconPlus}
|
||||
text="Add view"
|
||||
/>
|
||||
</StyledBoldDropdownMenuItemsContainer>
|
||||
</StyledDropdownMenu>
|
||||
}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
@ -0,0 +1 @@
|
||||
export const ViewsDropdownId = 'views';
|
||||
Reference in New Issue
Block a user