feat(cdr): Initial UI scaffolding

This is testing out [Approach 3](https://www.notion.so/coderhq/Workspaces-v2-Initial-UI-Scaffolding-3b07d2847eed48839a7e6f0f2bb9bf56#56256f25d2954897a8ee315f0820cedd) in the UI scaffolding RFC.

Fixes https://github.com/coder/coder/issues/11

The folder structure looks like:
- `site`
    - `components` (buttons, empty state, etc)
    - `pages` (large sections of UI -> composition of components)
    - `theme` (files defining our palette)

Several components were able to be brought in essentially unmodified:
- `SplitButton`
- `EmptyState`
- `Footer`
-  All the icons / logos
- Theming (removed several items that aren't necessary, yet, though)

Other components had more coupling, and need more refactoring:
- `NavBar`
- `Confetti`

Current State:

![2022-01-06 17 16 31](https://user-images.githubusercontent.com/88213859/148475521-96e080cc-1d33-4b8e-a434-29e388936e3f.gif)

For a full working app, there's potentially a lot more to bring in:
- User / Account Settings Stuff
- Users Page
- Organizations Page
(and all the supporting dependencies)
This commit is contained in:
Bryan
2022-01-12 14:25:12 -08:00
committed by GitHub
parent 0778f3e738
commit ace89161fb
27 changed files with 2977 additions and 7 deletions

View File

@ -0,0 +1,125 @@
import Button, { ButtonProps } from "@material-ui/core/Button"
import ButtonGroup from "@material-ui/core/ButtonGroup"
import ClickAwayListener from "@material-ui/core/ClickAwayListener"
import Grow from "@material-ui/core/Grow"
import MenuItem from "@material-ui/core/MenuItem"
import MenuList from "@material-ui/core/MenuList"
import Paper from "@material-ui/core/Paper"
import Popper from "@material-ui/core/Popper"
import ArrowDropDownIcon from "@material-ui/icons/ArrowDropDown"
import React, { useRef, useState } from "react"
interface SplitButtonOptions<T> {
/**
* label is shown in the SplitButton UI
*/
label: string
/**
* value is any value for this option
*/
value: T
}
export interface SplitButtonProps<T> extends Pick<ButtonProps, "color" | "disabled" | "startIcon"> {
/**
* onClick is called with the selectedOption
*/
onClick: (selectedOption: T) => void
/**
* options is a list of options
*/
options: SplitButtonOptions<T>[]
/**
* textTransform is applied to the primary button text. Defaults to
* uppercase
*/
textTransform?: React.CSSProperties["textTransform"]
}
/**
* SplitButton is a button with a primary option and a dropdown with secondary
* options.
* @remark The primary option is the 0th index (first option) in the array.
* @see https://mui.com/components/button-group/#split-button
*/
export const SplitButton = <T,>({
color,
disabled,
onClick,
options,
startIcon,
textTransform,
}: SplitButtonProps<T>): ReturnType<React.FC> => {
const [isPopperOpen, setIsPopperOpen] = useState<boolean>(false)
const anchorRef = useRef<HTMLDivElement>(null)
const displayedLabel = options[0].label
const handleClick = () => {
onClick(options[0].value)
}
const handleClose = (e: React.MouseEvent<Document, MouseEvent>) => {
if (anchorRef.current && anchorRef.current.contains(e.target as HTMLElement)) {
return
}
setIsPopperOpen(false)
}
const handleSelectOpt = (e: React.MouseEvent<HTMLLIElement, MouseEvent>, opt: number) => {
onClick(options[opt].value)
setIsPopperOpen(false)
}
const handleTogglePopper = () => {
setIsPopperOpen((prevOpen) => !prevOpen)
}
return (
<>
<ButtonGroup aria-label="split button" color={color} ref={anchorRef} variant="contained">
<Button disabled={disabled} onClick={handleClick} startIcon={startIcon} style={{ textTransform }}>
{displayedLabel}
</Button>
<Button
aria-controls={isPopperOpen ? "split-button-menu" : undefined}
aria-expanded={isPopperOpen ? "true" : undefined}
aria-label="select merge strategy"
aria-haspopup="menu"
disabled={disabled}
size="small"
onClick={handleTogglePopper}
>
<ArrowDropDownIcon />
</Button>
</ButtonGroup>
<Popper
anchorEl={anchorRef.current}
disablePortal
open={isPopperOpen}
role={undefined}
style={{ zIndex: 1 }}
transition
>
{({ TransitionProps, placement }) => (
<Grow
{...TransitionProps}
style={{
transformOrigin: placement === "bottom" ? "center top" : "center bottom",
}}
>
<Paper>
<ClickAwayListener onClickAway={handleClose}>
<MenuList id="split-button-menu">
{options.map((opt, idx) => (
<MenuItem key={opt.label} onClick={(e) => handleSelectOpt(e, idx)}>
{opt.label}
</MenuItem>
))}
</MenuList>
</ClickAwayListener>
</Paper>
</Grow>
)}
</Popper>
</>
)
}