@vinorcola/dynamic-table 1.1.1 → 1.2.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/lib/DefaultTheme.js +2 -2
- package/lib/index.js +3 -2
- package/lib/useItems.d.ts +2 -1
- package/lib/useItems.js +3 -2
- package/lib/useMaskableColumns.d.ts +1 -0
- package/lib/useSelection.d.ts +2 -0
- package/lib/useSelection.js +13 -9
- package/package.json +1 -1
package/lib/DefaultTheme.js
CHANGED
|
@@ -62,7 +62,7 @@ export function HeaderLine(props) {
|
|
|
62
62
|
return _jsx("tr", { className: "bg-table-header", children: props.children });
|
|
63
63
|
}
|
|
64
64
|
export function SelectionHeader(props) {
|
|
65
|
-
return (_jsxs("th", { className: "relative", children: [props.totalSelected, _jsxs("span", { className: "text-
|
|
65
|
+
return (_jsxs("th", { className: "relative", children: [props.totalSelected, _jsxs("span", { className: "block text-xs text-stone-400 italic", children: ["(", props.unvisibleSelected, " cach\u00E9", props.unvisibleSelected > 1 ? "s" : "", ")"] })] }));
|
|
66
66
|
}
|
|
67
67
|
export function Header(props) {
|
|
68
68
|
const filterPopup = usePopup();
|
|
@@ -110,7 +110,7 @@ export function Line(props) {
|
|
|
110
110
|
return (_jsx("tr", { className: "even:[&>*]:bg-table-line-even odd:[&>*]:bg-table-line-odd hover:bg-table-line-hover", children: props.children }));
|
|
111
111
|
}
|
|
112
112
|
export function SelectionCell(props) {
|
|
113
|
-
return (_jsx("td", { onClick: (event) => {
|
|
113
|
+
return (_jsx("td", { className: "cursor-pointer", onClick: (event) => {
|
|
114
114
|
event.preventDefault();
|
|
115
115
|
event.stopPropagation();
|
|
116
116
|
props.onToggle();
|
package/lib/index.js
CHANGED
|
@@ -11,13 +11,14 @@ import useSortState from "./useSortState.js";
|
|
|
11
11
|
export { default as Dictionary } from "./Dictionary.js";
|
|
12
12
|
export default function DynamicTable(props) {
|
|
13
13
|
const columns = useColumns(props.columns);
|
|
14
|
-
const items = useItems(props.items, props.itemTarget, columns);
|
|
14
|
+
const items = useItems(props.items, props.itemTarget, columns, props.canSelectItem);
|
|
15
15
|
const { columns: filteredColumns, items: filteredItems, clearFilterState, } = useFilterState(columns, items, props.initialFilterState);
|
|
16
16
|
const { columns: sortedColumns, items: sortedItems, clearSortState, } = useSortState(filteredColumns, filteredItems, props.initialSortState);
|
|
17
17
|
const { allColumns, columns: displayedColumns, items: displayedItems, } = useMaskableColumns(sortedColumns, sortedItems, props.initialColumnsMaskState);
|
|
18
18
|
const { items: paginatedItems, itemsPerPage, onItemsPerPageChange, totalPages, currentPage, onCurrentPageChange, } = usePagination(displayedItems, props.initialPaginationState);
|
|
19
19
|
const { isInSelectionMode, totalSelectedQuantity, unvisibleSelectedQuantity, isItemSelected, onItemSelectionToggle, } = useSelection(props, paginatedItems);
|
|
20
|
-
return (_jsx(UniquePopupProvider, { children: _jsxs(DynamicTable.TableContainer, { children: [_jsx(DynamicTable.Controller, { columns: allColumns, clearFilterState: clearFilterState, clearSortState: clearSortState }), _jsxs(DynamicTable.Table, { children: [_jsx(DynamicTable.HeaderContainer, { children: _jsxs(DynamicTable.HeaderLine, { children: [isInSelectionMode && (_jsx(DynamicTable.SelectionHeader, { totalSelected: totalSelectedQuantity, unvisibleSelected: unvisibleSelectedQuantity })), displayedColumns.map((column) => (_jsx(DynamicTable.Header, { column: column, children: column.title }, column.id)))] }) }), _jsx(DynamicTable.BodyContainer, { children: paginatedItems.map((item) => (_jsxs(DynamicTable.Line, { children: [isInSelectionMode &&
|
|
20
|
+
return (_jsx(UniquePopupProvider, { children: _jsxs(DynamicTable.TableContainer, { children: [_jsx(DynamicTable.Controller, { columns: allColumns, clearFilterState: clearFilterState, clearSortState: clearSortState }), _jsxs(DynamicTable.Table, { children: [_jsx(DynamicTable.HeaderContainer, { children: _jsxs(DynamicTable.HeaderLine, { children: [isInSelectionMode && (_jsx(DynamicTable.SelectionHeader, { totalSelected: totalSelectedQuantity, unvisibleSelected: unvisibleSelectedQuantity })), displayedColumns.map((column) => (_jsx(DynamicTable.Header, { column: column, children: column.title }, column.id)))] }) }), _jsx(DynamicTable.BodyContainer, { children: paginatedItems.map((item) => (_jsxs(DynamicTable.Line, { children: [isInSelectionMode &&
|
|
21
|
+
(item.isSelectable ? (_jsx(DynamicTable.SelectionCell, { selected: isItemSelected(item), onToggle: onItemSelectionToggle(item) })) : (_jsx(DynamicTable.Cell, {}))), item.target !== null
|
|
21
22
|
? item.values.map((value) => (_jsx(DynamicTable.ClickableCell, { target: item.target, children: value.loading ? _jsx(DynamicTable.Loader, {}) : value.display }, value.column)))
|
|
22
23
|
: item.values.map((value) => (_jsx(DynamicTable.Cell, { children: value.loading ? _jsx(DynamicTable.Loader, {}) : value.display }, value.column)))] }, item.key))) }), _jsx(DynamicTable.FooterContainer, { totalColums: displayedColumns.length, pageSelector: _jsx(DynamicTable.PageSelector, { totalPages: totalPages, currentPage: currentPage, onCurrentPageChange: onCurrentPageChange }), itemsPerPageSelector: _jsx(DynamicTable.ItemsPerPageSelector, { itemsPerPage: itemsPerPage, onItemsPerPageChange: onItemsPerPageChange }) })] })] }) }));
|
|
23
24
|
}
|
package/lib/useItems.d.ts
CHANGED
|
@@ -31,6 +31,7 @@ export type InternalValue = LoadingInternalValue | LoadedInternalValue;
|
|
|
31
31
|
export interface InternalItem<Item extends BaseItem> {
|
|
32
32
|
readonly key: Key;
|
|
33
33
|
readonly item: Item;
|
|
34
|
+
readonly isSelectable: boolean;
|
|
34
35
|
readonly target: string | null;
|
|
35
36
|
readonly values: InternalValue[];
|
|
36
37
|
}
|
|
@@ -38,4 +39,4 @@ export interface InternalItem<Item extends BaseItem> {
|
|
|
38
39
|
* A list of items.
|
|
39
40
|
*/
|
|
40
41
|
export type InternalItems<Item extends BaseItem> = readonly InternalItem<Item>[];
|
|
41
|
-
export default function useItems<Item extends BaseItem>(items: Item[], itemTarget: ((item: Item) => string) | undefined, columns: InternalColumns<Item
|
|
42
|
+
export default function useItems<Item extends BaseItem>(items: Item[], itemTarget: ((item: Item) => string) | undefined, columns: InternalColumns<Item>, canSelectItem?: (item: Item) => boolean): InternalItems<Item>;
|
package/lib/useItems.js
CHANGED
|
@@ -2,13 +2,14 @@ import { Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
2
2
|
import { displayInteger } from "@vinorcola/utils/number";
|
|
3
3
|
import { extractSearchableText } from "@vinorcola/utils/text";
|
|
4
4
|
import { useMemo } from "react";
|
|
5
|
-
export default function useItems(items, itemTarget, columns) {
|
|
5
|
+
export default function useItems(items, itemTarget, columns, canSelectItem) {
|
|
6
6
|
return useMemo(() => items.map((item) => ({
|
|
7
7
|
key: item.id,
|
|
8
8
|
item,
|
|
9
|
+
isSelectable: canSelectItem === undefined ? true : canSelectItem(item),
|
|
9
10
|
target: itemTarget === undefined ? null : itemTarget(item),
|
|
10
11
|
values: columns.map((column) => resolveInternalValue(item, column)),
|
|
11
|
-
})), [items, itemTarget, columns]);
|
|
12
|
+
})), [items, itemTarget, columns, canSelectItem]);
|
|
12
13
|
}
|
|
13
14
|
function resolveInternalValue(item, column) {
|
|
14
15
|
const raw = column.resolveValue(item);
|
package/lib/useSelection.d.ts
CHANGED
|
@@ -4,10 +4,12 @@ type ItemSelection<Item extends BaseItem> = Set<Item["id"]>;
|
|
|
4
4
|
interface SelectableOptions<Item extends BaseItem> {
|
|
5
5
|
selectedItems: ItemSelection<Item>;
|
|
6
6
|
onSelectionChange: (selected: ItemSelection<Item>) => void;
|
|
7
|
+
canSelectItem?: (item: Item) => boolean;
|
|
7
8
|
}
|
|
8
9
|
interface NonSelectableOptions {
|
|
9
10
|
selectedItems?: never;
|
|
10
11
|
onSelectionChange?: never;
|
|
12
|
+
canSelectItem?: never;
|
|
11
13
|
}
|
|
12
14
|
export type SelectionOptions<Item extends BaseItem> = SelectableOptions<Item> | NonSelectableOptions;
|
|
13
15
|
export default function useSelection<Item extends BaseItem>(options: SelectionOptions<Item>, visibleItems: InternalItems<Item>): {
|
package/lib/useSelection.js
CHANGED
|
@@ -3,28 +3,32 @@ function inSelectionMode(props) {
|
|
|
3
3
|
return (props.selectedItems !== undefined &&
|
|
4
4
|
props.onSelectionChange !== undefined);
|
|
5
5
|
}
|
|
6
|
+
const EMPTY_SELECTION = new Set();
|
|
7
|
+
const DEFAULT_SELECTION_CHANGE_HANDLER = () => { };
|
|
6
8
|
export default function useSelection(options, visibleItems) {
|
|
7
9
|
const isInSelectionMode = inSelectionMode(options);
|
|
8
|
-
const
|
|
9
|
-
|
|
10
|
-
|
|
10
|
+
const selectedItems = isInSelectionMode ? options.selectedItems : EMPTY_SELECTION;
|
|
11
|
+
const onSelectionChange = options.onSelectionChange ?? DEFAULT_SELECTION_CHANGE_HANDLER;
|
|
12
|
+
const unvisibleSelectedQuantity = useMemo(() => selectedItems.size > 0
|
|
13
|
+
? visibleItems.reduce((quantity, item) => quantity - (selectedItems.has(item.item.id) ? 1 : 0), selectedItems.size)
|
|
14
|
+
: 0, [visibleItems, selectedItems]);
|
|
11
15
|
return {
|
|
12
16
|
isInSelectionMode,
|
|
13
|
-
totalSelectedQuantity:
|
|
14
|
-
unvisibleSelectedQuantity
|
|
17
|
+
totalSelectedQuantity: selectedItems.size,
|
|
18
|
+
unvisibleSelectedQuantity,
|
|
15
19
|
isItemSelected: useCallback((item) => isInSelectionMode && options.selectedItems.has(item.item.id), [isInSelectionMode, options.selectedItems]),
|
|
16
20
|
onItemSelectionToggle: useCallback((item) => () => {
|
|
17
|
-
if (!isInSelectionMode) {
|
|
21
|
+
if (!isInSelectionMode || !item.isSelectable) {
|
|
18
22
|
return;
|
|
19
23
|
}
|
|
20
|
-
const selection = new Set(
|
|
24
|
+
const selection = new Set(selectedItems);
|
|
21
25
|
if (selection.has(item.item.id)) {
|
|
22
26
|
selection.delete(item.item.id);
|
|
23
27
|
}
|
|
24
28
|
else {
|
|
25
29
|
selection.add(item.item.id);
|
|
26
30
|
}
|
|
27
|
-
|
|
28
|
-
}, [isInSelectionMode,
|
|
31
|
+
onSelectionChange(selection);
|
|
32
|
+
}, [isInSelectionMode, selectedItems, onSelectionChange]),
|
|
29
33
|
};
|
|
30
34
|
}
|