Skip to content

chore(site): convert more component from Emotion to TailwindCSS #19370

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

Draft
wants to merge 13 commits into
base: main
Choose a base branch
from
7 changes: 7 additions & 0 deletions site/src/@types/react.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
declare module "react" {
interface CSSProperties {
[key: `--${string}`]: string | number;
}
}

export {};
Original file line number Diff line number Diff line change
Expand Up @@ -496,7 +496,7 @@ export const MultiSelectCombobox = forwardRef<
<Badge
key={option.value}
className={cn(
"data-[disabled]:bg-content-disabled data-[disabled]:text-surface-tertiarydata-[disabled]:hover:bg-content-disabled",
"data-[disabled]:bg-content-disabled data-[disabled]:text-surface-tertiary data-[disabled]:hover:bg-content-disabled",
"data-[fixed]:bg-content-disabled data-[fixed]:text-surface-tertiary data-[fixed]:hover:bg-surface-secondary",
badgeClassName,
)}
Expand Down
102 changes: 48 additions & 54 deletions site/src/components/Search/Search.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,18 @@
import type { Interpolation, Theme } from "@emotion/react";
// biome-ignore lint/style/noRestrictedImports: use it to have the component prop
import Box, { type BoxProps } from "@mui/material/Box";
import visuallyHidden from "@mui/utils/visuallyHidden";
import type { Interpolation } from "@emotion/react";
import type { Theme } from "@mui/material";
import { SearchIcon } from "lucide-react";
import type { FC, HTMLAttributes, InputHTMLAttributes, Ref } from "react";
import {
type FC,
type HTMLAttributes,
type InputHTMLAttributes,
type Ref,
useId,
} from "react";
import { cn } from "utils/cn";

