diff --git a/libs/shared/lib/vis/visualizations/tablevis/components/Table.tsx b/libs/shared/lib/vis/visualizations/tablevis/components/Table.tsx index ef6915a929ce916b1a502f7223a9ec35ae1700b2..a0c3a98520e9ac4e4e0ba1c67bde3467cfd95a79 100644 --- a/libs/shared/lib/vis/visualizations/tablevis/components/Table.tsx +++ b/libs/shared/lib/vis/visualizations/tablevis/components/Table.tsx @@ -147,7 +147,6 @@ export const Table = ({ data, itemsPerPage, showBarPlot, showAttributes, selecte data: [], numElements: 0, }; - if ( firstRowData.type[dataColumn] === 'string' || firstRowData.type[dataColumn] === 'date' || @@ -201,18 +200,31 @@ export const Table = ({ data, itemsPerPage, showBarPlot, showAttributes, selecte // number: float and int } else if (firstRowData.type[dataColumn] === 'int' || firstRowData.type[dataColumn] === 'float') { - categoryCounts = data.map((obj) => ({ - category: 'placeholder', // add something - count: obj.attribute[dataColumn] as number, // fill values of data - })); + const isArrayOfFloats = + Array.isArray(data[0].attribute[dataColumn]) && data[0].attribute[dataColumn].every((val) => typeof val === 'number'); - newData2Render.numElements = categoryCounts.length; - newData2Render.data = categoryCounts; - newData2Render.showBarPlot = true; + if (!isArrayOfFloats) { + categoryCounts = data.map((obj) => ({ + category: 'placeholder', // add something + count: obj.attribute[dataColumn] as number, // fill values of data + })); + + newData2Render.numElements = categoryCounts.length; + newData2Render.data = categoryCounts; + newData2Render.showBarPlot = true; + } else { + const groupedData = group(data, (d) => (d.attribute[dataColumn] as any)?.[0]); + categoryCounts = Array.from(groupedData, ([category, items]) => ({ + category: category as string, + count: items.length, + })); + + newData2Render.numElements = categoryCounts.length; + newData2Render.showBarPlot = false; + } } else { // there is also array type, when considering labels const groupedData = group(data, (d) => (d.attribute[dataColumn] as any)?.[0]); - categoryCounts = Array.from(groupedData, ([category, items]) => ({ category: category as string, count: items.length, @@ -224,7 +236,6 @@ export const Table = ({ data, itemsPerPage, showBarPlot, showAttributes, selecte return newData2Render; }); - const _data2RenderSorted = _data2Render.sort((a, b) => a.name.localeCompare(b.name)); setData2Render(_data2RenderSorted); }, [currentPage, data, sortedData, selectedEntity, maxBarsCount, showAttributes]); diff --git a/libs/shared/lib/vis/visualizations/tablevis/tablevis.tsx b/libs/shared/lib/vis/visualizations/tablevis/tablevis.tsx index 7a1485bc42e8d8d6ee93ce4a9af288e7ae1818bb..761d3a51798624a3167aea3287abfb0a13b50964 100644 --- a/libs/shared/lib/vis/visualizations/tablevis/tablevis.tsx +++ b/libs/shared/lib/vis/visualizations/tablevis/tablevis.tsx @@ -1,11 +1,11 @@ import { Accordion, AccordionBody, AccordionHead, AccordionItem } from '@graphpolaris/shared/lib/components/accordion'; import { Button } from '@graphpolaris/shared/lib/components/buttons'; import { Input } from '@graphpolaris/shared/lib/components/inputs'; -import { EntityPill } from '@graphpolaris/shared/lib/components/pills/Pill'; +import { EntityPill, RelationPill } from '@graphpolaris/shared/lib/components/pills/Pill'; import { useSearchResultData } from '@graphpolaris/shared/lib/data-access'; import { SettingsContainer } from '@graphpolaris/shared/lib/vis/components/config'; import html2canvas from 'html2canvas'; -import React, { forwardRef, useEffect, useImperativeHandle, useMemo, useRef } from 'react'; +import React, { forwardRef, useEffect, useImperativeHandle, useMemo, useRef, useState } from 'react'; import { VISComponentType, VisualizationPropTypes, VisualizationSettingsPropTypes } from '../../common'; import { AugmentedNodeAttributes, Table } from './components/Table'; @@ -19,7 +19,7 @@ export type TableProps = { showBarplot: boolean; itemsPerPage: number; displayAttributes: string[]; - displayEntity: string; + selectedEntity: string; maxBarsCount: number; }; @@ -29,24 +29,14 @@ const settings: TableProps = { itemsPerPage: 10, showBarplot: true, displayAttributes: [], - displayEntity: '', + selectedEntity: '', maxBarsCount: 10, }; export const TableVis = forwardRef<TableVisHandle, VisualizationPropTypes<TableProps>>( - ({ data, schema, settings, updateSettings, graphMetadata }, refExternal) => { + ({ data, schema, settings, graphMetadata }, refExternal) => { const searchResults = useSearchResultData(); const ref = useRef<HTMLDivElement>(null); - useEffect(() => { - if (graphMetadata != undefined && settings.displayEntity === '') { - if (!graphMetadata.nodes.labels.includes(settings.displayEntity)) { - updateSettings({ - displayEntity: graphMetadata.nodes.labels[0], - displayAttributes: Object.keys(graphMetadata.nodes.types[graphMetadata.nodes.labels[0]].attributes), - }); - } - } - }, [graphMetadata, data, settings]); const displayAttributesSorted = useMemo<string[]>(() => { if (settings.displayAttributes.length != 0) { @@ -56,12 +46,17 @@ export const TableVis = forwardRef<TableVisHandle, VisualizationPropTypes<TableP }, [settings.displayAttributes]); const attributesArray = useMemo<AugmentedNodeAttributes[]>(() => { - //const similiarityThreshold = 0.9; let displayAttributesSorted: string[]; - displayAttributesSorted = [...settings.displayAttributes].sort((a, b) => a.localeCompare(b)); - const dataNodes = (searchResults?.nodes?.length ?? 0) === 0 ? data.nodes : searchResults.nodes; + const nodesLabels = graphMetadata.nodes.labels; + + let dataNodes = []; + if (nodesLabels.includes(settings.selectedEntity)) { + dataNodes = (searchResults?.nodes?.length ?? 0) === 0 ? data.nodes : searchResults.nodes; + } else { + dataNodes = data.edges; + } return ( dataNodes @@ -74,7 +69,7 @@ export const TableVis = forwardRef<TableVisHandle, VisualizationPropTypes<TableP const idParts = node._id.split('/'); labelNode = idParts[0]; } - return labelNode === settings.displayEntity; + return labelNode === settings.selectedEntity; }) ///.filter((obj) => obj.similarity === undefined || obj.similarity >= similiarityThreshold) .map((node) => { @@ -111,7 +106,7 @@ export const TableVis = forwardRef<TableVisHandle, VisualizationPropTypes<TableP } }) ); - }, [data.nodes, settings.displayEntity, settings.displayAttributes, searchResults]); + }, [data.nodes, settings.selectedEntity, settings.displayAttributes, searchResults]); const exportImageInternal = () => { if (ref.current) { @@ -140,7 +135,7 @@ export const TableVis = forwardRef<TableVisHandle, VisualizationPropTypes<TableP itemsPerPage={settings.itemsPerPage} showBarPlot={settings.showBarplot} showAttributes={displayAttributesSorted} - selectedEntity={settings.displayEntity} + selectedEntity={settings.selectedEntity} maxBarsCount={settings.maxBarsCount} /> )} @@ -150,27 +145,37 @@ export const TableVis = forwardRef<TableVisHandle, VisualizationPropTypes<TableP ); const TableSettings = ({ settings, graphMetadata, updateSettings }: VisualizationSettingsPropTypes<TableProps>) => { + const [attributes, setAttributes] = useState<string[]>([]); + useEffect(() => { - if (graphMetadata && graphMetadata.nodes && graphMetadata.nodes.labels.length > 0 && settings.displayEntity === '') { - updateSettings({ displayEntity: graphMetadata.nodes.labels[0] }); + if (settings.selectedEntity === '' && graphMetadata && graphMetadata.nodes && graphMetadata.nodes.labels.length > 0) { + const firstEntity = graphMetadata.nodes.labels[0]; + const attributesFirstEntity = Object.keys(graphMetadata.nodes.types[firstEntity].attributes); + updateSettings({ selectedEntity: graphMetadata.nodes.labels[0], displayAttributes: attributesFirstEntity }); + setAttributes(attributesFirstEntity); } }, [graphMetadata]); - const selectedNodeAttributes = useMemo(() => { - if (settings.displayEntity) { - const nodeType = graphMetadata.nodes.types[settings.displayEntity]; - if (nodeType && nodeType.attributes) { - return Object.keys(nodeType.attributes).sort((a, b) => a.localeCompare(b)); + useEffect(() => { + if ( + graphMetadata && + graphMetadata.nodes && + graphMetadata.nodes.labels.length > 0 && + settings.selectedEntity != '' && + settings.displayAttributes.length != 0 + ) { + const nodesLabels = graphMetadata.nodes.labels; + let attributesFirstEntity = []; + if (nodesLabels.includes(settings.selectedEntity)) { + attributesFirstEntity = Object.keys(graphMetadata.nodes.types[settings.selectedEntity].attributes); + } else { + attributesFirstEntity = Object.keys(graphMetadata.edges.types[settings.selectedEntity].attributes); } - } - return []; - }, [settings.displayEntity, graphMetadata]); + setAttributes(attributesFirstEntity); - useEffect(() => { - if (graphMetadata && graphMetadata.nodes && graphMetadata.nodes.labels.length > 0 && settings.displayAttributes.length === 0) { - updateSettings({ displayAttributes: selectedNodeAttributes }); + updateSettings({ displayAttributes: attributesFirstEntity }); } - }, [selectedNodeAttributes, graphMetadata]); + }, [settings.selectedEntity, graphMetadata]); return ( <SettingsContainer> @@ -178,18 +183,29 @@ const TableSettings = ({ settings, graphMetadata, updateSettings }: Visualizatio <Input className="w-full text-justify justify-center" type="dropdown" - value={settings.displayEntity} - options={graphMetadata.nodes.labels} - onChange={(val) => updateSettings({ displayEntity: val as string })} + value={settings.selectedEntity} + options={[...graphMetadata.nodes.labels, ...graphMetadata.edges.labels]} + onChange={(val) => updateSettings({ selectedEntity: val as string })} overrideRender={ - <EntityPill - title={ - <div className="flex flex-row justify-between items-center cursor-pointer"> - <span>{settings.displayEntity || ''}</span> - <Button variantType="secondary" variant="ghost" size="2xs" iconComponent="icon-[ic--baseline-arrow-drop-down]" /> - </div> - } - /> + graphMetadata.nodes.labels.includes(settings.selectedEntity) ? ( + <EntityPill + title={ + <div className="flex flex-row justify-between items-center cursor-pointer"> + <span>{settings.selectedEntity || ''}</span> + <Button variantType="secondary" variant="ghost" size="2xs" iconComponent="icon-[ic--baseline-arrow-drop-down]" /> + </div> + } + /> + ) : ( + <RelationPill + title={ + <div className="flex flex-row justify-between items-center cursor-pointer"> + <span>{settings.selectedEntity || ''}</span> + <Button variantType="secondary" variant="ghost" size="2xs" iconComponent="icon-[ic--baseline-arrow-drop-down]" /> + </div> + } + /> + ) } ></Input> <div className="my-2"> @@ -226,7 +242,7 @@ const TableSettings = ({ settings, graphMetadata, updateSettings }: Visualizatio <Input type="checkbox" value={settings.displayAttributes} - options={selectedNodeAttributes} + options={attributes} onChange={(val: string[] | string) => { const updatedVal = Array.isArray(val) ? val : [val]; updateSettings({ displayAttributes: updatedVal });