Add board Action bar and context menu (#3680)
* Add board Action bar and context menu * Fix according to review
This commit is contained in:
@ -9,10 +9,6 @@ import { actionBarOpenState } from '../states/actionBarIsOpenState';
|
||||
|
||||
import { ActionBarItem } from './ActionBarItem';
|
||||
|
||||
type ActionBarProps = {
|
||||
selectedIds: string[];
|
||||
};
|
||||
|
||||
const StyledContainerActionBar = styled.div`
|
||||
align-items: center;
|
||||
background: ${({ theme }) => theme.background.secondary};
|
||||
@ -33,30 +29,24 @@ const StyledContainerActionBar = styled.div`
|
||||
z-index: 1;
|
||||
`;
|
||||
|
||||
export const ActionBar = ({ selectedIds }: ActionBarProps) => {
|
||||
export const ActionBar = () => {
|
||||
const actionBarOpen = useRecoilValue(actionBarOpenState);
|
||||
const contextMenuIsOpen = useRecoilValue(contextMenuIsOpenState);
|
||||
const actionBarEntries = useRecoilValue(actionBarEntriesState);
|
||||
const wrapperRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
if (selectedIds.length === 0 || !actionBarOpen || contextMenuIsOpen) {
|
||||
if (!actionBarOpen || contextMenuIsOpen) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<StyledContainerActionBar
|
||||
data-select-disable
|
||||
className="action-bar"
|
||||
ref={wrapperRef}
|
||||
>
|
||||
{actionBarEntries.map((item) => (
|
||||
<ActionBarItem
|
||||
Icon={item.Icon}
|
||||
accent={item.accent}
|
||||
label={item.label}
|
||||
onClick={item.onClick}
|
||||
key={item.label}
|
||||
subActions={item?.subActions}
|
||||
/>
|
||||
{actionBarEntries.map((item, index) => (
|
||||
<ActionBarItem key={index} item={item} />
|
||||
))}
|
||||
</StyledContainerActionBar>
|
||||
);
|
||||
|
||||
@ -3,22 +3,17 @@ import styled from '@emotion/styled';
|
||||
import { MenuItem } from 'tsup.ui.index';
|
||||
|
||||
import { IconChevronDown } from '@/ui/display/icon';
|
||||
import { IconComponent } from '@/ui/display/icon/types/IconComponent';
|
||||
import { Dropdown } from '@/ui/layout/dropdown/components/Dropdown';
|
||||
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
|
||||
import { useDropdown } from '@/ui/layout/dropdown/hooks/useDropdown';
|
||||
|
||||
import { ActionBarItemAccent } from '../types/ActionBarItemAccent';
|
||||
import { ActionBarEntry } from '@/ui/navigation/action-bar/types/ActionBarEntry';
|
||||
import { MenuItemAccent } from '@/ui/navigation/menu-item/types/MenuItemAccent';
|
||||
|
||||
type ActionBarItemProps = {
|
||||
Icon: IconComponent;
|
||||
label: string;
|
||||
accent?: ActionBarItemAccent;
|
||||
onClick?: () => void;
|
||||
subActions?: ActionBarItemProps[];
|
||||
item: ActionBarEntry;
|
||||
};
|
||||
|
||||
const StyledButton = styled.div<{ accent: ActionBarItemAccent }>`
|
||||
const StyledButton = styled.div<{ accent: MenuItemAccent }>`
|
||||
border-radius: ${({ theme }) => theme.border.radius.sm};
|
||||
color: ${(props) =>
|
||||
props.accent === 'danger'
|
||||
@ -45,19 +40,13 @@ const StyledButtonLabel = styled.div`
|
||||
margin-left: ${({ theme }) => theme.spacing(1)};
|
||||
`;
|
||||
|
||||
export const ActionBarItem = ({
|
||||
label,
|
||||
Icon,
|
||||
accent = 'standard',
|
||||
onClick,
|
||||
subActions,
|
||||
}: ActionBarItemProps) => {
|
||||
export const ActionBarItem = ({ item }: ActionBarItemProps) => {
|
||||
const theme = useTheme();
|
||||
const dropdownId = `action-bar-item-${label}`;
|
||||
const dropdownId = `action-bar-item-${item.label}`;
|
||||
const { toggleDropdown, closeDropdown } = useDropdown(dropdownId);
|
||||
return (
|
||||
<>
|
||||
{Array.isArray(subActions) ? (
|
||||
{Array.isArray(item.subActions) ? (
|
||||
<Dropdown
|
||||
dropdownId={dropdownId}
|
||||
dropdownPlacement="top-start"
|
||||
@ -65,15 +54,18 @@ export const ActionBarItem = ({
|
||||
scope: dropdownId,
|
||||
}}
|
||||
clickableComponent={
|
||||
<StyledButton accent={accent} onClick={toggleDropdown}>
|
||||
{Icon && <Icon size={theme.icon.size.md} />}
|
||||
<StyledButtonLabel>{label}</StyledButtonLabel>
|
||||
<StyledButton
|
||||
accent={item.accent ?? 'default'}
|
||||
onClick={toggleDropdown}
|
||||
>
|
||||
{item.Icon && <item.Icon size={theme.icon.size.md} />}
|
||||
<StyledButtonLabel>{item.label}</StyledButtonLabel>
|
||||
<IconChevronDown size={theme.icon.size.md} />
|
||||
</StyledButton>
|
||||
}
|
||||
dropdownComponents={
|
||||
<DropdownMenuItemsContainer>
|
||||
{subActions.map((subAction) => (
|
||||
{item.subActions.map((subAction) => (
|
||||
<MenuItem
|
||||
key={subAction.label}
|
||||
text={subAction.label}
|
||||
@ -88,9 +80,12 @@ export const ActionBarItem = ({
|
||||
}
|
||||
/>
|
||||
) : (
|
||||
<StyledButton accent={accent} onClick={onClick}>
|
||||
{Icon && <Icon size={theme.icon.size.md} />}
|
||||
<StyledButtonLabel>{label}</StyledButtonLabel>
|
||||
<StyledButton
|
||||
accent={item.accent ?? 'default'}
|
||||
onClick={() => item.onClick?.()}
|
||||
>
|
||||
{item.Icon && <item.Icon size={theme.icon.size.md} />}
|
||||
<StyledButtonLabel>{item.label}</StyledButtonLabel>
|
||||
</StyledButton>
|
||||
)}
|
||||
</>
|
||||
|
||||
@ -8,10 +8,10 @@ import { ComponentDecorator } from '~/testing/decorators/ComponentDecorator';
|
||||
import { actionBarOpenState } from '../../states/actionBarIsOpenState';
|
||||
import { ActionBar } from '../ActionBar';
|
||||
|
||||
const FilledActionBar = (props: { selectedIds: string[] }) => {
|
||||
const FilledActionBar = () => {
|
||||
const setActionBarOpenState = useSetRecoilState(actionBarOpenState);
|
||||
setActionBarOpenState(true);
|
||||
return <ActionBar selectedIds={props.selectedIds} />;
|
||||
return <ActionBar />;
|
||||
};
|
||||
|
||||
const meta: Meta<typeof ActionBar> = {
|
||||
|
||||
@ -1,11 +1,10 @@
|
||||
import { IconComponent } from '@/ui/display/icon/types/IconComponent';
|
||||
|
||||
import { ActionBarItemAccent } from './ActionBarItemAccent';
|
||||
import { MenuItemAccent } from '@/ui/navigation/menu-item/types/MenuItemAccent';
|
||||
|
||||
export type ActionBarEntry = {
|
||||
label: string;
|
||||
Icon: IconComponent;
|
||||
accent?: ActionBarItemAccent;
|
||||
accent?: MenuItemAccent;
|
||||
onClick?: () => void;
|
||||
subActions?: ActionBarEntry[];
|
||||
};
|
||||
|
||||
@ -14,10 +14,6 @@ import { PositionType } from '../types/PositionType';
|
||||
|
||||
import { ContextMenuItem } from './ContextMenuItem';
|
||||
|
||||
type ContextMenuProps = {
|
||||
selectedIds: string[];
|
||||
};
|
||||
|
||||
type StyledContainerProps = {
|
||||
position: PositionType;
|
||||
};
|
||||
@ -41,7 +37,7 @@ const StyledContainerContextMenu = styled.div<StyledContainerProps>`
|
||||
z-index: 2;
|
||||
`;
|
||||
|
||||
export const ContextMenu = ({ selectedIds }: ContextMenuProps) => {
|
||||
export const ContextMenu = () => {
|
||||
const contextMenuPosition = useRecoilValue(contextMenuPositionState);
|
||||
const contextMenuIsOpen = useRecoilValue(contextMenuIsOpenState);
|
||||
const contextMenuEntries = useRecoilValue(contextMenuEntriesState);
|
||||
@ -57,7 +53,7 @@ export const ContextMenu = ({ selectedIds }: ContextMenuProps) => {
|
||||
},
|
||||
});
|
||||
|
||||
if (selectedIds.length === 0 || !contextMenuIsOpen) {
|
||||
if (!contextMenuIsOpen) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@ -75,15 +71,9 @@ export const ContextMenu = ({ selectedIds }: ContextMenuProps) => {
|
||||
>
|
||||
<DropdownMenu data-select-disable width={width}>
|
||||
<DropdownMenuItemsContainer>
|
||||
{contextMenuEntries.map((item) => (
|
||||
<ContextMenuItem
|
||||
Icon={item.Icon}
|
||||
label={item.label}
|
||||
accent={item.accent}
|
||||
onClick={item.onClick}
|
||||
key={item.label}
|
||||
/>
|
||||
))}
|
||||
{contextMenuEntries.map((item, index) => {
|
||||
return <ContextMenuItem key={index} item={item} />;
|
||||
})}
|
||||
</DropdownMenuItemsContainer>
|
||||
</DropdownMenu>
|
||||
</StyledContainerContextMenu>
|
||||
|
||||
@ -1,20 +1,15 @@
|
||||
import { IconComponent } from '@/ui/display/icon/types/IconComponent';
|
||||
import { ContextMenuEntry } from '@/ui/navigation/context-menu/types/ContextMenuEntry';
|
||||
import { MenuItem } from '@/ui/navigation/menu-item/components/MenuItem';
|
||||
|
||||
import { ContextMenuItemAccent } from '../types/ContextMenuItemAccent';
|
||||
|
||||
type ContextMenuItemProps = {
|
||||
Icon: IconComponent;
|
||||
label: string;
|
||||
accent?: ContextMenuItemAccent;
|
||||
onClick: () => void;
|
||||
item: ContextMenuEntry;
|
||||
};
|
||||
|
||||
export const ContextMenuItem = ({
|
||||
label,
|
||||
Icon,
|
||||
accent = 'default',
|
||||
onClick,
|
||||
}: ContextMenuItemProps) => (
|
||||
<MenuItem LeftIcon={Icon} onClick={onClick} accent={accent} text={label} />
|
||||
export const ContextMenuItem = ({ item }: ContextMenuItemProps) => (
|
||||
<MenuItem
|
||||
LeftIcon={item.Icon}
|
||||
onClick={item.onClick}
|
||||
accent={item.accent}
|
||||
text={item.label}
|
||||
/>
|
||||
);
|
||||
|
||||
@ -9,7 +9,7 @@ import { contextMenuIsOpenState } from '../../states/contextMenuIsOpenState';
|
||||
import { contextMenuPositionState } from '../../states/contextMenuPositionState';
|
||||
import { ContextMenu } from '../ContextMenu';
|
||||
|
||||
const FilledContextMenu = (props: { selectedIds: string[] }) => {
|
||||
const FilledContextMenu = () => {
|
||||
const setContextMenuPosition = useSetRecoilState(contextMenuPositionState);
|
||||
setContextMenuPosition({
|
||||
x: 100,
|
||||
@ -17,7 +17,7 @@ const FilledContextMenu = (props: { selectedIds: string[] }) => {
|
||||
});
|
||||
const setContextMenuOpenState = useSetRecoilState(contextMenuIsOpenState);
|
||||
setContextMenuOpenState(true);
|
||||
return <ContextMenu selectedIds={props.selectedIds} />;
|
||||
return <ContextMenu />;
|
||||
};
|
||||
|
||||
const meta: Meta<typeof ContextMenu> = {
|
||||
|
||||
@ -1,10 +1,9 @@
|
||||
import { IconComponent } from '@/ui/display/icon/types/IconComponent';
|
||||
|
||||
import { ContextMenuItemAccent } from './ContextMenuItemAccent';
|
||||
import { MenuItemAccent } from '@/ui/navigation/menu-item/types/MenuItemAccent';
|
||||
|
||||
export type ContextMenuEntry = {
|
||||
label: string;
|
||||
Icon: IconComponent;
|
||||
accent?: ContextMenuItemAccent;
|
||||
accent?: MenuItemAccent;
|
||||
onClick: () => void;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user