2294 feat(frontend): styling shortcut keys (#2336)

* 2294 feat(frontend): styling shortcut keys

* 2294 fix(front): pr requested changes

* Fix component interface

---------

Co-authored-by: Charles Bochet <charles@twenty.com>
This commit is contained in:
Kanav Arora
2023-11-09 19:48:51 +05:30
committed by GitHub
parent aa09b5758c
commit 279630052f
8 changed files with 111 additions and 52 deletions

View File

@ -88,13 +88,9 @@ export const CommandMenu = () => {
const activities = activityData?.searchResults ?? []; const activities = activityData?.searchResults ?? [];
const checkInShortcuts = (cmd: Command, search: string) => { const checkInShortcuts = (cmd: Command, search: string) => {
if (cmd.shortcuts && cmd.shortcuts.length > 0) { return (cmd.firstHotKey + (cmd.secondHotKey ?? ''))
return cmd.shortcuts .toLowerCase()
.join('') .includes(search.toLowerCase());
.toLowerCase()
.includes(search.toLowerCase());
}
return false;
}; };
const checkInLabels = (cmd: Command, search: string) => { const checkInLabels = (cmd: Command, search: string) => {
@ -144,7 +140,8 @@ export const CommandMenu = () => {
Icon={cmd.Icon} Icon={cmd.Icon}
label={cmd.label} label={cmd.label}
onClick={cmd.onCommandClick} onClick={cmd.onCommandClick}
shortcuts={cmd.shortcuts || []} firstHotKey={cmd.firstHotKey}
secondHotKey={cmd.secondHotKey}
/> />
))} ))}
</CommandGroup> </CommandGroup>
@ -156,7 +153,8 @@ export const CommandMenu = () => {
label={cmd.label} label={cmd.label}
Icon={cmd.Icon} Icon={cmd.Icon}
onClick={cmd.onCommandClick} onClick={cmd.onCommandClick}
shortcuts={cmd.shortcuts || []} firstHotKey={cmd.firstHotKey}
secondHotKey={cmd.secondHotKey}
/> />
))} ))}
</CommandGroup> </CommandGroup>

View File

@ -12,7 +12,8 @@ export type CommandMenuItemProps = {
key: string; key: string;
onClick?: () => void; onClick?: () => void;
Icon?: IconComponent; Icon?: IconComponent;
shortcuts?: Array<string>; firstHotKey?: string;
secondHotKey?: string;
}; };
export const CommandMenuItem = ({ export const CommandMenuItem = ({
@ -20,7 +21,8 @@ export const CommandMenuItem = ({
to, to,
onClick, onClick,
Icon, Icon,
shortcuts, firstHotKey,
secondHotKey,
}: CommandMenuItemProps) => { }: CommandMenuItemProps) => {
const navigate = useNavigate(); const navigate = useNavigate();
const { closeCommandMenu } = useCommandMenu(); const { closeCommandMenu } = useCommandMenu();
@ -46,7 +48,8 @@ export const CommandMenuItem = ({
<MenuItemCommand <MenuItemCommand
LeftIcon={Icon} LeftIcon={Icon}
text={label} text={label}
command={shortcuts ? shortcuts.join(' then ') : ''} firstHotKey={firstHotKey}
secondHotKey={secondHotKey}
onClick={onItemClick} onClick={onItemClick}
/> />
); );

View File

@ -13,35 +13,40 @@ export const commandMenuCommands: Command[] = [
to: '/people', to: '/people',
label: 'Go to People', label: 'Go to People',
type: CommandType.Navigate, type: CommandType.Navigate,
shortcuts: ['G', 'P'], firstHotKey: 'G',
secondHotKey: 'P',
Icon: IconUser, Icon: IconUser,
}, },
{ {
to: '/companies', to: '/companies',
label: 'Go to Companies', label: 'Go to Companies',
type: CommandType.Navigate, type: CommandType.Navigate,
shortcuts: ['G', 'C'], firstHotKey: 'G',
secondHotKey: 'C',
Icon: IconBuildingSkyscraper, Icon: IconBuildingSkyscraper,
}, },
{ {
to: '/opportunities', to: '/opportunities',
label: 'Go to Opportunities', label: 'Go to Opportunities',
type: CommandType.Navigate, type: CommandType.Navigate,
shortcuts: ['G', 'O'], firstHotKey: 'G',
secondHotKey: 'O',
Icon: IconTargetArrow, Icon: IconTargetArrow,
}, },
{ {
to: '/settings/profile', to: '/settings/profile',
label: 'Go to Settings', label: 'Go to Settings',
type: CommandType.Navigate, type: CommandType.Navigate,
shortcuts: ['G', 'S'], firstHotKey: 'G',
secondHotKey: 'S',
Icon: IconSettings, Icon: IconSettings,
}, },
{ {
to: '/tasks', to: '/tasks',
label: 'Go to Tasks', label: 'Go to Tasks',
type: CommandType.Navigate, type: CommandType.Navigate,
shortcuts: ['G', 'T'], firstHotKey: 'G',
secondHotKey: 'T',
Icon: IconCheckbox, Icon: IconCheckbox,
}, },
]; ];

View File

@ -10,6 +10,7 @@ export type Command = {
label: string; label: string;
type: CommandType.Navigate | CommandType.Create; type: CommandType.Navigate | CommandType.Create;
Icon?: IconComponent; Icon?: IconComponent;
shortcuts?: string[]; firstHotKey?: string;
secondHotKey?: string;
onCommandClick?: () => void; onCommandClick?: () => void;
}; };

View File

@ -9,6 +9,8 @@ import {
StyledMenuItemLeftContent, StyledMenuItemLeftContent,
} from '../internals/components/StyledMenuItemBase'; } from '../internals/components/StyledMenuItemBase';
import { MenuItemCommandHotKeys } from './MenuItemCommandHotKeys';
const StyledMenuItemLabelText = styled(StyledMenuItemLabel)` const StyledMenuItemLabelText = styled(StyledMenuItemLabel)`
color: ${({ theme }) => theme.font.color.primary}; color: ${({ theme }) => theme.font.color.primary};
`; `;
@ -25,14 +27,6 @@ const StyledBigIconContainer = styled.div`
padding: ${({ theme }) => theme.spacing(1)}; padding: ${({ theme }) => theme.spacing(1)};
`; `;
const StyledCommandText = styled.div`
color: ${({ theme }) => theme.font.color.light};
padding-left: ${({ theme }) => theme.spacing(2)};
padding-right: ${({ theme }) => theme.spacing(2)};
white-space: nowrap;
`;
const StyledMenuItemCommandContainer = styled(Command.Item)` const StyledMenuItemCommandContainer = styled(Command.Item)`
--horizontal-padding: ${({ theme }) => theme.spacing(1)}; --horizontal-padding: ${({ theme }) => theme.spacing(1)};
--vertical-padding: ${({ theme }) => theme.spacing(2)}; --vertical-padding: ${({ theme }) => theme.spacing(2)};
@ -58,17 +52,6 @@ const StyledMenuItemCommandContainer = styled(Command.Item)`
} }
&[data-selected='true'] { &[data-selected='true'] {
background: ${({ theme }) => theme.background.tertiary}; background: ${({ theme }) => theme.background.tertiary};
/* Could be nice to add a caret like this for better accessibility in the future
But it needs to be consistend with other picker dropdown (e.g. company)
&:after {
background: ${({ theme }) => theme.background.quaternary};
content: '';
height: 100%;
left: 0;
position: absolute;
width: 3px;
z-index: ${({ theme }) => theme.lastLayerZIndex};
} */
} }
&[data-disabled='true'] { &[data-disabled='true'] {
color: ${({ theme }) => theme.font.color.light}; color: ${({ theme }) => theme.font.color.light};
@ -83,7 +66,8 @@ const StyledMenuItemCommandContainer = styled(Command.Item)`
export type MenuItemCommandProps = { export type MenuItemCommandProps = {
LeftIcon?: IconComponent; LeftIcon?: IconComponent;
text: string; text: string;
command: string; firstHotKey?: string;
secondHotKey?: string;
className?: string; className?: string;
onClick?: () => void; onClick?: () => void;
}; };
@ -91,7 +75,8 @@ export type MenuItemCommandProps = {
export const MenuItemCommand = ({ export const MenuItemCommand = ({
LeftIcon, LeftIcon,
text, text,
command, firstHotKey,
secondHotKey,
className, className,
onClick, onClick,
}: MenuItemCommandProps) => { }: MenuItemCommandProps) => {
@ -109,7 +94,10 @@ export const MenuItemCommand = ({
{text} {text}
</StyledMenuItemLabelText> </StyledMenuItemLabelText>
</StyledMenuItemLeftContent> </StyledMenuItemLeftContent>
<StyledCommandText>{command}</StyledCommandText> <MenuItemCommandHotKeys
firstHotKey={firstHotKey}
secondHotKey={secondHotKey}
/>
</StyledMenuItemCommandContainer> </StyledMenuItemCommandContainer>
); );
}; };

View File

@ -0,0 +1,62 @@
import styled from '@emotion/styled';
const StyledCommandTextContainer = styled.div`
align-items: center;
display: flex;
flex-direction: row;
gap: ${({ theme }) => theme.spacing(1)};
justify-content: center;
`;
const StyledCommandText = styled.div`
color: ${({ theme }) => theme.font.color.light};
padding-bottom: ${({ theme }) => theme.spacing(1)};
padding-left: ${({ theme }) => theme.spacing(2)};
padding-right: ${({ theme }) => theme.spacing(2)};
padding-top: ${({ theme }) => theme.spacing(1)};
white-space: nowrap;
`;
const StyledCommandKey = styled.div`
align-items: center;
background-color: ${({ theme }) => theme.background.secondary};
border: 1px solid ${({ theme }) => theme.border.color.strong};
border-radius: ${({ theme }) => theme.border.radius.sm};
box-shadow: ${({ theme }) => theme.boxShadow.underline};
display: flex;
flex-direction: column;
height: ${({ theme }) => theme.spacing(5)};
height: 18px;
justify-content: center;
text-align: center;
width: ${({ theme }) => theme.spacing(4)};
`;
export type MenuItemCommandHotKeysProps = {
firstHotKey?: string;
joinLabel?: string;
secondHotKey?: string;
};
export const MenuItemCommandHotKeys = ({
firstHotKey,
secondHotKey,
joinLabel = 'then',
}: MenuItemCommandHotKeysProps) => {
return (
<StyledCommandText>
{firstHotKey && (
<StyledCommandTextContainer>
<StyledCommandKey>{firstHotKey}</StyledCommandKey>
{secondHotKey && (
<>
{joinLabel}
<StyledCommandKey>{secondHotKey}</StyledCommandKey>
</>
)}
</StyledCommandTextContainer>
)}
</StyledCommandText>
);
};

View File

@ -20,14 +20,16 @@ type Story = StoryObj<typeof MenuItemCommand>;
export const Default: Story = { export const Default: Story = {
args: { args: {
text: 'First option', text: 'First option',
command: '⌘ 1', firstHotKey: '⌘',
secondHotKey: '1',
}, },
render: (props) => ( render: (props) => (
<Command> <Command>
<MenuItemCommand <MenuItemCommand
LeftIcon={props.LeftIcon} LeftIcon={props.LeftIcon}
text={props.text} text={props.text}
command={props.text} firstHotKey={props.firstHotKey}
secondHotKey={props.secondHotKey}
className={props.className} className={props.className}
onClick={props.onClick} onClick={props.onClick}
></MenuItemCommand> ></MenuItemCommand>
@ -37,12 +39,16 @@ export const Default: Story = {
}; };
export const Catalog: CatalogStory<Story, typeof MenuItemCommand> = { export const Catalog: CatalogStory<Story, typeof MenuItemCommand> = {
args: { LeftIcon: IconBell, text: 'Menu item', command: '⌘1' }, args: {
text: 'Menu item',
firstHotKey: '⌘',
secondHotKey: '1',
},
argTypes: { argTypes: {
className: { control: false }, className: { control: false },
}, },
parameters: { parameters: {
pseudo: { hover: ['.hover'], active: ['.pressed'], focus: ['.focus'] }, pseudo: { hover: ['.hover'] },
catalog: { catalog: {
dimensions: [ dimensions: [
{ {
@ -54,13 +60,6 @@ export const Catalog: CatalogStory<Story, typeof MenuItemCommand> = {
labels: (withIcon: boolean) => labels: (withIcon: boolean) =>
withIcon ? 'With left icon' : 'Without left icon', withIcon ? 'With left icon' : 'Without left icon',
}, },
{
name: 'selected',
values: [true, false],
props: () => ({}),
labels: (selected: boolean) =>
selected ? 'Selected' : 'Not selected',
},
{ {
name: 'states', name: 'states',
values: ['default', 'hover'], values: ['default', 'hover'],
@ -88,7 +87,8 @@ export const Catalog: CatalogStory<Story, typeof MenuItemCommand> = {
<MenuItemCommand <MenuItemCommand
LeftIcon={props.LeftIcon} LeftIcon={props.LeftIcon}
text={props.text} text={props.text}
command={props.text} firstHotKey={props.firstHotKey}
secondHotKey={props.secondHotKey}
className={props.className} className={props.className}
onClick={props.onClick} onClick={props.onClick}
></MenuItemCommand> ></MenuItemCommand>

View File

@ -10,6 +10,7 @@ export const boxShadowLight = {
grayScale.gray100, grayScale.gray100,
0.12, 0.12,
)}, 0px 2px 4px 0px ${rgba(grayScale.gray100, 0.04)}`, )}, 0px 2px 4px 0px ${rgba(grayScale.gray100, 0.04)}`,
underline: `0px 1px 0px 0px ${rgba(grayScale.gray100, 0.32)}`,
}; };
export const boxShadowDark = { export const boxShadowDark = {
@ -22,4 +23,5 @@ export const boxShadowDark = {
grayScale.gray100, grayScale.gray100,
0.16, 0.16,
)}, 0px 2px 4px 0px ${rgba(grayScale.gray100, 0.08)}`, )}, 0px 2px 4px 0px ${rgba(grayScale.gray100, 0.08)}`,
underline: `0px 1px 0px 0px ${rgba(grayScale.gray100, 0.32)}`,
}; };