diff --git a/libs/shared/lib/data-access/store/visualizationSlice.ts b/libs/shared/lib/data-access/store/visualizationSlice.ts index 4cb0d79e4f04e944fbae222ad2c6d619945de413..0a7d865bb55ea85fe2cdf7e31a48811cdd60b118 100644 --- a/libs/shared/lib/data-access/store/visualizationSlice.ts +++ b/libs/shared/lib/data-access/store/visualizationSlice.ts @@ -1,69 +1,49 @@ -import { createSelector, createSlice, PayloadAction } from '@reduxjs/toolkit'; +import { createSlice, PayloadAction } from '@reduxjs/toolkit'; import type { RootState } from './store'; -import { globalConfigTypes, VisualizationConfiguration } from '../../vis/types'; +import { VisualizationConfiguration } from '../../vis/types'; import { isEqual } from 'lodash-es'; -import { EncodingTypes } from '../../vis/configuration/encodings'; -import { SettingTypes } from '../../vis/configuration/settings'; -import { InteractionTypes } from '../../vis/configuration/interactions'; export type VisStateSettings = { - general: globalConfigTypes; [id: string]: VisualizationConfiguration; }; export type VisState = { - activeVisualization?: string; - settings: VisStateSettings; + active?: string; + visualizations: VisStateSettings; }; export const initialState: VisState = { - activeVisualization: 'NodeLinkVis', - settings: { - general: {}, - }, + active: 'NodeLinkVis', + visualizations: {}, }; export const visualizationSlice = createSlice({ name: 'visualization', initialState, reducers: { - addVisualization: ( - state, - action: PayloadAction<{ - id: string; - settings: SettingTypes; - encodings: EncodingTypes; - interactions: InteractionTypes; - }>, - ) => { - const { id, settings, encodings, interactions } = action.payload; - state.settings[id] = { settings, encodings, interactions }; - }, removeVisualization: (state, action: PayloadAction<string>) => { - if (state.settings[action.payload]) { - delete state.settings[action.payload]; + if (state.visualizations[action.payload]) { + delete state.visualizations[action.payload]; } }, setActiveVisualization: (state, action: PayloadAction<string>) => { - state.activeVisualization = action.payload; + state.active = action.payload; }, setVisualizationState: (state, action: PayloadAction<VisState>) => { - if (action.payload.activeVisualization && !isEqual(action.payload, state)) { - state.activeVisualization = action.payload.activeVisualization; - state.settings = action.payload.settings; + if (action.payload.active && !isEqual(action.payload, state)) { + state.active = action.payload.active; + state.visualizations = action.payload.visualizations; } }, - updateConfiguration: (state, action: PayloadAction<any>) => { - const { general, visualization } = action.payload; - state.settings.general = general; - if (state.activeVisualization) state.settings[state.activeVisualization] = visualization; + updateVisualization: (state, action: PayloadAction<{ id: string; settings: any }>) => { + const { id, settings } = action.payload; + state.visualizations[id] = settings; }, }, }); -export const { addVisualization, removeVisualization, setActiveVisualization, setVisualizationState, updateConfiguration } = - visualizationSlice.actions; +export const { removeVisualization, setActiveVisualization, setVisualizationState, updateVisualization } = visualizationSlice.actions; export const visualizationState = (state: RootState) => state.visualize; -export const visualizationAllSettings = (state: RootState) => state.visualize.settings; +export const visualizationAllSettings = (state: RootState) => state.visualize.visualizations; export default visualizationSlice.reducer; diff --git a/libs/shared/lib/vis/configuration/advanced/advanced.tsx b/libs/shared/lib/vis/configuration/advanced/advanced.tsx deleted file mode 100644 index 287aa679a1d7939b8e5aeb00a097b59a0d2893d4..0000000000000000000000000000000000000000 --- a/libs/shared/lib/vis/configuration/advanced/advanced.tsx +++ /dev/null @@ -1,25 +0,0 @@ -import React from 'react'; -import ReactJSONView from 'react-json-view'; -import { Configuration, PanelProps } from '../panel/panel.types'; - -interface AdvancedPanelProps extends PanelProps { - state: Configuration; - update: (configType: string, key: string, value: any) => void; // Adjusted type for update function -} - -export default function AdvancedPanel({ state, update }: AdvancedPanelProps) { - return ( - state && ( - <div className="m-2"> - <ReactJSONView - src={state} - name={false} - collapsed={1} - quotesOnKeys={false} - displayDataTypes={false} - onEdit={(v) => update('', '', v.updated_src)} - /> - </div> - ) - ); -} diff --git a/libs/shared/lib/vis/configuration/advanced/index.ts b/libs/shared/lib/vis/configuration/advanced/index.ts deleted file mode 100644 index a7584d4b2419e2fc6b2b26fe9a4c643639719453..0000000000000000000000000000000000000000 --- a/libs/shared/lib/vis/configuration/advanced/index.ts +++ /dev/null @@ -1 +0,0 @@ -export { default as AdvancedPanel } from './advanced'; diff --git a/libs/shared/lib/vis/configuration/encodings/encoding.tsx b/libs/shared/lib/vis/configuration/encodings/encoding.tsx deleted file mode 100644 index 96acc27ec4004cf5d13be05292a0024d201cd1b1..0000000000000000000000000000000000000000 --- a/libs/shared/lib/vis/configuration/encodings/encoding.tsx +++ /dev/null @@ -1,67 +0,0 @@ -import React, { useState } from 'react'; -import Accessor from './accessor'; -import Info from '@graphpolaris/shared/lib/components/info'; -import Selector from './selector'; -import { PanelProps } from '../panel/panel.types'; - -export default function EncodingPanel({ state, update }: PanelProps) { - const [encodingUpdates, setEncodingUpdates] = useState<{ - [id: string]: { - marking: any; - accessorPath: string; - }; - }>({}); - - const updateEncoding = (encoding: string, val: any, type: 'accessorPath' | 'marking') => { - setEncodingUpdates((prevState) => { - if (prevState) { - return { - ...prevState, - [encoding]: { - ...(prevState[encoding] || {}), - [type]: val, - }, - }; - } else { - return prevState; - } - }); - }; - - return ( - state && ( - <div> - {Object.keys(state).map((key) => { - const item = state[key]; - const value = encodingUpdates[key] ? encodingUpdates[key] : { marking: undefined, accessorPath: undefined }; - - return ( - <div key={key} className="bg-secondary-50 p-2 m-1"> - <div className="flex items-center justify-between"> - <h1 className="text-xs">{item.label ? item.label : key}</h1> - {item.description && <Info tooltip={item.description} />} - </div> - <Accessor - value={value.accessorPath} - onChange={(value: string | number) => updateEncoding(key, value, 'accessorPath')} - // onChange={(value: string | number) => update(key, { ...state[key], accessorPath: value })} - element={item.element} - dimension={item.dimension ?? []} - /> - {value.accessorPath && ( - <Selector - key={key} - selectorType={item.selector} - elementType={item.element} - marking={value.marking} - onChange={(value: any) => updateEncoding(key, value, 'marking')} - accessorPath={value.accessorPath} - /> - )} - </div> - ); - })} - </div> - ) - ); -} diff --git a/libs/shared/lib/vis/configuration/encodings/index.ts b/libs/shared/lib/vis/configuration/encodings/index.ts index 056c6ae4da4344f331c8bf5368e0d41fd1a88994..ea02a99517ed43155ffe83c4c4eb877a3f02259a 100644 --- a/libs/shared/lib/vis/configuration/encodings/index.ts +++ b/libs/shared/lib/vis/configuration/encodings/index.ts @@ -1,2 +1 @@ export type { Encoding, EncodingTypes, EncodingProps } from './encodings.types'; -export { default as EncodingPanel } from './encoding'; diff --git a/libs/shared/lib/vis/configuration/interactions/index.ts b/libs/shared/lib/vis/configuration/interactions/index.ts deleted file mode 100644 index 61687e06e2fff5a8911823cc0ee1359e047f91e5..0000000000000000000000000000000000000000 --- a/libs/shared/lib/vis/configuration/interactions/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export type { Interaction, InteractionTypes, InteractionProps } from './interaction.types'; -export { default as InteractionPanel } from './interaction'; diff --git a/libs/shared/lib/vis/configuration/interactions/interaction.tsx b/libs/shared/lib/vis/configuration/interactions/interaction.tsx deleted file mode 100644 index 5d25826ebfba7ce5ed00b37114a89276b489f1d7..0000000000000000000000000000000000000000 --- a/libs/shared/lib/vis/configuration/interactions/interaction.tsx +++ /dev/null @@ -1,12 +0,0 @@ -import React from 'react'; -import { PanelProps } from '../panel/panel.types'; - -export default function InteractionPanel({ state, update }: PanelProps) { - return ( - state && ( - <div> - <p>Interaction settings</p> - </div> - ) - ); -} diff --git a/libs/shared/lib/vis/configuration/interactions/interaction.types.ts b/libs/shared/lib/vis/configuration/interactions/interaction.types.ts deleted file mode 100644 index bb81b490925dd4e432865958c757150bdef092e8..0000000000000000000000000000000000000000 --- a/libs/shared/lib/vis/configuration/interactions/interaction.types.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { InputProps } from '@graphpolaris/shared/lib/components/inputs'; - -export type Interaction = InputProps & { condition?: (config: Record<string, any>) => boolean }; - -export type InteractionTypes = { [id: string]: Interaction }; - -export type InteractionProps = { [K in keyof InteractionTypes]?: any }; diff --git a/libs/shared/lib/vis/configuration/panel/panel-header.tsx b/libs/shared/lib/vis/configuration/panel/panel-header.tsx deleted file mode 100644 index f28390360f4b7a1cb846cb69bd5e47e075a62f63..0000000000000000000000000000000000000000 --- a/libs/shared/lib/vis/configuration/panel/panel-header.tsx +++ /dev/null @@ -1,37 +0,0 @@ -import React from 'react'; -import { FormTitle } from '@graphpolaris/shared/lib/components/forms'; -import { AutoAwesome, CandlestickChart, LockOpen, Settings } from '@mui/icons-material'; -import { useVisualization } from '@graphpolaris/shared/lib/data-access'; -import PanelItem from './panel-item'; - -type Props = { - onClose: any; - activeTab: number; - setActiveTab: any; -}; - -export default function PanelHeader({ onClose, activeTab, setActiveTab }: Props) { - const vis = useVisualization(); - - let encodingsExist: boolean = !!vis.settings[vis.activeVisualization || '']?.encodings || false; - let settingsExist: boolean = !!vis.settings[vis.activeVisualization || '']?.settings || false; - let interactionsExist: boolean = !!vis.settings[vis.activeVisualization || '']?.interactions || false; - - return ( - <div className="flex flex-col pt-2 bg-secondary-100"> - <FormTitle title="Settings" onClose={onClose} /> - <ul className="flex flex-wrap pt-4 pl-5 text-sm font-medium text-center text-gray-500 dark:text-gray-400"> - {encodingsExist && ( - <PanelItem active={activeTab === 0} onClick={() => setActiveTab(0)} icon={<AutoAwesome />} tooltip="Encodings" /> - )} - {settingsExist && <PanelItem active={activeTab === 1} onClick={() => setActiveTab(1)} icon={<Settings />} tooltip="Settings" />} - {interactionsExist && ( - <PanelItem active={activeTab === 2} onClick={() => setActiveTab(2)} icon={<CandlestickChart />} tooltip="Interactions" /> - )} - {(settingsExist || encodingsExist || interactionsExist) && ( - <PanelItem active={activeTab === 3} onClick={() => setActiveTab(3)} icon={<LockOpen />} tooltip="Advanced" /> - )} - </ul> - </div> - ); -} diff --git a/libs/shared/lib/vis/configuration/panel/panel-item.tsx b/libs/shared/lib/vis/configuration/panel/panel-item.tsx deleted file mode 100644 index 05623c8f0fbfbcd59f72bb6dafdfe6327b66fc30..0000000000000000000000000000000000000000 --- a/libs/shared/lib/vis/configuration/panel/panel-item.tsx +++ /dev/null @@ -1,27 +0,0 @@ -import React from 'react'; -import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '@graphpolaris/shared/lib/components/tooltip'; - -type PanelItemProps = { - active: boolean; - onClick: () => void; - icon: JSX.Element; - tooltip: string; -}; - -export default function PanelItem({ active, onClick, icon, tooltip }: PanelItemProps) { - return ( - <li - className={`me-2 inline-block bg-secondary-100 cursor-pointer ${active && 'border-b-2 border-primary-200 text-primary-200'}`} - onClick={onClick} - > - <TooltipProvider delayDuration={0}> - <Tooltip> - <TooltipTrigger>{icon}</TooltipTrigger> - <TooltipContent side={'top'}> - <p>{tooltip}</p> - </TooltipContent> - </Tooltip> - </TooltipProvider> - </li> - ); -} diff --git a/libs/shared/lib/vis/configuration/panel/panel.tsx b/libs/shared/lib/vis/configuration/panel/panel.tsx deleted file mode 100644 index 62ea4bca2a9d84db836b604ad5ed88a56ef5fe50..0000000000000000000000000000000000000000 --- a/libs/shared/lib/vis/configuration/panel/panel.tsx +++ /dev/null @@ -1,82 +0,0 @@ -import React, { useState, useEffect } from 'react'; -import { FormActions, FormBody, FormCard, FormDiv, FormHBar } from '@graphpolaris/shared/lib/components/forms'; -import { DialogProps } from '@graphpolaris/shared/lib/components/Dialog'; -import PanelHeader from './panel-header'; -import { SettingsPanel } from '../settings'; -import { AdvancedPanel } from '../advanced'; -import { EncodingPanel } from '../encodings'; -import { InteractionPanel } from '../interactions'; -import { useAppDispatch, useVisualization } from '@graphpolaris/shared/lib/data-access'; -import { updateConfiguration } from '@graphpolaris/shared/lib/data-access/store/visualizationSlice'; -import { ConfigTypes, Configuration } from './panel.types'; - -export default function VisualizationDialog(props: DialogProps) { - const dispatch = useAppDispatch(); - const vis = useVisualization(); - const [activeTab, setActiveTab] = useState<number>(1); - const [configuration, setConfiguration] = useState<Configuration>({}); - - useEffect(() => { - if (vis.activeVisualization) { - setConfiguration({ - general: vis.settings.general ?? {}, - settings: vis.settings[vis.activeVisualization]?.settings ?? {}, - encodings: vis.settings[vis.activeVisualization]?.encodings ?? {}, - interactions: vis.settings[vis.activeVisualization]?.interactions ?? {}, - }); - } - }, [vis]); - - const handlePanelUpdate = (configType: ConfigTypes, key: string, value: any) => { - if (configType === 'configuration') { - setConfiguration(value); - } else { - setConfiguration((prevState) => ({ - ...prevState, - [configType]: { - [key]: value, - ...prevState[configType], - }, - })); - } - }; - - const handleSubmit = (e: React.FormEvent<HTMLFormElement>) => { - e.preventDefault(); - dispatch(updateConfiguration(configuration)); - props.onClose(); - }; - - return ( - <> - {props.open && ( - <FormDiv> - <FormCard> - <FormBody onSubmit={handleSubmit}> - <PanelHeader onClose={props.onClose} activeTab={activeTab} setActiveTab={setActiveTab} /> - {activeTab === 0 && ( - <EncodingPanel state={configuration.encodings} update={(key, value) => handlePanelUpdate('encodings', key, value)} /> - )} - {activeTab === 1 && ( - <SettingsPanel - state={{ general: configuration.general, settings: configuration.settings }} - update={(configType, key, value) => handlePanelUpdate(configType, key, value)} - /> - )} - {activeTab === 2 && ( - <InteractionPanel - state={configuration.interactions} - update={(key, value) => handlePanelUpdate('interactions', key, value)} - /> - )} - {activeTab === 3 && <AdvancedPanel state={configuration} update={(value) => handlePanelUpdate('configuration', '', value)} />} - - <FormHBar /> - <FormActions onClose={props.onClose} /> - </FormBody> - </FormCard> - </FormDiv> - )} - </> - ); -} diff --git a/libs/shared/lib/vis/configuration/panel/panel.types.ts b/libs/shared/lib/vis/configuration/panel/panel.types.ts deleted file mode 100644 index 46edb57481764e8df92416cb6ec9e626205b22e0..0000000000000000000000000000000000000000 --- a/libs/shared/lib/vis/configuration/panel/panel.types.ts +++ /dev/null @@ -1,13 +0,0 @@ -export type ConfigTypes = 'configuration' | 'general' | 'settings' | 'encodings' | 'interactions'; - -export type PanelProps = { - state: any; - update: (configType: ConfigTypes, key: string, value: any) => void; -}; - -export type Configuration = { - general?: any; - settings?: any; - encodings?: any; - interactions?: any; -}; diff --git a/libs/shared/lib/vis/configuration/settings/index.ts b/libs/shared/lib/vis/configuration/settings/index.ts deleted file mode 100644 index 825e0031f676fb1604549de65832fce77fe9de90..0000000000000000000000000000000000000000 --- a/libs/shared/lib/vis/configuration/settings/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export type { Setting, SettingTypes, SettingProps } from './settings.types'; -export { default as SettingsPanel } from './settings'; diff --git a/libs/shared/lib/vis/configuration/settings/settings.tsx b/libs/shared/lib/vis/configuration/settings/settings.tsx deleted file mode 100644 index 1255476dca7bf009290291bbe241e0a4103b9e09..0000000000000000000000000000000000000000 --- a/libs/shared/lib/vis/configuration/settings/settings.tsx +++ /dev/null @@ -1,44 +0,0 @@ -import React from 'react'; -import { FormControl } from '@graphpolaris/shared/lib/components/forms'; -import Input from '@graphpolaris/shared/lib/components/inputs'; -import { PanelProps } from '../panel/panel.types'; - -export default function SettingsPanel({ state, update }: PanelProps) { - return ( - state && ( - <div> - <div> - {Object.keys(state?.general).map((key) => ( - <FormControl key={key}> - <Input - {...state.general[key]} - value={state.general[key]?.value as any} - onChange={(value: any) => update('general', key, { ...state[key].general, value: value })} - /> - </FormControl> - ))} - </div> - - <div> - {Object.keys(state?.settings).map((key) => { - const currentSetting = state.settings[key]; - const shouldShowSetting = currentSetting.condition ? currentSetting.condition?.(state.settings) : true; - return ( - shouldShowSetting && ( - <div key={key} className="bg-secondary-50 p-2 m-2"> - <FormControl> - <Input - {...state.settings[key]} - value={state.settings[key]?.value as any} - onChange={(value: any) => update('settings', key, { ...state.settings[key], value: value })} - /> - </FormControl> - </div> - ) - ); - })} - </div> - </div> - ) - ); -} diff --git a/libs/shared/lib/vis/configuration/settings/settings.types.ts b/libs/shared/lib/vis/configuration/settings/settings.types.ts deleted file mode 100644 index 9c93ba813d26201c93681f9346a39fd64914d5a9..0000000000000000000000000000000000000000 --- a/libs/shared/lib/vis/configuration/settings/settings.types.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { InputProps } from '@graphpolaris/shared/lib/components/inputs'; - -export type Setting = InputProps & { - condition?: (config: Record<string, any>) => boolean; - description?: string; -}; - -export type SettingTypes = { [id: string]: Setting }; - -export type SettingProps = { [K in keyof SettingTypes]: any }; diff --git a/libs/shared/lib/vis/manager.tsx b/libs/shared/lib/vis/manager.tsx new file mode 100644 index 0000000000000000000000000000000000000000..8702e6519b0e1ec0f1b972b81e7dc9005269275e --- /dev/null +++ b/libs/shared/lib/vis/manager.tsx @@ -0,0 +1,124 @@ +import React, { useState, useEffect } from 'react'; +import { VISComponentType } from './types'; +import { updateVisualization } from '../data-access/store/visualizationSlice'; +import { useAppDispatch, useGraphQueryResult, useGraphQueryResultMeta, useML, useSchemaGraph, useVisualization } from '../data-access'; +import { FormActions, FormBody, FormCard, FormDiv, FormHBar, FormTitle } from '../components'; + +export const Visualizations: Record<string, Function> = { + TableVis: () => import('./visualizations/tablevis/tablevis'), + PaohVis: () => import('./visualizations/paohvis/paohvis'), + RawJSONVis: () => import('./visualizations/rawjsonvis/rawjsonvis'), + NodeLinkVis: () => import('./visualizations/nodelinkvis/nodelinkvis'), + // MapVis: () => import('./visualizations/mapvis/mapvis'), + MatrixVis: () => import('./visualizations/matrixvis/matrixvis'), +}; + +export const useVisualizationManager = () => { + const dispatch = useAppDispatch(); + const ml = useML(); + const schema = useSchemaGraph(); + const graphQueryResult = useGraphQueryResult(); + const meta = useGraphQueryResultMeta(); + const { active, visualizations } = useVisualization(); + + const [configuration, setConfiguration] = useState<any>(); + const [visualization, setVisualization] = useState<VISComponentType>(); + const [settings, setSettings] = useState<any>(null); + const [settingsOpen, setSettingsOpen] = useState<boolean>(false); + const [hoverItem, setHoverItem] = useState<any>(); + const [selected, setSelected] = useState<any>(); + + useEffect(() => { + loadVisualization(); + }, [active]); + + const loadVisualization = async () => { + if (active && Visualizations[active]) { + const componentModule = await Visualizations[active](); + const component = componentModule.default; + setVisualization(component); + setSettings(null); + + if (!(active in Object.keys(visualizations))) { + // Visualization doesn't yet exist so add its configuration + const configuration = component.configuration; + updateSettings(configuration); + } + + setConfiguration(visualizations[active]); + } + }; + + const handleHover = (item: any) => { + setHoverItem(item); + }; + + const handleSelect = (item: any) => { + setSelected(item); + }; + + const updateSettings = (newSettings: any) => { + if (active) { + const updatedSettings = { ...configuration, ...newSettings }; + setSettings(updatedSettings); + dispatch(updateVisualization({ id: active, settings: updatedSettings })); + } + }; + + const openSettingsMenu = () => { + setSettingsOpen(!settingsOpen); + }; + + const renderSettings = () => { + return ( + visualization?.settings && + settings && + settingsOpen && ( + <FormDiv> + <FormCard> + <FormBody + onSubmit={(e) => { + e.preventDefault(); + setSettingsOpen(false); + }} + > + <FormTitle title="Visualization settings" onClose={() => {}} /> + <FormHBar /> + <visualization.settings configuration={settings} graph={meta} updateSettings={updateSettings} /> + <FormHBar /> + <FormActions + onClose={() => { + setSettingsOpen(false); + }} + /> + </FormBody> + </FormCard> + </FormDiv> + ) + ); + }; + + const renderComponent = () => { + return ( + visualization?.component && + settings && ( + <visualization.component + data={graphQueryResult} + schema={schema} + ml={ml} + settings={settings} + dispatch={dispatch} + handleHover={handleHover} + handleSelect={handleSelect} + /> + ) + ); + }; + + return { + active, + renderComponent, + renderSettings, + openSettingsMenu, + }; +}; diff --git a/libs/shared/lib/vis/types.ts b/libs/shared/lib/vis/types.ts index cf79a63d5f46d182445ac9ce591e329258d58bcc..6f174b7869deb06e99528cb53a3c5bb497b0d89b 100644 --- a/libs/shared/lib/vis/types.ts +++ b/libs/shared/lib/vis/types.ts @@ -2,38 +2,24 @@ import { GraphQueryResult } from '../data-access/store/graphQueryResultSlice'; import { ML } from '../data-access/store/mlSlice'; import { SchemaGraph } from '../schema'; import type { AppDispatch } from '../data-access'; -import { InputProps } from '../components/inputs'; import { FC } from 'react'; -import { EncodingProps, EncodingTypes } from './configuration/encodings'; -import { SettingProps, SettingTypes } from './configuration/settings'; -import { InteractionProps, InteractionTypes } from './configuration/interactions'; -import { Visualizations } from './visualizationManager'; +import { Visualizations } from './manager'; -export type globalConfigTypes = { [id: string]: InputProps }; - -export type globalConfigPropTypes = { [K in keyof globalConfigTypes]: any }; - -export type VisualizationConfiguration = { - settings?: SettingTypes; - encodings?: EncodingTypes; - interactions?: InteractionTypes; -}; +export type VisualizationConfiguration = { [id: string]: any }; export type VISComponentType = { displayName: keyof typeof Visualizations; - VIS: FC<any>; - settings?: SettingTypes; - encodings?: EncodingTypes; - interactions?: InteractionTypes; + component: FC<any>; + settings: FC<any>; + configuration: { [id: string]: any }; }; export type VisualizationPropTypes = { data: GraphQueryResult; schema: SchemaGraph; ml: ML; + configuration: VisualizationConfiguration; dispatch: AppDispatch; - globalConfig: globalConfigPropTypes; - settings: SettingProps; - encodings?: EncodingProps; - interactions?: InteractionProps; + handleHover: (val: any) => void; + handleSelect: (val: any) => void; }; diff --git a/libs/shared/lib/vis/visualizationManager.tsx b/libs/shared/lib/vis/visualizationManager.tsx deleted file mode 100644 index f9a963ddffcabf219deeadbd8431d30eecbe8ce3..0000000000000000000000000000000000000000 --- a/libs/shared/lib/vis/visualizationManager.tsx +++ /dev/null @@ -1,104 +0,0 @@ -import React, { useEffect, useState } from 'react'; -import { useAppDispatch } from '@graphpolaris/shared/lib/data-access'; -import { addVisualization } from '../data-access/store/visualizationSlice'; -import { useGraphQueryResult, useML, useSchemaGraph, useVisualization } from '@graphpolaris/shared/lib/data-access/store/hooks'; -import { VISComponentType, globalConfigPropTypes } from './types'; - -export const Visualizations: Record<string, Function> = { - TableVis: () => import('./visualizations/tablevis/tablevis'), - PaohVis: () => import('./visualizations/paohvis/paohvis'), - RawJSONVis: () => import('./visualizations/rawjsonvis/rawjsonvis'), - NodeLinkVis: () => import('./visualizations/nodelinkvis/nodelinkvis'), - // MapVis: () => import('./visualizations/mapvis/mapvis'), - MatrixVis: () => import('./visualizations/matrixvis/matrixvis'), -}; - -export const VisualizationManager = () => { - const dispatch = useAppDispatch(); - const vis = useVisualization(); - const graphQueryResult = useGraphQueryResult(); - const schema = useSchemaGraph(); - const ml = useML(); - - const [visualizationComponent, setVisualizationComponent] = useState<VISComponentType>(); - - useEffect(() => { - if (vis.activeVisualization && vis.activeVisualization in Visualizations) { - Visualizations[vis.activeVisualization]().then((r: any) => { - setVisualizationComponent(r.default); - }); - } - }, [vis.activeVisualization]); - - useEffect(() => { - if (visualizationComponent) { - const { displayName, settings = {}, encodings = {}, interactions = {} } = visualizationComponent; - dispatch(addVisualization({ id: displayName, settings, encodings, interactions })); - } - }, [visualizationComponent]); - - if (!visualizationComponent) { - return <></>; - } - - const globalConfig: globalConfigPropTypes = vis.settings.general - ? Object.keys(vis.settings.general).reduce((propsObject, val) => { - return { - ...propsObject, - [val]: vis.settings.general[val].value, - }; - }, {}) - : {}; - - let visSettings = {}; - let visEncodings = {}; - let visInteractions = {}; - - if (vis.activeVisualization && vis.settings[vis.activeVisualization]) { - const activeVisSettings = vis.settings[vis.activeVisualization]?.settings; - const activeVisEncodings = vis.settings[vis.activeVisualization]?.encodings; - const activeVisInteractions = vis.settings[vis.activeVisualization]?.interactions; - - visSettings = Object.keys(activeVisSettings ?? {}).reduce((propsObject, val) => { - return { - ...propsObject, - [val]: activeVisSettings?.[val]?.value, - }; - }, {}); - - visEncodings = Object.keys(activeVisEncodings ?? {}).reduce((propsObject, val) => { - return { - ...propsObject, - [val]: activeVisEncodings?.[val]?.marking, - }; - }, {}); - - visInteractions = Object.keys(activeVisInteractions ?? {}).reduce((propsObject, val) => { - return { - ...propsObject, - [val]: activeVisInteractions?.[val]?.value, - }; - }, {}); - } - - try { - return ( - vis.activeVisualization && ( - <div className="w-full h-full"> - <visualizationComponent.VIS - data={graphQueryResult} - schema={schema} - ml={ml} - dispatch={dispatch} - globalConfig={globalConfig} - settings={visSettings} - encodings={visEncodings} - interactions={visInteractions} - /> - </div> - ) - ); - } catch (error) { - return <div className="w-full h-full flex items-center justify-center">Something went wrong in the visualization component.</div>; - } -}; diff --git a/libs/shared/lib/vis/visualizationPanel.tsx b/libs/shared/lib/vis/visualizationPanel.tsx index ab722fc2c3c9fadaa52e4bb66ce61fe440aed42b..a2e6013a4291b731cafa9bcb7dae7939796d3710 100644 --- a/libs/shared/lib/vis/visualizationPanel.tsx +++ b/libs/shared/lib/vis/visualizationPanel.tsx @@ -5,19 +5,17 @@ import { setActiveVisualization } from '@graphpolaris/shared/lib/data-access/sto import { DropdownItem, DropdownItemContainer } from '@graphpolaris/shared/lib/components/dropdowns'; import ControlContainer from '@graphpolaris/shared/lib/components/controls'; import { Button } from '@graphpolaris/shared/lib/components/buttons'; - -import { VisualizationDialog } from './configuration'; import { Settings as SettingsIcon, Apps as AppsIcon, Fullscreen } from '@mui/icons-material'; -import { VisualizationManager, Visualizations } from './visualizationManager'; import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '../components/tooltip'; +import { useVisualizationManager, Visualizations } from './manager'; export const VisualizationPanel = () => { const graphQueryResult = useGraphQueryResult(); const query = useQuerybuilderGraph(); const dispatch = useAppDispatch(); const vis = useVisualization(); + const manager = useVisualizationManager(); const [visDropdownOpen, setVisDropdownOpen] = useState<boolean>(false); - const [showVisSettings, setShowVisSettings] = useState<boolean>(false); const visDropdownRef = useRef<HTMLDivElement>(null); useEffect(() => { @@ -27,19 +25,17 @@ export const VisualizationPanel = () => { } }; if (visDropdownOpen) document.addEventListener('mousedown', handleClickOutside); - return () => { - document.removeEventListener('mousedown', handleClickOutside); - }; + return () => document.removeEventListener('mousedown', handleClickOutside); }, [visDropdownOpen]); return ( <div className="vis-panel h-full w-full overflow-y-auto" style={graphQueryResult.nodes.length === 0 ? { overflow: 'hidden' } : {}}> - <VisualizationDialog open={showVisSettings} onClose={() => setShowVisSettings(false)} /> + {manager.renderSettings()} <div className="sticky top-0 flex items-center justify-between z-[2] py-0 px-2 bg-secondary-100 border-b border-secondary-200"> - <h1 className="text-xs font-semibold text-secondary-800">{vis.activeVisualization} visualization</h1> + <h1 className="text-xs font-semibold text-secondary-800">{vis.active} visualization</h1> <ControlContainer> <TooltipProvider delayDuration={0}> - <Tooltip disabled={showVisSettings}> + <Tooltip> <TooltipTrigger asChild> <Button type="secondary" @@ -48,11 +44,11 @@ export const VisualizationPanel = () => { iconComponent={<SettingsIcon />} onClick={() => { // TODO - // setShowVisSettings(!showVisSettings); + manager.openSettingsMenu(); }} /> </TooltipTrigger> - <TooltipContent side={'bottom'} disabled={showVisSettings}> + <TooltipContent side={'bottom'}> <p>Visualization settings</p> </TooltipContent> </Tooltip> @@ -92,7 +88,6 @@ export const VisualizationPanel = () => { )} </ControlContainer> </div> - {graphQueryResult.queryingBackend ? ( <div className="w-full h-full flex flex-col items-center justify-center overflow-hidden"> <LoadingSpinner>Querying backend...</LoadingSpinner> @@ -103,9 +98,7 @@ export const VisualizationPanel = () => { {query.nodes.length > 0 ? <p>Query resulted in empty dataset</p> : <p>Query for data to visualize</p>} </div> ) : ( - <div className="w-full h-full"> - <VisualizationManager /> - </div> + <div className="w-full h-full">{manager.renderComponent()}</div> )} </div> ); diff --git a/libs/shared/lib/vis/visualizations/mapvis/mapvis.tsx b/libs/shared/lib/vis/visualizations/mapvis/mapvis.tsx index 20d21800528020cc56ce8bbbdb5892794bcc930f..d9f84a2cc491d570976c5318bde1f4babee9b79a 100644 --- a/libs/shared/lib/vis/visualizations/mapvis/mapvis.tsx +++ b/libs/shared/lib/vis/visualizations/mapvis/mapvis.tsx @@ -3,10 +3,13 @@ import { MapPanel, LayerPanel } from './components'; import GraphModel from './graphModel'; import { GraphType, Layer } from './Types'; import { VISComponentType, VisualizationPropTypes } from '../../types'; +import { GraphMetaData } from '@graphpolaris/shared/lib/data-access/statistics'; export type MapProps = {}; -export default function MapVis({ data, schema, settings }: VisualizationPropTypes) { +const configuration: MapProps = {}; + +export default function MapVis({ data }: VisualizationPropTypes) { const [layers, setLayers] = React.useState<Layer[]>([]); const [showFilter, setShowFilter] = React.useState<boolean>(false); @@ -31,8 +34,21 @@ export default function MapVis({ data, schema, settings }: VisualizationPropType ); } +const MapSettings = ({ + configuration, + graph, + updateSettings, +}: { + configuration: MapProps; + graph: GraphMetaData; + updateSettings: (val: any) => void; +}) => { + return <div>To be implemented</div>; +}; + export const MapComponent: VISComponentType = { - displayName: 'Map', - VIS: MapVis, - settings: {}, + displayName: 'MapVis', + component: MapVis, + settings: MapSettings, + configuration: configuration, }; diff --git a/libs/shared/lib/vis/visualizations/matrixvis/matrixvis.tsx b/libs/shared/lib/vis/visualizations/matrixvis/matrixvis.tsx index 911a5fbae1729c34867306e5dff68b45101c2cec..86c3877bb6477d158b67855d56479c9ee1244d77 100644 --- a/libs/shared/lib/vis/visualizations/matrixvis/matrixvis.tsx +++ b/libs/shared/lib/vis/visualizations/matrixvis/matrixvis.tsx @@ -4,8 +4,18 @@ import { GraphQueryResult } from '../../../data-access/store'; import { LinkType, NodeType } from './Types'; import { MatrixPixi } from './components/MatrixPixi'; import { VisualizationPropTypes, VISComponentType } from '../../types'; +import Input from '@graphpolaris/shared/lib/components/inputs'; +import { GraphMetaData } from '@graphpolaris/shared/lib/data-access/statistics'; -export const MatrixVis = React.memo(({ data, ml, settings }: VisualizationPropTypes) => { +export interface MatrixVisProps { + marks: string; +} + +const configuration: MatrixVisProps = { + marks: 'rect', +}; + +export const MatrixVis = React.memo(({ data, ml, configuration }: VisualizationPropTypes) => { const ref = useRef<HTMLDivElement>(null); const [graph, setGraph] = useImmer<GraphQueryResult | undefined>(undefined); const [highlightNodes, setHighlightNodes] = useState<NodeType[]>([]); @@ -20,25 +30,39 @@ export const MatrixVis = React.memo(({ data, ml, settings }: VisualizationPropTy return ( <> <div className="h-full w-full overflow-hidden" ref={ref}> - <MatrixPixi graph={graph} highlightNodes={highlightNodes} highlightedLinks={highlightedLinks} localConfig={settings} /> + <MatrixPixi graph={graph} highlightNodes={highlightNodes} highlightedLinks={highlightedLinks} localConfig={configuration} /> </div> </> ); }); -const displayName = 'MatrixVis'; +const MatrixSettings = ({ + configuration, + graph, + updateSettings, +}: { + configuration: MatrixVisProps; + graph: GraphMetaData; + updateSettings: (val: any) => void; +}) => { + return ( + <div> + <Input + type="dropdown" + label="Configure marks" + value={configuration.marks} + options={['rect', 'circle']} + onChange={(val) => updateSettings({ marks: val })} + /> + </div> + ); +}; export const MatrixVisComponent: VISComponentType = { - displayName: displayName, - VIS: MatrixVis, - settings: { - marks: { - type: 'dropdown', - options: ['rect', 'circle'], - value: 'rect', - label: 'Configure Marks', - }, - }, + displayName: 'MatrixVis', + component: MatrixVis, + settings: MatrixSettings, + configuration: configuration, }; export default MatrixVisComponent; diff --git a/libs/shared/lib/vis/visualizations/nodelinkvis/nodelinkvis.tsx b/libs/shared/lib/vis/visualizations/nodelinkvis/nodelinkvis.tsx index 6c86c681c506e798f44afe892a62be241c18a40d..8d1d36db25dea6eaafd30b6759a7c99ca42b4882 100644 --- a/libs/shared/lib/vis/visualizations/nodelinkvis/nodelinkvis.tsx +++ b/libs/shared/lib/vis/visualizations/nodelinkvis/nodelinkvis.tsx @@ -5,6 +5,18 @@ import { parseQueryResult } from './components/query2NL'; import { useImmer } from 'use-immer'; import { ML, setShortestPathSource, setShortestPathTarget } from '../../../data-access/store/mlSlice'; import { VisualizationPropTypes, VISComponentType } from '../../types'; +import Input from '@graphpolaris/shared/lib/components/inputs'; +import { GraphMetaData } from '@graphpolaris/shared/lib/data-access/statistics'; + +export interface NodelinkVisProps { + layout: string; + showPopUpOnHover: boolean; +} + +const configuration: NodelinkVisProps = { + layout: 'Force directed', + showPopUpOnHover: true, +}; export const NodeLinkVis = React.memo(({ data, ml, dispatch }: VisualizationPropTypes) => { const ref = useRef<HTMLDivElement>(null); @@ -18,7 +30,7 @@ export const NodeLinkVis = React.memo(({ data, ml, dispatch }: VisualizationProp parseQueryResult(data, ml, { defaultX: (ref.current?.clientWidth || 1000) / 2, defaultY: (ref.current?.clientHeight || 1000) / 2, - }) + }), ); } }, [data, ml]); @@ -70,48 +82,39 @@ export const NodeLinkVis = React.memo(({ data, ml, dispatch }: VisualizationProp ); }); +const NodelinkSettings = ({ + configuration, + graph, + updateSettings, +}: { + configuration: NodelinkVisProps; + graph: GraphMetaData; + updateSettings: (val: any) => void; +}) => { + return ( + <div> + <Input + type="dropdown" + label="Layout" + value={configuration.layout} + options={['Force directed']} + onChange={(val) => updateSettings({ layout: val })} + /> + <Input + type="boolean" + label="Show pop-up on hover" + value={configuration.showPopUpOnHover} + onChange={(val) => updateSettings({ showPopUpOnHover: val })} + /> + </div> + ); +}; + export const NodeLinkComponent: VISComponentType = { displayName: 'NodeLinkVis', - VIS: NodeLinkVis, - settings: { - layout: { - value: 'Force directed', - type: 'dropdown', - label: 'Layout', - options: ['Force directed'], - description: 'Select a layout that is used for the visualization', - }, - }, - encodings: { - color: { - label: 'Node color', - element: 'node', - dimension: ['categorical', 'numerical'], - selector: 'Color', - description: 'Select a color for the nodes', - }, - shape: { - label: 'Node shape', - element: 'node', - dimension: ['categorical'], - selector: 'Shape', - description: 'Select a shape for the nodes', - }, - size: { - label: 'Node size', - element: 'node', - dimension: ['categorical'], - selector: 'Size', - description: 'Select a size for the nodes', - }, - }, - interactions: { - showPopUpOnHover: { - value: true, - type: 'boolean', - label: 'Show pop-up', - }, - }, + component: NodeLinkVis, + settings: NodelinkSettings, + configuration: configuration, }; export default NodeLinkComponent; diff --git a/libs/shared/lib/vis/visualizations/paohvis/paohvis.tsx b/libs/shared/lib/vis/visualizations/paohvis/paohvis.tsx index 90544d35de4d2f627d6cc66c5add48507556694c..d18c548ce060280b444cce0cb7d52fb01fd6bdc6 100644 --- a/libs/shared/lib/vis/visualizations/paohvis/paohvis.tsx +++ b/libs/shared/lib/vis/visualizations/paohvis/paohvis.tsx @@ -27,7 +27,8 @@ import { HyperEdgeRange } from './components/HyperEdgesRange'; import ToPaohvisDataParserUseCase from './utils/ToPaohvisDataParserUsecase'; import MakePaohvisMenu from './components/MakePaohvisMenu'; import { RowLabelColumn } from './components/RowLabelColumn'; -import { VISComponentType } from '../../types'; +import { VISComponentType, VisualizationPropTypes } from '../../types'; +import { GraphMetaData } from '@graphpolaris/shared/lib/data-access/statistics'; type PaohvisViewModelState = { rowHeight: number; @@ -63,7 +64,13 @@ export type PaohVisProps = { data?: PaohvisData; }; -export const PaohVis = (props: PaohVisProps) => { +const configuration: PaohVisProps = { + rowHeight: 30, + hyperedgeColumnWidth: 20, + gapBetweenRanges: 5, +}; + +export const PaohVis = ({ configuration }: VisualizationPropTypes) => { const svgRef = useRef<SVGSVGElement>(null); const graphQueryResult = useGraphQueryResult(); const schema = useSchemaGraph(); @@ -132,7 +139,7 @@ export const PaohVis = (props: PaohVisProps) => { entityOrRelationType: string, attribute: string, predicate: string, - compareValue: string + compareValue: string, ): void { const attributeName: string = attribute.split(':')[0]; const attributeType: string = attribute.split(':')[1]; @@ -373,7 +380,7 @@ export const PaohVis = (props: PaohVisProps) => { relationName: string, isEntityVerticalEqualToRelationFrom: boolean, chosenAttribute: Attribute, - nodeOrder: PaohvisNodeOrder + nodeOrder: PaohvisNodeOrder, ): void { setViewModel((draft) => { draft.entityVertical = entityVertical; @@ -512,7 +519,7 @@ export const PaohVis = (props: PaohVisProps) => { const hyperEdgeRanges = data.hyperEdgeRanges; const rowLabelColumnWidth = data.maxRowLabelWidth; - const hyperedgeColumnWidth = props.hyperedgeColumnWidth; + const hyperedgeColumnWidth = configuration.hyperedgeColumnWidth; //calculate yOffset let maxColWidth = 0; @@ -532,7 +539,7 @@ export const PaohVis = (props: PaohVisProps) => { const columnLabelWidth = Math.cos(columnLabelAngleInRadians) * getWidthOfText(hyperEdgeRange.rangeText, styles.tableFontFamily, styles.tableFontSize, styles.tableFontWeight); - const columnWidth = hyperEdgeRange.hyperEdges.length * hyperedgeColumnWidth + props.gapBetweenRanges * 3; + const columnWidth = hyperEdgeRange.hyperEdges.length * hyperedgeColumnWidth + configuration.gapBetweenRanges * 3; tableWidth += columnWidth; @@ -559,12 +566,12 @@ export const PaohVis = (props: PaohVisProps) => { colOffset={colOffset} xOffset={rowLabelColumnWidth} yOffset={yOffset} - rowHeight={props.rowHeight} + rowHeight={configuration.rowHeight} hyperedgeColumnWidth={hyperedgeColumnWidth} - gapBetweenRanges={props.gapBetweenRanges} + gapBetweenRanges={configuration.gapBetweenRanges} onMouseEnter={onMouseEnterHyperEdge} onMouseLeave={onMouseLeaveHyperEdge} - /> + />, ); colOffset += hyperEdgeRange.hyperEdges.length; }); @@ -591,7 +598,7 @@ export const PaohVis = (props: PaohVisProps) => { ref={svgRef} style={{ width: tableWidthWithExtraColumnLabelWidth, - height: yOffset + (data.rowLabels.length + 1) * props.rowHeight, + height: yOffset + (data.rowLabels.length + 1) * configuration.rowHeight, }} > <RowLabelColumn // render the PAOHvis itself @@ -599,7 +606,7 @@ export const PaohVis = (props: PaohVisProps) => { onMouseLeave={onMouseLeaveRow} titles={data.rowLabels} width={rowLabelColumnWidth} - rowHeight={props.rowHeight} // viewModel.rowHeight? + rowHeight={configuration.rowHeight} // viewModel.rowHeight? yOffset={yOffset} /> {hyperEdgeRangeColumns} @@ -638,10 +645,23 @@ export const PaohVis = (props: PaohVisProps) => { ); }; +const PaohSettings = ({ + configuration, + graph, + updateSettings, +}: { + configuration: PaohVisProps; + graph: GraphMetaData; + updateSettings: (val: any) => void; +}) => { + return <div>To be implemented</div>; +}; + export const PaohVisComponent: VISComponentType = { displayName: 'PaohVis', - VIS: PaohVis, - settings: {}, + component: PaohVis, + settings: PaohSettings, + configuration: configuration, }; export default PaohVisComponent; diff --git a/libs/shared/lib/vis/visualizations/rawjsonvis/rawjsonvis.tsx b/libs/shared/lib/vis/visualizations/rawjsonvis/rawjsonvis.tsx index f62cd2a94022d4592188021dc9650224818afbd7..29d370bb7eca54beacd37b8c17100ff3bd2ab03e 100644 --- a/libs/shared/lib/vis/visualizations/rawjsonvis/rawjsonvis.tsx +++ b/libs/shared/lib/vis/visualizations/rawjsonvis/rawjsonvis.tsx @@ -1,10 +1,11 @@ import React, { useEffect } from 'react'; import ReactJSONView from 'react-json-view'; import { VisualizationPropTypes, VISComponentType } from '../../types'; +import { GraphMetaData } from '@graphpolaris/shared/lib/data-access/statistics'; export interface RawJSONVisProps {} -const displayName = 'RawJSONVis'; +const configuration: RawJSONVisProps = {}; export const RawJSONVis = React.memo(({ data }: VisualizationPropTypes) => { return ( @@ -24,9 +25,22 @@ export const RawJSONVis = React.memo(({ data }: VisualizationPropTypes) => { ); }); +const RawJSONSettings = ({ + configuration, + graph, + updateSettings, +}: { + configuration: RawJSONVisProps; + graph: GraphMetaData; + updateSettings: (val: any) => void; +}) => { + return <div>To be implemented</div>; +}; + export const RawJSONComponent: VISComponentType = { displayName: 'RawJSONVis', - VIS: RawJSONVis, - settings: {}, + component: RawJSONVis, + settings: RawJSONSettings, + configuration: configuration, }; export default RawJSONComponent; diff --git a/libs/shared/lib/vis/visualizations/tablevis/tablevis.tsx b/libs/shared/lib/vis/visualizations/tablevis/tablevis.tsx index f1cc8dac14a57ed89f9c1bda0dc1454d167f5920..d740b4a7a77a21a591a7336c783e0bb8e47f0385 100644 --- a/libs/shared/lib/vis/visualizations/tablevis/tablevis.tsx +++ b/libs/shared/lib/vis/visualizations/tablevis/tablevis.tsx @@ -2,13 +2,20 @@ import React, { useMemo, useRef } from 'react'; import { Table, AugmentedNodeAttributes } from './components/Table'; import { SchemaAttribute } from '../../../schema'; import { VisualizationPropTypes, VISComponentType } from '../../types'; +import Input from '@graphpolaris/shared/lib/components/inputs'; +import { GraphMetaData } from '@graphpolaris/shared/lib/data-access/statistics'; export type TableProps = { showBarplot: boolean; itemsPerPage: number; }; -export const TableVis = ({ data, schema, settings }: VisualizationPropTypes) => { +const configuration: TableProps = { + itemsPerPage: 10, + showBarplot: false, +}; + +export const TableVis = ({ data, schema, configuration }: VisualizationPropTypes) => { const ref = useRef<HTMLDivElement>(null); const attributesArray = useMemo<AugmentedNodeAttributes[]>( @@ -24,33 +31,51 @@ export const TableVis = ({ data, schema, settings }: VisualizationPropTypes) => type: Object.fromEntries(types.map((t) => [t.name, t.type])), }; }), - [data.nodes] + [data.nodes], ); return ( <div className="h-full w-full" ref={ref}> {attributesArray.length > 0 && ( - <Table data={attributesArray} itemsPerPage={settings.itemsPerPage} showBarPlot={settings.showBarplot} /> + <Table data={attributesArray} itemsPerPage={configuration.itemsPerPage} showBarPlot={configuration.showBarplot} /> )} </div> ); }; +const TableSettings = ({ + configuration, + graph, + updateSettings, +}: { + configuration: TableProps; + graph: GraphMetaData; + updateSettings: (val: any) => void; +}) => { + return ( + <div> + <Input + type="dropdown" + label="Items per page" + value={configuration.itemsPerPage} + onChange={(val) => updateSettings({ itemsPerPage: val })} + options={[10, 20, 30]} + /> + <Input + type="boolean" + label="Show barplot" + value={configuration.showBarplot} + onChange={(val) => updateSettings({ showBarplot: val })} + /> + </div> + ); +}; + export const TableComponent: VISComponentType = { displayName: 'TableVis', - VIS: TableVis, - settings: { - showBarplot: { - value: true, - type: 'boolean', - label: 'Show barplot', - }, - itemsPerPage: { - value: 10, - type: 'dropdown', - label: 'Items per page', - options: [10, 20, 30], - }, - }, + component: TableVis, + settings: TableSettings, + configuration: configuration, }; + export default TableComponent;