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
3 changes: 3 additions & 0 deletions apps/class-solid/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@
"clsx": "^2.1.1",
"comlink": "^4.4.1",
"d3": "^7.9.0",
"file-saver": "^2.0.5",
"html-to-image": "^1.11.13",
"postcss": "^8.4.38",
"solid-js": "^1.8.18",
"tailwind-merge": "^2.4.0",
Expand All @@ -38,6 +40,7 @@
"devDependencies": {
"@playwright/test": "^1.45.3",
"@types/d3": "^7.4.3",
"@types/file-saver": "^2.0.7",
"@types/node": "^20.13.1",
"serve": "^14.2.3",
"typescript": "^5.3.3"
Expand Down
63 changes: 50 additions & 13 deletions apps/class-solid/src/components/Analysis.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import { BmiClass } from "@classmodel/class/bmi";
import type { Config } from "@classmodel/class/config";
import type { ClassOutput } from "@classmodel/class/runner";
import { saveAs } from "file-saver";
import { toBlob } from "html-to-image";
import {
type Accessor,
For,
Expand All @@ -27,7 +29,7 @@ import {
experiments,
updateAnalysis,
} from "~/lib/store";
import { MdiCog, MdiContentCopy, MdiDelete, MdiDownload } from "./icons";
import { MdiCamera, MdiDelete } from "./icons";
import { AxisBottom, AxisLeft, getNiceAxisLimits } from "./plots/Axes";
import { Chart, ChartContainer } from "./plots/ChartContainer";
import { Legend } from "./plots/Legend";
Expand Down Expand Up @@ -165,6 +167,7 @@ export function TimeSeriesPlot({ analysis }: { analysis: TimeseriesAnalysis }) {
label="y-axis"
/>
</div>
<div class="h-10" />
</>
);
}
Expand Down Expand Up @@ -344,10 +347,50 @@ export function ThermodynamicPlot({ analysis }: { analysis: SkewTAnalysis }) {
uniqueTimes,
(t) => updateAnalysis(analysis, { time: t }),
)}
<div class="h-14" />
</>
);
}

async function takeScreenshot(event: MouseEvent, analyse: Analysis) {
const target = event.target as HTMLElement;
const article = target.closest("[role='article']") as HTMLElement;
const figure = article?.querySelector("figure") as HTMLElement;

if (!figure) {
console.error("Could not find figure element");
return;
}

// TODO Make screenshot bigger than the original?
const scale = 1;
// Can not use toSvg as legend is written in HTML
// generated svg document contains foreignObject with html tag
// which can only be rendered using web browser, not Inkscape or PowerPoint
const blob = await toBlob(figure, {
backgroundColor: "white",
canvasWidth: figure.clientWidth * scale,
canvasHeight: figure.clientHeight * scale,
});
if (!blob) {
throw new Error("Failed to create blob");
}
// TODO put experiments/permutation names in filename?
const parts = [analyse.type];
if ("time" in analyse) {
const timesAnalyse = analyse as ProfilesAnalysis | SkewTAnalysis;
const time = uniqueTimes()[timesAnalyse.time];
const formattedTime = formatSeconds(time);
parts.push(formattedTime);
}
const fn = `class-${parts.join("-")}.png`;
console.log("Saving screenshot as", fn);
const file = new File([blob], fn, {
type: "image/png",
});
saveAs(file);
}

export function AnalysisCard(analysis: Analysis) {
const id = createUniqueId();
return (
Expand All @@ -356,18 +399,12 @@ export function AnalysisCard(analysis: Analysis) {
{/* TODO: make name & description editable */}
<CardTitle id={id}>{analysis.name}</CardTitle>

<div class="flex">
{/* TODO: implement download functionality */}
<Button variant="outline" title="Download">
<MdiDownload />
</Button>
{/* TODO: implement "configure" functionality */}
<Button variant="outline" title="Configure">
<MdiCog />
</Button>
{/* TODO: implement duplicate functionality */}
<Button variant="outline" title="Duplicate">
<MdiContentCopy />
<div class="flex gap-1">
<Button
variant="outline"
onClick={(e: MouseEvent) => takeScreenshot(e, analysis)}
>
<MdiCamera />
</Button>
<Button
variant="outline"
Expand Down
18 changes: 18 additions & 0 deletions apps/class-solid/src/components/icons.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -337,3 +337,21 @@ export function MdiDotsHorizontal(props: JSX.IntrinsicElements["svg"]) {
</svg>
);
}

export function MdiCamera(props: JSX.IntrinsicElements["svg"]) {
return (
<svg
xmlns="http://www.w3.org/2000/svg"
width="1em"
height="1em"
viewBox="0 0 24 24"
{...props}
>
<title>Take screenshot of plot</title>
<path
fill="#888888"
d="M4 4h3l2-2h6l2 2h3a2 2 0 0 1 2 2v12a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2m8 3a5 5 0 0 0-5 5a5 5 0 0 0 5 5a5 5 0 0 0 5-5a5 5 0 0 0-5-5m0 2a3 3 0 0 1 3 3a3 3 0 0 1-3 3a3 3 0 0 1-3-3a3 3 0 0 1 3-3"
/>
</svg>
);
}
27 changes: 23 additions & 4 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.