-
Joris Pelgröm authoredJoris Pelgröm authored
SidebarCategory.tsx 5.02 KiB
// @ts-strict-ignore
import React, { type CSSProperties, type Ref, useRef, useState } from 'react';
import {
type CategoryGroupEntity,
type CategoryEntity,
} from 'loot-core/src/types/models';
import { SvgCheveronDown } from '../../icons/v1';
import { theme } from '../../style';
import { Button } from '../common/Button';
import { Menu } from '../common/Menu';
import { Popover } from '../common/Popover';
import { View } from '../common/View';
import { NotesButton } from '../NotesButton';
import { InputCell } from '../table';
type SidebarCategoryProps = {
innerRef: Ref<HTMLDivElement>;
category: CategoryEntity;
categoryGroup?: CategoryGroupEntity;
dragPreview?: boolean;
dragging?: boolean;
editing: boolean;
style?: CSSProperties;
borderColor?: string;
isLast?: boolean;
onEditName: (id: string) => void;
onSave: (category: CategoryEntity) => void;
onDelete: (id: string) => Promise<void>;
onHideNewCategory?: () => void;
};
export function SidebarCategory({
innerRef,
category,
categoryGroup,
dragPreview,
dragging,
editing,
style,
isLast,
onEditName,
onSave,
onDelete,
onHideNewCategory,
}: SidebarCategoryProps) {
const temporary = category.id === 'new';
const [menuOpen, setMenuOpen] = useState(false);
const triggerRef = useRef(null);
const displayed = (
<View
style={{
flexDirection: 'row',
alignItems: 'center',
userSelect: 'none',
WebkitUserSelect: 'none',
opacity: category.hidden || categoryGroup?.hidden ? 0.33 : undefined,
}}
>
<div
data-testid="category-name"
style={{
textOverflow: 'ellipsis',
whiteSpace: 'nowrap',
overflow: 'hidden',
minWidth: 0,
}}
>
{category.name}
</div>
<View style={{ flexShrink: 0, marginLeft: 5 }} ref={triggerRef}>
<Button
type="bare"
className="hover-visible"
onClick={e => {
e.stopPropagation();
setMenuOpen(true);
}}
style={{ color: 'currentColor', padding: 3 }}
>
<SvgCheveronDown
width={14}
height={14}
style={{ color: 'currentColor' }}
/>
</Button>
<Popover
triggerRef={triggerRef}
placement="bottom start"
isOpen={menuOpen}
onOpenChange={() => setMenuOpen(false)}
style={{ width: 200 }}
>
<Menu
onMenuSelect={type => {
if (type === 'rename') {
onEditName(category.id);
} else if (type === 'delete') {
onDelete(category.id);
} else if (type === 'toggle-visibility') {
onSave({ ...category, hidden: !category.hidden });
}
setMenuOpen(false);
}}
items={[
!categoryGroup?.hidden && {
name: 'toggle-visibility',
text: category.hidden ? 'Show' : 'Hide',
},
{ name: 'rename', text: 'Rename' },
{ name: 'delete', text: 'Delete' },
]}
/>
</Popover>
</View>
<View style={{ flex: 1 }} />
<View style={{ flexShrink: 0 }}>
<NotesButton
id={category.id}
style={dragging && { color: 'currentColor' }}
defaultColor={theme.pageTextLight}
/>
</View>
</View>
);
return (
<View
innerRef={innerRef}
style={{
width: 200,
overflow: 'hidden',
'& .hover-visible': {
display: 'none',
},
...(!dragging &&
!dragPreview && {
'&:hover .hover-visible': {
display: 'flex',
},
}),
...(dragging && { color: theme.formInputTextPlaceholderSelected }),
// The zIndex here forces the the view on top of a row below
// it that may be "collapsed" and show a border on top
...(dragPreview && {
backgroundColor: theme.tableBackground,
zIndex: 10000,
borderRadius: 6,
overflow: 'hidden',
}),
...style,
}}
onKeyDown={e => {
if (e.key === 'Enter') {
onEditName(null);
e.stopPropagation();
}
}}
>
<InputCell
value={category.name}
formatter={() => displayed}
width="flex"
exposed={editing || temporary}
onUpdate={value => {
if (temporary) {
if (value === '') {
onHideNewCategory();
} else if (value !== '') {
onSave({ ...category, name: value });
}
} else {
if (value !== category.name) {
onSave({ ...category, name: value });
}
}
}}
onBlur={() => onEditName(null)}
style={{ paddingLeft: 13, ...(isLast && { borderBottomWidth: 0 }) }}
inputProps={{
placeholder: temporary ? 'New Category Name' : '',
}}
/>
</View>
);
}