Text input component #30
82
frontend/src/components/Icon/Icon.tsx
Normal file
82
frontend/src/components/Icon/Icon.tsx
Normal file
@ -0,0 +1,82 @@
|
|||||||
|
import type { SVGAttributes } from "react";
|
||||||
|
import clsx from "clsx";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Icon sizes matching the design system control sizes.
|
||||||
|
*/
|
||||||
|
export type IconSize = "sm" | "md" | "lg";
|
||||||
|
stne3960 marked this conversation as resolved
|
|||||||
|
|
||||||
|
/**
|
||||||
|
* Base props for all icon components.
|
||||||
|
* Extends SVG attributes but replaces `size` with our design system size.
|
||||||
|
*/
|
||||||
|
export interface IconProps extends Omit<SVGAttributes<SVGSVGElement>, "size"> {
|
||||||
|
size?: IconSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Type for icon components that can be passed to controls like TextInput.
|
||||||
|
* Usage: `<TextInput Icon={SearchIcon} />`
|
||||||
|
*/
|
||||||
|
export type IconComponent = React.ComponentType<IconProps>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Size classes using design system font-size tokens.
|
||||||
|
* Icons scale with typography.
|
||||||
|
*/
|
||||||
|
const iconSizeClasses: Record<IconSize, string> = {
|
||||||
|
sm: "w-(--font-size-body-md) h-(--font-size-body-md)",
|
||||||
|
md: "w-(--font-size-body-md) h-(--font-size-body-md)",
|
||||||
|
lg: "w-(--font-size-body-lg) h-(--font-size-body-lg)",
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Shared SVG attributes applied to all icons.
|
||||||
|
* Uses stroke (not fill) so icons inherit text color via currentColor.
|
||||||
|
*/
|
||||||
|
const baseSvgProps: SVGAttributes<SVGSVGElement> = {
|
||||||
|
viewBox: "0 0 24 24",
|
||||||
|
fill: "none",
|
||||||
|
stroke: "currentColor",
|
||||||
|
strokeWidth: 2,
|
||||||
|
strokeLinecap: "round",
|
||||||
|
strokeLinejoin: "round",
|
||||||
|
};
|
||||||
|
|
||||||
|
export function SearchIcon({ size = "md", className, ...props }: IconProps) {
|
||||||
|
return (
|
||||||
|
<svg
|
||||||
|
className={clsx(iconSizeClasses[size], className)}
|
||||||
|
{...baseSvgProps}
|
||||||
|
{...props}
|
||||||
|
>
|
||||||
|
<circle cx="11" cy="11" r="8" />
|
||||||
|
<path d="M21 21l-4.35-4.35" />
|
||||||
|
</svg>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function RemoveIcon({ size = "md", className, ...props }: IconProps) {
|
||||||
|
return (
|
||||||
|
<svg
|
||||||
|
className={clsx(iconSizeClasses[size], className)}
|
||||||
|
{...baseSvgProps}
|
||||||
|
{...props}
|
||||||
|
>
|
||||||
|
<line x1="18" y1="6" x2="6" y2="18" />
|
||||||
|
<line x1="6" y1="6" x2="18" y2="18" />
|
||||||
|
</svg>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function CheckmarkIcon({ size = "md", className, ...props }: IconProps) {
|
||||||
|
return (
|
||||||
|
<svg
|
||||||
|
className={clsx(iconSizeClasses[size], className)}
|
||||||
|
{...baseSvgProps}
|
||||||
|
{...props}
|
||||||
|
>
|
||||||
|
<polyline points="20 6 9 17 4 12" />
|
||||||
|
</svg>
|
||||||
|
);
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user
Shouldn't icons inherit their size from their context? Like if I add an icon to a small button or a large heading I would assume it would scale to that context rather than having its own independent size.
For certain cases, yes, it probably should. I've added inherit as a default. However, we need to be able to override it like in the TextInput component. Here, the icon is in a dedicated container that's sized by --control-height-* tokens, not font-size. There's no text for it to "inherit" from since the icon container and the input text are siblings, not parent/child.