|
|
@ -106,31 +106,35 @@ export function SingleTag(props: { |
|
|
|
const tag = props.tag; |
|
|
|
const tag = props.tag; |
|
|
|
const hasChildren = 'children' in tag && tag.children.length > 0; |
|
|
|
const hasChildren = 'children' in tag && tag.children.length > 0; |
|
|
|
|
|
|
|
|
|
|
|
const [menuAnchorEl, setMenuAnchorEl] = React.useState<null | HTMLElement>(null); |
|
|
|
const [menuPos, setMenuPos] = React.useState<null | number[]>(null); |
|
|
|
const [expanded, setExpanded] = useState<Boolean>(false); |
|
|
|
const [expanded, setExpanded] = useState<Boolean>(false); |
|
|
|
const theme = useTheme(); |
|
|
|
const theme = useTheme(); |
|
|
|
|
|
|
|
|
|
|
|
const onOpenMenu = (event: any) => { |
|
|
|
const onOpenMenu = (e: any) => { |
|
|
|
setMenuAnchorEl(event.currentTarget); |
|
|
|
setMenuPos([e.clientX, e.clientY]) |
|
|
|
}; |
|
|
|
}; |
|
|
|
const onCloseMenu = () => { |
|
|
|
const onCloseMenu = () => { |
|
|
|
setMenuAnchorEl(null); |
|
|
|
setMenuPos(null); |
|
|
|
}; |
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
const tagLabel = ("proposedName" in tag) ? |
|
|
|
var tagLabel: any = tag.name; |
|
|
|
<><del style={{ color: theme.palette.text.secondary }}>{tag.name}</del>→{tag.proposedName}</> : |
|
|
|
if ("proposedName" in tag) { |
|
|
|
tag.name; |
|
|
|
tagLabel = <><del style={{ color: theme.palette.text.secondary }}>{tag.name}</del>→{tag.proposedName}</>; |
|
|
|
|
|
|
|
} else if ("proposeDelete" in tag && tag.proposeDelete) { |
|
|
|
|
|
|
|
tagLabel = <><del style={{ color: theme.palette.text.secondary }}>{tag.name}</del></>; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
const expandArrow = expanded ? |
|
|
|
const expandArrow = expanded ? |
|
|
|
<IconButton size="small" onClick={() => setExpanded(false)}><ArrowDropDownIcon /></IconButton> : |
|
|
|
<IconButton size="small" onClick={() => setExpanded(false)}><ArrowDropDownIcon /></IconButton> : |
|
|
|
<IconButton size="small" onClick={() => setExpanded(true)}><ArrowRightIcon /></IconButton>; |
|
|
|
<IconButton size="small" onClick={() => setExpanded(true)}><ArrowRightIcon /></IconButton>; |
|
|
|
|
|
|
|
|
|
|
|
const GreyedTag = (props: any) => <Box |
|
|
|
const TagChip = (props: any) => <Box |
|
|
|
style={{ opacity: 0.5 }} |
|
|
|
style={{ opacity: props.transparent ? 0.5 : 1.0 }} |
|
|
|
> |
|
|
|
> |
|
|
|
<Chip |
|
|
|
<Chip |
|
|
|
size="small" |
|
|
|
size="small" |
|
|
|
label={tagLabel} |
|
|
|
label={props.label} |
|
|
|
|
|
|
|
onClick={onOpenMenu} |
|
|
|
/> |
|
|
|
/> |
|
|
|
</Box>; |
|
|
|
</Box>; |
|
|
|
|
|
|
|
|
|
|
@ -140,22 +144,19 @@ export function SingleTag(props: { |
|
|
|
{expandArrow} |
|
|
|
{expandArrow} |
|
|
|
</Box> |
|
|
|
</Box> |
|
|
|
{props.prependElems} |
|
|
|
{props.prependElems} |
|
|
|
<Chip |
|
|
|
<TagChip transparent={tag.proposeDelete} label={tagLabel} /> |
|
|
|
size="small" |
|
|
|
|
|
|
|
label={tagLabel} |
|
|
|
|
|
|
|
onClick={onOpenMenu} |
|
|
|
|
|
|
|
/> |
|
|
|
|
|
|
|
</Box> |
|
|
|
</Box> |
|
|
|
{hasChildren && expanded && tag.children.map((child: any) => <SingleTag |
|
|
|
{hasChildren && expanded && tag.children.map((child: any) => <SingleTag |
|
|
|
tag={child} |
|
|
|
tag={child} |
|
|
|
prependElems={[...props.prependElems, |
|
|
|
prependElems={[...props.prependElems, |
|
|
|
<GreyedTag tag={tag} />, |
|
|
|
<TagChip transparent={true} label={tagLabel} />, |
|
|
|
<Typography variant="h5">/</Typography>]} |
|
|
|
<Typography variant="h5">/</Typography>]} |
|
|
|
dispatch={props.dispatch} |
|
|
|
dispatch={props.dispatch} |
|
|
|
state={props.state} |
|
|
|
state={props.state} |
|
|
|
/>)} |
|
|
|
/>)} |
|
|
|
<ManageTagMenu |
|
|
|
<ManageTagMenu |
|
|
|
anchorEl={menuAnchorEl} |
|
|
|
position={menuPos} |
|
|
|
|
|
|
|
open={menuPos !== null} |
|
|
|
onClose={onCloseMenu} |
|
|
|
onClose={onCloseMenu} |
|
|
|
onRename={(s: string) => { |
|
|
|
onRename={(s: string) => { |
|
|
|
props.dispatch({ |
|
|
|
props.dispatch({ |
|
|
@ -170,6 +171,18 @@ export function SingleTag(props: { |
|
|
|
] |
|
|
|
] |
|
|
|
}) |
|
|
|
}) |
|
|
|
}} |
|
|
|
}} |
|
|
|
|
|
|
|
onDelete={() => { |
|
|
|
|
|
|
|
props.dispatch({ |
|
|
|
|
|
|
|
type: ManageTagsWindowActions.SetPendingChanges, |
|
|
|
|
|
|
|
value: [ |
|
|
|
|
|
|
|
...props.state.pendingChanges, |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
type: TagChangeType.Delete, |
|
|
|
|
|
|
|
id: tag.tagId, |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
] |
|
|
|
|
|
|
|
}) |
|
|
|
|
|
|
|
}} |
|
|
|
tag={tag} |
|
|
|
tag={tag} |
|
|
|
/> |
|
|
|
/> |
|
|
|
</> |
|
|
|
</> |
|
|
@ -177,11 +190,21 @@ export function SingleTag(props: { |
|
|
|
|
|
|
|
|
|
|
|
function addTagChanges(tags: Record<number, any>, changes: TagChange[]) { |
|
|
|
function addTagChanges(tags: Record<number, any>, changes: TagChange[]) { |
|
|
|
var retval = tags; |
|
|
|
var retval = tags; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const applyDelete = (id: number) => { |
|
|
|
|
|
|
|
retval[id].proposeDelete = true; |
|
|
|
|
|
|
|
Object.values(tags).filter((t: any) => t.parentId === id) |
|
|
|
|
|
|
|
.forEach((child: any) => applyDelete(child.tagId)); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
changes.forEach((change: TagChange) => { |
|
|
|
changes.forEach((change: TagChange) => { |
|
|
|
switch (change.type) { |
|
|
|
switch (change.type) { |
|
|
|
case TagChangeType.Rename: |
|
|
|
case TagChangeType.Rename: |
|
|
|
retval[change.id].proposedName = change.name; |
|
|
|
retval[change.id].proposedName = change.name; |
|
|
|
break; |
|
|
|
break; |
|
|
|
|
|
|
|
case TagChangeType.Delete: |
|
|
|
|
|
|
|
applyDelete(change.id); |
|
|
|
|
|
|
|
break; |
|
|
|
default: |
|
|
|
default: |
|
|
|
throw new Error("Unimplemented tag change") |
|
|
|
throw new Error("Unimplemented tag change") |
|
|
|
} |
|
|
|
} |
|
|
|