diff --git a/CHANGELOG.md b/CHANGELOG.md index c4ef1c9..4290ee7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,10 +8,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] ### Added -- Added compatibility with PrimeVue 4 [#16](https://github.com/archesproject/arches-references/pull/16) +- Add compatibility with PrimeVue 4 [#9](https://github.com/archesproject/arches-references/issues/9) +- Add front-end routing [#4](https://github.com/archesproject/arches-references/issues/4) ### Changed -- Generate URIs when not supplied [#17](https://github.com/archesproject/arches-references/pull/17) +- Generate URIs when not supplied [#8](https://github.com/archesproject/arches-references/issues/8) ### Fixed diff --git a/arches_references/media/js/views/components/plugins/controlled-list-manager.js b/arches_references/media/js/views/components/plugins/controlled-list-manager.js index 6ed3916..3823d50 100644 --- a/arches_references/media/js/views/components/plugins/controlled-list-manager.js +++ b/arches_references/media/js/views/components/plugins/controlled-list-manager.js @@ -1,12 +1,21 @@ import ko from 'knockout'; +import { routes } from '@/arches_references/routes.ts'; import ControlledListManager from '@/arches_references/plugins/ControlledListManager.vue'; import createVueApplication from 'utils/create-vue-application'; import ControlledListManagerTemplate from 'templates/views/components/plugins/controlled-list-manager.htm'; +import { createRouter, createWebHistory } from 'vue-router'; + +const router = createRouter({ + history: createWebHistory(), + routes, +}); + ko.components.register('controlled-list-manager', { viewModel: function() { createVueApplication(ControlledListManager).then((vueApp) => { + vueApp.use(router); vueApp.mount('#controlled-list-manager-mounting-point'); }); }, diff --git a/arches_references/src/arches_references/components/ControlledListsMain.vue b/arches_references/src/arches_references/components/ControlledListsMain.vue index 65cc993..a18ea65 100644 --- a/arches_references/src/arches_references/components/ControlledListsMain.vue +++ b/arches_references/src/arches_references/components/ControlledListsMain.vue @@ -1,31 +1,42 @@ diff --git a/arches_references/src/arches_references/components/MainSplitter.vue b/arches_references/src/arches_references/components/MainSplitter.vue new file mode 100644 index 0000000..f36822c --- /dev/null +++ b/arches_references/src/arches_references/components/MainSplitter.vue @@ -0,0 +1,63 @@ + + + diff --git a/arches_references/src/arches_references/components/tree/AddDeleteControls.vue b/arches_references/src/arches_references/components/tree/AddDeleteControls.vue index 59b7071..84d49c1 100644 --- a/arches_references/src/arches_references/components/tree/AddDeleteControls.vue +++ b/arches_references/src/arches_references/components/tree/AddDeleteControls.vue @@ -5,7 +5,6 @@ import { useGettext } from "vue3-gettext"; import { useConfirm } from "primevue/useconfirm"; import { useToast } from "primevue/usetoast"; import Button from "primevue/button"; -import ConfirmDialog from "primevue/confirmdialog"; import SplitButton from "primevue/splitbutton"; import { @@ -113,7 +112,6 @@ const createList = () => { }; nextNewList.value = newList; - newListFormValue.value = ""; newListCounter.value += 1; tree.value.push(listAsNode(newList, selectedLanguage.value)); @@ -262,29 +260,6 @@ await fetchListsAndPopulateTree(); :severity="shouldUseContrast() ? CONTRAST : PRIMARY" @click="createList" /> - -import { computed, inject, ref } from "vue"; +import { computed, inject, ref, watch } from "vue"; +import { useRoute } from "vue-router"; import { useGettext } from "vue3-gettext"; import Tree from "primevue/tree"; @@ -8,12 +9,18 @@ import { displayedRowKey, selectedLanguageKey, } from "@/arches_references/constants.ts"; -import { bestLabel, nodeIsList } from "@/arches_references/utils.ts"; +import { routeNames } from "@/arches_references/routes.ts"; +import { + bestLabel, + findNodeInTree, + nodeIsList, +} from "@/arches_references/utils.ts"; import LetterCircle from "@/arches_references/components/misc/LetterCircle.vue"; import ListTreeControls from "@/arches_references/components/tree/ListTreeControls.vue"; import TreeRow from "@/arches_references/components/tree/TreeRow.vue"; import type { ComponentPublicInstance, Ref } from "vue"; +import type { RouteLocationNormalizedLoadedGeneric } from "vue-router"; import type { TreePassThroughMethodOptions } from "primevue/tree"; import type { TreeExpandedKeys, TreeSelectionKeys } from "primevue/tree"; import type { TreeNode } from "primevue/treenode"; @@ -57,7 +64,89 @@ const { setDisplayedRow } = inject(displayedRowKey) as unknown as { setDisplayedRow: RowSetter; }; +const route = useRoute(); + +const navigate = (newRoute: RouteLocationNormalizedLoadedGeneric) => { + switch (newRoute.name) { + case routeNames.splash: + setDisplayedRow(null); + expandedKeys.value = {}; + selectedKeys.value = {}; + break; + case routeNames.list: { + if (!tree.value.length) { + return; + } + const list = tree.value.find( + (node) => node.data.id === newRoute.params.id, + ); + if (list) { + setDisplayedRow(list.data); + expandedKeys.value = { + ...expandedKeys.value, + [list.data.id]: true, + }; + selectedKeys.value = { [list.data.id]: true }; + } else { + setDisplayedRow(null); + } + break; + } + case routeNames.item: { + if (!tree.value.length) { + return; + } + const { found, path } = findNodeInTree( + tree.value, + newRoute.params.id as string, + ); + if (found) { + setDisplayedRow(found.data); + const itemsToExpandIds = path.map( + (itemInPath: TreeNode) => itemInPath.key, + ); + expandedKeys.value = { + ...expandedKeys.value, + ...Object.fromEntries( + [ + found.data.controlled_list_id, + ...itemsToExpandIds, + ].map((x) => [x, true]), + ), + }; + selectedKeys.value = { [found.data.id]: true }; + } + break; + } + } +}; + +// React to route changes. +watch( + [ + () => { + return { ...route }; + }, + ], + ([newRoute]) => { + navigate(newRoute); + }, +); + +// Navigate on initial load of the tree. +watch( + tree, + () => { + navigate(route); + }, + { once: true }, +); + const updateSelectedAndExpanded = (node: TreeNode) => { + if (isMultiSelecting.value || movingItem.value?.key) { + return; + } + setDisplayedRow(node.data); expandedKeys.value = { ...expandedKeys.value, @@ -169,7 +258,7 @@ const ptNodeContent = ({ instance }: TreePassThroughMethodOptions) => {