@vinorcola/dynamic-table 1.1.0 → 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.
@@ -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-sm text-stone-500 italic", children: ["(", props.unvisibleSelected, " cach\u00E9", props.unvisibleSelected > 1 ? "s" : "", ")"] })] }));
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 && (_jsx(DynamicTable.SelectionCell, { selected: isItemSelected(item), onToggle: onItemSelectionToggle(item) })), item.target !== null
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>): InternalItems<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);
@@ -29,6 +29,7 @@ export default function useMaskableColumns<Item extends BaseItem>(columns: Inter
29
29
  values: import("./useItems.js").InternalValue[];
30
30
  key: import("react").Key;
31
31
  item: Item;
32
+ isSelectable: boolean;
32
33
  target: string | null;
33
34
  }[];
34
35
  };
@@ -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
- selectedItems: never;
10
- onSelectionChange: never;
10
+ selectedItems?: never;
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>): {
@@ -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 visibleSelected = useMemo(() => isInSelectionMode
9
- ? visibleItems.reduce((quantity, item) => quantity - (options.selectedItems.has(item.item.id) ? 1 : 0), 0)
10
- : 0, [isInSelectionMode, visibleItems, options.selectedItems]);
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: options.selectedItems.size,
14
- unvisibleSelectedQuantity: options.selectedItems.size - visibleSelected,
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(options.selectedItems);
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
- options.onSelectionChange(selection);
28
- }, [isInSelectionMode, options]),
31
+ onSelectionChange(selection);
32
+ }, [isInSelectionMode, selectedItems, onSelectionChange]),
29
33
  };
30
34
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vinorcola/dynamic-table",
3
- "version": "1.1.0",
3
+ "version": "1.2.0",
4
4
  "description": "A table to ease the display of a filterable, sortable and paginable dataset.",
5
5
  "keywords": [
6
6
  "table",