Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit d89eceb

Browse files
1 parent cd92220 commit d89eceb

17 files changed

+2204
-0
lines changed

site/src/api/api.ts

+7
Original file line numberDiff line numberDiff line change
@@ -2179,6 +2179,13 @@ class ApiMethods {
21792179
) => {
21802180
await this.axios.post<void>("/api/v2/users/otp/change-password", req);
21812181
};
2182+
2183+
workspaceBuildTimings = async (workspaceBuildId: string) => {
2184+
const res = await this.axios.get<TypesGen.WorkspaceBuildTimings>(
2185+
`/api/v2/workspacebuilds/${workspaceBuildId}/timings`,
2186+
);
2187+
return res.data;
2188+
};
21822189
}
21832190

21842191
// This is a hard coded CSRF token/cookie pair for local development. In prod,

site/src/api/queries/workspaceBuilds.ts

+7
Original file line numberDiff line numberDiff line change
@@ -56,3 +56,10 @@ export const infiniteWorkspaceBuilds = (
5656
},
5757
};
5858
};
59+
60+
export const workspaceBuildTimings = (workspaceBuildId: string) => {
61+
return {
62+
queryKey: ["workspaceBuilds", workspaceBuildId, "timings"],
63+
queryFn: () => API.workspaceBuildTimings(workspaceBuildId),
64+
};
65+
};
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
import type { Interpolation, Theme } from "@emotion/react";
2+
import { type ButtonHTMLAttributes, type HTMLProps, forwardRef } from "react";
3+
4+
export type BarColors = {
5+
stroke: string;
6+
fill: string;
7+
};
8+
9+
type BaseBarProps<T> = Omit<T, "size" | "color"> & {
10+
/**
11+
* Scale used to determine the width based on the given value.
12+
*/
13+
scale: number;
14+
value: number;
15+
/**
16+
* The X position of the bar component.
17+
*/
18+
offset: number;
19+
/**
20+
* Color scheme for the bar. If not passed the default gray color will be
21+
* used.
22+
*/
23+
colors?: BarColors;
24+
};
25+
26+
type BarProps = BaseBarProps<HTMLProps<HTMLDivElement>>;
27+
28+
export const Bar = forwardRef<HTMLDivElement, BarProps>(
29+
({ colors, scale, value, offset, ...htmlProps }, ref) => {
30+
return (
31+
<div
32+
css={barCSS({ colors, scale, value, offset })}
33+
{...htmlProps}
34+
ref={ref}
35+
/>
36+
);
37+
},
38+
);
39+
40+
type ClickableBarProps = BaseBarProps<ButtonHTMLAttributes<HTMLButtonElement>>;
41+
42+
export const ClickableBar = forwardRef<HTMLButtonElement, ClickableBarProps>(
43+
({ colors, scale, value, offset, ...htmlProps }, ref) => {
44+
return (
45+
<button
46+
type="button"
47+
css={[...barCSS({ colors, scale, value, offset }), styles.clickable]}
48+
{...htmlProps}
49+
ref={ref}
50+
/>
51+
);
52+
},
53+
);
54+
55+
export const barCSS = ({
56+
scale,
57+
value,
58+
colors,
59+
offset,
60+
}: BaseBarProps<unknown>) => {
61+
return [
62+
styles.bar,
63+
{
64+
width: `calc((var(--x-axis-width) * ${value}) / ${scale})`,
65+
backgroundColor: colors?.fill,
66+
borderColor: colors?.stroke,
67+
marginLeft: `calc((var(--x-axis-width) * ${offset}) / ${scale})`,
68+
},
69+
];
70+
};
71+
72+
const styles = {
73+
bar: (theme) => ({
74+
border: "1px solid",
75+
borderColor: theme.palette.divider,
76+
backgroundColor: theme.palette.background.default,
77+
borderRadius: 8,
78+
// The bar should fill the row height.
79+
height: "inherit",
80+
display: "flex",
81+
padding: 7,
82+
minWidth: 24,
83+
// Increase hover area
84+
position: "relative",
85+
"&::after": {
86+
content: '""',
87+
position: "absolute",
88+
top: -2,
89+
right: -8,
90+
bottom: -2,
91+
left: -8,
92+
},
93+
}),
94+
clickable: {
95+
cursor: "pointer",
96+
// We need to make the bar width at least 34px to allow the "..." icons to be displayed.
97+
// The calculation is border * 1 + side paddings * 2 + icon width (which is 18px)
98+
minWidth: 34,
99+
100+
"&:focus, &:hover, &:active": {
101+
outline: "none",
102+
borderColor: "#38BDF8",
103+
},
104+
},
105+
} satisfies Record<string, Interpolation<Theme>>;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
import type { Interpolation, Theme } from "@emotion/react";
2+
import MoreHorizOutlined from "@mui/icons-material/MoreHorizOutlined";
3+
import { type FC, useEffect, useRef, useState } from "react";
4+
5+
const spaceBetweenBlocks = 4;
6+
const moreIconSize = 18;
7+
const blockSize = 20;
8+
9+
type BlocksProps = {
10+
count: number;
11+
};
12+
13+
export const Blocks: FC<BlocksProps> = ({ count }) => {
14+
const [availableWidth, setAvailableWidth] = useState<number>(0);
15+
const blocksRef = useRef<HTMLDivElement>(null);
16+
17+
// Fix: When using useLayoutEffect, Chromatic fails to calculate the right width.
18+
useEffect(() => {
19+
if (availableWidth || !blocksRef.current) {
20+
return;
21+
}
22+
setAvailableWidth(blocksRef.current.clientWidth);
23+
}, [availableWidth]);
24+
25+
const totalSpaceBetweenBlocks = (count - 1) * spaceBetweenBlocks;
26+
const necessarySize = blockSize * count + totalSpaceBetweenBlocks;
27+
const hasSpacing = necessarySize <= availableWidth;
28+
const nOfPossibleBlocks = Math.floor(
29+
(availableWidth - moreIconSize) / (blockSize + spaceBetweenBlocks),
30+
);
31+
const nOfBlocks = hasSpacing ? count : nOfPossibleBlocks;
32+
33+
return (
34+
<div ref={blocksRef} css={styles.blocks}>
35+
{Array.from({ length: nOfBlocks }, (_, i) => i + 1).map((n) => (
36+
<div key={n} css={styles.block} style={{ minWidth: blockSize }} />
37+
))}
38+
{!hasSpacing && (
39+
<div css={styles.more}>
40+
<MoreHorizOutlined />
41+
</div>
42+
)}
43+
</div>
44+
);
45+
};
46+
47+
const styles = {
48+
blocks: {
49+
display: "flex",
50+
width: "100%",
51+
height: "100%",
52+
gap: spaceBetweenBlocks,
53+
alignItems: "center",
54+
},
55+
block: {
56+
borderRadius: 4,
57+
height: 18,
58+
backgroundColor: "#082F49",
59+
border: "1px solid #38BDF8",
60+
flexShrink: 0,
61+
flex: 1,
62+
},
63+
more: {
64+
color: "#38BDF8",
65+
lineHeight: 0,
66+
flexShrink: 0,
67+
flex: 1,
68+
69+
"& svg": {
70+
fontSize: moreIconSize,
71+
},
72+
},
73+
} satisfies Record<string, Interpolation<Theme>>;

0 commit comments

Comments
 (0)