List components #31

Merged
stne3960 merged 68 commits from list_item into main 2025-12-18 12:41:13 +01:00
Showing only changes of commit 1e0093c887 - Show all commits

View File

@ -1,49 +1,56 @@
import type { HTMLAttributes } from 'react'; import type { HTMLAttributes } from "react";
import clsx from "clsx";
import { RemoveIcon } from "../Icon/Icon";
export interface ListCardProps extends Omit<HTMLAttributes<HTMLDivElement>, 'title'> { export interface ListCardProps
extends Omit<HTMLAttributes<HTMLDivElement>, "title"> {
title: string; title: string;
subtitle?: string; subtitle?: string;
onRemove?: () => void; onRemove?: () => void;
} }
const baseClasses = [ const baseClasses = clsx(
'px-(--padding-md) py-(--padding-md)', "px-(--padding-md) py-(--padding-md)",
'bg-sky-35 border border-sky-100 rounded-(--border-radius-md)', "bg-sky-35 border border-sky-100 rounded-(--border-radius-md)",
'flex items-center justify-between', "flex items-center justify-between",
'group text-base-ink-strong hover:bg-sky-70 hover:text-base-ink-max focus-visible:text-base-ink-max', "group cursor-pointer",
'focus-visible:border-primary focus-visible:border-[length:var(--border-width-sm)] focus-visible:outline focus-visible:outline-sky-100 focus-visible:outline-[length:var(--border-width-lg)]', );
].join(' ');
const removeIconClasses = 'w-(--font-size-body-md) h-(--font-size-body-md)'; const stateClasses = clsx(
"text-base-ink-strong",
"hover:bg-sky-70 hover:text-base-ink-max",
"focus-visible:text-base-ink-max",
"focus-visible:border-primary focus-visible:border-[length:var(--border-width-sm)]",
"focus-visible:outline focus-visible:outline-sky-100 focus-visible:outline-[length:var(--border-width-lg)]",
);
function RemoveIcon() { const removeButtonClasses = clsx(
return ( "shrink-0 ml-(--spacing-sm) cursor-pointer",
<svg "text-base-ink-placeholder",
className={removeIconClasses} "group-hover:text-base-ink-max group-focus-visible:text-base-ink-max",
viewBox="0 0 24 24" "focus-visible:outline-none",
fill="none" );
stroke="currentColor"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
>
<line x1="18" y1="6" x2="6" y2="18" />
<line x1="6" y1="6" x2="18" y2="18" />
</svg>
);
}
export default function ListCard({ title, subtitle = '', onRemove, className = '', ...props }: ListCardProps) {
const classes = [baseClasses, className].filter(Boolean).join(' ');
export default function ListCard({
title,
subtitle,
onRemove,
className,
...props
}: ListCardProps) {
const handleKeyDown = (e: React.KeyboardEvent) => { const handleKeyDown = (e: React.KeyboardEvent) => {
if (e.key === 'Enter' && onRemove) { if (e.key === "Enter" && onRemove) {
onRemove(); onRemove();
} }
}; };
return ( return (
<div className={classes} {...props} tabIndex={0} onKeyDown={handleKeyDown}> <div
className={clsx(baseClasses, stateClasses, className)}
tabIndex={0}
onKeyDown={handleKeyDown}
{...props}
>
<div> <div>
<div className="body-light-sm">{title}</div> <div className="body-light-sm">{title}</div>
{subtitle && <div className="body-light-sm">{subtitle}</div>} {subtitle && <div className="body-light-sm">{subtitle}</div>}
@ -53,7 +60,7 @@ export default function ListCard({ title, subtitle = '', onRemove, className = '
type="button" type="button"
tabIndex={-1} tabIndex={-1}
onClick={onRemove} onClick={onRemove}
className="shrink-0 ml-(--spacing-sm) text-base-ink-placeholder group-hover:text-base-ink-max group-focus-visible:text-base-ink-max focus-visible:outline-none cursor-pointer" className={removeButtonClasses}
> >
<RemoveIcon /> <RemoveIcon />
</button> </button>