schummar-table 0.3.1 → 0.4.2

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.
@@ -19,7 +19,7 @@ exports.Cell = (0, react_1.memo)(function Cell({ itemId, rowIndex }) {
19
19
  if (!column || !item)
20
20
  return null;
21
21
  (_b = (_a = state.getState().props).debug) === null || _b === void 0 ? void 0 : _b.call(_a, 'render cell', itemId, columnId);
22
- const className = (0, helpers_1.c)((0, row_1.calcClassName)(classes, rowIndex), (0, row_1.calcClassName)(column === null || column === void 0 ? void 0 : column.classes, rowIndex));
22
+ const className = (0, helpers_1.c)((0, row_1.calcClassName)(classes, item, rowIndex), (0, row_1.calcClassName)(column === null || column === void 0 ? void 0 : column.classes, item, rowIndex));
23
23
  const content = column.renderCell(column.value(item), item);
24
24
  if (!wrapCell && typeof content === 'string') {
25
25
  wrapCell = (content) => ((0, jsx_runtime_1.jsx)("div", Object.assign({ className: (0, helpers_1.c)(commonClasses.cell, className) }, { children: (0, jsx_runtime_1.jsx)("span", Object.assign({ className: (0, helpers_1.c)(commonClasses.text) }, { children: content }), void 0) }), void 0));
@@ -1,6 +1,6 @@
1
1
  import React from 'react';
2
2
  import { Id, InternalColumn } from '../types';
3
- export declare const calcClassName: (classes: InternalColumn<any, any>['classes'] | undefined, index: number) => string;
3
+ export declare function calcClassName<T>(classes: InternalColumn<any, any>['classes'] | undefined, item: T, index: number): string;
4
4
  export declare const Row: React.MemoExoticComponent<(<T>({ itemId, rowIndex }: {
5
5
  itemId: Id;
6
6
  rowIndex: number;
@@ -9,24 +9,27 @@ const cell_1 = require("./cell");
9
9
  const expandComponent_1 = require("./expandComponent");
10
10
  const selectComponent_1 = require("./selectComponent");
11
11
  const useCommonClasses_1 = require("./useCommonClasses");
12
- const calcClassName = (classes, index) => (0, helpers_1.c)(classes === null || classes === void 0 ? void 0 : classes.cell, (classes === null || classes === void 0 ? void 0 : classes.evenCell) === undefined ? undefined : { [classes.evenCell]: index % 2 === 0 }, (classes === null || classes === void 0 ? void 0 : classes.oddCell) === undefined ? undefined : { [classes.oddCell]: index % 2 === 1 });
12
+ function calcClassName(classes, item, index) {
13
+ return (0, helpers_1.c)((classes === null || classes === void 0 ? void 0 : classes.cell) instanceof Function ? classes.cell(item, index) : classes === null || classes === void 0 ? void 0 : classes.cell, (classes === null || classes === void 0 ? void 0 : classes.evenCell) === undefined ? undefined : { [classes.evenCell]: index % 2 === 0 }, (classes === null || classes === void 0 ? void 0 : classes.oddCell) === undefined ? undefined : { [classes.oddCell]: index % 2 === 1 });
14
+ }
13
15
  exports.calcClassName = calcClassName;
14
16
  exports.Row = (0, react_1.memo)(function Row({ itemId, rowIndex }) {
15
17
  var _a, _b;
16
18
  const commonClasses = (0, useCommonClasses_1.useCommonClasses)();
17
19
  const state = (0, table_1.useTableContext)();
18
20
  const divRef = (0, react_1.useRef)(null);
19
- const { className, indent, hasChildren, hasDeferredChildren, columnIds, enableSelection } = state.useState((state) => {
21
+ const { className, indent, hasChildren, hasDeferredChildren, columnIds, enableSelection, rowAction } = state.useState((state) => {
20
22
  var _a, _b;
21
23
  const item = state.activeItemsById.get(itemId);
22
24
  const index = !item ? -1 : state.activeItems.indexOf(item);
23
25
  return {
24
- className: (0, exports.calcClassName)(state.props.classes, index),
26
+ className: calcClassName(state.props.classes, item, index),
25
27
  indent: item ? (0, helpers_1.getAncestors)(state.activeItemsById, item).size : 0,
26
28
  hasChildren: !!(item === null || item === void 0 ? void 0 : item.children.length),
27
29
  hasDeferredChildren: item && ((_b = (_a = state.props).hasDeferredChildren) === null || _b === void 0 ? void 0 : _b.call(_a, item)),
28
30
  columnIds: state.activeColumns.map((column) => column.id),
29
31
  enableSelection: state.props.enableSelection,
32
+ rowAction: state.props.rowAction instanceof Function ? (item ? state.props.rowAction(item, index) : null) : state.props.rowAction,
30
33
  };
31
34
  }, [itemId]);
32
35
  (0, react_1.useEffect)(() => {
@@ -44,5 +47,5 @@ exports.Row = (0, react_1.memo)(function Row({ itemId, rowIndex }) {
44
47
  return () => o.disconnect();
45
48
  }, [divRef.current]);
46
49
  (_b = (_a = state.getState().props).debug) === null || _b === void 0 ? void 0 : _b.call(_a, 'render row', itemId);
47
- return ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)("div", { className: (0, helpers_1.c)(commonClasses.cellFill, className), ref: divRef }, void 0), (0, jsx_runtime_1.jsxs)("div", Object.assign({ className: (0, helpers_1.c)(commonClasses.cell, commonClasses.firstCell, className) }, { children: [(0, jsx_runtime_1.jsx)("div", { style: { width: indent * 20 } }, void 0), enableSelection && (0, jsx_runtime_1.jsx)(selectComponent_1.SelectComponent, { itemId: itemId }, void 0), (hasChildren || hasDeferredChildren) && (0, jsx_runtime_1.jsx)(expandComponent_1.ExpandComponent, { itemId: itemId }, void 0)] }), void 0), columnIds.map((columnId) => ((0, jsx_runtime_1.jsx)(table_1.ColumnContext.Provider, Object.assign({ value: columnId }, { children: (0, jsx_runtime_1.jsx)(cell_1.Cell, { itemId: itemId, rowIndex: rowIndex }, void 0) }), columnId))), (0, jsx_runtime_1.jsx)("div", { className: (0, helpers_1.c)(commonClasses.cellFill, className) }, void 0)] }, void 0));
50
+ return ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)("div", { className: (0, helpers_1.c)(commonClasses.cellFill, className), ref: divRef }, void 0), (0, jsx_runtime_1.jsxs)("div", Object.assign({ className: (0, helpers_1.c)(commonClasses.cell, commonClasses.firstCell, className) }, { children: [(0, jsx_runtime_1.jsx)("div", { style: { width: indent * 20 } }, void 0), enableSelection && (0, jsx_runtime_1.jsx)(selectComponent_1.SelectComponent, { itemId: itemId }, void 0), (hasChildren || hasDeferredChildren) && (0, jsx_runtime_1.jsx)(expandComponent_1.ExpandComponent, { itemId: itemId }, void 0), rowAction] }), void 0), columnIds.map((columnId) => ((0, jsx_runtime_1.jsx)(table_1.ColumnContext.Provider, Object.assign({ value: columnId }, { children: (0, jsx_runtime_1.jsx)(cell_1.Cell, { itemId: itemId, rowIndex: rowIndex }, void 0) }), columnId))), (0, jsx_runtime_1.jsx)("div", { className: (0, helpers_1.c)(commonClasses.cellFill, className) }, void 0)] }, void 0));
48
51
  });
