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 99a6d9bde5 - Show all commits

View File

@ -1,77 +1,90 @@
import Combobox, { type ComboboxOption, type ComboboxSize } from '../Combobox/Combobox';
import ListCard from '../ListCard/ListCard';
import type { HTMLAttributes } from "react";
import clsx from "clsx";
import Combobox, {
type ComboboxOption,
type ComboboxSize,
} from "../Combobox/Combobox";
import ListCard from "../ListCard/ListCard";
export interface ParticipantPickerProps {
options: ComboboxOption[];
value: string[];
onChange: (value: string[]) => void;
placeholder?: string;
label?: string;
noResultsText?: string;
size?: ComboboxSize;
fullWidth?: boolean;
customWidth?: string;
/** Called when search term changes. When provided, local filtering is disabled (assumes API filtering). */
onSearchChange?: (term: string) => void;
export interface ParticipantPickerProps
extends Omit<HTMLAttributes<HTMLDivElement>, "onChange"> {
options: ComboboxOption[];
value: string[];
onChange: (value: string[]) => void;
placeholder?: string;
label: string;
hideLabel?: boolean;
noResultsText?: string;
size?: ComboboxSize;
fullWidth?: boolean;
customWidth?: string;
/** Called when search term changes. When provided, local filtering is disabled (assumes API filtering). */
onSearchChange?: (term: string) => void;
}
const widthClasses: Record<ComboboxSize, string> = {
sm: 'w-(--text-input-default-width-md)',
md: 'w-(--text-input-default-width-md)',
lg: 'w-(--text-input-default-width-lg)',
sm: "w-(--text-input-default-width-md)",
md: "w-(--text-input-default-width-md)",
lg: "w-(--text-input-default-width-lg)",
};
export default function ParticipantPicker({
options,
value,
onChange,
placeholder = 'Sök...',
label,
noResultsText = 'Inga resultat',
size = 'md',
fullWidth = false,
customWidth,
onSearchChange,
options,
value,
onChange,
placeholder = "Sök...",
label,
hideLabel = false,
noResultsText = "Inga resultat",
size = "md",
fullWidth = false,
customWidth,
onSearchChange,
className,
style,
...props
}: ParticipantPickerProps) {
const handleRemove = (valueToRemove: string) => {
onChange(value.filter((v) => v !== valueToRemove));
};
const handleRemove = (valueToRemove: string) => {
onChange(value.filter((v) => v !== valueToRemove));
};
const selectedOptions = options.filter((opt) => value.includes(opt.value));
const selectedOptions = options.filter((opt) => value.includes(opt.value));
const containerClasses = fullWidth
? 'flex flex-col gap-(--spacing-sm) w-full'
: customWidth
? 'flex flex-col gap-(--spacing-sm)'
: `flex flex-col gap-(--spacing-sm) ${widthClasses[size]}`;
const widthStyle = customWidth ? { width: customWidth } : undefined;
const containerClasses = clsx(
"flex flex-col gap-(--spacing-sm)",
fullWidth && "w-full",
!fullWidth && !customWidth && widthClasses[size],
className,
);
const widthStyle = customWidth ? { width: customWidth, ...style } : style;
return (
<div className={containerClasses} style={widthStyle}>
<Combobox
options={options}
value={value}
onChange={(v) => onChange(v as string[])}
placeholder={placeholder}
label={label}
noResultsText={noResultsText}
size={size}
fullWidth
multiple
onSearchChange={onSearchChange}
return (
<div className={containerClasses} style={widthStyle} {...props}>
<Combobox
options={options}
value={value}
onChange={(v) => onChange(v as string[])}
placeholder={placeholder}
label={label}
hideLabel={hideLabel}
noResultsText={noResultsText}
size={size}
fullWidth
multiple
onSearchChange={onSearchChange}
/>
{selectedOptions.length > 0 && (
<div className="flex flex-col gap-(--spacing-sm)">
{selectedOptions.map((option) => (
<ListCard
key={option.value}
title={option.label}
subtitle={option.subtitle}
onRemove={() => handleRemove(option.value)}
/>
{selectedOptions.length > 0 && (
<div className="flex flex-col gap-(--spacing-sm)">
{selectedOptions.map((option) => (
<ListCard
key={option.value}
title={option.label}
subtitle={option.subtitle}
onRemove={() => handleRemove(option.value)}
/>
))}
</div>
)}
))}
</div>
);
)}
</div>
);
}