From 336507dad1f008915289e3411c4a77530c8a7bee Mon Sep 17 00:00:00 2001 From: orlinmalkja Date: Wed, 22 Jan 2025 10:41:06 +0100 Subject: [PATCH 01/13] wip (add panel from add button) --- src/App.tsx | 3 + src/components/LocalTreeModal.tsx | 48 +++++++++++++ src/components/TopBar.tsx | 20 ++++++ src/components/TreeView.tsx | 88 +++++++++++++++++++++++ src/components/tree/CollectionSubtree.tsx | 28 ++++++++ src/components/tree/Item.tsx | 31 ++++++++ src/components/tree/ManifestSubtree.tsx | 27 +++++++ src/utils/tree.ts | 65 +++++++++++++++++ 8 files changed, 310 insertions(+) create mode 100644 src/components/LocalTreeModal.tsx create mode 100644 src/components/TopBar.tsx create mode 100644 src/components/TreeView.tsx create mode 100644 src/components/tree/CollectionSubtree.tsx create mode 100644 src/components/tree/Item.tsx create mode 100644 src/components/tree/ManifestSubtree.tsx create mode 100644 src/utils/tree.ts diff --git a/src/App.tsx b/src/App.tsx index f17c57a8..ace024b9 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -2,6 +2,8 @@ import PanelsWrapper from './components/PanelsWrapper' import { FC } from 'react' import { configStore } from '@/store/ConfigStore.tsx' +import TopBar from '@/components/TopBar' + interface AppProps { customConfig: Config } @@ -12,6 +14,7 @@ const App: FC = ({ customConfig }) => { return (
+
) diff --git a/src/components/LocalTreeModal.tsx b/src/components/LocalTreeModal.tsx new file mode 100644 index 00000000..7928efed --- /dev/null +++ b/src/components/LocalTreeModal.tsx @@ -0,0 +1,48 @@ +import { FC, ReactNode, useState } from 'react' + +import { Popover, PopoverContent, PopoverTrigger } from '@/components/ui/popover' +import TreeView from '@/components/TreeView' + +interface LocalTreeProps { + TriggerButton: ReactNode +} + +const LocalTreeModal: FC = ({ TriggerButton }) => { + + // TODO: add a [loading, setLoading] => which shows the pop over when the tree has been loaded -> TreeView Component updates the loading of its parent + + const [selectClicked, setSelectClicked] = useState(false) + + function handleSelectClick(e) { + // TODO: check whether input text is provided or an item is clicked + + + } + + const selectButton = + + + return
+ + + { TriggerButton } + + +
+ Enter a collection/manifest Url + + Or choose: + +
+ {selectButton} +
+
+
+
+
+} + +export default LocalTreeModal \ No newline at end of file diff --git a/src/components/TopBar.tsx b/src/components/TopBar.tsx new file mode 100644 index 00000000..7c70bd3a --- /dev/null +++ b/src/components/TopBar.tsx @@ -0,0 +1,20 @@ +import { FC } from 'react' + + +import LocalTreeModal from '@/components/LocalTreeModal' + +const TopBar: FC = () => { + + const addButton = + + New + + + +return
+ +
+ +} + +export default TopBar \ No newline at end of file diff --git a/src/components/TreeView.tsx b/src/components/TreeView.tsx new file mode 100644 index 00000000..e99fcf8a --- /dev/null +++ b/src/components/TreeView.tsx @@ -0,0 +1,88 @@ +import { FC, useEffect, useState } from 'react' +import { configStore } from '@/store/ConfigStore.tsx' + + +import { createTree } from '@/utils/tree' +import CollectionSubtree from '@/components/tree/CollectionSubtree' + +const Tree: FC = () => { + + const config = configStore(state => state.config) + + const [treeNodes, setTreeNodes] = useState([]) + + const [loadingTree, setLoadingTreee] = useState(true) + + useEffect(() => { + async function initTree(panels?: PanelConfig[]) { + if (!panels) return + const nodes = await createTree(panels) + + setTreeNodes(nodes) + setLoadingTreee(false) + } + initTree(config.panels) + + }, []) + + console.log('tree', treeNodes) + + + + if (loadingTree) return <> + + function getCollectionUrl(itemLabel, treeNodes): string | null { + + for (let i = 0; i < treeNodes.length ; i++) { + const collectionNode = treeNodes[i] + for (let j = 0; j < collectionNode.children.length; j++) { + const manifest = collectionNode.children[j] + if (isItemInManifest(manifest, itemLabel)) { + console.log('manifest found', manifest) + return collectionNode.url + } + } + //if (isItemInManifests(collectionNode.children, itemLabel)) return collectionNode.url + } + + return null + } + + function isItemInManifests(manifests, itemLabel) { + for (let i = 0; i < manifests.length ; i++) { + if (isItemInManifest(manifests[i], itemLabel)) return true + } + + return false + } + + function isItemInManifest(manifest, itemLabel) { + const items = manifest.children + for (let i = 0; i < items.length ; i++) { + if (items[i].url === itemLabel) return true + } + + return false + } + + const collectionUrl = getCollectionUrl('https://api.ahiqar.sub.uni-goettingen.de/textapi/ahiqar/arabic-karshuni/3r177/16a/latest/item.json', treeNodes) + console.log('collection Url', collectionUrl) + const tree = + treeNodes.length > 0 && + treeNodes.map((collection, i) => ( +
+ +
+ )) + + + + return
+ {tree} +
+} + +export default Tree \ No newline at end of file diff --git a/src/components/tree/CollectionSubtree.tsx b/src/components/tree/CollectionSubtree.tsx new file mode 100644 index 00000000..3af0638f --- /dev/null +++ b/src/components/tree/CollectionSubtree.tsx @@ -0,0 +1,28 @@ + + +import { FC } from 'react' +import ManifestSubtree from '@/components/tree/ManifestSubtree' + + +interface CollectionSubtreeProps { + collectionData: any +} + +const CollectionSubtree: FC = ({ collectionData }) => { + + const manifestSubTree = collectionData.children.length > 0 + && + collectionData.children.map((manifest, i) => ( + + )) + + return
+ + {collectionData.title} +
+ { manifestSubTree} +
+
+} + +export default CollectionSubtree \ No newline at end of file diff --git a/src/components/tree/Item.tsx b/src/components/tree/Item.tsx new file mode 100644 index 00000000..f1a0b928 --- /dev/null +++ b/src/components/tree/Item.tsx @@ -0,0 +1,31 @@ + + +import { FC, useState } from 'react' + + +interface ItemProps { + label: string, + url: string +} + +const ItemTree: FC = ({ label, url }) => { + + const [active, setActive] = useState(false) + const [itemUrl] = useState(url) + + function handleClick( + e) { + e.preventDefault() + setActive(prevState => !prevState) + // find the collectionUrl based on itemUrl + } + + return
+ +
+} + +export default ItemTree \ No newline at end of file diff --git a/src/components/tree/ManifestSubtree.tsx b/src/components/tree/ManifestSubtree.tsx new file mode 100644 index 00000000..d48e319f --- /dev/null +++ b/src/components/tree/ManifestSubtree.tsx @@ -0,0 +1,27 @@ + + +import { FC } from 'react' +import ItemTree from '@/components/tree/Item' + +interface ManifestSubtreeProps { + manifestData: any +} + +const ManifestSubtree: FC = ({ manifestData }) => { + + const itemsLabels = manifestData.children.length > 0 + && + manifestData.children.map((item: Sequence, i) => ( + + )) + + return
+ + {manifestData.label} +
+ { itemsLabels} +
+
+} + +export default ManifestSubtree \ No newline at end of file diff --git a/src/utils/tree.ts b/src/utils/tree.ts new file mode 100644 index 00000000..8600b304 --- /dev/null +++ b/src/utils/tree.ts @@ -0,0 +1,65 @@ + +import { request } from '@/utils/http' + +export async function createTree(panels: PanelConfig[]) { + if (!panels || panels.length === 0) return [] + + const nodes = [] + + for (let i = 0; i< panels.length; i++) { + const collectionNode = await createCollectionNode(panels[i].entrypoint.url, i).then((node) => { + nodes.push(node) + }) + } + + return nodes +} + + + +async function createCollectionNode(url: string, key: number) { + const node = {} + const response = await request(url) + if (!response.success) return node + const collectionTitle = response.data.title[0].title + + node['url'] = url + node['key'] = key + node['title'] = collectionTitle + node['children'] = await getManifestNodes(node, response.data.sequence) + + return node +} + +async function getManifestNodes(parentNode, manifests) { + // items: 'sequence items' of collection + if (!manifests || manifests.length === 0) return [] + + const collectionNode = { ...parentNode } + collectionNode['children'] = [] + + for (let i = 0; i < manifests.length; i++) { + // here node refers to manifestNode + const node = {} + node['key'] = collectionNode.key + '-' + i.toString() + node['label'] = manifests[i].label + + // getItemsTitles + const response = await request(manifests[i].id) + if (!response.success) continue + + const data = response.data + node['children'] = getItemsNodes(node['key'], data.sequence) + + collectionNode['children'].push(node) + } + return collectionNode['children'] + } + +function getItemsNodes(parentKey: string, items: Sequence[]) { + const nodes = [] + for (let i = 0; i < items.length ; i++) { + nodes.push({ label: items[i].label, key: parentKey + '-' + i, url: items[i].id }) + } + return nodes +} \ No newline at end of file From c24a933297e5dae138fca186a8ecd04e618f792e Mon Sep 17 00:00:00 2001 From: orlinmalkja Date: Wed, 22 Jan 2025 11:33:44 +0100 Subject: [PATCH 02/13] wip: add tree --- src/components/LocalTreeModal.tsx | 21 +++++++------ src/components/TopBar.tsx | 4 +-- src/components/TreeView.tsx | 37 ----------------------- src/components/tree/CollectionSubtree.tsx | 2 +- src/components/tree/Item.tsx | 7 +++-- src/components/tree/ManifestSubtree.tsx | 4 +-- src/components/ui/popover.tsx | 3 +- src/types.d.ts | 25 +++++++++++++++ src/utils/tree.ts | 32 +++++++++++++++++++- 9 files changed, 78 insertions(+), 57 deletions(-) diff --git a/src/components/LocalTreeModal.tsx b/src/components/LocalTreeModal.tsx index 7928efed..25f4a28a 100644 --- a/src/components/LocalTreeModal.tsx +++ b/src/components/LocalTreeModal.tsx @@ -1,8 +1,9 @@ import { FC, ReactNode, useState } from 'react' -import { Popover, PopoverContent, PopoverTrigger } from '@/components/ui/popover' +import { Popover, PopoverContent, PopoverTrigger, ClosePopover } from '@/components/ui/popover' import TreeView from '@/components/TreeView' + interface LocalTreeProps { TriggerButton: ReactNode } @@ -15,16 +16,10 @@ const LocalTreeModal: FC = ({ TriggerButton }) => { function handleSelectClick(e) { // TODO: check whether input text is provided or an item is clicked - - + // add a class hidden to ref div PopoverContent, in order to close the pop up + console.log('click select button') } - const selectButton = - - return
@@ -35,10 +30,16 @@ const LocalTreeModal: FC = ({ TriggerButton }) => { Enter a collection/manifest Url Or choose: + +
- {selectButton} + handleSelectClick(e)}> + Select +
+
diff --git a/src/components/TopBar.tsx b/src/components/TopBar.tsx index 7c70bd3a..7a91a085 100644 --- a/src/components/TopBar.tsx +++ b/src/components/TopBar.tsx @@ -1,4 +1,4 @@ -import { FC } from 'react' +import { FC, MouseEvent, useState } from 'react' import LocalTreeModal from '@/components/LocalTreeModal' @@ -12,7 +12,7 @@ const TopBar: FC = () => { return
- +
} diff --git a/src/components/TreeView.tsx b/src/components/TreeView.tsx index e99fcf8a..b2dd5530 100644 --- a/src/components/TreeView.tsx +++ b/src/components/TreeView.tsx @@ -1,7 +1,6 @@ import { FC, useEffect, useState } from 'react' import { configStore } from '@/store/ConfigStore.tsx' - import { createTree } from '@/utils/tree' import CollectionSubtree from '@/components/tree/CollectionSubtree' @@ -31,42 +30,6 @@ const Tree: FC = () => { if (loadingTree) return <> - function getCollectionUrl(itemLabel, treeNodes): string | null { - - for (let i = 0; i < treeNodes.length ; i++) { - const collectionNode = treeNodes[i] - for (let j = 0; j < collectionNode.children.length; j++) { - const manifest = collectionNode.children[j] - if (isItemInManifest(manifest, itemLabel)) { - console.log('manifest found', manifest) - return collectionNode.url - } - } - //if (isItemInManifests(collectionNode.children, itemLabel)) return collectionNode.url - } - - return null - } - - function isItemInManifests(manifests, itemLabel) { - for (let i = 0; i < manifests.length ; i++) { - if (isItemInManifest(manifests[i], itemLabel)) return true - } - - return false - } - - function isItemInManifest(manifest, itemLabel) { - const items = manifest.children - for (let i = 0; i < items.length ; i++) { - if (items[i].url === itemLabel) return true - } - - return false - } - - const collectionUrl = getCollectionUrl('https://api.ahiqar.sub.uni-goettingen.de/textapi/ahiqar/arabic-karshuni/3r177/16a/latest/item.json', treeNodes) - console.log('collection Url', collectionUrl) const tree = treeNodes.length > 0 && treeNodes.map((collection, i) => ( diff --git a/src/components/tree/CollectionSubtree.tsx b/src/components/tree/CollectionSubtree.tsx index 3af0638f..f4cba69b 100644 --- a/src/components/tree/CollectionSubtree.tsx +++ b/src/components/tree/CollectionSubtree.tsx @@ -12,7 +12,7 @@ const CollectionSubtree: FC = ({ collectionData }) => { const manifestSubTree = collectionData.children.length > 0 && - collectionData.children.map((manifest, i) => ( + collectionData.children.map((manifest: ManifestNode, i: number) => ( )) diff --git a/src/components/tree/Item.tsx b/src/components/tree/Item.tsx index f1a0b928..ee411e53 100644 --- a/src/components/tree/Item.tsx +++ b/src/components/tree/Item.tsx @@ -1,6 +1,6 @@ -import { FC, useState } from 'react' +import { FC, MouseEvent, useState } from 'react' interface ItemProps { @@ -13,10 +13,11 @@ const ItemTree: FC = ({ label, url }) => { const [active, setActive] = useState(false) const [itemUrl] = useState(url) - function handleClick( - e) { + function handleClick(e: MouseEvent) { e.preventDefault() setActive(prevState => !prevState) + + // // find the collectionUrl based on itemUrl } diff --git a/src/components/tree/ManifestSubtree.tsx b/src/components/tree/ManifestSubtree.tsx index d48e319f..4ae8fa74 100644 --- a/src/components/tree/ManifestSubtree.tsx +++ b/src/components/tree/ManifestSubtree.tsx @@ -11,8 +11,8 @@ const ManifestSubtree: FC = ({ manifestData }) => { const itemsLabels = manifestData.children.length > 0 && - manifestData.children.map((item: Sequence, i) => ( - + manifestData.children.map((item: ItemNode, i: number) => ( + )) return
diff --git a/src/components/ui/popover.tsx b/src/components/ui/popover.tsx index 8bfac19e..3da98894 100644 --- a/src/components/ui/popover.tsx +++ b/src/components/ui/popover.tsx @@ -23,5 +23,6 @@ const PopoverContent = React.forwardRef< /> )) PopoverContent.displayName = PopoverPrimitive.Content.displayName +const ClosePopover = PopoverPrimitive.Close -export { Popover, PopoverTrigger, PopoverContent } +export { Popover, PopoverTrigger, PopoverContent, ClosePopover } diff --git a/src/types.d.ts b/src/types.d.ts index d01d16cd..a5825bcd 100644 --- a/src/types.d.ts +++ b/src/types.d.ts @@ -265,6 +265,31 @@ declare global { } type HttpResponse = SuccessResponse | ErrorResponse + + +// tree types + +interface ItemNode { + key: string, + label: string, + url: string } +interface ManifestNode { + key: string, + label: string, + children: ItemNode[] +} + +interface CollectionNode { + key: number, + title: string, + url: string, + children: ManifestNode +} + +} + + + export {} diff --git a/src/utils/tree.ts b/src/utils/tree.ts index 8600b304..c359f6b3 100644 --- a/src/utils/tree.ts +++ b/src/utils/tree.ts @@ -62,4 +62,34 @@ function getItemsNodes(parentKey: string, items: Sequence[]) { nodes.push({ label: items[i].label, key: parentKey + '-' + i, url: items[i].id }) } return nodes -} \ No newline at end of file +} + + +export function getCollectionUrl(itemUrl: string, treeNodes): string | null { + // find the collection url when clicking an item in local tree + + for (let i = 0; i < treeNodes.length ; i++) { + const collectionNode = treeNodes[i] + + for (let j = 0; j < collectionNode.children.length; j++) { + + const manifest = collectionNode.children[j] + + if (isItemInManifest(manifest, itemUrl)) { + return collectionNode.url + } + } + } + + return null + } + + + function isItemInManifest(manifest, itemUrl: string) { + const items = manifest.children + for (let i = 0; i < items.length ; i++) { + if (items[i].url === itemUrl) return true + } + + return false + } \ No newline at end of file From b43db85e340a9e9b4df01993289ca290c9702ef6 Mon Sep 17 00:00:00 2001 From: orlinmalkja Date: Wed, 22 Jan 2025 14:31:54 +0100 Subject: [PATCH 03/13] wip --- src/components/LocalTreeModal.tsx | 62 ++++++++++++++++++++++++++++--- src/components/TreeView.tsx | 10 +++-- src/components/panel/Panel.tsx | 1 + src/components/tree/Item.tsx | 6 +++ src/store/ConfigStore.tsx | 11 +++++- src/store/DataStore.tsx | 16 +++++++- src/utils/tree.ts | 26 ++++++++++--- 7 files changed, 115 insertions(+), 17 deletions(-) diff --git a/src/components/LocalTreeModal.tsx b/src/components/LocalTreeModal.tsx index 25f4a28a..404b11dc 100644 --- a/src/components/LocalTreeModal.tsx +++ b/src/components/LocalTreeModal.tsx @@ -1,8 +1,16 @@ -import { FC, ReactNode, useState } from 'react' +import { FC, ReactNode, useRef, useState } from 'react' + +import { dataStore } from '@/store/DataStore.tsx' +import { configStore } from '@/store/ConfigStore' import { Popover, PopoverContent, PopoverTrigger, ClosePopover } from '@/components/ui/popover' import TreeView from '@/components/TreeView' +import { getClickedItemIndices } from '@/utils/tree' +import { tree } from '@/utils/icons' + + + interface LocalTreeProps { TriggerButton: ReactNode @@ -12,12 +20,56 @@ const LocalTreeModal: FC = ({ TriggerButton }) => { // TODO: add a [loading, setLoading] => which shows the pop over when the tree has been loaded -> TreeView Component updates the loading of its parent + const clickedItemUrl = dataStore((state) => state.clickedItemUrl) + const treeNodes = dataStore((state) => state.treeNodes) + const addNewPanel = configStore((state) => state.addNewPanel) + + const inputCollectionRef = useRef(null); + const [selectClicked, setSelectClicked] = useState(false) + // check for the state temp variable of newCollection - if we have a new collection there -> if so then we retrieve that value and add a new panel in Configstore + function handleSelectClick(e) { - // TODO: check whether input text is provided or an item is clicked - // add a class hidden to ref div PopoverContent, in order to close the pop up - console.log('click select button') + // TODO: check whether a value is provided for newCollectionUrl in the data store + // if not, then ask the user to provide a value + // if yes, then add the new panel in config store with this new entrypoint AND set the newCollectionUrl as empty value + + let manifestIndex: number | undefined, itemIndex: number | undefined, collectionUrl: string | undefined + + if (clickedItemUrl) { + console.log('clickedItem Url', clickedItemUrl) + const data = getClickedItemIndices(clickedItemUrl, treeNodes) + if (!data) { + console.error('Indices of clicked item could not be found') + return + } + + collectionUrl = data?.collectionUrl + manifestIndex = data?.manifestIndex + itemIndex = data?.itemIndex + addNewPanel({ + entrypoint: { + url: collectionUrl, + type: "collection", + }, + manifestIndex: manifestIndex, + itemIndex: itemIndex + }) + } + + if (inputCollectionRef.current.value !== '') { + console.log('entering the input collectionRef if statement') + console.log('inputCollectionRef.current', inputCollectionRef.current) + collectionUrl = inputCollectionRef.current?.value + addNewPanel({ + entrypoint: { + url: collectionUrl, + type: "collection", + } + } + ) + } } return
@@ -28,7 +80,7 @@ const LocalTreeModal: FC = ({ TriggerButton }) => {
Enter a collection/manifest Url - + Or choose: diff --git a/src/components/TreeView.tsx b/src/components/TreeView.tsx index b2dd5530..fd6fe6d7 100644 --- a/src/components/TreeView.tsx +++ b/src/components/TreeView.tsx @@ -1,5 +1,7 @@ import { FC, useEffect, useState } from 'react' import { configStore } from '@/store/ConfigStore.tsx' +import { dataStore } from '@/store/DataStore.tsx' + import { createTree } from '@/utils/tree' import CollectionSubtree from '@/components/tree/CollectionSubtree' @@ -7,21 +9,23 @@ import CollectionSubtree from '@/components/tree/CollectionSubtree' const Tree: FC = () => { const config = configStore(state => state.config) + const initTreeNodes = dataStore(state => state.initTreeNodes) + - const [treeNodes, setTreeNodes] = useState([]) + const [treeNodes, setTreeNodes] = useState([]) const [loadingTree, setLoadingTreee] = useState(true) useEffect(() => { async function initTree(panels?: PanelConfig[]) { if (!panels) return - const nodes = await createTree(panels) + const nodes = await createTree(panels) setTreeNodes(nodes) setLoadingTreee(false) + initTreeNodes(nodes) } initTree(config.panels) - }, []) console.log('tree', treeNodes) diff --git a/src/components/panel/Panel.tsx b/src/components/panel/Panel.tsx index 9322d267..2950d836 100644 --- a/src/components/panel/Panel.tsx +++ b/src/components/panel/Panel.tsx @@ -28,6 +28,7 @@ const Panel: FC = ({ config }) => { const collectionUrl = config.entrypoint.url const init = async () => { try { + setLoading(true) const collection = await initCollection(collectionUrl) const manifest = await apiRequest(collection.sequence[config.manifestIndex ?? 0].id) diff --git a/src/components/tree/Item.tsx b/src/components/tree/Item.tsx index ee411e53..f45eb186 100644 --- a/src/components/tree/Item.tsx +++ b/src/components/tree/Item.tsx @@ -2,6 +2,7 @@ import { FC, MouseEvent, useState } from 'react' +import { dataStore } from '@/store/DataStore.tsx' interface ItemProps { label: string, @@ -13,8 +14,13 @@ const ItemTree: FC = ({ label, url }) => { const [active, setActive] = useState(false) const [itemUrl] = useState(url) + const setClickedItemUrl = dataStore(state => state.setClickedItemUrl) + function handleClick(e: MouseEvent) { + console.log('clicked item url in ITemTree', itemUrl) e.preventDefault() + if (!active) setClickedItemUrl(itemUrl) + setActive(prevState => !prevState) // diff --git a/src/store/ConfigStore.tsx b/src/store/ConfigStore.tsx index 3cad0d8c..4eb719d0 100644 --- a/src/store/ConfigStore.tsx +++ b/src/store/ConfigStore.tsx @@ -2,12 +2,19 @@ import { create } from 'zustand' interface ConfigStoreType { config: Config, - addCustomConfig: (customConfig: Config) => void + addCustomConfig: (customConfig: Config) => void, + addNewPanel: (newPanel: PanelConfig) => void } -export const configStore = create((set) => ({ +export const configStore = create((set, get) => ({ config: {}, addCustomConfig: (customConfig: Config) => { set({ config: customConfig }) + }, + addNewPanel: (newPanel: PanelConfig) => { + let newConfig = {...get().config} + newConfig.panels?.push(newPanel) + + set({config: newConfig}) } })) diff --git a/src/store/DataStore.tsx b/src/store/DataStore.tsx index 9af7e649..5060abcc 100644 --- a/src/store/DataStore.tsx +++ b/src/store/DataStore.tsx @@ -6,17 +6,31 @@ interface CollectionMap { } interface DataStoreType { - collections: CollectionMap + collections: CollectionMap, + treeNodes: CollectionNode[], + clickedItemUrl: string, initCollection: (url: string) => Promise + initTreeNodes: (newTreeNodes: CollectionNode[]) => void, + setClickedItemUrl: (newUrl: string) => void } export const dataStore = create((set, get) => ({ collections: {}, + treeNodes: [], + clickedItemUrl: '', initCollection: async (url: string) => { const collection = await apiRequest(url) const collections: CollectionMap = { ...get().collections } collections[collection.id] = collection set({ collections }) return collection + }, + initTreeNodes: (newTreeNodes: CollectionNode[]) => { + set({ treeNodes: newTreeNodes}) + }, + + setClickedItemUrl: (newUrl: string) => { + set({clickedItemUrl: newUrl}) } + })) diff --git a/src/utils/tree.ts b/src/utils/tree.ts index c359f6b3..ca31f515 100644 --- a/src/utils/tree.ts +++ b/src/utils/tree.ts @@ -64,32 +64,46 @@ function getItemsNodes(parentKey: string, items: Sequence[]) { return nodes } +interface ClickedItemIndices { + collectionUrl: string, + manifestIndex: number, + itemIndex: number +} -export function getCollectionUrl(itemUrl: string, treeNodes): string | null { +export function getClickedItemIndices(itemUrl: string, treeNodes: CollectionNode[]): ClickedItemIndices | null{ // find the collection url when clicking an item in local tree for (let i = 0; i < treeNodes.length ; i++) { const collectionNode = treeNodes[i] + if (!collectionNode.children || collectionNode.children.length === 0) return null + for (let j = 0; j < collectionNode.children.length; j++) { const manifest = collectionNode.children[j] - if (isItemInManifest(manifest, itemUrl)) { - return collectionNode.url + const itemIndex = getItemIndex(manifest, itemUrl) + if (itemIndex !== null) { + return { + collectionUrl: collectionNode.url, + manifestIndex: j, + itemIndex: itemIndex + } } } } + console.log('----') + return null } - function isItemInManifest(manifest, itemUrl: string) { + function getItemIndex(manifest, itemUrl: string): number | null { const items = manifest.children for (let i = 0; i < items.length ; i++) { - if (items[i].url === itemUrl) return true + if (items[i].url === itemUrl) return i } - return false + return null } \ No newline at end of file From e1282cc03ea2bb1c07d288be693ad0f0c37f7e5b Mon Sep 17 00:00:00 2001 From: orlinmalkja Date: Wed, 22 Jan 2025 14:35:20 +0100 Subject: [PATCH 04/13] wip --- src/components/LocalTreeModal.tsx | 4 ++-- src/types.d.ts | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/components/LocalTreeModal.tsx b/src/components/LocalTreeModal.tsx index 404b11dc..6130a33b 100644 --- a/src/components/LocalTreeModal.tsx +++ b/src/components/LocalTreeModal.tsx @@ -38,7 +38,6 @@ const LocalTreeModal: FC = ({ TriggerButton }) => { let manifestIndex: number | undefined, itemIndex: number | undefined, collectionUrl: string | undefined if (clickedItemUrl) { - console.log('clickedItem Url', clickedItemUrl) const data = getClickedItemIndices(clickedItemUrl, treeNodes) if (!data) { console.error('Indices of clicked item could not be found') @@ -55,7 +54,8 @@ const LocalTreeModal: FC = ({ TriggerButton }) => { }, manifestIndex: manifestIndex, itemIndex: itemIndex - }) + } + ) } if (inputCollectionRef.current.value !== '') { diff --git a/src/types.d.ts b/src/types.d.ts index a5825bcd..66ddcbd9 100644 --- a/src/types.d.ts +++ b/src/types.d.ts @@ -210,9 +210,9 @@ declare global { } interface PanelConfig { entrypoint: Entrypoint - colors: Colors - manifestIndex: number - itemIndex: number + color?: Colors + manifestIndex?: number + itemIndex?: number } type RangeSelector = { From ae7e2a962693bd39e157a1419942e5a02b976ef6 Mon Sep 17 00:00:00 2001 From: orlinmalkja Date: Wed, 22 Jan 2025 15:59:13 +0100 Subject: [PATCH 05/13] wip --- src/components/LocalTreeModal.tsx | 18 ++++++++++++------ src/components/TreeView.tsx | 4 ---- src/components/panel/Panel.tsx | 4 +++- src/components/tree/Item.tsx | 7 ++----- src/store/DataStore.tsx | 12 ++++++++++-- 5 files changed, 27 insertions(+), 18 deletions(-) diff --git a/src/components/LocalTreeModal.tsx b/src/components/LocalTreeModal.tsx index 6130a33b..44e6a2d4 100644 --- a/src/components/LocalTreeModal.tsx +++ b/src/components/LocalTreeModal.tsx @@ -25,10 +25,8 @@ const LocalTreeModal: FC = ({ TriggerButton }) => { const addNewPanel = configStore((state) => state.addNewPanel) const inputCollectionRef = useRef(null); - - const [selectClicked, setSelectClicked] = useState(false) - - // check for the state temp variable of newCollection - if we have a new collection there -> if so then we retrieve that value and add a new panel in Configstore + const selectButtonRef = useRef(null) + function handleSelectClick(e) { // TODO: check whether a value is provided for newCollectionUrl in the data store @@ -37,6 +35,11 @@ const LocalTreeModal: FC = ({ TriggerButton }) => { let manifestIndex: number | undefined, itemIndex: number | undefined, collectionUrl: string | undefined + if (!clickedItemUrl && inputCollectionRef.current.value === '') { + selectButtonRef.current.disabled = true + return + } + if (clickedItemUrl) { const data = getClickedItemIndices(clickedItemUrl, treeNodes) if (!data) { @@ -54,8 +57,9 @@ const LocalTreeModal: FC = ({ TriggerButton }) => { }, manifestIndex: manifestIndex, itemIndex: itemIndex - } + } ) + return } if (inputCollectionRef.current.value !== '') { @@ -67,9 +71,11 @@ const LocalTreeModal: FC = ({ TriggerButton }) => { url: collectionUrl, type: "collection", } - } + } ) + return } + } return
diff --git a/src/components/TreeView.tsx b/src/components/TreeView.tsx index fd6fe6d7..90c59030 100644 --- a/src/components/TreeView.tsx +++ b/src/components/TreeView.tsx @@ -28,10 +28,6 @@ const Tree: FC = () => { initTree(config.panels) }, []) - console.log('tree', treeNodes) - - - if (loadingTree) return <> const tree = diff --git a/src/components/panel/Panel.tsx b/src/components/panel/Panel.tsx index 2950d836..f444454b 100644 --- a/src/components/panel/Panel.tsx +++ b/src/components/panel/Panel.tsx @@ -17,6 +17,7 @@ interface Props { const Panel: FC = ({ config }) => { const initCollection = dataStore(state => state.initCollection) + const getCollection = dataStore(state => state.getCollection) const addPanelContent = contentStore((state) => state.addPanelContent) const [error, setError] = useState(false) @@ -30,7 +31,8 @@ const Panel: FC = ({ config }) => { try { setLoading(true) - const collection = await initCollection(collectionUrl) + const collection = await getCollection(collectionUrl) + const manifest = await apiRequest(collection.sequence[config.manifestIndex ?? 0].id) const item = await apiRequest(manifest.sequence[config.itemIndex ?? 0].id) const contentTypes: string[] = getContentTypes(item.content) diff --git a/src/components/tree/Item.tsx b/src/components/tree/Item.tsx index f45eb186..0081c74e 100644 --- a/src/components/tree/Item.tsx +++ b/src/components/tree/Item.tsx @@ -15,20 +15,17 @@ const ItemTree: FC = ({ label, url }) => { const [itemUrl] = useState(url) const setClickedItemUrl = dataStore(state => state.setClickedItemUrl) + const clickedItemUrl = dataStore(state => state.clickedItemUrl) function handleClick(e: MouseEvent) { - console.log('clicked item url in ITemTree', itemUrl) e.preventDefault() if (!active) setClickedItemUrl(itemUrl) setActive(prevState => !prevState) - - // - // find the collectionUrl based on itemUrl } return
- diff --git a/src/store/DataStore.tsx b/src/store/DataStore.tsx index 5060abcc..62ef08d9 100644 --- a/src/store/DataStore.tsx +++ b/src/store/DataStore.tsx @@ -11,7 +11,9 @@ interface DataStoreType { clickedItemUrl: string, initCollection: (url: string) => Promise initTreeNodes: (newTreeNodes: CollectionNode[]) => void, - setClickedItemUrl: (newUrl: string) => void + setClickedItemUrl: (newUrl: string) => void, + getCollection: (collectionUrl: string) => Promise + } export const dataStore = create((set, get) => ({ @@ -31,6 +33,12 @@ export const dataStore = create((set, get) => ({ setClickedItemUrl: (newUrl: string) => { set({clickedItemUrl: newUrl}) - } + }, + async getCollection(collectionUrl: string): Promise { + if (collectionUrl in get().collections) return get().collections[collectionUrl] + + const collection = await get().initCollection(collectionUrl) + return collection + } })) From fa3d77dbbacf40e06a079037ca51a02fbc81efda Mon Sep 17 00:00:00 2001 From: orlinmalkja Date: Wed, 22 Jan 2025 16:33:59 +0100 Subject: [PATCH 06/13] wip: display an error message when no input is given to open a new Panel --- src/components/LocalTreeModal.tsx | 31 ++++++++++++++++++------------- 1 file changed, 18 insertions(+), 13 deletions(-) diff --git a/src/components/LocalTreeModal.tsx b/src/components/LocalTreeModal.tsx index 44e6a2d4..be479e65 100644 --- a/src/components/LocalTreeModal.tsx +++ b/src/components/LocalTreeModal.tsx @@ -7,9 +7,6 @@ import { Popover, PopoverContent, PopoverTrigger, ClosePopover } from '@/compone import TreeView from '@/components/TreeView' import { getClickedItemIndices } from '@/utils/tree' -import { tree } from '@/utils/icons' - - interface LocalTreeProps { @@ -21,25 +18,28 @@ const LocalTreeModal: FC = ({ TriggerButton }) => { // TODO: add a [loading, setLoading] => which shows the pop over when the tree has been loaded -> TreeView Component updates the loading of its parent const clickedItemUrl = dataStore((state) => state.clickedItemUrl) + const setClickedItemUrl = dataStore(state => state.setClickedItemUrl) const treeNodes = dataStore((state) => state.treeNodes) const addNewPanel = configStore((state) => state.addNewPanel) + const [inputGiven, setInputGiven] = useState(false) + + const [clickedButton, setClickedButton] = useState(false) const inputCollectionRef = useRef(null); - const selectButtonRef = useRef(null) - + function handleSelectClick(e) { - // TODO: check whether a value is provided for newCollectionUrl in the data store - // if not, then ask the user to provide a value - // if yes, then add the new panel in config store with this new entrypoint AND set the newCollectionUrl as empty value let manifestIndex: number | undefined, itemIndex: number | undefined, collectionUrl: string | undefined if (!clickedItemUrl && inputCollectionRef.current.value === '') { - selectButtonRef.current.disabled = true + setClickedButton(true) + e.preventDefault(); return } + setInputGiven(true) + if (clickedItemUrl) { const data = getClickedItemIndices(clickedItemUrl, treeNodes) if (!data) { @@ -50,6 +50,7 @@ const LocalTreeModal: FC = ({ TriggerButton }) => { collectionUrl = data?.collectionUrl manifestIndex = data?.manifestIndex itemIndex = data?.itemIndex + addNewPanel({ entrypoint: { url: collectionUrl, @@ -59,13 +60,11 @@ const LocalTreeModal: FC = ({ TriggerButton }) => { itemIndex: itemIndex } ) - return } if (inputCollectionRef.current.value !== '') { - console.log('entering the input collectionRef if statement') - console.log('inputCollectionRef.current', inputCollectionRef.current) collectionUrl = inputCollectionRef.current?.value + addNewPanel({ entrypoint: { url: collectionUrl, @@ -73,9 +72,14 @@ const LocalTreeModal: FC = ({ TriggerButton }) => { } } ) - return } + setClickedItemUrl('') + setInputGiven(false) + setClickedButton(false) + + return + } return
@@ -85,6 +89,7 @@ const LocalTreeModal: FC = ({ TriggerButton }) => {
+
Please do provide a way to open a new collection
Enter a collection/manifest Url Or choose: From a4ee00581cdb65dbecde243307c7aeb10285d550 Mon Sep 17 00:00:00 2001 From: orlinmalkja Date: Wed, 22 Jan 2025 16:39:48 +0100 Subject: [PATCH 07/13] wip --- src/components/LocalTreeModal.tsx | 6 +++--- src/utils/tree.ts | 7 ++----- 2 files changed, 5 insertions(+), 8 deletions(-) diff --git a/src/components/LocalTreeModal.tsx b/src/components/LocalTreeModal.tsx index be479e65..8af21cc1 100644 --- a/src/components/LocalTreeModal.tsx +++ b/src/components/LocalTreeModal.tsx @@ -6,7 +6,7 @@ import { configStore } from '@/store/ConfigStore' import { Popover, PopoverContent, PopoverTrigger, ClosePopover } from '@/components/ui/popover' import TreeView from '@/components/TreeView' -import { getClickedItemIndices } from '@/utils/tree' +import { getItemIndices } from '@/utils/tree' interface LocalTreeProps { @@ -41,7 +41,7 @@ const LocalTreeModal: FC = ({ TriggerButton }) => { setInputGiven(true) if (clickedItemUrl) { - const data = getClickedItemIndices(clickedItemUrl, treeNodes) + const data = getItemIndices(clickedItemUrl, treeNodes) if (!data) { console.error('Indices of clicked item could not be found') return @@ -77,7 +77,7 @@ const LocalTreeModal: FC = ({ TriggerButton }) => { setClickedItemUrl('') setInputGiven(false) setClickedButton(false) - + return } diff --git a/src/utils/tree.ts b/src/utils/tree.ts index ca31f515..80f3266e 100644 --- a/src/utils/tree.ts +++ b/src/utils/tree.ts @@ -64,14 +64,13 @@ function getItemsNodes(parentKey: string, items: Sequence[]) { return nodes } -interface ClickedItemIndices { +interface ItemIndices { collectionUrl: string, manifestIndex: number, itemIndex: number } -export function getClickedItemIndices(itemUrl: string, treeNodes: CollectionNode[]): ClickedItemIndices | null{ - // find the collection url when clicking an item in local tree +export function getItemIndices(itemUrl: string, treeNodes: CollectionNode[]): ItemIndices | null{ for (let i = 0; i < treeNodes.length ; i++) { const collectionNode = treeNodes[i] @@ -93,8 +92,6 @@ export function getClickedItemIndices(itemUrl: string, treeNodes: CollectionNode } } - console.log('----') - return null } From e73f33daea6619d687f10f26c237f06a49f31ad8 Mon Sep 17 00:00:00 2001 From: orlinmalkja Date: Wed, 22 Jan 2025 16:48:05 +0100 Subject: [PATCH 08/13] wip --- src/utils/tree.ts | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/src/utils/tree.ts b/src/utils/tree.ts index 80f3266e..d097e3ae 100644 --- a/src/utils/tree.ts +++ b/src/utils/tree.ts @@ -81,6 +81,8 @@ export function getItemIndices(itemUrl: string, treeNodes: CollectionNode[]): It const manifest = collectionNode.children[j] + if (!manifest.children || manifest.children.length === 0) continue + const itemIndex = getItemIndex(manifest, itemUrl) if (itemIndex !== null) { return { @@ -96,11 +98,6 @@ export function getItemIndices(itemUrl: string, treeNodes: CollectionNode[]): It } - function getItemIndex(manifest, itemUrl: string): number | null { - const items = manifest.children - for (let i = 0; i < items.length ; i++) { - if (items[i].url === itemUrl) return i - } - - return null + function getItemIndex(manifest, itemUrl: string): number { + return manifest.children.findIndex((item: ItemNode) => item.url === itemUrl) } \ No newline at end of file From f9792f3005802e89b816298f679f83f4229ab97c Mon Sep 17 00:00:00 2001 From: orlinmalkja Date: Wed, 22 Jan 2025 16:50:07 +0100 Subject: [PATCH 09/13] wip --- src/utils/tree.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/utils/tree.ts b/src/utils/tree.ts index d097e3ae..7969493a 100644 --- a/src/utils/tree.ts +++ b/src/utils/tree.ts @@ -84,7 +84,7 @@ export function getItemIndices(itemUrl: string, treeNodes: CollectionNode[]): It if (!manifest.children || manifest.children.length === 0) continue const itemIndex = getItemIndex(manifest, itemUrl) - if (itemIndex !== null) { + if (itemIndex !== -1) { return { collectionUrl: collectionNode.url, manifestIndex: j, From 351c2bd2bb90d354b14b4ca97376978f9ecb64bb Mon Sep 17 00:00:00 2001 From: orlinmalkja Date: Wed, 22 Jan 2025 16:56:19 +0100 Subject: [PATCH 10/13] wip --- src/utils/tree.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/utils/tree.ts b/src/utils/tree.ts index 7969493a..be8437c9 100644 --- a/src/utils/tree.ts +++ b/src/utils/tree.ts @@ -4,7 +4,7 @@ import { request } from '@/utils/http' export async function createTree(panels: PanelConfig[]) { if (!panels || panels.length === 0) return [] - const nodes = [] + const nodes: CollectionNode[] = [] for (let i = 0; i< panels.length; i++) { const collectionNode = await createCollectionNode(panels[i].entrypoint.url, i).then((node) => { @@ -18,7 +18,7 @@ export async function createTree(panels: PanelConfig[]) { async function createCollectionNode(url: string, key: number) { - const node = {} + const node: CollectionNode = {} const response = await request(url) if (!response.success) return node const collectionTitle = response.data.title[0].title @@ -40,7 +40,7 @@ async function getManifestNodes(parentNode, manifests) { for (let i = 0; i < manifests.length; i++) { // here node refers to manifestNode - const node = {} + const node: ManifestNode = {} node['key'] = collectionNode.key + '-' + i.toString() node['label'] = manifests[i].label From 881ed6f3c2b4c904d7560cb596dc990ef0eac614 Mon Sep 17 00:00:00 2001 From: orlinmalkja Date: Wed, 22 Jan 2025 17:02:34 +0100 Subject: [PATCH 11/13] wip --- src/utils/tree.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/utils/tree.ts b/src/utils/tree.ts index be8437c9..8d09bcdb 100644 --- a/src/utils/tree.ts +++ b/src/utils/tree.ts @@ -7,7 +7,7 @@ export async function createTree(panels: PanelConfig[]) { const nodes: CollectionNode[] = [] for (let i = 0; i< panels.length; i++) { - const collectionNode = await createCollectionNode(panels[i].entrypoint.url, i).then((node) => { + await createCollectionNode(panels[i].entrypoint.url, i).then((node) => { nodes.push(node) }) } @@ -98,6 +98,6 @@ export function getItemIndices(itemUrl: string, treeNodes: CollectionNode[]): It } - function getItemIndex(manifest, itemUrl: string): number { + function getItemIndex(manifest: ManifestNode, itemUrl: string): number { return manifest.children.findIndex((item: ItemNode) => item.url === itemUrl) } \ No newline at end of file From 8620626538f92f183bef9871e15c018073d99416 Mon Sep 17 00:00:00 2001 From: orlinmalkja Date: Wed, 22 Jan 2025 17:13:47 +0100 Subject: [PATCH 12/13] wip --- src/components/tree/CollectionSubtree.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/tree/CollectionSubtree.tsx b/src/components/tree/CollectionSubtree.tsx index f4cba69b..2cd06f2e 100644 --- a/src/components/tree/CollectionSubtree.tsx +++ b/src/components/tree/CollectionSubtree.tsx @@ -10,7 +10,7 @@ interface CollectionSubtreeProps { const CollectionSubtree: FC = ({ collectionData }) => { - const manifestSubTree = collectionData.children.length > 0 + const collectionSubTree = collectionData.children.length > 0 && collectionData.children.map((manifest: ManifestNode, i: number) => ( @@ -20,7 +20,7 @@ const CollectionSubtree: FC = ({ collectionData }) => { {collectionData.title}
- { manifestSubTree} + { collectionSubTree}
} From 97fe4df1b5cbb86a857f085fb14d98f189394d2a Mon Sep 17 00:00:00 2001 From: orlinmalkja Date: Wed, 22 Jan 2025 17:19:54 +0100 Subject: [PATCH 13/13] wip --- src/components/LocalTreeModal.tsx | 1 + src/components/TopBar.tsx | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/components/LocalTreeModal.tsx b/src/components/LocalTreeModal.tsx index 8af21cc1..52a5e140 100644 --- a/src/components/LocalTreeModal.tsx +++ b/src/components/LocalTreeModal.tsx @@ -74,6 +74,7 @@ const LocalTreeModal: FC = ({ TriggerButton }) => { ) } + // lines below serve mainly for showing the error message. Error message appears when a user does not provide input for opening a new a collection/panel setClickedItemUrl('') setInputGiven(false) setClickedButton(false) diff --git a/src/components/TopBar.tsx b/src/components/TopBar.tsx index 7a91a085..f4e200fb 100644 --- a/src/components/TopBar.tsx +++ b/src/components/TopBar.tsx @@ -1,4 +1,4 @@ -import { FC, MouseEvent, useState } from 'react' +import { FC } from 'react' import LocalTreeModal from '@/components/LocalTreeModal'