seti-ramesesv1
Version:
Reusable components and context for Next.js apps
107 lines (104 loc) • 9.21 kB
JavaScript
import { jsxs, jsx } from 'react/jsx-runtime';
import { Filter, ArrowUp, RefreshCcw, ChevronsLeft, ChevronLeft, ChevronRight, Search, Pencil, File, Trash, ChevronDown } from 'lucide-react';
import React__default, { useState, useEffect, useMemo } from 'react';
import styles from '../../styles/Datalist.module.css.js';
import { Select } from '../ui/Select.js';
import { Text } from '../ui/Text.js';
import { Tooltip } from '../ui/Tooltip.js';
import MoreMenu from './DatalistMoreMenu.js';
import { IconButton } from './IconButton.js';
const getNestedValue = (obj, path) => {
return path.split(".").reduce((acc, part) => acc?.[part], obj);
};
const DataList = ({ cols, hiddencols, sortcol, orderby, handler, limit = 20, openItem, hideToolbar = false, allowSearch = false, searchMode = "manual", onEdit, onView, onDelete, onMore, emptyState, renderExpandedRow, actionColumnWidth = 120, searchFields, }) => {
const [items, setItems] = useState([]);
const [searchText, setSearchText] = useState("");
const [expandedRowIndex, setExpandedRowIndex] = useState(null);
const [start, setStart] = useState(0);
const [filterColumn, setFilterColumn] = useState("");
const [filters, setFilters] = useState({});
const [appliedFilters, setAppliedFilters] = useState({});
const showActions = onView || onDelete || onEdit || (onMore?.length ?? 0) > 0;
const doSearch = async (params) => {
const query = {
start: params._start ?? start,
limit,
searchtext: searchText,
cols: cols ?? null,
hiddencols: hiddencols ?? null,
sortcol: sortcol ?? params.sortcol ?? null,
orderby: orderby ?? null,
filters: params.filters ?? appliedFilters,
searchfields: searchFields ?? undefined,
};
const results = await handler(query);
setItems(results ?? []);
};
useEffect(() => {
doSearch({ _start: 0 });
}, []);
useEffect(() => {
if (searchMode === "auto") {
const timeout = setTimeout(() => {
doSearch({ _start: start });
}, 300);
return () => clearTimeout(timeout);
}
}, [searchText, appliedFilters, start, searchMode]);
const uniqueFilterValues = useMemo(() => {
const values = [];
if (filterColumn) {
for (const item of items) {
// const val = item[filterColumn];
const val = getNestedValue(item, filterColumn);
if (val && !values.includes(val))
values.push(val);
}
}
return values;
}, [filterColumn, items]);
const displayedItems = items.slice(0, limit);
const hasNext = items.length > limit;
return (jsxs("div", { className: styles.datalistContainer, children: [jsxs("div", { style: { visibility: hideToolbar ? "hidden" : "visible", marginTop: hideToolbar ? "-50px" : "" }, className: styles.toolbar, children: [jsxs("div", { className: styles.leftControls, children: [jsx(Select, { label: "Filter", value: filterColumn, onChange: setFilterColumn, options: cols.map((c) => ({ id: c.name, value: c.name, title: c.title })) }), filterColumn && (jsx(Select, { label: "Value", value: filters[filterColumn] || "", onChange: (val) => setFilters((prev) => ({ ...prev, [filterColumn]: val })), options: uniqueFilterValues.map((v) => ({ id: v, value: v, title: v })) })), jsx(Tooltip, { content: "Apply Filters", children: jsx(IconButton, { icon: jsx(Filter, { size: 16 }), onClick: () => {
const updated = { ...filters };
setAppliedFilters(updated);
doSearch({ _start: 0, filters: updated });
} }) }), Object.keys(appliedFilters).length > 0 && (jsx(Tooltip, { content: "Clear Filters", children: jsx(IconButton, { icon: jsx(ArrowUp, { size: 16 }), onClick: () => {
setFilters({});
setAppliedFilters({});
setFilterColumn("");
doSearch({ _start: 0, filters: {} });
} }) })), jsx(Tooltip, { content: "Refresh", children: jsx(IconButton, { icon: jsx(RefreshCcw, { size: 16 }), onClick: () => doSearch({ _start: start }) }) })] }), jsxs("div", { className: styles.rightControls, children: [jsx(IconButton, { icon: jsx(ChevronsLeft, { size: 16 }), onClick: () => {
setStart(0);
doSearch({ _start: 0 }); // ✅ reset start
}, disabled: start === 0 }), jsx(IconButton, { icon: jsx(ChevronLeft, { size: 16 }), onClick: () => {
const prevStart = Math.max(0, start - limit);
setStart(prevStart);
doSearch({ _start: prevStart }); // ✅ pass updated start
}, disabled: start === 0 }), jsx(IconButton, { icon: jsx(ChevronRight, { size: 16 }), onClick: () => {
const nextStart = start + limit;
console.log(nextStart);
setStart(nextStart);
doSearch({ _start: nextStart });
}, disabled: !hasNext }), allowSearch && (jsx("div", { className: styles.inlineControl, children: jsx(Text, { label: "Search", value: searchText, onChange: (e) => setSearchText(e.target.value), onKeyDown: (e) => {
if (searchMode === "manual" && e.key === "Enter") {
e.preventDefault();
doSearch({ _start: 0 });
}
}, endIcon: searchMode === "manual" ? jsx(Search, { size: 16 }) : undefined, onEndIconClick: searchMode === "manual" ? () => doSearch({ _start: 0 }) : undefined }) }))] })] }), jsx("div", { className: styles.datalistScroll, children: jsxs("table", { className: styles.datalistTable, children: [jsx("thead", { children: jsxs("tr", { children: [cols.map((col) => (jsx("th", { style: {
width: col.width,
textAlign: (["left", "right", "center", "justify", "start", "end"].includes(col.alignment || "")
? col.alignment
: "left"),
}, children: col.title }, col.name))), showActions && jsx("th", { style: { width: actionColumnWidth, textAlign: "center" }, children: "Actions" }), renderExpandedRow && jsx("th", {})] }) }), jsx("tbody", { children: displayedItems.length === 0 ? (jsx("tr", { children: jsxs("td", { colSpan: cols.length + 2, style: { textAlign: "center", padding: "2rem" }, children: [jsx("p", { children: jsx("strong", { children: emptyState?.title ?? "No Data" }) }), jsx("p", { children: emptyState?.message ?? "Try adjusting filters or search." })] }) })) : (displayedItems.map((item, idx) => {
const isExpanded = expandedRowIndex === idx;
return (jsxs(React__default.Fragment, { children: [jsxs("tr", { className: styles.hoverableRow, onClick: () => openItem?.(item), style: { cursor: openItem ? "pointer" : undefined }, children: [cols.map((col) => (
// <td key={col.name}>{item[col.name] ?? ""}</td>
jsx("td", { children: getNestedValue(item, col.name) ?? "" }, col.name))), showActions && (jsx("td", { children: jsxs("div", { className: styles.actionButtons, children: [onEdit && (jsx(IconButton, { icon: jsx(Pencil, { size: 12, color: "#3bd930" }), onClick: () => onEdit(item) })), onView && (jsx(IconButton, { icon: jsx(File, { size: 12, color: "#5452ff" }), onClick: () => onView(item) })), onDelete && (jsx(IconButton, { icon: jsx(Trash, { size: 12, color: "#ff4242" }), onClick: () => onDelete(item) })), onMore && jsx(MoreMenu, { actions: onMore, item: item })] }) })), renderExpandedRow && (jsx("td", { children: jsx(IconButton, { icon: isExpanded ? jsx(ChevronDown, { size: 16 }) : jsx(ChevronLeft, { size: 16 }), onClick: (e) => {
e.stopPropagation();
setExpandedRowIndex(isExpanded ? null : idx);
} }) }))] }), renderExpandedRow && isExpanded && (jsx("tr", { className: styles.expandedRow, children: jsx("td", { colSpan: cols.length + (showActions ? 1 : 0), children: jsx("div", { className: styles.expandedContent, children: renderExpandedRow(item) }) }) }))] }, idx));
})) })] }) })] }));
};
export { DataList as default };
//# sourceMappingURL=Datalist.js.map