-
Notifications
You must be signed in to change notification settings - Fork 925
feat(site): add workspace timings #15068
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
5268b1e
4d509f9
d48624b
62152ce
fd84ed9
f7f09ff
2ffc75a
a8372e1
f7c7488
0868185
54d13c8
714e37b
a2dd126
0b4747e
49d3a72
647635d
6c742aa
9a8bb59
4139151
bcff9c6
97b25d9
6d5c344
c4bd74e
939ec9a
49c69e0
61008a3
f969ef2
f55033a
00616d3
8cc7eef
6b95860
5d08ff0
ea3676a
1c74c96
57df89b
48a61d8
0726f39
a4752c3
b481edd
90b54b4
266d82d
a4a1ba8
4eb24f7
79f370c
a1ee002
75ea1e5
edb885c
f8bb22c
1ff245c
541ccba
b3f0351
65ca087
f288169
08874a3
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,105 @@ | ||
import type { Interpolation, Theme } from "@emotion/react"; | ||
import { type ButtonHTMLAttributes, type HTMLProps, forwardRef } from "react"; | ||
|
||
export type BarColors = { | ||
stroke: string; | ||
fill: string; | ||
}; | ||
|
||
type BaseBarProps<T> = Omit<T, "size" | "color"> & { | ||
/** | ||
* Scale used to determine the width based on the given value. | ||
*/ | ||
scale: number; | ||
value: number; | ||
/** | ||
* The X position of the bar component. | ||
*/ | ||
offset: number; | ||
/** | ||
* Color scheme for the bar. If not passed the default gray color will be | ||
* used. | ||
*/ | ||
colors?: BarColors; | ||
}; | ||
|
||
type BarProps = BaseBarProps<HTMLProps<HTMLDivElement>>; | ||
|
||
export const Bar = forwardRef<HTMLDivElement, BarProps>( | ||
({ colors, scale, value, offset, ...htmlProps }, ref) => { | ||
return ( | ||
<div | ||
css={barCSS({ colors, scale, value, offset })} | ||
{...htmlProps} | ||
ref={ref} | ||
/> | ||
); | ||
}, | ||
); | ||
|
||
type ClickableBarProps = BaseBarProps<ButtonHTMLAttributes<HTMLButtonElement>>; | ||
|
||
export const ClickableBar = forwardRef<HTMLButtonElement, ClickableBarProps>( | ||
({ colors, scale, value, offset, ...htmlProps }, ref) => { | ||
return ( | ||
<button | ||
type="button" | ||
css={[...barCSS({ colors, scale, value, offset }), styles.clickable]} | ||
{...htmlProps} | ||
ref={ref} | ||
/> | ||
); | ||
}, | ||
); | ||
|
||
export const barCSS = ({ | ||
scale, | ||
value, | ||
colors, | ||
offset, | ||
}: BaseBarProps<unknown>) => { | ||
return [ | ||
styles.bar, | ||
{ | ||
width: `calc((var(--x-axis-width) * ${value}) / ${scale})`, | ||
backgroundColor: colors?.fill, | ||
borderColor: colors?.stroke, | ||
marginLeft: `calc((var(--x-axis-width) * ${offset}) / ${scale})`, | ||
}, | ||
]; | ||
}; | ||
|
||
const styles = { | ||
bar: (theme) => ({ | ||
border: "1px solid", | ||
borderColor: theme.palette.divider, | ||
backgroundColor: theme.palette.background.default, | ||
borderRadius: 8, | ||
// The bar should fill the row height. | ||
height: "inherit", | ||
display: "flex", | ||
padding: 7, | ||
minWidth: 24, | ||
// Increase hover area | ||
position: "relative", | ||
"&::after": { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Neat trick! |
||
content: '""', | ||
position: "absolute", | ||
top: -2, | ||
right: -8, | ||
bottom: -2, | ||
left: -8, | ||
}, | ||
}), | ||
clickable: { | ||
cursor: "pointer", | ||
// We need to make the bar width at least 34px to allow the "..." icons to be displayed. | ||
// The calculation is border * 1 + side paddings * 2 + icon width (which is 18px) | ||
minWidth: 34, | ||
|
||
"&:focus, &:hover, &:active": { | ||
outline: "none", | ||
borderColor: "#38BDF8", | ||
}, | ||
}, | ||
} satisfies Record<string, Interpolation<Theme>>; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,73 @@ | ||
import type { Interpolation, Theme } from "@emotion/react"; | ||
import MoreHorizOutlined from "@mui/icons-material/MoreHorizOutlined"; | ||
import { type FC, useEffect, useRef, useState } from "react"; | ||
|
||
const spaceBetweenBlocks = 4; | ||
const moreIconSize = 18; | ||
const blockSize = 20; | ||
|
||
type BlocksProps = { | ||
count: number; | ||
}; | ||
|
||
export const Blocks: FC<BlocksProps> = ({ count }) => { | ||
const [availableWidth, setAvailableWidth] = useState<number>(0); | ||
const blocksRef = useRef<HTMLDivElement>(null); | ||
|
||
// Fix: When using useLayoutEffect, Chromatic fails to calculate the right width. | ||
useEffect(() => { | ||
if (availableWidth || !blocksRef.current) { | ||
return; | ||
} | ||
setAvailableWidth(blocksRef.current.clientWidth); | ||
}, [availableWidth]); | ||
|
||
const totalSpaceBetweenBlocks = (count - 1) * spaceBetweenBlocks; | ||
const necessarySize = blockSize * count + totalSpaceBetweenBlocks; | ||
const hasSpacing = necessarySize <= availableWidth; | ||
const nOfPossibleBlocks = Math.floor( | ||
(availableWidth - moreIconSize) / (blockSize + spaceBetweenBlocks), | ||
); | ||
const nOfBlocks = hasSpacing ? count : nOfPossibleBlocks; | ||
|
||
return ( | ||
<div ref={blocksRef} css={styles.blocks}> | ||
{Array.from({ length: nOfBlocks }, (_, i) => i + 1).map((n) => ( | ||
<div key={n} css={styles.block} style={{ minWidth: blockSize }} /> | ||
))} | ||
Comment on lines
+35
to
+37
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hopefully this is the right spot, but one thing that threw me off is that my template has five resources, and I saw five blue blocks, but only three of them show up in the chart when I click into the bar. Edit: I think because we filter Coder resources in the sub-chart but not the top-level chart. It was also a little surprising that the blue blocks were all even width instead of matching up with their timings. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fixed! |
||
{!hasSpacing && ( | ||
<div css={styles.more}> | ||
<MoreHorizOutlined /> | ||
</div> | ||
)} | ||
</div> | ||
); | ||
}; | ||
|
||
const styles = { | ||
blocks: { | ||
display: "flex", | ||
width: "100%", | ||
height: "100%", | ||
gap: spaceBetweenBlocks, | ||
alignItems: "center", | ||
}, | ||
block: { | ||
borderRadius: 4, | ||
height: 18, | ||
backgroundColor: "#082F49", | ||
border: "1px solid #38BDF8", | ||
flexShrink: 0, | ||
flex: 1, | ||
}, | ||
more: { | ||
color: "#38BDF8", | ||
lineHeight: 0, | ||
flexShrink: 0, | ||
flex: 1, | ||
|
||
"& svg": { | ||
fontSize: moreIconSize, | ||
}, | ||
}, | ||
} satisfies Record<string, Interpolation<Theme>>; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I kept thinking the gray box was a checkbox 😆
I wonder if we could change the shape or default color a bit, but then again I might be the only one dumb enough not to realize it was the legend color.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
lol, @chrifro wdyt?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You are referring to the "state refresh" color right?
We could totally change that color. Either to orange if we also want to draw more attention to the "state refresh"
orange-bg: #431407
orange-stroke: #FDBA74 (we use the same color already on different views)
or if we want to keep it more subtle to grey
grey-bg: #18181B
grey-stroke: #71717A
something to keep in mind: the colors will likely slightly change as part of #14780. Then the difference between the newly introduced grey colors and the bg will be more obvious (see screenshot).

Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry for the late reply, yes that is what I meant! When there are two or more, it is pretty obvious they are a legend, but with just one I thought it was supposed to be a checkbox, in particular the dark or grey ones. This might just be me being dumb though.