mirror of
https://github.com/twentyhq/twenty.git
synced 2024-11-25 20:00:34 +03:00
Fix
This commit is contained in:
parent
bc7144c111
commit
43e7b2d664
@ -161,7 +161,7 @@ export const ObjectFilterDropdownFilterSelect = ({
|
||||
setObjectFilterDropdownSearchInput(event.target.value)
|
||||
}
|
||||
/>
|
||||
<ScrollWrapper contextProviderName="filterSelectDropdown">
|
||||
<ScrollWrapper contextProviderName="dropdownMenuItemsContainer">
|
||||
<SelectableList
|
||||
hotkeyScope={FiltersHotkeyScope.ObjectFilterDropdownButton}
|
||||
selectableItemIdArray={selectableListItemIds}
|
||||
|
@ -16,6 +16,7 @@ import { useSelectableList } from '@/ui/layout/selectable-list/hooks/useSelectab
|
||||
import { MenuItem } from '@/ui/navigation/menu-item/components/MenuItem';
|
||||
import { MenuItemMultiSelect } from '@/ui/navigation/menu-item/components/MenuItemMultiSelect';
|
||||
import { useScopedHotkeys } from '@/ui/utilities/hotkey/hooks/useScopedHotkeys';
|
||||
import { ScrollWrapper } from '@/ui/utilities/scroll/components/ScrollWrapper';
|
||||
import { isDefined } from '~/utils/isDefined';
|
||||
|
||||
export const EMPTY_FILTER_VALUE = '';
|
||||
@ -162,22 +163,24 @@ export const ObjectFilterDropdownOptionSelect = () => {
|
||||
}
|
||||
}}
|
||||
>
|
||||
<DropdownMenuItemsContainer hasMaxHeight>
|
||||
{optionsInDropdown?.map((option) => (
|
||||
<MenuItemMultiSelect
|
||||
key={option.id}
|
||||
selected={option.isSelected}
|
||||
isKeySelected={option.id === selectedItemId}
|
||||
onSelectChange={(selected) =>
|
||||
handleMultipleOptionSelectChange(option, selected)
|
||||
}
|
||||
text={option.label}
|
||||
color={option.color}
|
||||
className=""
|
||||
/>
|
||||
))}
|
||||
</DropdownMenuItemsContainer>
|
||||
{showNoResult && <MenuItem text="No result" />}
|
||||
<ScrollWrapper contextProviderName="dropdownMenuItemsContainer">
|
||||
<DropdownMenuItemsContainer hasMaxHeight>
|
||||
{optionsInDropdown?.map((option) => (
|
||||
<MenuItemMultiSelect
|
||||
key={option.id}
|
||||
selected={option.isSelected}
|
||||
isKeySelected={option.id === selectedItemId}
|
||||
onSelectChange={(selected) =>
|
||||
handleMultipleOptionSelectChange(option, selected)
|
||||
}
|
||||
text={option.label}
|
||||
color={option.color}
|
||||
className=""
|
||||
/>
|
||||
))}
|
||||
</DropdownMenuItemsContainer>
|
||||
{showNoResult && <MenuItem text="No result" />}
|
||||
</ScrollWrapper>
|
||||
</SelectableList>
|
||||
);
|
||||
};
|
||||
|
@ -179,7 +179,7 @@ export const ObjectSortDropdownButton = ({
|
||||
setObjectSortDropdownSearchInput(event.target.value)
|
||||
}
|
||||
/>
|
||||
<ScrollWrapper contextProviderName="sortSelectDropdown">
|
||||
<ScrollWrapper contextProviderName="dropdownMenuItemsContainer">
|
||||
<DropdownMenuItemsContainer>
|
||||
{visibleColumnsSortDefinitions.map(
|
||||
(visibleSortDefinition, index) => (
|
||||
|
@ -21,7 +21,6 @@ import { turnIntoEmptyStringIfWhitespacesOnly } from '~/utils/string/turnIntoEmp
|
||||
|
||||
const StyledDropdownMenu = styled(DropdownMenu)`
|
||||
left: -1px;
|
||||
position: absolute;
|
||||
top: -1px;
|
||||
`;
|
||||
|
||||
@ -46,6 +45,7 @@ type MultiItemFieldInputProps<T> = {
|
||||
};
|
||||
|
||||
// Todo: the API of this component does not look healthy: we have renderInput, renderItem, formatInput, ...
|
||||
// This should be refactored with a hook instead that exposes those events in a context around this component and its children.
|
||||
export const MultiItemFieldInput = <T,>({
|
||||
items,
|
||||
onPersist,
|
||||
|
@ -1,14 +1,11 @@
|
||||
import { Dropdown } from '@/ui/layout/dropdown/components/Dropdown';
|
||||
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
|
||||
import { useDropdown } from '@/ui/layout/dropdown/hooks/useDropdown';
|
||||
import { MenuItem } from '@/ui/navigation/menu-item/components/MenuItem';
|
||||
import styled from '@emotion/styled';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { MenuItemWithOptionDropdown } from '@/ui/navigation/menu-item/components/MenuItemWithOptionDropdown';
|
||||
import { useState } from 'react';
|
||||
import {
|
||||
IconBookmark,
|
||||
IconBookmarkPlus,
|
||||
IconComponent,
|
||||
IconDotsVertical,
|
||||
IconPencil,
|
||||
IconTrash,
|
||||
} from 'twenty-ui';
|
||||
@ -24,12 +21,6 @@ type MultiItemFieldMenuItemProps<T> = {
|
||||
hasPrimaryButton?: boolean;
|
||||
};
|
||||
|
||||
const StyledIconBookmark = styled(IconBookmark)`
|
||||
color: ${({ theme }) => theme.font.color.light};
|
||||
height: ${({ theme }) => theme.icon.size.sm}px;
|
||||
width: ${({ theme }) => theme.icon.size.sm}px;
|
||||
`;
|
||||
|
||||
export const MultiItemFieldMenuItem = <T,>({
|
||||
dropdownId,
|
||||
isPrimary,
|
||||
@ -47,66 +38,51 @@ export const MultiItemFieldMenuItem = <T,>({
|
||||
const handleMouseLeave = () => setIsHovered(false);
|
||||
|
||||
const handleDeleteClick = () => {
|
||||
closeDropdown();
|
||||
setIsHovered(false);
|
||||
onDelete?.();
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (isDropdownOpen) {
|
||||
return () => closeDropdown();
|
||||
}
|
||||
}, [closeDropdown, isDropdownOpen]);
|
||||
const handleSetAsPrimaryClick = () => {
|
||||
closeDropdown();
|
||||
onSetAsPrimary?.();
|
||||
};
|
||||
|
||||
const handleEditClick = () => {
|
||||
closeDropdown();
|
||||
onEdit?.();
|
||||
};
|
||||
|
||||
return (
|
||||
<MenuItem
|
||||
<MenuItemWithOptionDropdown
|
||||
onMouseEnter={handleMouseEnter}
|
||||
onMouseLeave={handleMouseLeave}
|
||||
text={<DisplayComponent value={value} />}
|
||||
isIconDisplayedOnHoverOnly={!isPrimary && !isDropdownOpen}
|
||||
iconButtons={[
|
||||
{
|
||||
Wrapper: isHovered
|
||||
? ({ iconButton }) => (
|
||||
<Dropdown
|
||||
dropdownId={dropdownId}
|
||||
dropdownHotkeyScope={{ scope: dropdownId }}
|
||||
dropdownPlacement="right-start"
|
||||
dropdownStrategy="fixed"
|
||||
disableBlur
|
||||
clickableComponent={iconButton}
|
||||
dropdownComponents={
|
||||
<DropdownMenuItemsContainer>
|
||||
{hasPrimaryButton && !isPrimary && (
|
||||
<MenuItem
|
||||
LeftIcon={IconBookmarkPlus}
|
||||
text="Set as Primary"
|
||||
onClick={onSetAsPrimary}
|
||||
/>
|
||||
)}
|
||||
<MenuItem
|
||||
LeftIcon={IconPencil}
|
||||
text="Edit"
|
||||
onClick={onEdit}
|
||||
/>
|
||||
<MenuItem
|
||||
accent="danger"
|
||||
LeftIcon={IconTrash}
|
||||
text="Delete"
|
||||
onClick={handleDeleteClick}
|
||||
/>
|
||||
</DropdownMenuItemsContainer>
|
||||
}
|
||||
/>
|
||||
)
|
||||
: undefined,
|
||||
Icon:
|
||||
isPrimary && !isHovered
|
||||
? (StyledIconBookmark as IconComponent)
|
||||
: IconDotsVertical,
|
||||
accent: 'tertiary',
|
||||
onClick: isHovered ? () => {} : undefined,
|
||||
},
|
||||
]}
|
||||
RightIcon={isHovered ? null : IconBookmark}
|
||||
dropdownId={dropdownId}
|
||||
dropdownContent={
|
||||
<DropdownMenuItemsContainer>
|
||||
{hasPrimaryButton && !isPrimary && (
|
||||
<MenuItem
|
||||
LeftIcon={IconBookmarkPlus}
|
||||
text="Set as Primary"
|
||||
onClick={handleSetAsPrimaryClick}
|
||||
/>
|
||||
)}
|
||||
<MenuItem
|
||||
LeftIcon={IconPencil}
|
||||
text="Edit"
|
||||
onClick={handleEditClick}
|
||||
/>
|
||||
<MenuItem
|
||||
accent="danger"
|
||||
LeftIcon={IconTrash}
|
||||
text="Delete"
|
||||
onClick={handleDeleteClick}
|
||||
/>
|
||||
</DropdownMenuItemsContainer>
|
||||
}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
@ -40,6 +40,7 @@ import { MenuItemNavigate } from '@/ui/navigation/menu-item/components/MenuItemN
|
||||
import { MenuItemToggle } from '@/ui/navigation/menu-item/components/MenuItemToggle';
|
||||
import { navigationMemorizedUrlState } from '@/ui/navigation/states/navigationMemorizedUrlState';
|
||||
import { useScopedHotkeys } from '@/ui/utilities/hotkey/hooks/useScopedHotkeys';
|
||||
import { ScrollWrapper } from '@/ui/utilities/scroll/components/ScrollWrapper';
|
||||
import { ViewFieldsVisibilityDropdownSection } from '@/views/components/ViewFieldsVisibilityDropdownSection';
|
||||
import { ViewGroupsVisibilityDropdownSection } from '@/views/components/ViewGroupsVisibilityDropdownSection';
|
||||
import { useGetCurrentView } from '@/views/hooks/useGetCurrentView';
|
||||
@ -259,15 +260,17 @@ export const RecordIndexOptionsDropdownContent = ({
|
||||
<DropdownMenuHeader StartIcon={IconChevronLeft} onClick={resetMenu}>
|
||||
Fields
|
||||
</DropdownMenuHeader>
|
||||
<ViewFieldsVisibilityDropdownSection
|
||||
title="Visible"
|
||||
fields={visibleRecordFields}
|
||||
isDraggable
|
||||
onDragEnd={handleReorderFields}
|
||||
onVisibilityChange={handleChangeFieldVisibility}
|
||||
showSubheader={false}
|
||||
showDragGrip={true}
|
||||
/>
|
||||
<ScrollWrapper contextProviderName="dropdownMenuItemsContainer">
|
||||
<ViewFieldsVisibilityDropdownSection
|
||||
title="Visible"
|
||||
fields={visibleRecordFields}
|
||||
isDraggable
|
||||
onDragEnd={handleReorderFields}
|
||||
onVisibilityChange={handleChangeFieldVisibility}
|
||||
showSubheader={false}
|
||||
showDragGrip={true}
|
||||
/>
|
||||
</ScrollWrapper>
|
||||
<DropdownMenuSeparator />
|
||||
<DropdownMenuItemsContainer>
|
||||
<MenuItemNavigate
|
||||
|
@ -74,8 +74,8 @@ export const PhoneCountryPickerDropdownButton = ({
|
||||
);
|
||||
|
||||
const handleChange = (countryCode: string) => {
|
||||
onChange(countryCode);
|
||||
closeDropdown();
|
||||
onChange(countryCode);
|
||||
};
|
||||
|
||||
const countries = useCountries();
|
||||
@ -89,7 +89,6 @@ export const PhoneCountryPickerDropdownButton = ({
|
||||
|
||||
return (
|
||||
<Dropdown
|
||||
dropdownMenuWidth={'100%'}
|
||||
dropdownId="country-picker-dropdown-id"
|
||||
dropdownHotkeyScope={{ scope: CountryPickerHotkeyScope.CountryPicker }}
|
||||
clickableComponent={
|
||||
|
@ -7,7 +7,7 @@ import {
|
||||
size,
|
||||
useFloating,
|
||||
} from '@floating-ui/react';
|
||||
import { MouseEvent, useRef } from 'react';
|
||||
import { MouseEvent, ReactNode, useRef } from 'react';
|
||||
import { Keys } from 'react-hotkeys-hook';
|
||||
import { Key } from 'ts-key-enum';
|
||||
|
||||
@ -27,8 +27,8 @@ import { DropdownOnToggleEffect } from './DropdownOnToggleEffect';
|
||||
|
||||
type DropdownProps = {
|
||||
className?: string;
|
||||
clickableComponent?: JSX.Element | JSX.Element[];
|
||||
dropdownComponents: JSX.Element | JSX.Element[];
|
||||
clickableComponent?: ReactNode;
|
||||
dropdownComponents: ReactNode;
|
||||
hotkey?: {
|
||||
key: Keys;
|
||||
scope: string;
|
||||
@ -92,6 +92,7 @@ export const Dropdown = ({
|
||||
|
||||
elements.floating.style.height = 'auto';
|
||||
},
|
||||
boundary: document.querySelector('#root') ?? undefined,
|
||||
}),
|
||||
...offsetMiddlewares,
|
||||
],
|
||||
|
@ -24,7 +24,6 @@ const StyledDropdownMenu = styled.div<{
|
||||
display: flex;
|
||||
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
|
||||
flex-direction: column;
|
||||
z-index: 30;
|
||||
|
@ -13,7 +13,6 @@ const StyledDropdownMenuItemsExternalContainer = styled.div<{
|
||||
flex-direction: column;
|
||||
gap: 2px;
|
||||
max-height: ${({ hasMaxHeight }) => (hasMaxHeight ? '188px' : 'none')};
|
||||
overflow-y: auto;
|
||||
|
||||
padding: var(--padding);
|
||||
|
||||
@ -34,6 +33,8 @@ const StyledDropdownMenuItemsInternalContainer = styled.div`
|
||||
width: 100%;
|
||||
`;
|
||||
|
||||
// TODO: refactor this, the dropdown should handle the max height behavior + scroll with the size middleware
|
||||
// We should instead create a DropdownMenuItemsContainerScrollable or take for granted that it is the default behavior
|
||||
export const DropdownMenuItemsContainer = ({
|
||||
children,
|
||||
hasMaxHeight,
|
||||
|
@ -0,0 +1,104 @@
|
||||
import { useTheme } from '@emotion/react';
|
||||
import { FunctionComponent, MouseEvent, ReactElement, ReactNode } from 'react';
|
||||
import {
|
||||
IconChevronRight,
|
||||
IconComponent,
|
||||
IconDotsVertical,
|
||||
LightIconButton,
|
||||
LightIconButtonProps,
|
||||
} from 'twenty-ui';
|
||||
|
||||
import { Dropdown } from '@/ui/layout/dropdown/components/Dropdown';
|
||||
import { MenuItemLeftContent } from '../internals/components/MenuItemLeftContent';
|
||||
import {
|
||||
StyledHoverableMenuItemBase,
|
||||
StyledMenuItemLeftContent,
|
||||
} from '../internals/components/StyledMenuItemBase';
|
||||
import { MenuItemAccent } from '../types/MenuItemAccent';
|
||||
|
||||
export type MenuItemIconButton = {
|
||||
Wrapper?: FunctionComponent<{ iconButton: ReactElement }>;
|
||||
Icon: IconComponent;
|
||||
accent?: LightIconButtonProps['accent'];
|
||||
onClick?: (event: MouseEvent<any>) => void;
|
||||
};
|
||||
|
||||
export type MenuItemWithOptionDropdownProps = {
|
||||
accent?: MenuItemAccent;
|
||||
className?: string;
|
||||
dropdownContent: ReactNode;
|
||||
dropdownId: string;
|
||||
isIconDisplayedOnHoverOnly?: boolean;
|
||||
isTooltipOpen?: boolean;
|
||||
LeftIcon?: IconComponent | null;
|
||||
RightIcon?: IconComponent | null;
|
||||
onClick?: (event: MouseEvent<HTMLDivElement>) => void;
|
||||
onMouseEnter?: (event: MouseEvent<HTMLDivElement>) => void;
|
||||
onMouseLeave?: (event: MouseEvent<HTMLDivElement>) => void;
|
||||
testId?: string;
|
||||
text: ReactNode;
|
||||
hasSubMenu?: boolean;
|
||||
};
|
||||
|
||||
// TODO: refactor this
|
||||
export const MenuItemWithOptionDropdown = ({
|
||||
accent = 'default',
|
||||
className,
|
||||
isIconDisplayedOnHoverOnly = true,
|
||||
dropdownContent,
|
||||
dropdownId,
|
||||
LeftIcon,
|
||||
RightIcon,
|
||||
onClick,
|
||||
onMouseEnter,
|
||||
onMouseLeave,
|
||||
testId,
|
||||
text,
|
||||
hasSubMenu = false,
|
||||
}: MenuItemWithOptionDropdownProps) => {
|
||||
const theme = useTheme();
|
||||
|
||||
const handleMenuItemClick = (event: MouseEvent<HTMLDivElement>) => {
|
||||
if (!onClick) return;
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
|
||||
onClick?.(event);
|
||||
};
|
||||
|
||||
return (
|
||||
<StyledHoverableMenuItemBase
|
||||
data-testid={testId ?? undefined}
|
||||
onClick={handleMenuItemClick}
|
||||
className={className}
|
||||
accent={accent}
|
||||
isIconDisplayedOnHoverOnly={isIconDisplayedOnHoverOnly}
|
||||
onMouseEnter={onMouseEnter}
|
||||
onMouseLeave={onMouseLeave}
|
||||
>
|
||||
<StyledMenuItemLeftContent>
|
||||
<MenuItemLeftContent LeftIcon={LeftIcon ?? undefined} text={text} />
|
||||
</StyledMenuItemLeftContent>
|
||||
<div className="hoverable-buttons">
|
||||
<Dropdown
|
||||
clickableComponent={
|
||||
<LightIconButton
|
||||
Icon={RightIcon ?? IconDotsVertical}
|
||||
size="small"
|
||||
/>
|
||||
}
|
||||
dropdownComponents={dropdownContent}
|
||||
dropdownId={dropdownId}
|
||||
dropdownHotkeyScope={{ scope: 'sd' }}
|
||||
disableBlur
|
||||
/>
|
||||
</div>
|
||||
{hasSubMenu && (
|
||||
<IconChevronRight
|
||||
size={theme.icon.size.sm}
|
||||
color={theme.font.color.tertiary}
|
||||
/>
|
||||
)}
|
||||
</StyledHoverableMenuItemBase>
|
||||
);
|
||||
};
|
Loading…
Reference in New Issue
Block a user