diff --git a/packages/twenty-front/src/modules/action-menu/actions/display/components/ActionDropdownItem.tsx b/packages/twenty-front/src/modules/action-menu/actions/display/components/ActionDropdownItem.tsx index 53206e658..1eb31bbea 100644 --- a/packages/twenty-front/src/modules/action-menu/actions/display/components/ActionDropdownItem.tsx +++ b/packages/twenty-front/src/modules/action-menu/actions/display/components/ActionDropdownItem.tsx @@ -1,5 +1,10 @@ import { ActionDisplayProps } from '@/action-menu/actions/display/components/ActionDisplay'; import { getActionLabel } from '@/action-menu/utils/getActionLabel'; +import { SelectableListItem } from '@/ui/layout/selectable-list/components/SelectableListItem'; +import { SelectableListComponentInstanceContext } from '@/ui/layout/selectable-list/states/contexts/SelectableListComponentInstanceContext'; +import { isSelectedItemIdComponentFamilySelector } from '@/ui/layout/selectable-list/states/selectors/isSelectedItemIdComponentFamilySelector'; +import { useAvailableComponentInstanceIdOrThrow } from '@/ui/utilities/state/component-state/hooks/useAvailableComponentInstanceIdOrThrow'; +import { useRecoilComponentFamilyValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentFamilyValueV2'; import { useNavigate } from 'react-router-dom'; import { isDefined } from 'twenty-shared/utils'; import { MenuItem } from 'twenty-ui/navigation'; @@ -22,12 +27,25 @@ export const ActionDropdownItem = ({ } }; + const selectableListInstanceId = useAvailableComponentInstanceIdOrThrow( + SelectableListComponentInstanceContext, + ); + + const isSelected = useRecoilComponentFamilyValueV2( + isSelectedItemIdComponentFamilySelector, + action.key, + selectableListInstanceId, + ); + return ( - + + + ); }; diff --git a/packages/twenty-front/src/modules/action-menu/actions/display/components/ActionListItem.tsx b/packages/twenty-front/src/modules/action-menu/actions/display/components/ActionListItem.tsx index f617c394c..106a8f9ce 100644 --- a/packages/twenty-front/src/modules/action-menu/actions/display/components/ActionListItem.tsx +++ b/packages/twenty-front/src/modules/action-menu/actions/display/components/ActionListItem.tsx @@ -1,9 +1,7 @@ import { ActionDisplayProps } from '@/action-menu/actions/display/components/ActionDisplay'; import { getActionLabel } from '@/action-menu/utils/getActionLabel'; import { CommandMenuItem } from '@/command-menu/components/CommandMenuItem'; -import { SelectableItem } from '@/ui/layout/selectable-list/components/SelectableItem'; -import { useSelectableListListenToEnterHotkeyOnItem } from '@/ui/layout/selectable-list/hooks/useSelectableListListenToEnterHotkeyOnItem'; -import { AppHotkeyScope } from '@/ui/utilities/hotkey/types/AppHotkeyScope'; +import { SelectableListItem } from '@/ui/layout/selectable-list/components/SelectableListItem'; import { useNavigate } from 'react-router-dom'; import { isDefined } from 'twenty-shared/utils'; @@ -25,14 +23,8 @@ export const ActionListItem = ({ } }; - useSelectableListListenToEnterHotkeyOnItem({ - hotkeyScope: AppHotkeyScope.CommandMenuOpen, - itemId: action.key, - onEnter: handleClick, - }); - return ( - + - + ); }; diff --git a/packages/twenty-front/src/modules/action-menu/actions/display/components/__stories__/ActionDisplay.stories.tsx b/packages/twenty-front/src/modules/action-menu/actions/display/components/__stories__/ActionDisplay.stories.tsx index 5e47940aa..c71e704bb 100644 --- a/packages/twenty-front/src/modules/action-menu/actions/display/components/__stories__/ActionDisplay.stories.tsx +++ b/packages/twenty-front/src/modules/action-menu/actions/display/components/__stories__/ActionDisplay.stories.tsx @@ -114,6 +114,13 @@ export const AsDropdownItem: Story = { onClick: addToFavoritesMock, }, decorators: [ + (Story) => ( + + + + ), (Story) => ( = { title: 'Modules/ActionMenu/Actions/Display/ActionDropdownItem', component: ActionDropdownItem, - decorators: [ComponentDecorator, RouterDecorator], + decorators: [ + (Story) => ( + + + + ), + ComponentDecorator, + RouterDecorator, + ], }; export default meta; diff --git a/packages/twenty-front/src/modules/action-menu/components/CommandMenuActionMenuDropdown.tsx b/packages/twenty-front/src/modules/action-menu/components/CommandMenuActionMenuDropdown.tsx index ff1b2cd7f..3592a1d2f 100644 --- a/packages/twenty-front/src/modules/action-menu/components/CommandMenuActionMenuDropdown.tsx +++ b/packages/twenty-front/src/modules/action-menu/components/CommandMenuActionMenuDropdown.tsx @@ -7,6 +7,8 @@ import { getRightDrawerActionMenuDropdownIdFromActionMenuId } from '@/action-men import { Dropdown } from '@/ui/layout/dropdown/components/Dropdown'; import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer'; import { useDropdownV2 } from '@/ui/layout/dropdown/hooks/useDropdownV2'; +import { SelectableList } from '@/ui/layout/selectable-list/components/SelectableList'; +import { useSelectableList } from '@/ui/layout/selectable-list/hooks/useSelectableList'; import { useScopedHotkeys } from '@/ui/utilities/hotkey/hooks/useScopedHotkeys'; import { AppHotkeyScope } from '@/ui/utilities/hotkey/types/AppHotkeyScope'; import { useAvailableComponentInstanceIdOrThrow } from '@/ui/utilities/state/component-state/hooks/useAvailableComponentInstanceIdOrThrow'; @@ -41,6 +43,16 @@ export const CommandMenuActionMenuDropdown = () => { [toggleDropdown], ); + const recordSelectionActions = actions.filter( + (action) => action.scope === ActionScope.RecordSelection, + ); + + const selectableItemIdArray = recordSelectionActions.map( + (action) => action.key, + ); + + const { setSelectedItemId } = useSelectableList(actionMenuId); + return ( { } dropdownPlacement="top-end" dropdownOffset={{ y: parseInt(theme.spacing(2), 10) }} + onOpen={() => { + setSelectedItemId(selectableItemIdArray[0]); + }} dropdownComponents={ - {actions - .filter((action) => action.scope === ActionScope.RecordSelection) - .map((action) => ( + + {recordSelectionActions.map((action) => ( ))} + } /> diff --git a/packages/twenty-front/src/modules/action-menu/components/RecordIndexActionMenuDropdown.tsx b/packages/twenty-front/src/modules/action-menu/components/RecordIndexActionMenuDropdown.tsx index 4f83c112f..6fc83086d 100644 --- a/packages/twenty-front/src/modules/action-menu/components/RecordIndexActionMenuDropdown.tsx +++ b/packages/twenty-front/src/modules/action-menu/components/RecordIndexActionMenuDropdown.tsx @@ -9,8 +9,12 @@ import { getActionMenuDropdownIdFromActionMenuId } from '@/action-menu/utils/get import { useCommandMenu } from '@/command-menu/hooks/useCommandMenu'; 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 { useDropdownV2 } from '@/ui/layout/dropdown/hooks/useDropdownV2'; +import { SelectableList } from '@/ui/layout/selectable-list/components/SelectableList'; +import { SelectableListItem } from '@/ui/layout/selectable-list/components/SelectableListItem'; +import { selectedItemIdComponentState } from '@/ui/layout/selectable-list/states/selectedItemIdComponentState'; import { useAvailableComponentInstanceIdOrThrow } from '@/ui/utilities/state/component-state/hooks/useAvailableComponentInstanceIdOrThrow'; +import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2'; import { extractComponentState } from '@/ui/utilities/state/component-state/utils/extractComponentState'; import styled from '@emotion/styled'; import { useContext } from 'react'; @@ -42,7 +46,7 @@ export const RecordIndexActionMenuDropdown = () => { ); const dropdownId = getActionMenuDropdownIdFromActionMenuId(actionMenuId); - const { closeDropdown } = useDropdown(dropdownId); + const { closeDropdown } = useDropdownV2(); const actionMenuDropdownPosition = useRecoilValue( extractComponentState( @@ -53,6 +57,16 @@ export const RecordIndexActionMenuDropdown = () => { const { openCommandMenu } = useCommandMenu(); + const selectedItemIdArray = [ + ...recordIndexActions.map((action) => action.key), + 'more-actions', + ]; + + const selectedItemId = useRecoilComponentValueV2( + selectedItemIdComponentState, + dropdownId, + ); + return ( { dropdownComponents={ - {recordIndexActions.map((action) => ( - - ))} - { - closeDropdown(); - openCommandMenu(); - }} - text="More actions" - /> + + {recordIndexActions.map((action) => ( + + ))} + { + closeDropdown(dropdownId); + openCommandMenu(); + }} + > + { + closeDropdown(dropdownId); + openCommandMenu(); + }} + focused={selectedItemId === 'more-actions'} + text="More actions" + /> + + } diff --git a/packages/twenty-front/src/modules/command-menu/components/CommandMenu.tsx b/packages/twenty-front/src/modules/command-menu/components/CommandMenu.tsx index b42156df6..350bc4565 100644 --- a/packages/twenty-front/src/modules/command-menu/components/CommandMenu.tsx +++ b/packages/twenty-front/src/modules/command-menu/components/CommandMenu.tsx @@ -7,7 +7,6 @@ import { useMatchingCommandMenuActions } from '@/command-menu/hooks/useMatchingC import { commandMenuSearchState } from '@/command-menu/states/commandMenuSearchState'; import { contextStoreCurrentObjectMetadataItemIdComponentState } from '@/context-store/states/contextStoreCurrentObjectMetadataItemIdComponentState'; import { useObjectMetadataItems } from '@/object-metadata/hooks/useObjectMetadataItems'; -import { SelectableItem } from '@/ui/layout/selectable-list/components/SelectableItem'; import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2'; import { useLingui } from '@lingui/react/macro'; import { useRecoilValue } from 'recoil'; @@ -89,9 +88,7 @@ export const CommandMenu = () => { > {isDefined(previousContextStoreCurrentObjectMetadataItemId) && ( - - - + )} diff --git a/packages/twenty-front/src/modules/command-menu/components/CommandMenuList.tsx b/packages/twenty-front/src/modules/command-menu/components/CommandMenuList.tsx index 31577da2e..84cdefc83 100644 --- a/packages/twenty-front/src/modules/command-menu/components/CommandMenuList.tsx +++ b/packages/twenty-front/src/modules/command-menu/components/CommandMenuList.tsx @@ -4,8 +4,6 @@ import { ActionGroupConfig } from '@/command-menu/components/CommandMenu'; import { CommandMenuDefaultSelectionEffect } from '@/command-menu/components/CommandMenuDefaultSelectionEffect'; import { COMMAND_MENU_SEARCH_BAR_HEIGHT } from '@/command-menu/constants/CommandMenuSearchBarHeight'; import { COMMAND_MENU_SEARCH_BAR_PADDING } from '@/command-menu/constants/CommandMenuSearchBarPadding'; -import { RESET_CONTEXT_TO_SELECTION } from '@/command-menu/constants/ResetContextToSelection'; -import { useResetPreviousCommandMenuContext } from '@/command-menu/hooks/useResetPreviousCommandMenuContext'; import { hasUserSelectedCommandState } from '@/command-menu/states/hasUserSelectedCommandState'; import { SelectableList } from '@/ui/layout/selectable-list/components/SelectableList'; import { AppHotkeyScope } from '@/ui/utilities/hotkey/types/AppHotkeyScope'; @@ -64,9 +62,6 @@ export const CommandMenuList = ({ loading = false, noResults = false, }: CommandMenuListProps) => { - const { resetPreviousCommandMenuContext } = - useResetPreviousCommandMenuContext(); - const setHasUserSelectedCommand = useSetRecoilState( hasUserSelectedCommandState, ); @@ -82,12 +77,6 @@ export const CommandMenuList = ({ selectableListInstanceId="command-menu-list" hotkeyScope={AppHotkeyScope.CommandMenuOpen} selectableItemIdArray={selectableItemIds} - onEnter={(itemId) => { - if (itemId === RESET_CONTEXT_TO_SELECTION) { - resetPreviousCommandMenuContext(); - return; - } - }} onSelect={() => { setHasUserSelectedCommand(true); }} diff --git a/packages/twenty-front/src/modules/command-menu/components/ResetContextToSelectionCommandButton.tsx b/packages/twenty-front/src/modules/command-menu/components/ResetContextToSelectionCommandButton.tsx index 1912072bd..5b4b557b8 100644 --- a/packages/twenty-front/src/modules/command-menu/components/ResetContextToSelectionCommandButton.tsx +++ b/packages/twenty-front/src/modules/command-menu/components/ResetContextToSelectionCommandButton.tsx @@ -6,6 +6,7 @@ import { useResetPreviousCommandMenuContext } from '@/command-menu/hooks/useRese import { contextStoreCurrentObjectMetadataItemIdComponentState } from '@/context-store/states/contextStoreCurrentObjectMetadataItemIdComponentState'; import { contextStoreTargetedRecordsRuleComponentState } from '@/context-store/states/contextStoreTargetedRecordsRuleComponentState'; import { objectMetadataItemsState } from '@/object-metadata/states/objectMetadataItemsState'; +import { SelectableListItem } from '@/ui/layout/selectable-list/components/SelectableListItem'; import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2'; import { t } from '@lingui/core/macro'; import { useRecoilValue } from 'recoil'; @@ -42,17 +43,22 @@ export const ResetContextToSelectionCommandButton = () => { } return ( - - } - onClick={resetPreviousCommandMenuContext} - /> + + + } + onClick={resetPreviousCommandMenuContext} + /> + ); }; diff --git a/packages/twenty-front/src/modules/command-menu/hooks/__tests__/useCommandMenu.test.tsx b/packages/twenty-front/src/modules/command-menu/hooks/__tests__/useCommandMenu.test.tsx index 5c54c53ae..6c09a5580 100644 --- a/packages/twenty-front/src/modules/command-menu/hooks/__tests__/useCommandMenu.test.tsx +++ b/packages/twenty-front/src/modules/command-menu/hooks/__tests__/useCommandMenu.test.tsx @@ -8,6 +8,18 @@ import { commandMenuNavigationStackState } from '@/command-menu/states/commandMe import { commandMenuPageInfoState } from '@/command-menu/states/commandMenuPageInfoState'; import { commandMenuPageState } from '@/command-menu/states/commandMenuPageState'; import { isCommandMenuOpenedState } from '@/command-menu/states/isCommandMenuOpenedState'; +import { CommandMenuHotkeyScope } from '@/command-menu/types/CommandMenuHotkeyScope'; + +const mockGoBackToPreviousHotkeyScope = jest.fn(); +const mockSetHotkeyScopeAndMemorizePreviousScope = jest.fn(); + +jest.mock('@/ui/utilities/hotkey/hooks/usePreviousHotkeyScope', () => ({ + usePreviousHotkeyScope: () => ({ + goBackToPreviousHotkeyScope: mockGoBackToPreviousHotkeyScope, + setHotkeyScopeAndMemorizePreviousScope: + mockSetHotkeyScopeAndMemorizePreviousScope, + }), +})); const Wrapper = ({ children }: { children: React.ReactNode }) => ( @@ -47,6 +59,10 @@ const renderHooks = () => { }; describe('useCommandMenu', () => { + beforeEach(() => { + jest.clearAllMocks(); + }); + it('should open and close the command menu', () => { const { result } = renderHooks(); @@ -55,6 +71,12 @@ describe('useCommandMenu', () => { }); expect(result.current.isCommandMenuOpened).toBe(true); + expect(mockSetHotkeyScopeAndMemorizePreviousScope).toHaveBeenCalledWith( + CommandMenuHotkeyScope.CommandMenuFocused, + { + commandMenuOpen: true, + }, + ); act(() => { result.current.commandMenu.closeCommandMenu(); @@ -73,6 +95,12 @@ describe('useCommandMenu', () => { }); expect(result.current.isCommandMenuOpened).toBe(true); + expect(mockSetHotkeyScopeAndMemorizePreviousScope).toHaveBeenCalledWith( + CommandMenuHotkeyScope.CommandMenuFocused, + { + commandMenuOpen: true, + }, + ); act(() => { result.current.commandMenu.toggleCommandMenu(); @@ -80,4 +108,21 @@ describe('useCommandMenu', () => { expect(result.current.isCommandMenuOpened).toBe(false); }); + + it('should call goBackToPreviousHotkeyScope when closing the command menu', () => { + const { result } = renderHooks(); + + act(() => { + result.current.commandMenu.openCommandMenu(); + }); + + expect(result.current.isCommandMenuOpened).toBe(true); + expect(mockGoBackToPreviousHotkeyScope).not.toHaveBeenCalled(); + + act(() => { + result.current.commandMenu.closeCommandMenu(); + }); + + expect(mockGoBackToPreviousHotkeyScope).toHaveBeenCalledTimes(1); + }); }); diff --git a/packages/twenty-front/src/modules/command-menu/hooks/__tests__/useCommandMenuCloseAnimationCompleteCleanup.test.tsx b/packages/twenty-front/src/modules/command-menu/hooks/__tests__/useCommandMenuCloseAnimationCompleteCleanup.test.tsx index cc7165222..d8a5993f6 100644 --- a/packages/twenty-front/src/modules/command-menu/hooks/__tests__/useCommandMenuCloseAnimationCompleteCleanup.test.tsx +++ b/packages/twenty-front/src/modules/command-menu/hooks/__tests__/useCommandMenuCloseAnimationCompleteCleanup.test.tsx @@ -23,7 +23,6 @@ import { IconList } from 'twenty-ui/display'; const mockCloseDropdown = jest.fn(); const mockResetContextStoreStates = jest.fn(); const mockResetSelectedItem = jest.fn(); -const mockGoBackToPreviousHotkeyScope = jest.fn(); const mockEmitRightDrawerCloseEvent = jest.fn(); jest.mock('@/ui/layout/dropdown/hooks/useDropdownV2', () => ({ @@ -44,12 +43,6 @@ jest.mock('@/ui/layout/selectable-list/hooks/useSelectableList', () => ({ }), })); -jest.mock('@/ui/utilities/hotkey/hooks/usePreviousHotkeyScope', () => ({ - usePreviousHotkeyScope: () => ({ - goBackToPreviousHotkeyScope: mockGoBackToPreviousHotkeyScope, - }), -})); - jest.mock('@/ui/layout/right-drawer/utils/emitRightDrawerCloseEvent', () => ({ emitRightDrawerCloseEvent: () => { mockEmitRightDrawerCloseEvent(); @@ -231,7 +224,6 @@ describe('useCommandMenuCloseAnimationCompleteCleanup', () => { expect(mockCloseDropdown).toHaveBeenCalledTimes(1); expect(mockResetContextStoreStates).toHaveBeenCalledTimes(2); expect(mockResetSelectedItem).toHaveBeenCalledTimes(1); - expect(mockGoBackToPreviousHotkeyScope).toHaveBeenCalledTimes(1); expect(mockEmitRightDrawerCloseEvent).toHaveBeenCalledTimes(1); expect(mockCloseDropdown).toHaveBeenCalledWith( diff --git a/packages/twenty-front/src/modules/command-menu/hooks/useCommandMenu.ts b/packages/twenty-front/src/modules/command-menu/hooks/useCommandMenu.ts index 70d926f60..fd5e57453 100644 --- a/packages/twenty-front/src/modules/command-menu/hooks/useCommandMenu.ts +++ b/packages/twenty-front/src/modules/command-menu/hooks/useCommandMenu.ts @@ -2,11 +2,13 @@ import { useRecoilCallback } from 'recoil'; import { commandMenuSearchState } from '@/command-menu/states/commandMenuSearchState'; +import { COMMAND_MENU_COMPONENT_INSTANCE_ID } from '@/command-menu/constants/CommandMenuComponentInstanceId'; import { useNavigateCommandMenu } from '@/command-menu/hooks/useNavigateCommandMenu'; import { isCommandMenuClosingState } from '@/command-menu/states/isCommandMenuClosingState'; import { CommandMenuPages } from '@/command-menu/types/CommandMenuPages'; import { useCloseAnyOpenDropdown } from '@/ui/layout/dropdown/hooks/useCloseAnyOpenDropdown'; import { isDragSelectionStartEnabledState } from '@/ui/utilities/drag-select/states/internal/isDragSelectionStartEnabledState'; +import { usePreviousHotkeyScope } from '@/ui/utilities/hotkey/hooks/usePreviousHotkeyScope'; import { useCallback } from 'react'; import { IconDotsVertical } from 'twenty-ui/display'; import { isCommandMenuOpenedState } from '../states/isCommandMenuOpenedState'; @@ -14,16 +16,26 @@ import { isCommandMenuOpenedState } from '../states/isCommandMenuOpenedState'; export const useCommandMenu = () => { const { navigateCommandMenu } = useNavigateCommandMenu(); const { closeAnyOpenDropdown } = useCloseAnyOpenDropdown(); + const { goBackToPreviousHotkeyScope } = usePreviousHotkeyScope( + COMMAND_MENU_COMPONENT_INSTANCE_ID, + ); const closeCommandMenu = useRecoilCallback( - ({ set }) => + ({ set, snapshot }) => () => { - set(isCommandMenuOpenedState, false); - set(isCommandMenuClosingState, true); - set(isDragSelectionStartEnabledState, true); - closeAnyOpenDropdown(); + const isCommandMenuOpened = snapshot + .getLoadable(isCommandMenuOpenedState) + .getValue(); + + if (isCommandMenuOpened) { + set(isCommandMenuOpenedState, false); + set(isCommandMenuClosingState, true); + set(isDragSelectionStartEnabledState, true); + closeAnyOpenDropdown(); + goBackToPreviousHotkeyScope(); + } }, - [closeAnyOpenDropdown], + [closeAnyOpenDropdown, goBackToPreviousHotkeyScope], ); const openCommandMenu = useCallback(() => { diff --git a/packages/twenty-front/src/modules/command-menu/hooks/useCommandMenuCloseAnimationCompleteCleanup.ts b/packages/twenty-front/src/modules/command-menu/hooks/useCommandMenuCloseAnimationCompleteCleanup.ts index f46d77cfe..11f5927ab 100644 --- a/packages/twenty-front/src/modules/command-menu/hooks/useCommandMenuCloseAnimationCompleteCleanup.ts +++ b/packages/twenty-front/src/modules/command-menu/hooks/useCommandMenuCloseAnimationCompleteCleanup.ts @@ -18,7 +18,6 @@ import { emitRightDrawerCloseEvent } from '@/ui/layout/right-drawer/utils/emitRi import { useSelectableList } from '@/ui/layout/selectable-list/hooks/useSelectableList'; import { getShowPageTabListComponentId } from '@/ui/layout/show-page/utils/getShowPageTabListComponentId'; import { activeTabIdComponentState } from '@/ui/layout/tab/states/activeTabIdComponentState'; -import { usePreviousHotkeyScope } from '@/ui/utilities/hotkey/hooks/usePreviousHotkeyScope'; import { WORKFLOW_SERVERLESS_FUNCTION_TAB_LIST_COMPONENT_ID } from '@/workflow/workflow-steps/workflow-actions/code-action/constants/WorkflowServerlessFunctionTabListComponentId'; import { WorkflowServerlessFunctionTabId } from '@/workflow/workflow-steps/workflow-actions/code-action/types/WorkflowServerlessFunctionTabId'; import { useRecoilCallback } from 'recoil'; @@ -26,10 +25,6 @@ import { useRecoilCallback } from 'recoil'; export const useCommandMenuCloseAnimationCompleteCleanup = () => { const { resetSelectedItem } = useSelectableList('command-menu-list'); - const { goBackToPreviousHotkeyScope } = usePreviousHotkeyScope( - COMMAND_MENU_COMPONENT_INSTANCE_ID, - ); - const { resetContextStoreStates } = useResetContextStoreStates(); const { closeDropdown } = useDropdownV2(); @@ -56,7 +51,6 @@ export const useCommandMenuCloseAnimationCompleteCleanup = () => { set(commandMenuNavigationStackState, []); resetSelectedItem(); set(hasUserSelectedCommandState, false); - goBackToPreviousHotkeyScope(); emitRightDrawerCloseEvent(); set(isCommandMenuClosingState, false); @@ -81,12 +75,7 @@ export const useCommandMenuCloseAnimationCompleteCleanup = () => { ); } }, - [ - closeDropdown, - goBackToPreviousHotkeyScope, - resetContextStoreStates, - resetSelectedItem, - ], + [closeDropdown, resetContextStoreStates, resetSelectedItem], ); return { diff --git a/packages/twenty-front/src/modules/object-record/advanced-filter/components/AdvancedFilterFieldSelectMenu.tsx b/packages/twenty-front/src/modules/object-record/advanced-filter/components/AdvancedFilterFieldSelectMenu.tsx index 8f3e4e095..e194f5b29 100644 --- a/packages/twenty-front/src/modules/object-record/advanced-filter/components/AdvancedFilterFieldSelectMenu.tsx +++ b/packages/twenty-front/src/modules/object-record/advanced-filter/components/AdvancedFilterFieldSelectMenu.tsx @@ -1,17 +1,14 @@ import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer'; -import { OBJECT_FILTER_DROPDOWN_ID } from '@/object-record/object-filter-dropdown/constants/ObjectFilterDropdownId'; - import { objectFilterDropdownSearchInputComponentState } from '@/object-record/object-filter-dropdown/states/objectFilterDropdownSearchInputComponentState'; import { useRecordIndexContextOrThrow } from '@/object-record/record-index/contexts/RecordIndexContext'; import { visibleTableColumnsComponentSelector } from '@/object-record/record-table/states/selectors/visibleTableColumnsComponentSelector'; import { DropdownMenuSeparator } from '@/ui/layout/dropdown/components/DropdownMenuSeparator'; -import { SelectableItem } from '@/ui/layout/selectable-list/components/SelectableItem'; import { SelectableList } from '@/ui/layout/selectable-list/components/SelectableList'; +import { SelectableListItem } from '@/ui/layout/selectable-list/components/SelectableListItem'; import { useSelectableList } from '@/ui/layout/selectable-list/hooks/useSelectableList'; import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2'; -import { isDefined } from 'twenty-shared/utils'; import { FieldMetadataItem } from '@/object-metadata/types/FieldMetadataItem'; import { getFilterTypeFromFieldType } from '@/object-metadata/utils/formatFieldMetadataItemsAsFilterDefinitions'; @@ -22,7 +19,6 @@ import { ObjectFilterDropdownFilterSelectMenuItemV2 } from '@/object-record/obje import { fieldMetadataItemIdUsedInDropdownComponentState } from '@/object-record/object-filter-dropdown/states/fieldMetadataItemIdUsedInDropdownComponentState'; import { objectFilterDropdownIsSelectingCompositeFieldComponentState } from '@/object-record/object-filter-dropdown/states/objectFilterDropdownIsSelectingCompositeFieldComponentState'; import { objectFilterDropdownSubMenuFieldTypeComponentState } from '@/object-record/object-filter-dropdown/states/objectFilterDropdownSubMenuFieldTypeComponentState'; -import { FiltersHotkeyScope } from '@/object-record/object-filter-dropdown/types/FiltersHotkeyScope'; import { isCompositeField } from '@/object-record/object-filter-dropdown/utils/isCompositeField'; import { useFilterableFieldMetadataItemsInRecordIndexContext } from '@/object-record/record-filter/hooks/useFilterableFieldMetadataItemsInRecordIndexContext'; import { useRecoilComponentStateV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentStateV2'; @@ -37,8 +33,10 @@ export const AdvancedFilterFieldSelectMenu = ({ }: AdvancedFilterFieldSelectMenuProps) => { const { recordIndexId } = useRecordIndexContextOrThrow(); - const { closeAdvancedFilterFieldSelectDropdown } = - useAdvancedFilterFieldSelectDropdown(recordFilterId); + const { + closeAdvancedFilterFieldSelectDropdown, + advancedFilterFieldSelectDropdownId, + } = useAdvancedFilterFieldSelectDropdown(recordFilterId); const [objectFilterDropdownSearchInput] = useRecoilComponentStateV2( objectFilterDropdownSearchInputComponentState, @@ -76,12 +74,10 @@ export const AdvancedFilterFieldSelectMenu = ({ (fieldMetadataItem) => !visibleColumnsIds.includes(fieldMetadataItem.id), ); - const selectableFieldMetadataItemIds = filterableFieldMetadataItems.map( - (fieldMetadataItem) => fieldMetadataItem.id, + const { resetSelectedItem } = useSelectableList( + advancedFilterFieldSelectDropdownId, ); - const { resetSelectedItem } = useSelectableList(OBJECT_FILTER_DROPDOWN_ID); - const { selectFieldUsedInAdvancedFilterDropdown } = useSelectFieldUsedInAdvancedFilterDropdown(); @@ -98,18 +94,6 @@ export const AdvancedFilterFieldSelectMenu = ({ fieldMetadataItemIdUsedInDropdownComponentState, ); - const handleEnter = (fieldMetadataItemId: string) => { - const selectedFieldMetadataItem = filterableFieldMetadataItems.find( - (fieldMetadataItem) => fieldMetadataItem.id === fieldMetadataItemId, - ); - - if (!isDefined(selectedFieldMetadataItem)) { - return; - } - - handleFieldMetadataItemSelect(selectedFieldMetadataItem); - }; - const handleFieldMetadataItemSelect = ( selectedFieldMetadataItem: FieldMetadataItem, ) => { @@ -138,41 +122,55 @@ export const AdvancedFilterFieldSelectMenu = ({ visibleColumnsFieldMetadataItems.length > 0 && hiddenColumnsFieldMetadataItems.length > 0; + const selectableItemIdArray = [ + ...visibleColumnsFieldMetadataItems.map( + (fieldMetadataItem) => fieldMetadataItem.id, + ), + ...hiddenColumnsFieldMetadataItems.map( + (fieldMetadataItem) => fieldMetadataItem.id, + ), + ]; + return ( <> {visibleColumnsFieldMetadataItems.map( (visibleFieldMetadataItem, index) => ( - { + handleFieldMetadataItemSelect(visibleFieldMetadataItem); + }} > - + ), )} {shouldShowSeparator && } {hiddenColumnsFieldMetadataItems.map( (hiddenFieldMetadataItem, index) => ( - { + handleFieldMetadataItemSelect(hiddenFieldMetadataItem); + }} > - + ), )} diff --git a/packages/twenty-front/src/modules/object-record/advanced-filter/components/AdvancedFilterRecordFilterOperandSelect.tsx b/packages/twenty-front/src/modules/object-record/advanced-filter/components/AdvancedFilterRecordFilterOperandSelect.tsx index 97bfeb526..f26e9654f 100644 --- a/packages/twenty-front/src/modules/object-record/advanced-filter/components/AdvancedFilterRecordFilterOperandSelect.tsx +++ b/packages/twenty-front/src/modules/object-record/advanced-filter/components/AdvancedFilterRecordFilterOperandSelect.tsx @@ -11,6 +11,9 @@ import { SelectControl } from '@/ui/input/components/SelectControl'; 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 { SelectableList } from '@/ui/layout/selectable-list/components/SelectableList'; +import { SelectableListItem } from '@/ui/layout/selectable-list/components/SelectableListItem'; +import { selectedItemIdComponentState } from '@/ui/layout/selectable-list/states/selectedItemIdComponentState'; import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2'; import { ViewFilterOperand } from '@/views/types/ViewFilterOperand'; import styled from '@emotion/styled'; @@ -85,6 +88,11 @@ export const AdvancedFilterRecordFilterOperandSelect = ({ }) : []; + const selectedItemId = useRecoilComponentValueV2( + selectedItemIdComponentState, + dropdownId, + ); + if (isDisabled === true) { return ( - {operandsForFilterType.map((filterOperand, index) => ( - { - handleOperandChange(filterOperand); - }} - text={getOperandLabel(filterOperand)} - /> - ))} + operand, + )} + selectableListInstanceId={dropdownId} + > + {operandsForFilterType.map((filterOperand, index) => ( + { + handleOperandChange(filterOperand); + }} + > + { + handleOperandChange(filterOperand); + }} + text={getOperandLabel(filterOperand)} + /> + + ))} + } dropdownHotkeyScope={{ scope: dropdownId }} diff --git a/packages/twenty-front/src/modules/object-record/advanced-filter/components/AdvancedFilterSubFieldSelectMenu.tsx b/packages/twenty-front/src/modules/object-record/advanced-filter/components/AdvancedFilterSubFieldSelectMenu.tsx index c6a7161ff..13cb22dfd 100644 --- a/packages/twenty-front/src/modules/object-record/advanced-filter/components/AdvancedFilterSubFieldSelectMenu.tsx +++ b/packages/twenty-front/src/modules/object-record/advanced-filter/components/AdvancedFilterSubFieldSelectMenu.tsx @@ -15,6 +15,9 @@ import { isCompositeFieldTypeSubFieldsFilterable } from '@/object-record/record- import { SETTINGS_COMPOSITE_FIELD_TYPE_CONFIGS } from '@/settings/data-model/constants/SettingsCompositeFieldTypeConfigs'; 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'; +import { SelectableListItem } from '@/ui/layout/selectable-list/components/SelectableListItem'; +import { selectedItemIdComponentState } from '@/ui/layout/selectable-list/states/selectedItemIdComponentState'; import { useRecoilComponentStateV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentStateV2'; import { IconApps, IconChevronLeft, useIcons } from 'twenty-ui/display'; import { MenuItem } from 'twenty-ui/navigation'; @@ -72,6 +75,14 @@ export const AdvancedFilterSubFieldSelectMenu = ({ setObjectFilterDropdownIsSelectingCompositeField(false); }; + const { advancedFilterFieldSelectDropdownId } = + useAdvancedFilterFieldSelectDropdown(recordFilterId); + + const selectedItemId = useRecoilComponentValueV2( + selectedItemIdComponentState, + advancedFilterFieldSelectDropdownId, + ); + if (!isDefined(objectFilterDropdownSubMenuFieldType)) { return null; } @@ -86,6 +97,11 @@ export const AdvancedFilterSubFieldSelectMenu = ({ fieldMetadataItemUsedInDropdown.type, ); + const selectableItemIdArray = [ + '-1', + ...options.map((subFieldName) => subFieldName), + ]; + return ( <> - { - handleSelectFilter(fieldMetadataItemUsedInDropdown); - }} - LeftIcon={IconApps} - text={`Any ${getFilterableFieldTypeLabel(objectFilterDropdownSubMenuFieldType)} field`} - /> - {subFieldsAreFilterable && - options.map((subFieldName, index) => ( + + { + handleSelectFilter(fieldMetadataItemUsedInDropdown); + }} + > { - if (isDefined(fieldMetadataItemUsedInDropdown)) { + handleSelectFilter(fieldMetadataItemUsedInDropdown); + }} + LeftIcon={IconApps} + text={`Any ${getFilterableFieldTypeLabel(objectFilterDropdownSubMenuFieldType)} field`} + /> + + {subFieldsAreFilterable && + options.map((subFieldName, index) => ( + { handleSelectFilter( fieldMetadataItemUsedInDropdown, subFieldName, ); - } - }} - text={getCompositeSubFieldLabel( - objectFilterDropdownSubMenuFieldType, - subFieldName, - )} - LeftIcon={getIcon(fieldMetadataItemUsedInDropdown?.icon)} - /> - ))} + }} + > + { + handleSelectFilter( + fieldMetadataItemUsedInDropdown, + subFieldName, + ); + }} + text={getCompositeSubFieldLabel( + objectFilterDropdownSubMenuFieldType, + subFieldName, + )} + LeftIcon={getIcon(fieldMetadataItemUsedInDropdown?.icon)} + /> + + ))} + ); diff --git a/packages/twenty-front/src/modules/object-record/object-filter-dropdown/components/ObjectFilterDropdownBooleanSelect.tsx b/packages/twenty-front/src/modules/object-record/object-filter-dropdown/components/ObjectFilterDropdownBooleanSelect.tsx index 6fb77ac0b..f17f0a66e 100644 --- a/packages/twenty-front/src/modules/object-record/object-filter-dropdown/components/ObjectFilterDropdownBooleanSelect.tsx +++ b/packages/twenty-front/src/modules/object-record/object-filter-dropdown/components/ObjectFilterDropdownBooleanSelect.tsx @@ -94,9 +94,6 @@ export const ObjectFilterDropdownBooleanSelect = () => { selectableListInstanceId="boolean-select" selectableItemIdArray={options.map((option) => option.toString())} hotkeyScope={SingleRecordPickerHotkeyScope.SingleRecordPicker} - onEnter={(itemId) => { - handleOptionSelect(itemId === 'true'); - }} > {options.map((option) => ( diff --git a/packages/twenty-front/src/modules/object-record/object-filter-dropdown/components/ObjectFilterDropdownFilterSelect.tsx b/packages/twenty-front/src/modules/object-record/object-filter-dropdown/components/ObjectFilterDropdownFilterSelect.tsx index 34d630beb..0e3963eb2 100644 --- a/packages/twenty-front/src/modules/object-record/object-filter-dropdown/components/ObjectFilterDropdownFilterSelect.tsx +++ b/packages/twenty-front/src/modules/object-record/object-filter-dropdown/components/ObjectFilterDropdownFilterSelect.tsx @@ -11,14 +11,9 @@ import { objectFilterDropdownSearchInputComponentState } from '@/object-record/o import { useRecordIndexContextOrThrow } from '@/object-record/record-index/contexts/RecordIndexContext'; import { visibleTableColumnsComponentSelector } from '@/object-record/record-table/states/selectors/visibleTableColumnsComponentSelector'; import { DropdownMenuSeparator } from '@/ui/layout/dropdown/components/DropdownMenuSeparator'; -import { SelectableItem } from '@/ui/layout/selectable-list/components/SelectableItem'; import { SelectableList } from '@/ui/layout/selectable-list/components/SelectableList'; -import { useSelectableList } from '@/ui/layout/selectable-list/hooks/useSelectableList'; import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2'; -import { useSetRecoilComponentStateV2 } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentStateV2'; -import { useSelectFilterUsedInDropdown } from '@/object-record/object-filter-dropdown/hooks/useSelectFilterUsedInDropdown'; -import { fieldMetadataItemIdUsedInDropdownComponentState } from '@/object-record/object-filter-dropdown/states/fieldMetadataItemIdUsedInDropdownComponentState'; import { FiltersHotkeyScope } from '@/object-record/object-filter-dropdown/types/FiltersHotkeyScope'; import { useFilterableFieldMetadataItemsInRecordIndexContext } from '@/object-record/record-filter/hooks/useFilterableFieldMetadataItemsInRecordIndexContext'; import { useRecoilComponentStateV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentStateV2'; @@ -96,36 +91,6 @@ export const ObjectFilterDropdownFilterSelect = ({ (fieldMetadataItem) => !visibleColumnsIds.includes(fieldMetadataItem.id), ); - const selectableFieldMetadataItemIds = filterableFieldMetadataItems.map( - (fieldMetadataItem) => fieldMetadataItem.id, - ); - - const { selectFilterUsedInDropdown } = useSelectFilterUsedInDropdown(); - - const setFieldMetadataItemIdUsedInDropdown = useSetRecoilComponentStateV2( - fieldMetadataItemIdUsedInDropdownComponentState, - ); - - const { resetSelectedItem } = useSelectableList(OBJECT_FILTER_DROPDOWN_ID); - - const handleEnter = (fieldMetadataItemId: string) => { - const selectedFieldMetadataItem = filterableFieldMetadataItems.find( - (fieldMetadataItem) => fieldMetadataItem.id === fieldMetadataItemId, - ); - - if (!isDefined(selectedFieldMetadataItem)) { - return; - } - - resetSelectedItem(); - - selectFilterUsedInDropdown({ - fieldMetadataItemId, - }); - - setFieldMetadataItemIdUsedInDropdown(fieldMetadataItemId); - }; - const shouldShowSeparator = visibleColumnsFieldMetadataItems.length > 0 && hiddenColumnsFieldMetadataItems.length > 0; @@ -137,6 +102,15 @@ export const ObjectFilterDropdownFilterSelect = ({ const { t } = useLingui(); + const selectableFieldMetadataItemIds = [ + ...visibleColumnsFieldMetadataItems.map( + (fieldMetadataItem) => fieldMetadataItem.id, + ), + ...hiddenColumnsFieldMetadataItems.map( + (fieldMetadataItem) => fieldMetadataItem.id, + ), + ]; + return ( <> - {visibleColumnsFieldMetadataItems.map( - (visibleFieldMetadataItem, index) => ( - - - - ), - )} + {visibleColumnsFieldMetadataItems.map((visibleFieldMetadataItem) => ( + + ))} {shouldShowSeparator && } - {hiddenColumnsFieldMetadataItems.map( - (hiddenFieldMetadataItem, index) => ( - - - - ), - )} + {hiddenColumnsFieldMetadataItems.map((hiddenFieldMetadataItem) => ( + + ))} {shouldShowAdvancedFilterButton && } 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 14e5e27c4..855586469 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 @@ -1,5 +1,6 @@ import { FieldMetadataItem } from '@/object-metadata/types/FieldMetadataItem'; import { getFilterTypeFromFieldType } from '@/object-metadata/utils/formatFieldMetadataItemsAsFilterDefinitions'; +import { OBJECT_FILTER_DROPDOWN_ID } from '@/object-record/object-filter-dropdown/constants/ObjectFilterDropdownId'; import { fieldMetadataItemIdUsedInDropdownComponentState } from '@/object-record/object-filter-dropdown/states/fieldMetadataItemIdUsedInDropdownComponentState'; import { fieldMetadataItemUsedInDropdownComponentSelector } from '@/object-record/object-filter-dropdown/states/fieldMetadataItemUsedInDropdownComponentSelector'; import { objectFilterDropdownFilterIsSelectedComponentState } from '@/object-record/object-filter-dropdown/states/objectFilterDropdownFilterIsSelectedComponentState'; @@ -9,6 +10,7 @@ import { objectFilterDropdownSubMenuFieldTypeComponentState } from '@/object-rec import { selectedFilterComponentState } from '@/object-record/object-filter-dropdown/states/selectedFilterComponentState'; import { selectedOperandInDropdownComponentState } from '@/object-record/object-filter-dropdown/states/selectedOperandInDropdownComponentState'; import { subFieldNameUsedInDropdownComponentState } from '@/object-record/object-filter-dropdown/states/subFieldNameUsedInDropdownComponentState'; +import { FiltersHotkeyScope } from '@/object-record/object-filter-dropdown/types/FiltersHotkeyScope'; import { getCompositeSubFieldLabel } from '@/object-record/object-filter-dropdown/utils/getCompositeSubFieldLabel'; import { getFilterableFieldTypeLabel } from '@/object-record/object-filter-dropdown/utils/getFilterableFieldTypeLabel'; import { currentRecordFiltersComponentState } from '@/object-record/record-filter/states/currentRecordFiltersComponentState'; @@ -19,6 +21,9 @@ import { SETTINGS_COMPOSITE_FIELD_TYPE_CONFIGS } from '@/settings/data-model/con 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 { SelectableList } from '@/ui/layout/selectable-list/components/SelectableList'; +import { SelectableListItem } from '@/ui/layout/selectable-list/components/SelectableListItem'; +import { selectedItemIdComponentState } from '@/ui/layout/selectable-list/states/selectedItemIdComponentState'; import { useRecoilComponentStateV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentStateV2'; import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2'; import { useSetRecoilComponentStateV2 } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentStateV2'; @@ -130,6 +135,10 @@ export const ObjectFilterDropdownFilterSelectCompositeFieldSubMenu = () => { setObjectFilterDropdownFilterIsSelected(false); setSubFieldNameUsedInDropdown(null); }; + const selectedItemId = useRecoilComponentValueV2( + selectedItemIdComponentState, + OBJECT_FILTER_DROPDOWN_ID, + ); if (!isDefined(objectFilterDropdownSubMenuFieldType)) { return null; @@ -170,35 +179,65 @@ export const ObjectFilterDropdownFilterSelectCompositeFieldSubMenu = () => { } /> */} - { - handleSelectFilter(fieldMetadataItemUsedInDropdown); - }} - LeftIcon={IconApps} - text={`Any ${getFilterableFieldTypeLabel(objectFilterDropdownSubMenuFieldType)} field`} - /> - {subFieldsAreFilterable && - options.map((subFieldName, index) => ( + + { + handleSelectFilter(fieldMetadataItemUsedInDropdown); + }} + > { - if (isDefined(fieldMetadataItemUsedInDropdown)) { + handleSelectFilter(fieldMetadataItemUsedInDropdown); + }} + LeftIcon={IconApps} + text={`Any ${getFilterableFieldTypeLabel( + objectFilterDropdownSubMenuFieldType, + )} field`} + /> + + + {subFieldsAreFilterable && + options.map((subFieldName, index) => ( + { handleSelectFilter( fieldMetadataItemUsedInDropdown, subFieldName, ); - } - }} - text={getCompositeSubFieldLabel( - objectFilterDropdownSubMenuFieldType, - subFieldName, - )} - LeftIcon={getIcon(fieldMetadataItemUsedInDropdown?.icon)} - /> - ))} + }} + > + { + if (isDefined(fieldMetadataItemUsedInDropdown)) { + handleSelectFilter( + fieldMetadataItemUsedInDropdown, + subFieldName, + ); + } + }} + text={getCompositeSubFieldLabel( + objectFilterDropdownSubMenuFieldType, + subFieldName, + )} + LeftIcon={getIcon(fieldMetadataItemUsedInDropdown?.icon)} + /> + + ))} + ); diff --git a/packages/twenty-front/src/modules/object-record/object-filter-dropdown/components/ObjectFilterDropdownFilterSelectMenuItem.tsx b/packages/twenty-front/src/modules/object-record/object-filter-dropdown/components/ObjectFilterDropdownFilterSelectMenuItem.tsx index ad6a6142e..be665e28f 100644 --- a/packages/twenty-front/src/modules/object-record/object-filter-dropdown/components/ObjectFilterDropdownFilterSelectMenuItem.tsx +++ b/packages/twenty-front/src/modules/object-record/object-filter-dropdown/components/ObjectFilterDropdownFilterSelectMenuItem.tsx @@ -14,6 +14,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 { SingleRecordPickerHotkeyScope } from '@/object-record/record-picker/single-record-picker/types/SingleRecordPickerHotkeyScope'; +import { SelectableListItem } from '@/ui/layout/selectable-list/components/SelectableListItem'; import { useSelectableList } from '@/ui/layout/selectable-list/hooks/useSelectableList'; import { isSelectedItemIdComponentFamilySelector } from '@/ui/layout/selectable-list/states/selectors/isSelectedItemIdComponentFamilySelector'; import { useSetHotkeyScope } from '@/ui/utilities/hotkey/hooks/useSetHotkeyScope'; @@ -23,7 +24,7 @@ import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/ import { useSetRecoilComponentStateV2 } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentStateV2'; import { isDefined } from 'twenty-shared/utils'; import { useIcons } from 'twenty-ui/display'; -import { MenuItemSelect } from 'twenty-ui/navigation'; +import { MenuItem } from 'twenty-ui/navigation'; export type ObjectFilterDropdownFilterSelectMenuItemProps = { fieldMetadataItemToSelect: FieldMetadataItem; @@ -132,13 +133,17 @@ export const ObjectFilterDropdownFilterSelectMenuItem = ({ }; return ( - + + + ); }; diff --git a/packages/twenty-front/src/modules/object-record/object-filter-dropdown/components/ObjectFilterDropdownFilterSelectMenuItemV2.tsx b/packages/twenty-front/src/modules/object-record/object-filter-dropdown/components/ObjectFilterDropdownFilterSelectMenuItemV2.tsx index 267725671..bd7b4e59e 100644 --- a/packages/twenty-front/src/modules/object-record/object-filter-dropdown/components/ObjectFilterDropdownFilterSelectMenuItemV2.tsx +++ b/packages/twenty-front/src/modules/object-record/object-filter-dropdown/components/ObjectFilterDropdownFilterSelectMenuItemV2.tsx @@ -6,7 +6,7 @@ import { useSelectableList } from '@/ui/layout/selectable-list/hooks/useSelectab import { isSelectedItemIdComponentFamilySelector } from '@/ui/layout/selectable-list/states/selectors/isSelectedItemIdComponentFamilySelector'; import { useRecoilComponentFamilyValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentFamilyValueV2'; import { useIcons } from 'twenty-ui/display'; -import { MenuItemSelect } from 'twenty-ui/navigation'; +import { MenuItem } from 'twenty-ui/navigation'; export type ObjectFilterDropdownFilterSelectMenuItemV2Props = { fieldMetadataItemToSelect: FieldMetadataItem; @@ -37,9 +37,8 @@ export const ObjectFilterDropdownFilterSelectMenuItemV2 = ({ }; return ( - { selectableListInstanceId={componentInstanceId} selectableItemIdArray={objectRecordsIds} hotkeyScope={SingleRecordPickerHotkeyScope.SingleRecordPicker} - onEnter={(itemId) => { - const option = optionsInDropdown.find((option) => option.id === itemId); - if (isDefined(option)) { - handleMultipleOptionSelectChange(option, !option.isSelected); - } - }} > {optionsInDropdown?.map((option) => ( 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 f00ae7e47..e53339e53 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 @@ -1,13 +1,18 @@ +import { OBJECT_OPTIONS_DROPDOWN_ID } from '@/object-record/object-options-dropdown/constants/ObjectOptionsDropdownId'; import { useObjectOptionsForBoard } from '@/object-record/object-options-dropdown/hooks/useObjectOptionsForBoard'; import { useOptionsDropdown } from '@/object-record/object-options-dropdown/hooks/useOptionsDropdown'; import { useSetViewTypeFromLayoutOptionsMenu } from '@/object-record/object-options-dropdown/hooks/useSetViewTypeFromLayoutOptionsMenu'; 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 { 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 { 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'; +import { selectedItemIdComponentState } from '@/ui/layout/selectable-list/states/selectedItemIdComponentState'; import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2'; import { useGetCurrentViewOnly } from '@/views/hooks/useGetCurrentViewOnly'; import { ViewOpenRecordInType } from '@/views/types/ViewOpenRecordInType'; @@ -74,6 +79,18 @@ export const ObjectOptionsDropdownLayoutContent = () => { const isDefaultView = currentView?.key === 'INDEX'; const nbsp = '\u00A0'; + const selectableItemIdArray = [ + ViewType.Table, + ...(isDefaultView ? [] : [ViewType.Kanban]), + ViewOpenRecordInType.SIDE_PANEL, + ...(currentView?.type === ViewType.Kanban ? ['Group', 'Compact view'] : []), + ]; + + const selectedItemId = useRecoilComponentValueV2( + selectedItemIdComponentState, + OBJECT_OPTIONS_DROPDOWN_ID, + ); + return ( <> { > {t`Layout`} + {!!currentView && ( - { - if (currentView?.type !== ViewType.Table) { - await setAndPersistViewType(ViewType.Table); - } - }} - /> - - {nbsp}·{nbsp} - - - ) : availableFieldsForKanban.length === 0 ? ( - t`Create Select...` - ) : undefined - } - selected={currentView?.type === ViewType.Kanban} - onClick={handleSelectKanbanViewType} - /> - - onContentChange('layoutOpenIn')} - LeftIcon={ - recordIndexOpenRecordIn === ViewOpenRecordInType.SIDE_PANEL - ? IconLayoutSidebarRight - : IconLayoutNavbar - } - text={t`Open in`} - contextualText={ - recordIndexOpenRecordIn === ViewOpenRecordInType.SIDE_PANEL - ? t`Side Panel` - : t`Record Page` - } - hasSubMenu - /> - {currentView?.type === ViewType.Kanban && ( - <> - - isDefined(recordGroupFieldMetadata) - ? onContentChange('recordGroups') - : onContentChange('recordGroupFields') + + { + setAndPersistViewType(ViewType.Table); + }} + > + { + if (currentView?.type !== ViewType.Table) { + await setAndPersistViewType(ViewType.Table); + } + }} + /> + + { + setAndPersistViewType(ViewType.Kanban); + }} + > + + {nbsp}·{nbsp} + + + ) : availableFieldsForKanban.length === 0 ? ( + t`Create Select...` + ) : undefined + } + selected={currentView?.type === ViewType.Kanban} + onClick={handleSelectKanbanViewType} + /> + + + { + onContentChange('layoutOpenIn'); + }} + > + + + {currentView?.type === ViewType.Kanban && ( + <> + { + isDefined(recordGroupFieldMetadata) + ? onContentChange('recordGroups') + : onContentChange('recordGroupFields'); + }} + > + + isDefined(recordGroupFieldMetadata) + ? onContentChange('recordGroups') + : onContentChange('recordGroupFields') + } + LeftIcon={IconLayoutList} + text={t`Group`} + contextualText={recordGroupFieldMetadata?.label} + hasSubMenu + /> + - - setAndPersistIsCompactModeActive( - !isCompactModeActive, - currentView, - ) - } - toggled={isCompactModeActive} - text={t`Compact view`} - toggleSize="small" - /> - - )} + { + setAndPersistIsCompactModeActive( + !isCompactModeActive, + currentView, + ); + }} + > + + setAndPersistIsCompactModeActive( + !isCompactModeActive, + currentView, + ) + } + toggled={isCompactModeActive} + text={t`Compact view`} + toggleSize="small" + /> + + + )} + )} 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 d7cabe7d7..cefa938bb 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 @@ -1,9 +1,15 @@ +import { OBJECT_OPTIONS_DROPDOWN_ID } from '@/object-record/object-options-dropdown/constants/ObjectOptionsDropdownId'; import { useOptionsDropdown } from '@/object-record/object-options-dropdown/hooks/useOptionsDropdown'; 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 { 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 { SelectableList } from '@/ui/layout/selectable-list/components/SelectableList'; +import { SelectableListItem } from '@/ui/layout/selectable-list/components/SelectableListItem'; +import { selectedItemIdComponentState } from '@/ui/layout/selectable-list/states/selectedItemIdComponentState'; +import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2'; import { useGetCurrentViewOnly } from '@/views/hooks/useGetCurrentViewOnly'; import { ViewOpenRecordInType } from '@/views/types/ViewOpenRecordInType'; import { t } from '@lingui/core/macro'; @@ -21,6 +27,16 @@ export const ObjectOptionsDropdownLayoutOpenInContent = () => { const { currentView } = useGetCurrentViewOnly(); const { setAndPersistOpenRecordIn } = useUpdateObjectViewOptions(); + const selectedItemId = useRecoilComponentValueV2( + selectedItemIdComponentState, + OBJECT_OPTIONS_DROPDOWN_ID, + ); + + const selectableItemIdArray = [ + ViewOpenRecordInType.SIDE_PANEL, + ViewOpenRecordInType.RECORD_PAGE, + ]; + return ( <> { {t`Open in`} - - setAndPersistOpenRecordIn( - ViewOpenRecordInType.SIDE_PANEL, - currentView, - ) - } - /> - - setAndPersistOpenRecordIn( - ViewOpenRecordInType.RECORD_PAGE, - currentView, - ) - } - /> + + + setAndPersistOpenRecordIn( + ViewOpenRecordInType.SIDE_PANEL, + currentView, + ) + } + > + + setAndPersistOpenRecordIn( + ViewOpenRecordInType.SIDE_PANEL, + currentView, + ) + } + /> + + + setAndPersistOpenRecordIn( + ViewOpenRecordInType.RECORD_PAGE, + currentView, + ) + } + > + + setAndPersistOpenRecordIn( + ViewOpenRecordInType.RECORD_PAGE, + currentView, + ) + } + focused={selectedItemId === ViewOpenRecordInType.RECORD_PAGE} + /> + + ); 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 7caa6457c..2857d1fc5 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 @@ -1,6 +1,7 @@ import { Key } from 'ts-key-enum'; import { ObjectOptionsDropdownMenuViewName } from '@/object-record/object-options-dropdown/components/ObjectOptionsDropdownMenuViewName'; +import { OBJECT_OPTIONS_DROPDOWN_ID } from '@/object-record/object-options-dropdown/constants/ObjectOptionsDropdownId'; import { useObjectOptionsForBoard } from '@/object-record/object-options-dropdown/hooks/useObjectOptionsForBoard'; import { useOptionsDropdown } from '@/object-record/object-options-dropdown/hooks/useOptionsDropdown'; import { recordGroupFieldMetadataComponentState } from '@/object-record/record-group/states/recordGroupFieldMetadataComponentState'; @@ -9,6 +10,9 @@ import { SnackBarVariant } from '@/ui/feedback/snack-bar-manager/components/Snac import { useSnackBar } from '@/ui/feedback/snack-bar-manager/hooks/useSnackBar'; 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'; +import { SelectableListItem } from '@/ui/layout/selectable-list/components/SelectableListItem'; +import { selectedItemIdComponentState } from '@/ui/layout/selectable-list/states/selectedItemIdComponentState'; import { useScopedHotkeys } from '@/ui/utilities/hotkey/hooks/useScopedHotkeys'; import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2'; import { useSetRecoilComponentStateV2 } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentStateV2'; @@ -75,92 +79,155 @@ export const ObjectOptionsDropdownMenuContent = () => { const isDefaultView = currentView?.key === 'INDEX'; + const selectableItemIdArray = [ + 'Layout', + 'Fields', + ...(isDefaultView ? [] : ['Group']), + 'Copy link to view', + ...(isDefaultView ? [] : ['Delete view']), + ]; + + const selectedItemId = useRecoilComponentValueV2( + selectedItemIdComponentState, + OBJECT_OPTIONS_DROPDOWN_ID, + ); + return ( <> {currentView && ( )} - - onContentChange('layout')} - LeftIcon={viewTypeIconMapping(currentView?.type ?? ViewType.Table)} - text={t`Layout`} - contextualText={`${capitalize(currentView?.type ?? '')}`} - hasSubMenu - /> - - - - - onContentChange('fields')} - LeftIcon={IconListDetails} - text={t`Fields`} - contextualText={`${visibleBoardFields.length} shown`} - hasSubMenu - /> - -
- - isDefined(recordGroupFieldMetadata) - ? onContentChange('recordGroups') - : onContentChange('recordGroupFields') - } - LeftIcon={IconLayoutList} - text={t`Group`} - contextualText={ - isDefaultView - ? t`Not available on Default View` - : recordGroupFieldMetadata?.label - } - hasSubMenu - disabled={isDefaultView} - /> -
- {!isGroupByEnabled && ( - - )} + + + onContentChange('layout')} + > + onContentChange('layout')} + LeftIcon={viewTypeIconMapping( + currentView?.type ?? ViewType.Table, + )} + text={t`Layout`} + contextualText={`${capitalize(currentView?.type ?? '')}`} + hasSubMenu + /> + + - { - const currentUrl = window.location.href; - navigator.clipboard.writeText(currentUrl); - enqueueSnackBar('Link copied to clipboard', { - variant: SnackBarVariant.Success, - icon: , - duration: 2000, - }); - }} - LeftIcon={IconCopy} - text={t`Copy link to view`} - /> -
- handleDelete()} - LeftIcon={IconTrash} - text={t`Delete view`} - disabled={currentView?.key === 'INDEX'} - /> -
- {currentView?.key === 'INDEX' && ( - - )} -
+ + onContentChange('fields')} + > + onContentChange('fields')} + LeftIcon={IconListDetails} + text={t`Fields`} + contextualText={`${visibleBoardFields.length} shown`} + hasSubMenu + /> + + +
+ + isDefined(recordGroupFieldMetadata) + ? onContentChange('recordGroups') + : onContentChange('recordGroupFields') + } + > + + isDefined(recordGroupFieldMetadata) + ? onContentChange('recordGroups') + : onContentChange('recordGroupFields') + } + LeftIcon={IconLayoutList} + text={t`Group`} + contextualText={ + isDefaultView + ? t`Not available on Default View` + : recordGroupFieldMetadata?.label + } + hasSubMenu + disabled={isDefaultView} + /> + +
+ {!isGroupByEnabled && ( + + )} + + + { + const currentUrl = window.location.href; + navigator.clipboard.writeText(currentUrl); + enqueueSnackBar('Link copied to clipboard', { + variant: SnackBarVariant.Success, + icon: , + duration: 2000, + }); + }} + > + { + const currentUrl = window.location.href; + navigator.clipboard.writeText(currentUrl); + enqueueSnackBar('Link copied to clipboard', { + variant: SnackBarVariant.Success, + icon: , + duration: 2000, + }); + }} + LeftIcon={IconCopy} + text={t`Copy link to view`} + /> + +
+ handleDelete()} + > + handleDelete()} + LeftIcon={IconTrash} + text={t`Delete view`} + disabled={currentView?.key === 'INDEX'} + /> + +
+ {currentView?.key === 'INDEX' && ( + + )} +
+ ); }; 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 30120a903..e0f7468a4 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 @@ -1,14 +1,19 @@ import { useEffect } from 'react'; +import { OBJECT_OPTIONS_DROPDOWN_ID } from '@/object-record/object-options-dropdown/constants/ObjectOptionsDropdownId'; import { useOptionsDropdown } from '@/object-record/object-options-dropdown/hooks/useOptionsDropdown'; 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 { TableOptionsHotkeyScope } from '@/object-record/record-table/types/TableOptionsHotkeyScope'; 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 { SelectableList } from '@/ui/layout/selectable-list/components/SelectableList'; +import { SelectableListItem } from '@/ui/layout/selectable-list/components/SelectableListItem'; +import { selectedItemIdComponentState } from '@/ui/layout/selectable-list/states/selectedItemIdComponentState'; import { useRecoilComponentStateV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentStateV2'; import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2'; -import { DropdownMenuHeaderLeftComponent } from '@/ui/layout/dropdown/components/DropdownMenuHeader/internal/DropdownMenuHeaderLeftComponent'; import { IconChevronLeft, IconHandMove, @@ -32,6 +37,11 @@ export const ObjectOptionsDropdownRecordGroupSortContent = () => { setRecordGroupSort(sort); }; + const selectedItemId = useRecoilComponentValueV2( + selectedItemIdComponentState, + OBJECT_OPTIONS_DROPDOWN_ID, + ); + useEffect(() => { if ( currentContentId === 'hiddenRecordGroups' && @@ -41,6 +51,12 @@ export const ObjectOptionsDropdownRecordGroupSortContent = () => { } }, [hiddenRecordGroupIds, currentContentId, onContentChange]); + const selectableItemIdArray = [ + RecordGroupSort.Manual, + RecordGroupSort.Alphabetical, + RecordGroupSort.ReverseAlphabetical, + ]; + return ( <> { Sort - handleRecordGroupSortChange(RecordGroupSort.Manual)} - LeftIcon={IconHandMove} - text={RecordGroupSort.Manual} - selected={recordGroupSort === RecordGroupSort.Manual} - /> - - handleRecordGroupSortChange(RecordGroupSort.Alphabetical) - } - LeftIcon={IconSortAZ} - text={RecordGroupSort.Alphabetical} - selected={recordGroupSort === RecordGroupSort.Alphabetical} - /> - - handleRecordGroupSortChange(RecordGroupSort.ReverseAlphabetical) - } - LeftIcon={IconSortZA} - text={RecordGroupSort.ReverseAlphabetical} - selected={recordGroupSort === RecordGroupSort.ReverseAlphabetical} - /> + + handleRecordGroupSortChange(RecordGroupSort.Manual)} + > + + handleRecordGroupSortChange(RecordGroupSort.Manual) + } + LeftIcon={IconHandMove} + text={RecordGroupSort.Manual} + selected={recordGroupSort === RecordGroupSort.Manual} + focused={selectedItemId === RecordGroupSort.Manual} + /> + + + handleRecordGroupSortChange(RecordGroupSort.Alphabetical) + } + > + + handleRecordGroupSortChange(RecordGroupSort.Alphabetical) + } + LeftIcon={IconSortAZ} + text={RecordGroupSort.Alphabetical} + selected={recordGroupSort === RecordGroupSort.Alphabetical} + focused={selectedItemId === RecordGroupSort.Alphabetical} + /> + + + handleRecordGroupSortChange(RecordGroupSort.ReverseAlphabetical) + } + > + + handleRecordGroupSortChange(RecordGroupSort.ReverseAlphabetical) + } + LeftIcon={IconSortZA} + text={RecordGroupSort.ReverseAlphabetical} + selected={recordGroupSort === RecordGroupSort.ReverseAlphabetical} + focused={selectedItemId === RecordGroupSort.ReverseAlphabetical} + /> + + ); 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 e68e87892..d9d78a1a4 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 @@ -1,5 +1,6 @@ import { useEffect } from 'react'; +import { OBJECT_OPTIONS_DROPDOWN_ID } from '@/object-record/object-options-dropdown/constants/ObjectOptionsDropdownId'; import { useOptionsDropdown } from '@/object-record/object-options-dropdown/hooks/useOptionsDropdown'; import { RecordGroupReorderConfirmationModal } from '@/object-record/record-group/components/RecordGroupReorderConfirmationModal'; import { RecordGroupsVisibilityDropdownSection } from '@/object-record/record-group/components/RecordGroupsVisibilityDropdownSection'; @@ -10,10 +11,14 @@ 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 { TableOptionsHotkeyScope } from '@/object-record/record-table/types/TableOptionsHotkeyScope'; 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 { SelectableList } from '@/ui/layout/selectable-list/components/SelectableList'; +import { SelectableListItem } from '@/ui/layout/selectable-list/components/SelectableListItem'; +import { selectedItemIdComponentState } from '@/ui/layout/selectable-list/states/selectedItemIdComponentState'; import { useRecoilComponentFamilyValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentFamilyValueV2'; import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2'; import { useGetCurrentViewOnly } from '@/views/hooks/useGetCurrentViewOnly'; @@ -89,6 +94,17 @@ export const ObjectOptionsDropdownRecordGroupsContent = () => { } }, [hiddenRecordGroupIds, currentContentId, onContentChange]); + const selectedItemId = useRecoilComponentValueV2( + selectedItemIdComponentState, + OBJECT_OPTIONS_DROPDOWN_ID, + ); + + const selectableItemIdArray = [ + ...(currentView?.key !== 'INDEX' ? ['GroupBy', 'Sort'] : []), + 'HideEmptyGroups', + ...(hiddenRecordGroupIds.length > 0 ? ['HiddenGroups'] : []), + ]; + return ( <> { Group - {currentView?.key !== 'INDEX' && ( - <> - onContentChange('recordGroupFields')} - LeftIcon={IconLayoutList} - text={t`Group by`} - contextualText={recordGroupFieldMetadata?.label} - hasSubMenu + + {currentView?.key !== 'INDEX' && ( + <> + onContentChange('recordGroupFields')} + > + onContentChange('recordGroupFields')} + LeftIcon={IconLayoutList} + text={t`Group by`} + contextualText={recordGroupFieldMetadata?.label} + hasSubMenu + /> + + onContentChange('recordGroupSort')} + > + onContentChange('recordGroupSort')} + LeftIcon={IconSortDescending} + text={t`Sort`} + contextualText={recordGroupSort} + hasSubMenu + /> + + + )} + handleHideEmptyRecordGroupChange()} + > + - onContentChange('recordGroupSort')} - LeftIcon={IconSortDescending} - text={t`Sort`} - contextualText={recordGroupSort} - hasSubMenu - /> - - )} - + + {visibleRecordGroupIds.length > 0 && ( <> @@ -145,11 +185,16 @@ export const ObjectOptionsDropdownRecordGroupsContent = () => { <> - onContentChange('hiddenRecordGroups')} - LeftIcon={IconEyeOff} - text={`Hidden ${recordGroupFieldMetadata?.label ?? ''}`} - /> + onContentChange('hiddenRecordGroups')} + > + onContentChange('hiddenRecordGroups')} + LeftIcon={IconEyeOff} + text={`Hidden ${recordGroupFieldMetadata?.label ?? ''}`} + /> + )} 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 c70bde46b..59b1c4bf0 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 @@ -24,16 +24,19 @@ import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/Drop import { DropdownMenuSeparator } from '@/ui/layout/dropdown/components/DropdownMenuSeparator'; import { StyledHeaderDropdownButton } from '@/ui/layout/dropdown/components/StyledHeaderDropdownButton'; 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'; +import { selectedItemIdComponentState } from '@/ui/layout/selectable-list/states/selectedItemIdComponentState'; import { HotkeyScope } from '@/ui/utilities/hotkey/types/HotkeyScope'; import { useRecoilComponentStateV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentStateV2'; import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2'; import { useSetRecoilComponentStateV2 } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentStateV2'; +import { useTheme } from '@emotion/react'; import { Trans, useLingui } from '@lingui/react/macro'; import { useRecoilValue } from 'recoil'; -import { v4 } from 'uuid'; -import { useTheme } from '@emotion/react'; import { IconChevronDown, useIcons } from 'twenty-ui/display'; import { MenuItem } from 'twenty-ui/navigation'; +import { v4 } from 'uuid'; export const StyledInput = styled.input` background: transparent; @@ -191,6 +194,21 @@ export const ObjectSortDropdownButton = ({ const theme = useTheme(); + const selectableItemIdArray = [ + ...visibleFieldMetadataItems.map((item) => item.id), + ...hiddenFieldMetadataItems.map((item) => item.id), + ]; + + const selectedItemId = useRecoilComponentValueV2( + selectedItemIdComponentState, + OBJECT_SORT_DROPDOWN_ID, + ); + + const setSelectedItemId = useSetRecoilComponentStateV2( + selectedItemIdComponentState, + OBJECT_SORT_DROPDOWN_ID, + ); + return ( { + handleButtonClick(); + setSelectedItemId(selectableItemIdArray[0]); + }} isUnfolded={isDropdownOpen} > Sort } dropdownComponents={ - <> + {isRecordSortDirectionMenuUnfolded && ( {RECORD_SORT_DIRECTIONS.map((sortDirection, index) => ( handleSortDirectionClick(sortDirection)} text={ sortDirection === 'asc' ? t`Ascending` : t`Descending` @@ -244,27 +270,39 @@ export const ObjectSortDropdownButton = ({ {visibleFieldMetadataItems.map( (visibleFieldMetadataItem, index) => ( - handleAddSort(visibleFieldMetadataItem)} - LeftIcon={getIcon(visibleFieldMetadataItem.icon)} - text={visibleFieldMetadataItem.label} - /> + handleAddSort(visibleFieldMetadataItem)} + > + handleAddSort(visibleFieldMetadataItem)} + LeftIcon={getIcon(visibleFieldMetadataItem.icon)} + text={visibleFieldMetadataItem.label} + /> + ), )} {shouldShowSeparator && } {hiddenFieldMetadataItems.map((hiddenFieldMetadataItem, index) => ( - handleAddSort(hiddenFieldMetadataItem)} - LeftIcon={getIcon(hiddenFieldMetadataItem.icon)} - text={hiddenFieldMetadataItem.label} - /> + handleAddSort(hiddenFieldMetadataItem)} + > + handleAddSort(hiddenFieldMetadataItem)} + LeftIcon={getIcon(hiddenFieldMetadataItem.icon)} + text={hiddenFieldMetadataItem.label} + /> + ))} - + } onClose={handleDropdownButtonClose} /> diff --git a/packages/twenty-front/src/modules/object-record/record-board/hooks/useRecordBoardSelection.ts b/packages/twenty-front/src/modules/object-record/record-board/hooks/useRecordBoardSelection.ts index 975b79e72..fc7a42347 100644 --- a/packages/twenty-front/src/modules/object-record/record-board/hooks/useRecordBoardSelection.ts +++ b/packages/twenty-front/src/modules/object-record/record-board/hooks/useRecordBoardSelection.ts @@ -4,9 +4,8 @@ import { getActionMenuDropdownIdFromActionMenuId } from '@/action-menu/utils/get import { getActionMenuIdFromRecordIndexId } from '@/action-menu/utils/getActionMenuIdFromRecordIndexId'; import { isRecordBoardCardSelectedComponentFamilyState } from '@/object-record/record-board/states/isRecordBoardCardSelectedComponentFamilyState'; import { recordBoardSelectedRecordIdsComponentSelector } from '@/object-record/record-board/states/selectors/recordBoardSelectedRecordIdsComponentSelector'; -import { isDropdownOpenComponentState } from '@/ui/layout/dropdown/states/isDropdownOpenComponentState'; +import { useDropdownV2 } from '@/ui/layout/dropdown/hooks/useDropdownV2'; import { useRecoilComponentCallbackStateV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentCallbackStateV2'; -import { extractComponentState } from '@/ui/utilities/state/component-state/utils/extractComponentState'; import { getSnapshotValue } from '@/ui/utilities/state/utils/getSnapshotValue'; export const useRecordBoardSelection = (recordBoardId: string) => { @@ -22,17 +21,16 @@ export const useRecordBoardSelection = (recordBoardId: string) => { recordBoardId, ); - const isActionMenuDropdownOpenState = extractComponentState( - isDropdownOpenComponentState, - getActionMenuDropdownIdFromActionMenuId( - getActionMenuIdFromRecordIndexId(recordBoardId), - ), + const { closeDropdown } = useDropdownV2(); + + const dropdownId = getActionMenuDropdownIdFromActionMenuId( + getActionMenuIdFromRecordIndexId(recordBoardId), ); const resetRecordSelection = useRecoilCallback( ({ snapshot, set }) => () => { - set(isActionMenuDropdownOpenState, false); + closeDropdown(dropdownId); const recordIds = getSnapshotValue( snapshot, @@ -44,7 +42,8 @@ export const useRecordBoardSelection = (recordBoardId: string) => { } }, [ - isActionMenuDropdownOpenState, + closeDropdown, + dropdownId, recordBoardSelectedRecordIdsSelector, isRecordBoardCardSelectedFamilyState, ], @@ -67,17 +66,17 @@ export const useRecordBoardSelection = (recordBoardId: string) => { ); const checkIfLastUnselectAndCloseDropdown = useRecoilCallback( - ({ snapshot, set }) => + ({ snapshot }) => () => { const recordIds = getSnapshotValue( snapshot, recordBoardSelectedRecordIdsSelector, ); if (recordIds.length === 0) { - set(isActionMenuDropdownOpenState, false); + closeDropdown(dropdownId); } }, - [recordBoardSelectedRecordIdsSelector, isActionMenuDropdownOpenState], + [recordBoardSelectedRecordIdsSelector, closeDropdown, dropdownId], ); return { diff --git a/packages/twenty-front/src/modules/object-record/record-board/record-board-card/components/RecordBoardCard.tsx b/packages/twenty-front/src/modules/object-record/record-board/record-board-card/components/RecordBoardCard.tsx index f0c4c614c..d066372b3 100644 --- a/packages/twenty-front/src/modules/object-record/record-board/record-board-card/components/RecordBoardCard.tsx +++ b/packages/twenty-front/src/modules/object-record/record-board/record-board-card/components/RecordBoardCard.tsx @@ -7,6 +7,7 @@ import { isRecordBoardCardSelectedComponentFamilyState } from '@/object-record/r import { isRecordBoardCompactModeActiveComponentState } from '@/object-record/record-board/states/isRecordBoardCompactModeActiveComponentState'; import { recordBoardVisibleFieldDefinitionsComponentSelector } from '@/object-record/record-board/states/selectors/recordBoardVisibleFieldDefinitionsComponentSelector'; +import { ActionMenuDropdownHotkeyScope } from '@/action-menu/types/ActionMenuDropdownHotKeyScope'; import { useOpenRecordInCommandMenu } from '@/command-menu/hooks/useOpenRecordInCommandMenu'; import { RecordBoardCardBody } from '@/object-record/record-board/record-board-card/components/RecordBoardCardBody'; import { RecordBoardCardHeader } from '@/object-record/record-board/record-board-card/components/RecordBoardCardHeader'; @@ -121,7 +122,9 @@ export const RecordBoardCard = () => { x: event.clientX, y: event.clientY, }); - openDropdown(actionMenuDropdownId); + openDropdown(actionMenuDropdownId, { + scope: ActionMenuDropdownHotkeyScope.ActionMenuDropdown, + }); }; const handleCardClick = () => { diff --git a/packages/twenty-front/src/modules/object-record/record-picker/multiple-record-picker/components/MultipleRecordPicker.tsx b/packages/twenty-front/src/modules/object-record/record-picker/multiple-record-picker/components/MultipleRecordPicker.tsx index 2cd868675..57de829e8 100644 --- a/packages/twenty-front/src/modules/object-record/record-picker/multiple-record-picker/components/MultipleRecordPicker.tsx +++ b/packages/twenty-front/src/modules/object-record/record-picker/multiple-record-picker/components/MultipleRecordPicker.tsx @@ -15,7 +15,7 @@ import { DropdownMenuSkeletonItem } from '@/ui/input/relation-picker/components/ import { DropdownMenu } from '@/ui/layout/dropdown/components/DropdownMenu'; import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer'; import { DropdownMenuSeparator } from '@/ui/layout/dropdown/components/DropdownMenuSeparator'; -import { SelectableItem } from '@/ui/layout/selectable-list/components/SelectableItem'; +import { SelectableListItem } from '@/ui/layout/selectable-list/components/SelectableListItem'; import { useSelectableList } from '@/ui/layout/selectable-list/hooks/useSelectableList'; import { usePreviousHotkeyScope } from '@/ui/utilities/hotkey/hooks/usePreviousHotkeyScope'; import { useScopedHotkeys } from '@/ui/utilities/hotkey/hooks/useScopedHotkeys'; @@ -28,7 +28,7 @@ import { Key } from 'ts-key-enum'; import { isDefined } from 'twenty-shared/utils'; import { IconPlus } from 'twenty-ui/display'; -export const StyledSelectableItem = styled(SelectableItem)` +export const StyledSelectableItem = styled(SelectableListItem)` height: 100%; width: 100%; `; diff --git a/packages/twenty-front/src/modules/object-record/record-picker/multiple-record-picker/components/MultipleRecordPickerMenuItem.tsx b/packages/twenty-front/src/modules/object-record/record-picker/multiple-record-picker/components/MultipleRecordPickerMenuItem.tsx index fdeae6e97..09ac4c196 100644 --- a/packages/twenty-front/src/modules/object-record/record-picker/multiple-record-picker/components/MultipleRecordPickerMenuItem.tsx +++ b/packages/twenty-front/src/modules/object-record/record-picker/multiple-record-picker/components/MultipleRecordPickerMenuItem.tsx @@ -3,10 +3,10 @@ import styled from '@emotion/styled'; import { useRecordPickerGetSearchRecordAndObjectMetadataItemFromRecordId } from '@/object-record/record-picker/hooks/useRecordPickerGetSearchRecordAndObjectMetadataItemFromRecordId'; import { MultipleRecordPickerMenuItemContent } from '@/object-record/record-picker/multiple-record-picker/components/MultipleRecordPickerMenuItemContent'; import { RecordPickerPickableMorphItem } from '@/object-record/record-picker/types/RecordPickerPickableMorphItem'; -import { SelectableItem } from '@/ui/layout/selectable-list/components/SelectableItem'; +import { SelectableListItem } from '@/ui/layout/selectable-list/components/SelectableListItem'; import { isDefined } from 'twenty-shared/utils'; -export const StyledSelectableItem = styled(SelectableItem)` +export const StyledSelectableItem = styled(SelectableListItem)` height: 100%; width: 100%; `; diff --git a/packages/twenty-front/src/modules/object-record/record-picker/multiple-record-picker/components/MultipleRecordPickerMenuItemContent.tsx b/packages/twenty-front/src/modules/object-record/record-picker/multiple-record-picker/components/MultipleRecordPickerMenuItemContent.tsx index b20094847..b99e01728 100644 --- a/packages/twenty-front/src/modules/object-record/record-picker/multiple-record-picker/components/MultipleRecordPickerMenuItemContent.tsx +++ b/packages/twenty-front/src/modules/object-record/record-picker/multiple-record-picker/components/MultipleRecordPickerMenuItemContent.tsx @@ -6,7 +6,7 @@ import { MultipleRecordPickerComponentInstanceContext } from '@/object-record/re import { multipleRecordPickerIsSelectedComponentFamilySelector } from '@/object-record/record-picker/multiple-record-picker/states/selectors/multipleRecordPickerIsSelectedComponentFamilySelector'; import { getMultipleRecordPickerSelectableListId } from '@/object-record/record-picker/multiple-record-picker/utils/getMultipleRecordPickerSelectableListId'; import { RecordPickerPickableMorphItem } from '@/object-record/record-picker/types/RecordPickerPickableMorphItem'; -import { SelectableItem } from '@/ui/layout/selectable-list/components/SelectableItem'; +import { SelectableListItem } from '@/ui/layout/selectable-list/components/SelectableListItem'; import { isSelectedItemIdComponentFamilySelector } from '@/ui/layout/selectable-list/states/selectors/isSelectedItemIdComponentFamilySelector'; import { useAvailableComponentInstanceIdOrThrow } from '@/ui/utilities/state/component-state/hooks/useAvailableComponentInstanceIdOrThrow'; import { useRecoilComponentFamilyValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentFamilyValueV2'; @@ -14,7 +14,7 @@ import { Avatar } from 'twenty-ui/display'; import { MenuItemMultiSelectAvatar } from 'twenty-ui/navigation'; import { SearchRecord } from '~/generated-metadata/graphql'; -export const StyledSelectableItem = styled(SelectableItem)` +export const StyledSelectableItem = styled(SelectableListItem)` height: 100%; width: 100%; `; @@ -62,6 +62,7 @@ export const MultipleRecordPickerMenuItemContent = ({ handleSelectChange(!isRecordSelectedWithObjectItem)} > handleSelectChange(isSelected)} diff --git a/packages/twenty-front/src/modules/object-record/record-picker/multiple-record-picker/components/MultipleRecordPickerMenuItems.tsx b/packages/twenty-front/src/modules/object-record/record-picker/multiple-record-picker/components/MultipleRecordPickerMenuItems.tsx index 60f4a256e..08995df3d 100644 --- a/packages/twenty-front/src/modules/object-record/record-picker/multiple-record-picker/components/MultipleRecordPickerMenuItems.tsx +++ b/packages/twenty-front/src/modules/object-record/record-picker/multiple-record-picker/components/MultipleRecordPickerMenuItems.tsx @@ -4,21 +4,19 @@ import { MultipleRecordPickerMenuItem } from '@/object-record/record-picker/mult import { MultipleRecordPickerComponentInstanceContext } from '@/object-record/record-picker/multiple-record-picker/states/contexts/MultipleRecordPickerComponentInstanceContext'; import { multipleRecordPickerPickableMorphItemsComponentState } from '@/object-record/record-picker/multiple-record-picker/states/multipleRecordPickerPickableMorphItemsComponentState'; import { multipleRecordPickerPickableRecordIdsMatchingSearchComponentSelector } from '@/object-record/record-picker/multiple-record-picker/states/selectors/multipleRecordPickerPickableRecordIdsMatchingSearchComponentSelector'; -import { multipleRecordPickerSinglePickableMorphItemComponentFamilySelector } from '@/object-record/record-picker/multiple-record-picker/states/selectors/multipleRecordPickerSinglePickableMorphItemComponentFamilySelector'; import { MultipleRecordPickerHotkeyScope } from '@/object-record/record-picker/multiple-record-picker/types/MultipleRecordPickerHotkeyScope'; import { getMultipleRecordPickerSelectableListId } from '@/object-record/record-picker/multiple-record-picker/utils/getMultipleRecordPickerSelectableListId'; import { RecordPickerPickableMorphItem } from '@/object-record/record-picker/types/RecordPickerPickableMorphItem'; import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer'; -import { SelectableItem } from '@/ui/layout/selectable-list/components/SelectableItem'; import { SelectableList } from '@/ui/layout/selectable-list/components/SelectableList'; +import { SelectableListItem } from '@/ui/layout/selectable-list/components/SelectableListItem'; import { useSelectableList } from '@/ui/layout/selectable-list/hooks/useSelectableList'; import { useAvailableComponentInstanceIdOrThrow } from '@/ui/utilities/state/component-state/hooks/useAvailableComponentInstanceIdOrThrow'; import { useRecoilComponentCallbackStateV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentCallbackStateV2'; import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2'; import { useRecoilCallback } from 'recoil'; -import { isDefined } from 'twenty-shared/utils'; -export const StyledSelectableItem = styled(SelectableItem)` +export const StyledSelectableItem = styled(SelectableListItem)` height: 100%; width: 100%; `; @@ -45,11 +43,6 @@ export const MultipleRecordPickerMenuItems = ({ const { resetSelectedItem } = useSelectableList( selectableListComponentInstanceId, ); - const singlePickableMorphItemFamilySelector = - useRecoilComponentCallbackStateV2( - multipleRecordPickerSinglePickableMorphItemComponentFamilySelector, - componentInstanceId, - ); const multipleRecordPickerPickableMorphItemsState = useRecoilComponentCallbackStateV2( @@ -82,42 +75,12 @@ export const MultipleRecordPickerMenuItems = ({ [multipleRecordPickerPickableMorphItemsState], ); - const handleEnter = useRecoilCallback( - ({ snapshot }) => { - return (selectedId: string) => { - const pickableMorphItem = snapshot - .getLoadable(singlePickableMorphItemFamilySelector(selectedId)) - .getValue(); - - if (!isDefined(pickableMorphItem)) { - return; - } - - const selectedMorphItem = { - ...pickableMorphItem, - isSelected: !pickableMorphItem.isSelected, - }; - - handleChange(selectedMorphItem); - onChange?.(selectedMorphItem); - resetSelectedItem(); - }; - }, - [ - handleChange, - onChange, - resetSelectedItem, - singlePickableMorphItemFamilySelector, - ], - ); - return ( {pickableRecordIds.map((recordId) => { return ( diff --git a/packages/twenty-front/src/modules/object-record/record-picker/multiple-record-picker/components/MultipleRecordPickerSearchInput.tsx b/packages/twenty-front/src/modules/object-record/record-picker/multiple-record-picker/components/MultipleRecordPickerSearchInput.tsx index 42f222787..ab47a54a6 100644 --- a/packages/twenty-front/src/modules/object-record/record-picker/multiple-record-picker/components/MultipleRecordPickerSearchInput.tsx +++ b/packages/twenty-front/src/modules/object-record/record-picker/multiple-record-picker/components/MultipleRecordPickerSearchInput.tsx @@ -1,19 +1,11 @@ -import styled from '@emotion/styled'; - import { useMultipleRecordPickerPerformSearch } from '@/object-record/record-picker/multiple-record-picker/hooks/useMultipleRecordPickerPerformSearch'; import { MultipleRecordPickerComponentInstanceContext } from '@/object-record/record-picker/multiple-record-picker/states/contexts/MultipleRecordPickerComponentInstanceContext'; import { multipleRecordPickerSearchFilterComponentState } from '@/object-record/record-picker/multiple-record-picker/states/multipleRecordPickerSearchFilterComponentState'; import { DropdownMenuSearchInput } from '@/ui/layout/dropdown/components/DropdownMenuSearchInput'; -import { SelectableItem } from '@/ui/layout/selectable-list/components/SelectableItem'; import { useAvailableComponentInstanceIdOrThrow } from '@/ui/utilities/state/component-state/hooks/useAvailableComponentInstanceIdOrThrow'; import { useRecoilComponentStateV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentStateV2'; import { useCallback } from 'react'; -export const StyledSelectableItem = styled(SelectableItem)` - height: 100%; - width: 100%; -`; - export const MultipleRecordPickerSearchInput = () => { const componentInstanceId = useAvailableComponentInstanceIdOrThrow( MultipleRecordPickerComponentInstanceContext, diff --git a/packages/twenty-front/src/modules/object-record/record-picker/single-record-picker/components/SingleRecordPickerMenuItem.tsx b/packages/twenty-front/src/modules/object-record/record-picker/single-record-picker/components/SingleRecordPickerMenuItem.tsx index c0c477b58..023347ef3 100644 --- a/packages/twenty-front/src/modules/object-record/record-picker/single-record-picker/components/SingleRecordPickerMenuItem.tsx +++ b/packages/twenty-front/src/modules/object-record/record-picker/single-record-picker/components/SingleRecordPickerMenuItem.tsx @@ -3,7 +3,7 @@ import styled from '@emotion/styled'; import { SingleRecordPickerComponentInstanceContext } from '@/object-record/record-picker/single-record-picker/states/contexts/SingleRecordPickerComponentInstanceContext'; import { SingleRecordPickerRecord } from '@/object-record/record-picker/single-record-picker/types/SingleRecordPickerRecord'; import { getSingleRecordPickerSelectableListId } from '@/object-record/record-picker/single-record-picker/utils/getSingleRecordPickerSelectableListId'; -import { SelectableItem } from '@/ui/layout/selectable-list/components/SelectableItem'; +import { SelectableListItem } from '@/ui/layout/selectable-list/components/SelectableListItem'; import { isSelectedItemIdComponentFamilySelector } from '@/ui/layout/selectable-list/states/selectors/isSelectedItemIdComponentFamilySelector'; import { useAvailableComponentInstanceIdOrThrow } from '@/ui/utilities/state/component-state/hooks/useAvailableComponentInstanceIdOrThrow'; import { useRecoilComponentFamilyValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentFamilyValueV2'; @@ -16,7 +16,7 @@ type SingleRecordPickerMenuItemProps = { selectedRecord?: SingleRecordPickerRecord; }; -const StyledSelectableItem = styled(SelectableItem)` +const StyledSelectableItem = styled(SelectableListItem)` width: 100%; `; @@ -40,14 +40,19 @@ export const SingleRecordPickerMenuItem = ({ ); return ( - + { + onRecordSelected(record); + }} + > onRecordSelected(record)} text={record.name} selected={selectedRecord?.id === record.id} - hovered={isSelectedItemId} + focused={isSelectedItemId} avatar={ { - const recordIndex = recordsInDropdown.findIndex( - (record) => record.id === itemId, - ); - setSelectedRecordId(itemId); - onRecordSelected(recordsInDropdown[recordIndex]); - resetSelectedItem(); - }} > {loading && !isFiltered ? ( @@ -128,17 +121,25 @@ export const SingleRecordPickerMenuItems = ({ case 'select-none': { return ( emptyLabel && ( - { + itemId={record.id} + onEnter={() => { setSelectedRecordId(undefined); onRecordSelected(); }} - LeftIcon={EmptyIcon} - text={emptyLabel} - selected={isUndefined(selectedRecordId)} - hovered={isSelectedSelectNoneButton} - /> + > + { + setSelectedRecordId(undefined); + onRecordSelected(); + }} + LeftIcon={EmptyIcon} + text={emptyLabel} + selected={isUndefined(selectedRecordId)} + focused={isSelectedSelectNoneButton} + /> + ) ); } diff --git a/packages/twenty-front/src/modules/object-record/record-table/record-table-cell/hooks/useTriggerActionMenuDropdown.ts b/packages/twenty-front/src/modules/object-record/record-table/record-table-cell/hooks/useTriggerActionMenuDropdown.ts index e1bacd329..0e3eff0fc 100644 --- a/packages/twenty-front/src/modules/object-record/record-table/record-table-cell/hooks/useTriggerActionMenuDropdown.ts +++ b/packages/twenty-front/src/modules/object-record/record-table/record-table-cell/hooks/useTriggerActionMenuDropdown.ts @@ -2,15 +2,15 @@ import { useRecoilCallback } from 'recoil'; import { ActionMenuComponentInstanceContext } from '@/action-menu/states/contexts/ActionMenuComponentInstanceContext'; import { recordIndexActionMenuDropdownPositionComponentState } from '@/action-menu/states/recordIndexActionMenuDropdownPositionComponentState'; +import { ActionMenuDropdownHotkeyScope } from '@/action-menu/types/ActionMenuDropdownHotKeyScope'; import { getActionMenuDropdownIdFromActionMenuId } from '@/action-menu/utils/getActionMenuDropdownIdFromActionMenuId'; +import { useCommandMenu } from '@/command-menu/hooks/useCommandMenu'; import { isRowSelectedComponentFamilyState } from '@/object-record/record-table/record-table-row/states/isRowSelectedComponentFamilyState'; -import { useSetActiveDropdownFocusIdAndMemorizePrevious } from '@/ui/layout/dropdown/hooks/useSetFocusedDropdownIdAndMemorizePrevious'; -import { isDropdownOpenComponentState } from '@/ui/layout/dropdown/states/isDropdownOpenComponentState'; +import { useDropdown } from '@/ui/layout/dropdown/hooks/useDropdown'; import { getSnapshotValue } from '@/ui/utilities/recoil-scope/utils/getSnapshotValue'; import { useAvailableComponentInstanceIdOrThrow } from '@/ui/utilities/state/component-state/hooks/useAvailableComponentInstanceIdOrThrow'; import { useRecoilComponentCallbackStateV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentCallbackStateV2'; import { extractComponentState } from '@/ui/utilities/state/component-state/utils/extractComponentState'; - export const useTriggerActionMenuDropdown = ({ recordTableId, }: { @@ -25,18 +25,17 @@ export const useTriggerActionMenuDropdown = ({ recordTableId, ); + const actionMenuDropdownId = + getActionMenuDropdownIdFromActionMenuId(actionMenuInstanceId); + const recordIndexActionMenuDropdownPositionState = extractComponentState( recordIndexActionMenuDropdownPositionComponentState, - getActionMenuDropdownIdFromActionMenuId(actionMenuInstanceId), + actionMenuDropdownId, ); - const isActionMenuDropdownOpenState = extractComponentState( - isDropdownOpenComponentState, - getActionMenuDropdownIdFromActionMenuId(actionMenuInstanceId), - ); + const { openDropdown } = useDropdown(actionMenuDropdownId); - const { setActiveDropdownFocusIdAndMemorizePrevious } = - useSetActiveDropdownFocusIdAndMemorizePrevious(); + const { closeCommandMenu } = useCommandMenu(); const triggerActionMenuDropdown = useRecoilCallback( ({ set, snapshot }) => @@ -57,19 +56,17 @@ export const useTriggerActionMenuDropdown = ({ set(isRowSelectedFamilyState(recordId), true); } - set(isActionMenuDropdownOpenState, true); + closeCommandMenu(); - const actionMenuDropdownId = - getActionMenuDropdownIdFromActionMenuId(actionMenuInstanceId); - - setActiveDropdownFocusIdAndMemorizePrevious(actionMenuDropdownId); + openDropdown({ + scope: ActionMenuDropdownHotkeyScope.ActionMenuDropdown, + }); }, [ - isActionMenuDropdownOpenState, - isRowSelectedFamilyState, recordIndexActionMenuDropdownPositionState, - setActiveDropdownFocusIdAndMemorizePrevious, - actionMenuInstanceId, + isRowSelectedFamilyState, + closeCommandMenu, + openDropdown, ], ); diff --git a/packages/twenty-front/src/modules/object-record/select/components/MultipleSelectDropdown.tsx b/packages/twenty-front/src/modules/object-record/select/components/MultipleSelectDropdown.tsx index dcedda2d9..29087acbb 100644 --- a/packages/twenty-front/src/modules/object-record/select/components/MultipleSelectDropdown.tsx +++ b/packages/twenty-front/src/modules/object-record/select/components/MultipleSelectDropdown.tsx @@ -7,6 +7,7 @@ import { DropdownMenuSkeletonItem } from '@/ui/input/relation-picker/components/ 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'; +import { SelectableListItem } from '@/ui/layout/selectable-list/components/SelectableListItem'; import { useSelectableList } from '@/ui/layout/selectable-list/hooks/useSelectableList'; import { selectedItemIdComponentState } from '@/ui/layout/selectable-list/states/selectedItemIdComponentState'; import { useScopedHotkeys } from '@/ui/utilities/hotkey/hooks/useScopedHotkeys'; @@ -93,43 +94,38 @@ export const MultipleSelectDropdown = ({ selectableListInstanceId={selectableListId} selectableItemIdArray={selectableItemIds} hotkeyScope={hotkeyScope} - onEnter={(itemId) => { - const item = itemsInDropdown.findIndex( - (entity) => entity.id === itemId, - ); - const itemIsSelectedInDropwdown = filteredSelectedItems.find( - (entity) => entity.id === itemId, - ); - handleItemSelectChange( - itemsInDropdown[item], - !itemIsSelectedInDropwdown, - ); - resetSelectedItem(); - }} > {itemsInDropdown?.map((item) => { return ( - { + { resetSelectedItem(); - handleItemSelectChange(item, newCheckedValue); + handleItemSelectChange(item, !item.isSelected); }} - avatar={ - - } - /> + > + { + resetSelectedItem(); + handleItemSelectChange(item, newCheckedValue); + }} + avatar={ + + } + /> + ); })} {showNoResult && } diff --git a/packages/twenty-front/src/modules/ui/field/input/components/MultiSelectInput.tsx b/packages/twenty-front/src/modules/ui/field/input/components/MultiSelectInput.tsx index 94b107793..9a95f2d55 100644 --- a/packages/twenty-front/src/modules/ui/field/input/components/MultiSelectInput.tsx +++ b/packages/twenty-front/src/modules/ui/field/input/components/MultiSelectInput.tsx @@ -101,14 +101,6 @@ export const MultiSelectInput = ({ selectableListInstanceId={selectableListComponentInstanceId} selectableItemIdArray={optionIds} hotkeyScope={hotkeyScope} - onEnter={(itemId) => { - const option = filteredOptionsInDropDown.find( - (option) => option.value === itemId, - ); - if (isDefined(option)) { - onOptionSelected(formatNewSelectedOptions(option.value)); - } - }} > { - onChange({ iconKey, Icon: getIcon(iconKey) }); - closeDropdown(); - }} > ({ ? selectContainerRef.current?.clientWidth : dropdownWidth; + const selectableItemIdArray = filteredOptions.map((option) => option.label); + + const selectedItemId = useRecoilComponentValueV2( + selectedItemIdComponentState, + dropdownId, + ); + return ( ({ )} {!!filteredOptions.length && ( - {filteredOptions.map((option) => ( - { - onChange?.(option.value); - onBlur?.(); - closeDropdown(); - }} - /> - ))} + + {filteredOptions.map((option) => ( + { + onChange?.(option.value); + onBlur?.(); + closeDropdown(); + }} + > + { + onChange?.(option.value); + onBlur?.(); + closeDropdown(); + }} + /> + + ))} + )} {!!callToActionButton && !!filteredOptions.length && ( diff --git a/packages/twenty-front/src/modules/ui/input/components/SelectInput.tsx b/packages/twenty-front/src/modules/ui/input/components/SelectInput.tsx index a3be42613..674c60c7a 100644 --- a/packages/twenty-front/src/modules/ui/input/components/SelectInput.tsx +++ b/packages/twenty-front/src/modules/ui/input/components/SelectInput.tsx @@ -7,9 +7,9 @@ import { useListenClickOutside } from '@/ui/utilities/pointer-event/hooks/useLis import { useEffect, useMemo, useRef, useState } from 'react'; import { Key } from 'ts-key-enum'; import { isDefined } from 'twenty-shared/utils'; -import { MenuItemSelectTag } from 'twenty-ui/navigation'; -import { SelectOption } from 'twenty-ui/input'; import { TagColor } from 'twenty-ui/components'; +import { SelectOption } from 'twenty-ui/input'; +import { MenuItemSelectTag } from 'twenty-ui/navigation'; interface SelectInputProps { onOptionSelected: (selectedOption: SelectOption) => void; @@ -107,7 +107,6 @@ export const SelectInput = ({ {onClear && clearLabel && ( handleOptionChange(option)} diff --git a/packages/twenty-front/src/modules/ui/layout/selectable-list/components/SelectableList.tsx b/packages/twenty-front/src/modules/ui/layout/selectable-list/components/SelectableList.tsx index 6361efca1..34ecf3796 100644 --- a/packages/twenty-front/src/modules/ui/layout/selectable-list/components/SelectableList.tsx +++ b/packages/twenty-front/src/modules/ui/layout/selectable-list/components/SelectableList.tsx @@ -2,8 +2,8 @@ import { ReactNode, useEffect } from 'react'; import { useSelectableListHotKeys } from '@/ui/layout/selectable-list/hooks/internal/useSelectableListHotKeys'; import { SelectableListComponentInstanceContext } from '@/ui/layout/selectable-list/states/contexts/SelectableListComponentInstanceContext'; +import { SelectableListContextProvider } from '@/ui/layout/selectable-list/states/contexts/SelectableListContext'; import { selectableItemIdsComponentState } from '@/ui/layout/selectable-list/states/selectableItemIdsComponentState'; -import { selectableListOnEnterComponentState } from '@/ui/layout/selectable-list/states/selectableListOnEnterComponentState'; import { useSetRecoilComponentStateV2 } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentStateV2'; import { isDefined } from 'twenty-shared/utils'; import { arrayToChunks } from '~/utils/array/arrayToChunks'; @@ -14,7 +14,6 @@ type SelectableListProps = { selectableItemIdMatrix?: string[][]; onSelect?: (selected: string) => void; hotkeyScope: string; - onEnter?: (itemId: string) => void; selectableListInstanceId: string; }; @@ -24,25 +23,15 @@ export const SelectableList = ({ selectableItemIdArray, selectableItemIdMatrix, selectableListInstanceId, - onEnter, onSelect, }: SelectableListProps) => { useSelectableListHotKeys(selectableListInstanceId, hotkeyScope, onSelect); - const setSelectableListOnEnter = useSetRecoilComponentStateV2( - selectableListOnEnterComponentState, - selectableListInstanceId, - ); - const setSelectableItemIds = useSetRecoilComponentStateV2( selectableItemIdsComponentState, selectableListInstanceId, ); - useEffect(() => { - setSelectableListOnEnter(() => onEnter); - }, [onEnter, setSelectableListOnEnter]); - useEffect(() => { if (!selectableItemIdArray && !selectableItemIdMatrix) { throw new Error( @@ -65,7 +54,9 @@ export const SelectableList = ({ instanceId: selectableListInstanceId, }} > - {children} + + {children} + ); }; diff --git a/packages/twenty-front/src/modules/ui/layout/selectable-list/components/SelectableItem.tsx b/packages/twenty-front/src/modules/ui/layout/selectable-list/components/SelectableListItem.tsx similarity index 60% rename from packages/twenty-front/src/modules/ui/layout/selectable-list/components/SelectableItem.tsx rename to packages/twenty-front/src/modules/ui/layout/selectable-list/components/SelectableListItem.tsx index 1e7c89d94..a4e1b02db 100644 --- a/packages/twenty-front/src/modules/ui/layout/selectable-list/components/SelectableItem.tsx +++ b/packages/twenty-front/src/modules/ui/layout/selectable-list/components/SelectableListItem.tsx @@ -1,25 +1,29 @@ import { ReactNode, useEffect, useRef } from 'react'; +import { SelectableListItemHotkeyEffect } from '@/ui/layout/selectable-list/components/SelectableListItemHotkeyEffect'; import { isSelectedItemIdComponentFamilySelector } from '@/ui/layout/selectable-list/states/selectors/isSelectedItemIdComponentFamilySelector'; import { useRecoilComponentFamilyValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentFamilyValueV2'; import styled from '@emotion/styled'; +import { isDefined } from 'twenty-shared/utils'; const StyledContainer = styled.div` height: 100%; width: 100%; `; -export type SelectableItemProps = { +export type SelectableListItemProps = { itemId: string; children: ReactNode; className?: string; + onEnter?: () => void; }; -export const SelectableItem = ({ +export const SelectableListItem = ({ itemId, children, className, -}: SelectableItemProps) => { + onEnter, +}: SelectableListItemProps) => { const isSelectedItemId = useRecoilComponentFamilyValueV2( isSelectedItemIdComponentFamilySelector, itemId, @@ -34,8 +38,13 @@ export const SelectableItem = ({ }, [isSelectedItemId]); return ( - - {children} - + <> + {isSelectedItemId && isDefined(onEnter) && ( + + )} + + {children} + + ); }; diff --git a/packages/twenty-front/src/modules/ui/layout/selectable-list/components/SelectableListItemHotkeyEffect.tsx b/packages/twenty-front/src/modules/ui/layout/selectable-list/components/SelectableListItemHotkeyEffect.tsx new file mode 100644 index 000000000..c53b18842 --- /dev/null +++ b/packages/twenty-front/src/modules/ui/layout/selectable-list/components/SelectableListItemHotkeyEffect.tsx @@ -0,0 +1,19 @@ +import { useSelectableListListenToEnterHotkeyOnItem } from '@/ui/layout/selectable-list/hooks/useSelectableListListenToEnterHotkeyOnItem'; +import { useSelectableListContextOrThrow } from '@/ui/layout/selectable-list/states/contexts/SelectableListContext'; + +export const SelectableListItemHotkeyEffect = ({ + itemId, + onEnter, +}: { + itemId: string; + onEnter: () => void; +}) => { + const { hotkeyScope } = useSelectableListContextOrThrow(); + + useSelectableListListenToEnterHotkeyOnItem({ + hotkeyScope, + itemId, + onEnter, + }); + return null; +}; diff --git a/packages/twenty-front/src/modules/ui/layout/selectable-list/hooks/internal/useSelectableListHotKeys.ts b/packages/twenty-front/src/modules/ui/layout/selectable-list/hooks/internal/useSelectableListHotKeys.ts index 66df15c7f..1c5cf7355 100644 --- a/packages/twenty-front/src/modules/ui/layout/selectable-list/hooks/internal/useSelectableListHotKeys.ts +++ b/packages/twenty-front/src/modules/ui/layout/selectable-list/hooks/internal/useSelectableListHotKeys.ts @@ -3,7 +3,6 @@ import { useRecoilCallback } from 'recoil'; import { Key } from 'ts-key-enum'; import { selectableItemIdsComponentState } from '@/ui/layout/selectable-list/states/selectableItemIdsComponentState'; -import { selectableListOnEnterComponentState } from '@/ui/layout/selectable-list/states/selectableListOnEnterComponentState'; import { selectedItemIdComponentState } from '@/ui/layout/selectable-list/states/selectedItemIdComponentState'; import { isSelectedItemIdComponentFamilySelector } from '@/ui/layout/selectable-list/states/selectors/isSelectedItemIdComponentFamilySelector'; import { useScopedHotkeys } from '@/ui/utilities/hotkey/hooks/useScopedHotkeys'; @@ -147,35 +146,4 @@ export const useSelectableListHotKeys = ( hotkeyScope, [], ); - - useScopedHotkeys( - Key.Enter, - useRecoilCallback( - ({ snapshot }) => - () => { - const selectedItemId = getSnapshotValue( - snapshot, - selectedItemIdComponentState.atomFamily({ - instanceId: instanceId, - }), - ); - const onEnter = getSnapshotValue( - snapshot, - selectableListOnEnterComponentState.atomFamily({ - instanceId: instanceId, - }), - ); - - if (isNonEmptyString(selectedItemId)) { - onEnter?.(selectedItemId); - } - }, - [instanceId], - ), - hotkeyScope, - [], - { - preventDefault: false, - }, - ); }; diff --git a/packages/twenty-front/src/modules/ui/layout/selectable-list/hooks/useSelectableListListenToEnterHotkeyOnItem.ts b/packages/twenty-front/src/modules/ui/layout/selectable-list/hooks/useSelectableListListenToEnterHotkeyOnItem.ts index b870dee64..dd8b20146 100644 --- a/packages/twenty-front/src/modules/ui/layout/selectable-list/hooks/useSelectableListListenToEnterHotkeyOnItem.ts +++ b/packages/twenty-front/src/modules/ui/layout/selectable-list/hooks/useSelectableListListenToEnterHotkeyOnItem.ts @@ -39,8 +39,5 @@ export const useSelectableListListenToEnterHotkeyOnItem = ({ ), hotkeyScope, [itemId, onEnter], - { - preventDefault: false, - }, ); }; diff --git a/packages/twenty-front/src/modules/ui/layout/selectable-list/states/contexts/SelectableListContext.tsx b/packages/twenty-front/src/modules/ui/layout/selectable-list/states/contexts/SelectableListContext.tsx new file mode 100644 index 000000000..7c8d757b5 --- /dev/null +++ b/packages/twenty-front/src/modules/ui/layout/selectable-list/states/contexts/SelectableListContext.tsx @@ -0,0 +1,8 @@ +import { createRequiredContext } from '~/utils/createRequiredContext'; + +export type SelectableListContextValue = { + hotkeyScope: string; +}; + +export const [SelectableListContextProvider, useSelectableListContextOrThrow] = + createRequiredContext('SelectableListContext'); diff --git a/packages/twenty-front/src/modules/ui/layout/selectable-list/states/selectableListOnEnterComponentState.ts b/packages/twenty-front/src/modules/ui/layout/selectable-list/states/selectableListOnEnterComponentState.ts deleted file mode 100644 index 356553596..000000000 --- a/packages/twenty-front/src/modules/ui/layout/selectable-list/states/selectableListOnEnterComponentState.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { SelectableListComponentInstanceContext } from '@/ui/layout/selectable-list/states/contexts/SelectableListComponentInstanceContext'; -import { createComponentStateV2 } from '@/ui/utilities/state/component-state/utils/createComponentStateV2'; - -export const selectableListOnEnterComponentState = createComponentStateV2< - ((itemId: string) => void) | undefined ->({ - key: 'selectableListOnEnterComponentState', - defaultValue: undefined, - componentInstanceContext: SelectableListComponentInstanceContext, -}); 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 0a0cf94be..5fa9e0a3f 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 @@ -141,7 +141,7 @@ export const WorkflowVariablesDropdownFieldItems = ({ handleSelectField(key)} text={subStep.label || key} hasSubMenu={!subStep.isLeaf} diff --git a/packages/twenty-front/src/modules/workflow/workflow-variables/components/WorkflowVariablesDropdownObjectItems.tsx b/packages/twenty-front/src/modules/workflow/workflow-variables/components/WorkflowVariablesDropdownObjectItems.tsx index 389ebe1cf..3829e3d84 100644 --- a/packages/twenty-front/src/modules/workflow/workflow-variables/components/WorkflowVariablesDropdownObjectItems.tsx +++ b/packages/twenty-front/src/modules/workflow/workflow-variables/components/WorkflowVariablesDropdownObjectItems.tsx @@ -121,7 +121,7 @@ export const WorkflowVariablesDropdownObjectItems = ({ {shouldDisplaySubStepObject && displayedSubStepObject?.label && ( handleSelectField(key)} text={value.label || key} hasSubMenu={!value.isLeaf} 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 e8ea1357b..69f68a64d 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 @@ -55,7 +55,7 @@ export const WorkflowVariablesDropdownWorkflowStepItems = ({ onSelect(item.id)} text={item.name} LeftIcon={item.icon ? getIcon(item.icon) : undefined} diff --git a/packages/twenty-ui/src/navigation/menu-item/components/MenuItem.tsx b/packages/twenty-ui/src/navigation/menu-item/components/MenuItem.tsx index ef566fe98..d3c4ed196 100644 --- a/packages/twenty-ui/src/navigation/menu-item/components/MenuItem.tsx +++ b/packages/twenty-ui/src/navigation/menu-item/components/MenuItem.tsx @@ -35,6 +35,7 @@ export type MenuItemProps = { text: ReactNode; contextualText?: ReactNode; hasSubMenu?: boolean; + focused?: boolean; }; export const MenuItem = ({ @@ -53,6 +54,7 @@ export const MenuItem = ({ contextualText, hasSubMenu = false, disabled = false, + focused = false, }: MenuItemProps) => { const theme = useTheme(); const showIconButtons = Array.isArray(iconButtons) && iconButtons.length > 0; @@ -75,6 +77,7 @@ export const MenuItem = ({ isIconDisplayedOnHoverOnly={isIconDisplayedOnHoverOnly} onMouseEnter={onMouseEnter} onMouseLeave={onMouseLeave} + focused={focused} > ` - ${({ theme, selected, disabled, hovered }) => { - if (selected) { - return css` - background: ${theme.background.transparent.light}; - &:hover { - background: ${theme.background.transparent.medium}; - } - `; - } else if (disabled === true) { + ${({ theme, disabled, focused }) => { + if (disabled === true) { return css` background: inherit; &:hover { @@ -30,7 +22,7 @@ export const StyledMenuItemSelect = styled(StyledMenuItemBase)<{ cursor: default; `; - } else if (hovered === true) { + } else if (focused === true) { return css` background: ${theme.background.transparent.light}; `; @@ -46,7 +38,7 @@ type MenuItemSelectProps = { className?: string; onClick?: () => void; disabled?: boolean; - hovered?: boolean; + focused?: boolean; hasSubMenu?: boolean; contextualText?: ReactNode; }; @@ -59,7 +51,7 @@ export const MenuItemSelect = ({ className, onClick, disabled, - hovered, + focused, hasSubMenu = false, contextualText, }: MenuItemSelectProps) => { @@ -69,9 +61,8 @@ export const MenuItemSelect = ({ void; disabled?: boolean; - hovered?: boolean; + focused?: boolean; testId?: string; }; @@ -28,7 +28,7 @@ export const MenuItemSelectAvatar = ({ className, onClick, disabled, - hovered, + focused, testId, }: MenuItemSelectAvatarProps) => { const theme = useTheme(); @@ -37,9 +37,8 @@ export const MenuItemSelectAvatar = ({ void; disabled?: boolean; - hovered?: boolean; + focused?: boolean; color: ThemeColor; variant?: ColorSampleVariant; }; @@ -39,7 +39,7 @@ export const MenuItemSelectColor = ({ className, onClick, disabled, - hovered, + focused, variant = 'default', }: MenuItemSelectColorProps) => { const theme = useTheme(); @@ -48,9 +48,8 @@ export const MenuItemSelectColor = ({ diff --git a/packages/twenty-ui/src/navigation/menu-item/components/MenuItemSelectTag.tsx b/packages/twenty-ui/src/navigation/menu-item/components/MenuItemSelectTag.tsx index 515f208a5..35b5380c6 100644 --- a/packages/twenty-ui/src/navigation/menu-item/components/MenuItemSelectTag.tsx +++ b/packages/twenty-ui/src/navigation/menu-item/components/MenuItemSelectTag.tsx @@ -11,7 +11,8 @@ import { ThemeColor } from '@ui/theme'; import { StyledMenuItemSelect } from './MenuItemSelect'; type MenuItemSelectTagProps = { - selected: boolean; + selected?: boolean; + focused?: boolean; isKeySelected?: boolean; className?: string; onClick?: () => void; @@ -24,6 +25,7 @@ type MenuItemSelectTagProps = { export const MenuItemSelectTag = ({ color, selected, + focused, isKeySelected, className, onClick, @@ -36,7 +38,7 @@ export const MenuItemSelectTag = ({ diff --git a/packages/twenty-ui/src/navigation/menu-item/components/MenuItemToggle.tsx b/packages/twenty-ui/src/navigation/menu-item/components/MenuItemToggle.tsx index 813d9e357..1458ed03e 100644 --- a/packages/twenty-ui/src/navigation/menu-item/components/MenuItemToggle.tsx +++ b/packages/twenty-ui/src/navigation/menu-item/components/MenuItemToggle.tsx @@ -17,6 +17,7 @@ const StyledToggleContainer = styled.label` `; type MenuItemToggleProps = { + focused?: boolean; LeftIcon?: IconComponent; toggled: boolean; text: string; @@ -26,6 +27,7 @@ type MenuItemToggleProps = { }; export const MenuItemToggle = ({ + focused, LeftIcon, text, toggled, @@ -35,7 +37,7 @@ export const MenuItemToggle = ({ }: MenuItemToggleProps) => { const inputId = useId(); return ( - + diff --git a/packages/twenty-ui/src/navigation/menu-item/internals/components/StyledMenuItemBase.tsx b/packages/twenty-ui/src/navigation/menu-item/internals/components/StyledMenuItemBase.tsx index d67bb7af0..9e8806fb4 100644 --- a/packages/twenty-ui/src/navigation/menu-item/internals/components/StyledMenuItemBase.tsx +++ b/packages/twenty-ui/src/navigation/menu-item/internals/components/StyledMenuItemBase.tsx @@ -13,6 +13,7 @@ export type MenuItemBaseProps = { isHoverBackgroundDisabled?: boolean; hovered?: boolean; disabled?: boolean; + focused?: boolean; }; export const StyledMenuItemBase = styled.div` @@ -72,6 +73,12 @@ export const StyledMenuItemBase = styled.div` } }} + ${({ focused, theme }) => + focused && + css` + background: ${theme.background.transparent.light}; + `}; + position: relative; user-select: none;