From 8db8d9ad0000524d4cfcabd31cd06b76c4b56301 Mon Sep 17 00:00:00 2001 From: Antoine Moreaux Date: Mon, 17 Mar 2025 16:10:52 +0100 Subject: [PATCH] feat(twenty-front): improve dropdown menu header (#10672) --- ...pdownFilterSelectCompositeFieldSubMenu.tsx | 4 +- .../ObjectFilterDropdownOperandDropdown.tsx | 2 +- .../ObjectOptionsDropdownFieldsContent.tsx | 7 +- ...jectOptionsDropdownHiddenFieldsContent.tsx | 4 +- ...tionsDropdownHiddenRecordGroupsContent.tsx | 4 +- .../ObjectOptionsDropdownLayoutContent.tsx | 7 +- ...jectOptionsDropdownLayoutOpenInContent.tsx | 4 +- .../ObjectOptionsDropdownMenuContent.tsx | 2 +- ...ptionsDropdownRecordGroupFieldsContent.tsx | 4 +- ...tOptionsDropdownRecordGroupSortContent.tsx | 4 +- ...jectOptionsDropdownRecordGroupsContent.tsx | 7 +- .../components/ObjectSortDropdownButton.tsx | 2 +- ...mnHeaderAggregateDropdownFieldsContent.tsx | 4 +- ...nHeaderAggregateDropdownOptionsContent.tsx | 7 +- ...eColumnAggregateDropdownSubmenuContent.tsx | 7 +- .../layout/dropdown/components/Dropdown.tsx | 2 +- .../DropdownMenuHeader.tsx | 114 ++++++++---------- .../internal/DropdownMenuHeaderStartIcon.tsx | 64 ++++++++++ .../DropdownMenuHeaderWithDropdownMenu.tsx | 34 ++++++ .../__stories__/DropdownMenu.stories.tsx | 8 +- .../DropdownMenuHeader.stories.tsx | 81 +++++++++++++ .../ViewPickerContentCreateMode.tsx | 4 +- .../components/ViewPickerContentEditMode.tsx | 7 +- .../WorkflowVariablesDropdownFieldItems.tsx | 4 +- .../WorkflowVariablesDropdownObjectItems.tsx | 4 +- ...flowVariablesDropdownWorkflowStepItems.tsx | 4 +- .../button/components/LightIconButton.tsx | 9 +- 27 files changed, 294 insertions(+), 110 deletions(-) rename packages/twenty-front/src/modules/ui/layout/dropdown/components/{ => DropdownMenuHeader}/DropdownMenuHeader.tsx (50%) create mode 100644 packages/twenty-front/src/modules/ui/layout/dropdown/components/DropdownMenuHeader/internal/DropdownMenuHeaderStartIcon.tsx create mode 100644 packages/twenty-front/src/modules/ui/layout/dropdown/components/DropdownMenuHeader/internal/DropdownMenuHeaderWithDropdownMenu.tsx create mode 100644 packages/twenty-front/src/modules/ui/layout/dropdown/components/__stories__/DropdownMenuHeader.stories.tsx diff --git a/packages/twenty-front/src/modules/object-record/object-filter-dropdown/components/ObjectFilterDropdownFilterSelectCompositeFieldSubMenu.tsx b/packages/twenty-front/src/modules/object-record/object-filter-dropdown/components/ObjectFilterDropdownFilterSelectCompositeFieldSubMenu.tsx index 95f00a1d7..98ec5fd97 100644 --- a/packages/twenty-front/src/modules/object-record/object-filter-dropdown/components/ObjectFilterDropdownFilterSelectCompositeFieldSubMenu.tsx +++ b/packages/twenty-front/src/modules/object-record/object-filter-dropdown/components/ObjectFilterDropdownFilterSelectCompositeFieldSubMenu.tsx @@ -20,7 +20,7 @@ import { currentRecordFiltersComponentState } from '@/object-record/record-filte import { findDuplicateRecordFilterInNonAdvancedRecordFilters } from '@/object-record/record-filter/utils/findDuplicateRecordFilterInNonAdvancedRecordFilters'; import { getRecordFilterOperands } from '@/object-record/record-filter/utils/getRecordFilterOperands'; import { SETTINGS_COMPOSITE_FIELD_TYPE_CONFIGS } from '@/settings/data-model/constants/SettingsCompositeFieldTypeConfigs'; -import { DropdownMenuHeader } from '@/ui/layout/dropdown/components/DropdownMenuHeader'; +import { DropdownMenuHeader } from '@/ui/layout/dropdown/components/DropdownMenuHeader/DropdownMenuHeader'; import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer'; import { useRecoilComponentStateV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentStateV2'; import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2'; @@ -188,7 +188,7 @@ export const ObjectFilterDropdownFilterSelectCompositeFieldSubMenu = () => { <> {getFilterableFieldTypeLabel(objectFilterDropdownSubMenuFieldType)} diff --git a/packages/twenty-front/src/modules/object-record/object-filter-dropdown/components/ObjectFilterDropdownOperandDropdown.tsx b/packages/twenty-front/src/modules/object-record/object-filter-dropdown/components/ObjectFilterDropdownOperandDropdown.tsx index 8a7c27e95..d422773ab 100644 --- a/packages/twenty-front/src/modules/object-record/object-filter-dropdown/components/ObjectFilterDropdownOperandDropdown.tsx +++ b/packages/twenty-front/src/modules/object-record/object-filter-dropdown/components/ObjectFilterDropdownOperandDropdown.tsx @@ -1,6 +1,6 @@ import { IconChevronDown } from 'twenty-ui'; -import { DropdownMenuHeader } from '@/ui/layout/dropdown/components/DropdownMenuHeader'; +import { DropdownMenuHeader } from '@/ui/layout/dropdown/components/DropdownMenuHeader/DropdownMenuHeader'; import { ObjectFilterDropdownOperandSelect } from '@/object-record/object-filter-dropdown/components/ObjectFilterDropdownOperandSelect'; import { selectedOperandInDropdownComponentState } from '@/object-record/object-filter-dropdown/states/selectedOperandInDropdownComponentState'; diff --git a/packages/twenty-front/src/modules/object-record/object-options-dropdown/components/ObjectOptionsDropdownFieldsContent.tsx b/packages/twenty-front/src/modules/object-record/object-options-dropdown/components/ObjectOptionsDropdownFieldsContent.tsx index e2c033120..0c68109fd 100644 --- a/packages/twenty-front/src/modules/object-record/object-options-dropdown/components/ObjectOptionsDropdownFieldsContent.tsx +++ b/packages/twenty-front/src/modules/object-record/object-options-dropdown/components/ObjectOptionsDropdownFieldsContent.tsx @@ -3,7 +3,7 @@ import { IconChevronLeft, IconEyeOff, MenuItemNavigate } from 'twenty-ui'; 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 { DropdownMenuHeader } from '@/ui/layout/dropdown/components/DropdownMenuHeader'; +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'; import { ViewFieldsVisibilityDropdownSection } from '@/views/components/ViewFieldsVisibilityDropdownSection'; @@ -51,7 +51,10 @@ export const ObjectOptionsDropdownFieldsContent = () => { return ( <> - + {t`Fields`} { <> onContentChange('fields')} + onStartIconClick={() => onContentChange('fields')} > {t`Hidden Fields`} diff --git a/packages/twenty-front/src/modules/object-record/object-options-dropdown/components/ObjectOptionsDropdownHiddenRecordGroupsContent.tsx b/packages/twenty-front/src/modules/object-record/object-options-dropdown/components/ObjectOptionsDropdownHiddenRecordGroupsContent.tsx index aaf2447e5..c527bfc9a 100644 --- a/packages/twenty-front/src/modules/object-record/object-options-dropdown/components/ObjectOptionsDropdownHiddenRecordGroupsContent.tsx +++ b/packages/twenty-front/src/modules/object-record/object-options-dropdown/components/ObjectOptionsDropdownHiddenRecordGroupsContent.tsx @@ -14,7 +14,7 @@ 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 { DropdownMenuHeader } from '@/ui/layout/dropdown/components/DropdownMenuHeader'; +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'; import { navigationMemorizedUrlState } from '@/ui/navigation/states/navigationMemorizedUrlState'; @@ -75,7 +75,7 @@ export const ObjectOptionsDropdownHiddenRecordGroupsContent = () => { onContentChange('recordGroups')} + onStartIconClick={() => onContentChange('recordGroups')} > Hidden {recordGroupFieldMetadata?.label} diff --git a/packages/twenty-front/src/modules/object-record/object-options-dropdown/components/ObjectOptionsDropdownLayoutContent.tsx b/packages/twenty-front/src/modules/object-record/object-options-dropdown/components/ObjectOptionsDropdownLayoutContent.tsx index 63f8450f4..fca6a1a3d 100644 --- a/packages/twenty-front/src/modules/object-record/object-options-dropdown/components/ObjectOptionsDropdownLayoutContent.tsx +++ b/packages/twenty-front/src/modules/object-record/object-options-dropdown/components/ObjectOptionsDropdownLayoutContent.tsx @@ -10,7 +10,7 @@ import { import { useObjectOptionsForBoard } from '@/object-record/object-options-dropdown/hooks/useObjectOptionsForBoard'; import { useOptionsDropdown } from '@/object-record/object-options-dropdown/hooks/useOptionsDropdown'; import { recordIndexOpenRecordInState } from '@/object-record/record-index/states/recordIndexOpenRecordInState'; -import { DropdownMenuHeader } from '@/ui/layout/dropdown/components/DropdownMenuHeader'; +import { DropdownMenuHeader } from '@/ui/layout/dropdown/components/DropdownMenuHeader/DropdownMenuHeader'; import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer'; import { useGetCurrentViewOnly } from '@/views/hooks/useGetCurrentViewOnly'; import { ViewOpenRecordInType } from '@/views/types/ViewOpenRecordInType'; @@ -41,7 +41,10 @@ export const ObjectOptionsDropdownLayoutContent = () => { return ( <> - + {t`Layout`} diff --git a/packages/twenty-front/src/modules/object-record/object-options-dropdown/components/ObjectOptionsDropdownLayoutOpenInContent.tsx b/packages/twenty-front/src/modules/object-record/object-options-dropdown/components/ObjectOptionsDropdownLayoutOpenInContent.tsx index d1e02e64e..992e5e5d5 100644 --- a/packages/twenty-front/src/modules/object-record/object-options-dropdown/components/ObjectOptionsDropdownLayoutOpenInContent.tsx +++ b/packages/twenty-front/src/modules/object-record/object-options-dropdown/components/ObjectOptionsDropdownLayoutOpenInContent.tsx @@ -8,7 +8,7 @@ import { import { useObjectOptions } from '@/object-record/object-options-dropdown/hooks/useObjectOptions'; import { useOptionsDropdown } from '@/object-record/object-options-dropdown/hooks/useOptionsDropdown'; import { recordIndexOpenRecordInState } from '@/object-record/record-index/states/recordIndexOpenRecordInState'; -import { DropdownMenuHeader } from '@/ui/layout/dropdown/components/DropdownMenuHeader'; +import { DropdownMenuHeader } from '@/ui/layout/dropdown/components/DropdownMenuHeader/DropdownMenuHeader'; import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer'; import { useGetCurrentViewOnly } from '@/views/hooks/useGetCurrentViewOnly'; import { ViewOpenRecordInType } from '@/views/types/ViewOpenRecordInType'; @@ -25,7 +25,7 @@ export const ObjectOptionsDropdownLayoutOpenInContent = () => { <> onContentChange('layout')} + onStartIconClick={() => onContentChange('layout')} > {t`Open in`} diff --git a/packages/twenty-front/src/modules/object-record/object-options-dropdown/components/ObjectOptionsDropdownMenuContent.tsx b/packages/twenty-front/src/modules/object-record/object-options-dropdown/components/ObjectOptionsDropdownMenuContent.tsx index e1af431d2..689a88aaf 100644 --- a/packages/twenty-front/src/modules/object-record/object-options-dropdown/components/ObjectOptionsDropdownMenuContent.tsx +++ b/packages/twenty-front/src/modules/object-record/object-options-dropdown/components/ObjectOptionsDropdownMenuContent.tsx @@ -19,7 +19,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 { DropdownMenuHeader } from '@/ui/layout/dropdown/components/DropdownMenuHeader'; +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'; import { useScopedHotkeys } from '@/ui/utilities/hotkey/hooks/useScopedHotkeys'; diff --git a/packages/twenty-front/src/modules/object-record/object-options-dropdown/components/ObjectOptionsDropdownRecordGroupFieldsContent.tsx b/packages/twenty-front/src/modules/object-record/object-options-dropdown/components/ObjectOptionsDropdownRecordGroupFieldsContent.tsx index 1287b0e99..cd66ee63c 100644 --- a/packages/twenty-front/src/modules/object-record/object-options-dropdown/components/ObjectOptionsDropdownRecordGroupFieldsContent.tsx +++ b/packages/twenty-front/src/modules/object-record/object-options-dropdown/components/ObjectOptionsDropdownRecordGroupFieldsContent.tsx @@ -18,7 +18,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 { DropdownMenuHeader } from '@/ui/layout/dropdown/components/DropdownMenuHeader'; +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'; import { navigationMemorizedUrlState } from '@/ui/navigation/states/navigationMemorizedUrlState'; @@ -107,7 +107,7 @@ export const ObjectOptionsDropdownRecordGroupFieldsContent = () => { <> + onStartIconClick={() => isDefined(recordGroupFieldMetadata) ? onContentChange('recordGroups') : resetContent() diff --git a/packages/twenty-front/src/modules/object-record/object-options-dropdown/components/ObjectOptionsDropdownRecordGroupSortContent.tsx b/packages/twenty-front/src/modules/object-record/object-options-dropdown/components/ObjectOptionsDropdownRecordGroupSortContent.tsx index 488233469..305c09e1e 100644 --- a/packages/twenty-front/src/modules/object-record/object-options-dropdown/components/ObjectOptionsDropdownRecordGroupSortContent.tsx +++ b/packages/twenty-front/src/modules/object-record/object-options-dropdown/components/ObjectOptionsDropdownRecordGroupSortContent.tsx @@ -11,7 +11,7 @@ import { useOptionsDropdown } from '@/object-record/object-options-dropdown/hook import { hiddenRecordGroupIdsComponentSelector } from '@/object-record/record-group/states/selectors/hiddenRecordGroupIdsComponentSelector'; import { RecordGroupSort } from '@/object-record/record-group/types/RecordGroupSort'; import { recordIndexRecordGroupSortComponentState } from '@/object-record/record-index/states/recordIndexRecordGroupSortComponentState'; -import { DropdownMenuHeader } from '@/ui/layout/dropdown/components/DropdownMenuHeader'; +import { DropdownMenuHeader } from '@/ui/layout/dropdown/components/DropdownMenuHeader/DropdownMenuHeader'; import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer'; import { useRecoilComponentStateV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentStateV2'; import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2'; @@ -44,7 +44,7 @@ export const ObjectOptionsDropdownRecordGroupSortContent = () => { <> onContentChange('recordGroups')} + onStartIconClick={() => onContentChange('recordGroups')} > Sort diff --git a/packages/twenty-front/src/modules/object-record/object-options-dropdown/components/ObjectOptionsDropdownRecordGroupsContent.tsx b/packages/twenty-front/src/modules/object-record/object-options-dropdown/components/ObjectOptionsDropdownRecordGroupsContent.tsx index a030c1706..aded779c2 100644 --- a/packages/twenty-front/src/modules/object-record/object-options-dropdown/components/ObjectOptionsDropdownRecordGroupsContent.tsx +++ b/packages/twenty-front/src/modules/object-record/object-options-dropdown/components/ObjectOptionsDropdownRecordGroupsContent.tsx @@ -20,7 +20,7 @@ import { hiddenRecordGroupIdsComponentSelector } from '@/object-record/record-gr import { visibleRecordGroupIdsComponentFamilySelector } from '@/object-record/record-group/states/selectors/visibleRecordGroupIdsComponentFamilySelector'; import { recordIndexRecordGroupHideComponentFamilyState } from '@/object-record/record-index/states/recordIndexRecordGroupHideComponentFamilyState'; import { recordIndexRecordGroupSortComponentState } from '@/object-record/record-index/states/recordIndexRecordGroupSortComponentState'; -import { DropdownMenuHeader } from '@/ui/layout/dropdown/components/DropdownMenuHeader'; +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'; import { useRecoilComponentFamilyValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentFamilyValueV2'; @@ -88,7 +88,10 @@ export const ObjectOptionsDropdownRecordGroupsContent = () => { return ( <> - + Group by diff --git a/packages/twenty-front/src/modules/object-record/object-sort-dropdown/components/ObjectSortDropdownButton.tsx b/packages/twenty-front/src/modules/object-record/object-sort-dropdown/components/ObjectSortDropdownButton.tsx index 52dea7484..52c6d4455 100644 --- a/packages/twenty-front/src/modules/object-record/object-sort-dropdown/components/ObjectSortDropdownButton.tsx +++ b/packages/twenty-front/src/modules/object-record/object-sort-dropdown/components/ObjectSortDropdownButton.tsx @@ -20,7 +20,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 { DropdownMenuHeader } from '@/ui/layout/dropdown/components/DropdownMenuHeader'; +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'; import { StyledHeaderDropdownButton } from '@/ui/layout/dropdown/components/StyledHeaderDropdownButton'; diff --git a/packages/twenty-front/src/modules/object-record/record-board/record-board-column/components/RecordBoardColumnHeaderAggregateDropdownFieldsContent.tsx b/packages/twenty-front/src/modules/object-record/record-board/record-board-column/components/RecordBoardColumnHeaderAggregateDropdownFieldsContent.tsx index 91e5799bf..3ed222bc3 100644 --- a/packages/twenty-front/src/modules/object-record/record-board/record-board-column/components/RecordBoardColumnHeaderAggregateDropdownFieldsContent.tsx +++ b/packages/twenty-front/src/modules/object-record/record-board/record-board-column/components/RecordBoardColumnHeaderAggregateDropdownFieldsContent.tsx @@ -4,7 +4,7 @@ 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 { DropdownMenuHeader } from '@/ui/layout/dropdown/components/DropdownMenuHeader'; +import { DropdownMenuHeader } from '@/ui/layout/dropdown/components/DropdownMenuHeader/DropdownMenuHeader'; import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer'; import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2'; import { useUpdateViewAggregate } from '@/views/hooks/useUpdateViewAggregate'; @@ -50,7 +50,7 @@ export const RecordBoardColumnHeaderAggregateDropdownFieldsContent = () => { <> + onStartIconClick={() => previousContentId ? onContentChange(previousContentId) : resetContent() diff --git a/packages/twenty-front/src/modules/object-record/record-board/record-board-column/components/RecordBoardColumnHeaderAggregateDropdownOptionsContent.tsx b/packages/twenty-front/src/modules/object-record/record-board/record-board-column/components/RecordBoardColumnHeaderAggregateDropdownOptionsContent.tsx index 33768f1d1..51676441e 100644 --- a/packages/twenty-front/src/modules/object-record/record-board/record-board-column/components/RecordBoardColumnHeaderAggregateDropdownOptionsContent.tsx +++ b/packages/twenty-front/src/modules/object-record/record-board/record-board-column/components/RecordBoardColumnHeaderAggregateDropdownOptionsContent.tsx @@ -12,7 +12,7 @@ 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 { DropdownMenuHeader } from '@/ui/layout/dropdown/components/DropdownMenuHeader'; +import { DropdownMenuHeader } from '@/ui/layout/dropdown/components/DropdownMenuHeader/DropdownMenuHeader'; 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'; @@ -58,7 +58,10 @@ export const RecordBoardColumnHeaderAggregateDropdownOptionsContent = ({ return ( <> - + {title} diff --git a/packages/twenty-front/src/modules/object-record/record-table/record-table-footer/components/RecordTableColumnAggregateDropdownSubmenuContent.tsx b/packages/twenty-front/src/modules/object-record/record-table/record-table-footer/components/RecordTableColumnAggregateDropdownSubmenuContent.tsx index cc713891a..3beaed4af 100644 --- a/packages/twenty-front/src/modules/object-record/record-table/record-table-footer/components/RecordTableColumnAggregateDropdownSubmenuContent.tsx +++ b/packages/twenty-front/src/modules/object-record/record-table/record-table-footer/components/RecordTableColumnAggregateDropdownSubmenuContent.tsx @@ -2,7 +2,7 @@ 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 { DropdownMenuHeader } from '@/ui/layout/dropdown/components/DropdownMenuHeader'; +import { DropdownMenuHeader } from '@/ui/layout/dropdown/components/DropdownMenuHeader/DropdownMenuHeader'; import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer'; import { useDropdown } from '@/ui/layout/dropdown/hooks/useDropdown'; import { useScopedHotkeys } from '@/ui/utilities/hotkey/hooks/useScopedHotkeys'; @@ -32,7 +32,10 @@ export const RecordTableColumnAggregateFooterDropdownSubmenuContent = ({ ); return ( <> - + {title} diff --git a/packages/twenty-front/src/modules/ui/layout/dropdown/components/Dropdown.tsx b/packages/twenty-front/src/modules/ui/layout/dropdown/components/Dropdown.tsx index 6d81c4265..6c32ba218 100644 --- a/packages/twenty-front/src/modules/ui/layout/dropdown/components/Dropdown.tsx +++ b/packages/twenty-front/src/modules/ui/layout/dropdown/components/Dropdown.tsx @@ -28,7 +28,7 @@ const StyledDropdownFallbackAnchor = styled.div` top: 0; `; -type DropdownProps = { +export type DropdownProps = { className?: string; clickableComponent?: ReactNode; dropdownComponents: ReactNode; diff --git a/packages/twenty-front/src/modules/ui/layout/dropdown/components/DropdownMenuHeader.tsx b/packages/twenty-front/src/modules/ui/layout/dropdown/components/DropdownMenuHeader/DropdownMenuHeader.tsx similarity index 50% rename from packages/twenty-front/src/modules/ui/layout/dropdown/components/DropdownMenuHeader.tsx rename to packages/twenty-front/src/modules/ui/layout/dropdown/components/DropdownMenuHeader/DropdownMenuHeader.tsx index 39127f9f6..a811dc390 100644 --- a/packages/twenty-front/src/modules/ui/layout/dropdown/components/DropdownMenuHeader.tsx +++ b/packages/twenty-front/src/modules/ui/layout/dropdown/components/DropdownMenuHeader/DropdownMenuHeader.tsx @@ -1,8 +1,13 @@ -import { useTheme } from '@emotion/react'; import styled from '@emotion/styled'; -import { ComponentProps, MouseEvent } from 'react'; +import { ComponentProps, MouseEvent, ReactElement } from 'react'; +import { Avatar, AvatarProps, IconComponent } from 'twenty-ui'; +import { DropdownMenuHeaderStartIcon } from '@/ui/layout/dropdown/components/DropdownMenuHeader/internal/DropdownMenuHeaderStartIcon'; import { isDefined } from 'twenty-shared'; -import { IconComponent, LightIconButton } from 'twenty-ui'; +import { useTheme } from '@emotion/react'; +import { + Dropdown, + DropdownProps, +} from '@/ui/layout/dropdown/components/Dropdown'; const StyledHeader = styled.li` align-items: center; @@ -26,6 +31,13 @@ const StyledHeader = styled.li` } `; +const StyledChildrenWrapper = styled.span` + overflow: hidden; + padding: 0 ${({ theme }) => theme.spacing(1)}; + white-space: nowrap; + text-overflow: ellipsis; +`; + const StyledEndIcon = styled.div` display: inline-flex; color: ${({ theme }) => theme.font.color.tertiary}; @@ -39,83 +51,53 @@ const StyledEndIcon = styled.div` } `; -const StyledChildrenWrapper = styled.span` - overflow: hidden; - padding: 0 ${({ theme }) => theme.spacing(1)}; - white-space: nowrap; - text-overflow: ellipsis; -`; - -const StyledNonClickableStartIcon = styled.div` - align-items: center; - background: transparent; - border: none; - - display: flex; - flex-direction: row; - - font-family: ${({ theme }) => theme.font.family}; - font-weight: ${({ theme }) => theme.font.weight.regular}; - gap: ${({ theme }) => theme.spacing(1)}; - justify-content: center; - - white-space: nowrap; - height: 24px; - width: 24px; -`; - type DropdownMenuHeaderProps = ComponentProps<'li'> & { - StartIcon?: IconComponent; EndIcon?: IconComponent; - onClick?: (event: MouseEvent) => void; + onClick?: (event: MouseEvent) => void; + onStartIconClick?: (event: MouseEvent) => void; testId?: string; className?: string; -}; - + DropdownOnEndIcon?: ReactElement; +} & ( + | { StartIcon?: IconComponent } + | { StartAvatar?: ReactElement } + ); export const DropdownMenuHeader = ({ children, - StartIcon, EndIcon, + onStartIconClick, onClick, testId, className, + ...props }: DropdownMenuHeaderProps) => { const theme = useTheme(); + return ( - <> - {EndIcon && ( - - {children} - - - - + + {'StartIcon' in props && isDefined(props.StartIcon) && ( + )} - {StartIcon && ( - - {isDefined(onClick) ? ( - - ) : ( - - - - )} - {children} - + {!('StartIcon' in props) && + 'StartAvatar' in props && + isDefined(props.StartAvatar) && ( + + )} + {children} + {'DropdownOnEndIcon' in props && ( + {props.DropdownOnEndIcon} )} - + {!('DropdownOnEndIcon' in props) && EndIcon && ( + + + + )} + ); }; diff --git a/packages/twenty-front/src/modules/ui/layout/dropdown/components/DropdownMenuHeader/internal/DropdownMenuHeaderStartIcon.tsx b/packages/twenty-front/src/modules/ui/layout/dropdown/components/DropdownMenuHeader/internal/DropdownMenuHeaderStartIcon.tsx new file mode 100644 index 000000000..bc8274766 --- /dev/null +++ b/packages/twenty-front/src/modules/ui/layout/dropdown/components/DropdownMenuHeader/internal/DropdownMenuHeaderStartIcon.tsx @@ -0,0 +1,64 @@ +import { Avatar, AvatarProps, IconComponent, LightIconButton } from 'twenty-ui'; +import { MouseEvent, ReactElement } from 'react'; +import styled from '@emotion/styled'; +import { useTheme } from '@emotion/react'; + +const StyledNonClickableStartIcon = styled.div` + align-items: center; + background: transparent; + border: none; + + display: flex; + flex-direction: row; + + font-family: ${({ theme }) => theme.font.family}; + font-weight: ${({ theme }) => theme.font.weight.regular}; + gap: ${({ theme }) => theme.spacing(1)}; + justify-content: center; + + white-space: nowrap; + height: ${({ theme }) => theme.spacing(6)}; + width: ${({ theme }) => theme.spacing(6)}; +`; + +const StyledAvatarWrapper = styled.div` + padding: ${({ theme }) => theme.spacing(1)}; +`; + +export const DropdownMenuHeaderStartIcon = ({ + onClick, + ...props +}: { onClick?: (event: MouseEvent) => void } & ( + | { StartIcon: IconComponent } + | { + StartAvatar: ReactElement; + } + | Record +)) => { + const theme = useTheme(); + + return ( + <> + {'StartIcon' in props && + (onClick ? ( + + ) : ( + + + + ))} + + {'StartAvatar' in props && ( + {props.StartAvatar} + )} + + ); +}; diff --git a/packages/twenty-front/src/modules/ui/layout/dropdown/components/DropdownMenuHeader/internal/DropdownMenuHeaderWithDropdownMenu.tsx b/packages/twenty-front/src/modules/ui/layout/dropdown/components/DropdownMenuHeader/internal/DropdownMenuHeaderWithDropdownMenu.tsx new file mode 100644 index 000000000..d7b530f9b --- /dev/null +++ b/packages/twenty-front/src/modules/ui/layout/dropdown/components/DropdownMenuHeader/internal/DropdownMenuHeaderWithDropdownMenu.tsx @@ -0,0 +1,34 @@ +import { Dropdown } from '@/ui/layout/dropdown/components/Dropdown'; +import { IconComponent, IconDotsVertical, LightIconButton } from 'twenty-ui'; +import { SelectHotkeyScope } from '@/ui/input/types/SelectHotkeyScope'; +import { Placement } from '@floating-ui/react'; +import { ReactNode } from 'react'; + +export type DropdownMenuHeaderWithDropdownMenuProps = { + EndIcon?: IconComponent; + dropdownPlacement?: Placement; + dropdownComponents: ReactNode; + dropdownId: string; +}; + +export const DropdownMenuHeaderWithDropdownMenu = ( + props: DropdownMenuHeaderWithDropdownMenuProps, +) => { + return ( +
+ + } + dropdownPlacement={props.dropdownPlacement ?? 'bottom-end'} + dropdownComponents={props.dropdownComponents} + dropdownId={props.dropdownId} + dropdownHotkeyScope={{ scope: SelectHotkeyScope.Select }} + /> +
+ ); +}; diff --git a/packages/twenty-front/src/modules/ui/layout/dropdown/components/__stories__/DropdownMenu.stories.tsx b/packages/twenty-front/src/modules/ui/layout/dropdown/components/__stories__/DropdownMenu.stories.tsx index e24ef3556..28b04e72c 100644 --- a/packages/twenty-front/src/modules/ui/layout/dropdown/components/__stories__/DropdownMenu.stories.tsx +++ b/packages/twenty-front/src/modules/ui/layout/dropdown/components/__stories__/DropdownMenu.stories.tsx @@ -7,6 +7,7 @@ import { Avatar, Button, ComponentDecorator, + IconChevronLeft, MenuItem, MenuItemMultiSelectAvatar, MenuItemSelectAvatar, @@ -15,7 +16,7 @@ import { import { DropdownMenuSkeletonItem } from '@/ui/input/relation-picker/components/skeletons/DropdownMenuSkeletonItem'; import { Dropdown } from '../Dropdown'; -import { DropdownMenuHeader } from '../DropdownMenuHeader'; +import { DropdownMenuHeader } from '../DropdownMenuHeader/DropdownMenuHeader'; import { DropdownMenuInput } from '../DropdownMenuInput'; import { DropdownMenuItemsContainer } from '../DropdownMenuItemsContainer'; import { DropdownMenuSearchInput } from '../DropdownMenuSearchInput'; @@ -218,8 +219,9 @@ export const WithHeaders: Story = { args: { dropdownComponents: ( <> - Header - + + Header + Subheader 1 <> diff --git a/packages/twenty-front/src/modules/ui/layout/dropdown/components/__stories__/DropdownMenuHeader.stories.tsx b/packages/twenty-front/src/modules/ui/layout/dropdown/components/__stories__/DropdownMenuHeader.stories.tsx new file mode 100644 index 000000000..57ad19a78 --- /dev/null +++ b/packages/twenty-front/src/modules/ui/layout/dropdown/components/__stories__/DropdownMenuHeader.stories.tsx @@ -0,0 +1,81 @@ +import { Meta, StoryObj } from '@storybook/react'; +import { + Avatar, + AVATAR_URL_MOCK, + ComponentDecorator, + IconChevronLeft, + IconChevronRight, + IconPlus, + MenuItem, +} from 'twenty-ui'; + +import { DropdownMenuHeader } from '@/ui/layout/dropdown/components/DropdownMenuHeader/DropdownMenuHeader'; +import { SelectHotkeyScope } from '@/ui/input/types/SelectHotkeyScope'; +import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer'; +import { Dropdown } from '@/ui/layout/dropdown/components/Dropdown'; + +const meta: Meta = { + title: 'UI/Layout/Dropdown/DropdownMenuHeader', + component: DropdownMenuHeader, + decorators: [ComponentDecorator], + args: {}, +}; + +export default meta; +type Story = StoryObj; + +export const Text: Story = { + args: { + children: 'Text only', + }, +}; +export const StartIcon: Story = { + args: { + StartIcon: IconChevronLeft, + children: 'Start Icon', + }, +}; + +export const EndIcon: Story = { + args: { + EndIcon: IconChevronRight, + children: 'End Icon', + }, +}; + +export const StartAndEndIcon: Story = { + args: { + StartIcon: IconChevronLeft, + EndIcon: IconChevronRight, + children: 'Start and End Icon', + }, +}; + +export const StartAvatar: Story = { + args: { + StartAvatar: ( + + ), + children: 'Avatar', + }, +}; + +export const ContextDropdownAndAvatar: Story = { + args: { + children: 'Context Dropdown', + StartAvatar: ( + + ), + DropdownOnEndIcon: ( + + + + } + /> + ), + }, +}; diff --git a/packages/twenty-front/src/modules/views/view-picker/components/ViewPickerContentCreateMode.tsx b/packages/twenty-front/src/modules/views/view-picker/components/ViewPickerContentCreateMode.tsx index e145b4c6f..1584113fa 100644 --- a/packages/twenty-front/src/modules/views/view-picker/components/ViewPickerContentCreateMode.tsx +++ b/packages/twenty-front/src/modules/views/view-picker/components/ViewPickerContentCreateMode.tsx @@ -6,7 +6,7 @@ import { useObjectMetadataItemById } from '@/object-metadata/hooks/useObjectMeta import { IconPicker } from '@/ui/input/components/IconPicker'; import { Select } from '@/ui/input/components/Select'; import { TextInputV2 } from '@/ui/input/components/TextInputV2'; -import { DropdownMenuHeader } from '@/ui/layout/dropdown/components/DropdownMenuHeader'; +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'; import { useScopedHotkeys } from '@/ui/utilities/hotkey/hooks/useScopedHotkeys'; @@ -130,7 +130,7 @@ export const ViewPickerContentCreateMode = () => { return ( <> - + {t`Create view`} diff --git a/packages/twenty-front/src/modules/views/view-picker/components/ViewPickerContentEditMode.tsx b/packages/twenty-front/src/modules/views/view-picker/components/ViewPickerContentEditMode.tsx index 41dc8b6da..91e7e349c 100644 --- a/packages/twenty-front/src/modules/views/view-picker/components/ViewPickerContentEditMode.tsx +++ b/packages/twenty-front/src/modules/views/view-picker/components/ViewPickerContentEditMode.tsx @@ -3,7 +3,7 @@ import { IconChevronLeft } from 'twenty-ui'; import { IconPicker } from '@/ui/input/components/IconPicker'; import { TextInputV2 } from '@/ui/input/components/TextInputV2'; -import { DropdownMenuHeader } from '@/ui/layout/dropdown/components/DropdownMenuHeader'; +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'; import { useScopedHotkeys } from '@/ui/utilities/hotkey/hooks/useScopedHotkeys'; @@ -67,7 +67,10 @@ export const ViewPickerContentEditMode = () => { return ( <> - + Edit view diff --git a/packages/twenty-front/src/modules/workflow/workflow-variables/components/WorkflowVariablesDropdownFieldItems.tsx b/packages/twenty-front/src/modules/workflow/workflow-variables/components/WorkflowVariablesDropdownFieldItems.tsx index 5e0e92383..dc880657b 100644 --- a/packages/twenty-front/src/modules/workflow/workflow-variables/components/WorkflowVariablesDropdownFieldItems.tsx +++ b/packages/twenty-front/src/modules/workflow/workflow-variables/components/WorkflowVariablesDropdownFieldItems.tsx @@ -1,4 +1,4 @@ -import { DropdownMenuHeader } from '@/ui/layout/dropdown/components/DropdownMenuHeader'; +import { DropdownMenuHeader } from '@/ui/layout/dropdown/components/DropdownMenuHeader/DropdownMenuHeader'; import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer'; import { DropdownMenuSearchInput } from '@/ui/layout/dropdown/components/DropdownMenuSearchInput'; import { DropdownMenuSeparator } from '@/ui/layout/dropdown/components/DropdownMenuSeparator'; @@ -117,7 +117,7 @@ export const WorkflowVariablesDropdownFieldItems = ({ <> - + diff --git a/packages/twenty-front/src/modules/workflow/workflow-variables/components/WorkflowVariablesDropdownWorkflowStepItems.tsx b/packages/twenty-front/src/modules/workflow/workflow-variables/components/WorkflowVariablesDropdownWorkflowStepItems.tsx index 10ba87812..0fd7d8ffc 100644 --- a/packages/twenty-front/src/modules/workflow/workflow-variables/components/WorkflowVariablesDropdownWorkflowStepItems.tsx +++ b/packages/twenty-front/src/modules/workflow/workflow-variables/components/WorkflowVariablesDropdownWorkflowStepItems.tsx @@ -1,4 +1,4 @@ -import { DropdownMenuHeader } from '@/ui/layout/dropdown/components/DropdownMenuHeader'; +import { DropdownMenuHeader } from '@/ui/layout/dropdown/components/DropdownMenuHeader/DropdownMenuHeader'; import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer'; import { DropdownMenuSearchInput } from '@/ui/layout/dropdown/components/DropdownMenuSearchInput'; import { DropdownMenuSeparator } from '@/ui/layout/dropdown/components/DropdownMenuSeparator'; @@ -37,7 +37,7 @@ export const WorkflowVariablesDropdownWorkflowStepItems = ({ return ( <> - + theme.font.family}; font-weight: ${({ theme }) => theme.font.weight.regular}; gap: ${({ theme }) => theme.spacing(1)}; - height: ${({ size }) => (size === 'small' ? '24px' : '32px')}; + height: ${({ size, theme }) => + size === 'small' ? theme.spacing(6) : theme.spacing(8)}; justify-content: center; padding: ${({ theme }) => theme.spacing(1)}; transition: background 0.1s ease; white-space: nowrap; - width: ${({ size }) => (size === 'small' ? '24px' : '32px')}; - min-width: ${({ size }) => (size === 'small' ? '24px' : '32px')}; + width: ${({ size, theme }) => + size === 'small' ? theme.spacing(6) : theme.spacing(8)}; + min-width: ${({ size, theme }) => + size === 'small' ? theme.spacing(6) : theme.spacing(8)}; &:hover { background: ${({ theme, disabled }) =>