-
Notifications
You must be signed in to change notification settings - Fork 5
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
5 changed files
with
407 additions
and
22 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
149 changes: 149 additions & 0 deletions
149
platforms/interface/ui/components/saveDataButton.svelte
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,149 @@ | ||
<ModularDialog title={"Save data"} description={"You can save your data to a .csv file or save the graph as an image."}> | ||
<slot slot="openButton"> | ||
<Dialog.Trigger | ||
class="transparent-800 group m-1 inline-flex flex-shrink-0 flex-grow flex-col items-center justify-center rounded-lg p-1 px-3 text-gray-200 duration-200 ease-in-out" | ||
> | ||
<p class="text-xl">Save</p> | ||
</Dialog.Trigger> | ||
</slot> | ||
<div class="w-full"> | ||
<Tabs.Root value="file" class=""> | ||
<Tabs.List class="grid w-full grid-cols-2 gap-1 rounded-xl border-2 p-1 text-sm font-semibold leading-[0.01em]"> | ||
<Tabs.Trigger | ||
value="file" | ||
class="h-10 rounded-xl bg-transparent py-2 text-base data-[state=active]:bg-white data-[state=active]:text-black" | ||
> | ||
File | ||
</Tabs.Trigger> | ||
<Tabs.Trigger | ||
value="image" | ||
class="h-10 rounded-xl bg-transparent py-2 text-base data-[state=active]:bg-white data-[state=active]:text-black" | ||
> | ||
Image | ||
</Tabs.Trigger> | ||
</Tabs.List> | ||
<Tabs.Content value="file" class="pt-10"> | ||
<div> | ||
<button on:click={saveFile} class="button w-full">Save file</button> | ||
</div> | ||
</Tabs.Content> | ||
<Tabs.Content value="image" class="pt-10"> | ||
<div> | ||
<button on:click={saveImage} class="button w-full">Save image</button> | ||
</div> | ||
</Tabs.Content> | ||
</Tabs.Root> | ||
</div> | ||
</ModularDialog> | ||
|
||
<script lang="ts"> | ||
import { Dialog, Tabs } from "bits-ui" | ||
import ModularDialog from "./modularDialog.svelte" | ||
import { settings } from "../stores/settings.ts" | ||
interface Props { | ||
id: string | ||
statistics: { | ||
label: string | ||
data: number[] | ||
}[] | ||
} | ||
export let props: Props = { | ||
id: "", | ||
statistics: [], | ||
} | ||
const saveFile = () => { | ||
if (($settings.licenseKey === "" || $settings.licenseKey === "free") && import.meta.env.VITE_CORES_MODE === "host") { | ||
return (location.href = "/onboarding") | ||
} | ||
const headers = ["Time", ...props.statistics.map((stat) => stat.label)] | ||
// Create CSV content | ||
const csvRows = [headers.join(",")] | ||
// Use the length of the first statistics array | ||
const dataLength = props.statistics[0].data.length | ||
for (let i = 0; i < dataLength; i++) { | ||
const timestamp = new Date(Date.now() - $settings.interval * 1000 * (dataLength - 1 - i)) | ||
const row = [timestamp.toISOString(), ...props.statistics.map((stat) => stat.data[i])] | ||
csvRows.push(row.join(",")) | ||
} | ||
// Join rows with newlines and create download | ||
const csv = csvRows.join("\n") | ||
const blob = new Blob([csv], { type: "text/csv" }) | ||
const url = window.URL.createObjectURL(blob) | ||
const a = document.createElement("a") | ||
a.href = url | ||
a.download = `${props.id.toLowerCase()}.csv` | ||
a.click() | ||
window.URL.revokeObjectURL(url) | ||
} | ||
const saveImage = () => { | ||
if (($settings.licenseKey === "" || $settings.licenseKey === "free") && import.meta.env.VITE_CORES_MODE === "host") { | ||
return (location.href = "/onboarding") | ||
} | ||
const label = props.id.replaceAll("_", " ") | ||
const canvas = document.getElementById(props.id) as HTMLCanvasElement | ||
// Create a temporary canvas to add background and rounded corners | ||
const tempCanvas = document.createElement("canvas") | ||
const tempCtx = tempCanvas.getContext("2d") | ||
const margin = 20 | ||
const targetWidth = canvas.width | ||
const targetHeight = canvas.height | ||
// Make temp canvas larger to accommodate margin | ||
tempCanvas.width = targetWidth + margin * 2 | ||
tempCanvas.height = targetHeight + margin * 2 | ||
// Draw black background with rounded corners | ||
tempCtx.fillStyle = "rgb(30,30,30)" | ||
const radius = 20 | ||
tempCtx.beginPath() | ||
tempCtx.roundRect(0, 0, tempCanvas.width, tempCanvas.height, radius) | ||
tempCtx.fill() | ||
// Calculate centered position | ||
const x = Math.floor((tempCanvas.width - targetWidth) / 2) | ||
const y = Math.floor((tempCanvas.height - targetHeight) / 2) | ||
// Draw original canvas content centered with margin | ||
tempCtx.drawImage(canvas, x, y, targetWidth, targetHeight) | ||
// Add text at bottom | ||
tempCtx.fillStyle = "rgb(100,100,100)" | ||
tempCtx.font = "12px Arial" | ||
const bText = "Generated by: Cores (coresmonitor.com)" | ||
const bTextMetrics = tempCtx.measureText(bText) | ||
const bTextX = (tempCanvas.width - bTextMetrics.width) / 2 | ||
const bTextY = tempCanvas.height - margin / 2 | ||
tempCtx.fillText(bText, bTextX, bTextY) | ||
// Add text at top | ||
tempCtx.fillStyle = "rgb(100,100,100)" | ||
tempCtx.font = "12px Arial" | ||
const tText = `${label} Sensor Data (${new Date().toLocaleString()})` | ||
const tTextMetrics = tempCtx.measureText(tText) | ||
const tTextX = (tempCanvas.width - tTextMetrics.width) / 2 | ||
const tTextY = margin + 3 | ||
tempCtx.fillText(tText, tTextX, tTextY) | ||
// Get the final image | ||
const image = tempCanvas.toDataURL("image/png") | ||
const a = document.createElement("a") | ||
a.href = image | ||
a.download = `${props.id.toLowerCase()}.png` | ||
a.click() | ||
} | ||
</script> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.