graph view #4

Open
opened 2025-07-17 08:07:37 +10:00 by Ghost · 1 comment
Ghost commented 2025-07-17 08:07:37 +10:00 (Migrated from codeberg.org)

try d3 based, start with observable plot

try d3 based, start with observable plot
Ghost commented 2025-09-04 12:24:33 +10:00 (Migrated from codeberg.org)

could be an implementation in solidjs:

// Plot.tsx
import { createEffect, onCleanup } from "solid-js";
import * as Plot from "@observablehq/plot";

interface PlotProps {
  options: Plot.PlotOptions;
}

export default function PlotChart(props: PlotProps) {
  let ref: HTMLDivElement | undefined;
  let chart: HTMLElement | null = null;

  createEffect(() => {
    if (!ref) return;

    // clean up old chart
    if (chart) {
      chart.remove();
      chart = null;
    }

    // create new chart from updated options
    chart = Plot.plot(props.options);
    ref.appendChild(chart);

    // cleanup when component unmounts
    onCleanup(() => {
      chart?.remove();
      chart = null;
    });
  });

  return <div ref={ref!} />;
}
// usage
import { createSignal } from "solid-js";
import PlotChart from "./Plot";

export default function Dashboard() {
  const [data, setData] = createSignal([
    { x: 1, y: 3 },
    { x: 2, y: 8 },
    { x: 3, y: 5 },
  ]);

  return (
    <>
      <PlotChart
        options={{
          marks: [
            Plot.line(data(), { x: "x", y: "y" }),
            Plot.dot(data(), { x: "x", y: "y" }),
          ],
        }}
      />

      <button
        class="mt-4 rounded bg-blue-500 px-3 py-1 text-white"
        onClick={() =>
          setData([
            { x: 1, y: Math.random() * 10 },
            { x: 2, y: Math.random() * 10 },
            { x: 3, y: Math.random() * 10 },
          ])
        }
      >
        Randomize
      </button>
    </>
  );
**}**

more ai crap:

import { createSignal } from "solid-js";
import * as Plot from "@observablehq/plot";
import PlotChart from "./Plot";
import type { UserSession, UserSessionExercise } from "./Types";

// mock data
const sessions: UserSession[] = [
  {
    id: "s1",
    name: "Chest Day",
    user: "u1",
    userDay: "2025-08-20",
    notes: "",
    tags: [],
    userHeight: 180,
    userWeight: 75,
    itemsOrder: null,
    mealsOrder: null,
    expand: {
      userSessionExercises_via_userSession: [
        {
          id: "e1",
          user: "u1",
          exercise: "bench-press",
          userSession: "s1",
          perceivedEffort: 70,
          notes: "",
          tags: [],
          addedWeight: 20,
          restAfter: 120,
          isWarmup: false,
          measurementNumeric: 80, // 80kg
        } as UserSessionExercise,
      ],
    },
  },
  {
    id: "s2",
    name: "Chest Day",
    user: "u1",
    userDay: "2025-08-27",
    notes: "",
    tags: [],
    userHeight: 180,
    userWeight: 75,
    itemsOrder: null,
    mealsOrder: null,
    expand: {
      userSessionExercises_via_userSession: [
        {
          id: "e2",
          user: "u1",
          exercise: "bench-press",
          userSession: "s2",
          perceivedEffort: 75,
          notes: "",
          tags: [],
          addedWeight: 20,
          restAfter: 120,
          isWarmup: false,
          measurementNumeric: 85, // 85kg
        } as UserSessionExercise,
      ],
    },
  },
];

export default function ExercisePlot() {
  const [exerciseId] = createSignal("bench-press");

  const data = () => {
    return sessions.flatMap((s) =>
      (s.expand?.userSessionExercises_via_userSession ?? [])
        .filter((ex) => ex.exercise === exerciseId())
        .map((ex) => ({
          date: new Date(s.userDay),
          weight: ex.measurementNumeric ?? 0,
        }))
    );
  };

  return (
    <PlotChart
      options={{
        x: { label: "Date" },
        y: { label: "Weight (kg)" },
        marks: [
          Plot.line(data(), { x: "date", y: "weight", stroke: "blue" }),
          Plot.dot(data(), { x: "date", y: "weight", fill: "blue" }),
        ],
      }}
    />
  );
}
could be an implementation in solidjs: ``` // Plot.tsx import { createEffect, onCleanup } from "solid-js"; import * as Plot from "@observablehq/plot"; interface PlotProps { options: Plot.PlotOptions; } export default function PlotChart(props: PlotProps) { let ref: HTMLDivElement | undefined; let chart: HTMLElement | null = null; createEffect(() => { if (!ref) return; // clean up old chart if (chart) { chart.remove(); chart = null; } // create new chart from updated options chart = Plot.plot(props.options); ref.appendChild(chart); // cleanup when component unmounts onCleanup(() => { chart?.remove(); chart = null; }); }); return <div ref={ref!} />; } // usage import { createSignal } from "solid-js"; import PlotChart from "./Plot"; export default function Dashboard() { const [data, setData] = createSignal([ { x: 1, y: 3 }, { x: 2, y: 8 }, { x: 3, y: 5 }, ]); return ( <> <PlotChart options={{ marks: [ Plot.line(data(), { x: "x", y: "y" }), Plot.dot(data(), { x: "x", y: "y" }), ], }} /> <button class="mt-4 rounded bg-blue-500 px-3 py-1 text-white" onClick={() => setData([ { x: 1, y: Math.random() * 10 }, { x: 2, y: Math.random() * 10 }, { x: 3, y: Math.random() * 10 }, ]) } > Randomize </button> </> ); **}** ``` more ai crap: ``` import { createSignal } from "solid-js"; import * as Plot from "@observablehq/plot"; import PlotChart from "./Plot"; import type { UserSession, UserSessionExercise } from "./Types"; // mock data const sessions: UserSession[] = [ { id: "s1", name: "Chest Day", user: "u1", userDay: "2025-08-20", notes: "", tags: [], userHeight: 180, userWeight: 75, itemsOrder: null, mealsOrder: null, expand: { userSessionExercises_via_userSession: [ { id: "e1", user: "u1", exercise: "bench-press", userSession: "s1", perceivedEffort: 70, notes: "", tags: [], addedWeight: 20, restAfter: 120, isWarmup: false, measurementNumeric: 80, // 80kg } as UserSessionExercise, ], }, }, { id: "s2", name: "Chest Day", user: "u1", userDay: "2025-08-27", notes: "", tags: [], userHeight: 180, userWeight: 75, itemsOrder: null, mealsOrder: null, expand: { userSessionExercises_via_userSession: [ { id: "e2", user: "u1", exercise: "bench-press", userSession: "s2", perceivedEffort: 75, notes: "", tags: [], addedWeight: 20, restAfter: 120, isWarmup: false, measurementNumeric: 85, // 85kg } as UserSessionExercise, ], }, }, ]; export default function ExercisePlot() { const [exerciseId] = createSignal("bench-press"); const data = () => { return sessions.flatMap((s) => (s.expand?.userSessionExercises_via_userSession ?? []) .filter((ex) => ex.exercise === exerciseId()) .map((ex) => ({ date: new Date(s.userDay), weight: ex.measurementNumeric ?? 0, })) ); }; return ( <PlotChart options={{ x: { label: "Date" }, y: { label: "Weight (kg)" }, marks: [ Plot.line(data(), { x: "date", y: "weight", stroke: "blue" }), Plot.dot(data(), { x: "date", y: "weight", fill: "blue" }), ], }} /> ); } ```
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
davenh99/progressa#4
No description provided.