@tint-ui/data-table 0.3.5
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/LICENSE +21 -0
- package/README.md +9 -0
- package/adapters/boolean.d.ts +10 -0
- package/adapters/boolean.js +38 -0
- package/adapters/index.d.ts +6 -0
- package/adapters/index.js +18 -0
- package/adapters/number-format.d.ts +1 -0
- package/adapters/number-format.js +42 -0
- package/adapters/number.d.ts +12 -0
- package/adapters/number.js +51 -0
- package/adapters/text.d.ts +4 -0
- package/adapters/text.js +9 -0
- package/cell-adapter-type.d.ts +43 -0
- package/cell-adapter-type.js +306 -0
- package/cjs/adapters/boolean.js +43 -0
- package/cjs/adapters/index.js +21 -0
- package/cjs/adapters/number-format.js +46 -0
- package/cjs/adapters/number.js +56 -0
- package/cjs/adapters/text.js +13 -0
- package/cjs/cell-adapter-type.js +312 -0
- package/cjs/classes.js +27 -0
- package/cjs/context.js +14 -0
- package/cjs/data-table-content.js +105 -0
- package/cjs/data-table-pagination.js +38 -0
- package/cjs/data-table-text-filter.js +83 -0
- package/cjs/data-table-toolbar.js +103 -0
- package/cjs/data-table-views-options.js +137 -0
- package/cjs/data-table.js +63 -0
- package/cjs/filter-adapter-type.js +162 -0
- package/cjs/filter-adapters/index.js +10 -0
- package/cjs/filter-adapters/option.js +152 -0
- package/cjs/filter-adapters/use-option-filter.js +195 -0
- package/cjs/filter-classes.js +26 -0
- package/cjs/filter-fn.js +84 -0
- package/cjs/index.js +99 -0
- package/cjs/package.json +3 -0
- package/cjs/pagination-arrow.js +93 -0
- package/cjs/pagination-classes.js +20 -0
- package/cjs/pagination-number.js +66 -0
- package/cjs/pagination-size-options.js +48 -0
- package/cjs/row-button-menu.js +49 -0
- package/cjs/row-popover-menu.js +52 -0
- package/cjs/toolbar-classes.js +24 -0
- package/cjs/types.js +3 -0
- package/cjs/use-data-table.js +768 -0
- package/cjs/use-lexicon.js +155 -0
- package/cjs/use-row-menu.js +60 -0
- package/cjs/use-visibility-column.js +105 -0
- package/cjs/use-visibility-filter.js +149 -0
- package/cjs/utils.js +136 -0
- package/classes.d.ts +34 -0
- package/classes.js +23 -0
- package/context.d.ts +5 -0
- package/context.js +9 -0
- package/data-table-content.d.ts +9 -0
- package/data-table-content.js +98 -0
- package/data-table-pagination.d.ts +5 -0
- package/data-table-pagination.js +31 -0
- package/data-table-text-filter.d.ts +7 -0
- package/data-table-text-filter.js +76 -0
- package/data-table-toolbar.d.ts +5 -0
- package/data-table-toolbar.js +95 -0
- package/data-table-views-options.d.ts +2 -0
- package/data-table-views-options.js +133 -0
- package/data-table.d.ts +18 -0
- package/data-table.js +56 -0
- package/filter-adapter-type.d.ts +9 -0
- package/filter-adapter-type.js +155 -0
- package/filter-adapters/index.d.ts +5 -0
- package/filter-adapters/index.js +7 -0
- package/filter-adapters/option.d.ts +3 -0
- package/filter-adapters/option.js +147 -0
- package/filter-adapters/use-option-filter.d.ts +27 -0
- package/filter-adapters/use-option-filter.js +192 -0
- package/filter-classes.d.ts +32 -0
- package/filter-classes.js +22 -0
- package/filter-fn.d.ts +7 -0
- package/filter-fn.js +76 -0
- package/index.d.ts +16 -0
- package/index.js +16 -0
- package/package.json +97 -0
- package/pagination-arrow.d.ts +5 -0
- package/pagination-arrow.js +86 -0
- package/pagination-classes.d.ts +20 -0
- package/pagination-classes.js +16 -0
- package/pagination-number.d.ts +5 -0
- package/pagination-number.js +59 -0
- package/pagination-size-options.d.ts +3 -0
- package/pagination-size-options.js +44 -0
- package/row-button-menu.d.ts +5 -0
- package/row-button-menu.js +45 -0
- package/row-popover-menu.d.ts +5 -0
- package/row-popover-menu.js +48 -0
- package/styles-filter.css +1 -0
- package/styles-filter.module.css +64 -0
- package/styles-filter.module.scss +65 -0
- package/styles-pagination.css +1 -0
- package/styles-pagination.module.css +28 -0
- package/styles-pagination.module.scss +31 -0
- package/styles-toolbar.css +1 -0
- package/styles-toolbar.module.css +70 -0
- package/styles-toolbar.module.scss +67 -0
- package/styles.css +1 -0
- package/styles.json +8 -0
- package/styles.module.css +39 -0
- package/styles.module.scss +38 -0
- package/toolbar-classes.d.ts +26 -0
- package/toolbar-classes.js +20 -0
- package/types.d.ts +226 -0
- package/types.js +2 -0
- package/use-data-table.d.ts +3 -0
- package/use-data-table.js +768 -0
- package/use-lexicon.d.ts +12 -0
- package/use-lexicon.js +151 -0
- package/use-row-menu.d.ts +7 -0
- package/use-row-menu.js +58 -0
- package/use-visibility-column.d.ts +7 -0
- package/use-visibility-column.js +101 -0
- package/use-visibility-filter.d.ts +7 -0
- package/use-visibility-filter.js +145 -0
- package/utils.d.ts +14 -0
- package/utils.js +128 -0
package/use-lexicon.d.ts
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { DataTableLexicon, LexiconType } from "./types";
|
|
2
|
+
type LexiconReplacementProps = {
|
|
3
|
+
selected: number;
|
|
4
|
+
size: number;
|
|
5
|
+
total: number;
|
|
6
|
+
limit: number;
|
|
7
|
+
offset: number;
|
|
8
|
+
pageNumber: number;
|
|
9
|
+
pageCount: number;
|
|
10
|
+
};
|
|
11
|
+
declare const useLexicon: (customLexicon: Partial<DataTableLexicon>, replacement: LexiconReplacementProps) => LexiconType;
|
|
12
|
+
export { useLexicon };
|
package/use-lexicon.js
ADDED
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import * as React from "react";
|
|
4
|
+
import { useApp } from "@tint-ui/app";
|
|
5
|
+
const defaultLexicon = {
|
|
6
|
+
filterClear: "Clear filter",
|
|
7
|
+
filterSelected: "{{ selected }} selected",
|
|
8
|
+
filterNotFound: "Filter options not found",
|
|
9
|
+
filterReset: "Reset filter",
|
|
10
|
+
view: "View",
|
|
11
|
+
viewOptions: "Options",
|
|
12
|
+
viewColumn: "Toggle columns",
|
|
13
|
+
viewSortBy: "Sort By",
|
|
14
|
+
viewFilter: "Filters",
|
|
15
|
+
viewFilterText: "Search...",
|
|
16
|
+
pageFirst: "Go to first page",
|
|
17
|
+
pageLast: "Go to last page",
|
|
18
|
+
pagePrevious: "Go to previous page",
|
|
19
|
+
pageNext: "Go to next page",
|
|
20
|
+
pageOf: "{{ pageNumber }} page of {{ pageCount }}",
|
|
21
|
+
search: "Search...",
|
|
22
|
+
notFound: "Empty data",
|
|
23
|
+
notFoundFiltered: "Records not found",
|
|
24
|
+
title: "{{ size }} row(s) out of {{ total }}",
|
|
25
|
+
onePageTitle: "{{ size }} row(s)",
|
|
26
|
+
selected: "{{ selected }} of {{ size }} row(s) selected"
|
|
27
|
+
};
|
|
28
|
+
const plainKeyList = ["search", "notFound", "notFoundFiltered", "filterClear", "filterReset", "filterNotFound", "view", "viewOptions", "viewFilterText", "viewFilter", "viewSortBy", "viewColumn", "pageFirst", "pageLast", "pagePrevious", "pageNext"];
|
|
29
|
+
const useLexicon = (customLexicon, replacement) => {
|
|
30
|
+
const app = useApp();
|
|
31
|
+
const ref = React.useRef(customLexicon);
|
|
32
|
+
const refReplacement = React.useRef(replacement);
|
|
33
|
+
ref.current = customLexicon;
|
|
34
|
+
refReplacement.current = replacement;
|
|
35
|
+
const lexicon = React.useMemo(() => {
|
|
36
|
+
const defaultText = key => {
|
|
37
|
+
const line = app.line(`dataTable.${key}`);
|
|
38
|
+
if (typeof line === "string") {
|
|
39
|
+
return line;
|
|
40
|
+
}
|
|
41
|
+
return defaultLexicon[key];
|
|
42
|
+
};
|
|
43
|
+
const plain = key => {
|
|
44
|
+
const value = ref.current[key];
|
|
45
|
+
if (!value) {
|
|
46
|
+
return defaultText(key);
|
|
47
|
+
}
|
|
48
|
+
return typeof value === "function" ? value() : value;
|
|
49
|
+
};
|
|
50
|
+
const createPlainFn = key => () => {
|
|
51
|
+
return plain(key);
|
|
52
|
+
};
|
|
53
|
+
const replaceFn = (key, replacement) => {
|
|
54
|
+
const value = ref.current[key] || defaultText(key);
|
|
55
|
+
return typeof value === "function" ? value(replacement) : app.replace(value, replacement);
|
|
56
|
+
};
|
|
57
|
+
const lexicon = {
|
|
58
|
+
filterSearch(label) {
|
|
59
|
+
const value = ref.current.filterSearch;
|
|
60
|
+
if (!value) {
|
|
61
|
+
const line = app.line(`dataTable.filterSearch`);
|
|
62
|
+
if (typeof line === "string") {
|
|
63
|
+
return app.replace(line, {
|
|
64
|
+
label
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
return label;
|
|
68
|
+
}
|
|
69
|
+
return typeof value === "function" ? value({
|
|
70
|
+
label
|
|
71
|
+
}) : value;
|
|
72
|
+
},
|
|
73
|
+
filterSelected(selected) {
|
|
74
|
+
return replaceFn("filterSelected", {
|
|
75
|
+
selected
|
|
76
|
+
});
|
|
77
|
+
},
|
|
78
|
+
get pageOf() {
|
|
79
|
+
const {
|
|
80
|
+
pageNumber,
|
|
81
|
+
pageCount
|
|
82
|
+
} = refReplacement.current;
|
|
83
|
+
return replaceFn("pageOf", {
|
|
84
|
+
pageNumber,
|
|
85
|
+
pageCount
|
|
86
|
+
});
|
|
87
|
+
},
|
|
88
|
+
get selected() {
|
|
89
|
+
const {
|
|
90
|
+
size,
|
|
91
|
+
selected
|
|
92
|
+
} = refReplacement.current;
|
|
93
|
+
return replaceFn("selected", {
|
|
94
|
+
size,
|
|
95
|
+
selected
|
|
96
|
+
});
|
|
97
|
+
},
|
|
98
|
+
get onePageTitle() {
|
|
99
|
+
const {
|
|
100
|
+
size
|
|
101
|
+
} = refReplacement.current;
|
|
102
|
+
return replaceFn("onePageTitle", {
|
|
103
|
+
size
|
|
104
|
+
});
|
|
105
|
+
},
|
|
106
|
+
get title() {
|
|
107
|
+
const curr = ref.current;
|
|
108
|
+
const {
|
|
109
|
+
size,
|
|
110
|
+
offset,
|
|
111
|
+
pageCount,
|
|
112
|
+
pageNumber,
|
|
113
|
+
total,
|
|
114
|
+
limit
|
|
115
|
+
} = refReplacement.current;
|
|
116
|
+
if (size === total) {
|
|
117
|
+
let value = curr.onePageTitle;
|
|
118
|
+
if (!value && !curr.title) {
|
|
119
|
+
value = defaultText("onePageTitle");
|
|
120
|
+
}
|
|
121
|
+
if (value) {
|
|
122
|
+
const replacement = {
|
|
123
|
+
size
|
|
124
|
+
};
|
|
125
|
+
return typeof value === "function" ? value(replacement) : app.replace(value, replacement);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
const replacement = {
|
|
129
|
+
size,
|
|
130
|
+
offset,
|
|
131
|
+
total,
|
|
132
|
+
limit,
|
|
133
|
+
pageCount,
|
|
134
|
+
pageNumber
|
|
135
|
+
};
|
|
136
|
+
const value = curr.title || defaultText("title");
|
|
137
|
+
return typeof value === "function" ? value(replacement) : app.replace(value, replacement);
|
|
138
|
+
}
|
|
139
|
+
};
|
|
140
|
+
|
|
141
|
+
// define plain getters
|
|
142
|
+
plainKeyList.forEach(key => {
|
|
143
|
+
Object.defineProperty(lexicon, key, {
|
|
144
|
+
get: createPlainFn(key)
|
|
145
|
+
});
|
|
146
|
+
});
|
|
147
|
+
return lexicon;
|
|
148
|
+
}, [app]);
|
|
149
|
+
return lexicon;
|
|
150
|
+
};
|
|
151
|
+
export { useLexicon };
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { CellContext } from "@tanstack/react-table";
|
|
2
|
+
import type { RowMenuOption } from "./types";
|
|
3
|
+
declare const useRowMenu: <TData>(info: CellContext<TData, unknown>) => (item: Pick<RowMenuOption<TData>, "onClick" | "trigger" | "confirmation" | "enabledKey" | "triggerKey">) => {
|
|
4
|
+
disabled: boolean;
|
|
5
|
+
onClick(): void;
|
|
6
|
+
};
|
|
7
|
+
export { useRowMenu };
|
package/use-row-menu.js
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { useTrigger, createTriggerProp } from "@tint-ui/trigger";
|
|
4
|
+
import { useApp } from "@tint-ui/app";
|
|
5
|
+
const useRowMenu = info => {
|
|
6
|
+
const app = useApp();
|
|
7
|
+
const triggerService = useTrigger();
|
|
8
|
+
const data = info.row.original;
|
|
9
|
+
return item => {
|
|
10
|
+
const {
|
|
11
|
+
onClick,
|
|
12
|
+
trigger,
|
|
13
|
+
triggerKey,
|
|
14
|
+
confirmation,
|
|
15
|
+
enabledKey
|
|
16
|
+
} = item;
|
|
17
|
+
const clickHandler = () => {
|
|
18
|
+
if (typeof onClick === "function") {
|
|
19
|
+
onClick(data);
|
|
20
|
+
}
|
|
21
|
+
if (triggerKey) {
|
|
22
|
+
const triggerProp = data[triggerKey];
|
|
23
|
+
if (triggerProp != null) {
|
|
24
|
+
triggerService.emitProp(createTriggerProp(triggerProp));
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
if (trigger) {
|
|
28
|
+
const {
|
|
29
|
+
name,
|
|
30
|
+
props
|
|
31
|
+
} = createTriggerProp(trigger);
|
|
32
|
+
triggerService.emit(name, "data" in props ? props : {
|
|
33
|
+
...props,
|
|
34
|
+
data
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
};
|
|
38
|
+
return {
|
|
39
|
+
disabled: enabledKey ? data[enabledKey] === true : false,
|
|
40
|
+
onClick() {
|
|
41
|
+
if (confirmation) {
|
|
42
|
+
const message = app.replace(confirmation, data);
|
|
43
|
+
if (triggerService.registered("dialog:confirm")) {
|
|
44
|
+
triggerService.emit("dialog:confirm", {
|
|
45
|
+
message,
|
|
46
|
+
confirmHandler: clickHandler
|
|
47
|
+
});
|
|
48
|
+
} else if (window.confirm(message)) {
|
|
49
|
+
clickHandler();
|
|
50
|
+
}
|
|
51
|
+
} else {
|
|
52
|
+
clickHandler();
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
};
|
|
56
|
+
};
|
|
57
|
+
};
|
|
58
|
+
export { useRowMenu };
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { DataTableDisplayCell } from "./types";
|
|
2
|
+
import type { VisibilityState, OnChangeFn } from "@tanstack/react-table";
|
|
3
|
+
declare const useVisibilityColumn: (name: string, cells: DataTableDisplayCell[], options: {
|
|
4
|
+
storage: string | boolean;
|
|
5
|
+
ssr: boolean;
|
|
6
|
+
}) => [VisibilityState, OnChangeFn<VisibilityState>];
|
|
7
|
+
export { useVisibilityColumn };
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import * as React from "react";
|
|
4
|
+
import { onStore } from "./utils.js";
|
|
5
|
+
import { isLocalStorage } from "@tint-ui/tools/browser-support";
|
|
6
|
+
const readState = function (name, storage, readStore, cells) {
|
|
7
|
+
let initialData = {};
|
|
8
|
+
if (readStore) {
|
|
9
|
+
const data = onStore(name, storage, "visibility", (local, name) => {
|
|
10
|
+
const text = local.getItem(name);
|
|
11
|
+
return text && text.startsWith("{") ? JSON.parse(text) : null;
|
|
12
|
+
});
|
|
13
|
+
if (data != null) {
|
|
14
|
+
initialData = data;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
const data = {};
|
|
18
|
+
cells.forEach(item => {
|
|
19
|
+
if (item.invisible) {
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
if (item.required) {
|
|
23
|
+
data[item.name] = true;
|
|
24
|
+
} else {
|
|
25
|
+
const value = initialData[item.name];
|
|
26
|
+
if (value != null) {
|
|
27
|
+
data[item.name] = value;
|
|
28
|
+
} else {
|
|
29
|
+
data[item.name] = !item.hidden;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
});
|
|
33
|
+
return data;
|
|
34
|
+
};
|
|
35
|
+
const useVisibilityColumn = function (name, cells, options) {
|
|
36
|
+
const {
|
|
37
|
+
storage,
|
|
38
|
+
ssr
|
|
39
|
+
} = options;
|
|
40
|
+
const [columnVisibility, setColumnVisibility] = React.useState(() => readState(name, storage, !ssr, cells));
|
|
41
|
+
const ref = React.useRef({
|
|
42
|
+
name,
|
|
43
|
+
storage,
|
|
44
|
+
update(data, fn) {
|
|
45
|
+
const {
|
|
46
|
+
name,
|
|
47
|
+
storage
|
|
48
|
+
} = ref.current;
|
|
49
|
+
onStore(name, storage, "visibility", (local, name) => {
|
|
50
|
+
if (fn) {
|
|
51
|
+
fn(name);
|
|
52
|
+
}
|
|
53
|
+
local.setItem(name, JSON.stringify(data));
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
});
|
|
57
|
+
ref.current.name = name;
|
|
58
|
+
ref.current.storage = storage;
|
|
59
|
+
React.useEffect(() => {
|
|
60
|
+
let origin = columnVisibility;
|
|
61
|
+
const data = readState(name, storage, true, cells);
|
|
62
|
+
for (const key in data) {
|
|
63
|
+
if (columnVisibility[key] !== data[key]) {
|
|
64
|
+
origin = data;
|
|
65
|
+
setColumnVisibility(origin);
|
|
66
|
+
break;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
if (!storage || !isLocalStorage()) {
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
let storeKey = "";
|
|
73
|
+
ref.current.update(origin, name => {
|
|
74
|
+
storeKey = name;
|
|
75
|
+
});
|
|
76
|
+
if (storeKey.length) {
|
|
77
|
+
const fn = event => {
|
|
78
|
+
if (event.key === storeKey && event.newValue != null) {
|
|
79
|
+
const data = JSON.parse(event.newValue);
|
|
80
|
+
setColumnVisibility(data);
|
|
81
|
+
ref.current.update(data);
|
|
82
|
+
}
|
|
83
|
+
};
|
|
84
|
+
window.addEventListener("storage", fn);
|
|
85
|
+
return () => {
|
|
86
|
+
window.removeEventListener("storage", fn);
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
}, [name, storage, ssr, cells]);
|
|
90
|
+
const columnVisibilityHandler = React.useCallback(data => {
|
|
91
|
+
setColumnVisibility(prevState => {
|
|
92
|
+
if (typeof data === "function") {
|
|
93
|
+
data = data(prevState);
|
|
94
|
+
}
|
|
95
|
+
ref.current.update(data);
|
|
96
|
+
return data;
|
|
97
|
+
});
|
|
98
|
+
}, [setColumnVisibility]);
|
|
99
|
+
return [columnVisibility, columnVisibilityHandler];
|
|
100
|
+
};
|
|
101
|
+
export { useVisibilityColumn };
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { DataTableDisplayFilter, FilterVisibility } from "./types";
|
|
2
|
+
declare const useVisibilityFilter: <TData extends object>(name: string, filters: DataTableDisplayFilter<keyof TData, any>[], filterText: boolean, options: {
|
|
3
|
+
storage: boolean | string;
|
|
4
|
+
ssr: boolean;
|
|
5
|
+
requiredFilterText?: boolean;
|
|
6
|
+
}) => FilterVisibility<TData>;
|
|
7
|
+
export { useVisibilityFilter };
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import { onStore } from "./utils.js";
|
|
3
|
+
import { isLocalStorage } from "@tint-ui/tools/browser-support";
|
|
4
|
+
const readState = function (name, storage, readStore, filters, filterText, requiredFilterText) {
|
|
5
|
+
const data = {
|
|
6
|
+
filterText,
|
|
7
|
+
filters: {}
|
|
8
|
+
};
|
|
9
|
+
filters.forEach(filter => {
|
|
10
|
+
data.filters[filter.name] = filter.required || !filter.hidden;
|
|
11
|
+
});
|
|
12
|
+
if (readStore) {
|
|
13
|
+
const localData = onStore(name, storage, "filtering", (local, name) => {
|
|
14
|
+
const text = local.getItem(name);
|
|
15
|
+
return text && text.startsWith("{") ? JSON.parse(text) : null;
|
|
16
|
+
});
|
|
17
|
+
if (localData != null) {
|
|
18
|
+
data.filterText = filterText && localData.filterText;
|
|
19
|
+
const latest = localData.filters || {};
|
|
20
|
+
filters.forEach(({
|
|
21
|
+
required,
|
|
22
|
+
name
|
|
23
|
+
}) => {
|
|
24
|
+
const value = latest[name];
|
|
25
|
+
if (!required && typeof value === "boolean") {
|
|
26
|
+
data.filters[name] = value;
|
|
27
|
+
}
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
if (filterText && requiredFilterText) {
|
|
32
|
+
data.filterText = true;
|
|
33
|
+
}
|
|
34
|
+
return data;
|
|
35
|
+
};
|
|
36
|
+
const useVisibilityFilter = function (name, filters, filterText, options) {
|
|
37
|
+
const {
|
|
38
|
+
storage,
|
|
39
|
+
ssr,
|
|
40
|
+
requiredFilterText = false
|
|
41
|
+
} = options;
|
|
42
|
+
const [filteringState, setFilteringState] = React.useState(() => readState(name, storage, !ssr, filters, filterText, requiredFilterText));
|
|
43
|
+
const ref = React.useRef(filteringState);
|
|
44
|
+
ref.current = filteringState;
|
|
45
|
+
const refUpdate = React.useRef((data, fn) => {
|
|
46
|
+
onStore(name, storage, "filtering", (local, name) => {
|
|
47
|
+
if (fn) {
|
|
48
|
+
fn(name);
|
|
49
|
+
}
|
|
50
|
+
local.setItem(name, JSON.stringify(data));
|
|
51
|
+
});
|
|
52
|
+
});
|
|
53
|
+
React.useEffect(() => {
|
|
54
|
+
const data = readState(name, storage, true, filters, filterText, requiredFilterText);
|
|
55
|
+
const compare = () => {
|
|
56
|
+
for (const name in data.filters) {
|
|
57
|
+
if (data.filters[name] !== filteringState.filters[name]) {
|
|
58
|
+
return false;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
return true;
|
|
62
|
+
};
|
|
63
|
+
if (filteringState.filterText !== data.filterText || !compare()) {
|
|
64
|
+
setFilteringState(data);
|
|
65
|
+
}
|
|
66
|
+
if (!storage || !isLocalStorage()) {
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
let storeKey = "";
|
|
70
|
+
refUpdate.current(data, name => {
|
|
71
|
+
storeKey = name;
|
|
72
|
+
});
|
|
73
|
+
if (storeKey.length) {
|
|
74
|
+
const fn = event => {
|
|
75
|
+
if (event.key === storeKey && event.newValue != null) {
|
|
76
|
+
const data = JSON.parse(event.newValue);
|
|
77
|
+
setFilteringState(data);
|
|
78
|
+
refUpdate.current(data);
|
|
79
|
+
}
|
|
80
|
+
};
|
|
81
|
+
window.addEventListener("storage", fn);
|
|
82
|
+
return () => {
|
|
83
|
+
window.removeEventListener("storage", fn);
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
}, [name, storage, ssr]);
|
|
87
|
+
const {
|
|
88
|
+
getFilters,
|
|
89
|
+
getVisibleFilters,
|
|
90
|
+
hasFilterVisible,
|
|
91
|
+
onFilterVisibleChange,
|
|
92
|
+
onFilterTextVisibleChange
|
|
93
|
+
} = React.useMemo(() => {
|
|
94
|
+
return {
|
|
95
|
+
getFilters() {
|
|
96
|
+
return filters;
|
|
97
|
+
},
|
|
98
|
+
getVisibleFilters() {
|
|
99
|
+
return filters.filter(item => ref.current.filters[item.name] === true);
|
|
100
|
+
},
|
|
101
|
+
hasFilterVisible(name) {
|
|
102
|
+
return ref.current.filters[name] === true;
|
|
103
|
+
},
|
|
104
|
+
onFilterVisibleChange: (name, visible) => {
|
|
105
|
+
const filter = filters.find(item => item.name === name);
|
|
106
|
+
if (!filter || filter.required || ref.current.filters[name] === visible) {
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
setFilteringState(prev => {
|
|
110
|
+
const state = {
|
|
111
|
+
filterText: prev.filterText,
|
|
112
|
+
filters: {
|
|
113
|
+
...prev.filters,
|
|
114
|
+
[name]: visible
|
|
115
|
+
}
|
|
116
|
+
};
|
|
117
|
+
refUpdate.current(state);
|
|
118
|
+
return state;
|
|
119
|
+
});
|
|
120
|
+
},
|
|
121
|
+
onFilterTextVisibleChange: visible => {
|
|
122
|
+
if (!filterText || requiredFilterText || ref.current.filterText === visible) {
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
setFilteringState(prev => {
|
|
126
|
+
const state = {
|
|
127
|
+
filterText: !prev.filterText,
|
|
128
|
+
filters: prev.filters
|
|
129
|
+
};
|
|
130
|
+
refUpdate.current(state);
|
|
131
|
+
return state;
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
};
|
|
135
|
+
}, [filters, filterText, requiredFilterText]);
|
|
136
|
+
return {
|
|
137
|
+
filterText: filteringState.filterText,
|
|
138
|
+
getVisibleFilters,
|
|
139
|
+
getFilters,
|
|
140
|
+
hasFilterVisible,
|
|
141
|
+
onFilterVisibleChange,
|
|
142
|
+
onFilterTextVisibleChange
|
|
143
|
+
};
|
|
144
|
+
};
|
|
145
|
+
export { useVisibilityFilter };
|
package/utils.d.ts
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { NavbarConfig, ToolbarConfig } from "./types";
|
|
2
|
+
import type { ColumnFilter } from "@tanstack/react-table";
|
|
3
|
+
declare const onStore: <T = unknown>(name: string, storage: string | boolean, type: string, fn: (storage: Storage, name: string) => T) => T | null;
|
|
4
|
+
declare const getToolbarConfig: (config: null | undefined | Partial<Omit<ToolbarConfig, "onFilterReset">>) => Omit<ToolbarConfig, "onFilterReset">;
|
|
5
|
+
declare const getNavbarConfig: (config: null | undefined | Partial<Omit<NavbarConfig, "onPageSizeChange">>, defaultPageSize: number) => Omit<NavbarConfig, "onPageSizeChange">;
|
|
6
|
+
declare const getPaginationNumber: (pageNumber: number, pageCount: number, left: number) => ({
|
|
7
|
+
divider: true;
|
|
8
|
+
} | {
|
|
9
|
+
divider: false;
|
|
10
|
+
page: number;
|
|
11
|
+
selected: boolean;
|
|
12
|
+
})[];
|
|
13
|
+
declare const diffFilterId: (left: ColumnFilter[], right: ColumnFilter[]) => string;
|
|
14
|
+
export { onStore, getToolbarConfig, getNavbarConfig, getPaginationNumber, diffFilterId };
|
package/utils.js
ADDED
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import { isLocalStorage } from "@tint-ui/tools/browser-support";
|
|
2
|
+
const onStore = (name, storage, type, fn) => {
|
|
3
|
+
if (!storage || !isLocalStorage()) {
|
|
4
|
+
return null;
|
|
5
|
+
}
|
|
6
|
+
let key = `table:${name}:${type}`;
|
|
7
|
+
if (typeof storage === "string") {
|
|
8
|
+
key += `-${storage}`;
|
|
9
|
+
}
|
|
10
|
+
try {
|
|
11
|
+
return fn(window.localStorage, key);
|
|
12
|
+
} catch (err) {
|
|
13
|
+
console.error("Window storage failure", err);
|
|
14
|
+
}
|
|
15
|
+
return null;
|
|
16
|
+
};
|
|
17
|
+
const defaultSize = "md";
|
|
18
|
+
const getToolbarConfig = config => {
|
|
19
|
+
const {
|
|
20
|
+
size = defaultSize,
|
|
21
|
+
viewIconOnly = false,
|
|
22
|
+
resetIconOnly = false
|
|
23
|
+
} = config || {};
|
|
24
|
+
return {
|
|
25
|
+
size,
|
|
26
|
+
viewIconOnly,
|
|
27
|
+
resetIconOnly
|
|
28
|
+
};
|
|
29
|
+
};
|
|
30
|
+
const getNavbarConfig = (config, defaultPageSize) => {
|
|
31
|
+
const {
|
|
32
|
+
size = defaultSize,
|
|
33
|
+
numberSize = 2,
|
|
34
|
+
pageSize = defaultPageSize,
|
|
35
|
+
pageSizeOptions,
|
|
36
|
+
mode = "arrow"
|
|
37
|
+
} = config || {};
|
|
38
|
+
return {
|
|
39
|
+
size,
|
|
40
|
+
mode,
|
|
41
|
+
pageSize,
|
|
42
|
+
numberSize,
|
|
43
|
+
pageSizeOptions: Array.isArray(pageSizeOptions) && pageSizeOptions.length > 0 ? pageSizeOptions : [pageSize]
|
|
44
|
+
};
|
|
45
|
+
};
|
|
46
|
+
const getPaginationNumber = (pageNumber, pageCount, left) => {
|
|
47
|
+
if (pageCount < 2) {
|
|
48
|
+
return [];
|
|
49
|
+
}
|
|
50
|
+
const items = [];
|
|
51
|
+
const dots = left * 2 + 1 < pageCount;
|
|
52
|
+
const push = pg => {
|
|
53
|
+
if (pg === 0) {
|
|
54
|
+
items.push({
|
|
55
|
+
divider: true
|
|
56
|
+
});
|
|
57
|
+
} else {
|
|
58
|
+
items.push({
|
|
59
|
+
divider: false,
|
|
60
|
+
page: pg,
|
|
61
|
+
selected: pg === pageNumber
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
};
|
|
65
|
+
let start = pageNumber - left;
|
|
66
|
+
let end = pageNumber + left;
|
|
67
|
+
if (start < 1) {
|
|
68
|
+
start = 1;
|
|
69
|
+
end = left * 2 + 1;
|
|
70
|
+
}
|
|
71
|
+
if (end > pageCount) {
|
|
72
|
+
end = pageCount;
|
|
73
|
+
start = end - left * 2;
|
|
74
|
+
if (start < 1) {
|
|
75
|
+
start = 1;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
if (start > 1) {
|
|
79
|
+
push(1);
|
|
80
|
+
dots && start > 2 && push(0);
|
|
81
|
+
}
|
|
82
|
+
for (let i = start; i <= end; i++) {
|
|
83
|
+
push(i);
|
|
84
|
+
}
|
|
85
|
+
if (end < pageCount) {
|
|
86
|
+
dots && end + 1 < pageCount && push(0);
|
|
87
|
+
push(pageCount);
|
|
88
|
+
}
|
|
89
|
+
return items;
|
|
90
|
+
};
|
|
91
|
+
const diffCompare = (a, b) => {
|
|
92
|
+
if (Array.isArray(a)) {
|
|
93
|
+
a = a.join(",");
|
|
94
|
+
}
|
|
95
|
+
if (Array.isArray(b)) {
|
|
96
|
+
b = b.join(",");
|
|
97
|
+
}
|
|
98
|
+
return a === b;
|
|
99
|
+
};
|
|
100
|
+
const diffFilterId = (left, right) => {
|
|
101
|
+
const rightData = {};
|
|
102
|
+
for (const {
|
|
103
|
+
id,
|
|
104
|
+
value
|
|
105
|
+
} of right) {
|
|
106
|
+
rightData[id] = value;
|
|
107
|
+
}
|
|
108
|
+
const leftData = {};
|
|
109
|
+
for (const {
|
|
110
|
+
id,
|
|
111
|
+
value
|
|
112
|
+
} of left) {
|
|
113
|
+
if (leftData[id] == null) {
|
|
114
|
+
return id;
|
|
115
|
+
}
|
|
116
|
+
leftData[id] = value;
|
|
117
|
+
}
|
|
118
|
+
for (const {
|
|
119
|
+
id,
|
|
120
|
+
value
|
|
121
|
+
} of right) {
|
|
122
|
+
if (leftData[id] == null || !diffCompare(leftData[id], value)) {
|
|
123
|
+
return id;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
return "*";
|
|
127
|
+
};
|
|
128
|
+
export { onStore, getToolbarConfig, getNavbarConfig, getPaginationNumber, diffFilterId };
|