@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
|
@@ -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
|
+
}
|
|
@@ -67,20 +67,39 @@ const applyFilter = (filterValue, value, filterFn) => {
|
|
|
67
67
|
value: filterValue[key]
|
|
68
68
|
}));
|
|
69
69
|
filters.forEach((filter) => {
|
|
70
|
-
result =
|
|
70
|
+
result =
|
|
71
|
+
result &&
|
|
72
|
+
(filter.value !== undefined && filter.value !== '' && filter.value !== null
|
|
73
|
+
? filterFn(filter.option, filter.value, value)
|
|
74
|
+
: true);
|
|
71
75
|
});
|
|
72
76
|
return result;
|
|
73
77
|
};
|
|
78
|
+
const booleanFilter = (filterOption, filterValue, value) => {
|
|
79
|
+
const filterBool = typeof filterValue === 'string' ? filterValue === 'true' : !!filterValue;
|
|
80
|
+
const valBool = !!value;
|
|
81
|
+
switch (filterOption) {
|
|
82
|
+
case FilterOptionsEnum.e:
|
|
83
|
+
return valBool === filterBool;
|
|
84
|
+
case FilterOptionsEnum.ne:
|
|
85
|
+
return valBool !== filterBool;
|
|
86
|
+
default:
|
|
87
|
+
return false;
|
|
88
|
+
}
|
|
89
|
+
};
|
|
74
90
|
export const columnFilter = ({ filterValue, value }) => {
|
|
75
91
|
if (typeof value === 'object' && value instanceof Date) {
|
|
76
92
|
return applyFilter(filterValue, value, dateFilter);
|
|
77
93
|
}
|
|
78
|
-
else if (typeof value === 'number') {
|
|
94
|
+
else if (typeof value === 'number' || typeof value === 'bigint') {
|
|
79
95
|
return applyFilter(filterValue, value, numberFilter);
|
|
80
96
|
}
|
|
81
97
|
else if (typeof value === 'string') {
|
|
82
98
|
return applyFilter(filterValue, value, textFilter);
|
|
83
99
|
}
|
|
100
|
+
else if (typeof value === 'boolean') {
|
|
101
|
+
return applyFilter(filterValue, value, booleanFilter);
|
|
102
|
+
}
|
|
84
103
|
return false;
|
|
85
104
|
};
|
|
86
105
|
export const searchFilter = ({ filterValue, value }) => {
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -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 {};
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { SvelteComponent } from 'svelte';
|
|
2
2
|
import type { Writable } from 'svelte/store';
|
|
3
|
-
import { Receive } from '../../models/Models';
|
|
4
3
|
import type { FilterOptionsEnum } from '../../models/Enums';
|
|
5
4
|
import type { Columns, Filter, OrderBy, ServerColumn, ServerConfig } from '../../models/Models';
|
|
6
5
|
export declare const minWidth: (id: string, columns: Columns | undefined) => number;
|
|
@@ -23,7 +22,7 @@ export declare const missingValuesFn: (key: number | string, missingValues: {
|
|
|
23
22
|
}) => string | number;
|
|
24
23
|
export declare const updateTable: (pageSize: number, pageIndex: number, server: ServerConfig | undefined, filters: {
|
|
25
24
|
[key: string]: { [key in FilterOptionsEnum]?: number | string | Date; };
|
|
26
|
-
}, data: Writable<any[]>, serverItems: Writable<number> | undefined, columns: Columns | undefined, dispatch: any, order?: OrderBy[]) => Promise<
|
|
25
|
+
}, data: Writable<any[]>, serverItems: Writable<number> | undefined, columns: Columns | undefined, dispatch: any, order?: OrderBy[]) => Promise<Writable<any[]>>;
|
|
27
26
|
export declare const convertServerColumns: (serverColumns: ServerColumn[], columns: Columns | undefined) => Columns;
|
|
28
27
|
export declare const getMaxCellHeightInRow: (tableRef: HTMLTableElement, resizable: "columns" | "rows" | "none" | "both", optionsComponent: typeof SvelteComponent | undefined, rowHeights: Writable<{
|
|
29
28
|
[key: number]: {
|
|
@@ -31,9 +31,7 @@ export const cellStyle = (id, columns) => {
|
|
|
31
31
|
export const getResizeStyles = (rowHeights, id, index) => {
|
|
32
32
|
return `
|
|
33
33
|
min-height: ${rowHeights && rowHeights[+id] ? `${rowHeights[+id].min}px` : 'auto'};
|
|
34
|
-
max-height: ${index !== 0 && rowHeights && rowHeights[+id]
|
|
35
|
-
? `${rowHeights[+id].max}px`
|
|
36
|
-
: 'auto'};
|
|
34
|
+
max-height: ${index !== 0 && rowHeights && rowHeights[+id] ? `${rowHeights[+id].max}px` : 'auto'};
|
|
37
35
|
height: ${rowHeights && rowHeights[+id] ? `${rowHeights[+id].min}px` : 'auto'};
|
|
38
36
|
`;
|
|
39
37
|
};
|
|
@@ -86,7 +84,7 @@ export const jsonToCsv = (data) => {
|
|
|
86
84
|
// Create CSV rows
|
|
87
85
|
const rows = [
|
|
88
86
|
headers.join(','), // Header row
|
|
89
|
-
...json.map((row) => headers.map(header => escapeCsvCell(row[header])).join(',')) // Data rows
|
|
87
|
+
...json.map((row) => headers.map((header) => escapeCsvCell(row[header])).join(',')) // Data rows
|
|
90
88
|
];
|
|
91
89
|
// Join rows with newlines
|
|
92
90
|
return rows.join('\n');
|
|
@@ -154,7 +152,7 @@ export const updateTable = async (pageSize, pageIndex, server, filters, data, se
|
|
|
154
152
|
// remove %%% from the columns object
|
|
155
153
|
if (sendModel.order) {
|
|
156
154
|
sendModel.order.forEach((order) => {
|
|
157
|
-
if (order.column.includes(
|
|
155
|
+
if (order.column.includes('%%%')) {
|
|
158
156
|
const newKey = order.column.replaceAll('%%%', '.');
|
|
159
157
|
order.column = newKey;
|
|
160
158
|
}
|
|
@@ -176,19 +174,46 @@ export const updateTable = async (pageSize, pageIndex, server, filters, data, se
|
|
|
176
174
|
if (!fetchData.ok) {
|
|
177
175
|
throw new Error('Failed to fetch data');
|
|
178
176
|
}
|
|
179
|
-
|
|
177
|
+
// 1. Get the raw text of the response instead of parsing it automatically
|
|
178
|
+
const rawText = await fetchData.text();
|
|
179
|
+
// 2. Parse it with a reviver function that targets large integers
|
|
180
|
+
const response = JSON.parse(rawText, (key, value, context) => {
|
|
181
|
+
// If the parser thinks it's a number, but we have the raw source text
|
|
182
|
+
if (context && context.source && typeof value === 'number') {
|
|
183
|
+
// Check if the number exceeds JavaScript's safe integer limit
|
|
184
|
+
if (!Number.isSafeInteger(value)) {
|
|
185
|
+
// Return it natively as a JavaScript BigInt (e.g., 9223372036854775806n)
|
|
186
|
+
return BigInt(context.source);
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
return value;
|
|
190
|
+
});
|
|
191
|
+
const rawCount = response.count;
|
|
192
|
+
const hasRawCount = rawCount !== undefined && rawCount !== null && String(rawCount).trim().length > 0;
|
|
193
|
+
const parsedServerCount = hasRawCount
|
|
194
|
+
? typeof rawCount === 'number'
|
|
195
|
+
? rawCount
|
|
196
|
+
: Number(String(rawCount).replace(/[^0-9.-]/g, ''))
|
|
197
|
+
: Number.NaN;
|
|
198
|
+
const hasServerCount = hasRawCount && Number.isFinite(parsedServerCount) && parsedServerCount >= 0;
|
|
199
|
+
const normalizedCount = hasServerCount
|
|
200
|
+
? parsedServerCount
|
|
201
|
+
: Array.isArray(response.data)
|
|
202
|
+
? response.data.length
|
|
203
|
+
: 0;
|
|
204
|
+
// console.log('Server response', response);
|
|
180
205
|
// Format server columns to the client columns
|
|
181
206
|
if (response.columns !== undefined) {
|
|
182
|
-
console.log('Server columns', response.columns);
|
|
207
|
+
//console.log('Server columns', response.columns);
|
|
183
208
|
columns = convertServerColumns(response.columns, columns);
|
|
184
209
|
const clientCols = response.columns.reduce((acc, col) => {
|
|
185
|
-
console.log(col.key, col.column);
|
|
210
|
+
// console.log(col.key, col.column);
|
|
186
211
|
// replace the . with empty string
|
|
187
212
|
//const key = col.key.replaceAll('.' ,'');
|
|
188
213
|
///console.log(key, col.column);
|
|
189
214
|
// set the key to the columns object
|
|
190
|
-
col.column = col.column.replaceAll('.',
|
|
191
|
-
col.key = col.key.replaceAll('.',
|
|
215
|
+
col.column = col.column.replaceAll('.', '%%%');
|
|
216
|
+
col.key = col.key.replaceAll('.', '%%%');
|
|
192
217
|
acc[col.key] = col.column;
|
|
193
218
|
//acc[col.column] = col.column;
|
|
194
219
|
return acc;
|
|
@@ -198,20 +223,23 @@ export const updateTable = async (pageSize, pageIndex, server, filters, data, se
|
|
|
198
223
|
response.data.forEach((row, index) => {
|
|
199
224
|
const tmp = {};
|
|
200
225
|
Object.keys(row).forEach((key) => {
|
|
201
|
-
tmp[clientCols[key.replaceAll('.',
|
|
226
|
+
tmp[clientCols[key.replaceAll('.', '%%%')]] = row[key];
|
|
202
227
|
});
|
|
203
228
|
tmpArr.push(tmp);
|
|
204
229
|
});
|
|
205
|
-
dispatch('fetch',
|
|
230
|
+
dispatch('fetch', {
|
|
231
|
+
columns,
|
|
232
|
+
count: hasServerCount ? parsedServerCount : tmpArr.length
|
|
233
|
+
});
|
|
234
|
+
serverItems?.set(hasServerCount ? parsedServerCount : tmpArr.length);
|
|
206
235
|
data.set(tmpArr);
|
|
207
|
-
console.log('Server data', tmpArr);
|
|
236
|
+
// console.log('Server data', tmpArr);
|
|
208
237
|
return data;
|
|
209
238
|
}
|
|
210
|
-
serverItems?.set(
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
return response;
|
|
239
|
+
serverItems?.set(normalizedCount);
|
|
240
|
+
// If backend does not provide column metadata, still render returned rows.
|
|
241
|
+
data.set(response.data ?? []);
|
|
242
|
+
return data;
|
|
215
243
|
};
|
|
216
244
|
// Function to convert server data to client data
|
|
217
245
|
export const convertServerColumns = (serverColumns, columns) => {
|
|
@@ -253,24 +281,25 @@ export const convertServerColumns = (serverColumns, columns) => {
|
|
|
253
281
|
};
|
|
254
282
|
}
|
|
255
283
|
if (columns && col.column in columns) {
|
|
256
|
-
columnsConfig[col.column.replaceAll('.',
|
|
257
|
-
...columns[col.column.replaceAll('.',
|
|
284
|
+
columnsConfig[col.column.replaceAll('.', '%%%')] = {
|
|
285
|
+
...columns[col.column.replaceAll('.', '%%%')],
|
|
258
286
|
instructions
|
|
259
287
|
};
|
|
260
288
|
}
|
|
261
289
|
else {
|
|
262
|
-
columnsConfig[col.column.replaceAll('.',
|
|
290
|
+
columnsConfig[col.column.replaceAll('.', '%%%')] = {
|
|
263
291
|
instructions
|
|
264
292
|
};
|
|
265
293
|
}
|
|
266
294
|
});
|
|
267
|
-
console.log('Columns config', columnsConfig);
|
|
295
|
+
// console.log('Columns config', columnsConfig);
|
|
268
296
|
return columnsConfig;
|
|
269
297
|
};
|
|
270
298
|
// Calculates the maximum height of the cells in each row
|
|
271
299
|
export const getMaxCellHeightInRow = (tableRef, resizable, optionsComponent, rowHeights, tableId, rowHeight) => {
|
|
272
300
|
if (!tableRef || resizable === 'columns' || resizable === 'none')
|
|
273
301
|
return;
|
|
302
|
+
const measuredHeights = {};
|
|
274
303
|
tableRef.querySelectorAll('tbody tr').forEach((row, index) => {
|
|
275
304
|
const cells = row.querySelectorAll('td');
|
|
276
305
|
let maxHeight = optionsComponent ? 56 : 44;
|
|
@@ -285,17 +314,21 @@ export const getMaxCellHeightInRow = (tableRef, resizable, optionsComponent, row
|
|
|
285
314
|
minHeight = cellHeight + 2;
|
|
286
315
|
}
|
|
287
316
|
});
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
max: maxHeight - 24,
|
|
294
|
-
min: Math.max(minHeight - 24, rowHeight ?? 20)
|
|
295
|
-
}
|
|
317
|
+
const id = +row.id.split(`${tableId}-row-`)[1];
|
|
318
|
+
if (Number.isFinite(id)) {
|
|
319
|
+
measuredHeights[id] = {
|
|
320
|
+
max: maxHeight - 24,
|
|
321
|
+
min: Math.max(minHeight - 24, rowHeight ?? 20)
|
|
296
322
|
};
|
|
297
|
-
}
|
|
323
|
+
}
|
|
298
324
|
});
|
|
325
|
+
if (Object.keys(measuredHeights).length === 0) {
|
|
326
|
+
return;
|
|
327
|
+
}
|
|
328
|
+
rowHeights.update((rh) => ({
|
|
329
|
+
...rh,
|
|
330
|
+
...measuredHeights
|
|
331
|
+
}));
|
|
299
332
|
};
|
|
300
333
|
// Calculates the minimum width of the cells in each column
|
|
301
334
|
export const getMinCellWidthInColumn = (tableRef, colWidths, headerRowsLength, resizable) => {
|
|
@@ -12,19 +12,19 @@ export let feedback;
|
|
|
12
12
|
</script>
|
|
13
13
|
|
|
14
14
|
<InputContainer {id} label={title} {feedback} {required} {help} {description}>
|
|
15
|
-
<div
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
</div>
|
|
15
|
+
<div class="flex gap-2" class:flex-col={vertical}>
|
|
16
|
+
{#each source as item}
|
|
17
|
+
<label class="flex items-center space-x-2" for={item.key}>
|
|
18
|
+
<input
|
|
19
|
+
class="checkbox"
|
|
20
|
+
type="checkbox"
|
|
21
|
+
bind:group={target}
|
|
22
|
+
checked={item.key}
|
|
23
|
+
value={item.key}
|
|
24
|
+
id={item.key}
|
|
25
|
+
/>
|
|
26
|
+
<p>{item.value}</p>
|
|
27
|
+
</label>
|
|
28
|
+
{/each}
|
|
29
|
+
</div>
|
|
30
30
|
</InputContainer>
|