Refactored dropdown content and fixed all dropdown width bugs (#12334)
This PR refactors all the dropdown content wrapping mechanism across the entire app. It refactors the internals of the `Dropdown` component and introduces a new generic `DropdownContent` component that is a generic wrapper used for each dropdown. ## Why this PR ? Because we’ve been experiencing continuous regressions for months on the dropdown content width, with weird scrolling behaviors in some and not in others, and every time a solution was found for a particular set of dropdowns, it broke another set of dropdowns, which wasn’t noticed because doing the QA of all dropdowns of the app is very difficult for fixing an apparently small bug. ## Don’t we already have a `DropdownMenu` component ? Indeed, this new `DropdownContent` is almost like `DropdownMenu` and took inspiration from it but `DropdownContent` acts as a generic content container that sets the width of the whole dropdown, whether we have a menu or not. ## Why don’t we put it directly in Dropdown internals ? Because the Dropdown component is using a complex logic with floating-ui middleware to compute its position and size, and for this logic to work correctly, it cannot be responsible for the “wanted” width of its content, because the children components, which the dropdown is not aware of, can request different widths after the dropdown has been mounted. A good example with multiple use cases inside the same dropdown can be found in `AdvancedFilterDropdownFilterInput` Thus, it is the responsibility of the content of the dropdown to determine the width it wants to have. ## What is the difference with DropdownMenuItemsContainer ? We can have multiple `DropdownMenuItemsContainer` in a dropdown, alongside other components like `DropdownMenuSeparator` or `DropdownMenuHeader`, and each of those components behaves differently regarding to its width, paddings, etc. Therefore it is logical that the `DropdownMenuItemsContainer` cannot be responsible for the whole dropdown content width, and trying to do so has been the cause of many regressions for months. Now `DropdownMenuItemsContainer` is taking a width of `auto` by default, which is the best to adapt to a parent which has a defined width. ## How do I set the width of my dropdown now ? By passing a pixel width to the props `widthInPixels` of `DropdownContent`, which only accepts numbers to avoid any confusion with `auto` , `100%` or `160px` and other specific width variables. The `dropdownWidth` props has been removed from `<Dropdown>` to avoid any confusion. Also the `DropdownMenuItemsContainer` is now using `auto` as its default width to fill the available space inside `DropdownContent` . It is highly recommended to use the enum `GenericDropdownContentWidt` to define your width. ## Where to use this new `DropdownContent` component ? There are two main use cases. If the dropdown content is defined directly inline in the Dropdown props, then it is recommended to use it here too. On the other hand if the dropdown content is abstracted in another component, it’s recommended to use this new component alongside the others components like `DropdownMenuItemsContainer`. A good rule of thumb is to place `DropdownContent` where `DropdownMenuItemsContainer`, `DropdownMenuSearchInput`, etc. are placed. ## What if I have a custom width ? Just define a constant like `ICON_PICKER_DROPDOWN_CONTENT_WIDTH` and use it with the props `widthInPixels` . Otherwise there’s a `GenericDropdownContentWidth` enum. The default value being `GenericDropdownContentWidth.Medium` (or 200px), which most dropdowns use. ## QA Component | Comment -- | -- AttachmentDropdown | Fixed overflowing (thanks to DropdownContent) RecordIndexActionMenuDropdown | CommandMenuActionMenuDropdown | SupportDropdown | Fixed overflowing (thanks to DropdownContent) MessageThreadSubscribersDropdownButton | Removed because unused FavoriteFolderNavigationDrawerItemDropdown | Set width at Narrow FavoriteFolderPicker | ViewPickerOptionDropdown | PageFavoriteFolderDropdown | Removed because unused AdvancedFilterAddFilterRuleSelect | AdvancedFilterAddFilterRuleSelect | AdvancedFilterFieldSelectMenu | AdvancedFilterRecordFilterGroupOptionsDropdown | AdvancedFilterRecordFilterOperanceSelect | Set width at Narrow AdvancedFilterLogicalOperatorDropdown | Set width at Narrow AdvancedFilterRecordFilterOptionsDropdown | AdvancedFilterRootRecordFilterGroup | Fixed broken horizontal scrolling behavior AdvancedFilterSubFieldSelectMenu | AdvancedFilterDropdownFilterInput | ObjectFilterDropdownBooleanSelect | ObjectFilterDropdownCountrySelect | Fixed broken menu items container ObjectFilterDropdownCurrencySelect | Set width to Large ObjectFilterDropdownFilterInput | ObjectFilterDropdownOperandDropdown | Fixed width that was not fixed ObjectFilterDropdownFilterInput | Fixed width that wasn’t the same for EditableFilterChip ObjectFilterDropdownOperandSelect | Refactored ObjectOptionsDropdownRecordGroupFieldsContent | Added missing separator ObjectOptionDropdownFieldsContent | ObjectOptionsDropdownHiddenFieldsContent | ObjectOptionsDropdownLayoutContent | ObjectOptionsDropdownLayoutOpenInContent | ObjectOptionsDropdownMenuContent | ObjectOptionsDropdownRecordGroupFieldsContent | ObjectOptionsDropdownRecordGroupsContent | ObjectOptionsDropdownRecordGroupSortContent | ObjectOptionsDropdownHiddenRecordGroupsContent | Removed unnecessary DropdownMenuItemsContainer RecordBoardColumnHeaderAggregateDropdown | Fixed overflowing (thanks to DropdownContent) RecordBoardColumnHeaderAggregateDropdownFieldsContent | Fixed overflowing (thanks to DropdownContent) RecordBoardColumnHeaderAggregateDropdownMenuContent | Fixed overflowing (thanks to DropdownContent) RecordBoardColumnHeaderAggregateDropdownOptionsContent | Fixed overflowing (thanks to DropdownContent) MultiItemFieldInput | Fixed overflowing (thanks to DropdownContent) MultiItemFieldMenuItem | MultipleRecordPicker | Fixed overflowing (thanks to DropdownContent) SingleRecordPicker | RecordTableColumnAggregateDropdownSubmenuContent | RecordTableColumnAggregateFooterMenuContent | RecordTableColumnHeadDropdownMenu | Fixed overflowing (thanks to DropdownContent) RecordTableHeaderPlusButtonContent | MultipleSelectDropdown | Broken width fixed ObjectSortDropdownButton | RecordDetailRelationRecordsListItem | ConfigVariableDatabaseInput | ConfigVariableOptionsDropdownContent | SettingsObjectFieldActiveActionDropdown | Fixed overflowing (thanks to DropdownContent) SettingsObjectFieldDisabledActionDropdown | Set width at Narrow SettingsObjectSummaryCard | Removed because unused SettingsDataModelFieldSelectFormOptionRow | SettingsDataModelNewFieldBreadcrumbDropdown | SettingsObjectInactiveMenuDropDown | SettingsRoleAssignementWorkspaceMemberPickerDropdown | SettingsRolePermissionObjectLevelObjectPickerDropdownContent | SettingsSecurityApprovedAccessDomainRowDropdownMenu | Couldn’t test SettingsSecuritySSORowDropdownMenu | Couldn’t test SettingsAccountsRowDropdownMenu | Fixed overflowing (thanks to DropdownContent) SettingsIntegrationDatabaseConnectionSummaryCard | Couldn’t test SettingsServerlessFunctionTablEnvironmentVariableTableRow | Deactivated scope MatchColumnSelectFieldSelectDropdownContent | Removed now unnecessary width on DropdownMenuItemsContainer MatchColumnSelectSubFieldSelectDropdownContent | SubMatchingSelectInput | CurrencyPickerDropdownSelect | IconPicker | Fixed overflowing (thanks to DropdownContent) PhoneCountryPickerDropdownSelect | Select | Refactored to drilldown wanted width of content, in this case it’s intended ExpandedListDropdown | ShowPageAddButton | Removed because unused MultiWorkspaceDropdownDefaultComponent | MultiWorkspaceDropdownThemesComponent | MultiWorkspaceDropdownWorkspacesListComponent | AdvancedFilterDropdownButton | EditableFilterChip | EditableFilterDropdownButton | UpdateViewButtonGroup | ViewBarFilterDropdown | ViewBarFilterDropdownFieldSelectMenu | ViewPickerContentCreateMode | ViewPickerContentEditMode | ViewPickerListContent | WorkflowEditTriggerDatabaseEventForm | WorkflowVariablesDropdownFieldItems | WorkflowVariablesDropdownObjectItems | WorkflowVariablesDropdownWorkflowStepItems | CommandMenuContextChipGroups | RecordBoardColumnDropdownMenu | MultiSelectInput | SelectInput | CustomSlashMenu | DropdownMenu | Removed and replaced by DropdownContent OverlayContainer and around | <!-- notionvc: 1e23bdb8-2dda-4f8d-a64d-ecc829a768a2 --> ## Miscellaneous Side notes : - The `Select` component is now wrapping the `DropdownContent` because it computes a dynamic width. - The advanced filter dropdown has been fixed, it was broken when resizing the window horizontally, we couldn’t scroll. This specific edge case was taken into account when refactoring the whole dropdown content system - As discussed with Nitin, data-select-disable will probably be removed entirely, so I let it as is, because right now it is not used by the refactored d&d selection. - Duplicate separators under DropdownMenuHeader have been removed. Fixes : https://github.com/twentyhq/twenty/issues/12327 Fixes : https://github.com/twentyhq/core-team-issues/issues/951
This commit is contained in:
@ -11,6 +11,7 @@ import { RecordFilter } from '@/object-record/record-filter/types/RecordFilter';
|
||||
import { getDefaultSubFieldNameForCompositeFilterableFieldType } from '@/object-record/record-filter/utils/getDefaultSubFieldNameForCompositeFilterableFieldType';
|
||||
import { getRecordFilterOperands } from '@/object-record/record-filter/utils/getRecordFilterOperands';
|
||||
import { Dropdown } from '@/ui/layout/dropdown/components/Dropdown';
|
||||
import { DropdownContent } from '@/ui/layout/dropdown/components/DropdownContent';
|
||||
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
|
||||
import { useDropdown } from '@/ui/layout/dropdown/hooks/useDropdown';
|
||||
import { useGetCurrentViewOnly } from '@/views/hooks/useGetCurrentViewOnly';
|
||||
@ -151,20 +152,22 @@ export const AdvancedFilterAddFilterRuleSelect = ({
|
||||
<LightButton Icon={IconPlus} title="Add filter rule" />
|
||||
}
|
||||
dropdownComponents={
|
||||
<DropdownMenuItemsContainer>
|
||||
<MenuItem
|
||||
LeftIcon={IconPlus}
|
||||
text="Add rule"
|
||||
onClick={handleAddFilter}
|
||||
/>
|
||||
{isFilterRuleGroupOptionVisible && (
|
||||
<DropdownContent>
|
||||
<DropdownMenuItemsContainer>
|
||||
<MenuItem
|
||||
LeftIcon={IconLibraryPlus}
|
||||
text="Add rule group"
|
||||
onClick={handleAddFilterGroup}
|
||||
LeftIcon={IconPlus}
|
||||
text="Add rule"
|
||||
onClick={handleAddFilter}
|
||||
/>
|
||||
)}
|
||||
</DropdownMenuItemsContainer>
|
||||
{isFilterRuleGroupOptionVisible && (
|
||||
<MenuItem
|
||||
LeftIcon={IconLibraryPlus}
|
||||
text="Add rule group"
|
||||
onClick={handleAddFilterGroup}
|
||||
/>
|
||||
)}
|
||||
</DropdownMenuItemsContainer>
|
||||
</DropdownContent>
|
||||
}
|
||||
dropdownHotkeyScope={{ scope: dropdownId }}
|
||||
dropdownOffset={{ y: 8, x: 0 }}
|
||||
|
||||
@ -16,6 +16,7 @@ import { subFieldNameUsedInDropdownComponentState } from '@/object-record/object
|
||||
import { isExpectedSubFieldName } from '@/object-record/object-filter-dropdown/utils/isExpectedSubFieldName';
|
||||
import { isFilterOnActorSourceSubField } from '@/object-record/object-filter-dropdown/utils/isFilterOnActorSourceSubField';
|
||||
import { RecordFilter } from '@/object-record/record-filter/types/RecordFilter';
|
||||
import { DropdownContent } from '@/ui/layout/dropdown/components/DropdownContent';
|
||||
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
|
||||
import { FieldMetadataType } from 'twenty-shared/types';
|
||||
|
||||
@ -52,28 +53,24 @@ export const AdvancedFilterDropdownFilterInput = ({
|
||||
<ObjectFilterDropdownDateInput />
|
||||
)}
|
||||
{filterType === 'RELATION' && (
|
||||
<>
|
||||
<DropdownContent>
|
||||
<ObjectFilterDropdownSearchInput />
|
||||
<DropdownMenuSeparator />
|
||||
<ObjectFilterDropdownRecordSelect recordFilterId={recordFilter.id} />
|
||||
</>
|
||||
</DropdownContent>
|
||||
)}
|
||||
{filterType === 'ACTOR' &&
|
||||
(isActorSourceCompositeFilter ? (
|
||||
<>
|
||||
<ObjectFilterDropdownSourceSelect />
|
||||
</>
|
||||
<ObjectFilterDropdownSourceSelect />
|
||||
) : (
|
||||
<>
|
||||
<ObjectFilterDropdownTextInput />
|
||||
</>
|
||||
<ObjectFilterDropdownTextInput />
|
||||
))}
|
||||
{['SELECT', 'MULTI_SELECT'].includes(filterType) && (
|
||||
<>
|
||||
<DropdownContent>
|
||||
<ObjectFilterDropdownSearchInput />
|
||||
<DropdownMenuSeparator />
|
||||
<ObjectFilterDropdownOptionSelect />
|
||||
</>
|
||||
</DropdownContent>
|
||||
)}
|
||||
{filterType === 'BOOLEAN' && <ObjectFilterDropdownBooleanSelect />}
|
||||
{filterType === 'CURRENCY' &&
|
||||
@ -82,9 +79,7 @@ export const AdvancedFilterDropdownFilterInput = ({
|
||||
'currencyCode',
|
||||
recordFilter.subFieldName,
|
||||
) ? (
|
||||
<>
|
||||
<ObjectFilterDropdownCurrencySelect />
|
||||
</>
|
||||
<ObjectFilterDropdownCurrencySelect />
|
||||
) : (
|
||||
<></>
|
||||
))}
|
||||
|
||||
@ -21,6 +21,7 @@ import { objectFilterDropdownIsSelectingCompositeFieldComponentState } from '@/o
|
||||
import { objectFilterDropdownSubMenuFieldTypeComponentState } from '@/object-record/object-filter-dropdown/states/objectFilterDropdownSubMenuFieldTypeComponentState';
|
||||
import { isCompositeFieldType } from '@/object-record/object-filter-dropdown/utils/isCompositeFieldType';
|
||||
import { useFilterableFieldMetadataItemsInRecordIndexContext } from '@/object-record/record-filter/hooks/useFilterableFieldMetadataItemsInRecordIndexContext';
|
||||
import { DropdownContent } from '@/ui/layout/dropdown/components/DropdownContent';
|
||||
import { useRecoilComponentStateV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentStateV2';
|
||||
import { useSetRecoilComponentStateV2 } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentStateV2';
|
||||
|
||||
@ -132,7 +133,7 @@ export const AdvancedFilterFieldSelectMenu = ({
|
||||
];
|
||||
|
||||
return (
|
||||
<>
|
||||
<DropdownContent>
|
||||
<AdvancedFilterFieldSelectSearchInput />
|
||||
<SelectableList
|
||||
hotkeyScope={advancedFilterFieldSelectDropdownId}
|
||||
@ -175,6 +176,6 @@ export const AdvancedFilterFieldSelectMenu = ({
|
||||
)}
|
||||
</DropdownMenuItemsContainer>
|
||||
</SelectableList>
|
||||
</>
|
||||
</DropdownContent>
|
||||
);
|
||||
};
|
||||
|
||||
@ -4,6 +4,7 @@ import { useUpsertRecordFilterGroup } from '@/object-record/record-filter-group/
|
||||
import { RecordFilterGroup } from '@/object-record/record-filter-group/types/RecordFilterGroup';
|
||||
import { RecordFilterGroupLogicalOperator } from '@/object-record/record-filter-group/types/RecordFilterGroupLogicalOperator';
|
||||
import { Select } from '@/ui/input/components/Select';
|
||||
import { GenericDropdownContentWidth } from '@/ui/layout/dropdown/constants/GenericDropdownContentWidth';
|
||||
|
||||
type AdvancedFilterLogicalOperatorDropdownProps = {
|
||||
recordFilterGroup: RecordFilterGroup;
|
||||
@ -27,6 +28,7 @@ export const AdvancedFilterLogicalOperatorDropdown = ({
|
||||
return (
|
||||
<Select
|
||||
fullWidth
|
||||
dropdownWidth={GenericDropdownContentWidth.Narrow}
|
||||
dropdownId={`advanced-filter-logical-operator-${recordFilterGroup.id}`}
|
||||
value={recordFilterGroup.logicalOperator}
|
||||
onChange={handleChange}
|
||||
|
||||
@ -4,10 +4,11 @@ import { useRemoveRootRecordFilterGroupIfEmpty } from '@/object-record/record-fi
|
||||
import { useRemoveRecordFilter } from '@/object-record/record-filter/hooks/useRemoveRecordFilter';
|
||||
|
||||
import { Dropdown } from '@/ui/layout/dropdown/components/Dropdown';
|
||||
import { DropdownContent } from '@/ui/layout/dropdown/components/DropdownContent';
|
||||
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
|
||||
import { useDropdown } from '@/ui/layout/dropdown/hooks/useDropdown';
|
||||
import { IconButton } from 'twenty-ui/input';
|
||||
import { IconDotsVertical, IconTrash } from 'twenty-ui/display';
|
||||
import { IconButton } from 'twenty-ui/input';
|
||||
import { MenuItem } from 'twenty-ui/navigation';
|
||||
|
||||
type AdvancedFilterRecordFilterGroupOptionsDropdownProps = {
|
||||
@ -53,14 +54,16 @@ export const AdvancedFilterRecordFilterGroupOptionsDropdown = ({
|
||||
/>
|
||||
}
|
||||
dropdownComponents={
|
||||
<DropdownMenuItemsContainer>
|
||||
<MenuItem
|
||||
text="Remove rule group"
|
||||
onClick={handleRemove}
|
||||
LeftIcon={IconTrash}
|
||||
accent="danger"
|
||||
/>
|
||||
</DropdownMenuItemsContainer>
|
||||
<DropdownContent>
|
||||
<DropdownMenuItemsContainer>
|
||||
<MenuItem
|
||||
text="Remove rule group"
|
||||
onClick={handleRemove}
|
||||
LeftIcon={IconTrash}
|
||||
accent="danger"
|
||||
/>
|
||||
</DropdownMenuItemsContainer>
|
||||
</DropdownContent>
|
||||
}
|
||||
dropdownHotkeyScope={{ scope: dropdownId }}
|
||||
dropdownOffset={{ y: 2, x: 0 }}
|
||||
|
||||
@ -6,7 +6,9 @@ import { currentRecordFiltersComponentState } from '@/object-record/record-filte
|
||||
import { getRecordFilterOperands } from '@/object-record/record-filter/utils/getRecordFilterOperands';
|
||||
import { SelectControl } from '@/ui/input/components/SelectControl';
|
||||
import { Dropdown } from '@/ui/layout/dropdown/components/Dropdown';
|
||||
import { DropdownContent } from '@/ui/layout/dropdown/components/DropdownContent';
|
||||
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
|
||||
import { GenericDropdownContentWidth } from '@/ui/layout/dropdown/constants/GenericDropdownContentWidth';
|
||||
import { useDropdown } from '@/ui/layout/dropdown/hooks/useDropdown';
|
||||
import { SelectableList } from '@/ui/layout/selectable-list/components/SelectableList';
|
||||
import { SelectableListItem } from '@/ui/layout/selectable-list/components/SelectableListItem';
|
||||
@ -94,38 +96,39 @@ export const AdvancedFilterRecordFilterOperandSelect = ({
|
||||
/>
|
||||
}
|
||||
dropdownComponents={
|
||||
<DropdownMenuItemsContainer width="auto">
|
||||
<SelectableList
|
||||
hotkeyScope={dropdownId}
|
||||
selectableItemIdArray={operandsForFilterType.map(
|
||||
(operand) => operand,
|
||||
)}
|
||||
selectableListInstanceId={dropdownId}
|
||||
>
|
||||
{operandsForFilterType.map((filterOperand, index) => (
|
||||
<SelectableListItem
|
||||
itemId={filterOperand}
|
||||
key={`select-filter-operand-${index}`}
|
||||
onEnter={() => {
|
||||
handleOperandChange(filterOperand);
|
||||
}}
|
||||
>
|
||||
<MenuItem
|
||||
focused={selectedItemId === filterOperand}
|
||||
onClick={() => {
|
||||
<DropdownContent widthInPixels={GenericDropdownContentWidth.Narrow}>
|
||||
<DropdownMenuItemsContainer>
|
||||
<SelectableList
|
||||
hotkeyScope={dropdownId}
|
||||
selectableItemIdArray={operandsForFilterType.map(
|
||||
(operand) => operand,
|
||||
)}
|
||||
selectableListInstanceId={dropdownId}
|
||||
>
|
||||
{operandsForFilterType.map((filterOperand, index) => (
|
||||
<SelectableListItem
|
||||
itemId={filterOperand}
|
||||
key={`select-filter-operand-${index}`}
|
||||
onEnter={() => {
|
||||
handleOperandChange(filterOperand);
|
||||
}}
|
||||
text={getOperandLabel(filterOperand)}
|
||||
/>
|
||||
</SelectableListItem>
|
||||
))}
|
||||
</SelectableList>
|
||||
</DropdownMenuItemsContainer>
|
||||
>
|
||||
<MenuItem
|
||||
focused={selectedItemId === filterOperand}
|
||||
onClick={() => {
|
||||
handleOperandChange(filterOperand);
|
||||
}}
|
||||
text={getOperandLabel(filterOperand)}
|
||||
/>
|
||||
</SelectableListItem>
|
||||
))}
|
||||
</SelectableList>
|
||||
</DropdownMenuItemsContainer>
|
||||
</DropdownContent>
|
||||
}
|
||||
dropdownHotkeyScope={{ scope: dropdownId }}
|
||||
dropdownOffset={DEFAULT_ADVANCED_FILTER_DROPDOWN_OFFSET}
|
||||
dropdownPlacement="bottom-start"
|
||||
dropdownWidth={200}
|
||||
/>
|
||||
</StyledContainer>
|
||||
);
|
||||
|
||||
@ -7,12 +7,13 @@ import { currentRecordFiltersComponentState } from '@/object-record/record-filte
|
||||
|
||||
import { DEFAULT_ADVANCED_FILTER_DROPDOWN_OFFSET } from '@/object-record/advanced-filter/constants/DefaultAdvancedFilterDropdownOffset';
|
||||
import { Dropdown } from '@/ui/layout/dropdown/components/Dropdown';
|
||||
import { DropdownContent } from '@/ui/layout/dropdown/components/DropdownContent';
|
||||
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
|
||||
import { useDropdown } from '@/ui/layout/dropdown/hooks/useDropdown';
|
||||
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { IconButton } from 'twenty-ui/input';
|
||||
import { IconDotsVertical, IconTrash } from 'twenty-ui/display';
|
||||
import { IconButton } from 'twenty-ui/input';
|
||||
import { MenuItem } from 'twenty-ui/navigation';
|
||||
|
||||
type AdvancedFilterRecordFilterOptionsDropdownProps = {
|
||||
@ -73,14 +74,16 @@ export const AdvancedFilterRecordFilterOptionsDropdown = ({
|
||||
/>
|
||||
}
|
||||
dropdownComponents={
|
||||
<DropdownMenuItemsContainer>
|
||||
<MenuItem
|
||||
text="Remove rule"
|
||||
onClick={handleRemove}
|
||||
LeftIcon={IconTrash}
|
||||
accent="danger"
|
||||
/>
|
||||
</DropdownMenuItemsContainer>
|
||||
<DropdownContent>
|
||||
<DropdownMenuItemsContainer>
|
||||
<MenuItem
|
||||
text="Remove rule"
|
||||
onClick={handleRemove}
|
||||
LeftIcon={IconTrash}
|
||||
accent="danger"
|
||||
/>
|
||||
</DropdownMenuItemsContainer>
|
||||
</DropdownContent>
|
||||
}
|
||||
dropdownHotkeyScope={{ scope: dropdownId }}
|
||||
dropdownOffset={DEFAULT_ADVANCED_FILTER_DROPDOWN_OFFSET}
|
||||
|
||||
@ -2,9 +2,11 @@ import { AdvancedFilterAddFilterRuleSelect } from '@/object-record/advanced-filt
|
||||
|
||||
import { AdvancedFilterRecordFilterGroupRow } from '@/object-record/advanced-filter/components/AdvancedFilterRecordFilterGroupRow';
|
||||
import { AdvancedFilterRecordFilterRow } from '@/object-record/advanced-filter/components/AdvancedFilterRecordFilterRow';
|
||||
import { ADVANCED_FILTER_DROPDOWN_CONTENT_WIDTH } from '@/object-record/advanced-filter/constants/AdvancedFilterDropdownContentWidth';
|
||||
import { useChildRecordFiltersAndRecordFilterGroups } from '@/object-record/advanced-filter/hooks/useChildRecordFiltersAndRecordFilterGroups';
|
||||
import { rootLevelRecordFilterGroupComponentSelector } from '@/object-record/advanced-filter/states/rootLevelRecordFilterGroupComponentSelector';
|
||||
import { isRecordFilterGroupChildARecordFilterGroup } from '@/object-record/advanced-filter/utils/isRecordFilterGroupChildARecordFilterGroup';
|
||||
import { DropdownContent } from '@/ui/layout/dropdown/components/DropdownContent';
|
||||
import { ScrollWrapper } from '@/ui/utilities/scroll/components/ScrollWrapper';
|
||||
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
|
||||
import styled from '@emotion/styled';
|
||||
@ -18,7 +20,6 @@ const StyledContainer = styled.div<{ isGrayBackground?: boolean }>`
|
||||
flex-direction: column;
|
||||
gap: ${({ theme }) => theme.spacing(2)};
|
||||
padding: ${({ theme }) => theme.spacing(2)};
|
||||
min-width: 650px;
|
||||
`;
|
||||
|
||||
export const AdvancedFilterRootRecordFilterGroup = () => {
|
||||
@ -37,31 +38,33 @@ export const AdvancedFilterRootRecordFilterGroup = () => {
|
||||
|
||||
return (
|
||||
<ScrollWrapper componentInstanceId={`scroll-wrapper-dropdown-menu-${id}`}>
|
||||
<StyledContainer>
|
||||
{childRecordFiltersAndRecordFilterGroups.map(
|
||||
(recordFilterGroupChild, recordFilterGroupChildIndex) =>
|
||||
isRecordFilterGroupChildARecordFilterGroup(
|
||||
recordFilterGroupChild,
|
||||
) ? (
|
||||
<AdvancedFilterRecordFilterGroupRow
|
||||
key={recordFilterGroupChild.id}
|
||||
parentRecordFilterGroup={rootRecordFilterGroup}
|
||||
recordFilterGroup={recordFilterGroupChild}
|
||||
recordFilterGroupIndex={recordFilterGroupChildIndex}
|
||||
/>
|
||||
) : (
|
||||
<AdvancedFilterRecordFilterRow
|
||||
key={recordFilterGroupChild.id}
|
||||
recordFilterGroup={rootRecordFilterGroup}
|
||||
recordFilter={recordFilterGroupChild}
|
||||
recordFilterIndex={recordFilterGroupChildIndex}
|
||||
/>
|
||||
),
|
||||
)}
|
||||
<AdvancedFilterAddFilterRuleSelect
|
||||
recordFilterGroup={rootRecordFilterGroup}
|
||||
/>
|
||||
</StyledContainer>
|
||||
<DropdownContent widthInPixels={ADVANCED_FILTER_DROPDOWN_CONTENT_WIDTH}>
|
||||
<StyledContainer>
|
||||
{childRecordFiltersAndRecordFilterGroups.map(
|
||||
(recordFilterGroupChild, recordFilterGroupChildIndex) =>
|
||||
isRecordFilterGroupChildARecordFilterGroup(
|
||||
recordFilterGroupChild,
|
||||
) ? (
|
||||
<AdvancedFilterRecordFilterGroupRow
|
||||
key={recordFilterGroupChild.id}
|
||||
parentRecordFilterGroup={rootRecordFilterGroup}
|
||||
recordFilterGroup={recordFilterGroupChild}
|
||||
recordFilterGroupIndex={recordFilterGroupChildIndex}
|
||||
/>
|
||||
) : (
|
||||
<AdvancedFilterRecordFilterRow
|
||||
key={recordFilterGroupChild.id}
|
||||
recordFilterGroup={rootRecordFilterGroup}
|
||||
recordFilter={recordFilterGroupChild}
|
||||
recordFilterIndex={recordFilterGroupChildIndex}
|
||||
/>
|
||||
),
|
||||
)}
|
||||
<AdvancedFilterAddFilterRuleSelect
|
||||
recordFilterGroup={rootRecordFilterGroup}
|
||||
/>
|
||||
</StyledContainer>
|
||||
</DropdownContent>
|
||||
</ScrollWrapper>
|
||||
);
|
||||
};
|
||||
|
||||
@ -16,6 +16,7 @@ import { areCompositeTypeSubFieldsFilterable } from '@/object-record/record-filt
|
||||
import { isCompositeTypeFilterableByAnySubField } from '@/object-record/record-filter/utils/isCompositeTypeFilterableByAnySubField';
|
||||
import { SETTINGS_COMPOSITE_FIELD_TYPE_CONFIGS } from '@/settings/data-model/constants/SettingsCompositeFieldTypeConfigs';
|
||||
import { CompositeFieldSubFieldName } from '@/settings/data-model/types/CompositeFieldSubFieldName';
|
||||
import { DropdownContent } from '@/ui/layout/dropdown/components/DropdownContent';
|
||||
import { DropdownMenuHeader } from '@/ui/layout/dropdown/components/DropdownMenuHeader/DropdownMenuHeader';
|
||||
import { DropdownMenuHeaderLeftComponent } from '@/ui/layout/dropdown/components/DropdownMenuHeader/internal/DropdownMenuHeaderLeftComponent';
|
||||
import { SelectableList } from '@/ui/layout/selectable-list/components/SelectableList';
|
||||
@ -110,7 +111,7 @@ export const AdvancedFilterSubFieldSelectMenu = ({
|
||||
];
|
||||
|
||||
return (
|
||||
<>
|
||||
<DropdownContent>
|
||||
<DropdownMenuHeader
|
||||
StartComponent={
|
||||
<DropdownMenuHeaderLeftComponent
|
||||
@ -184,6 +185,6 @@ export const AdvancedFilterSubFieldSelectMenu = ({
|
||||
))}
|
||||
</SelectableList>
|
||||
</DropdownMenuItemsContainer>
|
||||
</>
|
||||
</DropdownContent>
|
||||
);
|
||||
};
|
||||
|
||||
@ -133,7 +133,6 @@ export const AdvancedFilterValueInput = ({
|
||||
dropdownHotkeyScope={{ scope: dropdownId }}
|
||||
dropdownOffset={dropdownContentOffset}
|
||||
dropdownPlacement="bottom-start"
|
||||
dropdownWidth={280}
|
||||
onClose={handleFilterValueDropdownClose}
|
||||
onOpen={handleFilterValueDropdownOpen}
|
||||
/>
|
||||
|
||||
@ -0,0 +1 @@
|
||||
export const ADVANCED_FILTER_DROPDOWN_CONTENT_WIDTH = 650;
|
||||
@ -5,6 +5,7 @@ import { useApplyObjectFilterDropdownFilterValue } from '@/object-record/object-
|
||||
import { useObjectFilterDropdownFilterValue } from '@/object-record/object-filter-dropdown/hooks/useObjectFilterDropdownFilterValue';
|
||||
import { SingleRecordPickerHotkeyScope } from '@/object-record/record-picker/single-record-picker/types/SingleRecordPickerHotkeyScope';
|
||||
import { BooleanDisplay } from '@/ui/field/display/components/BooleanDisplay';
|
||||
import { DropdownContent } from '@/ui/layout/dropdown/components/DropdownContent';
|
||||
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
|
||||
import { useDropdown } from '@/ui/layout/dropdown/hooks/useDropdown';
|
||||
import { SelectableList } from '@/ui/layout/selectable-list/components/SelectableList';
|
||||
@ -51,27 +52,29 @@ export const ObjectFilterDropdownBooleanSelect = () => {
|
||||
};
|
||||
|
||||
return (
|
||||
<SelectableList
|
||||
selectableListInstanceId="boolean-select"
|
||||
selectableItemIdArray={options.map((option) => option.toString())}
|
||||
hotkeyScope={SingleRecordPickerHotkeyScope.SingleRecordPicker}
|
||||
>
|
||||
<DropdownMenuItemsContainer hasMaxHeight width="auto">
|
||||
{options.map((option) => (
|
||||
<StyledBooleanSelectContainer
|
||||
key={String(option)}
|
||||
onClick={() => handleOptionSelect(option)}
|
||||
selected={objectFilterDropdownFilterValue === option.toString()}
|
||||
>
|
||||
<BooleanDisplay value={option} />
|
||||
{objectFilterDropdownFilterValue === option.toString() && (
|
||||
<StyledIconCheckContainer>
|
||||
<IconCheck color={theme.grayScale.gray50} size={16} />
|
||||
</StyledIconCheckContainer>
|
||||
)}
|
||||
</StyledBooleanSelectContainer>
|
||||
))}
|
||||
</DropdownMenuItemsContainer>
|
||||
</SelectableList>
|
||||
<DropdownContent>
|
||||
<SelectableList
|
||||
selectableListInstanceId="boolean-select"
|
||||
selectableItemIdArray={options.map((option) => option.toString())}
|
||||
hotkeyScope={SingleRecordPickerHotkeyScope.SingleRecordPicker}
|
||||
>
|
||||
<DropdownMenuItemsContainer hasMaxHeight width="auto">
|
||||
{options.map((option) => (
|
||||
<StyledBooleanSelectContainer
|
||||
key={String(option)}
|
||||
onClick={() => handleOptionSelect(option)}
|
||||
selected={objectFilterDropdownFilterValue === option.toString()}
|
||||
>
|
||||
<BooleanDisplay value={option} />
|
||||
{objectFilterDropdownFilterValue === option.toString() && (
|
||||
<StyledIconCheckContainer>
|
||||
<IconCheck color={theme.grayScale.gray50} size={16} />
|
||||
</StyledIconCheckContainer>
|
||||
)}
|
||||
</StyledBooleanSelectContainer>
|
||||
))}
|
||||
</DropdownMenuItemsContainer>
|
||||
</SelectableList>
|
||||
</DropdownContent>
|
||||
);
|
||||
};
|
||||
|
||||
@ -5,6 +5,7 @@ import { getCountryFlagMenuItemAvatar } from '@/object-record/object-filter-drop
|
||||
import { turnCountryIntoSelectableItem } from '@/object-record/object-filter-dropdown/utils/turnCountryIntoSelectableItem';
|
||||
import { SelectableItem } from '@/object-record/select/types/SelectableItem';
|
||||
import { useCountries } from '@/ui/input/components/internal/hooks/useCountries';
|
||||
import { DropdownContent } from '@/ui/layout/dropdown/components/DropdownContent';
|
||||
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
|
||||
import { DropdownMenuSearchInput } from '@/ui/layout/dropdown/components/DropdownMenuSearchInput';
|
||||
import { DropdownMenuSeparator } from '@/ui/layout/dropdown/components/DropdownMenuSeparator';
|
||||
@ -18,13 +19,7 @@ import { MenuItem, MenuItemMultiSelectAvatar } from 'twenty-ui/navigation';
|
||||
export const EMPTY_FILTER_VALUE = '[]';
|
||||
export const MAX_ITEMS_TO_DISPLAY = 5;
|
||||
|
||||
type ObjectFilterDropdownCountrySelectProps = {
|
||||
dropdownWidth?: number;
|
||||
};
|
||||
|
||||
export const ObjectFilterDropdownCountrySelect = ({
|
||||
dropdownWidth,
|
||||
}: ObjectFilterDropdownCountrySelectProps) => {
|
||||
export const ObjectFilterDropdownCountrySelect = () => {
|
||||
const [searchText, setSearchText] = useState('');
|
||||
|
||||
const objectFilterDropdownCurrentRecordFilter = useRecoilComponentValueV2(
|
||||
@ -101,7 +96,7 @@ export const ObjectFilterDropdownCountrySelect = ({
|
||||
const { t } = useLingui();
|
||||
|
||||
return (
|
||||
<>
|
||||
<DropdownContent>
|
||||
<DropdownMenuSearchInput
|
||||
autoFocus
|
||||
type="text"
|
||||
@ -112,7 +107,7 @@ export const ObjectFilterDropdownCountrySelect = ({
|
||||
}}
|
||||
/>
|
||||
<DropdownMenuSeparator />
|
||||
<DropdownMenuItemsContainer hasMaxHeight width={dropdownWidth ?? 200}>
|
||||
<DropdownMenuItemsContainer hasMaxHeight>
|
||||
{filteredSelectedItems?.map((item) => {
|
||||
return (
|
||||
<MenuItemMultiSelectAvatar
|
||||
@ -141,6 +136,6 @@ export const ObjectFilterDropdownCountrySelect = ({
|
||||
})}
|
||||
{showNoResult && <MenuItem text={t`No results`} />}
|
||||
</DropdownMenuItemsContainer>
|
||||
</>
|
||||
</DropdownContent>
|
||||
);
|
||||
};
|
||||
|
||||
@ -5,9 +5,11 @@ import { turnCurrencyIntoSelectableItem } from '@/object-record/object-filter-dr
|
||||
import { StyledMultipleSelectDropdownAvatarChip } from '@/object-record/select/components/StyledMultipleSelectDropdownAvatarChip';
|
||||
import { SelectableItem } from '@/object-record/select/types/SelectableItem';
|
||||
import { CURRENCIES } from '@/settings/data-model/constants/Currencies';
|
||||
import { DropdownContent } from '@/ui/layout/dropdown/components/DropdownContent';
|
||||
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
|
||||
import { DropdownMenuSearchInput } from '@/ui/layout/dropdown/components/DropdownMenuSearchInput';
|
||||
import { DropdownMenuSeparator } from '@/ui/layout/dropdown/components/DropdownMenuSeparator';
|
||||
import { GenericDropdownContentWidth } from '@/ui/layout/dropdown/constants/GenericDropdownContentWidth';
|
||||
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
|
||||
import { useLingui } from '@lingui/react/macro';
|
||||
import { isNonEmptyString } from '@sniptt/guards';
|
||||
@ -95,7 +97,7 @@ export const ObjectFilterDropdownCurrencySelect = () => {
|
||||
searchText !== '';
|
||||
|
||||
return (
|
||||
<>
|
||||
<DropdownContent widthInPixels={GenericDropdownContentWidth.Large}>
|
||||
<DropdownMenuSearchInput
|
||||
autoFocus
|
||||
type="text"
|
||||
@ -153,6 +155,6 @@ export const ObjectFilterDropdownCurrencySelect = () => {
|
||||
})}
|
||||
{showNoResult && <MenuItem text={t`No results`} />}
|
||||
</DropdownMenuItemsContainer>
|
||||
</>
|
||||
</DropdownContent>
|
||||
);
|
||||
};
|
||||
|
||||
@ -9,17 +9,16 @@ import { ViewFilterOperand } from '@/views/types/ViewFilterOperand';
|
||||
|
||||
import { getFilterTypeFromFieldType } from '@/object-metadata/utils/formatFieldMetadataItemsAsFilterDefinitions';
|
||||
import { ObjectFilterDropdownBooleanSelect } from '@/object-record/object-filter-dropdown/components/ObjectFilterDropdownBooleanSelect';
|
||||
import { ObjectFilterDropdownCurrencySelect } from '@/object-record/object-filter-dropdown/components/ObjectFilterDropdownCurrencySelect';
|
||||
import { ObjectFilterDropdownOperandDropdown } from '@/object-record/object-filter-dropdown/components/ObjectFilterDropdownOperandDropdown';
|
||||
import { ObjectFilterDropdownTextInput } from '@/object-record/object-filter-dropdown/components/ObjectFilterDropdownTextInput';
|
||||
import { DATE_FILTER_TYPES } from '@/object-record/object-filter-dropdown/constants/DateFilterTypes';
|
||||
import { DATE_PICKER_DROPDOWN_CONTENT_WIDTH } from '@/object-record/object-filter-dropdown/constants/DatePickerDropdownContentWidth';
|
||||
import { NUMBER_FILTER_TYPES } from '@/object-record/object-filter-dropdown/constants/NumberFilterTypes';
|
||||
import { TEXT_FILTER_TYPES } from '@/object-record/object-filter-dropdown/constants/TextFilterTypes';
|
||||
import { fieldMetadataItemUsedInDropdownComponentSelector } from '@/object-record/object-filter-dropdown/states/fieldMetadataItemUsedInDropdownComponentSelector';
|
||||
import { selectedOperandInDropdownComponentState } from '@/object-record/object-filter-dropdown/states/selectedOperandInDropdownComponentState';
|
||||
import { subFieldNameUsedInDropdownComponentState } from '@/object-record/object-filter-dropdown/states/subFieldNameUsedInDropdownComponentState';
|
||||
import { isExpectedSubFieldName } from '@/object-record/object-filter-dropdown/utils/isExpectedSubFieldName';
|
||||
import { DropdownContent } from '@/ui/layout/dropdown/components/DropdownContent';
|
||||
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
|
||||
import { FieldMetadataType } from 'twenty-shared/types';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
type ObjectFilterDropdownFilterInputProps = {
|
||||
@ -36,11 +35,6 @@ export const ObjectFilterDropdownFilterInput = ({
|
||||
filterDropdownId,
|
||||
);
|
||||
|
||||
const subFieldNameUsedInDropdown = useRecoilComponentValueV2(
|
||||
subFieldNameUsedInDropdownComponentState,
|
||||
filterDropdownId,
|
||||
);
|
||||
|
||||
const selectedOperandInDropdown = useRecoilComponentValueV2(
|
||||
selectedOperandInDropdownComponentState,
|
||||
filterDropdownId,
|
||||
@ -69,74 +63,52 @@ export const ObjectFilterDropdownFilterInput = ({
|
||||
fieldMetadataItemUsedInDropdown.type,
|
||||
);
|
||||
|
||||
const isNotASubFieldFilter = !isDefined(subFieldNameUsedInDropdown);
|
||||
const isDateFilter = DATE_FILTER_TYPES.includes(filterType);
|
||||
const isOnlyOperand = !isConfigurable;
|
||||
|
||||
return (
|
||||
<>
|
||||
{isConfigurable && selectedOperandInDropdown && (
|
||||
<>
|
||||
{TEXT_FILTER_TYPES.includes(filterType) && (
|
||||
<ObjectFilterDropdownTextInput />
|
||||
)}
|
||||
{NUMBER_FILTER_TYPES.includes(filterType) && (
|
||||
<ObjectFilterDropdownNumberInput />
|
||||
)}
|
||||
{filterType === 'RATING' && <ObjectFilterDropdownRatingInput />}
|
||||
{DATE_FILTER_TYPES.includes(filterType) && (
|
||||
<ObjectFilterDropdownDateInput />
|
||||
)}
|
||||
{filterType === 'RELATION' && (
|
||||
<>
|
||||
<ObjectFilterDropdownSearchInput />
|
||||
<DropdownMenuSeparator />
|
||||
<ObjectFilterDropdownRecordSelect
|
||||
recordFilterId={recordFilterId}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
{filterType === 'ACTOR' && (
|
||||
<>
|
||||
<ObjectFilterDropdownTextInput />
|
||||
</>
|
||||
)}
|
||||
{filterType === 'ADDRESS' &&
|
||||
(isNotASubFieldFilter ? (
|
||||
<>
|
||||
<ObjectFilterDropdownTextInput />
|
||||
</>
|
||||
) : (
|
||||
<></>
|
||||
))}
|
||||
{filterType === 'CURRENCY' &&
|
||||
(isExpectedSubFieldName(
|
||||
FieldMetadataType.CURRENCY,
|
||||
'currencyCode',
|
||||
subFieldNameUsedInDropdown,
|
||||
) ? (
|
||||
<>
|
||||
<ObjectFilterDropdownCurrencySelect />
|
||||
</>
|
||||
) : isExpectedSubFieldName(
|
||||
FieldMetadataType.CURRENCY,
|
||||
'amountMicros',
|
||||
subFieldNameUsedInDropdown,
|
||||
) ? (
|
||||
<>
|
||||
<ObjectFilterDropdownNumberInput />
|
||||
</>
|
||||
) : (
|
||||
<ObjectFilterDropdownNumberInput />
|
||||
))}
|
||||
{['SELECT', 'MULTI_SELECT'].includes(filterType) && (
|
||||
<>
|
||||
<ObjectFilterDropdownSearchInput />
|
||||
<DropdownMenuSeparator />
|
||||
<ObjectFilterDropdownOptionSelect />
|
||||
</>
|
||||
)}
|
||||
{filterType === 'BOOLEAN' && <ObjectFilterDropdownBooleanSelect />}
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
if (isOnlyOperand) {
|
||||
return (
|
||||
<DropdownContent>
|
||||
<ObjectFilterDropdownOperandDropdown />
|
||||
</DropdownContent>
|
||||
);
|
||||
} else if (isDateFilter) {
|
||||
return (
|
||||
<DropdownContent widthInPixels={DATE_PICKER_DROPDOWN_CONTENT_WIDTH}>
|
||||
<ObjectFilterDropdownOperandDropdown />
|
||||
<ObjectFilterDropdownDateInput />
|
||||
</DropdownContent>
|
||||
);
|
||||
} else {
|
||||
return (
|
||||
<DropdownContent>
|
||||
<ObjectFilterDropdownOperandDropdown />
|
||||
{TEXT_FILTER_TYPES.includes(filterType) && (
|
||||
<ObjectFilterDropdownTextInput />
|
||||
)}
|
||||
{NUMBER_FILTER_TYPES.includes(filterType) && (
|
||||
<ObjectFilterDropdownNumberInput />
|
||||
)}
|
||||
{filterType === 'RATING' && <ObjectFilterDropdownRatingInput />}
|
||||
{filterType === 'RELATION' && (
|
||||
<>
|
||||
<ObjectFilterDropdownSearchInput />
|
||||
<DropdownMenuSeparator />
|
||||
<ObjectFilterDropdownRecordSelect recordFilterId={recordFilterId} />
|
||||
</>
|
||||
)}
|
||||
{filterType === 'ACTOR' && <ObjectFilterDropdownTextInput />}
|
||||
{filterType === 'ADDRESS' && <ObjectFilterDropdownTextInput />}
|
||||
{filterType === 'CURRENCY' && <ObjectFilterDropdownNumberInput />}
|
||||
{['SELECT', 'MULTI_SELECT'].includes(filterType) && (
|
||||
<>
|
||||
<ObjectFilterDropdownSearchInput />
|
||||
<DropdownMenuSeparator />
|
||||
<ObjectFilterDropdownOptionSelect />
|
||||
</>
|
||||
)}
|
||||
{filterType === 'BOOLEAN' && <ObjectFilterDropdownBooleanSelect />}
|
||||
</DropdownContent>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
@ -5,16 +5,20 @@ import { selectedOperandInDropdownComponentState } from '@/object-record/object-
|
||||
|
||||
import { FiltersHotkeyScope } from '@/object-record/object-filter-dropdown/types/FiltersHotkeyScope';
|
||||
import { Dropdown } from '@/ui/layout/dropdown/components/Dropdown';
|
||||
import { ClickOutsideListenerContext } from '@/ui/utilities/pointer-event/contexts/ClickOutsideListenerContext';
|
||||
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
|
||||
import { useTheme } from '@emotion/react';
|
||||
import styled from '@emotion/styled';
|
||||
import { getOperandLabel } from '../utils/getOperandLabel';
|
||||
import { IconChevronDown } from 'twenty-ui/display';
|
||||
import { getOperandLabel } from '../utils/getOperandLabel';
|
||||
|
||||
const StyledDropdownMenuHeader = styled(DropdownMenuHeader)`
|
||||
cursor: pointer;
|
||||
`;
|
||||
|
||||
export const OPERAND_DROPDOWN_CLICK_OUTSIDE_ID =
|
||||
'object-filter-dropdown-operand-dropdown-click-outside-id';
|
||||
|
||||
export const ObjectFilterDropdownOperandDropdown = ({
|
||||
filterDropdownId,
|
||||
}: {
|
||||
@ -29,21 +33,25 @@ export const ObjectFilterDropdownOperandDropdown = ({
|
||||
const dropdownId = `${filterDropdownId}-operand-dropdown`;
|
||||
|
||||
return (
|
||||
<Dropdown
|
||||
dropdownId={dropdownId}
|
||||
clickableComponent={
|
||||
<StyledDropdownMenuHeader
|
||||
key={'selected-filter-operand'}
|
||||
EndComponent={<IconChevronDown />}
|
||||
>
|
||||
{getOperandLabel(selectedOperandInDropdown)}
|
||||
</StyledDropdownMenuHeader>
|
||||
}
|
||||
dropdownComponents={<ObjectFilterDropdownOperandSelect />}
|
||||
dropdownHotkeyScope={{
|
||||
scope: FiltersHotkeyScope.ObjectFilterDropdownOperandDropdown,
|
||||
}}
|
||||
dropdownOffset={{ x: parseInt(theme.spacing(2), 10) }}
|
||||
/>
|
||||
<ClickOutsideListenerContext.Provider
|
||||
value={{ excludedClickOutsideId: OPERAND_DROPDOWN_CLICK_OUTSIDE_ID }}
|
||||
>
|
||||
<Dropdown
|
||||
dropdownId={dropdownId}
|
||||
clickableComponent={
|
||||
<StyledDropdownMenuHeader
|
||||
key={'selected-filter-operand'}
|
||||
EndComponent={<IconChevronDown />}
|
||||
>
|
||||
{getOperandLabel(selectedOperandInDropdown)}
|
||||
</StyledDropdownMenuHeader>
|
||||
}
|
||||
dropdownComponents={<ObjectFilterDropdownOperandSelect />}
|
||||
dropdownHotkeyScope={{
|
||||
scope: FiltersHotkeyScope.ObjectFilterDropdownOperandDropdown,
|
||||
}}
|
||||
dropdownOffset={{ x: parseInt(theme.spacing(2), 10) }}
|
||||
/>
|
||||
</ClickOutsideListenerContext.Provider>
|
||||
);
|
||||
};
|
||||
|
||||
@ -3,6 +3,7 @@ import { useApplyObjectFilterDropdownOperand } from '@/object-record/object-filt
|
||||
import { fieldMetadataItemUsedInDropdownComponentSelector } from '@/object-record/object-filter-dropdown/states/fieldMetadataItemUsedInDropdownComponentSelector';
|
||||
import { subFieldNameUsedInDropdownComponentState } from '@/object-record/object-filter-dropdown/states/subFieldNameUsedInDropdownComponentState';
|
||||
import { getRecordFilterOperands } from '@/object-record/record-filter/utils/getRecordFilterOperands';
|
||||
import { DropdownContent } from '@/ui/layout/dropdown/components/DropdownContent';
|
||||
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
|
||||
import { useDropdown } from '@/ui/layout/dropdown/hooks/useDropdown';
|
||||
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
|
||||
@ -45,17 +46,19 @@ export const ObjectFilterDropdownOperandSelect = () => {
|
||||
};
|
||||
|
||||
return (
|
||||
<StyledDropdownMenuItemsContainer>
|
||||
{operandsForFilterType.map((filterOperand, index) => (
|
||||
<MenuItem
|
||||
key={`select-filter-operand-${index}`}
|
||||
onClick={() => {
|
||||
handleOperandChange(filterOperand);
|
||||
closeDropdown();
|
||||
}}
|
||||
text={getOperandLabel(filterOperand)}
|
||||
/>
|
||||
))}
|
||||
</StyledDropdownMenuItemsContainer>
|
||||
<DropdownContent>
|
||||
<StyledDropdownMenuItemsContainer>
|
||||
{operandsForFilterType.map((filterOperand, index) => (
|
||||
<MenuItem
|
||||
key={`select-filter-operand-${index}`}
|
||||
onClick={() => {
|
||||
handleOperandChange(filterOperand);
|
||||
closeDropdown();
|
||||
}}
|
||||
text={getOperandLabel(filterOperand)}
|
||||
/>
|
||||
))}
|
||||
</StyledDropdownMenuItemsContainer>
|
||||
</DropdownContent>
|
||||
);
|
||||
};
|
||||
|
||||
@ -1,19 +0,0 @@
|
||||
import { ObjectFilterDropdownFilterInput } from '@/object-record/object-filter-dropdown/components/ObjectFilterDropdownFilterInput';
|
||||
import { ObjectFilterDropdownOperandDropdown } from '@/object-record/object-filter-dropdown/components/ObjectFilterDropdownOperandDropdown';
|
||||
|
||||
type ObjectFilterOperandSelectAndInputProps = {
|
||||
filterDropdownId?: string;
|
||||
};
|
||||
|
||||
export const ObjectFilterOperandSelectAndInput = ({
|
||||
filterDropdownId,
|
||||
}: ObjectFilterOperandSelectAndInputProps) => {
|
||||
return (
|
||||
<>
|
||||
<ObjectFilterDropdownOperandDropdown
|
||||
filterDropdownId={filterDropdownId}
|
||||
/>
|
||||
<ObjectFilterDropdownFilterInput filterDropdownId={filterDropdownId} />
|
||||
</>
|
||||
);
|
||||
};
|
||||
@ -0,0 +1 @@
|
||||
export const DATE_PICKER_DROPDOWN_CONTENT_WIDTH = 280;
|
||||
@ -1,13 +1,14 @@
|
||||
import { useObjectOptionsForBoard } from '@/object-record/object-options-dropdown/hooks/useObjectOptionsForBoard';
|
||||
import { useObjectOptionsForTable } from '@/object-record/object-options-dropdown/hooks/useObjectOptionsForTable';
|
||||
import { useOptionsDropdown } from '@/object-record/object-options-dropdown/hooks/useOptionsDropdown';
|
||||
import { DropdownContent } from '@/ui/layout/dropdown/components/DropdownContent';
|
||||
import { DropdownMenuHeader } from '@/ui/layout/dropdown/components/DropdownMenuHeader/DropdownMenuHeader';
|
||||
import { DropdownMenuHeaderLeftComponent } from '@/ui/layout/dropdown/components/DropdownMenuHeader/internal/DropdownMenuHeaderLeftComponent';
|
||||
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
|
||||
import { DropdownMenuSeparator } from '@/ui/layout/dropdown/components/DropdownMenuSeparator';
|
||||
import { ViewFieldsVisibilityDropdownSection } from '@/views/components/ViewFieldsVisibilityDropdownSection';
|
||||
import { ViewType } from '@/views/types/ViewType';
|
||||
import { useLingui } from '@lingui/react/macro';
|
||||
import { DropdownMenuHeaderLeftComponent } from '@/ui/layout/dropdown/components/DropdownMenuHeader/internal/DropdownMenuHeaderLeftComponent';
|
||||
import { IconChevronLeft, IconEyeOff } from 'twenty-ui/display';
|
||||
import { MenuItemNavigate } from 'twenty-ui/navigation';
|
||||
|
||||
@ -51,7 +52,7 @@ export const ObjectOptionsDropdownFieldsContent = () => {
|
||||
: handleColumnVisibilityChange;
|
||||
|
||||
return (
|
||||
<>
|
||||
<DropdownContent>
|
||||
<DropdownMenuHeader
|
||||
StartComponent={
|
||||
<DropdownMenuHeaderLeftComponent
|
||||
@ -79,6 +80,6 @@ export const ObjectOptionsDropdownFieldsContent = () => {
|
||||
text={t`Hidden Fields`}
|
||||
/>
|
||||
</DropdownMenuItemsContainer>
|
||||
</>
|
||||
</DropdownContent>
|
||||
);
|
||||
};
|
||||
|
||||
@ -7,17 +7,18 @@ import { useObjectOptionsForBoard } from '@/object-record/object-options-dropdow
|
||||
import { useObjectOptionsForTable } from '@/object-record/object-options-dropdown/hooks/useObjectOptionsForTable';
|
||||
import { useOptionsDropdown } from '@/object-record/object-options-dropdown/hooks/useOptionsDropdown';
|
||||
import { SettingsPath } from '@/types/SettingsPath';
|
||||
import { DropdownContent } from '@/ui/layout/dropdown/components/DropdownContent';
|
||||
import { DropdownMenuHeader } from '@/ui/layout/dropdown/components/DropdownMenuHeader/DropdownMenuHeader';
|
||||
import { DropdownMenuHeaderLeftComponent } from '@/ui/layout/dropdown/components/DropdownMenuHeader/internal/DropdownMenuHeaderLeftComponent';
|
||||
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
|
||||
import { DropdownMenuSeparator } from '@/ui/layout/dropdown/components/DropdownMenuSeparator';
|
||||
import { navigationMemorizedUrlState } from '@/ui/navigation/states/navigationMemorizedUrlState';
|
||||
import { ViewFieldsVisibilityDropdownSection } from '@/views/components/ViewFieldsVisibilityDropdownSection';
|
||||
import { ViewType } from '@/views/types/ViewType';
|
||||
import { useLingui } from '@lingui/react/macro';
|
||||
import { getSettingsPath } from '~/utils/navigation/getSettingsPath';
|
||||
import { DropdownMenuHeaderLeftComponent } from '@/ui/layout/dropdown/components/DropdownMenuHeader/internal/DropdownMenuHeaderLeftComponent';
|
||||
import { IconChevronLeft, IconSettings } from 'twenty-ui/display';
|
||||
import { MenuItem, UndecoratedLink } from 'twenty-ui/navigation';
|
||||
import { getSettingsPath } from '~/utils/navigation/getSettingsPath';
|
||||
|
||||
export const ObjectOptionsDropdownHiddenFieldsContent = () => {
|
||||
const { t } = useLingui();
|
||||
@ -61,7 +62,7 @@ export const ObjectOptionsDropdownHiddenFieldsContent = () => {
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
<DropdownContent>
|
||||
<DropdownMenuHeader
|
||||
StartComponent={
|
||||
<DropdownMenuHeaderLeftComponent
|
||||
@ -94,6 +95,6 @@ export const ObjectOptionsDropdownHiddenFieldsContent = () => {
|
||||
<MenuItem LeftIcon={IconSettings} text={t`Edit Fields`} />
|
||||
</DropdownMenuItemsContainer>
|
||||
</UndecoratedLink>
|
||||
</>
|
||||
</DropdownContent>
|
||||
);
|
||||
};
|
||||
|
||||
@ -8,7 +8,9 @@ import { useRecordGroupVisibility } from '@/object-record/record-group/hooks/use
|
||||
import { recordGroupFieldMetadataComponentState } from '@/object-record/record-group/states/recordGroupFieldMetadataComponentState';
|
||||
import { hiddenRecordGroupIdsComponentSelector } from '@/object-record/record-group/states/selectors/hiddenRecordGroupIdsComponentSelector';
|
||||
import { SettingsPath } from '@/types/SettingsPath';
|
||||
import { DropdownContent } from '@/ui/layout/dropdown/components/DropdownContent';
|
||||
import { DropdownMenuHeader } from '@/ui/layout/dropdown/components/DropdownMenuHeader/DropdownMenuHeader';
|
||||
import { DropdownMenuHeaderLeftComponent } from '@/ui/layout/dropdown/components/DropdownMenuHeader/internal/DropdownMenuHeaderLeftComponent';
|
||||
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
|
||||
import { DropdownMenuSeparator } from '@/ui/layout/dropdown/components/DropdownMenuSeparator';
|
||||
import { navigationMemorizedUrlState } from '@/ui/navigation/states/navigationMemorizedUrlState';
|
||||
@ -16,10 +18,9 @@ import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/
|
||||
import { useLingui } from '@lingui/react/macro';
|
||||
import { useLocation } from 'react-router-dom';
|
||||
import { useSetRecoilState } from 'recoil';
|
||||
import { getSettingsPath } from '~/utils/navigation/getSettingsPath';
|
||||
import { DropdownMenuHeaderLeftComponent } from '@/ui/layout/dropdown/components/DropdownMenuHeader/internal/DropdownMenuHeaderLeftComponent';
|
||||
import { IconChevronLeft, IconSettings } from 'twenty-ui/display';
|
||||
import { MenuItem, UndecoratedLink } from 'twenty-ui/navigation';
|
||||
import { getSettingsPath } from '~/utils/navigation/getSettingsPath';
|
||||
|
||||
export const ObjectOptionsDropdownHiddenRecordGroupsContent = () => {
|
||||
const { t } = useLingui();
|
||||
@ -68,20 +69,17 @@ export const ObjectOptionsDropdownHiddenRecordGroupsContent = () => {
|
||||
}, [hiddenRecordGroupIds, currentContentId, onContentChange]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<DropdownMenuItemsContainer>
|
||||
<DropdownMenuHeader
|
||||
StartComponent={
|
||||
<DropdownMenuHeaderLeftComponent
|
||||
onClick={() => onContentChange('recordGroups')}
|
||||
Icon={IconChevronLeft}
|
||||
/>
|
||||
}
|
||||
>
|
||||
Hidden {recordGroupFieldMetadata?.label}
|
||||
</DropdownMenuHeader>
|
||||
</DropdownMenuItemsContainer>
|
||||
|
||||
<DropdownContent>
|
||||
<DropdownMenuHeader
|
||||
StartComponent={
|
||||
<DropdownMenuHeaderLeftComponent
|
||||
onClick={() => onContentChange('recordGroups')}
|
||||
Icon={IconChevronLeft}
|
||||
/>
|
||||
}
|
||||
>
|
||||
Hidden {recordGroupFieldMetadata?.label}
|
||||
</DropdownMenuHeader>
|
||||
<RecordGroupsVisibilityDropdownSection
|
||||
title={`Hidden ${recordGroupFieldMetadata?.label}`}
|
||||
recordGroupIds={hiddenRecordGroupIds}
|
||||
@ -102,6 +100,6 @@ export const ObjectOptionsDropdownHiddenRecordGroupsContent = () => {
|
||||
<MenuItem LeftIcon={IconSettings} text={t`Edit field values`} />
|
||||
</DropdownMenuItemsContainer>
|
||||
</UndecoratedLink>
|
||||
</>
|
||||
</DropdownContent>
|
||||
);
|
||||
};
|
||||
|
||||
@ -5,6 +5,7 @@ import { useSetViewTypeFromLayoutOptionsMenu } from '@/object-record/object-opti
|
||||
import { recordGroupFieldMetadataComponentState } from '@/object-record/record-group/states/recordGroupFieldMetadataComponentState';
|
||||
import { recordIndexOpenRecordInState } from '@/object-record/record-index/states/recordIndexOpenRecordInState';
|
||||
import { TableOptionsHotkeyScope } from '@/object-record/record-table/types/TableOptionsHotkeyScope';
|
||||
import { DropdownContent } from '@/ui/layout/dropdown/components/DropdownContent';
|
||||
import { DropdownMenuHeader } from '@/ui/layout/dropdown/components/DropdownMenuHeader/DropdownMenuHeader';
|
||||
import { DropdownMenuHeaderLeftComponent } from '@/ui/layout/dropdown/components/DropdownMenuHeader/internal/DropdownMenuHeaderLeftComponent';
|
||||
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
|
||||
@ -92,7 +93,7 @@ export const ObjectOptionsDropdownLayoutContent = () => {
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
<DropdownContent>
|
||||
<DropdownMenuHeader
|
||||
StartComponent={
|
||||
<DropdownMenuHeaderLeftComponent
|
||||
@ -234,6 +235,6 @@ export const ObjectOptionsDropdownLayoutContent = () => {
|
||||
</SelectableList>
|
||||
</DropdownMenuItemsContainer>
|
||||
)}
|
||||
</>
|
||||
</DropdownContent>
|
||||
);
|
||||
};
|
||||
|
||||
@ -3,6 +3,7 @@ import { useOptionsDropdown } from '@/object-record/object-options-dropdown/hook
|
||||
import { useUpdateObjectViewOptions } from '@/object-record/object-options-dropdown/hooks/useUpdateObjectViewOptions';
|
||||
import { recordIndexOpenRecordInState } from '@/object-record/record-index/states/recordIndexOpenRecordInState';
|
||||
import { TableOptionsHotkeyScope } from '@/object-record/record-table/types/TableOptionsHotkeyScope';
|
||||
import { DropdownContent } from '@/ui/layout/dropdown/components/DropdownContent';
|
||||
import { DropdownMenuHeader } from '@/ui/layout/dropdown/components/DropdownMenuHeader/DropdownMenuHeader';
|
||||
import { DropdownMenuHeaderLeftComponent } from '@/ui/layout/dropdown/components/DropdownMenuHeader/internal/DropdownMenuHeaderLeftComponent';
|
||||
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
|
||||
@ -38,7 +39,7 @@ export const ObjectOptionsDropdownLayoutOpenInContent = () => {
|
||||
];
|
||||
|
||||
return (
|
||||
<>
|
||||
<DropdownContent>
|
||||
<DropdownMenuHeader
|
||||
StartComponent={
|
||||
<DropdownMenuHeaderLeftComponent
|
||||
@ -105,6 +106,6 @@ export const ObjectOptionsDropdownLayoutOpenInContent = () => {
|
||||
</SelectableListItem>
|
||||
</SelectableList>
|
||||
</DropdownMenuItemsContainer>
|
||||
</>
|
||||
</DropdownContent>
|
||||
);
|
||||
};
|
||||
|
||||
@ -8,6 +8,7 @@ import { recordGroupFieldMetadataComponentState } from '@/object-record/record-g
|
||||
import { TableOptionsHotkeyScope } from '@/object-record/record-table/types/TableOptionsHotkeyScope';
|
||||
import { SnackBarVariant } from '@/ui/feedback/snack-bar-manager/components/SnackBar';
|
||||
import { useSnackBar } from '@/ui/feedback/snack-bar-manager/hooks/useSnackBar';
|
||||
import { DropdownContent } from '@/ui/layout/dropdown/components/DropdownContent';
|
||||
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
|
||||
import { DropdownMenuSeparator } from '@/ui/layout/dropdown/components/DropdownMenuSeparator';
|
||||
import { SelectableList } from '@/ui/layout/selectable-list/components/SelectableList';
|
||||
@ -93,7 +94,7 @@ export const ObjectOptionsDropdownMenuContent = () => {
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
<DropdownContent>
|
||||
{currentView && (
|
||||
<ObjectOptionsDropdownMenuViewName currentView={currentView} />
|
||||
)}
|
||||
@ -121,7 +122,6 @@ export const ObjectOptionsDropdownMenuContent = () => {
|
||||
</SelectableListItem>
|
||||
</DropdownMenuItemsContainer>
|
||||
<DropdownMenuSeparator />
|
||||
|
||||
<DropdownMenuItemsContainer scrollable={false}>
|
||||
<SelectableListItem
|
||||
itemId="Fields"
|
||||
@ -136,7 +136,6 @@ export const ObjectOptionsDropdownMenuContent = () => {
|
||||
hasSubMenu
|
||||
/>
|
||||
</SelectableListItem>
|
||||
|
||||
<div id="group-by-menu-item">
|
||||
<SelectableListItem
|
||||
itemId="Group"
|
||||
@ -175,7 +174,6 @@ export const ObjectOptionsDropdownMenuContent = () => {
|
||||
/>
|
||||
)}
|
||||
<DropdownMenuSeparator />
|
||||
|
||||
<SelectableListItem
|
||||
itemId="Copy link to view"
|
||||
onEnter={() => {
|
||||
@ -228,6 +226,6 @@ export const ObjectOptionsDropdownMenuContent = () => {
|
||||
)}
|
||||
</DropdownMenuItemsContainer>
|
||||
</SelectableList>
|
||||
</>
|
||||
</DropdownContent>
|
||||
);
|
||||
};
|
||||
|
||||
@ -9,6 +9,7 @@ import { recordGroupFieldMetadataComponentState } from '@/object-record/record-g
|
||||
import { hiddenRecordGroupIdsComponentSelector } from '@/object-record/record-group/states/selectors/hiddenRecordGroupIdsComponentSelector';
|
||||
import { useHandleRecordGroupField } from '@/object-record/record-index/hooks/useHandleRecordGroupField';
|
||||
import { SettingsPath } from '@/types/SettingsPath';
|
||||
import { DropdownContent } from '@/ui/layout/dropdown/components/DropdownContent';
|
||||
import { DropdownMenuHeader } from '@/ui/layout/dropdown/components/DropdownMenuHeader/DropdownMenuHeader';
|
||||
import { DropdownMenuHeaderLeftComponent } from '@/ui/layout/dropdown/components/DropdownMenuHeader/internal/DropdownMenuHeaderLeftComponent';
|
||||
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
|
||||
@ -103,7 +104,7 @@ export const ObjectOptionsDropdownRecordGroupFieldsContent = () => {
|
||||
}, [hiddenRecordGroupIds, currentContentId, onContentChange]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<DropdownContent>
|
||||
<DropdownMenuHeader
|
||||
StartComponent={
|
||||
<DropdownMenuHeaderLeftComponent
|
||||
@ -124,6 +125,7 @@ export const ObjectOptionsDropdownRecordGroupFieldsContent = () => {
|
||||
placeholder={t`Search fields`}
|
||||
onChange={(event) => setRecordGroupFieldSearchInput(event.target.value)}
|
||||
/>
|
||||
<DropdownMenuSeparator />
|
||||
<DropdownMenuItemsContainer>
|
||||
{viewType === ViewType.Table && (
|
||||
<MenuItemSelect
|
||||
@ -154,6 +156,6 @@ export const ObjectOptionsDropdownRecordGroupFieldsContent = () => {
|
||||
<MenuItem LeftIcon={IconSettings} text={t`Create select field`} />
|
||||
</UndecoratedLink>
|
||||
</DropdownMenuItemsContainer>
|
||||
</>
|
||||
</DropdownContent>
|
||||
);
|
||||
};
|
||||
|
||||
@ -6,6 +6,7 @@ import { hiddenRecordGroupIdsComponentSelector } from '@/object-record/record-gr
|
||||
import { RecordGroupSort } from '@/object-record/record-group/types/RecordGroupSort';
|
||||
import { recordIndexRecordGroupSortComponentState } from '@/object-record/record-index/states/recordIndexRecordGroupSortComponentState';
|
||||
import { TableOptionsHotkeyScope } from '@/object-record/record-table/types/TableOptionsHotkeyScope';
|
||||
import { DropdownContent } from '@/ui/layout/dropdown/components/DropdownContent';
|
||||
import { DropdownMenuHeader } from '@/ui/layout/dropdown/components/DropdownMenuHeader/DropdownMenuHeader';
|
||||
import { DropdownMenuHeaderLeftComponent } from '@/ui/layout/dropdown/components/DropdownMenuHeader/internal/DropdownMenuHeaderLeftComponent';
|
||||
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
|
||||
@ -58,7 +59,7 @@ export const ObjectOptionsDropdownRecordGroupSortContent = () => {
|
||||
];
|
||||
|
||||
return (
|
||||
<>
|
||||
<DropdownContent>
|
||||
<DropdownMenuHeader
|
||||
StartComponent={
|
||||
<DropdownMenuHeaderLeftComponent
|
||||
@ -123,6 +124,6 @@ export const ObjectOptionsDropdownRecordGroupSortContent = () => {
|
||||
</SelectableListItem>
|
||||
</SelectableList>
|
||||
</DropdownMenuItemsContainer>
|
||||
</>
|
||||
</DropdownContent>
|
||||
);
|
||||
};
|
||||
|
||||
@ -10,6 +10,7 @@ import { visibleRecordGroupIdsComponentFamilySelector } from '@/object-record/re
|
||||
import { recordIndexRecordGroupHideComponentFamilyState } from '@/object-record/record-index/states/recordIndexRecordGroupHideComponentFamilyState';
|
||||
import { recordIndexRecordGroupSortComponentState } from '@/object-record/record-index/states/recordIndexRecordGroupSortComponentState';
|
||||
import { TableOptionsHotkeyScope } from '@/object-record/record-table/types/TableOptionsHotkeyScope';
|
||||
import { DropdownContent } from '@/ui/layout/dropdown/components/DropdownContent';
|
||||
import { DropdownMenuHeader } from '@/ui/layout/dropdown/components/DropdownMenuHeader/DropdownMenuHeader';
|
||||
import { DropdownMenuHeaderLeftComponent } from '@/ui/layout/dropdown/components/DropdownMenuHeader/internal/DropdownMenuHeaderLeftComponent';
|
||||
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
|
||||
@ -94,7 +95,7 @@ export const ObjectOptionsDropdownRecordGroupsContent = () => {
|
||||
];
|
||||
|
||||
return (
|
||||
<>
|
||||
<DropdownContent>
|
||||
<DropdownMenuHeader
|
||||
StartComponent={
|
||||
<DropdownMenuHeaderLeftComponent
|
||||
@ -192,6 +193,6 @@ export const ObjectOptionsDropdownRecordGroupsContent = () => {
|
||||
</DropdownMenuItemsContainer>
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
</DropdownContent>
|
||||
);
|
||||
};
|
||||
|
||||
@ -10,19 +10,19 @@ import { RecordFiltersComponentInstanceContext } from '@/object-record/record-fi
|
||||
import { RecordIndexContextProvider } from '@/object-record/record-index/contexts/RecordIndexContext';
|
||||
import { RecordSortsComponentInstanceContext } from '@/object-record/record-sort/states/context/RecordSortsComponentInstanceContext';
|
||||
import { RecordTableComponentInstanceContext } from '@/object-record/record-table/states/context/RecordTableComponentInstanceContext';
|
||||
import { DropdownMenu } from '@/ui/layout/dropdown/components/DropdownMenu';
|
||||
import { DropdownContent } from '@/ui/layout/dropdown/components/DropdownContent';
|
||||
import { ViewComponentInstanceContext } from '@/views/states/contexts/ViewComponentInstanceContext';
|
||||
import { ViewType } from '@/views/types/ViewType';
|
||||
import { useEffect } from 'react';
|
||||
import { MemoryRouter } from 'react-router-dom';
|
||||
import { useSetRecoilState } from 'recoil';
|
||||
import { ComponentDecorator } from 'twenty-ui/testing';
|
||||
import { ContextStoreDecorator } from '~/testing/decorators/ContextStoreDecorator';
|
||||
import { I18nFrontDecorator } from '~/testing/decorators/I18nFrontDecorator';
|
||||
import { IconsProviderDecorator } from '~/testing/decorators/IconsProviderDecorator';
|
||||
import { ObjectMetadataItemsDecorator } from '~/testing/decorators/ObjectMetadataItemsDecorator';
|
||||
import { SnackBarDecorator } from '~/testing/decorators/SnackBarDecorator';
|
||||
import { generatedMockObjectMetadataItems } from '~/testing/mock-data/generatedMockObjectMetadataItems';
|
||||
import { ComponentDecorator } from 'twenty-ui/testing';
|
||||
|
||||
const instanceId = 'entity-options-scope';
|
||||
|
||||
@ -111,9 +111,9 @@ const createStory = (contentId: ObjectOptionsContentId | null): Story => ({
|
||||
dropdownId: OBJECT_OPTIONS_DROPDOWN_ID,
|
||||
}}
|
||||
>
|
||||
<DropdownMenu>
|
||||
<DropdownContent>
|
||||
<Story />
|
||||
</DropdownMenu>
|
||||
</DropdownContent>
|
||||
</ObjectOptionsDropdownContext.Provider>
|
||||
</RecordIndexContextProvider>
|
||||
);
|
||||
|
||||
@ -19,6 +19,7 @@ import {
|
||||
import { hiddenTableColumnsComponentSelector } from '@/object-record/record-table/states/selectors/hiddenTableColumnsComponentSelector';
|
||||
import { visibleTableColumnsComponentSelector } from '@/object-record/record-table/states/selectors/visibleTableColumnsComponentSelector';
|
||||
import { Dropdown } from '@/ui/layout/dropdown/components/Dropdown';
|
||||
import { DropdownContent } from '@/ui/layout/dropdown/components/DropdownContent';
|
||||
import { DropdownMenuHeader } from '@/ui/layout/dropdown/components/DropdownMenuHeader/DropdownMenuHeader';
|
||||
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
|
||||
import { DropdownMenuSeparator } from '@/ui/layout/dropdown/components/DropdownMenuSeparator';
|
||||
@ -232,88 +233,91 @@ export const ObjectSortDropdownButton = ({
|
||||
</StyledHeaderDropdownButton>
|
||||
}
|
||||
dropdownComponents={
|
||||
<SelectableList
|
||||
selectableListInstanceId={OBJECT_SORT_DROPDOWN_ID}
|
||||
hotkeyScope={hotkeyScope.scope}
|
||||
selectableItemIdArray={selectableItemIdArray}
|
||||
>
|
||||
{isRecordSortDirectionMenuUnfolded && (
|
||||
<StyledSelectedSortDirectionContainer>
|
||||
<DropdownMenuItemsContainer>
|
||||
{RECORD_SORT_DIRECTIONS.map((sortDirection, index) => (
|
||||
<MenuItem
|
||||
key={index}
|
||||
focused={selectedItemId === sortDirection}
|
||||
onClick={() => handleSortDirectionClick(sortDirection)}
|
||||
text={
|
||||
sortDirection === 'asc' ? t`Ascending` : t`Descending`
|
||||
}
|
||||
/>
|
||||
))}
|
||||
</DropdownMenuItemsContainer>
|
||||
</StyledSelectedSortDirectionContainer>
|
||||
)}
|
||||
<DropdownMenuHeader
|
||||
onClick={() =>
|
||||
setIsRecordSortDirectionMenuUnfolded(
|
||||
!isRecordSortDirectionMenuUnfolded,
|
||||
)
|
||||
}
|
||||
EndComponent={
|
||||
<StyledDropdownMenuHeaderEndComponent>
|
||||
<IconChevronDown size={theme.icon.size.md} />
|
||||
</StyledDropdownMenuHeaderEndComponent>
|
||||
}
|
||||
<DropdownContent>
|
||||
<SelectableList
|
||||
selectableListInstanceId={OBJECT_SORT_DROPDOWN_ID}
|
||||
hotkeyScope={hotkeyScope.scope}
|
||||
selectableItemIdArray={selectableItemIdArray}
|
||||
>
|
||||
{selectedRecordSortDirection === 'asc'
|
||||
? t`Ascending`
|
||||
: t`Descending`}
|
||||
</DropdownMenuHeader>
|
||||
<DropdownMenuSeparator />
|
||||
<StyledInput
|
||||
autoFocus
|
||||
value={objectSortDropdownSearchInput}
|
||||
placeholder={t`Search fields`}
|
||||
onChange={(event) =>
|
||||
setObjectSortDropdownSearchInput(event.target.value)
|
||||
}
|
||||
/>
|
||||
<DropdownMenuItemsContainer scrollable={false}>
|
||||
{visibleFieldMetadataItems.map(
|
||||
(visibleFieldMetadataItem, index) => (
|
||||
<SelectableListItem
|
||||
key={visibleFieldMetadataItem.id}
|
||||
itemId={visibleFieldMetadataItem.id}
|
||||
onEnter={() => handleAddSort(visibleFieldMetadataItem)}
|
||||
>
|
||||
<MenuItem
|
||||
focused={selectedItemId === visibleFieldMetadataItem.id}
|
||||
testId={`visible-select-sort-${index}`}
|
||||
onClick={() => handleAddSort(visibleFieldMetadataItem)}
|
||||
LeftIcon={getIcon(visibleFieldMetadataItem.icon)}
|
||||
text={visibleFieldMetadataItem.label}
|
||||
/>
|
||||
</SelectableListItem>
|
||||
),
|
||||
{isRecordSortDirectionMenuUnfolded && (
|
||||
<StyledSelectedSortDirectionContainer>
|
||||
<DropdownMenuItemsContainer>
|
||||
{RECORD_SORT_DIRECTIONS.map((sortDirection, index) => (
|
||||
<MenuItem
|
||||
key={index}
|
||||
focused={selectedItemId === sortDirection}
|
||||
onClick={() => handleSortDirectionClick(sortDirection)}
|
||||
text={
|
||||
sortDirection === 'asc' ? t`Ascending` : t`Descending`
|
||||
}
|
||||
/>
|
||||
))}
|
||||
</DropdownMenuItemsContainer>
|
||||
</StyledSelectedSortDirectionContainer>
|
||||
)}
|
||||
{shouldShowSeparator && <DropdownMenuSeparator />}
|
||||
{hiddenFieldMetadataItems.map((hiddenFieldMetadataItem, index) => (
|
||||
<SelectableListItem
|
||||
key={hiddenFieldMetadataItem.id}
|
||||
itemId={hiddenFieldMetadataItem.id}
|
||||
onEnter={() => handleAddSort(hiddenFieldMetadataItem)}
|
||||
>
|
||||
<MenuItem
|
||||
focused={selectedItemId === hiddenFieldMetadataItem.id}
|
||||
testId={`hidden-select-sort-${index}`}
|
||||
onClick={() => handleAddSort(hiddenFieldMetadataItem)}
|
||||
LeftIcon={getIcon(hiddenFieldMetadataItem.icon)}
|
||||
text={hiddenFieldMetadataItem.label}
|
||||
/>
|
||||
</SelectableListItem>
|
||||
))}
|
||||
</DropdownMenuItemsContainer>
|
||||
</SelectableList>
|
||||
<DropdownMenuHeader
|
||||
onClick={() =>
|
||||
setIsRecordSortDirectionMenuUnfolded(
|
||||
!isRecordSortDirectionMenuUnfolded,
|
||||
)
|
||||
}
|
||||
EndComponent={
|
||||
<StyledDropdownMenuHeaderEndComponent>
|
||||
<IconChevronDown size={theme.icon.size.md} />
|
||||
</StyledDropdownMenuHeaderEndComponent>
|
||||
}
|
||||
>
|
||||
{selectedRecordSortDirection === 'asc'
|
||||
? t`Ascending`
|
||||
: t`Descending`}
|
||||
</DropdownMenuHeader>
|
||||
<StyledInput
|
||||
autoFocus
|
||||
value={objectSortDropdownSearchInput}
|
||||
placeholder={t`Search fields`}
|
||||
onChange={(event) =>
|
||||
setObjectSortDropdownSearchInput(event.target.value)
|
||||
}
|
||||
/>
|
||||
<DropdownMenuItemsContainer scrollable={false}>
|
||||
{visibleFieldMetadataItems.map(
|
||||
(visibleFieldMetadataItem, index) => (
|
||||
<SelectableListItem
|
||||
key={visibleFieldMetadataItem.id}
|
||||
itemId={visibleFieldMetadataItem.id}
|
||||
onEnter={() => handleAddSort(visibleFieldMetadataItem)}
|
||||
>
|
||||
<MenuItem
|
||||
focused={selectedItemId === visibleFieldMetadataItem.id}
|
||||
testId={`visible-select-sort-${index}`}
|
||||
onClick={() => handleAddSort(visibleFieldMetadataItem)}
|
||||
LeftIcon={getIcon(visibleFieldMetadataItem.icon)}
|
||||
text={visibleFieldMetadataItem.label}
|
||||
/>
|
||||
</SelectableListItem>
|
||||
),
|
||||
)}
|
||||
{shouldShowSeparator && <DropdownMenuSeparator />}
|
||||
{hiddenFieldMetadataItems.map(
|
||||
(hiddenFieldMetadataItem, index) => (
|
||||
<SelectableListItem
|
||||
key={hiddenFieldMetadataItem.id}
|
||||
itemId={hiddenFieldMetadataItem.id}
|
||||
onEnter={() => handleAddSort(hiddenFieldMetadataItem)}
|
||||
>
|
||||
<MenuItem
|
||||
focused={selectedItemId === hiddenFieldMetadataItem.id}
|
||||
testId={`hidden-select-sort-${index}`}
|
||||
onClick={() => handleAddSort(hiddenFieldMetadataItem)}
|
||||
LeftIcon={getIcon(hiddenFieldMetadataItem.icon)}
|
||||
text={hiddenFieldMetadataItem.label}
|
||||
/>
|
||||
</SelectableListItem>
|
||||
),
|
||||
)}
|
||||
</DropdownMenuItemsContainer>
|
||||
</SelectableList>
|
||||
</DropdownContent>
|
||||
}
|
||||
onClose={handleDropdownButtonClose}
|
||||
/>
|
||||
|
||||
@ -2,7 +2,7 @@ import styled from '@emotion/styled';
|
||||
import { useCallback, useRef } from 'react';
|
||||
|
||||
import { useRecordGroupActions } from '@/object-record/record-group/hooks/useRecordGroupActions';
|
||||
import { DropdownMenu } from '@/ui/layout/dropdown/components/DropdownMenu';
|
||||
import { DropdownContent } from '@/ui/layout/dropdown/components/DropdownContent';
|
||||
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
|
||||
import { OverlayContainer } from '@/ui/layout/overlay/components/OverlayContainer';
|
||||
import { useListenClickOutside } from '@/ui/utilities/pointer-event/hooks/useListenClickOutside';
|
||||
@ -22,7 +22,6 @@ type RecordBoardColumnDropdownMenuProps = {
|
||||
stageId: string;
|
||||
};
|
||||
|
||||
// TODO: unify and use Dropdown component
|
||||
export const RecordBoardColumnDropdownMenu = ({
|
||||
onClose,
|
||||
}: RecordBoardColumnDropdownMenuProps) => {
|
||||
@ -45,7 +44,7 @@ export const RecordBoardColumnDropdownMenu = ({
|
||||
return (
|
||||
<StyledMenuContainer ref={boardColumnMenuRef}>
|
||||
<OverlayContainer>
|
||||
<DropdownMenu data-select-disable>
|
||||
<DropdownContent selectDisabled>
|
||||
<DropdownMenuItemsContainer>
|
||||
{recordGroupActions.map((action) => (
|
||||
<MenuItem
|
||||
@ -59,7 +58,7 @@ export const RecordBoardColumnDropdownMenu = ({
|
||||
/>
|
||||
))}
|
||||
</DropdownMenuItemsContainer>
|
||||
</DropdownMenu>
|
||||
</DropdownContent>
|
||||
</OverlayContainer>
|
||||
</StyledMenuContainer>
|
||||
);
|
||||
|
||||
@ -1,5 +1,4 @@
|
||||
import { DROPDOWN_OFFSET_Y } from '@/dropdown/constants/DropdownOffsetY';
|
||||
import { DROPDOWN_WIDTH } from '@/dropdown/constants/DropdownWidth';
|
||||
import { useCurrentContentId } from '@/dropdown/hooks/useCurrentContentId';
|
||||
import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
|
||||
import { RecordBoardColumnHeaderAggregateDropdownComponentInstanceContext } from '@/object-record/record-board/contexts/RecordBoardColumnHeaderAggregateDropdownComponentInstanceContext';
|
||||
@ -46,7 +45,6 @@ export const RecordBoardColumnHeaderAggregateDropdown = ({
|
||||
dropdownHotkeyScope={{
|
||||
scope: RecordBoardColumnHotkeyScope.ColumnHeader,
|
||||
}}
|
||||
dropdownWidth={DROPDOWN_WIDTH}
|
||||
dropdownOffset={{ y: DROPDOWN_OFFSET_Y }}
|
||||
clickableComponent={
|
||||
<RecordBoardColumnHeaderAggregateDropdownButton
|
||||
|
||||
@ -4,12 +4,13 @@ import { aggregateOperationComponentState } from '@/object-record/record-board/r
|
||||
import { availableFieldIdsForAggregateOperationComponentState } from '@/object-record/record-board/record-board-column/states/availableFieldIdsForAggregateOperationComponentState';
|
||||
import { getAggregateOperationLabel } from '@/object-record/record-board/record-board-column/utils/getAggregateOperationLabel';
|
||||
import { recordIndexKanbanAggregateOperationState } from '@/object-record/record-index/states/recordIndexKanbanAggregateOperationState';
|
||||
import { DropdownContent } from '@/ui/layout/dropdown/components/DropdownContent';
|
||||
import { DropdownMenuHeader } from '@/ui/layout/dropdown/components/DropdownMenuHeader/DropdownMenuHeader';
|
||||
import { DropdownMenuHeaderLeftComponent } from '@/ui/layout/dropdown/components/DropdownMenuHeader/internal/DropdownMenuHeaderLeftComponent';
|
||||
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
|
||||
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
|
||||
import { useUpdateViewAggregate } from '@/views/hooks/useUpdateViewAggregate';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
import { DropdownMenuHeaderLeftComponent } from '@/ui/layout/dropdown/components/DropdownMenuHeader/internal/DropdownMenuHeaderLeftComponent';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import {
|
||||
Icon123,
|
||||
@ -48,7 +49,7 @@ export const RecordBoardColumnHeaderAggregateDropdownFieldsContent = () => {
|
||||
|
||||
if (!isDefined(aggregateOperation)) return <></>;
|
||||
return (
|
||||
<>
|
||||
<DropdownContent>
|
||||
<DropdownMenuHeader
|
||||
StartComponent={
|
||||
<DropdownMenuHeaderLeftComponent
|
||||
@ -94,6 +95,6 @@ export const RecordBoardColumnHeaderAggregateDropdownFieldsContent = () => {
|
||||
);
|
||||
})}
|
||||
</DropdownMenuItemsContainer>
|
||||
</>
|
||||
</DropdownContent>
|
||||
);
|
||||
};
|
||||
|
||||
@ -7,6 +7,7 @@ import {
|
||||
} from '@/object-record/record-board/record-board-column/components/RecordBoardColumnHeaderAggregateDropdownContext';
|
||||
|
||||
import { TableOptionsHotkeyScope } from '@/object-record/record-table/types/TableOptionsHotkeyScope';
|
||||
import { DropdownContent } from '@/ui/layout/dropdown/components/DropdownContent';
|
||||
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
|
||||
import { useScopedHotkeys } from '@/ui/utilities/hotkey/hooks/useScopedHotkeys';
|
||||
import { useLingui } from '@lingui/react/macro';
|
||||
@ -29,7 +30,7 @@ export const RecordBoardColumnHeaderAggregateDropdownMenuContent = () => {
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
<DropdownContent>
|
||||
<DropdownMenuItemsContainer>
|
||||
<MenuItem
|
||||
onClick={() => {
|
||||
@ -60,6 +61,6 @@ export const RecordBoardColumnHeaderAggregateDropdownMenuContent = () => {
|
||||
hasSubMenu
|
||||
/>
|
||||
</DropdownMenuItemsContainer>
|
||||
</>
|
||||
</DropdownContent>
|
||||
);
|
||||
};
|
||||
|
||||
@ -12,7 +12,9 @@ import { AGGREGATE_OPERATIONS } from '@/object-record/record-table/constants/Agg
|
||||
import { ExtendedAggregateOperations } from '@/object-record/record-table/types/ExtendedAggregateOperations';
|
||||
import { TableOptionsHotkeyScope } from '@/object-record/record-table/types/TableOptionsHotkeyScope';
|
||||
import { AvailableFieldsForAggregateOperation } from '@/object-record/types/AvailableFieldsForAggregateOperation';
|
||||
import { DropdownContent } from '@/ui/layout/dropdown/components/DropdownContent';
|
||||
import { DropdownMenuHeader } from '@/ui/layout/dropdown/components/DropdownMenuHeader/DropdownMenuHeader';
|
||||
import { DropdownMenuHeaderLeftComponent } from '@/ui/layout/dropdown/components/DropdownMenuHeader/internal/DropdownMenuHeaderLeftComponent';
|
||||
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
|
||||
import { useScopedHotkeys } from '@/ui/utilities/hotkey/hooks/useScopedHotkeys';
|
||||
import { useSetRecoilComponentStateV2 } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentStateV2';
|
||||
@ -20,7 +22,6 @@ import { useUpdateViewAggregate } from '@/views/hooks/useUpdateViewAggregate';
|
||||
import isEmpty from 'lodash.isempty';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
import { Key } from 'ts-key-enum';
|
||||
import { DropdownMenuHeaderLeftComponent } from '@/ui/layout/dropdown/components/DropdownMenuHeader/internal/DropdownMenuHeaderLeftComponent';
|
||||
import { IconCheck, IconChevronLeft } from 'twenty-ui/display';
|
||||
|
||||
export const RecordBoardColumnHeaderAggregateDropdownOptionsContent = ({
|
||||
@ -58,7 +59,7 @@ export const RecordBoardColumnHeaderAggregateDropdownOptionsContent = ({
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
<DropdownContent>
|
||||
<DropdownMenuHeader
|
||||
StartComponent={
|
||||
<DropdownMenuHeaderLeftComponent
|
||||
@ -121,6 +122,6 @@ export const RecordBoardColumnHeaderAggregateDropdownOptionsContent = ({
|
||||
),
|
||||
)}
|
||||
</DropdownMenuItemsContainer>
|
||||
</>
|
||||
</DropdownContent>
|
||||
);
|
||||
};
|
||||
|
||||
@ -7,7 +7,7 @@ import {
|
||||
} from '@/object-record/record-field/meta-types/input/components/MultiItemBaseInput';
|
||||
import { FieldInputClickOutsideEvent } from '@/object-record/record-field/types/FieldInputEvent';
|
||||
import { PhoneRecord } from '@/object-record/record-field/types/FieldMetadata';
|
||||
import { DropdownMenu } from '@/ui/layout/dropdown/components/DropdownMenu';
|
||||
import { DropdownContent } from '@/ui/layout/dropdown/components/DropdownContent';
|
||||
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
|
||||
import { DropdownMenuSeparator } from '@/ui/layout/dropdown/components/DropdownMenuSeparator';
|
||||
import { useScopedHotkeys } from '@/ui/utilities/hotkey/hooks/useScopedHotkeys';
|
||||
@ -181,7 +181,7 @@ export const MultiItemFieldInput = <T,>({
|
||||
};
|
||||
|
||||
return (
|
||||
<DropdownMenu ref={containerRef} width={200}>
|
||||
<DropdownContent ref={containerRef}>
|
||||
{!!items.length && (
|
||||
<>
|
||||
<DropdownMenuItemsContainer>
|
||||
@ -232,6 +232,6 @@ export const MultiItemFieldInput = <T,>({
|
||||
/>
|
||||
</DropdownMenuItemsContainer>
|
||||
)}
|
||||
</DropdownMenu>
|
||||
</DropdownContent>
|
||||
);
|
||||
};
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
import { DropdownContent } from '@/ui/layout/dropdown/components/DropdownContent';
|
||||
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
|
||||
import { useDropdown } from '@/ui/layout/dropdown/hooks/useDropdown';
|
||||
import { MenuItemWithOptionDropdown } from '@/ui/navigation/menu-item/components/MenuItemWithOptionDropdown';
|
||||
@ -73,26 +74,28 @@ export const MultiItemFieldMenuItem = <T,>({
|
||||
RightIcon={!isHovered && showPrimaryIcon ? IconBookmark : null}
|
||||
dropdownId={dropdownId}
|
||||
dropdownContent={
|
||||
<DropdownMenuItemsContainer>
|
||||
{showSetAsPrimaryButton && (
|
||||
<DropdownContent>
|
||||
<DropdownMenuItemsContainer>
|
||||
{showSetAsPrimaryButton && (
|
||||
<MenuItem
|
||||
LeftIcon={IconBookmarkPlus}
|
||||
text="Set as Primary"
|
||||
onClick={handleSetAsPrimaryClick}
|
||||
/>
|
||||
)}
|
||||
<MenuItem
|
||||
LeftIcon={IconBookmarkPlus}
|
||||
text="Set as Primary"
|
||||
onClick={handleSetAsPrimaryClick}
|
||||
LeftIcon={IconPencil}
|
||||
text="Edit"
|
||||
onClick={handleEditClick}
|
||||
/>
|
||||
)}
|
||||
<MenuItem
|
||||
LeftIcon={IconPencil}
|
||||
text="Edit"
|
||||
onClick={handleEditClick}
|
||||
/>
|
||||
<MenuItem
|
||||
accent="danger"
|
||||
LeftIcon={IconTrash}
|
||||
text="Delete"
|
||||
onClick={handleDeleteClick}
|
||||
/>
|
||||
</DropdownMenuItemsContainer>
|
||||
<MenuItem
|
||||
accent="danger"
|
||||
LeftIcon={IconTrash}
|
||||
text="Delete"
|
||||
onClick={handleDeleteClick}
|
||||
/>
|
||||
</DropdownMenuItemsContainer>
|
||||
</DropdownContent>
|
||||
}
|
||||
/>
|
||||
);
|
||||
|
||||
@ -1,99 +0,0 @@
|
||||
import { recordGroupFieldMetadataComponentState } from '@/object-record/record-group/states/recordGroupFieldMetadataComponentState';
|
||||
import { availableRecordGroupIdsComponentSelector } from '@/object-record/record-group/states/selectors/availableRecordGroupIdsComponentSelector';
|
||||
import { RecordGroupDefinition } from '@/object-record/record-group/types/RecordGroupDefinition';
|
||||
import { RecordIndexPageKanbanAddMenuItem } from '@/object-record/record-index/components/RecordIndexPageKanbanAddMenuItem';
|
||||
import { useRecordIndexContextOrThrow } from '@/object-record/record-index/contexts/RecordIndexContext';
|
||||
import { useCreateNewIndexRecord } from '@/object-record/record-table/hooks/useCreateNewIndexRecord';
|
||||
import { isRecordGroupTableSectionToggledComponentState } from '@/object-record/record-table/record-table-section/states/isRecordGroupTableSectionToggledComponentState';
|
||||
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 { useSetActiveDropdownFocusIdAndMemorizePrevious } from '@/ui/layout/dropdown/hooks/useSetFocusedDropdownIdAndMemorizePrevious';
|
||||
import { useRecoilComponentCallbackStateV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentCallbackStateV2';
|
||||
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
|
||||
import { useRecoilCallback } from 'recoil';
|
||||
|
||||
type RecordIndexAddRecordInGroupDropdownProps = {
|
||||
dropdownId: string;
|
||||
clickableComponent: React.ReactNode;
|
||||
};
|
||||
|
||||
export const RecordIndexAddRecordInGroupDropdown = ({
|
||||
dropdownId,
|
||||
clickableComponent,
|
||||
}: RecordIndexAddRecordInGroupDropdownProps) => {
|
||||
const { objectMetadataItem } = useRecordIndexContextOrThrow();
|
||||
|
||||
const { setActiveDropdownFocusIdAndMemorizePrevious } =
|
||||
useSetActiveDropdownFocusIdAndMemorizePrevious();
|
||||
|
||||
const recordGroupIds = useRecoilComponentValueV2(
|
||||
availableRecordGroupIdsComponentSelector,
|
||||
);
|
||||
|
||||
const recordGroupFieldMetadata = useRecoilComponentValueV2(
|
||||
recordGroupFieldMetadataComponentState,
|
||||
);
|
||||
|
||||
const isRecordGroupTableSectionToggledState =
|
||||
useRecoilComponentCallbackStateV2(
|
||||
isRecordGroupTableSectionToggledComponentState,
|
||||
);
|
||||
|
||||
const selectFieldMetadataItem = objectMetadataItem.fields.find(
|
||||
(field) => field.id === recordGroupFieldMetadata?.id,
|
||||
);
|
||||
|
||||
const { closeDropdown } = useDropdown(dropdownId);
|
||||
|
||||
const { createNewIndexRecord } = useCreateNewIndexRecord({
|
||||
objectMetadataItem,
|
||||
});
|
||||
|
||||
const handleCreateNewTableRecordInGroup = useRecoilCallback(
|
||||
({ set }) =>
|
||||
(recordGroup: RecordGroupDefinition) => {
|
||||
set(isRecordGroupTableSectionToggledState(recordGroup.id), true);
|
||||
setActiveDropdownFocusIdAndMemorizePrevious(null);
|
||||
if (!selectFieldMetadataItem) {
|
||||
return;
|
||||
}
|
||||
createNewIndexRecord({
|
||||
[selectFieldMetadataItem.name]: recordGroup.value,
|
||||
});
|
||||
closeDropdown();
|
||||
},
|
||||
[
|
||||
isRecordGroupTableSectionToggledState,
|
||||
setActiveDropdownFocusIdAndMemorizePrevious,
|
||||
selectFieldMetadataItem,
|
||||
createNewIndexRecord,
|
||||
closeDropdown,
|
||||
],
|
||||
);
|
||||
|
||||
if (!selectFieldMetadataItem) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<Dropdown
|
||||
dropdownWidth="200px"
|
||||
dropdownPlacement="bottom-start"
|
||||
clickableComponent={clickableComponent}
|
||||
dropdownId={dropdownId}
|
||||
dropdownComponents={
|
||||
<DropdownMenuItemsContainer>
|
||||
{recordGroupIds.map((recordGroupId) => (
|
||||
<RecordIndexPageKanbanAddMenuItem
|
||||
key={recordGroupId}
|
||||
columnId={recordGroupId}
|
||||
onItemClick={handleCreateNewTableRecordInGroup}
|
||||
/>
|
||||
))}
|
||||
</DropdownMenuItemsContainer>
|
||||
}
|
||||
dropdownHotkeyScope={{ scope: dropdownId }}
|
||||
/>
|
||||
);
|
||||
};
|
||||
@ -9,7 +9,7 @@ import { RecordPickerLayoutDirection } from '@/object-record/record-picker/types
|
||||
import { RecordPickerPickableMorphItem } from '@/object-record/record-picker/types/RecordPickerPickableMorphItem';
|
||||
import { useHasObjectReadOnlyPermission } from '@/settings/roles/hooks/useHasObjectReadOnlyPermission';
|
||||
import { CreateNewButton } from '@/ui/input/relation-picker/components/CreateNewButton';
|
||||
import { DropdownMenu } from '@/ui/layout/dropdown/components/DropdownMenu';
|
||||
import { DropdownContent } from '@/ui/layout/dropdown/components/DropdownContent';
|
||||
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
|
||||
import { SelectableListItem } from '@/ui/layout/selectable-list/components/SelectableListItem';
|
||||
import { useSelectableList } from '@/ui/layout/selectable-list/hooks/useSelectableList';
|
||||
@ -110,7 +110,7 @@ export const MultipleRecordPicker = ({
|
||||
containerRef={containerRef}
|
||||
onClickOutside={onClickOutside}
|
||||
/>
|
||||
<DropdownMenu ref={containerRef} data-select-disable width={200}>
|
||||
<DropdownContent ref={containerRef}>
|
||||
{layoutDirection === 'search-bar-on-bottom' && (
|
||||
<>
|
||||
{createNewButtonSection}
|
||||
@ -124,7 +124,7 @@ export const MultipleRecordPicker = ({
|
||||
{createNewButtonSection}
|
||||
</>
|
||||
)}
|
||||
</DropdownMenu>
|
||||
</DropdownContent>
|
||||
</MultipleRecordPickerComponentInstanceContext.Provider>
|
||||
);
|
||||
};
|
||||
|
||||
@ -5,7 +5,7 @@ import {
|
||||
SingleRecordPickerMenuItemsWithSearchProps,
|
||||
} from '@/object-record/record-picker/single-record-picker/components/SingleRecordPickerMenuItemsWithSearch';
|
||||
import { SingleRecordPickerComponentInstanceContext } from '@/object-record/record-picker/single-record-picker/states/contexts/SingleRecordPickerComponentInstanceContext';
|
||||
import { DropdownMenu } from '@/ui/layout/dropdown/components/DropdownMenu';
|
||||
import { DropdownContent } from '@/ui/layout/dropdown/components/DropdownContent';
|
||||
import { useListenClickOutside } from '@/ui/utilities/pointer-event/hooks/useListenClickOutside';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
@ -49,7 +49,7 @@ export const SingleRecordPicker = ({
|
||||
<SingleRecordPickerComponentInstanceContext.Provider
|
||||
value={{ instanceId: componentInstanceId }}
|
||||
>
|
||||
<DropdownMenu ref={containerRef} data-select-disable>
|
||||
<DropdownContent ref={containerRef}>
|
||||
<SingleRecordPickerMenuItemsWithSearch
|
||||
{...{
|
||||
EmptyIcon,
|
||||
@ -62,7 +62,7 @@ export const SingleRecordPicker = ({
|
||||
layoutDirection,
|
||||
}}
|
||||
/>
|
||||
</DropdownMenu>
|
||||
</DropdownContent>
|
||||
</SingleRecordPickerComponentInstanceContext.Provider>
|
||||
);
|
||||
};
|
||||
|
||||
@ -29,6 +29,7 @@ import { getForeignKeyNameFromRelationFieldName } from '@/object-record/utils/ge
|
||||
import { getRecordFieldInputId } from '@/object-record/utils/getRecordFieldInputId';
|
||||
import { isFieldCellSupported } from '@/object-record/utils/isFieldCellSupported';
|
||||
import { Dropdown } from '@/ui/layout/dropdown/components/Dropdown';
|
||||
import { DropdownContent } from '@/ui/layout/dropdown/components/DropdownContent';
|
||||
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
|
||||
import { useDropdown } from '@/ui/layout/dropdown/hooks/useDropdown';
|
||||
import { DropdownScope } from '@/ui/layout/dropdown/scopes/DropdownScope';
|
||||
@ -246,21 +247,23 @@ export const RecordDetailRelationRecordsListItem = ({
|
||||
/>
|
||||
}
|
||||
dropdownComponents={
|
||||
<DropdownMenuItemsContainer>
|
||||
<MenuItem
|
||||
LeftIcon={IconUnlink}
|
||||
text="Detach"
|
||||
onClick={handleDetach}
|
||||
/>
|
||||
{!isAccountOwnerRelation && (
|
||||
<DropdownContent>
|
||||
<DropdownMenuItemsContainer>
|
||||
<MenuItem
|
||||
LeftIcon={IconTrash}
|
||||
text="Delete"
|
||||
accent="danger"
|
||||
onClick={handleDelete}
|
||||
LeftIcon={IconUnlink}
|
||||
text="Detach"
|
||||
onClick={handleDetach}
|
||||
/>
|
||||
)}
|
||||
</DropdownMenuItemsContainer>
|
||||
{!isAccountOwnerRelation && (
|
||||
<MenuItem
|
||||
LeftIcon={IconTrash}
|
||||
text="Delete"
|
||||
accent="danger"
|
||||
onClick={handleDelete}
|
||||
/>
|
||||
)}
|
||||
</DropdownMenuItemsContainer>
|
||||
</DropdownContent>
|
||||
}
|
||||
dropdownHotkeyScope={{ scope: dropdownScopeId }}
|
||||
/>
|
||||
|
||||
@ -2,13 +2,14 @@ import { RecordTableColumnAggregateFooterAggregateOperationMenuItems } from '@/o
|
||||
import { RecordTableColumnAggregateFooterDropdownContext } from '@/object-record/record-table/record-table-footer/components/RecordTableColumnAggregateFooterDropdownContext';
|
||||
import { ExtendedAggregateOperations } from '@/object-record/record-table/types/ExtendedAggregateOperations';
|
||||
import { TableOptionsHotkeyScope } from '@/object-record/record-table/types/TableOptionsHotkeyScope';
|
||||
import { DropdownContent } from '@/ui/layout/dropdown/components/DropdownContent';
|
||||
import { DropdownMenuHeader } from '@/ui/layout/dropdown/components/DropdownMenuHeader/DropdownMenuHeader';
|
||||
import { DropdownMenuHeaderLeftComponent } from '@/ui/layout/dropdown/components/DropdownMenuHeader/internal/DropdownMenuHeaderLeftComponent';
|
||||
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
|
||||
import { useDropdown } from '@/ui/layout/dropdown/hooks/useDropdown';
|
||||
import { useScopedHotkeys } from '@/ui/utilities/hotkey/hooks/useScopedHotkeys';
|
||||
import { useContext } from 'react';
|
||||
import { Key } from 'ts-key-enum';
|
||||
import { DropdownMenuHeaderLeftComponent } from '@/ui/layout/dropdown/components/DropdownMenuHeader/internal/DropdownMenuHeaderLeftComponent';
|
||||
import { IconChevronLeft } from 'twenty-ui/display';
|
||||
|
||||
export const RecordTableColumnAggregateFooterDropdownSubmenuContent = ({
|
||||
@ -32,7 +33,7 @@ export const RecordTableColumnAggregateFooterDropdownSubmenuContent = ({
|
||||
TableOptionsHotkeyScope.Dropdown,
|
||||
);
|
||||
return (
|
||||
<>
|
||||
<DropdownContent>
|
||||
<DropdownMenuHeader
|
||||
StartComponent={
|
||||
<DropdownMenuHeaderLeftComponent
|
||||
@ -48,6 +49,6 @@ export const RecordTableColumnAggregateFooterDropdownSubmenuContent = ({
|
||||
aggregateOperations={aggregateOperations}
|
||||
/>
|
||||
</DropdownMenuItemsContainer>
|
||||
</>
|
||||
</DropdownContent>
|
||||
);
|
||||
};
|
||||
|
||||
@ -5,16 +5,17 @@ import { NON_STANDARD_AGGREGATE_OPERATION_OPTIONS } from '@/object-record/record
|
||||
import { useViewFieldAggregateOperation } from '@/object-record/record-table/record-table-footer/hooks/useViewFieldAggregateOperation';
|
||||
import { getAvailableAggregateOperationsForFieldMetadataType } from '@/object-record/record-table/record-table-footer/utils/getAvailableAggregateOperationsForFieldMetadataType';
|
||||
import { TableOptionsHotkeyScope } from '@/object-record/record-table/types/TableOptionsHotkeyScope';
|
||||
import { DropdownContent } from '@/ui/layout/dropdown/components/DropdownContent';
|
||||
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
|
||||
import { useDropdown } from '@/ui/layout/dropdown/hooks/useDropdown';
|
||||
import { useScopedHotkeys } from '@/ui/utilities/hotkey/hooks/useScopedHotkeys';
|
||||
import { t } from '@lingui/core/macro';
|
||||
import { useContext, useMemo } from 'react';
|
||||
import { Key } from 'ts-key-enum';
|
||||
import { FieldMetadataType } from '~/generated-metadata/graphql';
|
||||
import { isDefined, isFieldMetadataDateKind } from 'twenty-shared/utils';
|
||||
import { IconCheck } from 'twenty-ui/display';
|
||||
import { MenuItem } from 'twenty-ui/navigation';
|
||||
import { FieldMetadataType } from '~/generated-metadata/graphql';
|
||||
|
||||
export const RecordTableColumnAggregateFooterMenuContent = () => {
|
||||
const {
|
||||
@ -64,7 +65,7 @@ export const RecordTableColumnAggregateFooterMenuContent = () => {
|
||||
} = useViewFieldAggregateOperation();
|
||||
|
||||
return (
|
||||
<>
|
||||
<DropdownContent>
|
||||
<DropdownMenuItemsContainer>
|
||||
<MenuItem
|
||||
onClick={() => {
|
||||
@ -116,6 +117,6 @@ export const RecordTableColumnAggregateFooterMenuContent = () => {
|
||||
aria-selected={!isDefined(currentViewFieldAggregateOperation)}
|
||||
/>
|
||||
</DropdownMenuItemsContainer>
|
||||
</>
|
||||
</DropdownContent>
|
||||
);
|
||||
};
|
||||
|
||||
@ -6,6 +6,7 @@ import { useDropdown } from '@/ui/layout/dropdown/hooks/useDropdown';
|
||||
import { useOpenRecordFilterChipFromTableHeader } from '@/object-record/record-table/record-table-header/hooks/useOpenRecordFilterChipFromTableHeader';
|
||||
import { onToggleColumnSortComponentState } from '@/object-record/record-table/states/onToggleColumnSortComponentState';
|
||||
import { visibleTableColumnsComponentSelector } from '@/object-record/record-table/states/selectors/visibleTableColumnsComponentSelector';
|
||||
import { DropdownContent } from '@/ui/layout/dropdown/components/DropdownContent';
|
||||
import { useToggleScrollWrapper } from '@/ui/utilities/scroll/hooks/useToggleScrollWrapper';
|
||||
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
|
||||
import styled from '@emotion/styled';
|
||||
@ -107,43 +108,45 @@ export const RecordTableColumnHeadDropdownMenu = ({
|
||||
const canHide = column.isLabelIdentifier !== true;
|
||||
|
||||
return (
|
||||
<StyledDropdownMenuItemsContainer>
|
||||
{isFilterable && (
|
||||
<MenuItem
|
||||
LeftIcon={IconFilter}
|
||||
onClick={handleFilterClick}
|
||||
text={t`Filter`}
|
||||
/>
|
||||
)}
|
||||
{isSortable && (
|
||||
<MenuItem
|
||||
LeftIcon={IconSortDescending}
|
||||
onClick={handleSortClick}
|
||||
text={t`Sort`}
|
||||
/>
|
||||
)}
|
||||
{showSeparator && <DropdownMenuSeparator />}
|
||||
{canMoveLeft && (
|
||||
<MenuItem
|
||||
LeftIcon={IconArrowLeft}
|
||||
onClick={handleColumnMoveLeft}
|
||||
text={t`Move left`}
|
||||
/>
|
||||
)}
|
||||
{canMoveRight && (
|
||||
<MenuItem
|
||||
LeftIcon={IconArrowRight}
|
||||
onClick={handleColumnMoveRight}
|
||||
text={t`Move right`}
|
||||
/>
|
||||
)}
|
||||
{canHide && (
|
||||
<MenuItem
|
||||
LeftIcon={IconEyeOff}
|
||||
onClick={handleColumnVisibility}
|
||||
text={t`Hide`}
|
||||
/>
|
||||
)}
|
||||
</StyledDropdownMenuItemsContainer>
|
||||
<DropdownContent>
|
||||
<StyledDropdownMenuItemsContainer>
|
||||
{isFilterable && (
|
||||
<MenuItem
|
||||
LeftIcon={IconFilter}
|
||||
onClick={handleFilterClick}
|
||||
text={t`Filter`}
|
||||
/>
|
||||
)}
|
||||
{isSortable && (
|
||||
<MenuItem
|
||||
LeftIcon={IconSortDescending}
|
||||
onClick={handleSortClick}
|
||||
text={t`Sort`}
|
||||
/>
|
||||
)}
|
||||
{showSeparator && <DropdownMenuSeparator />}
|
||||
{canMoveLeft && (
|
||||
<MenuItem
|
||||
LeftIcon={IconArrowLeft}
|
||||
onClick={handleColumnMoveLeft}
|
||||
text={t`Move left`}
|
||||
/>
|
||||
)}
|
||||
{canMoveRight && (
|
||||
<MenuItem
|
||||
LeftIcon={IconArrowRight}
|
||||
onClick={handleColumnMoveRight}
|
||||
text={t`Move right`}
|
||||
/>
|
||||
)}
|
||||
{canHide && (
|
||||
<MenuItem
|
||||
LeftIcon={IconEyeOff}
|
||||
onClick={handleColumnVisibility}
|
||||
text={t`Hide`}
|
||||
/>
|
||||
)}
|
||||
</StyledDropdownMenuItemsContainer>
|
||||
</DropdownContent>
|
||||
);
|
||||
};
|
||||
|
||||
@ -8,15 +8,16 @@ import { useTableColumns } from '@/object-record/record-table/hooks/useTableColu
|
||||
import { hiddenTableColumnsComponentSelector } from '@/object-record/record-table/states/selectors/hiddenTableColumnsComponentSelector';
|
||||
import { ColumnDefinition } from '@/object-record/record-table/types/ColumnDefinition';
|
||||
import { SettingsPath } from '@/types/SettingsPath';
|
||||
import { DropdownContent } from '@/ui/layout/dropdown/components/DropdownContent';
|
||||
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
|
||||
import { DropdownMenuSeparator } from '@/ui/layout/dropdown/components/DropdownMenuSeparator';
|
||||
import { useDropdown } from '@/ui/layout/dropdown/hooks/useDropdown';
|
||||
import { navigationMemorizedUrlState } from '@/ui/navigation/states/navigationMemorizedUrlState';
|
||||
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
|
||||
import { getSettingsPath } from '~/utils/navigation/getSettingsPath';
|
||||
import { useLingui } from '@lingui/react/macro';
|
||||
import { IconSettings, useIcons } from 'twenty-ui/display';
|
||||
import { MenuItem, UndecoratedLink } from 'twenty-ui/navigation';
|
||||
import { getSettingsPath } from '~/utils/navigation/getSettingsPath';
|
||||
|
||||
export const RecordTableHeaderPlusButtonContent = () => {
|
||||
const { t } = useLingui();
|
||||
@ -45,7 +46,7 @@ export const RecordTableHeaderPlusButtonContent = () => {
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
<DropdownContent>
|
||||
<DropdownMenuItemsContainer>
|
||||
{hiddenTableColumns.map((column) => (
|
||||
<MenuItem
|
||||
@ -70,6 +71,6 @@ export const RecordTableHeaderPlusButtonContent = () => {
|
||||
<MenuItem LeftIcon={IconSettings} text={t`Customize fields`} />
|
||||
</UndecoratedLink>
|
||||
</DropdownMenuItemsContainer>
|
||||
</>
|
||||
</DropdownContent>
|
||||
);
|
||||
};
|
||||
|
||||
@ -3,6 +3,7 @@ import { Key } from 'ts-key-enum';
|
||||
import { StyledMultipleSelectDropdownAvatarChip } from '@/object-record/select/components/StyledMultipleSelectDropdownAvatarChip';
|
||||
import { SelectableItem } from '@/object-record/select/types/SelectableItem';
|
||||
import { DropdownMenuSkeletonItem } from '@/ui/input/relation-picker/components/skeletons/DropdownMenuSkeletonItem';
|
||||
import { DropdownContent } from '@/ui/layout/dropdown/components/DropdownContent';
|
||||
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
|
||||
import { useDropdown } from '@/ui/layout/dropdown/hooks/useDropdown';
|
||||
import { SelectableList } from '@/ui/layout/selectable-list/components/SelectableList';
|
||||
@ -80,47 +81,49 @@ export const MultipleSelectDropdown = ({
|
||||
const selectableItemIds = itemsInDropdown.map((item) => item.id);
|
||||
|
||||
return (
|
||||
<SelectableList
|
||||
selectableListInstanceId={selectableListId}
|
||||
selectableItemIdArray={selectableItemIds}
|
||||
hotkeyScope={hotkeyScope}
|
||||
>
|
||||
<DropdownMenuItemsContainer hasMaxHeight width="auto">
|
||||
{itemsInDropdown?.map((item) => {
|
||||
return (
|
||||
<SelectableListItem
|
||||
itemId={item.id}
|
||||
onEnter={() => {
|
||||
resetSelectedItem();
|
||||
handleItemSelectChange(item, !item.isSelected);
|
||||
}}
|
||||
>
|
||||
<MenuItemMultiSelectAvatar
|
||||
key={item.id}
|
||||
selected={item.isSelected}
|
||||
isKeySelected={item.id === selectedItemId}
|
||||
onSelectChange={(newCheckedValue) => {
|
||||
<DropdownContent>
|
||||
<SelectableList
|
||||
selectableListInstanceId={selectableListId}
|
||||
selectableItemIdArray={selectableItemIds}
|
||||
hotkeyScope={hotkeyScope}
|
||||
>
|
||||
<DropdownMenuItemsContainer hasMaxHeight width="auto">
|
||||
{itemsInDropdown?.map((item) => {
|
||||
return (
|
||||
<SelectableListItem
|
||||
itemId={item.id}
|
||||
onEnter={() => {
|
||||
resetSelectedItem();
|
||||
handleItemSelectChange(item, newCheckedValue);
|
||||
handleItemSelectChange(item, !item.isSelected);
|
||||
}}
|
||||
avatar={
|
||||
<StyledMultipleSelectDropdownAvatarChip
|
||||
className="avatar-icon-container"
|
||||
name={item.name}
|
||||
avatarUrl={item.avatarUrl}
|
||||
LeftIcon={item.AvatarIcon}
|
||||
avatarType={item.avatarType}
|
||||
isIconInverted={item.isIconInverted}
|
||||
placeholderColorSeed={item.id}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
</SelectableListItem>
|
||||
);
|
||||
})}
|
||||
{showNoResult && <MenuItem text="No results" />}
|
||||
{loadingItems && <DropdownMenuSkeletonItem />}
|
||||
</DropdownMenuItemsContainer>
|
||||
</SelectableList>
|
||||
>
|
||||
<MenuItemMultiSelectAvatar
|
||||
key={item.id}
|
||||
selected={item.isSelected}
|
||||
isKeySelected={item.id === selectedItemId}
|
||||
onSelectChange={(newCheckedValue) => {
|
||||
resetSelectedItem();
|
||||
handleItemSelectChange(item, newCheckedValue);
|
||||
}}
|
||||
avatar={
|
||||
<StyledMultipleSelectDropdownAvatarChip
|
||||
className="avatar-icon-container"
|
||||
name={item.name}
|
||||
avatarUrl={item.avatarUrl}
|
||||
LeftIcon={item.AvatarIcon}
|
||||
avatarType={item.avatarType}
|
||||
isIconInverted={item.isIconInverted}
|
||||
placeholderColorSeed={item.id}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
</SelectableListItem>
|
||||
);
|
||||
})}
|
||||
{showNoResult && <MenuItem text="No results" />}
|
||||
{loadingItems && <DropdownMenuSkeletonItem />}
|
||||
</DropdownMenuItemsContainer>
|
||||
</SelectableList>
|
||||
</DropdownContent>
|
||||
);
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user