Update menu items across the app (#14724)
Closes https://github.com/twentyhq/core-team-issues/issues/1568 https://github.com/user-attachments/assets/a0fe6538-8405-4b52-a6ce-7afa846ee5f3
This commit is contained in:
@@ -5,7 +5,7 @@ import { isSelectedItemIdComponentFamilySelector } from '@/ui/layout/selectable-
|
||||
import { useRecoilComponentFamilyValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentFamilyValue';
|
||||
import { type ReactNode } from 'react';
|
||||
import { IconArrowUpRight, type IconComponent } from 'twenty-ui/display';
|
||||
import { MenuItemCommand } from 'twenty-ui/navigation';
|
||||
import { MenuItem } from 'twenty-ui/navigation';
|
||||
|
||||
export type CommandMenuItemProps = {
|
||||
label: string;
|
||||
@@ -40,10 +40,11 @@ export const CommandMenuItem = ({
|
||||
);
|
||||
|
||||
return (
|
||||
<MenuItemCommand
|
||||
<MenuItem
|
||||
withIconContainer={true}
|
||||
LeftIcon={Icon}
|
||||
text={label}
|
||||
description={description}
|
||||
contextualText={description}
|
||||
hotKeys={hotKeys}
|
||||
onClick={() =>
|
||||
onItemClick({
|
||||
@@ -51,7 +52,7 @@ export const CommandMenuItem = ({
|
||||
to,
|
||||
})
|
||||
}
|
||||
isSelected={isSelectedItemId}
|
||||
focused={isSelectedItemId}
|
||||
RightComponent={RightComponent}
|
||||
/>
|
||||
);
|
||||
|
||||
+3
-2
@@ -11,7 +11,7 @@ import {
|
||||
IconGauge,
|
||||
IconNumber,
|
||||
} from 'twenty-ui/display';
|
||||
import { MenuItemCommand } from 'twenty-ui/navigation';
|
||||
import { MenuItem } from 'twenty-ui/navigation';
|
||||
|
||||
const StyledContainer = styled.div`
|
||||
display: flex;
|
||||
@@ -74,7 +74,8 @@ export const CommandMenuPageLayoutGraphTypeSelect = () => {
|
||||
<StyledSectionTitle>Graph type</StyledSectionTitle>
|
||||
|
||||
{graphTypeOptions.map((option) => (
|
||||
<MenuItemCommand
|
||||
<MenuItem
|
||||
withIconContainer={true}
|
||||
key={option.type}
|
||||
LeftIcon={option.icon}
|
||||
text={option.title}
|
||||
|
||||
+6
-5
@@ -6,7 +6,7 @@ import { pageLayoutDraggedAreaComponentState } from '@/page-layout/states/pageLa
|
||||
import { useSetRecoilComponentState } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentState';
|
||||
import styled from '@emotion/styled';
|
||||
import { IconChartPie, IconFrame, IconList } from 'twenty-ui/display';
|
||||
import { MenuItemCommand } from 'twenty-ui/navigation';
|
||||
import { MenuItem } from 'twenty-ui/navigation';
|
||||
|
||||
const StyledContainer = styled.div`
|
||||
display: flex;
|
||||
@@ -86,8 +86,9 @@ export const CommandMenuPageLayoutWidgetTypeSelect = () => {
|
||||
<StyledContainer>
|
||||
<StyledSectionTitle>Widget type</StyledSectionTitle>
|
||||
{widgetTypeOptions.map((option) => {
|
||||
const MenuItem = (
|
||||
<MenuItemCommand
|
||||
const MenuItemComponent = (
|
||||
<MenuItem
|
||||
withIconContainer={true}
|
||||
key={option.type}
|
||||
LeftIcon={option.icon}
|
||||
text={option.title}
|
||||
@@ -97,10 +98,10 @@ export const CommandMenuPageLayoutWidgetTypeSelect = () => {
|
||||
|
||||
return option.disabled ? (
|
||||
<StyledDisabledMenuItem key={option.type}>
|
||||
{MenuItem}
|
||||
{MenuItemComponent}
|
||||
</StyledDisabledMenuItem>
|
||||
) : (
|
||||
MenuItem
|
||||
MenuItemComponent
|
||||
);
|
||||
})}
|
||||
</StyledContainer>
|
||||
|
||||
+3
-2
@@ -2,7 +2,7 @@ import { type WorkflowActionType } from '@/workflow/types/Workflow';
|
||||
import { getActionIconColorOrThrow } from '@/workflow/workflow-steps/workflow-actions/utils/getActionIconColorOrThrow';
|
||||
import { useTheme } from '@emotion/react';
|
||||
import { useIcons } from 'twenty-ui/display';
|
||||
import { MenuItemCommand } from 'twenty-ui/navigation';
|
||||
import { MenuItem } from 'twenty-ui/navigation';
|
||||
|
||||
type Action = { type: WorkflowActionType; label: string; icon: string };
|
||||
|
||||
@@ -22,7 +22,8 @@ export const WorkflowActionMenuItems = ({
|
||||
const Icon = getIcon(action.icon);
|
||||
|
||||
return (
|
||||
<MenuItemCommand
|
||||
<MenuItem
|
||||
withIconContainer={true}
|
||||
key={action.type}
|
||||
LeftIcon={() => (
|
||||
<Icon
|
||||
|
||||
+5
-3
@@ -15,7 +15,7 @@ import { getTriggerDefaultDefinition } from '@/workflow/workflow-trigger/utils/g
|
||||
import { useTheme } from '@emotion/react';
|
||||
import { TRIGGER_STEP_ID } from 'twenty-shared/workflow';
|
||||
import { useIcons } from 'twenty-ui/display';
|
||||
import { MenuItemCommand } from 'twenty-ui/navigation';
|
||||
import { MenuItem } from 'twenty-ui/navigation';
|
||||
|
||||
export const CommandMenuWorkflowSelectTriggerTypeContent = ({
|
||||
workflow,
|
||||
@@ -71,7 +71,8 @@ export const CommandMenuWorkflowSelectTriggerTypeContent = ({
|
||||
{DATABASE_TRIGGER_TYPES.map((action) => {
|
||||
const Icon = getIcon(action.icon);
|
||||
return (
|
||||
<MenuItemCommand
|
||||
<MenuItem
|
||||
withIconContainer={true}
|
||||
key={action.defaultLabel}
|
||||
LeftIcon={() => <Icon color={theme.color.blue} />}
|
||||
text={action.defaultLabel}
|
||||
@@ -86,7 +87,8 @@ export const CommandMenuWorkflowSelectTriggerTypeContent = ({
|
||||
{OTHER_TRIGGER_TYPES.map((action) => {
|
||||
const Icon = getIcon(action.icon);
|
||||
return (
|
||||
<MenuItemCommand
|
||||
<MenuItem
|
||||
withIconContainer={true}
|
||||
key={action.defaultLabel}
|
||||
LeftIcon={() => <Icon color={theme.color.purple} />}
|
||||
text={action.defaultLabel}
|
||||
|
||||
+5
@@ -137,6 +137,7 @@ export const ObjectOptionsDropdownCustomView = ({
|
||||
)}
|
||||
text={t`Layout`}
|
||||
contextualText={`${capitalize(customViewData?.type ?? '')}`}
|
||||
contextualTextPosition="right"
|
||||
hasSubMenu
|
||||
/>
|
||||
</SelectableListItem>
|
||||
@@ -160,6 +161,7 @@ export const ObjectOptionsDropdownCustomView = ({
|
||||
? t`Not available on Default View`
|
||||
: calendarFieldMetadata?.label
|
||||
}
|
||||
contextualTextPosition="right"
|
||||
hasSubMenu
|
||||
disabled={isDefaultView}
|
||||
/>
|
||||
@@ -181,6 +183,7 @@ export const ObjectOptionsDropdownCustomView = ({
|
||||
? t`Week`
|
||||
: t`Day`
|
||||
}
|
||||
contextualTextPosition="right"
|
||||
/>
|
||||
</SelectableListItem>
|
||||
</>
|
||||
@@ -195,6 +198,7 @@ export const ObjectOptionsDropdownCustomView = ({
|
||||
LeftIcon={IconListDetails}
|
||||
text={t`Fields`}
|
||||
contextualText={`${visibleBoardFields.length} shown`}
|
||||
contextualTextPosition="right"
|
||||
hasSubMenu
|
||||
/>
|
||||
</SelectableListItem>
|
||||
@@ -222,6 +226,7 @@ export const ObjectOptionsDropdownCustomView = ({
|
||||
? t`Not available on Default View`
|
||||
: recordGroupFieldMetadata?.label
|
||||
}
|
||||
contextualTextPosition="right"
|
||||
hasSubMenu
|
||||
disabled={isDefaultView}
|
||||
/>
|
||||
|
||||
+2
@@ -87,6 +87,7 @@ export const ObjectOptionsDropdownDefaultView = () => {
|
||||
LeftIcon={IconListDetails}
|
||||
text={t`Fields`}
|
||||
contextualText={`${visibleBoardFields.length} shown`}
|
||||
contextualTextPosition="right"
|
||||
hasSubMenu
|
||||
/>
|
||||
</SelectableListItem>
|
||||
@@ -119,6 +120,7 @@ export const ObjectOptionsDropdownDefaultView = () => {
|
||||
onClick={handleCreateCustomView}
|
||||
LeftIcon={IconLayout}
|
||||
text={t`Create custom view`}
|
||||
contextualTextPosition="right"
|
||||
/>
|
||||
</SelectableListItem>
|
||||
</DropdownMenuItemsContainer>
|
||||
|
||||
+5
@@ -209,6 +209,7 @@ export const ObjectOptionsDropdownLayoutContent = () => {
|
||||
t`Create Select...`
|
||||
) : undefined
|
||||
}
|
||||
contextualTextPosition="right"
|
||||
selected={currentView?.type === ViewType.Kanban}
|
||||
onClick={handleSelectKanbanViewType}
|
||||
/>
|
||||
@@ -228,6 +229,7 @@ export const ObjectOptionsDropdownLayoutContent = () => {
|
||||
LeftIcon={IconCalendar}
|
||||
text={t`Date field`}
|
||||
contextualText={calendarFieldMetadata?.label}
|
||||
contextualTextPosition="right"
|
||||
hasSubMenu
|
||||
/>
|
||||
</SelectableListItem>
|
||||
@@ -247,6 +249,7 @@ export const ObjectOptionsDropdownLayoutContent = () => {
|
||||
? t`Week`
|
||||
: t`Day`
|
||||
}
|
||||
contextualTextPosition="right"
|
||||
hasSubMenu
|
||||
/>
|
||||
</SelectableListItem>
|
||||
@@ -274,6 +277,7 @@ export const ObjectOptionsDropdownLayoutContent = () => {
|
||||
? t`Side Panel`
|
||||
: t`Record Page`
|
||||
}
|
||||
contextualTextPosition="right"
|
||||
hasSubMenu
|
||||
/>
|
||||
</SelectableListItem>
|
||||
@@ -296,6 +300,7 @@ export const ObjectOptionsDropdownLayoutContent = () => {
|
||||
LeftIcon={IconLayoutList}
|
||||
text={t`Group`}
|
||||
contextualText={recordGroupFieldMetadata?.label}
|
||||
contextualTextPosition="right"
|
||||
hasSubMenu
|
||||
/>
|
||||
</SelectableListItem>
|
||||
|
||||
+2
@@ -125,6 +125,7 @@ export const ObjectOptionsDropdownRecordGroupsContent = () => {
|
||||
LeftIcon={IconLayoutList}
|
||||
text={t`Group by`}
|
||||
contextualText={recordGroupFieldMetadata?.label}
|
||||
contextualTextPosition="right"
|
||||
hasSubMenu
|
||||
/>
|
||||
</SelectableListItem>
|
||||
@@ -138,6 +139,7 @@ export const ObjectOptionsDropdownRecordGroupsContent = () => {
|
||||
LeftIcon={IconSortDescending}
|
||||
text={t`Sort`}
|
||||
contextualText={recordGroupSort}
|
||||
contextualTextPosition="right"
|
||||
hasSubMenu
|
||||
/>
|
||||
</SelectableListItem>
|
||||
|
||||
@@ -27,12 +27,10 @@ export type {
|
||||
export { MenuItem } from './menu/menu-item/components/MenuItem';
|
||||
export type { MenuItemAvatarProps } from './menu/menu-item/components/MenuItemAvatar';
|
||||
export { MenuItemAvatar } from './menu/menu-item/components/MenuItemAvatar';
|
||||
export type { MenuItemCommandProps } from './menu/menu-item/components/MenuItemCommand';
|
||||
export { MenuItemCommand } from './menu/menu-item/components/MenuItemCommand';
|
||||
export type { MenuItemCommandHotKeysProps } from './menu/menu-item/components/MenuItemCommandHotKeys';
|
||||
export { MenuItemCommandHotKeys } from './menu/menu-item/components/MenuItemCommandHotKeys';
|
||||
export type { MenuItemDraggableProps } from './menu/menu-item/components/MenuItemDraggable';
|
||||
export { MenuItemDraggable } from './menu/menu-item/components/MenuItemDraggable';
|
||||
export type { MenuItemHotKeysProps } from './menu/menu-item/components/MenuItemHotKeys';
|
||||
export { MenuItemHotKeys } from './menu/menu-item/components/MenuItemHotKeys';
|
||||
export { MenuItemMultiSelect } from './menu/menu-item/components/MenuItemMultiSelect';
|
||||
export { MenuItemMultiSelectAvatar } from './menu/menu-item/components/MenuItemMultiSelectAvatar';
|
||||
export { MenuItemMultiSelectTag } from './menu/menu-item/components/MenuItemMultiSelectTag';
|
||||
@@ -62,6 +60,8 @@ export {
|
||||
StyledDraggableItem,
|
||||
StyledHoverableMenuItemBase,
|
||||
StyledMenuItemIconCheck,
|
||||
StyledMenuItemContextualText,
|
||||
StyledRightMenuItemContextualText,
|
||||
} from './menu/menu-item/internals/components/StyledMenuItemBase';
|
||||
export type { MenuItemAccent } from './menu/menu-item/types/MenuItemAccent';
|
||||
export { NavigationBar } from './navigation-bar/components/NavigationBar';
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
import { useTheme } from '@emotion/react';
|
||||
import { IconChevronRight, type IconComponent } from '@ui/display';
|
||||
import {
|
||||
IconChevronRight,
|
||||
OverflowingTextWithTooltip,
|
||||
type IconComponent,
|
||||
} from '@ui/display';
|
||||
import { type LightIconButtonProps } from '@ui/input/button/components/LightIconButton';
|
||||
import { LightIconButtonGroup } from '@ui/input/button/components/LightIconButtonGroup';
|
||||
import {
|
||||
@@ -9,10 +13,17 @@ import {
|
||||
type ReactNode,
|
||||
} from 'react';
|
||||
|
||||
import styled from '@emotion/styled';
|
||||
import { isString } from '@sniptt/guards';
|
||||
import { MenuItemHotKeys } from '@ui/navigation/menu/menu-item/components/MenuItemHotKeys';
|
||||
import { motion } from 'framer-motion';
|
||||
import { MenuItemLeftContent } from '../internals/components/MenuItemLeftContent';
|
||||
import {
|
||||
StyledHoverableMenuItemBase,
|
||||
StyledMenuItemLabel,
|
||||
StyledMenuItemLeftContent,
|
||||
StyledMenuItemRightContent,
|
||||
StyledRightMenuItemContextualText,
|
||||
} from '../internals/components/StyledMenuItemBase';
|
||||
import { type MenuItemAccent } from '../types/MenuItemAccent';
|
||||
|
||||
@@ -26,40 +37,56 @@ export type MenuItemIconButton = {
|
||||
export type MenuItemProps = {
|
||||
accent?: MenuItemAccent;
|
||||
className?: string;
|
||||
withIconContainer?: boolean;
|
||||
iconButtons?: MenuItemIconButton[];
|
||||
isIconDisplayedOnHoverOnly?: boolean;
|
||||
isTooltipOpen?: boolean;
|
||||
LeftIcon?: IconComponent | null;
|
||||
LeftComponent?: ReactNode;
|
||||
RightIcon?: IconComponent | null;
|
||||
RightComponent?: ReactNode;
|
||||
onClick?: (event: MouseEvent<HTMLDivElement>) => void;
|
||||
onMouseEnter?: (event: MouseEvent<HTMLDivElement>) => void;
|
||||
onMouseLeave?: (event: MouseEvent<HTMLDivElement>) => void;
|
||||
testId?: string;
|
||||
disabled?: boolean;
|
||||
text: ReactNode;
|
||||
contextualTextPosition?: 'left' | 'right';
|
||||
contextualText?: ReactNode;
|
||||
hasSubMenu?: boolean;
|
||||
focused?: boolean;
|
||||
hotKeys?: string[];
|
||||
isSubMenuOpened?: boolean;
|
||||
};
|
||||
|
||||
const StyledSubMenuIcon = styled(motion.div)`
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
`;
|
||||
|
||||
export const MenuItem = ({
|
||||
accent = 'default',
|
||||
className,
|
||||
withIconContainer = false,
|
||||
iconButtons,
|
||||
isIconDisplayedOnHoverOnly = true,
|
||||
LeftIcon,
|
||||
LeftComponent,
|
||||
RightIcon,
|
||||
RightComponent,
|
||||
onClick,
|
||||
onMouseEnter,
|
||||
onMouseLeave,
|
||||
testId,
|
||||
text,
|
||||
contextualTextPosition = 'left',
|
||||
contextualText,
|
||||
hasSubMenu = false,
|
||||
disabled = false,
|
||||
focused = false,
|
||||
hotKeys,
|
||||
isSubMenuOpened = false,
|
||||
}: MenuItemProps) => {
|
||||
const theme = useTheme();
|
||||
const showIconButtons = Array.isArray(iconButtons) && iconButtons.length > 0;
|
||||
@@ -88,25 +115,50 @@ export const MenuItem = ({
|
||||
<MenuItemLeftContent
|
||||
LeftIcon={LeftIcon ?? undefined}
|
||||
LeftComponent={LeftComponent}
|
||||
withIconContainer={withIconContainer}
|
||||
text={text}
|
||||
contextualText={contextualText}
|
||||
contextualText={
|
||||
contextualTextPosition === 'left' ? contextualText : null
|
||||
}
|
||||
disabled={disabled}
|
||||
/>
|
||||
</StyledMenuItemLeftContent>
|
||||
<div className="hoverable-buttons">
|
||||
{showIconButtons && (
|
||||
<LightIconButtonGroup iconButtons={iconButtons} size="small" />
|
||||
<StyledMenuItemRightContent>
|
||||
{contextualTextPosition === 'right' && (
|
||||
<StyledMenuItemLabel>
|
||||
{isString(contextualText) ? (
|
||||
<StyledRightMenuItemContextualText>
|
||||
<OverflowingTextWithTooltip text={contextualText} />
|
||||
</StyledRightMenuItemContextualText>
|
||||
) : (
|
||||
contextualText
|
||||
)}
|
||||
</StyledMenuItemLabel>
|
||||
)}
|
||||
</div>
|
||||
{RightIcon && (
|
||||
<RightIcon size={theme.icon.size.md} stroke={theme.icon.stroke.sm} />
|
||||
)}
|
||||
{hasSubMenu && !disabled && (
|
||||
<IconChevronRight
|
||||
size={theme.icon.size.sm}
|
||||
color={theme.font.color.tertiary}
|
||||
/>
|
||||
)}
|
||||
{iconButtons && (
|
||||
<div className="hoverable-buttons">
|
||||
{showIconButtons && (
|
||||
<LightIconButtonGroup iconButtons={iconButtons} size="small" />
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
{hotKeys && <MenuItemHotKeys hotKeys={hotKeys} />}
|
||||
{RightIcon && (
|
||||
<RightIcon size={theme.icon.size.md} stroke={theme.icon.stroke.sm} />
|
||||
)}
|
||||
{RightComponent}
|
||||
{hasSubMenu && !disabled && (
|
||||
<StyledSubMenuIcon
|
||||
animate={{ rotate: isSubMenuOpened ? 90 : 0 }}
|
||||
transition={{ duration: theme.animation.duration.normal }}
|
||||
>
|
||||
<IconChevronRight
|
||||
size={theme.icon.size.sm}
|
||||
color={theme.font.color.light}
|
||||
/>
|
||||
</StyledSubMenuIcon>
|
||||
)}
|
||||
</StyledMenuItemRightContent>
|
||||
</StyledHoverableMenuItemBase>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,131 +0,0 @@
|
||||
import { useTheme } from '@emotion/react';
|
||||
import styled from '@emotion/styled';
|
||||
|
||||
import {
|
||||
StyledMenuItemLabel,
|
||||
StyledMenuItemLeftContent,
|
||||
} from '../internals/components/StyledMenuItemBase';
|
||||
|
||||
import { type IconComponent, OverflowingTextWithTooltip } from '@ui/display';
|
||||
import { useIsMobile } from '@ui/utilities/responsive/hooks/useIsMobile';
|
||||
import { type ReactNode } from 'react';
|
||||
import { MenuItemCommandHotKeys } from './MenuItemCommandHotKeys';
|
||||
|
||||
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 StyledMenuItemCommandContainer = styled.div<{ isSelected?: boolean }>`
|
||||
--horizontal-padding: ${({ theme }) => theme.spacing(1)};
|
||||
--vertical-padding: ${({ theme }) => theme.spacing(2)};
|
||||
align-items: center;
|
||||
background: ${({ isSelected, theme }) =>
|
||||
isSelected ? theme.background.transparent.light : 'transparent'};
|
||||
border-radius: ${({ theme }) => theme.border.radius.sm};
|
||||
color: ${({ theme }) => theme.font.color.secondary};
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
font-size: ${({ theme }) => theme.font.size.sm};
|
||||
gap: ${({ theme }) => theme.spacing(2)};
|
||||
justify-content: space-between;
|
||||
padding: var(--vertical-padding) var(--horizontal-padding);
|
||||
position: relative;
|
||||
transition: all 150ms ease;
|
||||
transition-property: none;
|
||||
user-select: none;
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
height: 40px;
|
||||
&:hover {
|
||||
background: ${({ theme }) => theme.background.transparent.lighter};
|
||||
}
|
||||
&[data-selected='true'] {
|
||||
background: ${({ theme }) => theme.background.transparent.light};
|
||||
}
|
||||
&[data-disabled='true'] {
|
||||
color: ${({ theme }) => theme.font.color.light};
|
||||
cursor: not-allowed;
|
||||
}
|
||||
svg {
|
||||
height: 16px;
|
||||
width: 16px;
|
||||
}
|
||||
`;
|
||||
|
||||
const StyledDescription = styled.span`
|
||||
color: ${({ theme }) => theme.font.color.light};
|
||||
|
||||
&::before {
|
||||
content: '·';
|
||||
margin: ${({ theme }) => theme.spacing(0, 1)};
|
||||
}
|
||||
`;
|
||||
|
||||
const StyledTextContainer = styled.div`
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
max-width: calc(100% - 2 * var(--horizontal-padding));
|
||||
overflow: hidden;
|
||||
`;
|
||||
|
||||
export type MenuItemCommandProps = {
|
||||
LeftIcon?: IconComponent;
|
||||
text: string;
|
||||
description?: string;
|
||||
hotKeys?: string[];
|
||||
className?: string;
|
||||
isSelected?: boolean;
|
||||
onClick?: () => void;
|
||||
RightComponent?: ReactNode;
|
||||
};
|
||||
|
||||
export const MenuItemCommand = ({
|
||||
LeftIcon,
|
||||
text,
|
||||
description,
|
||||
hotKeys,
|
||||
className,
|
||||
isSelected,
|
||||
onClick,
|
||||
RightComponent,
|
||||
}: MenuItemCommandProps) => {
|
||||
const theme = useTheme();
|
||||
const isMobile = useIsMobile();
|
||||
|
||||
return (
|
||||
<StyledMenuItemCommandContainer
|
||||
onClick={onClick}
|
||||
className={className}
|
||||
isSelected={isSelected}
|
||||
>
|
||||
<StyledMenuItemLeftContent>
|
||||
{LeftIcon && (
|
||||
<StyledBigIconContainer>
|
||||
<LeftIcon size={theme.icon.size.sm} />
|
||||
</StyledBigIconContainer>
|
||||
)}
|
||||
<StyledTextContainer>
|
||||
<StyledMenuItemLabelText>
|
||||
<OverflowingTextWithTooltip text={text} />
|
||||
</StyledMenuItemLabelText>
|
||||
{description && <StyledDescription>{description}</StyledDescription>}
|
||||
</StyledTextContainer>
|
||||
{RightComponent}
|
||||
</StyledMenuItemLeftContent>
|
||||
{!isMobile && <MenuItemCommandHotKeys hotKeys={hotKeys} />}
|
||||
</StyledMenuItemCommandContainer>
|
||||
);
|
||||
};
|
||||
@@ -9,6 +9,7 @@ import { type MenuItemIconButton } from './MenuItem';
|
||||
|
||||
export type MenuItemDraggableProps = {
|
||||
LeftIcon?: IconComponent | undefined;
|
||||
withIconContainer?: boolean;
|
||||
accent?: MenuItemAccent;
|
||||
iconButtons?: MenuItemIconButton[];
|
||||
isTooltipOpen?: boolean;
|
||||
@@ -23,6 +24,7 @@ export type MenuItemDraggableProps = {
|
||||
|
||||
export const MenuItemDraggable = ({
|
||||
LeftIcon,
|
||||
withIconContainer = false,
|
||||
accent = 'default',
|
||||
iconButtons,
|
||||
onClick,
|
||||
@@ -51,6 +53,7 @@ export const MenuItemDraggable = ({
|
||||
<MenuItemLeftContent
|
||||
LeftIcon={LeftIcon}
|
||||
text={text}
|
||||
withIconContainer={withIconContainer}
|
||||
disabled={isDragDisabled}
|
||||
showGrip={showGrip}
|
||||
/>
|
||||
|
||||
+3
-3
@@ -31,15 +31,15 @@ const StyledCommandKey = styled.div`
|
||||
width: ${({ theme }) => theme.spacing(4)};
|
||||
`;
|
||||
|
||||
export type MenuItemCommandHotKeysProps = {
|
||||
export type MenuItemHotKeysProps = {
|
||||
hotKeys?: string[];
|
||||
joinLabel?: string;
|
||||
};
|
||||
|
||||
export const MenuItemCommandHotKeys = ({
|
||||
export const MenuItemHotKeys = ({
|
||||
hotKeys,
|
||||
joinLabel = 'then',
|
||||
}: MenuItemCommandHotKeysProps) => {
|
||||
}: MenuItemHotKeysProps) => {
|
||||
return (
|
||||
<StyledCommandText>
|
||||
{hotKeys && (
|
||||
@@ -19,6 +19,7 @@ type MenuItemMultiSelectProps = {
|
||||
LeftIcon?: IconComponent;
|
||||
selected: boolean;
|
||||
isKeySelected?: boolean;
|
||||
withIconContainer?: boolean;
|
||||
text: string;
|
||||
className: string;
|
||||
onSelectChange?: (selected: boolean) => void;
|
||||
@@ -27,6 +28,7 @@ type MenuItemMultiSelectProps = {
|
||||
export const MenuItemMultiSelect = ({
|
||||
color,
|
||||
LeftIcon,
|
||||
withIconContainer = false,
|
||||
text,
|
||||
selected,
|
||||
isKeySelected,
|
||||
@@ -48,7 +50,11 @@ export const MenuItemMultiSelect = ({
|
||||
{color ? (
|
||||
<Tag color={color} text={text} Icon={LeftIcon} />
|
||||
) : (
|
||||
<MenuItemLeftContent LeftIcon={LeftIcon} text={text} />
|
||||
<MenuItemLeftContent
|
||||
LeftIcon={LeftIcon}
|
||||
text={text}
|
||||
withIconContainer={withIconContainer}
|
||||
/>
|
||||
)}
|
||||
</StyledLeftContentWithCheckboxContainer>
|
||||
</StyledMenuItemBase>
|
||||
|
||||
@@ -9,6 +9,7 @@ import {
|
||||
|
||||
export type MenuItemNavigateProps = {
|
||||
LeftIcon?: IconComponent;
|
||||
withIconContainer?: boolean;
|
||||
text: string;
|
||||
onClick?: () => void;
|
||||
className?: string;
|
||||
@@ -16,6 +17,7 @@ export type MenuItemNavigateProps = {
|
||||
|
||||
export const MenuItemNavigate = ({
|
||||
LeftIcon,
|
||||
withIconContainer = false,
|
||||
text,
|
||||
className,
|
||||
onClick,
|
||||
@@ -25,7 +27,11 @@ export const MenuItemNavigate = ({
|
||||
return (
|
||||
<StyledMenuItemBase onClick={onClick} className={className}>
|
||||
<StyledMenuItemLeftContent>
|
||||
<MenuItemLeftContent LeftIcon={LeftIcon} text={text} />
|
||||
<MenuItemLeftContent
|
||||
LeftIcon={LeftIcon}
|
||||
text={text}
|
||||
withIconContainer={withIconContainer}
|
||||
/>
|
||||
</StyledMenuItemLeftContent>
|
||||
<IconChevronRight
|
||||
size={theme.icon.size.sm}
|
||||
|
||||
@@ -1,10 +1,21 @@
|
||||
import { css, useTheme } from '@emotion/react';
|
||||
import styled from '@emotion/styled';
|
||||
|
||||
import { IconCheck, IconChevronRight, type IconComponent } from '@ui/display';
|
||||
import { isString } from '@sniptt/guards';
|
||||
import {
|
||||
IconCheck,
|
||||
IconChevronRight,
|
||||
OverflowingTextWithTooltip,
|
||||
type IconComponent,
|
||||
} from '@ui/display';
|
||||
import { type ReactNode } from 'react';
|
||||
import { MenuItemLeftContent } from '../internals/components/MenuItemLeftContent';
|
||||
import { StyledMenuItemBase } from '../internals/components/StyledMenuItemBase';
|
||||
import {
|
||||
StyledMenuItemBase,
|
||||
StyledMenuItemLabel,
|
||||
StyledMenuItemRightContent,
|
||||
StyledRightMenuItemContextualText,
|
||||
} from '../internals/components/StyledMenuItemBase';
|
||||
|
||||
export const StyledMenuItemSelect = styled(StyledMenuItemBase)<{
|
||||
disabled?: boolean;
|
||||
@@ -32,6 +43,7 @@ export const StyledMenuItemSelect = styled(StyledMenuItemBase)<{
|
||||
|
||||
type MenuItemSelectProps = {
|
||||
LeftIcon?: IconComponent | null | undefined;
|
||||
withIconContainer?: boolean;
|
||||
selected: boolean;
|
||||
needIconCheck?: boolean;
|
||||
text: string;
|
||||
@@ -41,10 +53,12 @@ type MenuItemSelectProps = {
|
||||
focused?: boolean;
|
||||
hasSubMenu?: boolean;
|
||||
contextualText?: ReactNode;
|
||||
contextualTextPosition?: 'left' | 'right';
|
||||
};
|
||||
|
||||
export const MenuItemSelect = ({
|
||||
LeftIcon,
|
||||
withIconContainer = false,
|
||||
text,
|
||||
selected,
|
||||
needIconCheck = true,
|
||||
@@ -54,6 +68,7 @@ export const MenuItemSelect = ({
|
||||
focused,
|
||||
hasSubMenu = false,
|
||||
contextualText,
|
||||
contextualTextPosition = 'left',
|
||||
}: MenuItemSelectProps) => {
|
||||
const theme = useTheme();
|
||||
|
||||
@@ -70,16 +85,33 @@ export const MenuItemSelect = ({
|
||||
<MenuItemLeftContent
|
||||
LeftIcon={LeftIcon}
|
||||
text={text}
|
||||
contextualText={contextualText}
|
||||
contextualText={
|
||||
contextualTextPosition === 'left' ? contextualText : null
|
||||
}
|
||||
withIconContainer={withIconContainer}
|
||||
/>
|
||||
{selected && needIconCheck && <IconCheck size={theme.icon.size.md} />}
|
||||
<StyledMenuItemRightContent>
|
||||
{contextualTextPosition === 'right' && (
|
||||
<StyledMenuItemLabel>
|
||||
{isString(contextualText) ? (
|
||||
<StyledRightMenuItemContextualText>
|
||||
<OverflowingTextWithTooltip text={contextualText} />
|
||||
</StyledRightMenuItemContextualText>
|
||||
) : (
|
||||
contextualText
|
||||
)}
|
||||
</StyledMenuItemLabel>
|
||||
)}
|
||||
|
||||
{hasSubMenu && (
|
||||
<IconChevronRight
|
||||
size={theme.icon.size.sm}
|
||||
color={theme.font.color.tertiary}
|
||||
/>
|
||||
)}
|
||||
{selected && needIconCheck && <IconCheck size={theme.icon.size.md} />}
|
||||
|
||||
{hasSubMenu && (
|
||||
<IconChevronRight
|
||||
size={theme.icon.size.sm}
|
||||
color={theme.font.color.tertiary}
|
||||
/>
|
||||
)}
|
||||
</StyledMenuItemRightContent>
|
||||
</StyledMenuItemSelect>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -8,6 +8,7 @@ import { StyledMenuItemLeftContent } from '../internals/components/StyledMenuIte
|
||||
|
||||
export type MenuItemSuggestionProps = {
|
||||
LeftIcon?: IconComponent | null;
|
||||
withIconContainer?: boolean;
|
||||
text: string;
|
||||
selected?: boolean;
|
||||
className?: string;
|
||||
@@ -52,6 +53,7 @@ const StyledSuggestionMenuItem = styled.li<{
|
||||
|
||||
export const MenuItemSuggestion = ({
|
||||
LeftIcon,
|
||||
withIconContainer = false,
|
||||
text,
|
||||
className,
|
||||
selected,
|
||||
@@ -72,7 +74,11 @@ export const MenuItemSuggestion = ({
|
||||
selected={selected}
|
||||
>
|
||||
<StyledMenuItemLeftContent>
|
||||
<MenuItemLeftContent LeftIcon={LeftIcon ?? undefined} text={text} />
|
||||
<MenuItemLeftContent
|
||||
LeftIcon={LeftIcon ?? undefined}
|
||||
text={text}
|
||||
withIconContainer={withIconContainer}
|
||||
/>
|
||||
</StyledMenuItemLeftContent>
|
||||
</StyledSuggestionMenuItem>
|
||||
);
|
||||
|
||||
@@ -19,6 +19,7 @@ const StyledToggleContainer = styled.label`
|
||||
type MenuItemToggleProps = {
|
||||
focused?: boolean;
|
||||
LeftIcon?: IconComponent;
|
||||
withIconContainer?: boolean;
|
||||
toggled: boolean;
|
||||
text: string;
|
||||
className?: string;
|
||||
@@ -29,6 +30,7 @@ type MenuItemToggleProps = {
|
||||
export const MenuItemToggle = ({
|
||||
focused,
|
||||
LeftIcon,
|
||||
withIconContainer = false,
|
||||
text,
|
||||
toggled,
|
||||
className,
|
||||
@@ -39,7 +41,11 @@ export const MenuItemToggle = ({
|
||||
return (
|
||||
<StyledMenuItemBase className={className} focused={focused}>
|
||||
<StyledToggleContainer htmlFor={instanceId}>
|
||||
<MenuItemLeftContent LeftIcon={LeftIcon} text={text} />
|
||||
<MenuItemLeftContent
|
||||
LeftIcon={LeftIcon}
|
||||
text={text}
|
||||
withIconContainer={withIconContainer}
|
||||
/>
|
||||
<StyledMenuItemRightContent>
|
||||
<Toggle
|
||||
id={instanceId}
|
||||
|
||||
+148
@@ -52,6 +52,15 @@ export const Catalog: CatalogStory<Story, typeof MenuItem> = {
|
||||
labels: (withIcon: boolean) =>
|
||||
withIcon ? 'With left icon' : 'Without left icon',
|
||||
},
|
||||
{
|
||||
name: 'withIconContainer',
|
||||
values: [true, false],
|
||||
props: (withIconContainer: boolean) => ({ withIconContainer }),
|
||||
labels: (withIconContainer: boolean) =>
|
||||
withIconContainer
|
||||
? 'With icon container'
|
||||
: 'Without icon container',
|
||||
},
|
||||
{
|
||||
name: 'accents',
|
||||
values: ['default', 'danger'] satisfies MenuItemAccent[],
|
||||
@@ -115,3 +124,142 @@ export const Catalog: CatalogStory<Story, typeof MenuItem> = {
|
||||
},
|
||||
decorators: [CatalogDecorator],
|
||||
};
|
||||
|
||||
export const HotKeysCatalog: CatalogStory<Story, typeof MenuItem> = {
|
||||
args: {
|
||||
text: 'Menu item with hotkeys',
|
||||
LeftIcon: IconBell,
|
||||
hotKeys: ['⌘', 'K'],
|
||||
},
|
||||
argTypes: {
|
||||
className: { control: false },
|
||||
iconButtons: { control: false },
|
||||
},
|
||||
parameters: {
|
||||
pseudo: { hover: ['.hover'], active: ['.pressed'], focus: ['.focus'] },
|
||||
catalog: {
|
||||
dimensions: [
|
||||
{
|
||||
name: 'hotKeyTypes',
|
||||
values: ['no hotkeys', 'single key', 'modifier + key'],
|
||||
props: (choice: string) => {
|
||||
switch (choice) {
|
||||
case 'no hotkeys':
|
||||
return { hotKeys: undefined };
|
||||
case 'single key':
|
||||
return { hotKeys: ['K'] };
|
||||
case 'modifier + key':
|
||||
return { hotKeys: ['⌘', 'K'] };
|
||||
default:
|
||||
return {};
|
||||
}
|
||||
},
|
||||
labels: (choice: string) => {
|
||||
switch (choice) {
|
||||
case 'no hotkeys':
|
||||
return 'No hotkeys';
|
||||
case 'single key':
|
||||
return 'Single key (K)';
|
||||
case 'modifier + key':
|
||||
return 'Modifier + key (⌘K)';
|
||||
default:
|
||||
return choice;
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'states',
|
||||
values: ['default', 'hover'],
|
||||
props: (state: string) => {
|
||||
switch (state) {
|
||||
case 'default':
|
||||
return {};
|
||||
case 'hover':
|
||||
return { className: 'hover' };
|
||||
default:
|
||||
return {};
|
||||
}
|
||||
},
|
||||
},
|
||||
],
|
||||
options: {
|
||||
elementContainer: {
|
||||
width: 300,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
decorators: [CatalogDecorator],
|
||||
};
|
||||
|
||||
export const ContextualTextCatalog: CatalogStory<Story, typeof MenuItem> = {
|
||||
args: {
|
||||
text: 'Menu item with contextual text',
|
||||
},
|
||||
decorators: [CatalogDecorator],
|
||||
parameters: {
|
||||
catalog: {
|
||||
dimensions: [
|
||||
{
|
||||
name: 'contextualTextPosition',
|
||||
values: ['left', 'right'],
|
||||
props: (contextualTextPosition: 'left' | 'right') => ({
|
||||
contextualTextPosition,
|
||||
}),
|
||||
labels: (contextualTextPosition: 'left' | 'right') =>
|
||||
contextualTextPosition === 'left' ? 'Left' : 'Right',
|
||||
},
|
||||
{
|
||||
name: 'contextualText',
|
||||
values: [
|
||||
'Contextual text',
|
||||
'Lorem ipsum dolor sit amet consectetur adipisicing elit. Eveniet, corporis.',
|
||||
],
|
||||
props: (contextualText: string) => ({ contextualText }),
|
||||
labels: (contextualText: string) => {
|
||||
switch (contextualText) {
|
||||
case 'Contextual text':
|
||||
return 'Contextual text';
|
||||
case 'Lorem ipsum dolor sit amet consectetur adipisicing elit. Eveniet, corporis.':
|
||||
return 'Long contextual text';
|
||||
default:
|
||||
return contextualText;
|
||||
}
|
||||
},
|
||||
},
|
||||
],
|
||||
options: {
|
||||
elementContainer: {
|
||||
style: { width: '400px', overflow: 'hidden' },
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export const SubMenuCatalog: CatalogStory<Story, typeof MenuItem> = {
|
||||
args: {
|
||||
text: 'Menu item with sub menu',
|
||||
LeftIcon: IconBell,
|
||||
hasSubMenu: true,
|
||||
},
|
||||
parameters: {
|
||||
catalog: {
|
||||
dimensions: [
|
||||
{
|
||||
name: 'isSubMenuOpened',
|
||||
values: [true, false],
|
||||
props: (isSubMenuOpened: boolean) => ({ isSubMenuOpened }),
|
||||
labels: (isSubMenuOpened: boolean) =>
|
||||
isSubMenuOpened ? 'Opened' : 'Closed',
|
||||
},
|
||||
],
|
||||
options: {
|
||||
elementContainer: {
|
||||
width: 200,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
decorators: [CatalogDecorator],
|
||||
};
|
||||
|
||||
-101
@@ -1,101 +0,0 @@
|
||||
import { type Meta, type StoryObj } from '@storybook/react';
|
||||
|
||||
import { IconBell } from '@ui/display';
|
||||
import {
|
||||
CatalogDecorator,
|
||||
type CatalogStory,
|
||||
ComponentDecorator,
|
||||
} from '@ui/testing';
|
||||
import { MenuItemCommand } from '../MenuItemCommand';
|
||||
|
||||
const meta: Meta<typeof MenuItemCommand> = {
|
||||
title: 'UI/Navigation/Menu/MenuItem/MenuItemCommand',
|
||||
component: MenuItemCommand,
|
||||
};
|
||||
|
||||
export default meta;
|
||||
|
||||
type Story = StoryObj<typeof MenuItemCommand>;
|
||||
|
||||
export const Default: Story = {
|
||||
args: {
|
||||
text: 'First option',
|
||||
hotKeys: ['⌘', '1'],
|
||||
},
|
||||
render: (props) => (
|
||||
<MenuItemCommand
|
||||
LeftIcon={props.LeftIcon}
|
||||
text={props.text}
|
||||
hotKeys={props.hotKeys}
|
||||
className={props.className}
|
||||
onClick={props.onClick}
|
||||
isSelected={false}
|
||||
></MenuItemCommand>
|
||||
),
|
||||
decorators: [ComponentDecorator],
|
||||
};
|
||||
|
||||
export const WithDescription: Story = {
|
||||
args: {
|
||||
text: 'Menu item',
|
||||
hotKeys: ['⌘', '1'],
|
||||
description: 'Description',
|
||||
},
|
||||
decorators: [ComponentDecorator],
|
||||
};
|
||||
|
||||
export const Catalog: CatalogStory<Story, typeof MenuItemCommand> = {
|
||||
args: {
|
||||
text: 'Menu item',
|
||||
hotKeys: ['⌘', '1'],
|
||||
},
|
||||
argTypes: {
|
||||
className: { control: false },
|
||||
},
|
||||
parameters: {
|
||||
pseudo: { hover: ['.hover'] },
|
||||
catalog: {
|
||||
dimensions: [
|
||||
{
|
||||
name: 'withIcon',
|
||||
values: [true, false],
|
||||
props: (withIcon: boolean) => ({
|
||||
LeftIcon: withIcon ? IconBell : undefined,
|
||||
}),
|
||||
labels: (withIcon: boolean) =>
|
||||
withIcon ? 'With left icon' : 'Without left icon',
|
||||
},
|
||||
{
|
||||
name: 'states',
|
||||
values: ['default', 'hover'],
|
||||
props: (state: string) => {
|
||||
switch (state) {
|
||||
case 'default':
|
||||
return {};
|
||||
case 'hover':
|
||||
return { className: state };
|
||||
default:
|
||||
return {};
|
||||
}
|
||||
},
|
||||
},
|
||||
],
|
||||
options: {
|
||||
elementContainer: {
|
||||
width: 200,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
render: (props) => (
|
||||
<MenuItemCommand
|
||||
LeftIcon={props.LeftIcon}
|
||||
text={props.text}
|
||||
hotKeys={props.hotKeys}
|
||||
className={props.className}
|
||||
onClick={props.onClick}
|
||||
isSelected={false}
|
||||
></MenuItemCommand>
|
||||
),
|
||||
decorators: [CatalogDecorator],
|
||||
};
|
||||
+9
@@ -42,6 +42,15 @@ export const Catalog: Story = {
|
||||
pseudo: { hover: ['.hover'] },
|
||||
catalog: {
|
||||
dimensions: [
|
||||
{
|
||||
name: 'withIconContainer',
|
||||
values: [true, false],
|
||||
props: (withIconContainer: boolean) => ({ withIconContainer }),
|
||||
labels: (withIconContainer: boolean) =>
|
||||
withIconContainer
|
||||
? 'With icon container'
|
||||
: 'Without icon container',
|
||||
},
|
||||
{
|
||||
name: 'isDragDisabled',
|
||||
values: [true, false],
|
||||
|
||||
+9
@@ -44,6 +44,15 @@ export const Catalog: CatalogStory<Story, typeof MenuItemMultiSelect> = {
|
||||
labels: (withIcon: boolean) =>
|
||||
withIcon ? 'With left icon' : 'Without left icon',
|
||||
},
|
||||
{
|
||||
name: 'withIconContainer',
|
||||
values: [true, false],
|
||||
props: (withIconContainer: boolean) => ({ withIconContainer }),
|
||||
labels: (withIconContainer: boolean) =>
|
||||
withIconContainer
|
||||
? 'With icon container'
|
||||
: 'Without icon container',
|
||||
},
|
||||
{
|
||||
name: 'selected',
|
||||
values: [true, false],
|
||||
|
||||
+9
@@ -45,6 +45,15 @@ export const Catalog: CatalogStory<Story, typeof MenuItemNavigate> = {
|
||||
labels: (withIcon: boolean) =>
|
||||
withIcon ? 'With left icon' : 'Without left icon',
|
||||
},
|
||||
{
|
||||
name: 'withIconContainer',
|
||||
values: [true, false],
|
||||
props: (withIconContainer: boolean) => ({ withIconContainer }),
|
||||
labels: (withIconContainer: boolean) =>
|
||||
withIconContainer
|
||||
? 'With icon container'
|
||||
: 'Without icon container',
|
||||
},
|
||||
{
|
||||
name: 'states',
|
||||
values: ['default', 'hover'],
|
||||
|
||||
+9
@@ -48,6 +48,15 @@ export const Catalog: CatalogStory<Story, typeof MenuItemSelect> = {
|
||||
labels: (withIcon: boolean) =>
|
||||
withIcon ? 'With left icon' : 'Without left icon',
|
||||
},
|
||||
{
|
||||
name: 'withIconContainer',
|
||||
values: [true, false],
|
||||
props: (withIconContainer: boolean) => ({ withIconContainer }),
|
||||
labels: (withIconContainer: boolean) =>
|
||||
withIconContainer
|
||||
? 'With icon container'
|
||||
: 'Without icon container',
|
||||
},
|
||||
{
|
||||
name: 'states',
|
||||
values: ['default', 'hover', 'focused', 'hover+focused'],
|
||||
|
||||
+9
@@ -44,6 +44,15 @@ export const Catalog: CatalogStory<Story, typeof MenuItemToggle> = {
|
||||
labels: (withIcon: boolean) =>
|
||||
withIcon ? 'With left icon' : 'Without left icon',
|
||||
},
|
||||
{
|
||||
name: 'withIconContainer',
|
||||
values: [true, false],
|
||||
props: (withIconContainer: boolean) => ({ withIconContainer }),
|
||||
labels: (withIconContainer: boolean) =>
|
||||
withIconContainer
|
||||
? 'With icon container'
|
||||
: 'Without icon container',
|
||||
},
|
||||
{
|
||||
name: 'toggled',
|
||||
values: [true, false],
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
export * from '../../components/MenuPicker';
|
||||
export * from './MenuItem';
|
||||
export * from './MenuItemAvatar';
|
||||
export * from './MenuItemCommand';
|
||||
export * from './MenuItemCommandHotKeys';
|
||||
export * from './MenuItemDraggable';
|
||||
export * from './MenuItemHotKeys';
|
||||
export * from './MenuItemMultiSelect';
|
||||
export * from './MenuItemMultiSelectAvatar';
|
||||
export * from './MenuItemMultiSelectTag';
|
||||
|
||||
+31
-17
@@ -1,5 +1,5 @@
|
||||
import { useTheme } from '@emotion/react';
|
||||
import { isString } from '@sniptt/guards';
|
||||
import { isNonEmptyString, isString } from '@sniptt/guards';
|
||||
import { type ReactNode } from 'react';
|
||||
|
||||
import styled from '@emotion/styled';
|
||||
@@ -10,6 +10,7 @@ import {
|
||||
} from '@ui/display';
|
||||
import {
|
||||
StyledDraggableItem,
|
||||
StyledMenuItemContextualText,
|
||||
StyledMenuItemLabel,
|
||||
StyledMenuItemLeftContent,
|
||||
} from './StyledMenuItemBase';
|
||||
@@ -22,23 +23,20 @@ const StyledMainText = styled.div`
|
||||
max-width: 100%;
|
||||
`;
|
||||
|
||||
const StyledContextualText = styled.div`
|
||||
color: ${({ theme }) => theme.font.color.light};
|
||||
font-family: inherit;
|
||||
font-size: inherit;
|
||||
font-weight: inherit;
|
||||
max-width: 100%;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
padding-left: ${({ theme }) => theme.spacing(1)};
|
||||
flex-shrink: 1;
|
||||
const StyledIconContainer = styled.div`
|
||||
align-items: flex-start;
|
||||
background: ${({ theme }) => theme.background.transparent.light};
|
||||
border-radius: ${({ theme }) => theme.border.radius.sm};
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding: ${({ theme }) => theme.spacing(1)};
|
||||
`;
|
||||
|
||||
type MenuItemLeftContentProps = {
|
||||
className?: string;
|
||||
LeftComponent?: ReactNode;
|
||||
LeftIcon: IconComponent | null | undefined;
|
||||
withIconContainer?: boolean;
|
||||
showGrip?: boolean;
|
||||
disabled?: boolean;
|
||||
text: ReactNode;
|
||||
@@ -49,6 +47,7 @@ export const MenuItemLeftContent = ({
|
||||
className,
|
||||
LeftComponent,
|
||||
LeftIcon,
|
||||
withIconContainer = false,
|
||||
text,
|
||||
contextualText,
|
||||
showGrip = false,
|
||||
@@ -64,14 +63,23 @@ export const MenuItemLeftContent = ({
|
||||
size={theme.icon.size.md}
|
||||
stroke={theme.icon.stroke.sm}
|
||||
color={
|
||||
disabled ? theme.font.color.extraLight : theme.font.color.light
|
||||
withIconContainer
|
||||
? theme.font.color.tertiary
|
||||
: disabled
|
||||
? theme.font.color.extraLight
|
||||
: theme.font.color.light
|
||||
}
|
||||
/>
|
||||
</StyledDraggableItem>
|
||||
)}
|
||||
{LeftIcon && (
|
||||
<LeftIcon size={theme.icon.size.md} stroke={theme.icon.stroke.sm} />
|
||||
)}
|
||||
{LeftIcon &&
|
||||
(withIconContainer ? (
|
||||
<StyledIconContainer>
|
||||
<LeftIcon size={theme.icon.size.md} stroke={theme.icon.stroke.sm} />
|
||||
</StyledIconContainer>
|
||||
) : (
|
||||
<LeftIcon size={theme.icon.size.md} stroke={theme.icon.stroke.sm} />
|
||||
))}
|
||||
{LeftComponent}
|
||||
<StyledMenuItemLabel>
|
||||
{isString(text) ? (
|
||||
@@ -82,7 +90,13 @@ export const MenuItemLeftContent = ({
|
||||
text
|
||||
)}
|
||||
{isString(contextualText) ? (
|
||||
<StyledContextualText>{`· ${contextualText}`}</StyledContextualText>
|
||||
<>
|
||||
{isNonEmptyString(contextualText) && (
|
||||
<StyledMenuItemContextualText>
|
||||
<OverflowingTextWithTooltip text={`· ${contextualText}`} />
|
||||
</StyledMenuItemContextualText>
|
||||
)}
|
||||
</>
|
||||
) : (
|
||||
contextualText
|
||||
)}
|
||||
|
||||
+21
-4
@@ -108,17 +108,16 @@ export const StyledMenuItemLeftContent = styled.div`
|
||||
|
||||
gap: ${({ theme }) => theme.spacing(2)};
|
||||
min-width: 0;
|
||||
width: 100%;
|
||||
max-width: 100%;
|
||||
|
||||
& > svg {
|
||||
flex-shrink: 0;
|
||||
}
|
||||
flex-shrink: 0;
|
||||
`;
|
||||
|
||||
export const StyledMenuItemRightContent = styled.div`
|
||||
align-items: center;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
overflow: hidden;
|
||||
`;
|
||||
|
||||
export const StyledDraggableItem = styled.div`
|
||||
@@ -170,3 +169,21 @@ export const StyledMenuItemIconCheck = styled(IconCheck)`
|
||||
flex-shrink: 0;
|
||||
margin-right: ${({ theme }) => theme.spacing(1)};
|
||||
`;
|
||||
|
||||
export const StyledMenuItemContextualText = styled.div`
|
||||
color: ${({ theme }) => theme.font.color.light};
|
||||
font-family: inherit;
|
||||
font-size: inherit;
|
||||
font-weight: inherit;
|
||||
padding-left: ${({ theme }) => theme.spacing(1)};
|
||||
flex-shrink: 1;
|
||||
overflow: hidden;
|
||||
`;
|
||||
|
||||
export const StyledRightMenuItemContextualText = styled(
|
||||
StyledMenuItemContextualText,
|
||||
)`
|
||||
display: flex;
|
||||
text-align: right;
|
||||
padding-right: ${({ theme }) => theme.spacing(1)};
|
||||
`;
|
||||
|
||||
Reference in New Issue
Block a user