@pallasoft/ps-table-components 1.8.0 → 1.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/ps-data-table.d.ts +2 -1
- package/dist/components/ps-data-table.js +3 -3
- package/dist/components/ps-table-card-list.d.ts +2 -1
- package/dist/components/ps-table-card-list.js +14 -6
- package/dist/components/ps-table-toolbar.js +1 -1
- package/dist/constants/ps-table-search-config.js +1 -1
- package/dist/constants/table-card-role.d.ts +6 -0
- package/dist/constants/table-card-role.js +8 -0
- package/dist/esm/components/ps-data-table.js +133 -0
- package/dist/esm/components/ps-table-bulk-bar.js +3 -0
- package/dist/esm/components/ps-table-card-list.js +36 -0
- package/dist/esm/components/ps-table-column-picker.js +16 -0
- package/dist/esm/components/ps-table-filter-field.js +12 -0
- package/dist/esm/components/ps-table-pagination.js +8 -0
- package/dist/esm/components/ps-table-sort-header.js +14 -0
- package/dist/esm/components/ps-table-toolbar.js +14 -0
- package/dist/esm/constants/ps-table-search-config.js +6 -0
- package/dist/esm/constants/table-card-role.js +5 -0
- package/dist/esm/constants/table-config.js +5 -0
- package/dist/esm/constants/table-filter-kind.js +6 -0
- package/dist/esm/index.js +13 -0
- package/dist/esm/interfaces/table-column-filter.js +1 -0
- package/dist/esm/interfaces/table-column.js +1 -0
- package/dist/esm/interfaces/table-filter-option.js +1 -0
- package/dist/esm/interfaces/table-filter-value.js +1 -0
- package/dist/esm/interfaces/table-query.js +1 -0
- package/dist/esm/interfaces/table-row-action.js +1 -0
- package/dist/esm/interfaces/table-sort.js +1 -0
- package/dist/esm/services/ps-table-query-service.js +93 -0
- package/dist/esm/services/ps-table-search-service.js +24 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/interfaces/table-column.d.ts +2 -0
- package/package.json +5 -2
|
@@ -18,6 +18,7 @@ export interface PsDataTableProps<RowData> {
|
|
|
18
18
|
onPageChange?: (page: number) => void;
|
|
19
19
|
onQueryChange?: (query: PsTableQuery) => void;
|
|
20
20
|
onRowClick?: (row: RowData) => void;
|
|
21
|
+
onRowHover?: (row: RowData) => void;
|
|
21
22
|
page?: number;
|
|
22
23
|
pageSize?: number;
|
|
23
24
|
query?: PsTableQuery;
|
|
@@ -27,4 +28,4 @@ export interface PsDataTableProps<RowData> {
|
|
|
27
28
|
toolbarActions?: ReactNode;
|
|
28
29
|
totalRows?: number;
|
|
29
30
|
}
|
|
30
|
-
export declare const PsDataTable: <RowData>({ caption, className, columns, defaultSort, density, expandedRowIds, mobileLayout, getBulkActions, getRowId, getRowLabel, getSearchText, onPageChange, onQueryChange, onRowClick, page, pageSize, query, renderExpandedRow, rows, searchPlaceholder, toolbarActions, totalRows, }: PsDataTableProps<RowData>) => import("react").JSX.Element;
|
|
31
|
+
export declare const PsDataTable: <RowData>({ caption, className, columns, defaultSort, density, expandedRowIds, mobileLayout, getBulkActions, getRowId, getRowLabel, getSearchText, onPageChange, onQueryChange, onRowClick, onRowHover, page, pageSize, query, renderExpandedRow, rows, searchPlaceholder, toolbarActions, totalRows, }: PsDataTableProps<RowData>) => import("react").JSX.Element;
|
|
@@ -30,7 +30,7 @@ const alignClassNames = {
|
|
|
30
30
|
left: 'text-left',
|
|
31
31
|
right: 'text-right tabular-nums',
|
|
32
32
|
};
|
|
33
|
-
const PsDataTable = ({ caption, className, columns, defaultSort, density = 'normal', expandedRowIds, mobileLayout = 'cards', getBulkActions, getRowId, getRowLabel, getSearchText, onPageChange, onQueryChange, onRowClick, page, pageSize, query, renderExpandedRow, rows, searchPlaceholder = table_config_1.PsTableConfig.searchPlaceholder, toolbarActions, totalRows, }) => {
|
|
33
|
+
const PsDataTable = ({ caption, className, columns, defaultSort, density = 'normal', expandedRowIds, mobileLayout = 'cards', getBulkActions, getRowId, getRowLabel, getSearchText, onPageChange, onQueryChange, onRowClick, onRowHover, page, pageSize, query, renderExpandedRow, rows, searchPlaceholder = table_config_1.PsTableConfig.searchPlaceholder, toolbarActions, totalRows, }) => {
|
|
34
34
|
const containerRef = (0, react_1.useRef)(null);
|
|
35
35
|
const [uncontrolledPage, setUncontrolledPage] = (0, react_1.useState)(1);
|
|
36
36
|
const [uncontrolledQuery, setUncontrolledQuery] = (0, react_1.useState)(() => ps_table_query_service_1.PsTableQueryService.empty(defaultSort ? defaultSort : null));
|
|
@@ -104,7 +104,7 @@ const PsDataTable = ({ caption, className, columns, defaultSort, density = 'norm
|
|
|
104
104
|
}, onSearchChange: (next) => {
|
|
105
105
|
updateQuery({ search: next });
|
|
106
106
|
resetPage();
|
|
107
|
-
}, rows: rows, search: search, searchPlaceholder: searchPlaceholder })) : null, (0, jsx_runtime_1.jsxs)("div", { className: 'min-w-0 overflow-hidden rounded-[1.25rem] border border-line bg-surface', children: [mobileLayout === 'cards' ? ((0, jsx_runtime_1.jsx)("div", { className: 'sm:hidden', children: (0, jsx_runtime_1.jsx)(ps_table_card_list_1.PsTableCardList, { columns: visibleColumns, emptyContent: visibleRows.length === 0 && isQueried ? ((0, jsx_runtime_1.jsxs)("div", { className: 'px-4 py-10 text-center', children: [(0, jsx_runtime_1.jsx)("p", { className: 'text-sm font-semibold text-ink', children: table_config_1.PsTableConfig.noMatchesTitle }), (0, jsx_runtime_1.jsx)("button", { className: 'ps-focus-ring mt-2 rounded-full px-3 py-1.5 text-sm font-semibold text-primary', onClick: clearFilters, type: 'button', children: "Clear search and filters" })] })) : null, expandedRowIds: expandedRowIds, footerColumns: hasFooter ? visibleColumns.filter((column) => column.footer !== undefined) : [], getRowId: getRowId, getRowLabel: (row) => (getRowLabel ? getRowLabel(row) : getRowId(row)), isSelectable: isSelectable, onRowClick: onRowClick, onToggleRow: toggleRow, renderExpandedRow: renderExpandedRow, rows: visibleRows, selectedIds: selectedIds }) })) : null, (0, jsx_runtime_1.jsx)("div", { className: ps_design_system_1.PallasoftClassNameService.merge('ps-scrollbar overflow-x-auto', mobileLayout === 'cards' ? 'hidden sm:block' : ''), children: (0, jsx_runtime_1.jsxs)("table", { className: 'w-full border-collapse text-left', children: [caption ? (0, jsx_runtime_1.jsx)("caption", { className: 'sr-only', children: caption }) : null, (0, jsx_runtime_1.jsx)("thead", { className: 'bg-mist/70 text-[0.68rem] font-semibold uppercase tracking-[0.18em] text-ink/45', children: (0, jsx_runtime_1.jsxs)("tr", { children: [isSelectable ? ((0, jsx_runtime_1.jsx)("th", { className: 'w-10 pl-4 pr-0', scope: 'col', children: (0, jsx_runtime_1.jsx)(ps_input_components_1.PsCheckbox, { isIndeterminate: !isPageSelected && pageIds.some((rowId) => selectedIds.includes(rowId)), isSelected: isPageSelected, label: 'Select all rows on this page', onChange: togglePage }) })) : null, visibleColumns.map((column) => {
|
|
107
|
+
}, rows: rows, search: search, searchPlaceholder: searchPlaceholder })) : null, (0, jsx_runtime_1.jsxs)("div", { className: 'min-w-0 overflow-hidden rounded-[1.25rem] border border-line bg-surface', children: [mobileLayout === 'cards' ? ((0, jsx_runtime_1.jsx)("div", { className: 'sm:hidden', children: (0, jsx_runtime_1.jsx)(ps_table_card_list_1.PsTableCardList, { columns: visibleColumns, emptyContent: visibleRows.length === 0 && isQueried ? ((0, jsx_runtime_1.jsxs)("div", { className: 'px-4 py-10 text-center', children: [(0, jsx_runtime_1.jsx)("p", { className: 'text-sm font-semibold text-ink', children: table_config_1.PsTableConfig.noMatchesTitle }), (0, jsx_runtime_1.jsx)("button", { className: 'ps-focus-ring mt-2 rounded-full px-3 py-1.5 text-sm font-semibold text-primary', onClick: clearFilters, type: 'button', children: "Clear search and filters" })] })) : null, expandedRowIds: expandedRowIds, footerColumns: hasFooter ? visibleColumns.filter((column) => column.footer !== undefined) : [], getRowId: getRowId, getRowLabel: (row) => (getRowLabel ? getRowLabel(row) : getRowId(row)), isSelectable: isSelectable, onRowClick: onRowClick, onRowHover: onRowHover, onToggleRow: toggleRow, renderExpandedRow: renderExpandedRow, rows: visibleRows, selectedIds: selectedIds }) })) : null, (0, jsx_runtime_1.jsx)("div", { className: ps_design_system_1.PallasoftClassNameService.merge('ps-scrollbar overflow-x-auto', mobileLayout === 'cards' ? 'hidden sm:block' : ''), children: (0, jsx_runtime_1.jsxs)("table", { className: 'w-full border-collapse text-left', children: [caption ? (0, jsx_runtime_1.jsx)("caption", { className: 'sr-only', children: caption }) : null, (0, jsx_runtime_1.jsx)("thead", { className: 'bg-mist/70 text-[0.68rem] font-semibold uppercase tracking-[0.18em] text-ink/45', children: (0, jsx_runtime_1.jsxs)("tr", { children: [isSelectable ? ((0, jsx_runtime_1.jsx)("th", { className: 'w-10 pl-4 pr-0', scope: 'col', children: (0, jsx_runtime_1.jsx)(ps_input_components_1.PsCheckbox, { isIndeterminate: !isPageSelected && pageIds.some((rowId) => selectedIds.includes(rowId)), isSelected: isPageSelected, label: 'Select all rows on this page', onChange: togglePage }) })) : null, visibleColumns.map((column) => {
|
|
108
108
|
const align = column.align !== null && column.align !== undefined ? column.align : 'left';
|
|
109
109
|
const isSorted = sort !== null && sort.columnId === column.id;
|
|
110
110
|
return ((0, jsx_runtime_1.jsx)("th", { "aria-sort": isSorted
|
|
@@ -118,7 +118,7 @@ const PsDataTable = ({ caption, className, columns, defaultSort, density = 'norm
|
|
|
118
118
|
const rowLabel = getRowLabel ? getRowLabel(row) : rowId;
|
|
119
119
|
return ((0, jsx_runtime_1.jsxs)(react_1.Fragment, { children: [(0, jsx_runtime_1.jsxs)("tr", { "aria-expanded": renderExpandedRow ? isExpanded : undefined, "aria-label": onRowClick ? rowLabel : undefined, className: ps_design_system_1.PallasoftClassNameService.merge('align-middle transition-colors', onRowClick
|
|
120
120
|
? 'ps-focus-ring cursor-pointer hover:bg-mist/60'
|
|
121
|
-
: 'hover:bg-mist/40', isExpanded ? 'bg-mist/40' : ''), onClick: onRowClick ? () => onRowClick(row) : undefined, onKeyDown: onRowClick
|
|
121
|
+
: 'hover:bg-mist/40', isExpanded ? 'bg-mist/40' : ''), onClick: onRowClick ? () => onRowClick(row) : undefined, onFocus: onRowHover ? () => onRowHover(row) : undefined, onPointerEnter: onRowHover ? () => onRowHover(row) : undefined, onKeyDown: onRowClick
|
|
122
122
|
? (event) => {
|
|
123
123
|
if (event.target !== event.currentTarget)
|
|
124
124
|
return;
|
|
@@ -9,9 +9,10 @@ export interface PsTableCardListProps<RowData> {
|
|
|
9
9
|
getRowLabel: (row: RowData) => string;
|
|
10
10
|
isSelectable: boolean;
|
|
11
11
|
onRowClick?: (row: RowData) => void;
|
|
12
|
+
onRowHover?: (row: RowData) => void;
|
|
12
13
|
onToggleRow: (rowId: string) => void;
|
|
13
14
|
renderExpandedRow?: (row: RowData) => ReactNode;
|
|
14
15
|
rows: RowData[];
|
|
15
16
|
selectedIds: string[];
|
|
16
17
|
}
|
|
17
|
-
export declare const PsTableCardList: <RowData>({ columns, emptyContent, expandedRowIds, footerColumns, getRowId, getRowLabel, isSelectable, onRowClick, onToggleRow, renderExpandedRow, rows, selectedIds, }: PsTableCardListProps<RowData>) => import("react").JSX.Element;
|
|
18
|
+
export declare const PsTableCardList: <RowData>({ columns, emptyContent, expandedRowIds, footerColumns, getRowId, getRowLabel, isSelectable, onRowClick, onRowHover, onToggleRow, renderExpandedRow, rows, selectedIds, }: PsTableCardListProps<RowData>) => import("react").JSX.Element;
|
|
@@ -5,14 +5,19 @@ const jsx_runtime_1 = require("react/jsx-runtime");
|
|
|
5
5
|
const ps_design_system_1 = require("@pallasoft/ps-design-system");
|
|
6
6
|
const ps_input_components_1 = require("@pallasoft/ps-input-components");
|
|
7
7
|
const ps_motion_components_1 = require("@pallasoft/ps-motion-components");
|
|
8
|
-
const
|
|
8
|
+
const table_card_role_1 = require("../constants/table-card-role");
|
|
9
|
+
const PsTableCardList = ({ columns, emptyContent, expandedRowIds, footerColumns, getRowId, getRowLabel, isSelectable, onRowClick, onRowHover, onToggleRow, renderExpandedRow, rows, selectedIds, }) => {
|
|
9
10
|
const [primary, ...details] = columns.filter((column) => !column.hideOnCards);
|
|
10
11
|
const trailing = details.find((column) => column.align === 'right');
|
|
11
|
-
const
|
|
12
|
+
const others = details.filter((column) => column !== trailing);
|
|
13
|
+
const subtitles = others.filter((column) => column.cardRole === table_card_role_1.PsTableCardRole.subtitle);
|
|
14
|
+
const metas = others.filter((column) => column.cardRole === table_card_role_1.PsTableCardRole.meta);
|
|
15
|
+
const rest = others.filter((column) => column.cardRole === undefined || column.cardRole === table_card_role_1.PsTableCardRole.detail);
|
|
16
|
+
const hasValue = (value) => value !== null && value !== undefined && value !== '';
|
|
12
17
|
return ((0, jsx_runtime_1.jsxs)("ul", { className: 'divide-y divide-line', children: [rows.length === 0 ? (0, jsx_runtime_1.jsx)("li", { children: emptyContent }) : null, rows.map((row) => {
|
|
13
18
|
const rowId = getRowId(row);
|
|
14
19
|
const isExpanded = expandedRowIds ? expandedRowIds.includes(rowId) : false;
|
|
15
|
-
return ((0, jsx_runtime_1.jsxs)("li", { children: [(0, jsx_runtime_1.jsxs)("div", { "aria-label": onRowClick ? getRowLabel(row) : undefined, className: ps_design_system_1.PallasoftClassNameService.merge('flex gap-3 px-4 py-3
|
|
20
|
+
return ((0, jsx_runtime_1.jsxs)("li", { children: [(0, jsx_runtime_1.jsxs)("div", { "aria-label": onRowClick ? getRowLabel(row) : undefined, className: ps_design_system_1.PallasoftClassNameService.merge('flex gap-3 px-4 py-3 transition-colors', onRowClick ? 'ps-focus-ring cursor-pointer active:bg-mist/70' : ''), onClick: onRowClick ? () => onRowClick(row) : undefined, onFocus: onRowHover ? () => onRowHover(row) : undefined, onPointerEnter: onRowHover ? () => onRowHover(row) : undefined, onKeyDown: onRowClick
|
|
16
21
|
? (event) => {
|
|
17
22
|
if (event.target !== event.currentTarget)
|
|
18
23
|
return;
|
|
@@ -21,12 +26,15 @@ const PsTableCardList = ({ columns, emptyContent, expandedRowIds, footerColumns,
|
|
|
21
26
|
event.preventDefault();
|
|
22
27
|
onRowClick(row);
|
|
23
28
|
}
|
|
24
|
-
: undefined, role: onRowClick ? 'button' : undefined, tabIndex: onRowClick ? 0 : undefined, children: [isSelectable ? ((0, jsx_runtime_1.jsx)("div", { className: 'pt-0.5', onClick: (event) => event.stopPropagation(), onKeyDown: (event) => event.stopPropagation(), children: (0, jsx_runtime_1.jsx)(ps_input_components_1.PsCheckbox, { isSelected: selectedIds.includes(rowId), label: `Select ${getRowLabel(row)}`, onChange: () => onToggleRow(rowId) }) })) : null, (0, jsx_runtime_1.jsxs)("div", { className: 'flex min-w-0 flex-1 flex-col gap-2', children: [(0, jsx_runtime_1.jsxs)("div", { className: 'flex items-start justify-between gap-3', children: [(0, jsx_runtime_1.jsx)("div", { className: 'min-w-0 text-sm font-semibold text-ink', children: primary ? primary.render(row) : null }), trailing ? ((0, jsx_runtime_1.jsx)("div", { className: 'shrink-0 text-right text-sm font-semibold tabular-nums text-ink', children: trailing.render(row) })) : null] }), rest.length > 0 ? ((0, jsx_runtime_1.jsx)("dl", { className: 'grid grid-cols-2 gap-x-4 gap-y-1.5', children: rest
|
|
29
|
+
: undefined, role: onRowClick ? 'button' : undefined, tabIndex: onRowClick ? 0 : undefined, children: [isSelectable ? ((0, jsx_runtime_1.jsx)("div", { className: 'pt-0.5', onClick: (event) => event.stopPropagation(), onKeyDown: (event) => event.stopPropagation(), children: (0, jsx_runtime_1.jsx)(ps_input_components_1.PsCheckbox, { isSelected: selectedIds.includes(rowId), label: `Select ${getRowLabel(row)}`, onChange: () => onToggleRow(rowId) }) })) : null, (0, jsx_runtime_1.jsxs)("div", { className: 'flex min-w-0 flex-1 flex-col gap-2', children: [(0, jsx_runtime_1.jsxs)("div", { className: 'flex items-start justify-between gap-3', children: [(0, jsx_runtime_1.jsxs)("div", { className: 'flex min-w-0 flex-col gap-0.5', children: [(0, jsx_runtime_1.jsx)("div", { className: 'min-w-0 text-sm font-semibold text-ink', children: primary ? primary.render(row) : null }), subtitles.map((column) => ((0, jsx_runtime_1.jsx)("div", { className: 'truncate text-sm text-ink/65', children: column.render(row) }, column.id)))] }), trailing ? ((0, jsx_runtime_1.jsx)("div", { className: 'shrink-0 text-right text-sm font-semibold tabular-nums text-ink', children: trailing.render(row) })) : null] }), rest.length > 0 ? ((0, jsx_runtime_1.jsx)("dl", { className: 'grid grid-cols-2 gap-x-4 gap-y-1.5', children: rest
|
|
25
30
|
.map((column) => ({ column, value: column.render(row) }))
|
|
26
|
-
.filter(({ value }) => value
|
|
31
|
+
.filter(({ value }) => hasValue(value))
|
|
27
32
|
.map(({ column, value }) => ((0, jsx_runtime_1.jsxs)("div", { className: column.cardFullWidth ? 'col-span-2 min-w-0' : 'min-w-0', children: [(0, jsx_runtime_1.jsx)("dt", { className: 'text-[0.68rem] font-semibold uppercase tracking-[0.12em] text-ink/40', children: column.label }), (0, jsx_runtime_1.jsx)("dd", { className: column.cardFullWidth
|
|
28
33
|
? 'text-sm text-ink/80'
|
|
29
|
-
: 'truncate text-sm text-ink/80', children: value })] }, column.id))) })) : null
|
|
34
|
+
: 'truncate text-sm text-ink/80', children: value })] }, column.id))) })) : null, metas.length > 0 ? ((0, jsx_runtime_1.jsx)("div", { className: 'flex flex-wrap items-center gap-x-4 gap-y-1.5 text-xs text-ink/70', children: metas
|
|
35
|
+
.map((column) => ({ column, value: column.render(row) }))
|
|
36
|
+
.filter(({ value }) => hasValue(value))
|
|
37
|
+
.map(({ column, value }) => ((0, jsx_runtime_1.jsxs)("div", { className: 'flex min-w-0 items-center gap-1.5', children: [(0, jsx_runtime_1.jsx)("span", { className: 'text-ink/40', children: column.label }), (0, jsx_runtime_1.jsx)("span", { className: 'min-w-0 truncate', children: value })] }, column.id))) })) : null] })] }), renderExpandedRow ? ((0, jsx_runtime_1.jsx)(ps_motion_components_1.PsCollapse, { isOpen: isExpanded, children: (0, jsx_runtime_1.jsx)("div", { className: 'border-t border-line bg-mist/40 px-4 py-4', children: renderExpandedRow(row) }) })) : null] }, rowId));
|
|
30
38
|
}), footerColumns.length > 0 ? ((0, jsx_runtime_1.jsx)("li", { className: 'grid grid-cols-2 gap-x-4 gap-y-1.5 bg-mist/50 px-4 py-3', children: footerColumns.map((column) => ((0, jsx_runtime_1.jsxs)("div", { className: 'min-w-0', children: [(0, jsx_runtime_1.jsx)("p", { className: 'text-[0.68rem] font-semibold uppercase tracking-[0.12em] text-ink/40', children: column.label }), (0, jsx_runtime_1.jsx)("p", { className: 'truncate text-sm font-semibold text-ink', children: column.footer })] }, column.id))) })) : null] }));
|
|
31
39
|
};
|
|
32
40
|
exports.PsTableCardList = PsTableCardList;
|
|
@@ -13,6 +13,6 @@ const PsTableToolbar = ({ actions, columns, filters, isSearchable, onClearFilter
|
|
|
13
13
|
const filterableColumns = columns.filter((column) => column.filter);
|
|
14
14
|
const zIndex = (0, ps_core_components_1.usePsLayerZIndex)(ps_design_system_1.PallasoftZIndex.Dropdown);
|
|
15
15
|
const activeCount = filterableColumns.filter((column) => ps_table_query_service_1.PsTableQueryService.isActive(filters[column.id])).length;
|
|
16
|
-
return ((0, jsx_runtime_1.jsxs)("div", { className: 'flex flex-wrap items-center gap-2', children: [isSearchable ? ((0, jsx_runtime_1.jsxs)("label", { className: 'relative basis-full sm:min-w-[12rem] sm:flex-1 sm:basis-auto sm:max-w-xs', children: [(0, jsx_runtime_1.jsx)("span", { className: 'sr-only', children: searchPlaceholder }), (0, jsx_runtime_1.jsx)(ps_icons_1.PsIcon, { "aria-hidden": 'true', className: 'pointer-events-none absolute left-3.5 top-1/2 h-4 w-4 -translate-y-1/2 text-ink/45', iconKey: ps_icons_1.PsIconKey.Search }), (0, jsx_runtime_1.jsx)("input", { className: 'ps-input-control ps-focus-ring min-h-10 rounded-full pl-10 text-sm', onChange: (event) => onSearchChange(event.target.value), placeholder: searchPlaceholder, type: 'search', value: search })] })) : null, filterableColumns.length > 0 ? ((0, jsx_runtime_1.jsxs)(react_aria_components_1.DialogTrigger, { children: [(0, jsx_runtime_1.jsxs)(react_aria_components_1.Button, { className: toolbarButtonClassName, children: [(0, jsx_runtime_1.jsx)(ps_icons_1.PsIcon, { "aria-hidden": 'true', className: 'block h-4 w-4', iconKey: ps_icons_1.PsIconKey.Filter }), (0, jsx_runtime_1.jsx)("span", { className: 'sr-only sm:not-sr-only', children: "Filters" }), activeCount > 0 ? ((0, jsx_runtime_1.jsx)("span", { className: 'inline-flex h-5 min-w-5 items-center justify-center rounded-full bg-primary px-1.5 text-xs font-semibold text-[color:var(--ps-color-primary-contrast)]', children: activeCount })) : null] }), (0, jsx_runtime_1.jsx)(react_aria_components_1.Popover, { className: 'ps-floating-panel ps-scrollbar max-h-[min(34rem,80vh)] w-[min(22rem,calc(100vw-2rem))] overflow-y-auto outline-none', offset: 6, placement: 'bottom start', style: { zIndex }, children: (0, jsx_runtime_1.jsx)(react_aria_components_1.Dialog, { "aria-label": 'Filters', className: 'flex flex-col gap-4 p-4 outline-none', children: (0, jsx_runtime_1.jsxs)(ps_core_components_1.PsLayer, { zIndex: zIndex, children: [filterableColumns.map((column) => ((0, jsx_runtime_1.jsx)(ps_table_filter_field_1.PsTableFilterField, { kind: column.filter ? column.filter.kind : 'text', label: column.label, onChange: (value) => onFilterChange(column.id, value), options: ps_table_query_service_1.PsTableQueryService.options(rows, column), value: filters[column.id] ? filters[column.id] : {} }, column.id))), (0, jsx_runtime_1.jsx)("div", { className: 'flex justify-end border-t border-line pt-3', children: (0, jsx_runtime_1.jsx)("button", { className: 'ps-focus-ring rounded-lg px-2 py-1 text-sm font-semibold text-ink/65 hover:text-ink disabled:opacity-40', disabled: activeCount === 0, onClick: onClearFilters, type: 'button', children: "Clear all" }) })] }) }) })] })) : null, activeCount > 0 ? ((0, jsx_runtime_1.jsx)("button", { className: ps_design_system_1.PallasoftClassNameService.merge('ps-focus-ring rounded-full px-3 py-2 text-sm font-semibold text-ink/65 hover:text-ink'), onClick: onClearFilters, type: 'button', children: "Clear filters" })) : null, actions ? ((0, jsx_runtime_1.jsx)("div", { className: 'ml-auto flex min-w-0 flex-wrap items-center justify-end gap-2', children: actions })) : null] }));
|
|
16
|
+
return ((0, jsx_runtime_1.jsxs)("div", { className: 'flex flex-wrap items-center gap-2', children: [isSearchable ? ((0, jsx_runtime_1.jsxs)("label", { className: 'relative basis-full sm:min-w-[12rem] sm:flex-1 sm:basis-auto sm:max-w-xs', children: [(0, jsx_runtime_1.jsx)("span", { className: 'sr-only', children: searchPlaceholder }), (0, jsx_runtime_1.jsx)(ps_icons_1.PsIcon, { "aria-hidden": 'true', className: 'pointer-events-none absolute left-3.5 top-1/2 h-4 w-4 -translate-y-1/2 text-ink/45', iconKey: ps_icons_1.PsIconKey.Search }), (0, jsx_runtime_1.jsx)("input", { className: 'ps-input-control ps-focus-ring min-h-10 rounded-full pl-10 text-sm', onChange: (event) => onSearchChange(event.target.value), placeholder: searchPlaceholder, type: 'search', value: search })] })) : null, filterableColumns.length > 0 ? ((0, jsx_runtime_1.jsxs)(react_aria_components_1.DialogTrigger, { children: [(0, jsx_runtime_1.jsxs)(react_aria_components_1.Button, { className: toolbarButtonClassName, children: [(0, jsx_runtime_1.jsx)(ps_icons_1.PsIcon, { "aria-hidden": 'true', className: 'block h-4 w-4', iconKey: ps_icons_1.PsIconKey.Filter }), (0, jsx_runtime_1.jsx)("span", { className: 'sr-only sm:not-sr-only', children: "Filters" }), activeCount > 0 ? ((0, jsx_runtime_1.jsx)("span", { className: 'inline-flex h-5 min-w-5 items-center justify-center rounded-full bg-primary px-1.5 text-xs font-semibold text-[color:var(--ps-color-primary-contrast)]', children: activeCount })) : null] }), (0, jsx_runtime_1.jsx)(react_aria_components_1.Popover, { className: 'ps-floating-panel ps-scrollbar-hidden max-h-[min(34rem,80vh)] w-[min(22rem,calc(100vw-2rem))] overflow-y-auto outline-none', offset: 6, placement: 'bottom start', style: { zIndex }, children: (0, jsx_runtime_1.jsx)(react_aria_components_1.Dialog, { "aria-label": 'Filters', className: 'flex flex-col gap-4 p-4 outline-none', children: (0, jsx_runtime_1.jsxs)(ps_core_components_1.PsLayer, { zIndex: zIndex, children: [filterableColumns.map((column) => ((0, jsx_runtime_1.jsx)(ps_table_filter_field_1.PsTableFilterField, { kind: column.filter ? column.filter.kind : 'text', label: column.label, onChange: (value) => onFilterChange(column.id, value), options: ps_table_query_service_1.PsTableQueryService.options(rows, column), value: filters[column.id] ? filters[column.id] : {} }, column.id))), (0, jsx_runtime_1.jsx)("div", { className: 'flex justify-end border-t border-line pt-3', children: (0, jsx_runtime_1.jsx)("button", { className: 'ps-focus-ring rounded-lg px-2 py-1 text-sm font-semibold text-ink/65 hover:text-ink disabled:opacity-40', disabled: activeCount === 0, onClick: onClearFilters, type: 'button', children: "Clear all" }) })] }) }) })] })) : null, activeCount > 0 ? ((0, jsx_runtime_1.jsx)("button", { className: ps_design_system_1.PallasoftClassNameService.merge('ps-focus-ring rounded-full px-3 py-2 text-sm font-semibold text-ink/65 hover:text-ink'), onClick: onClearFilters, type: 'button', children: "Clear filters" })) : null, actions ? ((0, jsx_runtime_1.jsx)("div", { className: 'ml-auto flex min-w-0 flex-wrap items-center justify-end gap-2', children: actions })) : null] }));
|
|
17
17
|
};
|
|
18
18
|
exports.PsTableToolbar = PsTableToolbar;
|
|
@@ -3,7 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.PsTableSearchConfig = void 0;
|
|
4
4
|
exports.PsTableSearchConfig = {
|
|
5
5
|
currencySymbols: /[$€£¥]/g,
|
|
6
|
-
diacritics: /[
|
|
6
|
+
diacritics: /[\u0300-\u036f]/g,
|
|
7
7
|
thousandsSeparator: /(\d),(?=\d{3}\b)/g,
|
|
8
8
|
whitespace: /\s+/g,
|
|
9
9
|
};
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Fragment, useMemo, useRef, useState } from 'react';
|
|
3
|
+
import { PallasoftClassNameService } from '@pallasoft/ps-design-system';
|
|
4
|
+
import { PsCheckbox } from '@pallasoft/ps-input-components';
|
|
5
|
+
import { PsCollapse } from '@pallasoft/ps-motion-components';
|
|
6
|
+
import { PsTableConfig } from '../constants/table-config';
|
|
7
|
+
import { PsTableQueryService } from '../services/ps-table-query-service';
|
|
8
|
+
import { PsTableBulkBar } from './ps-table-bulk-bar';
|
|
9
|
+
import { PsTableCardList } from './ps-table-card-list';
|
|
10
|
+
import { PsTableColumnPicker } from './ps-table-column-picker';
|
|
11
|
+
import { PsTablePagination } from './ps-table-pagination';
|
|
12
|
+
import { PsTableSortHeader } from './ps-table-sort-header';
|
|
13
|
+
import { PsTableToolbar } from './ps-table-toolbar';
|
|
14
|
+
const densityClassNames = {
|
|
15
|
+
compact: {
|
|
16
|
+
cell: 'px-3 py-1.5 text-[0.8125rem] leading-5',
|
|
17
|
+
head: 'px-3 py-2',
|
|
18
|
+
},
|
|
19
|
+
normal: {
|
|
20
|
+
cell: 'px-5 py-4',
|
|
21
|
+
head: 'px-5 py-3.5',
|
|
22
|
+
},
|
|
23
|
+
};
|
|
24
|
+
const columnStyle = (column) => column.width || column.minWidth ? { minWidth: column.minWidth, width: column.width } : undefined;
|
|
25
|
+
const alignClassNames = {
|
|
26
|
+
center: 'text-center',
|
|
27
|
+
left: 'text-left',
|
|
28
|
+
right: 'text-right tabular-nums',
|
|
29
|
+
};
|
|
30
|
+
export const PsDataTable = ({ caption, className, columns, defaultSort, density = 'normal', expandedRowIds, mobileLayout = 'cards', getBulkActions, getRowId, getRowLabel, getSearchText, onPageChange, onQueryChange, onRowClick, onRowHover, page, pageSize, query, renderExpandedRow, rows, searchPlaceholder = PsTableConfig.searchPlaceholder, toolbarActions, totalRows, }) => {
|
|
31
|
+
const containerRef = useRef(null);
|
|
32
|
+
const [uncontrolledPage, setUncontrolledPage] = useState(1);
|
|
33
|
+
const [uncontrolledQuery, setUncontrolledQuery] = useState(() => PsTableQueryService.empty(defaultSort ? defaultSort : null));
|
|
34
|
+
const [selectedIds, setSelectedIds] = useState([]);
|
|
35
|
+
const currentQuery = query ? query : uncontrolledQuery;
|
|
36
|
+
const { filters, hiddenColumnIds, search, sort } = currentQuery;
|
|
37
|
+
const visibleColumns = columns.filter((column) => !hiddenColumnIds.includes(column.id));
|
|
38
|
+
const updateQuery = (patch) => {
|
|
39
|
+
const next = { ...currentQuery, ...patch };
|
|
40
|
+
if (!query)
|
|
41
|
+
setUncontrolledQuery(next);
|
|
42
|
+
onQueryChange?.(next);
|
|
43
|
+
};
|
|
44
|
+
const isServerPaginated = totalRows !== undefined;
|
|
45
|
+
const hasFooter = columns.some((column) => column.footer !== undefined);
|
|
46
|
+
const isSearchable = Boolean(getSearchText) && !isServerPaginated;
|
|
47
|
+
const hasFilters = columns.some((column) => column.filter) && !isServerPaginated;
|
|
48
|
+
const hasToolbar = isSearchable || hasFilters || Boolean(toolbarActions);
|
|
49
|
+
const isSelectable = Boolean(getBulkActions);
|
|
50
|
+
const columnCount = visibleColumns.length + (isSelectable ? 1 : 0);
|
|
51
|
+
const queriedRows = useMemo(() => isServerPaginated
|
|
52
|
+
? rows
|
|
53
|
+
: PsTableQueryService.apply(rows, columns, filters, search, getSearchText, sort), [columns, filters, getSearchText, isServerPaginated, rows, search, sort]);
|
|
54
|
+
const currentPage = page !== null && page !== undefined ? page : uncontrolledPage;
|
|
55
|
+
const rowCount = isServerPaginated ? totalRows : queriedRows.length;
|
|
56
|
+
const pageCount = pageSize ? Math.max(Math.ceil(rowCount / pageSize), 1) : 1;
|
|
57
|
+
const safePage = Math.min(Math.max(currentPage, 1), pageCount);
|
|
58
|
+
const isQueried = search.trim() !== '' ||
|
|
59
|
+
Object.values(filters).some((value) => PsTableQueryService.isActive(value));
|
|
60
|
+
const visibleRows = useMemo(() => {
|
|
61
|
+
if (!pageSize || isServerPaginated)
|
|
62
|
+
return queriedRows;
|
|
63
|
+
return queriedRows.slice((safePage - 1) * pageSize, safePage * pageSize);
|
|
64
|
+
}, [isServerPaginated, pageSize, queriedRows, safePage]);
|
|
65
|
+
const changePage = (next) => {
|
|
66
|
+
if (onPageChange) {
|
|
67
|
+
onPageChange(next);
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
containerRef.current?.scrollIntoView({ behavior: 'smooth', block: 'nearest' });
|
|
71
|
+
setUncontrolledPage(next);
|
|
72
|
+
};
|
|
73
|
+
const resetPage = () => {
|
|
74
|
+
if (safePage === 1)
|
|
75
|
+
return;
|
|
76
|
+
if (onPageChange)
|
|
77
|
+
onPageChange(1);
|
|
78
|
+
else
|
|
79
|
+
setUncontrolledPage(1);
|
|
80
|
+
};
|
|
81
|
+
const updateSort = (columnId) => {
|
|
82
|
+
updateQuery({ sort: PsTableQueryService.nextSort(sort, columnId) });
|
|
83
|
+
resetPage();
|
|
84
|
+
};
|
|
85
|
+
const clearFilters = () => {
|
|
86
|
+
updateQuery({ filters: {}, search: '' });
|
|
87
|
+
resetPage();
|
|
88
|
+
};
|
|
89
|
+
const selectedRows = queriedRows.filter((row) => selectedIds.includes(getRowId(row)));
|
|
90
|
+
const pageIds = visibleRows.map((row) => getRowId(row));
|
|
91
|
+
const isPageSelected = pageIds.length > 0 && pageIds.every((rowId) => selectedIds.includes(rowId));
|
|
92
|
+
const togglePage = () => setSelectedIds((current) => isPageSelected
|
|
93
|
+
? current.filter((rowId) => !pageIds.includes(rowId))
|
|
94
|
+
: [...new Set([...current, ...pageIds])]);
|
|
95
|
+
const toggleRow = (rowId) => setSelectedIds((current) => current.includes(rowId) ? current.filter((id) => id !== rowId) : [...current, rowId]);
|
|
96
|
+
return (_jsxs("div", { ref: containerRef, className: PallasoftClassNameService.merge('flex min-w-0 flex-col gap-3', className), children: [isSelectable && selectedRows.length > 0 && getBulkActions ? (_jsx(PsTableBulkBar, { actions: getBulkActions(selectedRows), onClear: () => setSelectedIds([]), selectedCount: selectedRows.length })) : hasToolbar || columns.some((column) => column.canHide) ? (_jsx(PsTableToolbar, { actions: _jsxs(_Fragment, { children: [toolbarActions, columns.some((column) => column.canHide) ? (_jsx(PsTableColumnPicker, { columns: columns
|
|
97
|
+
.filter((column) => column.canHide)
|
|
98
|
+
.map((column) => ({ id: column.id, label: column.label })), hiddenColumnIds: hiddenColumnIds, onChange: (nextHidden) => updateQuery({ hiddenColumnIds: nextHidden }) })) : null] }), columns: hasFilters ? columns : [], filters: filters, isSearchable: isSearchable, onClearFilters: clearFilters, onFilterChange: (columnId, value) => {
|
|
99
|
+
updateQuery({ filters: { ...filters, [columnId]: value } });
|
|
100
|
+
resetPage();
|
|
101
|
+
}, onSearchChange: (next) => {
|
|
102
|
+
updateQuery({ search: next });
|
|
103
|
+
resetPage();
|
|
104
|
+
}, rows: rows, search: search, searchPlaceholder: searchPlaceholder })) : null, _jsxs("div", { className: 'min-w-0 overflow-hidden rounded-[1.25rem] border border-line bg-surface', children: [mobileLayout === 'cards' ? (_jsx("div", { className: 'sm:hidden', children: _jsx(PsTableCardList, { columns: visibleColumns, emptyContent: visibleRows.length === 0 && isQueried ? (_jsxs("div", { className: 'px-4 py-10 text-center', children: [_jsx("p", { className: 'text-sm font-semibold text-ink', children: PsTableConfig.noMatchesTitle }), _jsx("button", { className: 'ps-focus-ring mt-2 rounded-full px-3 py-1.5 text-sm font-semibold text-primary', onClick: clearFilters, type: 'button', children: "Clear search and filters" })] })) : null, expandedRowIds: expandedRowIds, footerColumns: hasFooter ? visibleColumns.filter((column) => column.footer !== undefined) : [], getRowId: getRowId, getRowLabel: (row) => (getRowLabel ? getRowLabel(row) : getRowId(row)), isSelectable: isSelectable, onRowClick: onRowClick, onRowHover: onRowHover, onToggleRow: toggleRow, renderExpandedRow: renderExpandedRow, rows: visibleRows, selectedIds: selectedIds }) })) : null, _jsx("div", { className: PallasoftClassNameService.merge('ps-scrollbar overflow-x-auto', mobileLayout === 'cards' ? 'hidden sm:block' : ''), children: _jsxs("table", { className: 'w-full border-collapse text-left', children: [caption ? _jsx("caption", { className: 'sr-only', children: caption }) : null, _jsx("thead", { className: 'bg-mist/70 text-[0.68rem] font-semibold uppercase tracking-[0.18em] text-ink/45', children: _jsxs("tr", { children: [isSelectable ? (_jsx("th", { className: 'w-10 pl-4 pr-0', scope: 'col', children: _jsx(PsCheckbox, { isIndeterminate: !isPageSelected && pageIds.some((rowId) => selectedIds.includes(rowId)), isSelected: isPageSelected, label: 'Select all rows on this page', onChange: togglePage }) })) : null, visibleColumns.map((column) => {
|
|
105
|
+
const align = column.align !== null && column.align !== undefined ? column.align : 'left';
|
|
106
|
+
const isSorted = sort !== null && sort.columnId === column.id;
|
|
107
|
+
return (_jsx("th", { "aria-sort": isSorted
|
|
108
|
+
? sort.direction
|
|
109
|
+
: column.sortValue && !isServerPaginated
|
|
110
|
+
? 'none'
|
|
111
|
+
: undefined, className: PallasoftClassNameService.merge(densityClassNames[density].head, alignClassNames[align], 'whitespace-nowrap'), scope: 'col', style: columnStyle(column), children: column.sortValue && !isServerPaginated ? (_jsx(PsTableSortHeader, { align: align, columnId: column.id, label: column.label, onSort: updateSort, sort: sort })) : (column.label) }, column.id));
|
|
112
|
+
})] }) }), _jsxs("tbody", { className: 'divide-y divide-line', children: [visibleRows.length === 0 && isQueried ? (_jsx("tr", { children: _jsxs("td", { className: 'px-4 py-12 text-center', colSpan: columnCount, children: [_jsx("p", { className: 'text-sm font-semibold text-ink', children: PsTableConfig.noMatchesTitle }), _jsx("p", { className: 'mt-1 text-sm text-ink/55', children: PsTableConfig.noMatchesDescription }), _jsx("button", { className: 'ps-focus-ring mt-3 rounded-full px-3 py-1.5 text-sm font-semibold text-primary hover:underline', onClick: clearFilters, type: 'button', children: "Clear search and filters" })] }) })) : null, visibleRows.map((row) => {
|
|
113
|
+
const rowId = getRowId(row);
|
|
114
|
+
const isExpanded = expandedRowIds ? expandedRowIds.includes(rowId) : false;
|
|
115
|
+
const rowLabel = getRowLabel ? getRowLabel(row) : rowId;
|
|
116
|
+
return (_jsxs(Fragment, { children: [_jsxs("tr", { "aria-expanded": renderExpandedRow ? isExpanded : undefined, "aria-label": onRowClick ? rowLabel : undefined, className: PallasoftClassNameService.merge('align-middle transition-colors', onRowClick
|
|
117
|
+
? 'ps-focus-ring cursor-pointer hover:bg-mist/60'
|
|
118
|
+
: 'hover:bg-mist/40', isExpanded ? 'bg-mist/40' : ''), onClick: onRowClick ? () => onRowClick(row) : undefined, onFocus: onRowHover ? () => onRowHover(row) : undefined, onPointerEnter: onRowHover ? () => onRowHover(row) : undefined, onKeyDown: onRowClick
|
|
119
|
+
? (event) => {
|
|
120
|
+
if (event.target !== event.currentTarget)
|
|
121
|
+
return;
|
|
122
|
+
if (event.key !== 'Enter' && event.key !== ' ')
|
|
123
|
+
return;
|
|
124
|
+
event.preventDefault();
|
|
125
|
+
onRowClick(row);
|
|
126
|
+
}
|
|
127
|
+
: undefined, tabIndex: onRowClick ? 0 : undefined, children: [isSelectable ? (_jsx("td", { className: 'w-10 pl-4 pr-0', onClick: (event) => event.stopPropagation(), onKeyDown: (event) => event.stopPropagation(), children: _jsx(PsCheckbox, { isSelected: selectedIds.includes(rowId), label: `Select ${rowLabel}`, onChange: () => toggleRow(rowId) }) })) : null, visibleColumns.map((column) => (_jsx("td", { className: PallasoftClassNameService.merge(densityClassNames[density].cell, alignClassNames[column.align !== null && column.align !== undefined
|
|
128
|
+
? column.align
|
|
129
|
+
: 'left'], column.wrap ? '' : 'whitespace-nowrap', 'text-sm text-ink'), style: columnStyle(column), children: column.render(row) }, column.id)))] }), renderExpandedRow && isExpanded ? (_jsx("tr", { className: 'border-t-0', children: _jsx("td", { className: 'p-0', colSpan: columnCount, children: _jsx(PsCollapse, { isOpen: true, children: _jsx("div", { className: 'w-0 min-w-full border-l-2 border-primary bg-mist/40 px-4 py-4', children: renderExpandedRow(row) }) }) }) })) : null] }, rowId));
|
|
130
|
+
})] }), hasFooter ? (_jsx("tfoot", { className: 'border-t-2 border-line bg-mist/50 text-sm font-semibold text-ink', children: _jsxs("tr", { children: [isSelectable ? _jsx("td", {}) : null, visibleColumns.map((column) => (_jsx("td", { className: PallasoftClassNameService.merge(densityClassNames[density].cell, alignClassNames[column.align !== null && column.align !== undefined
|
|
131
|
+
? column.align
|
|
132
|
+
: 'left'], 'whitespace-nowrap'), children: column.footer }, column.id)))] }) })) : null] }) }), pageSize ? (_jsx(PsTablePagination, { onPageChange: changePage, page: safePage, pageCount: pageCount, pageSize: pageSize, totalRows: rowCount })) : null] })] }));
|
|
133
|
+
};
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import { jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { PallasoftClassNameService } from '@pallasoft/ps-design-system';
|
|
3
|
+
export const PsTableBulkBar = ({ actions, onClear, selectedCount }) => (_jsxs("div", { "aria-live": 'polite', className: 'flex flex-wrap items-center gap-1 rounded-2xl border border-primary/30 bg-primary/10 px-2 py-1.5 sm:gap-2 sm:rounded-full', children: [_jsxs("span", { className: 'px-2 text-sm font-semibold text-ink', children: [selectedCount, " selected"] }), actions.map((action) => (_jsx("button", { className: PallasoftClassNameService.merge('ps-focus-ring inline-flex min-h-8 items-center rounded-full px-3 text-sm font-semibold transition-colors disabled:opacity-40', action.isDanger ? 'text-danger hover:bg-danger/10' : 'text-ink hover:bg-ink/5'), disabled: action.isDisabled, onClick: action.onSelect, type: 'button', children: action.label }, action.id))), _jsx("button", { className: 'ps-focus-ring ml-auto inline-flex min-h-8 items-center rounded-full px-3 text-sm font-semibold text-ink/65 hover:text-ink', onClick: onClear, type: 'button', children: "Clear selection" })] }));
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { PallasoftClassNameService } from '@pallasoft/ps-design-system';
|
|
3
|
+
import { PsCheckbox } from '@pallasoft/ps-input-components';
|
|
4
|
+
import { PsCollapse } from '@pallasoft/ps-motion-components';
|
|
5
|
+
import { PsTableCardRole } from '../constants/table-card-role';
|
|
6
|
+
export const PsTableCardList = ({ columns, emptyContent, expandedRowIds, footerColumns, getRowId, getRowLabel, isSelectable, onRowClick, onRowHover, onToggleRow, renderExpandedRow, rows, selectedIds, }) => {
|
|
7
|
+
const [primary, ...details] = columns.filter((column) => !column.hideOnCards);
|
|
8
|
+
const trailing = details.find((column) => column.align === 'right');
|
|
9
|
+
const others = details.filter((column) => column !== trailing);
|
|
10
|
+
const subtitles = others.filter((column) => column.cardRole === PsTableCardRole.subtitle);
|
|
11
|
+
const metas = others.filter((column) => column.cardRole === PsTableCardRole.meta);
|
|
12
|
+
const rest = others.filter((column) => column.cardRole === undefined || column.cardRole === PsTableCardRole.detail);
|
|
13
|
+
const hasValue = (value) => value !== null && value !== undefined && value !== '';
|
|
14
|
+
return (_jsxs("ul", { className: 'divide-y divide-line', children: [rows.length === 0 ? _jsx("li", { children: emptyContent }) : null, rows.map((row) => {
|
|
15
|
+
const rowId = getRowId(row);
|
|
16
|
+
const isExpanded = expandedRowIds ? expandedRowIds.includes(rowId) : false;
|
|
17
|
+
return (_jsxs("li", { children: [_jsxs("div", { "aria-label": onRowClick ? getRowLabel(row) : undefined, className: PallasoftClassNameService.merge('flex gap-3 px-4 py-3 transition-colors', onRowClick ? 'ps-focus-ring cursor-pointer active:bg-mist/70' : ''), onClick: onRowClick ? () => onRowClick(row) : undefined, onFocus: onRowHover ? () => onRowHover(row) : undefined, onPointerEnter: onRowHover ? () => onRowHover(row) : undefined, onKeyDown: onRowClick
|
|
18
|
+
? (event) => {
|
|
19
|
+
if (event.target !== event.currentTarget)
|
|
20
|
+
return;
|
|
21
|
+
if (event.key !== 'Enter' && event.key !== ' ')
|
|
22
|
+
return;
|
|
23
|
+
event.preventDefault();
|
|
24
|
+
onRowClick(row);
|
|
25
|
+
}
|
|
26
|
+
: undefined, role: onRowClick ? 'button' : undefined, tabIndex: onRowClick ? 0 : undefined, children: [isSelectable ? (_jsx("div", { className: 'pt-0.5', onClick: (event) => event.stopPropagation(), onKeyDown: (event) => event.stopPropagation(), children: _jsx(PsCheckbox, { isSelected: selectedIds.includes(rowId), label: `Select ${getRowLabel(row)}`, onChange: () => onToggleRow(rowId) }) })) : null, _jsxs("div", { className: 'flex min-w-0 flex-1 flex-col gap-2', children: [_jsxs("div", { className: 'flex items-start justify-between gap-3', children: [_jsxs("div", { className: 'flex min-w-0 flex-col gap-0.5', children: [_jsx("div", { className: 'min-w-0 text-sm font-semibold text-ink', children: primary ? primary.render(row) : null }), subtitles.map((column) => (_jsx("div", { className: 'truncate text-sm text-ink/65', children: column.render(row) }, column.id)))] }), trailing ? (_jsx("div", { className: 'shrink-0 text-right text-sm font-semibold tabular-nums text-ink', children: trailing.render(row) })) : null] }), rest.length > 0 ? (_jsx("dl", { className: 'grid grid-cols-2 gap-x-4 gap-y-1.5', children: rest
|
|
27
|
+
.map((column) => ({ column, value: column.render(row) }))
|
|
28
|
+
.filter(({ value }) => hasValue(value))
|
|
29
|
+
.map(({ column, value }) => (_jsxs("div", { className: column.cardFullWidth ? 'col-span-2 min-w-0' : 'min-w-0', children: [_jsx("dt", { className: 'text-[0.68rem] font-semibold uppercase tracking-[0.12em] text-ink/40', children: column.label }), _jsx("dd", { className: column.cardFullWidth
|
|
30
|
+
? 'text-sm text-ink/80'
|
|
31
|
+
: 'truncate text-sm text-ink/80', children: value })] }, column.id))) })) : null, metas.length > 0 ? (_jsx("div", { className: 'flex flex-wrap items-center gap-x-4 gap-y-1.5 text-xs text-ink/70', children: metas
|
|
32
|
+
.map((column) => ({ column, value: column.render(row) }))
|
|
33
|
+
.filter(({ value }) => hasValue(value))
|
|
34
|
+
.map(({ column, value }) => (_jsxs("div", { className: 'flex min-w-0 items-center gap-1.5', children: [_jsx("span", { className: 'text-ink/40', children: column.label }), _jsx("span", { className: 'min-w-0 truncate', children: value })] }, column.id))) })) : null] })] }), renderExpandedRow ? (_jsx(PsCollapse, { isOpen: isExpanded, children: _jsx("div", { className: 'border-t border-line bg-mist/40 px-4 py-4', children: renderExpandedRow(row) }) })) : null] }, rowId));
|
|
35
|
+
}), footerColumns.length > 0 ? (_jsx("li", { className: 'grid grid-cols-2 gap-x-4 gap-y-1.5 bg-mist/50 px-4 py-3', children: footerColumns.map((column) => (_jsxs("div", { className: 'min-w-0', children: [_jsx("p", { className: 'text-[0.68rem] font-semibold uppercase tracking-[0.12em] text-ink/40', children: column.label }), _jsx("p", { className: 'truncate text-sm font-semibold text-ink', children: column.footer })] }, column.id))) })) : null] }));
|
|
36
|
+
};
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { PsLayer, usePsLayerZIndex } from '@pallasoft/ps-core-components';
|
|
3
|
+
import { PallasoftZIndex } from '@pallasoft/ps-design-system';
|
|
4
|
+
import { PsIcon, PsIconKey } from '@pallasoft/ps-icons';
|
|
5
|
+
import { PsCheckbox } from '@pallasoft/ps-input-components';
|
|
6
|
+
import { Button, Dialog, DialogTrigger, Popover } from 'react-aria-components';
|
|
7
|
+
export const PsTableColumnPicker = ({ columns, hiddenColumnIds, onChange, }) => {
|
|
8
|
+
const zIndex = usePsLayerZIndex(PallasoftZIndex.Dropdown);
|
|
9
|
+
const visibleCount = columns.filter((column) => !hiddenColumnIds.includes(column.id)).length;
|
|
10
|
+
return (_jsxs(DialogTrigger, { children: [_jsxs(Button, { className: 'ps-focus-ring ps-liquid ps-liquid-control inline-flex min-h-10 min-w-10 items-center justify-center gap-2 rounded-full px-3 text-sm font-semibold text-ink outline-none transition-colors hover:border-primary/40 sm:px-4', children: [_jsx(PsIcon, { "aria-hidden": 'true', className: 'block h-4 w-4 sm:hidden', iconKey: PsIconKey.Columns }), _jsx("span", { className: 'sr-only sm:not-sr-only', children: "Columns" }), _jsx(PsIcon, { "aria-hidden": 'true', className: 'hidden h-4 w-4 sm:block', iconKey: PsIconKey.Chevron })] }), _jsx(Popover, { className: 'ps-floating-panel w-60 outline-none', offset: 6, placement: 'bottom end', style: { zIndex }, children: _jsx(Dialog, { "aria-label": 'Columns', className: 'flex flex-col gap-0.5 p-1.5 outline-none', children: _jsx(PsLayer, { zIndex: zIndex, children: columns.map((column) => {
|
|
11
|
+
const isVisible = !hiddenColumnIds.includes(column.id);
|
|
12
|
+
return (_jsx(PsCheckbox, { className: 'ps-floating-option w-full', isDisabled: isVisible && visibleCount <= 1, isSelected: isVisible, label: column.label, onChange: () => onChange(isVisible
|
|
13
|
+
? [...hiddenColumnIds, column.id]
|
|
14
|
+
: hiddenColumnIds.filter((hiddenId) => hiddenId !== column.id)), children: column.label }, column.id));
|
|
15
|
+
}) }) }) })] }));
|
|
16
|
+
};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { PsDateInput, PsMultiSelectInput, PsTextInput } from '@pallasoft/ps-input-components';
|
|
3
|
+
import { PsTableFilterKind } from '../constants/table-filter-kind';
|
|
4
|
+
export const PsTableFilterField = ({ kind, label, onChange, options, value, }) => {
|
|
5
|
+
if (kind === PsTableFilterKind.select)
|
|
6
|
+
return (_jsx(PsMultiSelectInput, { label: label, onChange: (values) => onChange({ values }), options: options, placeholder: 'Any', value: value.values ? value.values : [] }));
|
|
7
|
+
if (kind === PsTableFilterKind.text)
|
|
8
|
+
return (_jsx(PsTextInput, { label: label, onChange: (event) => onChange({ text: event.target.value }), placeholder: 'Contains', value: value.text ? value.text : '' }));
|
|
9
|
+
if (kind === PsTableFilterKind.number)
|
|
10
|
+
return (_jsxs("fieldset", { className: 'grid grid-cols-2 gap-2', children: [_jsx("legend", { className: 'ps-input-label col-span-2 mb-2', children: label }), _jsx(PsTextInput, { hideLabel: true, inputMode: 'decimal', label: `${label} minimum`, onChange: (event) => onChange({ ...value, from: event.target.value }), placeholder: 'Min', value: value.from ? value.from : '' }), _jsx(PsTextInput, { hideLabel: true, inputMode: 'decimal', label: `${label} maximum`, onChange: (event) => onChange({ ...value, to: event.target.value }), placeholder: 'Max', value: value.to ? value.to : '' })] }));
|
|
11
|
+
return (_jsxs("fieldset", { className: 'grid grid-cols-2 gap-2', children: [_jsx("legend", { className: 'ps-input-label col-span-2 mb-2', children: label }), _jsx(PsDateInput, { hideLabel: true, label: `${label} from`, onChange: (from) => onChange({ ...value, from }), placeholder: 'From', value: value.from ? value.from : '' }), _jsx(PsDateInput, { hideLabel: true, label: `${label} to`, onChange: (to) => onChange({ ...value, to }), placeholder: 'To', value: value.to ? value.to : '' })] }));
|
|
12
|
+
};
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { PallasoftClassNameService } from '@pallasoft/ps-design-system';
|
|
3
|
+
export const PsTablePagination = ({ className, onPageChange, page, pageCount, pageSize, totalRows, }) => {
|
|
4
|
+
const firstRow = totalRows === 0 ? 0 : (page - 1) * pageSize + 1;
|
|
5
|
+
const lastRow = Math.min(page * pageSize, totalRows);
|
|
6
|
+
const buttonClassName = 'ps-focus-ring inline-flex min-h-9 items-center rounded-full border border-line px-3.5 text-sm font-semibold text-ink transition-colors disabled:cursor-not-allowed disabled:opacity-40 enabled:hover:border-primary/40';
|
|
7
|
+
return (_jsxs("nav", { "aria-label": 'Pagination', className: PallasoftClassNameService.merge('flex flex-wrap items-center justify-between gap-3 border-t border-line px-4 py-3', className), children: [_jsx("p", { className: 'text-sm text-ink/55', children: totalRows === 0 ? 'No rows' : `${firstRow}–${lastRow} of ${totalRows}` }), _jsxs("div", { className: 'flex items-center gap-2', children: [_jsx("button", { className: buttonClassName, disabled: page <= 1, onClick: () => onPageChange(page - 1), type: 'button', children: "Previous" }), _jsxs("span", { className: 'hidden text-sm text-ink/65 sm:inline', children: ["Page ", Math.min(page, pageCount), " of ", pageCount] }), _jsx("button", { className: buttonClassName, disabled: page >= pageCount, onClick: () => onPageChange(page + 1), type: 'button', children: "Next" })] })] }));
|
|
8
|
+
};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { PallasoftClassNameService } from '@pallasoft/ps-design-system';
|
|
3
|
+
import { PsIcon, PsIconKey } from '@pallasoft/ps-icons';
|
|
4
|
+
export const PsTableSortHeader = ({ align, columnId, label, onSort, sort, }) => {
|
|
5
|
+
const isSorted = sort !== null && sort.columnId === columnId;
|
|
6
|
+
const iconKey = !isSorted
|
|
7
|
+
? PsIconKey.Sort
|
|
8
|
+
: sort.direction === 'ascending'
|
|
9
|
+
? PsIconKey.SortAscending
|
|
10
|
+
: PsIconKey.SortDescending;
|
|
11
|
+
return (_jsxs("button", { className: PallasoftClassNameService.merge('ps-focus-ring group inline-flex items-center gap-1 rounded-md uppercase tracking-[0.18em] transition-colors hover:text-ink', align === 'right' ? 'flex-row-reverse' : '', isSorted ? 'text-ink' : ''), onClick: () => onSort(columnId), type: 'button', children: [_jsx("span", { children: label }), _jsx(PsIcon, { "aria-hidden": 'true', className: PallasoftClassNameService.merge('block h-3.5 w-3.5 shrink-0', isSorted
|
|
12
|
+
? 'text-primary'
|
|
13
|
+
: 'opacity-0 group-hover:opacity-60 group-focus-visible:opacity-60'), iconKey: iconKey })] }));
|
|
14
|
+
};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { PsLayer, usePsLayerZIndex } from '@pallasoft/ps-core-components';
|
|
3
|
+
import { PallasoftClassNameService, PallasoftZIndex } from '@pallasoft/ps-design-system';
|
|
4
|
+
import { PsIcon, PsIconKey } from '@pallasoft/ps-icons';
|
|
5
|
+
import { Button, Dialog, DialogTrigger, Popover } from 'react-aria-components';
|
|
6
|
+
import { PsTableQueryService } from '../services/ps-table-query-service';
|
|
7
|
+
import { PsTableFilterField } from './ps-table-filter-field';
|
|
8
|
+
const toolbarButtonClassName = 'ps-focus-ring ps-liquid ps-liquid-control inline-flex min-h-10 min-w-10 items-center justify-center gap-2 rounded-full px-3 text-sm font-semibold text-ink outline-none transition-colors hover:border-primary/40 sm:px-4';
|
|
9
|
+
export const PsTableToolbar = ({ actions, columns, filters, isSearchable, onClearFilters, onFilterChange, onSearchChange, rows, search, searchPlaceholder, }) => {
|
|
10
|
+
const filterableColumns = columns.filter((column) => column.filter);
|
|
11
|
+
const zIndex = usePsLayerZIndex(PallasoftZIndex.Dropdown);
|
|
12
|
+
const activeCount = filterableColumns.filter((column) => PsTableQueryService.isActive(filters[column.id])).length;
|
|
13
|
+
return (_jsxs("div", { className: 'flex flex-wrap items-center gap-2', children: [isSearchable ? (_jsxs("label", { className: 'relative basis-full sm:min-w-[12rem] sm:flex-1 sm:basis-auto sm:max-w-xs', children: [_jsx("span", { className: 'sr-only', children: searchPlaceholder }), _jsx(PsIcon, { "aria-hidden": 'true', className: 'pointer-events-none absolute left-3.5 top-1/2 h-4 w-4 -translate-y-1/2 text-ink/45', iconKey: PsIconKey.Search }), _jsx("input", { className: 'ps-input-control ps-focus-ring min-h-10 rounded-full pl-10 text-sm', onChange: (event) => onSearchChange(event.target.value), placeholder: searchPlaceholder, type: 'search', value: search })] })) : null, filterableColumns.length > 0 ? (_jsxs(DialogTrigger, { children: [_jsxs(Button, { className: toolbarButtonClassName, children: [_jsx(PsIcon, { "aria-hidden": 'true', className: 'block h-4 w-4', iconKey: PsIconKey.Filter }), _jsx("span", { className: 'sr-only sm:not-sr-only', children: "Filters" }), activeCount > 0 ? (_jsx("span", { className: 'inline-flex h-5 min-w-5 items-center justify-center rounded-full bg-primary px-1.5 text-xs font-semibold text-[color:var(--ps-color-primary-contrast)]', children: activeCount })) : null] }), _jsx(Popover, { className: 'ps-floating-panel ps-scrollbar-hidden max-h-[min(34rem,80vh)] w-[min(22rem,calc(100vw-2rem))] overflow-y-auto outline-none', offset: 6, placement: 'bottom start', style: { zIndex }, children: _jsx(Dialog, { "aria-label": 'Filters', className: 'flex flex-col gap-4 p-4 outline-none', children: _jsxs(PsLayer, { zIndex: zIndex, children: [filterableColumns.map((column) => (_jsx(PsTableFilterField, { kind: column.filter ? column.filter.kind : 'text', label: column.label, onChange: (value) => onFilterChange(column.id, value), options: PsTableQueryService.options(rows, column), value: filters[column.id] ? filters[column.id] : {} }, column.id))), _jsx("div", { className: 'flex justify-end border-t border-line pt-3', children: _jsx("button", { className: 'ps-focus-ring rounded-lg px-2 py-1 text-sm font-semibold text-ink/65 hover:text-ink disabled:opacity-40', disabled: activeCount === 0, onClick: onClearFilters, type: 'button', children: "Clear all" }) })] }) }) })] })) : null, activeCount > 0 ? (_jsx("button", { className: PallasoftClassNameService.merge('ps-focus-ring rounded-full px-3 py-2 text-sm font-semibold text-ink/65 hover:text-ink'), onClick: onClearFilters, type: 'button', children: "Clear filters" })) : null, actions ? (_jsx("div", { className: 'ml-auto flex min-w-0 flex-wrap items-center justify-end gap-2', children: actions })) : null] }));
|
|
14
|
+
};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export * from './components/ps-data-table';
|
|
2
|
+
export * from './components/ps-table-pagination';
|
|
3
|
+
export * from './constants/table-card-role';
|
|
4
|
+
export * from './constants/table-filter-kind';
|
|
5
|
+
export * from './interfaces/table-column';
|
|
6
|
+
export * from './interfaces/table-column-filter';
|
|
7
|
+
export * from './interfaces/table-filter-option';
|
|
8
|
+
export * from './interfaces/table-filter-value';
|
|
9
|
+
export * from './interfaces/table-query';
|
|
10
|
+
export * from './interfaces/table-row-action';
|
|
11
|
+
export * from './interfaces/table-sort';
|
|
12
|
+
export * from './services/ps-table-query-service';
|
|
13
|
+
export * from './services/ps-table-search-service';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { PsTableFilterKind } from '../constants/table-filter-kind';
|
|
2
|
+
import { PsTableSearchService } from './ps-table-search-service';
|
|
3
|
+
export class PsTableQueryService {
|
|
4
|
+
static isActive(value) {
|
|
5
|
+
if (!value)
|
|
6
|
+
return false;
|
|
7
|
+
return Boolean((value.text && value.text.trim() !== '') ||
|
|
8
|
+
(value.values && value.values.length > 0) ||
|
|
9
|
+
(value.from && value.from !== '') ||
|
|
10
|
+
(value.to && value.to !== ''));
|
|
11
|
+
}
|
|
12
|
+
static matchesFilter(row, column, value) {
|
|
13
|
+
if (!column.filter || !PsTableQueryService.isActive(value))
|
|
14
|
+
return true;
|
|
15
|
+
const raw = column.filter.getValue(row);
|
|
16
|
+
const cell = raw === null ? '' : String(raw);
|
|
17
|
+
if (column.filter.kind === PsTableFilterKind.text)
|
|
18
|
+
return cell.toLowerCase().includes((value.text ? value.text : '').trim().toLowerCase());
|
|
19
|
+
if (column.filter.kind === PsTableFilterKind.select)
|
|
20
|
+
return value.values ? value.values.includes(cell) : true;
|
|
21
|
+
if (column.filter.kind === PsTableFilterKind.number) {
|
|
22
|
+
const amount = Number(cell);
|
|
23
|
+
if (cell === '' || Number.isNaN(amount))
|
|
24
|
+
return false;
|
|
25
|
+
if (value.from && amount < Number(value.from))
|
|
26
|
+
return false;
|
|
27
|
+
if (value.to && amount > Number(value.to))
|
|
28
|
+
return false;
|
|
29
|
+
return true;
|
|
30
|
+
}
|
|
31
|
+
if (cell === '')
|
|
32
|
+
return false;
|
|
33
|
+
if (value.from && cell < value.from)
|
|
34
|
+
return false;
|
|
35
|
+
if (value.to && cell > value.to)
|
|
36
|
+
return false;
|
|
37
|
+
return true;
|
|
38
|
+
}
|
|
39
|
+
static apply(rows, columns, filters, search, getSearchText, sort) {
|
|
40
|
+
const filtered = rows.filter((row) => (!getSearchText || PsTableSearchService.matches(getSearchText(row), search)) &&
|
|
41
|
+
columns.every((column) => PsTableQueryService.matchesFilter(row, column, filters[column.id] ? filters[column.id] : {})));
|
|
42
|
+
const sortColumn = sort ? columns.find((column) => column.id === sort.columnId) : undefined;
|
|
43
|
+
if (!sort || !sortColumn || !sortColumn.sortValue)
|
|
44
|
+
return filtered;
|
|
45
|
+
const getValue = sortColumn.sortValue;
|
|
46
|
+
const direction = sort.direction === 'ascending' ? 1 : -1;
|
|
47
|
+
return [...filtered].sort((left, right) => {
|
|
48
|
+
const a = getValue(left);
|
|
49
|
+
const b = getValue(right);
|
|
50
|
+
if (a === b)
|
|
51
|
+
return 0;
|
|
52
|
+
if (a === null)
|
|
53
|
+
return 1;
|
|
54
|
+
if (b === null)
|
|
55
|
+
return -1;
|
|
56
|
+
if (typeof a === 'number' && typeof b === 'number')
|
|
57
|
+
return (a - b) * direction;
|
|
58
|
+
return String(a).localeCompare(String(b), undefined, { numeric: true }) * direction;
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
static options(rows, column) {
|
|
62
|
+
if (!column.filter)
|
|
63
|
+
return [];
|
|
64
|
+
if (column.filter.options)
|
|
65
|
+
return column.filter.options;
|
|
66
|
+
const getValue = column.filter.getValue;
|
|
67
|
+
const values = new Set(rows.map((row) => getValue(row)).filter((value) => value !== null));
|
|
68
|
+
return [...values]
|
|
69
|
+
.map((value) => String(value))
|
|
70
|
+
.sort((a, b) => a.localeCompare(b))
|
|
71
|
+
.map((value) => ({ label: value, value }));
|
|
72
|
+
}
|
|
73
|
+
static empty(sort) {
|
|
74
|
+
return { filters: {}, hiddenColumnIds: [], search: '', sort };
|
|
75
|
+
}
|
|
76
|
+
static isEqual(left, right) {
|
|
77
|
+
const activeFilters = (query) => Object.entries(query.filters)
|
|
78
|
+
.filter(([, value]) => PsTableQueryService.isActive(value))
|
|
79
|
+
.sort(([a], [b]) => a.localeCompare(b));
|
|
80
|
+
return (left.search.trim() === right.search.trim() &&
|
|
81
|
+
JSON.stringify(left.sort) === JSON.stringify(right.sort) &&
|
|
82
|
+
JSON.stringify([...left.hiddenColumnIds].sort()) ===
|
|
83
|
+
JSON.stringify([...right.hiddenColumnIds].sort()) &&
|
|
84
|
+
JSON.stringify(activeFilters(left)) === JSON.stringify(activeFilters(right)));
|
|
85
|
+
}
|
|
86
|
+
static nextSort(current, columnId) {
|
|
87
|
+
if (!current || current.columnId !== columnId)
|
|
88
|
+
return { columnId, direction: 'ascending' };
|
|
89
|
+
if (current.direction === 'ascending')
|
|
90
|
+
return { columnId, direction: 'descending' };
|
|
91
|
+
return null;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { PsTableSearchConfig } from '../constants/ps-table-search-config';
|
|
2
|
+
export class PsTableSearchService {
|
|
3
|
+
static normalise(value) {
|
|
4
|
+
return value
|
|
5
|
+
.normalize('NFD')
|
|
6
|
+
.replace(PsTableSearchConfig.diacritics, '')
|
|
7
|
+
.toLowerCase()
|
|
8
|
+
.replace(PsTableSearchConfig.thousandsSeparator, '$1')
|
|
9
|
+
.replace(PsTableSearchConfig.currencySymbols, ' ')
|
|
10
|
+
.replace(PsTableSearchConfig.whitespace, ' ')
|
|
11
|
+
.trim();
|
|
12
|
+
}
|
|
13
|
+
static tokens(query) {
|
|
14
|
+
const normalised = PsTableSearchService.normalise(query);
|
|
15
|
+
return normalised === '' ? [] : normalised.split(' ');
|
|
16
|
+
}
|
|
17
|
+
static matches(text, query) {
|
|
18
|
+
const tokens = PsTableSearchService.tokens(query);
|
|
19
|
+
if (tokens.length === 0)
|
|
20
|
+
return true;
|
|
21
|
+
const haystack = PsTableSearchService.normalise(text);
|
|
22
|
+
return tokens.every((token) => haystack.includes(token));
|
|
23
|
+
}
|
|
24
|
+
}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
export * from './components/ps-data-table';
|
|
2
2
|
export * from './components/ps-table-pagination';
|
|
3
|
+
export * from './constants/table-card-role';
|
|
3
4
|
export * from './constants/table-filter-kind';
|
|
4
5
|
export * from './interfaces/table-column';
|
|
5
6
|
export * from './interfaces/table-column-filter';
|
package/dist/index.js
CHANGED
|
@@ -16,6 +16,7 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
|
16
16
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
17
|
__exportStar(require("./components/ps-data-table"), exports);
|
|
18
18
|
__exportStar(require("./components/ps-table-pagination"), exports);
|
|
19
|
+
__exportStar(require("./constants/table-card-role"), exports);
|
|
19
20
|
__exportStar(require("./constants/table-filter-kind"), exports);
|
|
20
21
|
__exportStar(require("./interfaces/table-column"), exports);
|
|
21
22
|
__exportStar(require("./interfaces/table-column-filter"), exports);
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import type { ReactNode } from 'react';
|
|
2
|
+
import type { PsTableCardRole } from '../constants/table-card-role';
|
|
2
3
|
import type { PsTableColumnFilter } from './table-column-filter';
|
|
3
4
|
export interface PsTableColumn<RowData> {
|
|
4
5
|
align?: 'center' | 'left' | 'right';
|
|
5
6
|
canHide?: boolean;
|
|
6
7
|
cardFullWidth?: boolean;
|
|
8
|
+
cardRole?: PsTableCardRole;
|
|
7
9
|
filter?: PsTableColumnFilter<RowData>;
|
|
8
10
|
footer?: ReactNode;
|
|
9
11
|
hideOnCards?: boolean;
|
package/package.json
CHANGED
|
@@ -1,12 +1,15 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pallasoft/ps-table-components",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.9.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
|
+
"module": "./dist/esm/index.js",
|
|
6
7
|
"types": "./dist/index.d.ts",
|
|
8
|
+
"sideEffects": false,
|
|
7
9
|
"exports": {
|
|
8
10
|
".": {
|
|
9
11
|
"types": "./dist/index.d.ts",
|
|
12
|
+
"module": "./dist/esm/index.js",
|
|
10
13
|
"require": "./dist/index.js",
|
|
11
14
|
"default": "./dist/index.js"
|
|
12
15
|
}
|
|
@@ -16,7 +19,7 @@
|
|
|
16
19
|
],
|
|
17
20
|
"scripts": {
|
|
18
21
|
"husky:prepare": "npx husky",
|
|
19
|
-
"build": "tsc",
|
|
22
|
+
"build": "tsc && tsc -p tsconfig.esm.json",
|
|
20
23
|
"setup:graphify": "sh scripts/graphify-setup.sh",
|
|
21
24
|
"update:graphify": "sh scripts/graphify-update.sh",
|
|
22
25
|
"lint": "npm run lint:eslint",
|