diff --git a/packages/twenty-front/src/modules/action-menu/actions/record-actions/components/DeleteRecordsActionEffect.tsx b/packages/twenty-front/src/modules/action-menu/actions/record-actions/components/DeleteRecordsActionEffect.tsx index 208ae8ffc..d53e118ae 100644 --- a/packages/twenty-front/src/modules/action-menu/actions/record-actions/components/DeleteRecordsActionEffect.tsx +++ b/packages/twenty-front/src/modules/action-menu/actions/record-actions/components/DeleteRecordsActionEffect.tsx @@ -102,6 +102,7 @@ export const DeleteRecordsActionEffect = ({ position, Icon: IconTrash, accent: 'danger', + isPinned: true, onClick: () => { setIsDeleteRecordsModalOpen(true); }, diff --git a/packages/twenty-front/src/modules/action-menu/components/RecordIndexActionMenuBar.tsx b/packages/twenty-front/src/modules/action-menu/components/RecordIndexActionMenuBar.tsx index ea0383727..9a22c29f9 100644 --- a/packages/twenty-front/src/modules/action-menu/components/RecordIndexActionMenuBar.tsx +++ b/packages/twenty-front/src/modules/action-menu/components/RecordIndexActionMenuBar.tsx @@ -1,5 +1,6 @@ import styled from '@emotion/styled'; +import { RecordIndexActionMenuBarAllActionsButton } from '@/action-menu/components/RecordIndexActionMenuBarAllActionsButton'; import { RecordIndexActionMenuBarEntry } from '@/action-menu/components/RecordIndexActionMenuBarEntry'; import { actionMenuEntriesComponentSelector } from '@/action-menu/states/actionMenuEntriesComponentSelector'; import { ActionMenuComponentInstanceContext } from '@/action-menu/states/contexts/ActionMenuComponentInstanceContext'; @@ -30,7 +31,9 @@ export const RecordIndexActionMenuBar = () => { actionMenuEntriesComponentSelector, ); - if (actionMenuEntries.length === 0) { + const pinnedEntries = actionMenuEntries.filter((entry) => entry.isPinned); + + if (pinnedEntries.length === 0) { return null; } @@ -42,9 +45,10 @@ export const RecordIndexActionMenuBar = () => { }} > {contextStoreNumberOfSelectedRecords} selected: - {actionMenuEntries.map((entry, index) => ( + {pinnedEntries.map((entry, index) => ( ))} + ); }; diff --git a/packages/twenty-front/src/modules/action-menu/components/RecordIndexActionMenuBarAllActionsButton.tsx b/packages/twenty-front/src/modules/action-menu/components/RecordIndexActionMenuBarAllActionsButton.tsx new file mode 100644 index 000000000..336b4f734 --- /dev/null +++ b/packages/twenty-front/src/modules/action-menu/components/RecordIndexActionMenuBarAllActionsButton.tsx @@ -0,0 +1,53 @@ +import { useCommandMenu } from '@/command-menu/hooks/useCommandMenu'; +import { useTheme } from '@emotion/react'; +import styled from '@emotion/styled'; +import { IconLayoutSidebarRightExpand } from 'twenty-ui'; + +const StyledButton = styled.div` + border-radius: ${({ theme }) => theme.border.radius.sm}; + color: ${({ theme }) => theme.font.color.secondary}; + cursor: pointer; + display: flex; + justify-content: center; + + padding: ${({ theme }) => theme.spacing(2)}; + transition: background ${({ theme }) => theme.animation.duration.fast} ease; + user-select: none; + + &:hover { + background: ${({ theme }) => theme.background.tertiary}; + } +`; + +const StyledButtonLabel = styled.div` + font-weight: ${({ theme }) => theme.font.weight.medium}; + margin-left: ${({ theme }) => theme.spacing(1)}; +`; + +const StyledShortcutLabel = styled.div` + color: ${({ theme }) => theme.font.color.light}; + font-weight: ${({ theme }) => theme.font.weight.medium}; +`; + +const StyledSeparator = styled.div<{ size: 'sm' | 'md' }>` + background: ${({ theme }) => theme.border.color.light}; + height: ${({ theme, size }) => theme.spacing(size === 'sm' ? 4 : 8)}; + margin: 0 ${({ theme }) => theme.spacing(1)}; + width: 1px; +`; + +export const RecordIndexActionMenuBarAllActionsButton = () => { + const theme = useTheme(); + const { openCommandMenu } = useCommandMenu(); + return ( + <> + + openCommandMenu()}> + + All Actions + + ⌘K + + + ); +}; diff --git a/packages/twenty-front/src/modules/action-menu/components/RecordIndexActionMenuBarEntry.tsx b/packages/twenty-front/src/modules/action-menu/components/RecordIndexActionMenuBarEntry.tsx index 8be474798..ffa52d205 100644 --- a/packages/twenty-front/src/modules/action-menu/components/RecordIndexActionMenuBarEntry.tsx +++ b/packages/twenty-front/src/modules/action-menu/components/RecordIndexActionMenuBarEntry.tsx @@ -2,31 +2,24 @@ import { useTheme } from '@emotion/react'; import styled from '@emotion/styled'; import { ActionMenuEntry } from '@/action-menu/types/ActionMenuEntry'; -import { MenuItemAccent } from '@/ui/navigation/menu-item/types/MenuItemAccent'; type RecordIndexActionMenuBarEntryProps = { entry: ActionMenuEntry; }; -const StyledButton = styled.div<{ accent: MenuItemAccent }>` +const StyledButton = styled.div` border-radius: ${({ theme }) => theme.border.radius.sm}; - color: ${(props) => - props.accent === 'danger' - ? props.theme.color.red - : props.theme.font.color.secondary}; + color: ${({ theme }) => theme.font.color.secondary}; cursor: pointer; display: flex; justify-content: center; padding: ${({ theme }) => theme.spacing(2)}; - transition: background 0.1s ease; + transition: background ${({ theme }) => theme.animation.duration.fast} ease; user-select: none; &:hover { - background: ${({ theme, accent }) => - accent === 'danger' - ? theme.background.danger - : theme.background.tertiary}; + background: ${({ theme }) => theme.background.tertiary}; } `; @@ -40,10 +33,7 @@ export const RecordIndexActionMenuBarEntry = ({ }: RecordIndexActionMenuBarEntryProps) => { const theme = useTheme(); return ( - entry.onClick?.()} - > + entry.onClick?.()}> {entry.Icon && } {entry.label} diff --git a/packages/twenty-front/src/modules/action-menu/components/__stories__/RecordIndexActionMenuBar.stories.tsx b/packages/twenty-front/src/modules/action-menu/components/__stories__/RecordIndexActionMenuBar.stories.tsx index f3a3eab2f..4cb1ef1a4 100644 --- a/packages/twenty-front/src/modules/action-menu/components/__stories__/RecordIndexActionMenuBar.stories.tsx +++ b/packages/twenty-front/src/modules/action-menu/components/__stories__/RecordIndexActionMenuBar.stories.tsx @@ -10,15 +10,15 @@ import { contextStoreNumberOfSelectedRecordsComponentState } from '@/context-sto import { contextStoreTargetedRecordsRuleComponentState } from '@/context-store/states/contextStoreTargetedRecordsRuleComponentState'; import { isBottomBarOpenedComponentState } from '@/ui/layout/bottom-bar/states/isBottomBarOpenedComponentState'; import { userEvent, waitFor, within } from '@storybook/test'; -import { IconCheckbox, IconTrash } from 'twenty-ui'; +import { IconTrash, RouterDecorator } from 'twenty-ui'; const deleteMock = jest.fn(); -const markAsDoneMock = jest.fn(); const meta: Meta = { title: 'Modules/ActionMenu/RecordIndexActionMenuBar', component: RecordIndexActionMenuBar, decorators: [ + RouterDecorator, (Story) => ( = { [ 'delete', { + isPinned: true, key: 'delete', label: 'Delete', position: 0, @@ -55,16 +56,6 @@ const meta: Meta = { onClick: deleteMock, }, ], - [ - 'markAsDone', - { - key: 'markAsDone', - label: 'Mark as done', - position: 1, - Icon: IconCheckbox, - onClick: markAsDoneMock, - }, - ], ]), ); set( @@ -120,12 +111,8 @@ export const WithButtonClicks: Story = { const deleteButton = await canvas.findByText('Delete'); await userEvent.click(deleteButton); - const markAsDoneButton = await canvas.findByText('Mark as done'); - await userEvent.click(markAsDoneButton); - await waitFor(() => { expect(deleteMock).toHaveBeenCalled(); - expect(markAsDoneMock).toHaveBeenCalled(); }); }, }; diff --git a/packages/twenty-front/src/modules/action-menu/types/ActionMenuEntry.ts b/packages/twenty-front/src/modules/action-menu/types/ActionMenuEntry.ts index 4fe180955..c6f559be8 100644 --- a/packages/twenty-front/src/modules/action-menu/types/ActionMenuEntry.ts +++ b/packages/twenty-front/src/modules/action-menu/types/ActionMenuEntry.ts @@ -8,6 +8,7 @@ export type ActionMenuEntry = { label: string; position: number; Icon: IconComponent; + isPinned?: boolean; accent?: MenuItemAccent; onClick?: (event?: MouseEvent) => void; ConfirmationModal?: ReactNode;