From e81f61637f09e1460c79a75b9f3d1efa62e15332 Mon Sep 17 00:00:00 2001 From: Sander Vocke Date: Mon, 28 Sep 2020 23:30:33 +0200 Subject: [PATCH] Can move tags around. --- client/src/components/appbar/AddTabMenu.tsx | 2 +- .../windows/manage_tags/ManageTagMenu.tsx | 37 +++++++++- .../windows/manage_tags/ManageTagsWindow.tsx | 70 ++++++++++++++++--- 3 files changed, 99 insertions(+), 10 deletions(-) diff --git a/client/src/components/appbar/AddTabMenu.tsx b/client/src/components/appbar/AddTabMenu.tsx index f3eb48f..a9e0755 100644 --- a/client/src/components/appbar/AddTabMenu.tsx +++ b/client/src/components/appbar/AddTabMenu.tsx @@ -35,6 +35,6 @@ export default function AddTabMenu(props: IProps) { windowType: WindowType.ManageTags, }) }} - >{WindowType.ManageTags} + >Manage Tags } \ No newline at end of file diff --git a/client/src/components/windows/manage_tags/ManageTagMenu.tsx b/client/src/components/windows/manage_tags/ManageTagMenu.tsx index 758b689..d71cbf6 100644 --- a/client/src/components/windows/manage_tags/ManageTagMenu.tsx +++ b/client/src/components/windows/manage_tags/ManageTagMenu.tsx @@ -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 && props.onPick(null)}>/} + {props.tags.map((tag: any) => { + if ('children' in tag && tag.children.length > 0) { + return props.onPick(tag.tagId)} + > + + + } + return props.onPick(tag.tagId)}>{tag.name} + }) + } +} + 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) { }} /> - + + { + console.log("onPick:", v) + props.onClose(); + props.onMove(v); + }} /> + } \ No newline at end of file diff --git a/client/src/components/windows/manage_tags/ManageTagsWindow.tsx b/client/src/components/windows/manage_tags/ManageTagsWindow.tsx index 816bf1f..d77468a 100644 --- a/client/src/components/windows/manage_tags/ManageTagsWindow.tsx +++ b/client/src/components/windows/manage_tags/ManageTagsWindow.tsx @@ -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, 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: { /]} dispatch={props.dispatch} state={props.state} + changedTags={props.changedTags} />)} { + 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, changes: TagChange[]) { - var retval = tags; +function annotateTagsWithChanges(tags: Record, changes: TagChange[]) { + var retval = _.cloneDeep(tags); const applyDelete = (id: number) => { retval[id].proposeDelete = true; @@ -205,6 +224,37 @@ function addTagChanges(tags: Record, 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, 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 @@ -263,6 +316,7 @@ export default function ManageTagsWindow(props: IProps) { prependElems={[]} dispatch={props.dispatch} state={props.state} + changedTags={changedTags} />; })}