interface SearchProps extends Omit<BoxProps, "ref"> {
$$ref?: Ref<unknown>;
interface SearchProps extends Omit<HTMLAttributes<HTMLDivElement>, "ref"> {
$$ref?: Ref<HTMLDivElement>;
}

/**
Expand All @@ -18,93 +24,81 @@ interface SearchProps extends Omit<BoxProps, "ref"> {
* </Search>
* ```
*/
export const Search: FC<SearchProps> = ({ children, $$ref, ...boxProps }) => {
export const Search: FC<SearchProps> = ({
children,
$$ref,
className,
...boxProps
}) => {
return (
<Box ref={$$ref} {...boxProps} css={SearchStyles.container}>
<SearchIcon className="size-icon-xs" css={SearchStyles.icon} />
<div
ref={$$ref}
{...boxProps}
className={cn(
"flex items-center pl-4 h-10 border-b border-solid",
className,
)}
>
<SearchIcon className="size-icon-xs text-content-secondary" />
{children}
</Box>
</div>
);
};

const SearchStyles = {
container: (theme) => ({
display: "flex",
alignItems: "center",
paddingLeft: 16,
height: 40,
borderBottom: `1px solid ${theme.palette.divider}`,
}),

icon: (theme) => ({
fontSize: 14,
color: theme.palette.text.secondary,
}),
} satisfies Record<string, Interpolation<Theme>>;

type SearchInputProps = InputHTMLAttributes<HTMLInputElement> & {
label?: string;
$$ref?: Ref<HTMLInputElement>;
};

export const SearchInput: FC<SearchInputProps> = ({
label,
$$ref,
id,
label,
className,
...inputProps
}) => {
const hookId = useId();
const inputId = id || `${hookId}-input`;

return (
<>
<label css={{ ...visuallyHidden }} htmlFor={inputProps.id}>
<label className="sr-only" htmlFor={inputId}>
{label}
</label>
<input
ref={$$ref}
id={inputId}
tabIndex={-1}
type="text"
placeholder="Search..."
css={SearchInputStyles.input}
{...inputProps}
className={cn(
"text-inherit h-full border-none bg-transparent grow shrink ml-4 outline-none placeholder:text-content-secondary",
className,
)}
/>
</>
);
};

const SearchInputStyles = {
input: (theme) => ({
color: "inherit",
height: "100%",
border: 0,
background: "none",
flex: 1,
marginLeft: 16,
outline: 0,
"&::placeholder": {
color: theme.palette.text.secondary,
},
}),
} satisfies Record<string, Interpolation<Theme>>;

export const SearchEmpty: FC<HTMLAttributes<HTMLDivElement>> = ({
children = "Not found",
className,
...props
}) => {
return (
<div css={SearchEmptyStyles.empty} {...props}>
<div
{...props}
className={cn(
"text-[13px] text-content-secondary items-center py-2",
className,
)}
>
{children}
</div>
);
};

const SearchEmptyStyles = {
empty: (theme) => ({
fontSize: 13,
color: theme.palette.text.secondary,
textAlign: "center",
paddingTop: 8,
paddingBottom: 8,
}),
} satisfies Record<string, Interpolation<Theme>>;

/**
* Reusable styles for consumers of the base components
*/
Expand Down
33 changes: 6 additions & 27 deletions site/src/components/SearchField/SearchField.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
import { useTheme } from "@emotion/react";
import IconButton from "@mui/material/IconButton";
import InputAdornment from "@mui/material/InputAdornment";
import TextField, { type TextFieldProps } from "@mui/material/TextField";
import Tooltip from "@mui/material/Tooltip";
import visuallyHidden from "@mui/utils/visuallyHidden";
import { SearchIcon, XIcon } from "lucide-react";
import { type FC, useEffect, useRef } from "react";
import type { FC } from "react";

export type SearchFieldProps = Omit<TextFieldProps, "onChange"> & {
onChange: (query: string) => void;
Expand All @@ -19,46 +17,27 @@ export const SearchField: FC<SearchFieldProps> = ({
InputProps,
...textFieldProps
}) => {
const theme = useTheme();
const inputRef = useRef<HTMLInputElement>(null);

useEffect(() => {
if (autoFocus) {
inputRef.current?.focus();
}
});

return (
<TextField
autoFocus={autoFocus}
// Specifying `minWidth` so that the text box can't shrink so much
// that it becomes un-clickable as we add more filter controls
css={{ minWidth: "280px" }}
className="min-w-[280px]"
size="small"
value={value}
onChange={(e) => onChange(e.target.value)}
inputRef={inputRef}
InputProps={{
startAdornment: (
<InputAdornment position="start">
<SearchIcon
className="size-icon-xs"
css={{
color: theme.palette.text.secondary,
}}
/>
<SearchIcon className="size-icon-xs text-content-secondary" />
</InputAdornment>
),
endAdornment: value !== "" && (
<InputAdornment position="end">
<Tooltip title="Clear search">
<IconButton
size="small"
onClick={() => {
onChange("");
}}
>
<IconButton size="small" onClick={() => onChange("")}>
<XIcon className="size-icon-xs" />
<span css={{ ...visuallyHidden }}>Clear search</span>
<span className="sr-only">Clear search</span>
</IconButton>
</Tooltip>
</InputAdornment>
Expand Down
33 changes: 3 additions & 30 deletions site/src/components/SelectMenu/SelectMenu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,6 @@ import {
useMemo,
} from "react";

const SIDE_PADDING = 16;

export const SelectMenu = Popover;

export const SelectMenuTrigger = PopoverTrigger;
Expand Down Expand Up @@ -54,22 +52,7 @@ export const SelectMenuSearch: FC<SearchFieldProps> = (props) => {
<SearchField
fullWidth
size="medium"
css={(theme) => ({
borderBottom: `1px solid ${theme.palette.divider}`,
"& input": {
fontSize: 14,
},
"& fieldset": {
border: 0,
borderRadius: 0,
},
"& .MuiInputBase-root": {
padding: `12px ${SIDE_PADDING}px`,
},
"& .MuiInputAdornment-positionStart": {
marginRight: SIDE_PADDING,
},
})}
className="border-solid border-b-px [&_input]:font-sm [&_fieldset]:border-none [&_fieldset]:rounded-none [&.MuiInputBase-root]:py-3 [&.MuiInputBase-root]:px-4 [&.MuiInputAdornment-positionStart]:mr-4"
{...props}
inputProps={{ autoFocus: true, ...props.inputProps }}
/>
Expand Down Expand Up @@ -112,19 +95,9 @@ export const SelectMenuIcon: FC<HTMLProps<HTMLDivElement>> = (props) => {

export const SelectMenuItem: FC<MenuItemProps> = (props) => {
return (
<MenuItem
css={{
fontSize: 14,
gap: 0,
lineHeight: 1,
padding: `12px ${SIDE_PADDING}px`,
}}
{...props}
>
<MenuItem className="text-sm gap-0 leading-none py-3 px-4" {...props}>
{props.children}
{props.selected && (
<CheckIcon className="size-icon-xs" css={{ marginLeft: "auto" }} />
)}
{props.selected && <CheckIcon className="size-icon-xs ml-auto" />}
</MenuItem>
);
};
41 changes: 6 additions & 35 deletions site/src/components/SignInLayout/SignInLayout.tsx
Original file line number Diff line number Diff line change
@@ -1,45 +1,16 @@
import type { Interpolation, Theme } from "@emotion/react";
import type { FC, PropsWithChildren } from "react";

export const SignInLayout: FC<PropsWithChildren> = ({ children }) => {
return (
<div css={styles.container}>
<div css={styles.content}>
<div css={styles.signIn}>{children}</div>
<div css={styles.copyright}>
<div className="grow shrink h-[100vh,-webkit-fill-available] flex justify-center items-center">
<div className="flex flex-col items-center">
<div className="w-full max-w-[384px] flex flex-col items-center">
{children}
</div>
<div className="text-xs text-content-secondary mt-6">
{"\u00a9"} {new Date().getFullYear()} Coder Technologies, Inc.
</div>
</div>
</div>
);
};

const styles = {
container: {
flex: 1,
// Fallback to 100vh
height: ["100vh", "-webkit-fill-available"],
display: "flex",
justifyContent: "center",
alignItems: "center",
},

content: {
display: "flex",
flexDirection: "column",
alignItems: "center",
},

signIn: {
maxWidth: 385,
display: "flex",
flexDirection: "column",
alignItems: "center",
},

copyright: (theme) => ({
fontSize: 12,
color: theme.palette.text.secondary,
marginTop: 24,
}),
} satisfies Record<string, Interpolation<Theme>>;
Loading
Loading
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy