Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

New MenuItem components #1389

Merged
merged 6 commits into from
Sep 1, 2023
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Finished
  • Loading branch information
lucasbordeau committed Aug 30, 2023
commit 165058849889d7d68ab01bb13f93c063dad13df7
3 changes: 3 additions & 0 deletions front/src/modules/ui/icon/types/IconComponent.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import { ComponentType } from 'react';

export type IconComponent = ComponentType<{ size: number }>;
109 changes: 22 additions & 87 deletions front/src/modules/ui/menu-item/components/MenuItem.tsx
Original file line number Diff line number Diff line change
@@ -1,85 +1,26 @@
import { ComponentType } from 'react';
import { useTheme } from '@emotion/react';
import styled from '@emotion/styled';

import { Button } from '@/ui/button/components/Button';
import { IconButton } from '@/ui/button/components/IconButton';
import { IconButtonGroup } from '@/ui/button/components/IconButtonGroup';
import { hoverBackground } from '@/ui/theme/constants/effects';
import { IconComponent } from '@/ui/icon/types/IconComponent';

import { MenuItemLeftContent } from '../internals/components/MenuItemLeftContent';
import { StyledMenuItemBase } from '../internals/components/StyledMenuItemBase';
import { MenuItemAccent } from '../types/MenuItemAccent';

const StyledItem = styled.li<Pick<MenuItemProps, 'accent'>>`
--horizontal-padding: ${({ theme }) => theme.spacing(1)};
--vertical-padding: ${({ theme }) => theme.spacing(2)};

align-items: center;

border-radius: ${({ theme }) => theme.border.radius.sm};

cursor: pointer;
display: flex;
flex-direction: row;

font-size: ${({ theme }) => theme.font.size.sm};

height: calc(32px - 2 * var(--vertical-padding));

justify-content: space-between;

padding: var(--vertical-padding) var(--horizontal-padding);

${hoverBackground};

${({ theme, accent }) => accent === 'danger' && `color: ${theme.color.red};`}

position: relative;
user-select: none;

width: calc(100% - 2 * var(--horizontal-padding));
`;

const StyledMenuItemLabel = styled.div`
font-size: ${({ theme }) => theme.font.size.sm};
font-weight: ${({ theme }) => theme.font.weight.regular};
`;

const StyledNoIconFiller = styled.div`
width: ${({ theme }) => theme.spacing(1)};
`;

export const StyledMenuItemLeftContent = styled.div`
align-items: center;
color: ${({ theme }) => theme.font.color.secondary};
display: flex;

flex-direction: row;

gap: ${({ theme }) => theme.spacing(2)};
`;

export const StyledMenuItemIconContainer = styled.div`
align-items: center;

display: flex;
justify-content: center;
padding-left: ${({ theme }) => theme.spacing(0.5)};
`;

export const StyledMenuItemRightContent = styled.div`
align-items: center;
display: flex;
flex-direction: row;
`;
export type MenuItemIconButton = {
Icon: IconComponent;
onClick: () => void;
};

export type MenuItemProps = {
LeftIcon?: ComponentType<{ size: number }>;
LeftIcon?: IconComponent;
accent: MenuItemAccent;
text: string;
iconButtons?: {
icon: ComponentType<{ size: number }>;
onClick: () => void;
}[];
iconButtons?: MenuItemIconButton[];
className: string;
onClick?: () => void;
};

