@bexis2/bexis2-core-ui 0.4.95 → 0.4.97
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/README.md +105 -24
- package/dist/components/CodeEditor/CodeEditor.svelte +2 -2
- package/dist/components/Facets/ShowMore.svelte +4 -6
- package/dist/components/File/FileUploader.svelte +6 -3
- package/dist/components/Table/Table.svelte +31 -5
- package/dist/components/Table/TableContent.svelte +226 -27
- package/dist/components/Table/TableFilter.svelte +25 -12
- package/dist/components/Table/TableFilterServer.svelte +51 -6
- package/dist/components/Table/TablePaginationServer.svelte +27 -8
- package/dist/components/Table/clientDB.d.ts +22 -0
- package/dist/components/Table/clientDB.js +236 -0
- package/dist/components/Table/filter.js +21 -2
- package/dist/components/Table/tableWorker.d.ts +1 -0
- package/dist/components/Table/tableWorker.js +179 -0
- package/dist/components/Table/utils.d.ts +1 -2
- package/dist/components/Table/utils.js +64 -31
- package/dist/components/form/CheckboxKvPList.svelte +15 -15
- package/dist/components/form/CheckboxList.svelte +1 -1
- package/dist/components/form/DateInput.svelte +12 -1
- package/dist/components/form/DatePickerInput.svelte +51 -29
- package/dist/components/form/Dropdown.svelte +14 -3
- package/dist/components/form/DropdownKvP.svelte +13 -2
- package/dist/components/form/InputContainer.svelte +17 -15
- package/dist/components/form/MultiSelect.svelte +30 -29
- package/dist/components/form/NumberInput.svelte +12 -1
- package/dist/components/form/TextArea.svelte +12 -1
- package/dist/components/form/TextInput.svelte +12 -1
- package/dist/components/page/Alert.svelte +5 -1
- package/dist/components/page/BackToTop.svelte +3 -1
- package/dist/components/page/Docs.svelte +9 -2
- package/dist/components/page/GoToTop.svelte +14 -15
- package/dist/components/page/Page.svelte +50 -56
- package/dist/components/page/breadcrumb/BreadcrumbDataCaller.js +11 -15
- package/dist/components/page/menu/MenuAccountBar.svelte +1 -5
- package/dist/components/page/menu/MenuDataCaller.js +1 -1
- package/dist/components/page/menu/MenuItem.svelte +9 -7
- package/dist/components/page/menu/MenuSublist.svelte +7 -5
- package/dist/components/page/menu/SettingsBar.svelte +4 -1
- package/dist/components/toggle/Toggle.svelte +9 -9
- package/dist/css/core.ui.postcss +1 -2
- package/dist/services/Api.js +4 -4
- package/dist/services/BaseCaller.js +1 -1
- package/package.json +114 -114
- package/src/lib/components/CodeEditor/CodeEditor.svelte +4 -4
- package/src/lib/components/Facets/Facets.svelte +2 -2
- package/src/lib/components/Facets/ShowMore.svelte +4 -6
- package/src/lib/components/File/FileUploader.svelte +17 -14
- package/src/lib/components/Table/Table.svelte +32 -5
- package/src/lib/components/Table/TableContent.svelte +261 -38
- package/src/lib/components/Table/TableFilter.svelte +34 -8
- package/src/lib/components/Table/TableFilterServer.svelte +65 -9
- package/src/lib/components/Table/TablePagination.svelte +6 -2
- package/src/lib/components/Table/TablePaginationServer.svelte +36 -10
- package/src/lib/components/Table/clientDB.js +236 -0
- package/src/lib/components/Table/filter.ts +44 -24
- package/src/lib/components/Table/tableWorker.js +179 -0
- package/src/lib/components/Table/utils.ts +80 -46
- package/src/lib/components/form/Checkbox.svelte +1 -1
- package/src/lib/components/form/CheckboxKvPList.svelte +15 -16
- package/src/lib/components/form/CheckboxList.svelte +1 -1
- package/src/lib/components/form/DateInput.svelte +13 -2
- package/src/lib/components/form/DatePickerInput.svelte +51 -29
- package/src/lib/components/form/Dropdown.svelte +14 -3
- package/src/lib/components/form/DropdownKvP.svelte +13 -2
- package/src/lib/components/form/InputContainer.svelte +18 -17
- package/src/lib/components/form/MultiSelect.svelte +30 -29
- package/src/lib/components/form/NumberInput.svelte +21 -12
- package/src/lib/components/form/TextArea.svelte +13 -2
- package/src/lib/components/form/TextInput.svelte +13 -2
- package/src/lib/components/page/Alert.svelte +5 -1
- package/src/lib/components/page/BackToTop.svelte +3 -1
- package/src/lib/components/page/Docs.svelte +9 -2
- package/src/lib/components/page/GoToTop.svelte +14 -15
- package/src/lib/components/page/Notification.svelte +1 -1
- package/src/lib/components/page/Page.svelte +67 -78
- package/src/lib/components/page/breadcrumb/Breadcrumb.svelte +2 -3
- package/src/lib/components/page/breadcrumb/BreadcrumbDataCaller.js +11 -15
- package/src/lib/components/page/menu/MenuAccountBar.svelte +1 -6
- package/src/lib/components/page/menu/MenuDataCaller.js +1 -1
- package/src/lib/components/page/menu/MenuItem.svelte +10 -8
- package/src/lib/components/page/menu/MenuSublist.svelte +35 -41
- package/src/lib/components/page/menu/SettingsBar.svelte +5 -2
- package/src/lib/components/toggle/Toggle.svelte +28 -30
- package/src/lib/css/core.ui.postcss +1 -2
- package/src/lib/index.ts +1 -2
- package/src/lib/services/Api.ts +21 -20
- package/src/lib/services/BaseCaller.js +1 -1
- package/src/lib/stores/apiStores.ts +1 -5
- package/src/lib/stores/pageStores.ts +0 -2
|
@@ -11,6 +11,21 @@
|
|
|
11
11
|
export let updateTable; // Function to update table
|
|
12
12
|
export let data;
|
|
13
13
|
|
|
14
|
+
const toCount = (value) => {
|
|
15
|
+
if (typeof value === 'number') return Number.isNaN(value) ? 0 : value;
|
|
16
|
+
const parsed = Number(String(value ?? '').replace(/[^0-9.-]/g, ''));
|
|
17
|
+
return Number.isNaN(parsed) ? 0 : parsed;
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
const getEffectiveCount = () => {
|
|
21
|
+
const count = toCount(itemCount);
|
|
22
|
+
if (count > 0) return count;
|
|
23
|
+
// Fallback for edge cases where server count has not propagated yet.
|
|
24
|
+
return Array.isArray($data) ? $data.length : 0;
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
let effectiveCount = 0;
|
|
28
|
+
|
|
14
29
|
let indexInformation = '';
|
|
15
30
|
|
|
16
31
|
const { pageIndex, pageCount, pageSize } = pageConfig;
|
|
@@ -25,12 +40,13 @@
|
|
|
25
40
|
};
|
|
26
41
|
|
|
27
42
|
const getIndexInfomationString = () => {
|
|
28
|
-
|
|
43
|
+
const count = effectiveCount;
|
|
44
|
+
return count === 0
|
|
29
45
|
? 'No items'
|
|
30
46
|
: `Displaying items ${$pageIndex * $pageSize + 1} - ${Math.min(
|
|
31
47
|
($pageIndex + 1) * $pageSize,
|
|
32
|
-
|
|
33
|
-
|
|
48
|
+
count
|
|
49
|
+
)} of ${count}`;
|
|
34
50
|
};
|
|
35
51
|
|
|
36
52
|
function updateTableServer() {
|
|
@@ -38,17 +54,21 @@
|
|
|
38
54
|
updateTable();
|
|
39
55
|
}
|
|
40
56
|
|
|
57
|
+
$: effectiveCount = getEffectiveCount();
|
|
41
58
|
$: paginationSettings = {
|
|
42
|
-
size:
|
|
59
|
+
size: effectiveCount,
|
|
43
60
|
limit: $pageSize,
|
|
44
61
|
page: $pageIndex,
|
|
45
62
|
amounts: pageSizes
|
|
46
63
|
};
|
|
47
64
|
$: $pageSize = pageSizeDropdownValue;
|
|
48
|
-
$: $pageCount,
|
|
65
|
+
$: ($pageCount,
|
|
66
|
+
$pageIndex,
|
|
67
|
+
$pageSize,
|
|
68
|
+
itemCount,
|
|
69
|
+
(indexInformation = getIndexInfomationString()));
|
|
49
70
|
|
|
50
71
|
// updateTable();
|
|
51
|
-
|
|
52
72
|
</script>
|
|
53
73
|
|
|
54
74
|
<div class="grid grid-cols-3 w-full items-stretch gap-10">
|
|
@@ -67,9 +87,12 @@
|
|
|
67
87
|
{#each pageSizes as size}
|
|
68
88
|
<ListBoxItem
|
|
69
89
|
bind:group={pageSizeDropdownValue}
|
|
70
|
-
name="medium"
|
|
71
|
-
|
|
72
|
-
|
|
90
|
+
name="medium"
|
|
91
|
+
value={size}
|
|
92
|
+
on:click={() => {
|
|
93
|
+
$pageSize = size;
|
|
94
|
+
updateTableServer();
|
|
95
|
+
}}>{size}</ListBoxItem
|
|
73
96
|
>
|
|
74
97
|
{/each}
|
|
75
98
|
</ListBox>
|
|
@@ -78,7 +101,10 @@
|
|
|
78
101
|
</div>
|
|
79
102
|
<div class="flex justify-center">
|
|
80
103
|
<Paginator
|
|
81
|
-
on:page={(page) => {
|
|
104
|
+
on:page={(page) => {
|
|
105
|
+
$pageIndex = page.detail;
|
|
106
|
+
updateTableServer();
|
|
107
|
+
}}
|
|
82
108
|
settings={paginationSettings}
|
|
83
109
|
select="hidden"
|
|
84
110
|
active="!variant-filled-secondary !text-on-secondary-token"
|
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
// Simple client-side DB wrapper using IndexedDB directly (no worker)
|
|
2
|
+
const DB_PREFIX = 'table-db-';
|
|
3
|
+
|
|
4
|
+
function openDB(name) {
|
|
5
|
+
return new Promise((resolve, reject) => {
|
|
6
|
+
const req = indexedDB.open(DB_PREFIX + name, 1);
|
|
7
|
+
req.onupgradeneeded = () => {
|
|
8
|
+
const db = req.result;
|
|
9
|
+
if (!db.objectStoreNames.contains('rows')) {
|
|
10
|
+
db.createObjectStore('rows', { keyPath: '__id', autoIncrement: true });
|
|
11
|
+
}
|
|
12
|
+
};
|
|
13
|
+
req.onsuccess = () => resolve(req.result);
|
|
14
|
+
req.onerror = () => reject(req.error);
|
|
15
|
+
});
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function clearStore(db) {
|
|
19
|
+
return new Promise((resolve, reject) => {
|
|
20
|
+
const tx = db.transaction('rows', 'readwrite');
|
|
21
|
+
const store = tx.objectStore('rows');
|
|
22
|
+
const req = store.clear();
|
|
23
|
+
req.onsuccess = () => resolve(undefined);
|
|
24
|
+
req.onerror = () => reject(req.error);
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function bulkInsert(db, rows) {
|
|
29
|
+
return new Promise((resolve, reject) => {
|
|
30
|
+
const tx = db.transaction('rows', 'readwrite');
|
|
31
|
+
const store = tx.objectStore('rows');
|
|
32
|
+
for (let i = 0; i < rows.length; i++) {
|
|
33
|
+
store.add({ __r: rows[i] });
|
|
34
|
+
}
|
|
35
|
+
tx.oncomplete = () => resolve(undefined);
|
|
36
|
+
tx.onerror = () => reject(tx.error);
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function matches(rowObj, q, filters) {
|
|
41
|
+
const row = rowObj.__r || rowObj;
|
|
42
|
+
|
|
43
|
+
if (q && q.length > 0) {
|
|
44
|
+
const ql = String(q).toLowerCase();
|
|
45
|
+
let found = false;
|
|
46
|
+
for (const k in row) {
|
|
47
|
+
const v = row[k];
|
|
48
|
+
if (v == null) continue;
|
|
49
|
+
if (typeof v === 'string' && v.toLowerCase().includes(ql)) {
|
|
50
|
+
found = true;
|
|
51
|
+
break;
|
|
52
|
+
}
|
|
53
|
+
if (typeof v === 'number' && String(v).includes(ql)) {
|
|
54
|
+
found = true;
|
|
55
|
+
break;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
if (!found) return false;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const normalizeKey = (k) => String(k || '').replaceAll('%%%', '.');
|
|
62
|
+
const getValue = (column) => {
|
|
63
|
+
const dot = normalizeKey(column);
|
|
64
|
+
return row[dot] ?? row[column] ?? row[dot.replaceAll('.', '%%%')];
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
const asComparableNumber = (v) => {
|
|
68
|
+
if (typeof v === 'number') return v;
|
|
69
|
+
if (typeof v === 'bigint') return Number(v);
|
|
70
|
+
const n = Number(v);
|
|
71
|
+
return Number.isNaN(n) ? null : n;
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
const asTime = (v) => {
|
|
75
|
+
if (v instanceof Date) return v.getTime();
|
|
76
|
+
const d = new Date(v);
|
|
77
|
+
const t = d.getTime();
|
|
78
|
+
return Number.isNaN(t) ? null : t;
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
if (filters && filters.length > 0) {
|
|
82
|
+
for (const f of filters) {
|
|
83
|
+
const op = f.filterBy;
|
|
84
|
+
const val = f.value;
|
|
85
|
+
const actual = getValue(f.column);
|
|
86
|
+
if (val == null) continue;
|
|
87
|
+
|
|
88
|
+
const aStr = String(actual ?? '').toLowerCase();
|
|
89
|
+
const vStr = String(val ?? '').toLowerCase();
|
|
90
|
+
const aNum = asComparableNumber(actual);
|
|
91
|
+
const vNum = asComparableNumber(val);
|
|
92
|
+
const aDate = asTime(actual);
|
|
93
|
+
const vDate = asTime(val);
|
|
94
|
+
|
|
95
|
+
if (op === 'c' && !aStr.includes(vStr)) return false;
|
|
96
|
+
if (op === 'nc' && aStr.includes(vStr)) return false;
|
|
97
|
+
if (op === 'sw' && !aStr.startsWith(vStr)) return false;
|
|
98
|
+
if (op === 'ew' && !aStr.endsWith(vStr)) return false;
|
|
99
|
+
|
|
100
|
+
if (op === 'e' && actual != val) return false;
|
|
101
|
+
if (op === 'ne' && actual == val) return false;
|
|
102
|
+
|
|
103
|
+
if (op === 'gt') {
|
|
104
|
+
if (aNum == null || vNum == null || !(aNum > vNum)) return false;
|
|
105
|
+
}
|
|
106
|
+
if (op === 'lt') {
|
|
107
|
+
if (aNum == null || vNum == null || !(aNum < vNum)) return false;
|
|
108
|
+
}
|
|
109
|
+
if (op === 'gte') {
|
|
110
|
+
if (aNum == null || vNum == null || !(aNum >= vNum)) return false;
|
|
111
|
+
}
|
|
112
|
+
if (op === 'lte') {
|
|
113
|
+
if (aNum == null || vNum == null || !(aNum <= vNum)) return false;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
if (op === 'o') {
|
|
117
|
+
if (aDate == null || vDate == null || aDate !== vDate) return false;
|
|
118
|
+
}
|
|
119
|
+
if (op === 'sf' || op === 'a') {
|
|
120
|
+
if (aDate == null || vDate == null || !(aDate > vDate || aDate === vDate)) return false;
|
|
121
|
+
}
|
|
122
|
+
if (op === 'u' || op === 'b') {
|
|
123
|
+
if (aDate == null || vDate == null || !(aDate < vDate || aDate === vDate)) return false;
|
|
124
|
+
}
|
|
125
|
+
if (op === 'no') {
|
|
126
|
+
if (aDate == null || vDate == null || aDate === vDate) return false;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
return true;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
export default class ClientDB {
|
|
135
|
+
constructor(tableId) {
|
|
136
|
+
this.tableId = tableId;
|
|
137
|
+
this.db = null;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
async init(rows) {
|
|
141
|
+
this.db = this.db || (await openDB(this.tableId));
|
|
142
|
+
await clearStore(this.db);
|
|
143
|
+
|
|
144
|
+
const CHUNK = 1000;
|
|
145
|
+
for (let i = 0; i < rows.length; i += CHUNK) {
|
|
146
|
+
const chunk = rows.slice(i, i + CHUNK);
|
|
147
|
+
await bulkInsert(this.db, chunk);
|
|
148
|
+
// Yield to keep the UI responsive while importing
|
|
149
|
+
await new Promise((r) => setTimeout(r, 0));
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
return { tableId: this.tableId, count: rows.length };
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
async query({ q = '', filters = [], order = [], offset = 0, limit = 100 } = {}) {
|
|
156
|
+
this.db = this.db || (await openDB(this.tableId));
|
|
157
|
+
|
|
158
|
+
const tx = this.db.transaction('rows', 'readonly');
|
|
159
|
+
const store = tx.objectStore('rows');
|
|
160
|
+
const req = store.openCursor();
|
|
161
|
+
|
|
162
|
+
const matched = [];
|
|
163
|
+
|
|
164
|
+
await new Promise((resolve, reject) => {
|
|
165
|
+
req.onsuccess = (evt) => {
|
|
166
|
+
const cursor = evt.target.result;
|
|
167
|
+
if (!cursor) {
|
|
168
|
+
resolve(undefined);
|
|
169
|
+
return;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
const rec = cursor.value;
|
|
173
|
+
if (matches(rec, q, filters)) {
|
|
174
|
+
matched.push(rec.__r || rec);
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
cursor.continue();
|
|
178
|
+
};
|
|
179
|
+
req.onerror = () => reject(req.error);
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
const getValue = (row, column) => {
|
|
183
|
+
const dot = String(column || '').replaceAll('%%%', '.');
|
|
184
|
+
return row[dot] ?? row[column] ?? row[dot.replaceAll('.', '%%%')];
|
|
185
|
+
};
|
|
186
|
+
|
|
187
|
+
if (order && order.length > 0) {
|
|
188
|
+
const { column, direction } = order[0];
|
|
189
|
+
const dir = direction === 'desc' ? -1 : 1;
|
|
190
|
+
matched.sort((a, b) => {
|
|
191
|
+
const av = getValue(a, column);
|
|
192
|
+
const bv = getValue(b, column);
|
|
193
|
+
|
|
194
|
+
if (av == null && bv == null) return 0;
|
|
195
|
+
if (av == null) return -1 * dir;
|
|
196
|
+
if (bv == null) return 1 * dir;
|
|
197
|
+
|
|
198
|
+
if (typeof av === 'number' && typeof bv === 'number') {
|
|
199
|
+
return (av - bv) * dir;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
if (typeof av === 'bigint' || typeof bv === 'bigint') {
|
|
203
|
+
const ai = BigInt(av);
|
|
204
|
+
const bi = BigInt(bv);
|
|
205
|
+
if (ai === bi) return 0;
|
|
206
|
+
return ai > bi ? dir : -dir;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
const ad = new Date(av).getTime();
|
|
210
|
+
const bd = new Date(bv).getTime();
|
|
211
|
+
if (!Number.isNaN(ad) && !Number.isNaN(bd)) {
|
|
212
|
+
return (ad - bd) * dir;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
return String(av).localeCompare(String(bv)) * dir;
|
|
216
|
+
});
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
const total = matched.length;
|
|
220
|
+
const rows = matched.slice(offset, offset + limit);
|
|
221
|
+
|
|
222
|
+
return { tableId: this.tableId, rows, total };
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
async clear() {
|
|
226
|
+
this.db = this.db || (await openDB(this.tableId));
|
|
227
|
+
await clearStore(this.db);
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
destroy() {
|
|
231
|
+
if (this.db) {
|
|
232
|
+
this.db.close();
|
|
233
|
+
this.db = null;
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
}
|
|
@@ -4,29 +4,29 @@ import type { TableFilterFn } from 'svelte-headless-table/lib/plugins/addTableFi
|
|
|
4
4
|
import { FilterOptionsEnum } from '$models/Enums';
|
|
5
5
|
|
|
6
6
|
const textFilter = (filterOption, filterValue, value) => {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
7
|
+
// check if filterValue is a object or string
|
|
8
|
+
let filterStr = String(filterValue);
|
|
9
|
+
if (typeof filterValue === 'object' && filterValue !== null) {
|
|
10
|
+
filterStr = filterValue.text;
|
|
11
|
+
}
|
|
12
|
+
const valueStr = String(value);
|
|
13
13
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
14
|
+
switch (filterOption) {
|
|
15
|
+
case FilterOptionsEnum.e:
|
|
16
|
+
return valueStr.toLowerCase() === filterStr.toLowerCase();
|
|
17
|
+
case FilterOptionsEnum.ne:
|
|
18
|
+
return valueStr.toLowerCase() !== filterStr.toLowerCase();
|
|
19
|
+
case FilterOptionsEnum.sw:
|
|
20
|
+
return valueStr.toLowerCase().startsWith(filterStr.toLowerCase());
|
|
21
|
+
case FilterOptionsEnum.ew:
|
|
22
|
+
return valueStr.toLowerCase().endsWith(filterStr.toLowerCase());
|
|
23
|
+
case FilterOptionsEnum.c:
|
|
24
|
+
return valueStr.toLowerCase().includes(filterStr.toLowerCase());
|
|
25
|
+
case FilterOptionsEnum.nc:
|
|
26
|
+
return !valueStr.toLowerCase().includes(filterStr.toLowerCase());
|
|
27
|
+
default:
|
|
28
|
+
return false;
|
|
29
|
+
}
|
|
30
30
|
};
|
|
31
31
|
|
|
32
32
|
const numberFilter = (filterOption, filterValue, value) => {
|
|
@@ -78,19 +78,39 @@ const applyFilter = (filterValue, value, filterFn) => {
|
|
|
78
78
|
}));
|
|
79
79
|
|
|
80
80
|
filters.forEach((filter) => {
|
|
81
|
-
result =
|
|
81
|
+
result =
|
|
82
|
+
result &&
|
|
83
|
+
(filter.value !== undefined && filter.value !== '' && filter.value !== null
|
|
84
|
+
? filterFn(filter.option, filter.value, value)
|
|
85
|
+
: true);
|
|
82
86
|
});
|
|
83
87
|
|
|
84
88
|
return result;
|
|
85
89
|
};
|
|
86
90
|
|
|
91
|
+
const booleanFilter = (filterOption, filterValue, value) => {
|
|
92
|
+
const filterBool = typeof filterValue === 'string' ? filterValue === 'true' : !!filterValue;
|
|
93
|
+
const valBool = !!value;
|
|
94
|
+
|
|
95
|
+
switch (filterOption) {
|
|
96
|
+
case FilterOptionsEnum.e:
|
|
97
|
+
return valBool === filterBool;
|
|
98
|
+
case FilterOptionsEnum.ne:
|
|
99
|
+
return valBool !== filterBool;
|
|
100
|
+
default:
|
|
101
|
+
return false;
|
|
102
|
+
}
|
|
103
|
+
};
|
|
104
|
+
|
|
87
105
|
export const columnFilter: ColumnFilterFn = ({ filterValue, value }) => {
|
|
88
106
|
if (typeof value === 'object' && value instanceof Date) {
|
|
89
107
|
return applyFilter(filterValue, value, dateFilter);
|
|
90
|
-
} else if (typeof value === 'number') {
|
|
108
|
+
} else if (typeof value === 'number' || typeof value === 'bigint') {
|
|
91
109
|
return applyFilter(filterValue, value, numberFilter);
|
|
92
110
|
} else if (typeof value === 'string') {
|
|
93
111
|
return applyFilter(filterValue, value, textFilter);
|
|
112
|
+
} else if (typeof value === 'boolean') {
|
|
113
|
+
return applyFilter(filterValue, value, booleanFilter);
|
|
94
114
|
}
|
|
95
115
|
|
|
96
116
|
return false;
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
// @ts-nocheck
|
|
2
|
+
// Lightweight web worker that stores rows in IndexedDB and answers simple queries
|
|
3
|
+
const DB_PREFIX = 'table-db-';
|
|
4
|
+
|
|
5
|
+
function openDB(name) {
|
|
6
|
+
return new Promise((resolve, reject) => {
|
|
7
|
+
const req = indexedDB.open(DB_PREFIX + name, 1);
|
|
8
|
+
req.onupgradeneeded = () => {
|
|
9
|
+
const db = req.result;
|
|
10
|
+
if (!db.objectStoreNames.contains('rows')) {
|
|
11
|
+
db.createObjectStore('rows', { keyPath: '__id', autoIncrement: true });
|
|
12
|
+
}
|
|
13
|
+
};
|
|
14
|
+
req.onsuccess = () => resolve(req.result);
|
|
15
|
+
req.onerror = () => reject(req.error);
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
async function clearStore(db) {
|
|
20
|
+
return new Promise((res, rej) => {
|
|
21
|
+
const tx = db.transaction('rows', 'readwrite');
|
|
22
|
+
const store = tx.objectStore('rows');
|
|
23
|
+
const r = store.clear();
|
|
24
|
+
r.onsuccess = () => res(undefined);
|
|
25
|
+
r.onerror = () => rej(r.error);
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
async function bulkInsert(db, rows) {
|
|
30
|
+
return new Promise((res, rej) => {
|
|
31
|
+
const tx = db.transaction('rows', 'readwrite');
|
|
32
|
+
const store = tx.objectStore('rows');
|
|
33
|
+
for (let i = 0; i < rows.length; i++) {
|
|
34
|
+
// wrap row to avoid clashes with internal __id
|
|
35
|
+
const toPut = { __r: rows[i] };
|
|
36
|
+
store.add(toPut);
|
|
37
|
+
}
|
|
38
|
+
tx.oncomplete = () => res(undefined);
|
|
39
|
+
tx.onerror = () => rej(tx.error);
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function matches(rowObj, q, filters) {
|
|
44
|
+
const row = rowObj.__r || rowObj;
|
|
45
|
+
// full text query
|
|
46
|
+
if (q && q.length > 0) {
|
|
47
|
+
const ql = String(q).toLowerCase();
|
|
48
|
+
let found = false;
|
|
49
|
+
for (const k in row) {
|
|
50
|
+
const v = row[k];
|
|
51
|
+
if (v == null) continue;
|
|
52
|
+
if (typeof v === 'string' && v.toLowerCase().includes(ql)) {
|
|
53
|
+
found = true;
|
|
54
|
+
break;
|
|
55
|
+
}
|
|
56
|
+
if (typeof v === 'number' && String(v).includes(ql)) {
|
|
57
|
+
found = true;
|
|
58
|
+
break;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
if (!found) return false;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// simple equality filters: filters is array of {column, filterBy, value}
|
|
65
|
+
if (filters && filters.length > 0) {
|
|
66
|
+
for (const f of filters) {
|
|
67
|
+
const col = (f.column || '').replaceAll('%%%', '.');
|
|
68
|
+
const val = f.value;
|
|
69
|
+
const actual = row[col] ?? row[f.column] ?? row[col.replaceAll('.', '%%%')];
|
|
70
|
+
if (val == null) continue;
|
|
71
|
+
if (typeof actual === 'string') {
|
|
72
|
+
if (!String(actual).toLowerCase().includes(String(val).toLowerCase())) return false;
|
|
73
|
+
} else if (typeof actual === 'number') {
|
|
74
|
+
if (Number(val) !== actual) return false;
|
|
75
|
+
} else {
|
|
76
|
+
if (actual !== val) return false;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
return true;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// handle messages
|
|
85
|
+
let dbs = {};
|
|
86
|
+
let initCounts = {};
|
|
87
|
+
self.addEventListener('message', async (e) => {
|
|
88
|
+
const { type, payload } = e.data;
|
|
89
|
+
try {
|
|
90
|
+
if (type === 'init-start') {
|
|
91
|
+
const { tableId } = payload;
|
|
92
|
+
const db = await openDB(tableId);
|
|
93
|
+
await clearStore(db);
|
|
94
|
+
dbs[tableId] = db;
|
|
95
|
+
initCounts[tableId] = 0;
|
|
96
|
+
postMessage({ type: 'init-ack', payload: { tableId } });
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
if (type === 'init-chunk') {
|
|
101
|
+
const { tableId, rows } = payload;
|
|
102
|
+
const db = dbs[tableId] || (await openDB(tableId));
|
|
103
|
+
await bulkInsert(db, rows);
|
|
104
|
+
initCounts[tableId] = (initCounts[tableId] || 0) + rows.length;
|
|
105
|
+
postMessage({ type: 'init-progress', payload: { tableId, inserted: initCounts[tableId] } });
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
if (type === 'init-done') {
|
|
110
|
+
const { tableId } = payload;
|
|
111
|
+
const total = initCounts[tableId] || 0;
|
|
112
|
+
postMessage({ type: 'inited', payload: { tableId, count: total } });
|
|
113
|
+
delete initCounts[tableId];
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
if (type === 'init') {
|
|
118
|
+
const { tableId, rows } = payload;
|
|
119
|
+
const db = await openDB(tableId);
|
|
120
|
+
await clearStore(db);
|
|
121
|
+
// insert in chunks to avoid blocking
|
|
122
|
+
const CHUNK = 1000;
|
|
123
|
+
for (let i = 0; i < rows.length; i += CHUNK) {
|
|
124
|
+
const chunk = rows.slice(i, i + CHUNK);
|
|
125
|
+
await bulkInsert(db, chunk);
|
|
126
|
+
}
|
|
127
|
+
dbs[tableId] = db;
|
|
128
|
+
postMessage({ type: 'inited', payload: { tableId, count: rows.length } });
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
if (type === 'query') {
|
|
132
|
+
const { tableId, q, filters = [], offset = 0, limit = 100 } = payload;
|
|
133
|
+
const db = dbs[tableId] || (await openDB(tableId));
|
|
134
|
+
dbs[tableId] = db;
|
|
135
|
+
|
|
136
|
+
const tx = db.transaction('rows', 'readonly');
|
|
137
|
+
const store = tx.objectStore('rows');
|
|
138
|
+
const req = store.openCursor();
|
|
139
|
+
|
|
140
|
+
const results = [];
|
|
141
|
+
let total = 0;
|
|
142
|
+
let skipped = 0;
|
|
143
|
+
|
|
144
|
+
await new Promise((res, rej) => {
|
|
145
|
+
req.onsuccess = (evt) => {
|
|
146
|
+
const cursor = evt.target.result;
|
|
147
|
+
if (!cursor) {
|
|
148
|
+
res();
|
|
149
|
+
return;
|
|
150
|
+
}
|
|
151
|
+
const rec = cursor.value;
|
|
152
|
+
if (matches(rec, q, filters)) {
|
|
153
|
+
total++;
|
|
154
|
+
if (skipped < offset) {
|
|
155
|
+
skipped++;
|
|
156
|
+
} else if (results.length < limit) {
|
|
157
|
+
results.push(rec.__r || rec);
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
cursor.continue();
|
|
161
|
+
};
|
|
162
|
+
req.onerror = () => rej(req.error);
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
postMessage({ type: 'result', payload: { tableId, rows: results, total } });
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
if (type === 'clear') {
|
|
169
|
+
const { tableId } = payload;
|
|
170
|
+
const db = dbs[tableId] || (await openDB(tableId));
|
|
171
|
+
await clearStore(db);
|
|
172
|
+
postMessage({ type: 'cleared', payload: { tableId } });
|
|
173
|
+
}
|
|
174
|
+
} catch (err) {
|
|
175
|
+
postMessage({ type: 'error', payload: { message: err?.message ?? String(err) } });
|
|
176
|
+
}
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
export {};
|