META-INF.resources.js.components.OrderableTable.tsx Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of com.liferay.frontend.data.set.admin.web
Show all versions of com.liferay.frontend.data.set.admin.web
Liferay Frontend Data Set Admin Web
/**
* SPDX-FileCopyrightText: (c) 2000 Liferay, Inc. https://liferay.com
* SPDX-License-Identifier: LGPL-2.1-or-later OR LicenseRef-Liferay-DXP-EULA-2.0.0-2023-06
*/
import ClayButton, {ClayButtonWithIcon} from '@clayui/button';
import ClayDropDown, {ClayDropDownWithItems} from '@clayui/drop-down';
import ClayEmptyState from '@clayui/empty-state';
import ClayIcon from '@clayui/icon';
import ClayLayout from '@clayui/layout';
import ClayTable from '@clayui/table';
import classNames from 'classnames';
import {ManagementToolbar} from 'frontend-js-components-web';
import fuzzy from 'fuzzy';
import React, {useEffect, useRef, useState} from 'react';
import {DndProvider, useDrag, useDrop} from 'react-dnd';
import {HTML5Backend} from 'react-dnd-html5-backend';
import {FUZZY_OPTIONS} from '../utils/constants';
import Search from './Search';
import '../../css/components/OrderableTable.scss';
const ROW_DRAGGABLE = 'rowDraggable';
interface IAction {
icon: string;
label: string;
onClick: Function;
}
interface IContentRendererProps {
item: any;
query: string;
}
interface IContentRenderer {
component: React.FC;
textMatch?: Function;
}
interface IField {
contentRenderer?: IContentRenderer;
headingTitle?: boolean;
label: string;
name: string;
}
const Row = ({
actions,
fields,
index,
item,
onDragCrossover,
onDrop,
query,
}: {
actions?: Array;
fields: Array;
index: number;
item: any;
onDragCrossover: Function;
onDrop: Function;
query: string;
}) => {
const tableRowRef = useRef(null);
const [{isDragging}, dragRef] = useDrag({
collect: (monitor) => ({
isDragging: monitor.isDragging(),
}),
item: {
index,
type: ROW_DRAGGABLE,
},
});
const onBlur = () => {
const currentRow = tableRowRef?.current;
if (currentRow) {
const dragging = currentRow.classList.contains('dragging');
if (dragging) {
currentRow.classList.remove('dragging');
onDrop();
}
}
};
const onKeyDown = (event: React.KeyboardEvent) => {
const currentRow = tableRowRef?.current;
if (currentRow) {
const dragging = currentRow.classList.contains('dragging');
if (event.key === 'Enter') {
if (!dragging) {
currentRow.classList.add('dragging');
const draggedIndex = index;
const targetIndex = index;
onDragCrossover({draggedIndex, targetIndex});
}
else {
currentRow.classList.remove('dragging');
onDrop();
}
}
else if (event.key === 'ArrowDown' && dragging) {
const draggedIndex = index;
const targetIndex = index + 1;
onDragCrossover({draggedIndex, targetIndex});
}
else if (event.key === 'ArrowUp' && dragging) {
const draggedIndex = index;
const targetIndex = index - 1;
if (targetIndex >= 0) {
onDragCrossover({draggedIndex, targetIndex});
}
}
else if (
(event.key === 'Escape' || event.key === 'Tab') &&
dragging
) {
currentRow.classList.remove('dragging');
onDrop();
}
}
};
const [, dropRef] = useDrop({
accept: ROW_DRAGGABLE,
hover(item: {index: number; type: string}, monitor) {
if (!tableRowRef.current || !onDragCrossover) {
return;
}
const draggedIndex = item.index;
const targetIndex = index;
if (draggedIndex === targetIndex) {
return;
}
const targetSize = tableRowRef.current.getBoundingClientRect();
const targetCenter = (targetSize.bottom - targetSize.top) / 2;
const draggedOffset: {
x: number;
y: number;
} | null = monitor.getClientOffset();
if (!draggedOffset) {
return;
}
const draggedTop = draggedOffset.y - targetSize.top;
if (
(draggedIndex < targetIndex && draggedTop < targetCenter) ||
(draggedIndex > targetIndex && draggedTop > targetCenter)
) {
return;
}
onDragCrossover({draggedIndex, targetIndex});
item.index = targetIndex;
},
});
dragRef(dropRef(tableRowRef));
return (
{tableRowRef?.current?.classList.contains('dragging') ? (
{Liferay.Language.get(
'use-up-and-down-arrows-to-move-the-field-and-press-enter-to-place-it-in-desired-position'
)}
) : null}
{fields.map((field) => {
if (field.contentRenderer) {
const Component = field.contentRenderer
.component as React.FC;
return (
);
}
const itemFieldValue = String(item[field.name]);
const fuzzyMatch = fuzzy.match(
query,
itemFieldValue,
FUZZY_OPTIONS
);
return (
{fuzzyMatch ? (
) : (
{itemFieldValue}
)}
);
})}
{actions && (
{Liferay.Language.get('actions')}
}
>
{actions.map(({icon, label, onClick}) => (
onClick({
item,
})
}
>
{icon && (
)}
{label}
))}
)}
);
};
const Table = ({
actions,
fields,
items,
onDragCrossover,
onDrop,
query,
}: {
actions?: Array;
fields: Array;
items: Array;
onDragCrossover: Function;
onDrop: Function;
query: string;
}) => {
const [, dropRef] = useDrop({
accept: ROW_DRAGGABLE,
drop() {
onDrop();
},
});
return (
{fields.map((field) => (
{field.label}
))}
{actions && }
{items.map((item, index) => (
))}
);
};
interface IOrderableTableProps {
actions?: Array;
className?: string;
creationMenuItems?: React.ComponentProps<
typeof ClayDropDownWithItems
>['items'];
creationMenuLabel?: string;
fields: Array;
items: Array;
noItemsButtonLabel: string;
noItemsDescription: string;
noItemsTitle: string;
onOrderChange: (args: {order: string}) => void;
title?: string;
}
const OrderableTable = ({
actions,
className,
creationMenuItems,
creationMenuLabel = Liferay.Language.get('new'),
fields,
items: initialItems,
noItemsButtonLabel,
noItemsDescription,
noItemsTitle,
onOrderChange,
title,
}: IOrderableTableProps) => {
const [items, setItems] = useState(initialItems);
const [order, setOrder] = useState(
initialItems.map((item) => item.id).join(',')
);
const [query, setQuery] = useState('');
useEffect(() => setItems(initialItems), [initialItems]);
const onSearch = (query: string) => {
setQuery(query);
const regexp = new RegExp(query, 'i');
setItems(
query
? initialItems.filter((item) =>
fields.some((field) => {
if (field.contentRenderer?.textMatch) {
return String(
field.contentRenderer.textMatch(item)
).match(regexp);
}
return String(item[field.name]).match(regexp);
})
) || []
: initialItems
);
};
return (
{title && (
{title}
)}
{creationMenuItems?.length && (
{creationMenuItems.length > 1 ? (
}
/>
) : (
)}
)}
{items.length ? (
{
const orderedItems = [...items];
if (draggedIndex !== targetIndex) {
orderedItems.splice(draggedIndex, 1);
orderedItems.splice(
targetIndex,
0,
items[draggedIndex]
);
}
setItems(orderedItems);
}}
onDrop={() => {
const newOrder = items
.map((item) => item.id)
.join(',');
if (newOrder !== order) {
setOrder(newOrder);
onOrderChange({order: newOrder});
}
}}
query={query}
/>
) : query ? (
) : (
{creationMenuItems?.length &&
(creationMenuItems.length > 1 ? (
{noItemsButtonLabel}
}
/>
) : (
{noItemsButtonLabel}
))}
)}
);
};
export default OrderableTable;
© 2015 - 2024 Weber Informatics LLC | Privacy Policy