fix: выбранные позиции справочника отображаются горизонтально рядом с полем

This commit is contained in:
2026-07-24 14:14:56 +03:00
parent b4bded2ffb
commit 031d477630

View File

@@ -142,102 +142,100 @@ export default function TableFieldMultiSelect({
}
return (
<Popover open={isOpen} onOpenChange={setIsOpen} modal={false}>
<PopoverAnchor asChild>
<div className="relative w-full">
<Input
ref={inputRef}
className="h-8"
placeholder={placeholder || 'Выберите значения...'}
value={isOpen ? searchTerm : (selectedLabels.length > 0 ? `${selectedLabels.length} выбрано` : placeholder || 'Выберите значения...')}
onChange={(e) => {
setSearchTerm(e.target.value);
setIsOpen(true);
}}
onFocus={() => {
setSearchTerm('');
setIsOpen(true);
}}
data-testid={`select-table-multi-${fieldCode}`}
/>
</div>
</PopoverAnchor>
<PopoverContent
className="p-0"
style={{ width: 'var(--radix-popper-anchor-width)' }}
onOpenAutoFocus={(e) => e.preventDefault()}
onInteractOutside={(e) => {
if (inputRef.current && inputRef.current.contains(e.target as Node)) {
e.preventDefault();
}
}}
>
<div
className="max-h-60 overflow-auto"
onWheel={(e) => {
const el = e.currentTarget;
const isUp = e.deltaY < 0;
const isDown = e.deltaY > 0;
const atTop = el.scrollTop <= 0;
const atBottom = el.scrollTop + el.clientHeight >= el.scrollHeight - 1;
if ((isUp && atTop) || (isDown && atBottom)) return;
e.preventDefault();
el.scrollTop += e.deltaY;
<div className="flex flex-wrap items-center gap-1 w-full">
<Popover open={isOpen} onOpenChange={setIsOpen} modal={false}>
<PopoverAnchor asChild>
<div className="relative flex-1 min-w-[120px]">
<Input
ref={inputRef}
className="h-8"
placeholder={placeholder || 'Выберите значения...'}
value={isOpen ? searchTerm : (selectedLabels.length > 0 ? `${selectedLabels.length} выбрано` : placeholder || 'Выберите значения...')}
onChange={(e) => {
setSearchTerm(e.target.value);
setIsOpen(true);
}}
onFocus={() => {
setSearchTerm('');
setIsOpen(true);
}}
data-testid={`select-table-multi-${fieldCode}`}
/>
</div>
</PopoverAnchor>
<PopoverContent
className="p-0"
style={{ width: 'var(--radix-popper-anchor-width)' }}
onOpenAutoFocus={(e) => e.preventDefault()}
onInteractOutside={(e) => {
if (inputRef.current && inputRef.current.contains(e.target as Node)) {
e.preventDefault();
}
}}
>
{hasTree ? (
<DirectoryTreeMultiSelect
nodes={nodes}
selected={selectedSet}
onToggle={toggle}
searchTerm={searchTerm}
/>
) : (
<ul className="py-1">
{filtered.map((row) => {
const rowId = row.id.toString();
const label = row.values[displayColumnIndex!] || row.values[0] || rowId;
return (
<li
key={row.id}
className="flex items-center gap-2 px-3 py-2 hover:bg-accent cursor-pointer text-sm"
title={label}
onMouseDown={(e) => { e.preventDefault(); toggle(rowId); }}
>
<Checkbox checked={selectedSet.has(rowId)} />
<Table className="w-3 h-3 text-muted-foreground flex-shrink-0" />
<span className="flex-1">{label}</span>
</li>
);
})}
{filtered.length === 0 && (
<li className="px-3 py-2 text-sm text-muted-foreground">Нет совпадений</li>
)}
</ul>
)}
</div>
</PopoverContent>
{selectedLabels.length > 0 && (
<div className="flex flex-wrap gap-1 mt-1.5">
{selectedLabels.map((label, idx) => {
const rowId = selectedValues[idx];
return (
<div key={rowId} className="inline-flex items-center gap-1 px-1.5 py-0.5 rounded bg-muted text-xs">
<span className="truncate max-w-[150px]">{label}</span>
<Button
type="button"
variant="ghost"
size="icon"
className="h-4 w-4"
onClick={() => toggle(rowId)}
>
<X className="w-3 h-3" />
</Button>
</div>
);
})}
</div>
)}
</Popover>
<div
className="max-h-60 overflow-auto"
onWheel={(e) => {
const el = e.currentTarget;
const isUp = e.deltaY < 0;
const isDown = e.deltaY > 0;
const atTop = el.scrollTop <= 0;
const atBottom = el.scrollTop + el.clientHeight >= el.scrollHeight - 1;
if ((isUp && atTop) || (isDown && atBottom)) return;
e.preventDefault();
el.scrollTop += e.deltaY;
}}
>
{hasTree ? (
<DirectoryTreeMultiSelect
nodes={nodes}
selected={selectedSet}
onToggle={toggle}
searchTerm={searchTerm}
/>
) : (
<ul className="py-1">
{filtered.map((row) => {
const rowId = row.id.toString();
const label = row.values[displayColumnIndex!] || row.values[0] || rowId;
return (
<li
key={row.id}
className="flex items-center gap-2 px-3 py-2 hover:bg-accent cursor-pointer text-sm"
title={label}
onMouseDown={(e) => { e.preventDefault(); toggle(rowId); }}
>
<Checkbox checked={selectedSet.has(rowId)} />
<Table className="w-3 h-3 text-muted-foreground flex-shrink-0" />
<span className="flex-1">{label}</span>
</li>
);
})}
{filtered.length === 0 && (
<li className="px-3 py-2 text-sm text-muted-foreground">Нет совпадений</li>
)}
</ul>
)}
</div>
</PopoverContent>
</Popover>
{selectedLabels.map((label, idx) => {
const rowId = selectedValues[idx];
return (
<div key={rowId} className="inline-flex items-center gap-1 px-1.5 py-0.5 rounded bg-muted text-xs shrink-0">
<span className="truncate max-w-[150px]">{label}</span>
<Button
type="button"
variant="ghost"
size="icon"
className="h-4 w-4"
onClick={() => toggle(rowId)}
>
<X className="w-3 h-3" />
</Button>
</div>
);
})}
</div>
);
}