From c19a23bcdfa43e6c1e5d0d6e62fe532a45934499 Mon Sep 17 00:00:00 2001 From: Marcos Pieras <pieras.marcos@gmail.com> Date: Mon, 13 May 2024 12:45:18 +0000 Subject: [PATCH] fix(vis): flickering on hyperedge of paohvis --- .../paohvis/components/CustomLine.tsx | 4 +- .../paohvis/components/HyperRangeBlock.tsx | 16 +- .../paohvis/components/RowLabels.tsx | 6 +- .../vis/visualizations/paohvis/paohvis.tsx | 224 +++++++++++------- .../lib/vis/visualizations/paohvis/types.ts | 6 + .../vis/visualizations/tablevis/tablevis.tsx | 87 ++++--- 6 files changed, 212 insertions(+), 131 deletions(-) diff --git a/libs/shared/lib/vis/visualizations/paohvis/components/CustomLine.tsx b/libs/shared/lib/vis/visualizations/paohvis/components/CustomLine.tsx index df2a4a5e0..d6cd9e5c7 100644 --- a/libs/shared/lib/vis/visualizations/paohvis/components/CustomLine.tsx +++ b/libs/shared/lib/vis/visualizations/paohvis/components/CustomLine.tsx @@ -9,8 +9,6 @@ interface LineProps { fill: string; } -const CustomLine: React.FC<LineProps> = (props) => { +export const CustomLine: React.FC<LineProps> = (props) => { return <line x1={props.x1} x2={props.x2} y1={props.y1} y2={props.y2} strokeWidth={props.strokeWidth} fill={props.fill} />; }; - -export default CustomLine; diff --git a/libs/shared/lib/vis/visualizations/paohvis/components/HyperRangeBlock.tsx b/libs/shared/lib/vis/visualizations/paohvis/components/HyperRangeBlock.tsx index 0b23a88c0..a313a2950 100644 --- a/libs/shared/lib/vis/visualizations/paohvis/components/HyperRangeBlock.tsx +++ b/libs/shared/lib/vis/visualizations/paohvis/components/HyperRangeBlock.tsx @@ -1,12 +1,12 @@ import React, { useEffect, useState, useMemo } from 'react'; -import CustomLine from './CustomLine'; -import { intersectionElements } from '../utils/utils'; -import { PaohvisDataPaginated, RowInformation } from '../types'; +import { CustomLine } from './CustomLine'; +import { PaohvisDataPaginated, RowInformation, LinesHyperedges } from '../types'; import { ArrowDownward, ArrowUpward, Sort } from '@mui/icons-material'; import { select } from 'd3'; interface HyperEdgeRangesBlockProps { dataModel: PaohvisDataPaginated; + dataLinesHyperedges: LinesHyperedges[]; rowHeight: number; yOffset: number; rowLabelColumnWidth: number; @@ -29,6 +29,7 @@ interface HyperEdgeRangesBlockProps { export const HyperEdgeRangesBlock: React.FC<HyperEdgeRangesBlockProps> = ({ dataModel, + dataLinesHyperedges, rowHeight, yOffset, rowLabelColumnWidth, @@ -45,6 +46,7 @@ export const HyperEdgeRangesBlock: React.FC<HyperEdgeRangesBlockProps> = ({ onMouseLeaveHyperEdge, handleClickHeaderSorting, }) => { + /* const [linePositions, setLinePositions] = useState<{ y0: number; y1: number; valid: boolean }[]>([]); useEffect(() => { @@ -53,11 +55,13 @@ export const HyperEdgeRangesBlock: React.FC<HyperEdgeRangesBlockProps> = ({ //console.log(hyperEdgeRange); return intersectionElements([currentPageRows.startIndexRow, currentPageRows.endIndexRow], hyperEdgeRange.hyperEdges.indices); }); + console.log('internal: ', newLinePositions); setLinePositions(newLinePositions); } else { setLinePositions([]); } }, [currentPageRows, dataModel]); + */ const accumulatedWidthHeaders = useMemo(() => { const accumulatedWidths: number[] = [0]; @@ -281,13 +285,13 @@ export const HyperEdgeRangesBlock: React.FC<HyperEdgeRangesBlockProps> = ({ key={'groupBlockExtra-' + indexHyperEdgeRange} transform={`translate(${rowLabelColumnWidth + indexHyperEdgeRange * rowHeight + 0.5 * rowHeight},${yOffset + 0.5 * rowHeight})`} > - {currentPageRows && linePositions[indexHyperEdgeRange]?.valid && ( + {currentPageRows && dataLinesHyperedges[indexHyperEdgeRange]?.valid && ( <line key={'hyperRangeBlockLine line_' + indexHyperEdgeRange} x1={0} - y1={(linePositions[indexHyperEdgeRange].y0 - currentPageRows.startIndexRow) * rowHeight} + y1={(dataLinesHyperedges[indexHyperEdgeRange].y0 - currentPageRows.startIndexRow) * rowHeight} x2={0} - y2={(linePositions[indexHyperEdgeRange].y1 - currentPageRows.startIndexRow) * rowHeight} + y2={(dataLinesHyperedges[indexHyperEdgeRange].y1 - currentPageRows.startIndexRow) * rowHeight} strokeWidth={1.5} stroke="hsl(var(--clr-sec--800))" /> diff --git a/libs/shared/lib/vis/visualizations/paohvis/components/RowLabels.tsx b/libs/shared/lib/vis/visualizations/paohvis/components/RowLabels.tsx index 1fae1e321..425472216 100644 --- a/libs/shared/lib/vis/visualizations/paohvis/components/RowLabels.tsx +++ b/libs/shared/lib/vis/visualizations/paohvis/components/RowLabels.tsx @@ -1,5 +1,5 @@ import React, { useEffect, useState } from 'react'; -import CustomLine from './CustomLine'; +import { CustomLine } from './CustomLine'; import { RowInformation } from '../types'; import { ArrowDownward, ArrowUpward, Sort } from '@mui/icons-material'; import { select, selectAll } from 'd3'; @@ -18,7 +18,7 @@ interface RowLabelsProps { handleClickHeaderSorting: (event: React.MouseEvent<SVGGElement, MouseEvent>) => void; } -export const RowLabels: React.FC<RowLabelsProps> = ({ +export const RowLabels = ({ dataRows, rowHeight, yOffset, @@ -30,7 +30,7 @@ export const RowLabels: React.FC<RowLabelsProps> = ({ onMouseEnterRowLabels, onMouseLeaveRowLabels, handleClickHeaderSorting, -}) => { +}: RowLabelsProps) => { const accumulatedWidthHeaders = [0]; let sum = 0; dataRows.forEach((row, index) => { diff --git a/libs/shared/lib/vis/visualizations/paohvis/paohvis.tsx b/libs/shared/lib/vis/visualizations/paohvis/paohvis.tsx index 0e321f471..48b70b406 100644 --- a/libs/shared/lib/vis/visualizations/paohvis/paohvis.tsx +++ b/libs/shared/lib/vis/visualizations/paohvis/paohvis.tsx @@ -1,12 +1,12 @@ import React, { useEffect, useRef, useState, useMemo } from 'react'; -import { PaohvisDataPaginated, RowInformation } from './types'; +import { PaohvisDataPaginated, RowInformation, LinesHyperedges } from './types'; import { parseQueryResult } from './utils/dataProcessing'; import { GraphMetadata } from '@graphpolaris/shared/lib/data-access/statistics'; import { RowLabels } from './components/RowLabels'; import { HyperEdgeRangesBlock } from './components/HyperRangeBlock'; -import { sortRowInformation, sortIndices } from './utils/utils'; +import { sortRowInformation, sortIndices, intersectionElements } from './utils/utils'; import { select, selectAll } from 'd3'; import { SettingsContainer } from '@graphpolaris/shared/lib/vis/components/config'; @@ -17,6 +17,7 @@ import { EntityPill } from '@graphpolaris/shared/lib/components/pills/Pill'; import { ArrowDropDown } from '@mui/icons-material'; import { cloneDeep } from 'lodash-es'; import { useImmer } from 'use-immer'; +import PillDropdown, { PillDropdownProps } from '@graphpolaris/shared/lib/components/PillDropdown'; export type PaohVisProps = { rowHeight: number; @@ -75,6 +76,9 @@ export const PaohVis = ({ data, graphMetadata, schema, configuration: conf, upda const [permutationIndicesColumn, setPermutationIndicesColumn] = useState<number[]>([]); const [previousHeaderColumn, setPreviousHeaderColumn] = useState<string>('none'); + // lines hyperEdge + const [lineHyperEdges, setLineHyperEdges] = useState<LinesHyperedges[]>([]); + // const [indicesRowsForColumnSort, setIndicesRowsForColumnSort] = useState<number[]>([]); const [indicesColumnForRowSort, setIndicesColumnForRowSort] = useState<number[]>([]); @@ -349,6 +353,14 @@ export const PaohVis = ({ data, graphMetadata, schema, configuration: conf, upda draft.data.hyperEdgeRanges = dataModelOriginalTemporalSorted; }); + // Update lines + if (currentPageRows) { + const newLinePositions = sortedArrayDataModelFiltered.map((hyperEdgeRange) => { + return intersectionElements([currentPageRows?.startIndexRow, currentPageRows?.endIndexRow], hyperEdgeRange.hyperEdges.indices); + }); + setLineHyperEdges(newLinePositions); + } + /* setDataModel({ rowLabels: dataModelOriginalTemporal.rowLabels.slice(currentPageRows?.startIndexRow, currentPageRows?.endIndexRow), @@ -462,6 +474,14 @@ export const PaohVis = ({ data, graphMetadata, schema, configuration: conf, upda draft.pageData.hyperEdgeRanges = sortedArrayDataModelFiltered; draft.data.hyperEdgeRanges = sortedArrayDataModel; }); + + // Update lines hyperedges + if (currentPageRows?.startIndexRow !== undefined && currentPageRows?.endIndexRow !== undefined) { + const newLinePositions = sortedArrayDataModelFiltered.map((hyperEdgeRange) => { + return intersectionElements([currentPageRows?.startIndexRow, currentPageRows?.endIndexRow], hyperEdgeRange.hyperEdges.indices); + }); + setLineHyperEdges(newLinePositions); + } }; useEffect(() => { @@ -530,6 +550,15 @@ export const PaohVis = ({ data, graphMetadata, schema, configuration: conf, upda originalData: newData, }); + // Update lines hyperedges + const newLinePositions = newData.hyperEdgeRanges.slice(0, configPaohvis.columnsMaxPerPage).map((hyperEdgeRange) => { + return intersectionElements( + [0, Math.min(configPaohvis.rowsMaxPerPage, newData.rowLabels.length)], + hyperEdgeRange.hyperEdges.indices, + ); + }); + setLineHyperEdges(newLinePositions); + const originalIndicesColumns = Array.from({ length: newData.hyperEdgeRanges.length + 1 }, (_, index) => index); setIndicesColumnForRowSort(originalIndicesColumns); @@ -745,6 +774,14 @@ export const PaohVis = ({ data, graphMetadata, schema, configuration: conf, upda draft.pageData.hyperEdgeRanges = slicedHyperEdgeRanges; }); + // Update lines hyperedges + if (currentPageRows?.startIndexRow !== undefined && currentPageRows?.endIndexRow !== undefined) { + const newLinePositions = slicedHyperEdgeRanges.map((hyperEdgeRange) => { + return intersectionElements([currentPageRows?.startIndexRow, currentPageRows?.endIndexRow], hyperEdgeRange.hyperEdges.indices); + }); + setLineHyperEdges(newLinePositions); + } + // columns const dataColumnsSorted = cloneDeep(informationColumnAllData); @@ -790,6 +827,12 @@ export const PaohVis = ({ data, graphMetadata, schema, configuration: conf, upda startIndexRow: startIndexRow, endIndexRow: endIndexRow, }); + + // Update lines hyperedges + const newLinePositions = dataModel.pageData.hyperEdgeRanges.map((hyperEdgeRange) => { + return intersectionElements([startIndexRow, endIndexRow], hyperEdgeRange.hyperEdges.indices); + }); + setLineHyperEdges(newLinePositions); }; return ( @@ -818,6 +861,7 @@ export const PaohVis = ({ data, graphMetadata, schema, configuration: conf, upda <HyperEdgeRangesBlock dataModel={dataModel} + dataLinesHyperedges={lineHyperEdges} rowHeight={configuration.rowHeight} yOffset={computedSizesSvg.colWidth} rowLabelColumnWidth={widthTotalRowInformation} @@ -892,97 +936,105 @@ const PaohSettings = ({ return ( <SettingsContainer> - <span className="text-xs font-semibold">Node used in Row</span> - <Input - type="dropdown" - value={configuration.rowNode} - options={graphMetadata.nodes.labels} - onChange={(val) => updateSettings({ rowNode: val })} - overrideRender={<EntityPill title={configuration.rowNode} />} - /> - <Button - className="w-full text-justify justify-start" - type="secondary" - variant="ghost" - size="sm" - onClick={toggleCollapseAttrRows} - iconComponent={<ArrowDropDown />} - > - attributes:{' '} - </Button> - {!areCollapsedAttrRows && ( - <div className=""> + <div className="overflow-x-hidden"> + <div> + <span className="text-xs font-semibold">Node used in Row</span> <Input - type="checkbox" - value={configuration.attributeRowShow} - options={rowNodeAttributes} - onChange={(val: string[] | string) => { - const updatedVal = Array.isArray(val) ? val : [val]; - updateSettings({ attributeRowShow: updatedVal }); - }} + type="dropdown" + value={configuration.rowNode} + options={graphMetadata.nodes.labels} + onChange={(val) => updateSettings({ rowNode: val })} + overrideRender={<EntityPill title={configuration.rowNode} />} /> </div> - )} - <span className="text-xs font-semibold">Node used in Column</span> - <Input - type="dropdown" - value={configuration.columnNode} - options={graphMetadata.nodes.labels} - onChange={(val) => updateSettings({ columnNode: val })} - overrideRender={<EntityPill title={configuration.columnNode} />} - /> - <Button - className="w-full text-justify justify-start" - type="secondary" - variant="ghost" - size="sm" - onClick={toggleCollapseAttrColumns} - iconComponent={<ArrowDropDown />} - > - attributes:{' '} - </Button> - - {!areCollapsedAttrColumns && ( - <div className=""> + <Button + className="w-full text-justify justify-start" + type="secondary" + variant="ghost" + size="sm" + onClick={toggleCollapseAttrRows} + iconComponent={<ArrowDropDown />} + > + attributes:{' '} + </Button> + {!areCollapsedAttrRows && ( + <div className=""> + <Input + type="checkbox" + value={configuration.attributeRowShow} + options={rowNodeAttributes} + onChange={(val: string[] | string) => { + const updatedVal = Array.isArray(val) ? val : [val]; + updateSettings({ attributeRowShow: updatedVal }); + }} + /> + </div> + )} + + <div> + <span className="text-xs font-semibold">Node used in Column</span> <Input - type="checkbox" - value={configuration.attributeColumnShow} - options={columnsNodeAttributes} - onChange={(val: string[] | string) => { - const updatedVal = Array.isArray(val) ? val : [val]; - updateSettings({ attributeColumnShow: updatedVal }); - }} + type="dropdown" + value={configuration.columnNode} + options={graphMetadata.nodes.labels} + onChange={(val) => updateSettings({ columnNode: val })} + overrideRender={<EntityPill title={configuration.columnNode} />} /> </div> - )} - <Input type="number" label="Row height" value={configuration.rowHeight} onChange={(val) => updateSettings({ rowHeight: val })} /> - - <Input - type="number" - label="# Rows" - value={configuration.numRowsDisplay} - onChange={(val) => updateSettings({ numRowsDisplay: val })} - /> - <Input - type="number" - label="# Columns" - value={configuration.numColumnsDisplay} - onChange={(val) => updateSettings({ numColumnsDisplay: val })} - /> - <Input - type="number" - label="Row jump sensitivity" - value={configuration.rowJumpAmount} - onChange={(val) => updateSettings({ rowJumpAmount: val })} - /> - <Input - type="number" - label="Column jump sensitivity" - value={configuration.colJumpAmount} - onChange={(val) => updateSettings({ colJumpAmount: val })} - /> - <Input type="boolean" label="Merge Data" value={configuration.mergeData} onChange={(val) => updateSettings({ mergeData: val })} /> + <Button + className="w-full text-justify justify-start" + type="secondary" + variant="ghost" + size="sm" + onClick={toggleCollapseAttrColumns} + iconComponent={<ArrowDropDown />} + > + attributes:{' '} + </Button> + + {!areCollapsedAttrColumns && ( + <div className=""> + <Input + type="checkbox" + value={configuration.attributeColumnShow} + options={columnsNodeAttributes} + onChange={(val: string[] | string) => { + const updatedVal = Array.isArray(val) ? val : [val]; + updateSettings({ attributeColumnShow: updatedVal }); + }} + /> + </div> + )} + + <Input type="number" label="Row height" value={configuration.rowHeight} onChange={(val) => updateSettings({ rowHeight: val })} /> + + <Input + type="number" + label="# Rows" + value={configuration.numRowsDisplay} + onChange={(val) => updateSettings({ numRowsDisplay: val })} + /> + <Input + type="number" + label="# Columns" + value={configuration.numColumnsDisplay} + onChange={(val) => updateSettings({ numColumnsDisplay: val })} + /> + <Input + type="number" + label="Row jump sensitivity" + value={configuration.rowJumpAmount} + onChange={(val) => updateSettings({ rowJumpAmount: val })} + /> + <Input + type="number" + label="Column jump sensitivity" + value={configuration.colJumpAmount} + onChange={(val) => updateSettings({ colJumpAmount: val })} + /> + <Input type="boolean" label="Merge Data" value={configuration.mergeData} onChange={(val) => updateSettings({ mergeData: val })} /> + </div> </SettingsContainer> ); }; diff --git a/libs/shared/lib/vis/visualizations/paohvis/types.ts b/libs/shared/lib/vis/visualizations/paohvis/types.ts index 0d28f205c..705120df6 100644 --- a/libs/shared/lib/vis/visualizations/paohvis/types.ts +++ b/libs/shared/lib/vis/visualizations/paohvis/types.ts @@ -28,6 +28,12 @@ export type RowInformation = { data: any[]; }[]; +export type LinesHyperedges = { + y0: number; + y1: number; + valid: boolean; +}; + /** The ranges of the hyperEdges consisting of the name of the range with all adequate hyperEdges. */ /* export type HyperEdgeRange = { diff --git a/libs/shared/lib/vis/visualizations/tablevis/tablevis.tsx b/libs/shared/lib/vis/visualizations/tablevis/tablevis.tsx index 3aa5192f3..7a8137151 100644 --- a/libs/shared/lib/vis/visualizations/tablevis/tablevis.tsx +++ b/libs/shared/lib/vis/visualizations/tablevis/tablevis.tsx @@ -1,10 +1,13 @@ -import React, { useEffect, useMemo, useRef } from 'react'; +import React, { useEffect, useMemo, useRef, useState } from 'react'; import { Table, AugmentedNodeAttributes } from './components/Table'; import { SchemaAttribute } from '../../../schema'; import { VisualizationPropTypes, VISComponentType } from '../../common'; import { Input } from '@graphpolaris/shared/lib/components/inputs'; import { GraphMetadata } from '@graphpolaris/shared/lib/data-access/statistics'; import { SettingsContainer } from '@graphpolaris/shared/lib/vis/components/config'; +import PillDropdown, { PillDropdownProps } from '@graphpolaris/shared/lib/components/PillDropdown'; +import { Button } from '@graphpolaris/shared/lib/components/buttons'; +import { ArrowDropDown } from '@mui/icons-material'; export type TableProps = { showBarplot: boolean; @@ -77,6 +80,10 @@ const TableSettings = ({ } }, [graphMetadata]); + const [areCollapsedAttr, setAreCollapsedAttr] = useState<boolean>(true); + const toggleCollapseAttr = () => { + setAreCollapsedAttr(!areCollapsedAttr); + }; const selectedNodeAttributes = useMemo(() => { if (configuration.displayEntity) { const nodeType = graphMetadata.nodes.types[configuration.displayEntity]; @@ -95,38 +102,52 @@ const TableSettings = ({ return ( <SettingsContainer> - <Input - type="dropdown" - label="Select entity" - value={configuration.displayEntity} - onChange={(val) => updateSettings({ displayEntity: val })} - options={graphMetadata.nodes.labels} - /> - <Input - type="boolean" - label="Show barplot" - value={configuration.showBarplot} - onChange={(val) => updateSettings({ showBarplot: val })} - /> - <Input - type="dropdown" - label="Items per page" - value={configuration.itemsPerPage} - onChange={(val) => updateSettings({ itemsPerPage: val })} - options={[10, 25, 50, 100]} - /> - <div> - <span className="text-sm">Attributes to display</span> - <div className=""> - <Input - type="checkbox" - value={configuration.displayAttributes} - options={selectedNodeAttributes} - onChange={(val: string[] | string) => { - const updatedVal = Array.isArray(val) ? val : [val]; - updateSettings({ displayAttributes: updatedVal }); - }} - /> + <div className="overflow-x-hidden w-6"> + <Input + type="dropdown" + label="Select entity" + value={configuration.displayEntity} + onChange={(val) => updateSettings({ displayEntity: val })} + options={graphMetadata.nodes.labels} + /> + <Input + type="boolean" + label="Show barplot" + value={configuration.showBarplot} + onChange={(val) => updateSettings({ showBarplot: val })} + /> + <Input + type="dropdown" + label="Items per page" + value={configuration.itemsPerPage} + onChange={(val) => updateSettings({ itemsPerPage: val })} + options={[10, 25, 50, 100]} + /> + <div> + <span className="text-sm">Attributes to display:</span> + <Button + className="w-full text-justify justify-start" + type="secondary" + variant="ghost" + size="sm" + onClick={toggleCollapseAttr} + iconComponent={<ArrowDropDown />} + > + attributes:{' '} + </Button> + <div className=""> + {!areCollapsedAttr && ( + <Input + type="checkbox" + value={configuration.displayAttributes} + options={selectedNodeAttributes} + onChange={(val: string[] | string) => { + const updatedVal = Array.isArray(val) ? val : [val]; + updateSettings({ displayAttributes: updatedVal }); + }} + /> + )} + </div> </div> </div> </SettingsContainer> -- GitLab