198 lines
6.0 KiB
TypeScript
198 lines
6.0 KiB
TypeScript
import { useState } from 'react';
|
|
import { useRecoilValue } from 'recoil';
|
|
|
|
import { useOpenActivityRightDrawer } from '@/activities/hooks/useOpenActivityRightDrawer';
|
|
import { useFindManyObjectRecords } from '@/object-record/hooks/useFindManyObjectRecords';
|
|
import { Person } from '@/people/types/Person';
|
|
import { IconNotes } from '@/ui/display/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 { commandMenuCommandsState } from '../states/commandMenuCommandsState';
|
|
import { isCommandMenuOpenedState } from '../states/isCommandMenuOpenedState';
|
|
import { Command, CommandType } from '../types/Command';
|
|
|
|
import { CommandGroup } from './CommandGroup';
|
|
import { CommandMenuItem } from './CommandMenuItem';
|
|
import {
|
|
StyledDialog,
|
|
StyledEmpty,
|
|
StyledInput,
|
|
StyledList,
|
|
} from './CommandMenuStyles';
|
|
|
|
export const CommandMenu = () => {
|
|
const { openCommandMenu, closeCommandMenu, toggleCommandMenu } =
|
|
useCommandMenu();
|
|
const openActivityRightDrawer = useOpenActivityRightDrawer();
|
|
const isCommandMenuOpened = useRecoilValue(isCommandMenuOpenedState);
|
|
const [search, setSearch] = useState('');
|
|
const commandMenuCommands = useRecoilValue(commandMenuCommandsState);
|
|
|
|
useScopedHotkeys(
|
|
'ctrl+k,meta+k',
|
|
() => {
|
|
setSearch('');
|
|
toggleCommandMenu();
|
|
},
|
|
AppHotkeyScope.CommandMenu,
|
|
[openCommandMenu, setSearch],
|
|
);
|
|
|
|
const { objects: people } = useFindManyObjectRecords<Person>({
|
|
skip: !isCommandMenuOpened,
|
|
objectNamePlural: 'people',
|
|
filter: {
|
|
or: [
|
|
{ name: { firstName: { like: `%${search}%` } } },
|
|
{ name: { firstName: { like: `%${search}%` } } },
|
|
],
|
|
},
|
|
limit: 3,
|
|
});
|
|
|
|
const { objects: companies } = useFindManyObjectRecords<Person>({
|
|
skip: !isCommandMenuOpened,
|
|
objectNamePlural: 'companies',
|
|
filter: {
|
|
name: { like: `%${search}%` },
|
|
},
|
|
limit: 3,
|
|
});
|
|
|
|
const { objects: activities } = useFindManyObjectRecords<Person>({
|
|
skip: !isCommandMenuOpened,
|
|
objectNamePlural: 'activities',
|
|
filter: {
|
|
or: [
|
|
{ title: { like: `%${search}%` } },
|
|
{ body: { like: `%${search}%` } },
|
|
],
|
|
},
|
|
limit: 3,
|
|
});
|
|
|
|
const checkInShortcuts = (cmd: Command, search: string) => {
|
|
return (cmd.firstHotKey + (cmd.secondHotKey ?? ''))
|
|
.toLowerCase()
|
|
.includes(search.toLowerCase());
|
|
};
|
|
|
|
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>
|
|
<StyledEmpty>No results found.</StyledEmpty>
|
|
<CommandGroup heading="Create">
|
|
{matchingCreateCommand.map((cmd) => (
|
|
<CommandMenuItem
|
|
to={cmd.to}
|
|
key={cmd.label}
|
|
Icon={cmd.Icon}
|
|
label={cmd.label}
|
|
onClick={cmd.onCommandClick}
|
|
firstHotKey={cmd.firstHotKey}
|
|
secondHotKey={cmd.secondHotKey}
|
|
/>
|
|
))}
|
|
</CommandGroup>
|
|
<CommandGroup heading="Navigate">
|
|
{matchingNavigateCommand.map((cmd) => (
|
|
<CommandMenuItem
|
|
to={cmd.to}
|
|
key={cmd.label}
|
|
label={cmd.label}
|
|
Icon={cmd.Icon}
|
|
onClick={cmd.onCommandClick}
|
|
firstHotKey={cmd.firstHotKey}
|
|
secondHotKey={cmd.secondHotKey}
|
|
/>
|
|
))}
|
|
</CommandGroup>
|
|
<CommandGroup heading="People">
|
|
{people.map((person) => (
|
|
<CommandMenuItem
|
|
key={person.id}
|
|
to={`object/person/${person.id}`}
|
|
label={person.name?.firstName + ' ' + person.name?.lastName}
|
|
Icon={() => (
|
|
<Avatar
|
|
type="rounded"
|
|
avatarUrl={person.avatarUrl}
|
|
colorId={person.id}
|
|
placeholder={
|
|
person.name?.firstName + ' ' + person.name?.lastName
|
|
}
|
|
/>
|
|
)}
|
|
/>
|
|
))}
|
|
</CommandGroup>
|
|
<CommandGroup heading="Companies">
|
|
{companies.map((company) => (
|
|
<CommandMenuItem
|
|
key={company.id}
|
|
label={company.name}
|
|
to={`object/company/${company.id}`}
|
|
Icon={() => (
|
|
<Avatar
|
|
colorId={company.id}
|
|
placeholder={company.name}
|
|
avatarUrl={getLogoUrlFromDomainName(company.domainName)}
|
|
/>
|
|
)}
|
|
/>
|
|
))}
|
|
</CommandGroup>
|
|
<CommandGroup heading="Notes">
|
|
{activities.map((activity) => (
|
|
<CommandMenuItem
|
|
Icon={IconNotes}
|
|
key={activity.id}
|
|
label={activity.title ?? ''}
|
|
onClick={() => openActivityRightDrawer(activity.id)}
|
|
/>
|
|
))}
|
|
</CommandGroup>
|
|
</StyledList>
|
|
</StyledDialog>
|
|
);
|
|
};
|