export function MenuItem({
Expand All @@ -88,6 +29,7 @@ export function MenuItem({
text,
iconButtons,
className,
onClick,
}: MenuItemProps) {
const theme = useTheme();

Expand All @@ -98,42 +40,35 @@ export function MenuItem({
Array.isArray(iconButtons) && iconButtons.length > 1;

return (
<StyledItem className={className} accent={accent}>
<StyledMenuItemLeftContent>
{LeftIcon ? (
<StyledMenuItemIconContainer>
<LeftIcon size={theme.icon.size.md} />
</StyledMenuItemIconContainer>
) : (
<StyledNoIconFiller />
)}
<StyledMenuItemLabel>{text}</StyledMenuItemLabel>
</StyledMenuItemLeftContent>
<StyledMenuItemBase onClick={onClick} className={className} accent={accent}>
<MenuItemLeftContent LeftIcon={LeftIcon} text={text} />
{showOneIconButton ? (
<>
{iconButtons?.map(({ icon: Icon, onClick }, index) => (
{iconButtons?.map(({ Icon, onClick }, index) => (
<IconButton
variant="tertiary"
size="small"
icon={<Icon size={theme.icon.size.sm} />}
key={index}
accent={accent}
onClick={onClick}
/>
))}
</>
) : showMultipleIconButtons ? (
<IconButtonGroup>
{iconButtons?.map(({ icon: Icon, onClick }, index) => (
<IconButton
size="small"
<IconButtonGroup size="small" variant="secondary">
{iconButtons?.map(({ Icon, onClick }, index) => (
<Button
icon={<Icon size={theme.icon.size.sm} />}
key={index}
onClick={onClick}
accent={accent}
/>
))}
</IconButtonGroup>
) : (
<></>
)}
</StyledItem>
</StyledMenuItemBase>
);
}
68 changes: 68 additions & 0 deletions front/src/modules/ui/menu-item/components/MenuItemCommand.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import { useTheme } from '@emotion/react';
import styled from '@emotion/styled';

import { IconComponent } from '@/ui/icon/types/IconComponent';

import {
StyledMenuItemBase,
StyledMenuItemLabel,
StyledMenuItemLeftContent,
} from '../internals/components/StyledMenuItemBase';

const StyledMenuItemLabelText = styled(StyledMenuItemLabel)`
color: ${({ theme }) => theme.font.color.primary};
`;

const StyledBigIconContainer = styled.div`
align-items: center;
background: ${({ theme }) => theme.background.transparent.light};
border-radius: ${({ theme }) => theme.border.radius.sm};

display: flex;

flex-direction: row;

padding: ${({ theme }) => theme.spacing(1)};
`;

const StyledCommandText = styled.div`
color: ${({ theme }) => theme.font.color.light};
`;

const StyledMenuItemCommandContainer = styled(StyledMenuItemBase)`
height: 24px;
`;

export type MenuItemProps = {
LeftIcon?: IconComponent;
text: string;
command: string;
className: string;
onClick?: () => void;
};

export function MenuItemCommand({
LeftIcon,
text,
command,
className,
onClick,
}: MenuItemProps) {
const theme = useTheme();

return (
<StyledMenuItemCommandContainer onClick={onClick} className={className}>
<StyledMenuItemLeftContent>
{LeftIcon && (
<StyledBigIconContainer>
<LeftIcon size={theme.icon.size.sm} />
</StyledBigIconContainer>
)}
<StyledMenuItemLabelText hasLeftIcon={!!LeftIcon}>
{text}
</StyledMenuItemLabelText>
</StyledMenuItemLeftContent>
<StyledCommandText>{command}</StyledCommandText>
</StyledMenuItemCommandContainer>
);
}
43 changes: 43 additions & 0 deletions front/src/modules/ui/menu-item/components/MenuItemMultiSelect.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import styled from '@emotion/styled';

import { IconComponent } from '@/ui/icon/types/IconComponent';
import { Checkbox } from '@/ui/input/checkbox/components/Checkbox';

import { MenuItemLeftContent } from '../internals/components/MenuItemLeftContent';
import { StyledMenuItemBase } from '../internals/components/StyledMenuItemBase';

const StyledLeftContentWithCheckboxContainer = styled.div`
align-items: center;
display: flex;
flex-direction: row;
gap: ${({ theme }) => theme.spacing(2)};
`;

type OwnProps = {
LeftIcon?: IconComponent;
selected: boolean;
text: string;
className: string;
onSelectChange?: (selected: boolean) => void;
};

export function MenuItemMultiSelect({
LeftIcon,
text,
selected,
className,
onSelectChange,
}: OwnProps) {
function handleOnClick() {
onSelectChange?.(!selected);
}

return (
<StyledMenuItemBase className={className} onClick={handleOnClick}>
<StyledLeftContentWithCheckboxContainer>
<Checkbox checked={selected} />
<MenuItemLeftContent LeftIcon={LeftIcon} text={text} />
</StyledLeftContentWithCheckboxContainer>
</StyledMenuItemBase>
);
}
30 changes: 30 additions & 0 deletions front/src/modules/ui/menu-item/components/MenuItemNavigate.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { useTheme } from '@emotion/react';
import { IconChevronRight } from '@tabler/icons-react';

import { IconComponent } from '@/ui/icon/types/IconComponent';

import { MenuItemLeftContent } from '../internals/components/MenuItemLeftContent';
import { StyledMenuItemBase } from '../internals/components/StyledMenuItemBase';

export type MenuItemProps = {
LeftIcon?: IconComponent;
text: string;
onClick?: () => void;
className: string;
};

export function MenuItemNavigate({
LeftIcon,
text,
className,
onClick,
}: MenuItemProps) {
const theme = useTheme();

return (
<StyledMenuItemBase onClick={onClick} className={className}>
<MenuItemLeftContent LeftIcon={LeftIcon} text={text} />
<IconChevronRight size={theme.icon.size.sm} />
</StyledMenuItemBase>
);
}
50 changes: 50 additions & 0 deletions front/src/modules/ui/menu-item/components/MenuItemSelect.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import { css, useTheme } from '@emotion/react';
import styled from '@emotion/styled';
import { IconCheck } from '@tabler/icons-react';

import { IconComponent } from '@/ui/icon/types/IconComponent';

import { MenuItemLeftContent } from '../internals/components/MenuItemLeftContent';
import { StyledMenuItemBase } from '../internals/components/StyledMenuItemBase';

const StyledMenuItemSelect = styled(StyledMenuItemBase)<{ selected: boolean }>`
${({ theme, selected }) => {
if (selected) {
return css`
background: ${theme.background.transparent.light};
&:hover {
background: ${theme.background.transparent.medium};
}
`;
}
}}
`;

type OwnProps = {
LeftIcon?: IconComponent;
selected: boolean;
text: string;
className: string;
onClick?: () => void;
};

export function MenuItemSelect({
LeftIcon,
text,
selected,
className,
onClick,
}: OwnProps) {
const theme = useTheme();

return (
<StyledMenuItemSelect
onClick={onClick}
className={className}
selected={selected}
>
<MenuItemLeftContent LeftIcon={LeftIcon} text={text} />
{selected && <IconCheck size={theme.icon.size.sm} />}
</StyledMenuItemSelect>
);
}
32 changes: 32 additions & 0 deletions front/src/modules/ui/menu-item/components/MenuItemToggle.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { IconComponent } from '@/ui/icon/types/IconComponent';
import { Toggle } from '@/ui/input/toggle/components/Toggle';

import { MenuItemLeftContent } from '../internals/components/MenuItemLeftContent';
import { StyledMenuItemBase } from '../internals/components/StyledMenuItemBase';

type OwnProps = {
LeftIcon?: IconComponent;
toggled: boolean;
text: string;
className: string;
onToggleChange?: (toggled: boolean) => void;
};

export function MenuItemMultiToggle({
LeftIcon,
text,
toggled,
className,
onToggleChange,
}: OwnProps) {
function handleOnClick() {
onToggleChange?.(!toggled);
}

return (
<StyledMenuItemBase className={className} onClick={handleOnClick}>
<MenuItemLeftContent LeftIcon={LeftIcon} text={text} />
<Toggle value={toggled} onChange={onToggleChange} />
</StyledMenuItemBase>
);
}
Loading