@gravity-ui/uikit
Version:
Gravity UI base styling and components
199 lines (198 loc) • 10.9 kB
JavaScript
'use client';
import { createElement as _createElement } from "react";
import { jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime";
import * as React from 'react';
import { Gear, Grip, Lock } from '@gravity-ui/icons';
import { DragDropContext, Draggable, Droppable } from 'react-beautiful-dnd';
import { useUniqId } from "../../../../../hooks/index.js";
import { createOnKeyDownHandler } from "../../../../../hooks/useActionHandlers/useActionHandlers.js";
import { Button } from "../../../../Button/index.js";
import { Icon } from "../../../../Icon/index.js";
import { Text } from "../../../../Text/index.js";
import { TreeSelect } from "../../../../TreeSelect/TreeSelect.js";
import { TextInput } from "../../../../controls/TextInput/index.js";
import { Flex } from "../../../../layout/Flex/Flex.js";
import { ListContainerView, ListItemView, useListFilter } from "../../../../useList/index.js";
import { block } from "../../../../utils/cn.js";
import i18n from "./i18n/index.js";
import "./TableColumnSetup.css";
const b = block('inner-table-column-setup');
const controlsCn = b('controls');
const filterInputCn = b('filter-input');
const emptyPlaceholderCn = b('empty-placeholder');
const reorderArray = (list, startIndex, endIndex) => {
const result = [...list];
const [removed] = result.splice(startIndex, 1);
result.splice(endIndex, 0, removed);
return result;
};
const prepareStickyState = (itemsById, visibleFlattenIds) => {
let lastStickyStartIdx = 0;
for (; lastStickyStartIdx !== visibleFlattenIds.length; lastStickyStartIdx++) {
const visibleFlattenId = visibleFlattenIds[lastStickyStartIdx];
const item = itemsById[visibleFlattenId];
if (item?.sticky !== 'left' && item?.sticky !== 'start') {
break;
}
}
let firstStickyEndIdx = visibleFlattenIds.length;
for (; firstStickyEndIdx !== 0; firstStickyEndIdx--) {
const visibleFlattenId = visibleFlattenIds[firstStickyEndIdx - 1];
const item = itemsById[visibleFlattenId];
if (item?.sticky !== 'right' && item?.sticky !== 'end') {
break;
}
}
return {
stickyStartItemIdList: visibleFlattenIds.slice(0, lastStickyStartIdx),
sortableItemIdList: visibleFlattenIds.slice(lastStickyStartIdx, firstStickyEndIdx),
stickyEndItemIdList: visibleFlattenIds.slice(firstStickyEndIdx),
};
};
const prepareValue = (tableColumnItems) => {
const selectedIds = [];
tableColumnItems.forEach(({ id, isSelected }) => {
if (isSelected) {
selectedIds.push(id);
}
});
return selectedIds;
};
const RENDER_DRAG_DISABLED_CONTAINER_PROPS = { isDragDisabled: true };
const useDndRenderContainer = ({ onDragEnd, renderControls }) => {
const uniqId = useUniqId();
const dndRenderContainer = ({ renderItem, list, containerRef, id, className, }) => {
const renderDndActiveItem = (provided, snapshot, rubric) => {
const renderContainerProps = {
provided,
snapshot,
};
return renderItem(list.structure.visibleFlattenIds[rubric.source.index], rubric.source.index, renderContainerProps);
};
const { stickyStartItemIdList, sortableItemIdList, stickyEndItemIdList } = prepareStickyState(list.structure.itemsById, list.structure.visibleFlattenIds);
const stickyStartItemList = stickyStartItemIdList.map((visibleFlattenId, idx) => {
return renderItem(visibleFlattenId, idx, RENDER_DRAG_DISABLED_CONTAINER_PROPS);
});
const sortableItemList = sortableItemIdList.map((visibleFlattenId, idx) => {
return renderItem(visibleFlattenId, idx + stickyStartItemIdList.length);
});
const stickyEndItemList = stickyEndItemIdList.map((visibleFlattenId, idx) => {
return renderItem(visibleFlattenId, stickyStartItemList.length + sortableItemList.length + idx, RENDER_DRAG_DISABLED_CONTAINER_PROPS);
});
return (_jsxs(React.Fragment, { children: [_jsxs(ListContainerView, { ref: containerRef, id: id, className: className, children: [stickyStartItemList, _jsx(DragDropContext, { onDragEnd: onDragEnd, children: _jsx(Droppable, { droppableId: uniqId, renderClone: renderDndActiveItem, children: (droppableProvided) => {
return (_jsxs("div", { ...droppableProvided.droppableProps, ref: droppableProvided.innerRef, children: [sortableItemList, droppableProvided.placeholder] }));
} }) }), stickyEndItemList] }), _jsx("div", { className: controlsCn, children: renderControls() })] }));
};
return dndRenderContainer;
};
const useDndRenderItem = (sortable) => {
const renderDndItem = ({ data: item, props, index, renderContainerProps, }) => {
const isDragDisabled = sortable === false || renderContainerProps?.isDragDisabled === true;
const endSlot = isDragDisabled ? undefined : _jsx(Icon, { data: Grip, size: 16 });
const startSlot = item.isRequired ? _jsx(Icon, { data: Lock }) : undefined;
const selected = item.isRequired ? false : props.selected;
const commonProps = {
...props,
selected,
selectionViewType: item.isRequired ? 'single' : 'multiple',
content: {
...props.content,
startSlot,
endSlot,
},
};
if (isDragDisabled) {
return _createElement(ListItemView, { ...commonProps, key: commonProps.id });
}
const renderItem = (provided, snapshot) => (_jsx(ListItemView, { ...commonProps, ...provided.draggableProps, ...provided.dragHandleProps, ref: provided.innerRef, dragging: snapshot.isDragging }));
if (renderContainerProps?.provided && renderContainerProps.snapshot) {
return renderItem(renderContainerProps.provided, renderContainerProps.snapshot);
}
return (_jsx(Draggable, { draggableId: props.id, index: index, isDragDisabled: isDragDisabled, children: renderItem }, `item-key-${props.id}`));
};
return renderDndItem;
};
const mapItemDataToContentProps = (item) => {
return {
title: item.title,
};
};
const defaultFilterSettingsFn = (value, item) => {
return typeof item.title === 'string'
? item.title.toLowerCase().includes(value.trim().toLowerCase())
: true;
};
const useEmptyRenderContainer = (placeholder) => {
const emptyRenderContainer = () => _jsx(Text, { className: emptyPlaceholderCn, children: placeholder });
return emptyRenderContainer;
};
export const TableColumnSetup = (props) => {
const { renderSwitcher, popupWidth, popupPlacement, items: propsItems, onUpdate: propsOnUpdate, sortable, renderControls, className, defaultItems = propsItems, showResetButton: propsShowResetButton, filterable, filterPlaceholder, filterEmptyMessage, filterSettings = defaultFilterSettingsFn, } = props;
const [open, setOpen] = React.useState(false);
const [sortingEnabled, setSortingEnabled] = React.useState(sortable);
const [prevSortingEnabled, setPrevSortingEnabled] = React.useState(sortable);
if (sortable !== prevSortingEnabled) {
setPrevSortingEnabled(sortable);
setSortingEnabled(sortable);
}
const [items, setItems] = React.useState(propsItems);
const [prevPropsItems, setPrevPropsItems] = React.useState(propsItems);
if (propsItems !== prevPropsItems) {
setPrevPropsItems(propsItems);
setItems(propsItems);
}
const filterState = useListFilter({ items, filterItem: filterSettings, debounceTimeout: 0 });
const onApply = () => {
const newSettings = items.map(({ id, isSelected }) => ({ id, isSelected }));
propsOnUpdate(newSettings);
onOpenChange(false);
};
const DefaultApplyButton = () => (_jsx(Button, { view: "action", width: "max", onClick: onApply, children: i18n('button_apply') }));
const onDragEnd = ({ destination, source }) => {
if (destination?.index !== undefined && destination?.index !== source.index) {
setItems((prevItems) => {
return reorderArray(prevItems, source.index, destination.index);
});
}
};
const showResetButton = typeof propsShowResetButton === 'function'
? propsShowResetButton(items)
: propsShowResetButton;
const dndRenderContainer = useDndRenderContainer({
onDragEnd,
renderControls: () => renderControls ? (renderControls({ DefaultApplyButton, onApply })) : (_jsxs(Flex, { gapRow: 1, direction: "column", className: controlsCn, children: [showResetButton && (_jsx(Button, { onClick: () => {
setItems(defaultItems);
}, width: "max", children: i18n('button_reset') })), _jsx(DefaultApplyButton, {})] })),
});
const dndRenderItem = useDndRenderItem(sortingEnabled);
const renderControl = ({ toggleOpen }) => {
const onKeyDown = createOnKeyDownHandler(toggleOpen);
return (renderSwitcher?.({ onClick: toggleOpen, onKeyDown }) || (_jsxs(Button, { onClick: toggleOpen, onKeyDown: onKeyDown, children: [_jsx(Icon, { data: Gear }), i18n('button_switcher')] })));
};
const onOpenChange = (open) => {
setOpen(open);
if (open === false) {
setItems(propsItems);
setSortingEnabled(sortable);
filterState.reset();
}
};
const onUpdate = (selectedItemsIds) => {
setItems((prevItems) => {
return prevItems.map((item) => ({
...item,
isSelected: item.isRequired || selectedItemsIds.includes(item.id),
}));
});
};
const value = React.useMemo(() => prepareValue(items), [items]);
const emptyRenderContainer = useEmptyRenderContainer(filterEmptyMessage);
const onFilterValueUpdate = (value) => {
filterState.onFilterUpdate(value);
setSortingEnabled(!value.length);
};
const slotBeforeListBody = filterable ? (_jsx(TextInput, { size: "m", view: "clear", placeholder: filterPlaceholder, value: filterState.filter, className: filterInputCn, onUpdate: onFilterValueUpdate, hasClear: true })) : null;
const renderContainer = filterState.filter && !filterState.items.length ? emptyRenderContainer : dndRenderContainer;
return (_jsx(TreeSelect, { className: b(null, className), mapItemDataToContentProps: mapItemDataToContentProps, multiple: true, size: "l", open: open, value: value, items: filterState.filter ? filterState.items : items, onUpdate: onUpdate, popupWidth: popupWidth, onOpenChange: onOpenChange, placement: popupPlacement, slotBeforeListBody: slotBeforeListBody, renderContainer: renderContainer, renderControl: renderControl, renderItem: dndRenderItem }));
};
//# sourceMappingURL=TableColumnSetup.js.map