Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 15 additions & 2 deletions frontend/src/modules/DistributionPlot/interfaces.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,22 @@
import type { InterfaceInitialization } from "@framework/UniDirectionalModuleComponentsInterface";

import { numBinsAtom, orientationAtom, plotTypeAtom } from "./settings/atoms/baseAtoms";
import type { PlotType } from "./typesAndEnums";
import {
barSortByAtom,
numBinsAtom,
orientationAtom,
plotTypeAtom,
sharedXAxesAtom,
sharedYAxesAtom,
} from "./settings/atoms/baseAtoms";
import type { BarSortBy, PlotType } from "./typesAndEnums";

type SettingsToViewInterface = {
plotType: PlotType | null;
numBins: number;
orientation: "h" | "v";
sharedXAxes: boolean;
sharedYAxes: boolean;
barSortBy: BarSortBy;
};

export type Interfaces = {
Expand All @@ -17,4 +27,7 @@ export const settingsToViewInterfaceInitialization: InterfaceInitialization<Sett
plotType: (get) => get(plotTypeAtom),
numBins: (get) => get(numBinsAtom),
orientation: (get) => get(orientationAtom),
sharedXAxes: (get) => get(sharedXAxesAtom),
sharedYAxes: (get) => get(sharedYAxesAtom),
barSortBy: (get) => get(barSortByAtom),
};
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import { atom } from "jotai";

import { PlotType } from "@modules/DistributionPlot/typesAndEnums";

import { BarSortBy, PlotType } from "@modules/DistributionPlot/typesAndEnums";

export const plotTypeAtom = atom<PlotType | null>(PlotType.Histogram);
export const numBinsAtom = atom<number>(10);
export const orientationAtom = atom<"h" | "v">("h");
export const sharedXAxesAtom = atom<boolean>(false);
export const sharedYAxesAtom = atom<boolean>(false);
export const barSortByAtom = atom<BarSortBy>(BarSortBy.Value);
59 changes: 55 additions & 4 deletions frontend/src/modules/DistributionPlot/settings/settings.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,24 @@ import { useAtom } from "jotai";

import { useApplyInitialSettingsToState } from "@framework/InitialSettings";
import type { ModuleSettingsProps } from "@framework/Module";
import { Checkbox } from "@lib/components/Checkbox";
import { CollapsibleGroup } from "@lib/components/CollapsibleGroup";
import { Dropdown } from "@lib/components/Dropdown";
import { Label } from "@lib/components/Label";
import { RadioGroup } from "@lib/components/RadioGroup";
import { Slider } from "@lib/components/Slider";

import type { Interfaces } from "../interfaces";
import { PlotType } from "../typesAndEnums";
import { BarSortBy, PlotType } from "../typesAndEnums";

import { numBinsAtom, orientationAtom, plotTypeAtom } from "./atoms/baseAtoms";
import {
barSortByAtom,
numBinsAtom,
orientationAtom,
plotTypeAtom,
sharedXAxesAtom,
sharedYAxesAtom,
} from "./atoms/baseAtoms";

