Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
  • graphpolaris/frontend-v2
  • rijkheere/frontend-v-2-reordering-paoh
2 results
Show changes
Commits on Source (2)
Showing
with 714 additions and 612 deletions
import React from 'react';
import { StoryObj, Meta } from '@storybook/react';
import { Accordion, AccordionItem, AccordionHead, AccordionBody } from '.';
import { EntityPill, RelationPill } from '../pills';
export default {
title: 'Components/Accordion',
component: Accordion,
decorators: [(Story) => <div className="flex m-5">{Story()}</div>],
} as Meta<typeof Accordion>;
type Story = StoryObj<typeof Accordion>;
export const Default: Story = {
render: () => (
<div className="max-w-md mx-auto my-10">
<Accordion defaultOpenIndex={0} className="w-64">
<AccordionItem>
<AccordionHead>
<EntityPill title="PERSON" />
</AccordionHead>
<AccordionBody>This is the content of Section 1.</AccordionBody>
</AccordionItem>
<AccordionItem>
<AccordionHead>
<EntityPill title="INDICENT" />
</AccordionHead>
<AccordionBody>
<Accordion>
<AccordionItem>
<AccordionHead showArrow={false}>Location info</AccordionHead>
<AccordionBody>Location info settings</AccordionBody>
</AccordionItem>
<AccordionItem>
<AccordionHead showArrow={false}>Color and shape</AccordionHead>
<AccordionBody>Color and shape settings</AccordionBody>
</AccordionItem>
</Accordion>
</AccordionBody>
</AccordionItem>
<AccordionItem>
<AccordionHead>
<RelationPill title="PERSON_OF" />
</AccordionHead>
<AccordionBody>This is the content of Section 3.</AccordionBody>
</AccordionItem>
</Accordion>
</div>
),
};
import React, { useState, ReactElement } from 'react';
import { Button } from '../buttons';
type AccordionProps = {
children: React.ReactNode;
defaultOpenIndex?: number;
defaultOpenAll?: boolean;
className?: string;
};
export function Accordion({ children, defaultOpenIndex, defaultOpenAll = false, className = '' }: AccordionProps) {
const childrenArray = React.Children.toArray(children);
const [openIndexes, setOpenIndexes] = useState<number[]>(() => {
if (defaultOpenAll) {
return childrenArray.map((_, index) => index);
} else if (defaultOpenIndex !== undefined) {
return [defaultOpenIndex];
} else {
return [];
}
});
const toggleIndex = (index: number) => {
setOpenIndexes((currentIndexes) =>
currentIndexes.includes(index) ? currentIndexes.filter((i) => i !== index) : [...currentIndexes, index],
);
};
return (
<div className={`w-full ${className}`}>
{React.Children.map(children, (child, index) => {
if (React.isValidElement(child)) {
return React.cloneElement(child as ReactElement<AccordionItemProps>, {
isOpen: openIndexes.includes(index),
onToggle: () => toggleIndex(index),
});
}
return child;
})}
</div>
);
}
type AccordionItemProps = {
isOpen?: boolean;
onToggle?: () => void;
children: React.ReactNode;
className?: string;
};
export function AccordionItem({ isOpen = false, onToggle, children, className = '' }: AccordionItemProps) {
return (
<div className={`w-full ${className}`}>
{React.Children.map(children, (child) => {
if (React.isValidElement(child)) {
return React.cloneElement(child as ReactElement<AccordionHeadProps | AccordionBodyProps>, {
isOpen,
onToggle,
});
}
return child;
})}
</div>
);
}
type AccordionHeadProps = {
isOpen?: boolean;
onToggle?: () => void;
children: React.ReactNode;
showArrow?: boolean;
className?: string;
};
export function AccordionHead({ isOpen = false, onToggle, children, showArrow = true, className = '' }: AccordionHeadProps) {
return (
<div className={`cursor-pointer flex items-center w-full box-border ${className}`} onClick={onToggle}>
{showArrow && (
<Button
size="2xs"
iconComponent={!isOpen ? 'icon-[ic--baseline-arrow-right]' : 'icon-[ic--baseline-arrow-drop-down]'}
variant="ghost"
className="mr-1"
/>
)}
{children}
</div>
);
}
type AccordionBodyProps = {
isOpen?: boolean;
children: React.ReactNode;
className?: string;
};
export function AccordionBody({ isOpen = false, children, className = '' }: AccordionBodyProps) {
return (
<div
className={`overflow-hidden transition-max-height duration-300 ease-in-out w-full box-border ml-2 ${isOpen ? 'max-h-screen' : 'max-h-0'} ${className}`}
>
{isOpen && <div>{children}</div>}
</div>
);
}
import React, { useState } from 'react';
import type { Meta, StoryObj } from '@storybook/react';
import { ColorPicker } from '.';
const Component: Meta<typeof ColorPicker> = {
title: 'ColorManager/Legend/ColorPicker',
component: ColorPicker,
argTypes: { onChange: { action: 'changed' } },
decorators: [(Story) => <div className="w-52 m-5">{Story()}</div>],
};
export default Component;
type Story = StoryObj<typeof Component>;
export const ColorPickerStory: Story = (args: any) => {
const [value, setValue] = useState<[number, number, number]>([251, 150, 55]);
return <ColorPicker value={value} onChange={setValue} />;
};
ColorPickerStory.args = {};
import React from 'react'; import React from 'react';
import { TwitterPicker } from 'react-color'; import { visualizationColors } from 'config';
import { useFloating, autoUpdate, offset, flip, shift, useInteractions, useClick, FloatingPortal } from '@floating-ui/react'; import { Popover, PopoverTrigger, PopoverContent } from '@graphpolaris/shared/lib/components/layout/Popover';
type Props = { const hexToRgb = (hex: string): [number, number, number] => {
value: any; const r = parseInt(hex.slice(1, 3), 16);
updateValue: (val: [number, number, number]) => void; const g = parseInt(hex.slice(3, 5), 16);
const b = parseInt(hex.slice(5, 7), 16);
return [r, g, b];
}; };
export default function ColorPicker({ value, updateValue }: Props) { type Props = {
const [open, setOpen] = React.useState(false); value: [number, number, number];
onChange: (val: [number, number, number]) => void;
const { x, y, strategy, context, refs, floatingStyles } = useFloating({ };
placement: 'bottom',
open,
onOpenChange: setOpen,
whileElementsMounted: autoUpdate,
middleware: [offset(5), flip(), shift({ padding: 5 })],
});
const { getReferenceProps, getFloatingProps } = useInteractions([useClick(context)]);
export function ColorPicker({ value, onChange }: Props) {
return ( return (
<> <div>
<div <Popover>
className="p-1 inline-block cursor-pointer" <PopoverTrigger
ref={refs.setReference} onClick={(e) => {
{...getReferenceProps({ e.stopPropagation();
onClick: () => setOpen(!open),
})}
>
<div
className="w-5 h-5"
style={{
backgroundColor: `rgb(${value?.[0] ?? 0}, ${value?.[1] ?? 0}, ${value?.[2] ?? 0})`,
}} }}
/> >
</div> <div className="w-4 h-4 rounded-sm" style={{ backgroundColor: `rgb(${value[0]}, ${value[1]}, ${value[2]})` }} />
{open && ( </PopoverTrigger>
<FloatingPortal> <PopoverContent>
<div <div
ref={refs.setFloating} className="grid grid-cols-4 gap-2 p-2"
style={{ onClick={(e) => {
position: strategy, e.stopPropagation();
top: y ?? 0,
left: x ?? 0,
...floatingStyles,
}} }}
className="z-10"
{...getFloatingProps()}
> >
<TwitterPicker {visualizationColors.GPCat.colors[14].map((hexColor) => {
triangle="top-right" const [r, g, b] = hexToRgb(hexColor);
color={{ r: value[0], g: value[1], b: value[2] }} return (
onChangeComplete={(color) => { <div
const rgb = color.rgb; key={hexColor}
const newValue: [number, number, number] = [rgb.r, rgb.g, rgb.b]; className="w-4 h-4 rounded-sm cursor-pointer"
updateValue(newValue); style={{ backgroundColor: hexColor }}
setOpen(false); onClick={(e) => {
}} e.stopPropagation();
/> onChange([r, g, b]);
}}
/>
);
})}
</div> </div>
</FloatingPortal> </PopoverContent>
)} </Popover>
</> </div>
); );
} }
import React, { useEffect } from 'react'; import React, { useEffect } from 'react';
import ColorPicker from '@graphpolaris/shared/lib/components/colorComponents/colorPicker'; import { ColorPicker } from '@graphpolaris/shared/lib/components/colorComponents/colorPicker';
import { MapProps } from '../../mapvis'; import { MapProps } from '../../mapvis';
import { Button, DropdownColorLegend, EntityPill, Input, RelationPill } from '@graphpolaris/shared/lib/components'; import { DropdownColorLegend, EntityPill, Input, RelationPill } from '@graphpolaris/shared/lib/components';
import { LayerSettingsComponentType } from '../../mapvis.types'; import { LayerSettingsComponentType } from '../../mapvis.types';
import { Accordion, AccordionBody, AccordionHead, AccordionItem } from '@graphpolaris/shared/lib/components/accordion';
const areaColoringStrategies = ['Node count', 'Edge count', 'Incoming edges', 'Outgoing edges', 'Connected edges', 'Attribute']; const areaColoringStrategies = ['Node count', 'Edge count', 'Incoming edges', 'Outgoing edges', 'Connected edges', 'Attribute'];
...@@ -50,131 +51,108 @@ export function ChoroplethOptions({ ...@@ -50,131 +51,108 @@ export function ChoroplethOptions({
} }
}, [graphMetadata, layerType, settings, updateLayerSettings]); }, [graphMetadata, layerType, settings, updateLayerSettings]);
const handleCollapseToggle = (type: string, itemType: 'nodes' | 'edges') => {
if (layerSettings) {
updateLayerSettings({
[itemType]: {
...layerSettings[itemType],
[type]: {
...layerSettings[itemType][type],
collapsed: !layerSettings[itemType][type]?.collapsed ?? true,
},
},
});
}
};
return ( return (
layerSettings && ( layerSettings && (
<div> <div>
<div className="mt-2"> <Accordion defaultOpenAll={true}>
<p className="text-bold">Area color</p> <AccordionItem className="mt-2">
<Input <AccordionHead className="flex items-center">
inline <span className="font-semibold">General</span>
label="Based on" </AccordionHead>
type="dropdown" <AccordionBody>
value={layerSettings?.coloringStrategy} <p className="text-bold">Area color</p>
options={areaColoringStrategies} <Input
onChange={(val) => updateLayerSettings({ coloringStrategy: val as coloringStrategiesType })} inline
/> label="Based on"
<DropdownColorLegend value={settings?.colorScale} onChange={(val) => updateLayerSettings({ colorScale: val })} /> type="dropdown"
<Input value={layerSettings?.coloringStrategy}
label="Opacity" options={areaColoringStrategies}
type="slider" onChange={(val) => updateLayerSettings({ coloringStrategy: val as coloringStrategiesType })}
min={0} />
max={1} <DropdownColorLegend value={settings?.colorScale} onChange={(val) => updateLayerSettings({ colorScale: val })} />
step={0.05} <Input
unit="%" label="Opacity"
value={layerSettings?.opacity ?? 0.8} type="slider"
onChange={(val) => updateLayerSettings({ opacity: val as number })} min={0}
/> max={1}
</div> step={0.05}
unit="%"
{graphMetadata.nodes.labels.map((nodeType) => { value={layerSettings?.opacity ?? 0.8}
const nodeSettings = layerSettings?.nodes?.[nodeType] || {}; onChange={(val) => updateLayerSettings({ opacity: val as number })}
/>
return ( </AccordionBody>
<div className="mt-2" key={nodeType}> </AccordionItem>
<div className="flex items-center">
<Button {graphMetadata.nodes.labels.map((nodeType) => {
size="2xs" const nodeSettings = layerSettings?.nodes?.[nodeType] || {};
iconComponent={nodeSettings.collapsed ? 'icon-[ic--baseline-arrow-right]' : 'icon-[ic--baseline-arrow-drop-down]'}
variant="ghost" return (
onClick={() => handleCollapseToggle(nodeType, 'nodes')} <AccordionItem className="mt-2" key={nodeType}>
/> <AccordionHead className="flex items-center">
<div className="w-3/4 mr-6 cursor-pointer" onClick={() => handleCollapseToggle(nodeType, 'nodes')}>
<EntityPill title={nodeType} /> <EntityPill title={nodeType} />
</div> </AccordionHead>
</div>
<AccordionBody>
{!nodeSettings.collapsed && ( <div>
<div> <Input
<Input inline
inline label="Latitude"
label="Latitude" type="dropdown"
type="dropdown" value={settings?.location[nodeType].lat}
value={settings?.location[nodeType].lat} options={[...spatialAttributes[nodeType]]}
options={[...spatialAttributes[nodeType]]} disabled={spatialAttributes[nodeType].length < 1}
disabled={spatialAttributes[nodeType].length < 1} onChange={(val) => updateSpatialAttribute(nodeType, 'lat', val as string)}
onChange={(val) => updateSpatialAttribute(nodeType, 'lat', val as string)} />
/> <Input
<Input inline
inline label="Longitude"
label="Longitude" type="dropdown"
type="dropdown" value={settings?.location[nodeType].lon}
value={settings?.location[nodeType].lon} options={[...spatialAttributes[nodeType]]}
options={[...spatialAttributes[nodeType]]} disabled={spatialAttributes[nodeType].length < 1}
disabled={spatialAttributes[nodeType].length < 1} onChange={(val) => updateSpatialAttribute(nodeType, 'lon', val as string)}
onChange={(val) => updateSpatialAttribute(nodeType, 'lon', val as string)} />
/> </div>
</div> </AccordionBody>
)} </AccordionItem>
</div> );
); })}
})}
{graphMetadata.edges.labels.map((edgeType) => { {graphMetadata.edges.labels.map((edgeType) => {
const edgeSettings = layerSettings?.edges?.[edgeType] || {}; const edgeSettings = layerSettings?.edges?.[edgeType] || {};
return ( return (
<div key={edgeType} className="mt-2"> <AccordionItem key={edgeType} className="mt-2">
<div className="flex items-center"> <AccordionHead className="flex items-center">
<Button
size="2xs"
iconComponent={edgeSettings?.collapsed ? 'icon-[ic--baseline-arrow-right]' : 'icon-[ic--baseline-arrow-drop-down]'}
variant="ghost"
onClick={() => handleCollapseToggle(edgeType, 'edges')}
/>
<div className="w-3/4 mr-6 cursor-pointer" onClick={() => handleCollapseToggle(edgeType, 'edges')}>
<RelationPill title={edgeType} /> <RelationPill title={edgeType} />
</div> </AccordionHead>
</div>
<AccordionBody>
{!edgeSettings.collapsed && ( <div>
<div> <div className="flex justify-between">
<div className="flex justify-between"> <span className="font-bold">Color</span>
<span className="font-bold">Color</span> <ColorPicker
<ColorPicker value={edgeSettings.color}
value={edgeSettings.color} onChange={(val) =>
updateValue={(val) => updateLayerSettings({ edges: { ...settings.edges, [edgeType]: { ...edgeSettings, color: val } } })
updateLayerSettings({ edges: { ...settings.edges, [edgeType]: { ...edgeSettings, color: val } } }) }
} />
</div>
<Input
label="Edges on hover"
type="boolean"
value={layerSettings?.enableBrushing}
onChange={(val) => {
updateLayerSettings({ enableBrushing: val as boolean });
}}
/> />
</div> </div>
</AccordionBody>
<Input </AccordionItem>
label="Edges on hover" );
type="boolean" })}
value={layerSettings?.enableBrushing} </Accordion>
onChange={(val) => {
updateLayerSettings({ enableBrushing: val as boolean });
}}
/>
</div>
)}
</div>
);
})}
</div> </div>
) )
); );
......
import React, { useEffect } from 'react'; import React, { useEffect } from 'react';
import { MapProps } from '../../mapvis'; import { MapProps } from '../../mapvis';
import { Button, EntityPill, Input } from '@graphpolaris/shared/lib/components'; import { EntityPill, Input } from '@graphpolaris/shared/lib/components';
import { LayerSettingsComponentType } from '../../mapvis.types'; import { LayerSettingsComponentType } from '../../mapvis.types';
import { Accordion, AccordionBody, AccordionHead, AccordionItem } from '@graphpolaris/shared/lib/components/accordion';
export function HeatLayerOptions({ export function HeatLayerOptions({
settings, settings,
...@@ -38,84 +39,64 @@ export function HeatLayerOptions({ ...@@ -38,84 +39,64 @@ export function HeatLayerOptions({
} }
}, [graphMetadata, layerType, settings, updateLayerSettings]); }, [graphMetadata, layerType, settings, updateLayerSettings]);
const handleCollapseToggle = (type: string, itemType: 'nodes' | 'edges') => {
if (layerSettings) {
updateLayerSettings({
[itemType]: {
...layerSettings[itemType],
[type]: {
...layerSettings[itemType][type],
collapsed: !layerSettings[itemType][type]?.collapsed ?? true,
},
},
});
}
};
return ( return (
layerSettings && ( layerSettings && (
<div> <div>
{graphMetadata.nodes.labels.map((nodeType) => { <Accordion defaultOpenAll={true}>
const nodeSettings = layerSettings?.nodes?.[nodeType] || {}; {graphMetadata.nodes.labels.map((nodeType) => {
const nodeSettings = layerSettings?.nodes?.[nodeType] || {};
return ( return (
<div className="mt-2" key={nodeType}> <AccordionItem className="mt-2" key={nodeType}>
<div className="flex items-center"> <AccordionHead className="flex items-center">
<Button
size="2xs"
iconComponent={nodeSettings.collapsed ? 'icon-[ic--baseline-arrow-right]' : 'icon-[ic--baseline-arrow-drop-down]'}
variant="ghost"
onClick={() => handleCollapseToggle(nodeType, 'nodes')}
/>
<div className="w-3/4 mr-6 cursor-pointer" onClick={() => handleCollapseToggle(nodeType, 'nodes')}>
<EntityPill title={nodeType} /> <EntityPill title={nodeType} />
</div> </AccordionHead>
</div>
{!nodeSettings.collapsed && ( <AccordionBody>
<div> <div>
<Input <Input
label="Hidden" label="Hidden"
type="boolean" type="boolean"
value={nodeSettings.hidden ?? false} value={nodeSettings.hidden ?? false}
onChange={(val) => { onChange={(val) => {
updateLayerSettings({ nodes: { ...layerSettings.nodes, [nodeType]: { ...nodeSettings, hidden: val } } }); updateLayerSettings({ nodes: { ...layerSettings.nodes, [nodeType]: { ...nodeSettings, hidden: val } } });
}} }}
/> />
<Input <Input
inline inline
label="Latitude" label="Latitude"
type="dropdown" type="dropdown"
value={settings?.location[nodeType].lat} value={settings?.location[nodeType].lat}
options={[...spatialAttributes[nodeType]]} options={[...spatialAttributes[nodeType]]}
disabled={spatialAttributes[nodeType].length < 1} disabled={spatialAttributes[nodeType].length < 1}
onChange={(val) => updateSpatialAttribute(nodeType, 'lat', val as string)} onChange={(val) => updateSpatialAttribute(nodeType, 'lat', val as string)}
/> />
<Input <Input
inline inline
label="Longitude" label="Longitude"
type="dropdown" type="dropdown"
value={settings?.location[nodeType].lon} value={settings?.location[nodeType].lon}
options={[...spatialAttributes[nodeType]]} options={[...spatialAttributes[nodeType]]}
disabled={spatialAttributes[nodeType].length < 1} disabled={spatialAttributes[nodeType].length < 1}
onChange={(val) => updateSpatialAttribute(nodeType, 'lon', val as string)} onChange={(val) => updateSpatialAttribute(nodeType, 'lon', val as string)}
/> />
<Input <Input
label="Size" label="Size"
type="slider" type="slider"
min={0} min={0}
max={40} max={40}
step={1} step={1}
value={nodeSettings.size} value={nodeSettings.size}
onChange={(val) => { onChange={(val) => {
updateLayerSettings({ nodes: { ...layerSettings.nodes, [nodeType]: { ...nodeSettings, size: Number(val) } } }); updateLayerSettings({ nodes: { ...layerSettings.nodes, [nodeType]: { ...nodeSettings, size: Number(val) } } });
}} }}
/> />
</div> </div>
)} </AccordionBody>
</div> </AccordionItem>
); );
})} })}
</Accordion>
</div> </div>
) )
); );
......
...@@ -31,21 +31,28 @@ export class NodeLinkLayer extends CompositeLayer<CompositeLayerType> { ...@@ -31,21 +31,28 @@ export class NodeLinkLayer extends CompositeLayer<CompositeLayerType> {
// TODO: Remove any here // TODO: Remove any here
if (changeFlags.propsOrDataChanged) { if (changeFlags.propsOrDataChanged) {
const colorScales: ColorScales = {}; const colorScales: ColorScales = {};
Object.keys(props.settings[NodeLinkLayer.type].nodes).map((label) => { const nodesSettings = props.settings?.[NodeLinkLayer.type]?.nodes;
const nodeSettings = props.settings[NodeLinkLayer.type].nodes[label]; const nodesMetadata = props.graphMetadata?.nodes?.types;
const nodeDistribution = props.graphMetadata.nodes.types[label].attributes;
if (nodeSettings.colorByAttribute) { if (nodesSettings && nodesMetadata) {
if (nodeSettings.colorAttributeType === 'numerical') { Object.keys(nodesSettings).forEach((label) => {
colorScales[label] = this.setNumericalColor( const nodeSettings = nodesSettings[label];
nodeDistribution[nodeSettings?.colorAttribute]?.min, const nodeDistribution = nodesMetadata[label]?.attributes;
nodeDistribution[nodeSettings?.colorAttribute]?.max,
nodeSettings.colorScale, if (nodeSettings?.colorByAttribute && nodeDistribution) {
); const colorAttribute = nodeSettings.colorAttribute;
const attributeData = nodeDistribution[colorAttribute];
if (nodeSettings.colorAttributeType === 'numerical' && attributeData) {
colorScales[label] = this.setNumericalColor(attributeData.min, attributeData.max, nodeSettings.colorScale);
}
} }
} });
}); }
this.setState({ colorScales }); this.setState({ colorScales });
} }
return changeFlags.propsOrDataChanged || changeFlags.somethingChanged; return changeFlags.propsOrDataChanged || changeFlags.somethingChanged;
} }
......
...@@ -10,7 +10,7 @@ import { Attribution, ActionBar, MapTooltip, MapSettings } from './components'; ...@@ -10,7 +10,7 @@ import { Attribution, ActionBar, MapTooltip, MapSettings } from './components';
import { useSelectionLayer, useCoordinateLookup } from './hooks'; import { useSelectionLayer, useCoordinateLookup } from './hooks';
import { VisualizationTooltip } from '@graphpolaris/shared/lib/components/VisualizationTooltip'; import { VisualizationTooltip } from '@graphpolaris/shared/lib/components/VisualizationTooltip';
import { Tooltip, TooltipContent, TooltipTrigger } from '@graphpolaris/shared/lib/components/tooltip'; import { Tooltip, TooltipContent, TooltipTrigger } from '@graphpolaris/shared/lib/components/tooltip';
import { isGeoJsonType } from './utils'; import { isGeoJsonType, rgbToHex } from './utils';
import { NodeType } from '../nodelinkvis/types'; import { NodeType } from '../nodelinkvis/types';
export type MapProps = { export type MapProps = {
...@@ -270,7 +270,11 @@ export const MapVis = forwardRef((props: VisualizationPropTypes<MapProps>, refEx ...@@ -270,7 +270,11 @@ export const MapVis = forwardRef((props: VisualizationPropTypes<MapProps>, refEx
? (node as SearchResultType)?.name ? (node as SearchResultType)?.name
: 'N/A' : 'N/A'
} }
colorHeader="#FB9637" colorHeader={rgbToHex(
props?.settings?.nodelink?.nodes?.[node.label]?.color?.[0] ?? 0,
props?.settings?.nodelink?.nodes?.[node.label]?.color?.[1] ?? 0,
props?.settings?.nodelink?.nodes?.[node.label]?.color?.[2] ?? 0,
)}
> >
<MapTooltip type={node.selectedType} data={{ ...node }} key={node._id} /> <MapTooltip type={node.selectedType} data={{ ...node }} key={node._id} />
</VisualizationTooltip> </VisualizationTooltip>
...@@ -281,7 +285,9 @@ export const MapVis = forwardRef((props: VisualizationPropTypes<MapProps>, refEx ...@@ -281,7 +285,9 @@ export const MapVis = forwardRef((props: VisualizationPropTypes<MapProps>, refEx
<Tooltip open={true} interactive={false} boundaryElement={ref} showArrow={true}> <Tooltip open={true} interactive={false} boundaryElement={ref} showArrow={true}>
<TooltipTrigger x={searchResult.x} y={searchResult.y} /> <TooltipTrigger x={searchResult.x} y={searchResult.y} />
<TooltipContent> <TooltipContent>
<MapTooltip type="location" data={{ ...searchResult }} key={searchResult.name} /> <VisualizationTooltip name={searchResult.name} colorHeader="#FB9637">
<MapTooltip type="location" data={{ ...searchResult }} key={searchResult.name} />
</VisualizationTooltip>
</TooltipContent> </TooltipContent>
</Tooltip> </Tooltip>
)} )}
......
...@@ -15,3 +15,8 @@ export function nodeColorRGB(num: number) { ...@@ -15,3 +15,8 @@ export function nodeColorRGB(num: number) {
export const isGeoJsonType = (data: NodeType | GeoJsonType | SearchResultType): data is GeoJsonType => { export const isGeoJsonType = (data: NodeType | GeoJsonType | SearchResultType): data is GeoJsonType => {
return (data as GeoJsonType).properties !== undefined; return (data as GeoJsonType).properties !== undefined;
}; };
export const rgbToHex = (r: number, g: number, b: number): string => {
const toHex = (n: number) => n.toString(16).padStart(2, '0');
return `#${toHex(r)}${toHex(g)}${toHex(b)}`;
};
...@@ -15,6 +15,7 @@ import { Button } from '@graphpolaris/shared/lib/components/buttons'; ...@@ -15,6 +15,7 @@ import { Button } from '@graphpolaris/shared/lib/components/buttons';
import { EntityPill } from '@graphpolaris/shared/lib/components/pills/Pill'; import { EntityPill } from '@graphpolaris/shared/lib/components/pills/Pill';
import { cloneDeep } from 'lodash-es'; import { cloneDeep } from 'lodash-es';
import { useImmer } from 'use-immer'; import { useImmer } from 'use-immer';
import { Accordion, AccordionBody, AccordionHead, AccordionItem } from '@graphpolaris/shared/lib/components/accordion';
import html2canvas from 'html2canvas'; import html2canvas from 'html2canvas';
export interface PaohVisHandle { export interface PaohVisHandle {
...@@ -1030,29 +1031,25 @@ const PaohSettings = ({ settings, graphMetadata, updateSettings }: Visualization ...@@ -1030,29 +1031,25 @@ const PaohSettings = ({ settings, graphMetadata, updateSettings }: Visualization
} }
></Input> ></Input>
</div> </div>
<Button
variantType="secondary" <Accordion>
variant="ghost" <AccordionItem>
size="sm" <AccordionHead>
className="-pt-3 mt-1" <span className="font-semibold">attributes: </span>
onClick={toggleCollapseAttrRows} </AccordionHead>
iconComponent={areCollapsedAttrRows ? 'icon-[ic--baseline-arrow-right]' : 'icon-[ic--baseline-arrow-drop-down]'} <AccordionBody>
> <Input
attributes:{' '} type="checkbox"
</Button> value={settings.attributeRowShow}
{!areCollapsedAttrRows && ( options={rowNodeInformation.attributes}
<div className=""> onChange={(val: string[] | string) => {
<Input const updatedVal = Array.isArray(val) ? val : [val];
type="checkbox" updateSettings({ attributeRowShow: updatedVal });
value={settings.attributeRowShow} }}
options={rowNodeInformation.attributes} />
onChange={(val: string[] | string) => { </AccordionBody>
const updatedVal = Array.isArray(val) ? val : [val]; </AccordionItem>
updateSettings({ attributeRowShow: updatedVal }); </Accordion>
}}
/>
</div>
)}
<div> <div>
<span className="text-xs font-semibold">Node used in Column</span> <span className="text-xs font-semibold">Node used in Column</span>
...@@ -1075,30 +1072,24 @@ const PaohSettings = ({ settings, graphMetadata, updateSettings }: Visualization ...@@ -1075,30 +1072,24 @@ const PaohSettings = ({ settings, graphMetadata, updateSettings }: Visualization
/> />
</div> </div>
<Button <Accordion>
variantType="secondary" <AccordionItem>
variant="ghost" <AccordionHead>
size="sm" <span className="font-semibold">attributes: </span>
className="-pt-3 mt-1" </AccordionHead>
onClick={toggleCollapseAttrColumns} <AccordionBody>
iconComponent={areCollapsedAttrColumns ? 'icon-[ic--baseline-arrow-right]' : 'icon-[ic--baseline-arrow-drop-down]'} <Input
> type="checkbox"
attributes:{' '} value={settings.attributeColumnShow}
</Button> options={columnsNodeInformation.attributes}
onChange={(val: string[] | string) => {
{!areCollapsedAttrColumns && ( const updatedVal = Array.isArray(val) ? val : [val];
<div className=""> updateSettings({ attributeColumnShow: updatedVal });
<Input }}
type="checkbox" />
value={settings.attributeColumnShow} </AccordionBody>
options={columnsNodeInformation.attributes} </AccordionItem>
onChange={(val: string[] | string) => { </Accordion>
const updatedVal = Array.isArray(val) ? val : [val];
updateSettings({ attributeColumnShow: updatedVal });
}}
/>
</div>
)}
<Input <Input
type="slider" type="slider"
......
...@@ -6,6 +6,7 @@ import { SettingsContainer } from '@graphpolaris/shared/lib/vis/components/confi ...@@ -6,6 +6,7 @@ import { SettingsContainer } from '@graphpolaris/shared/lib/vis/components/confi
import { Button } from '@graphpolaris/shared/lib/components/buttons'; import { Button } from '@graphpolaris/shared/lib/components/buttons';
import { useSearchResultData } from '@graphpolaris/shared/lib/data-access'; import { useSearchResultData } from '@graphpolaris/shared/lib/data-access';
import { EntityPill } from '@graphpolaris/shared/lib/components/pills/Pill'; import { EntityPill } from '@graphpolaris/shared/lib/components/pills/Pill';
import { Accordion, AccordionBody, AccordionHead, AccordionItem } from '@graphpolaris/shared/lib/components/accordion';
import html2canvas from 'html2canvas'; import html2canvas from 'html2canvas';
export interface TableVisHandle { export interface TableVisHandle {
...@@ -148,11 +149,6 @@ const TableSettings = ({ settings, graphMetadata, updateSettings }: Visualizatio ...@@ -148,11 +149,6 @@ const TableSettings = ({ settings, graphMetadata, updateSettings }: Visualizatio
} }
}, [graphMetadata]); }, [graphMetadata]);
const [areCollapsedAttr, setAreCollapsedAttr] = useState<boolean>(true);
const toggleCollapseAttr = () => {
setAreCollapsedAttr(!areCollapsedAttr);
};
const selectedNodeAttributes = useMemo(() => { const selectedNodeAttributes = useMemo(() => {
if (settings.displayEntity) { if (settings.displayEntity) {
const nodeType = graphMetadata.nodes.types[settings.displayEntity]; const nodeType = graphMetadata.nodes.types[settings.displayEntity];
...@@ -214,20 +210,12 @@ const TableSettings = ({ settings, graphMetadata, updateSettings }: Visualizatio ...@@ -214,20 +210,12 @@ const TableSettings = ({ settings, graphMetadata, updateSettings }: Visualizatio
onChange={(val) => updateSettings({ maxBarsCount: val })} onChange={(val) => updateSettings({ maxBarsCount: val })}
/> />
</div> </div>
<div className="flex flex-col items-start space-y-2"> <Accordion>
<span className="text-sm">Attributes to display:</span> <AccordionItem>
<Button <AccordionHead>
className="w-full text-justify justify-start" <span className="text-sm">Attributes to display:</span>
variantType="secondary" </AccordionHead>
variant="ghost" <AccordionBody>
size="sm"
onClick={toggleCollapseAttr}
iconComponent={areCollapsedAttr ? 'icon-[ic--baseline-arrow-right]' : 'icon-[ic--baseline-arrow-drop-down]'}
>
attributes:{' '}
</Button>
<div className="">
{!areCollapsedAttr && (
<Input <Input
type="checkbox" type="checkbox"
value={settings.displayAttributes} value={settings.displayAttributes}
...@@ -237,9 +225,10 @@ const TableSettings = ({ settings, graphMetadata, updateSettings }: Visualizatio ...@@ -237,9 +225,10 @@ const TableSettings = ({ settings, graphMetadata, updateSettings }: Visualizatio
updateSettings({ displayAttributes: updatedVal }); updateSettings({ displayAttributes: updatedVal });
}} }}
/> />
)} test
</div> </AccordionBody>
</div> </AccordionItem>
</Accordion>
</div> </div>
</SettingsContainer> </SettingsContainer>
); );
......
...@@ -54,7 +54,6 @@ ...@@ -54,7 +54,6 @@
"pixi-actions": "^1.1.10", "pixi-actions": "^1.1.10",
"pixi-viewport": "5.0.2", "pixi-viewport": "5.0.2",
"pixi.js": "^7.4.2", "pixi.js": "^7.4.2",
"react-color": "^2.19.3",
"react-cookie": "^7.1.0", "react-cookie": "^7.1.0",
"react-draggable": "^4.4.6", "react-draggable": "^4.4.6",
"react-grid-layout": "^1.4.4", "react-grid-layout": "^1.4.4",
...@@ -85,7 +84,6 @@ ...@@ -85,7 +84,6 @@
"@types/lodash-es": "^4.17.12", "@types/lodash-es": "^4.17.12",
"@types/node": "20.11.27", "@types/node": "20.11.27",
"@types/react": "^18.2.65", "@types/react": "^18.2.65",
"@types/react-color": "^3.0.12",
"@types/react-dom": "^18.2.22", "@types/react-dom": "^18.2.22",
"@types/react-window": "^1.8.8", "@types/react-window": "^1.8.8",
"@typescript-eslint/eslint-plugin": "~7.2.0", "@typescript-eslint/eslint-plugin": "~7.2.0",
......