mirror of
https://github.com/lingble/twenty.git
synced 2026-03-20 04:04:04 +00:00
make command menu actions searchable
This commit is contained in:
@@ -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<Command[]>([]);
|
||||
|
||||
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}
|
||||
/>
|
||||
)}
|
||||
<CommandGroup heading="Create">
|
||||
|
||||
@@ -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 (
|
||||
<CommandGroup heading="Actions">
|
||||
{actionMenuEntries?.map((actionMenuEntry) => (
|
||||
<SelectableItem
|
||||
itemId={actionMenuEntry.label}
|
||||
key={actionMenuEntry.label}
|
||||
>
|
||||
{matchingActionCommands?.map((actionCommand) => (
|
||||
<SelectableItem itemId={actionCommand.id} key={actionCommand.id}>
|
||||
<CommandMenuItem
|
||||
id={actionMenuEntry.label}
|
||||
label={actionMenuEntry.label}
|
||||
Icon={actionMenuEntry.Icon}
|
||||
onClick={actionMenuEntry.onClick}
|
||||
id={actionCommand.id}
|
||||
label={actionCommand.label}
|
||||
Icon={actionCommand.Icon}
|
||||
onClick={actionCommand.onCommandClick}
|
||||
/>
|
||||
</SelectableItem>
|
||||
))}
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user