const plotTypes = [
{
Expand All @@ -39,6 +47,9 @@ export function Settings({ initialSettings }: ModuleSettingsProps<Interfaces>) {
const [plotType, setPlotType] = useAtom(plotTypeAtom);
const [numBins, setNumBins] = useAtom(numBinsAtom);
const [orientation, setOrientation] = useAtom(orientationAtom);
const [sharedXAxes, setSharedXAxes] = useAtom(sharedXAxesAtom);
const [sharedYAxes, setSharedYAxes] = useAtom(sharedYAxesAtom);
const [barSortBy, setBarSortBy] = useAtom(barSortByAtom);

useApplyInitialSettingsToState(initialSettings, "plotType", "string", setPlotType);
useApplyInitialSettingsToState(initialSettings, "numBins", "number", setNumBins);
Expand All @@ -64,7 +75,24 @@ export function Settings({ initialSettings }: ModuleSettingsProps<Interfaces>) {
return null;
}
const content: React.ReactNode[] = [];

const axisContent: React.ReactNode = (
<>
<div className="mb-2 text-gray-500">
<Checkbox
label="Shared X Axes"
checked={sharedXAxes}
onChange={(_, checked) => setSharedXAxes(checked)}
/>
</div>
<div className="mb-2">
<Checkbox
label="Shared Y Axes"
checked={sharedYAxes}
onChange={(_, checked) => setSharedYAxes(checked)}
/>
</div>
</>
);
if (plotType === PlotType.Histogram) {
content.push(
<CollapsibleGroup title="Plot settings" expanded>
Expand All @@ -77,10 +105,17 @@ export function Settings({ initialSettings }: ModuleSettingsProps<Interfaces>) {
valueLabelDisplay="auto"
/>
</Label>
{axisContent}
</CollapsibleGroup>,
);
}
if (plotType === PlotType.Scatter || plotType === PlotType.ScatterWithColorMapping) {
content.push(
<CollapsibleGroup title="Plot settings" expanded>
{axisContent}
</CollapsibleGroup>,
);
}

if (plotType === PlotType.BarChart) {
content.push(
<CollapsibleGroup title="Plot settings" expanded>
Expand All @@ -100,6 +135,22 @@ export function Settings({ initialSettings }: ModuleSettingsProps<Interfaces>) {
value={orientation}
/>
</Label>
<Label text="Sort bars by" key="bar-sort-by">
<RadioGroup
options={[
{
label: "Value",
value: BarSortBy.Value,
},
{
label: "Key",
value: BarSortBy.Key,
},
]}
onChange={(_, value) => setBarSortBy(value as typeof barSortBy)}
value={barSortBy}
/>
</Label>
</CollapsibleGroup>,
);
}
Expand Down
4 changes: 4 additions & 0 deletions frontend/src/modules/DistributionPlot/typesAndEnums.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,7 @@ export enum PlotType {
Scatter = "scatter",
ScatterWithColorMapping = "scatterWithColor",
}
export enum BarSortBy {
Value = "value",
Key = "key",
}
73 changes: 50 additions & 23 deletions frontend/src/modules/DistributionPlot/view.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,10 @@ import { ContentWarning } from "@modules/_shared/components/ContentMessage/conte
import { Plot } from "@modules/_shared/components/Plot";
import { makeSubplots } from "@modules/_shared/Figure";
import { makeHistogramTrace } from "@modules/_shared/histogram";
import { formatNumber } from "@modules/_shared/utils/numberFormatting";

import type { Interfaces } from "./interfaces";
import { PlotType } from "./typesAndEnums";
import { BarSortBy, PlotType } from "./typesAndEnums";
import { makeHoverText, makeHoverTextWithColor, makeTitleFromChannelContent } from "./utils/stringUtils";
import { calcTextSize } from "./utils/textSize";

Expand Down Expand Up @@ -47,10 +48,16 @@ export const View = ({ viewContext, workbenchSettings }: ModuleViewProps<Interfa
const [prevNumBins, setPrevNumBins] = React.useState<number | null>(null);
const [prevOrientation, setPrevOrientation] = React.useState<"v" | "h" | null>(null);
const [prevSize, setPrevSize] = React.useState<Size2D | null>(null);
const [prevSharedXAxes, setPrevSharedXAxes] = React.useState<boolean | null>(null);
const [prevSharedYAxes, setPrevSharedYAxes] = React.useState<boolean | null>(null);
const [prevBarSortBy, setPrevBarSortBy] = React.useState<BarSortBy>(BarSortBy.Value);

const plotType = viewContext.useSettingsToViewInterfaceValue("plotType");
const sharedXAxes = viewContext.useSettingsToViewInterfaceValue("sharedXAxes");
const sharedYAxes = viewContext.useSettingsToViewInterfaceValue("sharedYAxes");
const numBins = viewContext.useSettingsToViewInterfaceValue("numBins");
const orientation = viewContext.useSettingsToViewInterfaceValue("orientation");
const barSortBy = viewContext.useSettingsToViewInterfaceValue("barSortBy");

const statusWriter = useViewStatusWriter(viewContext);

Expand Down Expand Up @@ -84,7 +91,10 @@ export const View = ({ viewContext, workbenchSettings }: ModuleViewProps<Interfa
plotType !== prevPlotType ||
numBins !== prevNumBins ||
orientation !== prevOrientation ||
wrapperDivSize !== prevSize
wrapperDivSize !== prevSize ||
sharedXAxes !== prevSharedXAxes ||
sharedYAxes !== prevSharedYAxes ||
barSortBy !== prevBarSortBy
) {
setRevNumberX(receiverX.revisionNumber);
setRevNumberY(receiverY.revisionNumber);
Expand All @@ -93,6 +103,9 @@ export const View = ({ viewContext, workbenchSettings }: ModuleViewProps<Interfa
setPrevNumBins(numBins);
setPrevOrientation(orientation);
setPrevSize(wrapperDivSize);
setPrevSharedXAxes(sharedXAxes);
setPrevSharedYAxes(sharedYAxes);
setPrevBarSortBy(barSortBy);

startTransition(function makeContent() {
if (!receiverX.channel) {
Expand Down Expand Up @@ -167,8 +180,8 @@ export const View = ({ viewContext, workbenchSettings }: ModuleViewProps<Interfa
numCols,
width: wrapperDivSize.width,
height: wrapperDivSize.height,
sharedXAxes: false,
sharedYAxes: false,
sharedXAxes: sharedXAxes,
sharedYAxes: sharedYAxes,
verticalSpacing: 100 / (wrapperDivSize.height - 50),
horizontalSpacing: 0.2 / numCols,

Expand Down Expand Up @@ -255,9 +268,12 @@ export const View = ({ viewContext, workbenchSettings }: ModuleViewProps<Interfa
const data = receiverX.channel.contents[cellIndex];
const keyData = data.dataArray.map((el: any) => el.key);
const valueData = data.dataArray.map((el: any) => el.value);

const dataTitle = makeTitleFromChannelContent(data);
const kindOfKeyTitle = `${receiverX.channel.kindOfKey}`;
const hoverText = data.dataArray.map(
(el) =>
`${kindOfKeyTitle}: <b>${el.key}</b><br>${dataTitle}: <b>${formatNumber(Number(el.value))}</b><extra></extra>`,
);

const trace: Partial<PlotData> = {
x: orientation === "h" ? valueData : keyData,
Expand All @@ -269,18 +285,31 @@ export const View = ({ viewContext, workbenchSettings }: ModuleViewProps<Interfa
showlegend: false,
type: "bar",
orientation,
hovertemplate: hoverText,
hoverlabel: {
bgcolor: "white",
font: { size: 12, color: "black" },
},
};

const xAxisTitle = orientation === "h" ? dataTitle : kindOfKeyTitle;
const yAxisTitle = orientation === "h" ? kindOfKeyTitle : dataTitle;
const xAxisTitle = orientation === "h" ? dataTitle : `${kindOfKeyTitle} (hover to see values)`;
const yAxisTitle = orientation === "h" ? `${kindOfKeyTitle} (hover to see values)` : dataTitle;

figure.addTrace(trace, rowIndex + 1, colIndex + 1);
const xBinsInDescendingOrder = orientation === "v" && barSortBy === BarSortBy.Value;
const yBinsInDescendingOrder = orientation === "h" && barSortBy === BarSortBy.Value;
const patch: Partial<Layout> = {
[`xaxis${cellIndex + 1}`]: {
title: { text: xAxisTitle },
type: xBinsInDescendingOrder ? "category" : "linear",
categoryorder: xBinsInDescendingOrder ? "total descending" : "trace",
showticklabels: xBinsInDescendingOrder ? false : true,
},
[`yaxis${cellIndex + 1}`]: {
title: { text: yAxisTitle },
type: yBinsInDescendingOrder ? "category" : "linear",
categoryorder: yBinsInDescendingOrder ? "total descending" : "trace",
showticklabels: yBinsInDescendingOrder ? false : true,
},
};
figure.updateLayout(patch);
Expand All @@ -306,8 +335,8 @@ export const View = ({ viewContext, workbenchSettings }: ModuleViewProps<Interfa
numCols: receiverY.channel.contents.length,
width: wrapperDivSize.width,
height: wrapperDivSize.height,
sharedXAxes: true,
sharedYAxes: true,
sharedXAxes: sharedXAxes,
sharedYAxes: sharedYAxes,
verticalSpacing: 20 / (wrapperDivSize.height - 80),
horizontalSpacing: 20 / (wrapperDivSize.width - 80),
margin: {
Expand Down Expand Up @@ -336,12 +365,11 @@ export const View = ({ viewContext, workbenchSettings }: ModuleViewProps<Interfa

let cellIndex = 0;

receiverX.channel.contents.forEach((contentX, rowIndex, rowArr) => {
receiverX.channel.contents.forEach((contentX, rowIndex) => {
if (!receiverY.channel) {
return;
}

const numRows = rowArr.length;
receiverY.channel.contents.forEach((contentY, colIndex) => {
cellIndex++;

Expand Down Expand Up @@ -405,7 +433,7 @@ export const View = ({ viewContext, workbenchSettings }: ModuleViewProps<Interfa
: undefined,
},
showlegend: false,
type: "scattergl",
type: "scatter",
hovertemplate: realizations.map((real) =>
dataColor
? makeHoverTextWithColor(contentX, contentY, dataColor, real)
Expand All @@ -415,22 +443,21 @@ export const View = ({ viewContext, workbenchSettings }: ModuleViewProps<Interfa

figure.addTrace(trace, rowIndex + 1, colIndex + 1);

if (rowIndex === numRows - 1) {
const patch: Partial<Layout> = {
[`xaxis${cellIndex}`]: {
title: {
text: makeTitleFromChannelContent(contentX),
font,
},
const patch: Partial<Layout> = {
[`xaxis${cellIndex}`]: {
title: {
text: makeTitleFromChannelContent(contentY),
font,
},
};
figure.updateLayout(patch);
}
},
};
figure.updateLayout(patch);

if (colIndex === 0) {
const patch: Partial<Layout> = {
[`yaxis${cellIndex}`]: {
title: {
text: makeTitleFromChannelContent(contentY),
text: makeTitleFromChannelContent(contentX),
font,
},
},
Expand Down
Loading