List components #31
@ -1,77 +1,90 @@
|
|||||||
import Combobox, { type ComboboxOption, type ComboboxSize } from '../Combobox/Combobox';
|
import type { HTMLAttributes } from "react";
|
||||||
import ListCard from '../ListCard/ListCard';
|
import clsx from "clsx";
|
||||||
|
import Combobox, {
|
||||||
|
type ComboboxOption,
|
||||||
|
type ComboboxSize,
|
||||||
|
} from "../Combobox/Combobox";
|
||||||
|
import ListCard from "../ListCard/ListCard";
|
||||||
|
|
||||||
export interface ParticipantPickerProps {
|
export interface ParticipantPickerProps
|
||||||
options: ComboboxOption[];
|
extends Omit<HTMLAttributes<HTMLDivElement>, "onChange"> {
|
||||||
value: string[];
|
options: ComboboxOption[];
|
||||||
onChange: (value: string[]) => void;
|
value: string[];
|
||||||
placeholder?: string;
|
onChange: (value: string[]) => void;
|
||||||
label?: string;
|
placeholder?: string;
|
||||||
noResultsText?: string;
|
label: string;
|
||||||
size?: ComboboxSize;
|
hideLabel?: boolean;
|
||||||
fullWidth?: boolean;
|
noResultsText?: string;
|
||||||
customWidth?: string;
|
size?: ComboboxSize;
|
||||||
/** Called when search term changes. When provided, local filtering is disabled (assumes API filtering). */
|
fullWidth?: boolean;
|
||||||
onSearchChange?: (term: string) => void;
|
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> = {
|
const widthClasses: Record<ComboboxSize, string> = {
|
||||||
sm: 'w-(--text-input-default-width-md)',
|
sm: "w-(--text-input-default-width-md)",
|
||||||
md: 'w-(--text-input-default-width-md)',
|
md: "w-(--text-input-default-width-md)",
|
||||||
lg: 'w-(--text-input-default-width-lg)',
|
lg: "w-(--text-input-default-width-lg)",
|
||||||
};
|
};
|
||||||
|
|
||||||
export default function ParticipantPicker({
|
export default function ParticipantPicker({
|
||||||
options,
|
options,
|
||||||
value,
|
value,
|
||||||
onChange,
|
onChange,
|
||||||
placeholder = 'Sök...',
|
placeholder = "Sök...",
|
||||||
label,
|
label,
|
||||||
noResultsText = 'Inga resultat',
|
hideLabel = false,
|
||||||
size = 'md',
|
noResultsText = "Inga resultat",
|
||||||
fullWidth = false,
|
size = "md",
|
||||||
customWidth,
|
fullWidth = false,
|
||||||
onSearchChange,
|
customWidth,
|
||||||
|
onSearchChange,
|
||||||
|
className,
|
||||||
|
style,
|
||||||
|
...props
|
||||||
}: ParticipantPickerProps) {
|
}: ParticipantPickerProps) {
|
||||||
const handleRemove = (valueToRemove: string) => {
|
const handleRemove = (valueToRemove: string) => {
|
||||||
onChange(value.filter((v) => v !== valueToRemove));
|
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
|
const containerClasses = clsx(
|
||||||
? 'flex flex-col gap-(--spacing-sm) w-full'
|
"flex flex-col gap-(--spacing-sm)",
|
||||||
: customWidth
|
fullWidth && "w-full",
|
||||||
? 'flex flex-col gap-(--spacing-sm)'
|
!fullWidth && !customWidth && widthClasses[size],
|
||||||
: `flex flex-col gap-(--spacing-sm) ${widthClasses[size]}`;
|
className,
|
||||||
const widthStyle = customWidth ? { width: customWidth } : undefined;
|
);
|
||||||
|
const widthStyle = customWidth ? { width: customWidth, ...style } : style;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={containerClasses} style={widthStyle}>
|
<div className={containerClasses} style={widthStyle} {...props}>
|
||||||
<Combobox
|
<Combobox
|
||||||
options={options}
|
options={options}
|
||||||
value={value}
|
value={value}
|
||||||
onChange={(v) => onChange(v as string[])}
|
onChange={(v) => onChange(v as string[])}
|
||||||
placeholder={placeholder}
|
placeholder={placeholder}
|
||||||
label={label}
|
label={label}
|
||||||
noResultsText={noResultsText}
|
hideLabel={hideLabel}
|
||||||
size={size}
|
noResultsText={noResultsText}
|
||||||
fullWidth
|
size={size}
|
||||||
multiple
|
fullWidth
|
||||||
onSearchChange={onSearchChange}
|
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>
|
||||||
);
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user