import React from 'react';
import {ChevronDown, Xmark} from '@gravity-ui/icons';
import {Button, Icon, Label, List, Popup} from '@gravity-ui/uikit';
import {cn} from '../../../../utils/cn';
import {BRAND_BUTTON_CLASS} from '../../../../utils/constants';
import i18n from '../i18n';
import type {Column} from '../types';
import './ColumnSelectorField.scss';
interface ColumnSelectorFieldProps {
value: string[];
onChange: (value: string[]) => void;
columns: Column[];
invalid?: boolean;
className?: string;
}
const b = cn('ydb-table-form-column-selector');
const getColumnId = (item: Column) => item.name;
const filterColumnItem = (filter: string) => (item: Column) => {
const lower = filter.toLowerCase();
return item.name.toLowerCase().includes(lower);
};
interface ItemSelectorProps {
items: Column[];
value: string[];
onUpdate: (value: string[]) => void;
}
function ItemSelector({items, value, onUpdate}: ItemSelectorProps) {
const valueSet = React.useMemo(() => new Set(value), [value]);
const availableItems = React.useMemo(
() => items.filter((item) => !valueSet.has(getColumnId(item))),
[items, valueSet],
);
const selectedItems = React.useMemo(
() =>
value
.map((id) => items.find((item) => getColumnId(item) === id))
.filter(Boolean) as Column[],
[value, items],
);
const handleSelect = React.useCallback(
(item: Column) => {
const id = getColumnId(item);
if (!valueSet.has(id)) {
onUpdate([...value, id]);
}
},
[onUpdate, value, valueSet],
);
const handleRemove = React.useCallback(
(item: Column) => {
onUpdate(value.filter((v) => v !== getColumnId(item)));
},
[onUpdate, value],
);
const handleSelectAll = React.useCallback(() => {
onUpdate(items.map(getColumnId));
}, [items, onUpdate]);
const handleClear = React.useCallback(() => {
onUpdate([]);
}, [onUpdate]);
const renderAvailableItem = React.useCallback(
(item: Column, isActive: boolean) => (
{item.name}
),
[handleSelect],
);
const renderSelectedItem = React.useCallback(
(item: Column, isActive: boolean) => (
{item.name}
),
[handleRemove],
);
return (
{i18n('label_columns')}
items={availableItems}
filterItem={filterColumnItem}
filterPlaceholder={i18n('label_search')}
itemsHeight={196}
renderItem={renderAvailableItem}
/>
{i18n('label_selected-count', {count: selectedItems.length})}
items={selectedItems}
filterItem={filterColumnItem}
filterPlaceholder={i18n('label_search')}
itemsHeight={196}
renderItem={renderSelectedItem}
/>
);
}
export function ColumnSelectorField({
value,
onChange,
columns,
invalid,
className,
}: ColumnSelectorFieldProps) {
const [open, setOpen] = React.useState(false);
const [currentValue, setCurrentValue] = React.useState(undefined);
const controlRef = React.useRef(null);
const items = React.useMemo(() => columns.filter(({name}) => Boolean(name)), [columns]);
React.useEffect(() => {
setCurrentValue(undefined);
}, [value, columns]);
React.useEffect(() => {
if (!value.length) {
return;
}
const available = new Set(items.map(getColumnId));
const allPresent = value.every((id) => available.has(id));
if (!allPresent) {
onChange(value.filter((id) => available.has(id)));
}
}, [items, value, onChange]);
const handleToggle = React.useCallback(() => {
setOpen((prev) => !prev);
setCurrentValue(undefined);
}, []);
const handleApply = React.useCallback(() => {
setOpen(false);
if (currentValue !== undefined) {
onChange(currentValue);
}
setCurrentValue(undefined);
}, [currentValue, onChange]);
const handleCancel = React.useCallback(() => {
setOpen(false);
setCurrentValue(undefined);
}, []);
return (
{
if (!isOpen) {
handleCancel();
}
}}
>
);
}