fix: Command bar is broken (#1617)
* Update CommandMenu.tsx * remove broken states * convert to array * convert filter conditions * empty condition * finally * update the logic * add test * lint * move file
This commit is contained in:
@ -17,7 +17,7 @@ import { getLogoUrlFromDomainName } from '~/utils';
|
||||
import { useCommandMenu } from '../hooks/useCommandMenu';
|
||||
import { commandMenuCommandsState } from '../states/commandMenuCommandsState';
|
||||
import { isCommandMenuOpenedState } from '../states/isCommandMenuOpenedState';
|
||||
import { CommandType } from '../types/Command';
|
||||
import { Command, CommandType } from '../types/Command';
|
||||
|
||||
import { CommandMenuItem } from './CommandMenuItem';
|
||||
import {
|
||||
@ -65,6 +65,7 @@ export const CommandMenu = () => {
|
||||
limit: 3,
|
||||
},
|
||||
});
|
||||
|
||||
const companies = companyData?.searchResults ?? [];
|
||||
|
||||
const { data: activityData } = useSearchActivityQuery({
|
||||
@ -78,18 +79,38 @@ export const CommandMenu = () => {
|
||||
limit: 3,
|
||||
},
|
||||
});
|
||||
|
||||
const activities = activityData?.searchResults ?? [];
|
||||
|
||||
const matchingNavigateCommand = commandMenuCommands.find(
|
||||
const checkInShortcuts = (cmd: Command, search: string) => {
|
||||
if (cmd.shortcuts && cmd.shortcuts.length > 0) {
|
||||
return cmd.shortcuts
|
||||
.join('')
|
||||
.toLowerCase()
|
||||
.includes(search.toLowerCase());
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
const checkInLabels = (cmd: Command, search: string) => {
|
||||
if (cmd.label) {
|
||||
return cmd.label.toLowerCase().includes(search.toLowerCase());
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
const matchingNavigateCommand = commandMenuCommands.filter(
|
||||
(cmd) =>
|
||||
cmd.shortcuts?.join('') === search?.toUpperCase() &&
|
||||
cmd.type === CommandType.Navigate,
|
||||
(search.length > 0
|
||||
? checkInShortcuts(cmd, search) || checkInLabels(cmd, search)
|
||||
: true) && cmd.type === CommandType.Navigate,
|
||||
);
|
||||
|
||||
const matchingCreateCommand = commandMenuCommands.find(
|
||||
const matchingCreateCommand = commandMenuCommands.filter(
|
||||
(cmd) =>
|
||||
cmd.shortcuts?.join('') === search?.toUpperCase() &&
|
||||
cmd.type === CommandType.Create,
|
||||
(search.length > 0
|
||||
? checkInShortcuts(cmd, search) || checkInLabels(cmd, search)
|
||||
: true) && cmd.type === CommandType.Create,
|
||||
);
|
||||
|
||||
return (
|
||||
@ -100,122 +121,96 @@ export const CommandMenu = () => {
|
||||
closeCommandMenu();
|
||||
}
|
||||
}}
|
||||
label="Global Command Menu"
|
||||
shouldFilter={false}
|
||||
label="Global Command Menu"
|
||||
>
|
||||
<StyledInput
|
||||
placeholder="Search"
|
||||
value={search}
|
||||
placeholder="Search"
|
||||
onValueChange={setSearch}
|
||||
/>
|
||||
<StyledList>
|
||||
<StyledEmpty>No results found.</StyledEmpty>
|
||||
{!matchingCreateCommand && (
|
||||
{matchingCreateCommand.length < 1 &&
|
||||
matchingNavigateCommand.length < 1 &&
|
||||
people.length < 1 &&
|
||||
companies.length < 1 &&
|
||||
activities.length < 1 && <StyledEmpty>No results found.</StyledEmpty>}
|
||||
{matchingCreateCommand.length > 0 && (
|
||||
<StyledGroup heading="Create">
|
||||
{commandMenuCommands
|
||||
.filter((cmd) => cmd.type === CommandType.Create)
|
||||
.map((cmd) => (
|
||||
<CommandMenuItem
|
||||
key={cmd.label}
|
||||
to={cmd.to}
|
||||
label={cmd.label}
|
||||
Icon={cmd.Icon}
|
||||
shortcuts={cmd.shortcuts || []}
|
||||
onClick={cmd.onCommandClick}
|
||||
/>
|
||||
))}
|
||||
{matchingCreateCommand.map((cmd) => (
|
||||
<CommandMenuItem
|
||||
to={cmd.to}
|
||||
key={cmd.label}
|
||||
Icon={cmd.Icon}
|
||||
label={cmd.label}
|
||||
onClick={cmd.onCommandClick}
|
||||
shortcuts={cmd.shortcuts || []}
|
||||
/>
|
||||
))}
|
||||
</StyledGroup>
|
||||
)}
|
||||
{matchingCreateCommand && (
|
||||
<StyledGroup heading="Create">
|
||||
<CommandMenuItem
|
||||
key={matchingCreateCommand.label}
|
||||
to={matchingCreateCommand.to}
|
||||
label={matchingCreateCommand.label}
|
||||
Icon={matchingCreateCommand.Icon}
|
||||
shortcuts={matchingCreateCommand.shortcuts || []}
|
||||
onClick={matchingCreateCommand.onCommandClick}
|
||||
/>
|
||||
</StyledGroup>
|
||||
)}
|
||||
{matchingNavigateCommand && (
|
||||
{matchingNavigateCommand.length > 0 && (
|
||||
<StyledGroup heading="Navigate">
|
||||
<CommandMenuItem
|
||||
to={matchingNavigateCommand.to}
|
||||
label={matchingNavigateCommand.label}
|
||||
shortcuts={matchingNavigateCommand.shortcuts}
|
||||
key={matchingNavigateCommand.label}
|
||||
/>
|
||||
{matchingNavigateCommand.map((cmd) => (
|
||||
<CommandMenuItem
|
||||
to={cmd.to}
|
||||
key={cmd.label}
|
||||
label={cmd.label}
|
||||
onClick={cmd.onCommandClick}
|
||||
shortcuts={cmd.shortcuts || []}
|
||||
/>
|
||||
))}
|
||||
</StyledGroup>
|
||||
)}
|
||||
{!!people.length && (
|
||||
{people.length > 0 && (
|
||||
<StyledGroup heading="People">
|
||||
{people.map((person) => (
|
||||
<CommandMenuItem
|
||||
key={person.id}
|
||||
to={`person/${person.id}`}
|
||||
label={person.displayName}
|
||||
key={person.id}
|
||||
Icon={() => (
|
||||
<Avatar
|
||||
avatarUrl={null}
|
||||
placeholder={person.displayName}
|
||||
colorId={person.id}
|
||||
type="rounded"
|
||||
avatarUrl={null}
|
||||
colorId={person.id}
|
||||
placeholder={person.displayName}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
))}
|
||||
</StyledGroup>
|
||||
)}
|
||||
{!!companies.length && (
|
||||
{companies.length > 0 && (
|
||||
<StyledGroup heading="Companies">
|
||||
{companies.map((company) => (
|
||||
<CommandMenuItem
|
||||
to={`companies/${company.id}`}
|
||||
label={company.name}
|
||||
key={company.id}
|
||||
label={company.name}
|
||||
to={`companies/${company.id}`}
|
||||
Icon={() => (
|
||||
<Avatar
|
||||
avatarUrl={getLogoUrlFromDomainName(company.domainName)}
|
||||
colorId={company.id}
|
||||
placeholder={company.name}
|
||||
avatarUrl={getLogoUrlFromDomainName(company.domainName)}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
))}
|
||||
</StyledGroup>
|
||||
)}
|
||||
{!!activities.length && (
|
||||
{activities.length > 0 && (
|
||||
<StyledGroup heading="Notes">
|
||||
{activities.map((activity) => (
|
||||
<CommandMenuItem
|
||||
onClick={() => openActivityRightDrawer(activity.id)}
|
||||
label={activity.title ?? ''}
|
||||
key={activity.id}
|
||||
Icon={IconNotes}
|
||||
key={activity.id}
|
||||
label={activity.title ?? ''}
|
||||
onClick={() => openActivityRightDrawer(activity.id)}
|
||||
/>
|
||||
))}
|
||||
</StyledGroup>
|
||||
)}
|
||||
{!matchingNavigateCommand && (
|
||||
<StyledGroup heading="Navigate">
|
||||
{commandMenuCommands
|
||||
.filter(
|
||||
(cmd) =>
|
||||
(cmd.shortcuts?.join('').includes(search?.toUpperCase()) ||
|
||||
cmd.label?.toUpperCase().includes(search?.toUpperCase())) &&
|
||||
cmd.type === CommandType.Navigate,
|
||||
)
|
||||
.map((cmd) => (
|
||||
<CommandMenuItem
|
||||
key={cmd.shortcuts?.join('') ?? ''}
|
||||
to={cmd.to}
|
||||
label={cmd.label}
|
||||
shortcuts={cmd.shortcuts}
|
||||
/>
|
||||
))}
|
||||
</StyledGroup>
|
||||
)}
|
||||
</StyledList>
|
||||
</StyledDialog>
|
||||
);
|
||||
|
||||
@ -1,15 +1,160 @@
|
||||
import { MemoryRouter } from 'react-router-dom';
|
||||
import { expect } from '@storybook/jest';
|
||||
import type { Meta, StoryObj } from '@storybook/react';
|
||||
import { fireEvent, userEvent, within } from '@storybook/testing-library';
|
||||
|
||||
import { ComponentDecorator } from '~/testing/decorators/ComponentDecorator';
|
||||
import { sleep } from '~/testing/sleep';
|
||||
|
||||
import { CommandMenu } from '../CommandMenu';
|
||||
import { WrapperCommandMenu } from './WrapperCommandMenu';
|
||||
|
||||
const meta: Meta<typeof CommandMenu> = {
|
||||
title: 'Modules/CommandMenu/CommandMenu',
|
||||
component: CommandMenu,
|
||||
enum CommandType {
|
||||
Navigate = 'Navigate',
|
||||
Create = 'Create',
|
||||
}
|
||||
|
||||
const meta: Meta<typeof WrapperCommandMenu> = {
|
||||
title: 'Modules/CommandMenu/WrapperCommandMenu',
|
||||
component: () => (
|
||||
<WrapperCommandMenu
|
||||
companies={[
|
||||
{
|
||||
__typename: 'Company',
|
||||
accountOwner: null,
|
||||
address: '',
|
||||
createdAt: '2023-09-19T08:35:37.174Z',
|
||||
domainName: 'facebook.com',
|
||||
employees: null,
|
||||
linkedinUrl: null,
|
||||
xUrl: null,
|
||||
annualRecurringRevenue: null,
|
||||
idealCustomerProfile: false,
|
||||
id: 'twenty-118995f3-5d81-46d6-bf83-f7fd33ea6102',
|
||||
name: 'Facebook',
|
||||
_activityCount: 0,
|
||||
},
|
||||
{
|
||||
__typename: 'Company',
|
||||
accountOwner: null,
|
||||
address: '',
|
||||
createdAt: '2023-09-19T08:35:37.188Z',
|
||||
domainName: 'airbnb.com',
|
||||
employees: null,
|
||||
linkedinUrl: null,
|
||||
xUrl: null,
|
||||
annualRecurringRevenue: null,
|
||||
idealCustomerProfile: false,
|
||||
id: 'twenty-89bb825c-171e-4bcc-9cf7-43448d6fb278',
|
||||
name: 'Airbnb',
|
||||
_activityCount: 0,
|
||||
},
|
||||
{
|
||||
__typename: 'Company',
|
||||
accountOwner: null,
|
||||
address: '',
|
||||
createdAt: '2023-09-19T08:35:37.206Z',
|
||||
domainName: 'claap.io',
|
||||
employees: null,
|
||||
linkedinUrl: null,
|
||||
xUrl: null,
|
||||
annualRecurringRevenue: null,
|
||||
idealCustomerProfile: false,
|
||||
id: 'twenty-9d162de6-cfbf-4156-a790-e39854dcd4eb',
|
||||
name: 'Claap',
|
||||
_activityCount: 0,
|
||||
},
|
||||
]}
|
||||
activities={[
|
||||
{
|
||||
__typename: 'Activity',
|
||||
id: 'twenty-fe256b39-3ec3-4fe3-8997-b76aa0bfb400',
|
||||
title: 'Performance update',
|
||||
body: '[{"id":"555df0c3-ab88-4c62-abae-c9b557c37c5b","type":"paragraph","props":{"textColor":"default","backgroundColor":"default","textAlignment":"left"},"content":[{"type":"text","text":"In the North American region, we have observed a strong growth rate of 18% in sales. Europe followed suit with a significant 14% increase, while Asia-Pacific sustained its performance with a steady 10% rise. Special kudos to the North American team for the excellent work done in penetrating new markets and establishing stronger footholds in the existing ones.","styles":{}}],"children":[]},{"id":"13530934-b3ce-4332-9238-3760aa4acb3e","type":"paragraph","props":{"textColor":"default","backgroundColor":"default","textAlignment":"left"},"content":[],"children":[]}]',
|
||||
},
|
||||
{
|
||||
__typename: 'Activity',
|
||||
id: 'twenty-fe256b39-3ec3-4fe3-8997-b76aa0bfc408',
|
||||
title: 'Buyout Proposal',
|
||||
body: '[{"id":"333df0c3-ab88-4c62-abae-c9b557c37c5b","type":"paragraph","props":{"textColor":"default","backgroundColor":"default","textAlignment":"left"},"content":[{"type":"text","text":"We are considering the potential acquisition of [Company], a leading company in [Industry/Specific Technology]. This company has demonstrated remarkable success and pioneering advancements in their field, paralleling our own commitment to progress. By integrating their expertise with our own, we believe that we can amplify our growth, broaden our offerings, and fortify our position at the forefront of technology. This prospective partnership could help to ensure our continued leadership in the industry and allow us to deliver even more innovative solutions for our customers.","styles":{}}],"children":[]},{"id":"13530934-b3ce-4332-9238-3760aa4acb3e","type":"paragraph","props":{"textColor":"default","backgroundColor":"default","textAlignment":"left"},"content":[],"children":[]}]',
|
||||
},
|
||||
]}
|
||||
values={[
|
||||
{
|
||||
to: '/people',
|
||||
label: 'Go to People',
|
||||
type: CommandType.Navigate,
|
||||
shortcuts: ['G', 'P'],
|
||||
},
|
||||
{
|
||||
to: '/companies',
|
||||
label: 'Go to Companies',
|
||||
type: CommandType.Navigate,
|
||||
shortcuts: ['G', 'C'],
|
||||
},
|
||||
{
|
||||
to: '/opportunities',
|
||||
label: 'Go to Opportunities',
|
||||
type: CommandType.Navigate,
|
||||
shortcuts: ['G', 'O'],
|
||||
},
|
||||
{
|
||||
to: '/settings/profile',
|
||||
label: 'Go to Settings',
|
||||
type: CommandType.Navigate,
|
||||
shortcuts: ['G', 'S'],
|
||||
},
|
||||
{
|
||||
to: '/tasks',
|
||||
label: 'Go to Tasks',
|
||||
type: CommandType.Navigate,
|
||||
shortcuts: ['G', 'T'],
|
||||
},
|
||||
{
|
||||
to: '',
|
||||
label: 'Create Task',
|
||||
type: CommandType.Create,
|
||||
},
|
||||
]}
|
||||
people={[
|
||||
{
|
||||
__typename: 'Person',
|
||||
id: 'twenty-0aa00beb-ac73-4797-824e-87a1f5aea9e0',
|
||||
phone: '+33780123456',
|
||||
email: 'sylvie.palmer@linkedin.com',
|
||||
city: 'Los Angeles',
|
||||
firstName: 'Sylvie',
|
||||
lastName: 'Palmer',
|
||||
displayName: 'Sylvie Palmer',
|
||||
avatarUrl: null,
|
||||
createdAt: '2023-09-19T08:35:37.240Z',
|
||||
},
|
||||
{
|
||||
__typename: 'Person',
|
||||
id: 'twenty-1d151852-490f-4466-8391-733cfd66a0c8',
|
||||
phone: '+33782345678',
|
||||
email: 'isabella.scott@microsoft.com',
|
||||
city: 'New York',
|
||||
firstName: 'Isabella',
|
||||
lastName: 'Scott',
|
||||
displayName: 'Isabella Scott',
|
||||
avatarUrl: null,
|
||||
createdAt: '2023-09-19T08:35:37.257Z',
|
||||
},
|
||||
{
|
||||
__typename: 'Person',
|
||||
id: 'twenty-240da2ec-2d40-4e49-8df4-9c6a049190df',
|
||||
phone: '+33788901234',
|
||||
email: 'bertrand.voulzy@google.com',
|
||||
city: 'Seattle',
|
||||
firstName: 'Bertrand',
|
||||
lastName: 'Voulzy',
|
||||
displayName: 'Bertrand Voulzy',
|
||||
avatarUrl: null,
|
||||
createdAt: '2023-09-19T08:35:37.291Z',
|
||||
},
|
||||
]}
|
||||
/>
|
||||
),
|
||||
decorators: [
|
||||
(Story) => (
|
||||
<MemoryRouter>
|
||||
@ -21,36 +166,94 @@ const meta: Meta<typeof CommandMenu> = {
|
||||
};
|
||||
|
||||
export default meta;
|
||||
type Story = StoryObj<typeof CommandMenu>;
|
||||
type Story = StoryObj<typeof WrapperCommandMenu>;
|
||||
|
||||
export const Default: Story = {};
|
||||
|
||||
export const CmdK: Story = {
|
||||
export const DefaultWithoutSearch: Story = {
|
||||
play: async ({ canvasElement }) => {
|
||||
fireEvent.keyDown(canvasElement, {
|
||||
key: 'k',
|
||||
code: 'KeyK',
|
||||
metaKey: true,
|
||||
});
|
||||
|
||||
await sleep(50);
|
||||
|
||||
const canvas = within(document.body);
|
||||
|
||||
const searchInput = await canvas.findByPlaceholderText('Search');
|
||||
await sleep(10);
|
||||
await userEvent.type(searchInput, '');
|
||||
expect(await canvas.findByText('Create Task')).toBeInTheDocument();
|
||||
expect(await canvas.findByText('Go to People')).toBeInTheDocument();
|
||||
expect(await canvas.findByText('Go to Companies')).toBeInTheDocument();
|
||||
expect(await canvas.findByText('Go to Opportunities')).toBeInTheDocument();
|
||||
expect(await canvas.findByText('Go to Settings')).toBeInTheDocument();
|
||||
expect(await canvas.findByText('Go to Tasks')).toBeInTheDocument();
|
||||
},
|
||||
};
|
||||
|
||||
await userEvent.type(searchInput, '{arrowdown}');
|
||||
await userEvent.type(searchInput, '{arrowup}');
|
||||
await userEvent.type(searchInput, '{arrowdown}');
|
||||
await userEvent.type(searchInput, '{arrowdown}');
|
||||
await userEvent.type(searchInput, '{enter}');
|
||||
|
||||
await sleep(50);
|
||||
|
||||
export const MatchingPersonCompanyActivityCreateNavigate: Story = {
|
||||
play: async ({ canvasElement }) => {
|
||||
fireEvent.keyDown(canvasElement, {
|
||||
key: 'k',
|
||||
code: 'KeyK',
|
||||
metaKey: true,
|
||||
});
|
||||
await sleep(50);
|
||||
const canvas = within(document.body);
|
||||
const searchInput = await canvas.findByPlaceholderText('Search');
|
||||
await sleep(10);
|
||||
await userEvent.type(searchInput, 'a');
|
||||
expect(await canvas.findByText('Isabella Scott')).toBeInTheDocument();
|
||||
expect(await canvas.findByText('Airbnb')).toBeInTheDocument();
|
||||
expect(await canvas.findByText('Buyout Proposal')).toBeInTheDocument();
|
||||
expect(await canvas.findByText('Create Task')).toBeInTheDocument();
|
||||
expect(await canvas.findByText('Go to Tasks')).toBeInTheDocument();
|
||||
},
|
||||
};
|
||||
|
||||
export const OnlyMatchingCreateAndNavigate: Story = {
|
||||
play: async ({ canvasElement }) => {
|
||||
fireEvent.keyDown(canvasElement, {
|
||||
key: 'k',
|
||||
code: 'KeyK',
|
||||
metaKey: true,
|
||||
});
|
||||
await sleep(50);
|
||||
const canvas = within(document.body);
|
||||
const searchInput = await canvas.findByPlaceholderText('Search');
|
||||
await sleep(10);
|
||||
await userEvent.type(searchInput, 'tas');
|
||||
expect(await canvas.findByText('Create Task')).toBeInTheDocument();
|
||||
expect(await canvas.findByText('Go to Tasks')).toBeInTheDocument();
|
||||
},
|
||||
};
|
||||
|
||||
export const AtleastMatchingOnePerson: Story = {
|
||||
play: async ({ canvasElement }) => {
|
||||
fireEvent.keyDown(canvasElement, {
|
||||
key: 'k',
|
||||
code: 'KeyK',
|
||||
metaKey: true,
|
||||
});
|
||||
await sleep(50);
|
||||
const canvas = within(document.body);
|
||||
const searchInput = await canvas.findByPlaceholderText('Search');
|
||||
await sleep(10);
|
||||
await userEvent.type(searchInput, 'sy');
|
||||
expect(await canvas.findByText('Sylvie Palmer')).toBeInTheDocument();
|
||||
},
|
||||
};
|
||||
|
||||
export const NotMatchingAnything: Story = {
|
||||
play: async ({ canvasElement }) => {
|
||||
fireEvent.keyDown(canvasElement, {
|
||||
key: 'k',
|
||||
code: 'KeyK',
|
||||
metaKey: true,
|
||||
});
|
||||
await sleep(50);
|
||||
const canvas = within(document.body);
|
||||
const searchInput = await canvas.findByPlaceholderText('Search');
|
||||
await sleep(10);
|
||||
await userEvent.type(searchInput, 'asdasdasd');
|
||||
expect(await canvas.findByText('No results found.')).toBeInTheDocument();
|
||||
},
|
||||
};
|
||||
|
||||
@ -0,0 +1,247 @@
|
||||
import { useState } from 'react';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
|
||||
import { useOpenActivityRightDrawer } from '@/activities/hooks/useOpenActivityRightDrawer';
|
||||
import { IconNotes } from '@/ui/icon';
|
||||
import { useScopedHotkeys } from '@/ui/utilities/hotkey/hooks/useScopedHotkeys';
|
||||
import { AppHotkeyScope } from '@/ui/utilities/hotkey/types/AppHotkeyScope';
|
||||
import { Avatar } from '@/users/components/Avatar';
|
||||
import { getLogoUrlFromDomainName } from '~/utils';
|
||||
|
||||
import { useCommandMenu } from '../../hooks/useCommandMenu';
|
||||
import { isCommandMenuOpenedState } from '../../states/isCommandMenuOpenedState';
|
||||
import { Command, CommandType } from '../../types/Command';
|
||||
import { CommandMenuItem } from '../CommandMenuItem';
|
||||
import {
|
||||
StyledDialog,
|
||||
StyledEmpty,
|
||||
StyledGroup,
|
||||
StyledInput,
|
||||
StyledList,
|
||||
} from '../CommandMenuStyles';
|
||||
|
||||
export const WrapperCommandMenu = ({
|
||||
values,
|
||||
people,
|
||||
companies,
|
||||
activities,
|
||||
}: {
|
||||
values: Command[];
|
||||
people: Array<{
|
||||
__typename?: 'Person';
|
||||
id: string;
|
||||
phone?: string | null;
|
||||
email?: string | null;
|
||||
city?: string | null;
|
||||
firstName?: string | null;
|
||||
lastName?: string | null;
|
||||
displayName: string;
|
||||
avatarUrl?: string | null;
|
||||
createdAt: string;
|
||||
}>;
|
||||
companies: Array<{
|
||||
__typename?: 'Company';
|
||||
address: string;
|
||||
createdAt: string;
|
||||
domainName: string;
|
||||
employees?: number | null;
|
||||
linkedinUrl?: string | null;
|
||||
xUrl?: string | null;
|
||||
annualRecurringRevenue?: number | null;
|
||||
idealCustomerProfile: boolean;
|
||||
id: string;
|
||||
name: string;
|
||||
_activityCount: number;
|
||||
accountOwner?: {
|
||||
__typename?: 'User';
|
||||
id: string;
|
||||
email: string;
|
||||
displayName: string;
|
||||
avatarUrl?: string | null;
|
||||
} | null;
|
||||
}>;
|
||||
activities: Array<{
|
||||
__typename?: 'Activity';
|
||||
id: string;
|
||||
title?: string | null;
|
||||
body?: string | null;
|
||||
}>;
|
||||
}) => {
|
||||
const { openCommandMenu, closeCommandMenu } = useCommandMenu();
|
||||
const openActivityRightDrawer = useOpenActivityRightDrawer();
|
||||
const isCommandMenuOpened = useRecoilValue(isCommandMenuOpenedState);
|
||||
const [search, setSearch] = useState('');
|
||||
const commandMenuCommands = values;
|
||||
|
||||
const peopleData = people.filter((i) =>
|
||||
search.length > 0
|
||||
? (i.firstName
|
||||
? i.firstName?.toLowerCase().includes(search.toLowerCase())
|
||||
: false) ||
|
||||
(i.lastName
|
||||
? i.lastName?.toLowerCase().includes(search.toLowerCase())
|
||||
: false)
|
||||
: false,
|
||||
);
|
||||
|
||||
const companyData = companies.filter((i) =>
|
||||
search.length > 0
|
||||
? i.name
|
||||
? i.name?.toLowerCase().includes(search.toLowerCase())
|
||||
: false
|
||||
: false,
|
||||
);
|
||||
|
||||
const activityData = activities.filter((i) =>
|
||||
search.length > 0
|
||||
? (i.title
|
||||
? i.title?.toLowerCase().includes(search.toLowerCase())
|
||||
: false) ||
|
||||
(i.body ? i.body?.toLowerCase().includes(search.toLowerCase()) : false)
|
||||
: false,
|
||||
);
|
||||
|
||||
useScopedHotkeys(
|
||||
'ctrl+k,meta+k',
|
||||
() => {
|
||||
openCommandMenu();
|
||||
},
|
||||
AppHotkeyScope.CommandMenu,
|
||||
[openCommandMenu],
|
||||
);
|
||||
|
||||
const checkInShortcuts = (cmd: Command, search: string) => {
|
||||
if (cmd.shortcuts && cmd.shortcuts.length > 0) {
|
||||
return cmd.shortcuts
|
||||
.join('')
|
||||
.toLowerCase()
|
||||
.includes(search.toLowerCase());
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
const checkInLabels = (cmd: Command, search: string) => {
|
||||
if (cmd.label) {
|
||||
return cmd.label.toLowerCase().includes(search.toLowerCase());
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
const matchingNavigateCommand = commandMenuCommands.filter(
|
||||
(cmd) =>
|
||||
(search.length > 0
|
||||
? checkInShortcuts(cmd, search) || checkInLabels(cmd, search)
|
||||
: true) && cmd.type === CommandType.Navigate,
|
||||
);
|
||||
|
||||
const matchingCreateCommand = commandMenuCommands.filter(
|
||||
(cmd) =>
|
||||
(search.length > 0
|
||||
? checkInShortcuts(cmd, search) || checkInLabels(cmd, search)
|
||||
: true) && cmd.type === CommandType.Create,
|
||||
);
|
||||
|
||||
return (
|
||||
<StyledDialog
|
||||
open={isCommandMenuOpened}
|
||||
onOpenChange={(opened) => {
|
||||
if (!opened) {
|
||||
closeCommandMenu();
|
||||
}
|
||||
}}
|
||||
shouldFilter={false}
|
||||
label="Global Command Menu"
|
||||
>
|
||||
<StyledInput
|
||||
value={search}
|
||||
placeholder="Search"
|
||||
onValueChange={setSearch}
|
||||
/>
|
||||
<StyledList>
|
||||
{matchingCreateCommand.length < 1 &&
|
||||
matchingNavigateCommand.length < 1 &&
|
||||
peopleData.length < 1 &&
|
||||
companyData.length < 1 &&
|
||||
activityData.length < 1 && (
|
||||
<StyledEmpty>No results found.</StyledEmpty>
|
||||
)}
|
||||
{matchingCreateCommand.length > 0 && (
|
||||
<StyledGroup heading="Create">
|
||||
{matchingCreateCommand.map((cmd) => (
|
||||
<CommandMenuItem
|
||||
to={cmd.to}
|
||||
key={cmd.label}
|
||||
Icon={cmd.Icon}
|
||||
label={cmd.label}
|
||||
onClick={cmd.onCommandClick}
|
||||
shortcuts={cmd.shortcuts || []}
|
||||
/>
|
||||
))}
|
||||
</StyledGroup>
|
||||
)}
|
||||
{matchingNavigateCommand.length > 0 && (
|
||||
<StyledGroup heading="Navigate">
|
||||
{matchingNavigateCommand.map((cmd) => (
|
||||
<CommandMenuItem
|
||||
to={cmd.to}
|
||||
key={cmd.label}
|
||||
label={cmd.label}
|
||||
onClick={cmd.onCommandClick}
|
||||
shortcuts={cmd.shortcuts || []}
|
||||
/>
|
||||
))}
|
||||
</StyledGroup>
|
||||
)}
|
||||
{peopleData.length > 0 && (
|
||||
<StyledGroup heading="People">
|
||||
{peopleData.map((person) => (
|
||||
<CommandMenuItem
|
||||
key={person.id}
|
||||
to={`person/${person.id}`}
|
||||
label={person.displayName}
|
||||
Icon={() => (
|
||||
<Avatar
|
||||
type="rounded"
|
||||
avatarUrl={null}
|
||||
colorId={person.id}
|
||||
placeholder={person.displayName}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
))}
|
||||
</StyledGroup>
|
||||
)}
|
||||
{companyData.length > 0 && (
|
||||
<StyledGroup heading="Companies">
|
||||
{companyData.map((company) => (
|
||||
<CommandMenuItem
|
||||
key={company.id}
|
||||
label={company.name}
|
||||
to={`companies/${company.id}`}
|
||||
Icon={() => (
|
||||
<Avatar
|
||||
colorId={company.id}
|
||||
placeholder={company.name}
|
||||
avatarUrl={getLogoUrlFromDomainName(company.domainName)}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
))}
|
||||
</StyledGroup>
|
||||
)}
|
||||
{activityData.length > 0 && (
|
||||
<StyledGroup heading="Notes">
|
||||
{activityData.map((activity) => (
|
||||
<CommandMenuItem
|
||||
Icon={IconNotes}
|
||||
key={activity.id}
|
||||
label={activity.title ?? ''}
|
||||
onClick={() => openActivityRightDrawer(activity.id)}
|
||||
/>
|
||||
))}
|
||||
</StyledGroup>
|
||||
)}
|
||||
</StyledList>
|
||||
</StyledDialog>
|
||||
);
|
||||
};
|
||||
Reference in New Issue
Block a user