Command menu search bar (#4337)

* Improve performance on findMany queries

* Fix

* Fix command menu not emptying the search on toggle

* Fix tests
This commit is contained in:
Charles Bochet 2024-03-06 14:20:05 +01:00 committed by GitHub
parent e7857d7fa3
commit b08b361dc0
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 59 additions and 113 deletions

View File

@ -1,10 +1,11 @@
import { useMemo, useRef, useState } from 'react';
import { useMemo, useRef } from 'react';
import styled from '@emotion/styled';
import { useRecoilValue } from 'recoil';
import { useRecoilState, useRecoilValue } from 'recoil';
import { Key } from 'ts-key-enum';
import { useOpenActivityRightDrawer } from '@/activities/hooks/useOpenActivityRightDrawer';
import { Activity } from '@/activities/types/Activity';
import { commandMenuSearchState } from '@/command-menu/states/commandMenuSearchState';
import { Company } from '@/companies/types/Company';
import { useKeyboardShortcutMenu } from '@/keyboard-shortcut-menu/hooks/useKeyboardShortcutMenu';
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
@ -105,23 +106,24 @@ export const CommandMenu = () => {
const openActivityRightDrawer = useOpenActivityRightDrawer();
const isCommandMenuOpened = useRecoilValue(isCommandMenuOpenedState);
const [search, setSearch] = useState('');
const [commandMenuSearch, setCommandMenuSearch] = useRecoilState(
commandMenuSearchState,
);
const commandMenuCommands = useRecoilValue(commandMenuCommandsState);
const { closeKeyboardShortcutMenu } = useKeyboardShortcutMenu();
const handleSearchChange = (event: React.ChangeEvent<HTMLInputElement>) => {
setSearch(event.target.value);
setCommandMenuSearch(event.target.value);
};
useScopedHotkeys(
'ctrl+k,meta+k',
() => {
closeKeyboardShortcutMenu();
setSearch('');
toggleCommandMenu();
},
AppHotkeyScope.CommandMenu,
[toggleCommandMenu, setSearch],
[toggleCommandMenu],
);
useScopedHotkeys(
@ -136,12 +138,12 @@ export const CommandMenu = () => {
const { records: people } = useFindManyRecords<Person>({
skip: !isCommandMenuOpened,
objectNameSingular: CoreObjectNameSingular.Person,
filter: search
filter: commandMenuSearch
? makeOrFilterVariables([
{ name: { firstName: { ilike: `%${search}%` } } },
{ name: { lastName: { ilike: `%${search}%` } } },
{ email: { ilike: `%${search}%` } },
{ phone: { ilike: `%${search}%` } },
{ name: { firstName: { ilike: `%${commandMenuSearch}%` } } },
{ name: { lastName: { ilike: `%${commandMenuSearch}%` } } },
{ email: { ilike: `%${commandMenuSearch}%` } },
{ phone: { ilike: `%${commandMenuSearch}%` } },
])
: undefined,
limit: 3,
@ -150,9 +152,9 @@ export const CommandMenu = () => {
const { records: companies } = useFindManyRecords<Company>({
skip: !isCommandMenuOpened,
objectNameSingular: CoreObjectNameSingular.Company,
filter: search
filter: commandMenuSearch
? {
name: { ilike: `%${search}%` },
name: { ilike: `%${commandMenuSearch}%` },
}
: undefined,
limit: 3,
@ -161,10 +163,10 @@ export const CommandMenu = () => {
const { records: activities } = useFindManyRecords<Activity>({
skip: !isCommandMenuOpened,
objectNameSingular: CoreObjectNameSingular.Activity,
filter: search
filter: commandMenuSearch
? makeOrFilterVariables([
{ title: { ilike: `%${search}%` } },
{ body: { ilike: `%${search}%` } },
{ title: { ilike: `%${commandMenuSearch}%` } },
{ body: { ilike: `%${commandMenuSearch}%` } },
])
: undefined,
limit: 3,
@ -224,15 +226,17 @@ export const CommandMenu = () => {
const matchingNavigateCommand = commandMenuCommands.filter(
(cmd) =>
(search.length > 0
? checkInShortcuts(cmd, search) || checkInLabels(cmd, search)
(commandMenuSearch.length > 0
? checkInShortcuts(cmd, commandMenuSearch) ||
checkInLabels(cmd, commandMenuSearch)
: true) && cmd.type === CommandType.Navigate,
);
const matchingCreateCommand = commandMenuCommands.filter(
(cmd) =>
(search.length > 0
? checkInShortcuts(cmd, search) || checkInLabels(cmd, search)
(commandMenuSearch.length > 0
? checkInShortcuts(cmd, commandMenuSearch) ||
checkInLabels(cmd, commandMenuSearch)
: true) && cmd.type === CommandType.Create,
);
@ -254,7 +258,7 @@ export const CommandMenu = () => {
<StyledDialog ref={commandMenuRef}>
<StyledInput
autoFocus
value={search}
value={commandMenuSearch}
placeholder="Search"
onChange={handleSearchChange}
/>

View File

@ -2,6 +2,7 @@ import { useCallback } from 'react';
import { useNavigate } from 'react-router-dom';
import { useRecoilCallback, useSetRecoilState } from 'recoil';
import { commandMenuSearchState } from '@/command-menu/states/commandMenuSearchState';
import { useSelectableList } from '@/ui/layout/selectable-list/hooks/useSelectableList';
import { usePreviousHotkeyScope } from '@/ui/utilities/hotkey/hooks/usePreviousHotkeyScope';
import { AppHotkeyScope } from '@/ui/utilities/hotkey/types/AppHotkeyScope';
@ -42,17 +43,22 @@ export const useCommandMenu = () => {
[goBackToPreviousHotkeyScope, resetSelectedItem, setIsCommandMenuOpened],
);
const toggleCommandMenu = useRecoilCallback(({ snapshot }) => async () => {
const isCommandMenuOpened = snapshot
.getLoadable(isCommandMenuOpenedState)
.getValue();
const toggleCommandMenu = useRecoilCallback(
({ snapshot, set }) =>
async () => {
const isCommandMenuOpened = snapshot
.getLoadable(isCommandMenuOpenedState)
.getValue();
if (isCommandMenuOpened) {
closeCommandMenu();
} else {
openCommandMenu();
}
});
set(commandMenuSearchState, '');
if (isCommandMenuOpened) {
closeCommandMenu();
} else {
openCommandMenu();
}
},
);
const addToCommandMenu = useCallback(
(addCommand: Command[]) => {

View File

@ -0,0 +1,6 @@
import { atom } from 'recoil';
export const commandMenuSearchState = atom<string>({
key: 'command-menu/commandMenuSearchState',
default: '',
});

View File

@ -20,43 +20,20 @@ const getOneToManyRelation = () => {
return {
field: objectMetadataItem.fields.find((field) => field.name === 'company')!,
res: `company
{
__typename
id
{
__typename
id
xLink
{
label
url
}
accountOwner
{
__typename
id
}
linkedinLink
{
label
url
}
attachments
{
edges {
node {
__typename
id
}
}
}
domainName
opportunities
{
edges {
node {
__typename
id
}
}
}
annualRecurringRevenue
{
amountMicros
@ -65,39 +42,12 @@ opportunities
createdAt
address
updatedAt
activityTargets
{
edges {
node {
__typename
id
}
}
}
favorites
{
edges {
node {
__typename
id
}
}
}
people
{
edges {
node {
__typename
id
}
}
}
name
accountOwnerId
employees
id
idealCustomerProfile
}`,
}`,
};
};
@ -133,27 +83,17 @@ const getOneToManyFromRelationField = () => {
return {
field,
res: `opportunities
{
edges {
node {
__typename
id
personId
{
edges {
node {
__typename
id
personId
pointOfContactId
updatedAt
company
{
__typename
id
}
companyId
pipelineStepId
probability
pipelineStep
{
__typename
id
}
closeDate
amount
{
@ -162,19 +102,9 @@ closeDate
}
id
createdAt
pointOfContact
{
__typename
id
}
person
{
__typename
id
}
}
}
}
}`,
}`,
};
};