Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
171 changes: 98 additions & 73 deletions src/components/ui/multi-select.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -736,6 +736,20 @@ export const MultiSelect = React.forwardRef<MultiSelectRef, MultiSelectProps>(
);
}, [options, searchValue, searchable, isGroupedOptions]);

const hasFilteredResults = isGroupedOptions(filteredOptions)
? filteredOptions.some((group) => group.options.length > 0)
: filteredOptions.length > 0;

const filteredOptionValues = isGroupedOptions(filteredOptions)
? filteredOptions.flatMap((group) =>
group.options.map((option) => option.value),
)
: filteredOptions.map((option) => option.value);

const hasSelectedInFiltered = filteredOptionValues.some((value) =>
selectedValues.includes(value),
);

const handleInputKeyDown = (
event: React.KeyboardEvent<HTMLInputElement>,
) => {
Expand Down Expand Up @@ -1190,7 +1204,7 @@ export const MultiSelect = React.forwardRef<MultiSelectRef, MultiSelectProps>(
align="start"
onEscapeKeyDown={() => setIsPopoverOpen(false)}
>
<Command>
<Command shouldFilter={false}>
{searchable && (
<CommandInput
placeholder="Wyszukaj opcję..."
Expand All @@ -1213,85 +1227,96 @@ export const MultiSelect = React.forwardRef<MultiSelectRef, MultiSelectProps>(
"overscroll-behavior-y-contain",
)}
>
<CommandEmpty>{emptyIndicator || "Brak wyników."}</CommandEmpty>{" "}
{!hideSelectAll && !searchValue && (
<CommandGroup>
<CommandItem
key="all"
onSelect={toggleAll}
role="option"
aria-selected={
selectedValues.length ===
getAllOptions().filter((opt) => !opt.disabled).length
}
aria-label={`Select all ${
getAllOptions().length
} options`}
className="cursor-pointer"
>
<div
className={cn(
"border-primary mr-2 flex h-4 w-4 items-center justify-center rounded-sm border",
selectedValues.length ===
{hasFilteredResults ? (
<>
{!hideSelectAll && !searchValue && (
<CommandGroup>
<CommandItem
key="all"
onSelect={toggleAll}
role="option"
aria-selected={
selectedValues.length ===
getAllOptions().filter((opt) => !opt.disabled)
.length
? "bg-primary"
: "opacity-50 [&_svg]:invisible",
)}
aria-hidden="true"
>
<CheckIcon className="text-primary-foreground h-4 w-4" />
</div>
<span>
(Wybierz wszystkie
{getAllOptions().length > 20
? ` – ${declineNumeric(
getAllOptions().length,
"option",
{ singularCase: GrammaticalCase.Accusative },
)}`
: ""}
)
</span>
</CommandItem>
</CommandGroup>
)}
{isGroupedOptions(filteredOptions) ? (
filteredOptions.map((group) => (
<CommandGroup key={group.heading} heading={group.heading}>
{group.options.map((option) => (
<MultiSelectOptionItem
key={option.value}
option={option}
isReadOnly={isReadOnly}
selectedValues={selectedValues}
toggleOption={toggleOption}
setOptionSelected={setOptionSelected}
onEditItem={onEditItem}
/>
))}
</CommandGroup>
))
}
aria-label={`Select all ${
getAllOptions().length
} options`}
className="cursor-pointer"
>
<div
className={cn(
"border-primary mr-2 flex h-4 w-4 items-center justify-center rounded-sm border",
selectedValues.length ===
getAllOptions().filter((opt) => !opt.disabled)
.length
? "bg-primary"
: "opacity-50 [&_svg]:invisible",
)}
aria-hidden="true"
>
<CheckIcon className="text-primary-foreground h-4 w-4" />
</div>
<span>
(Wybierz wszystkie
{getAllOptions().length > 20
? ` – ${declineNumeric(
getAllOptions().length,
"option",
{ singularCase: GrammaticalCase.Accusative },
)}`
: ""}
)
</span>
</CommandItem>
</CommandGroup>
)}
{isGroupedOptions(filteredOptions) ? (
filteredOptions.map((group) => (
<CommandGroup
key={group.heading}
heading={group.heading}
>
{group.options.map((option) => (
<MultiSelectOptionItem
key={option.value}
option={option}
isReadOnly={isReadOnly}
selectedValues={selectedValues}
toggleOption={toggleOption}
setOptionSelected={setOptionSelected}
onEditItem={onEditItem}
/>
))}
</CommandGroup>
))
) : (
<CommandGroup>
{filteredOptions.map((option) => (
<MultiSelectOptionItem
key={option.value}
option={option}
isReadOnly={isReadOnly}
selectedValues={selectedValues}
toggleOption={toggleOption}
setOptionSelected={setOptionSelected}
onEditItem={onEditItem}
/>
))}
</CommandGroup>
)}
</>
) : (
<CommandGroup>
{filteredOptions.map((option) => (
<MultiSelectOptionItem
key={option.value}
option={option}
isReadOnly={isReadOnly}
selectedValues={selectedValues}
toggleOption={toggleOption}
setOptionSelected={setOptionSelected}
onEditItem={onEditItem}
/>
))}
</CommandGroup>
<div className="text-muted-foreground px-3 py-6 text-sm">
{emptyIndicator || "Brak wyników."}
</div>
)}
</CommandList>
<CommandSeparator />
<Separator className="bg-border" />
<CommandGroup>
<div className="flex items-center justify-between">
{selectedValues.length > 0 && (
{hasSelectedInFiltered && (
<>
<CommandItem
onSelect={handleClear}
Expand Down