mod-arch-shared
Version:
Shared UI components and utilities for modular architecture micro-frontend projects
112 lines • 7.03 kB
JavaScript
import * as React from 'react';
import { Button, Checkbox, EmptyState, EmptyStateBody, EmptyStateVariant, Flex, FlexItem, Label, Stack, StackItem, Tooltip, } from '@patternfly/react-core';
import { SearchIcon } from '@patternfly/react-icons';
import { DragDropSort, } from '@patternfly/react-drag-drop';
import ContentModal from '../../../components/modals/ContentModal';
import { ManageColumnSearchInput } from './ManageColumnSearchInput';
import { reorderColumns } from './utils';
import './ManageColumnsModal.scss';
export const ManageColumnsModal = ({ manageColumnsResult, title = 'Customize columns', description, maxSelections, maxSelectionsTooltip = 'Maximum columns selected.', searchPlaceholder = 'Filter by column name', dataTestId = 'manage-columns-modal', }) => {
const { managedColumns: initialColumns, setVisibleColumnIds, defaultVisibleColumnIds, isModalOpen, closeModal, } = manageColumnsResult;
const [columns, setColumns] = React.useState(initialColumns);
const [searchValue, setSearchValue] = React.useState('');
// Normalize whitespace (including NBSP) to regular spaces for search comparison
const normalizeWhitespace = (str) => str.replace(/\s+/g, ' ');
// Derive filtered columns from search
const columnsMatchingSearch = React.useMemo(() => searchValue
? columns.filter((col) => normalizeWhitespace(col.label.toLowerCase()).includes(normalizeWhitespace(searchValue.toLowerCase())))
: columns, [columns, searchValue]);
// Reset state when modal opens with new columns
React.useEffect(() => {
if (isModalOpen) {
setColumns(initialColumns);
setSearchValue('');
}
}, [isModalOpen, initialColumns]);
const selectedCount = columns.filter((col) => col.isVisible).length;
const isMaxReached = maxSelections !== undefined && selectedCount >= maxSelections;
const handleUpdate = React.useCallback(() => {
const visibleColumnIds = columns.filter((col) => col.isVisible).map((col) => col.id);
setVisibleColumnIds(visibleColumnIds);
closeModal();
}, [columns, setVisibleColumnIds, closeModal]);
const handleSearch = React.useCallback((value) => {
setSearchValue(value);
}, []);
const handleToggleColumn = React.useCallback((columnId, isChecked) => {
setColumns((prev) => prev.map((col) => (col.id === columnId ? { ...col, isVisible: isChecked } : col)));
}, []);
const handleDrop = React.useCallback((_, newItems) => {
const reorderedIds = newItems.map((item) => String(item.id));
const matchingIds = new Set(columnsMatchingSearch.map((c) => c.id));
setColumns((prev) => reorderColumns(prev, matchingIds, reorderedIds));
}, [columnsMatchingSearch]);
const handleRestoreDefaults = React.useCallback(() => {
if (!defaultVisibleColumnIds) {
return;
}
// Update visibility based on default column IDs
// Also reorder to put default columns first in their original order
setColumns((prev) => {
const defaultSet = new Set(defaultVisibleColumnIds);
const defaultColumns = defaultVisibleColumnIds
.map((id) => prev.find((col) => col.id === id))
.filter((col) => col !== undefined)
.map((col) => ({ ...col, isVisible: true }));
const nonDefaultColumns = prev
.filter((col) => !defaultSet.has(col.id))
.map((col) => ({ ...col, isVisible: false }));
return [...defaultColumns, ...nonDefaultColumns];
});
}, [defaultVisibleColumnIds]);
if (!isModalOpen) {
return null;
}
const buttonActions = [
{
label: 'Update',
onClick: handleUpdate,
variant: 'primary',
dataTestId: `${dataTestId}-update-button`,
},
{
label: 'Cancel',
onClick: closeModal,
variant: 'link',
dataTestId: `${dataTestId}-cancel-button`,
},
];
const descriptionContent = (React.createElement(Stack, { hasGutter: true, className: "pf-v6-u-pb-md" },
description && React.createElement(StackItem, { className: "pf-v6-u-mt-sm" }, description),
React.createElement(StackItem, null,
React.createElement(ManageColumnSearchInput, { value: searchValue, placeholder: searchPlaceholder, onSearch: handleSearch, dataTestId: `${dataTestId}-search` })),
React.createElement(StackItem, null,
React.createElement(Flex, { justifyContent: { default: 'justifyContentSpaceBetween' }, alignItems: { default: 'alignItemsCenter' } },
React.createElement(FlexItem, null,
React.createElement(Label, null,
selectedCount,
" / total ",
columns.length,
" selected")),
defaultVisibleColumnIds && (React.createElement(FlexItem, null,
React.createElement(Button, { variant: "link", isInline: true, onClick: handleRestoreDefaults, "data-testid": `${dataTestId}-restore-defaults` }, "Restore default columns")))))));
const draggableItems = columnsMatchingSearch.map((col) => {
const currentCol = columns.find((c) => c.id === col.id) ?? col;
const isDisabled = isMaxReached && !currentCol.isVisible;
const checkbox = (React.createElement(Checkbox, { id: col.id, "aria-label": col.label, isChecked: currentCol.isVisible, isDisabled: isDisabled, onChange: (_, checked) => handleToggleColumn(col.id, checked) }));
return {
id: col.id,
content: (React.createElement("div", { className: "pf-v6-u-display-inline-block" },
React.createElement(Flex, { alignItems: { default: 'alignItemsCenter' }, flexWrap: { default: 'nowrap' }, spaceItems: { default: 'spaceItemsSm' } },
React.createElement(FlexItem, null, isDisabled ? React.createElement(Tooltip, { content: maxSelectionsTooltip }, checkbox) : checkbox),
React.createElement(FlexItem, null, col.label)))),
props: {
checked: currentCol.isVisible,
className: 'mod-arch-manage-columns__draggable',
},
};
});
return (React.createElement(ContentModal, { onClose: closeModal, title: title, description: descriptionContent, contents: columnsMatchingSearch.length === 0 && searchValue ? (React.createElement(EmptyState, { headingLevel: "h4", icon: SearchIcon, titleText: "No results found", variant: EmptyStateVariant.sm },
React.createElement(EmptyStateBody, null, "No columns match your search. Try adjusting your search terms."))) : (React.createElement(DragDropSort, { items: draggableItems, variant: "default", onDrop: handleDrop })), buttonActions: buttonActions, dataTestId: dataTestId, variant: "small", bodyLabel: "Column names" }));
};
//# sourceMappingURL=ManageColumnsModal.js.map