Can move tags around.

pull/24/head
Sander Vocke 5 years ago
parent 3d00b5b6ca
commit e81f61637f
  1. 2
      client/src/components/appbar/AddTabMenu.tsx
  2. 37
      client/src/components/windows/manage_tags/ManageTagMenu.tsx
  3. 70
      client/src/components/windows/manage_tags/ManageTagsWindow.tsx

@ -35,6 +35,6 @@ export default function AddTabMenu(props: IProps) {
windowType: WindowType.ManageTags,
})
}}
>{WindowType.ManageTags}</MenuItem>
>Manage Tags</MenuItem>
</Menu>
}

@ -25,13 +25,39 @@ export function MenuEditText(props: {
/>
}
export function PickTag(props: {
tags: any[]
open: boolean
root: boolean
onPick: (v: number | null) => void
}) {
return <>
{props.root && <MenuItem onClick={() => props.onPick(null)}>/</MenuItem>}
{props.tags.map((tag: any) => {
if ('children' in tag && tag.children.length > 0) {
return <NestedMenuItem
parentMenuOpen={props.open}
label={tag.name}
onClick={() => props.onPick(tag.tagId)}
>
<PickTag tags={tag.children} open={props.open} root={false} onPick={props.onPick} />
</NestedMenuItem>
}
return <MenuItem onClick={() => props.onPick(tag.tagId)}>{tag.name}</MenuItem>
})
}</>
}
export interface IProps {
position: null | number[],
open: boolean,
onClose: () => void,
onRename: (s: string) => void,
onDelete: () => void,
onMove: (to: number | null) => void,
tag: any,
changedTags: any[], // Tags organized hierarchically with "children" fields
}
export default function ManageTagMenu(props: IProps) {
@ -64,6 +90,15 @@ export default function ManageTagMenu(props: IProps) {
}}
/>
</NestedMenuItem>
<NestedMenuItem
parentMenuOpen={props.open}
label="Move to"
>
<PickTag tags={props.changedTags} open={props.open} root={true} onPick={(v: number | null) => {
console.log("onPick:", v)
props.onClose();
props.onMove(v);
}} />
</NestedMenuItem>
</Menu>
}

@ -19,7 +19,8 @@ export enum TagChangeType {
export interface TagChange {
type: TagChangeType,
id: number, // MuDBase ID. If not in database yet, negative IDs will be used until submitted.
parent?: number, // MuDBase ID. If not in database yet, negative IDs will be used until submitted.
parent?: number | null, // MuDBase ID. If not in database yet, negative IDs will be used until submitted.
// null refers to the tags root.
name?: string,
}
@ -51,10 +52,12 @@ export function ManageTagsWindowReducer(state: ManageTagsWindowState, action: an
}
export function organiseTags(allTags: Record<number, any>, fromId: number | null): any[] {
const base = Object.values(allTags).filter((tag: any) =>
(fromId === null && !tag.parentId) ||
(tag.parentId && tag.parentId === fromId)
);
const base = Object.values(allTags).filter((tag: any) => {
var par: any = ("proposedParent" in tag) ? tag.proposedParent : tag.parentId;
return (fromId === null && !par) ||
(par && par === fromId)
});
return base.map((tag: any) => {
return {
@ -102,6 +105,7 @@ export function SingleTag(props: {
prependElems: any[],
dispatch: (action: any) => void,
state: ManageTagsWindowState,
changedTags: any[],
}) {
const tag = props.tag;
const hasChildren = 'children' in tag && tag.children.length > 0;
@ -153,6 +157,7 @@ export function SingleTag(props: {
<Typography variant="h5">/</Typography>]}
dispatch={props.dispatch}
state={props.state}
changedTags={props.changedTags}
/>)}
<ManageTagMenu
position={menuPos}
@ -183,13 +188,27 @@ export function SingleTag(props: {
]
})
}}
onMove={(to: number | null) => {
props.dispatch({
type: ManageTagsWindowActions.SetPendingChanges,
value: [
...props.state.pendingChanges,
{
type: TagChangeType.MoveTo,
id: tag.tagId,
parent: to,
}
]
})
}}
tag={tag}
changedTags={props.changedTags}
/>
</>
}
function addTagChanges(tags: Record<number, any>, changes: TagChange[]) {
var retval = tags;
function annotateTagsWithChanges(tags: Record<number, any>, changes: TagChange[]) {
var retval = _.cloneDeep(tags);
const applyDelete = (id: number) => {
retval[id].proposeDelete = true;
@ -205,6 +224,37 @@ function addTagChanges(tags: Record<number, any>, changes: TagChange[]) {
case TagChangeType.Delete:
applyDelete(change.id);
break;
case TagChangeType.MoveTo:
retval[change.id].proposedParent = change.parent;
break;
default:
throw new Error("Unimplemented tag change")
}
})
return retval;
}
function applyTagsChanges(tags: Record<number, any>, changes: TagChange[]) {
var retval = _.cloneDeep(tags);
const applyDelete = (id: number) => {
Object.values(tags).filter((t: any) => t.parentId === id)
.forEach((child: any) => applyDelete(child.tagId));
delete retval[id].proposeDelete;
}
changes.forEach((change: TagChange) => {
switch (change.type) {
case TagChangeType.Rename:
retval[change.id].name = change.name;
break;
case TagChangeType.Delete:
applyDelete(change.id);
break;
case TagChangeType.MoveTo:
retval[change.id].parentId = change.parent;
if (change.parent === null) { delete retval[change.id].parentId; }
break;
default:
throw new Error("Unimplemented tag change")
}
@ -234,7 +284,10 @@ export default function ManageTagsWindow(props: IProps) {
})();
}, [props.state.fetchedTags]);
const tagsWithChanges = addTagChanges(props.state.fetchedTags || {}, props.state.pendingChanges)
const tagsWithChanges = annotateTagsWithChanges(props.state.fetchedTags || {}, props.state.pendingChanges)
const changedTags = organiseTags(
applyTagsChanges(props.state.fetchedTags || {}, props.state.pendingChanges),
null);
const tags = organiseTags(tagsWithChanges, null);
return <Box width="100%" justifyContent="center" display="flex" flexWrap="wrap">
@ -263,6 +316,7 @@ export default function ManageTagsWindow(props: IProps) {
prependElems={[]}
dispatch={props.dispatch}
state={props.state}
changedTags={changedTags}
/>;
})}
</Box>

Loading…
Cancel
Save