@@ -30,7 +30,7 @@ function SortComponent({ children }) {
30
30
  const index = (_a = state.sort.findIndex((s) => s.columnId === columnId)) !== null && _a !== void 0 ? _a : -1;
31
31
  return {
32
32
  direction: (_b = state.sort[index]) === null || _b === void 0 ? void 0 : _b.direction,
33
- index: index >= 0 ? index + 1 : undefined,
33
+ index: index >= 0 && state.sort.length > 1 ? index + 1 : undefined,
34
34
  };
35
35
  });
36
36
  function toggle(e) {
@@ -1,5 +1,4 @@
1
1
  /// <reference types="react" />
2
- import '../misc/textMatch';
3
2
  import { Filter } from './filterComponent';
4
3
  export declare class TextFilter<T> implements Filter<T> {
5
4
  readonly query: string;
@@ -6,7 +6,6 @@ const core_1 = require("@material-ui/core");
6
6
  const icons_1 = require("@material-ui/icons");
7
7
  const react_1 = require("react");
8
8
  const helpers_1 = require("../misc/helpers");
9
- require("../misc/textMatch");
10
9
  const textMatch_1 = require("../misc/textMatch");
11
10
  const table_1 = require("../table");
12
11
  class TextFilter {
@@ -1,5 +1,7 @@
1
1
  export { DefaultFilter, DefaultFilterComponent } from './components/defaultFilterComponent';
2
+ export type { Filter } from './components/filterComponent';
2
3
  export { TextFilter, TextFilterComponent } from './components/textFilterComponent';
4
+ export type { TableStateStorage } from './internalState/tableStateStorage';
3
5
  export { termMatch, textMatch } from './misc/textMatch';
4
- export { ColumnContext, Table, TableContext, useColumnContext, useTableContext } from './table';
5
- export type { Column, Id, Rows, Sort, SortDirection, TableProps } from './types';
6
+ export { ColumnContext, configureTables, Table, TableContext, useColumnContext, useTableContext } from './table';
7
+ export type { Column, Id, InternalColumn, Rows, Sort, SortDirection, TableProps } from './types';
package/dist/cjs/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.useTableContext = exports.useColumnContext = exports.TableContext = exports.Table = exports.ColumnContext = exports.textMatch = exports.termMatch = exports.TextFilterComponent = exports.TextFilter = exports.DefaultFilterComponent = exports.DefaultFilter = void 0;
3
+ exports.useTableContext = exports.useColumnContext = exports.TableContext = exports.Table = exports.configureTables = exports.ColumnContext = exports.textMatch = exports.termMatch = exports.TextFilterComponent = exports.TextFilter = exports.DefaultFilterComponent = exports.DefaultFilter = void 0;
4
4
  var defaultFilterComponent_1 = require("./components/defaultFilterComponent");
5
5
  Object.defineProperty(exports, "DefaultFilter", { enumerable: true, get: function () { return defaultFilterComponent_1.DefaultFilter; } });
6
6
  Object.defineProperty(exports, "DefaultFilterComponent", { enumerable: true, get: function () { return defaultFilterComponent_1.DefaultFilterComponent; } });
@@ -12,6 +12,7 @@ Object.defineProperty(exports, "termMatch", { enumerable: true, get: function ()
12
12
  Object.defineProperty(exports, "textMatch", { enumerable: true, get: function () { return textMatch_1.textMatch; } });
13
13
  var table_1 = require("./table");
14
14
  Object.defineProperty(exports, "ColumnContext", { enumerable: true, get: function () { return table_1.ColumnContext; } });
15
+ Object.defineProperty(exports, "configureTables", { enumerable: true, get: function () { return table_1.configureTables; } });
15
16
  Object.defineProperty(exports, "Table", { enumerable: true, get: function () { return table_1.Table; } });
16
17
  Object.defineProperty(exports, "TableContext", { enumerable: true, get: function () { return table_1.TableContext; } });
17
18
  Object.defineProperty(exports, "useColumnContext", { enumerable: true, get: function () { return table_1.useColumnContext; } });
@@ -0,0 +1,11 @@
1
+ export declare type TableStateStorage = {
2
+ getItem: (key: string) => string | null | Promise<string | null>;
3
+ setItem: (key: string, value: string) => unknown | Promise<unknown>;
4
+ removeItem: (key: string) => unknown | Promise<unknown>;
5
+ } & ({
6
+ keys: () => string[] | Promise<string[]>;
7
+ } | {
8
+ length: number | (() => number | Promise<number>);
9
+ key: (keyIndex: number) => string | null | Promise<string | null>;
10
+ });
11
+ export declare function useTableStateStorage(): void;
@@ -0,0 +1,74 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.useTableStateStorage = void 0;
13
+ const react_1 = require("react");
14
+ const __1 = require("..");
15
+ const queue_1 = require("../misc/queue");
16
+ const KEYS = ['sort', 'selection', 'expanded', 'hiddenColumns'];
17
+ function useTableStateStorage() {
18
+ const state = (0, __1.useTableContext)();
19
+ // On mount: load
20
+ (0, react_1.useEffect)(() => {
21
+ const { storeState, debug } = state.getState().props;
22
+ if (!storeState)
23
+ return;
24
+ let isCanceled = false;
25
+ const q = new queue_1.Queue();
26
+ q.run(() => __awaiter(this, void 0, void 0, function* () {
27
+ try {
28
+ const { storage, id = 'table', include } = storeState;
29
+ const json = yield storage.getItem(`${id}_state`);
30
+ if (isCanceled || !json)
31
+ return;
32
+ const data = JSON.parse(json);
33
+ state.update((state) => {
34
+ for (const key of KEYS) {
35
+ if (!include || include[key]) {
36
+ const value = key === 'sort' ? data.sort : new Set(data[key]);
37
+ state[key] = value;
38
+ }
39
+ }
40
+ });
41
+ }
42
+ catch (e) {
43
+ debug === null || debug === void 0 ? void 0 : debug('Failed to load table state:', e);
44
+ }
45
+ }));
46
+ state.subscribe((state) => {
47
+ if (!state.props.storeState)
48
+ return;
49
+ const data = {};
50
+ for (const key of KEYS) {
51
+ if (!state.props.storeState.include || state.props.storeState.include[key]) {
52
+ const value = key === 'sort' ? state.sort : Array.from(state[key]);
53
+ data[key] = value;
54
+ }
55
+ }
56
+ return {
57
+ storage: state.props.storeState.storage,
58
+ id: state.props.storeState.id,
59
+ data,
60
+ };
61
+ }, (props) => {
62
+ if (!props)
63
+ return;
64
+ const { storage, id = 'table', data } = props;
65
+ q.run(() => __awaiter(this, void 0, void 0, function* () {
66
+ yield storage.setItem(`${id}_state`, JSON.stringify(data));
67
+ }));
68
+ });
69
+ return () => {
70
+ isCanceled = true;
71
+ };
72
+ }, []);
73
+ }
74
+ exports.useTableStateStorage = useTableStateStorage;
@@ -0,0 +1,6 @@
1
+ export declare class Queue {
2
+ private q;
3
+ private isRunning;
4
+ run<T>(job: () => Promise<T>): Promise<T>;
5
+ private start;
6
+ }
@@ -0,0 +1,42 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.Queue = void 0;
13
+ class Queue {
14
+ constructor() {
15
+ this.q = new Array();
16
+ this.isRunning = false;
17
+ }
18
+ run(job) {
19
+ return new Promise((resolve, reject) => {
20
+ this.q.push({ job, resolve, reject });
21
+ if (!this.isRunning)
22
+ this.start();
23
+ });
24
+ }
25
+ start() {
26
+ return __awaiter(this, void 0, void 0, function* () {
27
+ this.isRunning = true;
28
+ let next;
29
+ while ((next = this.q.shift())) {
30
+ try {
31
+ const result = yield next.job();
32
+ next.resolve(result);
33
+ }
34
+ catch (e) {
35
+ next.reject(e);
36
+ }
37
+ }
38
+ this.isRunning = false;
39
+ });
40
+ }
41
+ }
42
+ exports.Queue = Queue;
@@ -5,4 +5,7 @@ export declare const TableContext: React.Context<Store<InternalTableState<any>>
5
5
  export declare const ColumnContext: React.Context<Id | null>;
6
6
  export declare function useTableContext<T>(): Store<InternalTableState<T>>;
7
7
  export declare function useColumnContext(): Id;
8
+ declare let defaultProps: Pick<TableProps<unknown>, 'text' | 'classes'>;
9
+ export declare function configureTables(props: typeof defaultProps): void;
8
10
  export declare function Table<T>(props: TableProps<T>): JSX.Element;
11
+ export {};
package/dist/cjs/table.js CHANGED
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.Table = exports.useColumnContext = exports.useTableContext = exports.ColumnContext = exports.TableContext = void 0;
3
+ exports.Table = exports.configureTables = exports.useColumnContext = exports.useTableContext = exports.ColumnContext = exports.TableContext = void 0;
4
4
  const jsx_runtime_1 = require("react/jsx-runtime");
5
5
  const react_1 = require("react");
6
6
  const columnSelection_1 = require("./components/columnSelection");
@@ -11,6 +11,7 @@ const selectComponent_1 = require("./components/selectComponent");
11
11
  const sortComponent_1 = require("./components/sortComponent");
12
12
  const useCommonClasses_1 = require("./components/useCommonClasses");
13
13
  const virtualized_1 = require("./components/virtualized");
14
+ const tableStateStorage_1 = require("./internalState/tableStateStorage");
14
15
  const useTableState_1 = require("./internalState/useTableState");
15
16
  const helpers_1 = require("./misc/helpers");
16
17
  exports.TableContext = (0, react_1.createContext)(null);
@@ -29,8 +30,17 @@ function useColumnContext() {
29
30
  return value;
30
31
  }
31
32
  exports.useColumnContext = useColumnContext;
33
+ let defaultProps = {};
34
+ function configureTables(props) {
35
+ defaultProps = props;
36
+ }
37
+ exports.configureTables = configureTables;
38
+ function mergeProps(props) {
39
+ return Object.assign(Object.assign({}, props), { text: Object.assign(Object.assign({}, defaultProps.text), props.text), classes: Object.assign(Object.assign({}, defaultProps.classes), props.classes) });
40
+ }
32
41
  function Table(props) {
33
42
  var _a, _b;
43
+ props = mergeProps(props);
34
44
  const state = (0, useTableState_1.useTableState)(props);
35
45
  (_b = (_a = state.getState().props).debug) === null || _b === void 0 ? void 0 : _b.call(_a, 'render table');
36
46
  return ((0, jsx_runtime_1.jsx)(exports.TableContext.Provider, Object.assign({ value: state }, { children: (0, jsx_runtime_1.jsx)(TableInner, {}, void 0) }), void 0));
@@ -38,6 +48,7 @@ function Table(props) {
38
48
  exports.Table = Table;
39
49
  const TableInner = (0, react_1.memo)(function TableInner() {
40
50
  var _a, _b;
51
+ (0, tableStateStorage_1.useTableStateStorage)();
41
52
  const commonClasses = (0, useCommonClasses_1.useCommonClasses)();
42
53
  const state = useTableContext();
43
54
  const fullWidth = state.useState('props.fullWidth');
@@ -1,6 +1,7 @@
1
1
  import { CSSProperties, ReactNode } from 'react';
2
2
  import { Filter } from './components/filterComponent';
3
3
  import { CsvExportOptions } from './misc/csvExport';
4
+ import { TableStateStorage } from './internalState/tableStateStorage';
4
5
  export declare type Sort = {
5
6
  columnId: string | number;
6
7
  direction: SortDirection;
@@ -16,6 +17,7 @@ export declare type TableProps<T> = {
16
17
  parentId?: ((item: T) => Id | undefined) | KeyOfType<T, Id | undefined>;
17
18
  hasDeferredChildren?: (item: T) => boolean;
18
19
  columns: Column<T, any>[] | ((col: <V>(value: (item: T) => V, column: Omit<Column<T, V>, 'value'>) => Column<T, V>) => Column<T, any>[]);
20
+ rowAction?: ReactNode | ((item: T, index: number) => ReactNode);
19
21
  defaultSort?: Sort[];
20
22
  sort?: Sort[];
21
23
  onSortChange?: (sort: Sort[]) => void;
@@ -43,15 +45,15 @@ export declare type TableProps<T> = {
43
45
  overscanTop?: number;
44
46
  };
45
47
  text?: {
46
- deselectAll?: string;
47
- exportTitle?: string;
48
- exportCopy?: string;
49
- exportDownload?: string;
48
+ deselectAll?: ReactNode;
49
+ exportTitle?: ReactNode;
50
+ exportCopy?: ReactNode;
51
+ exportDownload?: ReactNode;
50
52
  };
51
53
  classes?: {
52
54
  table?: string;
53
55
  headerCell?: string;
54
- cell?: string;
56
+ cell?: string | ((item: T, index: number) => string | undefined);
55
57
  evenCell?: string;
56
58
  oddCell?: string;
57
59
  };
@@ -63,6 +65,16 @@ export declare type TableProps<T> = {
63
65
  copy?: boolean | CsvExportOptions;
64
66
  download?: boolean | CsvExportOptions;
65
67
  };
68
+ storeState?: {
69
+ storage: TableStateStorage;
70
+ id?: string;
71
+ include?: {
72
+ sort?: boolean;
73
+ selection?: boolean;
74
+ expanded?: boolean;
75
+ hiddenColumns?: boolean;
76
+ };
77
+ };
66
78
  };
67
79
  export declare type InternalTableProps<T> = Omit<TableProps<T>, 'id' | 'parentId' | 'columns' | 'enableSelection' | 'enableColumnSelection' | 'enableExport'> & {
68
80
  id: (item: T) => Id;
@@ -96,7 +108,7 @@ export declare type Column<T, V> = {
96
108
  justifyContent?: CSSProperties['justifyContent'];
97
109
  classes?: {
98
110
  headerCell?: string;
99
- cell?: string;
111
+ cell?: string | ((item: T, index: number) => string | undefined);
100
112
  evenCell?: string;
101
113
  oddCell?: string;
102
114
  };
@@ -16,7 +16,7 @@ export const Cell = memo(function Cell({ itemId, rowIndex }) {
16
16
  if (!column || !item)
17
17
  return null;
18
18
  (_b = (_a = state.getState().props).debug) === null || _b === void 0 ? void 0 : _b.call(_a, 'render cell', itemId, columnId);
19
- const className = c(calcClassName(classes, rowIndex), calcClassName(column === null || column === void 0 ? void 0 : column.classes, rowIndex));
19
+ const className = c(calcClassName(classes, item, rowIndex), calcClassName(column === null || column === void 0 ? void 0 : column.classes, item, rowIndex));
20
20
  const content = column.renderCell(column.value(item), item);
21
21
  if (!wrapCell && typeof content === 'string') {
22
22
  wrapCell = (content) => (_jsx("div", Object.assign({ className: c(commonClasses.cell, className) }, { children: _jsx("span", Object.assign({ className: c(commonClasses.text) }, { children: content }), void 0) }), void 0));
@@ -1,6 +1,6 @@
1
1
  import React from 'react';
2
2
  import { Id, InternalColumn } from '../types';
3
- export declare const calcClassName: (classes: InternalColumn<any, any>['classes'] | undefined, index: number) => string;
3
+ export declare function calcClassName<T>(classes: InternalColumn<any, any>['classes'] | undefined, item: T, index: number): string;
4
4
  export declare const Row: React.MemoExoticComponent<(<T>({ itemId, rowIndex }: {
5
5
  itemId: Id;
6
6
  rowIndex: number;
@@ -6,23 +6,26 @@ import { Cell } from './cell';
6
6
  import { ExpandComponent } from './expandComponent';
7
7
  import { SelectComponent } from './selectComponent';
8
8
  import { useCommonClasses } from './useCommonClasses';
9
- export const calcClassName = (classes, index) => c(classes === null || classes === void 0 ? void 0 : classes.cell, (classes === null || classes === void 0 ? void 0 : classes.evenCell) === undefined ? undefined : { [classes.evenCell]: index % 2 === 0 }, (classes === null || classes === void 0 ? void 0 : classes.oddCell) === undefined ? undefined : { [classes.oddCell]: index % 2 === 1 });
9
+ export function calcClassName(classes, item, index) {
10
+ return c((classes === null || classes === void 0 ? void 0 : classes.cell) instanceof Function ? classes.cell(item, index) : classes === null || classes === void 0 ? void 0 : classes.cell, (classes === null || classes === void 0 ? void 0 : classes.evenCell) === undefined ? undefined : { [classes.evenCell]: index % 2 === 0 }, (classes === null || classes === void 0 ? void 0 : classes.oddCell) === undefined ? undefined : { [classes.oddCell]: index % 2 === 1 });
11
+ }
10
12
  export const Row = memo(function Row({ itemId, rowIndex }) {
11
13
  var _a, _b;
12
14
  const commonClasses = useCommonClasses();
13
15
  const state = useTableContext();
14
16
  const divRef = useRef(null);
15
- const { className, indent, hasChildren, hasDeferredChildren, columnIds, enableSelection } = state.useState((state) => {
17
+ const { className, indent, hasChildren, hasDeferredChildren, columnIds, enableSelection, rowAction } = state.useState((state) => {
16
18
  var _a, _b;
17
19
  const item = state.activeItemsById.get(itemId);
18
20
  const index = !item ? -1 : state.activeItems.indexOf(item);
19
21
  return {
20
- className: calcClassName(state.props.classes, index),
22
+ className: calcClassName(state.props.classes, item, index),
21
23
  indent: item ? getAncestors(state.activeItemsById, item).size : 0,
22
24
  hasChildren: !!(item === null || item === void 0 ? void 0 : item.children.length),
23
25
  hasDeferredChildren: item && ((_b = (_a = state.props).hasDeferredChildren) === null || _b === void 0 ? void 0 : _b.call(_a, item)),
24
26
  columnIds: state.activeColumns.map((column) => column.id),
25
27
  enableSelection: state.props.enableSelection,
28
+ rowAction: state.props.rowAction instanceof Function ? (item ? state.props.rowAction(item, index) : null) : state.props.rowAction,
26
29
  };
27
30
  }, [itemId]);
28
31
  useEffect(() => {
@@ -40,5 +43,5 @@ export const Row = memo(function Row({ itemId, rowIndex }) {
40
43
  return () => o.disconnect();
41
44
  }, [divRef.current]);
42
45
  (_b = (_a = state.getState().props).debug) === null || _b === void 0 ? void 0 : _b.call(_a, 'render row', itemId);
43
- return (_jsxs(_Fragment, { children: [_jsx("div", { className: c(commonClasses.cellFill, className), ref: divRef }, void 0), _jsxs("div", Object.assign({ className: c(commonClasses.cell, commonClasses.firstCell, className) }, { children: [_jsx("div", { style: { width: indent * 20 } }, void 0), enableSelection && _jsx(SelectComponent, { itemId: itemId }, void 0), (hasChildren || hasDeferredChildren) && _jsx(ExpandComponent, { itemId: itemId }, void 0)] }), void 0), columnIds.map((columnId) => (_jsx(ColumnContext.Provider, Object.assign({ value: columnId }, { children: _jsx(Cell, { itemId: itemId, rowIndex: rowIndex }, void 0) }), columnId))), _jsx("div", { className: c(commonClasses.cellFill, className) }, void 0)] }, void 0));
46
+ return (_jsxs(_Fragment, { children: [_jsx("div", { className: c(commonClasses.cellFill, className), ref: divRef }, void 0), _jsxs("div", Object.assign({ className: c(commonClasses.cell, commonClasses.firstCell, className) }, { children: [_jsx("div", { style: { width: indent * 20 } }, void 0), enableSelection && _jsx(SelectComponent, { itemId: itemId }, void 0), (hasChildren || hasDeferredChildren) && _jsx(ExpandComponent, { itemId: itemId }, void 0), rowAction] }), void 0), columnIds.map((columnId) => (_jsx(ColumnContext.Provider, Object.assign({ value: columnId }, { children: _jsx(Cell, { itemId: itemId, rowIndex: rowIndex }, void 0) }), columnId))), _jsx("div", { className: c(commonClasses.cellFill, className) }, void 0)] }, void 0));
44
47
  });
@@ -27,7 +27,7 @@ export function SortComponent({ children }) {
27
27
  const index = (_a = state.sort.findIndex((s) => s.columnId === columnId)) !== null && _a !== void 0 ? _a : -1;
28
28
  return {
29
29
  direction: (_b = state.sort[index]) === null || _b === void 0 ? void 0 : _b.direction,
30
- index: index >= 0 ? index + 1 : undefined,
30
+ index: index >= 0 && state.sort.length > 1 ? index + 1 : undefined,
31
31
  };
32
32
  });
33
33
  function toggle(e) {
@@ -1,5 +1,4 @@
1
1
  /// <reference types="react" />
2
- import '../misc/textMatch';
3
2
  import { Filter } from './filterComponent';
4
3
  export declare class TextFilter<T> implements Filter<T> {
5
4
  readonly query: string;
@@ -3,7 +3,6 @@ import { IconButton, makeStyles, TextField } from '@material-ui/core';
3
3
  import { Clear, Search } from '@material-ui/icons';
4
4
  import { useEffect, useState } from 'react';
5
5
  import { asString } from '../misc/helpers';
6
- import '../misc/textMatch';
7
6
  import { textMatch } from '../misc/textMatch';
8
7
  import { useColumnContext, useTableContext } from '../table';
9
8
  export class TextFilter {
@@ -1,5 +1,7 @@
1
1
  export { DefaultFilter, DefaultFilterComponent } from './components/defaultFilterComponent';
2
+ export type { Filter } from './components/filterComponent';
2
3
  export { TextFilter, TextFilterComponent } from './components/textFilterComponent';
4
+ export type { TableStateStorage } from './internalState/tableStateStorage';
3
5
  export { termMatch, textMatch } from './misc/textMatch';
4
- export { ColumnContext, Table, TableContext, useColumnContext, useTableContext } from './table';
5
- export type { Column, Id, Rows, Sort, SortDirection, TableProps } from './types';
6
+ export { ColumnContext, configureTables, Table, TableContext, useColumnContext, useTableContext } from './table';
7
+ export type { Column, Id, InternalColumn, Rows, Sort, SortDirection, TableProps } from './types';
package/dist/esm/index.js CHANGED
@@ -1,4 +1,4 @@
1
1
  export { DefaultFilter, DefaultFilterComponent } from './components/defaultFilterComponent';
2
2
  export { TextFilter, TextFilterComponent } from './components/textFilterComponent';
3
3
  export { termMatch, textMatch } from './misc/textMatch';
4
- export { ColumnContext, Table, TableContext, useColumnContext, useTableContext } from './table';
4
+ export { ColumnContext, configureTables, Table, TableContext, useColumnContext, useTableContext } from './table';
@@ -0,0 +1,11 @@
1
+ export declare type TableStateStorage = {
2
+ getItem: (key: string) => string | null | Promise<string | null>;
3
+ setItem: (key: string, value: string) => unknown | Promise<unknown>;
4
+ removeItem: (key: string) => unknown | Promise<unknown>;
5
+ } & ({
6
+ keys: () => string[] | Promise<string[]>;
7
+ } | {
8
+ length: number | (() => number | Promise<number>);
9
+ key: (keyIndex: number) => string | null | Promise<string | null>;
10
+ });
11
+ export declare function useTableStateStorage(): void;
@@ -0,0 +1,70 @@
1
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
2
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
3
+ return new (P || (P = Promise))(function (resolve, reject) {
4
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
5
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
6
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
7
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
8
+ });
9
+ };
10
+ import { useEffect } from 'react';
11
+ import { useTableContext } from '..';
12
+ import { Queue } from '../misc/queue';
13
+ const KEYS = ['sort', 'selection', 'expanded', 'hiddenColumns'];
14
+ export function useTableStateStorage() {
15
+ const state = useTableContext();
16
+ // On mount: load
17
+ useEffect(() => {
18
+ const { storeState, debug } = state.getState().props;
19
+ if (!storeState)
20
+ return;
21
+ let isCanceled = false;
22
+ const q = new Queue();
23
+ q.run(() => __awaiter(this, void 0, void 0, function* () {
24
+ try {
25
+ const { storage, id = 'table', include } = storeState;
26
+ const json = yield storage.getItem(`${id}_state`);
27
+ if (isCanceled || !json)
28
+ return;
29
+ const data = JSON.parse(json);
30
+ state.update((state) => {
31
+ for (const key of KEYS) {
32
+ if (!include || include[key]) {
33
+ const value = key === 'sort' ? data.sort : new Set(data[key]);
34
+ state[key] = value;
35
+ }
36
+ }
37
+ });
38
+ }
39
+ catch (e) {
40
+ debug === null || debug === void 0 ? void 0 : debug('Failed to load table state:', e);
41
+ }
42
+ }));
43
+ state.subscribe((state) => {
44
+ if (!state.props.storeState)
45
+ return;
46
+ const data = {};
47
+ for (const key of KEYS) {
48
+ if (!state.props.storeState.include || state.props.storeState.include[key]) {
49
+ const value = key === 'sort' ? state.sort : Array.from(state[key]);
50
+ data[key] = value;
51
+ }
52
+ }
53
+ return {
54
+ storage: state.props.storeState.storage,
55
+ id: state.props.storeState.id,
56
+ data,
57
+ };
58
+ }, (props) => {
59
+ if (!props)
60
+ return;
61
+ const { storage, id = 'table', data } = props;
62
+ q.run(() => __awaiter(this, void 0, void 0, function* () {
63
+ yield storage.setItem(`${id}_state`, JSON.stringify(data));
64
+ }));
65
+ });
66
+ return () => {
67
+ isCanceled = true;
68
+ };
69
+ }, []);
70
+ }
@@ -0,0 +1,6 @@
1
+ export declare class Queue {
2
+ private q;
3
+ private isRunning;
4
+ run<T>(job: () => Promise<T>): Promise<T>;
5
+ private start;
6
+ }
@@ -0,0 +1,38 @@
1
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
2
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
3
+ return new (P || (P = Promise))(function (resolve, reject) {
4
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
5
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
6
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
7
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
8
+ });
9
+ };
10
+ export class Queue {
11
+ constructor() {
12
+ this.q = new Array();
13
+ this.isRunning = false;
14
+ }
15
+ run(job) {
16
+ return new Promise((resolve, reject) => {
17
+ this.q.push({ job, resolve, reject });
18
+ if (!this.isRunning)
19
+ this.start();
20
+ });
21
+ }
22
+ start() {
23
+ return __awaiter(this, void 0, void 0, function* () {
24
+ this.isRunning = true;
25
+ let next;
26
+ while ((next = this.q.shift())) {
27
+ try {
28
+ const result = yield next.job();
29
+ next.resolve(result);
30
+ }
31
+ catch (e) {
32
+ next.reject(e);
33
+ }
34
+ }
35
+ this.isRunning = false;
36
+ });
37
+ }
38
+ }
@@ -5,4 +5,7 @@ export declare const TableContext: React.Context<Store<InternalTableState<any>>
5
5
  export declare const ColumnContext: React.Context<Id | null>;
6
6
  export declare function useTableContext<T>(): Store<InternalTableState<T>>;
7
7
  export declare function useColumnContext(): Id;
8
+ declare let defaultProps: Pick<TableProps<unknown>, 'text' | 'classes'>;
9
+ export declare function configureTables(props: typeof defaultProps): void;
8
10
  export declare function Table<T>(props: TableProps<T>): JSX.Element;
11
+ export {};
package/dist/esm/table.js CHANGED
@@ -8,6 +8,7 @@ import { SelectComponent } from './components/selectComponent';
8
8
  import { SortComponent } from './components/sortComponent';
9
9
  import { useCommonClasses } from './components/useCommonClasses';
10
10
  import { Virtualized } from './components/virtualized';
11
+ import { useTableStateStorage } from './internalState/tableStateStorage';
11
12
  import { useTableState } from './internalState/useTableState';
12
13
  import { c } from './misc/helpers';
13
14
  export const TableContext = createContext(null);
@@ -24,14 +25,23 @@ export function useColumnContext() {
24
25
  throw Error('No column context available');
25
26
  return value;
26
27
  }
28
+ let defaultProps = {};
29
+ export function configureTables(props) {
30
+ defaultProps = props;
31
+ }
32
+ function mergeProps(props) {
33
+ return Object.assign(Object.assign({}, props), { text: Object.assign(Object.assign({}, defaultProps.text), props.text), classes: Object.assign(Object.assign({}, defaultProps.classes), props.classes) });
34
+ }
27
35
  export function Table(props) {
28
36
  var _a, _b;
37
+ props = mergeProps(props);
29
38
  const state = useTableState(props);
30
39
  (_b = (_a = state.getState().props).debug) === null || _b === void 0 ? void 0 : _b.call(_a, 'render table');
31
40
  return (_jsx(TableContext.Provider, Object.assign({ value: state }, { children: _jsx(TableInner, {}, void 0) }), void 0));
32
41
  }
33
42
  const TableInner = memo(function TableInner() {
34
43
  var _a, _b;
44
+ useTableStateStorage();
35
45
  const commonClasses = useCommonClasses();
36
46
  const state = useTableContext();
37
47
  const fullWidth = state.useState('props.fullWidth');
@@ -1,6 +1,7 @@
1
1
  import { CSSProperties, ReactNode } from 'react';
2
2
  import { Filter } from './components/filterComponent';
3
3
  import { CsvExportOptions } from './misc/csvExport';
4
+ import { TableStateStorage } from './internalState/tableStateStorage';
4
5
  export declare type Sort = {
5
6
  columnId: string | number;
6
7
  direction: SortDirection;
@@ -16,6 +17,7 @@ export declare type TableProps<T> = {
16
17
  parentId?: ((item: T) => Id | undefined) | KeyOfType<T, Id | undefined>;
17
18
  hasDeferredChildren?: (item: T) => boolean;
18
19
  columns: Column<T, any>[] | ((col: <V>(value: (item: T) => V, column: Omit<Column<T, V>, 'value'>) => Column<T, V>) => Column<T, any>[]);
20
+ rowAction?: ReactNode | ((item: T, index: number) => ReactNode);
19
21
  defaultSort?: Sort[];
20
22
  sort?: Sort[];
21
23
  onSortChange?: (sort: Sort[]) => void;
@@ -43,15 +45,15 @@ export declare type TableProps<T> = {
43
45
  overscanTop?: number;
44
46
  };
45
47
  text?: {
46
- deselectAll?: string;
47
- exportTitle?: string;
48
- exportCopy?: string;
49
- exportDownload?: string;
48
+ deselectAll?: ReactNode;
49
+ exportTitle?: ReactNode;
50
+ exportCopy?: ReactNode;
51
+ exportDownload?: ReactNode;
50
52
  };
51
53
  classes?: {
52
54
  table?: string;
53
55
  headerCell?: string;
54
- cell?: string;
56
+ cell?: string | ((item: T, index: number) => string | undefined);
55
57
  evenCell?: string;
56
58
  oddCell?: string;
57
59
  };
@@ -63,6 +65,16 @@ export declare type TableProps<T> = {
63
65
  copy?: boolean | CsvExportOptions;
64
66
  download?: boolean | CsvExportOptions;
65
67
  };
68
+ storeState?: {
69
+ storage: TableStateStorage;
70
+ id?: string;
71
+ include?: {
72
+ sort?: boolean;
73
+ selection?: boolean;
74
+ expanded?: boolean;
75
+ hiddenColumns?: boolean;
76
+ };
77
+ };
66
78
  };
67
79
  export declare type InternalTableProps<T> = Omit<TableProps<T>, 'id' | 'parentId' | 'columns' | 'enableSelection' | 'enableColumnSelection' | 'enableExport'> & {
68
80
  id: (item: T) => Id;
@@ -96,7 +108,7 @@ export declare type Column<T, V> = {
96
108
  justifyContent?: CSSProperties['justifyContent'];
97
109
  classes?: {
98
110
  headerCell?: string;
99
- cell?: string;
111
+ cell?: string | ((item: T, index: number) => string | undefined);
100
112
  evenCell?: string;
101
113
  oddCell?: string;
102
114
  };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "schummar-table",
3
3
  "repository": "schummar/schummar-table",
4
- "version": "0.3.1",
4
+ "version": "0.4.2",
5
5
  "description": "",
6
6
  "type": "module",
7
7
  "sideEffects": false,
@@ -51,6 +51,7 @@
51
51
  "global-jsdom": "^8.3.0",
52
52
  "immer": "*",
53
53
  "jsdom": "^18.1.0",
54
+ "localforage": "^1.10.0",
54
55
  "npm-run-all": "^4.1.5",
55
56
  "nyc": "^15.1.0",
56
57
  "prettier": "^2.4.1",