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 (5)
......@@ -182,6 +182,7 @@
}
body.dark-mode {
color-scheme: dark;
--clr-light: var(--clr-neutral-100);
--clr-dark: var(--clr-white);
......
import React, { useState, useEffect } from 'react';
import { Button } from '../buttons'; // Adjust the import path according to your project structure
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '../../components/tooltip';
import { setTheme, Theme } from '@graphpolaris/shared/lib/data-access/store/configSlice';
import { useAppDispatch, useConfig } from '@graphpolaris/shared/lib/data-access/store';
const ColorMode = () => {
// Initialize theme state without setting a default yet
const [theme, setTheme] = useState<string>('');
const config = useConfig();
const dispatch = useAppDispatch();
// Function to update the body class and local storage
// Function to update the body class
const applyTheme = (themeValue: string) => {
document.body.className = themeValue;
localStorage.setItem('theme', themeValue);
};
// Load the theme preference from local storage on initial render
useEffect(() => {
const savedTheme = localStorage.getItem('theme');
if (savedTheme) {
setTheme(savedTheme);
} else {
// Fallback to system preference if no saved theme
// const prefersDarkMode = window.matchMedia('(prefers-color-scheme: dark)').matches;
const prefersDarkMode = false; // TODO: remove once dark theme is fixed
const defaultTheme = prefersDarkMode ? 'dark-mode' : 'light-mode';
setTheme(defaultTheme);
applyTheme(defaultTheme);
}
}, []);
// Update the local storage and body class whenever the theme changes
useEffect(() => {
if (theme) {
applyTheme(theme);
if (config.theme) {
applyTheme(config.theme);
}
}, [theme]);
}, [config.theme]);
// Function to toggle the theme
const toggleTheme = () => {
setTheme((currentTheme) => (currentTheme === 'light-mode' ? 'dark-mode' : 'light-mode'));
const themes = [
Theme.light,
Theme.dark,
]
const newTheme = themes[(themes.indexOf(config.theme) + 1) % themes.length];
dispatch(setTheme(newTheme));
};
const iconComponent = theme === 'dark-mode' ? 'icon-[ic--baseline-dark-mode]' : 'icon-[ic--baseline-light-mode]';
const iconComponent = config.theme === Theme.dark ? 'icon-[ic--baseline-dark-mode]' : 'icon-[ic--baseline-light-mode]';
return (
<TooltipProvider delayDuration={0}>
......@@ -47,7 +39,7 @@ const ColorMode = () => {
<Button variant="ghost" size="sm" iconComponent={iconComponent} onClick={toggleTheme} />
</TooltipTrigger>
<TooltipContent>
<p>{`Switch to ${theme === 'dark-mode' ? 'light' : 'dark'}-mode`}</p>
<p>{`Switch to ${config.theme === Theme.dark ? 'light' : 'dark'}-mode`}</p>
</TooltipContent>
</Tooltip>
</TooltipProvider>
......
import { createSlice, PayloadAction } from '@reduxjs/toolkit';
import type { RootState } from './store';
export enum Theme {
light = 'light-mode',
dark = 'dark-mode',
}
// Define the initial state using that type
export type ConfigStateI = {
theme: Theme;
autoSendQueries: boolean;
errors: string[];
warnings: string[];
......@@ -10,6 +16,7 @@ export type ConfigStateI = {
successes: string[];
};
export const initialState: ConfigStateI = {
theme: localStorage.getItem('theme') as Theme ?? Theme.light,
autoSendQueries: true,
errors: [],
warnings: [],
......@@ -48,10 +55,14 @@ export const configSlice = createSlice({
removeLastWarning: (state) => {
state.warnings.shift();
},
setTheme: (state, action: PayloadAction<Theme>) => {
localStorage.setItem('theme', action.payload);
state.theme = action.payload;
},
},
});
export const { addError, removeLastError, addWarning, removeLastWarning, addSuccess, removeLastSuccess, addInfo, removeLastInfo } =
export const { addError, removeLastError, addWarning, removeLastWarning, addSuccess, removeLastSuccess, addInfo, removeLastInfo, setTheme } =
configSlice.actions;
// Other code such as selectors can use the imported `RootState` type
......
......@@ -2,7 +2,7 @@ import { Edge, GraphQueryResult, Node, useML, useSearchResultData } from '@graph
import { dataColors, visualizationColors } from 'config';
import { Viewport } from 'pixi-viewport';
import { Application, ColorSource, Container, FederatedPointerEvent, Graphics, IPointData, Point, Text } from 'pixi.js';
import { useEffect, useRef, useState } from 'react';
import { useEffect, useRef, useState, useImperativeHandle } from 'react';
import { LinkType, NodeType } from '../types';
import { NLPopup } from './MatrixPopup';
......@@ -26,6 +26,8 @@ import {
type Axis,
} from 'd3';
import { MatrixVisProps } from '../matrixvis';
import { Theme } from '@graphpolaris/shared/lib/data-access/store/configSlice';
import { useConfig } from '@graphpolaris/shared/lib/data-access/store';
const styleMatrixSize = 50;
......@@ -56,6 +58,14 @@ export const MatrixPixi = (props: Props) => {
cellWidth: 100,
};
const globalConfig = useConfig();
useEffect(() => {
if (props.graph) {
setup();
}
}, [globalConfig.theme]);
let columnOrder: string[] = [];
let rowOrder: string[] = [];
......@@ -70,7 +80,13 @@ export const MatrixPixi = (props: Props) => {
const isSetup = useRef(false);
const ml = useML();
// const imperative = useRef<any>(null);
const imperative = useRef<any>(null);
useImperativeHandle(imperative, () => ({
getBackgroundColor() {
// Colors corresponding to .bg-light class
return globalConfig.theme === Theme.dark ? 0x121621 : 0xffffff;
},
}));
let app: Application;
......@@ -257,7 +273,7 @@ export const MatrixPixi = (props: Props) => {
if (app == null) {
app = new Application({
background: 0xffffff,
backgroundAlpha: 0,
antialias: true,
autoDensity: true,
eventMode: 'auto',
......@@ -267,21 +283,23 @@ export const MatrixPixi = (props: Props) => {
}
if (svg.current != null) {
select(svg.current).select('*').remove();
select(svg.current).selectAll('*').remove();
}
columnsContainer.removeChildren();
app.stage.removeChildren();
const size = ref.current?.getBoundingClientRect();
viewport.current = new Viewport({
screenWidth: size?.width || 1000,
screenHeight: size?.height || 1000,
worldWidth: size?.width || 1000,
worldHeight: size?.height || 1000,
stopPropagation: true,
events: app.renderer.events, // the interaction module is important for wheel to work properly when renderer.view is placed or scaled
});
if (viewport.current == null) {
viewport.current = new Viewport({
screenWidth: size?.width || 1000,
screenHeight: size?.height || 1000,
worldWidth: size?.width || 1000,
worldHeight: size?.height || 1000,
stopPropagation: true,
events: app.renderer.events, // the interaction module is important for wheel to work properly when renderer.view is placed or scaled
});
}
app.stage.addChild(viewport.current);
viewport.current.addChild(columnsContainer);
......@@ -362,19 +380,19 @@ export const MatrixPixi = (props: Props) => {
if (!props.graph) throw new Error('Graph is undefined; cannot setup matrix');
const visMapping = []; // TODO type
let colorNeutralString = dataColors.neutral['5'];
let colorNeutralString = globalConfig.theme === Theme.dark ? dataColors.neutral['80'] : dataColors.neutral['15'];
const colorNeutral = Color(colorNeutralString);
// make adjacency
// const adjacenyScale = scaleLinear([dataColors.neutral['5'], tailwindColors.entity.DEFAULT]);
const adjacenyScale = scaleLinear([colorNeutral, visualizationColors.GPSelected.colors[1][0]]);
const adjacenyScale = scaleLinear([colorNeutral, visualizationColors.GPPrimary.colors[1][0]]);
visMapping.push({
attribute: 'adjacency',
encoding: settings.marks,
colorScale: adjacenyScale,
renderFunction: function (i: number, color: ColorSource, gfxContext: Graphics) {
// Clear locally
gfxContext.beginFill(0xffffff, 1);
gfxContext.beginFill(imperative.current.getBackgroundColor(), 1);
gfxContext.drawRect(0, i * config.cellHeight, config.cellWidth, config.cellHeight);
gfxContext.endFill();
......@@ -474,7 +492,7 @@ export const MatrixPixi = (props: Props) => {
.attr('class', 'label')
.style('text-anchor', 'middle')
.style('alignment-baseline', 'hanging')
.style('fill', 'black')
.style('fill', globalConfig.theme === Theme.dark ? 'white' : 'black')
.style('font-size', '14')
.text(label);
......@@ -572,7 +590,7 @@ export const MatrixPixi = (props: Props) => {
.attr('class', 'label')
.style('text-anchor', 'middle')
.style('alignment-baseline', 'hanging')
.style('fill', 'black')
.style('fill', globalConfig.theme === Theme.dark ? 'white' : 'black')
.style('font-size', '14')
.text(label);
......@@ -659,8 +677,8 @@ export const MatrixPixi = (props: Props) => {
bottom: 0,
width: styleMatrixSize,
backdropFilter: 'blur(10px)',
background: 'rgba(255,255,255, 0.5)',
boxShadow: '1px 0px 0px 0px rgba(0,0,0,0.2)',
background: globalConfig.theme === Theme.dark ? 'rgba(0,0,0,0.2)' : 'rgba(255,255,255, 0.5)',
boxShadow: globalConfig.theme === Theme.dark ? '1px 0px 0px 0px rgba(255,255,255,0.2)' : '1px 0px 0px 0px rgba(0,0,0,0.2)',
}}
></div>
<div
......@@ -672,8 +690,8 @@ export const MatrixPixi = (props: Props) => {
right: 0,
height: styleMatrixSize,
backdropFilter: 'blur(10px)',
background: 'rgba(255,255,255, 0.5)',
boxShadow: `${styleMatrixSize}px 0px 0px 0px rgba(0,0,0,0.2)`,
background: globalConfig.theme === Theme.dark ? 'rgba(0,0,0,0.2)' : 'rgba(255,255,255, 0.5)',
boxShadow: globalConfig.theme === Theme.dark ? `${styleMatrixSize}px 1px 0px 0px rgba(255,255,255,0.2)` : `${styleMatrixSize}px 1px 0px 0px rgba(0,0,0,0.2)`,
}}
></div>
<svg
......
......@@ -23,6 +23,8 @@ import { Viewport } from 'pixi-viewport';
import { NodelinkVisProps } from '../nodelinkvis';
import { Tooltip, TooltipContent, TooltipTrigger } from '@graphpolaris/shared/lib/components/tooltip';
import { MovedEvent } from 'pixi-viewport/dist/types';
import { Theme } from '@graphpolaris/shared/lib/data-access/store/configSlice';
import { useConfig } from '@graphpolaris/shared/lib/data-access/store';
type Props = {
onClick: (event?: { node: NodeTypeD3; pos: IPointData }) => void;
......@@ -47,6 +49,12 @@ export const NLPixi = (props: Props) => {
const [quickPopup, setQuickPopup] = useState<{ node: NodeType; pos: IPointData } | undefined>();
const [popups, setPopups] = useState<{ node: NodeTypeD3; pos: IPointData }[]>([]);
const globalConfig = useConfig();
useEffect(() => {
update();
}, [globalConfig.theme]);
const app = useMemo(
() =>
new Application({
......@@ -314,7 +322,12 @@ export const NLPixi = (props: Props) => {
getLinkWidth() {
return props.configuration.edges.width.width || config.LINE_WIDTH_DEFAULT;
}
},
getBackgroundColor() {
// Colors corresponding to .bg-light class
return globalConfig.theme === Theme.dark ? 0x121621 : 0xffffff;
},
}));
function resize() {
......@@ -458,7 +471,7 @@ export const NLPixi = (props: Props) => {
const text = new Text(linkMeta.name, {
fontSize: 60,
fill: config.LINE_COLOR_DEFAULT,
stroke: 0xffffff,
stroke: imperative.current.getBackgroundColor(),
strokeThickness: 30,
});
text.cullable = true;
......@@ -588,7 +601,9 @@ export const NLPixi = (props: Props) => {
} else {
text.rotation = rads;
}
}
text.style.stroke = imperative.current.getBackgroundColor();
};
const updateNodeLabel = (node: NodeTypeD3) => {
if (graph.current.nodes.length > config.LABEL_MAX_NODES) return;
......
import React, { useEffect } from 'react';
import ReactJSONView from 'react-json-view';
import React, { useState, useEffect } from 'react';
import ReactJSONView, { ThemeKeys } from 'react-json-view';
import { VisualizationPropTypes, VISComponentType, VisualizationSettingsPropTypes } from '../../common';
import { SettingsContainer } from '@graphpolaris/shared/lib/vis/components/config';
import { Input } from '@graphpolaris/shared/lib/components/inputs';
import { useConfig } from '@graphpolaris/shared/lib/data-access/store';
import { Theme } from '@graphpolaris/shared/lib/data-access/store/configSlice';
export interface RawJSONVisProps {
theme: string;
......@@ -15,13 +17,28 @@ const settings: RawJSONVisProps = {
};
export const RawJSONVis = React.memo(({ data, settings }: VisualizationPropTypes) => {
const config = useConfig();
let [theme, setTheme] = useState<ThemeKeys>(settings.theme || 'bright:inverted');
useEffect(() => {
if (theme.startsWith('bright')) {
setTheme(config.theme === Theme.dark ? 'bright' : 'bright:inverted');
}
}, [config.theme]);
if (config.theme === Theme.dark && theme === 'bright:inverted') {
setTheme('bright');
}
return (
<ReactJSONView
src={data}
collapsed={1}
quotesOnKeys={false}
style={{ padding: '20px', flexGrow: 1 }}
theme={settings.theme || 'bright:inverted'}
theme={theme}
iconStyle={settings.iconStyle}
enableClipboard={true}
/>
......
......@@ -5,6 +5,7 @@ import { Input } from '@graphpolaris/shared/lib/components/inputs';
import { SettingsContainer } from '@graphpolaris/shared/lib/vis/components/config';
import { Button } from '@graphpolaris/shared/lib/components/buttons';
import { useSearchResultData } from '@graphpolaris/shared/lib/data-access';
import { EntityPill } from '@graphpolaris/shared/lib/components/pills/Pill';
export type TableProps = {
id: string;
......@@ -144,51 +145,74 @@ const TableSettings = ({ settings, graphMetadata, updateSettings }: Visualizatio
return (
<SettingsContainer>
<Input
type="dropdown"
label="Select entity"
value={settings.displayEntity}
onChange={(val) => updateSettings({ displayEntity: val as string })}
options={graphMetadata.nodes.labels}
/>
<Input type="boolean" label="Show barplot" value={settings.showBarplot} onChange={(val) => updateSettings({ showBarplot: val })} />
<Input
type="dropdown"
label="Items per page"
value={settings.itemsPerPage}
onChange={(val) => updateSettings({ itemsPerPage: val as number })}
options={[10, 25, 50, 100]}
/>
<Input
type="number"
label="Max Bars in Bar Plots"
value={settings.maxBarsCount}
onChange={(val) => updateSettings({ maxBarsCount: val })}
/>
<div>
<span className="text-sm">Attributes to display:</span>
<Button
className="w-full text-justify justify-start"
variantType="secondary"
variant="ghost"
size="sm"
onClick={toggleCollapseAttr}
iconComponent="icon-[ic--baseline-arrow-drop-down]"
>
attributes:{' '}
</Button>
<div className="">
{!areCollapsedAttr && (
<Input
type="checkbox"
value={settings.displayAttributes}
options={selectedNodeAttributes}
onChange={(val: string[] | string) => {
const updatedVal = Array.isArray(val) ? val : [val];
updateSettings({ displayAttributes: updatedVal });
}}
<div className="my-2">
<Input
className="w-full text-justify justify-center"
type="dropdown"
value={settings.displayEntity}
options={graphMetadata.nodes.labels}
onChange={(val) => updateSettings({ displayEntity: val as string })}
overrideRender={
<EntityPill
title={
<div className="flex flex-row justify-between items-center cursor-pointer">
<span>{settings.displayEntity || ''}</span>
<Button variantType="secondary" variant="ghost" size="2xs" iconComponent="icon-[ic--baseline-arrow-drop-down]" />
</div>
}
/>
)}
}
></Input>
<div className="my-2">
<Input
type="boolean"
label="Show barplot"
value={settings.showBarplot}
onChange={(val) => updateSettings({ showBarplot: val })}
/>
</div>
<div className="my-2">
<Input
type="dropdown"
label="Items per page"
value={settings.itemsPerPage}
onChange={(val) => updateSettings({ itemsPerPage: val as number })}
options={[10, 25, 50, 100]}
/>
</div>
<div className="my-2">
<Input
type="number"
label="Max Bars in Bar Plots"
value={settings.maxBarsCount}
onChange={(val) => updateSettings({ maxBarsCount: val })}
/>
</div>
<div className="flex flex-col items-start space-y-2">
<span className="text-sm">Attributes to display:</span>
<Button
className="w-full text-justify justify-start"
variantType="secondary"
variant="ghost"
size="sm"
onClick={toggleCollapseAttr}
iconComponent={areCollapsedAttr ? 'icon-[ic--baseline-arrow-right]' : 'icon-[ic--baseline-arrow-drop-down]'}
>
attributes:{' '}
</Button>
<div className="">
{!areCollapsedAttr && (
<Input
type="checkbox"
value={settings.displayAttributes}
options={selectedNodeAttributes}
onChange={(val: string[] | string) => {
const updatedVal = Array.isArray(val) ? val : [val];
updateSettings({ displayAttributes: updatedVal });
}}
/>
)}
</div>
</div>
</div>
</SettingsContainer>
......