Skip to content

Commit 0c51d7d

Browse files
authored
Time slider (#93)
1 parent 4238bb4 commit 0c51d7d

File tree

10 files changed

+371
-264
lines changed

10 files changed

+371
-264
lines changed

apps/class-solid/src/components/Analysis.tsx

Lines changed: 195 additions & 183 deletions
Large diffs are not rendered by default.

apps/class-solid/src/components/plots/Axes.tsx

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ type AxisProps = {
99
domain?: () => [number, number]; // TODO: is this needed for reactivity?
1010
label?: string;
1111
tickValues?: number[];
12-
tickFormat?: (n: number | { valueOf(): number }) => string;
12+
tickFormat?: (n: number) => string;
1313
};
1414

1515
export const AxisBottom = (props: AxisProps) => {
@@ -80,14 +80,17 @@ export const AxisLeft = (props: AxisProps) => {
8080
* Calculate a "nice" step size by rounding up to the nearest power of 10
8181
* Snap the min and max to the nearest multiple of step
8282
*/
83-
export function getNiceAxisLimits(data: number[]): [number, number] {
83+
export function getNiceAxisLimits(
84+
data: number[],
85+
extraMargin = 0,
86+
): [number, number] {
8487
const max = Math.max(...data);
8588
const min = Math.min(...data);
8689
const range = max - min;
8790
const step = 10 ** Math.floor(Math.log10(range));
8891

89-
const niceMin = Math.floor(min / step) * step;
90-
const niceMax = Math.ceil(max / step) * step;
92+
const niceMin = Math.floor(min / step) * step - extraMargin * step;
93+
const niceMax = Math.ceil(max / step) * step + extraMargin * step;
9194

9295
return [niceMin, niceMax];
9396
}

apps/class-solid/src/components/plots/ChartContainer.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ export function ChartContainer(props: {
6161
const scaleX = supportedScales[chart.scalePropsX.type]()
6262
.range(chart.scalePropsX.range)
6363
.domain(chart.scalePropsX.domain);
64-
// .nice(); // TODO: could use this instead of getNiceAxisLimits
64+
// .nice(); // TODO: could use this instead of getNiceAxisLimits but messes up skewT
6565
updateChart("scaleX", () => scaleX);
6666
});
6767

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import * as d3 from "d3";
2+
import { createSignal } from "solid-js";
3+
import type { ChartData } from "./ChartContainer";
4+
import { useChartContext } from "./ChartContainer";
5+
6+
export interface Point {
7+
x: number;
8+
y: number;
9+
}
10+
11+
export function Line(d: ChartData<Point>) {
12+
const [chart, updateChart] = useChartContext();
13+
const [hovered, setHovered] = createSignal(false);
14+
15+
const l = d3.line<Point>(
16+
(d) => chart.scaleX(d.x),
17+
(d) => chart.scaleY(d.y),
18+
);
19+
return (
20+
<path
21+
onMouseEnter={() => setHovered(true)}
22+
onMouseLeave={() => setHovered(false)}
23+
fill="none"
24+
stroke={d.color}
25+
stroke-dasharray={d.linestyle}
26+
stroke-width={hovered() ? 5 : 3}
27+
d={l(d.data) || ""}
28+
>
29+
<title>{d.label}</title>
30+
</path>
31+
);
32+
}

apps/class-solid/src/components/plots/LinePlot.tsx

Lines changed: 0 additions & 59 deletions
This file was deleted.
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import type { Component, ComponentProps } from "solid-js";
2+
import { splitProps } from "solid-js";
3+
4+
import { cn } from "~/lib/utils";
5+
6+
const Label: Component<ComponentProps<"label">> = (props) => {
7+
const [local, others] = splitProps(props, ["class"]);
8+
return (
9+
<label
10+
class={cn(
11+
"font-medium text-sm leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70",
12+
local.class,
13+
)}
14+
{...others}
15+
/>
16+
);
17+
};
18+
19+
export { Label };
Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
import type { JSX, ValidComponent } from "solid-js";
2+
import { splitProps } from "solid-js";
3+
4+
import type { PolymorphicProps } from "@kobalte/core/polymorphic";
5+
import * as SliderPrimitive from "@kobalte/core/slider";
6+
7+
import { Label } from "~/components/ui/label";
8+
import { cn } from "~/lib/utils";
9+
10+
type SliderRootProps<T extends ValidComponent = "div"> =
11+
SliderPrimitive.SliderRootProps<T> & {
12+
class?: string | undefined;
13+
};
14+
15+
const Slider = <T extends ValidComponent = "div">(
16+
props: PolymorphicProps<T, SliderRootProps<T>>,
17+
) => {
18+
const [local, others] = splitProps(props as SliderRootProps, ["class"]);
19+
return (
20+
<SliderPrimitive.Root
21+
class={cn(
22+
"relative flex w-full touch-none select-none flex-col items-center",
23+
local.class,
24+
)}
25+
{...others}
26+
/>
27+
);
28+
};
29+
30+
type SliderTrackProps<T extends ValidComponent = "div"> =
31+
SliderPrimitive.SliderTrackProps<T> & {
32+
class?: string | undefined;
33+
};
34+
35+
const SliderTrack = <T extends ValidComponent = "div">(
36+
props: PolymorphicProps<T, SliderTrackProps<T>>,
37+
) => {
38+
const [local, others] = splitProps(props as SliderTrackProps, ["class"]);
39+
return (
40+
<SliderPrimitive.Track
41+
class={cn(
42+
"relative h-2 w-full grow rounded-full bg-secondary",
43+
local.class,
44+
)}
45+
{...others}
46+
/>
47+
);
48+
};
49+
50+
type SliderFillProps<T extends ValidComponent = "div"> =
51+
SliderPrimitive.SliderFillProps<T> & {
52+
class?: string | undefined;
53+
};
54+
55+
const SliderFill = <T extends ValidComponent = "div">(
56+
props: PolymorphicProps<T, SliderFillProps<T>>,
57+
) => {
58+
const [local, others] = splitProps(props as SliderFillProps, ["class"]);
59+
return (
60+
<SliderPrimitive.Fill
61+
class={cn("absolute h-full rounded-full bg-primary", local.class)}
62+
{...others}
63+
/>
64+
);
65+
};
66+
67+
type SliderThumbProps<T extends ValidComponent = "span"> =
68+
SliderPrimitive.SliderThumbProps<T> & {
69+
class?: string | undefined;
70+
children?: JSX.Element;
71+
};
72+
73+
const SliderThumb = <T extends ValidComponent = "span">(
74+
props: PolymorphicProps<T, SliderThumbProps<T>>,
75+
) => {
76+
const [local, others] = splitProps(props as SliderThumbProps, [
77+
"class",
78+
"children",
79+
]);
80+
return (
81+
<SliderPrimitive.Thumb
82+
class={cn(
83+
"top-[-6px] block size-5 rounded-full border-2 border-primary bg-background ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50",
84+
local.class,
85+
)}
86+
{...others}
87+
>
88+
<SliderPrimitive.Input />
89+
</SliderPrimitive.Thumb>
90+
);
91+
};
92+
93+
const SliderLabel = <T extends ValidComponent = "label">(
94+
props: PolymorphicProps<T, SliderPrimitive.SliderLabelProps<T>>,
95+
) => {
96+
return <SliderPrimitive.Label as={Label} {...props} />;
97+
};
98+
99+
const SliderValueLabel = <T extends ValidComponent = "label">(
100+
props: PolymorphicProps<T, SliderPrimitive.SliderValueLabelProps<T>>,
101+
) => {
102+
return <SliderPrimitive.ValueLabel as={Label} {...props} />;
103+
};
104+
105+
export {
106+
Slider,
107+
SliderTrack,
108+
SliderFill,
109+
SliderThumb,
110+
SliderLabel,
111+
SliderValueLabel,
112+
};

apps/class-solid/src/lib/profiles.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import type { ClassOutput } from "@classmodel/class/runner";
22
import type { PartialConfig } from "@classmodel/class/validate";
3-
import type { Point } from "~/components/plots/LinePlot";
3+
import type { Point } from "~/components/plots/Line";
44

55
// Get vertical profiles for a single class run
66
export function getVerticalProfiles(
@@ -16,7 +16,7 @@ export function getVerticalProfiles(
1616

1717
// Extract height profile
1818
const height = output.h.slice(t)[0];
19-
const dh = 400; // how much free troposphere to display?
19+
const dh = 1600; // how much free troposphere to display?
2020
const hProfile = [0, height, height, height + dh];
2121

2222
if (variable === "theta") {

apps/class-solid/src/lib/store.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -312,7 +312,7 @@ export function addAnalysis(name: string) {
312312
type: "profiles",
313313
name: "Vertical profiles",
314314
variable: "Potential temperature [K]",
315-
time: -1,
315+
time: Number.POSITIVE_INFINITY,
316316
} as ProfilesAnalysis;
317317
break;
318318
case "Thermodynamic diagram":
@@ -321,7 +321,7 @@ export function addAnalysis(name: string) {
321321
description: "",
322322
type: "skewT",
323323
name: "Thermodynamic diagram",
324-
time: -1,
324+
time: Number.POSITIVE_INFINITY,
325325
} as SkewTAnalysis;
326326
break;
327327
default:

apps/class-solid/tests/share.spec.ts

Lines changed: 1 addition & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -31,19 +31,7 @@ test("Create share link from an experiment", async ({ page }) => {
3131
const config1 = await parseDownload(downloadPromise1);
3232
expect(config1.reference.initialState?.h_0).toEqual(800);
3333

34-
// // Check that shared experiment has been run by
35-
// // checking height in final height analysis
36-
// const finalHeightAnalysis = sharedPage.getByRole("article", {
37-
// name: "Final height",
38-
// });
39-
// const finalHeightOfExperiment = finalHeightAnalysis.getByRole("listitem", {
40-
// name: "My experiment 1",
41-
// exact: true,
42-
// });
43-
// expect(await finalHeightOfExperiment.textContent()).toMatch(
44-
// /My experiment 1: \d+ m/,
45-
// );
46-
// TODO: implement alternative check to see that experiment finished
34+
// TODO: finalheight is gone; implement alternative check to see that experiment finished
4735
});
4836

4937
test("Given large app state, sharing is not possible", async ({ page }) => {

0 commit comments

Comments
 (0)