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:
@ -1,10 +1,11 @@
|
|||||||
import { useMemo, useRef, useState } from 'react';
|
import { useMemo, useRef } from 'react';
|
||||||
import styled from '@emotion/styled';
|
import styled from '@emotion/styled';
|
||||||
import { useRecoilValue } from 'recoil';
|
import { useRecoilState, useRecoilValue } from 'recoil';
|
||||||
import { Key } from 'ts-key-enum';
|
import { Key } from 'ts-key-enum';
|
||||||
|
|
||||||
import { useOpenActivityRightDrawer } from '@/activities/hooks/useOpenActivityRightDrawer';
|
import { useOpenActivityRightDrawer } from '@/activities/hooks/useOpenActivityRightDrawer';
|
||||||
import { Activity } from '@/activities/types/Activity';
|
import { Activity } from '@/activities/types/Activity';
|
||||||
|
import { commandMenuSearchState } from '@/command-menu/states/commandMenuSearchState';
|
||||||
import { Company } from '@/companies/types/Company';
|
import { Company } from '@/companies/types/Company';
|
||||||
import { useKeyboardShortcutMenu } from '@/keyboard-shortcut-menu/hooks/useKeyboardShortcutMenu';
|
import { useKeyboardShortcutMenu } from '@/keyboard-shortcut-menu/hooks/useKeyboardShortcutMenu';
|
||||||
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
|
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
|
||||||
@ -105,23 +106,24 @@ export const CommandMenu = () => {
|
|||||||
|
|
||||||
const openActivityRightDrawer = useOpenActivityRightDrawer();
|
const openActivityRightDrawer = useOpenActivityRightDrawer();
|
||||||
const isCommandMenuOpened = useRecoilValue(isCommandMenuOpenedState);
|
const isCommandMenuOpened = useRecoilValue(isCommandMenuOpenedState);
|
||||||
const [search, setSearch] = useState('');
|
const [commandMenuSearch, setCommandMenuSearch] = useRecoilState(
|
||||||
|
commandMenuSearchState,
|
||||||
|
);
|
||||||
const commandMenuCommands = useRecoilValue(commandMenuCommandsState);
|
const commandMenuCommands = useRecoilValue(commandMenuCommandsState);
|
||||||
const { closeKeyboardShortcutMenu } = useKeyboardShortcutMenu();
|
const { closeKeyboardShortcutMenu } = useKeyboardShortcutMenu();
|
||||||
|
|
||||||
const handleSearchChange = (event: React.ChangeEvent<HTMLInputElement>) => {
|
const handleSearchChange = (event: React.ChangeEvent<HTMLInputElement>) => {
|
||||||
setSearch(event.target.value);
|
setCommandMenuSearch(event.target.value);
|
||||||
};
|
};
|
||||||
|
|
||||||
useScopedHotkeys(
|
useScopedHotkeys(
|
||||||
'ctrl+k,meta+k',
|
'ctrl+k,meta+k',
|
||||||
() => {
|
() => {
|
||||||
closeKeyboardShortcutMenu();
|
closeKeyboardShortcutMenu();
|
||||||
setSearch('');
|
|
||||||
toggleCommandMenu();
|
toggleCommandMenu();
|
||||||
},
|
},
|
||||||
AppHotkeyScope.CommandMenu,
|
AppHotkeyScope.CommandMenu,
|
||||||
[toggleCommandMenu, setSearch],
|
[toggleCommandMenu],
|
||||||
);
|
);
|
||||||
|
|
||||||
useScopedHotkeys(
|
useScopedHotkeys(
|
||||||
@ -136,12 +138,12 @@ export const CommandMenu = () => {
|
|||||||
const { records: people } = useFindManyRecords<Person>({
|
const { records: people } = useFindManyRecords<Person>({
|
||||||
skip: !isCommandMenuOpened,
|
skip: !isCommandMenuOpened,
|
||||||
objectNameSingular: CoreObjectNameSingular.Person,
|
objectNameSingular: CoreObjectNameSingular.Person,
|
||||||
filter: search
|
filter: commandMenuSearch
|
||||||
? makeOrFilterVariables([
|
? makeOrFilterVariables([
|
||||||
{ name: { firstName: { ilike: `%${search}%` } } },
|
{ name: { firstName: { ilike: `%${commandMenuSearch}%` } } },
|
||||||
{ name: { lastName: { ilike: `%${search}%` } } },
|
{ name: { lastName: { ilike: `%${commandMenuSearch}%` } } },
|
||||||
{ email: { ilike: `%${search}%` } },
|
{ email: { ilike: `%${commandMenuSearch}%` } },
|
||||||
{ phone: { ilike: `%${search}%` } },
|
{ phone: { ilike: `%${commandMenuSearch}%` } },
|
||||||
])
|
])
|
||||||
: undefined,
|
: undefined,
|
||||||
limit: 3,
|
limit: 3,
|
||||||
@ -150,9 +152,9 @@ export const CommandMenu = () => {
|
|||||||
const { records: companies } = useFindManyRecords<Company>({
|
const { records: companies } = useFindManyRecords<Company>({
|
||||||
skip: !isCommandMenuOpened,
|
skip: !isCommandMenuOpened,
|
||||||
objectNameSingular: CoreObjectNameSingular.Company,
|
objectNameSingular: CoreObjectNameSingular.Company,
|
||||||
filter: search
|
filter: commandMenuSearch
|
||||||
? {
|
? {
|
||||||
name: { ilike: `%${search}%` },
|
name: { ilike: `%${commandMenuSearch}%` },
|
||||||
}
|
}
|
||||||
: undefined,
|
: undefined,
|
||||||
limit: 3,
|
limit: 3,
|
||||||
@ -161,10 +163,10 @@ export const CommandMenu = () => {
|
|||||||
const { records: activities } = useFindManyRecords<Activity>({
|
const { records: activities } = useFindManyRecords<Activity>({
|
||||||
skip: !isCommandMenuOpened,
|
skip: !isCommandMenuOpened,
|
||||||
objectNameSingular: CoreObjectNameSingular.Activity,
|
objectNameSingular: CoreObjectNameSingular.Activity,
|
||||||
filter: search
|
filter: commandMenuSearch
|
||||||
? makeOrFilterVariables([
|
? makeOrFilterVariables([
|
||||||
{ title: { ilike: `%${search}%` } },
|
{ title: { ilike: `%${commandMenuSearch}%` } },
|
||||||
{ body: { ilike: `%${search}%` } },
|
{ body: { ilike: `%${commandMenuSearch}%` } },
|
||||||
])
|
])
|
||||||
: undefined,
|
: undefined,
|
||||||
limit: 3,
|
limit: 3,
|
||||||
@ -224,15 +226,17 @@ export const CommandMenu = () => {
|
|||||||
|
|
||||||
const matchingNavigateCommand = commandMenuCommands.filter(
|
const matchingNavigateCommand = commandMenuCommands.filter(
|
||||||
(cmd) =>
|
(cmd) =>
|
||||||
(search.length > 0
|
(commandMenuSearch.length > 0
|
||||||
? checkInShortcuts(cmd, search) || checkInLabels(cmd, search)
|
? checkInShortcuts(cmd, commandMenuSearch) ||
|
||||||
|
checkInLabels(cmd, commandMenuSearch)
|
||||||
: true) && cmd.type === CommandType.Navigate,
|
: true) && cmd.type === CommandType.Navigate,
|
||||||
);
|
);
|
||||||
|
|
||||||
const matchingCreateCommand = commandMenuCommands.filter(
|
const matchingCreateCommand = commandMenuCommands.filter(
|
||||||
(cmd) =>
|
(cmd) =>
|
||||||
(search.length > 0
|
(commandMenuSearch.length > 0
|
||||||
? checkInShortcuts(cmd, search) || checkInLabels(cmd, search)
|
? checkInShortcuts(cmd, commandMenuSearch) ||
|
||||||
|
checkInLabels(cmd, commandMenuSearch)
|
||||||
: true) && cmd.type === CommandType.Create,
|
: true) && cmd.type === CommandType.Create,
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -254,7 +258,7 @@ export const CommandMenu = () => {
|
|||||||
<StyledDialog ref={commandMenuRef}>
|
<StyledDialog ref={commandMenuRef}>
|
||||||
<StyledInput
|
<StyledInput
|
||||||
autoFocus
|
autoFocus
|
||||||
value={search}
|
value={commandMenuSearch}
|
||||||
placeholder="Search"
|
placeholder="Search"
|
||||||
onChange={handleSearchChange}
|
onChange={handleSearchChange}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@ -2,6 +2,7 @@ import { useCallback } from 'react';
|
|||||||
import { useNavigate } from 'react-router-dom';
|
import { useNavigate } from 'react-router-dom';
|
||||||
import { useRecoilCallback, useSetRecoilState } from 'recoil';
|
import { useRecoilCallback, useSetRecoilState } from 'recoil';
|
||||||
|
|
||||||
|
import { commandMenuSearchState } from '@/command-menu/states/commandMenuSearchState';
|
||||||
import { useSelectableList } from '@/ui/layout/selectable-list/hooks/useSelectableList';
|
import { useSelectableList } from '@/ui/layout/selectable-list/hooks/useSelectableList';
|
||||||
import { usePreviousHotkeyScope } from '@/ui/utilities/hotkey/hooks/usePreviousHotkeyScope';
|
import { usePreviousHotkeyScope } from '@/ui/utilities/hotkey/hooks/usePreviousHotkeyScope';
|
||||||
import { AppHotkeyScope } from '@/ui/utilities/hotkey/types/AppHotkeyScope';
|
import { AppHotkeyScope } from '@/ui/utilities/hotkey/types/AppHotkeyScope';
|
||||||
@ -42,17 +43,22 @@ export const useCommandMenu = () => {
|
|||||||
[goBackToPreviousHotkeyScope, resetSelectedItem, setIsCommandMenuOpened],
|
[goBackToPreviousHotkeyScope, resetSelectedItem, setIsCommandMenuOpened],
|
||||||
);
|
);
|
||||||
|
|
||||||
const toggleCommandMenu = useRecoilCallback(({ snapshot }) => async () => {
|
const toggleCommandMenu = useRecoilCallback(
|
||||||
const isCommandMenuOpened = snapshot
|
({ snapshot, set }) =>
|
||||||
.getLoadable(isCommandMenuOpenedState)
|
async () => {
|
||||||
.getValue();
|
const isCommandMenuOpened = snapshot
|
||||||
|
.getLoadable(isCommandMenuOpenedState)
|
||||||
|
.getValue();
|
||||||
|
|
||||||
if (isCommandMenuOpened) {
|
set(commandMenuSearchState, '');
|
||||||
closeCommandMenu();
|
|
||||||
} else {
|
if (isCommandMenuOpened) {
|
||||||
openCommandMenu();
|
closeCommandMenu();
|
||||||
}
|
} else {
|
||||||
});
|
openCommandMenu();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
const addToCommandMenu = useCallback(
|
const addToCommandMenu = useCallback(
|
||||||
(addCommand: Command[]) => {
|
(addCommand: Command[]) => {
|
||||||
|
|||||||
@ -0,0 +1,6 @@
|
|||||||
|
import { atom } from 'recoil';
|
||||||
|
|
||||||
|
export const commandMenuSearchState = atom<string>({
|
||||||
|
key: 'command-menu/commandMenuSearchState',
|
||||||
|
default: '',
|
||||||
|
});
|
||||||
@ -20,43 +20,20 @@ const getOneToManyRelation = () => {
|
|||||||
return {
|
return {
|
||||||
field: objectMetadataItem.fields.find((field) => field.name === 'company')!,
|
field: objectMetadataItem.fields.find((field) => field.name === 'company')!,
|
||||||
res: `company
|
res: `company
|
||||||
{
|
{
|
||||||
__typename
|
__typename
|
||||||
id
|
id
|
||||||
xLink
|
xLink
|
||||||
{
|
{
|
||||||
label
|
label
|
||||||
url
|
url
|
||||||
}
|
}
|
||||||
accountOwner
|
|
||||||
{
|
|
||||||
__typename
|
|
||||||
id
|
|
||||||
}
|
|
||||||
linkedinLink
|
linkedinLink
|
||||||
{
|
{
|
||||||
label
|
label
|
||||||
url
|
url
|
||||||
}
|
}
|
||||||
attachments
|
|
||||||
{
|
|
||||||
edges {
|
|
||||||
node {
|
|
||||||
__typename
|
|
||||||
id
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
domainName
|
domainName
|
||||||
opportunities
|
|
||||||
{
|
|
||||||
edges {
|
|
||||||
node {
|
|
||||||
__typename
|
|
||||||
id
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
annualRecurringRevenue
|
annualRecurringRevenue
|
||||||
{
|
{
|
||||||
amountMicros
|
amountMicros
|
||||||
@ -65,39 +42,12 @@ opportunities
|
|||||||
createdAt
|
createdAt
|
||||||
address
|
address
|
||||||
updatedAt
|
updatedAt
|
||||||
activityTargets
|
|
||||||
{
|
|
||||||
edges {
|
|
||||||
node {
|
|
||||||
__typename
|
|
||||||
id
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
favorites
|
|
||||||
{
|
|
||||||
edges {
|
|
||||||
node {
|
|
||||||
__typename
|
|
||||||
id
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
people
|
|
||||||
{
|
|
||||||
edges {
|
|
||||||
node {
|
|
||||||
__typename
|
|
||||||
id
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
name
|
name
|
||||||
accountOwnerId
|
accountOwnerId
|
||||||
employees
|
employees
|
||||||
id
|
id
|
||||||
idealCustomerProfile
|
idealCustomerProfile
|
||||||
}`,
|
}`,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -133,27 +83,17 @@ const getOneToManyFromRelationField = () => {
|
|||||||
return {
|
return {
|
||||||
field,
|
field,
|
||||||
res: `opportunities
|
res: `opportunities
|
||||||
{
|
{
|
||||||
edges {
|
edges {
|
||||||
node {
|
node {
|
||||||
__typename
|
__typename
|
||||||
id
|
id
|
||||||
personId
|
personId
|
||||||
pointOfContactId
|
pointOfContactId
|
||||||
updatedAt
|
updatedAt
|
||||||
company
|
|
||||||
{
|
|
||||||
__typename
|
|
||||||
id
|
|
||||||
}
|
|
||||||
companyId
|
companyId
|
||||||
pipelineStepId
|
pipelineStepId
|
||||||
probability
|
probability
|
||||||
pipelineStep
|
|
||||||
{
|
|
||||||
__typename
|
|
||||||
id
|
|
||||||
}
|
|
||||||
closeDate
|
closeDate
|
||||||
amount
|
amount
|
||||||
{
|
{
|
||||||
@ -162,19 +102,9 @@ closeDate
|
|||||||
}
|
}
|
||||||
id
|
id
|
||||||
createdAt
|
createdAt
|
||||||
pointOfContact
|
}
|
||||||
{
|
|
||||||
__typename
|
|
||||||
id
|
|
||||||
}
|
|
||||||
person
|
|
||||||
{
|
|
||||||
__typename
|
|
||||||
id
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}`,
|
||||||
}`,
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user