From fcc4f0bb809cee48ebf9db8aa020c149bd5f70a5 Mon Sep 17 00:00:00 2001 From: bosiraphael Date: Wed, 23 Oct 2024 11:39:34 +0200 Subject: [PATCH] make command menu actions searchable --- .../command-menu/components/CommandMenu.tsx | 18 +++++++++- .../components/CommandMenuActions.tsx | 35 ++++++++++++++----- .../src/modules/command-menu/types/Command.ts | 5 +-- 3 files changed, 46 insertions(+), 12 deletions(-) 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 990769b03..98f493e5d 100644 --- a/packages/twenty-front/src/modules/command-menu/components/CommandMenu.tsx +++ b/packages/twenty-front/src/modules/command-menu/components/CommandMenu.tsx @@ -31,7 +31,7 @@ import { ScrollWrapper } from '@/ui/utilities/scroll/components/ScrollWrapper'; import { useIsFeatureEnabled } from '@/workspace/hooks/useIsFeatureEnabled'; import styled from '@emotion/styled'; import { isNonEmptyString } from '@sniptt/guards'; -import { useMemo, useRef } from 'react'; +import { useMemo, useRef, useState } from 'react'; import { useRecoilState, useRecoilValue, useSetRecoilState } from 'recoil'; import { Key } from 'ts-key-enum'; import { Avatar, IconNotes, IconSparkles, IconX, isDefined } from 'twenty-ui'; @@ -146,6 +146,8 @@ export const CommandMenu = () => { setCommandMenuSearch(event.target.value); }; + const [actionCommands, setActionCommands] = useState([]); + const isMobile = useIsMobile(); useScopedHotkeys( @@ -290,6 +292,14 @@ export const CommandMenu = () => { : true) && cmd.type === CommandType.Create, ); + const matchingActionCommands = actionCommands.filter( + (cmd) => + (deferredCommandMenuSearch.length > 0 + ? checkInShortcuts(cmd, deferredCommandMenuSearch) || + checkInLabels(cmd, deferredCommandMenuSearch) + : true) && cmd.type === CommandType.Action, + ); + useListenClickOutside({ refs: [commandMenuRef], callback: closeCommandMenu, @@ -315,6 +325,7 @@ export const CommandMenu = () => { const selectableItemIds = copilotCommands .map((cmd) => cmd.id) + .concat(matchingActionCommands.map((cmd) => cmd.id)) .concat(matchingCreateCommand.map((cmd) => cmd.id)) .concat(matchingNavigateCommand.map((cmd) => cmd.id)) .concat(people?.map((person) => person.id)) @@ -323,12 +334,14 @@ export const CommandMenu = () => { .concat(notes?.map((note) => note.id)); const isNoResults = + !matchingActionCommands.length && !matchingCreateCommand.length && !matchingNavigateCommand.length && !people?.length && !companies?.length && !notes?.length && !opportunities?.length; + const isLoading = isPeopleLoading || isNotesLoading || @@ -370,6 +383,7 @@ export const CommandMenu = () => { hotkeyScope={AppHotkeyScope.CommandMenu} onEnter={(itemId) => { const command = [ + ...actionCommands, ...copilotCommands, ...commandMenuCommands, ...otherCommands, @@ -405,6 +419,8 @@ export const CommandMenu = () => { mainContextStoreComponentInstanceId={ mainContextStoreComponentInstanceId } + matchingActionCommands={matchingActionCommands} + setActionCommands={setActionCommands} /> )} diff --git a/packages/twenty-front/src/modules/command-menu/components/CommandMenuActions.tsx b/packages/twenty-front/src/modules/command-menu/components/CommandMenuActions.tsx index 5e0296609..3d6fe0b66 100644 --- a/packages/twenty-front/src/modules/command-menu/components/CommandMenuActions.tsx +++ b/packages/twenty-front/src/modules/command-menu/components/CommandMenuActions.tsx @@ -1,31 +1,48 @@ import { CommandGroup } from '@/command-menu/components/CommandGroup'; import { CommandMenuItem } from '@/command-menu/components/CommandMenuItem'; +import { Command, CommandType } from '@/command-menu/types/Command'; import { contextStoreActionMenuEntriesComponentSelector } from '@/context-store/states/contextStoreActionMenuEntriesComponentSelector'; import { SelectableItem } from '@/ui/layout/selectable-list/components/SelectableItem'; import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2'; +import { useEffect, useMemo } from 'react'; export const CommandMenuActions = ({ mainContextStoreComponentInstanceId, + matchingActionCommands, + setActionCommands, }: { mainContextStoreComponentInstanceId: string; + matchingActionCommands: Command[]; + setActionCommands: (actionCommands: Command[]) => void; }) => { const actionMenuEntries = useRecoilComponentValueV2( contextStoreActionMenuEntriesComponentSelector, mainContextStoreComponentInstanceId, ); + const actionCommands = useMemo(() => { + return actionMenuEntries?.map((actionMenuEntry) => ({ + id: actionMenuEntry.label, + label: actionMenuEntry.label, + Icon: actionMenuEntry.Icon, + onCommandClick: actionMenuEntry.onClick, + type: CommandType.Action, + })); + }, [actionMenuEntries]); + + useEffect(() => { + setActionCommands(actionCommands); + }, [actionCommands, setActionCommands]); + return ( - {actionMenuEntries?.map((actionMenuEntry) => ( - + {matchingActionCommands?.map((actionCommand) => ( + ))} diff --git a/packages/twenty-front/src/modules/command-menu/types/Command.ts b/packages/twenty-front/src/modules/command-menu/types/Command.ts index 38b430bb3..b43b23f9c 100644 --- a/packages/twenty-front/src/modules/command-menu/types/Command.ts +++ b/packages/twenty-front/src/modules/command-menu/types/Command.ts @@ -3,13 +3,14 @@ import { IconComponent } from 'twenty-ui'; export enum CommandType { Navigate = 'Navigate', Create = 'Create', + Action = 'Action', } export type Command = { id: string; - to: string; + to?: string; label: string; - type: CommandType.Navigate | CommandType.Create; + type: CommandType.Navigate | CommandType.Create | CommandType.Action; Icon?: IconComponent; firstHotKey?: string; secondHotKey?: string;