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 957735bba3 - Show all commits

View File

@ -1,65 +1,65 @@
import type { HTMLAttributes } from 'react';
import type { HTMLAttributes } from "react";
import clsx from "clsx";
import { CheckmarkIcon } from "../Icon/Icon";
export interface ListItemProps extends HTMLAttributes<HTMLDivElement> {
export interface ListItemProps
extends Omit<HTMLAttributes<HTMLDivElement>, "title"> {
title: string;
subtitle?: string;
selected?: boolean;
focused?: boolean;
}
const checkmarkIconClasses = 'w-(--font-size-body-md) h-(--font-size-body-md) ml-(--spacing-sm)';
const baseClasses =
"w-full px-(--padding-md) py-(--padding-md) cursor-pointer flex items-center justify-between";
function CheckmarkIcon() {
return (
<svg
className={checkmarkIconClasses}
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
>
<polyline points="20 6 9 17 4 12" />
</svg>
);
const defaultStateClasses = clsx(
"bg-base-canvas text-base-ink-strong",
"hover:bg-sky-100 hover:text-base-ink-max",
"focus-visible:bg-sky-100 focus-visible:text-primary focus-visible:outline-none",
);
const selectedStateClasses = clsx(
"bg-base-canvas text-base-ink-placeholder",
"hover:bg-sky-100 hover:text-base-ink-max",
"focus-visible:bg-sky-100 focus-visible:text-primary focus-visible:outline-none",
);
const focusedStateClasses = "bg-sky-100 text-base-ink-max";
function getStateClasses(selected: boolean, focused: boolean): string {
if (selected && focused) return focusedStateClasses;
if (selected) return selectedStateClasses;
if (focused) return focusedStateClasses;
return defaultStateClasses;
}
const baseClasses = 'w-full px-(--padding-md) py-(--padding-md) cursor-pointer flex items-center justify-between';
const defaultStateClasses =
'bg-base-canvas text-base-ink-strong hover:bg-sky-100 hover:text-base-ink-max focus-visible:bg-sky-100 focus-visible:text-primary focus-visible:outline-none';
const selectedStateClasses =
'bg-base-canvas text-base-ink-placeholder hover:bg-sky-100 hover:text-base-ink-max focus-visible:bg-sky-100 focus-visible:text-primary focus-visible:outline-none';
const focusedStateClasses = 'bg-sky-100 text-base-ink-max';
export default function ListItem({
title,
subtitle,
selected = false,
focused = false,
className = '',
className,
...props
}: ListItemProps) {
const getStateClasses = () => {
if (selected && focused) return focusedStateClasses;
if (selected) return selectedStateClasses;
if (focused) return focusedStateClasses;
return defaultStateClasses;
};
const classes = [baseClasses, getStateClasses(), className].filter(Boolean).join(' ');
return (
<div className={classes} tabIndex={-1} role="option" aria-selected={selected} {...props}>
<div
className={clsx(
baseClasses,
getStateClasses(selected, focused),
className,
)}
tabIndex={-1}
role="option"
aria-selected={selected}
{...props}
>
<div>
<div className="body-normal-md">{title}</div>
{subtitle && <div className="body-light-sm">{subtitle}</div>}
</div>
{selected && (
<div className="shrink-0">
<div className="shrink-0 ml-(--spacing-sm)">
<CheckmarkIcon />
</div>
)}